telepathy-gabble-0.18.2/0000755000175000017500000000000012312537051015014 5ustar00smcvsmcv00000000000000telepathy-gabble-0.18.2/gabble/0000755000175000017500000000000012312537051016230 5ustar00smcvsmcv00000000000000telepathy-gabble-0.18.2/gabble/types.h0000644000175000017500000000226512200204333017540 0ustar00smcvsmcv00000000000000/* * types.h - type definitions available to telepathy-gabble plugins * Copyright © 2010 Collabora Ltd. * Copyright © 2010 Nokia Corporation * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 GABBLE_PLUGINS_TYPES_H #define GABBLE_PLUGINS_TYPES_H #include G_BEGIN_DECLS typedef struct _GabbleConnection GabbleConnection; typedef struct _GabbleDiscoIdentity GabbleDiscoIdentity; typedef struct _GabblePlugin GabblePlugin; typedef struct _GabbleSidecar GabbleSidecar; G_END_DECLS #endif telepathy-gabble-0.18.2/gabble/sidecar.h0000644000175000017500000000407612200204333020010 0ustar00smcvsmcv00000000000000/* * sidecar.h — sidecar API available to telepathy-gabble plugins * Copyright © 2009 Collabora Ltd. * Copyright © 2009 Nokia Corporation * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 GABBLE_PLUGINS_SIDECAR_H #define GABBLE_PLUGINS_SIDECAR_H #include #include G_BEGIN_DECLS #define GABBLE_TYPE_SIDECAR (gabble_sidecar_get_type ()) #define GABBLE_SIDECAR(obj) \ (G_TYPE_CHECK_INSTANCE_CAST ((obj), GABBLE_TYPE_SIDECAR, GabbleSidecar)) #define GABBLE_IS_SIDECAR(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GABBLE_TYPE_SIDECAR)) #define GABBLE_SIDECAR_GET_INTERFACE(obj) \ (G_TYPE_INSTANCE_GET_INTERFACE ((obj), GABBLE_TYPE_SIDECAR, \ GabbleSidecarInterface)) typedef struct _GabbleSidecarInterface GabbleSidecarInterface; typedef GHashTable * (*GabbleSidecarGetImmutablePropertiesImpl) ( GabbleSidecar *); struct _GabbleSidecarInterface { GTypeInterface parent; /** * The D-Bus interface implemented by this sidecar. */ const gchar *interface; /** * An implementation of gabble_sidecar_get_immutable_properties(). */ GabbleSidecarGetImmutablePropertiesImpl get_immutable_properties; }; GType gabble_sidecar_get_type (void); const gchar *gabble_sidecar_get_interface (GabbleSidecar *sidecar); GHashTable *gabble_sidecar_get_immutable_properties (GabbleSidecar *sidecar); G_END_DECLS #endif telepathy-gabble-0.18.2/gabble/plugin.h0000644000175000017500000001175512200204333017676 0ustar00smcvsmcv00000000000000/* * plugin.h — plugin API for telepathy-gabble plugins * Copyright © 2009 Collabora Ltd. * Copyright © 2009 Nokia Corporation * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 GABBLE_PLUGINS_PLUGIN_H #define GABBLE_PLUGINS_PLUGIN_H #include #include #include #include #include #include #include G_BEGIN_DECLS #define GABBLE_TYPE_PLUGIN (gabble_plugin_get_type ()) #define GABBLE_PLUGIN(obj) \ (G_TYPE_CHECK_INSTANCE_CAST ((obj), GABBLE_TYPE_PLUGIN, GabblePlugin)) #define GABBLE_IS_PLUGIN(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GABBLE_TYPE_PLUGIN)) #define GABBLE_PLUGIN_GET_INTERFACE(obj) \ (G_TYPE_INSTANCE_GET_INTERFACE ((obj), GABBLE_TYPE_PLUGIN, \ GabblePluginInterface)) typedef struct _GabblePluginInterface GabblePluginInterface; typedef void (*GabblePluginCreateSidecarImpl) ( GabblePlugin *plugin, const gchar *sidecar_interface, GabblePluginConnection *plugin_connection, WockySession *session, GAsyncReadyCallback callback, gpointer user_data); /* The return type should be a new GPtrArray* which will be freed * straight after this function is called, so the pointer array must * not have a free function. */ typedef GPtrArray * (*GabblePluginCreateChannelManagersImpl) ( GabblePlugin *plugin, GabblePluginConnection *plugin_connection); typedef GabbleSidecar * (*GabblePluginCreateSidecarFinishImpl) ( GabblePlugin *plugin, GAsyncResult *result, GError **error); struct _GabblePluginPrivacyListMap { const gchar *presence_status_name; const gchar *privacy_list_name; }; typedef struct _GabblePluginPrivacyListMap GabblePluginPrivacyListMap; struct _GabblePluginInterface { GTypeInterface parent; /** * An arbitrary human-readable name identifying this plugin. */ const gchar *name; /** * A %NULL-terminated array of strings listing the sidecar D-Bus interfaces * implemented by this plugin. */ const gchar * const *sidecar_interfaces; /** * An implementation of gabble_plugin_create_sidecar_async(). */ GabblePluginCreateSidecarImpl create_sidecar_async; /** * An implementation of gabble_plugin_create_sidecar_finish(). */ GabblePluginCreateSidecarFinishImpl create_sidecar_finish; /** * The plugin's version, conventionally a "."-separated sequence of * numbers. */ const gchar *version; /** * Additional custom statuses supported by the plugin. */ TpPresenceStatusSpec *presence_statuses; /** * Privacy lists implementing specific statuses */ GabblePluginPrivacyListMap *privacy_list_map; /** * An optional callback to create additional channel managers. */ GabblePluginCreateChannelManagersImpl create_channel_managers; }; GType gabble_plugin_get_type (void); const gchar *gabble_plugin_get_name ( GabblePlugin *plugin); const gchar *gabble_plugin_get_version ( GabblePlugin *plugin); const gchar * const *gabble_plugin_get_sidecar_interfaces ( GabblePlugin *plugin); gboolean gabble_plugin_implements_sidecar ( GabblePlugin *plugin, const gchar *sidecar_interface); void gabble_plugin_create_sidecar_async ( GabblePlugin *plugin, const gchar *sidecar_interface, GabblePluginConnection *plugin_connection, WockySession *session, GAsyncReadyCallback callback, gpointer user_data); GabbleSidecar *gabble_plugin_create_sidecar_finish ( GabblePlugin *plugin, GAsyncResult *result, GError **error); const TpPresenceStatusSpec *gabble_plugin_get_custom_presence_statuses ( GabblePlugin *plugin); gboolean gabble_plugin_implements_presence_status ( GabblePlugin *plugin, const gchar *status); const gchar *gabble_plugin_presence_status_for_privacy_list ( GabblePlugin *plugin, const gchar *list_name); GPtrArray * gabble_plugin_create_channel_managers (GabblePlugin *plugin, GabblePluginConnection *plugin_connection); /** * gabble_plugin_create: * * Prototype for the plugin entry point. * * Returns: a new instance of this plugin, which must not be %NULL. */ GabblePlugin *gabble_plugin_create (void); typedef GabblePlugin *(*GabblePluginCreateImpl) (void); G_END_DECLS #endif telepathy-gabble-0.18.2/gabble/namespaces.h0000644000175000017500000000214112200204333020504 0ustar00smcvsmcv00000000000000/* * namespaces.h — namespace constants definitions that can be used by telepathy-gabble plugins * Copyright © 2009 Collabora Ltd. * Copyright © 2009 Nokia Corporation * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 GABBLE_PLUGINS_NAMESPACES_H #define GABBLE_PLUGINS_NAMESPACES_H #define NS_CAPS "http://jabber.org/protocol/caps" #define NS_GABBLE_CAPS "http://telepathy.freedesktop.org/caps" #endif telepathy-gabble-0.18.2/gabble/gabble.h0000644000175000017500000000245012200204333017604 0ustar00smcvsmcv00000000000000/* * gabble.h — API for telepathy-gabble plugins * Copyright © 2010 Collabora Ltd. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 GABBLE_PLUGINS_GABBLE_H #define GABBLE_PLUGINS_GABBLE_H #define IN_GABBLE_PLUGINS_GABBLE_H #include #include #include #include #include #include #include #include #include #include #include #undef IN_GABBLE_PLUGINS_GABBLE_H #endif telepathy-gabble-0.18.2/gabble/error.h0000644000175000017500000000243512200204333017524 0ustar00smcvsmcv00000000000000/* * error.h — error API available to telepathy-gabble plugins (and internals) * Copyright © 2010 Collabora Ltd. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 GABBLE_PLUGINS_ERROR_H #define GABBLE_PLUGINS_ERROR_H #include #include #include G_BEGIN_DECLS void gabble_set_tp_error_from_wocky (const GError *wocky_error, GError **error); void gabble_set_tp_conn_error_from_wocky (const GError *wocky_error, TpConnectionStatus previous_status, TpConnectionStatusReason *conn_reason, GError **error); G_END_DECLS #endif telepathy-gabble-0.18.2/gabble/plugin-connection.h0000644000175000017500000001137412200204333022030 0ustar00smcvsmcv00000000000000/* * plugin-connection.h — Connection API available to telepathy-gabble plugins * Copyright © 2012 Collabora Ltd. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 GABBLE_PLUGIN_CONNECTION_H #define GABBLE_PLUGIN_CONNECTION_H #include #include #include #include #include G_BEGIN_DECLS typedef struct _GabblePluginConnection GabblePluginConnection; typedef struct _GabblePluginConnectionInterface GabblePluginConnectionInterface; #define GABBLE_TYPE_PLUGIN_CONNECTION (gabble_plugin_connection_get_type ()) #define GABBLE_PLUGIN_CONNECTION(o) \ (G_TYPE_CHECK_INSTANCE_CAST ((o), GABBLE_TYPE_PLUGIN_CONNECTION, \ GabblePluginConnection)) #define GABBLE_IS_PLUGIN_CONNECTION(o) \ (G_TYPE_CHECK_INSTANCE_TYPE ((o), GABBLE_TYPE_PLUGIN_CONNECTION)) #define GABBLE_PLUGIN_CONNECTION_GET_IFACE(o) \ (G_TYPE_INSTANCE_GET_INTERFACE ((o), GABBLE_TYPE_PLUGIN_CONNECTION, \ GabblePluginConnectionInterface)) GType gabble_plugin_connection_get_type (void) G_GNUC_CONST; typedef gchar * (*GabblePluginConnectionAddSidecarCapsFunc) ( GabblePluginConnection *connection_service, const GabbleCapabilitySet *cap_set, const GPtrArray *identities); typedef gchar * (*GabblePluginConnectionAddSidecarCapsFullFunc) ( GabblePluginConnection *plugin_connection, const GabbleCapabilitySet *cap_set, const GPtrArray *identities, GPtrArray *data_forms); typedef WockySession * (*GabblePluginConnectionGetSessionFunc) ( GabblePluginConnection *plugin_connection); typedef gchar *(*GabblePluginConnectionGetFullJidFunc) ( GabblePluginConnection *plugin_connection); typedef const gchar * (*GabblePluginConnectionGetJidForCapsFunc) ( GabblePluginConnection *plugin_connection, WockyXep0115Capabilities *caps); typedef const gchar* (*GabblePluginConnectionPickBestResourceForCaps) ( GabblePluginConnection *plugin_connection, const gchar *jid, GabbleCapabilitySetPredicate predicate, gconstpointer user_data); typedef TpBaseContactList * (*GabblePluginConnectionGetContactList) ( GabblePluginConnection *plugin_connection); typedef WockyXep0115Capabilities * (*GabblePluginConnectionGetCaps) ( GabblePluginConnection *plugin_connection, TpHandle handle); struct _GabblePluginConnectionInterface { GTypeInterface parent; GabblePluginConnectionAddSidecarCapsFunc add_sidecar_own_caps; GabblePluginConnectionAddSidecarCapsFullFunc add_sidecar_own_caps_full; GabblePluginConnectionGetSessionFunc get_session; GabblePluginConnectionGetFullJidFunc get_full_jid; GabblePluginConnectionGetJidForCapsFunc get_jid_for_caps; GabblePluginConnectionPickBestResourceForCaps pick_best_resource_for_caps; GabblePluginConnectionGetContactList get_contact_list; GabblePluginConnectionGetCaps get_caps; }; gchar *gabble_plugin_connection_add_sidecar_own_caps ( GabblePluginConnection *plugin_service, const GabbleCapabilitySet *cap_set, const GPtrArray *identities); gchar *gabble_plugin_connection_add_sidecar_own_caps_full ( GabblePluginConnection *plugin_connection, const GabbleCapabilitySet *cap_set, const GPtrArray *identities, GPtrArray *data_forms) G_GNUC_WARN_UNUSED_RESULT; WockySession *gabble_plugin_connection_get_session ( GabblePluginConnection *plugin_connection); gchar *gabble_plugin_connection_get_full_jid (GabblePluginConnection *conn); const gchar *gabble_plugin_connection_get_jid_for_caps ( GabblePluginConnection *plugin_connection, WockyXep0115Capabilities *caps); const gchar *gabble_plugin_connection_pick_best_resource_for_caps ( GabblePluginConnection *plugin_connection, const gchar *jid, GabbleCapabilitySetPredicate predicate, gconstpointer user_data); TpBaseContactList *gabble_plugin_connection_get_contact_list ( GabblePluginConnection *plugin_connection); WockyXep0115Capabilities *gabble_plugin_connection_get_caps ( GabblePluginConnection *plugin_connection, TpHandle handle); G_END_DECLS #endif telepathy-gabble-0.18.2/gabble/caps-hash.h0000644000175000017500000000250312200204333020236 0ustar00smcvsmcv00000000000000/* * caps-hash.h - caps computing string hash (XEP-0115 v1.5) API available to telepathy-gabble plugins * Copyright (C) 2008-2010 Collabora Ltd. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 GABBLE_PLUGINS_CAPS_HASH_H #define GABBLE_PLUGINS_CAPS_HASH_H #include #include G_BEGIN_DECLS gchar *gabble_caps_hash_compute_full (const GabbleCapabilitySet *cap_set, const GPtrArray *identities, GPtrArray *data_forms) G_GNUC_WARN_UNUSED_RESULT; gchar *gabble_caps_hash_compute (const GabbleCapabilitySet *cap_set, const GPtrArray *identities) G_GNUC_WARN_UNUSED_RESULT; G_END_DECLS #endif telepathy-gabble-0.18.2/gabble/caps-channel-manager.h0000644000175000017500000000625312200204333022341 0ustar00smcvsmcv00000000000000/* * caps-channel-manager.h - interface holding capabilities functions for * channel managers * * Copyright (C) 2008 Collabora Ltd. * Copyright (C) 2008 Nokia Corporation * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 GABBLE_CAPS_CHANNEL_MANAGER_H #define GABBLE_CAPS_CHANNEL_MANAGER_H #include #include #include "capabilities.h" G_BEGIN_DECLS #define GABBLE_TYPE_CAPS_CHANNEL_MANAGER \ (gabble_caps_channel_manager_get_type ()) #define GABBLE_CAPS_CHANNEL_MANAGER(obj) \ (G_TYPE_CHECK_INSTANCE_CAST ((obj), \ GABBLE_TYPE_CAPS_CHANNEL_MANAGER, GabbleCapsChannelManager)) #define GABBLE_IS_CAPS_CHANNEL_MANAGER(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \ GABBLE_TYPE_CAPS_CHANNEL_MANAGER)) #define GABBLE_CAPS_CHANNEL_MANAGER_GET_INTERFACE(obj) \ (G_TYPE_INSTANCE_GET_INTERFACE ((obj), \ GABBLE_TYPE_CAPS_CHANNEL_MANAGER, GabbleCapsChannelManagerInterface)) typedef struct _GabbleCapsChannelManager GabbleCapsChannelManager; typedef struct _GabbleCapsChannelManagerInterface GabbleCapsChannelManagerInterface; /* virtual methods */ typedef void (*GabbleCapsChannelManagerGetContactCapsFunc) ( GabbleCapsChannelManager *manager, TpHandle handle, const GabbleCapabilitySet *caps, GPtrArray *arr); typedef void (*GabbleCapsChannelManagerResetCapsFunc) ( GabbleCapsChannelManager *manager); typedef void (*GabbleCapsChannelManagerAddCapFunc) ( GabbleCapsChannelManager *manager, GHashTable *cap, GabbleCapabilitySet *cap_set); typedef void (*GabbleCapsChannelManagerRepresentClientFunc) ( GabbleCapsChannelManager *manager, const gchar *client_name, const GPtrArray *filters, const gchar * const *cap_tokens, GabbleCapabilitySet *cap_set, GPtrArray *data_forms); void gabble_caps_channel_manager_get_contact_capabilities ( GabbleCapsChannelManager *caps_manager, TpHandle handle, const GabbleCapabilitySet *caps, GPtrArray *arr); void gabble_caps_channel_manager_represent_client ( GabbleCapsChannelManager *caps_manager, const gchar *client_name, const GPtrArray *filters, const gchar * const *cap_tokens, GabbleCapabilitySet *cap_set, GPtrArray *data_forms); struct _GabbleCapsChannelManagerInterface { GTypeInterface parent; GabbleCapsChannelManagerGetContactCapsFunc get_contact_caps; GabbleCapsChannelManagerRepresentClientFunc represent_client; gpointer priv; }; GType gabble_caps_channel_manager_get_type (void); G_END_DECLS #endif telepathy-gabble-0.18.2/gabble/capabilities-set.h0000644000175000017500000000677712200204333021632 0ustar00smcvsmcv00000000000000/* * capabilities-set.h - capabilities set API available to telepathy-gabble plugins * Copyright (C) 2005-2010 Collabora Ltd. * Copyright (C) 2005-2010 Nokia Corporation * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 GABBLE_PLUGINS_CAPABILITIES_SET_H #define GABBLE_PLUGINS_CAPABILITIES_SET_H #include #include G_BEGIN_DECLS /** * GabbleCapabilitySet: * * A set of capabilities. */ typedef struct _GabbleCapabilitySet GabbleCapabilitySet; GabbleCapabilitySet *gabble_capability_set_new (void); GabbleCapabilitySet *gabble_capability_set_new_from_stanza ( WockyNode *query_result); GabbleCapabilitySet *gabble_capability_set_copy ( const GabbleCapabilitySet *caps); void gabble_capability_set_update (GabbleCapabilitySet *target, const GabbleCapabilitySet *source); void gabble_capability_set_add (GabbleCapabilitySet *caps, const gchar *cap); gboolean gabble_capability_set_remove (GabbleCapabilitySet *caps, const gchar *cap); void gabble_capability_set_exclude (GabbleCapabilitySet *caps, const GabbleCapabilitySet *removed); void gabble_capability_set_intersect (GabbleCapabilitySet *target, const GabbleCapabilitySet *source); gint gabble_capability_set_size (const GabbleCapabilitySet *caps); gboolean gabble_capability_set_has (const GabbleCapabilitySet *caps, const gchar *cap); gboolean gabble_capability_set_has_one (const GabbleCapabilitySet *caps, const GabbleCapabilitySet *alternatives); gboolean gabble_capability_set_at_least (const GabbleCapabilitySet *caps, const GabbleCapabilitySet *query); gboolean gabble_capability_set_equals (const GabbleCapabilitySet *a, const GabbleCapabilitySet *b); void gabble_capability_set_clear (GabbleCapabilitySet *caps); void gabble_capability_set_free (GabbleCapabilitySet *caps); void gabble_capability_set_foreach (const GabbleCapabilitySet *caps, GFunc func, gpointer user_data); gchar *gabble_capability_set_dump (const GabbleCapabilitySet *caps, const gchar *indent); gchar *gabble_capability_set_dump_diff (const GabbleCapabilitySet *old_caps, const GabbleCapabilitySet *new_caps, const gchar *indent); typedef gboolean (*GabbleCapabilitySetPredicate) ( const GabbleCapabilitySet *set, gconstpointer user_data); /* These functions are compatible with GabbleCapabilitySetPredicate; * pass in the desired capabilities as the user_data */ #define gabble_capability_set_predicate_equals \ ((GabbleCapabilitySetPredicate) gabble_capability_set_equals) #define gabble_capability_set_predicate_has \ ((GabbleCapabilitySetPredicate) gabble_capability_set_has) #define gabble_capability_set_predicate_has_one \ ((GabbleCapabilitySetPredicate) gabble_capability_set_has_one) #define gabble_capability_set_predicate_at_least \ ((GabbleCapabilitySetPredicate) gabble_capability_set_at_least) G_END_DECLS #endif telepathy-gabble-0.18.2/gabble/capabilities.h0000644000175000017500000000655512200204333021033 0ustar00smcvsmcv00000000000000/* * capabilities.h - Connection.Interface.Capabilities constants and utilities * Copyright (C) 2005 Collabora Ltd. * Copyright (C) 2005 Nokia Corporation * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 __GABBLE_CAPABILITIES__H__ #define __GABBLE_CAPABILITIES__H__ #include #include /* Pseudo-capabilities for buggy or strange implementations, represented as * strings starting with a character not allowed in XML (the ASCII beep :-) */ #define QUIRK_PREFIX_CHAR '\x07' #define QUIRK_PREFIX "\x07" /* Gabble 0.7.x with 16 <= x < 29 omits @creator on */ #define QUIRK_OMITS_CONTENT_CREATORS "\x07omits-content-creators" /* The Google Webmail client doesn't support some features */ #define QUIRK_GOOGLE_WEBMAIL_CLIENT "\x07google-webmail-client" /* The Android GTalk client needs a quirk for component names */ #define QUIRK_ANDROID_GTALK_CLIENT "\x07android-gtalk-client" /* Some useful capability sets for Jingle etc. */ const GabbleCapabilitySet *gabble_capabilities_get_legacy (void); const GabbleCapabilitySet *gabble_capabilities_get_any_audio (void); const GabbleCapabilitySet *gabble_capabilities_get_any_video (void); const GabbleCapabilitySet *gabble_capabilities_get_any_audio_video (void); const GabbleCapabilitySet *gabble_capabilities_get_any_google_av (void); const GabbleCapabilitySet *gabble_capabilities_get_any_jingle_av (void); const GabbleCapabilitySet *gabble_capabilities_get_any_transport (void); const GabbleCapabilitySet *gabble_capabilities_get_geoloc_notify (void); const GabbleCapabilitySet *gabble_capabilities_get_olpc_notify (void); /* XEP-0115 version 1.3: * * "The names of the feature bundles MUST NOT be used for semantic purposes: * they are merely opaque identifiers" * * However, some old Jabber clients (e.g. Gabble 0.2) and various Google * clients require the bundle names "voice-v1" and "video-v1". We keep these * names for compatibility. */ #define BUNDLE_SHARE_V1 "share-v1" #define BUNDLE_VOICE_V1 "voice-v1" #define BUNDLE_VIDEO_V1 "video-v1" #define BUNDLE_CAMERA_V1 "camera-v1" #define BUNDLE_PMUC_V1 "pmuc-v1" const GabbleCapabilitySet *gabble_capabilities_get_bundle_share_v1 (void); const GabbleCapabilitySet *gabble_capabilities_get_bundle_voice_v1 (void); const GabbleCapabilitySet *gabble_capabilities_get_bundle_video_v1 (void); const GabbleCapabilitySet *gabble_capabilities_get_bundle_camera_v1 (void); /* Return the capabilities we always have */ const GabbleCapabilitySet *gabble_capabilities_get_fixed_caps (void); void gabble_capabilities_init (gpointer conn); void gabble_capabilities_finalize (gpointer conn); #endif /* __GABBLE_CAPABILITIES__H__ */ telepathy-gabble-0.18.2/gabble/telepathy-gabble.pc.in0000644000175000017500000000103412200204333022356 0ustar00smcvsmcv00000000000000prefix=@prefix@ exec_prefix=@exec_prefix@ libdir=@libdir@ includedir=@includedir@ libexecdir=@libexecdir@ pluginexecdir=@pluginexecdir@ pluginexeclibdir=@pluginexeclibdir@ gabblepath=${libexecdir}/telepathy-gabble # For plugins' tests Name: Telepathy-Gabble Description: XMPP backend for the Telepathy framework Version: @VERSION@ Requires: pkg-config >= 0.21 Requires.private: glib-2.0 >= 2.32, gobject-2.0, gio-2.0, telepathy-glib >= 0.19.9 Libs: -L${pluginexeclibdir} -lgabble-plugins -lwocky Cflags: -I${includedir}/telepathy-gabble-0 telepathy-gabble-0.18.2/gabble/telepathy-gabble-uninstalled.pc.in0000644000175000017500000000106612200204333024703 0ustar00smcvsmcv00000000000000prefix= exec_prefix= abs_top_srcdir=@abs_top_srcdir@ abs_top_builddir=@abs_top_builddir@ libdir=@libdir@ pluginexecdir=@pluginexecdir@ pluginexeclibdir=@pluginexeclibdir@ gabblepath=@abs_top_builddir@/src/telepathy-gabble # For plugins' tests Name: Telepathy-Gabble Description: XMPP backend for the Telepathy framework Version: @VERSION@ Requires: pkg-config >= 0.21 Requires.private: glib-2.0 >= 2.32, gobject-2.0, gio-2.0, telepathy-glib >= 0.19.9 Libs: ${abs_top_builddir}/src/libgabble-plugins.la Cflags: -I${abs_top_srcdir} -I${abs_top_srcdir}/lib/ext/wocky telepathy-gabble-0.18.2/gabble/Makefile.am0000644000175000017500000000070212200204333020251 0ustar00smcvsmcv00000000000000if ENABLE_PLUGIN_API pkgconfigdir = $(libdir)/pkgconfig pkgconfig_DATA = telepathy-gabble.pc gabbleincludedir = $(includedir)/telepathy-gabble-0/gabble gabbleinclude_HEADERS = $(headers) else EXTRA_DIST = $(headers) endif headers = \ capabilities.h \ capabilities-set.h \ caps-channel-manager.h \ caps-hash.h \ plugin-connection.h \ error.h \ gabble.h \ namespaces.h \ plugin.h \ sidecar.h \ types.h telepathy-gabble-0.18.2/gabble/Makefile.in0000644000175000017500000004614612312536074020314 0ustar00smcvsmcv00000000000000# Makefile.in generated by automake 1.14.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2013 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ VPATH = @srcdir@ am__is_gnu_make = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)' am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ subdir = gabble DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am \ $(srcdir)/telepathy-gabble-uninstalled.pc.in \ $(srcdir)/telepathy-gabble.pc.in \ $(am__gabbleinclude_HEADERS_DIST) ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/ax_config_dir.m4 \ $(top_srcdir)/m4/compiler.m4 $(top_srcdir)/m4/libtool.m4 \ $(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \ $(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \ $(top_srcdir)/m4/tp-compiler-flag.m4 \ $(top_srcdir)/m4/tp-compiler-warnings.m4 \ $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = telepathy-gabble-uninstalled.pc \ telepathy-gabble.pc CONFIG_CLEAN_VPATH_FILES = AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = SOURCES = DIST_SOURCES = am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; am__vpath_adj = case $$p in \ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ *) f=$$p;; \ esac; am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; am__install_max = 40 am__nobase_strip_setup = \ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` am__nobase_strip = \ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" am__nobase_list = $(am__nobase_strip_setup); \ for p in $$list; do echo "$$p $$p"; done | \ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ if (++n[$$2] == $(am__install_max)) \ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ END { for (dir in files) print dir, files[dir] }' am__base_list = \ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' am__uninstall_files_from_dir = { \ test -z "$$files" \ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ $(am__cd) "$$dir" && rm -f $$files; }; \ } am__installdirs = "$(DESTDIR)$(pkgconfigdir)" \ "$(DESTDIR)$(gabbleincludedir)" DATA = $(pkgconfig_DATA) am__gabbleinclude_HEADERS_DIST = capabilities.h capabilities-set.h \ caps-channel-manager.h caps-hash.h plugin-connection.h error.h \ gabble.h namespaces.h plugin.h sidecar.h types.h HEADERS = $(gabbleinclude_HEADERS) am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) # Read a list of newline-separated strings from the standard input, # and print each of them once, without duplicates. Input order is # *not* preserved. am__uniquify_input = $(AWK) '\ BEGIN { nonempty = 0; } \ { items[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in items) print i; }; } \ ' # Make sure the list of sources is unique. This is necessary because, # e.g., the same source file might be shared among _SOURCES variables # for different programs/libraries. am__define_uniq_tagged_files = \ list='$(am__tagged_files)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | $(am__uniquify_input)` ETAGS = etags CTAGS = ctags DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ AR = @AR@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CFLAGS = @CFLAGS@ CLIENT_TYPE = @CLIENT_TYPE@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CYGPATH_W = @CYGPATH_W@ DBUS_CFLAGS = @DBUS_CFLAGS@ DBUS_LIBS = @DBUS_LIBS@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DLLTOOL = @DLLTOOL@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ ENABLE_CODING_STYLE_CHECKS = @ENABLE_CODING_STYLE_CHECKS@ ENABLE_PLUGINS = @ENABLE_PLUGINS@ ERROR_CFLAGS = @ERROR_CFLAGS@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ GLIB_CFLAGS = @GLIB_CFLAGS@ GLIB_GENMARSHAL = @GLIB_GENMARSHAL@ GLIB_LIBS = @GLIB_LIBS@ GMODULE_CFLAGS = @GMODULE_CFLAGS@ GMODULE_LIBS = @GMODULE_LIBS@ GREP = @GREP@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ LD = @LD@ LDFLAGS = @LDFLAGS@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBTOOL = @LIBTOOL@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBOBJS = @LTLIBOBJS@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ NICE_CFLAGS = @NICE_CFLAGS@ NICE_LIBS = @NICE_LIBS@ NM = @NM@ NMEDIT = @NMEDIT@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ PYTHON = @PYTHON@ PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@ PYTHON_PLATFORM = @PYTHON_PLATFORM@ PYTHON_PREFIX = @PYTHON_PREFIX@ PYTHON_VERSION = @PYTHON_VERSION@ RANLIB = @RANLIB@ SED = @SED@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ SOUP_CFLAGS = @SOUP_CFLAGS@ SOUP_LIBS = @SOUP_LIBS@ STRIP = @STRIP@ TEST_PYTHON = @TEST_PYTHON@ TP_GLIB_CFLAGS = @TP_GLIB_CFLAGS@ TP_GLIB_LIBS = @TP_GLIB_LIBS@ VERSION = @VERSION@ WOCKY_CFLAGS = @WOCKY_CFLAGS@ WOCKY_LIBS = @WOCKY_LIBS@ XSLTPROC = @XSLTPROC@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ gabbletestsdir = @gabbletestsdir@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ mandir = @mandir@ mkdir_p = @mkdir_p@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ pkgpyexecdir = @pkgpyexecdir@ pkgpythondir = @pkgpythondir@ pluginexecdir = @pluginexecdir@ pluginexeclibdir = @pluginexeclibdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ pyexecdir = @pyexecdir@ pythondir = @pythondir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ srcdir = @srcdir@ sysconfdir = @sysconfdir@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ @ENABLE_PLUGIN_API_TRUE@pkgconfigdir = $(libdir)/pkgconfig @ENABLE_PLUGIN_API_TRUE@pkgconfig_DATA = telepathy-gabble.pc @ENABLE_PLUGIN_API_TRUE@gabbleincludedir = $(includedir)/telepathy-gabble-0/gabble @ENABLE_PLUGIN_API_TRUE@gabbleinclude_HEADERS = $(headers) @ENABLE_PLUGIN_API_FALSE@EXTRA_DIST = $(headers) headers = \ capabilities.h \ capabilities-set.h \ caps-channel-manager.h \ caps-hash.h \ plugin-connection.h \ error.h \ gabble.h \ namespaces.h \ plugin.h \ sidecar.h \ types.h all: all-am .SUFFIXES: $(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu gabble/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --gnu gabble/Makefile .PRECIOUS: Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): telepathy-gabble-uninstalled.pc: $(top_builddir)/config.status $(srcdir)/telepathy-gabble-uninstalled.pc.in cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ telepathy-gabble.pc: $(top_builddir)/config.status $(srcdir)/telepathy-gabble.pc.in cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs install-pkgconfigDATA: $(pkgconfig_DATA) @$(NORMAL_INSTALL) @list='$(pkgconfig_DATA)'; test -n "$(pkgconfigdir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(pkgconfigdir)'"; \ $(MKDIR_P) "$(DESTDIR)$(pkgconfigdir)" || exit 1; \ fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; \ done | $(am__base_list) | \ while read files; do \ echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(pkgconfigdir)'"; \ $(INSTALL_DATA) $$files "$(DESTDIR)$(pkgconfigdir)" || exit $$?; \ done uninstall-pkgconfigDATA: @$(NORMAL_UNINSTALL) @list='$(pkgconfig_DATA)'; test -n "$(pkgconfigdir)" || list=; \ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ dir='$(DESTDIR)$(pkgconfigdir)'; $(am__uninstall_files_from_dir) install-gabbleincludeHEADERS: $(gabbleinclude_HEADERS) @$(NORMAL_INSTALL) @list='$(gabbleinclude_HEADERS)'; test -n "$(gabbleincludedir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(gabbleincludedir)'"; \ $(MKDIR_P) "$(DESTDIR)$(gabbleincludedir)" || exit 1; \ fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; \ done | $(am__base_list) | \ while read files; do \ echo " $(INSTALL_HEADER) $$files '$(DESTDIR)$(gabbleincludedir)'"; \ $(INSTALL_HEADER) $$files "$(DESTDIR)$(gabbleincludedir)" || exit $$?; \ done uninstall-gabbleincludeHEADERS: @$(NORMAL_UNINSTALL) @list='$(gabbleinclude_HEADERS)'; test -n "$(gabbleincludedir)" || list=; \ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ dir='$(DESTDIR)$(gabbleincludedir)'; $(am__uninstall_files_from_dir) ID: $(am__tagged_files) $(am__define_uniq_tagged_files); mkid -fID $$unique tags: tags-am TAGS: tags tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) set x; \ here=`pwd`; \ $(am__define_uniq_tagged_files); \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ if test $$# -gt 0; then \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ "$$@" $$unique; \ else \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ $$unique; \ fi; \ fi ctags: ctags-am CTAGS: ctags ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) $(am__define_uniq_tagged_files); \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" cscopelist: cscopelist-am cscopelist-am: $(am__tagged_files) list='$(am__tagged_files)'; \ case "$(srcdir)" in \ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ *) sdir=$(subdir)/$(srcdir) ;; \ esac; \ for i in $$list; do \ if test -f "$$i"; then \ echo "$(subdir)/$$i"; \ else \ echo "$$sdir/$$i"; \ fi; \ done >> $(top_builddir)/cscope.files distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags distdir: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done check-am: all-am check: check-am all-am: Makefile $(DATA) $(HEADERS) installdirs: for dir in "$(DESTDIR)$(pkgconfigdir)" "$(DESTDIR)$(gabbleincludedir)"; do \ test -z "$$dir" || $(MKDIR_P) "$$dir"; \ done install: install-am install-exec: install-exec-am install-data: install-data-am uninstall: uninstall-am install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-am install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." clean: clean-am clean-am: clean-generic clean-libtool mostlyclean-am distclean: distclean-am -rm -f Makefile distclean-am: clean-am distclean-generic distclean-tags dvi: dvi-am dvi-am: html: html-am html-am: info: info-am info-am: install-data-am: install-gabbleincludeHEADERS install-pkgconfigDATA install-dvi: install-dvi-am install-dvi-am: install-exec-am: install-html: install-html-am install-html-am: install-info: install-info-am install-info-am: install-man: install-pdf: install-pdf-am install-pdf-am: install-ps: install-ps-am install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-am -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-am mostlyclean-am: mostlyclean-generic mostlyclean-libtool pdf: pdf-am pdf-am: ps: ps-am ps-am: uninstall-am: uninstall-gabbleincludeHEADERS uninstall-pkgconfigDATA .MAKE: install-am install-strip .PHONY: CTAGS GTAGS TAGS all all-am check check-am clean clean-generic \ clean-libtool cscopelist-am ctags ctags-am distclean \ distclean-generic distclean-libtool distclean-tags distdir dvi \ dvi-am html html-am info info-am install install-am \ install-data install-data-am install-dvi install-dvi-am \ install-exec install-exec-am install-gabbleincludeHEADERS \ install-html install-html-am install-info install-info-am \ install-man install-pdf install-pdf-am install-pkgconfigDATA \ install-ps install-ps-am install-strip installcheck \ installcheck-am installdirs maintainer-clean \ maintainer-clean-generic mostlyclean mostlyclean-generic \ mostlyclean-libtool pdf pdf-am ps ps-am tags tags-am uninstall \ uninstall-am uninstall-gabbleincludeHEADERS \ uninstall-pkgconfigDATA # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: telepathy-gabble-0.18.2/test-driver0000755000175000017500000001027712312536074017225 0ustar00smcvsmcv00000000000000#! /bin/sh # test-driver - basic testsuite driver script. scriptversion=2013-07-13.22; # UTC # Copyright (C) 2011-2013 Free Software Foundation, Inc. # # This program 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 2, or (at your option) # any later version. # # This program 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 this program. If not, see . # As a special exception to the GNU General Public License, if you # distribute this file as part of a program that contains a # configuration script generated by Autoconf, you may include it under # the same distribution terms that you use for the rest of that program. # This file is maintained in Automake, please report # bugs to or send patches to # . # Make unconditional expansion of undefined variables an error. This # helps a lot in preventing typo-related bugs. set -u usage_error () { echo "$0: $*" >&2 print_usage >&2 exit 2 } print_usage () { cat <$log_file 2>&1 estatus=$? if test $enable_hard_errors = no && test $estatus -eq 99; then estatus=1 fi case $estatus:$expect_failure in 0:yes) col=$red res=XPASS recheck=yes gcopy=yes;; 0:*) col=$grn res=PASS recheck=no gcopy=no;; 77:*) col=$blu res=SKIP recheck=no gcopy=yes;; 99:*) col=$mgn res=ERROR recheck=yes gcopy=yes;; *:yes) col=$lgn res=XFAIL recheck=no gcopy=yes;; *:*) col=$red res=FAIL recheck=yes gcopy=yes;; esac # Report outcome to console. echo "${col}${res}${std}: $test_name" # Register the test result, and other relevant metadata. echo ":test-result: $res" > $trs_file echo ":global-test-result: $res" >> $trs_file echo ":recheck: $recheck" >> $trs_file echo ":copy-in-global-log: $gcopy" >> $trs_file # Local Variables: # mode: shell-script # sh-indentation: 2 # eval: (add-hook 'write-file-hooks 'time-stamp) # time-stamp-start: "scriptversion=" # time-stamp-format: "%:y-%02m-%02d.%02H" # time-stamp-time-zone: "UTC" # time-stamp-end: "; # UTC" # End: telepathy-gabble-0.18.2/tests/0000755000175000017500000000000012312537051016156 5ustar00smcvsmcv00000000000000telepathy-gabble-0.18.2/tests/suppressions/0000755000175000017500000000000012312537051020733 5ustar00smcvsmcv00000000000000telepathy-gabble-0.18.2/tests/suppressions/tp-glib.supp0000644000175000017500000001020112200204333023162 0ustar00smcvsmcv00000000000000# Valgrind error suppression file # ============================= libc ================================== { ld.so initialization + selinux Memcheck:Leak ... fun:_dl_init obj:/lib/ld-*.so } { dlopen initialization, triggered by handle-leak-debug code Memcheck:Leak ... fun:__libc_dlopen_mode fun:init fun:backtrace fun:handle_leak_debug_bt fun:dynamic_ensure_handle fun:tp_handle_ensure } # ============================= GLib ================================== { g_set_prgname copies its argument Memcheck:Leak ... fun:g_set_prgname } { one g_get_charset per child^Wprocess Memcheck:Leak ... fun:g_get_charset } { GQuarks can't be freed Memcheck:Leak ... fun:g_quark_from_static_string } { GQuarks can't be freed Memcheck:Leak ... fun:g_quark_from_string } { interned strings can't be freed Memcheck:Leak ... fun:g_intern_string } { interned strings can't be freed Memcheck:Leak ... fun:g_intern_static_string } { shared global default g_main_context Memcheck:Leak ... fun:g_main_context_new fun:g_main_context_default } { GTest initialization Memcheck:Leak ... fun:g_test_init fun:main } { GTest admin Memcheck:Leak ... fun:g_test_add_vtable } { GTest pseudorandomness Memcheck:Leak ... fun:g_rand_new_with_seed_array fun:test_run_seed ... fun:g_test_run } { GSLice initialization Memcheck:Leak ... fun:g_malloc0 fun:g_slice_init_nomessage fun:g_slice_alloc } # ============================= GObject =============================== { g_type_init Memcheck:Leak ... fun:g_type_init } { g_type_register_static Memcheck:Leak ... fun:g_type_register_static } # ============================= dbus-glib ============================= { dbus-glib, https://bugs.freedesktop.org/show_bug.cgi?id=14125 Memcheck:Addr4 fun:g_hash_table_foreach obj:/usr/lib/libdbus-glib-1.so.2.1.0 fun:g_object_run_dispose } { registering marshallers is permanent Memcheck:Leak ... fun:dbus_g_object_register_marshaller_array fun:dbus_g_object_register_marshaller } { dbus-glib specialized GTypes are permanent Memcheck:Leak ... fun:dbus_g_type_specialized_init } { libdbus shared connection Memcheck:Leak ... fun:dbus_g_bus_get } { dbus-gobject registrations aren't freed unless we fall off the bus Memcheck:Leak ... fun:g_slist_append fun:dbus_g_connection_register_g_object } { DBusGProxy slots aren't freed unless we fall off the bus Memcheck:Leak ... fun:dbus_connection_allocate_data_slot ... fun:dbus_g_proxy_constructor } { error registrations are for life, not just for Christmas Memcheck:Leak ... fun:dbus_g_error_domain_register } # ============================= telepathy-glib ======================== { tp_dbus_daemon_constructor @daemons once per DBusConnection Memcheck:Leak ... fun:g_slice_alloc fun:tp_dbus_daemon_constructor } { tp_proxy_subclass_add_error_mapping refs the enum Memcheck:Leak ... fun:g_type_class_ref fun:tp_proxy_subclass_add_error_mapping } { tp_proxy_or_subclass_hook_on_interface_add never frees its list Memcheck:Leak ... fun:tp_proxy_or_subclass_hook_on_interface_add } { tp_dbus_daemon_constructor filter not freed til we fall off the bus Memcheck:Leak ... fun:dbus_connection_add_filter fun:tp_dbus_daemon_constructor } # ============================= unclassified ========================== { creating param specs in tp_proxy_class_intern_init Memcheck:Leak fun:memalign fun:posix_memalign fun:slab_allocator_alloc_chunk fun:g_slice_alloc fun:g_slice_alloc0 fun:g_type_create_instance fun:g_param_spec_internal fun:g_param_spec_string } { ld.so initialization on glibc 2.9 Memcheck:Cond fun:_dl_relocate_object fun:dl_main fun:_dl_sysdep_start fun:_dl_start obj:/lib/ld-2.9.so } { ld.so initialization on glibc 2.9 Memcheck:Cond fun:strlen fun:_dl_init_paths fun:dl_main fun:_dl_sysdep_start fun:_dl_start obj:/lib/ld-2.9.so } telepathy-gabble-0.18.2/tests/suppressions/gabble.supp0000644000175000017500000000702212200204333023047 0ustar00smcvsmcv00000000000000# Gabble leaks { we leak one default resource hash per process Memcheck:Leak ... fun:g_compute_checksum_for_data fun:sha1_hex fun:gabble_connection_constructed } { Test resolver leaks the records that were added Memcheck:Leak ... fun:test_resolver_add_A } # Glib type registration one-time leaks { g_type_init_with_debug_flags leaks one-time per registered type Memcheck:Leak ... fun:g_type_init_with_debug_flags } { g_type_register_fundamental, same story Memcheck:Leak ... fun:g_type_register_fundamental } { Various mixins set type qdata, types stay alive Memcheck:Leak ... fun:g_type_set_qdata } { Information about static interface lives forever Memcheck:Leak ... fun:g_type_add_interface_static } { Type prerequisites Memcheck:Leak ... fun:g_type_interface_add_prerequisite } { Various memory is never freed when first initializing a type class Memcheck:Leak ... fun:g_type_class_ref } # Glib mainloop one time leaks { Default main context stays alive an keeps an array around for pending fds Memcheck:Leak fun:malloc fun:g_malloc fun:g_main_context_iterate } { Default main context stays alive an keeps an array for pending dispatches Memcheck:Leak ... fun:g_ptr_array_add fun:g_main_context_check fun:g_main_context_iterate } { Global hashtable of signal handlers, memory allocated when resized Memcheck:Leak fun:calloc fun:g_malloc0 fun:g_hash_table_remove_internal fun:g_signal_handlers_destroy } { g_main_loop_run constructs a GStaticPrivate GMainDispatch Memcheck:Leak ... fun:get_dispatch } # glib one-time initialisaton of various bits { Random seed initialization Memcheck:Leak ... fun:g_rand_new fun:g_random_int_range } { GDataSet has a global hashtable that leaks per process Memcheck:Leak ... fun:g_data_initialize } { GIO has a static mapping to various connection factories Memcheck:Leak ... fun:g_socket_connection_factory_register_type } { GLib has a static copy of the userdir Memcheck:Leak ... fun:g_init_user_config_dir } { Caching of the tmp location Memcheck:Leak ... fun:g_get_any_init_do } { thread init causes g_get_language_name to cache stuff Memcheck:Leak ... fun:g_get_language_names } { Thread initialisation Memcheck:Leak ... fun:g_private_new_posix_impl } { Thread initialisation Memcheck:Leak ... fun:g_thread_init_glib } # telepathy-glib leaks the dbus connection, which causes dbus to have some # stuff around on exit... { the subtree that stores objects is reallocated in _register_g_object Memcheck:Leak ... fun:dbus_g_connection_register_g_object } { As we leak a connection, the corresponding dataslots bookkeeping is leaked Memcheck:Leak ... fun:dbus_realloc fun:_dbus_data_slot_allocator_alloc fun:tp_dbus_daemon_constructor } { As we leak a connection, the corresponding dataslots bookkeeping is leaked Memcheck:Leak ... fun:dbus_realloc fun:_dbus_data_slot_list_set fun:dbus_connection_set_data fun:tp_dbus_daemon_constructor } # dbus-glib type registration one-time leaks { dbus-glib specialized GTypes are permanent Memcheck:Leak ... fun:lookup_or_register_specialized } { dbus-glib object type information leaks Memcheck:Leak ... fun:dbus_g_object_type_install_info } # misc library one-time leaks { global gnutls data Memcheck:Leak ... fun:gnutls_global_init } { selinux, we just don't know Memcheck:Leak fun:malloc fun:getdelim obj:/lib/libselinux.so.1 } telepathy-gabble-0.18.2/tests/suppressions/Makefile.am0000644000175000017500000000004712200204333022756 0ustar00smcvsmcv00000000000000EXTRA_DIST = gabble.supp tp-glib.supp telepathy-gabble-0.18.2/tests/suppressions/Makefile.in0000644000175000017500000003125512312536074023012 0ustar00smcvsmcv00000000000000# Makefile.in generated by automake 1.14.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2013 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ VPATH = @srcdir@ am__is_gnu_make = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)' am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ subdir = tests/suppressions DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/ax_config_dir.m4 \ $(top_srcdir)/m4/compiler.m4 $(top_srcdir)/m4/libtool.m4 \ $(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \ $(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \ $(top_srcdir)/m4/tp-compiler-flag.m4 \ $(top_srcdir)/m4/tp-compiler-warnings.m4 \ $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = SOURCES = DIST_SOURCES = am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ AR = @AR@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CFLAGS = @CFLAGS@ CLIENT_TYPE = @CLIENT_TYPE@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CYGPATH_W = @CYGPATH_W@ DBUS_CFLAGS = @DBUS_CFLAGS@ DBUS_LIBS = @DBUS_LIBS@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DLLTOOL = @DLLTOOL@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ ENABLE_CODING_STYLE_CHECKS = @ENABLE_CODING_STYLE_CHECKS@ ENABLE_PLUGINS = @ENABLE_PLUGINS@ ERROR_CFLAGS = @ERROR_CFLAGS@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ GLIB_CFLAGS = @GLIB_CFLAGS@ GLIB_GENMARSHAL = @GLIB_GENMARSHAL@ GLIB_LIBS = @GLIB_LIBS@ GMODULE_CFLAGS = @GMODULE_CFLAGS@ GMODULE_LIBS = @GMODULE_LIBS@ GREP = @GREP@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ LD = @LD@ LDFLAGS = @LDFLAGS@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBTOOL = @LIBTOOL@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBOBJS = @LTLIBOBJS@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ NICE_CFLAGS = @NICE_CFLAGS@ NICE_LIBS = @NICE_LIBS@ NM = @NM@ NMEDIT = @NMEDIT@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ PYTHON = @PYTHON@ PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@ PYTHON_PLATFORM = @PYTHON_PLATFORM@ PYTHON_PREFIX = @PYTHON_PREFIX@ PYTHON_VERSION = @PYTHON_VERSION@ RANLIB = @RANLIB@ SED = @SED@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ SOUP_CFLAGS = @SOUP_CFLAGS@ SOUP_LIBS = @SOUP_LIBS@ STRIP = @STRIP@ TEST_PYTHON = @TEST_PYTHON@ TP_GLIB_CFLAGS = @TP_GLIB_CFLAGS@ TP_GLIB_LIBS = @TP_GLIB_LIBS@ VERSION = @VERSION@ WOCKY_CFLAGS = @WOCKY_CFLAGS@ WOCKY_LIBS = @WOCKY_LIBS@ XSLTPROC = @XSLTPROC@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ gabbletestsdir = @gabbletestsdir@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ mandir = @mandir@ mkdir_p = @mkdir_p@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ pkgpyexecdir = @pkgpyexecdir@ pkgpythondir = @pkgpythondir@ pluginexecdir = @pluginexecdir@ pluginexeclibdir = @pluginexeclibdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ pyexecdir = @pyexecdir@ pythondir = @pythondir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ srcdir = @srcdir@ sysconfdir = @sysconfdir@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ EXTRA_DIST = gabble.supp tp-glib.supp all: all-am .SUFFIXES: $(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu tests/suppressions/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --gnu tests/suppressions/Makefile .PRECIOUS: Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs tags TAGS: ctags CTAGS: cscope cscopelist: distdir: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done check-am: all-am check: check-am all-am: Makefile installdirs: install: install-am install-exec: install-exec-am install-data: install-data-am uninstall: uninstall-am install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-am install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." clean: clean-am clean-am: clean-generic clean-libtool mostlyclean-am distclean: distclean-am -rm -f Makefile distclean-am: clean-am distclean-generic dvi: dvi-am dvi-am: html: html-am html-am: info: info-am info-am: install-data-am: install-dvi: install-dvi-am install-dvi-am: install-exec-am: install-html: install-html-am install-html-am: install-info: install-info-am install-info-am: install-man: install-pdf: install-pdf-am install-pdf-am: install-ps: install-ps-am install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-am -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-am mostlyclean-am: mostlyclean-generic mostlyclean-libtool pdf: pdf-am pdf-am: ps: ps-am ps-am: uninstall-am: .MAKE: install-am install-strip .PHONY: all all-am check check-am clean clean-generic clean-libtool \ cscopelist-am ctags-am distclean distclean-generic \ distclean-libtool distdir dvi dvi-am html html-am info info-am \ install install-am install-data install-data-am install-dvi \ install-dvi-am install-exec install-exec-am install-html \ install-html-am install-info install-info-am install-man \ install-pdf install-pdf-am install-ps install-ps-am \ install-strip installcheck installcheck-am installdirs \ maintainer-clean maintainer-clean-generic mostlyclean \ mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ tags-am uninstall uninstall-am # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: telepathy-gabble-0.18.2/tests/twisted/0000755000175000017500000000000012312537051017641 5ustar00smcvsmcv00000000000000telepathy-gabble-0.18.2/tests/twisted/run-test.sh.in0000644000175000017500000000316312227000321022354 0ustar00smcvsmcv00000000000000#!/bin/sh if test "x$GABBLE_TEST_UNINSTALLED" = x; then script_fullname=`readlink -e "@gabbletestsdir@/twisted/run-test.sh"` if [ `readlink -e "$0"` != "$script_fullname" ] ; then echo "This script is meant to be installed at $script_fullname" >&2 exit 1 fi test_src="@gabbletestsdir@" test_build="@gabbletestsdir@" config_file="@gabbletestsdir@/twisted/tools/servicedir/tmp-session-bus.conf" PYTHONPATH="@gabbletestsdir@/twisted" export PYTHONPATH GABBLE_TWISTED_PATH="@gabbletestsdir@/twisted" export GABBLE_TWISTED_PATH else if test -z "$GABBLE_ABS_TOP_SRCDIR"; then echo "GABBLE_ABS_TOP_SRCDIR must be set" >&2 exit 1 fi if test -z "$GABBLE_ABS_TOP_BUILDDIR"; then echo "GABBLE_ABS_TOP_BUILDDIR must be set" >&2 exit 1 fi test_src="${GABBLE_ABS_TOP_SRCDIR}/tests" test_build="${GABBLE_ABS_TOP_BUILDDIR}/tests" config_file="${test_build}/twisted/tools/servicedir-uninstalled/tmp-session-bus.conf" PYTHONPATH="${test_src}/twisted:${test_build}/twisted" export PYTHONPATH GABBLE_TWISTED_PATH="${test_src}/twisted" export GABBLE_TWISTED_PATH fi if [ -n "$1" ] ; then list="$1" else list=$(cat "${test_build}"/twisted/gabble-twisted-tests.list) fi any_failed=0 for i in $list ; do echo "Testing $i ..." sh "${test_src}/twisted/tools/with-session-bus.sh" \ ${GABBLE_TEST_SLEEP} \ --config-file="${config_file}" \ -- \ @TEST_PYTHON@ -u "${test_src}/twisted/$i" e=$? case "$e" in (0) echo "PASS: $i" ;; (77) echo "SKIP: $i" ;; (*) any_failed=1 echo "FAIL: $i ($e)" ;; esac done exit $any_failed telepathy-gabble-0.18.2/tests/twisted/test-resolver.h0000644000175000017500000000413212200204333022616 0ustar00smcvsmcv00000000000000/* * test-resolver.c - Source for TestResolver * Copyright © 2009 Collabora Ltd. * @author Vivek Dasmohapatra * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 __TEST_RESOLVER_H__ #define __TEST_RESOLVER_H__ #include #include G_BEGIN_DECLS GType test_resolver_get_type (void); #define TEST_TYPE_RESOLVER (test_resolver_get_type ()) #define TEST_RESOLVER(o) \ (G_TYPE_CHECK_INSTANCE_CAST ((o), TEST_TYPE_RESOLVER, TestResolver)) #define TEST_RESOLVER_CLASS(k) \ (G_TYPE_CHECK_CLASS_CAST((k), TEST_TYPE_RESOLVER, TestResolverClass)) #define TEST_IS_RESOLVER(o) \ (G_TYPE_CHECK_INSTANCE_TYPE ((o), TEST_TYPE_RESOLVER)) #define TEST_IS_RESOLVER_CLASS(k) \ (G_TYPE_CHECK_CLASS_TYPE ((k), TEST_TYPE_RESOLVER)) #define TEST_RESOLVER_GET_CLASS(o) \ (G_TYPE_INSTANCE_GET_CLASS ((o), TEST_TYPE_RESOLVER, TestResolverClass)) typedef struct { GResolver parent_instance; GList *fake_A; GList *fake_SRV; } TestResolver; typedef struct { GResolverClass parent_class; } TestResolverClass; void test_resolver_reset (TestResolver *tr); gboolean test_resolver_add_A (TestResolver *tr, const char *hostname, const char *addr); gboolean test_resolver_add_SRV (TestResolver *tr, const char *service, const char *protocol, const char *domain, const char *addr, guint16 port); G_END_DECLS #endif /* __TEST_RESOLVER_H__ */ telepathy-gabble-0.18.2/tests/twisted/test-resolver.c0000644000175000017500000001625012200204333022615 0ustar00smcvsmcv00000000000000/* * test-resolver.c - Source for TestResolver * Copyright © 2009 Collabora Ltd. * @author Vivek Dasmohapatra * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 */ /* this code largely culled from gunixresolver.c in glib and modified to * make a dummy resolver we can insert duff records in on the fly */ /* examples: * GResolver *kludged; * kludged = g_object_new (TEST_TYPE_RESOLVER, NULL); * g_resolver_set_default (kludged); * test_resolver_add_SRV (TEST_RESOLVER (kludged), * "xmpp-client", "tcp", "jabber.earth.li", "localhost", 1337); * test_resolver_add_A (TEST_RESOLVER (kludged), "localhost", "127.0.1.1"); */ #include "config.h" #include #include #ifdef G_OS_WIN32 #include #else #include #include #include #endif #include "test-resolver.h" typedef struct _fake_host { char *key; char *addr; } fake_host; typedef struct _fake_serv { char *key; GSrvTarget *srv; } fake_serv; G_DEFINE_TYPE (TestResolver, test_resolver, G_TYPE_RESOLVER); /* ************************************************************************* */ static gchar * _service_rrname (const char *service, const char *protocol, const char *domain) { gchar *rrname, *ascii_domain; ascii_domain = g_hostname_to_ascii (domain); rrname = g_strdup_printf ("_%s._%s.%s", service, protocol, ascii_domain); g_free (ascii_domain); return rrname; } static GList * find_fake_services (TestResolver *tr, const char *name) { GList *fake = NULL; GList *rval = NULL; for (fake = tr->fake_SRV; fake != NULL; fake = g_list_next (fake)) { fake_serv *entry = fake->data; if (entry != NULL && !g_strcmp0 (entry->key, name)) rval = g_list_append (rval, g_srv_target_copy (entry->srv)); } return rval; } static GList * find_fake_hosts (TestResolver *tr, const char *name) { GList *fake = NULL; GList *rval = NULL; for (fake = tr->fake_A; fake != NULL; fake = g_list_next (fake)) { fake_host *entry = fake->data; if (entry != NULL && !g_strcmp0 (entry->key, name)) rval = g_list_append (rval, g_inet_address_new_from_string (entry->addr)); } return rval; } static void lookup_service_async (GResolver *resolver, const char *rr, GCancellable *cancellable, GAsyncReadyCallback cb, gpointer data) { TestResolver *tr = TEST_RESOLVER (resolver); GList *addr = find_fake_services (tr, rr); GObject *source = G_OBJECT (resolver); GSimpleAsyncResult *res = g_simple_async_result_new (source, cb, data, lookup_service_async); if (addr != NULL) g_simple_async_result_set_op_res_gpointer (res, addr, NULL); else g_simple_async_result_set_error (res, G_RESOLVER_ERROR, G_RESOLVER_ERROR_NOT_FOUND, "No fake SRV record registered"); g_simple_async_result_complete_in_idle (res); g_object_unref (res); } static GList * lookup_service_finish (GResolver *resolver, GAsyncResult *result, GError **error) { GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (result); if (g_simple_async_result_propagate_error (simple, error)) return NULL; return g_simple_async_result_get_op_res_gpointer (simple); } static GList * lookup_by_name (GResolver *resolver, const gchar *hostname, GCancellable *cancellable, GError **error) { GList *result; result = find_fake_hosts (TEST_RESOLVER (resolver), hostname); if (result == NULL) g_set_error (error, G_RESOLVER_ERROR, G_RESOLVER_ERROR_NOT_FOUND, "No fake hostname record registered"); return result; } static void lookup_by_name_async (GResolver *resolver, const gchar *hostname, GCancellable *cancellable, GAsyncReadyCallback cb, gpointer data) { GObject *source = G_OBJECT (resolver); GSimpleAsyncResult *res = g_simple_async_result_new (source, cb, data, NULL); GList *addr; GError *error = NULL; addr = lookup_by_name (resolver, hostname, NULL, &error); if (addr != NULL) { g_simple_async_result_set_op_res_gpointer (res, addr, NULL); } else { g_simple_async_result_set_from_error (res, error); g_error_free (error); } g_simple_async_result_complete_in_idle (res); g_object_unref (res); } static GList * lookup_by_name_finish (GResolver *resolver, GAsyncResult *result, GError **error) { GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (result); if (g_simple_async_result_propagate_error (simple, error)) return NULL; return g_simple_async_result_get_op_res_gpointer (simple); } /* ************************************************************************* */ static void test_resolver_init (TestResolver *tr) { } static void test_resolver_class_init (TestResolverClass *klass) { GResolverClass *resolver_class = G_RESOLVER_CLASS (klass); resolver_class->lookup_by_name_async = lookup_by_name_async; resolver_class->lookup_by_name_finish = lookup_by_name_finish; resolver_class->lookup_service_async = lookup_service_async; resolver_class->lookup_service_finish = lookup_service_finish; resolver_class->lookup_by_name = lookup_by_name; } void test_resolver_reset (TestResolver *tr) { GList *fake = NULL; for (fake = tr->fake_A; fake != NULL; fake = g_list_next (fake)) { fake_host *entry = fake->data; g_free (entry->key); g_free (entry->addr); g_free (entry); } g_list_free (tr->fake_A); tr->fake_A = NULL; for (fake = tr->fake_SRV; fake != NULL; fake = g_list_next (fake)) { fake_serv *entry = fake->data; g_free (entry->key); g_srv_target_free (entry->srv); g_free (entry); } g_list_free (tr->fake_SRV); tr->fake_SRV = NULL; } gboolean test_resolver_add_A (TestResolver *tr, const char *hostname, const char *addr) { fake_host *entry = g_new0( fake_host, 1 ); entry->key = g_strdup (hostname); entry->addr = g_strdup (addr); tr->fake_A = g_list_append (tr->fake_A, entry); return TRUE; } gboolean test_resolver_add_SRV (TestResolver *tr, const char *service, const char *protocol, const char *domain, const char *addr, guint16 port) { char *key = _service_rrname (service, protocol, domain); fake_serv *entry = g_new0 (fake_serv, 1); GSrvTarget *serv = g_srv_target_new (addr, port, 0, 0); entry->key = key; entry->srv = serv; tr->fake_SRV = g_list_append (tr->fake_SRV, entry); return TRUE; } telepathy-gabble-0.18.2/tests/twisted/main-debug.c0000644000175000017500000000740612200204333022012 0ustar00smcvsmcv00000000000000/* * main.c - entry point for telepathy-gabble-debug used by tests * Copyright (C) 2008 Collabora Ltd. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 "config.h" #include #include #ifdef G_OS_UNIX #include #include #endif #include "gabble.h" #include "connection.h" #include "vcard-manager.h" #ifdef ENABLE_JINGLE_FILE_TRANSFER #include "gtalk-file-collection.h" #endif #include "test-resolver.h" #include #ifdef G_OS_UNIX static gboolean connection_established_cb (GSignalInvocationHint *ihint, guint n_param_values, const GValue *param_values, gpointer user_data) { GSocketConnection *conn; GSocket *sock; gint flag, ret, fd; conn = g_value_get_object (param_values + 1); sock = g_socket_connection_get_socket (conn); flag = 1; fd = g_socket_get_fd (sock); ret = setsockopt (fd, IPPROTO_TCP, TCP_NODELAY, (const char *) &flag, sizeof (flag)); if (ret == -1) /* not the worst thing ever. */ g_print ("Couldn't setsockopt(TCP_NODELAY) on the connection; ain't so bad.\n"); return TRUE; } #endif int main (int argc, char **argv) { int ret = 1; GResolver *kludged; #ifdef G_OS_UNIX gpointer cls; #endif gabble_init (); /* needed for connect/disco-no-reply.py */ gabble_connection_set_disco_reply_timeout (3); /* needed for test-avatar-async.py */ gabble_vcard_manager_set_suspend_reply_timeout (3); gabble_vcard_manager_set_default_request_timeout (3); /* hook up the fake DNS resolver that lets us divert A and SRV queries * * into our local cache before asking the real DNS */ kludged = g_object_new (TEST_TYPE_RESOLVER, NULL); g_resolver_set_default (kludged); g_object_unref (kludged); test_resolver_add_A (TEST_RESOLVER (kludged), "resolves-to-5.4.3.2", "5.4.3.2"); test_resolver_add_A (TEST_RESOLVER (kludged), "resolves-to-1.2.3.4", "1.2.3.4"); test_resolver_add_A (TEST_RESOLVER (kludged), "localhost", "127.0.0.1"); test_resolver_add_A (TEST_RESOLVER (kludged), "stun.telepathy.im", "6.7.8.9"); test_resolver_add_SRV (TEST_RESOLVER (kludged), "stun", "udp", "stunning.localhost", "resolves-to-5.4.3.2", 1); #ifdef ENABLE_VOIP wocky_jingle_info_set_test_mode (); #endif #ifdef ENABLE_JINGLE_FILE_TRANSFER gtalk_file_collection_set_test_mode (); #endif #ifdef G_OS_UNIX /* We want to set TCP_NODELAY on the socket as soon as possible in * the connector so let's use ::connection-established. We need to * ref the class type as it's not loaded yet. */ cls = g_type_class_ref (WOCKY_TYPE_CONNECTOR); if (g_getenv ("GABBLE_NODELAY") != NULL) { g_signal_add_emission_hook ( g_signal_lookup ("connection-established", WOCKY_TYPE_CONNECTOR), 0, connection_established_cb, NULL, NULL); } #endif ret = gabble_main (argc, argv); /* Hack, remove the ref g_resolver has on this object, atm there is no way to * unset a custom resolver */ g_object_unref (kludged); #ifdef G_OS_UNIX g_type_class_unref (cls); #endif dbus_shutdown (); return ret; } telepathy-gabble-0.18.2/tests/twisted/tls-key.pem0000644000175000017500000000321312200204333021721 0ustar00smcvsmcv00000000000000-----BEGIN RSA PRIVATE KEY----- MIIEowIBAAKCAQEAwt9AF1py3GpF66t6xyH8vtLu8zQJnUWMPPDCAO8KWWeC8TBz KruGJ58srKETSPcG5Qj5CdvV8gZoXy3PRpp55eqYc39OJ/cfd2fLfSR558Kqic3b cX7h7AFmv/th3t7jcVjC/nVMvzgeYmsz3gsCrasJoee6CxSx5Bfg4Ya8HlJKEAw5 yf9cU9Gx5nbJUPapzCMYYw1KGfU5AJPvD6bKB2z3JX6+srZwoxnyzqAPb1noT6JE w70D0Jl3OdryBTafAN4dOY3qdwwWM5BvliXCUTYcsQtHcxasqxSVtlzLQrNKdXD2 h4F1TwnuInFy2OPHoVWhbwUdNit41UO1pHrc8QIDAQABAoIBAC0rgHipT4yF2bU5 51i3KRW2YQrgmgXpdAtAJ0f+IKD+nFx5xYg3NW6Dt+A/6e90yxVV0hwV5+6Uy6ac QLp13iGMElBbRut+nb2YwpM8XEF7XvpYTDBvn8CLxpxjkZkOgxvn3jMLT4HXaTuY 68nhNXq59Z6gzv/4iQ989XRxPbOtKWI1m96ZTFstkG8TJjaO9AnDxR2sEGHQ+rhP IRMbHLagY1qA0OxMhHCHMTwq/oVBR7xtgraLzeHnSg+Q4Z+hY+TSNmlmJxZINHTK c4kxVhAv2iGJaLDC7XviyKs/AMmoVfcSuG+BGDDOxhpOjx/G4ZzKmp2VVwNOEYk1 E2z7JH0CgYEAy+zMzpdgNIYIcKRAYqQITInsNnfGY+y2tOhUeNA558ZVoqt7Gf1Z TWegqyxtP71ZYkLqidNlJasgX2cU6aAkAHfO1EnpZiaX6WKSXGIfukLhH9Jwc6MW Xnnv0dZ9i83IL5sLIkAVb0TqqIIBvbxZjEYFSQVwpfG8BCvxNwQJ2U8CgYEA9KKh /DkoULrC8SIQ1TroOFcqLZPUFcCPHEfxHC+urWkYpRCrQ43jiZMCegPnKuUEQ9Nu 7UZqOrP0WYyEqQ3P+jC4I4xBozJigGttMTBnC2rm40PCPZcUz28Ja/Tos4oiEN6q qVG5/IlfcjuZZGsFIKbTCxM2uXwykmJDeAC91b8CgYAvbIWAsfF8pYMG9xvGFNGd QyH81MP9bwpabgFfC0W8IgK+TtTVCXcgKi5SQIWzogxMbrVukgvew7pGlYlmf4h/ 11zxP7MYv3bqnrLc6zDnty/1n5HpQo8sL31XNmOCBLw+Xfcr4u1ZMBTGVV2kS04j 8hC+l5ZH8TzBV5rEKZtEvwKBgQCtltmqyEQ7RMsfoDShmfM+R1u+i69q4ACs6L/G aG9izbiXKITeoshazt5rBmn6nhewqU+FPvoSPa+d+4AHFa4Gspt3XgcVbqNGzPPm e5ojF/BOQ76JRbOWngvpdxfIjrQtlFM1YrC+6hu4S2JFR0uUJ8yJh1DFvcOE7AVE GgKasQKBgET77FdaEiiajroe0CE8Ulmge4mpok7QQ81ag6gV6X3R7Ru20pg9iqGo U9OS2VgpqhoWz9PSoI/ZZghRYq0oQz9XDNLtZeGswFJgl4A/TrVs6gWcRxbVfAtg 9EmaJucDIxXNHqoD2yXj7XJPqDkNuD7rV7IOnrsg4La1N7SQpQk2 -----END RSA PRIVATE KEY----- telepathy-gabble-0.18.2/tests/twisted/tls-cert.pem0000644000175000017500000000271412200204333022073 0ustar00smcvsmcv00000000000000-----BEGIN CERTIFICATE----- MIIEHDCCAwagAwIBAgIBAjALBgkqhkiG9w0BAQUwbDELMAkGA1UEBhMCVUsxEjAQ BgNVBAoTCUNvbGxhYm9yYTEZMBcGA1UECxMQV29ja3kgVGVzdCBTdWl0ZTERMA8G A1UECBMIQ29uZnVzZWQxGzAZBgNVBAMTEldvY2t5IFhNUFAgTGlicmFyeTAeFw0w OTA5MTgxMjU0MDdaFw0zNzA5MTExMjU0MDdaMGkxCzAJBgNVBAYTAlVLMRIwEAYD VQQKEwlDb2xsYWJvcmExGTAXBgNVBAsTEFdvY2t5IFRlc3QgU3VpdGUxDjAMBgNV BAgTBURhemVkMRswGQYDVQQDExJXb2NreSBYTVBQIExpYnJhcnkwggEfMAsGCSqG SIb3DQEBAQOCAQ4AMIIBCQKCAQDC30AXWnLcakXrq3rHIfy+0u7zNAmdRYw88MIA 7wpZZ4LxMHMqu4YnnyysoRNI9wblCPkJ29XyBmhfLc9Gmnnl6phzf04n9x93Z8t9 JHnnwqqJzdtxfuHsAWa/+2He3uNxWML+dUy/OB5iazPeCwKtqwmh57oLFLHkF+Dh hrweUkoQDDnJ/1xT0bHmdslQ9qnMIxhjDUoZ9TkAk+8PpsoHbPclfr6ytnCjGfLO oA9vWehPokTDvQPQmXc52vIFNp8A3h05jep3DBYzkG+WJcJRNhyxC0dzFqyrFJW2 XMtCs0p1cPaHgXVPCe4icXLY48ehVaFvBR02K3jVQ7WketzxAgMBAAGjgdIwgc8w DAYDVR0TAQH/BAIwADAdBgNVHSUEFjAUBggrBgEFBQcDAgYIKwYBBQUHAwEwIQYD VR0RBBowGIIQd2Vhc2VsLWp1aWNlLm9yZ4cEfwAAATAPBgNVHQ8BAf8EBQMDByAA MB0GA1UdDgQWBBRDAfw/7QRZO5a0qmJ75Oeo3hA01zAfBgNVHSMEGDAWgBRJMCYI jJrWac2LwMwOXAKOSh+mjDAsBgNVHR8EJTAjMCGgH6AdhhtmaWxlOi8vL3RtcC93 b2NreS10ZXN0cy9jcmwwCwYJKoZIhvcNAQEFA4IBAQAIcwQ8FN7lnnQPm4al6y5v zrGzVSxkUuN+I8457E9ZAoFpItMGqWWKjjbOgjS3d95yJWmEW2eBVC3/LMEAvv4z Q6HkTRhafkiLWmXNa8DtbUq1cZ2hNrR1lNTOL4zXwg9JQbtFw0EAM7LfSgqHhTzs xO0AbXaO0TlbYkn9/amPCNQcFjg6Dgdm3x0T3g/tLQjtzjro/hdgYZqPng0MYBpG AUj99FwahI5D8cAPoUjtpxZOlsexz4r8UVGNRvL0Wqg57w8KKF7GVr15b2ZAeQwo pNJkMSXAyPpKW24Q06zwYFnC+Cp32udf2wIB9FEC3zNQugUbtFitHzPjzhum13iR -----END CERTIFICATE----- telepathy-gabble-0.18.2/tests/twisted/test-helper.py0000644000175000017500000000421012200204333022432 0ustar00smcvsmcv00000000000000 """ Helper script for writing tests. How it works: - Each event that occurs generates a Python function that expects exactly that event. - Each time an event occurs, you're dropped into a Python debugger. The commands you type will be inserted into the function that expects that event. - Executing "!return False" in the debugger will stop the script, as will SIGINT (Control-C). Once your'e done, the test code is written to a file called test.dump. """ import pdb import pprint from twisted.internet import glib2reactor glib2reactor.install() from gabbletest import EventTest, run class LoggingPdb(pdb.Pdb): def __init__(self): pdb.Pdb.__init__(self) self.log = [] def default(self, line): self.log.append(line) return pdb.Pdb.default(self, line) def expect(event, data): return True class TestShell: def __init__(self): self.events = [] def expect(self, event, data): print 'event:' for item in event: print '-', pprint.pformat(item) db = LoggingPdb() ret = db.runcall(expect, event, data) print (ret, db.log) self.events.append((event, db.log, ret)) return False def make_expect_func(self, suffix, event, log, ret): lines = [] lines.append('def expect_%s(event, data):\n' % suffix) lines.extend([ ' if event[%d] != %r:\n return False\n\n' % (i, item) for i, item in enumerate(event)]) lines.extend([' %s\n' % line for line in log]) lines.append(' return %r\n' % ret) return ''.join(lines) def make_script(self): return '\n'.join([ self.make_expect_func(i, event, log, ret) for i, (event, log, ret) in enumerate(self.events)]) class NoTimeoutEventTest(EventTest): def timeout_cb(self): pass if __name__ == '__main__': test = NoTimeoutEventTest() shell = TestShell() test.expect(shell.expect) try: run(test) except KeyboardInterrupt: pass print 'writing test to test.dump' fh = file('test.dump', 'w') fh.write(shell.make_script()) telepathy-gabble-0.18.2/tests/twisted/rostertest.py0000644000175000017500000001334712227000321022427 0ustar00smcvsmcv00000000000000from twisted.words.protocols.jabber.client import IQ from gabbletest import (wrap_channel,) from servicetest import (assertEquals, assertLength, EventPattern, assertContains) import constants as cs import ns def make_roster_push(stream, jid, subscription, ask_subscribe=False, name=None): iq = IQ(stream, "set") iq['id'] = 'push' query = iq.addElement('query') query['xmlns'] = ns.ROSTER item = query.addElement('item') item['jid'] = jid item['subscription'] = subscription if name is not None: item['name'] = name if ask_subscribe: item['ask'] = 'subscribe' return iq def send_roster_push(stream, jid, subscription, ask_subscribe=False, name=None): iq = make_roster_push(stream, jid, subscription, ask_subscribe=ask_subscribe, name=name) stream.send(iq) def get_contact_list_event_patterns(q, bus, conn, expected_handle_type, name): expected_handle = conn.RequestHandles(expected_handle_type, [name])[0] def new_channel_predicate(e): path, type, handle_type, handle, suppress_handler = e.args if type != cs.CHANNEL_TYPE_CONTACT_LIST: return False if handle_type != expected_handle_type: return False if handle != expected_handle: return False return True new_channel_repr = ('NewChannel(., ContactList, %u, "%s", .)' % (expected_handle_type, name)) new_channel_predicate.__repr__ = lambda: new_channel_repr def new_channels_predicate(e): info, = e.args if len(info) != 1: return False path, props = info[0] if props.get(cs.CHANNEL_TYPE) != cs.CHANNEL_TYPE_CONTACT_LIST: return False if props.get(cs.TARGET_HANDLE_TYPE) != expected_handle_type: return False if props.get(cs.TARGET_HANDLE) != expected_handle: return False return True new_channels_repr = ('NewChannels(... ct=ContactList, ht=%u, name="%s"... )' % (expected_handle_type, name)) new_channels_predicate.__repr__ = lambda: new_channels_repr return ( EventPattern('dbus-signal', signal='NewChannel', predicate=new_channel_predicate), EventPattern('dbus-signal', signal='NewChannels', predicate=new_channels_predicate) ) def expect_contact_list_signals(q, bus, conn, lists, groups=[], expect_more=None): assert lists or groups if expect_more is None: eps = [] else: eps = expect_more[:] for name in lists: eps.extend(get_contact_list_event_patterns(q, bus, conn, cs.HT_LIST, name)) for name in groups: eps.extend(get_contact_list_event_patterns(q, bus, conn, cs.HT_GROUP, name)) events = q.expect_many(*eps) ret = [] more = [] if expect_more is not None: for ep in expect_more: more.append(events.pop(0)) for name in lists: old_signal = events.pop(0) new_signal = events.pop(0) ret.append((old_signal, new_signal)) for name in groups: old_signal = events.pop(0) new_signal = events.pop(0) ret.append((old_signal, new_signal)) assert len(events) == 0 if expect_more is not None: return ret, more return ret def check_contact_list_signals(q, bus, conn, signals, ht, name, contacts, lp_contacts=[], rp_contacts=[]): """ Looks at NewChannel and NewChannels signals for the contact list with ID 'name' and checks that its members, lp members and rp members are exactly 'contacts', 'lp_contacts' and 'rp_contacts'. Returns a proxy for the channel. """ old_signal, new_signal = signals path, type, handle_type, handle, suppress_handler = old_signal.args assertEquals(cs.CHANNEL_TYPE_CONTACT_LIST, type) assertEquals(name, conn.InspectHandles(handle_type, [handle])[0]) chan = wrap_channel(bus.get_object(conn.bus_name, path), cs.CHANNEL_TYPE_CONTACT_LIST) members = chan.Group.GetMembers() assertEquals(sorted(contacts), sorted(conn.InspectHandles(cs.HT_CONTACT, members))) lp_handles = conn.RequestHandles(cs.HT_CONTACT, lp_contacts) rp_handles = conn.RequestHandles(cs.HT_CONTACT, rp_contacts) # NB. comma: we're unpacking args. Thython! info, = new_signal.args assertLength(1, info) # one channel path_, emitted_props = info[0] assertEquals(path_, path) assertEquals(cs.CHANNEL_TYPE_CONTACT_LIST, emitted_props[cs.CHANNEL_TYPE]) assertEquals(ht, emitted_props[cs.TARGET_HANDLE_TYPE]) assertEquals(handle, emitted_props[cs.TARGET_HANDLE]) channel_props = chan.Properties.GetAll(cs.CHANNEL) assertEquals(handle, channel_props.get('TargetHandle')) assertEquals(ht, channel_props.get('TargetHandleType')) assertEquals(cs.CHANNEL_TYPE_CONTACT_LIST, channel_props.get('ChannelType')) assertContains(cs.CHANNEL_IFACE_GROUP, channel_props.get('Interfaces')) assertEquals(name, channel_props['TargetID']) assertEquals(False, channel_props['Requested']) assertEquals('', channel_props['InitiatorID']) assertEquals(0, channel_props['InitiatorHandle']) group_props = chan.Properties.GetAll(cs.CHANNEL_IFACE_GROUP) assertContains('HandleOwners', group_props) assertContains('Members', group_props) assertEquals(members, group_props['Members']) assertContains('LocalPendingMembers', group_props) actual_lp_handles = [x[0] for x in group_props['LocalPendingMembers']] assertEquals(sorted(lp_handles), sorted(actual_lp_handles)) assertContains('RemotePendingMembers', group_props) assertEquals(sorted(rp_handles), sorted(group_props['RemotePendingMembers'])) assertContains('GroupFlags', group_props) return chan telepathy-gabble-0.18.2/tests/twisted/ns.py0000644000175000017500000000774412200204333020635 0ustar00smcvsmcv00000000000000AMP = "http://jabber.org/protocol/amp" BYTESTREAMS = 'http://jabber.org/protocol/bytestreams' CHAT_STATES = 'http://jabber.org/protocol/chatstates' CAPS = "http://jabber.org/protocol/caps" CLIENT = "jabber:client" DISCO_INFO = "http://jabber.org/protocol/disco#info" DISCO_ITEMS = "http://jabber.org/protocol/disco#items" FEATURE_NEG = 'http://jabber.org/protocol/feature-neg' FILE_TRANSFER = 'http://jabber.org/protocol/si/profile/file-transfer' GEOLOC = 'http://jabber.org/protocol/geoloc' GOOGLE_FEAT_SESSION = 'http://www.google.com/xmpp/protocol/session' GOOGLE_FEAT_SHARE = 'http://google.com/xmpp/protocol/share/v1' GOOGLE_FEAT_VOICE = 'http://www.google.com/xmpp/protocol/voice/v1' GOOGLE_FEAT_VIDEO = 'http://www.google.com/xmpp/protocol/video/v1' GOOGLE_JINGLE_INFO = 'google:jingleinfo' GOOGLE_P2P = "http://www.google.com/transport/p2p" GOOGLE_QUEUE = 'google:queue' GOOGLE_ROSTER = 'google:roster' GOOGLE_SESSION = "http://www.google.com/session" GOOGLE_SESSION_SHARE = "http://www.google.com/session/share" GOOGLE_SESSION_PHONE = "http://www.google.com/session/phone" GOOGLE_SESSION_VIDEO = "http://www.google.com/session/video" GOOGLE_MAIL_NOTIFY = "google:mail:notify" IBB = 'http://jabber.org/protocol/ibb' JINGLE_015 = "http://jabber.org/protocol/jingle" JINGLE_015_AUDIO = "http://jabber.org/protocol/jingle/description/audio" JINGLE_015_VIDEO = "http://jabber.org/protocol/jingle/description/video" JINGLE = "urn:xmpp:jingle:1" JINGLE_ERRORS = "urn:xmpp:jingle:errors:1" JINGLE_RTP = "urn:xmpp:jingle:apps:rtp:1" JINGLE_RTP_AUDIO = "urn:xmpp:jingle:apps:rtp:audio" JINGLE_RTP_VIDEO = "urn:xmpp:jingle:apps:rtp:video" JINGLE_RTP_ERRORS = "urn:xmpp:jingle:apps:rtp:errors:1" JINGLE_RTP_INFO_1 = "urn:xmpp:jingle:apps:rtp:info:1" JINGLE_TRANSPORT_ICEUDP = "urn:xmpp:jingle:transports:ice-udp:1" JINGLE_TRANSPORT_RAWUDP = "urn:xmpp:jingle:transports:raw-udp:1" LAST = "jabber:iq:last" MUC = 'http://jabber.org/protocol/muc' MUC_BYTESTREAM = 'http://telepathy.freedesktop.org/xmpp/protocol/muc-bytestream' MUC_OWNER = '%s#owner' % MUC MUC_ROOMINFO = '%s#roominfo' % MUC MUC_USER = '%s#user' % MUC NICK = "http://jabber.org/protocol/nick" NS_XMPP_SASL = 'urn:ietf:params:xml:ns:xmpp-sasl' NS_XMPP_BIND = 'urn:ietf:params:xml:ns:xmpp-bind' NS_XMPP_TLS = 'urn:ietf:params:xml:ns:xmpp-tls' NS_XMPP_SESSION = 'urn:ietf:params:xml:ns:xmpp-session' OLPC_ACTIVITIES = "http://laptop.org/xmpp/activities" OLPC_ACTIVITIES_NOTIFY = "%s+notify" % OLPC_ACTIVITIES OLPC_ACTIVITY = "http://laptop.org/xmpp/activity" OLPC_ACTIVITY_PROPS = "http://laptop.org/xmpp/activity-properties" OLPC_ACTIVITY_PROPS_NOTIFY = "%s+notify" % OLPC_ACTIVITY_PROPS OLPC_BUDDY = "http://laptop.org/xmpp/buddy" OLPC_BUDDY_PROPS = "http://laptop.org/xmpp/buddy-properties" OLPC_BUDDY_PROPS_NOTIFY = "%s+notify" % OLPC_BUDDY_PROPS OLPC_CURRENT_ACTIVITY = "http://laptop.org/xmpp/current-activity" OLPC_CURRENT_ACTIVITY_NOTIFY = "%s+notify" % OLPC_CURRENT_ACTIVITY PUBSUB = "http://jabber.org/protocol/pubsub" PUBSUB_EVENT = "%s#event" % PUBSUB RECEIPTS = "urn:xmpp:receipts" REGISTER = "jabber:iq:register" ROSTER = "jabber:iq:roster" SEARCH = 'jabber:iq:search' SI = 'http://jabber.org/protocol/si' SI_MULTIPLE = 'http://telepathy.freedesktop.org/xmpp/si-multiple' STANZA = "urn:ietf:params:xml:ns:xmpp-stanzas" STREAMS = "urn:ietf:params:xml:ns:xmpp-streams" TEMPPRES = "urn:xmpp:temppres:0" TUBES = 'http://telepathy.freedesktop.org/xmpp/tubes' MUJI = 'http://telepathy.freedesktop.org/xmpp/muji' VCARD_TEMP = 'vcard-temp' VCARD_TEMP_UPDATE = 'vcard-temp:x:update' X_DATA = 'jabber:x:data' X_DELAY = 'jabber:x:delay' XML = 'http://www.w3.org/XML/1998/namespace' X_OOB = 'jabber:x:oob' GABBLE_CAPS="http://telepathy.freedesktop.org/caps" PRESENCE_INVISIBLE = 'presence-invisible' PRIVACY = 'jabber:iq:privacy' INVISIBLE = 'urn:xmpp:invisible:0' GOOGLE_SHARED_STATUS = 'google:shared-status' VERSION = 'jabber:iq:version' TP_FT_METADATA_SERVICE = 'http://telepathy.freedesktop.org/xmpp/file-transfer-service' TP_FT_METADATA = 'http://telepathy.freedesktop.org/xmpp/file-transfer-metadata' telepathy-gabble-0.18.2/tests/twisted/mucutil.py0000644000175000017500000000671612227000321021675 0ustar00smcvsmcv00000000000000# vim: set fileencoding=utf-8 : Python sucks! """ Utility functions for tests that need to interact with MUCs. """ import dbus from twisted.words.xish import domish, xpath from servicetest import call_async, wrap_channel, EventPattern, assertLength from gabbletest import make_muc_presence, request_muc_handle import constants as cs import ns def echo_muc_presence (q, stream, stanza, affiliation, role): x = stanza.addElement((ns.MUC_USER, 'x')) stanza['from'] = stanza['to'] del stanza['to'] item = x.addElement('item') item['affiliation'] = affiliation item['role'] = role stream.send (stanza) def try_to_join_muc(q, bus, conn, stream, muc, request=None): """ Ask Gabble to join a MUC, and expect it to send Returns: the stream-presence Event object. """ if request is None: request = { cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_TEXT, cs.TARGET_HANDLE_TYPE: cs.HT_ROOM, cs.TARGET_ID: muc, } call_async(q, conn.Requests, 'CreateChannel', dbus.Dictionary(request, signature='sv')) join_event = q.expect('stream-presence', to='%s/test' % muc) # XEP-0045 §7.1.2 sez: # “MUC clients SHOULD signal their ability to speak the MUC protocol by # including in the initial presence stanza an empty element # qualified by the 'http://jabber.org/protocol/muc' namespace.” x_muc_nodes = xpath.queryForNodes('/presence/x[@xmlns="%s"]' % ns.MUC, join_event.stanza) assertLength(1, x_muc_nodes) return join_event def join_muc(q, bus, conn, stream, muc, request=None, also_capture=[], role='participant', affiliation='none'): """ Joins 'muc', returning the muc's handle, a proxy object for the channel, its path and its immutable properties just after the CreateChannel event has fired. The room contains one other member. """ muc_handle = request_muc_handle(q, conn, stream, muc) try_to_join_muc(q, bus, conn, stream, muc, request=request) # Send presence for other member of room. stream.send(make_muc_presence('owner', 'moderator', muc, 'bob')) # Send presence for own membership of room. stream.send(make_muc_presence(affiliation, role, muc, 'test')) captured = q.expect_many( EventPattern('dbus-return', method='CreateChannel'), *also_capture) path, props = captured[0].value chan = wrap_channel(bus.get_object(conn.bus_name, path), 'Text', ['Messages', 'Subject.DRAFT', 'RoomConfig1', 'ChatState']) return (muc_handle, chan, path, props) + tuple(captured[1:]) def join_muc_and_check(q, bus, conn, stream, muc, request=None): """ Like join_muc(), but also checks the NewChannels and NewChannel signals and the Members property, and returns both members' handles. """ muc_handle, chan, path, props = \ join_muc(q, bus, conn, stream, muc, request=request) q.expect('dbus-signal', signal='NewChannels', args=[[(path, props)]]) q.expect('dbus-signal', signal='NewChannel', args=[path, cs.CHANNEL_TYPE_TEXT, cs.HT_ROOM, muc_handle, True]) test_handle, bob_handle = conn.RequestHandles(cs.HT_CONTACT, ['%s/test' % muc, '%s/bob' % muc]) members = chan.Get(cs.CHANNEL_IFACE_GROUP, 'Members', dbus_interface=cs.PROPERTIES_IFACE) assert set(members) == set([test_handle, bob_handle]), \ (members, (test_handle, bob_handle)) return (muc_handle, chan, test_handle, bob_handle) telepathy-gabble-0.18.2/tests/twisted/httptest.py0000644000175000017500000000152612200204333022064 0ustar00smcvsmcv00000000000000 from twisted.web import http from twisted.internet import reactor from servicetest import Event class Request(http.Request): def process(self): self.queue.append(Event('http-request', method=self.method, path=self.path, request=self)) class HTTPChannel(http.HTTPChannel): def requestFactory(self, *misc): request = Request(*misc) request.queue = self.queue return request class HTTPFactory(http.HTTPFactory): protocol = HTTPChannel def __init__(self, queue): http.HTTPFactory.__init__(self) self.queue = queue def buildProtocol(self, addr): protocol = http.HTTPFactory.buildProtocol(self, addr) protocol.queue = self.queue return protocol def listen_http(q, port=0): return reactor.listenTCP(port, HTTPFactory(q), interface='localhost') telepathy-gabble-0.18.2/tests/twisted/gabbletest.py0000644000175000017500000006752112227000321022330 0ustar00smcvsmcv00000000000000 """ Infrastructure code for testing Gabble by pretending to be a Jabber server. """ import base64 import os import hashlib import sys import random import re import traceback import ns import constants as cs import servicetest from servicetest import ( assertEquals, assertLength, assertContains, wrap_channel, EventPattern, call_async, unwrap, Event) import twisted from twisted.words.xish import domish, xpath from twisted.words.protocols.jabber.client import IQ from twisted.words.protocols.jabber import xmlstream from twisted.internet import reactor, ssl import dbus def make_result_iq(stream, iq, add_query_node=True): result = IQ(stream, "result") result["id"] = iq["id"] to = iq.getAttribute('to') if to is not None: result["from"] = to query = iq.firstChildElement() if query and add_query_node: result.addElement((query.uri, query.name)) return result def acknowledge_iq(stream, iq): stream.send(make_result_iq(stream, iq)) def send_error_reply(stream, iq, error_stanza=None): result = IQ(stream, "error") result["id"] = iq["id"] query = iq.firstChildElement() to = iq.getAttribute('to') if to is not None: result["from"] = to if query: result.addElement((query.uri, query.name)) if error_stanza: result.addChild(error_stanza) stream.send(result) def request_muc_handle(q, conn, stream, muc_jid): servicetest.call_async(q, conn, 'RequestHandles', 2, [muc_jid]) event = q.expect('dbus-return', method='RequestHandles') return event.value[0][0] def make_muc_presence(affiliation, role, muc_jid, alias, jid=None, photo=None): presence = domish.Element((None, 'presence')) presence['from'] = '%s/%s' % (muc_jid, alias) x = presence.addElement((ns.MUC_USER, 'x')) item = x.addElement('item') item['affiliation'] = affiliation item['role'] = role if jid is not None: item['jid'] = jid if photo is not None: presence.addChild( elem(ns.VCARD_TEMP_UPDATE, 'x')( elem('photo')(unicode(photo)) )) return presence def sync_stream(q, stream): """Used to ensure that Gabble has processed all stanzas sent to it.""" iq = IQ(stream, "get") id = iq['id'] iq.addElement(('http://jabber.org/protocol/disco#info', 'query')) stream.send(iq) q.expect('stream-iq', query_ns='http://jabber.org/protocol/disco#info', predicate=(lambda event: event.stanza['id'] == id and event.iq_type == 'result')) class GabbleAuthenticator(xmlstream.Authenticator): def __init__(self, username, password, resource=None): self.username = username self.password = password self.resource = resource self.bare_jid = None self.full_jid = None self._event_func = lambda e: None xmlstream.Authenticator.__init__(self) def set_event_func(self, event_func): self._event_func = event_func class JabberAuthenticator(GabbleAuthenticator): "Trivial XML stream authenticator that accepts one username/digest pair." # Patch in fix from http://twistedmatrix.com/trac/changeset/23418. # This monkeypatch taken from Gadget source code from twisted.words.xish.utility import EventDispatcher def _addObserver(self, onetime, event, observerfn, priority, *args, **kwargs): if self._dispatchDepth > 0: self._updateQueue.append(lambda: self._addObserver(onetime, event, observerfn, priority, *args, **kwargs)) return self._oldAddObserver(onetime, event, observerfn, priority, *args, **kwargs) EventDispatcher._oldAddObserver = EventDispatcher._addObserver EventDispatcher._addObserver = _addObserver def __init__(self, username, password, resource=None, emit_events=False): GabbleAuthenticator.__init__(self, username, password, resource) self.emit_events = emit_events def streamStarted(self, root=None): if root: self.xmlstream.sid = '%x' % random.randint(1, sys.maxint) self.xmlstream.domain = root.getAttribute('to') self.xmlstream.sendHeader() self.xmlstream.addOnetimeObserver( "/iq/query[@xmlns='jabber:iq:auth']", self.initialIq) def initialIq(self, iq): if self.emit_events: self._event_func(Event('auth-initial-iq', authenticator=self, iq=iq, id=iq["id"])) else: self.respondToInitialIq(iq) self.xmlstream.addOnetimeObserver('/iq/query/username', self.secondIq) def respondToInitialIq(self, iq): result = IQ(self.xmlstream, "result") result["id"] = iq["id"] query = result.addElement('query') query["xmlns"] = "jabber:iq:auth" query.addElement('username', content='test') query.addElement('password') query.addElement('digest') query.addElement('resource') self.xmlstream.send(result) def secondIq(self, iq): if self.emit_events: self._event_func(Event('auth-second-iq', authenticator=self, iq=iq, id=iq["id"])) else: self.respondToSecondIq(iq) def respondToSecondIq(self, iq): username = xpath.queryForNodes('/iq/query/username', iq) assert map(str, username) == [self.username] digest = xpath.queryForNodes('/iq/query/digest', iq) expect = hashlib.sha1(self.xmlstream.sid + self.password).hexdigest() assert map(str, digest) == [expect] resource = xpath.queryForNodes('/iq/query/resource', iq) assertLength(1, resource) if self.resource is not None: assertEquals(self.resource, str(resource[0])) self.bare_jid = '%s@%s' % (self.username, self.xmlstream.domain) self.full_jid = '%s/%s' % (self.bare_jid, resource) result = IQ(self.xmlstream, "result") result["id"] = iq["id"] self.xmlstream.send(result) self.xmlstream.dispatch(self.xmlstream, xmlstream.STREAM_AUTHD_EVENT) class XmppAuthenticator(GabbleAuthenticator): def __init__(self, username, password, resource=None): GabbleAuthenticator.__init__(self, username, password, resource) self.authenticated = False self._mechanisms = ['PLAIN'] def streamInitialize(self, root): if root: self.xmlstream.sid = root.getAttribute('id') self.xmlstream.domain = root.getAttribute('to') if self.xmlstream.sid is None: self.xmlstream.sid = '%x' % random.randint(1, sys.maxint) self.xmlstream.sendHeader() def streamIQ(self): features = elem(xmlstream.NS_STREAMS, 'features')( elem(ns.NS_XMPP_BIND, 'bind'), elem(ns.NS_XMPP_SESSION, 'session'), ) self.xmlstream.send(features) self.xmlstream.addOnetimeObserver( "/iq/bind[@xmlns='%s']" % ns.NS_XMPP_BIND, self.bindIq) self.xmlstream.addOnetimeObserver( "/iq/session[@xmlns='%s']" % ns.NS_XMPP_SESSION, self.sessionIq) def streamSASL(self): features = domish.Element((xmlstream.NS_STREAMS, 'features')) mechanisms = features.addElement((ns.NS_XMPP_SASL, 'mechanisms')) for mechanism in self._mechanisms: mechanisms.addElement('mechanism', content=mechanism) self.xmlstream.send(features) self.xmlstream.addOnetimeObserver("/auth", self.auth) def streamStarted(self, root=None): self.streamInitialize(root) if self.authenticated: # Initiator authenticated itself, and has started a new stream. self.streamIQ() else: self.streamSASL() def auth(self, auth): assert (base64.b64decode(str(auth)) == '\x00%s\x00%s' % (self.username, self.password)) success = domish.Element((ns.NS_XMPP_SASL, 'success')) self.xmlstream.send(success) self.xmlstream.reset() self.authenticated = True def bindIq(self, iq): resource = xpath.queryForString('/iq/bind/resource', iq) if self.resource is not None: assertEquals(self.resource, resource) else: assert resource is not None result = IQ(self.xmlstream, "result") result["id"] = iq["id"] bind = result.addElement((ns.NS_XMPP_BIND, 'bind')) self.bare_jid = '%s@%s' % (self.username, self.xmlstream.domain) self.full_jid = '%s/%s' % (self.bare_jid, resource) jid = bind.addElement('jid', content=self.full_jid) self.xmlstream.send(result) self.xmlstream.dispatch(self.xmlstream, xmlstream.STREAM_AUTHD_EVENT) def sessionIq(self, iq): self.xmlstream.send(make_result_iq(self.xmlstream, iq)) class StreamEvent(servicetest.Event): def __init__(self, type_, stanza, stream): servicetest.Event.__init__(self, type_, stanza=stanza) self.stream = stream self.to = stanza.getAttribute("to") class IQEvent(StreamEvent): def __init__(self, stream, iq): StreamEvent.__init__(self, 'stream-iq', iq, stream) self.iq_type = iq.getAttribute("type") self.iq_id = iq.getAttribute("id") query = iq.firstChildElement() if query: self.query = query self.query_ns = query.uri self.query_name = query.name if query.getAttribute("node"): self.query_node = query.getAttribute("node") else: self.query = None class PresenceEvent(StreamEvent): def __init__(self, stream, stanza): StreamEvent.__init__(self, 'stream-presence', stanza, stream) self.presence_type = stanza.getAttribute('type') statuses = xpath.queryForNodes('/presence/status', stanza) if statuses: self.presence_status = str(statuses[0]) class MessageEvent(StreamEvent): def __init__(self, stream, stanza): StreamEvent.__init__(self, 'stream-message', stanza, stream) self.message_type = stanza.getAttribute('type') class StreamFactory(twisted.internet.protocol.Factory): def __init__(self, streams, jids): self.streams = streams self.jids = jids self.presences = {} self.mappings = dict(map (lambda jid, stream: (jid, stream), jids, streams)) # Make a copy of the streams self.factory_streams = list(streams) self.factory_streams.reverse() # Do not add observers for single instances because it's unnecessary and # some unit tests need to respond to the roster request, and we shouldn't # answer it for them otherwise we break compatibility if len(streams) > 1: # We need to have a function here because lambda keeps a reference on # the stream and jid and in the for loop, there is no context def addObservers(stream, jid): stream.addObserver('/iq', lambda x: \ self.forward_iq(stream, jid, x)) stream.addObserver('/presence', lambda x: \ self.got_presence(stream, jid, x)) for (jid, stream) in self.mappings.items(): addObservers(stream, jid) def protocol(self, *args): return self.factory_streams.pop() def got_presence (self, stream, jid, stanza): stanza.attributes['from'] = jid self.presences[jid] = stanza for dest_jid in self.presences.keys(): # Dispatch the new presence to other clients stanza.attributes['to'] = dest_jid self.mappings[dest_jid].send(stanza) # Don't echo the presence twice if dest_jid != jid: # Dispatch other client's presence to this stream presence = self.presences[dest_jid] presence.attributes['to'] = jid stream.send(presence) def lost_presence(self, stream, jid): if self.presences.has_key(jid): del self.presences[jid] for dest_jid in self.presences.keys(): presence = domish.Element(('jabber:client', 'presence')) presence['from'] = jid presence['to'] = dest_jid presence['type'] = 'unavailable' self.mappings[dest_jid].send(presence) def forward_iq(self, stream, jid, stanza): stanza.attributes['from'] = jid query = stanza.firstChildElement() # Fake other accounts as being part of our roster if query and query.uri == ns.ROSTER: roster = make_result_iq(stream, stanza) query = roster.firstChildElement() for roster_jid in self.mappings.keys(): if jid != roster_jid: item = query.addElement('item') item['jid'] = roster_jid item['subscription'] = 'both' stream.send(roster) return to = stanza.getAttribute('to') dest = None if to is not None: dest = self.mappings.get(to) if dest is not None: dest.send(stanza) class BaseXmlStream(xmlstream.XmlStream): initiating = False namespace = 'jabber:client' pep_support = True disco_features = [] handle_privacy_lists = True def __init__(self, event_func, authenticator): xmlstream.XmlStream.__init__(self, authenticator) self.event_func = event_func self.addObserver('//iq', lambda x: event_func( IQEvent(self, x))) self.addObserver('//message', lambda x: event_func( MessageEvent(self, x))) self.addObserver('//presence', lambda x: event_func( PresenceEvent(self, x))) self.addObserver('//event/stream/authd', self._cb_authd) if self.handle_privacy_lists: self.addObserver("/iq/query[@xmlns='%s']" % ns.PRIVACY, self._cb_priv_list) def connectionMade(self): xmlstream.XmlStream.connectionMade(self) if 'GABBLE_NODELAY' in os.environ: self.transport.setTcpNoDelay(True) def _cb_priv_list(self, iq): send_error_reply(self, iq) def _cb_authd(self, _): # called when stream is authenticated assert self.authenticator.full_jid is not None assert self.authenticator.bare_jid is not None self.addObserver( "/iq[@to='%s']/query[@xmlns='http://jabber.org/protocol/disco#info']" % self.domain, self._cb_disco_iq) self.addObserver( "/iq[@to='%s']/query[@xmlns='http://jabber.org/protocol/disco#info']" % self.authenticator.bare_jid, self._cb_bare_jid_disco_iq) self.event_func(servicetest.Event('stream-authenticated')) def _cb_disco_iq(self, iq): nodes = xpath.queryForNodes( "/iq/query[@xmlns='http://jabber.org/protocol/disco#info']", iq) query = nodes[0] for feature in self.disco_features: query.addChild(elem('feature', var=feature)) iq['type'] = 'result' iq['from'] = iq['to'] self.send(iq) def _cb_bare_jid_disco_iq(self, iq): # advertise PEP support nodes = xpath.queryForNodes( "/iq/query[@xmlns='http://jabber.org/protocol/disco#info']", iq) query = nodes[0] identity = query.addElement('identity') identity['category'] = 'pubsub' identity['type'] = 'pep' iq['type'] = 'result' iq['from'] = iq['to'] self.send(iq) def onDocumentEnd(self): self.event_func(servicetest.Event('stream-closed')) # We don't chain up XmlStream.onDocumentEnd() because it will # disconnect the TCP connection making tests as # connect/disconnect-timeout.py not working def connectionLost(self, reason): self.event_func(servicetest.Event('stream-connection-lost')) xmlstream.XmlStream.connectionLost(self, reason) def send_stream_error(self, error='system-shutdown'): # Yes, there are meant to be two different STREAMS namespaces. go_away = \ elem(xmlstream.NS_STREAMS, 'error')( elem(ns.STREAMS, error) ) self.send(go_away) class JabberXmlStream(BaseXmlStream): version = (0, 9) class XmppXmlStream(BaseXmlStream): version = (1, 0) class GoogleXmlStream(BaseXmlStream): version = (1, 0) pep_support = False disco_features = [ns.GOOGLE_ROSTER, ns.GOOGLE_JINGLE_INFO, ns.GOOGLE_MAIL_NOTIFY, ns.GOOGLE_QUEUE, ] def _cb_bare_jid_disco_iq(self, iq): # Google talk doesn't support PEP :( iq['type'] = 'result' iq['from'] = iq['to'] self.send(iq) def make_connection(bus, event_func, params=None, suffix=''): # Gabble accepts a resource in 'account', but the value of 'resource' # overrides it if there is one. test_name = re.sub('(.*tests/twisted/|\./)', '', sys.argv[0]) account = 'test%s@localhost/%s' % (suffix, test_name) default_params = { 'account': account, 'password': 'pass', 'resource': 'Resource', 'server': 'localhost', 'port': dbus.UInt32(4242), 'fallback-socks5-proxies': dbus.Array([], signature='s'), 'require-encryption': False, } if params: default_params.update(params) # Allow omitting the 'password' param if default_params['password'] is None: del default_params['password'] # Allow omitting the 'account' param if default_params['account'] is None: del default_params['account'] jid = default_params.get('account', None) conn = servicetest.make_connection(bus, event_func, 'gabble', 'jabber', default_params) return (conn, jid) def make_stream(event_func, authenticator=None, protocol=None, resource=None, suffix=''): # set up Jabber server if authenticator is None: authenticator = XmppAuthenticator('test%s' % suffix, 'pass', resource=resource) authenticator.set_event_func(event_func) if protocol is None: protocol = XmppXmlStream stream = protocol(event_func, authenticator) return stream def disconnect_conn(q, conn, stream, expected_before=[], expected_after=[]): call_async(q, conn, 'Disconnect') tmp = expected_before + [ EventPattern('dbus-signal', signal='StatusChanged', args=[cs.CONN_STATUS_DISCONNECTED, cs.CSR_REQUESTED]), EventPattern('stream-closed')] before_events = q.expect_many(*tmp) stream.sendFooter() tmp = expected_after + [EventPattern('dbus-return', method='Disconnect')] after_events = q.expect_many(*tmp) return before_events[:-2], after_events[:-1] def element_repr(element): """__repr__ cannot safely return non-ASCII: see . So we print non-ASCII characters as \uXXXX escapes in debug output """ return element.toXml().encode('unicode-escape') def expect_connected(queue): queue.expect('dbus-signal', signal='StatusChanged', args=[cs.CONN_STATUS_CONNECTING, cs.CSR_REQUESTED]) queue.expect('stream-authenticated') queue.expect('dbus-signal', signal='PresencesChanged', args=[{1L: (cs.PRESENCE_AVAILABLE, u'available', '')}]) queue.expect('dbus-signal', signal='StatusChanged', args=[cs.CONN_STATUS_CONNECTED, cs.CSR_REQUESTED]) def exec_test_deferred(fun, params, protocol=None, timeout=None, authenticator=None, num_instances=1, do_connect=True, make_connection_func=make_connection, expect_connected_func=expect_connected): # hack to ease debugging domish.Element.__repr__ = element_repr colourer = None if sys.stdout.isatty() or 'CHECK_FORCE_COLOR' in os.environ: colourer = servicetest.install_colourer() try: bus = dbus.SessionBus() except dbus.exceptions.DBusException as e: print e os._exit(1) queue = servicetest.IteratingEventQueue(timeout) queue.verbose = ( os.environ.get('CHECK_TWISTED_VERBOSE', '') != '' or '-v' in sys.argv) conns = [] jids = [] streams = [] resource = params.get('resource') if params is not None else None for i in range(0, num_instances): if i == 0: suffix = '' else: suffix = str(i) try: (conn, jid) = make_connection_func(bus, queue.append, params, suffix) except Exception, e: # Crap. This is normally because the connection's still kicking # around on the bus. Let's bin any connections we *did* manage to # get going and then bail out unceremoniously. print e for conn in conns: conn.Disconnect() os._exit(1) conns.append(conn) jids.append(jid) streams.append(make_stream(queue.append, protocol=protocol, authenticator=authenticator, resource=resource, suffix=suffix)) factory = StreamFactory(streams, jids) port = reactor.listenTCP(4242, factory, interface='localhost') def signal_receiver(*args, **kw): if kw['path'] == '/org/freedesktop/DBus' and \ kw['member'] == 'NameOwnerChanged': bus_name, old_name, new_name = args if new_name == '': for i, conn in enumerate(conns): stream = streams[i] jid = jids[i] if conn._requested_bus_name == bus_name: factory.lost_presence(stream, jid) break queue.append(Event('dbus-signal', path=unwrap(kw['path']), signal=kw['member'], args=map(unwrap, args), interface=kw['interface'])) match_all_signals = bus.add_signal_receiver( signal_receiver, None, # signal name None, # interface None, path_keyword='path', member_keyword='member', interface_keyword='interface', byte_arrays=True ) error = None try: if do_connect: for conn in conns: conn.Connect() expect_connected_func(queue) if len(conns) == 1: fun(queue, bus, conns[0], streams[0]) else: fun(queue, bus, conns, streams) except Exception, e: traceback.print_exc() error = e queue.verbose = False if colourer: sys.stdout = colourer.fh d = port.stopListening() # Does the Connection object still exist? for i, conn in enumerate(conns): if not bus.name_has_owner(conn.object.bus_name): # Connection has already been disconnected and destroyed continue try: if conn.GetStatus() == cs.CONN_STATUS_CONNECTED: # Connection is connected, properly disconnect it disconnect_conn(queue, conn, streams[i]) else: # Connection is not connected, call Disconnect() to destroy it conn.Disconnect() except dbus.DBusException, e: pass except Exception, e: traceback.print_exc() error = e try: conn.Disconnect() raise AssertionError("Connection didn't disappear; " "all subsequent tests will probably fail") except dbus.DBusException, e: pass except Exception, e: traceback.print_exc() error = e match_all_signals.remove() if error is None: d.addBoth((lambda *args: reactor.crash())) else: # please ignore the POSIX behind the curtain d.addBoth((lambda *args: os._exit(1))) def exec_test(fun, params=None, protocol=None, timeout=None, authenticator=None, num_instances=1, do_connect=True): reactor.callWhenRunning( exec_test_deferred, fun, params, protocol, timeout, authenticator, num_instances, do_connect) reactor.run() # Useful routines for server-side vCard handling current_vcard = domish.Element(('vcard-temp', 'vCard')) def expect_and_handle_get_vcard(q, stream): get_vcard_event = q.expect('stream-iq', query_ns=ns.VCARD_TEMP, query_name='vCard', iq_type='get') iq = get_vcard_event.stanza vcard = iq.firstChildElement() assert vcard.name == 'vCard', vcard.toXml() # Send back current vCard result = make_result_iq(stream, iq, add_query_node=False) result.addChild(current_vcard) stream.send(result) def expect_and_handle_set_vcard(q, stream, check=None): global current_vcard set_vcard_event = q.expect('stream-iq', query_ns=ns.VCARD_TEMP, query_name='vCard', iq_type='set') iq = set_vcard_event.stanza vcard = iq.firstChildElement() assert vcard.name == 'vCard', vcard.toXml() if check is not None: check(vcard) # Update current vCard current_vcard = vcard stream.send(make_result_iq(stream, iq)) def _elem_add(elem, *children): for child in children: if isinstance(child, domish.Element): elem.addChild(child) elif isinstance(child, unicode): elem.addContent(child) else: raise ValueError( 'invalid child object %r (must be element or unicode)', child) def elem(a, b=None, attrs={}, **kw): r""" >>> elem('foo')().toXml() u'' >>> elem('foo', x='1')().toXml() u"" >>> elem('foo', x='1')(u'hello').toXml() u"hello" >>> elem('foo', x='1')(u'hello', ... elem('http://foo.org', 'bar', y='2')(u'bye')).toXml() u"hellobye" >>> elem('foo', attrs={'xmlns:bar': 'urn:bar', 'bar:cake': 'yum'})( ... elem('bar:e')(u'i') ... ).toXml() u"i" """ class _elem(domish.Element): def __call__(self, *children): _elem_add(self, *children) return self if b is not None: elem = _elem((a, b)) else: elem = _elem((None, a)) # Can't just update kw into attrs, because that *modifies the parameter's # default*. Thanks python. allattrs = {} allattrs.update(kw) allattrs.update(attrs) # First, let's pull namespaces out realattrs = {} for k, v in allattrs.iteritems(): if k.startswith('xmlns:'): abbr = k[len('xmlns:'):] elem.localPrefixes[abbr] = v else: realattrs[k] = v for k, v in realattrs.iteritems(): if k == 'from_': elem['from'] = v else: elem[k] = v return elem def elem_iq(server, type, **kw): class _iq(IQ): def __call__(self, *children): _elem_add(self, *children) return self iq = _iq(server, type) for k, v in kw.iteritems(): if k == 'from_': iq['from'] = v else: iq[k] = v return iq def make_presence(_from, to='test@localhost', type=None, show=None, status=None, caps=None, photo=None): presence = domish.Element((None, 'presence')) presence['from'] = _from presence['to'] = to if type is not None: presence['type'] = type if show is not None: presence.addElement('show', content=show) if status is not None: presence.addElement('status', content=status) if caps is not None: cel = presence.addElement(('http://jabber.org/protocol/caps', 'c')) for key,value in caps.items(): cel[key] = value # 4a1... if photo is not None: x = presence.addElement((ns.VCARD_TEMP_UPDATE, 'x')) x.addElement('photo').addContent(photo) return presence telepathy-gabble-0.18.2/tests/twisted/constants.py0000644000175000017500000004174312227000321022226 0ustar00smcvsmcv00000000000000""" Some handy constants for other tests to share and enjoy. """ from dbus import PROPERTIES_IFACE CM = "org.freedesktop.Telepathy.ConnectionManager" HT_NONE = 0 HT_CONTACT = 1 HT_ROOM = 2 HT_LIST = 3 HT_GROUP = 4 CHANNEL = "org.freedesktop.Telepathy.Channel" CHANNEL_IFACE_CALL_STATE = CHANNEL + ".Interface.CallState" CHANNEL_IFACE_CHAT_STATE = CHANNEL + '.Interface.ChatState' CHANNEL_IFACE_DESTROYABLE = CHANNEL + ".Interface.Destroyable" CHANNEL_IFACE_DTMF = CHANNEL + ".Interface.DTMF" CHANNEL_IFACE_GROUP = CHANNEL + ".Interface.Group" CHANNEL_IFACE_HOLD = CHANNEL + ".Interface.Hold" CHANNEL_IFACE_MEDIA_SIGNALLING = CHANNEL + ".Interface.MediaSignalling" CHANNEL_IFACE_MESSAGES = CHANNEL + ".Interface.Messages" CHANNEL_IFACE_PASSWORD = CHANNEL + ".Interface.Password" CHANNEL_IFACE_TUBE = CHANNEL + ".Interface.Tube" CHANNEL_IFACE_SASL_AUTH = CHANNEL + ".Interface.SASLAuthentication" CHANNEL_IFACE_CONFERENCE = CHANNEL + '.Interface.Conference' CHANNEL_IFACE_ROOM = CHANNEL + '.Interface.Room2' CHANNEL_IFACE_ROOM_CONFIG = CHANNEL + '.Interface.RoomConfig1' CHANNEL_IFACE_SUBJECT = CHANNEL + '.Interface.Subject2' CHANNEL_IFACE_FILE_TRANSFER_METADATA = CHANNEL + '.Interface.FileTransfer.Metadata' CHANNEL_TYPE_CALL = CHANNEL + ".Type.Call1" CHANNEL_TYPE_CONTACT_LIST = CHANNEL + ".Type.ContactList" CHANNEL_TYPE_CONTACT_SEARCH = CHANNEL + ".Type.ContactSearch" CHANNEL_TYPE_TEXT = CHANNEL + ".Type.Text" CHANNEL_TYPE_TUBES = CHANNEL + ".Type.Tubes" CHANNEL_TYPE_STREAM_TUBE = CHANNEL + ".Type.StreamTube" CHANNEL_TYPE_DBUS_TUBE = CHANNEL + ".Type.DBusTube" CHANNEL_TYPE_STREAMED_MEDIA = CHANNEL + ".Type.StreamedMedia" CHANNEL_TYPE_TEXT = CHANNEL + ".Type.Text" CHANNEL_TYPE_FILE_TRANSFER = CHANNEL + ".Type.FileTransfer" CHANNEL_TYPE_SERVER_AUTHENTICATION = \ CHANNEL + ".Type.ServerAuthentication" CHANNEL_TYPE_SERVER_TLS_CONNECTION = \ CHANNEL + ".Type.ServerTLSConnection" TP_AWKWARD_PROPERTIES = "org.freedesktop.Telepathy.Properties" PROPERTY_FLAG_READ = 1 PROPERTY_FLAG_WRITE = 2 PROPERTY_FLAGS_RW = PROPERTY_FLAG_READ | PROPERTY_FLAG_WRITE CHANNEL_TYPE = CHANNEL + '.ChannelType' TARGET_HANDLE_TYPE = CHANNEL + '.TargetHandleType' TARGET_HANDLE = CHANNEL + '.TargetHandle' TARGET_ID = CHANNEL + '.TargetID' REQUESTED = CHANNEL + '.Requested' INITIATOR_HANDLE = CHANNEL + '.InitiatorHandle' INITIATOR_ID = CHANNEL + '.InitiatorID' INTERFACES = CHANNEL + '.Interfaces' INITIAL_AUDIO = CHANNEL_TYPE_STREAMED_MEDIA + '.InitialAudio' INITIAL_VIDEO = CHANNEL_TYPE_STREAMED_MEDIA + '.InitialVideo' IMMUTABLE_STREAMS = CHANNEL_TYPE_STREAMED_MEDIA + '.ImmutableStreams' CALL_INITIAL_AUDIO = CHANNEL_TYPE_CALL + '.InitialAudio' CALL_INITIAL_AUDIO_NAME = CHANNEL_TYPE_CALL + '.InitialAudioName' CALL_INITIAL_VIDEO = CHANNEL_TYPE_CALL + '.InitialVideo' CALL_INITIAL_VIDEO_NAME = CHANNEL_TYPE_CALL + '.InitialVideoName' CALL_MUTABLE_CONTENTS = CHANNEL_TYPE_CALL + '.MutableContents' CALL_CONTENT = 'org.freedesktop.Telepathy.Call1.Content' CALL_CONTENT_IFACE_MEDIA = \ 'org.freedesktop.Telepathy.Call1.Content.Interface.Media' CALL_CONTENT_IFACE_DTMF = \ 'org.freedesktop.Telepathy.Call1.Content.Interface.DTMF' CALL_CONTENT_MEDIADESCRIPTION = \ 'org.freedesktop.Telepathy.Call1.Content.MediaDescription' CALL_STREAM = 'org.freedesktop.Telepathy.Call1.Stream' CALL_STREAM_IFACE_MEDIA = \ 'org.freedesktop.Telepathy.Call1.Stream.Interface.Media' CALL_STREAM_ENDPOINT = 'org.freedesktop.Telepathy.Call1.Stream.Endpoint' CALL_MEDIA_TYPE_AUDIO = 0 CALL_MEDIA_TYPE_VIDEO = 1 CALL_CONTENT_PACKETIZATION_RTP = 0 CALL_CONTENT_PACKETIZATION_RAW = 1 CALL_CONTENT_PACKETIZATION_MSN_WEBCAM = 2 CALL_STREAM_TRANSPORT_UNKNOWN = 0 CALL_STREAM_TRANSPORT_RAW_UDP = 1 CALL_STREAM_TRANSPORT_ICE = 2 CALL_STREAM_TRANSPORT_GTALK_P2P = 3 CALL_STREAM_TRANSPORT_WLM_2009 = 4 CALL_STREAM_TRANSPORT_SHM = 5 CALL_STREAM_TRANSPORT_MULTICAST = 6 #for streamed media CALL_STATE_RINGING = 1 CALL_STATE_HELD = 4 CALL_STATE_UNKNOWN = 0, CALL_STATE_PENDING_INITIATOR = 1 CALL_STATE_INITIALISING = 2 CALL_STATE_INITIALISED = 3 CALL_STATE_ACCEPTED = 4 CALL_STATE_ACTIVE = 5 CALL_STATE_ENDED = 6 CALL_FLAG_LOCALLY_HELD = 1 CALL_FLAG_LOCALLY_RINGING = 2 CALL_FLAG_LOCALLY_QUEUED = 4 CALL_FLAG_FORWARDED = 8 CALL_FLAG_CLEARING = 16 CALL_MEMBER_FLAG_RINGING = 1 CALL_MEMBER_FLAG_HELD = 2 CALL_DISPOSITION_NONE = 0 CALL_DISPOSITION_INITIAL = 1 CALL_SENDING_STATE_NONE = 0 CALL_SENDING_STATE_PENDING_SEND = 1 CALL_SENDING_STATE_SENDING = 2 CALL_SENDING_STATE_PENDING_STOP_SENDING = 3 CALL_STREAM_FLOW_STATE_STOPPED = 0 CALL_STREAM_FLOW_STATE_PENDING_START = 1 CALL_STREAM_FLOW_STATE_PENDING_STOP = 2 CALL_STREAM_FLOW_STATE_STARTED = 3 CALL_STREAM_ENDPOINT_STATE_CONNECTING = 0 CALL_STREAM_ENDPOINT_STATE_PROVISIONALLY_CONNECTED = 1 CALL_STREAM_ENDPOINT_STATE_FULLY_CONNECTED = 2 CALL_STREAM_ENDPOINT_STATE_EXHAUSTED_CANDIDATES = 3 CALL_STREAM_ENDPOINT_STATE_FAILED = 4 CALL_STREAM_CANDIDATE_TYPE_HOST = 1 CALL_STREAM_CANDIDATE_TYPE_SERVER_REFLEXIVE = 2 CALL_STREAM_CANDIDATE_TYPE_RELAY = 4 CALL_STATE_CHANGE_REASON_UNKNOWN = 0 CALL_STATE_CHANGE_REASON_PROGRESS_MADE = 1 CALL_STATE_CHANGE_REASON_USER_REQUESTED = 2 CALL_STATE_CHANGE_REASON_FORWARDED = 3 CALL_STATE_CHANGE_REASON_REJECTED = 4 CALL_STATE_CHANGE_REASON_NO_ANSWER = 5 CALL_STATE_CHANGE_REASON_INVALID_CONTACT = 6 CALL_STATE_CHANGE_REASON_PERMISSION_DENIED = 7 CALL_STATE_CHANGE_REASON_BUSY = 8 CALL_STATE_CHANGE_REASON_INTERNAL_ERROR = 9 CALL_STATE_CHANGE_REASON_SERVICE_ERROR = 10 CALL_STATE_CHANGE_REASON_NETWORK_ERROR = 11 CALL_STATE_CHANGE_REASON_MEDIA_ERROR = 12 CALL_STATE_CHANGE_REASON_CONNECTIVITY_ERROR = 13 CALL_STREAM_COMPONENT_UNKNOWN = 0 CALL_STREAM_COMPONENT_DATA = 1 CALL_STREAM_COMPONENT_CONTROL = 2 SUBSCRIPTION_STATE_UNKNOWN = 0 SUBSCRIPTION_STATE_NO = 1 SUBSCRIPTION_STATE_REMOVED_REMOTELY = 2 SUBSCRIPTION_STATE_ASK = 3 SUBSCRIPTION_STATE_YES = 4 CONTACT_LIST_STATE_NONE = 0 CONTACT_LIST_STATE_WAITING = 1 CONTACT_LIST_STATE_FAILURE = 2 CONTACT_LIST_STATE_SUCCESS = 3 CONN = "org.freedesktop.Telepathy.Connection" CONN_IFACE_AVATARS = CONN + '.Interface.Avatars' CONN_IFACE_ALIASING = CONN + '.Interface.Aliasing' CONN_IFACE_CAPS = CONN + '.Interface.Capabilities' CONN_IFACE_CONTACTS = CONN + '.Interface.Contacts' CONN_IFACE_CONTACT_CAPS = CONN + '.Interface.ContactCapabilities' CONN_IFACE_CONTACT_INFO = CONN + ".Interface.ContactInfo" CONN_IFACE_PRESENCE = CONN + '.Interface.Presence' CONN_IFACE_SIMPLE_PRESENCE = CONN + '.Interface.SimplePresence' CONN_IFACE_REQUESTS = CONN + '.Interface.Requests' CONN_IFACE_LOCATION = CONN + '.Interface.Location' CONN_IFACE_GABBLE_DECLOAK = CONN + '.Interface.Gabble.Decloak' CONN_IFACE_MAIL_NOTIFICATION = CONN + '.Interface.MailNotification' CONN_IFACE_CONTACT_LIST = CONN + '.Interface.ContactList' CONN_IFACE_CONTACT_GROUPS = CONN + '.Interface.ContactGroups' CONN_IFACE_CLIENT_TYPES = CONN + '.Interface.ClientTypes' CONN_IFACE_POWER_SAVING = CONN + '.Interface.PowerSaving' CONN_IFACE_CONTACT_BLOCKING = CONN + '.Interface.ContactBlocking' CONN_IFACE_ADDRESSING = CONN + '.Interface.Addressing1' ATTR_CONTACT_CAPABILITIES = CONN_IFACE_CONTACT_CAPS + '/capabilities' STREAM_HANDLER = 'org.freedesktop.Telepathy.Media.StreamHandler' ERROR = 'org.freedesktop.Telepathy.Error' INVALID_ARGUMENT = ERROR + '.InvalidArgument' NOT_IMPLEMENTED = ERROR + '.NotImplemented' NOT_AVAILABLE = ERROR + '.NotAvailable' PERMISSION_DENIED = ERROR + '.PermissionDenied' OFFLINE = ERROR + '.Offline' NOT_CAPABLE = ERROR + '.NotCapable' CONNECTION_REFUSED = ERROR + '.ConnectionRefused' CONNECTION_FAILED = ERROR + '.ConnectionFailed' CONNECTION_LOST = ERROR + '.ConnectionLost' CANCELLED = ERROR + '.Cancelled' DISCONNECTED = ERROR + '.Disconnected' REGISTRATION_EXISTS = ERROR + '.RegistrationExists' AUTHENTICATION_FAILED = ERROR + '.AuthenticationFailed' CONNECTION_REPLACED = ERROR + '.ConnectionReplaced' ALREADY_CONNECTED = ERROR + '.AlreadyConnected' NETWORK_ERROR = ERROR + '.NetworkError' NOT_YET = ERROR + '.NotYet' INVALID_HANDLE = ERROR + '.InvalidHandle' CERT_UNTRUSTED = ERROR + '.Cert.Untrusted' SERVICE_BUSY = ERROR + '.ServiceBusy' SERVICE_CONFUSED = ERROR + '.ServiceConfused' BANNED = ERROR + '.Channel.Banned' UNKNOWN_METHOD = 'org.freedesktop.DBus.Error.UnknownMethod' TUBE_PARAMETERS = CHANNEL_IFACE_TUBE + '.Parameters' TUBE_STATE = CHANNEL_IFACE_TUBE + '.State' STREAM_TUBE_SERVICE = CHANNEL_TYPE_STREAM_TUBE + '.Service' DBUS_TUBE_SERVICE_NAME = CHANNEL_TYPE_DBUS_TUBE + '.ServiceName' DBUS_TUBE_DBUS_NAMES = CHANNEL_TYPE_DBUS_TUBE + '.DBusNames' DBUS_TUBE_SUPPORTED_ACCESS_CONTROLS = CHANNEL_TYPE_DBUS_TUBE + '.SupportedAccessControls' STREAM_TUBE_SUPPORTED_SOCKET_TYPES = CHANNEL_TYPE_STREAM_TUBE + '.SupportedSocketTypes' CONFERENCE_INITIAL_CHANNELS = CHANNEL_IFACE_CONFERENCE + '.InitialChannels' CONFERENCE_INITIAL_INVITEE_HANDLES = CHANNEL_IFACE_CONFERENCE + '.InitialInviteeHandles' CONFERENCE_INITIAL_INVITEE_IDS = CHANNEL_IFACE_CONFERENCE + '.InitialInviteeIDs' CONTACT_SEARCH_ASK = CHANNEL_TYPE_CONTACT_SEARCH + '.AvailableSearchKeys' CONTACT_SEARCH_SERVER = CHANNEL_TYPE_CONTACT_SEARCH + '.Server' CONTACT_SEARCH_STATE = CHANNEL_TYPE_CONTACT_SEARCH + '.SearchState' SEARCH_NOT_STARTED = 0 SEARCH_IN_PROGRESS = 1 SEARCH_MORE_AVAILABLE = 2 SEARCH_COMPLETED = 3 SEARCH_FAILED = 4 TUBE_CHANNEL_STATE_LOCAL_PENDING = 0 TUBE_CHANNEL_STATE_REMOTE_PENDING = 1 TUBE_CHANNEL_STATE_OPEN = 2 TUBE_CHANNEL_STATE_NOT_OFFERED = 3 MEDIA_STREAM_TYPE_AUDIO = 0 MEDIA_STREAM_TYPE_VIDEO = 1 MEDIA_STREAM_BASE_PROTO_UDP = 0 MEDIA_STREAM_BASE_PROTO_TCP = 1 MEDIA_STREAM_TRANSPORT_TYPE_LOCAL = 0 MEDIA_STREAM_TRANSPORT_TYPE_DERIVED = 1 MEDIA_STREAM_TRANSPORT_TYPE_RELAY = 2 SOCKET_ADDRESS_TYPE_UNIX = 0 SOCKET_ADDRESS_TYPE_ABSTRACT_UNIX = 1 SOCKET_ADDRESS_TYPE_IPV4 = 2 SOCKET_ADDRESS_TYPE_IPV6 = 3 SOCKET_ACCESS_CONTROL_LOCALHOST = 0 SOCKET_ACCESS_CONTROL_PORT = 1 SOCKET_ACCESS_CONTROL_NETMASK = 2 SOCKET_ACCESS_CONTROL_CREDENTIALS = 3 TUBE_STATE_LOCAL_PENDING = 0 TUBE_STATE_REMOTE_PENDING = 1 TUBE_STATE_OPEN = 2 TUBE_STATE_NOT_OFFERED = 3 TUBE_TYPE_DBUS = 0 TUBE_TYPE_STREAM = 1 MEDIA_STREAM_DIRECTION_NONE = 0 MEDIA_STREAM_DIRECTION_SEND = 1 MEDIA_STREAM_DIRECTION_RECEIVE = 2 MEDIA_STREAM_DIRECTION_BIDIRECTIONAL = 3 MEDIA_STREAM_PENDING_LOCAL_SEND = 1 MEDIA_STREAM_PENDING_REMOTE_SEND = 2 MEDIA_STREAM_TYPE_AUDIO = 0 MEDIA_STREAM_TYPE_VIDEO = 1 MEDIA_STREAM_STATE_DISCONNECTED = 0 MEDIA_STREAM_STATE_CONNECTING = 1 MEDIA_STREAM_STATE_CONNECTED = 2 MEDIA_STREAM_DIRECTION_NONE = 0 MEDIA_STREAM_DIRECTION_SEND = 1 MEDIA_STREAM_DIRECTION_RECEIVE = 2 MEDIA_STREAM_DIRECTION_BIDIRECTIONAL = 3 FT_STATE_NONE = 0 FT_STATE_PENDING = 1 FT_STATE_ACCEPTED = 2 FT_STATE_OPEN = 3 FT_STATE_COMPLETED = 4 FT_STATE_CANCELLED = 5 FT_STATE_CHANGE_REASON_NONE = 0 FT_STATE_CHANGE_REASON_REQUESTED = 1 FT_STATE_CHANGE_REASON_LOCAL_STOPPED = 2 FT_STATE_CHANGE_REASON_REMOTE_STOPPED = 3 FT_STATE_CHANGE_REASON_LOCAL_ERROR = 4 FT_STATE_CHANGE_REASON_REMOTE_ERROR = 5 FILE_HASH_TYPE_NONE = 0 FILE_HASH_TYPE_MD5 = 1 FILE_HASH_TYPE_SHA1 = 2 FILE_HASH_TYPE_SHA256 = 3 FT_STATE = CHANNEL_TYPE_FILE_TRANSFER + '.State' FT_CONTENT_TYPE = CHANNEL_TYPE_FILE_TRANSFER + '.ContentType' FT_FILENAME = CHANNEL_TYPE_FILE_TRANSFER + '.Filename' FT_SIZE = CHANNEL_TYPE_FILE_TRANSFER + '.Size' FT_CONTENT_HASH_TYPE = CHANNEL_TYPE_FILE_TRANSFER + '.ContentHashType' FT_CONTENT_HASH = CHANNEL_TYPE_FILE_TRANSFER + '.ContentHash' FT_DESCRIPTION = CHANNEL_TYPE_FILE_TRANSFER + '.Description' FT_DATE = CHANNEL_TYPE_FILE_TRANSFER + '.Date' FT_AVAILABLE_SOCKET_TYPES = CHANNEL_TYPE_FILE_TRANSFER + '.AvailableSocketTypes' FT_TRANSFERRED_BYTES = CHANNEL_TYPE_FILE_TRANSFER + '.TransferredBytes' FT_INITIAL_OFFSET = CHANNEL_TYPE_FILE_TRANSFER + '.InitialOffset' FT_FILE_COLLECTION = CHANNEL_TYPE_FILE_TRANSFER + '.FUTURE.FileCollection' FT_URI = CHANNEL_TYPE_FILE_TRANSFER + '.URI' FT_SERVICE_NAME = CHANNEL_IFACE_FILE_TRANSFER_METADATA + '.ServiceName' FT_METADATA = CHANNEL_IFACE_FILE_TRANSFER_METADATA + '.Metadata' GF_CAN_ADD = 1 GF_CAN_REMOVE = 2 GF_CAN_RESCIND = 4 GF_MESSAGE_ADD = 8 GF_MESSAGE_REMOVE = 16 GF_MESSAGE_ACCEPT = 32 GF_MESSAGE_REJECT = 64 GF_MESSAGE_RESCIND = 128 GF_CHANNEL_SPECIFIC_HANDLES = 256 GF_ONLY_ONE_GROUP = 512 GF_HANDLE_OWNERS_NOT_AVAILABLE = 1024 GF_PROPERTIES = 2048 GF_MEMBERS_CHANGED_DETAILED = 4096 GC_REASON_NONE = 0 GC_REASON_OFFLINE = 1 GC_REASON_KICKED = 2 GC_REASON_BUSY = 3 GC_REASON_INVITED = 4 GC_REASON_BANNED = 5 GC_REASON_ERROR = 6 GC_REASON_INVALID_CONTACT = 7 GC_REASON_NO_ANSWER = 8 GC_REASON_RENAMED = 9 GC_REASON_PERMISSION_DENIED = 10 GC_REASON_SEPARATED = 11 HS_UNHELD = 0 HS_HELD = 1 HS_PENDING_HOLD = 2 HS_PENDING_UNHOLD = 3 HSR_NONE = 0 HSR_REQUESTED = 1 HSR_RESOURCE_NOT_AVAILABLE = 2 CONN_STATUS_CONNECTED = 0 CONN_STATUS_CONNECTING = 1 CONN_STATUS_DISCONNECTED = 2 CSR_NONE_SPECIFIED = 0 CSR_REQUESTED = 1 CSR_NETWORK_ERROR = 2 CSR_AUTHENTICATION_FAILED = 3 CSR_ENCRYPTION_ERROR = 4 CSR_NAME_IN_USE = 5 CSR_CERT_NOT_PROVIDED = 6 CSR_CERT_UNTRUSTED = 7 CSR_CERT_EXPIRED = 8 CSR_CERT_NOT_ACTIVATED = 9 CSR_CERT_HOSTNAME_MISMATCH = 10 CSR_CERT_FINGERPRINT_MISMATCH = 11 CSR_CERT_SELF_SIGNED = 12 CSR_CERT_OTHER_ERROR = 13 BUDDY_INFO = 'org.laptop.Telepathy.BuddyInfo' ACTIVITY_PROPERTIES = 'org.laptop.Telepathy.ActivityProperties' CHAT_STATE_GONE = 0 CHAT_STATE_INACTIVE = 1 CHAT_STATE_ACTIVE = 2 CHAT_STATE_PAUSED = 3 CHAT_STATE_COMPOSING = 4 # Channel_Media_Capabilities MEDIA_CAP_AUDIO = 1 MEDIA_CAP_VIDEO = 2 MEDIA_CAP_STUN = 4 MEDIA_CAP_GTALKP2P = 8 MEDIA_CAP_ICEUDP = 16 MEDIA_CAP_IMMUTABLE_STREAMS = 32 CLIENT = 'org.freedesktop.Telepathy.Client' PRESENCE_OFFLINE = 1 PRESENCE_AVAILABLE = 2 PRESENCE_AWAY = 3 PRESENCE_EXTENDED_AWAY = 4 PRESENCE_HIDDEN = 5 PRESENCE_BUSY = 6 PRESENCE_UNKNOWN = 7 PRESENCE_ERROR = 8 CONTACT_INFO_FLAG_CAN_SET = 1 CONTACT_INFO_FLAG_PUSH = 2 CONTACT_INFO_FIELD_FLAG_PARAMETERS_EXACT = 1 CONTACT_INFO_FIELD_FLAG_OVERWRITTEN_BY_NICKNAME = 2 # Channel_Interface_SaslAuthentication SASL_STATUS_NOT_STARTED = 0 SASL_STATUS_IN_PROGRESS = 1 SASL_STATUS_SERVER_SUCCEEDED = 2 SASL_STATUS_CLIENT_ACCEPTED = 3 SASL_STATUS_SUCCEEDED = 4 SASL_STATUS_SERVER_FAILED = 5 SASL_STATUS_CLIENT_FAILED = 6 SASL_ABORT_REASON_INVALID_CHALLENGE = 0 SASL_ABORT_REASON_USER_ABORT = 1 AUTH_METHOD = CHANNEL_TYPE_SERVER_AUTHENTICATION + ".AuthenticationMethod" SASL_AVAILABLE_MECHANISMS = CHANNEL_IFACE_SASL_AUTH + ".AvailableMechanisms" SASL_STATUS = CHANNEL_IFACE_SASL_AUTH + ".SASLStatus" SASL_ERROR = CHANNEL_IFACE_SASL_AUTH + ".SASLError" SASL_ERROR_DETAILS = CHANNEL_IFACE_SASL_AUTH + ".SASLErrorDetails" SASL_CONTEXT = CHANNEL_IFACE_SASL_AUTH + ".SASLContext" SASL_AUTHORIZATION_IDENTITY = CHANNEL_IFACE_SASL_AUTH + ".AuthorizationIdentity" SASL_DEFAULT_REALM = CHANNEL_IFACE_SASL_AUTH + ".DefaultRealm" SASL_DEFAULT_USERNAME = CHANNEL_IFACE_SASL_AUTH + ".DefaultUsername" # Channel_Type_ServerTLSConnection TLS_CERT_PATH = CHANNEL_TYPE_SERVER_TLS_CONNECTION + ".ServerCertificate" TLS_HOSTNAME = CHANNEL_TYPE_SERVER_TLS_CONNECTION + ".Hostname" TLS_REFERENCE_IDENTITIES = \ CHANNEL_TYPE_SERVER_TLS_CONNECTION + ".ReferenceIdentities" # Connection.Interface.Location LOCATION_FEATURE_CAN_SET = 1 # Channel.Type.Text MT_NORMAL = 0 MT_ACTION = 1 MT_NOTICE = 2 MT_AUTO_REPLY = 3 MT_DELIVERY_REPORT = 4 class MessageFlag(object): TRUNCATED = 1 NON_TEXT_CONTENT = 2 SCROLLBACK = 4 RESCUED = 8 class SendError(object): UNKNOWN = 0 OFFLINE = 1 INVALID_CONTACT = 2 PERMISSION_DENIED = 3 TOO_LONG = 4 NOT_IMPLEMENTED = 5 PROTOCOL = 'org.freedesktop.Telepathy.Protocol' PROTOCOL_IFACE_PRESENCES = PROTOCOL + '.Interface.Presence' PROTOCOL_IFACE_ADDRESSING = PROTOCOL + '.Interface.Addressing' PARAM_REQUIRED = 1 PARAM_REGISTER = 2 PARAM_HAS_DEFAULT = 4 PARAM_SECRET = 8 PARAM_DBUS_PROPERTY = 16 AUTHENTICATION = 'org.freedesktop.Telepathy.Authentication' AUTH_TLS_CERT = AUTHENTICATION + ".TLSCertificate" TLS_CERT_STATE_PENDING = 0 TLS_CERT_STATE_ACCEPTED = 1 TLS_CERT_STATE_REJECTED = 2 TLS_REJECT_REASON_UNKNOWN = 0 TLS_REJECT_REASON_UNTRUSTED = 1 # Channel.Interface.Messages MESSAGE_PART_SUPPORT_FLAGS = CHANNEL_IFACE_MESSAGES + '.MessagePartSupportFlags' DELIVERY_REPORTING_SUPPORT = CHANNEL_IFACE_MESSAGES + '.DeliveryReportingSupport' SUPPORTED_CONTENT_TYPES = CHANNEL_IFACE_MESSAGES + '.SupportedContentTypes' MSG_SENDING_FLAGS_REPORT_DELIVERY = 1 MSG_SENDING_FLAGS_REPORT_READ = 2 MSG_SENDING_FLAGS_REPORT_DELETED = 4 DELIVERY_REPORTING_SUPPORT_FLAGS_RECEIVE_FAILURES = 1 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 MEDIA_STREAM_ERROR_CONNECTION_FAILED = 3 MEDIA_STREAM_ERROR_NETWORK_ERROR = 4 MEDIA_STREAM_ERROR_NO_CODECS = 5 MEDIA_STREAM_ERROR_INVALID_CM_BEHAVIOR = 6 MEDIA_STREAM_ERROR_MEDIA_ERROR = 7 PASSWORD_FLAG_PROVIDE = 8 # Channel.Interface.Room ROOM_NAME = CHANNEL_IFACE_ROOM + '.RoomName' ROOM_SERVER = CHANNEL_IFACE_ROOM + '.Server' # Channel.Interface.Subject SUBJECT = CHANNEL_IFACE_ROOM + '.Subject' SUBJECT_PRESENT = 1 SUBJECT_CAN_SET = 2 telepathy-gabble-0.18.2/tests/twisted/bytestream.py0000644000175000017500000007253312200204333022372 0ustar00smcvsmcv00000000000000import base64 import hashlib import sys import random import socket from twisted.internet.protocol import Factory, Protocol from twisted.internet import reactor from twisted.words.protocols.jabber.client import IQ from twisted.words.xish import xpath, domish from twisted.internet.error import CannotListenError from servicetest import Event, EventPattern from gabbletest import acknowledge_iq, make_result_iq, elem_iq, elem import ns def wait_events(q, expected, my_event): tmp = expected + [my_event] events = q.expect_many(*tmp) return events[:-1], events[-1] def create_from_si_offer(stream, q, bytestream_cls, iq, initiator): si_nodes = xpath.queryForNodes('/iq/si', iq) assert si_nodes is not None assert len(si_nodes) == 1 si = si_nodes[0] feature = xpath.queryForNodes('/si/feature', si)[0] x = xpath.queryForNodes('/feature/x', feature)[0] assert x['type'] == 'form' field = xpath.queryForNodes('/x/field', x)[0] assert field['var'] == 'stream-method' assert field['type'] == 'list-single' bytestreams = [] for value in xpath.queryForNodes('/field/option/value', field): bytestreams.append(str(value)) bytestream = bytestream_cls(stream, q, si['id'], initiator, iq['to'], False) bytestream.check_si_offer(iq, bytestreams) return bytestream, si['profile'] def is_ipv4(address): try: socket.inet_pton(socket.AF_INET, address) except (ValueError, socket.error): return False return True class Bytestream(object): def __init__(self, stream, q, sid, initiator, target, initiated): self.stream = stream self.q = q self.stream_id = sid self.initiator = initiator self.target = target self.initiated = initiated def open_bytestream(self, expected_before=[], expected_after=[]): raise NotImplemented def send_data(self, data): raise NotImplemented def get_ns(self): raise NotImplemented def wait_bytestream_open(self): raise NotImplemented def get_data(self, size=0): raise NotImplemented def wait_bytestream_closed(self, expected=[]): raise NotImplemented def check_si_offer(self, iq, bytestreams): assert self.get_ns() in bytestreams def close(self): raise NotImplemented ##### XEP-0095: Stream Initiation ##### def _create_si_offer(self, profile, to=None): assert self.initiated iq = IQ(self.stream, 'set') iq['from'] = self.initiator if to is None: iq['to'] = self.target else: iq['to'] = to si = iq.addElement((ns.SI, 'si')) si['id'] = self.stream_id si['profile'] = profile feature = si.addElement((ns.FEATURE_NEG, 'feature')) x = feature.addElement((ns.X_DATA, 'x')) x['type'] = 'form' field = x.addElement((None, 'field')) field['var'] = 'stream-method' field['type'] = 'list-single' return iq, si, field def create_si_offer(self, profile, to=None): iq, si, field = self._create_si_offer(profile, to) option = field.addElement((None, 'option')) value = option.addElement((None, 'value')) value.addContent(self.get_ns()) return iq, si def create_si_reply(self, iq, to=None): result = make_result_iq(self.stream, iq) result['from'] = iq['to'] if to is None: result['to'] = self.initiator else: result['to'] = to res_si = result.firstChildElement() res_feature = res_si.addElement((ns.FEATURE_NEG, 'feature')) res_x = res_feature.addElement((ns.X_DATA, 'x')) res_x['type'] = 'submit' res_field = res_x.addElement((None, 'field')) res_field['var'] = 'stream-method' res_value = res_field.addElement((None, 'value')) res_value.addContent(self.get_ns()) return result, res_si def check_si_reply(self, iq): si = xpath.queryForNodes('/iq/si[@xmlns="%s"]' % ns.SI, iq)[0] value = xpath.queryForNodes('/si/feature/x/field/value', si) assert len(value) == 1 proto = value[0] assert str(proto) == self.get_ns() ##### XEP-0065: SOCKS5 Bytestreams ##### def listen_socks5(q): for port in range(5000, 5100): try: reactor.listenTCP(port, S5BFactory(q.append), interface='localhost') except CannotListenError: continue else: return port assert False, "Can't find a free port" def announce_socks5_proxy(q, stream, disco_stanza): reply = make_result_iq(stream, disco_stanza) query = xpath.queryForNodes('/iq/query', reply)[0] item = query.addElement((None, 'item')) item['jid'] = 'proxy.localhost' stream.send(reply) # wait for proxy disco#info query event = q.expect('stream-iq', to='proxy.localhost', query_ns=ns.DISCO_INFO, iq_type='get') reply = elem_iq(stream, 'result', from_='proxy.localhost', id=event.stanza['id'])( elem(ns.DISCO_INFO, 'query')( elem('identity', category='proxy', type='bytestreams', name='SOCKS5 Bytestreams')(), elem('feature', var=ns.BYTESTREAMS)())) stream.send(reply) # Gabble asks for SOCKS5 info event = q.expect('stream-iq', to='proxy.localhost', query_ns=ns.BYTESTREAMS, iq_type='get') port = listen_socks5(q) reply = elem_iq(stream, 'result', id=event.stanza['id'], from_='proxy.localhost')( elem(ns.BYTESTREAMS, 'query')( elem('streamhost', jid='proxy.localhost', host='127.0.0.1', port=str(port))())) stream.send(reply) class BytestreamS5B(Bytestream): def __init__(self, stream, q, sid, initiator, target, initiated): Bytestream.__init__(self, stream, q, sid, initiator, target, initiated) # hosts that will be announced when sending S5B open IQ self.hosts = [ # Not working streamhost ('invalid.invalid', 'invalid.invalid'), # Working streamhost (self.initiator, '127.0.0.1'), # This works too but should not be tried as Gabble should just # connect to the previous one ('Not me', '127.0.0.1')] def get_ns(self): return ns.BYTESTREAMS def _send_socks5_init(self, port): iq = IQ(self.stream, 'set') iq['to'] = self.target iq['from'] = self.initiator query = iq.addElement((ns.BYTESTREAMS, 'query')) query['sid'] = self.stream_id query['mode'] = 'tcp' for jid, host in self.hosts: streamhost = query.addElement('streamhost') streamhost['jid'] = jid streamhost['host'] = host streamhost['port'] = str(port) self.stream.send(iq) def _wait_auth_request(self): event = self.q.expect('s5b-data-received') assert event.data == '\x05\x01\x00' # version 5, 1 auth method, no auth self.transport = event.transport def _send_auth_reply(self): self.transport.write('\x05\x00') # version 5, no auth def _compute_hash_domain(self): # sha-1(sid + initiator + target) unhashed_domain = self.stream_id + self.initiator + self.target return hashlib.sha1(unhashed_domain).hexdigest() def _wait_connect_cmd(self): event = self.q.expect('s5b-data-received', transport=self.transport) # version 5, connect, reserved, domain type expected_connect = '\x05\x01\x00\x03' expected_connect += chr(40) # len (SHA-1) expected_connect += self._compute_hash_domain() expected_connect += '\x00\x00' # port assert event.data == expected_connect def _send_connect_reply(self): connect_reply = '\x05\x00\x00\x03' connect_reply += chr(40) # len (SHA-1) connect_reply += self._compute_hash_domain() connect_reply += '\x00\x00' # port self.transport.write(connect_reply) def _check_s5b_reply(self, iq): streamhost = xpath.queryForNodes('/iq/query/streamhost-used', iq)[0] assert streamhost['jid'] == self.initiator def _socks5_expect_connection(self, expected_before, expected_after): events_before, _ = wait_events(self.q, expected_before, EventPattern('s5b-connected')) self._wait_auth_request() self._send_auth_reply() self._wait_connect_cmd() self._send_connect_reply() # wait for S5B IQ reply events_after, e = wait_events(self.q, expected_after, EventPattern('stream-iq', iq_type='result', to=self.initiator)) self._check_s5b_reply(e.stanza) return events_before, events_after def open_bytestream(self, expected_before=[], expected_after=[]): port = listen_socks5(self.q) self._send_socks5_init(port) return self._socks5_expect_connection(expected_before, expected_after) def send_data(self, data): self.transport.write(data) def _expect_socks5_init(self): event = self.q.expect('stream-iq', iq_type='set') iq = event.stanza query = xpath.queryForNodes('/iq/query', iq)[0] assert query.uri == ns.BYTESTREAMS mode = query['mode'] sid = query['sid'] hosts = [] for streamhost in xpath.queryForNodes('/query/streamhost', query): hosts.append((streamhost['jid'], streamhost['host'], int(streamhost['port']))) return iq['id'], mode, sid, hosts def _send_auth_cmd(self): #version 5, 1 auth method, no auth self.transport.write('\x05\x01\x00') def _wait_auth_reply(self): event = self.q.expect('s5b-data-received') assert event.data == '\x05\x00' # version 5, no auth def _send_connect_cmd(self): # version 5, connect, reserved, domain type connect = '\x05\x01\x00\x03' connect += chr(40) # len (SHA-1) connect += self._compute_hash_domain() connect += '\x00\x00' # port self.transport.write(connect) def _wait_connect_reply(self): event = self.q.expect('s5b-data-received') # version 5, succeed, reserved, domain type expected_reply = '\x05\x00\x00\x03' expected_reply += chr(40) # len (SHA-1) expected_reply += self._compute_hash_domain() expected_reply += '\x00\x00' # port assert event.data == expected_reply def _socks5_connect(self, host, port): reactor.connectTCP(host, port, S5BFactory(self.q.append)) event = self.q.expect('s5b-connected') self.transport = event.transport self._send_auth_cmd() self._wait_auth_reply() self._send_connect_cmd() self._wait_connect_reply() return True def _send_socks5_reply(self, id, stream_used): result = IQ(self.stream, 'result') result['id'] = id result['from'] = self.target result['to'] = self.initiator query = result.addElement((ns.BYTESTREAMS, 'query')) streamhost_used = query.addElement((None, 'streamhost-used')) streamhost_used['jid'] = stream_used result.send() def wait_bytestream_open(self): id, mode, sid, hosts = self._expect_socks5_init() assert mode == 'tcp' assert sid == self.stream_id stream_host_found = False for jid, host, port in hosts: if not is_ipv4(host): continue if jid == self.initiator: stream_host_found = True if self._socks5_connect(host, port): self._send_socks5_reply(id, jid) else: # Connection failed self.send_not_found(id) break assert stream_host_found def get_data(self, size=0): binary = '' received = False while not received: e = self.q.expect('s5b-data-received', transport=self.transport) binary += e.data if len(binary) >= size or size == 0: received = True return binary def wait_bytestream_closed(self, expected=[]): events, _ = wait_events(self.q, expected, EventPattern('s5b-connection-lost')) return events def check_error_stanza(self, iq): error = xpath.queryForNodes('/iq/error', iq)[0] assert error['code'] == '404' assert error['type'] == 'cancel' def send_not_found(self, id): iq = IQ(self.stream, 'error') iq['to'] = self.initiator iq['from'] = self.target iq['id'] = id error = iq.addElement(('', 'error')) error['type'] = 'cancel' error['code'] = '404' self.stream.send(iq) def close(self): self.transport.loseConnection() class BytestreamS5BPidgin(BytestreamS5B): """Simulate buggy S5B implementation (as Pidgin's one)""" def _send_connect_reply(self): # version 5, ok, reserved, domain type connect_reply = '\x05\x00\x00\x03' # I'm Pidgin, why should I respect SOCKS5 XEP? domain = '127.0.0.1' connect_reply += chr(len(domain)) connect_reply += domain connect_reply += '\x00\x00' # port self.transport.write(connect_reply) class BytestreamS5BCannotConnect(BytestreamS5B): """SOCKS5 bytestream not working because target can't connect to initiator.""" def __init__(self, stream, q, sid, initiator, target, initiated): BytestreamS5B.__init__(self, stream, q, sid, initiator, target, initiated) self.hosts = [('invalid.invalid', 'invalid.invalid')] def open_bytestream(self, expected_before=[], expected_after=[]): self._send_socks5_init(12345) events_before, iq_event = wait_events(self.q, expected_before, EventPattern('stream-iq', iq_type='error', to=self.initiator)) self.check_error_stanza(iq_event.stanza) return events_before, [] def _socks5_connect(self, host, port): # Pretend we can't connect to it return False class BytestreamS5BWrongHash(BytestreamS5B): """Connection is closed because target sends the wrong hash""" def __init__(self, stream, q, sid, initiator, target, initiated): BytestreamS5B.__init__(self, stream, q, sid, initiator, target, initiated) self.hosts = [(self.initiator, '127.0.0.1')] def _send_connect_cmd(self): # version 5, connect, reserved, domain type connect = '\x05\x01\x00\x03' # send wrong hash as domain domain = 'this is wrong' connect += chr(len(domain)) connect += domain connect += '\x00\x00' # port self.transport.write(connect) def _socks5_connect(self, host, port): reactor.connectTCP(host, port, S5BFactory(self.q.append)) event = self.q.expect('s5b-connected') self.transport = event.transport self._send_auth_cmd() self._wait_auth_reply() self._send_connect_cmd() # Gabble disconnects the connection because we sent a wrong hash self.q.expect('s5b-connection-lost') return False def _socks5_expect_connection(self, expected_before, expected_after): events_before, _ = wait_events(self.q, expected_before, EventPattern('s5b-connected')) self._wait_auth_request() self._send_auth_reply() self._wait_connect_cmd() # pretend the hash was wrong and close the transport self.transport.loseConnection() iq_event = self.q.expect('stream-iq', iq_type='error', to=self.initiator) self.check_error_stanza(iq_event.stanza) return events_before, [] class BytestreamS5BRelay(BytestreamS5B): """Direct connection doesn't work so we use a relay""" def __init__(self, stream, q, sid, initiator, target, initiated): BytestreamS5B.__init__(self, stream, q, sid, initiator, target, initiated) self.hosts = [(self.initiator, 'invalid.invalid'), ('proxy.localhost', '127.0.0.1')] # This is the only thing we need to check to test the Target side as the # protocol is similar from this side. def _check_s5b_reply(self, iq): streamhost = xpath.queryForNodes('/iq/query/streamhost-used', iq)[0] assert streamhost['jid'] == 'proxy.localhost' def wait_bytestream_open(self): # The only difference of using a relay on the Target side is to # connect to another streamhost. id, mode, sid, hosts = self._expect_socks5_init() assert mode == 'tcp' assert sid == self.stream_id proxy_found = False for jid, host, port in hosts: if jid != self.initiator: proxy_found = True # connect to the (fake) relay if self._socks5_connect(host, port): self._send_socks5_reply(id, jid) else: assert False break assert proxy_found # The initiator (Gabble) is now supposed to connect to the proxy too self._wait_connect_to_proxy() def _wait_connect_to_proxy(self): e = self.q.expect('s5b-connected') self.transport = e.transport self._wait_auth_request() self._send_auth_reply() self._wait_connect_cmd() self._send_connect_reply() self._wait_activation_iq() def _wait_activation_iq(self): e = self.q.expect('stream-iq', iq_type='set', to='proxy.localhost', query_ns=ns.BYTESTREAMS) query = xpath.queryForNodes('/iq/query', e.stanza)[0] assert query['sid'] == self.stream_id activate = xpath.queryForNodes('/iq/query/activate', e.stanza)[0] assert str(activate) == self.target self._reply_activation_iq(e.stanza) def _reply_activation_iq(self, iq): reply = make_result_iq(self.stream, iq) reply.send() def _socks5_connect(self, host, port): # No point to emulate the proxy. Just pretend the Target properly # connects, auth and requests connection return True def wait_bytestream_closed(self, expected=[]): if expected == []: return [] return self.q.expect_many(*expected) class BytestreamS5BRelayBugged(BytestreamS5BRelay): """Simulate bugged ejabberd (< 2.0.2) proxy sending wrong CONNECT reply""" def _send_connect_reply(self): # send a 6 bytes wrong reply connect_reply = '\x05\x00\x00\x00\x00\x00' self.transport.write(connect_reply) class S5BProtocol(Protocol): def connectionMade(self): self.factory.event_func(Event('s5b-connected', transport=self.transport)) def dataReceived(self, data): self.factory.event_func(Event('s5b-data-received', data=data, transport=self.transport)) class S5BFactory(Factory): protocol = S5BProtocol def __init__(self, event_func): self.event_func = event_func def buildProtocol(self, addr): protocol = Factory.buildProtocol(self, addr) return protocol def startedConnecting(self, connector): self.event_func(Event('s5b-started-connecting', connector=connector)) def clientConnectionLost(self, connector, reason): self.event_func(Event('s5b-connection-lost', connector=connector, reason=reason)) def clientConnectionFailed(self, connector, reason): self.event_func(Event('s5b-connection-failed', reason=reason)) def expect_socks5_reply(q): event = q.expect('stream-iq', iq_type='result') iq = event.stanza query = xpath.queryForNodes('/iq/query', iq)[0] assert query.uri == ns.BYTESTREAMS streamhost_used = xpath.queryForNodes('/query/streamhost-used', query)[0] return streamhost_used ##### XEP-0047: In-Band Bytestreams (IBB) ##### class BytestreamIBB(Bytestream): def __init__(self, stream, q, sid, initiator, target, initiated): Bytestream.__init__(self, stream, q, sid, initiator, target, initiated) self.seq = 0 self.checked = False def get_ns(self): return ns.IBB def check_si_reply(self, iq): self.checked = True def open_bytestream(self, expected_before=[], expected_after=[]): # open IBB bytestream iq = IQ(self.stream, 'set') iq['to'] = self.target iq['from'] = self.initiator open = iq.addElement((ns.IBB, 'open')) open['sid'] = self.stream_id # set a ridiculously small block size to stress test IBB buffering open['block-size'] = '1' assert self.checked events_before = self.q.expect_many(*expected_before) self.stream.send(iq) events_after = self.q.expect_many(*expected_after) return events_before, events_after def _send(self, from_, to, data): raise NotImplemented def send_data(self, data): if self.initiated: from_ = self.initiator to = self.target else: from_ = self.target to = self.initiator self._send(from_, to, data) self.seq += 1 def wait_bytestream_open(self): # Wait IBB open iq event = self.q.expect('stream-iq', iq_type='set') open = xpath.queryForNodes('/iq/open', event.stanza)[0] assert open.uri == ns.IBB assert open['sid'] == self.stream_id # open IBB bytestream acknowledge_iq(self.stream, event.stanza) def get_data(self, size=0): # wait for IBB stanza. Gabble always uses IQ binary = '' received = False while not received: ibb_event = self.q.expect('stream-iq', query_ns=ns.IBB) data_nodes = xpath.queryForNodes('/iq/data[@xmlns="%s"]' % ns.IBB, ibb_event.stanza) assert data_nodes is not None assert len(data_nodes) == 1 ibb_data = data_nodes[0] binary += base64.b64decode(str(ibb_data)) assert ibb_data['sid'] == self.stream_id # ack the IQ result = make_result_iq(self.stream, ibb_event.stanza) result.send() if len(binary) >= size or size == 0: received = True return binary def wait_bytestream_closed(self, expected=[]): events, close_event = wait_events(self.q, expected, EventPattern('stream-iq', iq_type='set', query_name='close', query_ns=ns.IBB)) # sender finish to send the file and so close the bytestream acknowledge_iq(self.stream, close_event.stanza) return events def close(self): if self.initiated: from_ = self.initiator to = self.target else: from_ = self.target to = self.initiator iq = elem_iq(self.stream, 'set', from_=from_, to=to, id=str(id))( elem('close', xmlns=ns.IBB, sid=self.stream_id)()) self.stream.send(iq) class BytestreamIBBMsg(BytestreamIBB): def _send(self, from_, to, data): message = domish.Element(('jabber:client', 'message')) message['to'] = to message['from'] = from_ data_node = message.addElement((ns.IBB, 'data')) data_node['sid'] = self.stream_id data_node['seq'] = str(self.seq) data_node.addContent(base64.b64encode(data)) self.stream.send(message) def _wait_data_event(self): ibb_event = self.q.expect('stream-message') data_nodes = xpath.queryForNodes('/message/data[@xmlns="%s"]' % ns.IBB, ibb_event.stanza) assert data_nodes is not None assert len(data_nodes) == 1 ibb_data = data_nodes[0] assert ibb_data['sid'] == self.stream_id return str(ibb_data), ibb_data['sid'] class BytestreamIBBIQ(BytestreamIBB): def _send(self, from_, to, data): id = random.randint(0, sys.maxint) iq = elem_iq(self.stream, 'set', from_=from_, to=to, id=str(id))( elem('data', xmlns=ns.IBB, sid=self.stream_id, seq=str(self.seq))( (unicode(base64.b64encode(data))))) self.stream.send(iq) ##### SI Fallback (Gabble specific extension) ##### class BytestreamSIFallback(Bytestream): """Abstract class used for all the SI fallback scenarios""" def __init__(self, stream, q, sid, initiator, target, initiated): Bytestream.__init__(self, stream, q, sid, initiator, target, initiated) self.socks5 = BytestreamS5B(stream, q, sid, initiator, target, initiated) self.ibb = BytestreamIBBMsg(stream, q, sid, initiator, target, initiated) def create_si_offer(self, profile, to=None): iq, si, field = self._create_si_offer(profile, to) # add SOCKS5 option = field.addElement((None, 'option')) value = option.addElement((None, 'value')) value.addContent(self.socks5.get_ns()) # add IBB option = field.addElement((None, 'option')) value = option.addElement((None, 'value')) value.addContent(self.ibb.get_ns()) si_multiple = si.addElement((ns.SI_MULTIPLE, 'si-multiple')) return iq, si def check_si_reply(self, iq): value = xpath.queryForNodes( '/iq/si[@xmlns="%s"]/si-multiple[@xmlns="%s"]/value' % (ns.SI, ns.SI_MULTIPLE), iq) assert len(value) == 2 assert str(value[0]) == self.socks5.get_ns() assert str(value[1]) == self.ibb.get_ns() def create_si_reply(self, iq, to=None): result = make_result_iq(self.stream, iq) result['from'] = iq['to'] if to is None: result['to'] = self.initiator else: result['to'] = to res_si = result.firstChildElement() si_multiple = res_si.addElement((ns.SI_MULTIPLE, 'si-multiple')) # add SOCKS5 res_value = si_multiple.addElement((None, 'value')) res_value.addContent(self.socks5.get_ns()) # add IBB res_value = si_multiple.addElement((None, 'value')) res_value.addContent(self.ibb.get_ns()) return result, res_si def open_bytestream(self, expected_before=[], expected_after=[]): # first propose to peer to connect using SOCKS5 # We set an invalid IP so that won't work self.socks5._send_socks5_init([ # Not working streamhost (self.initiator, 'invalid.invalid', 12345), ]) events_before, iq_event = wait_events(self.q, expected_before, EventPattern('stream-iq', iq_type='error', to=self.initiator)) self.socks5.check_error_stanza(iq_event.stanza) # socks5 failed, let's try IBB _, events_after = self.ibb.open_bytestream([], expected_after) return events_before, events_after def send_data(self, data): self.used.send_data(data) def get_data(self, size=0): return self.used.get_data(size) def wait_bytestream_closed(self, expected=[]): return self.used.wait_bytestream_closed(expected) def check_si_offer(self, iq, bytestreams): assert self.socks5.get_ns() in bytestreams assert self.ibb.get_ns() in bytestreams # check if si-multiple is supported si_multiple = xpath.queryForNodes( '/iq/si[@xmlns="%s"]/si-multiple[@xmlns="%s"]' % (ns.SI, ns.SI_MULTIPLE), iq) assert si_multiple is not None def close(self): return self.used.close() class BytestreamSIFallbackS5CannotConnect(BytestreamSIFallback): """Try to use SOCKS5 and fallback to IBB because the target can't connect to the receiver.""" def __init__(self, stream, q, sid, initiator, target, initiated): BytestreamSIFallback.__init__(self, stream, q, sid, initiator, target, initiated) self.socks5 = BytestreamS5BCannotConnect(stream, q, sid, initiator, target, initiated) self.used = self.ibb def open_bytestream(self, expected_before=[], expected_after=[]): # First propose to peer to connect using SOCKS5 # That won't work as target can't connect events_before, _ = self.socks5.open_bytestream(expected_before) # socks5 failed, let's try IBB _, events_after = self.ibb.open_bytestream([], expected_after) return events_before, events_after def wait_bytestream_open(self): # Gabble tries SOCKS5 first self.socks5.wait_bytestream_open() # Gabble now tries IBB self.ibb.wait_bytestream_open() def check_si_reply (self, iq): self.ibb.check_si_reply (iq) class BytestreamSIFallbackS5WrongHash(BytestreamSIFallback): """Try to use SOCKS5 and fallback to IBB because target sent the wrong hash as domain in the CONNECT command.""" def __init__(self, stream, q, sid, initiator, target, initiated): BytestreamSIFallback.__init__(self, stream, q, sid, initiator, target, initiated) self.socks5 = BytestreamS5BWrongHash(stream, q, sid, initiator, target, initiated) self.used = self.ibb def open_bytestream(self, expected_before=[], expected_after=[]): # SOCKS5 won't work because we'll pretend the hash was wrong and # close the connection events_before, _ = self.socks5.open_bytestream(expected_before) # socks5 failed, let's try IBB _, events_after = self.ibb.open_bytestream([], expected_after) return events_before, events_after def wait_bytestream_open(self): # BytestreamS5BWrongHash will send a wrong hash so Gabble will # disconnect the connection self.socks5.wait_bytestream_open() # Gabble now tries IBB self.ibb.wait_bytestream_open() def check_si_reply (self, iq): self.ibb.check_si_reply (iq) telepathy-gabble-0.18.2/tests/twisted/version.py0000644000175000017500000000236712227000321021676 0ustar00smcvsmcv00000000000000# vim: set encoding=utf-8 : """ Tests Gabble's implementation of XEP-0092. """ from twisted.words.xish import xpath from servicetest import assertLength, assertEquals from gabbletest import exec_test, elem_iq, elem import ns def test(q, bus, conn, stream): request = elem_iq(stream, 'get')( elem(ns.VERSION, 'query') ) stream.send(request) reply = q.expect('stream-iq', iq_id=request['id'], query_ns=ns.VERSION, query_name='query') # Both and are REQUIRED. What they actually contain is # someone else's problem™. names = xpath.queryForNodes('/query/name', reply.query) assertLength(1, names) versions = xpath.queryForNodes('/query/version', reply.query) assertLength(1, versions) # is OPTIONAL. “Revealing the application's underlying operating # system may open the user or system to attacks directed against that # operating system; therefore, an application MUST provide a way for a # human user or administrator to disable sharing of information about the # operating system.” The “way” that we provide is never to send it. oss = xpath.queryForNodes('/query/os', reply.query) assert oss is None if __name__ == '__main__': exec_test(test) telepathy-gabble-0.18.2/tests/twisted/test-register.py0000644000175000017500000001217612200204333023011 0ustar00smcvsmcv00000000000000 """ Test registration. """ from gabbletest import ( exec_test, make_result_iq, acknowledge_iq, send_error_reply, ) from servicetest import assertEquals from twisted.words.xish import domish, xpath import ns import constants as cs def connect_and_send_form(q, conn, stream): conn.Connect() q.expect('dbus-signal', signal='StatusChanged', args=[cs.CONN_STATUS_CONNECTING, cs.CSR_REQUESTED]) event = q.expect('stream-iq', query_ns=ns.REGISTER) result = make_result_iq(stream, event.stanza) query = result.firstChildElement() query.addElement('username') query.addElement('password') stream.send(result) event = q.expect('stream-iq') iq = event.stanza assert xpath.queryForString('/iq/query/username', iq) == 'test' assert xpath.queryForString('/iq/query/password', iq) == 'pass' return iq def test_success(q, bus, conn, stream): iq = connect_and_send_form(q, conn, stream) acknowledge_iq(stream, iq) q.expect('dbus-signal', signal='StatusChanged', args=[cs.CONN_STATUS_CONNECTED, cs.CSR_REQUESTED]) def test_conflict(q, bus, conn, stream): iq = connect_and_send_form(q, conn, stream) error = domish.Element((None, 'error')) error['code'] = '409' error['type'] = 'cancel' error.addElement((ns.STANZA, 'conflict')) send_error_reply(stream, iq, error) e = q.expect('dbus-signal', signal='ConnectionError') assertEquals(cs.REGISTRATION_EXISTS, e.args[0]) q.expect('dbus-signal', signal='StatusChanged', args=[cs.CONN_STATUS_DISCONNECTED, cs.CSR_NAME_IN_USE]) def test_with_email(q, bus, conn, stream): # The form requires ; so, Gabble should give up. conn.Connect() q.expect('dbus-signal', signal='StatusChanged', args=[cs.CONN_STATUS_CONNECTING, cs.CSR_REQUESTED]) event = q.expect('stream-iq', query_ns=ns.REGISTER) result = make_result_iq(stream, event.stanza) query = result.firstChildElement() query.addElement('username') query.addElement('password') query.addElement('email') stream.send(result) # This is really WOCKY_CONNECTOR_ERROR_REGISTRATION_REJECTED e = q.expect('dbus-signal', signal='ConnectionError') assertEquals(cs.PERMISSION_DENIED, e.args[0]) q.expect('dbus-signal', signal='StatusChanged', args=[cs.CONN_STATUS_DISCONNECTED, cs.CSR_AUTHENTICATION_FAILED]) def test_data_forms(q, bus, conn, stream): conn.Connect() q.expect('dbus-signal', signal='StatusChanged', args=[cs.CONN_STATUS_CONNECTING, cs.CSR_REQUESTED]) event = q.expect('stream-iq', query_ns=ns.REGISTER) result = make_result_iq(stream, event.stanza) query = result.firstChildElement() query.addElement((None, 'instructions')).addChild("Hope you like x:data") # Use the same element names as in jabber:iq:register as per XEP 0077's # Extensibility section. x = query.addElement((ns.X_DATA, 'x')) x['type'] = 'form' x.addElement((None, 'title')).addChild("Account Registration") x.addElement((None, 'instructions')).addChild( "This is gratuitously a data form!") form_type = x.addElement((None, 'field')) form_type['type'] = 'hidden' form_type['var'] = 'FORM_TYPE' form_type.addElement((None, 'value')).addChild(ns.REGISTER) first = x.addElement((None, 'field')) first['type'] = 'text-single' first['label'] = 'Username' first['var'] = 'username' first.addElement((None, 'required')) first = x.addElement((None, 'field')) first['type'] = 'text-single' first['label'] = 'Password' first['var'] = 'password' first.addElement((None, 'required')) stream.send(result) # This is really WOCKY_CONNECTOR_ERROR_REGISTRATION_UNSUPPORTED e = q.expect('dbus-signal', signal='ConnectionError') assertEquals(cs.NOT_AVAILABLE, e.args[0]) q.expect('dbus-signal', signal='StatusChanged', args=[cs.CONN_STATUS_DISCONNECTED, cs.CSR_AUTHENTICATION_FAILED]) def test_redirection(q, bus, conn, stream): conn.Connect() q.expect('dbus-signal', signal='StatusChanged', args=[cs.CONN_STATUS_CONNECTING, cs.CSR_REQUESTED]) event = q.expect('stream-iq', query_ns=ns.REGISTER) result = make_result_iq(stream, event.stanza) query = result.firstChildElement() query.addElement((None, 'instructions')).addChild("Sigh.") # Tell the user to go to some website url = query.addElement((ns.X_OOB, 'x')).addElement((None, 'url')) url.addChild("http://foogle.talk.example/newaccount") stream.send(result) # This is really WOCKY_CONNECTOR_ERROR_REGISTRATION_UNSUPPORTED e = q.expect('dbus-signal', signal='ConnectionError') assertEquals(cs.NOT_AVAILABLE, e.args[0]) q.expect('dbus-signal', signal='StatusChanged', args=[cs.CONN_STATUS_DISCONNECTED, cs.CSR_AUTHENTICATION_FAILED]) if __name__ == '__main__': exec_test(test_success, {'register': True}, do_connect=False) exec_test(test_conflict, {'register': True}, do_connect=False) exec_test(test_with_email, {'register': True}, do_connect=False) exec_test(test_data_forms, {'register': True}, do_connect=False) exec_test(test_redirection, {'register': True}, do_connect=False) telepathy-gabble-0.18.2/tests/twisted/test-location.py0000644000175000017500000002665512227000321023004 0ustar00smcvsmcv00000000000000import dbus import time import datetime from gabbletest import ( exec_test, elem, acknowledge_iq, send_error_reply, sync_stream, make_result_iq, disconnect_conn, ) from servicetest import ( call_async, EventPattern, assertEquals, assertLength, assertContains, ) from twisted.words.xish import xpath import constants as cs import ns Rich_Presence_Access_Control_Type_Publish_List = 1 def test(q, bus, conn, stream): # we don't yet know we have PEP assertEquals(0, conn.Get(cs.CONN_IFACE_LOCATION, "SupportedLocationFeatures", dbus_interface=cs.PROPERTIES_IFACE)) conn.Connect() # discard activities request and status change q.expect_many( EventPattern('stream-iq', iq_type='set', query_ns=ns.PUBSUB), EventPattern('dbus-signal', signal='StatusChanged', args=[cs.CONN_STATUS_CONNECTED, cs.CSR_REQUESTED]), ) # we now know we have PEP assertEquals(cs.LOCATION_FEATURE_CAN_SET, conn.Get(cs.CONN_IFACE_LOCATION, "SupportedLocationFeatures", dbus_interface=cs.PROPERTIES_IFACE)) # check location properties access_control_types = conn.Get( cs.CONN_IFACE_LOCATION, "LocationAccessControlTypes", dbus_interface=cs.PROPERTIES_IFACE) # only one access control is implemented in Gabble at the moment: assert len(access_control_types) == 1, access_control_types assert access_control_types[0] == \ Rich_Presence_Access_Control_Type_Publish_List access_control = conn.Get( cs.CONN_IFACE_LOCATION, "LocationAccessControl", dbus_interface=cs.PROPERTIES_IFACE) assert len(access_control) == 2, access_control assert access_control[0] == \ Rich_Presence_Access_Control_Type_Publish_List properties = conn.GetAll( cs.CONN_IFACE_LOCATION, dbus_interface=cs.PROPERTIES_IFACE) assert properties.get('LocationAccessControlTypes') == access_control_types assert properties.get('LocationAccessControl') == access_control # Test setting the properties # Enum out of range bad_access_control = dbus.Struct([dbus.UInt32(99), dbus.UInt32(0, variant_level=1)], signature=dbus.Signature('uv')) try: conn.Set (cs.CONN_IFACE_LOCATION, 'LocationAccessControl', bad_access_control, dbus_interface =cs.PROPERTIES_IFACE) except dbus.DBusException, e: pass else: assert False, "Should have had an error!" # Bad type bad_access_control = dbus.String("This should not be a string") try: conn.Set (cs.CONN_IFACE_LOCATION, 'LocationAccessControl', bad_access_control, dbus_interface =cs.PROPERTIES_IFACE) except dbus.DBusException, e: assert e.get_dbus_name() == cs.INVALID_ARGUMENT, e.get_dbus_name() else: assert False, "Should have had an error!" # Bad type bad_access_control = dbus.Struct([dbus.String("bad"), dbus.String("!"), dbus.UInt32(0, variant_level=1)], signature=dbus.Signature('ssv')) try: conn.Set (cs.CONN_IFACE_LOCATION, 'LocationAccessControl', bad_access_control, dbus_interface =cs.PROPERTIES_IFACE) except dbus.DBusException, e: assert e.get_dbus_name() == cs.INVALID_ARGUMENT, e.get_dbus_name() else: assert False, "Should have had an error!" # Correct conn.Set (cs.CONN_IFACE_LOCATION, 'LocationAccessControl', access_control, dbus_interface =cs.PROPERTIES_IFACE) # LocationAccessControlTypes is read-only, check Gabble return the # PermissionDenied error try: conn.Set (cs.CONN_IFACE_LOCATION, 'LocationAccessControlTypes', access_control_types, dbus_interface =cs.PROPERTIES_IFACE) except dbus.DBusException, e: assert e.get_dbus_name() == cs.PERMISSION_DENIED, e.get_dbus_name() else: assert False, "Should have had an error!" date = dbus.Int64(time.time()) date_str = datetime.datetime.utcfromtimestamp(date).strftime('%FT%H:%M:%SZ') # set a Location call_async(q, conn.Location, 'SetLocation', { 'lat': dbus.Double(0.0, variant_level=1), 'lon': 0.0, 'language': 'en', 'timestamp': date, 'country': 'Congo', 'accuracy': 1.4, # Gabble silently ignores unknown keys 'badger': 'mushroom'}) geoloc_iq_set_event = EventPattern('stream-iq', predicate=lambda x: xpath.queryForNodes("/iq/pubsub/publish/item/geoloc", x.stanza)) event = q.expect_many(geoloc_iq_set_event)[0] geoloc = xpath.queryForNodes("/iq/pubsub/publish/item/geoloc", event.stanza)[0] assertEquals(geoloc.getAttribute((ns.XML, 'lang')), 'en') lon = xpath.queryForNodes('/geoloc/lon', geoloc)[0] assertEquals(float(str(lon)), 0.0) lat = xpath.queryForNodes('/geoloc/lat', geoloc)[0] assertEquals(float(str(lat)), 0.0) timestamp = xpath.queryForNodes('/geoloc/timestamp', geoloc)[0] assertEquals(str(timestamp), date_str) country = xpath.queryForNodes('/geoloc/country', geoloc)[0] assertEquals(str(country), 'Congo') lat = xpath.queryForNodes('/geoloc/accuracy', geoloc)[0] assertEquals(float(str(lat)), 1.4) acknowledge_iq(stream, event.stanza) q.expect('dbus-return', method='SetLocation') # Server refuses to set Location call_async(q, conn.Location, 'SetLocation', { 'lat': 0.0, 'lon': 0.0}) geoloc_iq_set_event = EventPattern('stream-iq', predicate=lambda x: xpath.queryForNodes("/iq/pubsub/publish/item/geoloc", x.stanza)) event = q.expect_many(geoloc_iq_set_event)[0] send_error_reply(stream, event.stanza) q.expect('dbus-error', method='SetLocation') # Request Bob's location bob_handle = conn.RequestHandles(1, ['bob@foo.com'])[0] call_async(q, conn.Location, 'GetLocations', [bob_handle]) # Gabble should not send a pubsub query. The point of PEP is that we don't # have to do this. pubsub_get_pattern = EventPattern('stream-iq', iq_type='get', query_ns=ns.PUBSUB) q.forbid_events([ pubsub_get_pattern ]) # GetLocations returns immediately. get_locations = q.expect('dbus-return', method='GetLocations') locations = get_locations.value[0] # Location isn't known yet assertLength(0, locations) # Sync the XMPP stream to ensure Gabble hasn't sent a query. sync_stream(q, stream) # Bob updates his location message = elem('message', from_='bob@foo.com')( elem((ns.PUBSUB_EVENT), 'event')( elem('items', node=ns.GEOLOC)( elem('item', id='12345')( elem(ns.GEOLOC, 'geoloc', attrs={'xml:lang': 'en'})( elem('lat')(u'1.25'), elem('lon')(u'5.5'), elem('country')(u'Belgium'), elem('accuracy')(u'2.3'), elem('timestamp')(unicode(date_str)), # invalid element, will be ignored by Gabble elem('badger')(u'mushroom'), ) ) ) ) ) stream.send(message) update_event = q.expect('dbus-signal', signal='LocationUpdated') handle, location = update_event.args assertEquals(bob_handle, handle) assertLength(6, location) assertEquals(location['language'], 'en') assertEquals(location['lat'], 1.25) assertEquals(location['lon'], 5.5) assertEquals(location['country'], 'Belgium') assertEquals(location['accuracy'], 2.3) assertEquals(location['timestamp'], date) # Get location again; Gabble should return the cached location locations = conn.Location.GetLocations([bob_handle]) assertLength(1, locations) assertEquals(locations[bob_handle], location) charles_handle = conn.RequestHandles(cs.HT_CONTACT, ['charles@foo.com'])[0] # check that Contacts interface supports location attributes = conn.Contacts.GetContactAttributes( [bob_handle, charles_handle], [cs.CONN_IFACE_LOCATION], False) assertLength(2, attributes) assertContains(bob_handle, attributes) assertContains(charles_handle, attributes) assertEquals( { cs.CONN_IFACE_LOCATION + '/location': location, cs.CONN + '/contact-id': 'bob@foo.com'}, attributes[bob_handle]) assertEquals( { cs.CONN + '/contact-id': 'charles@foo.com'}, attributes[charles_handle]) # Try to set our location by passing a valid with an invalid type (lat is # supposed to be a double) q.forbid_events([geoloc_iq_set_event]) try: conn.Location.SetLocation({'lat': 'pony'}) except dbus.DBusException, e: assertEquals(e.get_dbus_name(), cs.INVALID_ARGUMENT) else: assert False # Bob updates his location again message = elem('message', from_='bob@foo.com')( elem((ns.PUBSUB_EVENT), 'event')( elem('items', node=ns.GEOLOC)( elem('item', id='12345')( elem(ns.GEOLOC, 'geoloc')( elem ('country') (u'France') ) ) ) ) ) stream.send(message) update_event = q.expect('dbus-signal', signal='LocationUpdated') handle, location = update_event.args assertEquals(handle, bob_handle) assertLength(1, location) assertEquals(location['country'], 'France') # Now we test explicitly retrieving Bob's location, so we should not forbid # such queries. :) q.unforbid_events([ pubsub_get_pattern ]) call_async(q, conn.Location, 'RequestLocation', bob_handle) e = q.expect('stream-iq', iq_type='get', query_ns=ns.PUBSUB, to='bob@foo.com') # Hey, while we weren't looking Bob moved abroad! result = make_result_iq(stream, e.stanza) result['from'] = 'bob@foo.com' pubsub_node = result.firstChildElement() pubsub_node.addChild( elem('items', node=ns.GEOLOC)( elem('item', id='12345')( elem(ns.GEOLOC, 'geoloc')( elem ('country') (u'Chad') ) ) ) ) stream.send(result) ret = q.expect('dbus-return', method='RequestLocation') location, = ret.value assertLength(1, location) assertEquals(location['country'], 'Chad') # Let's ask again; this time Bob's server hates us for some reason. call_async(q, conn.Location, 'RequestLocation', bob_handle) e = q.expect('stream-iq', iq_type='get', query_ns=ns.PUBSUB, to='bob@foo.com') send_error_reply(stream, e.stanza, elem('error', type='auth')( elem(ns.STANZA, 'forbidden') )) e = q.expect('dbus-error', method='RequestLocation') assertEquals(cs.PERMISSION_DENIED, e.name) # FIXME: maybe we should check that the cache gets invalidated in this # case? We should also test whether or not the cache is invalidated # properly if the contact clears their PEP node. # Let's ask a final time, and disconnect while we're doing so, to make sure # this doesn't break Gabble or Wocky. call_async(q, conn.Location, 'RequestLocation', bob_handle) e = q.expect('stream-iq', iq_type='get', query_ns=ns.PUBSUB, to='bob@foo.com') # Tasty argument unpacking. disconnect_conn returns two lists, one for # expeced_before=[] and one for expected_after=[...] _, (e, ) = disconnect_conn(q, conn, stream, expected_after=[EventPattern('dbus-error', method='RequestLocation')]) assertEquals(cs.CANCELLED, e.name) if __name__ == '__main__': exec_test(test, do_connect=False) telepathy-gabble-0.18.2/tests/twisted/test-fallback-socks5-proxy.py0000644000175000017500000003221312227000321025302 0ustar00smcvsmcv00000000000000 import socket from gabbletest import ( exec_test, elem, elem_iq, sync_stream, make_presence, send_error_reply, make_result_iq, sync_stream) from servicetest import ( EventPattern, call_async, assertEquals, assertLength, assertDoesNotContain) from caps_helper import send_disco_reply from bytestream import create_from_si_offer, BytestreamS5B import ns import constants as cs from twisted.words.xish import xpath import dbus from config import FILE_TRANSFER_ENABLED proxy_query_events = [ EventPattern('stream-iq', to='fallback1-proxy.localhost', iq_type='get', query_ns=ns.BYTESTREAMS), EventPattern('stream-iq', to='fallback2-proxy.localhost', iq_type='get', query_ns=ns.BYTESTREAMS)] proxy_port = {'fallback1-proxy.localhost': '12345', 'fallback2-proxy.localhost': '6789', 'fallback3-proxy.localhost': '3333', 'fallback4-proxy.localhost': '4444', 'fallback5-proxy.localhost': '5555', 'fallback6-proxy.localhost': '6666',} def connect_and_announce_alice(q, bus, conn, stream): q.forbid_events(proxy_query_events) # Send Alice's presence caps = { 'ext': '', 'ver': '0.0.0', 'node': 'http://example.com/fake-client0' } presence = make_presence('alice@localhost/Test', caps=caps) stream.send(presence) disco_event = q.expect('stream-iq', to='alice@localhost/Test', query_ns=ns.DISCO_INFO) send_disco_reply( stream, disco_event.stanza, [], [ns.TUBES, ns.FILE_TRANSFER]) sync_stream(q, stream) q.unforbid_events(proxy_query_events) def send_socks5_reply(stream, iq, jid=None, host=None, port=None): if jid is None: jid = iq['to'] if port is None: port = proxy_port[jid] if host is None: host = '127.0.0.1' reply = elem_iq(stream, 'result', id=iq['id'], from_=iq['to'])( elem(ns.BYTESTREAMS, 'query')( elem('streamhost', jid=jid, host=host, port=port)())) stream.send(reply) def wait_si_and_return_proxies(q, stream): e = q.expect('stream-iq', to='alice@localhost/Test') bytestream, profile = create_from_si_offer(stream, q, BytestreamS5B, e.stanza, 'test@localhost/Resource') # Alice accepts the SI result, si = bytestream.create_si_reply(e.stanza) stream.send(result) e = q.expect('stream-iq', to='alice@localhost/Test') proxies = [] for node in xpath.queryForNodes('/iq/query/streamhost', e.stanza): if node['jid'] == 'test@localhost/Resource': # skip our own stream hosts continue proxies.append((node['jid'], node['host'], node['port'])) return proxies def check_proxies(expected, proxies): assertEquals(set(expected), set(proxies)) def offer_dbus_tube(q, bus, conn, stream): connect_and_announce_alice(q, bus, conn, stream) # Offer a private D-Bus tube just to check if the proxy is present in the # SOCKS5 offer call_async(q, conn.Requests, 'CreateChannel', { cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_DBUS_TUBE, cs.TARGET_HANDLE_TYPE: cs.HT_CONTACT, cs.TARGET_ID: 'alice@localhost', cs.DBUS_TUBE_SERVICE_NAME: 'com.example.TestCase'}) # Proxy queries are send when creating the channel return_event, e1, e2 = q.expect_many( EventPattern('dbus-return', method='CreateChannel'), proxy_query_events[0], proxy_query_events[1]) send_socks5_reply(stream, e1.stanza) send_socks5_reply(stream, e2.stanza) path, props = return_event.value tube_chan = bus.get_object(conn.bus_name, path) dbus_tube_iface = dbus.Interface(tube_chan, cs.CHANNEL_TYPE_DBUS_TUBE) dbus_tube_iface.Offer({}, cs.SOCKET_ACCESS_CONTROL_CREDENTIALS) proxies = wait_si_and_return_proxies(q, stream) check_proxies([('fallback2-proxy.localhost', '127.0.0.1', '6789'), ('fallback1-proxy.localhost', '127.0.0.1', '12345')], proxies) def accept_stream_tube(q, bus, conn, stream): connect_and_announce_alice(q, bus, conn, stream) # Accept a stream tube, we'll need SOCKS5 proxies each time we'll connect # on the tube socket # Alice offers us a stream tube message = elem('message', to='test@localhost/Resource', from_='alice@localhost/Test')( elem(ns.TUBES, 'tube', type='stream', service='http', id='10')) stream.send(message) # we are interested in the 'NewChannels' announcing the tube channel def new_chan_predicate(e): path, props = e.args[0][0] return props[cs.CHANNEL_TYPE] == cs.CHANNEL_TYPE_STREAM_TUBE # Proxy queries are send when receiving an incoming stream tube new_chan, e1, e2 = q.expect_many( EventPattern('dbus-signal', signal='NewChannels', predicate=new_chan_predicate), proxy_query_events[0], proxy_query_events[1]) send_socks5_reply(stream, e1.stanza) send_socks5_reply(stream, e2.stanza) path, props = new_chan.args[0][0] assert props[cs.CHANNEL_TYPE] == cs.CHANNEL_TYPE_STREAM_TUBE tube_chan = bus.get_object(conn.bus_name, path) tube_iface = dbus.Interface(tube_chan, cs.CHANNEL_TYPE_STREAM_TUBE) # connect to the socket so a SOCKS5 bytestream will be created address = tube_iface.Accept(cs.SOCKET_ADDRESS_TYPE_IPV4, cs.SOCKET_ACCESS_CONTROL_LOCALHOST, 0, byte_arrays=True) s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.connect(address) proxies = wait_si_and_return_proxies(q, stream) check_proxies([('fallback2-proxy.localhost', '127.0.0.1', '6789'), ('fallback1-proxy.localhost', '127.0.0.1', '12345')], proxies) def send_file_to_alice(q, conn): call_async(q, conn.Requests, 'CreateChannel', { cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_FILE_TRANSFER, cs.TARGET_HANDLE_TYPE: cs.HT_CONTACT, cs.TARGET_ID: 'alice@localhost', cs.FT_FILENAME: 'test.txt', cs.FT_CONTENT_TYPE: 'text/plain', cs.FT_SIZE: 10}) def send_file(q, bus, conn, stream): connect_and_announce_alice(q, bus, conn, stream) # Send a file; proxy queries are send when creating the FT channel send_file_to_alice(q, conn) return_event, e1, e2 = q.expect_many( EventPattern('dbus-return', method='CreateChannel'), proxy_query_events[0], proxy_query_events[1]) send_socks5_reply(stream, e1.stanza) send_socks5_reply(stream, e2.stanza) # ensure that the same proxy is not queried more than once q.forbid_events(proxy_query_events) proxies = wait_si_and_return_proxies(q, stream) check_proxies([('fallback2-proxy.localhost', '127.0.0.1', '6789'), ('fallback1-proxy.localhost', '127.0.0.1', '12345')], proxies) def double_server(q, bus, conn, stream): # For some reason the 2 proxies are actually the same. Check that we don't # set them twice in the SOCKS5 init stanza connect_and_announce_alice(q, bus, conn, stream) send_file_to_alice(q, conn) return_event, e1, e2 = q.expect_many( EventPattern('dbus-return', method='CreateChannel'), proxy_query_events[0], proxy_query_events[1]) send_socks5_reply(stream, e1.stanza) # send the same reply for the second stanza with with a different port send_socks5_reply(stream, e2.stanza, 'fallback1-proxy.localhost', '127.0.0.1', '6789') proxies = wait_si_and_return_proxies(q, stream) # check that the proxy has been set only once check_proxies([('fallback1-proxy.localhost', '127.0.0.1', '6789')], proxies) def cache_full(q, bus, conn, stream): # Test how Gabble manages the proxy cache once it's full connect_and_announce_alice(q, bus, conn, stream) send_file_to_alice(q, conn) # 3 proxies are queried (NB_MIN_SOCKS5_PROXIES) return_event, e1, e2, e3 = q.expect_many( EventPattern('dbus-return', method='CreateChannel'), EventPattern('stream-iq', iq_type='get', query_ns=ns.BYTESTREAMS), EventPattern('stream-iq', iq_type='get', query_ns=ns.BYTESTREAMS), EventPattern('stream-iq', iq_type='get', query_ns=ns.BYTESTREAMS)) send_socks5_reply(stream, e1.stanza) send_socks5_reply(stream, e2.stanza) send_socks5_reply(stream, e3.stanza) proxies = wait_si_and_return_proxies(q, stream) assertLength(3, set(proxies)) oldest_proxy = proxies[2] # send another file, one more proxy is queried send_file_to_alice(q, conn) return_event, e1, = q.expect_many( EventPattern('dbus-return', method='CreateChannel'), EventPattern('stream-iq', iq_type='get', query_ns=ns.BYTESTREAMS)) send_socks5_reply(stream, e1.stanza) proxies = wait_si_and_return_proxies(q, stream) assertLength(4, set(proxies)) # the new proxy is the head of the list assertEquals(e1.stanza['to'], proxies[0][0]) # send another file, one more proxy is queried send_file_to_alice(q, conn) return_event, e1, = q.expect_many( EventPattern('dbus-return', method='CreateChannel'), EventPattern('stream-iq', iq_type='get', query_ns=ns.BYTESTREAMS)) send_socks5_reply(stream, e1.stanza) proxies = wait_si_and_return_proxies(q, stream) assertLength(5, set(proxies)) # the new proxy is the head of the list assertEquals(e1.stanza['to'], proxies[0][0]) # send another file, one more proxy is queried send_file_to_alice(q, conn) return_event, e1, = q.expect_many( EventPattern('dbus-return', method='CreateChannel'), EventPattern('stream-iq', iq_type='get', query_ns=ns.BYTESTREAMS)) send_socks5_reply(stream, e1.stanza) proxies = wait_si_and_return_proxies(q, stream) # we reached the max size of the cache (FALLBACK_PROXY_CACHE_SIZE) so the # oldest proxy has been removed assertLength(5, set(proxies)) # the new proxy is the head of the list assertEquals(e1.stanza['to'], proxies[0][0]) # the oldest proxy has been removed assertDoesNotContain(oldest_proxy, proxies) #send another file. We already queried all the proxies so the list is recycled send_file_to_alice(q, conn) # the oldest proxy is re-requested first return_event, e1, = q.expect_many( EventPattern('dbus-return', method='CreateChannel'), EventPattern('stream-iq', to=oldest_proxy[0], iq_type='get', query_ns=ns.BYTESTREAMS)) def proxy_error(q, bus, conn, stream): # Test if another proxy is queried if a query failed connect_and_announce_alice(q, bus, conn, stream) send_file_to_alice(q, conn) return_event, e1, e2, e3 = q.expect_many( EventPattern('dbus-return', method='CreateChannel'), EventPattern('stream-iq', iq_type='get', query_ns=ns.BYTESTREAMS), EventPattern('stream-iq', iq_type='get', query_ns=ns.BYTESTREAMS), EventPattern('stream-iq', iq_type='get', query_ns=ns.BYTESTREAMS)) # Return errors for all the requests; the bugged proxies shouldn't be queried again q.forbid_events([EventPattern('stream-iq', to=e1.stanza['to'], iq_type='get', query_ns=ns.BYTESTREAMS)]) send_error_reply(stream, e1.stanza) # the fourth proxy is queried q.expect('stream-iq', iq_type='get', query_ns=ns.BYTESTREAMS) q.forbid_events([EventPattern('stream-iq', to=e2.stanza['to'], iq_type='get', query_ns=ns.BYTESTREAMS)]) send_error_reply(stream, e2.stanza) sync_stream(q, stream) q.forbid_events([EventPattern('stream-iq', to=e3.stanza['to'], iq_type='get', query_ns=ns.BYTESTREAMS)]) send_error_reply(stream, e3.stanza) sync_stream(q, stream) def proxies_telepathy_im(q, bus, conn, stream): # Test if proxies.telepathy.im is properly used when no fallback proxies # are passed to Gabble connect_and_announce_alice(q, bus, conn, stream) send_file_to_alice(q, conn) # Gabble asks for a proxy list to our server return_event, e, = q.expect_many( EventPattern('dbus-return', method='CreateChannel'), EventPattern('stream-iq', to='proxies.telepathy.im', iq_type='get', query_ns=ns.DISCO_ITEMS)) # reply with 2 servers reply = make_result_iq(stream, e.stanza) query = xpath.queryForNodes('/iq/query', reply)[0] item = query.addElement((None, 'item')) item['jid'] = 'proxy1.localhost' item = query.addElement((None, 'item')) item['jid'] = 'proxy2.localhost' stream.send(reply) # These servers are queried e1, e2 = q.expect_many( EventPattern('stream-iq', to='proxy1.localhost', iq_type='get', query_ns=ns.BYTESTREAMS), EventPattern('stream-iq', to='proxy2.localhost', iq_type='get', query_ns=ns.BYTESTREAMS)) if __name__ == '__main__': params = {'fallback-socks5-proxies': ['fallback1-proxy.localhost', 'fallback2-proxy.localhost']} exec_test(offer_dbus_tube, params=params) exec_test(accept_stream_tube, params=params) if FILE_TRANSFER_ENABLED: exec_test(send_file, params=params) exec_test(double_server, params=params) params6 = {'fallback-socks5-proxies': ['fallback1-proxy.localhost', 'fallback2-proxy.localhost', 'fallback3-proxy.localhost', 'fallback4-proxy.localhost', 'fallback5-proxy.localhost', 'fallback6-proxy.localhost']} exec_test(cache_full, params=params6) params4 = {'fallback-socks5-proxies': ['fallback1-proxy.localhost', 'fallback2-proxy.localhost', 'fallback3-proxy.localhost', 'fallback4-proxy.localhost']} exec_test(proxy_error, params=params4) exec_test(proxies_telepathy_im, params={}) telepathy-gabble-0.18.2/tests/twisted/test-debug.py0000644000175000017500000000364212227000321022251 0ustar00smcvsmcv00000000000000 """ Test the debug message interface. """ import dbus from servicetest import assertEquals, sync_dbus, call_async, ProxyWrapper from servicetest import EventPattern from gabbletest import exec_test import constants as cs from config import DEBUGGING path = '/org/freedesktop/Telepathy/debug' iface = 'org.freedesktop.Telepathy.Debug' def test(q, bus, conn, stream): messages = [] def new_message(timestamp, domain, level, string): messages.append((timestamp, domain, level, string)) debug = ProxyWrapper(bus.get_object(conn.bus_name, path), iface) debug.connect_to_signal('NewDebugMessage', new_message) if not DEBUGGING: # If we're built with --disable-debug, check that the Debug object # isn't present. call_async(q, debug, 'GetMessages') q.expect('dbus-error', method='GetMessages') return assert len(debug.GetMessages()) > 0 # Turn signalling on and generate some messages. assert len(messages) == 0 assert debug.Properties.Get(iface, 'Enabled') == False debug.Properties.Set(iface, 'Enabled', True) channel_path = conn.RequestChannel( cs.CHANNEL_TYPE_TEXT, cs.HT_CONTACT, conn.GetSelfHandle(), True) q.expect_many( EventPattern ('dbus-signal', signal='NewChannel'), EventPattern ('dbus-signal', signal = 'NewDebugMessage')) assert len(messages) > 0 # Turn signalling off and check we don't get any more messages. debug.Properties.Set(iface, 'Enabled', False) sync_dbus(bus, q, conn) snapshot = list(messages) channel = bus.get_object(conn.bus_name, channel_path) channel.Close(dbus_interface=cs.CHANNEL) q.expect('dbus-signal', signal='Closed') conn.RequestChannel( cs.CHANNEL_TYPE_TEXT, cs.HT_CONTACT, conn.GetSelfHandle(), True) q.expect('dbus-signal', signal='NewChannel') assertEquals (snapshot, messages) if __name__ == '__main__': exec_test(test) telepathy-gabble-0.18.2/tests/twisted/sidecars.py0000644000175000017500000001056512227000321022005 0ustar00smcvsmcv00000000000000""" Test Gabble's implementation of sidecars, using the test plugin. """ from servicetest import ( sync_dbus, call_async, EventPattern, assertEquals, assertContains, ) from gabbletest import exec_test, send_error_reply, acknowledge_iq, sync_stream import constants as cs from config import PLUGINS_ENABLED TEST_PLUGIN_IFACE = "org.freedesktop.Telepathy.Gabble.Plugin.Test" if not PLUGINS_ENABLED: print "NOTE: built without --enable-plugins, not testing plugins" print " (but still testing failing calls to EnsureSidecar)" def test(q, bus, conn, stream): # Request a sidecar thate we support before we're connected; it should just # wait around until we're connected. call_async(q, conn.Future, 'EnsureSidecar', TEST_PLUGIN_IFACE) if PLUGINS_ENABLED: # Now we're connected, the call we made earlier should return. path, props = q.expect('dbus-return', method='EnsureSidecar').value # This sidecar doesn't even implement get_immutable_properties; it # should just get the empty dict filled in for it. assertEquals({}, props) # We should get the same sidecar if we request it again path2, props2 = conn.Future.EnsureSidecar(TEST_PLUGIN_IFACE) assertEquals((path, props), (path2, props2)) else: # Only now does it fail. q.expect('dbus-error', method='EnsureSidecar') # This is not a valid interface name call_async(q, conn.Future, 'EnsureSidecar', 'not an interface') q.expect('dbus-error', name=cs.INVALID_ARGUMENT) # The test plugin makes no reference to this interface. call_async(q, conn.Future, 'EnsureSidecar', 'unsupported.sidecar') q.expect('dbus-error', name=cs.NOT_IMPLEMENTED) if PLUGINS_ENABLED: # This sidecar does have some properties: path, props = conn.Future.EnsureSidecar(TEST_PLUGIN_IFACE + ".Props") assertContains(TEST_PLUGIN_IFACE + ".Props.Greeting", props) # The plugin claims it implements this sidecar, but actually doesn't. # Check that we don't blow up (although this is no different from # Gabble's perspective to creating a sidecar failing because a network # service wasn't there, for instance). call_async(q, conn.Future, 'EnsureSidecar', TEST_PLUGIN_IFACE + ".Buggy") q.expect('dbus-error', name=cs.NOT_IMPLEMENTED) # This sidecar sends a stanza, and waits for a reply, before being # created. pattern = EventPattern('stream-iq', to='sidecar.example.com', query_ns='http://example.com/sidecar') call_async(q, conn.Future, 'EnsureSidecar', TEST_PLUGIN_IFACE + ".IQ") e = q.expect_many(pattern)[0] sync_dbus(bus, q, conn) # If the server says no, EnsureSidecar should fail. send_error_reply(stream, e.stanza) q.expect('dbus-error', method='EnsureSidecar', name=cs.NOT_AVAILABLE) # Let's try again. The plugin should get a chance to ping the server # again. call_async(q, conn.Future, 'EnsureSidecar', TEST_PLUGIN_IFACE + ".IQ") e = q.expect_many(pattern)[0] # The server said yes, so we should get a sidecar back! acknowledge_iq(stream, e.stanza) q.expect('dbus-return', method='EnsureSidecar') # If we ask again once the plugin has been created, it should return at # once without any more network traffic. q.forbid_events([pattern]) conn.Future.EnsureSidecar(TEST_PLUGIN_IFACE + ".IQ") sync_stream(q, stream) # TODO: test ensuring a sidecar that waits for something from the # network, disconnecting while it's waiting, and ensuring that nothing # breaks regardless of whether the network replies before # or not. call_async(q, conn, 'Disconnect') q.expect_many( EventPattern('dbus-signal', signal='StatusChanged', args=[cs.CONN_STATUS_DISCONNECTED, cs.CSR_REQUESTED]), EventPattern('stream-closed'), ) call_async(q, conn.Future, 'EnsureSidecar', 'zomg.what') # With older telepathy-glib this would be DISCONNECTED; # with newer telepathy-glib the Connection disappears from the bus # sooner, and you get UnknownMethod or something from dbus-glib. q.expect('dbus-error') stream.sendFooter() q.expect('dbus-return', method='Disconnect') if __name__ == '__main__': exec_test(test) telepathy-gabble-0.18.2/tests/twisted/sidecar-own-caps.py0000644000175000017500000000402012227000321023334 0ustar00smcvsmcv00000000000000""" Test Gabble's implementation of sidecars own caps, using the test plugin. """ from twisted.words.protocols.jabber.client import IQ from twisted.words.xish import xpath from servicetest import call_async, EventPattern, assertEquals, assertContains from gabbletest import exec_test, acknowledge_iq from caps_helper import compute_caps_hash import constants as cs import ns from config import PLUGINS_ENABLED TEST_PLUGIN_IFACE = "org.freedesktop.Telepathy.Gabble.Plugin.Test" if not PLUGINS_ENABLED: print "NOTE: built without --enable-plugins, skipping" raise SystemExit(77) # which makes the test show up as skipped def test(q, bus, conn, stream): # This sidecar sends a stanza, and waits for a reply, before being # created. pattern = EventPattern('stream-iq', to='sidecar.example.com', query_ns='http://example.com/sidecar') call_async(q, conn.Future, 'EnsureSidecar', TEST_PLUGIN_IFACE + ".IQ") e = q.expect_many(pattern)[0] # The server said yes, so we should get a sidecar back! acknowledge_iq(stream, e.stanza) q.expect('dbus-return', method='EnsureSidecar') identities = ["test/app-list//Test"] features = ["com.example.test1", "com.example.test2"] ver = compute_caps_hash(identities, features, {}) iq = IQ(stream, "get") id = iq['id'] query = iq.addElement((ns.DISCO_INFO, 'query')) query['node'] = ns.GABBLE_CAPS + '#' + ver stream.send(iq) e = q.expect('stream-iq', query_ns='http://jabber.org/protocol/disco#info') returned_features = [feature['var'] for feature in xpath.queryForNodes('/iq/query/feature', e.stanza)] assertEquals(features, returned_features) returned_identities = [identity['category'] + "/" + identity['type']+"//" + identity['name'] for identity in xpath.queryForNodes('/iq/query/identity', e.stanza)] assertEquals(identities, returned_identities) new_ver = compute_caps_hash(returned_identities, returned_features, {}) assertEquals(new_ver, ver) if __name__ == '__main__': exec_test(test) telepathy-gabble-0.18.2/tests/twisted/servicetest.py0000644000175000017500000005136112227000321022547 0ustar00smcvsmcv00000000000000 """ Infrastructure code for testing connection managers. """ from twisted.internet import glib2reactor from twisted.internet.protocol import Protocol, Factory, ClientFactory glib2reactor.install() import sys import time import os import pprint import unittest import dbus.glib from twisted.internet import reactor import constants as cs tp_name_prefix = 'org.freedesktop.Telepathy' tp_path_prefix = '/org/freedesktop/Telepathy' class DictionarySupersetOf (object): """Utility class for expecting "a dictionary with at least these keys".""" def __init__(self, dictionary): self._dictionary = dictionary def __repr__(self): return "DictionarySupersetOf(%s)" % self._dictionary def __eq__(self, other): """would like to just do: return set(other.items()).issuperset(self._dictionary.items()) but it turns out that this doesn't work if you have another dict nested in the values of your dicts""" try: for k,v in self._dictionary.items(): if k not in other or other[k] != v: return False return True except TypeError: # other is not iterable return False class Event(object): def __init__(self, type, **kw): self.__dict__.update(kw) self.type = type (self.subqueue, self.subtype) = type.split ("-", 1) def __str__(self): return '\n'.join([ str(type(self)) ] + format_event(self)) def format_event(event): ret = ['- type %s' % event.type] for key in sorted(dir(event)): if key != 'type' and not key.startswith('_'): ret.append('- %s: %s' % ( key, pprint.pformat(getattr(event, key)))) if key == 'error': ret.append('%s' % getattr(event, key)) return ret class EventPattern: def __init__(self, type, **properties): self.type = type self.predicate = None if 'predicate' in properties: self.predicate = properties['predicate'] del properties['predicate'] self.properties = properties (self.subqueue, self.subtype) = type.split ("-", 1) def __repr__(self): properties = dict(self.properties) if self.predicate is not None: properties['predicate'] = self.predicate return '%s(%r, **%r)' % ( self.__class__.__name__, self.type, properties) def match(self, event): if event.type != self.type: return False for key, value in self.properties.iteritems(): try: if getattr(event, key) != value: return False except AttributeError: return False if self.predicate is None or self.predicate(event): return True return False class TimeoutError(Exception): pass class ForbiddenEventOccurred(Exception): def __init__(self, event): Exception.__init__(self) self.event = event def __str__(self): return '\n' + '\n'.join(format_event(self.event)) class BaseEventQueue: """Abstract event queue base class. Implement the wait() method to have something that works. """ def __init__(self, timeout=None): self.verbose = False self.forbidden_events = set() self.event_queues = {} if timeout is None: self.timeout = 5 else: self.timeout = timeout def log(self, s): if self.verbose: print s def log_queues(self, queues): self.log ("Waiting for event on: %s" % ", ".join(queues)) def log_event(self, event): self.log('got event:') if self.verbose: map(self.log, format_event(event)) def forbid_events(self, patterns): """ Add patterns (an iterable of EventPattern) to the set of forbidden events. If a forbidden event occurs during an expect or expect_many, the test will fail. """ self.forbidden_events.update(set(patterns)) def unforbid_events(self, patterns): """ Remove 'patterns' (an iterable of EventPattern) from the set of forbidden events. These must be the same EventPattern pointers that were passed to forbid_events. """ self.forbidden_events.difference_update(set(patterns)) def unforbid_all(self): """ Remove all patterns from the set of forbidden events. """ self.forbidden_events.clear() def _check_forbidden(self, event): for e in self.forbidden_events: if e.match(event): raise ForbiddenEventOccurred(event) def expect(self, type, **kw): """ Waits for an event matching the supplied pattern to occur, and returns it. For example, to await a D-Bus signal with particular arguments: e = q.expect('dbus-signal', signal='Badgers', args=["foo", 42]) """ pattern = EventPattern(type, **kw) t = time.time() while True: event = self.wait([pattern.subqueue]) self._check_forbidden(event) if pattern.match(event): self.log('handled, took %0.3f ms' % ((time.time() - t) * 1000.0) ) self.log('') return event self.log('not handled') self.log('') def expect_many(self, *patterns): """ Waits for events matching all of the supplied EventPattern instances to return, and returns a list of events in the same order as the patterns they matched. After a pattern is successfully matched, it is not considered for future events; if more than one unsatisfied pattern matches an event, the first "wins". Note that the expected events may occur in any order. If you're expecting a series of events in a particular order, use repeated calls to expect() instead. This method is useful when you're awaiting a number of events which may happen in any order. For instance, in telepathy-gabble, calling a D-Bus method often causes a value to be returned immediately, as well as a query to be sent to the server. Since these events may reach the test in either order, the following is incorrect and will fail if the IQ happens to reach the test first: ret = q.expect('dbus-return', method='Foo') query = q.expect('stream-iq', query_ns=ns.FOO) The following would be correct: ret, query = q.expect_many( EventPattern('dbus-return', method='Foo'), EventPattern('stream-iq', query_ns=ns.FOO), ) """ ret = [None] * len(patterns) t = time.time() while None in ret: try: queues = set() for i, pattern in enumerate(patterns): if ret[i] is None: queues.add(pattern.subqueue) event = self.wait(queues) except TimeoutError: self.log('timeout') self.log('still expecting:') for i, pattern in enumerate(patterns): if ret[i] is None: self.log(' - %r' % pattern) raise self._check_forbidden(event) for i, pattern in enumerate(patterns): if ret[i] is None and pattern.match(event): self.log('handled, took %0.3f ms' % ((time.time() - t) * 1000.0) ) self.log('') ret[i] = event break else: self.log('not handled') self.log('') return ret def demand(self, type, **kw): pattern = EventPattern(type, **kw) event = self.wait([pattern.subqueue]) if pattern.match(event): self.log('handled') self.log('') return event self.log('not handled') raise RuntimeError('expected %r, got %r' % (pattern, event)) def queues_available(self, queues): if queues == None: return self.event_queues.keys() else: available = self.event_queues.keys() return filter(lambda x: x in available, queues) def pop_next(self, queue): events = self.event_queues[queue] e = events.pop(0) if not events: self.event_queues.pop (queue) return e def append(self, event): self.log ("Adding to queue") self.log_event (event) self.event_queues[event.subqueue] = \ self.event_queues.get(event.subqueue, []) + [event] class IteratingEventQueue(BaseEventQueue): """Event queue that works by iterating the Twisted reactor.""" def __init__(self, timeout=None): BaseEventQueue.__init__(self, timeout) def wait(self, queues=None): stop = [False] def later(): stop[0] = True delayed_call = reactor.callLater(self.timeout, later) self.log_queues(queues) qa = self.queues_available(queues) while not qa and (not stop[0]): reactor.iterate(0.01) qa = self.queues_available(queues) if qa: delayed_call.cancel() e = self.pop_next (qa[0]) self.log_event (e) return e else: raise TimeoutError class TestEventQueue(BaseEventQueue): def __init__(self, events): BaseEventQueue.__init__(self) for e in events: self.append (e) def wait(self, queues = None): qa = self.queues_available(queues) if qa: return self.pop_next (qa[0]) else: raise TimeoutError class EventQueueTest(unittest.TestCase): def test_expect(self): queue = TestEventQueue([Event('test-foo'), Event('test-bar')]) assert queue.expect('test-foo').type == 'test-foo' assert queue.expect('test-bar').type == 'test-bar' def test_expect_many(self): queue = TestEventQueue([Event('test-foo'), Event('test-bar')]) bar, foo = queue.expect_many( EventPattern('test-bar'), EventPattern('test-foo')) assert bar.type == 'test-bar' assert foo.type == 'test-foo' def test_expect_many2(self): # Test that events are only matched against patterns that haven't yet # been matched. This tests a regression. queue = TestEventQueue([Event('test-foo', x=1), Event('test-foo', x=2)]) foo1, foo2 = queue.expect_many( EventPattern('test-foo'), EventPattern('test-foo')) assert foo1.type == 'test-foo' and foo1.x == 1 assert foo2.type == 'test-foo' and foo2.x == 2 def test_expect_queueing(self): queue = TestEventQueue([Event('foo-test', x=1), Event('foo-test', x=2)]) queue.append(Event('bar-test', x=1)) queue.append(Event('bar-test', x=2)) queue.append(Event('baz-test', x=1)) queue.append(Event('baz-test', x=2)) for x in xrange(1,2): e = queue.expect ('baz-test') assertEquals (x, e.x) e = queue.expect ('bar-test') assertEquals (x, e.x) e = queue.expect ('foo-test') assertEquals (x, e.x) def test_timeout(self): queue = TestEventQueue([]) self.assertRaises(TimeoutError, queue.expect, 'test-foo') def test_demand(self): queue = TestEventQueue([Event('test-foo'), Event('test-bar')]) foo = queue.demand('test-foo') assert foo.type == 'test-foo' def test_demand_fail(self): queue = TestEventQueue([Event('test-foo'), Event('test-bar')]) self.assertRaises(RuntimeError, queue.demand, 'test-bar') def unwrap(x): """Hack to unwrap D-Bus values, so that they're easier to read when printed.""" if isinstance(x, list): return map(unwrap, x) if isinstance(x, tuple): return tuple(map(unwrap, x)) if isinstance(x, dict): return dict([(unwrap(k), unwrap(v)) for k, v in x.iteritems()]) if isinstance(x, dbus.Boolean): return bool(x) for t in [unicode, str, long, int, float]: if isinstance(x, t): return t(x) return x def call_async(test, proxy, method, *args, **kw): """Call a D-Bus method asynchronously and generate an event for the resulting method return/error.""" def reply_func(*ret): test.append(Event('dbus-return', method=method, value=unwrap(ret))) def error_func(err): test.append(Event('dbus-error', method=method, error=err, name=err.get_dbus_name(), message=str(err))) method_proxy = getattr(proxy, method) kw.update({'reply_handler': reply_func, 'error_handler': error_func}) method_proxy(*args, **kw) def sync_dbus(bus, q, conn): # Dummy D-Bus method call. We can't use DBus.Peer.Ping() because libdbus # replies to that message immediately, rather than handing it up to # dbus-glib and thence Gabble, which means that Ping()ing Gabble doesn't # ensure that it's processed all D-Bus messages prior to our ping. # # This won't do the right thing unless the proxy has a unique name. assert conn.object.bus_name.startswith(':') root_object = bus.get_object(conn.object.bus_name, '/', introspect=False) call_async(q, dbus.Interface(root_object, 'org.freedesktop.Telepathy.Tests'), 'DummySyncDBus') q.expect('dbus-error', method='DummySyncDBus') class ProxyWrapper: def __init__(self, object, default, others={}): self.object = object self.default_interface = dbus.Interface(object, default) self.Properties = dbus.Interface(object, dbus.PROPERTIES_IFACE) self.TpProperties = \ dbus.Interface(object, tp_name_prefix + '.Properties') self.interfaces = dict([ (name, dbus.Interface(object, iface)) for name, iface in others.iteritems()]) def __getattr__(self, name): if name in self.interfaces: return self.interfaces[name] if name in self.object.__dict__: return getattr(self.object, name) return getattr(self.default_interface, name) def wrap_connection(conn): return ProxyWrapper(conn, tp_name_prefix + '.Connection', dict([ (name, tp_name_prefix + '.Connection.Interface.' + name) for name in ['Aliasing', 'Avatars', 'Capabilities', 'Contacts', 'SimplePresence', 'Requests']] + [('Peer', 'org.freedesktop.DBus.Peer'), ('ContactCapabilities', cs.CONN_IFACE_CONTACT_CAPS), ('ContactInfo', cs.CONN_IFACE_CONTACT_INFO), ('Location', cs.CONN_IFACE_LOCATION), ('Future', tp_name_prefix + '.Connection.FUTURE'), ('MailNotification', cs.CONN_IFACE_MAIL_NOTIFICATION), ('ContactList', cs.CONN_IFACE_CONTACT_LIST), ('ContactGroups', cs.CONN_IFACE_CONTACT_GROUPS), ('PowerSaving', cs.CONN_IFACE_POWER_SAVING), ('Addressing', cs.CONN_IFACE_ADDRESSING), ])) def wrap_channel(chan, type_, extra=None): interfaces = { type_: tp_name_prefix + '.Channel.Type.' + type_, 'Group': tp_name_prefix + '.Channel.Interface.Group', } if extra: interfaces.update(dict([ (name, tp_name_prefix + '.Channel.Interface.' + name) for name in extra])) return ProxyWrapper(chan, tp_name_prefix + '.Channel', interfaces) def wrap_content(chan, extra=None): interfaces = { } if extra: interfaces.update(dict([ (name, tp_name_prefix + '.Call1.Content.Interface.' + name) for name in extra])) return ProxyWrapper(chan, tp_name_prefix + '.Call1.Content', interfaces) def make_connection(bus, event_func, name, proto, params): cm = bus.get_object( tp_name_prefix + '.ConnectionManager.%s' % name, tp_path_prefix + '/ConnectionManager/%s' % name, introspect=False) cm_iface = dbus.Interface(cm, tp_name_prefix + '.ConnectionManager') connection_name, connection_path = cm_iface.RequestConnection( proto, dbus.Dictionary(params, signature='sv')) conn = wrap_connection(bus.get_object(connection_name, connection_path)) return conn def make_channel_proxy(conn, path, iface): bus = dbus.SessionBus() chan = bus.get_object(conn.object.bus_name, path) chan = dbus.Interface(chan, tp_name_prefix + '.' + iface) return chan # block_reading can be used if the test want to choose when we start to read # data from the socket. class EventProtocol(Protocol): def __init__(self, queue=None, block_reading=False): self.queue = queue self.block_reading = block_reading def dataReceived(self, data): if self.queue is not None: self.queue.append(Event('socket-data', protocol=self, data=data)) def sendData(self, data): self.transport.write(data) def connectionMade(self): if self.block_reading: self.transport.stopReading() def connectionLost(self, reason=None): if self.queue is not None: self.queue.append(Event('socket-disconnected', protocol=self)) class EventProtocolFactory(Factory): def __init__(self, queue, block_reading=False): self.queue = queue self.block_reading = block_reading def _create_protocol(self): return EventProtocol(self.queue, self.block_reading) def buildProtocol(self, addr): proto = self._create_protocol() self.queue.append(Event('socket-connected', protocol=proto)) return proto class EventProtocolClientFactory(EventProtocolFactory, ClientFactory): pass def watch_tube_signals(q, tube): def got_signal_cb(*args, **kwargs): q.append(Event('tube-signal', path=kwargs['path'], signal=kwargs['member'], args=map(unwrap, args), tube=tube)) tube.add_signal_receiver(got_signal_cb, path_keyword='path', member_keyword='member', byte_arrays=True) def pretty(x): return pprint.pformat(unwrap(x)) def assertEquals(expected, value): if expected != value: raise AssertionError( "expected:\n%s\ngot:\n%s" % (pretty(expected), pretty(value))) def assertSameSets(expected, value): exp_set = set(expected) val_set = set(value) if exp_set != val_set: raise AssertionError( "expected contents:\n%s\ngot:\n%s" % ( pretty(exp_set), pretty(val_set))) def assertNotEquals(expected, value): if expected == value: raise AssertionError( "expected something other than:\n%s" % pretty(value)) def assertContains(element, value): if element not in value: raise AssertionError( "expected:\n%s\nin:\n%s" % (pretty(element), pretty(value))) def assertDoesNotContain(element, value): if element in value: raise AssertionError( "expected:\n%s\nnot in:\n%s" % (pretty(element), pretty(value))) def assertLength(length, value): if len(value) != length: raise AssertionError("expected: length %d, got length %d:\n%s" % ( length, len(value), pretty(value))) def assertFlagsSet(flags, value): masked = value & flags if masked != flags: raise AssertionError( "expected flags %u, of which only %u are set in %u" % ( flags, masked, value)) def assertFlagsUnset(flags, value): masked = value & flags if masked != 0: raise AssertionError( "expected none of flags %u, but %u are set in %u" % ( flags, masked, value)) def assertDBusError(name, error): if error.get_dbus_name() != name: raise AssertionError( "expected DBus error named:\n %s\ngot:\n %s\n(with message: %s)" % (name, error.get_dbus_name(), error.message)) def install_colourer(): def red(s): return '\x1b[31m%s\x1b[0m' % s def green(s): return '\x1b[32m%s\x1b[0m' % s patterns = { 'handled': green, 'not handled': red, } class Colourer: def __init__(self, fh, patterns): self.fh = fh self.patterns = patterns def write(self, s): for p, f in self.patterns.items(): if s.startswith(p): self.fh.write(f(p) + s[len(p):]) return self.fh.write(s) sys.stdout = Colourer(sys.stdout, patterns) return sys.stdout # this is just to shut up unittest. class DummyStream(object): def write(self, s): if 'CHECK_TWISTED_VERBOSE' in os.environ: print s, def flush(self): pass if __name__ == '__main__': stream = DummyStream() runner = unittest.TextTestRunner(stream=stream) unittest.main(testRunner=runner) telepathy-gabble-0.18.2/tests/twisted/pubsub.py0000644000175000017500000000616312227000321021507 0ustar00smcvsmcv00000000000000"""Send malformed pubsub notifications to be sure that Gabble isn't confused about those""" from gabbletest import exec_test, elem, sync_stream import constants as cs import ns def make_pubsub_event(from_, node, *contents): return elem('message', from_=from_)( elem((ns.PUBSUB_EVENT), 'event')( elem('items', node=node)( elem('item')( *contents ) ) ) ) def test(q, bus, conn, stream): # event node without NS message = elem('message', from_='bob@foo.com')( elem('event')( elem('items', node=ns.GEOLOC)( elem('item', id='12345')( elem(ns.GEOLOC, 'geoloc')( elem ('country') (u'France')))))) stream.send(message) # event node with a wrong NS message = elem('message', from_='bob@foo.com')( elem('badger', 'event')( elem('items', node=ns.GEOLOC)( elem('item', id='12345')( elem(ns.GEOLOC, 'geoloc')( elem ('country') (u'France')))))) stream.send(message) # event node without 'from' message = elem('message')( elem((ns.PUBSUB_EVENT), 'event')( elem('items', node=ns.GEOLOC)( elem('item', id='12345')( elem(ns.GEOLOC, 'geoloc')( elem ('country') (u'France')))))) stream.send(message) # event node with an invalid 'from' message = elem('message', from_='aaaa')( elem((ns.PUBSUB_EVENT), 'event')( elem('items', node=ns.GEOLOC)( elem('item', id='12345')( elem(ns.GEOLOC, 'geoloc')( elem ('country') (u'France')))))) stream.send(message) # no items node message = elem('message', from_='bob@foo.com')( elem((ns.PUBSUB_EVENT), 'event')()) stream.send(message) # no item node message = elem('message', from_='bob@foo.com')( elem((ns.PUBSUB_EVENT), 'event')( elem('items', node=ns.GEOLOC)())) stream.send(message) # item node doesn't have any child message = elem('message', from_='bob@foo.com')( elem((ns.PUBSUB_EVENT), 'event')( elem('items', node=ns.GEOLOC)( elem('item', id='12345')()))) stream.send(message) # the child of the item node doesn't have a NS message = elem('message', from_='bob@foo.com')( elem((ns.PUBSUB_EVENT), 'event')( elem('items', node=ns.GEOLOC)( elem('item', id='12345')( elem('geoloc')( elem ('country') (u'France')))))) stream.send(message) # valid but unknown pubsub notification message = elem('message', from_='bob@foo.com')( elem((ns.PUBSUB_EVENT), 'event')( elem('items', node='http://www.badger.com')( elem('item', id='12345')( elem('http://www.badger.com', 'badger')( elem ('mushroom') (u'snake')))))) stream.send(message) sync_stream(q, stream) if __name__ == '__main__': exec_test(test) telepathy-gabble-0.18.2/tests/twisted/power-save.py0000644000175000017500000001676212227000321022305 0ustar00smcvsmcv00000000000000""" Test entering and leaving power saving mode. """ import config import constants as cs from gabbletest import exec_test, GoogleXmlStream, make_result_iq, \ send_error_reply, disconnect_conn, make_presence, sync_stream, elem, \ acknowledge_iq from servicetest import call_async, Event, assertEquals, EventPattern, \ assertContains, assertDoesNotContain, sync_dbus import ns import dbus import dbus.service from twisted.internet import reactor from twisted.words.xish import domish def expect_command(q, name): event = q.expect('stream-iq', query_name='query', query_ns=ns.GOOGLE_QUEUE) # Regression test: when we split MCE out of Gabble and moved Gabble's code # to conn-power-saving, this erroneously became a 'get'. assertEquals('set', event.iq_type) command = event.query.firstChildElement() assertEquals(name, command.name) return event.stanza def test_error(q, bus, conn, stream): assertContains(cs.CONN_IFACE_POWER_SAVING, conn.Get(cs.CONN, "Interfaces", dbus_interface=cs.PROPERTIES_IFACE)) assertEquals (False, conn.Get(cs.CONN_IFACE_POWER_SAVING, "PowerSavingActive", dbus_interface=cs.PROPERTIES_IFACE)) call_async(q, conn.PowerSaving, 'SetPowerSaving', True) stanza = expect_command(q, 'enable') error = domish.Element((None, 'error')) error.addElement((ns.STANZA, 'service-unavailable')) send_error_reply(stream, stanza, error) q.expect('dbus-error', method='SetPowerSaving', name=cs.NOT_AVAILABLE) # Power saving state should remain false assertEquals (False, conn.Get(cs.CONN_IFACE_POWER_SAVING, "PowerSavingActive", dbus_interface=cs.PROPERTIES_IFACE)) def test_local_queueing(q, bus, conn, stream): assertContains(cs.CONN_IFACE_POWER_SAVING, conn.Get(cs.CONN, "Interfaces", dbus_interface=cs.PROPERTIES_IFACE)) assertEquals (False, conn.Get(cs.CONN_IFACE_POWER_SAVING, "PowerSavingActive", dbus_interface=cs.PROPERTIES_IFACE)) event = q.expect('stream-iq', to=None, query_ns='vcard-temp', query_name='vCard') acknowledge_iq(stream, event.stanza) presence_update = [EventPattern('dbus-signal', signal='PresencesChanged')] q.forbid_events(presence_update) call_async(q, conn.PowerSaving, 'SetPowerSaving', True) q.expect_many(EventPattern('dbus-return', method='SetPowerSaving'), EventPattern('dbus-signal', signal='PowerSavingChanged', args=[True])) assertEquals (True, conn.Get(cs.CONN_IFACE_POWER_SAVING, "PowerSavingActive", dbus_interface=cs.PROPERTIES_IFACE)) # These presence stanzas should be queued stream.send(make_presence('amy@foo.com', show='away', status='At the pub')) stream.send(make_presence('bob@foo.com', show='xa', status='Somewhere over the rainbow')) # Pep notifications too message = elem('message', from_='bob@foo.com')( elem((ns.PUBSUB_EVENT), 'event')( elem('items', node=ns.NICK)( elem('item')( elem(ns.NICK, 'nick')(u'Robert') ) ) ) ) stream.send(message.toXml()) sync_dbus(bus, q, conn) q.unforbid_events(presence_update) # Incoming important stanza will flush the queue m = domish.Element((None, 'message')) m['from'] = 'foo@bar.com/Pidgin' m['id'] = '123' m['type'] = 'chat' m.addElement('body', content='important message') stream.send(m) # Presence updates should come in the original order ... p1 = q.expect('dbus-signal', signal='PresencesChanged') p2 = q.expect('dbus-signal', signal='PresencesChanged') assertEquals('away', p1.args[0].values()[0][1]) assertEquals('xa', p2.args[0].values()[0][1]) # .. followed by the result of PEP notification .. event = q.expect('dbus-signal', signal='AliasesChanged') # .. and finally the message that flushed the stanza queue q.expect('dbus-signal', signal='NewChannel') sync_stream(q, stream) q.forbid_events(presence_update) stream.send(make_presence('carl@foo.com', show='away', status='Home')) # Carl's presence update is queued sync_dbus(bus, q, conn) q.unforbid_events(presence_update) # Disable powersaving, flushing the queue conn.PowerSaving.SetPowerSaving(False) q.expect('dbus-signal', signal='PresencesChanged') def test(q, bus, conn, stream): assertContains(cs.CONN_IFACE_POWER_SAVING, conn.Get(cs.CONN, "Interfaces", dbus_interface=cs.PROPERTIES_IFACE)) assertEquals (False, conn.Get(cs.CONN_IFACE_POWER_SAVING, "PowerSavingActive", dbus_interface=cs.PROPERTIES_IFACE)) call_async(q, conn.PowerSaving, 'SetPowerSaving', True) stanza = expect_command(q, 'enable') stream.send(make_result_iq(stream, stanza, False)) q.expect_many(EventPattern('dbus-return', method='SetPowerSaving'), EventPattern('dbus-signal', signal='PowerSavingChanged', args=[True])) assertEquals (True, conn.Get(cs.CONN_IFACE_POWER_SAVING, "PowerSavingActive", dbus_interface=cs.PROPERTIES_IFACE)) pattern = [EventPattern('stream-iq', query_name='query', query_ns=ns.GOOGLE_QUEUE), EventPattern('dbus-signal', signal='PowerSavingChanged', args=[True])] q.forbid_events(pattern) # Enabling power saving again should be a no-op. call_async(q, conn.PowerSaving, 'SetPowerSaving', True) q.expect('dbus-return', method='SetPowerSaving') q.unforbid_events(pattern) call_async(q, conn.PowerSaving, 'SetPowerSaving', False) stanza = expect_command(q, 'disable') stream.send(make_result_iq(stream, stanza, False)) event, _, _ = q.expect_many( EventPattern('stream-iq', query_name='query', query_ns=ns.GOOGLE_QUEUE), EventPattern('dbus-return', method='SetPowerSaving'), EventPattern('dbus-signal', signal='PowerSavingChanged', args=[False])) command = event.query.firstChildElement() assertEquals("flush", command.name) assertEquals (False, conn.Get(cs.CONN_IFACE_POWER_SAVING, "PowerSavingActive", dbus_interface=cs.PROPERTIES_IFACE)) def test_disconnect(q, bus, conn, stream): assertContains(cs.CONN_IFACE_POWER_SAVING, conn.Get(cs.CONN, "Interfaces", dbus_interface=cs.PROPERTIES_IFACE)) assertEquals (False, conn.Get(cs.CONN_IFACE_POWER_SAVING, "PowerSavingActive", dbus_interface=cs.PROPERTIES_IFACE)) call_async(q, conn.PowerSaving, 'SetPowerSaving', True) stanza = expect_command(q, 'enable') disconnect_conn(q, conn, stream) if __name__ == '__main__': exec_test(test, protocol=GoogleXmlStream) exec_test(test_local_queueing) exec_test(test_error, protocol=GoogleXmlStream) exec_test(test_disconnect, protocol=GoogleXmlStream) telepathy-gabble-0.18.2/tests/twisted/plugin-channel-managers.py0000644000175000017500000000172712227000321024707 0ustar00smcvsmcv00000000000000""" Test Gabble's implementation of channel managers from plugins. """ from servicetest import ( sync_dbus, call_async, EventPattern, assertEquals, assertContains, ) from gabbletest import exec_test, send_error_reply, acknowledge_iq, sync_stream import constants as cs from config import PLUGINS_ENABLED TEST_PLUGIN_IFACE = "org.freedesktop.Telepathy.Gabble.Plugin.Test" if not PLUGINS_ENABLED: print "NOTE: built without --enable-plugins, not testing plugins" raise SystemExit(77) # which makes the test show up as skipped def test(q, bus, conn, stream): rccs = conn.Properties.Get(cs.CONN_IFACE_REQUESTS, 'RequestableChannelClasses') # These values are from plugins/test.c fixed = { cs.CHANNEL_TYPE: "com.jonnylamb.lolbags", cs.TARGET_HANDLE_TYPE: cs.HT_NONE, } allowed = ["com.jonnylamb.omg", "com.jonnylamb.brokethebuild"] assertContains((fixed, allowed), rccs) if __name__ == '__main__': exec_test(test) telepathy-gabble-0.18.2/tests/twisted/pep-support.py0000644000175000017500000000522112227000321022477 0ustar00smcvsmcv00000000000000import dbus from gabbletest import exec_test, GoogleXmlStream, acknowledge_iq, BaseXmlStream,\ sync_stream from servicetest import call_async, EventPattern from twisted.words.protocols.jabber.client import IQ from twisted.words.xish import xpath import constants as cs import ns # PEP supports is advertised in Server's disco which is wrong but that's what # old ejabberd used to do. class PepInServerDiscoXmlStream(BaseXmlStream): version = (1, 0) def _cb_disco_iq(self, iq): # Advertise PEP support in server disco rather than when discoing our # bare JID nodes = xpath.queryForNodes( "/iq/query[@xmlns='http://jabber.org/protocol/disco#info']", iq) query = nodes[0] identity = query.addElement('identity') identity['category'] = 'pubsub' identity['type'] = 'pep' iq['type'] = 'result' iq['from'] = 'localhost' self.send(iq) def _cb_bare_jid_disco_iq(self, iq): # Additionally, Prosody 0.6.1 doesn't like us discoing our own bare # JID, and responds with an error which doesn't have the 'from' # attribute. Wocky used to discard this, but now tolerates it. result = IQ(self, 'error') result['id'] = iq['id'] error = result.addElement((None, 'error')) error['type'] = 'cancel' error.addElement((ns.STANZA, 'service-unavailable')) self.send(result) def test_legacy(q, bus, conn, stream): call_async(q, conn.Location, 'SetLocation', { 'lat': 0.0, 'lon': 0.0}) geoloc_iq_set_event = EventPattern('stream-iq', predicate=lambda x: xpath.queryForNodes("/iq/pubsub/publish/item/geoloc", x.stanza)) event = q.expect_many(geoloc_iq_set_event)[0] acknowledge_iq(stream, event.stanza) q.expect('dbus-return', method='SetLocation') # PEP is not supported. def test_no_pep(q, bus, conn, stream): call_async(q, conn.Location, 'SetLocation', { 'lat': 0.0, 'lon': 0.0}) q.expect('dbus-error', name=cs.NOT_IMPLEMENTED) #PEP is advertised using the right protocol def test_pep(q, bus, conn, stream): call_async(q, conn.Location, 'SetLocation', { 'lat': 0.0, 'lon': 0.0}) geoloc_iq_set_event = EventPattern('stream-iq', predicate=lambda x: xpath.queryForNodes("/iq/pubsub/publish/item/geoloc", x.stanza)) event = q.expect_many(geoloc_iq_set_event)[0] acknowledge_iq(stream, event.stanza) q.expect('dbus-return', method='SetLocation') if __name__ == '__main__': exec_test(test_legacy, protocol=PepInServerDiscoXmlStream) exec_test(test_no_pep, protocol=GoogleXmlStream) exec_test(test_pep) telepathy-gabble-0.18.2/tests/twisted/mail-notification.py0000644000175000017500000003031712227000321023613 0ustar00smcvsmcv00000000000000""" Test Connection.Interface.MailNotification """ from twisted.words.xish import domish from gabbletest import exec_test, make_result_iq, GoogleXmlStream from servicetest import EventPattern import constants as cs import ns import dbus def check_properties_empty(conn, expected_flags=0): """Check that all mail notification properties are empty and that mail notification flags match the expected flags""" flags = conn.Get( cs.CONN_IFACE_MAIL_NOTIFICATION, 'MailNotificationFlags', dbus_interface=cs.PROPERTIES_IFACE) assert flags == expected_flags mail_count = conn.Get( cs.CONN_IFACE_MAIL_NOTIFICATION, 'UnreadMailCount', dbus_interface=cs.PROPERTIES_IFACE) assert mail_count == 0 unread_mails = conn.Get( cs.CONN_IFACE_MAIL_NOTIFICATION, 'UnreadMails', dbus_interface=cs.PROPERTIES_IFACE) assert len(unread_mails) == 0 def test_google_featured(q, bus, conn, stream): """Test functionnality when google mail notification is supported""" inbox_url = 'http://mail.google.com/mail' # E-mail thread 1 data thread1_id = "1" # Dates are 32bit unsigned integers, let's use the biggest possible value thread1_date = (pow(2,32) - 1) * 1000L thread1_url = 'http://mail.google.com/mail/#inbox/%x' % long(thread1_id) thread1_senders = [('John Smith', 'john@smith.com'), ('Denis Tremblay', 'denis@tremblay.qc.ca')] thread1_subject = "subject1" thread1_snippet = "body1" # Email thread 2 data thread2_id = "2" thread2_date = 1234L thread2_url = 'http://mail.google.com/mail/#inbox/%x' % long(thread2_id) thread2_senders = [('Sam Gratte', 'sam@gratte.edu'),] thread2_subject = "subject2" thread2_snippet = "body2" # Email thread 3 data thread3_id = "3" thread3_date = 1235L thread3_url = 'http://mail.google.com/mail/#inbox/%x' % long(thread3_id) thread3_senders = [('Le Chat', 'le@chat.fr'),] thread3_subject = "subject3" thread3_snippet = "body3" # Supported mail notification flags Supports_Unread_Mail_Count = 1 Supports_Unread_Mails = 2 Supports_Request_Inbox_URL = 8 Supports_Request_Mail_URL = 16 Thread_Based = 32 expected_flags = Supports_Unread_Mail_Count\ | Supports_Unread_Mails\ | Supports_Request_Inbox_URL\ | Supports_Request_Mail_URL\ | Thread_Based # Nobody is subscribed yet, attributes should all be empty, and # mail notification flags are set properly. check_properties_empty(conn, expected_flags) # Check that Gabble queries mail data on initial interest. conn.AddClientInterest([cs.CONN_IFACE_MAIL_NOTIFICATION]) event = q.expect('stream-iq', query_ns=ns.GOOGLE_MAIL_NOTIFY) result = make_result_iq(stream, event.stanza, False) mailbox = result.addElement('mailbox') mailbox['xmlns'] = ns.GOOGLE_MAIL_NOTIFY mailbox['url'] = inbox_url # Set e-mail thread 1 mail = mailbox.addElement('mail-thread-info') mail['tid'] = thread1_id mail['date'] = str(thread1_date) senders = mail.addElement('senders') for t1_sender in thread1_senders: sender = senders.addElement('sender') sender['name'] = t1_sender[0] sender['address'] = t1_sender[1] sender['unread'] = '1' mail.addElement('subject', content=thread1_subject) mail.addElement('snippet', content=thread1_snippet) # Set e-mail thread 2 mail = mailbox.addElement('mail-thread-info') mail['tid'] = thread2_id mail['date'] = str(thread2_date) senders = mail.addElement('senders') for t2_sender in thread2_senders: sender = senders.addElement('sender') sender['name'] = t2_sender[0] sender['address'] = t2_sender[1] sender['unread'] = '1' sender = senders.addElement('sender') sender['name'] = 'Read Sender' sender['address'] = 'read@sender.net' mail.addElement('subject', content=thread2_subject) mail.addElement('snippet', content=thread2_snippet) stream.send(result) # Then we expect UnreadMailsChanged with all the mail information. event = q.expect('dbus-signal', signal="UnreadMailsChanged") # Check that inbox URL is correct stored_url = conn.MailNotification.RequestInboxURL() assert stored_url[0] == inbox_url assert stored_url[1] == 0 # HTTP GET assert len(stored_url[2]) == 0 # UnreadMailsChanged(u: count, aa{sv}: mails_added, ax: mails_removed) unread_count = event.args[0] mails_added = event.args[1] mails_removed = event.args[2] # Get stored data to check we have same thing stored_unread_count = conn.Get( cs.CONN_IFACE_MAIL_NOTIFICATION, 'UnreadMailCount', dbus_interface=cs.PROPERTIES_IFACE) stored_unread_mails = conn.Get( cs.CONN_IFACE_MAIL_NOTIFICATION, 'UnreadMails', dbus_interface=cs.PROPERTIES_IFACE) assert unread_count == 2 assert stored_unread_count == unread_count assert len(stored_unread_mails) == unread_count assert len(mails_added) == unread_count assert len(mails_removed) == 0 # Extract mails from signal, order is unknown mail1 = None mail2 = None for mail in mails_added: if mail['id'] == thread1_id: mail1 = mail elif mail['id'] == thread2_id: mail2 = mail else: assert False, "Gabble sent an unknown mail id=" + str(mail['id']) # Validate added e-mails with original data. assert mail1 != None # While date is in millisecond, the received timestamp is in seconds thus # we need to divided by 1000 assert mail1['received-timestamp'] == thread1_date / 1000 assert mail1['subject'] == thread1_subject assert mail1['truncated'] == True assert mail1['content'] == thread1_snippet assert mail1['senders'] == thread1_senders assert mail2 != None assert mail2['received-timestamp'] == thread2_date / 1000 assert mail2['subject'] == thread2_subject assert mail2['truncated'] == True assert mail2['content'] == thread2_snippet assert mail2['senders'] == thread2_senders # Extract mails from stored mails, order is unkown stored_mail1 = None stored_mail2 = None for mail in stored_unread_mails: if mail['id'] == thread1_id: stored_mail1 = mail elif mail['id'] == thread2_id: stored_mail2 = mail else: assert False, "Gabble stored an unkown mail id=" + str(mail['id']) # Validate stored e-mails with original data assert stored_mail1 != None assert stored_mail1['received-timestamp'] == thread1_date / 1000 assert stored_mail1['subject'] == thread1_subject assert stored_mail1['truncated'] == True assert stored_mail1['content'] == thread1_snippet assert stored_mail1['senders'] == thread1_senders assert stored_mail2 != None assert stored_mail2['received-timestamp'] == thread2_date / 1000 assert stored_mail2['subject'] == thread2_subject assert stored_mail2['truncated'] == True assert stored_mail2['content'] == thread2_snippet assert stored_mail2['senders'] == thread2_senders # Check the we can get an URL for a specific mail mail_url1 = conn.MailNotification.RequestMailURL( stored_mail1['id'], stored_mail1['url-data']); mail_url2 = conn.MailNotification.RequestMailURL( stored_mail2['id'], stored_mail2['url-data']); assert mail_url1[0] == thread1_url assert mail_url1[1] == 0 assert len(mail_url1[2]) == 0 assert mail_url2[0] == thread2_url assert mail_url2[1] == 0 assert len(mail_url2[2]) == 0 # Now we want to validate the update mechanism. Thus we wil send an # new-mail event, wait for gabble to query the latest mail and reply # a different list. m = domish.Element((None, 'iq')) m['type'] = 'set' m['from'] = 'test@localhost' m['id'] = '3' m.addElement((ns.GOOGLE_MAIL_NOTIFY, 'new-mail')) stream.send(m) # Wait for mail information request event = q.expect('stream-iq', query_ns=ns.GOOGLE_MAIL_NOTIFY) result = make_result_iq(stream, event.stanza, False) mailbox = result.addElement('mailbox') mailbox['xmlns'] = ns.GOOGLE_MAIL_NOTIFY # We alter the URL to see if it gets detected mailbox['url'] = inbox_url + 'diff' # Set e-mail thread 1 and change snippet to see if it's detected mail = mailbox.addElement('mail-thread-info') mail['tid'] = str(thread1_id) mail['date'] = str(thread1_date) senders = mail.addElement('senders') for t1_sender in thread1_senders: sender = senders.addElement('sender') sender['name'] = t1_sender[0] sender['address'] = t1_sender[1] sender['unread'] = '1' mail.addElement('subject', content=thread1_subject) mail.addElement('snippet', content=thread1_snippet + 'diff') # We don't set the thread 2, as if it was removed # Set e-mail thread 3 mail = mailbox.addElement('mail-thread-info') mail['tid'] = str(thread3_id) mail['date'] = str(thread3_date) senders = mail.addElement('senders') for t3_sender in thread3_senders: sender = senders.addElement('sender') sender['name'] = t3_sender[0] sender['address'] = t3_sender[1] sender['unread'] = '1' mail.addElement('subject', content=thread3_subject) mail.addElement('snippet', content=thread3_snippet) stream.send(result) event = q.expect('dbus-signal', signal='UnreadMailsChanged') unread_count = event.args[0] mails_added = event.args[1] mails_removed = event.args[2] # Validate that changed is set for correct items assert unread_count == 2 assert len(mails_added) == 2 assert mails_added[0]['id'] in (thread1_id, thread3_id) assert mails_added[1]['id'] in (thread1_id, thread3_id) assert mails_added[0]['id'] != mails_added[1]['id'] assert len(mails_removed) == 1 assert mails_removed[0] == thread2_id # Check attribue MailAddres mail_address = conn.Get( cs.CONN_IFACE_MAIL_NOTIFICATION, 'MailAddress', dbus_interface=cs.PROPERTIES_IFACE) assert mail_address == "test@localhost" # Unsubscribe and check that all data has been dropped conn.RemoveClientInterest([cs.CONN_IFACE_MAIL_NOTIFICATION]) check_properties_empty(conn, expected_flags) def test_no_google_featured(q, bus, conn, stream): """Check that Gabble reacts correctly when called on MailNotification while the feature is not supported.""" # Google mail notification is not supported, gabble should not emit any # signals. forbidden = [EventPattern('dbus-signal', signal='MailsReceived'), EventPattern('dbus-signal', signal='UnreadMailsChanged'), EventPattern('stream-iq', query_ns=ns.GOOGLE_MAIL_NOTIFY)] q.forbid_events(forbidden) # Make sure gabble does not query mail data on an unexpected new-mail # notification. m = domish.Element((None, 'iq')) m['type'] = 'set' m['from'] = 'test@localhost' m['id'] = '2' m.addElement((ns.GOOGLE_MAIL_NOTIFY, 'new-mail')) stream.send(m) # AddClientInterest and RemoveClientInterest always trivially "succeed" conn.AddClientInterest([cs.CONN_IFACE_MAIL_NOTIFICATION]) conn.RemoveClientInterest([cs.CONN_IFACE_MAIL_NOTIFICATION]) try: conn.MailNotification.RequestInboxURL() except dbus.DBusException, e: assert e.get_dbus_name() == cs.NOT_IMPLEMENTED try: conn.MailNotification.RequestMailURL("1", "http://test.com/mail") except dbus.DBusException, e: assert e.get_dbus_name() == cs.NOT_IMPLEMENTED # Make sure all properties return with empty or 0 data including # MailNotificationFlags check_properties_empty(conn) q.unforbid_events(forbidden) def test(q, bus, conn, stream): interfaces = conn.GetInterfaces() if stream.__class__ is GoogleXmlStream: assert cs.CONN_IFACE_MAIL_NOTIFICATION in interfaces test_google_featured(q, bus, conn, stream) else: assert cs.CONN_IFACE_MAIL_NOTIFICATION not in interfaces test_no_google_featured(q, bus, conn, stream) if __name__ == '__main__': exec_test(test, protocol=GoogleXmlStream) exec_test(test) telepathy-gabble-0.18.2/tests/twisted/last-activity.py0000644000175000017500000000262112227000321022777 0ustar00smcvsmcv00000000000000""" Trivial smoke-test for XEP-0012 support. """ from servicetest import assertEquals, assertContains from gabbletest import exec_test, elem, elem_iq import ns def test(q, bus, conn, stream): e = q.expect('stream-iq', iq_type='get', query_ns=ns.ROSTER) e.stanza['type'] = 'result' e.query.addChild( elem('item', jid='romeo@montague.lit', subscription='both')) stream.send(e.stanza) # Romeo's on the roster. stream.send( elem_iq(stream, 'get', from_='romeo@montague.lit')( elem(ns.LAST, 'query') ) ) e = q.expect('stream-iq', iq_type='result', query_ns=ns.LAST, query_name='query') # No real assertions about the number of seconds; this is just a smoke # test. seconds = e.query['seconds'] assert seconds >= 0 # Juliet is not. stream.send( elem_iq(stream, 'get', from_='juliet@capulet.lit')( elem(ns.LAST, 'query') ) ) e = q.expect('stream-iq', iq_type='error', query_ns=ns.LAST, query_name='query') # Yuck. assertEquals('forbidden', e.stanza.children[1].children[0].name) # If the server asks, Gabble had better not crash. stream.send( elem_iq(stream, 'get')( elem(ns.LAST, 'query') ) ) e = q.expect('stream-iq', iq_type='result', query_ns=ns.LAST, query_name='query') if __name__ == '__main__': exec_test(test) telepathy-gabble-0.18.2/tests/twisted/gateways.py0000644000175000017500000000757612227000321022044 0ustar00smcvsmcv00000000000000""" Test the gateways plugin """ import dbus from twisted.words.xish import domish, xpath from servicetest import ( sync_dbus, call_async, EventPattern, assertEquals, assertContains, ) from gabbletest import ( exec_test, send_error_reply, acknowledge_iq, sync_stream, make_presence, ) import constants as cs import ns from config import PLUGINS_ENABLED PLUGIN_IFACE = "org.freedesktop.Telepathy.Gabble.Plugin.Gateways" if not PLUGINS_ENABLED: print "NOTE: built without --enable-plugins, not testing plugins" raise SystemExit(77) def test_success(q, gateways_iface, stream): call_async(q, gateways_iface, 'Register', 'talkd.example.com', '1970', 's3kr1t') e = q.expect('stream-iq', iq_type='set', query_name='query', query_ns=ns.REGISTER, to='talkd.example.com') assertEquals('1970', xpath.queryForString('/query/username', e.query)) assertEquals('s3kr1t', xpath.queryForString('/query/password', e.query)) acknowledge_iq(stream, e.stanza) q.expect_many( EventPattern('dbus-return', method='Register'), EventPattern('stream-presence', presence_type='subscribe', to='talkd.example.com'), ) stream.send(make_presence('talkd.example.com', type='subscribed')) def test_conflict(q, gateways_iface, stream): call_async(q, gateways_iface, 'Register', 'sip.example.com', '8675309', 'jenny') e = q.expect('stream-iq', iq_type='set', query_name='query', query_ns=ns.REGISTER, to='sip.example.com') assertEquals('8675309', xpath.queryForString('/query/username', e.query)) assertEquals('jenny', xpath.queryForString('/query/password', e.query)) error = domish.Element((None, 'error')) error['type'] = 'cancel' error['code'] = '409' error.addElement((ns.STANZA, 'conflict')) send_error_reply(stream, e.stanza, error) q.expect('dbus-error', method='Register', name=cs.REGISTRATION_EXISTS) def test_not_acceptable(q, gateways_iface, stream): call_async(q, gateways_iface, 'Register', 'fully-captcha-enabled.example.com', 'lalala', 'stoats') e = q.expect('stream-iq', iq_type='set', query_name='query', query_ns=ns.REGISTER, to='fully-captcha-enabled.example.com') assertEquals('lalala', xpath.queryForString('/query/username', e.query)) assertEquals('stoats', xpath.queryForString('/query/password', e.query)) error = domish.Element((None, 'error')) error['type'] = 'modify' error['code'] = '406' error.addElement((ns.STANZA, 'not-acceptable')) send_error_reply(stream, e.stanza, error) q.expect('dbus-error', method='Register', name=cs.NOT_AVAILABLE) def test(q, bus, conn, stream): # Request a sidecar thate we support before we're connected; it should just # wait around until we're connected. call_async(q, conn.Future, 'EnsureSidecar', PLUGIN_IFACE) conn.Connect() q.expect('dbus-signal', signal='StatusChanged', args=[cs.CONN_STATUS_CONNECTED, cs.CSR_REQUESTED]) # Now we're connected, the call we made earlier should return. path, props = q.expect('dbus-return', method='EnsureSidecar').value # This sidecar doesn't even implement get_immutable_properties; it # should just get the empty dict filled in for it. assertEquals({}, props) gateways_iface = dbus.Interface(bus.get_object(conn.bus_name, path), PLUGIN_IFACE) test_success(q, gateways_iface, stream) test_conflict(q, gateways_iface, stream) test_not_acceptable(q, gateways_iface, stream) call_async(q, conn, 'Disconnect') q.expect_many( EventPattern('dbus-signal', signal='StatusChanged', args=[cs.CONN_STATUS_DISCONNECTED, cs.CSR_REQUESTED]), EventPattern('stream-closed'), ) stream.sendFooter() q.expect('dbus-return', method='Disconnect') if __name__ == '__main__': exec_test(test, do_connect=False) telepathy-gabble-0.18.2/tests/twisted/dataforms.py0000644000175000017500000000245612227000321022170 0ustar00smcvsmcv00000000000000""" Test dataforms """ from servicetest import sync_dbus, assertEquals from gabbletest import exec_test, sync_stream import ns import constants as cs from caps_helper import receive_presence_and_ask_caps from config import VOIP_ENABLED # For historical reasons we advertise some bonus caps until the first call # to UpdateCapabilities or AdvertiseCapabilities. These caps are all related # to VoIP, so disabling VoIP breaks the assumptions this test is based on. if not VOIP_ENABLED: print "NOTE: built with --disable-voip" raise SystemExit(77) def test(q, bus, conn, stream): q.expect('stream-presence') # gabble won't try to represent the client if doesn't have any # RCCs or HCTs, so let's add some conn.ContactCapabilities.UpdateCapabilities( [ ('dataformtest', [], ['banan', 'hi']) ]) _, _, forms, _ = receive_presence_and_ask_caps(q, stream) assertEquals({'gabble:test:channel:manager:data:form': {'cheese': ['omgnothorriblecheese'], 'running_out_of': ['ideas', 'cake'], 'favourite_crane': ['a tall one', 'a short one'], 'animal': ['badger', 'snake', 'weasel'] } }, forms) if __name__ == '__main__': exec_test(test) telepathy-gabble-0.18.2/tests/twisted/console.py0000644000175000017500000000556412227000321021655 0ustar00smcvsmcv00000000000000""" A smoketest for the XMPP console API. """ from servicetest import ( ProxyWrapper, EventPattern, call_async, assertEquals, assertContains, assertNotEquals, sync_dbus, ) from gabbletest import exec_test, acknowledge_iq, elem, elem_iq from config import PLUGINS_ENABLED from twisted.words.xish import domish import ns CONSOLE_PLUGIN_IFACE = "org.freedesktop.Telepathy.Gabble.Plugin.Console" STACY = 'stacy@pilgrim.lit' if not PLUGINS_ENABLED: print "NOTE: built without --enable-plugins, not testing XMPP console" raise SystemExit(77) def send_unrecognised_get(q, stream): stream.send( elem_iq(stream, 'get')( elem('urn:unimaginative', 'dont-handle-me-bro') )) return q.expect('stream-iq', iq_type='error') def test(q, bus, conn, stream): path, _ = conn.Future.EnsureSidecar(CONSOLE_PLUGIN_IFACE) console = ProxyWrapper(bus.get_object(conn.bus_name, path), CONSOLE_PLUGIN_IFACE) assert not console.Properties.Get(CONSOLE_PLUGIN_IFACE, 'SpewStanzas') es = [ EventPattern('dbus-signal', signal='StanzaReceived'), EventPattern('dbus-signal', signal='StanzaSent'), ] q.forbid_events(es) call_async(q, console, 'SendIQ', 'get', STACY, '') e = q.expect('stream-iq', iq_type='get', query_ns='urn:unimaginative', query_name='coffee') acknowledge_iq(stream, e.stanza) e = q.expect('dbus-return', method='SendIQ') type_, body = e.value assertEquals('result', type_) # We just assume the body works. # Turn on signalling incoming and outgoing stanzas console.Properties.Set(CONSOLE_PLUGIN_IFACE, 'SpewStanzas', True) sync_dbus(bus, q, conn) q.unforbid_events(es) send_unrecognised_get(q, stream) e = q.expect('dbus-signal', signal='StanzaReceived') xml, = e.args assertContains(' Hi sis. ''' % { 'stacy': STACY }) e = q.expect('stream-message', to=STACY, message_type='headline') # Make sure that Wocky has filled in the jabber:client namespace we # carelessly omitted. message = e.stanza assertEquals('message', message.name) assertEquals(ns.CLIENT, message.uri) body = message.firstChildElement() assertEquals('body', body.name) assertEquals(ns.CLIENT, body.uri) if __name__ == '__main__': exec_test(test) telepathy-gabble-0.18.2/tests/twisted/client-types.py0000644000175000017500000002346412227000321022632 0ustar00smcvsmcv00000000000000""" Test Conn.I.ClientTypes """ import random from functools import partial from servicetest import EventPattern, assertEquals, assertLength, assertContains, assertSameSets from gabbletest import exec_test, make_presence, sync_stream import constants as cs import ns from caps_helper import ( presence_and_disco, send_presence, expect_disco, send_disco_reply, compute_caps_hash, ) client_base = 'http://telepathy.freedesktop.org/fake-client/client-types-' caps_base = { 'ver': '0.1' } features = [ ns.JINGLE_015, ns.JINGLE_015_AUDIO, ns.JINGLE_015_VIDEO, ns.GOOGLE_P2P, ] # our identities BOT = ['client/bot/en/doesnotcompute'] CONSOLE = ['client/console/en/dumb'] GAME = ['client/game/en/wiibox3'] HANDHELD = ['client/handheld/en/lolpaq'] PC = ['client/pc/en/Lolclient 0.L0L'] PHONE = ['client/phone/en/gr8phone 101'] WEB = ['client/web/en/webcat'] SMS = ['client/phone/en/tlk 2 u l8r'] TRANSIENT_PHONE = ['client/phone/en/fleeting visit'] BANANAPHONE = ['client/phone/en/banana milk is pretty disgusting'] def build_stuff(identities): types = map(lambda x: x.split('/')[1], identities) # add something to the end of the client string so that the caps # hashes aren't all the same and so stop discoing client = client_base + ''.join(types) caps = caps_base caps['node'] = client return (caps, client, types) def contact_online(q, conn, stream, contact, identities, disco=True, dataforms={}, initial=True, show=None): (caps, client, types) = build_stuff(identities) handle = conn.RequestHandles(cs.HT_CONTACT, [contact])[0] # make contact come online presence_and_disco (q, conn, stream, contact, disco, client, caps, features, identities, dataforms=dataforms, initial = initial, show = show) if initial: event = q.expect('dbus-signal', signal='ClientTypesUpdated') assertEquals([handle, types], event.args) def test(q, bus, conn, stream): # check all these types appear as they should contact_online(q, conn, stream, 'bot@bot.com/lol', BOT) contact_online(q, conn, stream, 'console@console.com/lol', CONSOLE) contact_online(q, conn, stream, 'game@game.com/lol', GAME) contact_online(q, conn, stream, 'handheld@handheld.com/lol', HANDHELD) contact_online(q, conn, stream, 'pc@pc.com/lol', PC) contact_online(q, conn, stream, 'phone@phone.com/lol', PHONE) contact_online(q, conn, stream, 'web@web.com/lol', WEB) contact_online(q, conn, stream, 'sms@sms.com/lol', SMS) meredith_one = 'meredith@foo.com/One' meredith_two = 'meredith@foo.com/Two' meredith_three = 'meredith@foo.com/Three' meredith_handle = conn.RequestHandles(cs.HT_CONTACT, [meredith_one])[0] # Meredith signs in from one resource contact_online(q, conn, stream, meredith_one, PC, show='chat') # * One: chat: pc # ClientTypes should be: ['pc'] # Meredith signs in from another resource contact_online(q, conn, stream, meredith_two, PHONE, show='dnd', initial=False) # * One: chat: pc # * Two: dnd: phone # ClientTypes should be: ['pc'] # check we're still a PC types = conn.GetClientTypes([meredith_handle], dbus_interface=cs.CONN_IFACE_CLIENT_TYPES) assertLength(1, types) assertLength(1, types[meredith_handle]) assertEquals('pc', types[meredith_handle][0]) # Two now becomes more available stream.send(make_presence(meredith_two, show='chat')) # * One: chat: pc # * Two: chat: phone # ClientTypes should be: ['pc'] types = conn.GetClientTypes([meredith_handle], dbus_interface=cs.CONN_IFACE_CLIENT_TYPES) assertEquals('pc', types[meredith_handle][0]) # One now becomes less available stream.send(make_presence(meredith_one, show='away')) # * One: away: pc # * Two: chat: phone # ClientTypes should be: ['phone'] # wait for the presence change q.expect('dbus-signal', signal='PresencesChanged', args=[{meredith_handle: (cs.PRESENCE_AVAILABLE, 'chat', '')}]) # now wait for the change in client type event = q.expect('dbus-signal', signal='ClientTypesUpdated') assertEquals([meredith_handle, ['phone']], event.args) # make One more available again stream.send(make_presence(meredith_one, show='chat', status='lawl')) # * One: chat: pc # * Two: chat: phone # ClientTypes should be: ['pc'] # wait for the presence change q.expect('dbus-signal', signal='PresencesChanged', args=[{meredith_handle: (cs.PRESENCE_AVAILABLE, 'chat', 'lawl')}]) # now wait for the change in client type event = q.expect('dbus-signal', signal='ClientTypesUpdated') assertEquals([meredith_handle, ['pc']], event.args) # both One and Two go away stream.send(make_presence(meredith_one, show='away')) # * One: away: pc # * Two: chat: phone # ClientTypes should be: ['phone'] stream.send(make_presence(meredith_two, show='away')) # * One: away: pc # * Two: away: phone # ClientTypes should be: ['pc'] # wait for the presence change q.expect('dbus-signal', signal='PresencesChanged', args=[{meredith_handle: (cs.PRESENCE_AWAY, 'away', '')}]) # check it still thinks we're a PC types = conn.GetClientTypes([meredith_handle], dbus_interface=cs.CONN_IFACE_CLIENT_TYPES) assertEquals('pc', types[meredith_handle][0]) # Three, with multiple identities, signs in identities = [PHONE[0], CONSOLE[0], HANDHELD[0], BOT[0]] contact_online(q, conn, stream, meredith_three, identities, show='chat', initial=False) # * One: away: pc # * Two: away: phone # * Three: chat: phone, console, handheld, bot # ClientTypes should be: ['phone', 'console', 'handheld', 'bot'] in some order # wait for the presence change q.expect('dbus-signal', signal='PresencesChanged', args=[{meredith_handle: (cs.PRESENCE_AVAILABLE, 'chat', 'hello')}]) # now wait for the change in client type event = q.expect('dbus-signal', signal='ClientTypesUpdated') assertEquals(meredith_handle, event.args[0]) assertEquals(['bot', 'console', 'handheld', 'phone'], sorted(event.args[1])) # that'll do # # ... # # wait wait! no it won't! Here's a regression test for # . (caps, client, types) = build_stuff(TRANSIENT_PHONE) contact = 'mini9@meegoconf.ie/hai' send_presence(q, conn, stream, contact, caps) stanza = expect_disco(q, contact, client, caps) stream.send(make_presence(contact, type='unavailable')) send_disco_reply(stream, stanza, TRANSIENT_PHONE, []) # Gabble used to crash upon receiving a disco reply from a contact who's no # longer in the presence cache. So we sync here to check if it's died. sync_stream(q, stream) def test2(q, bus, conn, stream): marco_pidgin = 'marco@fancy.italian.restaurant/Pidgin' marco_phone = 'marco@fancy.italian.restaurant/N900' handle = conn.RequestHandles(cs.HT_CONTACT, [marco_pidgin])[0] # pidgin comes online contact_online(q, conn, stream, marco_pidgin, PC) types = conn.GetClientTypes([handle], dbus_interface=cs.CONN_IFACE_CLIENT_TYPES) assertSameSets(['pc'], types[handle]) # phone comes online contact_online(q, conn, stream, marco_phone, PHONE, initial=False) types = conn.GetClientTypes([handle], dbus_interface=cs.CONN_IFACE_CLIENT_TYPES) assertSameSets(['pc'], types[handle]) sync_stream(q, stream) # pidgin goes offline stream.send(make_presence(marco_pidgin, type='unavailable')) # no presence signal q.expect('dbus-signal', signal='ClientTypesUpdated', args=[handle, ['phone']]) # pidgin comes back online caps, _, _ = build_stuff(PC) stream.send(make_presence(marco_pidgin, status='hello', caps=caps)) q.expect('dbus-signal', signal='ClientTypesUpdated', args=[handle, ['pc']]) attrs = conn.Contacts.GetContactAttributes([handle], [cs.CONN_IFACE_CLIENT_TYPES], False) assertContains(handle, attrs) attr = cs.CONN_IFACE_CLIENT_TYPES + '/client-types' assertContains(attr, attrs[handle]) assertEquals(['pc'], attrs[handle][attr]) def two_contacts_with_the_same_hash(q, bus, conn, stream, bare_jids): contact1 = 'bowyer.place@tfl.gov.uk' contact2 = 'albany.road@tfl.gov.uk' if not bare_jids: contact1 += '/lol' contact2 += '/whut' h1, h2 = conn.RequestHandles(cs.HT_CONTACT, [contact1, contact2]) ver = compute_caps_hash(BANANAPHONE, features, {}) caps = { # Uniquify slightly with a stringified boolean ;-) 'node': '%s%s' % (client_base, bare_jids), 'ver': ver, 'hash': 'sha-1', } send_presence(q, conn, stream, contact1, caps) stanza = expect_disco(q, contact1, caps['node'], caps) send_presence(q, conn, stream, contact2, caps) q.forbid_events([ EventPattern('stream-iq', to=contact2, query_ns=ns.DISCO_INFO), ]) sync_stream(q, stream) send_disco_reply(stream, stanza, BANANAPHONE, features, {}) q.expect_many( EventPattern('dbus-signal', signal='ClientTypesUpdated', args=[h1, ['phone']]), # Gabble previously did not emit ClientTypesUpdated for anyone beside # the contact we sent the disco request to; so this second event would # never arrive. EventPattern('dbus-signal', signal='ClientTypesUpdated', args=[h2, ['phone']]), ) if __name__ == '__main__': exec_test(test) exec_test(test2) exec_test(partial(two_contacts_with_the_same_hash, bare_jids=False)) exec_test(partial(two_contacts_with_the_same_hash, bare_jids=True)) telepathy-gabble-0.18.2/tests/twisted/caps_helper.py0000644000175000017500000002674412227000321022503 0ustar00smcvsmcv00000000000000# vim: set fileencoding=utf-8 : import hashlib import base64 import dbus from twisted.words.xish import domish, xpath from gabbletest import make_result_iq, make_presence, elem_iq, elem from servicetest import ( EventPattern, assertEquals, assertContains, assertDoesNotContain, assertLength, ) import config import ns import constants as cs if config.VOIP_ENABLED: FIXED_JINGLE_CAPS = [ ns.JINGLE, ns.JINGLE_015, ns.JINGLE_TRANSPORT_RAWUDP, ] else: FIXED_JINGLE_CAPS = [] # The caps we have regardless of any clients' caps FIXED_CAPS = FIXED_JINGLE_CAPS + [ ns.GOOGLE_FEAT_SESSION, ns.NICK, ns.NICK + '+notify', ns.CHAT_STATES, ns.SI, ns.IBB, ns.BYTESTREAMS, ] JINGLE_CAPS = [ # Additional Jingle transports ns.JINGLE_TRANSPORT_ICEUDP, ns.GOOGLE_P2P, # Jingle content types ns.GOOGLE_FEAT_VOICE, ns.GOOGLE_FEAT_VIDEO, ns.JINGLE_015_AUDIO, ns.JINGLE_015_VIDEO, ns.JINGLE_RTP, ns.JINGLE_RTP_AUDIO, ns.JINGLE_RTP_VIDEO, ] VARIABLE_CAPS = ( JINGLE_CAPS + [ ns.FILE_TRANSFER, # FIXME: currently we always advertise these, but in future we should # only advertise them if >= 1 client supports them: # ns.TUBES, # there is an unlimited set of these; only the ones actually relevant to # the tests so far are shown here ns.TUBES + '/stream#x-abiword', ns.TUBES + '/stream#daap', ns.TUBES + '/stream#http', ns.TUBES + '/dbus#com.example.Go', ns.TUBES + '/dbus#com.example.Xiangqi', ]) def check_caps(namespaces, desired): """Assert that all the FIXED_CAPS are supported, and of the VARIABLE_CAPS, every capability in desired is supported, and every other capability is not. """ for c in FIXED_CAPS: assertContains(c, namespaces) for c in VARIABLE_CAPS: if c in desired: assertContains(c, namespaces) else: assertDoesNotContain(c, namespaces) text_fixed_properties = dbus.Dictionary({ cs.TARGET_HANDLE_TYPE: cs.HT_CONTACT, cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_TEXT }) text_allowed_properties = dbus.Array([cs.TARGET_HANDLE]) stream_tube_fixed_properties = dbus.Dictionary({ cs.TARGET_HANDLE_TYPE: cs.HT_CONTACT, cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_STREAM_TUBE }) stream_tube_allowed_properties = dbus.Array([cs.TARGET_HANDLE, cs.TARGET_ID, cs.STREAM_TUBE_SERVICE]) dbus_tube_fixed_properties = dbus.Dictionary({ cs.TARGET_HANDLE_TYPE: cs.HT_CONTACT, cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_DBUS_TUBE }) dbus_tube_allowed_properties = dbus.Array([cs.TARGET_HANDLE, cs.TARGET_ID, cs.DBUS_TUBE_SERVICE_NAME]) ft_fixed_properties = dbus.Dictionary({ cs.TARGET_HANDLE_TYPE: cs.HT_CONTACT, cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_FILE_TRANSFER, }) ft_allowed_properties = dbus.Array([ cs.CHANNEL_TYPE_FILE_TRANSFER + '.ContentHashType', cs.TARGET_HANDLE, cs.TARGET_ID, cs.CHANNEL_TYPE_FILE_TRANSFER + '.ContentType', cs.CHANNEL_TYPE_FILE_TRANSFER + '.Filename', cs.CHANNEL_TYPE_FILE_TRANSFER + '.Size', cs.CHANNEL_TYPE_FILE_TRANSFER + '.ContentHash', cs.CHANNEL_TYPE_FILE_TRANSFER + '.Description', cs.CHANNEL_TYPE_FILE_TRANSFER + '.Date', cs.FT_URI]) ft_allowed_properties_with_metadata = dbus.Array( ft_allowed_properties + [cs.FT_SERVICE_NAME, cs.FT_METADATA]) fake_client_dataforms = { 'urn:xmpp:dataforms:softwareinfo': {'software': ['A Fake Client with Twisted'], 'software_version': ['5.11.2-svn-20080512'], 'os': ['Debian GNU/Linux unstable (sid) unstable sid'], 'os_version': ['2.6.24-1-amd64'], }, } def compute_caps_hash(identities, features, dataforms): """ Accepts a list of slash-separated identities, a list of feature namespaces, and a map from FORM_TYPE to (map from field name to values), returns the verification string as defined by . """ components = [] for identity in sorted(identities): if len(identity.split('/')) != 4: raise ValueError( "expecting identities of the form " + "'category/type/lang/client': got " + repr(identity)) components.append(identity) for feature in sorted(features): components.append(feature) for form_type in sorted(dataforms.keys()): components.append(form_type) for var in sorted(dataforms[form_type].keys()): components.append(var) for value in sorted(dataforms[form_type][var]): components.append(value) components.append('') m = hashlib.sha1() S = u'<'.join(components) m.update(S.encode('utf-8')) return base64.b64encode(m.digest()) def add_data_forms(root, dataforms): for type, fields in dataforms.iteritems(): x = root.addElement((ns.X_DATA, 'x')) x['type'] = 'result' field = x.addElement('field') field['var'] = 'FORM_TYPE' field['type'] = 'hidden' field.addElement('value', content=type) for var, values in fields.iteritems(): field = x.addElement('field') field['var'] = var for value in values: field.addElement('value', content=value) def make_caps_disco_reply(stream, req, identities, features, dataforms={}): iq = make_result_iq(stream, req) query = iq.firstChildElement() for identity in identities: category, type_, lang, name = identity.split('/') el = query.addElement('identity') el['category'] = category el['type'] = type_ el['name'] = name el['xml:lang'] = lang for f in features: el = domish.Element((None, 'feature')) el['var'] = f query.addChild(el) add_data_forms(query, dataforms) return iq def receive_presence_and_ask_caps(q, stream, expect_dbus=True): # receive presence stanza if expect_dbus: presence, event_dbus = q.expect_many( EventPattern('stream-presence'), EventPattern('dbus-signal', signal='ContactCapabilitiesChanged') ) assertLength(1, event_dbus.args) signaled_caps = event_dbus.args[0] else: presence = q.expect('stream-presence') signaled_caps = None return disco_caps(q, stream, presence) + (signaled_caps,) def extract_data_forms(x_nodes): dataforms = {} if not x_nodes: return dataforms for form in x_nodes: name = None fields = {} for field in xpath.queryForNodes('/x/field', form): if field['var'] == 'FORM_TYPE': name = str(field.firstChildElement()) else: value_nodes = xpath.queryForNodes('/field/value', field) or [] values = [str(x) for x in value_nodes] fields[field['var']] = values if name is not None: dataforms[name] = fields return dataforms def extract_disco_parts(stanza): identity_nodes = xpath.queryForNodes('/iq/query/identity', stanza) assertLength(1, identity_nodes) identity_node = identity_nodes[0] assertEquals('client', identity_node['category']) assertEquals(config.CLIENT_TYPE, identity_node['type']) assertEquals(config.PACKAGE_STRING, identity_node['name']) assertDoesNotContain('xml:lang', identity_node.attributes) identity = 'client/%s//%s' % (config.CLIENT_TYPE, config.PACKAGE_STRING) features = [] for feature in xpath.queryForNodes('/iq/query/feature', stanza): features.append(feature['var']) # a quick and ugly data form extractor x_nodes = xpath.queryForNodes('/iq/query/x', stanza) or [] dataforms = extract_data_forms(x_nodes) return ([identity], features, dataforms) def disco_caps(q, stream, presence): c_nodes = xpath.queryForNodes('/presence/c', presence.stanza) assert c_nodes is not None assertLength(1, c_nodes) hash = c_nodes[0].attributes['hash'] ver = c_nodes[0].attributes['ver'] node = c_nodes[0].attributes['node'] assertEquals('sha-1', hash) # ask caps request = \ elem_iq(stream, 'get', from_='fake_contact@jabber.org/resource')( elem(ns.DISCO_INFO, 'query', node=(node + '#' + ver)) ) stream.send(request) # receive caps event = q.expect('stream-iq', query_ns=ns.DISCO_INFO, iq_id=request['id']) # Check that Gabble's announcing the identity we think it should be. (identities, features, dataforms) = extract_disco_parts(event.stanza) # Check if the hash matches the announced capabilities assertEquals(compute_caps_hash(identities, features, dataforms), ver) return (event, features, dataforms) def caps_contain(event, cap): node = xpath.queryForNodes('/iq/query/feature[@var="%s"]' % cap, event.stanza) if node is None: return False if len(node) != 1: return False var = node[0].attributes['var'] if var is None: return False return var == cap def presence_and_disco(q, conn, stream, contact, disco, client, caps, features, identities=[], dataforms={}, initial=True, show=None): h = send_presence(q, conn, stream, contact, caps, initial=initial, show=show) if disco: stanza = expect_disco(q, contact, client, caps) send_disco_reply(stream, stanza, identities, features, dataforms) return h def send_presence(q, conn, stream, contact, caps, initial=True, show=None): h = conn.RequestHandles(cs.HT_CONTACT, [contact])[0] if initial: stream.send(make_presence(contact, status='hello')) q.expect('dbus-signal', signal='PresencesChanged', args=[{h: (2, u'available', 'hello')}]) # no special capabilities assertEquals([(h, cs.CHANNEL_TYPE_TEXT, 3, 0)], conn.Capabilities.GetCapabilities([h])) # send updated presence with caps info stream.send(make_presence(contact, show=show, status='hello', caps=caps)) return h def expect_disco(q, contact, client, caps): # Gabble looks up our capabilities event = q.expect('stream-iq', to=contact, query_ns=ns.DISCO_INFO) assertEquals(client + '#' + caps['ver'], event.query['node']) return event.stanza def send_disco_reply(stream, stanza, identities, features, dataforms={}): stream.send( make_caps_disco_reply(stream, stanza, identities, features, dataforms)) if __name__ == '__main__': # example from XEP-0115 assertEquals('QgayPKawpkPSDYmwT/WM94uAlu0=', compute_caps_hash(['client/pc//Exodus 0.9.1'], ["http://jabber.org/protocol/disco#info", "http://jabber.org/protocol/disco#items", "http://jabber.org/protocol/muc", "http://jabber.org/protocol/caps"], {})) # another example from XEP-0115 identities = [u'client/pc/en/Psi 0.11', u'client/pc/el/Ψ 0.11'] features = [ u'http://jabber.org/protocol/caps', u'http://jabber.org/protocol/disco#info', u'http://jabber.org/protocol/disco#items', u'http://jabber.org/protocol/muc', ] dataforms = { u'urn:xmpp:dataforms:softwareinfo': { u'ip_version': [u'ipv4', u'ipv6'], u'os': [u'Mac'], u'os_version': [u'10.5.1'], u'software': [u'Psi'], u'software_version': [u'0.11'], }, } assertEquals('q07IKJEyjvHSyhy//CH0CxmKi8w=', compute_caps_hash(identities, features, dataforms)) telepathy-gabble-0.18.2/tests/twisted/avatar-requirements.py0000644000175000017500000000302412227000321024177 0ustar00smcvsmcv00000000000000from servicetest import call_async, EventPattern from gabbletest import exec_test, acknowledge_iq, make_result_iq import constants as cs def test_get_all(conn): props = conn.GetAll(cs.CONN_IFACE_AVATARS, dbus_interface=cs.PROPERTIES_IFACE) types = props['SupportedAvatarMIMETypes'] minw = props['MinimumAvatarWidth'] minh = props['MinimumAvatarHeight'] maxw = props['MaximumAvatarWidth'] maxh = props['MaximumAvatarHeight'] maxb = props['MaximumAvatarBytes'] rech = props['RecommendedAvatarHeight'] recw = props['RecommendedAvatarWidth'] assert types[0] == 'image/png', types assert 'image/jpeg' in types, types assert 'image/gif' in types, types assert minw == 32, minw assert minh == 32, minh assert maxw == 96, maxw assert maxh == 96, maxh assert maxb == 8192, maxb assert recw == 64, recw assert rech == 64, rech def test(q, bus, conn, stream): test_get_all(conn) conn.Connect() q.expect('dbus-signal', signal='StatusChanged', args=[cs.CONN_STATUS_CONNECTED, cs.CSR_REQUESTED]) test_get_all(conn) # deprecated version types, minw, minh, maxw, maxh, maxb = conn.Avatars.GetAvatarRequirements() assert types[0] == 'image/png', types assert 'image/jpeg' in types, types assert 'image/gif' in types, types assert minw == 32, minw assert minh == 32, minh assert maxw == 96, maxw assert maxh == 96, maxh assert maxb == 8192, maxb if __name__ == '__main__': exec_test(test, do_connect=False) telepathy-gabble-0.18.2/tests/twisted/addressing.py0000644000175000017500000001622012227000321022325 0ustar00smcvsmcv00000000000000""" Test Gabble's different addressing interfaces. """ import dbus from servicetest import unwrap, tp_path_prefix, assertEquals, ProxyWrapper, \ assertContains, assertSameSets, assertDoesNotContain, pretty from gabbletest import exec_test, call_async import constants as cs import ns import time def test_protocol(q, bus, conn, stream): proto = ProxyWrapper( bus.get_object(cs.CM + '.gabble', tp_path_prefix + '/ConnectionManager/gabble/jabber'), cs.PROTOCOL, {"Addressing" : cs.PROTOCOL_IFACE_ADDRESSING}) # AddressableVCardFields and AddressableURISchemes addr_props = proto.Properties.GetAll(cs.PROTOCOL_IFACE_ADDRESSING) assertEquals(sorted(["x-jabber", "x-facebook-id"]), sorted(addr_props["AddressableVCardFields"])) assertEquals(["xmpp"], addr_props["AddressableURISchemes"]) # NormalizeVCardAddress normalized_address = proto.Addressing.NormalizeVCardAddress( "X-JABBER", "eitan@EXAMPLE.com/somewhere") assertEquals("eitan@example.com", normalized_address) normalized_address = proto.Addressing.NormalizeVCardAddress( "X-FACEBOOK-ID", "12345") assertEquals("12345", normalized_address) normalized_address = proto.Addressing.NormalizeVCardAddress( "x-facebook-id", "12345") assertEquals("12345", normalized_address) call_async(q, proto.Addressing, "NormalizeVCardAddress", "x-facebook-id", "abcde") q.expect('dbus-error', method="NormalizeVCardAddress", name=cs.INVALID_ARGUMENT) call_async(q, proto.Addressing, "NormalizeVCardAddress", "X-WEIRD-FIELD", "eitan@example.com") q.expect('dbus-error', method="NormalizeVCardAddress", name=cs.NOT_IMPLEMENTED) call_async(q, proto.Addressing, "NormalizeVCardAddress", "X-JABBER", "eitan!example.com") q.expect('dbus-error', method="NormalizeVCardAddress", name=cs.INVALID_ARGUMENT) call_async(q, proto.Addressing, "NormalizeVCardAddress", "X-FACEBOOK-ID", "-12345!chat.facebook.com") q.expect('dbus-error', method="NormalizeVCardAddress", name=cs.INVALID_ARGUMENT) call_async(q, proto.Addressing, "NormalizeVCardAddress", "X-FACEBOOK-ID", "12345@chat.facebook.com") q.expect('dbus-error', method="NormalizeVCardAddress", name=cs.INVALID_ARGUMENT) call_async(q, proto.Addressing, "NormalizeVCardAddress", "X-FACEBOOK-ID", "-12345@chat.facebook.com") q.expect('dbus-error', method="NormalizeVCardAddress", name=cs.INVALID_ARGUMENT) call_async(q, proto.Addressing, "NormalizeVCardAddress", "X-FACEBOOK-ID", "-12345@example.com") q.expect('dbus-error', method="NormalizeVCardAddress", name=cs.INVALID_ARGUMENT) call_async(q, proto.Addressing, "NormalizeVCardAddress", "X-FACEBOOK-ID", "12345@example.com") q.expect('dbus-error', method="NormalizeVCardAddress", name=cs.INVALID_ARGUMENT) call_async(q, proto.Addressing, "NormalizeVCardAddress", "X-FACEBOOK-ID", "-12345@example.com") q.expect('dbus-error', method="NormalizeVCardAddress", name=cs.INVALID_ARGUMENT) # NormalizeContactURI normalized_uri = proto.Addressing.NormalizeContactURI( "xmpp:EITAN?@example.COM/resource") assertEquals("xmpp:eitan%3F@example.com", normalized_uri) normalized_uri = proto.Addressing.NormalizeContactURI( "xmpp:EITAN?@example.COM/resourc?e") assertEquals("xmpp:eitan%3F@example.com", normalized_uri) call_async(q, proto.Addressing, "NormalizeContactURI", "Something that is far from a URI") q.expect('dbus-error', method="NormalizeContactURI", name=cs.INVALID_ARGUMENT) call_async(q, proto.Addressing, "NormalizeContactURI", "http://this@isnotawebbrowser") q.expect('dbus-error', method="NormalizeContactURI", name=cs.NOT_IMPLEMENTED) call_async(q, proto.Addressing, "NormalizeContactURI", "xmpp:something") q.expect('dbus-error', method="NormalizeContactURI", name=cs.INVALID_ARGUMENT) def test_connection(q, bus, conn, stream): event = q.expect('stream-iq', query_ns=ns.ROSTER) event.stanza['type'] = 'result' normalized_buddies = ['amy@foo.com', 'bob@foo.com', 'che@foo.com'] buddies = ['AMY@foo.com', 'bob@FOO.com', 'che@foo.com/resource'] for buddy in normalized_buddies: item = event.query.addElement('item') item['jid'] = buddy item['subscription'] = 'both' stream.send(event.stanza) requested, attributes = conn.Addressing.GetContactsByVCardField( "X-JABBER", buddies[:2] + ['bad!jid'] + buddies[2:], []) addresses = [] assertEquals(3, len(attributes)) assertEquals(3, len(requested)) for attr in attributes.values(): assertContains(cs.CONN_IFACE_ADDRESSING + '/addresses', attr.keys()) assertContains('x-jabber', attr[cs.CONN_IFACE_ADDRESSING + '/addresses'].keys()) addresses.append(attr[cs.CONN_IFACE_ADDRESSING + '/addresses']['x-jabber']) assertSameSets(normalized_buddies, addresses) assertSameSets(buddies, requested.keys()); normalized_buddies = ['12345', '54321'] buddies = ['12345', '54321'] bad_jid_buddies = ['-12345!CHAT.facebook.com', '12345@chat.facebook.com'] for buddy in buddies: item = event.query.addElement('item') item['jid'] = buddy item['subscription'] = 'both' stream.send(event.stanza) requested, attributes = conn.Addressing.GetContactsByVCardField( "X-FACEBOOK-ID", buddies + bad_jid_buddies, []) addresses = [] for attr in attributes.values(): assertContains(cs.CONN_IFACE_ADDRESSING + '/addresses', attr.keys()) assertContains('x-facebook-id', attr[cs.CONN_IFACE_ADDRESSING + '/addresses'].keys()) addr = attr[cs.CONN_IFACE_ADDRESSING + '/addresses']['x-facebook-id'] addresses.append(addr) assertEquals(attr[cs.CONN + '/contact-id'], "-" + addr + "@chat.facebook.com") assertSameSets(normalized_buddies, addresses) assertSameSets(buddies, requested.keys()); normalized_buddies = ['amy%3F@foo.com', 'bob@foo.com', 'che@foo.com'] buddies = ['AMY?@foo.com', 'bob@FOO.com', 'che@foo.com/resource'] normalized_schemes = ["xmpp", "xmpp", "http"] schemes = ["xmpp", "XMPP", "http"] valid_schemes = ["xmpp", "XMPP"] request_uris = [a + ":" + b for a, b in zip(schemes, buddies)] valid_request_uris = [a + ":" + b for a, b in zip(valid_schemes, buddies)] normalized_request_uris = [a + ":" + b for a, b in zip(normalized_schemes, normalized_buddies)] requested, attributes = conn.Addressing.GetContactsByURI(request_uris, []) assertEquals(2, len(attributes)) assertEquals(2, len(requested)) for attr in attributes.values(): assertContains(attr[cs.CONN_IFACE_ADDRESSING + '/uris'][0], normalized_request_uris) assertContains(cs.CONN_IFACE_ADDRESSING + '/uris', attr.keys()) assertSameSets(valid_request_uris, requested.keys()) if __name__ == '__main__': exec_test(test_protocol) exec_test(test_connection) telepathy-gabble-0.18.2/tests/twisted/Makefile.am0000644000175000017500000003527512227000321021677 0ustar00smcvsmcv00000000000000TWISTED_TESTS = \ addressing.py \ avatar-requirements.py \ caps/advertise-contact-caps.py \ caps/advertise-legacy.py \ caps/broken-reply.py \ caps/caps-cache.py \ caps/caps-persistent-cache.py \ caps/compat-bundles.py \ caps/disco-without-node.py \ caps/double-disco.py \ caps/from-bare-jid.py \ caps/hashed-caps.py \ caps_helper.py \ caps/initial-caps.py \ caps/jingle-caps.py \ caps/offline.py \ caps/receive-jingle.py \ caps/trust-thyself.py \ caps/tube-caps.py \ client-types.py \ cm/protocol.py \ connect/disco-error-from-bare-jid.py \ connect/disco-facebook.py \ connect/disconnect-timeout.py \ connect/disco-no-reply.py \ connect/network-error.py \ connect/stream-closed.py \ connect/test-connection-params.py \ connect/test-fail.py \ connect/test-nonblocking-tls.py \ connect/test-success.py \ connect/test-twice.py \ console.py \ dataforms.py \ gateways.py \ last-activity.py \ mail-notification.py \ muc/avatars.py \ muc/banned.py \ muc/chat-states.py \ muc/conference.py \ muc/kicked.py \ muc/name-conflict.py \ muc/password.py \ muc/presence-before-closing.py \ muc/renamed.py \ muc/room-config.py \ muc/roomlist.py \ muc/room.py \ muc/scrollback.py \ muc/send-error.py \ muc/subject.py \ muc/test-ensure.py \ muc/test-muc-alias.py \ muc/test-muc-invitation.py \ muc/test-muc-ownership.py \ muc/test-muc.py \ olpc/change-notifications.py \ olpc/current-activity.py \ olpc/olpc-muc-invitation.py \ olpc/olpc-muc-prop-change.py \ olpc/test-olpc-bundle.py \ olpc/test-olpc-set-props-preload.py \ pep-support.py \ plugin-channel-managers.py \ power-save.py \ presence/decloak.py \ presence/error.py \ presence/initial-contact-presence.py \ presence/initial-presence.py \ presence/invisible_xep_0126.py \ presence/invisible_xep_0186.py \ presence/plugins.py \ presence/presence.py \ presence/set-idempotence.py \ presence/shared-status.py \ pubsub.py \ roster/authorize.py \ roster/edit-before-roster.py \ roster/ensure.py \ roster/groups-12791.py \ roster/groups.py \ roster/initial-aliases.py \ roster/push-from-contact.py \ roster/push-without-id.py \ roster/removed-from-rp-subscribe.py \ roster/request-group-after-roster.py \ roster/request-group-before-roster.py \ roster/test-google-roster.py \ roster/test-roster-item-deletion.py \ roster/test-roster.py \ roster/test-roster-subscribe.py \ roster/test-save-alias-to-roster.py \ sasl/abort.py \ sasl/close.py \ sasl/complex.py \ sasl/jabber_auth.py \ sasl/plain.py \ sasl/telepathy-password.py \ search/ceci-nest-pas-un-serveur.py \ search/extended.py \ search/no-server-property.py \ search/unextended.py \ servicetest.py \ sidecar-own-caps.py \ sidecars.py \ test-debug.py \ test-fallback-socks5-proxy.py \ test-location.py \ test-register.py \ text/destroy.py \ text/ensure.py \ text/facebook-own-message.py \ text/initiate.py \ text/initiate-requestotron.py \ text/receipts.py \ text/respawn.py \ text/send-error.py \ text/send-to-correct-resource.py \ text/test-chat-state.py \ text/test-text-delayed.py \ text/test-text-no-body.py \ text/test-text.py \ tls/legacy-jabber.py \ tls/server-tls-channel.py \ version.py \ $(NULL) TWISTED_TUBE_TESTS = \ tubes/accept-muc-dbus-tube.py \ tubes/accept-muc-stream-tube.py \ tubes/accept-private-dbus-tube.py \ tubes/accept-private-stream-tube.py \ tubes/check-create-tube-return.py \ tubes/close-muc-with-closed-tube.py \ tubes/create-invalid-tube-channels.py \ tubes/ensure-si-tube.py \ tubes/offer-muc-dbus-tube.py \ tubes/offer-muc-stream-tube.py \ tubes/offer-no-caps.py \ tubes/offer-private-dbus-tube.py \ tubes/offer-private-stream-tube.py \ tubes/request-invalid-dbus-tube.py \ tubes/test-get-available-tubes.py \ tubes/test-socks5-muc.py \ $(NULL) TWISTED_VCARD_TESTS = \ vcard/clear-avatar.py \ vcard/disconnect-during-pep.py \ vcard/get-contact-info.py \ vcard/item-not-found.py \ vcard/overlapping-sets.py \ vcard/redundant-set.py \ vcard/refresh-contact-info.py \ vcard/set-avatar.py \ vcard/set-contact-info.py \ vcard/set-set-disconnect.py \ vcard/supported-fields.py \ vcard/test-alias-empty-vcard.py \ vcard/test-alias-message.py \ vcard/test-alias-pep.py \ vcard/test-alias.py \ vcard/test-avatar-async.py \ vcard/test-avatar-multiple-resources.py \ vcard/test-avatar.py \ vcard/test-avatar-retrieved.py \ vcard/test-avatar-tokens.py \ vcard/test-save-alias-to-vcard.py \ vcard/test-set-alias.py \ vcard/test-vcard-cache.py \ vcard/test-vcard-race.py \ vcard/update-get-failed.py \ vcard/update-rejected.py \ $(NULL) TWISTED_JINGLE_TESTS = \ jingle/accept-extra-stream.py \ jingle/call-basics.py \ jingle/call-codecoffer.py \ jingle/call-content-adding-removal.py \ jingle/call-dtmf.py \ jingle/call-google-relay.py \ jingle/call-hold-audio.py \ jingle/call-hold-av.py \ jingle/call-muc-cancel.py \ jingle/call-muc.py \ jingle/call-muc-re-re-request.py \ jingle/call-state.py \ jingle/decloak-peer.py \ jingle/dtmf-no-audio.py \ jingle/dtmf.py \ jingle/google-relay.py \ jingle/hold-audio.py \ jingle/hold-av.py \ jingle/incoming-basics.py \ jingle/incoming-call-stream-error.py \ jingle/incoming-gmail-modern-jingle.py \ jingle/initial-audio-video.py \ jingle/misuse.py \ jingle/outgoing-basics.py \ jingle/outgoing-ensure.py \ jingle/outgoing-many-streams.py \ jingle/payload-types.py \ jingle/preload-caps-crash.py \ jingle/session-id-collision.py \ jingle/stream-errors-on-content-reject.py \ jingle/stream-errors-on-terminate.py \ jingle/stream-handler-error.py \ jingle/stun-server.py \ jingle/test-content-adding-removal.py \ jingle/test-content-complex.py \ jingle/test-description-info.py \ jingle/test-incoming-call-reject.py \ jingle/test-incoming-iceudp.py \ jingle/test-outgoing-call-rejected.py \ jingle/test-outgoing-iceudp.py \ jingle/test-wait-for-caps-incomplete.py \ jingle/test-wait-for-caps.py \ jingle/transport-info-parsing.py \ jingle/unknown-session.py \ $(NULL) TWISTED_FT_TESTS = \ file-transfer/test-caps-file-transfer.py \ file-transfer/test-ibb-too-early.py \ file-transfer/test-receive-file-and-close-socket-while-receiving.py \ file-transfer/test-receive-file-and-disconnect.py \ file-transfer/test-receive-file-and-sender-disconnect-while-pending.py \ file-transfer/test-receive-file-and-sender-disconnect-while-transfering.py \ file-transfer/test-receive-file-decline.py \ file-transfer/test-receive-file.py \ file-transfer/test-send-file-and-cancel-immediately.py \ file-transfer/test-send-file-declined.py \ file-transfer/test-send-file-provide-immediately.py \ file-transfer/test-send-file-send-before-accept.py \ file-transfer/test-send-file-to-unknown-contact.py \ file-transfer/test-send-file-wait-to-provide.py \ file-transfer/test-uri.py \ file-transfer/metadata.py \ file-transfer/ft-client-caps.py \ jingle-share/test-caps-file-transfer.py \ jingle-share/test-multift.py \ jingle-share/test-receive-file-and-close-socket-while-receiving.py \ jingle-share/test-receive-file-and-disconnect.py \ jingle-share/test-receive-file-and-sender-disconnect-while-pending.py \ jingle-share/test-receive-file-and-sender-disconnect-while-transfering.py \ jingle-share/test-receive-file-decline.py \ jingle-share/test-send-file-and-cancel-immediately.py \ jingle-share/test-send-file.py \ jingle-share/test-send-file-send-before-accept.py \ jingle-share/test-send-file-wait-to-provide.py \ $(NULL) # other files used by the twisted tests, but are not tests and are not built # source TWISTED_OTHER_FILES = \ bytestream.py \ connect/torture.py \ constants.py \ file-transfer/file_transfer_helper.py \ gabbletest.py \ httptest.py \ jingle/call_helper.py \ jingle/callutils.py \ jingle/__init__.py \ jingle/jingletest2.py \ jingle-share/file_transfer_helper.py \ jingle-share/jingleshareutils.py \ mucutil.py \ ns.py \ olpc/util.py \ presence/__init__.py \ presence/invisible_helper.py \ rostertest.py \ sasl/saslutil.py \ search/search_helper.py \ test-helper.py \ tls-cert.pem \ tls-key.pem \ tubes/tubetestutil.py \ $(NULL) if ENABLE_INSTALLED_TESTS # Install files in each directory. They could be tests, pristine data files, # scripts or built source twistedtestsdir = @gabbletestsdir@/twisted nobase_nodist_twistedtests_SCRIPTS = \ run-test.sh \ tools/exec-with-log.sh \ tools/run-gabble.sh \ $(NULL) nobase_dist_twistedtests_SCRIPTS = \ tools/with-session-bus.sh \ $(NULL) nobase_dist_twistedtests_DATA = \ $(TWISTED_TESTS) \ $(TWISTED_OTHER_FILES) \ $(NULL) nobase_nodist_twistedtests_DATA = \ config.py \ gabble-twisted-tests.list \ $(installed_conf_files) \ $(service_files) $(NULL) endif check-local: check-coding-style check-twisted # set to 6 when using refdbg, to give Gabble time to exit CHECK_TWISTED_SLEEP=0 check-twisted: $(BUILT_SOURCES) if WANT_TWISTED_TESTS rm -f tools/core rm -f tools/vgcore.* rm -f tools/gabble-testing.log rm -f tools/strace.log if test -n "$$GABBLE_TEST_REFDBG"; then \ sleep=6; \ else \ sleep=$(CHECK_TWISTED_SLEEP); \ fi; \ failed=0; \ GABBLE_TEST_UNINSTALLED=1 \ GABBLE_TEST_SLEEP="--sleep=$$sleep" \ GABBLE_ABS_TOP_SRCDIR=@abs_top_srcdir@ \ GABBLE_ABS_TOP_BUILDDIR=@abs_top_builddir@ \ sh run-test.sh "$(TWISTED_TESTS)" || failed=1; \ if test -e tools/core; then\ echo -e "\033[0;31;1mCore dump exists: tools/core\033[0m";\ exit 1;\ fi; \ if test $$failed = 1; then\ exit 1;\ fi; else @echo "Configured without Twisted test support. To enable them," @echo "ensure that these Python modules are available:" @echo " • twisted.words.xish.domish" @echo " • twisted.words.protocols.jabber" @echo " • twisted.internet.reactor" @echo "and then re-run configure." endif if ENABLE_DEBUG DEBUGGING_PYBOOL = True else DEBUGGING_PYBOOL = False endif if ENABLE_PLUGINS PLUGINS_ENABLED_PYBOOL = True else PLUGINS_ENABLED_PYBOOL = False endif if ENABLE_CHANNEL_TYPE_CALL CHANNEL_TYPE_CALL_ENABLED_PYBOOL = True else CHANNEL_TYPE_CALL_ENABLED_PYBOOL = False endif if ENABLE_GOOGLE_RELAY GOOGLE_RELAY_ENABLED_PYBOOL = True else GOOGLE_RELAY_ENABLED_PYBOOL = False endif if ENABLE_FILE_TRANSFER FILE_TRANSFER_ENABLED_PYBOOL = True else FILE_TRANSFER_ENABLED_PYBOOL = False endif if ENABLE_VOIP VOIP_ENABLED_PYBOOL = True else VOIP_ENABLED_PYBOOL = False endif if ENABLE_JINGLE_FILE_TRANSFER JINGLE_FILE_TRANSFER_ENABLED_PYBOOL = True else JINGLE_FILE_TRANSFER_ENABLED_PYBOOL = False endif config.py: Makefile $(AM_V_GEN) { \ echo "PACKAGE_STRING = \"$(PACKAGE_STRING)\""; \ echo "CLIENT_TYPE = '$(CLIENT_TYPE)'"; \ echo "DEBUGGING = $(DEBUGGING_PYBOOL)"; \ echo "PLUGINS_ENABLED = $(PLUGINS_ENABLED_PYBOOL)"; \ echo "CHANNEL_TYPE_CALL_ENABLED = $(CHANNEL_TYPE_CALL_ENABLED_PYBOOL)"; \ echo "GOOGLE_RELAY_ENABLED = $(GOOGLE_RELAY_ENABLED_PYBOOL)"; \ echo "FILE_TRANSFER_ENABLED = $(FILE_TRANSFER_ENABLED_PYBOOL)"; \ echo "VOIP_ENABLED = $(VOIP_ENABLED_PYBOOL)"; \ echo "JINGLE_FILE_TRANSFER_ENABLED = $(JINGLE_FILE_TRANSFER_ENABLED_PYBOOL)"; \ } > $@ BUILT_SOURCES = config.py TWISTED_TESTS += $(TWISTED_FT_TESTS) $(TWISTED_TUBE_TESTS) TWISTED_TESTS += $(TWISTED_JINGLE_TESTS) $(TWISTED_VCARD_TESTS) if ENABLE_INSTALLED_TESTS gabbledebugdir = @gabbletestsdir@/twisted gabbledebug_PROGRAMS = \ telepathy-gabble-debug else noinst_PROGRAMS = \ telepathy-gabble-debug endif telepathy_gabble_debug_SOURCES = \ main-debug.c \ test-resolver.c \ test-resolver.h telepathy_gabble_debug_LDADD = \ $(top_builddir)/src/libgabble-convenience.la \ $(ALL_LIBS) telepathy_gabble_debug_LDFLAGS = -export-dynamic AM_CFLAGS = $(ERROR_CFLAGS) @DBUS_CFLAGS@ @GLIB_CFLAGS@ @WOCKY_CFLAGS@ \ @TP_GLIB_CFLAGS@ \ -I $(top_srcdir)/src -I $(top_builddir)/src \ -I $(top_srcdir)/lib -I $(top_builddir)/lib \ -I $(top_srcdir) -I $(top_builddir) ALL_LIBS = @DBUS_LIBS@ @GLIB_LIBS@ @WOCKY_LIBS@ @TP_GLIB_LIBS@ CLEANFILES = gabble-[1-9]*.log *.pyc */*.pyc config.py check_misc_sources = $(TESTS) # the following used to be in tools/ include $(top_srcdir)/tools/check-coding-style.mk gabble-twisted-tests.list: Makefile $(AM_V_GEN)echo $(TWISTED_TESTS) > $@ run-test.sh: run-test.sh.in Makefile $(AM_V_GEN)sed -e "s|[@]gabbletestsdir[@]|@gabbletestsdir@|g" \ -e "s|[@]PYTHON[@]|$(PYTHON)|g" \ -e "s|[@]TEST_PYTHON[@]|$(TEST_PYTHON)|g" \ $< > $@ @chmod +x $@ tools/run-gabble.sh: tools/run-gabble.sh.in Makefile @mkdir -p tools $(AM_V_GEN)sed \ -e "s|[@]gabbletestsdir[@]|@gabbletestsdir@|g" \ -e "s|[@]pluginexecdir[@]|@pluginexecdir@|g" \ -e "s|[@]libdir[@]|$(libdir)|g" \ $< > $@ @chmod +x $@ tools/exec-with-log.sh: tools/exec-with-log.sh.in $(MKDIR_P) tools $(AM_V_GEN)sed -e "s|[@]abs_top_builddir[@]|@abs_top_builddir@|g" \ -e "s|[@]abs_top_srcdir[@]|@abs_top_srcdir@|g" $< > $@ @chmod +x $@ # The wildcard % matches both config files! tools/%.conf: tools/%.conf.in Makefile $(MKDIR_P) tools/servicedir $(MKDIR_P) tools/servicedir-uninstalled $(AM_V_GEN)sed -e "s|[@]gabbletestsdir[@]|@gabbletestsdir@|g" \ -e "s|[@]abs_top_builddir[@]|@abs_top_builddir@|g" $< > $@ # We don't use the full filename for the .in because > 99 character filenames # in tarballs are non-portable (and automake 1.8 doesn't let us build # non-archaic tarballs) tools/servicedir/org.freedesktop.Telepathy.ConnectionManager.%.service: tools/servicedir/%.service.in Makefile $(MKDIR_P) tools/servicedir $(AM_V_GEN)sed -e "s|[@]gabbletestsdir[@]|@gabbletestsdir@|g" $< > $@ tools/servicedir-uninstalled/org.freedesktop.Telepathy.ConnectionManager.%.service: tools/servicedir-uninstalled/%.service.in $(MKDIR_P) tools/servicedir-uninstalled $(AM_V_GEN)sed -e "s|[@]abs_top_builddir[@]|@abs_top_builddir@|g" $< > $@ # D-Bus service file for testing installed_service_in_files = tools/servicedir/gabble.service.in service_files = tools/servicedir/org.freedesktop.Telepathy.ConnectionManager.gabble.service uninstalled_service_in_files = tools/servicedir-uninstalled/gabble.service.in uninstalled_service_files = tools/servicedir-uninstalled/org.freedesktop.Telepathy.ConnectionManager.gabble.service # D-Bus config file for testing installed_conf_in_files = tools/servicedir/tmp-session-bus.conf.in installed_conf_files = $(installed_conf_in_files:.conf.in=.conf) uninstalled_conf_in_files = tools/servicedir-uninstalled/tmp-session-bus.conf.in uninstalled_conf_files = $(uninstalled_conf_in_files:.conf.in=.conf) BUILT_SOURCES += \ $(service_files) \ $(installed_conf_files) \ $(uninstalled_service_files) \ $(uninstalled_conf_files) \ gabble-twisted-tests.list \ run-test.sh \ tools/exec-with-log.sh \ tools/run-gabble.sh \ $(NULL) EXTRA_DIST = \ $(installed_service_in_files) \ $(uninstalled_service_in_files) \ $(installed_conf_in_files) \ $(uninstalled_conf_in_files) \ tools/exec-with-log.sh.in \ tools/run-gabble.sh.in \ run-test.sh.in \ $(NULL) CLEANFILES += \ $(BUILT_SOURCES) \ tools/gabble-testing.log telepathy-gabble-0.18.2/tests/twisted/Makefile.in0000644000175000017500000015234512312536074021724 0ustar00smcvsmcv00000000000000# Makefile.in generated by automake 1.14.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2013 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ VPATH = @srcdir@ am__is_gnu_make = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)' am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ @ENABLE_INSTALLED_TESTS_TRUE@gabbledebug_PROGRAMS = \ @ENABLE_INSTALLED_TESTS_TRUE@ telepathy-gabble-debug$(EXEEXT) @ENABLE_INSTALLED_TESTS_FALSE@noinst_PROGRAMS = \ @ENABLE_INSTALLED_TESTS_FALSE@ telepathy-gabble-debug$(EXEEXT) DIST_COMMON = $(top_srcdir)/tools/check-coding-style.mk \ $(srcdir)/Makefile.in $(srcdir)/Makefile.am \ $(am__nobase_dist_twistedtests_SCRIPTS_DIST) \ $(top_srcdir)/depcomp \ $(am__nobase_dist_twistedtests_DATA_DIST) subdir = tests/twisted ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/ax_config_dir.m4 \ $(top_srcdir)/m4/compiler.m4 $(top_srcdir)/m4/libtool.m4 \ $(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \ $(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \ $(top_srcdir)/m4/tp-compiler-flag.m4 \ $(top_srcdir)/m4/tp-compiler-warnings.m4 \ $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = am__installdirs = "$(DESTDIR)$(gabbledebugdir)" \ "$(DESTDIR)$(twistedtestsdir)" "$(DESTDIR)$(twistedtestsdir)" \ "$(DESTDIR)$(twistedtestsdir)" "$(DESTDIR)$(twistedtestsdir)" PROGRAMS = $(gabbledebug_PROGRAMS) $(noinst_PROGRAMS) am_telepathy_gabble_debug_OBJECTS = main-debug.$(OBJEXT) \ test-resolver.$(OBJEXT) telepathy_gabble_debug_OBJECTS = $(am_telepathy_gabble_debug_OBJECTS) am__DEPENDENCIES_1 = telepathy_gabble_debug_DEPENDENCIES = \ $(top_builddir)/src/libgabble-convenience.la \ $(am__DEPENDENCIES_1) AM_V_lt = $(am__v_lt_@AM_V@) am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) am__v_lt_0 = --silent am__v_lt_1 = telepathy_gabble_debug_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ $(AM_CFLAGS) $(CFLAGS) $(telepathy_gabble_debug_LDFLAGS) \ $(LDFLAGS) -o $@ am__nobase_dist_twistedtests_SCRIPTS_DIST = tools/with-session-bus.sh am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; am__vpath_adj = case $$p in \ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ *) f=$$p;; \ esac; am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; am__install_max = 40 am__nobase_strip_setup = \ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` am__nobase_strip = \ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" am__nobase_list = $(am__nobase_strip_setup); \ for p in $$list; do echo "$$p $$p"; done | \ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ if (++n[$$2] == $(am__install_max)) \ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ END { for (dir in files) print dir, files[dir] }' am__base_list = \ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' am__uninstall_files_from_dir = { \ test -z "$$files" \ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ $(am__cd) "$$dir" && rm -f $$files; }; \ } SCRIPTS = $(nobase_dist_twistedtests_SCRIPTS) \ $(nobase_nodist_twistedtests_SCRIPTS) AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) depcomp = $(SHELL) $(top_srcdir)/depcomp am__depfiles_maybe = depfiles am__mv = mv -f COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ $(AM_CFLAGS) $(CFLAGS) AM_V_CC = $(am__v_CC_@AM_V@) am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) am__v_CC_0 = @echo " CC " $@; am__v_CC_1 = CCLD = $(CC) LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ $(AM_LDFLAGS) $(LDFLAGS) -o $@ AM_V_CCLD = $(am__v_CCLD_@AM_V@) am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) am__v_CCLD_0 = @echo " CCLD " $@; am__v_CCLD_1 = SOURCES = $(telepathy_gabble_debug_SOURCES) DIST_SOURCES = $(telepathy_gabble_debug_SOURCES) am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac am__nobase_dist_twistedtests_DATA_DIST = addressing.py \ avatar-requirements.py caps/advertise-contact-caps.py \ caps/advertise-legacy.py caps/broken-reply.py \ caps/caps-cache.py caps/caps-persistent-cache.py \ caps/compat-bundles.py caps/disco-without-node.py \ caps/double-disco.py caps/from-bare-jid.py caps/hashed-caps.py \ caps_helper.py caps/initial-caps.py caps/jingle-caps.py \ caps/offline.py caps/receive-jingle.py caps/trust-thyself.py \ caps/tube-caps.py client-types.py cm/protocol.py \ connect/disco-error-from-bare-jid.py connect/disco-facebook.py \ connect/disconnect-timeout.py connect/disco-no-reply.py \ connect/network-error.py connect/stream-closed.py \ connect/test-connection-params.py connect/test-fail.py \ connect/test-nonblocking-tls.py connect/test-success.py \ connect/test-twice.py console.py dataforms.py gateways.py \ last-activity.py mail-notification.py muc/avatars.py \ muc/banned.py muc/chat-states.py muc/conference.py \ muc/kicked.py muc/name-conflict.py muc/password.py \ muc/presence-before-closing.py muc/renamed.py \ muc/room-config.py muc/roomlist.py muc/room.py \ muc/scrollback.py muc/send-error.py muc/subject.py \ muc/test-ensure.py muc/test-muc-alias.py \ muc/test-muc-invitation.py muc/test-muc-ownership.py \ muc/test-muc.py olpc/change-notifications.py \ olpc/current-activity.py olpc/olpc-muc-invitation.py \ olpc/olpc-muc-prop-change.py olpc/test-olpc-bundle.py \ olpc/test-olpc-set-props-preload.py pep-support.py \ plugin-channel-managers.py power-save.py presence/decloak.py \ presence/error.py presence/initial-contact-presence.py \ presence/initial-presence.py presence/invisible_xep_0126.py \ presence/invisible_xep_0186.py presence/plugins.py \ presence/presence.py presence/set-idempotence.py \ presence/shared-status.py pubsub.py roster/authorize.py \ roster/edit-before-roster.py roster/ensure.py \ roster/groups-12791.py roster/groups.py \ roster/initial-aliases.py roster/push-from-contact.py \ roster/push-without-id.py roster/removed-from-rp-subscribe.py \ roster/request-group-after-roster.py \ roster/request-group-before-roster.py \ roster/test-google-roster.py \ roster/test-roster-item-deletion.py roster/test-roster.py \ roster/test-roster-subscribe.py \ roster/test-save-alias-to-roster.py sasl/abort.py \ sasl/close.py sasl/complex.py sasl/jabber_auth.py \ sasl/plain.py sasl/telepathy-password.py \ search/ceci-nest-pas-un-serveur.py search/extended.py \ search/no-server-property.py search/unextended.py \ servicetest.py sidecar-own-caps.py sidecars.py test-debug.py \ test-fallback-socks5-proxy.py test-location.py \ test-register.py text/destroy.py text/ensure.py \ text/facebook-own-message.py text/initiate.py \ text/initiate-requestotron.py text/receipts.py text/respawn.py \ text/send-error.py text/send-to-correct-resource.py \ text/test-chat-state.py text/test-text-delayed.py \ text/test-text-no-body.py text/test-text.py \ tls/legacy-jabber.py tls/server-tls-channel.py version.py \ file-transfer/test-caps-file-transfer.py \ file-transfer/test-ibb-too-early.py \ file-transfer/test-receive-file-and-close-socket-while-receiving.py \ file-transfer/test-receive-file-and-disconnect.py \ file-transfer/test-receive-file-and-sender-disconnect-while-pending.py \ file-transfer/test-receive-file-and-sender-disconnect-while-transfering.py \ file-transfer/test-receive-file-decline.py \ file-transfer/test-receive-file.py \ file-transfer/test-send-file-and-cancel-immediately.py \ file-transfer/test-send-file-declined.py \ file-transfer/test-send-file-provide-immediately.py \ file-transfer/test-send-file-send-before-accept.py \ file-transfer/test-send-file-to-unknown-contact.py \ file-transfer/test-send-file-wait-to-provide.py \ file-transfer/test-uri.py file-transfer/metadata.py \ file-transfer/ft-client-caps.py \ jingle-share/test-caps-file-transfer.py \ jingle-share/test-multift.py \ jingle-share/test-receive-file-and-close-socket-while-receiving.py \ jingle-share/test-receive-file-and-disconnect.py \ jingle-share/test-receive-file-and-sender-disconnect-while-pending.py \ jingle-share/test-receive-file-and-sender-disconnect-while-transfering.py \ jingle-share/test-receive-file-decline.py \ jingle-share/test-send-file-and-cancel-immediately.py \ jingle-share/test-send-file.py \ jingle-share/test-send-file-send-before-accept.py \ jingle-share/test-send-file-wait-to-provide.py \ tubes/accept-muc-dbus-tube.py tubes/accept-muc-stream-tube.py \ tubes/accept-private-dbus-tube.py \ tubes/accept-private-stream-tube.py \ tubes/check-create-tube-return.py \ tubes/close-muc-with-closed-tube.py \ tubes/create-invalid-tube-channels.py tubes/ensure-si-tube.py \ tubes/offer-muc-dbus-tube.py tubes/offer-muc-stream-tube.py \ tubes/offer-no-caps.py tubes/offer-private-dbus-tube.py \ tubes/offer-private-stream-tube.py \ tubes/request-invalid-dbus-tube.py \ tubes/test-get-available-tubes.py tubes/test-socks5-muc.py \ jingle/accept-extra-stream.py jingle/call-basics.py \ jingle/call-codecoffer.py \ jingle/call-content-adding-removal.py jingle/call-dtmf.py \ jingle/call-google-relay.py jingle/call-hold-audio.py \ jingle/call-hold-av.py jingle/call-muc-cancel.py \ jingle/call-muc.py jingle/call-muc-re-re-request.py \ jingle/call-state.py jingle/decloak-peer.py \ jingle/dtmf-no-audio.py jingle/dtmf.py jingle/google-relay.py \ jingle/hold-audio.py jingle/hold-av.py \ jingle/incoming-basics.py jingle/incoming-call-stream-error.py \ jingle/incoming-gmail-modern-jingle.py \ jingle/initial-audio-video.py jingle/misuse.py \ jingle/outgoing-basics.py jingle/outgoing-ensure.py \ jingle/outgoing-many-streams.py jingle/payload-types.py \ jingle/preload-caps-crash.py jingle/session-id-collision.py \ jingle/stream-errors-on-content-reject.py \ jingle/stream-errors-on-terminate.py \ jingle/stream-handler-error.py jingle/stun-server.py \ jingle/test-content-adding-removal.py \ jingle/test-content-complex.py jingle/test-description-info.py \ jingle/test-incoming-call-reject.py \ jingle/test-incoming-iceudp.py \ jingle/test-outgoing-call-rejected.py \ jingle/test-outgoing-iceudp.py \ jingle/test-wait-for-caps-incomplete.py \ jingle/test-wait-for-caps.py jingle/transport-info-parsing.py \ jingle/unknown-session.py vcard/clear-avatar.py \ vcard/disconnect-during-pep.py vcard/get-contact-info.py \ vcard/item-not-found.py vcard/overlapping-sets.py \ vcard/redundant-set.py vcard/refresh-contact-info.py \ vcard/set-avatar.py vcard/set-contact-info.py \ vcard/set-set-disconnect.py vcard/supported-fields.py \ vcard/test-alias-empty-vcard.py vcard/test-alias-message.py \ vcard/test-alias-pep.py vcard/test-alias.py \ vcard/test-avatar-async.py \ vcard/test-avatar-multiple-resources.py vcard/test-avatar.py \ vcard/test-avatar-retrieved.py vcard/test-avatar-tokens.py \ vcard/test-save-alias-to-vcard.py vcard/test-set-alias.py \ vcard/test-vcard-cache.py vcard/test-vcard-race.py \ vcard/update-get-failed.py vcard/update-rejected.py \ bytestream.py connect/torture.py constants.py \ file-transfer/file_transfer_helper.py gabbletest.py \ httptest.py jingle/call_helper.py jingle/callutils.py \ jingle/__init__.py jingle/jingletest2.py \ jingle-share/file_transfer_helper.py \ jingle-share/jingleshareutils.py mucutil.py ns.py olpc/util.py \ presence/__init__.py presence/invisible_helper.py \ rostertest.py sasl/saslutil.py search/search_helper.py \ test-helper.py tls-cert.pem tls-key.pem tubes/tubetestutil.py DATA = $(nobase_dist_twistedtests_DATA) \ $(nobase_nodist_twistedtests_DATA) am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) # Read a list of newline-separated strings from the standard input, # and print each of them once, without duplicates. Input order is # *not* preserved. am__uniquify_input = $(AWK) '\ BEGIN { nonempty = 0; } \ { items[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in items) print i; }; } \ ' # Make sure the list of sources is unique. This is necessary because, # e.g., the same source file might be shared among _SOURCES variables # for different programs/libraries. am__define_uniq_tagged_files = \ list='$(am__tagged_files)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | $(am__uniquify_input)` ETAGS = etags CTAGS = ctags DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ AR = @AR@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CFLAGS = @CFLAGS@ CLIENT_TYPE = @CLIENT_TYPE@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CYGPATH_W = @CYGPATH_W@ DBUS_CFLAGS = @DBUS_CFLAGS@ DBUS_LIBS = @DBUS_LIBS@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DLLTOOL = @DLLTOOL@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ ENABLE_CODING_STYLE_CHECKS = @ENABLE_CODING_STYLE_CHECKS@ ENABLE_PLUGINS = @ENABLE_PLUGINS@ ERROR_CFLAGS = @ERROR_CFLAGS@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ GLIB_CFLAGS = @GLIB_CFLAGS@ GLIB_GENMARSHAL = @GLIB_GENMARSHAL@ GLIB_LIBS = @GLIB_LIBS@ GMODULE_CFLAGS = @GMODULE_CFLAGS@ GMODULE_LIBS = @GMODULE_LIBS@ GREP = @GREP@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ LD = @LD@ LDFLAGS = @LDFLAGS@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBTOOL = @LIBTOOL@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBOBJS = @LTLIBOBJS@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ NICE_CFLAGS = @NICE_CFLAGS@ NICE_LIBS = @NICE_LIBS@ NM = @NM@ NMEDIT = @NMEDIT@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ PYTHON = @PYTHON@ PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@ PYTHON_PLATFORM = @PYTHON_PLATFORM@ PYTHON_PREFIX = @PYTHON_PREFIX@ PYTHON_VERSION = @PYTHON_VERSION@ RANLIB = @RANLIB@ SED = @SED@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ SOUP_CFLAGS = @SOUP_CFLAGS@ SOUP_LIBS = @SOUP_LIBS@ STRIP = @STRIP@ TEST_PYTHON = @TEST_PYTHON@ TP_GLIB_CFLAGS = @TP_GLIB_CFLAGS@ TP_GLIB_LIBS = @TP_GLIB_LIBS@ VERSION = @VERSION@ WOCKY_CFLAGS = @WOCKY_CFLAGS@ WOCKY_LIBS = @WOCKY_LIBS@ XSLTPROC = @XSLTPROC@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ gabbletestsdir = @gabbletestsdir@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ mandir = @mandir@ mkdir_p = @mkdir_p@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ pkgpyexecdir = @pkgpyexecdir@ pkgpythondir = @pkgpythondir@ pluginexecdir = @pluginexecdir@ pluginexeclibdir = @pluginexeclibdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ pyexecdir = @pyexecdir@ pythondir = @pythondir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ srcdir = @srcdir@ sysconfdir = @sysconfdir@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ TWISTED_TESTS = addressing.py avatar-requirements.py \ caps/advertise-contact-caps.py caps/advertise-legacy.py \ caps/broken-reply.py caps/caps-cache.py \ caps/caps-persistent-cache.py caps/compat-bundles.py \ caps/disco-without-node.py caps/double-disco.py \ caps/from-bare-jid.py caps/hashed-caps.py caps_helper.py \ caps/initial-caps.py caps/jingle-caps.py caps/offline.py \ caps/receive-jingle.py caps/trust-thyself.py caps/tube-caps.py \ client-types.py cm/protocol.py \ connect/disco-error-from-bare-jid.py connect/disco-facebook.py \ connect/disconnect-timeout.py connect/disco-no-reply.py \ connect/network-error.py connect/stream-closed.py \ connect/test-connection-params.py connect/test-fail.py \ connect/test-nonblocking-tls.py connect/test-success.py \ connect/test-twice.py console.py dataforms.py gateways.py \ last-activity.py mail-notification.py muc/avatars.py \ muc/banned.py muc/chat-states.py muc/conference.py \ muc/kicked.py muc/name-conflict.py muc/password.py \ muc/presence-before-closing.py muc/renamed.py \ muc/room-config.py muc/roomlist.py muc/room.py \ muc/scrollback.py muc/send-error.py muc/subject.py \ muc/test-ensure.py muc/test-muc-alias.py \ muc/test-muc-invitation.py muc/test-muc-ownership.py \ muc/test-muc.py olpc/change-notifications.py \ olpc/current-activity.py olpc/olpc-muc-invitation.py \ olpc/olpc-muc-prop-change.py olpc/test-olpc-bundle.py \ olpc/test-olpc-set-props-preload.py pep-support.py \ plugin-channel-managers.py power-save.py presence/decloak.py \ presence/error.py presence/initial-contact-presence.py \ presence/initial-presence.py presence/invisible_xep_0126.py \ presence/invisible_xep_0186.py presence/plugins.py \ presence/presence.py presence/set-idempotence.py \ presence/shared-status.py pubsub.py roster/authorize.py \ roster/edit-before-roster.py roster/ensure.py \ roster/groups-12791.py roster/groups.py \ roster/initial-aliases.py roster/push-from-contact.py \ roster/push-without-id.py roster/removed-from-rp-subscribe.py \ roster/request-group-after-roster.py \ roster/request-group-before-roster.py \ roster/test-google-roster.py \ roster/test-roster-item-deletion.py roster/test-roster.py \ roster/test-roster-subscribe.py \ roster/test-save-alias-to-roster.py sasl/abort.py \ sasl/close.py sasl/complex.py sasl/jabber_auth.py \ sasl/plain.py sasl/telepathy-password.py \ search/ceci-nest-pas-un-serveur.py search/extended.py \ search/no-server-property.py search/unextended.py \ servicetest.py sidecar-own-caps.py sidecars.py test-debug.py \ test-fallback-socks5-proxy.py test-location.py \ test-register.py text/destroy.py text/ensure.py \ text/facebook-own-message.py text/initiate.py \ text/initiate-requestotron.py text/receipts.py text/respawn.py \ text/send-error.py text/send-to-correct-resource.py \ text/test-chat-state.py text/test-text-delayed.py \ text/test-text-no-body.py text/test-text.py \ tls/legacy-jabber.py tls/server-tls-channel.py version.py \ $(NULL) $(TWISTED_FT_TESTS) $(TWISTED_TUBE_TESTS) \ $(TWISTED_JINGLE_TESTS) $(TWISTED_VCARD_TESTS) TWISTED_TUBE_TESTS = \ tubes/accept-muc-dbus-tube.py \ tubes/accept-muc-stream-tube.py \ tubes/accept-private-dbus-tube.py \ tubes/accept-private-stream-tube.py \ tubes/check-create-tube-return.py \ tubes/close-muc-with-closed-tube.py \ tubes/create-invalid-tube-channels.py \ tubes/ensure-si-tube.py \ tubes/offer-muc-dbus-tube.py \ tubes/offer-muc-stream-tube.py \ tubes/offer-no-caps.py \ tubes/offer-private-dbus-tube.py \ tubes/offer-private-stream-tube.py \ tubes/request-invalid-dbus-tube.py \ tubes/test-get-available-tubes.py \ tubes/test-socks5-muc.py \ $(NULL) TWISTED_VCARD_TESTS = \ vcard/clear-avatar.py \ vcard/disconnect-during-pep.py \ vcard/get-contact-info.py \ vcard/item-not-found.py \ vcard/overlapping-sets.py \ vcard/redundant-set.py \ vcard/refresh-contact-info.py \ vcard/set-avatar.py \ vcard/set-contact-info.py \ vcard/set-set-disconnect.py \ vcard/supported-fields.py \ vcard/test-alias-empty-vcard.py \ vcard/test-alias-message.py \ vcard/test-alias-pep.py \ vcard/test-alias.py \ vcard/test-avatar-async.py \ vcard/test-avatar-multiple-resources.py \ vcard/test-avatar.py \ vcard/test-avatar-retrieved.py \ vcard/test-avatar-tokens.py \ vcard/test-save-alias-to-vcard.py \ vcard/test-set-alias.py \ vcard/test-vcard-cache.py \ vcard/test-vcard-race.py \ vcard/update-get-failed.py \ vcard/update-rejected.py \ $(NULL) TWISTED_JINGLE_TESTS = \ jingle/accept-extra-stream.py \ jingle/call-basics.py \ jingle/call-codecoffer.py \ jingle/call-content-adding-removal.py \ jingle/call-dtmf.py \ jingle/call-google-relay.py \ jingle/call-hold-audio.py \ jingle/call-hold-av.py \ jingle/call-muc-cancel.py \ jingle/call-muc.py \ jingle/call-muc-re-re-request.py \ jingle/call-state.py \ jingle/decloak-peer.py \ jingle/dtmf-no-audio.py \ jingle/dtmf.py \ jingle/google-relay.py \ jingle/hold-audio.py \ jingle/hold-av.py \ jingle/incoming-basics.py \ jingle/incoming-call-stream-error.py \ jingle/incoming-gmail-modern-jingle.py \ jingle/initial-audio-video.py \ jingle/misuse.py \ jingle/outgoing-basics.py \ jingle/outgoing-ensure.py \ jingle/outgoing-many-streams.py \ jingle/payload-types.py \ jingle/preload-caps-crash.py \ jingle/session-id-collision.py \ jingle/stream-errors-on-content-reject.py \ jingle/stream-errors-on-terminate.py \ jingle/stream-handler-error.py \ jingle/stun-server.py \ jingle/test-content-adding-removal.py \ jingle/test-content-complex.py \ jingle/test-description-info.py \ jingle/test-incoming-call-reject.py \ jingle/test-incoming-iceudp.py \ jingle/test-outgoing-call-rejected.py \ jingle/test-outgoing-iceudp.py \ jingle/test-wait-for-caps-incomplete.py \ jingle/test-wait-for-caps.py \ jingle/transport-info-parsing.py \ jingle/unknown-session.py \ $(NULL) TWISTED_FT_TESTS = \ file-transfer/test-caps-file-transfer.py \ file-transfer/test-ibb-too-early.py \ file-transfer/test-receive-file-and-close-socket-while-receiving.py \ file-transfer/test-receive-file-and-disconnect.py \ file-transfer/test-receive-file-and-sender-disconnect-while-pending.py \ file-transfer/test-receive-file-and-sender-disconnect-while-transfering.py \ file-transfer/test-receive-file-decline.py \ file-transfer/test-receive-file.py \ file-transfer/test-send-file-and-cancel-immediately.py \ file-transfer/test-send-file-declined.py \ file-transfer/test-send-file-provide-immediately.py \ file-transfer/test-send-file-send-before-accept.py \ file-transfer/test-send-file-to-unknown-contact.py \ file-transfer/test-send-file-wait-to-provide.py \ file-transfer/test-uri.py \ file-transfer/metadata.py \ file-transfer/ft-client-caps.py \ jingle-share/test-caps-file-transfer.py \ jingle-share/test-multift.py \ jingle-share/test-receive-file-and-close-socket-while-receiving.py \ jingle-share/test-receive-file-and-disconnect.py \ jingle-share/test-receive-file-and-sender-disconnect-while-pending.py \ jingle-share/test-receive-file-and-sender-disconnect-while-transfering.py \ jingle-share/test-receive-file-decline.py \ jingle-share/test-send-file-and-cancel-immediately.py \ jingle-share/test-send-file.py \ jingle-share/test-send-file-send-before-accept.py \ jingle-share/test-send-file-wait-to-provide.py \ $(NULL) # other files used by the twisted tests, but are not tests and are not built # source TWISTED_OTHER_FILES = \ bytestream.py \ connect/torture.py \ constants.py \ file-transfer/file_transfer_helper.py \ gabbletest.py \ httptest.py \ jingle/call_helper.py \ jingle/callutils.py \ jingle/__init__.py \ jingle/jingletest2.py \ jingle-share/file_transfer_helper.py \ jingle-share/jingleshareutils.py \ mucutil.py \ ns.py \ olpc/util.py \ presence/__init__.py \ presence/invisible_helper.py \ rostertest.py \ sasl/saslutil.py \ search/search_helper.py \ test-helper.py \ tls-cert.pem \ tls-key.pem \ tubes/tubetestutil.py \ $(NULL) # Install files in each directory. They could be tests, pristine data files, # scripts or built source @ENABLE_INSTALLED_TESTS_TRUE@twistedtestsdir = @gabbletestsdir@/twisted @ENABLE_INSTALLED_TESTS_TRUE@nobase_nodist_twistedtests_SCRIPTS = \ @ENABLE_INSTALLED_TESTS_TRUE@ run-test.sh \ @ENABLE_INSTALLED_TESTS_TRUE@ tools/exec-with-log.sh \ @ENABLE_INSTALLED_TESTS_TRUE@ tools/run-gabble.sh \ @ENABLE_INSTALLED_TESTS_TRUE@ $(NULL) @ENABLE_INSTALLED_TESTS_TRUE@nobase_dist_twistedtests_SCRIPTS = \ @ENABLE_INSTALLED_TESTS_TRUE@ tools/with-session-bus.sh \ @ENABLE_INSTALLED_TESTS_TRUE@ $(NULL) @ENABLE_INSTALLED_TESTS_TRUE@nobase_dist_twistedtests_DATA = \ @ENABLE_INSTALLED_TESTS_TRUE@ $(TWISTED_TESTS) \ @ENABLE_INSTALLED_TESTS_TRUE@ $(TWISTED_OTHER_FILES) \ @ENABLE_INSTALLED_TESTS_TRUE@ $(NULL) @ENABLE_INSTALLED_TESTS_TRUE@nobase_nodist_twistedtests_DATA = \ @ENABLE_INSTALLED_TESTS_TRUE@ config.py \ @ENABLE_INSTALLED_TESTS_TRUE@ gabble-twisted-tests.list \ @ENABLE_INSTALLED_TESTS_TRUE@ $(installed_conf_files) \ @ENABLE_INSTALLED_TESTS_TRUE@ $(service_files) # set to 6 when using refdbg, to give Gabble time to exit CHECK_TWISTED_SLEEP = 0 @ENABLE_DEBUG_FALSE@DEBUGGING_PYBOOL = False @ENABLE_DEBUG_TRUE@DEBUGGING_PYBOOL = True @ENABLE_PLUGINS_FALSE@PLUGINS_ENABLED_PYBOOL = False @ENABLE_PLUGINS_TRUE@PLUGINS_ENABLED_PYBOOL = True @ENABLE_CHANNEL_TYPE_CALL_FALSE@CHANNEL_TYPE_CALL_ENABLED_PYBOOL = False @ENABLE_CHANNEL_TYPE_CALL_TRUE@CHANNEL_TYPE_CALL_ENABLED_PYBOOL = True @ENABLE_GOOGLE_RELAY_FALSE@GOOGLE_RELAY_ENABLED_PYBOOL = False @ENABLE_GOOGLE_RELAY_TRUE@GOOGLE_RELAY_ENABLED_PYBOOL = True @ENABLE_FILE_TRANSFER_FALSE@FILE_TRANSFER_ENABLED_PYBOOL = False @ENABLE_FILE_TRANSFER_TRUE@FILE_TRANSFER_ENABLED_PYBOOL = True @ENABLE_VOIP_FALSE@VOIP_ENABLED_PYBOOL = False @ENABLE_VOIP_TRUE@VOIP_ENABLED_PYBOOL = True @ENABLE_JINGLE_FILE_TRANSFER_FALSE@JINGLE_FILE_TRANSFER_ENABLED_PYBOOL = False @ENABLE_JINGLE_FILE_TRANSFER_TRUE@JINGLE_FILE_TRANSFER_ENABLED_PYBOOL = True BUILT_SOURCES = config.py $(service_files) $(installed_conf_files) \ $(uninstalled_service_files) $(uninstalled_conf_files) \ gabble-twisted-tests.list run-test.sh tools/exec-with-log.sh \ tools/run-gabble.sh $(NULL) @ENABLE_INSTALLED_TESTS_TRUE@gabbledebugdir = @gabbletestsdir@/twisted telepathy_gabble_debug_SOURCES = \ main-debug.c \ test-resolver.c \ test-resolver.h telepathy_gabble_debug_LDADD = \ $(top_builddir)/src/libgabble-convenience.la \ $(ALL_LIBS) telepathy_gabble_debug_LDFLAGS = -export-dynamic AM_CFLAGS = $(ERROR_CFLAGS) @DBUS_CFLAGS@ @GLIB_CFLAGS@ @WOCKY_CFLAGS@ \ @TP_GLIB_CFLAGS@ \ -I $(top_srcdir)/src -I $(top_builddir)/src \ -I $(top_srcdir)/lib -I $(top_builddir)/lib \ -I $(top_srcdir) -I $(top_builddir) ALL_LIBS = @DBUS_LIBS@ @GLIB_LIBS@ @WOCKY_LIBS@ @TP_GLIB_LIBS@ CLEANFILES = gabble-[1-9]*.log *.pyc */*.pyc config.py \ $(BUILT_SOURCES) tools/gabble-testing.log check_misc_sources = $(TESTS) # D-Bus service file for testing installed_service_in_files = tools/servicedir/gabble.service.in service_files = tools/servicedir/org.freedesktop.Telepathy.ConnectionManager.gabble.service uninstalled_service_in_files = tools/servicedir-uninstalled/gabble.service.in uninstalled_service_files = tools/servicedir-uninstalled/org.freedesktop.Telepathy.ConnectionManager.gabble.service # D-Bus config file for testing installed_conf_in_files = tools/servicedir/tmp-session-bus.conf.in installed_conf_files = $(installed_conf_in_files:.conf.in=.conf) uninstalled_conf_in_files = tools/servicedir-uninstalled/tmp-session-bus.conf.in uninstalled_conf_files = $(uninstalled_conf_in_files:.conf.in=.conf) EXTRA_DIST = \ $(installed_service_in_files) \ $(uninstalled_service_in_files) \ $(installed_conf_in_files) \ $(uninstalled_conf_in_files) \ tools/exec-with-log.sh.in \ tools/run-gabble.sh.in \ run-test.sh.in \ $(NULL) all: $(BUILT_SOURCES) $(MAKE) $(AM_MAKEFLAGS) all-am .SUFFIXES: .SUFFIXES: .c .lo .o .obj $(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(top_srcdir)/tools/check-coding-style.mk $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu tests/twisted/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --gnu tests/twisted/Makefile .PRECIOUS: Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ esac; $(top_srcdir)/tools/check-coding-style.mk: $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): install-gabbledebugPROGRAMS: $(gabbledebug_PROGRAMS) @$(NORMAL_INSTALL) @list='$(gabbledebug_PROGRAMS)'; test -n "$(gabbledebugdir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(gabbledebugdir)'"; \ $(MKDIR_P) "$(DESTDIR)$(gabbledebugdir)" || exit 1; \ fi; \ for p in $$list; do echo "$$p $$p"; done | \ sed 's/$(EXEEXT)$$//' | \ while read p p1; do if test -f $$p \ || test -f $$p1 \ ; then echo "$$p"; echo "$$p"; else :; fi; \ done | \ sed -e 'p;s,.*/,,;n;h' \ -e 's|.*|.|' \ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \ sed 'N;N;N;s,\n, ,g' | \ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \ if ($$2 == $$4) files[d] = files[d] " " $$1; \ else { print "f", $$3 "/" $$4, $$1; } } \ END { for (d in files) print "f", d, files[d] }' | \ while read type dir files; do \ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \ test -z "$$files" || { \ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(gabbledebugdir)$$dir'"; \ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(gabbledebugdir)$$dir" || exit $$?; \ } \ ; done uninstall-gabbledebugPROGRAMS: @$(NORMAL_UNINSTALL) @list='$(gabbledebug_PROGRAMS)'; test -n "$(gabbledebugdir)" || list=; \ files=`for p in $$list; do echo "$$p"; done | \ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \ -e 's/$$/$(EXEEXT)/' \ `; \ test -n "$$list" || exit 0; \ echo " ( cd '$(DESTDIR)$(gabbledebugdir)' && rm -f" $$files ")"; \ cd "$(DESTDIR)$(gabbledebugdir)" && rm -f $$files clean-gabbledebugPROGRAMS: @list='$(gabbledebug_PROGRAMS)'; test -n "$$list" || exit 0; \ echo " rm -f" $$list; \ rm -f $$list || exit $$?; \ test -n "$(EXEEXT)" || exit 0; \ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \ echo " rm -f" $$list; \ rm -f $$list clean-noinstPROGRAMS: @list='$(noinst_PROGRAMS)'; test -n "$$list" || exit 0; \ echo " rm -f" $$list; \ rm -f $$list || exit $$?; \ test -n "$(EXEEXT)" || exit 0; \ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \ echo " rm -f" $$list; \ rm -f $$list telepathy-gabble-debug$(EXEEXT): $(telepathy_gabble_debug_OBJECTS) $(telepathy_gabble_debug_DEPENDENCIES) $(EXTRA_telepathy_gabble_debug_DEPENDENCIES) @rm -f telepathy-gabble-debug$(EXEEXT) $(AM_V_CCLD)$(telepathy_gabble_debug_LINK) $(telepathy_gabble_debug_OBJECTS) $(telepathy_gabble_debug_LDADD) $(LIBS) install-nobase_dist_twistedtestsSCRIPTS: $(nobase_dist_twistedtests_SCRIPTS) @$(NORMAL_INSTALL) @list='$(nobase_dist_twistedtests_SCRIPTS)'; test -n "$(twistedtestsdir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(twistedtestsdir)'"; \ $(MKDIR_P) "$(DESTDIR)$(twistedtestsdir)" || exit 1; \ fi; \ $(am__nobase_strip_setup); \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ if test -f "$$d$$p"; then echo "$$d$$p"; echo "$$p"; else :; fi; \ done | \ sed -e 'p;s,.*/,,;n' \ -e "s|$$srcdirstrip/||" -e 'h;s|[^/]*$$||; s|^$$|.|' \ -e 'p;x;s,.*/,,;$(transform)' | sed 'N;N;N;s,\n, ,g' | \ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1; } \ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \ if ($$2 == $$4) { files[d] = files[d] " " $$1; \ if (++n[d] == $(am__install_max)) { \ print "f", d, files[d]; n[d] = 0; files[d] = "" } } \ else { print "f", d "/" $$4, $$1 } } \ END { for (d in files) print "f", d, files[d] }' | \ while read type dir files; do \ case $$type in \ d) echo " $(MKDIR_P) '$(DESTDIR)$(twistedtestsdir)/$$dir'"; \ $(MKDIR_P) "$(DESTDIR)$(twistedtestsdir)/$$dir" || exit $$?;; \ f) \ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \ test -z "$$files" || { \ echo " $(INSTALL_SCRIPT) $$files '$(DESTDIR)$(twistedtestsdir)$$dir'"; \ $(INSTALL_SCRIPT) $$files "$(DESTDIR)$(twistedtestsdir)$$dir" || exit $$?; \ } \ ;; esac \ ; done uninstall-nobase_dist_twistedtestsSCRIPTS: @$(NORMAL_UNINSTALL) @list='$(nobase_dist_twistedtests_SCRIPTS)'; test -n "$(twistedtestsdir)" || exit 0; \ $(am__nobase_strip_setup); \ files=`$(am__nobase_strip) \ -e 'h;s,.*/,,;$(transform);x;s|[^/]*$$||;G;s,\n,,'`; \ dir='$(DESTDIR)$(twistedtestsdir)'; $(am__uninstall_files_from_dir) install-nobase_nodist_twistedtestsSCRIPTS: $(nobase_nodist_twistedtests_SCRIPTS) @$(NORMAL_INSTALL) @list='$(nobase_nodist_twistedtests_SCRIPTS)'; test -n "$(twistedtestsdir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(twistedtestsdir)'"; \ $(MKDIR_P) "$(DESTDIR)$(twistedtestsdir)" || exit 1; \ fi; \ $(am__nobase_strip_setup); \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ if test -f "$$d$$p"; then echo "$$d$$p"; echo "$$p"; else :; fi; \ done | \ sed -e 'p;s,.*/,,;n' \ -e "s|$$srcdirstrip/||" -e 'h;s|[^/]*$$||; s|^$$|.|' \ -e 'p;x;s,.*/,,;$(transform)' | sed 'N;N;N;s,\n, ,g' | \ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1; } \ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \ if ($$2 == $$4) { files[d] = files[d] " " $$1; \ if (++n[d] == $(am__install_max)) { \ print "f", d, files[d]; n[d] = 0; files[d] = "" } } \ else { print "f", d "/" $$4, $$1 } } \ END { for (d in files) print "f", d, files[d] }' | \ while read type dir files; do \ case $$type in \ d) echo " $(MKDIR_P) '$(DESTDIR)$(twistedtestsdir)/$$dir'"; \ $(MKDIR_P) "$(DESTDIR)$(twistedtestsdir)/$$dir" || exit $$?;; \ f) \ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \ test -z "$$files" || { \ echo " $(INSTALL_SCRIPT) $$files '$(DESTDIR)$(twistedtestsdir)$$dir'"; \ $(INSTALL_SCRIPT) $$files "$(DESTDIR)$(twistedtestsdir)$$dir" || exit $$?; \ } \ ;; esac \ ; done uninstall-nobase_nodist_twistedtestsSCRIPTS: @$(NORMAL_UNINSTALL) @list='$(nobase_nodist_twistedtests_SCRIPTS)'; test -n "$(twistedtestsdir)" || exit 0; \ $(am__nobase_strip_setup); \ files=`$(am__nobase_strip) \ -e 'h;s,.*/,,;$(transform);x;s|[^/]*$$||;G;s,\n,,'`; \ dir='$(DESTDIR)$(twistedtestsdir)'; $(am__uninstall_files_from_dir) mostlyclean-compile: -rm -f *.$(OBJEXT) distclean-compile: -rm -f *.tab.c @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/main-debug.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test-resolver.Po@am__quote@ .c.o: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< .c.obj: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` .c.lo: @am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs install-nobase_dist_twistedtestsDATA: $(nobase_dist_twistedtests_DATA) @$(NORMAL_INSTALL) @list='$(nobase_dist_twistedtests_DATA)'; test -n "$(twistedtestsdir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(twistedtestsdir)'"; \ $(MKDIR_P) "$(DESTDIR)$(twistedtestsdir)" || exit 1; \ fi; \ $(am__nobase_list) | while read dir files; do \ xfiles=; for file in $$files; do \ if test -f "$$file"; then xfiles="$$xfiles $$file"; \ else xfiles="$$xfiles $(srcdir)/$$file"; fi; done; \ test -z "$$xfiles" || { \ test "x$$dir" = x. || { \ echo " $(MKDIR_P) '$(DESTDIR)$(twistedtestsdir)/$$dir'"; \ $(MKDIR_P) "$(DESTDIR)$(twistedtestsdir)/$$dir"; }; \ echo " $(INSTALL_DATA) $$xfiles '$(DESTDIR)$(twistedtestsdir)/$$dir'"; \ $(INSTALL_DATA) $$xfiles "$(DESTDIR)$(twistedtestsdir)/$$dir" || exit $$?; }; \ done uninstall-nobase_dist_twistedtestsDATA: @$(NORMAL_UNINSTALL) @list='$(nobase_dist_twistedtests_DATA)'; test -n "$(twistedtestsdir)" || list=; \ $(am__nobase_strip_setup); files=`$(am__nobase_strip)`; \ dir='$(DESTDIR)$(twistedtestsdir)'; $(am__uninstall_files_from_dir) install-nobase_nodist_twistedtestsDATA: $(nobase_nodist_twistedtests_DATA) @$(NORMAL_INSTALL) @list='$(nobase_nodist_twistedtests_DATA)'; test -n "$(twistedtestsdir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(twistedtestsdir)'"; \ $(MKDIR_P) "$(DESTDIR)$(twistedtestsdir)" || exit 1; \ fi; \ $(am__nobase_list) | while read dir files; do \ xfiles=; for file in $$files; do \ if test -f "$$file"; then xfiles="$$xfiles $$file"; \ else xfiles="$$xfiles $(srcdir)/$$file"; fi; done; \ test -z "$$xfiles" || { \ test "x$$dir" = x. || { \ echo " $(MKDIR_P) '$(DESTDIR)$(twistedtestsdir)/$$dir'"; \ $(MKDIR_P) "$(DESTDIR)$(twistedtestsdir)/$$dir"; }; \ echo " $(INSTALL_DATA) $$xfiles '$(DESTDIR)$(twistedtestsdir)/$$dir'"; \ $(INSTALL_DATA) $$xfiles "$(DESTDIR)$(twistedtestsdir)/$$dir" || exit $$?; }; \ done uninstall-nobase_nodist_twistedtestsDATA: @$(NORMAL_UNINSTALL) @list='$(nobase_nodist_twistedtests_DATA)'; test -n "$(twistedtestsdir)" || list=; \ $(am__nobase_strip_setup); files=`$(am__nobase_strip)`; \ dir='$(DESTDIR)$(twistedtestsdir)'; $(am__uninstall_files_from_dir) ID: $(am__tagged_files) $(am__define_uniq_tagged_files); mkid -fID $$unique tags: tags-am TAGS: tags tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) set x; \ here=`pwd`; \ $(am__define_uniq_tagged_files); \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ if test $$# -gt 0; then \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ "$$@" $$unique; \ else \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ $$unique; \ fi; \ fi ctags: ctags-am CTAGS: ctags ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) $(am__define_uniq_tagged_files); \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" cscopelist: cscopelist-am cscopelist-am: $(am__tagged_files) list='$(am__tagged_files)'; \ case "$(srcdir)" in \ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ *) sdir=$(subdir)/$(srcdir) ;; \ esac; \ for i in $$list; do \ if test -f "$$i"; then \ echo "$(subdir)/$$i"; \ else \ echo "$$sdir/$$i"; \ fi; \ done >> $(top_builddir)/cscope.files distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags distdir: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done check-am: all-am $(MAKE) $(AM_MAKEFLAGS) check-local check: $(BUILT_SOURCES) $(MAKE) $(AM_MAKEFLAGS) check-am all-am: Makefile $(PROGRAMS) $(SCRIPTS) $(DATA) installdirs: for dir in "$(DESTDIR)$(gabbledebugdir)" "$(DESTDIR)$(twistedtestsdir)" "$(DESTDIR)$(twistedtestsdir)" "$(DESTDIR)$(twistedtestsdir)" "$(DESTDIR)$(twistedtestsdir)"; do \ test -z "$$dir" || $(MKDIR_P) "$$dir"; \ done install: $(BUILT_SOURCES) $(MAKE) $(AM_MAKEFLAGS) install-am install-exec: install-exec-am install-data: install-data-am uninstall: uninstall-am install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-am install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: clean-generic: -test -z "$(CLEANFILES)" || rm -f $(CLEANFILES) distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." -test -z "$(BUILT_SOURCES)" || rm -f $(BUILT_SOURCES) clean: clean-am clean-am: clean-gabbledebugPROGRAMS clean-generic clean-libtool \ clean-noinstPROGRAMS mostlyclean-am distclean: distclean-am -rm -rf ./$(DEPDIR) -rm -f Makefile distclean-am: clean-am distclean-compile distclean-generic \ distclean-tags dvi: dvi-am dvi-am: html: html-am html-am: info: info-am info-am: install-data-am: install-gabbledebugPROGRAMS \ install-nobase_dist_twistedtestsDATA \ install-nobase_dist_twistedtestsSCRIPTS \ install-nobase_nodist_twistedtestsDATA \ install-nobase_nodist_twistedtestsSCRIPTS install-dvi: install-dvi-am install-dvi-am: install-exec-am: install-html: install-html-am install-html-am: install-info: install-info-am install-info-am: install-man: install-pdf: install-pdf-am install-pdf-am: install-ps: install-ps-am install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-am -rm -rf ./$(DEPDIR) -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-am mostlyclean-am: mostlyclean-compile mostlyclean-generic \ mostlyclean-libtool pdf: pdf-am pdf-am: ps: ps-am ps-am: uninstall-am: uninstall-gabbledebugPROGRAMS \ uninstall-nobase_dist_twistedtestsDATA \ uninstall-nobase_dist_twistedtestsSCRIPTS \ uninstall-nobase_nodist_twistedtestsDATA \ uninstall-nobase_nodist_twistedtestsSCRIPTS .MAKE: all check check-am install install-am install-strip .PHONY: CTAGS GTAGS TAGS all all-am check check-am check-local clean \ clean-gabbledebugPROGRAMS clean-generic clean-libtool \ clean-noinstPROGRAMS cscopelist-am ctags ctags-am distclean \ distclean-compile distclean-generic distclean-libtool \ distclean-tags distdir dvi dvi-am html html-am info info-am \ install install-am install-data install-data-am install-dvi \ install-dvi-am install-exec install-exec-am \ install-gabbledebugPROGRAMS install-html install-html-am \ install-info install-info-am install-man \ install-nobase_dist_twistedtestsDATA \ install-nobase_dist_twistedtestsSCRIPTS \ install-nobase_nodist_twistedtestsDATA \ install-nobase_nodist_twistedtestsSCRIPTS install-pdf \ install-pdf-am install-ps install-ps-am install-strip \ installcheck installcheck-am installdirs maintainer-clean \ maintainer-clean-generic mostlyclean mostlyclean-compile \ mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ tags tags-am uninstall uninstall-am \ uninstall-gabbledebugPROGRAMS \ uninstall-nobase_dist_twistedtestsDATA \ uninstall-nobase_dist_twistedtestsSCRIPTS \ uninstall-nobase_nodist_twistedtestsDATA \ uninstall-nobase_nodist_twistedtestsSCRIPTS @ENABLE_INSTALLED_TESTS_TRUE@ $(NULL) check-local: check-coding-style check-twisted check-twisted: $(BUILT_SOURCES) @WANT_TWISTED_TESTS_TRUE@ rm -f tools/core @WANT_TWISTED_TESTS_TRUE@ rm -f tools/vgcore.* @WANT_TWISTED_TESTS_TRUE@ rm -f tools/gabble-testing.log @WANT_TWISTED_TESTS_TRUE@ rm -f tools/strace.log @WANT_TWISTED_TESTS_TRUE@ if test -n "$$GABBLE_TEST_REFDBG"; then \ @WANT_TWISTED_TESTS_TRUE@ sleep=6; \ @WANT_TWISTED_TESTS_TRUE@ else \ @WANT_TWISTED_TESTS_TRUE@ sleep=$(CHECK_TWISTED_SLEEP); \ @WANT_TWISTED_TESTS_TRUE@ fi; \ @WANT_TWISTED_TESTS_TRUE@ failed=0; \ @WANT_TWISTED_TESTS_TRUE@ GABBLE_TEST_UNINSTALLED=1 \ @WANT_TWISTED_TESTS_TRUE@ GABBLE_TEST_SLEEP="--sleep=$$sleep" \ @WANT_TWISTED_TESTS_TRUE@ GABBLE_ABS_TOP_SRCDIR=@abs_top_srcdir@ \ @WANT_TWISTED_TESTS_TRUE@ GABBLE_ABS_TOP_BUILDDIR=@abs_top_builddir@ \ @WANT_TWISTED_TESTS_TRUE@ sh run-test.sh "$(TWISTED_TESTS)" || failed=1; \ @WANT_TWISTED_TESTS_TRUE@ if test -e tools/core; then\ @WANT_TWISTED_TESTS_TRUE@ echo -e "\033[0;31;1mCore dump exists: tools/core\033[0m";\ @WANT_TWISTED_TESTS_TRUE@ exit 1;\ @WANT_TWISTED_TESTS_TRUE@ fi; \ @WANT_TWISTED_TESTS_TRUE@ if test $$failed = 1; then\ @WANT_TWISTED_TESTS_TRUE@ exit 1;\ @WANT_TWISTED_TESTS_TRUE@ fi; @WANT_TWISTED_TESTS_FALSE@ @echo "Configured without Twisted test support. To enable them," @WANT_TWISTED_TESTS_FALSE@ @echo "ensure that these Python modules are available:" @WANT_TWISTED_TESTS_FALSE@ @echo " • twisted.words.xish.domish" @WANT_TWISTED_TESTS_FALSE@ @echo " • twisted.words.protocols.jabber" @WANT_TWISTED_TESTS_FALSE@ @echo " • twisted.internet.reactor" @WANT_TWISTED_TESTS_FALSE@ @echo "and then re-run configure." config.py: Makefile $(AM_V_GEN) { \ echo "PACKAGE_STRING = \"$(PACKAGE_STRING)\""; \ echo "CLIENT_TYPE = '$(CLIENT_TYPE)'"; \ echo "DEBUGGING = $(DEBUGGING_PYBOOL)"; \ echo "PLUGINS_ENABLED = $(PLUGINS_ENABLED_PYBOOL)"; \ echo "CHANNEL_TYPE_CALL_ENABLED = $(CHANNEL_TYPE_CALL_ENABLED_PYBOOL)"; \ echo "GOOGLE_RELAY_ENABLED = $(GOOGLE_RELAY_ENABLED_PYBOOL)"; \ echo "FILE_TRANSFER_ENABLED = $(FILE_TRANSFER_ENABLED_PYBOOL)"; \ echo "VOIP_ENABLED = $(VOIP_ENABLED_PYBOOL)"; \ echo "JINGLE_FILE_TRANSFER_ENABLED = $(JINGLE_FILE_TRANSFER_ENABLED_PYBOOL)"; \ } > $@ check-coding-style: @fail=0; \ if test -n "$(check_misc_sources)"; then \ tools_dir=$(top_srcdir)/tools \ sh $(top_srcdir)/tools/check-misc.sh \ $(addprefix $(srcdir)/,$(check_misc_sources)) || fail=1; \ fi; \ if test -n "$(check_c_sources)"; then \ tools_dir=$(top_srcdir)/tools \ sh $(top_srcdir)/tools/check-c-style.sh \ $(addprefix $(srcdir)/,$(check_c_sources)) || fail=1; \ fi;\ if test yes = "$(ENABLE_CODING_STYLE_CHECKS)"; then \ exit "$$fail";\ else \ exit 0;\ fi # the following used to be in tools/ gabble-twisted-tests.list: Makefile $(AM_V_GEN)echo $(TWISTED_TESTS) > $@ run-test.sh: run-test.sh.in Makefile $(AM_V_GEN)sed -e "s|[@]gabbletestsdir[@]|@gabbletestsdir@|g" \ -e "s|[@]PYTHON[@]|$(PYTHON)|g" \ -e "s|[@]TEST_PYTHON[@]|$(TEST_PYTHON)|g" \ $< > $@ @chmod +x $@ tools/run-gabble.sh: tools/run-gabble.sh.in Makefile @mkdir -p tools $(AM_V_GEN)sed \ -e "s|[@]gabbletestsdir[@]|@gabbletestsdir@|g" \ -e "s|[@]pluginexecdir[@]|@pluginexecdir@|g" \ -e "s|[@]libdir[@]|$(libdir)|g" \ $< > $@ @chmod +x $@ tools/exec-with-log.sh: tools/exec-with-log.sh.in $(MKDIR_P) tools $(AM_V_GEN)sed -e "s|[@]abs_top_builddir[@]|@abs_top_builddir@|g" \ -e "s|[@]abs_top_srcdir[@]|@abs_top_srcdir@|g" $< > $@ @chmod +x $@ # The wildcard % matches both config files! tools/%.conf: tools/%.conf.in Makefile $(MKDIR_P) tools/servicedir $(MKDIR_P) tools/servicedir-uninstalled $(AM_V_GEN)sed -e "s|[@]gabbletestsdir[@]|@gabbletestsdir@|g" \ -e "s|[@]abs_top_builddir[@]|@abs_top_builddir@|g" $< > $@ # We don't use the full filename for the .in because > 99 character filenames # in tarballs are non-portable (and automake 1.8 doesn't let us build # non-archaic tarballs) tools/servicedir/org.freedesktop.Telepathy.ConnectionManager.%.service: tools/servicedir/%.service.in Makefile $(MKDIR_P) tools/servicedir $(AM_V_GEN)sed -e "s|[@]gabbletestsdir[@]|@gabbletestsdir@|g" $< > $@ tools/servicedir-uninstalled/org.freedesktop.Telepathy.ConnectionManager.%.service: tools/servicedir-uninstalled/%.service.in $(MKDIR_P) tools/servicedir-uninstalled $(AM_V_GEN)sed -e "s|[@]abs_top_builddir[@]|@abs_top_builddir@|g" $< > $@ # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: telepathy-gabble-0.18.2/tests/twisted/vcard/0000755000175000017500000000000012312537051020740 5ustar00smcvsmcv00000000000000telepathy-gabble-0.18.2/tests/twisted/vcard/update-rejected.py0000644000175000017500000000233312200204333024346 0ustar00smcvsmcv00000000000000""" Regression test for fd.o #20442, where the XMPP error returned by a server that doesn't like the avatar you tried to set was not mapped to a TP error before being sent over the bus. """ from twisted.words.xish import domish from servicetest import call_async from gabbletest import exec_test, expect_and_handle_get_vcard, send_error_reply, sync_stream import ns import constants as cs def test(q, bus, conn, stream): expect_and_handle_get_vcard(q, stream) sync_stream(q, stream) call_async(q, conn.Avatars, 'SetAvatar', 'william shatner', 'image/x-actor-name') # Gabble request the last version of the vCard before changing it expect_and_handle_get_vcard(q, stream) set_vcard_event = q.expect('stream-iq', query_ns=ns.VCARD_TEMP, query_name='vCard', iq_type='set') iq = set_vcard_event.stanza error = domish.Element((None, 'error')) error['code'] = '400' error['type'] = 'modify' error.addElement((ns.STANZA, 'bad-request')) send_error_reply(stream, iq, error) event = q.expect('dbus-error', method='SetAvatar') assert event.error.get_dbus_name() == cs.INVALID_ARGUMENT, \ event.error.get_dbus_name() if __name__ == '__main__': exec_test(test) telepathy-gabble-0.18.2/tests/twisted/vcard/update-get-failed.py0000644000175000017500000000212412227000321024560 0ustar00smcvsmcv00000000000000 """ Test the case where the vCard get made prior to a vCard set fails. """ from servicetest import call_async, EventPattern from gabbletest import ( acknowledge_iq, elem, exec_test, make_result_iq, sync_stream) import constants as cs import ns def test(q, bus, conn, stream): event = q.expect('stream-iq', to=None, query_ns='vcard-temp', query_name='vCard') acknowledge_iq(stream, event.stanza) # Force Gabble to process the vCard before calling any methods. sync_stream(q, stream) handle = conn.GetSelfHandle() call_async(q, conn.Avatars, 'SetAvatar', 'william shatner', 'image/x-actor-name') event = q.expect('stream-iq', iq_type='get', to=None, query_ns='vcard-temp', query_name='vCard') reply = make_result_iq(stream, event.stanza) reply['type'] = 'error' reply.addChild(elem('error')( elem(ns.STANZA, 'forbidden')(), elem(ns.STANZA, 'text')(u'zomg whoops'))) stream.send(reply) event = q.expect('dbus-error', method='SetAvatar', name=cs.NOT_AVAILABLE) if __name__ == '__main__': exec_test(test) telepathy-gabble-0.18.2/tests/twisted/vcard/test-vcard-race.py0000644000175000017500000000523712227000321024273 0ustar00smcvsmcv00000000000000""" Regression test for https://bugs.freedesktop.org/show_bug.cgi?id=11201 - We try to set our own alias and our own avatar at about the same time - SetAliases requests vCard v1 - SetAvatar requests vCard v1 - SetAliases receives v1, writes back v2 with new NICKNAME - SetAvatar receives v1, writes back v2' with new PHOTO - Change to NICKNAME in v2 is lost """ import base64 from twisted.words.xish import xpath from servicetest import call_async, sync_dbus, assertEquals from gabbletest import ( exec_test, expect_and_handle_get_vcard, expect_and_handle_set_vcard, make_result_iq, sync_stream) import ns def test(q, bus, conn, stream): expect_and_handle_get_vcard(q, stream) # Ensure that Gabble's actually got the initial vCard reply; if it hasn't # processed it by the time we call SetAliases, the latter will wait for it # to reply and then set immediately. sync_stream(q, stream) handle = conn.GetSelfHandle() call_async(q, conn.Aliasing, 'SetAliases', {handle: 'Some Guy'}) # SetAliases requests vCard v1 get_vcard_event = q.expect('stream-iq', query_ns=ns.VCARD_TEMP, query_name='vCard', iq_type='get') iq = get_vcard_event.stanza vcard = iq.firstChildElement() assert vcard.name == 'vCard', vcard.toXml() call_async(q, conn.Avatars, 'SetAvatar', 'hello', 'image/png') # We don't expect Gabble to send a second vCard request, since there's one # outstanding. But we want to ensure that SetAvatar reaches Gabble before # the empty vCard does. sync_dbus(bus, q, conn) # Send back current empty vCard result = make_result_iq(stream, iq) # result already includes the from the query, which is all we need stream.send(result) def has_nickname_and_photo(vcard): nicknames = xpath.queryForNodes('/vCard/NICKNAME', vcard) assert nicknames is not None assert len(nicknames) == 1 assert str(nicknames[0]) == 'Some Guy' photos = xpath.queryForNodes('/vCard/PHOTO', vcard) assert photos is not None and len(photos) == 1, repr(photos) types = xpath.queryForNodes('/PHOTO/TYPE', photos[0]) binvals = xpath.queryForNodes('/PHOTO/BINVAL', photos[0]) assert types is not None and len(types) == 1, repr(types) assert binvals is not None and len(binvals) == 1, repr(binvals) assert str(types[0]) == 'image/png' got = str(binvals[0]).strip() exp = base64.b64encode('hello') assertEquals(exp, got) # Now Gabble should set a new vCard with both of the above changes. expect_and_handle_set_vcard(q, stream, has_nickname_and_photo) if __name__ == '__main__': exec_test(test) telepathy-gabble-0.18.2/tests/twisted/vcard/test-vcard-cache.py0000644000175000017500000000302112227000321024411 0ustar00smcvsmcv00000000000000""" Tests basic vCard caching functionality. """ from servicetest import call_async, EventPattern from gabbletest import exec_test, acknowledge_iq, sync_stream import constants as cs def test(q, bus, conn, stream): event = q.expect('stream-iq', to=None, query_ns='vcard-temp', query_name='vCard') acknowledge_iq(stream, event.stanza) # Force Gabble to process the vCard before calling any methods. sync_stream(q, stream) # Request our alias and avatar, expect them to be resolved from cache. handle = conn.GetSelfHandle() call_async(q, conn.Avatars, 'RequestAvatar', handle) call_async(q, conn.Aliasing, 'RequestAliases', [handle]) # FIXME - find out why RequestAliases returns before RequestAvatar even # though everything's cached. Probably due to queueing, which means # conn-avatars don't look into the cache before making a request. Prolly # should make vcard_request look into the cache itself, and return # immediately. Or not, if it's g_idle()'d. So it's better if conn-aliasing # look into the cache itself. r1, r2 = q.expect_many( EventPattern('dbus-return', method='RequestAliases'), EventPattern('dbus-error', method='RequestAvatar')) # Default alias is our jid assert r1.value[0] == ['test@localhost'] # We don't have a vCard yet assert r2.error.get_dbus_name() == cs.NOT_AVAILABLE, \ r2.error.get_dbus_name() assert r2.error.args[0] == 'contact vCard has no photo' if __name__ == '__main__': exec_test(test) telepathy-gabble-0.18.2/tests/twisted/vcard/test-set-alias.py0000644000175000017500000000365512227000321024150 0ustar00smcvsmcv00000000000000 """ Test alias setting support. """ from servicetest import EventPattern, assertEquals, assertLength from gabbletest import ( exec_test, acknowledge_iq, expect_and_handle_get_vcard, expect_and_handle_set_vcard, ) import constants as cs import ns def validate_pep_update(pep_update, expected_nickname): publish = pep_update.query.elements(uri=ns.PUBSUB, name='publish').next() assertEquals(ns.NICK, publish['node']) item = publish.elements(uri=ns.PUBSUB, name='item').next() nick = item.elements(uri=ns.NICK, name='nick').next() if expected_nickname is not None: assertEquals(expected_nickname, nick.children[0]) else: assertLength(0, nick.children) def test(q, bus, conn, stream): self_handle = conn.GetSelfHandle() conn.Aliasing.SetAliases({self_handle: 'lala'}) expect_and_handle_get_vcard(q, stream) pep_update = q.expect('stream-iq', iq_type='set', query_ns=ns.PUBSUB, query_name='pubsub') validate_pep_update(pep_update, 'lala') acknowledge_iq(stream, pep_update.stanza) def check(vCard): nickname = vCard.elements(uri=ns.VCARD_TEMP, name='NICKNAME').next() assertEquals('lala', nickname.children[0]) expect_and_handle_set_vcard(q, stream, check=check) event = q.expect('dbus-signal', signal='AliasesChanged', args=[[(self_handle, u'lala')]]) conn.Aliasing.SetAliases({self_handle: ''}) pep_update = q.expect('stream-iq', iq_type='set', query_ns=ns.PUBSUB, query_name='pubsub') validate_pep_update(pep_update, None) expect_and_handle_get_vcard(q, stream) def check(vCard): # The vCard should be empty, rather than having an empty # element. assertLength(0, vCard.children) expect_and_handle_set_vcard(q, stream, check=check) event = q.expect('dbus-signal', signal='AliasesChanged', args=[[(self_handle, u'test@localhost')]]) if __name__ == '__main__': exec_test(test) telepathy-gabble-0.18.2/tests/twisted/vcard/test-save-alias-to-vcard.py0000644000175000017500000000132112200204333026014 0ustar00smcvsmcv00000000000000""" Regression test. - the 'alias' connection parameter is set - our vCard doesn't have a NICKNAME field - we crash when trying to save a vcard with NICKNAME set to the alias parameter """ from gabbletest import ( exec_test, expect_and_handle_get_vcard, expect_and_handle_set_vcard) def test(q, bus, conn, stream): expect_and_handle_get_vcard(q, stream) def check_vcard(vcard): for e in vcard.elements(): if e.name == 'NICKNAME': assert str(e) == 'Some Guy', e.toXml() return assert False, vcard.toXml() expect_and_handle_set_vcard(q, stream, check_vcard) if __name__ == '__main__': exec_test(test, params={'alias': 'Some Guy'}) telepathy-gabble-0.18.2/tests/twisted/vcard/test-avatar-tokens.py0000644000175000017500000000322612227000321025037 0ustar00smcvsmcv00000000000000 """ Test GetAvatarTokens() and GetKnownAvatarTokens(). """ from twisted.words.xish import domish from servicetest import unwrap, EventPattern from gabbletest import exec_test, make_result_iq import ns import constants as cs def make_presence(jid, sha1sum): p = domish.Element((None, 'presence')) p['from'] = jid p['to'] = 'test@localhost/Resource' x = p.addElement((ns.VCARD_TEMP_UPDATE, 'x')) x.addElement('photo', content=sha1sum) return p def test(q, bus, conn, stream): event = q.expect('stream-iq', to=None, query_ns=ns.ROSTER, query_name='query') result = make_result_iq(stream, event.stanza) item = result.addElement('item') item['jid'] = 'amy@foo.com' item['subscription'] = 'both' item = result.addElement('item') item['jid'] = 'bob@foo.com' item['subscription'] = 'both' item = result.addElement('item') item['jid'] = 'che@foo.com' item['subscription'] = 'both' stream.send(result) stream.send(make_presence('amy@foo.com', 'SHA1SUM-FOR-AMY')) stream.send(make_presence('bob@foo.com', 'SHA1SUM-FOR-BOB')) stream.send(make_presence('che@foo.com', None)) q.expect('dbus-signal', signal='AvatarUpdated') handles = conn.RequestHandles(1, [ 'amy@foo.com', 'bob@foo.com', 'che@foo.com', 'daf@foo.com' ]) tokens = unwrap(conn.Avatars.GetAvatarTokens(handles)) assert tokens == ['SHA1SUM-FOR-AMY', 'SHA1SUM-FOR-BOB', '', ''] tokens = unwrap(conn.Avatars.GetKnownAvatarTokens(handles)) tokens = sorted(tokens.items()) assert tokens == [(2, 'SHA1SUM-FOR-AMY'), (3, 'SHA1SUM-FOR-BOB'), (4, u'')] if __name__ == '__main__': exec_test(test) telepathy-gabble-0.18.2/tests/twisted/vcard/test-avatar-retrieved.py0000644000175000017500000000230312227000321025520 0ustar00smcvsmcv00000000000000 """ Test that gabble emits only one AvatarRetrieved for multiple queued RequestAvatar calls for the same contact. """ import base64 from servicetest import EventPattern from gabbletest import exec_test, acknowledge_iq, make_result_iq import constants as cs def test(q, bus, conn, stream): iq_event = q.expect('stream-iq', to=None, query_ns='vcard-temp', query_name='vCard') acknowledge_iq(stream, iq_event.stanza) handle = conn.RequestHandles(1, ['bob@foo.com'])[0] conn.Avatars.RequestAvatars([handle]) conn.Avatars.RequestAvatars([handle]) conn.Avatars.RequestAvatars([handle]) conn.Avatars.RequestAvatars([handle]) iq_event = q.expect('stream-iq', to='bob@foo.com', query_ns='vcard-temp', query_name='vCard') iq = make_result_iq(stream, iq_event.stanza) vcard = iq.firstChildElement() photo = vcard.addElement('PHOTO') photo.addElement('TYPE', content='image/png') photo.addElement('BINVAL', content=base64.b64encode('hello')) stream.send(iq) event = q.expect('dbus-signal', signal='AvatarRetrieved') q.forbid_events([EventPattern('dbus-signal', signal='AvatarRetrieved')]) if __name__ == '__main__': exec_test(test) telepathy-gabble-0.18.2/tests/twisted/vcard/test-avatar.py0000644000175000017500000000174312227000321023540 0ustar00smcvsmcv00000000000000 """ Test avatar support. """ import base64 from servicetest import call_async, EventPattern from gabbletest import exec_test, acknowledge_iq, make_result_iq import constants as cs def test(q, bus, conn, stream): event = q.expect('stream-iq', to=None, query_ns='vcard-temp', query_name='vCard') acknowledge_iq(stream, event.stanza) handle = conn.RequestHandles(1, ['bob@foo.com'])[0] call_async(q, conn.Avatars, 'RequestAvatar', handle, byte_arrays=True) event = q.expect('stream-iq', iq_type='get', to='bob@foo.com', query_ns='vcard-temp', query_name='vCard') result = make_result_iq(stream, event.stanza) photo = result.firstChildElement().addElement('PHOTO') photo.addElement('TYPE', content='image/png') photo.addElement('BINVAL', content=base64.b64encode('hello')) stream.send(result) q.expect('dbus-return', method='RequestAvatar', value=('hello', 'image/png')) if __name__ == '__main__': exec_test(test) telepathy-gabble-0.18.2/tests/twisted/vcard/test-avatar-multiple-resources.py0000644000175000017500000000445212200204333027401 0ustar00smcvsmcv00000000000000 """ Test avatar support with multiple resources as defined in XEP-0153 section 4.3. """ import base64 from twisted.words.xish import domish from servicetest import call_async, EventPattern, sync_dbus from gabbletest import exec_test, acknowledge_iq, make_result_iq, sync_stream import constants as cs import ns def make_presence(jid, sha1sum): p = domish.Element((None, 'presence')) p['from'] = jid x = p.addElement((ns.VCARD_TEMP_UPDATE, 'x')) x.addElement('photo', content=sha1sum) return p def test(q, bus, conn, stream): event = q.expect('stream-iq', to=None, query_ns='vcard-temp', query_name='vCard') acknowledge_iq(stream, event.stanza) sync_stream(q, stream) sync_dbus(bus, q, conn) # A presence from a contact stream.send(make_presence('contact1@localhost/client', 'SHA1SUM-FOR-CONTACT1')) event = q.expect('dbus-signal', signal='AvatarUpdated') assert event.args[0] == 2, event.args assert event.args[1] == "SHA1SUM-FOR-CONTACT1", event.args AvatarRetrieved_event = EventPattern('dbus-signal', signal='AvatarRetrieved') AvatarUpdated_event = EventPattern('dbus-signal', signal='AvatarUpdated') StreamPresence_event = EventPattern('stream-presence') StreamIqVcard_event = EventPattern('stream-iq', query_ns='vcard-temp') # A presence from myself on another resource stream.send(make_presence('test@localhost/resource1', 'SHA1SUM-FOR-MYSELF-RES1')) q.forbid_events([AvatarRetrieved_event, AvatarUpdated_event]) stream_presence, stream_iq = q.expect_many( EventPattern('stream-presence'), EventPattern('stream-iq', to=None, query_ns='vcard-temp', query_name='vCard')) sync_dbus(bus, q, conn) q.unforbid_events([AvatarRetrieved_event, AvatarUpdated_event]) # If the server wrongly send a presence stanza with our resource, # AvatarUpdated must not be emitted q.forbid_events([StreamPresence_event, StreamIqVcard_event, AvatarRetrieved_event, AvatarUpdated_event]) stream.send(make_presence('test@localhost/Resource', 'SHA1SUM-FOR-MYSELF')) sync_dbus(bus, q, conn) sync_stream(q, stream) q.unforbid_events([StreamPresence_event, StreamIqVcard_event, AvatarRetrieved_event, AvatarUpdated_event]) if __name__ == '__main__': exec_test(test) telepathy-gabble-0.18.2/tests/twisted/vcard/test-avatar-async.py0000644000175000017500000002035012227000321024646 0ustar00smcvsmcv00000000000000 """ Test support for retrieving avatars asynchronously using RequestAvatars. """ import base64 import hashlib from twisted.words.xish import domish from servicetest import EventPattern, sync_dbus, assertEquals from gabbletest import (exec_test, acknowledge_iq, make_result_iq, sync_stream, send_error_reply, make_presence) import constants as cs import ns import dbus avatar_retrieved_event = EventPattern('dbus-signal', signal='AvatarRetrieved') avatar_request_event = EventPattern('stream-iq', query_ns='vcard-temp') def test_get_avatar(q, bus, conn, stream, contact, handle, in_cache=False): conn.Avatars.RequestAvatars([handle]) if in_cache: q.forbid_events([avatar_request_event]) else: iq_event = q.expect('stream-iq', to=contact, query_ns='vcard-temp', query_name='vCard') iq = make_result_iq(stream, iq_event.stanza) vcard = iq.firstChildElement() photo = vcard.addElement('PHOTO') photo.addElement('TYPE', content='image/png') photo.addElement('BINVAL', content=base64.b64encode('hello')) stream.send(iq) event = q.expect('dbus-signal', signal='AvatarRetrieved') assertEquals(handle, event.args[0]) assertEquals(hashlib.sha1('hello').hexdigest(), event.args[1]) assertEquals('hello', event.args[2]) assertEquals('image/png', event.args[3]) if in_cache: sync_stream(q, stream) q.unforbid_events([avatar_request_event]) def test(q, bus, conn, stream): iq_event = q.expect('stream-iq', to=None, query_ns='vcard-temp', query_name='vCard') # When we start, there is no avatar acknowledge_iq(stream, iq_event.stanza) self_handle = conn.GetSelfHandle() # Another resource confirms we have no avatar. We don't request our vCard # because we already know there is no avatar presence_stanza = make_presence('test@localhost/noavatar', to='test@localhost/Resource', show='away', status='At the pub', photo="") q.forbid_events([avatar_request_event, avatar_retrieved_event]) # Gabble must resist temptation to send vCard requests even with several # presence stanza sent! stream.send(presence_stanza) stream.send(presence_stanza) sync_stream(q, stream) # Twice because the vCard request is done in sync_stream(q, stream) # g_idle_add q.unforbid_events([avatar_request_event, avatar_retrieved_event]) # Request on the first contact. Test the cache. handle = conn.RequestHandles(cs.HT_CONTACT, ['bob@foo.com'])[0] test_get_avatar(q, bus, conn, stream, 'bob@foo.com', handle, in_cache=False) test_get_avatar(q, bus, conn, stream, 'bob@foo.com', handle, in_cache=True) # Request another vCard and get resource-constraint busy_contact = 'jean@busy-server.com' busy_handle = conn.RequestHandles(cs.HT_CONTACT, [busy_contact])[0] conn.Avatars.RequestAvatars([busy_handle]) iq_event = q.expect('stream-iq', to=busy_contact, query_ns='vcard-temp', query_name='vCard') iq = iq_event.stanza error = domish.Element((None, 'error')) error['code'] = '500' error['type'] = 'wait' error.addElement((ns.STANZA, 'resource-constraint')) q.forbid_events([avatar_retrieved_event, avatar_request_event]) send_error_reply(stream, iq, error) # Request the same vCard again during the suspended delay # We should not get the avatar conn.Avatars.RequestAvatars([busy_handle]) sync_stream(q, stream) sync_dbus(bus, q, conn) q.unforbid_events([avatar_retrieved_event, avatar_request_event]) # Request on a different contact, on another server # We should get the avatar handle = conn.RequestHandles(cs.HT_CONTACT, ['bob2@foo.com'])[0] test_get_avatar(q, bus, conn, stream, 'bob2@foo.com', handle) # Try again the contact on the busy server. # We should not get the avatar # Note: the timeout is 3 seconds for the test suites. We assume that # a few stanza with be processed fast enough to avoid the race. q.forbid_events([avatar_retrieved_event, avatar_request_event]) conn.Avatars.RequestAvatars([busy_handle]) sync_stream(q, stream) sync_dbus(bus, q, conn) q.unforbid_events([avatar_retrieved_event, avatar_request_event]) # After 3 seconds, we receive a new vCard request on the busy server iq_event = q.expect('stream-iq', to=busy_contact, query_ns='vcard-temp', query_name='vCard') iq = make_result_iq(stream, iq_event.stanza) vcard = iq.firstChildElement() photo = vcard.addElement('PHOTO') photo.addElement('TYPE', content='image/png') photo.addElement('BINVAL', content=base64.b64encode('hello')) stream.send(iq) event = q.expect('dbus-signal', signal='AvatarRetrieved') assertEquals(busy_handle, event.args[0]) assertEquals(hashlib.sha1('hello').hexdigest(), event.args[1]) assertEquals('hello', event.args[2]) assertEquals('image/png', event.args[3]) # Test with our own avatar test@localhost/Resource2 presence_stanza = make_presence('test@localhost/Resource2', to='test@localhost/Resource', show='away', status='At the pub', photo=hashlib.sha1(':-D').hexdigest()) stream.send(presence_stanza) iq_event = q.expect('stream-iq', to=None, query_ns='vcard-temp', query_name='vCard') iq = make_result_iq(stream, iq_event.stanza) vcard = iq.firstChildElement() photo = vcard.addElement('PHOTO') photo.addElement('TYPE', content='image/png') photo.addElement('BINVAL', content=base64.b64encode(':-D')) # do not send the vCard reply now. First, send another presence. q.forbid_events([avatar_request_event]) stream.send(presence_stanza) sync_stream(q, stream) # Now send the reply. stream.send(iq) # Which results in an AvatarUpdated signal event = q.expect('dbus-signal', signal='AvatarUpdated') assertEquals(self_handle, event.args[0]) assertEquals(hashlib.sha1(':-D').hexdigest(), event.args[1]) # So Gabble has the right hash, and no need to ask the vCard again stream.send(presence_stanza) sync_stream(q, stream) q.unforbid_events([avatar_request_event]) # But if the hash is different, the vCard is asked again presence_stanza = make_presence('test@localhost/Resource2', to='test@localhost/Resource', show='away', status='At the pub', photo=hashlib.sha1('\o/').hexdigest()) stream.send(presence_stanza) iq_event = q.expect('stream-iq', to=None, query_ns='vcard-temp', query_name='vCard') iq = make_result_iq(stream, iq_event.stanza) vcard = iq.firstChildElement() photo = vcard.addElement('PHOTO') photo.addElement('TYPE', content='image/png') photo.addElement('BINVAL', content=base64.b64encode('\o/')) stream.send(iq) event = q.expect('dbus-signal', signal='AvatarUpdated') assertEquals(self_handle, event.args[0]) assertEquals(hashlib.sha1('\o/').hexdigest(), event.args[1]) # Gabble must reply without asking the vCard to the server because the # avatar must be in the cache q.forbid_events([avatar_request_event]) data, mime = conn.Avatars.RequestAvatar(self_handle, byte_arrays=True) assertEquals('\o/', data) data, mime = conn.Avatars.RequestAvatar(handle, byte_arrays=True) assertEquals('hello', data) q.unforbid_events([avatar_request_event]) # First, ensure the pipeline is full contacts = ['random_user_%s@bigserver.com' % i for i in range(1, 100) ] handles = conn.RequestHandles(cs.HT_CONTACT, contacts) conn.Avatars.RequestAvatars(handles) # Then, request yet another avatar. The request will time out before # the IQ is sent, which used to trigger a crash in Gabble # (LP#445847). So, we assert that the error is NotAvailable (rather # than the error returned when the service crashes). try: conn.Avatars.RequestAvatar(handles[-1]) except dbus.DBusException, e: assertEquals(cs.NOT_AVAILABLE, e.get_dbus_name()) else: assert False if __name__ == '__main__': exec_test(test) telepathy-gabble-0.18.2/tests/twisted/vcard/test-alias.py0000644000175000017500000000311612227000321023347 0ustar00smcvsmcv00000000000000 """ Test alias support. """ from servicetest import call_async, EventPattern from gabbletest import exec_test, make_result_iq, acknowledge_iq import constants as cs def test(q, bus, conn, stream): event = q.expect('stream-iq', to=None, query_ns='vcard-temp', query_name='vCard') acknowledge_iq(stream, event.stanza) handle = conn.RequestHandles(1, ['bob@foo.com'])[0] call_async(q, conn.Aliasing, 'RequestAliases', [handle]) # Nack PEP query. event = q.expect('stream-iq', to='bob@foo.com', iq_type='get', query_ns='http://jabber.org/protocol/pubsub', query_name='pubsub') items = event.query.firstChildElement() assert items.name == 'items' assert items['node'] == "http://jabber.org/protocol/nick" result = make_result_iq(stream, event.stanza) result['type'] = 'error' error = result.addElement('error') error['type'] = 'auth' error.addElement('forbidden', 'urn:ietf:params:xml:ns:xmpp-stanzas') stream.send(result) event = q.expect('stream-iq', to='bob@foo.com', query_ns='vcard-temp', query_name='vCard') result = make_result_iq(stream, event.stanza) vcard = result.firstChildElement() vcard.addElement('NICKNAME', content='Bobby') stream.send(result) q.expect('dbus-signal', signal='AliasesChanged', args=[[(handle, u'Bobby')]]) q.expect('dbus-return', method='RequestAliases', value=(['Bobby'],)) # A second request should be satisfied from the cache. assert conn.Aliasing.RequestAliases([handle]) == ['Bobby'] if __name__ == '__main__': exec_test(test) telepathy-gabble-0.18.2/tests/twisted/vcard/test-alias-pep.py0000644000175000017500000000415212227000321024132 0ustar00smcvsmcv00000000000000 """ Test PEP alias support. """ from servicetest import call_async, EventPattern, assertEquals, assertLength from gabbletest import exec_test, make_result_iq, acknowledge_iq, elem import constants as cs import ns def test(q, bus, conn, stream): event = q.expect('stream-iq', to=None, query_ns='vcard-temp', query_name='vCard') acknowledge_iq(stream, event.stanza) handle = conn.RequestHandles(cs.HT_CONTACT, ['bob@foo.com'])[0] call_async(q, conn.Aliasing, 'RequestAliases', [handle]) event = q.expect('stream-iq', to='bob@foo.com', iq_type='get', query_ns=ns.PUBSUB, query_name='pubsub') items = event.query.firstChildElement() assertEquals('items', items.name) assertEquals(ns.NICK, items['node']) result = make_result_iq(stream, event.stanza) pubsub = result.firstChildElement() items = pubsub.addElement('items') items['node'] = ns.NICK item = items.addElement('item') item.addElement('nick', ns.NICK, content='Bobby') stream.send(result) q.expect('dbus-signal', signal='AliasesChanged', args=[[(handle, u'Bobby')]]) q.expect('dbus-return', method='RequestAliases', value=(['Bobby'],)) # A second request should be satisfied from the cache. assertEquals(['Bobby'], conn.Aliasing.RequestAliases([handle])) # Bobby grows up, decides he hates his nickname. # This is a regression test for # , where this would # crash Gabble. message = elem('message', from_='bob@foo.com')( elem((ns.PUBSUB_EVENT), 'event')( elem('items', node=ns.NICK)( elem('item')( elem(ns.NICK, 'nick') ) ) ) ) stream.send(message.toXml()) event = q.expect('dbus-signal', signal='AliasesChanged') aliases = event.args[0] assertLength(1, aliases) h, a = aliases[0] assertEquals(handle, h) # The contact explicitly cleared their PEP nick; Gabble should fall back to # their JID. assertEquals(a, 'bob@foo.com') if __name__ == '__main__': exec_test(test) telepathy-gabble-0.18.2/tests/twisted/vcard/test-alias-message.py0000644000175000017500000000762212227000321024777 0ustar00smcvsmcv00000000000000# vim: set fileencoding=utf-8 : """ Tests grabbing aliases from incoming messages, as described by . This test has nothing to do with vcards, just like a lot of other tests in the vcard/ directory. """ from servicetest import EventPattern, assertEquals, wrap_channel from gabbletest import exec_test, elem, expect_and_handle_get_vcard from mucutil import join_muc, make_muc_presence import constants as cs import ns def test(q, bus, conn, stream): expect_and_handle_get_vcard(q, stream) jid = u'bora.horza.gobuchul@culture.lit' alias = u'Horza' handle = conn.RequestHandles(cs.HT_CONTACT, [jid])[0] # We don't have an interesting alias for Horza assertEquals({handle: jid}, conn.Aliasing.GetAliases([handle])) # Horza sends us a message containing his preferred nickname. stream.send( elem('message', from_=jid, type='chat')( elem('body')(u"It's a long story."), elem(ns.NICK, 'nick')(alias) )) _, mr = q.expect_many( EventPattern('dbus-signal', signal='AliasesChanged', args=[[(handle, alias)]]), EventPattern('dbus-signal', signal='MessageReceived'), ) channel = wrap_channel(bus.get_object(conn.bus_name, mr.path), 'Text') # So now we know his alias. assertEquals({handle: alias}, conn.Aliasing.GetAliases([handle])) # Presumably to avoid non-contacts being able to make Gabble's memory # footprint grow forever, Gabble throws the alias away when we close the # channel. header = mr.args[0][0] channel.Text.AcknowledgePendingMessages([header['pending-message-id']]) channel.Close() # FIXME: Gabble forgets the alias, but it doesn't signal that it has done # so; it probably should. # q.expect('dbus-signal', signal='AliasesChanged', args=[[(handle, jid)]]) assertEquals({handle: jid}, conn.Aliasing.GetAliases([handle])) # Basically the same test, but in a MUC. # # It's a bit questionable whether this ought to work. # doesn't have anything to # say about including in messages; it does talk about including # in your MUC presence, which is actually equally sketchy! If I join # a muc with the resource '/wjt', and you join with resource '/ohai' but # say that your nickname is 'wjt', what on earth is Alice's UI supposed to # show when you send a message? # # But anyway, at the time of writing this "works", so I'm adding a test to # make it explicit. Perhaps in future we might change this test to verify # that it doesn't "work". room_jid = 'clear-air-turbulence@culture.lit' _, muc, _, _ = join_muc(q, bus, conn, stream, room_jid) bob_jid = room_jid + '/bob' bob_handle = conn.RequestHandles(cs.HT_CONTACT, [bob_jid])[0] assertEquals({bob_handle: 'bob'}, conn.Aliasing.GetAliases([bob_handle])) stream.send( elem('message', from_=bob_jid, type='groupchat')( elem('body')(u'My religion dies with me.'), elem(ns.NICK, 'nick')(alias), )) q.expect_many( EventPattern('dbus-signal', signal='AliasesChanged', args=[[(bob_handle, alias)]]), EventPattern('dbus-signal', signal='MessageReceived'),) assertEquals({bob_handle: alias}, conn.Aliasing.GetAliases([bob_handle])) muc.Close() q.expect('stream-presence', to=room_jid + '/test') echo = make_muc_presence('member', 'none', room_jid, 'test') echo['type'] = 'unavailable' stream.send(echo) q.expect('dbus-signal', signal='ChannelClosed') # FIXME: Gabble forgets the alias, but it doesn't signal that it has done # so; it probably should. # q.expect('dbus-signal', signal='AliasesChanged', # args=[[(bob_handle, 'bob')]]) assertEquals({bob_handle: 'bob'}, conn.Aliasing.GetAliases([bob_handle])) if __name__ == '__main__': exec_test(test) telepathy-gabble-0.18.2/tests/twisted/vcard/test-alias-empty-vcard.py0000644000175000017500000000272512227000321025605 0ustar00smcvsmcv00000000000000 """ Regression test for a bug where receiving an empty vCard could make RequesAliases fail to return. """ from servicetest import call_async, EventPattern from gabbletest import exec_test, make_result_iq, acknowledge_iq import constants as cs def test(q, bus, conn, stream): event = q.expect('stream-iq', to=None, query_ns='vcard-temp', query_name='vCard') acknowledge_iq(stream, event.stanza) handle = conn.RequestHandles(1, ['bob@foo.com'])[0] call_async(q, conn.Aliasing, 'RequestAliases', [handle]) # Nack PEP query. event = q.expect('stream-iq', to='bob@foo.com', iq_type='get', query_ns='http://jabber.org/protocol/pubsub', query_name='pubsub') items = event.query.firstChildElement() assert items.name == 'items' assert items['node'] == "http://jabber.org/protocol/nick" result = make_result_iq(stream, event.stanza) result['type'] = 'error' error = result.addElement('error') error['type'] = 'auth' error.addElement('forbidden', 'urn:ietf:params:xml:ns:xmpp-stanzas') stream.send(result) event = q.expect('stream-iq', to='bob@foo.com', query_ns='vcard-temp', query_name='vCard') acknowledge_iq(stream, event.stanza) q.expect('dbus-return', method='RequestAliases', value=([u'bob@foo.com'],)) # A second request should be satisfied from the cache. assert conn.Aliasing.RequestAliases([handle]) == ['bob@foo.com'] if __name__ == '__main__': exec_test(test) telepathy-gabble-0.18.2/tests/twisted/vcard/supported-fields.py0000644000175000017500000001002412200204333024566 0ustar00smcvsmcv00000000000000# -*- coding: utf-8 -*- """Feature test for ContactInfo.SupportedFields """ # Copyright © 2010 Collabora Ltd. # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # # 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 from servicetest import (EventPattern, assertEquals, call_async) from gabbletest import (exec_test, GoogleXmlStream) import constants as cs PARAMS_EXACT = cs.CONTACT_INFO_FIELD_FLAG_PARAMETERS_EXACT OVERWRITTEN_BY_NICKNAME = cs.CONTACT_INFO_FIELD_FLAG_OVERWRITTEN_BY_NICKNAME UNLIMITED = 0xffffffffL def types(s): ret = ['type=%s' % t for t in s.split()] ret.sort() return ret def check_google_props(props): assertEquals(cs.CONTACT_INFO_FLAG_CAN_SET, props['ContactInfoFlags']) sf = props['SupportedFields'] sf.sort() for f in sf: f[1].sort() # type-parameters assertEquals([ ('fn', [], PARAMS_EXACT | OVERWRITTEN_BY_NICKNAME, 1), ('n', [], PARAMS_EXACT, 1), ('url', [], PARAMS_EXACT, UNLIMITED), ], sf) def check_normal_props(props): assertEquals(cs.CONTACT_INFO_FLAG_CAN_SET, props['ContactInfoFlags']) sf = props['SupportedFields'] sf.sort() for f in sf: f[1].sort() # type-parameters assertEquals([ ('adr', types('home work postal parcel dom intl pref'), 0, UNLIMITED), ('bday', [], PARAMS_EXACT, UNLIMITED), ('email', types('home work internet pref x400'), 0, UNLIMITED), ('fn', [], PARAMS_EXACT, 1), ('geo', [], PARAMS_EXACT, 1), ('label', types('home work postal parcel dom intl pref'), 0, UNLIMITED), ('mailer', [], PARAMS_EXACT, UNLIMITED), ('n', [], PARAMS_EXACT, 1), ('nickname', [], PARAMS_EXACT | OVERWRITTEN_BY_NICKNAME, UNLIMITED), ('note', [], PARAMS_EXACT, UNLIMITED), ('org', [], PARAMS_EXACT, UNLIMITED), ('prodid', [], PARAMS_EXACT, UNLIMITED), ('rev', [], PARAMS_EXACT, UNLIMITED), ('role', [], PARAMS_EXACT, UNLIMITED), ('sort-string', [], PARAMS_EXACT, UNLIMITED), ('tel', types('home work voice fax pager msg cell video bbs ' 'modem isdn pcs pref'), 0, UNLIMITED), ('title', [], PARAMS_EXACT, UNLIMITED), ('tz', [], PARAMS_EXACT, UNLIMITED), ('uid', [], PARAMS_EXACT, UNLIMITED), ('url', [], PARAMS_EXACT, UNLIMITED), ('x-desc', [], PARAMS_EXACT, UNLIMITED), ('x-jabber', [], PARAMS_EXACT, UNLIMITED), ], sf) def test_google(q, bus, conn, stream): test(q, bus, conn, stream, is_google=True) def test(q, bus, conn, stream, is_google=False): props = conn.GetAll(cs.CONN_IFACE_CONTACT_INFO, dbus_interface=cs.PROPERTIES_IFACE) check_normal_props(props) conn.Connect() q.expect('dbus-signal', signal='StatusChanged', args=[cs.CONN_STATUS_CONNECTED, cs.CSR_REQUESTED]) props = conn.GetAll(cs.CONN_IFACE_CONTACT_INFO, dbus_interface=cs.PROPERTIES_IFACE) if is_google: check_google_props(props) # on a Google server, we can't use most vCard fields call_async(q, conn.ContactInfo, 'SetContactInfo', [('x-jabber', [], ['wee.ninja@collabora.co.uk'])]) q.expect('dbus-error', method='SetContactInfo', name=cs.INVALID_ARGUMENT) else: check_normal_props(props) if __name__ == '__main__': exec_test(test, do_connect=False) exec_test(test_google, protocol=GoogleXmlStream, do_connect=False) telepathy-gabble-0.18.2/tests/twisted/vcard/set-set-disconnect.py0000644000175000017500000000170312200204333025014 0ustar00smcvsmcv00000000000000 """ Regression test for crash when disconnecting in the middle of a set. """ from servicetest import EventPattern, call_async, sync_dbus from gabbletest import exec_test, acknowledge_iq, expect_and_handle_get_vcard, sync_stream, disconnect_conn import constants as cs def test(q, bus, conn, stream): expect_and_handle_get_vcard(q, stream) sync_stream(q, stream) call_async( q, conn.Avatars, 'SetAvatar', 'Guy.brush', 'image/x-mighty-pirate') expect_and_handle_get_vcard(q, stream) iq_event = q.expect( 'stream-iq', iq_type='set', query_ns='vcard-temp', query_name='vCard') call_async( q, conn.Avatars, 'SetAvatar', 'LeChuck.brush', 'image/x-ghost-pirate') disconnect_conn(q, conn, stream) q.expect('dbus-error', method='SetAvatar', name=cs.NOT_AVAILABLE) q.expect('dbus-error', method='SetAvatar', name=cs.NOT_AVAILABLE) sync_dbus(bus, q, conn) if __name__ == '__main__': exec_test(test) telepathy-gabble-0.18.2/tests/twisted/vcard/set-contact-info.py0000644000175000017500000003126512200204333024464 0ustar00smcvsmcv00000000000000 """ Test ContactInfo setting support. """ from servicetest import (EventPattern, call_async, assertEquals, assertLength, assertContains, sync_dbus) from gabbletest import exec_test, acknowledge_iq, sync_stream import constants as cs from twisted.words.xish import xpath from twisted.words.protocols.jabber.client import IQ def repeat_previous_vcard(stream, iq, previous): result = IQ(stream, 'result') result['id'] = iq['id'] to = iq.getAttribute('to') if to is not None: result["from"] = to result.addRawXml(previous.firstChildElement().toXml()) stream.send(result) def test(q, bus, conn, stream): event = q.expect('stream-iq', to=None, query_ns='vcard-temp', query_name='vCard') call_async(q, conn.ContactInfo, 'SetContactInfo', [(u'fn', [], [u'Wee Ninja']), (u'n', [], [u'Ninja', u'Wee', u'', u'', u'-san']), (u'org', [], ['Collabora, Ltd.']), (u'adr', ['type=work','type=postal','type=parcel'], ['', '', '11 Kings Parade', 'Cambridge', 'Cambridgeshire', 'CB2 1SJ', 'UK']), (u'label', ['type=work'], [ '11 Kings Parade\n' 'Cambridge\n' 'Cambridgeshire\n' 'CB2 1SJ\n' 'UK\n']), (u'tel', ['type=voice','type=work'], ['+44 1223 362967']), (u'tel', ['type=voice','type=work'], ['+44 7700 900753']), (u'email', ['type=internet','type=pref'], ['wee.ninja@collabora.co.uk']), (u'email', ['type=internet'], ['wee.ninja@example.com']), (u'x-jabber', [], ['wee.ninja@collabora.co.uk']), (u'x-jabber', [], ['wee.ninja@example.com']), (u'url', [], ['http://www.thinkgeek.com/geektoys/plush/8823/']), (u'nickname', [], [u'HR Ninja']), (u'nickname', [], [u'Enforcement Ninja'])]) # We don't acknowledge the initial vCard get until we're sure that Gabble's # received our call to SetContactInfo. This ensures that it will issue a # set when we send the reply, rather than issuing another get to ensure the # vCard is really, absolutely up to date before editing it. sync_dbus(bus, q, conn) acknowledge_iq(stream, event.stanza) vcard_set_event = q.expect('stream-iq', iq_type='set', query_ns='vcard-temp', query_name='vCard') assertLength(2, xpath.queryForNodes('/iq/vCard/NICKNAME', vcard_set_event.stanza)) nicknames = [] for nickname in xpath.queryForNodes('/iq/vCard/NICKNAME', vcard_set_event.stanza): nicknames.append(str(nickname)) assertEquals(['HR Ninja', 'Enforcement Ninja'], nicknames) assertEquals(None, xpath.queryForNodes('/iq/vCard/PHOTO', vcard_set_event.stanza)) assertLength(1, xpath.queryForNodes('/iq/vCard/N', vcard_set_event.stanza)) assertEquals('Wee', xpath.queryForString('/iq/vCard/N/GIVEN', vcard_set_event.stanza)) assertEquals('Ninja', xpath.queryForString('/iq/vCard/N/FAMILY', vcard_set_event.stanza)) assertEquals('-san', xpath.queryForString('/iq/vCard/N/SUFFIX', vcard_set_event.stanza)) assertEquals('Wee Ninja', xpath.queryForString('/iq/vCard/FN', vcard_set_event.stanza)) assertLength(1, xpath.queryForNodes('/iq/vCard/ORG', vcard_set_event.stanza)) assertEquals('Collabora, Ltd.', xpath.queryForString('/iq/vCard/ORG/ORGNAME', vcard_set_event.stanza)) assertEquals(None, xpath.queryForNodes('/iq/vCard/ORG/ORGUNIT', vcard_set_event.stanza)) assertLength(1, xpath.queryForNodes('/iq/vCard/LABEL', vcard_set_event.stanza)) lines = xpath.queryForNodes('/iq/vCard/LABEL/LINE', vcard_set_event.stanza) assertLength(5, lines) for i, exp_line in enumerate(['11 Kings Parade', 'Cambridge', 'Cambridgeshire', 'CB2 1SJ', 'UK']): assertEquals(exp_line, str(lines[i])) assertLength(2, xpath.queryForNodes('/iq/vCard/TEL', vcard_set_event.stanza)) for tel in xpath.queryForNodes('/iq/vCard/TEL', vcard_set_event.stanza): assertLength(1, xpath.queryForNodes('/TEL/NUMBER', tel)) assertContains(xpath.queryForString('/TEL/NUMBER', tel), ('+44 1223 362967', '+44 7700 900753')) assertLength(1, xpath.queryForNodes('/TEL/VOICE', tel)) assertLength(1, xpath.queryForNodes('/TEL/WORK', tel)) assertLength(2, xpath.queryForNodes('/iq/vCard/EMAIL', vcard_set_event.stanza)) for email in xpath.queryForNodes('/iq/vCard/EMAIL', vcard_set_event.stanza): assertContains(xpath.queryForString('/EMAIL/USERID', email), ('wee.ninja@example.com', 'wee.ninja@collabora.co.uk')) assertLength(1, xpath.queryForNodes('/EMAIL/INTERNET', email)) if 'collabora' in xpath.queryForString('/EMAIL/USERID', email): assertLength(1, xpath.queryForNodes('/EMAIL/PREF', email)) else: assertEquals(None, xpath.queryForNodes('/EMAIL/PREF', email)) assertLength(2, xpath.queryForNodes('/iq/vCard/JABBERID', vcard_set_event.stanza)) for jid in xpath.queryForNodes('/iq/vCard/JABBERID', vcard_set_event.stanza): assertContains(xpath.queryForString('/JABBERID', jid), ('wee.ninja@example.com', 'wee.ninja@collabora.co.uk')) acknowledge_iq(stream, vcard_set_event.stanza) q.expect_many( EventPattern('dbus-return', method='SetContactInfo'), EventPattern('dbus-signal', signal='AliasesChanged', predicate=lambda e: e.args[0][0][1] == 'HR Ninja'), EventPattern('dbus-signal', signal='ContactInfoChanged'), ) # Exercise various invalid SetContactInfo operations sync_stream(q, stream) sync_dbus(bus, q, conn) forbidden = [EventPattern('stream-iq', query_ns='vcard-temp')] q.forbid_events(forbidden) # unknown field call_async(q, conn.ContactInfo, 'SetContactInfo', [('x-salary', [], ['547 espressos per month'])]) q.expect('dbus-error', method='SetContactInfo', name=cs.INVALID_ARGUMENT) # not enough values for a simple field call_async(q, conn.ContactInfo, 'SetContactInfo', [('fn', [], [])]) q.expect('dbus-error', method='SetContactInfo', name=cs.INVALID_ARGUMENT) # too many values for a simple field call_async(q, conn.ContactInfo, 'SetContactInfo', [('fn', [], ['Wee', 'Ninja'])]) q.expect('dbus-error', method='SetContactInfo', name=cs.INVALID_ARGUMENT) # unsupported type-parameter for a simple field call_async(q, conn.ContactInfo, 'SetContactInfo', [('fn', ['language=ja'], ['Wee Ninja'])]) q.expect('dbus-error', method='SetContactInfo', name=cs.INVALID_ARGUMENT) # unsupported type-parameter for a structured field call_async(q, conn.ContactInfo, 'SetContactInfo', [(u'n', ['language=ja'], [u'Ninja', u'Wee', u'', u'', u'-san'])]) q.expect('dbus-error', method='SetContactInfo', name=cs.INVALID_ARGUMENT) # unsupported type-parameter for LABEL call_async(q, conn.ContactInfo, 'SetContactInfo', [('label', ['language=en'], ['Collabora Ltd.\n11 Kings Parade'])]) q.expect('dbus-error', method='SetContactInfo', name=cs.INVALID_ARGUMENT) # not enough values for LABEL call_async(q, conn.ContactInfo, 'SetContactInfo', [('label', [], [])]) q.expect('dbus-error', method='SetContactInfo', name=cs.INVALID_ARGUMENT) # too many values for LABEL call_async(q, conn.ContactInfo, 'SetContactInfo', [('label', [], ['11 Kings Parade', 'Cambridge'])]) q.expect('dbus-error', method='SetContactInfo', name=cs.INVALID_ARGUMENT) # unsupported type-parameter for ORG call_async(q, conn.ContactInfo, 'SetContactInfo', [('org', ['language=en'], ['Collabora Ltd.'])]) q.expect('dbus-error', method='SetContactInfo', name=cs.INVALID_ARGUMENT) # empty ORG call_async(q, conn.ContactInfo, 'SetContactInfo', [('org', [], [])]) q.expect('dbus-error', method='SetContactInfo', name=cs.INVALID_ARGUMENT) # not enough values for N call_async(q, conn.ContactInfo, 'SetContactInfo', [('n', [], ['Ninja'])]) q.expect('dbus-error', method='SetContactInfo', name=cs.INVALID_ARGUMENT) # too many values for N call_async(q, conn.ContactInfo, 'SetContactInfo', [('n', [], 'what could it mean if you have too many field values?'.split())]) q.expect('dbus-error', method='SetContactInfo', name=cs.INVALID_ARGUMENT) q.unforbid_events(forbidden) # Following a reshuffle, Company Policy Enforcement is declared to be # a sub-department within Human Resources, and the ninja no longer # qualifies for a company phone vcard_in = [(u'fn', [], [u'Wee Ninja']), (u'n', [], [u'Ninja', u'Wee', u'', u'', u'-san']), (u'org', [], ['Collabora, Ltd.', 'Human Resources', 'Company Policy Enforcement']), (u'adr', ['type=work','type=postal','type=parcel'], ['', '', '11 Kings Parade', 'Cambridge', 'Cambridgeshire', 'CB2 1SJ', 'UK']), (u'tel', ['type=voice','type=work'], ['+44 1223 362967']), (u'email', ['type=internet','type=pref'], ['wee.ninja@collabora.co.uk']), (u'email', ['type=internet'], ['wee.ninja@example.com']), (u'url', [], ['http://www.thinkgeek.com/geektoys/plush/8823/']), (u'nickname', [], [u'HR Ninja']), (u'nickname', [], [u'Enforcement Ninja'])] call_async(q, conn.ContactInfo, 'SetContactInfo', vcard_in) event = q.expect('stream-iq', iq_type='get', query_ns='vcard-temp', query_name='vCard') repeat_previous_vcard(stream, event.stanza, vcard_set_event.stanza) _, vcard_set_event = q.expect_many( EventPattern('dbus-signal', signal='ContactInfoChanged'), EventPattern('stream-iq', iq_type='set', query_ns='vcard-temp', query_name='vCard'), ) assertLength(1, xpath.queryForNodes('/iq/vCard/ORG', vcard_set_event.stanza)) assertEquals('Collabora, Ltd.', xpath.queryForString('/iq/vCard/ORG/ORGNAME', vcard_set_event.stanza)) units = xpath.queryForNodes('/iq/vCard/ORG/ORGUNIT', vcard_set_event.stanza) assertLength(2, units) for i, exp_unit in enumerate(['Human Resources', 'Company Policy Enforcement']): assertEquals(exp_unit, str(units[i])) assertLength(1, xpath.queryForNodes('/iq/vCard/TEL', vcard_set_event.stanza)) for tel in xpath.queryForNodes('/iq/vCard/TEL', vcard_set_event.stanza): assertLength(1, xpath.queryForNodes('/TEL/NUMBER', tel)) assertEquals('+44 1223 362967', xpath.queryForString('/TEL/NUMBER', tel)) assertLength(1, xpath.queryForNodes('/TEL/VOICE', tel)) assertLength(1, xpath.queryForNodes('/TEL/WORK', tel)) acknowledge_iq(stream, vcard_set_event.stanza) _, event = q.expect_many( EventPattern('dbus-return', method='SetContactInfo'), EventPattern('dbus-signal', signal='ContactInfoChanged'), ) vcard_out = event.args[1][:] # the only change we expect to see is that perhaps the fields are # re-ordered, and perhaps the types on the 'tel' are re-ordered assertEquals(vcard_in[4][0], 'tel') vcard_in[4][1].sort() assertEquals(vcard_out[4][0], 'tel') vcard_out[4][1].sort() assertEquals(vcard_in, vcard_out) # Finally, the ninja decides that publishing his contact details is not # very ninja-like, and decides to be anonymous. The first (most important) # of his nicknames from the old vCard is kept, due to nickname's dual role # as ContactInfo and the alias. call_async(q, conn.ContactInfo, 'SetContactInfo', []) event = q.expect('stream-iq', iq_type='get', query_ns='vcard-temp', query_name='vCard') repeat_previous_vcard(stream, event.stanza, vcard_set_event.stanza) vcard_set_event = q.expect('stream-iq', iq_type='set', query_ns='vcard-temp', query_name='vCard') assertLength(1, xpath.queryForNodes('/iq/vCard/*', vcard_set_event.stanza)) assertEquals('HR Ninja', xpath.queryForString('/iq/vCard/NICKNAME', vcard_set_event.stanza)) acknowledge_iq(stream, vcard_set_event.stanza) q.expect_many( EventPattern('dbus-return', method='SetContactInfo'), EventPattern('dbus-signal', signal='ContactInfoChanged'), ) if __name__ == '__main__': exec_test(test) telepathy-gabble-0.18.2/tests/twisted/vcard/set-avatar.py0000644000175000017500000000660712200204333023360 0ustar00smcvsmcv00000000000000""" Tests the very simple case of "setting your own avatar". """ from twisted.words.xish import xpath from servicetest import call_async, assertEquals from gabbletest import ( exec_test, expect_and_handle_get_vcard, expect_and_handle_set_vcard, ) import base64 import functools def test(q, bus, conn, stream, image_data, mime_type): call_async(q, conn.Avatars, 'SetAvatar', image_data, mime_type) expect_and_handle_get_vcard(q, stream) def check(vcard): assertEquals(mime_type, xpath.queryForString('/vCard/PHOTO/TYPE', vcard)) binval = xpath.queryForString('/vCard/PHOTO/BINVAL', vcard) # says: # # 5. The image data MUST conform to the base64Binary datatype and thus # be encoded in accordance with Section 6.8 of RFC 2045, which # recommends that base64 data should have lines limited to at most # 76 characters in length. lines = binval.split('\n') for line in lines: assert len(line) <= 76, line assertEquals(image_data, base64.decodestring(binval)) expect_and_handle_set_vcard(q, stream, check=check) q.expect('dbus-return', method='SetAvatar') def test_little_avatar(q, bus, conn, stream): test(q, bus, conn, stream, image_data='Guy.brush', mime_type='image/x-mighty-pirate') def test_massive_avatar(q, bus, conn, stream): """Regression test for , where a too-small buffer was allocated if the base64-encoded avatar spanned multiple lines. """ avatar = '\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00\xec\x00\x00\x00\x94\x04\x03\x00\x00\x00b\x8c\xd8\x8b\x00\x00\x00\x18PLTEap\x00\xff\x99\x99\xff\xff\xff\xff3\x99\xff\xcc\x99\x99\x99\x99\xff\x99\xff\x00\x00\x00\xcdt\xca\x11\x00\x00\x00\x01tRNS\x00@\xe6\xd8f\x00\x00\x00\x01bKGD\x00\x88\x05\x1dH\x00\x00\x00\tpHYs\x00\x00\x0b\x13\x00\x00\x0b\x13\x01\x00\x9a\x9c\x18\x00\x00\x00\x07tIME\x07\xdc\x0b\x0e\x0f"%\xd9\xcd\xe5\x8c\x00\x00\x01\x88IDATx\xda\xed\xdb=n\xc20\x14\xc0\xf1l\xcc\x0c\\ K\x0f\x10\t.P\x95\xb9\x95\x10\x07\xa0jW\x96\xe0\xeb\x97\x90 ?\xdb\x0f;\x1f.i\xa3\xff\x9bBb\xfb\x17$\xec\xbc\xd8\xa6(ba\xa6E12`aa\xc7\xb2\xd7z\xaf\xd3\xe2\xda\xc2\x19\x16vN\xd68\xe6\xdb\xd7\xb0\xf8\xb4U\xf7\x83\xfa\x11,,l\x1eVm\xf8TU\xd5\xd6\x1eo\xc3\x12\xb0\xb0\xb03\xb1\n2$`aa\xe7c\xe3\xf1\xdd\xbeU^\xc2[\x14\xec\xaa-t\x86\x85\xfd\x93lC\xd6\x87[\x1c-\xedu \xe3\x16\x82\x85\x85\x9d\xca\xea\xb3\x93QVd\xa8\x82\xedZ\xbd\x84\xec\xc1)\x00\x0b\x0b\x9b\x83\x15\xa7\xee\xf2\x88\xe1\xc2k\xc5\x1d&`a\x9f\xc0\x1a\xf1#\x175\\\xb6ID\xfb\xb0\xf1\x80\x85\x85\xcd\xc1Z\xb9\xf6\xe4\xd8p\xd1\xdc\xc4n )\xbf\x19,\xecrX\x99@\x8aK\x1d\xfbp\xbe\xd1\xeb\xb3\x1feY\xbe\xa87R\xb6W`aa\x97\xc1\xda\xb9W\xd3g\x94\xd2_2\xfd|\xbc\xfb\xa8d\x12\xb0\xb0\xbf\xc7\xfa}Ie\xc5\n\xc4\x9d}_\xdfb\x93z\xce\xae\xbb\x80\x85\x85\xcd\xcf\x86\x9b[\xf4Er\x85=\xba\xd9\xb68\xacaaa\x9f\xc8F7\x93\xea\x8fz%\x06\xadh\xc2\xc2\xe6`\xdd\x9c5\x1d\xfb\x9e\x136\x89\xcd-\xb0\xb0\xb0cYwM\xdb\xe4\x8c\xc4n4X\xd8e\xb1\xb6\xf4\xca}`\x86\x9dO)\xe0e\x8e\xe9\xed\x9c\xb0\xb0\xb0\x13\xd8\xb0\x87\x8b\xb9\xd7\xc7\xd5\x95B\xfd\xffP\x0e\x0b\xfb?\xd9\x1f\xd9\xb2to\xf9Q\xd6\xee\x00\x00\x00\x00IEND\xaeB`\x82' test(q, bus, conn, stream, image_data=avatar, mime_type='image/png') if __name__ == '__main__': exec_test(test_little_avatar) exec_test(test_massive_avatar) telepathy-gabble-0.18.2/tests/twisted/vcard/refresh-contact-info.py0000644000175000017500000000554312227000321025327 0ustar00smcvsmcv00000000000000 """ Test ContactInfo support. """ from servicetest import call_async, EventPattern, assertEquals, sync_dbus from gabbletest import exec_test, acknowledge_iq, make_result_iq import constants as cs import dbus def test(q, bus, conn, stream): event = q.expect('stream-iq', to=None, query_ns='vcard-temp', query_name='vCard') acknowledge_iq(stream, event.stanza) handle = conn.RequestHandles(1, ['bob@foo.com'])[0] call_async(q, conn.ContactInfo, 'RefreshContactInfo', [handle]) event = q.expect('stream-iq', to='bob@foo.com', query_ns='vcard-temp', query_name='vCard') result = make_result_iq(stream, event.stanza) result.firstChildElement().addElement('FN', content='Bob') n = result.firstChildElement().addElement('N') n.addElement('GIVEN', content='Bob') result.firstChildElement().addElement('NICKNAME', content=r'bob,bob1\,,bob2,bob3\,bob4') label = result.firstChildElement().addElement('LABEL') label.addElement('LINE', content='42 West Wallaby Street') label.addElement('LINE', content="Bishop's Stortford\n") label.addElement('LINE', content='Huntingdon') org = result.firstChildElement().addElement('ORG') # ORG is a sequence of decreasingly large org.units, starting # with the organisation name itself (but here we've moved the org name # to the end, to make sure that works.) org.addElement('ORGUNIT', content='Dept. of Examples') org.addElement('ORGUNIT', content='Exemplary Team') org.addElement('ORGNAME', content='Collabora Ltd.') stream.send(result) q.expect('dbus-signal', signal='ContactInfoChanged', args=[handle, [(u'fn', [], [u'Bob']), (u'n', [], [u'', u'Bob', u'', u'', u'']), (u'nickname', [], [r'bob,bob1\,,bob2,bob3\,bob4']), # LABEL comes out as a single blob of text (u'label', [], ['42 West Wallaby Street\n' "Bishop's Stortford\n" 'Huntingdon\n']), # ORG is a sequence of decreasingly large org.units, starting # with the organisation (u'org', [], [u'Collabora Ltd.', u'Dept. of Examples', u'Exemplary Team']), ]]) # ContactInfoChanged should not be signalled again forbidden = [EventPattern('dbus-signal', signal='ContactInfoChanged')] q.forbid_events(forbidden) # Refresh the contact info again; gabble should contact the server again call_async(q, conn.ContactInfo, 'RefreshContactInfo', [handle]) event = q.expect('stream-iq', to='bob@foo.com', query_ns='vcard-temp', query_name='vCard') sync_dbus(bus, q, conn) q.unforbid_events(forbidden) if __name__ == '__main__': exec_test(test) telepathy-gabble-0.18.2/tests/twisted/vcard/redundant-set.py0000644000175000017500000000315712200204333024063 0ustar00smcvsmcv00000000000000""" Regression test for , where Gabble redundantly re-set the user's own vCard even if nothing had changed. """ from servicetest import EventPattern, sync_dbus from gabbletest import exec_test, make_result_iq, sync_stream, GoogleXmlStream import constants as cs def not_google(q, bus, conn, stream): test(q, bus, conn, stream, False) def google(q, bus, conn, stream): test(q, bus, conn, stream, True) def test(q, bus, conn, stream, is_google): iq_event = q.expect('stream-iq', to=None, query_ns='vcard-temp', query_name='vCard') result = make_result_iq(stream, iq_event.stanza) # Testing reveals that Google's vCard server does not actually support # NICKNAME (or indeed any fields beside FN, N and PHOTO): if you set a # vCard including it, it accepts the request but strips out the unsupported # fields. So if the server looks like Google, it's a redundant set # operation on FN that we want to avoid. if is_google: vcard = result.firstChildElement() vcard.addElement('FN', content='oh hello there') else: vcard = result.firstChildElement() vcard.addElement('NICKNAME', content='oh hello there') stream.send(result) q.forbid_events([ EventPattern('stream-iq', iq_type='set', query_ns='vcard-temp', query_name='vCard') ]) sync_stream(q, stream) sync_dbus(bus, q, conn) if __name__ == '__main__': exec_test(not_google, params={ 'alias': 'oh hello there' }) exec_test(google, params={ 'alias': 'oh hello there' }, protocol=GoogleXmlStream) telepathy-gabble-0.18.2/tests/twisted/vcard/overlapping-sets.py0000644000175000017500000001271612227000321024611 0ustar00smcvsmcv00000000000000 import base64 from twisted.words.xish import xpath import constants as cs from servicetest import (EventPattern, call_async, sync_dbus, assertEquals, assertLength) from gabbletest import ( acknowledge_iq, exec_test, expect_and_handle_get_vcard, make_result_iq, sync_stream, disconnect_conn) import ns def test(q, bus, conn, stream): # Initial vCard request. Respond only after we call SetAliases(). vcard_get_event = q.expect('stream-iq', iq_type='get', to=None, query_ns=ns.VCARD_TEMP, query_name='vCard') sync_stream(q, stream) handle = conn.GetSelfHandle() call_async(q, conn.Aliasing, 'SetAliases', {handle: 'Robert the Bruce'}) sync_dbus(bus, q, conn) acknowledge_iq(stream, vcard_get_event.stanza) # Gabble sets a new vCard with our nickname. vcard_set_event = q.expect('stream-iq', iq_type='set', query_ns=ns.VCARD_TEMP, query_name='vCard') assertEquals('Robert the Bruce', xpath.queryForString('/iq/vCard/NICKNAME', vcard_set_event.stanza)) assertEquals(None, xpath.queryForNodes('/iq/vCard/PHOTO', vcard_set_event.stanza)) assertEquals(None, xpath.queryForNodes('/iq/vCard/FN', vcard_set_event.stanza)) assertEquals(None, xpath.queryForNodes('/iq/vCard/N', vcard_set_event.stanza)) # Before the server replies, the user sets their avatar call_async(q, conn.Avatars, 'SetAvatar', 'hello', 'image/png') sync_dbus(bus, q, conn) # This acknowledgement is for the nickname acknowledge_iq(stream, vcard_set_event.stanza) hello_binval = base64.b64encode('hello') # This sets the avatar vcard_set_event = q.expect('stream-iq', iq_type='set', query_ns=ns.VCARD_TEMP, query_name='vCard') assertEquals('Robert the Bruce', xpath.queryForString('/iq/vCard/NICKNAME', vcard_set_event.stanza)) assertLength(1, xpath.queryForNodes('/iq/vCard/PHOTO', vcard_set_event.stanza)) assertEquals('image/png', xpath.queryForString('/iq/vCard/PHOTO/TYPE', vcard_set_event.stanza)) binval = xpath.queryForString('/iq/vCard/PHOTO/BINVAL', vcard_set_event.stanza) assertEquals(hello_binval, binval.strip()) assertEquals(None, xpath.queryForNodes('/iq/vCard/FN', vcard_set_event.stanza)) assertEquals(None, xpath.queryForNodes('/iq/vCard/N', vcard_set_event.stanza)) # Before the server replies, the user sets their ContactInfo call_async(q, conn.ContactInfo, 'SetContactInfo', [(u'fn', [], [u'King Robert I']), (u'n', [], [u'de Brus', u'Robert', u'', u'King', u'']), (u'nickname', [], [u'Bob'])]) sync_dbus(bus, q, conn) # This acknowledgement is for the avatar; SetAvatar won't happen # until this has acknowledge_iq(stream, vcard_set_event.stanza) # This sets the ContactInfo vcard_set_event, _ = q.expect_many( EventPattern('stream-iq', iq_type='set', query_ns=ns.VCARD_TEMP, query_name='vCard'), EventPattern('dbus-return', method='SetAvatar')) assertEquals('Bob', xpath.queryForString('/iq/vCard/NICKNAME', vcard_set_event.stanza)) assertLength(1, xpath.queryForNodes('/iq/vCard/PHOTO', vcard_set_event.stanza)) assertEquals('image/png', xpath.queryForString('/iq/vCard/PHOTO/TYPE', vcard_set_event.stanza)) binval = xpath.queryForString('/iq/vCard/PHOTO/BINVAL', vcard_set_event.stanza) assertEquals(hello_binval, binval.strip()) assertLength(1, xpath.queryForNodes('/iq/vCard/N', vcard_set_event.stanza)) assertEquals('Robert', xpath.queryForString('/iq/vCard/N/GIVEN', vcard_set_event.stanza)) assertEquals('de Brus', xpath.queryForString('/iq/vCard/N/FAMILY', vcard_set_event.stanza)) assertEquals('King', xpath.queryForString('/iq/vCard/N/PREFIX', vcard_set_event.stanza)) assertEquals('King Robert I', xpath.queryForString('/iq/vCard/FN', vcard_set_event.stanza)) # Before the server replies, the user unsets their avatar call_async(q, conn.Avatars, 'SetAvatar', '', '') sync_dbus(bus, q, conn) # This acknowledgement is for the ContactInfo; SetContactInfo won't happen # until this has acknowledge_iq(stream, vcard_set_event.stanza) vcard_set_event, _ = q.expect_many( EventPattern('stream-iq', iq_type='set', query_ns=ns.VCARD_TEMP, query_name='vCard'), EventPattern('dbus-return', method='SetContactInfo')) assertEquals('Bob', xpath.queryForString('/iq/vCard/NICKNAME', vcard_set_event.stanza)) assertEquals(None, xpath.queryForNodes('/iq/vCard/PHOTO', vcard_set_event.stanza)) assertLength(1, xpath.queryForNodes('/iq/vCard/N', vcard_set_event.stanza)) assertEquals('Robert', xpath.queryForString('/iq/vCard/N/GIVEN', vcard_set_event.stanza)) assertEquals('de Brus', xpath.queryForString('/iq/vCard/N/FAMILY', vcard_set_event.stanza)) assertEquals('King', xpath.queryForString('/iq/vCard/N/PREFIX', vcard_set_event.stanza)) assertEquals('King Robert I', xpath.queryForString('/iq/vCard/FN', vcard_set_event.stanza)) # This acknowledgement is for the avatar; SetAvatar won't finish # until this is received acknowledge_iq(stream, vcard_set_event.stanza) q.expect('dbus-return', method='SetAvatar') # Now Gabble gets disconnected. sync_stream(q, stream) disconnect_conn(q, conn, stream) if __name__ == '__main__': exec_test(test) telepathy-gabble-0.18.2/tests/twisted/vcard/item-not-found.py0000644000175000017500000000235012200204333024145 0ustar00smcvsmcv00000000000000""" Regression test for where Gabble didn't understand that just means the user doesn't have a vCard yet, and so it can go right ahead and set one. """ from twisted.words.xish import domish from servicetest import call_async from gabbletest import ( exec_test, sync_stream, send_error_reply, expect_and_handle_set_vcard, ) import ns def expect_get_and_send_item_not_found(q, stream): get_vcard_event = q.expect('stream-iq', query_ns=ns.VCARD_TEMP, query_name='vCard', iq_type='get') error = domish.Element((None, 'error')) error['type'] = 'cancel' error.addElement((ns.STANZA, 'item-not-found')) send_error_reply(stream, get_vcard_event.stanza, error) def test(q, bus, conn, stream): expect_get_and_send_item_not_found(q, stream) sync_stream(q, stream) call_async( q, conn.Avatars, 'SetAvatar', 'Guy.brush', 'image/x-mighty-pirate') # Gabble checks again, but we still don't have a vCard expect_get_and_send_item_not_found(q, stream) # Never mind! It creates a new one. expect_and_handle_set_vcard(q, stream) q.expect('dbus-return', method='SetAvatar') if __name__ == '__main__': exec_test(test) telepathy-gabble-0.18.2/tests/twisted/vcard/get-contact-info.py0000644000175000017500000000566112227000321024451 0ustar00smcvsmcv00000000000000 """ Test ContactInfo support. """ from servicetest import call_async, EventPattern, assertEquals from gabbletest import exec_test, acknowledge_iq, make_result_iq import constants as cs import dbus def test(q, bus, conn, stream): event = q.expect('stream-iq', to=None, query_ns='vcard-temp', query_name='vCard') acknowledge_iq(stream, event.stanza) # returning an empty vcard will cause ContactInfoChanged to fire q.expect('dbus-signal', signal='ContactInfoChanged') handle = conn.RequestHandles(1, ['bob@foo.com'])[0] call_async(q, conn.ContactInfo, 'RefreshContactInfo', [handle]) event = q.expect('stream-iq', to='bob@foo.com', query_ns='vcard-temp', query_name='vCard') result = make_result_iq(stream, event.stanza) result.firstChildElement().addElement('FN', content='Bob') n = result.firstChildElement().addElement('N') n.addElement('GIVEN', content='Bob') result.firstChildElement().addElement('NICKNAME', content=r'bob,bob1\,,bob2,bob3\,bob4') label = result.firstChildElement().addElement('LABEL') label.addElement('LINE', content='42 West Wallaby Street') label.addElement('LINE', content="Bishop's Stortford\n") label.addElement('LINE', content='Huntingdon') org = result.firstChildElement().addElement('ORG') # ORG is a sequence of decreasingly large org.units, starting # with the organisation name itself (but here we've moved the org name # to the end, to make sure that works.) org.addElement('ORGUNIT', content='Dept. of Examples') org.addElement('ORGUNIT', content='Exemplary Team') org.addElement('ORGNAME', content='Collabora Ltd.') stream.send(result) q.expect('dbus-signal', signal='ContactInfoChanged') contact_info = [(u'fn', [], [u'Bob']), (u'n', [], [u'', u'Bob', u'', u'', u'']), (u'nickname', [], [r'bob,bob1\,,bob2,bob3\,bob4']), # LABEL comes out as a single blob of text (u'label', [], ['42 West Wallaby Street\n' "Bishop's Stortford\n" 'Huntingdon\n']), # ORG is a sequence of decreasingly large org.units, starting # with the organisation (u'org', [], [u'Collabora Ltd.', u'Dept. of Examples', u'Exemplary Team']), ] # The request should be satisfied from the cache. assertEquals( {handle: contact_info}, conn.ContactInfo.GetContactInfo([handle])) # check the ContactAttribute assertEquals( {handle: {cs.CONN_IFACE_CONTACT_INFO + '/info': contact_info, 'org.freedesktop.Telepathy.Connection/contact-id': 'bob@foo.com'}}, conn.Contacts.GetContactAttributes([handle], [cs.CONN_IFACE_CONTACT_INFO], False)) if __name__ == '__main__': exec_test(test) telepathy-gabble-0.18.2/tests/twisted/vcard/disconnect-during-pep.py0000644000175000017500000000225712227000321025507 0ustar00smcvsmcv00000000000000""" Regression test for https://bugs.freedesktop.org/show_bug.cgi?id=31412 """ import dbus from servicetest import call_async, EventPattern, sync_dbus from gabbletest import (exec_test, make_result_iq, acknowledge_iq, disconnect_conn) import constants as cs def test(q, bus, conn, stream): event = q.expect('stream-iq', to=None, query_ns='vcard-temp', query_name='vCard') acknowledge_iq(stream, event.stanza) handle = conn.RequestHandles(cs.HT_CONTACT, ['bob@foo.com'])[0] call_async(q, conn.Aliasing, 'RequestAliases', [handle]) # First, Gabble sends a PEP query event = q.expect('stream-iq', to='bob@foo.com', iq_type='get', query_ns='http://jabber.org/protocol/pubsub', query_name='pubsub') # We disconnect too soon to get a reply disconnect_conn(q, conn, stream) # fd.o #31412 was that while the request pipeline was shutting down, # it would give the PEP query an error; the aliasing code would # respond by falling back to vCard via the request pipeline, which # was no longer there, *crash*. # check that Gabble hasn't crashed sync_dbus(bus, q, conn) if __name__ == '__main__': exec_test(test) telepathy-gabble-0.18.2/tests/twisted/vcard/clear-avatar.py0000644000175000017500000000137512200204333023650 0ustar00smcvsmcv00000000000000""" Tests the very simple case of "clearing your own avatar". """ from servicetest import call_async from gabbletest import ( exec_test, expect_and_handle_get_vcard, expect_and_handle_set_vcard, current_vcard ) def test(q, bus, conn, stream): photo = current_vcard.addElement((None, 'PHOTO')) photo.addElement((None, 'TYPE')).addContent('image/fake') photo.addElement((None, 'BINVAL')).addContent('NYANYANYANYANYAN') call_async(q, conn.Avatars, 'ClearAvatar') expect_and_handle_get_vcard(q, stream) def check(vcard): assert len(vcard.children) == 0, vcard.toXml() expect_and_handle_set_vcard(q, stream, check=check) q.expect('dbus-return', method='ClearAvatar') if __name__ == '__main__': exec_test(test) telepathy-gabble-0.18.2/tests/twisted/tubes/0000755000175000017500000000000012312537051020763 5ustar00smcvsmcv00000000000000telepathy-gabble-0.18.2/tests/twisted/tubes/tubetestutil.py0000644000175000017500000003425512227000321024071 0ustar00smcvsmcv00000000000000""" Helper functions for writing tubes tests """ import errno import os import socket import sys import tempfile import dbus from servicetest import unwrap, assertContains, EventProtocolClientFactory,\ EventProtocolFactory, assertEquals, EventProtocol, EventPattern from gabbletest import exec_test import constants as cs import bytestream import ns from twisted.internet import reactor from twisted.internet.protocol import Factory, Protocol from twisted.internet.error import CannotListenError from twisted.internet import tcp _to_cleanup = [] def cleanup(): for f in _to_cleanup: try: os.remove(f) except OSError: pass # worse things have happened del _to_cleanup[:] def check_tube_in_tubes(tube, tubes): """ Check that 'tube' is in 'tubes', which should be the return value of ListTubes(). tube[0] may be None to check that a new-style tube is represented on the old interface (because you don't know what its id is in those cases) """ utube = unwrap(tube) if tube[0] is None: for t in tubes: if tube[1:] == t[1:]: return else: for t in tubes: if tube[0] != t[0]: continue pair = "\n %s,\n %s" % (utube, unwrap(t)) assert tube[1] == t[1], "self handles don't match: %s" % pair assert tube[2] == t[2], "tube types don't match: %s" % pair assert tube[3] == t[3], "services don't match: %s " % pair assert tube[4] == t[4], "parameters don't match: %s" % pair assert tube[5] == t[5], "states don't match: %s" % pair return assert False, "tube %s not in %s" % (unwrap (tube), unwrap (tubes)) def check_conn_properties(q, conn, channel_list=None): """ Check that Connection.Interface.Requests.Channels matches channel_list, and that RequestableChannelClasses contains the expected tube types. """ properties = conn.GetAll( cs.CONN_IFACE_REQUESTS, dbus_interface=cs.PROPERTIES_IFACE) if channel_list == None: assert properties.get('Channels') == [], properties['Channels'] else: for i in channel_list: assert i in properties['Channels'], \ (i, properties['Channels']) # 1-1 StreamTube channel assert ({cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_STREAM_TUBE, cs.TARGET_HANDLE_TYPE: cs.HT_CONTACT }, [cs.TARGET_HANDLE, cs.TARGET_ID, cs.STREAM_TUBE_SERVICE] ) in properties.get('RequestableChannelClasses'),\ properties['RequestableChannelClasses'] # muc StreamTube channel assert ({cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_STREAM_TUBE, cs.TARGET_HANDLE_TYPE: cs.HT_ROOM }, [cs.TARGET_HANDLE, cs.TARGET_ID, cs.STREAM_TUBE_SERVICE] ) in properties.get('RequestableChannelClasses'),\ properties['RequestableChannelClasses'] # 1-1 D-Bus tube channel assert ({cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_DBUS_TUBE, cs.TARGET_HANDLE_TYPE: cs.HT_CONTACT}, [cs.TARGET_HANDLE, cs.TARGET_ID, cs.DBUS_TUBE_SERVICE_NAME] ) in properties.get('RequestableChannelClasses'),\ properties['RequestableChannelClasses'] # muc D-Bus tube channel assert ({cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_DBUS_TUBE, cs.TARGET_HANDLE_TYPE: cs.HT_ROOM}, [cs.TARGET_HANDLE, cs.TARGET_ID, cs.DBUS_TUBE_SERVICE_NAME] ) in properties.get('RequestableChannelClasses'),\ properties['RequestableChannelClasses'] def check_NewChannel_signal(args, channel_type, chan_path, contact_handle, suppress_handler): """ Checks the first argument, a tuple of arguments from NewChannel, matches the other arguments. """ if chan_path is not None: assert args[0] == chan_path, (args, chan_path) assert args[1] == channel_type, (args, channel_type) assert args[2] == cs.HT_CONTACT, (args, cs.HT_CONTACT) assert args[3] == contact_handle, (args, contact_handle) assert args[4] == suppress_handler, (args, suppress_handler) def check_NewChannels_signal(conn, args, channel_type, chan_path, contact_handle, contact_id, initiator_handle): """ Checks the first argument, a one-tuple of arguments from NewChannels, matches the other arguments. """ assert len(args) == 1, args assert len(args[0]) == 1 # one channel path, props = args[0][0] assert path == chan_path, (emitted_path, chan_path) assert props[cs.CHANNEL_TYPE] == channel_type, (props, channel_type) assert props[cs.TARGET_HANDLE_TYPE] == cs.HT_CONTACT, props assert props[cs.TARGET_HANDLE] == contact_handle, (props, contact_handle) assert props[cs.TARGET_ID] == contact_id, (props, contact_id) assert props[cs.REQUESTED] == True, props assert props[cs.INITIATOR_HANDLE] == initiator_handle, \ (props, initiator_handle) assert props[cs.INITIATOR_ID] == 'test@localhost', props # check that the newly announced channel is in the channels list all_channels = conn.Get(cs.CONN_IFACE_REQUESTS, 'Channels', dbus_interface=cs.PROPERTIES_IFACE, byte_arrays=True) assertContains((path, props), all_channels) def check_platform_socket_types(sock_types): assertContains(cs.SOCKET_ADDRESS_TYPE_IPV4, sock_types) assertContains(cs.SOCKET_ADDRESS_TYPE_IPV6, sock_types) if os.name == 'posix': # true on at least Linux assertContains(cs.SOCKET_ADDRESS_TYPE_UNIX, sock_types) def check_channel_properties(q, bus, conn, channel, channel_type, contact_handle, contact_id, state=None): """ Checks the D-Bus properties of a 1-1 Tubes, StreamTube or DBusTube channel, initiated by the test user """ # Check o.fd.T.Channel properties. channel_props = channel.GetAll(cs.CHANNEL, dbus_interface=cs.PROPERTIES_IFACE) assert channel_props.get('TargetHandle') == contact_handle, \ (channel_props.get('TargetHandle'), contact_handle) assert channel_props.get('TargetHandleType') == cs.HT_CONTACT, \ channel_props.get('TargetHandleType') assert channel_props.get('ChannelType') == channel_type, \ channel_props.get('ChannelType') assert 'Interfaces' in channel_props, channel_props assert cs.CHANNEL_IFACE_GROUP not in \ channel_props['Interfaces'], \ channel_props['Interfaces'] assert channel_props['TargetID'] == contact_id assert channel_props['Requested'] == True assert channel_props['InitiatorID'] == 'test@localhost' assert channel_props['InitiatorHandle'] == conn.GetSelfHandle() if channel_type == cs.CHANNEL_TYPE_TUBES: assert state is None assert len(channel_props['Interfaces']) == 0, channel_props['Interfaces'] supported_socket_types = channel.GetAvailableStreamTubeTypes() else: assert state is not None tube_props = channel.GetAll(cs.CHANNEL_IFACE_TUBE, dbus_interface=cs.PROPERTIES_IFACE) assert tube_props['State'] == state # no strict check but at least check the properties exist assert tube_props['Parameters'] is not None assert channel_props['Interfaces'] == \ dbus.Array([cs.CHANNEL_IFACE_TUBE], signature='s'), \ channel_props['Interfaces'] if channel_type == cs.CHANNEL_TYPE_STREAM_TUBE: supported_socket_types = channel.Get(cs.CHANNEL_TYPE_STREAM_TUBE, 'SupportedSocketTypes', dbus_interface=cs.PROPERTIES_IFACE) else: supported_socket_types = None if supported_socket_types is not None: check_platform_socket_types(supported_socket_types) class Echo(EventProtocol): """ A trivial protocol that just echoes back whatever you send it, in lowercase. """ def __init__(self, queue=None, block_reading=False): EventProtocol.__init__(self, queue, block_reading) self.echoed = True def dataReceived(self, data): EventProtocol.dataReceived(self, data) if self.echoed: self.transport.write(data.lower()) class EchoFactory(EventProtocolFactory): def _create_protocol(self): return Echo(self.queue, self.block_reading) def set_up_echo(q, address_type, block_reading=False, streamfile='stream'): """ Sets up an instance of Echo listening on a socket of type @address_type """ factory = EchoFactory(q, block_reading) return create_server(q, address_type, factory, streamfile=streamfile) # Twisted doesn't set the REUSEADDR option on client sockets. # As we need this option to be able to bind successively on the same port # during the tests, we define our own client and connector to be able to set # this option. class MyTCPClient(tcp.Client): def createInternetSocket(self): s = tcp.Client.createInternetSocket(self) s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) return s class MyTCPConnector(tcp.Connector): def _makeTransport(self): return MyTCPClient(self.host, self.port, self.bindAddress, self, self.reactor) def connect_socket(q, address_type, address, access_control, access_control_param): factory = EventProtocolClientFactory(q) if address_type == cs.SOCKET_ADDRESS_TYPE_UNIX: reactor.connectUNIX(address, factory) elif address_type == cs.SOCKET_ADDRESS_TYPE_IPV4: ip, port = address assert port > 0 if access_control == cs.SOCKET_ACCESS_CONTROL_PORT: # connect from the ip/port specified # This means the test will fail if the port is already binded. It # would be better to bind the port before connecting but that's # not easily doable with twisted... c = MyTCPConnector(ip, port, factory, 30, access_control_param, reactor) c.connect() else: reactor.connectTCP(ip, port, factory) else: assert False def create_server(q, address_type, factory=None, block_reading=False, streamfile='stream'): if factory is None: factory = EventProtocolFactory(q, block_reading) if address_type == cs.SOCKET_ADDRESS_TYPE_UNIX: # don't use os.getcwd() here because it can be quite long and # can easily hit the AF_UNIX max path length. path = tempfile.mkstemp(suffix=streamfile)[1] try: os.remove(path) except OSError, e: if e.errno != errno.ENOENT: raise reactor.listenUNIX(path, factory) _to_cleanup.append(path) return dbus.ByteArray(path) elif address_type == cs.SOCKET_ADDRESS_TYPE_IPV4: for port in range(5000,6000): try: reactor.listenTCP(port, factory, interface='localhost') except CannotListenError: continue else: return ('127.0.0.1', dbus.UInt16(port)) else: assert False def check_new_connection_access(q, access_control, access_control_param, protocol): if access_control == cs.SOCKET_ACCESS_CONTROL_LOCALHOST: # nothing to check return elif access_control == cs.SOCKET_ACCESS_CONTROL_PORT: ip, port = access_control_param address = protocol.transport.getPeer() assertEquals(ip, address.host) assertEquals(port, address.port) elif access_control == cs.SOCKET_ACCESS_CONTROL_CREDENTIALS: byte = access_control_param e = q.expect('socket-data', protocol=protocol) assert ord(e.data) == byte # FIXME: check if credentials are actually passed. This is actually # really hard to test because Python doesn't implement recvmsg(). # Twisted's transport abstraction doesn't help either. # Credentials passing is tested in Gibber's tests so not testing it # here is actually not that bad. else: assert False def connect_to_cm_socket(q, to, address_type, address, access_control, access_control_param): connect_socket(q, address_type, address, access_control, access_control_param) if access_control == cs.SOCKET_ACCESS_CONTROL_CREDENTIALS: socket_event = q.expect('socket-connected') # socket is connected. Let's send our credentials (the byte is # meaningless) socket_event.protocol.sendData('a') # once credentials have been sent, Gabble sends SI request si_event, sig = q.expect_many( EventPattern('stream-iq', to=to, query_ns=ns.SI, query_name='si'), EventPattern('dbus-signal', signal='NewLocalConnection')) else: socket_event, si_event, sig = q.expect_many( EventPattern('socket-connected'), # expect SI request EventPattern('stream-iq', to=to, query_ns=ns.SI, query_name='si'), EventPattern('dbus-signal', signal='NewLocalConnection')) connection_id = sig.args[0] assert connection_id != 0 return socket_event, si_event, connection_id def exec_tube_test(test, *args): for bytestream_cls in [ bytestream.BytestreamIBBMsg, bytestream.BytestreamIBBIQ, bytestream.BytestreamS5B, bytestream.BytestreamSIFallbackS5CannotConnect, bytestream.BytestreamSIFallbackS5WrongHash, bytestream.BytestreamS5BRelay, bytestream.BytestreamS5BRelayBugged]: exec_test(lambda q, bus, conn, stream: test(q, bus, conn, stream, bytestream_cls, *args)) def exec_stream_tube_test(test): if os.name == 'posix': exec_tube_test(test, cs.SOCKET_ADDRESS_TYPE_UNIX, cs.SOCKET_ACCESS_CONTROL_LOCALHOST, "") exec_tube_test(test, cs.SOCKET_ADDRESS_TYPE_IPV4, cs.SOCKET_ACCESS_CONTROL_LOCALHOST, "") exec_tube_test(test, cs.SOCKET_ADDRESS_TYPE_IPV4, cs.SOCKET_ACCESS_CONTROL_PORT, ('127.0.0.1', dbus.UInt16(8631))) # we only guarantee to support credentials-passing on Linux if sys.platform == 'linux2': exec_tube_test(test, cs.SOCKET_ADDRESS_TYPE_UNIX, cs.SOCKET_ACCESS_CONTROL_CREDENTIALS, dbus.Byte(77)) def exec_dbus_tube_test(test): exec_tube_test(test, cs.SOCKET_ACCESS_CONTROL_CREDENTIALS) exec_tube_test(test, cs.SOCKET_ACCESS_CONTROL_LOCALHOST) telepathy-gabble-0.18.2/tests/twisted/tubes/test-socks5-muc.py0000644000175000017500000000564012227000321024276 0ustar00smcvsmcv00000000000000"""Check if SOCKS5 relays are disabled in muc""" import os if os.name != 'posix': # skipped on non-Unix for now, because it uses a Unix socket raise SystemExit(77) import dbus from servicetest import call_async, EventPattern, EventProtocolClientFactory from gabbletest import acknowledge_iq, make_muc_presence, exec_test import constants as cs import ns from mucutil import join_muc from bytestream import BytestreamS5BRelay, create_from_si_offer, announce_socks5_proxy from twisted.internet import reactor def test(q, bus, conn, stream): iq_event, disco_event = q.expect_many( EventPattern('stream-iq', to=None, query_ns='vcard-temp', query_name='vCard'), EventPattern('stream-iq', to='localhost', query_ns=ns.DISCO_ITEMS)) acknowledge_iq(stream, iq_event.stanza) announce_socks5_proxy(q, stream, disco_event.stanza) text_chan = join_muc(q, bus, conn, stream, 'chat@conf.localhost') # bob offers a stream tube stream_tube_id = 1 presence = make_muc_presence('owner', 'moderator', 'chat@conf.localhost', 'bob') tubes = presence.addElement((ns.TUBES, 'tubes')) tube = tubes.addElement((None, 'tube')) tube['type'] = 'stream' tube['service'] = 'echo' tube['id'] = str(stream_tube_id) parameters = tube.addElement((None, 'parameters')) stream.send(presence) def new_chan_predicate(e): path, props = e.args[0][0] return props[cs.CHANNEL_TYPE] == cs.CHANNEL_TYPE_STREAM_TUBE e = q.expect('dbus-signal', signal='NewChannels', predicate=new_chan_predicate) channels = e.args[0] assert len(channels) == 1 path, props = channels[0] assert props[cs.CHANNEL_TYPE] == cs.CHANNEL_TYPE_STREAM_TUBE tube_chan = bus.get_object(conn.bus_name, path) tube_iface = dbus.Interface(tube_chan, cs.CHANNEL_TYPE_STREAM_TUBE) call_async(q, tube_iface, 'Accept', 0, 0, '', byte_arrays=True) accept_return_event, _ = q.expect_many( EventPattern('dbus-return', method='Accept'), EventPattern('dbus-signal', signal='TubeChannelStateChanged', args=[cs.TUBE_CHANNEL_STATE_OPEN])) unix_socket_adr = accept_return_event.value[0] factory = EventProtocolClientFactory(q) reactor.connectUNIX(unix_socket_adr, factory) # expect SI request e = q.expect('stream-iq', to='chat@conf.localhost/bob', query_ns=ns.SI, query_name='si') bytestream, profile = create_from_si_offer(stream, q, BytestreamS5BRelay, e.stanza, 'chat@conf.localhost/bob') result, si = bytestream.create_si_reply(e.stanza, 'test@localhost/Resource') si.addElement((ns.TUBES, 'tube')) stream.send(result) # wait SOCKS5 init iq id, mode, si, hosts = bytestream._expect_socks5_init() for jid, host, port in hosts: # the proxy is not announced because we are in a muc assert jid != 'proxy.localhost' if __name__ == '__main__': exec_test(test) telepathy-gabble-0.18.2/tests/twisted/tubes/test-get-available-tubes.py0000644000175000017500000000477612227000321026133 0ustar00smcvsmcv00000000000000"""Test GetAvailableStreamTubeTypes and GetAvailableTubeTypes""" import os import sys import dbus from servicetest import call_async, EventPattern, tp_name_prefix,\ assertContains, assertEquals, assertLength from gabbletest import ( exec_test, make_result_iq, acknowledge_iq, make_muc_presence) import constants as cs sample_parameters = dbus.Dictionary({ 's': 'hello', 'ay': dbus.ByteArray('hello'), 'u': dbus.UInt32(123), 'i': dbus.Int32(-123), }, signature='sv') def test(q, bus, conn, stream): iq_event = q.expect('stream-iq', to=None, query_ns='vcard-temp', query_name='vCard') acknowledge_iq(stream, iq_event.stanza) # muc stream tube call_async(q, conn.Requests, 'CreateChannel', { cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_STREAM_TUBE, cs.TARGET_HANDLE_TYPE: cs.HT_ROOM, cs.TARGET_ID: 'chat@conf.localhost', cs.STREAM_TUBE_SERVICE: 'test'}) q.expect('stream-presence', to='chat@conf.localhost/test') # Send presence for other member of room. stream.send(make_muc_presence('owner', 'moderator', 'chat@conf.localhost', 'bob')) # Send presence for own membership of room. stream.send(make_muc_presence('owner', 'moderator', 'chat@conf.localhost', 'test')) members, event = q.expect_many( EventPattern('dbus-signal', signal='MembersChanged', args=[u'', [2, 3], [], [], [], 0, 0]), EventPattern('dbus-return', method='CreateChannel')) path = event.value[0] props = event.value[1] tube = bus.get_object(conn.bus_name, path) stream_tubes_types = tube.Get(cs.CHANNEL_TYPE_STREAM_TUBE, 'SupportedSocketTypes', dbus_interface=cs.PROPERTIES_IFACE) if os.name == 'posix': assert cs.SOCKET_ACCESS_CONTROL_LOCALHOST in \ stream_tubes_types[cs.SOCKET_ADDRESS_TYPE_UNIX], \ stream_tubes_types[cs.SOCKET_ADDRESS_TYPE_UNIX] # so far we only guarantee to support credentials-passing on Linux if sys.platform == 'linux2': assert cs.SOCKET_ACCESS_CONTROL_CREDENTIALS in \ stream_tubes_types[cs.SOCKET_ADDRESS_TYPE_UNIX], \ stream_tubes_types[cs.SOCKET_ADDRESS_TYPE_UNIX] assertEquals([cs.SOCKET_ACCESS_CONTROL_LOCALHOST, cs.SOCKET_ACCESS_CONTROL_PORT], stream_tubes_types[cs.SOCKET_ADDRESS_TYPE_IPV4]) assertEquals([cs.SOCKET_ACCESS_CONTROL_LOCALHOST, cs.SOCKET_ACCESS_CONTROL_PORT], stream_tubes_types[cs.SOCKET_ADDRESS_TYPE_IPV6]) if __name__ == '__main__': exec_test(test) telepathy-gabble-0.18.2/tests/twisted/tubes/request-invalid-dbus-tube.py0000644000175000017500000000201712200204333026327 0ustar00smcvsmcv00000000000000import dbus from gabbletest import exec_test import constants as cs invalid_service_names = [ 'invalidServiceName' , 'one ten hundred thousand million' , 'me.is.it.you?.hello.you.sexy.sons.o.@#$%.heh' , ':1.1' , '' ] def test(q, bus, conn, stream): for invalid_service_name in invalid_service_names: try: conn.Requests.CreateChannel( {cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_DBUS_TUBE, cs.TARGET_HANDLE_TYPE: cs.HT_CONTACT, cs.TARGET_ID: 'alice@localhost', cs.DBUS_TUBE_SERVICE_NAME: invalid_service_name }) except dbus.DBusException, e: assert e.get_dbus_name() == cs.INVALID_ARGUMENT, \ (e.get_dbus_name(), invalid_service_name) else: assert False # TODO: do the same with muc D-Bus tubes if __name__ == '__main__': exec_test(test) telepathy-gabble-0.18.2/tests/twisted/tubes/offer-private-stream-tube.py0000644000175000017500000002437512227000321026335 0ustar00smcvsmcv00000000000000"""Test 1-1 tubes support.""" import dbus from servicetest import call_async, EventPattern, sync_dbus, assertEquals from gabbletest import acknowledge_iq, sync_stream, make_result_iq import constants as cs import ns import tubetestutil as t from twisted.words.xish import domish, xpath sample_parameters = dbus.Dictionary({ 's': 'hello', 'ay': dbus.ByteArray('hello'), 'u': dbus.UInt32(123), 'i': dbus.Int32(-123), }, signature='sv') new_sample_parameters = dbus.Dictionary({ 's': 'newhello', 'ay': dbus.ByteArray('newhello'), 'u': dbus.UInt32(123), 'i': dbus.Int32(-123), }, signature='sv') def contact_offer_dbus_tube(bytestream, tube_id): iq, si = bytestream.create_si_offer(ns.TUBES) tube = si.addElement((ns.TUBES, 'tube')) tube['type'] = 'dbus' tube['service'] = 'com.example.TestCase2' tube['id'] = tube_id parameters = tube.addElement((None, 'parameters')) parameter = parameters.addElement((None, 'parameter')) parameter['type'] = 'str' parameter['name'] = 'login' parameter.addContent('TEST') bytestream.stream.send(iq) def test(q, bus, conn, stream, bytestream_cls, address_type, access_control, access_control_param): address1 = t.set_up_echo(q, address_type, True, streamfile='stream') address2 = t.set_up_echo(q, address_type, True, streamfile='stream2') t.check_conn_properties(q, conn) vcard_event, roster_event = q.expect_many( EventPattern('stream-iq', to=None, query_ns='vcard-temp', query_name='vCard'), EventPattern('stream-iq', query_ns=ns.ROSTER)) self_handle = conn.GetSelfHandle() acknowledge_iq(stream, vcard_event.stanza) roster = roster_event.stanza roster['type'] = 'result' item = roster_event.query.addElement('item') item['jid'] = 'bob@localhost' # Bob can do tubes item['subscription'] = 'both' stream.send(roster) bob_full_jid = 'bob@localhost/Bob' self_full_jid = 'test@localhost/Resource' # Send Bob presence and his tube caps presence = domish.Element(('jabber:client', 'presence')) presence['from'] = bob_full_jid presence['to'] = self_full_jid c = presence.addElement('c') c['xmlns'] = 'http://jabber.org/protocol/caps' c['node'] = 'http://example.com/ICantBelieveItsNotTelepathy' c['ver'] = '1.2.3' stream.send(presence) event = q.expect('stream-iq', iq_type='get', query_ns='http://jabber.org/protocol/disco#info', to=bob_full_jid) assert event.query['node'] == \ 'http://example.com/ICantBelieveItsNotTelepathy#1.2.3' result = make_result_iq(stream, event.stanza) query = result.firstChildElement() feature = query.addElement('feature') feature['var'] = ns.TUBES stream.send(result) # A tube request can be done only if the contact has tube capabilities # Ensure that Bob's caps have been received sync_stream(q, stream) # Also ensure that all the new contact list channels have been announced, # so that the NewChannel(s) signals we look for after calling # RequestChannel are the ones we wanted. sync_dbus(bus, q, conn) # Test tubes with Bob. Bob has tube capabilities. bob_handle = conn.RequestHandles(1, ['bob@localhost'])[0] # Try CreateChannel with correct properties # Gabble must succeed call_async(q, conn.Requests, 'CreateChannel', {cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_STREAM_TUBE, cs.TARGET_HANDLE_TYPE: cs.HT_CONTACT, cs.TARGET_HANDLE: bob_handle, cs.STREAM_TUBE_SERVICE: "newecho", }) def find_stream_tube(channels): for path, props in channels: if props[cs.CHANNEL_TYPE] == cs.CHANNEL_TYPE_STREAM_TUBE: return path, props return None, None def new_chan_predicate(e): path, _ = find_stream_tube(e.args[0]) return path is not None ret, new_sig = q.expect_many( EventPattern('dbus-return', method='CreateChannel'), EventPattern('dbus-signal', signal='NewChannels', predicate=new_chan_predicate), ) new_chan_path = ret.value[0] new_chan_prop_asv = ret.value[1] # State and Parameters are mutables so not announced assert cs.TUBE_STATE not in new_chan_prop_asv assert cs.TUBE_PARAMETERS not in new_chan_prop_asv assert new_chan_path.find("StreamTube") != -1, new_chan_path assert new_chan_path.find("SITubesChannel") == -1, new_chan_path new_tube_chan = bus.get_object(conn.bus_name, new_chan_path) new_tube_iface = dbus.Interface(new_tube_chan, cs.CHANNEL_TYPE_STREAM_TUBE) # check State and Parameters new_tube_props = new_tube_chan.GetAll(cs.CHANNEL_IFACE_TUBE, dbus_interface=cs.PROPERTIES_IFACE) # the tube created using the new API is in the "not offered" state assert new_tube_props['State'] == cs.TUBE_CHANNEL_STATE_NOT_OFFERED _, stream_tube_channel_properties = find_stream_tube(new_sig.args[0]) assert cs.TUBE_STATE not in stream_tube_channel_properties assert cs.TUBE_PARAMETERS not in stream_tube_channel_properties # Offer the first tube created call_async(q, new_tube_iface, 'Offer', address_type, address2, access_control, new_sample_parameters) msg_event, state_event = q.expect_many( EventPattern('stream-message'), EventPattern('dbus-signal', signal='TubeChannelStateChanged')) assert state_event.args[0] == cs.TUBE_CHANNEL_STATE_REMOTE_PENDING message = msg_event.stanza assert message['to'] == bob_full_jid tube_nodes = xpath.queryForNodes('/message/tube[@xmlns="%s"]' % ns.TUBES, message) assert tube_nodes is not None assert len(tube_nodes) == 1 tube = tube_nodes[0] assert tube['service'] == 'newecho' assert tube['type'] == 'stream' assert not tube.hasAttribute('initiator') stream_tube_id = long(tube['id']) params = {} parameter_nodes = xpath.queryForNodes('/tube/parameters/parameter', tube) for node in parameter_nodes: assert node['name'] not in params params[node['name']] = (node['type'], str(node)) assert params == {'ay': ('bytes', 'bmV3aGVsbG8='), 's': ('str', 'newhello'), 'i': ('int', '-123'), 'u': ('uint', '123'), } # The new tube has been offered, the parameters cannot be changed anymore # We need to use call_async to check the error tube_prop_iface = dbus.Interface(new_tube_chan, cs.PROPERTIES_IFACE) call_async(q, tube_prop_iface, 'Set', cs.CHANNEL_IFACE_TUBE, 'Parameters', dbus.Dictionary( {dbus.String(u'foo2'): dbus.String(u'bar2')}, signature=dbus.Signature('sv')), dbus_interface=cs.PROPERTIES_IFACE) set_error = q.expect('dbus-error') # check it is *not* correctly changed new_tube_props = new_tube_chan.GetAll(cs.CHANNEL_IFACE_TUBE, dbus_interface=cs.PROPERTIES_IFACE, byte_arrays=True) assert new_tube_props.get("Parameters") == new_sample_parameters, \ new_tube_props.get("Parameters") # The CM is the server, so fake a client wanting to talk to it # Old API tube bytestream1 = bytestream_cls(stream, q, 'alpha', bob_full_jid, self_full_jid, True) iq, si = bytestream1.create_si_offer(ns.TUBES) stream_node = si.addElement((ns.TUBES, 'stream')) stream_node['tube'] = str(stream_tube_id) stream.send(iq) si_reply_event, _, new_conn_event, socket_event = q.expect_many( EventPattern('stream-iq', iq_type='result'), EventPattern('dbus-signal', signal='TubeChannelStateChanged', args=[cs.TUBE_STATE_OPEN]), EventPattern('dbus-signal', signal='NewRemoteConnection'), EventPattern('socket-connected')) bytestream1.check_si_reply(si_reply_event.stanza) tube = xpath.queryForNodes('/iq/si/tube[@xmlns="%s"]' % ns.TUBES, si_reply_event.stanza) assert len(tube) == 1 handle, access, id = new_conn_event.args assert handle == bob_handle protocol = socket_event.protocol # we don't want to echo the access control byte protocol.echoed = False # start to read from the transport so we can read the control byte protocol.transport.startReading() t.check_new_connection_access(q, access_control, access, protocol) protocol.echoed = True # The CM is the server, so fake a client wanting to talk to it # New API tube bytestream2 = bytestream_cls(stream, q, 'beta', bob_full_jid, self_full_jid, True) iq, si = bytestream2.create_si_offer(ns.TUBES) stream_node = si.addElement((ns.TUBES, 'stream')) stream_node['tube'] = str(stream_tube_id) stream.send(iq) si_reply_event, new_conn_event, socket_event = q.expect_many( EventPattern('stream-iq', iq_type='result'), EventPattern('dbus-signal', signal='NewRemoteConnection'), EventPattern('socket-connected')) bytestream2.check_si_reply(si_reply_event.stanza) tube = xpath.queryForNodes('/iq/si/tube[@xmlns="%s"]' % ns.TUBES, si_reply_event.stanza) assert len(tube) == 1 handle, access, conn_id = new_conn_event.args assert handle == bob_handle protocol = socket_event.protocol # we don't want to echo the access control byte protocol.echoed = False # start to read from the transport so we can read the control byte protocol.transport.startReading() t.check_new_connection_access(q, access_control, access, protocol) protocol.echoed = True # have the fake client open the stream bytestream1.open_bytestream() # have the fake client send us some data data = 'hello, world' bytestream1.send_data(data) binary = bytestream1.get_data(len(data)) assert binary == data, binary # have the fake client open the stream bytestream2.open_bytestream() # have the fake client send us some data data = 'hello, new world' bytestream2.send_data(data) binary = bytestream2.get_data(len(data)) assert binary == data, binary # peer closes the bytestream bytestream2.close() e = q.expect('dbus-signal', signal='ConnectionClosed') assertEquals(conn_id, e.args[0]) assertEquals(cs.CONNECTION_LOST, e.args[1]) t.cleanup() if __name__ == '__main__': t.exec_stream_tube_test(test) telepathy-gabble-0.18.2/tests/twisted/tubes/offer-private-dbus-tube.py0000644000175000017500000002054112227000321025766 0ustar00smcvsmcv00000000000000"""Test D-Bus private tube support""" import dbus from dbus.connection import Connection from dbus.lowlevel import SignalMessage from servicetest import call_async, EventPattern, unwrap, watch_tube_signals,\ assertContains, assertEquals from gabbletest import sync_stream, make_presence import constants as cs import tubetestutil as t from twisted.words.xish import xpath import ns from bytestream import create_from_si_offer, announce_socks5_proxy from caps_helper import send_disco_reply sample_parameters = dbus.Dictionary({ 's': 'hello', 'ay': dbus.ByteArray('hello'), 'u': dbus.UInt32(123), 'i': dbus.Int32(-123), }, signature='sv') def alice_accepts_tube(q, stream, iq_event, dbus_tube_id, bytestream_cls): iq = iq_event.stanza bytestream, profile = create_from_si_offer(stream, q, bytestream_cls, iq, 'test@localhost/Resource') assert profile == ns.TUBES tube_nodes = xpath.queryForNodes('/iq/si/tube[@xmlns="%s"]' % ns.TUBES, iq) assert len(tube_nodes) == 1 tube = tube_nodes[0] tube['type'] = 'dbus' assert tube['initiator'] == 'test@localhost' assert tube['service'] == 'com.example.TestCase' assert tube['id'] == str(dbus_tube_id) params = {} parameter_nodes = xpath.queryForNodes('/tube/parameters/parameter', tube) for node in parameter_nodes: assert node['name'] not in params params[node['name']] = (node['type'], str(node)) assert params == {'ay': ('bytes', 'aGVsbG8='), 's': ('str', 'hello'), 'i': ('int', '-123'), 'u': ('uint', '123'), } # Alice accepts the tube result, si = bytestream.create_si_reply(iq) si.addElement((ns.TUBES, 'tube')) stream.send(result) bytestream.wait_bytestream_open() q.expect('dbus-signal', signal='TubeChannelStateChanged', args=[cs.TUBE_STATE_OPEN]) return bytestream def send_dbus_message_to_alice(q, stream, dbus_tube_adr, bytestream): tube = Connection(dbus_tube_adr) signal = SignalMessage('/', 'foo.bar', 'baz') signal.append(42, signature='u') tube.send_message(signal) binary = bytestream.get_data() # little and big endian versions of: SIGNAL, NO_REPLY, protocol v1, # 4-byte payload assert binary.startswith('l\x04\x01\x01' '\x04\x00\x00\x00') or \ binary.startswith('B\x04\x01\x01' '\x00\x00\x00\x04') # little and big endian versions of the 4-byte payload, UInt32(42) assert (binary[0] == 'l' and binary.endswith('\x2a\x00\x00\x00')) or \ (binary[0] == 'B' and binary.endswith('\x00\x00\x00\x2a')) # XXX: verify that it's actually in the "sender" slot, rather than just # being in the message somewhere watch_tube_signals(q, tube) dbus_message = binary # Have the fake client send us a message all in one go... bytestream.send_data(dbus_message) q.expect('tube-signal', signal='baz', args=[42], tube=tube) # ... and a message one byte at a time ... for byte in dbus_message: bytestream.send_data(byte) q.expect('tube-signal', signal='baz', args=[42], tube=tube) # ... and two messages in one go bytestream.send_data(dbus_message + dbus_message) q.expect('tube-signal', signal='baz', args=[42], tube=tube) q.expect('tube-signal', signal='baz', args=[42], tube=tube) def offer_new_dbus_tube(q, bus, conn, stream, self_handle, alice_handle, bytestream_cls, access_control): # Offer a tube to Alice (new API) def new_chan_predicate(e): types = [] for _, props in e.args[0]: types.append(props[cs.CHANNEL_TYPE]) return cs.CHANNEL_TYPE_DBUS_TUBE in types def find_dbus_tube(channels): for path, props in channels: if props[cs.CHANNEL_TYPE] == cs.CHANNEL_TYPE_DBUS_TUBE: return path, props return None, None call_async(q, conn.Requests, 'CreateChannel', {cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_DBUS_TUBE, cs.TARGET_HANDLE_TYPE: cs.HT_CONTACT, cs.TARGET_ID: 'alice@localhost', cs.DBUS_TUBE_SERVICE_NAME: 'com.example.TestCase' }, byte_arrays=True) cc_ret, nc = q.expect_many( EventPattern('dbus-return', method='CreateChannel'), EventPattern('dbus-signal', signal='NewChannels', predicate=new_chan_predicate), ) tube_path, tube_props = cc_ret.value _, new_channel_props = find_dbus_tube(nc.args[0]) # check tube channel properties assert tube_props[cs.CHANNEL_TYPE] == cs.CHANNEL_TYPE_DBUS_TUBE assert tube_props[cs.INTERFACES] == [cs.CHANNEL_IFACE_TUBE] assert tube_props[cs.TARGET_HANDLE_TYPE] == cs.HT_CONTACT assert tube_props[cs.TARGET_HANDLE] == alice_handle assert tube_props[cs.TARGET_ID] == 'alice@localhost' assert tube_props[cs.REQUESTED] == True assert tube_props[cs.INITIATOR_HANDLE] == self_handle assert tube_props[cs.INITIATOR_ID] == "test@localhost" assert tube_props[cs.DBUS_TUBE_SERVICE_NAME] == 'com.example.TestCase' assert tube_props[cs.DBUS_TUBE_SUPPORTED_ACCESS_CONTROLS] == [cs.SOCKET_ACCESS_CONTROL_CREDENTIALS, cs.SOCKET_ACCESS_CONTROL_LOCALHOST] assert cs.DBUS_TUBE_DBUS_NAMES not in tube_props assert cs.TUBE_PARAMETERS not in tube_props assert cs.TUBE_STATE not in tube_props # get the list of all channels to check that newly announced ones are in it all_channels = conn.Get(cs.CONN_IFACE_REQUESTS, 'Channels', dbus_interface=cs.PROPERTIES_IFACE, byte_arrays=True) for path, props in nc.args[0]: assertContains((path, props), all_channels) assertEquals(tube_props, new_channel_props) tube_chan = bus.get_object(conn.bus_name, tube_path) tube_chan_iface = dbus.Interface(tube_chan, cs.CHANNEL) dbus_tube_iface = dbus.Interface(tube_chan, cs.CHANNEL_TYPE_DBUS_TUBE) # check State and Parameters props = tube_chan.GetAll(cs.CHANNEL_IFACE_TUBE, dbus_interface=cs.PROPERTIES_IFACE, byte_arrays=True) assert props['State'] == cs.TUBE_CHANNEL_STATE_NOT_OFFERED # check ServiceName and DBusNames props = tube_chan.GetAll(cs.CHANNEL_TYPE_DBUS_TUBE, dbus_interface=cs.PROPERTIES_IFACE) assert props['ServiceName'] == 'com.example.TestCase' assert props['DBusNames'] == {} # Only when we offer the tube should it appear on the Tubes channel and an # IQ be sent to Alice. We sync the stream to ensure the IQ would have # arrived if it had been sent. sync_stream(q, stream) call_async(q, dbus_tube_iface, 'Offer', sample_parameters, access_control) offer_return_event, iq_event, state_event = q.expect_many( EventPattern('dbus-return', method='Offer'), EventPattern('stream-iq', to='alice@localhost/Test'), EventPattern('dbus-signal', signal='TubeChannelStateChanged'), ) tube_address = offer_return_event.value[0] assert len(tube_address) > 0 assert state_event.args[0] == cs.TUBE_CHANNEL_STATE_REMOTE_PENDING status = tube_chan.Get(cs.CHANNEL_IFACE_TUBE, 'State', dbus_interface=cs.PROPERTIES_IFACE) assert status == cs.TUBE_STATE_REMOTE_PENDING tube_chan_iface.Close() q.expect_many( EventPattern('dbus-signal', signal='Closed'), EventPattern('dbus-signal', signal='ChannelClosed')) def test(q, bus, conn, stream, bytestream_cls, access_control): disco_event = q.expect('stream-iq', to='localhost', query_ns=ns.DISCO_ITEMS) announce_socks5_proxy(q, stream, disco_event.stanza) t.check_conn_properties(q, conn) self_handle = conn.GetSelfHandle() alice_handle = conn.RequestHandles(cs.HT_CONTACT, ["alice@localhost"])[0] # send Alice's presence caps = { 'ext': '', 'ver': '0.0.0', 'node': 'http://example.com/fake-client0' } presence = make_presence('alice@localhost/Test', caps=caps) stream.send(presence) _, disco_event = q.expect_many( EventPattern('dbus-signal', signal='PresencesChanged', args = [{alice_handle: (2L, u'available', u'')}]), EventPattern('stream-iq', to='alice@localhost/Test', query_ns=ns.DISCO_INFO), ) # reply to disco query send_disco_reply(stream, disco_event.stanza, [], [ns.TUBES]) sync_stream(q, stream) offer_new_dbus_tube(q, bus, conn, stream, self_handle, alice_handle, bytestream_cls, access_control) if __name__ == '__main__': t.exec_dbus_tube_test(test) telepathy-gabble-0.18.2/tests/twisted/tubes/offer-no-caps.py0000644000175000017500000000643312200204333023770 0ustar00smcvsmcv00000000000000""" Test that offering a tube to a contact without tube capabilities fails appropriately. """ import dbus from twisted.words.xish import domish from servicetest import EventPattern, make_channel_proxy, call_async from gabbletest import exec_test, acknowledge_iq, sync_stream import constants as cs import tubetestutil as t import ns def props(ct, extra=None): ret = { cs.TARGET_HANDLE_TYPE: cs.HT_CONTACT, cs.TARGET_ID: 'joe@localhost', cs.CHANNEL_TYPE: ct, } if extra is not None: ret.update(extra) return ret def test(q, bus, conn, stream): vcard_event, roster_event = q.expect_many( EventPattern('stream-iq', to=None, query_ns='vcard-temp', query_name='vCard'), EventPattern('stream-iq', query_ns=ns.ROSTER)) acknowledge_iq(stream, vcard_event.stanza) # Send a roster with one member, Joe. roster = roster_event.stanza roster['type'] = 'result' item = roster_event.query.addElement('item') item['jid'] = 'joe@localhost' item['subscription'] = 'both' stream.send(roster) # Send Joe's presence. presence = domish.Element(('jabber:client', 'presence')) presence['from'] = 'joe@localhost/Joe' presence['to'] = 'test@localhost/Resource' c = presence.addElement('c') c['xmlns'] = 'http://jabber.org/protocol/caps' c['node'] = 'http://example.com/IDontSupportTubes' c['ver'] = '1.0' stream.send(presence) # Gabble discoes Joe, because it doesn't know his client's caps event = q.expect('stream-iq', iq_type='get', query_ns='http://jabber.org/protocol/disco#info', to='joe@localhost/Joe') assert event.query['node'] == 'http://example.com/IDontSupportTubes#1.0' # Send a "Joe doesn't have any caps" response. result = event.stanza result['type'] = 'result' stream.send(result) # Ensure Joe's caps have been received. # FIXME: we shouldn't need to do this, the tubes code should wait for the # caps to appear, just like the StreamedMedia channel does. sync_stream(q, stream) address = t.create_server(q, cs.SOCKET_ADDRESS_TYPE_IPV4) # Now we try making new-style DBusTube and StreamTube channels, and calling # the relevant Offer method on them; this should fail with NotAvailable. # FIXME: I think this should be NotCapable st_path, _ = conn.Requests.CreateChannel(props(cs.CHANNEL_TYPE_STREAM_TUBE, {cs.STREAM_TUBE_SERVICE: "newecho"})) st_chan = bus.get_object(conn.bus_name, st_path) st = dbus.Interface(st_chan, cs.CHANNEL_TYPE_STREAM_TUBE) call_async(q, st, 'Offer', cs.SOCKET_ADDRESS_TYPE_IPV4, address, cs.SOCKET_ACCESS_CONTROL_LOCALHOST, {}) e = q.expect('dbus-error', method='Offer').error assert e.get_dbus_name() == cs.NOT_AVAILABLE, e.get_dbus_name() dt_path, _ = conn.Requests.CreateChannel(props(cs.CHANNEL_TYPE_DBUS_TUBE, { cs.DBUS_TUBE_SERVICE_NAME: "com.newecho" })) dt_chan = bus.get_object(conn.bus_name, dt_path) dt = dbus.Interface(dt_chan, cs.CHANNEL_TYPE_DBUS_TUBE) call_async(q, dt, 'Offer', {}, cs.SOCKET_ACCESS_CONTROL_CREDENTIALS) e = q.expect('dbus-error', method='Offer').error assert e.get_dbus_name() == cs.NOT_AVAILABLE, e.get_dbus_name() t.cleanup() if __name__ == '__main__': exec_test(test) telepathy-gabble-0.18.2/tests/twisted/tubes/offer-muc-stream-tube.py0000644000175000017500000001575212227000321025446 0ustar00smcvsmcv00000000000000"""Test stream tube support in the context of a MUC.""" import errno import os import dbus from servicetest import call_async, EventPattern, unwrap, assertContains, assertEquals from gabbletest import acknowledge_iq, make_muc_presence import constants as cs import ns import tubetestutil as t from mucutil import join_muc from bytestream import BytestreamS5BRelay, BytestreamS5BRelayBugged from twisted.words.xish import xpath from twisted.internet import reactor sample_parameters = dbus.Dictionary({ 's': 'hello', 'ay': dbus.ByteArray('hello'), 'u': dbus.UInt32(123), 'i': dbus.Int32(-123), }, signature='sv') def connect_to_tube(stream, q, bytestream_cls, muc, stream_tube_id): # The CM is the server, so fake a client wanting to talk to it bytestream = bytestream_cls(stream, q, 'alpha', '%s/bob' % muc, '%s/test' % muc, True) # set the real jid of the target as 'to' because the XMPP server changes # it when delivering the IQ iq, si = bytestream.create_si_offer(ns.TUBES, 'test@localhost/Resource') stream_node = si.addElement((ns.TUBES, 'muc-stream')) stream_node['tube'] = str(stream_tube_id) stream.send(iq) return bytestream def use_tube(q, bytestream, protocol, conn_id): # have the fake client open the stream bytestream.open_bytestream() # have the fake client send us some data bytestream.send_data('hello initiator') # the server reply event = q.expect('socket-data', data='hello initiator', protocol=protocol) data = 'hello joiner' protocol.sendData(data) # we receive server's data binary = bytestream.get_data(len(data)) assert binary == data, binary # peer closes the bytestream bytestream.close() e = q.expect('dbus-signal', signal='ConnectionClosed') assertEquals(conn_id, e.args[0]) assertEquals(cs.CONNECTION_LOST, e.args[1]) def test(q, bus, conn, stream, bytestream_cls, address_type, access_control, access_control_param): if bytestream_cls in [BytestreamS5BRelay, BytestreamS5BRelayBugged]: # disable SOCKS5 relay tests because proxy can't be used with muc # contacts atm return iq_event = q.expect('stream-iq', to=None, query_ns='vcard-temp', query_name='vCard') acknowledge_iq(stream, iq_event.stanza) t.check_conn_properties(q, conn) bob_handle = conn.RequestHandles(cs.HT_CONTACT, ['chat@conf.localhost/bob'])[0] address = t.create_server(q, address_type) def new_chan_predicate(e): types = [] for _, props in e.args[0]: types.append(props[cs.CHANNEL_TYPE]) return cs.CHANNEL_TYPE_STREAM_TUBE in types def find_stream_tube(channels): for path, props in channels: if props[cs.CHANNEL_TYPE] == cs.CHANNEL_TYPE_STREAM_TUBE: return path, props return None, None # offer a stream tube to another room (new API) address = t.create_server(q, address_type, block_reading=True) request = { cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_STREAM_TUBE, cs.TARGET_HANDLE_TYPE: cs.HT_ROOM, cs.TARGET_ID: 'chat@conf.localhost', cs.STREAM_TUBE_SERVICE: 'newecho', } _, _, new_tube_path, new_tube_props = \ join_muc(q, bus, conn, stream, 'chat@conf.localhost', request) e = q.expect('dbus-signal', signal='NewChannels', predicate=new_chan_predicate) path, prop = find_stream_tube(e.args[0]) assert prop[cs.CHANNEL_TYPE] == cs.CHANNEL_TYPE_STREAM_TUBE assert prop[cs.INITIATOR_ID] == 'chat@conf.localhost/test' assert prop[cs.REQUESTED] == True assert prop[cs.TARGET_HANDLE_TYPE] == cs.HT_ROOM assert prop[cs.TARGET_ID] == 'chat@conf.localhost' assert prop[cs.STREAM_TUBE_SERVICE] == 'newecho' # check that the tube channel is in the channels list all_channels = conn.Get(cs.CONN_IFACE_REQUESTS, 'Channels', dbus_interface=cs.PROPERTIES_IFACE, byte_arrays=True) assertContains((path, prop), all_channels) tube_chan = bus.get_object(conn.bus_name, path) stream_tube_iface = dbus.Interface(tube_chan, cs.CHANNEL_TYPE_STREAM_TUBE) chan_iface = dbus.Interface(tube_chan, cs.CHANNEL) tube_props = tube_chan.GetAll(cs.CHANNEL_IFACE_TUBE, dbus_interface=cs.PROPERTIES_IFACE) assert tube_props['State'] == cs.TUBE_CHANNEL_STATE_NOT_OFFERED # offer the tube call_async(q, stream_tube_iface, 'Offer', address_type, address, access_control, {'foo': 'bar'}) stream_event, _, status_event = q.expect_many( EventPattern('stream-presence', to='chat@conf.localhost/test'), EventPattern('dbus-return', method='Offer'), EventPattern('dbus-signal', signal='TubeChannelStateChanged', args=[cs.TUBE_CHANNEL_STATE_OPEN])) tube_self_handle = tube_chan.GetSelfHandle(dbus_interface=cs.CHANNEL_IFACE_GROUP) assert conn.InspectHandles(cs.HT_CONTACT, [tube_self_handle]) == ['chat@conf.localhost/test'] presence = stream_event.stanza tubes_nodes = xpath.queryForNodes('/presence/tubes[@xmlns="%s"]' % ns.TUBES, presence) assert tubes_nodes is not None assert len(tubes_nodes) == 1 stream_tube_id = 666 tube_nodes = xpath.queryForNodes('/tubes/tube', tubes_nodes[0]) assert tube_nodes is not None assert len(tube_nodes) == 1 for tube in tube_nodes: assert tube['type'] == 'stream' assert not tube.hasAttribute('initiator') assert tube['service'] == 'newecho' assert not tube.hasAttribute('stream-id') assert not tube.hasAttribute('dbus-name') stream_tube_id = int(tube['id']) params = {} parameter_nodes = xpath.queryForNodes('/tube/parameters/parameter', tube) for node in parameter_nodes: assert node['name'] not in params params[node['name']] = (node['type'], str(node)) assert params == {'foo': ('str', 'bar')} bob_handle = conn.RequestHandles(cs.HT_CONTACT, ['chat@conf.localhost/bob'])[0] bytestream = connect_to_tube(stream, q, bytestream_cls, 'chat@conf.localhost', stream_tube_id) iq_event, socket_event, conn_event = q.expect_many( EventPattern('stream-iq', iq_type='result'), EventPattern('socket-connected'), EventPattern('dbus-signal', signal='NewRemoteConnection', interface=cs.CHANNEL_TYPE_STREAM_TUBE)) handle, access, conn_id = conn_event.args assert handle == bob_handle protocol = socket_event.protocol # start to read from the transport so we can read the control byte protocol.transport.startReading() t.check_new_connection_access(q, access_control, access, protocol) # handle iq_event bytestream.check_si_reply(iq_event.stanza) tube = xpath.queryForNodes('/iq//si/tube[@xmlns="%s"]' % ns.TUBES, iq_event.stanza) assert len(tube) == 1 use_tube(q, bytestream, protocol, conn_id) chan_iface.Close() q.expect_many( EventPattern('dbus-signal', signal='Closed'), EventPattern('dbus-signal', signal='ChannelClosed')) t.cleanup() if __name__ == '__main__': t.exec_stream_tube_test(test) telepathy-gabble-0.18.2/tests/twisted/tubes/offer-muc-dbus-tube.py0000644000175000017500000002757112227000321025112 0ustar00smcvsmcv00000000000000"""Test IBB tube support in the context of a MUC.""" import base64 import dbus from dbus.connection import Connection from dbus.lowlevel import SignalMessage from servicetest import call_async, EventPattern, assertContains, assertEquals from gabbletest import exec_test, acknowledge_iq, elem, make_muc_presence, sync_stream import ns import constants as cs import tubetestutil as t from twisted.words.xish import xpath from mucutil import join_muc, echo_muc_presence sample_parameters = dbus.Dictionary({ 's': 'hello', 'ay': dbus.ByteArray('hello'), 'u': dbus.UInt32(123), 'i': dbus.Int32(-123), }, signature='sv') def check_tube_in_presence(presence, initiator): tubes_nodes = xpath.queryForNodes('/presence/tubes[@xmlns="%s"]' % ns.TUBES, presence) assert tubes_nodes is not None assert len(tubes_nodes) == 1 tube_nodes = xpath.queryForNodes('/tubes/tube', tubes_nodes[0]) assert tube_nodes is not None assert len(tube_nodes) == 1 for tube in tube_nodes: tube['type'] = 'dbus' assert tube['initiator'] == initiator assert tube['service'] == 'com.example.TestCase' dbus_stream_id = tube['stream-id'] my_bus_name = tube['dbus-name'] dbus_tube_id = tube['id'] params = {} parameter_nodes = xpath.queryForNodes('/tube/parameters/parameter', tube) for node in parameter_nodes: assert node['name'] not in params params[node['name']] = (node['type'], str(node)) assert params == {'ay': ('bytes', 'aGVsbG8='), 's': ('str', 'hello'), 'i': ('int', '-123'), 'u': ('uint', '123'), } return dbus_stream_id, my_bus_name, dbus_tube_id def fire_signal_on_tube(q, tube, chatroom, dbus_stream_id, my_bus_name): signal = SignalMessage('/', 'foo.bar', 'baz') signal.append(42, signature='u') tube.send_message(signal) event = q.expect('stream-message', to=chatroom, message_type='groupchat') message = event.stanza data_nodes = xpath.queryForNodes('/message/data[@xmlns="%s"]' % ns.MUC_BYTESTREAM, message) assert data_nodes is not None assert len(data_nodes) == 1 ibb_data = data_nodes[0] assert ibb_data['sid'] == dbus_stream_id binary = base64.b64decode(str(ibb_data)) # little and big endian versions of: SIGNAL, NO_REPLY, protocol v1, # 4-byte payload assert binary.startswith('l\x04\x01\x01' '\x04\x00\x00\x00') or \ binary.startswith('B\x04\x01\x01' '\x00\x00\x00\x04') # little and big endian versions of the 4-byte payload, UInt32(42) assert (binary[0] == 'l' and binary.endswith('\x2a\x00\x00\x00')) or \ (binary[0] == 'B' and binary.endswith('\x00\x00\x00\x2a')) # XXX: verify that it's actually in the "sender" slot, rather than just # being in the message somewhere assert my_bus_name in binary # Send another big signal which has to be split on 3 stanzas signal = SignalMessage('/', 'foo.bar', 'baz') signal.append('a' * 100000, signature='s') tube.send_message(signal) def wait_for_data(q): event = q.expect('stream-message', to=chatroom, message_type='groupchat') data_nodes = xpath.queryForNodes('/message/data[@xmlns="%s"]' % ns.MUC_BYTESTREAM, event.stanza) ibb_data = data_nodes[0] return ibb_data['frag'] frag = wait_for_data(q) assertEquals(frag, 'first') frag = wait_for_data(q) assertEquals(frag, 'middle') frag = wait_for_data(q) assertEquals(frag, 'last') def test(q, bus, conn, stream, access_control): iq_event = q.expect('stream-iq', to=None, query_ns='vcard-temp', query_name='vCard') acknowledge_iq(stream, iq_event.stanza) # check if we can request muc D-Bus tube t.check_conn_properties(q, conn) self_handle = conn.GetSelfHandle() self_name = conn.InspectHandles(1, [self_handle])[0] # offer a D-Bus tube to another room using new API muc = 'chat2@conf.localhost' request = { cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_DBUS_TUBE, cs.TARGET_HANDLE_TYPE: cs.HT_ROOM, cs.TARGET_ID: 'chat2@conf.localhost', cs.DBUS_TUBE_SERVICE_NAME: 'com.example.TestCase', } join_muc(q, bus, conn, stream, muc, request=request) e = q.expect('dbus-signal', signal='NewChannels') channels = e.args[0] assert len(channels) == 1 path, prop = channels[0] assert prop[cs.CHANNEL_TYPE] == cs.CHANNEL_TYPE_DBUS_TUBE assert prop[cs.INITIATOR_ID] == 'chat2@conf.localhost/test' assert prop[cs.REQUESTED] == True assert prop[cs.TARGET_HANDLE_TYPE] == cs.HT_ROOM assert prop[cs.TARGET_ID] == 'chat2@conf.localhost' assert prop[cs.DBUS_TUBE_SERVICE_NAME] == 'com.example.TestCase' assert prop[cs.DBUS_TUBE_SUPPORTED_ACCESS_CONTROLS] == [cs.SOCKET_ACCESS_CONTROL_CREDENTIALS, cs.SOCKET_ACCESS_CONTROL_LOCALHOST] # check that the tube channel is in the channels list all_channels = conn.Get(cs.CONN_IFACE_REQUESTS, 'Channels', dbus_interface=cs.PROPERTIES_IFACE, byte_arrays=True) assertContains((path, prop), all_channels) tube_chan = bus.get_object(conn.bus_name, path) dbus_tube_iface = dbus.Interface(tube_chan, cs.CHANNEL_TYPE_DBUS_TUBE) chan_iface = dbus.Interface(tube_chan, cs.CHANNEL) tube_props = tube_chan.GetAll(cs.CHANNEL_IFACE_TUBE, dbus_interface=cs.PROPERTIES_IFACE, byte_arrays=True) assert tube_props['State'] == cs.TUBE_CHANNEL_STATE_NOT_OFFERED # try to offer using a wrong access control try: dbus_tube_iface.Offer(sample_parameters, cs.SOCKET_ACCESS_CONTROL_PORT) except dbus.DBusException, e: assertEquals(e.get_dbus_name(), cs.INVALID_ARGUMENT) else: assert False # offer the tube call_async(q, dbus_tube_iface, 'Offer', sample_parameters, access_control) presence_event, return_event, status_event, dbus_changed_event = q.expect_many( EventPattern('stream-presence', to='chat2@conf.localhost/test'), EventPattern('dbus-return', method='Offer'), EventPattern('dbus-signal', signal='TubeChannelStateChanged', args=[cs.TUBE_CHANNEL_STATE_OPEN]), EventPattern('dbus-signal', signal='DBusNamesChanged', interface=cs.CHANNEL_TYPE_DBUS_TUBE)) tube_self_handle = tube_chan.GetSelfHandle(dbus_interface=cs.CHANNEL_IFACE_GROUP) assert tube_self_handle != 0 # handle presence_event # We announce our newly created tube in our muc presence presence = presence_event.stanza dbus_stream_id, my_bus_name, dbus_tube_id = check_tube_in_presence(presence, 'chat2@conf.localhost/test') # handle dbus_changed_event added, removed = dbus_changed_event.args assert added == {tube_self_handle: my_bus_name} assert removed == [] dbus_tube_adr = return_event.value[0] bob_bus_name = ':2.Ym9i' bob_handle = conn.RequestHandles(cs.HT_CONTACT, ['chat2@conf.localhost/bob'])[0] def bob_in_tube(): presence = elem('presence', from_='chat2@conf.localhost/bob', to='chat2@conf.localhost')( elem('x', xmlns=ns.MUC_USER), elem('tubes', xmlns=ns.TUBES)( elem('tube', type='dbus', initiator='chat2@conf.localhost/test', service='com.example.TestCase', id=str(dbus_tube_id))( elem('parameters')( elem('parameter', name='ay', type='bytes')(u'aGVsbG8='), elem('parameter', name='s', type='str')(u'hello'), elem('parameter', name='i', type='int')(u'-123'), elem('parameter', name='u', type='uint')(u'123') )))) # have to add stream-id and dbus-name attributes manually as we can't use # keyword with '-'... tube_node = xpath.queryForNodes('/presence/tubes/tube', presence)[0] tube_node['stream-id'] = dbus_stream_id tube_node['dbus-name'] = bob_bus_name stream.send(presence) # Bob joins the tube bob_in_tube() dbus_changed_event = q.expect('dbus-signal', signal='DBusNamesChanged', interface=cs.CHANNEL_TYPE_DBUS_TUBE) added, removed = dbus_changed_event.args assert added == {bob_handle: bob_bus_name} assert removed == [] tube = Connection(dbus_tube_adr) fire_signal_on_tube(q, tube, 'chat2@conf.localhost', dbus_stream_id, my_bus_name) names = tube_chan.Get(cs.CHANNEL_TYPE_DBUS_TUBE, 'DBusNames', dbus_interface=cs.PROPERTIES_IFACE) assert names == {tube_self_handle: my_bus_name, bob_handle: bob_bus_name} # Bob leave the tube presence = elem('presence', from_='chat2@conf.localhost/bob', to='chat2@conf.localhost')( elem('x', xmlns=ns.MUC_USER), elem('tubes', xmlns=ns.TUBES)) stream.send(presence) dbus_changed_event = q.expect('dbus-signal', signal='DBusNamesChanged', interface=cs.CHANNEL_TYPE_DBUS_TUBE) added, removed = dbus_changed_event.args assert added == {} assert removed == [bob_handle] names = tube_chan.Get(cs.CHANNEL_TYPE_DBUS_TUBE, 'DBusNames', dbus_interface=cs.PROPERTIES_IFACE) assert names == {tube_self_handle: my_bus_name} chan_iface.Close() _, _, event = q.expect_many( EventPattern('dbus-signal', signal='Closed'), EventPattern('dbus-signal', signal='ChannelClosed'), EventPattern('stream-presence', to='chat2@conf.localhost/test', presence_type='unavailable')) # we must echo the MUC presence so the room will actually close # and we should wait to make sure gabble has actually parsed our # echo before trying to rejoin echo_muc_presence(q, stream, event.stanza, 'none', 'participant') sync_stream(q, stream) # rejoin the room call_async(q, conn.Requests, 'CreateChannel', { cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_TEXT, cs.TARGET_HANDLE_TYPE: cs.HT_ROOM, cs.TARGET_ID: 'chat2@conf.localhost' }) q.expect('stream-presence', to='chat2@conf.localhost/test') # Bob is in the room and in the tube bob_in_tube() # Send presence for own membership of room. stream.send(make_muc_presence('none', 'participant', muc, 'test')) def new_tube(e): path, props = e.args[0][0] return props[cs.CHANNEL_TYPE] == cs.CHANNEL_TYPE_DBUS_TUBE def new_text(e): path, props = e.args[0][0] return props[cs.CHANNEL_TYPE] == cs.CHANNEL_TYPE_TEXT # tube and text is created text_event, tube_event = q.expect_many(EventPattern('dbus-signal', signal='NewChannels', predicate=new_text), EventPattern('dbus-signal', signal='NewChannels', predicate=new_tube)) channels = e.args[0] tube_path, props = tube_event.args[0][0] assertEquals(cs.CHANNEL_TYPE_DBUS_TUBE, props[cs.CHANNEL_TYPE]) assertEquals('chat2@conf.localhost/test', props[cs.INITIATOR_ID]) assertEquals(False, props[cs.REQUESTED]) assertEquals(cs.HT_ROOM, props[cs.TARGET_HANDLE_TYPE]) assertEquals('com.example.TestCase', props[cs.DBUS_TUBE_SERVICE_NAME]) _, props = text_event.args[0][0] assertEquals(cs.CHANNEL_TYPE_TEXT, props[cs.CHANNEL_TYPE]) assertEquals(True, props[cs.REQUESTED]) # tube is local-pending tube_chan = bus.get_object(conn.bus_name, tube_path) state = tube_chan.Get(cs.CHANNEL_IFACE_TUBE, 'State', dbus_interface=dbus.PROPERTIES_IFACE) assertEquals(cs.TUBE_STATE_LOCAL_PENDING, state) if __name__ == '__main__': # We can't use t.exec_dbus_tube_test() as we can use only the muc bytestream exec_test(lambda q, bus, conn, stream: test(q, bus, conn, stream, cs.SOCKET_ACCESS_CONTROL_CREDENTIALS)) exec_test(lambda q, bus, conn, stream: test(q, bus, conn, stream, cs.SOCKET_ACCESS_CONTROL_LOCALHOST)) telepathy-gabble-0.18.2/tests/twisted/tubes/ensure-si-tube.py0000644000175000017500000001071512227000321024176 0ustar00smcvsmcv00000000000000""" Test support for creating and retrieving 1-1 tubes with EnsureChannel """ import dbus from servicetest import call_async, EventPattern, tp_name_prefix, unwrap from gabbletest import exec_test, acknowledge_iq import constants as cs import ns from twisted.words.xish import domish import tubetestutil as t sample_parameters = dbus.Dictionary({ 's': 'hello', 'ay': dbus.ByteArray('hello'), 'u': dbus.UInt32(123), 'i': dbus.Int32(-123), }, signature='sv') def test(q, bus, conn, stream): conn.Connect() properties = conn.GetAll( cs.CONN_IFACE_REQUESTS, dbus_interface=cs.PROPERTIES_IFACE) assert properties.get('Channels') == [], properties['Channels'] assert ({cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_STREAM_TUBE, cs.TARGET_HANDLE_TYPE: cs.HT_CONTACT, }, [cs.TARGET_HANDLE, cs.TARGET_ID, cs.STREAM_TUBE_SERVICE] ) in properties.get('RequestableChannelClasses'),\ unwrap(properties['RequestableChannelClasses']) _, vcard_event, roster_event = q.expect_many( EventPattern('dbus-signal', signal='StatusChanged', args=[cs.CONN_STATUS_CONNECTED, cs.CSR_REQUESTED]), EventPattern('stream-iq', to=None, query_ns='vcard-temp', query_name='vCard'), EventPattern('stream-iq', query_ns=ns.ROSTER)) acknowledge_iq(stream, vcard_event.stanza) roster = roster_event.stanza roster['type'] = 'result' item = roster_event.query.addElement('item') item['jid'] = 'bob@localhost' item['subscription'] = 'both' stream.send(roster) presence = domish.Element(('jabber:client', 'presence')) presence['from'] = 'bob@localhost/Bob' presence['to'] = 'test@localhost/Resource' c = presence.addElement('c') c['xmlns'] = 'http://jabber.org/protocol/caps' c['node'] = 'http://example.com/ICantBelieveItsNotTelepathy' c['ver'] = '1.2.3' stream.send(presence) event = q.expect('stream-iq', iq_type='get', query_ns='http://jabber.org/protocol/disco#info', to='bob@localhost/Bob') result = event.stanza result['type'] = 'result' assert event.query['node'] == \ 'http://example.com/ICantBelieveItsNotTelepathy#1.2.3' feature = event.query.addElement('feature') feature['var'] = ns.TUBES stream.send(result) bob_handle = conn.RequestHandles(1, ['bob@localhost'])[0] def new_chan_predicate(e): types = [] for _, props in e.args[0]: types.append(props[cs.CHANNEL_TYPE]) return cs.CHANNEL_TYPE_STREAM_TUBE in types call_async(q, conn.Requests, 'CreateChannel', { cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_STREAM_TUBE, cs.TARGET_HANDLE_TYPE: cs.HT_CONTACT, cs.TARGET_HANDLE: bob_handle, cs.STREAM_TUBE_SERVICE: 'the.service', }) ret, _ = q.expect_many( EventPattern('dbus-return', method='CreateChannel'), EventPattern('dbus-signal', signal='NewChannels', predicate=new_chan_predicate), ) chan_path, props = ret.value # Ensure a tube to the same person; check it's the same one. # call_async(q, conn.Requests, 'EnsureChannel', # { cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_STREAM_TUBE, # cs.TARGET_HANDLE_TYPE: cs.HT_CONTACT, # cs.TARGET_HANDLE: bob_handle, # cs.STREAM_TUBE_SERVICE: 'the.service', # }) # ret = q.expect('dbus-return', method='EnsureChannel') # yours, ensured_path, _ = ret.value # assert ensured_path == chan_path, (ensured_path, chan_path) # assert not yours chan = bus.get_object(conn.bus_name, chan_path) chan.Close() # Now let's try ensuring a new tube. call_async(q, conn.Requests, 'EnsureChannel', { cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_STREAM_TUBE, cs.TARGET_HANDLE_TYPE: cs.HT_CONTACT, cs.TARGET_HANDLE: bob_handle, cs.STREAM_TUBE_SERVICE: 'the.service', }) ret, new_sig = q.expect_many( EventPattern('dbus-return', method='EnsureChannel'), EventPattern('dbus-signal', signal='NewChannels', predicate=new_chan_predicate), ) yours, path, props = ret.value assert yours emitted_props = new_sig.args[0][0][1] assert props == emitted_props, (props, emitted_props) chan = bus.get_object(conn.bus_name, path) chan.Close() if __name__ == '__main__': exec_test(test, do_connect=False) telepathy-gabble-0.18.2/tests/twisted/tubes/create-invalid-tube-channels.py0000644000175000017500000000276312200204333026750 0ustar00smcvsmcv00000000000000""" Check that Gabble rejects invalid requests for tubes channels. """ import dbus from servicetest import call_async from gabbletest import exec_test import constants as cs def is_tube(path, props): ct = props[cs.CHANNEL_TYPE] return ct in [cs.CHANNEL_TYPE_STREAM_TUBE, cs.CHANNEL_TYPE_DBUS_TUBE] def check_no_tubes(conn_props): channels = conn_props.Get(cs.CONN_IFACE_REQUESTS, 'Channels') tube_channels = filter(is_tube, channels) assert len(tube_channels) == 0, tube_channels def test(q, bus, conn, stream): conn_props = dbus.Interface(conn, cs.PROPERTIES_IFACE) # Try to CreateChannel with unknown properties # Gabble must return an error call_async(q, conn.Requests, 'CreateChannel', {cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_STREAM_TUBE, cs.TARGET_HANDLE_TYPE: cs.HT_CONTACT, cs.TARGET_ID: "foo@example.com", 'this.property.does.not.exist': 'this.value.should.not.exist' }) ret = q.expect('dbus-error', method='CreateChannel') check_no_tubes(conn_props) # Try to CreateChannel with missing properties ("Service") # Gabble must return an error call_async(q, conn.Requests, 'CreateChannel', {cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_STREAM_TUBE, cs.TARGET_HANDLE_TYPE: cs.HT_CONTACT, cs.TARGET_ID: "foo@example.com", }) ret = q.expect('dbus-error', method='CreateChannel') check_no_tubes(conn_props) if __name__ == '__main__': exec_test(test) telepathy-gabble-0.18.2/tests/twisted/tubes/close-muc-with-closed-tube.py0000644000175000017500000000760112227000321026373 0ustar00smcvsmcv00000000000000"""Test IBB stream tube support in the context of a MUC.""" import dbus from servicetest import call_async, EventPattern, unwrap, assertEquals from gabbletest import exec_test, make_result_iq, acknowledge_iq, make_muc_presence import constants as cs import ns import tubetestutil as t sample_parameters = dbus.Dictionary({ 's': 'hello', 'ay': dbus.ByteArray('hello'), 'u': dbus.UInt32(123), 'i': dbus.Int32(-123), }, signature='sv') def test(q, bus, conn, stream): iq_event = q.expect('stream-iq', to=None, query_ns='vcard-temp', query_name='vCard') acknowledge_iq(stream, iq_event.stanza) call_async(q, conn, 'RequestHandles', cs.HT_ROOM, ['chat@conf.localhost']) event = q.expect('dbus-return', method='RequestHandles') handles = event.value[0] room_handle = handles[0] # join the muc call_async(q, conn, 'RequestChannel', cs.CHANNEL_TYPE_TEXT, cs.HT_ROOM, room_handle, True) _, stream_event = q.expect_many( EventPattern('dbus-signal', signal='MembersChanged', args=[u'', [], [], [], [2], 0, 0]), EventPattern('stream-presence', to='chat@conf.localhost/test')) # Send presence for other member of room. stream.send(make_muc_presence('owner', 'moderator', 'chat@conf.localhost', 'bob')) # Send presence for own membership of room. stream.send(make_muc_presence('none', 'participant', 'chat@conf.localhost', 'test')) q.expect('dbus-signal', signal='MembersChanged', args=[u'', [2, 3], [], [], [], 0, 0]) assert conn.InspectHandles(cs.HT_CONTACT, [2, 3]) == \ ['chat@conf.localhost/test', 'chat@conf.localhost/bob'] bob_handle = 3 event = q.expect('dbus-return', method='RequestChannel') text_chan = bus.get_object(conn.bus_name, event.value[0]) # Bob offers a muc tube tube_id = 666 stream_id = 1234 presence = make_muc_presence('owner', 'moderator', 'chat@conf.localhost', 'bob') tubes = presence.addElement((ns.TUBES, 'tubes')) tube = tubes.addElement((None, 'tube')) tube['type'] = 'dbus' tube['service'] = 'org.telepathy.freedesktop.test' tube['id'] = str(tube_id) tube['stream-id'] = str(stream_id) tube['dbus-name'] = ':2.Y2Fzc2lkeS10ZXN0MgAA' tube['initiator'] = 'chat@conf.localhost/bob' parameters = tube.addElement((None, 'parameters')) parameter = parameters.addElement((None, 'parameter')) parameter['name'] = 's' parameter['type'] = 'str' parameter.addContent('hello') parameter = parameters.addElement((None, 'parameter')) parameter['name'] = 'ay' parameter['type'] = 'bytes' parameter.addContent('aGVsbG8=') parameter = parameters.addElement((None, 'parameter')) parameter['name'] = 'u' parameter['type'] = 'uint' parameter.addContent('123') parameter = parameters.addElement((None, 'parameter')) parameter['name'] = 'i' parameter['type'] = 'int' parameter.addContent('-123') stream.send(presence) def new_chan_predicate(e): path, props = e.args[0][0] return props[cs.CHANNEL_TYPE] == cs.CHANNEL_TYPE_DBUS_TUBE # tube channel is automatically created event = q.expect('dbus-signal', signal='NewChannels', predicate=new_chan_predicate) path, props = event.args[0][0] assertEquals(cs.CHANNEL_TYPE_DBUS_TUBE, props[cs.CHANNEL_TYPE]) assertEquals(cs.HT_ROOM, props[cs.TARGET_HANDLE_TYPE]) assertEquals(room_handle, props[cs.TARGET_HANDLE]) assertEquals('chat@conf.localhost', props[cs.TARGET_ID]) assertEquals(False, props[cs.REQUESTED]) tube_chan = bus.get_object(conn.bus_name, path) tube_iface = dbus.Interface(tube_chan, cs.CHANNEL_TYPE_DBUS_TUBE) # reject the tube tube_iface.Close(dbus_interface=cs.CHANNEL) q.expect('dbus-signal', signal='ChannelClosed') # close the text channel text_chan.Close() if __name__ == '__main__': exec_test(test) telepathy-gabble-0.18.2/tests/twisted/tubes/check-create-tube-return.py0000644000175000017500000000511412227000321026114 0ustar00smcvsmcv00000000000000""" fd.o#25533 demonstrated that if you requested a {dbus,stream} tube channel to a room before you had actually joined the room (i.e. GabbleMucChannel was not ready yet) then the {Create,Request,Ensure}Channel return would be the Tubes channel (there for compatibility reasons), not the Tube channel you requested. """ import dbus from servicetest import call_async, EventPattern, assertContains, assertEquals from gabbletest import exec_test, acknowledge_iq, elem, make_muc_presence import constants as cs import tubetestutil as t from mucutil import join_muc from twisted.words.xish import xpath def test(q, bus, conn, stream): iq_event = q.expect('stream-iq', to=None, query_ns='vcard-temp', query_name='vCard') acknowledge_iq(stream, iq_event.stanza) t.check_conn_properties(q, conn) # Create new style tube channel and make sure that is indeed # returned. muc = 'chat@conf.localhost' call_async(q, conn.Requests, 'CreateChannel', { cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_DBUS_TUBE, cs.TARGET_HANDLE_TYPE: cs.HT_ROOM, cs.TARGET_ID: muc, cs.DBUS_TUBE_SERVICE_NAME: 'com.example.LolDongs'}) q.expect('stream-presence', to='%s/test' % muc) stream.send(make_muc_presence('owner', 'moderator', muc, 'bob')) stream.send(make_muc_presence('none', 'participant', muc, 'test')) ret, _, _ = q.expect_many( EventPattern('dbus-return', method='CreateChannel'), EventPattern('dbus-signal', signal='NewChannel'), EventPattern('dbus-signal', signal='NewChannels'), ) _, props = ret.value assertEquals(props[cs.CHANNEL_TYPE], cs.CHANNEL_TYPE_DBUS_TUBE) assertEquals(props[cs.TARGET_HANDLE_TYPE], cs.HT_ROOM) assertEquals(props[cs.TARGET_ID], muc) # Now try joining the text muc before asking for the tube channel. muc = 'chat2@conf.localhost' join_muc(q, bus, conn, stream, muc) call_async(q, conn.Requests, 'CreateChannel', { cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_DBUS_TUBE, cs.TARGET_HANDLE_TYPE: cs.HT_ROOM, cs.TARGET_ID: muc, cs.DBUS_TUBE_SERVICE_NAME: 'com.example.LolDongs'}) ret, _, _ = q.expect_many( EventPattern('dbus-return', method='CreateChannel'), EventPattern('dbus-signal', signal='NewChannel'), EventPattern('dbus-signal', signal='NewChannels'), ) _, props = ret.value assertEquals(props[cs.CHANNEL_TYPE], cs.CHANNEL_TYPE_DBUS_TUBE) assertEquals(props[cs.TARGET_HANDLE_TYPE], cs.HT_ROOM) assertEquals(props[cs.TARGET_ID], muc) if __name__ == '__main__': exec_test(test) telepathy-gabble-0.18.2/tests/twisted/tubes/accept-private-stream-tube.py0000644000175000017500000001533512200204333026467 0ustar00smcvsmcv00000000000000""" Receives several tube offers: - Test to accept a 1-1 stream tube - using UNIX sockets and IPv4 sockets - using the old tube iface and the new tube iface - Test to accept with bad parameters - Test to refuse the tube offer - using the old tube iface and the new tube iface """ import dbus from servicetest import call_async, EventPattern, sync_dbus, assertEquals from gabbletest import acknowledge_iq, send_error_reply, make_result_iq from twisted.words.xish import domish, xpath from twisted.internet import reactor import ns import constants as cs from bytestream import create_from_si_offer, announce_socks5_proxy import tubetestutil as t bob_jid = 'bob@localhost/Bob' stream_tube_id = 49 def receive_tube_offer(q, bus, conn, stream): global stream_tube_id message = domish.Element(('jabber:client', 'message')) message['to'] = 'test@localhost/Resource' message['from'] = bob_jid tube_node = message.addElement((ns.TUBES, 'tube')) tube_node['type'] = 'stream' tube_node['service'] = 'http' stream_tube_id += 1 tube_node['id'] = str(stream_tube_id) stream.send(message) def new_chan_predicate(e): path, props = e.args[0][0] return props[cs.CHANNEL_TYPE] == cs.CHANNEL_TYPE_STREAM_TUBE new_sig = q.expect('dbus-signal', signal='NewChannels', predicate=new_chan_predicate) assert len(new_sig.args) == 1 assert len(new_sig.args[0]) == 1 path, props = new_sig.args[0][0] assertEquals(cs.CHANNEL_TYPE_STREAM_TUBE, props[cs.CHANNEL_TYPE]) assertEquals(cs.HT_CONTACT, props[cs.TARGET_HANDLE_TYPE]) assertEquals(False, props[cs.REQUESTED]) # create channel proxies new_tube_chan = bus.get_object(conn.bus_name, path) new_tube_iface = dbus.Interface(new_tube_chan, cs.CHANNEL_TYPE_STREAM_TUBE) return (new_tube_chan, new_tube_iface) def expect_tube_activity(q, bus, conn, stream, bytestream_cls, address_type, address, access_control, access_control_param): event_socket, event_iq, conn_id = t.connect_to_cm_socket(q, bob_jid, address_type, address, access_control, access_control_param) protocol = event_socket.protocol data = "hello initiator" protocol.sendData(data) bytestream, profile = create_from_si_offer(stream, q, bytestream_cls, event_iq.stanza, 'test@localhost/Resource') assert profile == ns.TUBES stream_node = xpath.queryForNodes('/iq/si/stream[@xmlns="%s"]' % ns.TUBES, event_iq.stanza)[0] assert stream_node is not None assert stream_node['tube'] == str(stream_tube_id) result, si = bytestream.create_si_reply(event_iq.stanza) si.addElement((ns.TUBES, 'tube')) stream.send(result) bytestream.wait_bytestream_open() binary = bytestream.get_data(len(data)) assert data == binary, binary # reply to the initiator bytestream.send_data('hello joiner') e = q.expect('socket-data') assert e.data == 'hello joiner' return bytestream, conn_id def test(q, bus, conn, stream, bytestream_cls, address_type, access_control, access_control_param): if access_control == cs.SOCKET_ACCESS_CONTROL_CREDENTIALS: print "Skip Socket_Access_Control_Credentials (fdo #45445)" return vcard_event, roster_event, disco_event = q.expect_many( EventPattern('stream-iq', to=None, query_ns='vcard-temp', query_name='vCard'), EventPattern('stream-iq', query_ns=ns.ROSTER), EventPattern('stream-iq', to='localhost', query_ns=ns.DISCO_ITEMS)) acknowledge_iq(stream, vcard_event.stanza) announce_socks5_proxy(q, stream, disco_event.stanza) roster = roster_event.stanza roster['type'] = 'result' item = roster_event.query.addElement('item') item['jid'] = 'bob@localhost' # Bob can do tubes item['subscription'] = 'both' stream.send(roster) # Send Bob presence and his caps presence = domish.Element(('jabber:client', 'presence')) presence['from'] = 'bob@localhost/Bob' presence['to'] = 'test@localhost/Resource' c = presence.addElement('c') c['xmlns'] = 'http://jabber.org/protocol/caps' c['node'] = 'http://example.com/ICantBelieveItsNotTelepathy' c['ver'] = '1.2.3' stream.send(presence) event = q.expect('stream-iq', iq_type='get', query_ns='http://jabber.org/protocol/disco#info', to='bob@localhost/Bob') assert event.query['node'] == \ 'http://example.com/ICantBelieveItsNotTelepathy#1.2.3' result = make_result_iq(stream, event.stanza) query = result.firstChildElement() feature = query.addElement('feature') feature['var'] = ns.TUBES stream.send(result) sync_dbus(bus, q, conn) # Receive a tube offer from Bob (new_tube_chan, new_tube_iface) = \ receive_tube_offer(q, bus, conn, stream) # Try bad parameters on the new iface call_async(q, new_tube_iface, 'Accept', 20, 0, '', byte_arrays=True) q.expect('dbus-error', method='Accept') call_async(q, new_tube_iface, 'Accept', 0, 1, '', byte_arrays=True) q.expect('dbus-error', method='Accept') # Receive a tube offer from Bob (new_tube_chan, new_tube_iface) = \ receive_tube_offer(q, bus, conn, stream) # Accept the tube with new iface, and use UNIX sockets call_async(q, new_tube_iface, 'Accept', address_type, access_control, access_control_param, byte_arrays=True) accept_return_event, _ = q.expect_many( EventPattern('dbus-return', method='Accept'), EventPattern('dbus-signal', signal='TubeChannelStateChanged', args=[2])) socket_address = accept_return_event.value[0] bytestream, conn_id = expect_tube_activity(q, bus, conn, stream, bytestream_cls, address_type, socket_address, access_control, access_control_param) # peer closes the bytestream bytestream.close() e = q.expect('dbus-signal', signal='ConnectionClosed') assertEquals(conn_id, e.args[0]) assertEquals(cs.CONNECTION_LOST, e.args[1]) # establish another tube connection event_socket, si_event, conn_id = t.connect_to_cm_socket(q, bob_jid, address_type, socket_address, access_control, access_control_param) # bytestream is refused send_error_reply(stream, si_event.stanza) e = q.expect('dbus-signal', signal='ConnectionClosed') assertEquals(conn_id, e.args[0]) assertEquals(cs.CONNECTION_REFUSED, e.args[1]) new_tube_chan.Close() # Receive a tube offer from Bob (new_tube_chan, new_tube_iface) = \ receive_tube_offer(q, bus, conn, stream) # Just close the tube new_tube_chan.Close() q.expect_many( EventPattern('dbus-signal', signal='Closed'), EventPattern('dbus-signal', signal='ChannelClosed')) if __name__ == '__main__': t.exec_stream_tube_test(test) telepathy-gabble-0.18.2/tests/twisted/tubes/accept-private-dbus-tube.py0000644000175000017500000001267712227000321026137 0ustar00smcvsmcv00000000000000"""Test 1-1 tubes support.""" import dbus from servicetest import call_async, EventPattern, sync_dbus, assertEquals from gabbletest import acknowledge_iq, sync_stream import constants as cs import ns import tubetestutil as t from twisted.words.xish import domish, xpath last_tube_id = 69 def contact_offer_dbus_tube(bytestream, tube_id): iq, si = bytestream.create_si_offer(ns.TUBES) tube = si.addElement((ns.TUBES, 'tube')) tube['type'] = 'dbus' tube['service'] = 'com.example.TestCase2' tube['id'] = str(tube_id) parameters = tube.addElement((None, 'parameters')) parameter = parameters.addElement((None, 'parameter')) parameter['type'] = 'str' parameter['name'] = 'login' parameter.addContent('TEST') bytestream.stream.send(iq) def test(q, bus, conn, stream, bytestream_cls, access_control): global last_tube_id t.check_conn_properties(q, conn) vcard_event, roster_event = q.expect_many( EventPattern('stream-iq', to=None, query_ns='vcard-temp', query_name='vCard'), EventPattern('stream-iq', query_ns=ns.ROSTER)) self_handle = conn.GetSelfHandle() acknowledge_iq(stream, vcard_event.stanza) roster = roster_event.stanza roster['type'] = 'result' item = roster_event.query.addElement('item') item['jid'] = 'bob@localhost' # Bob can do tubes item['subscription'] = 'both' stream.send(roster) bob_full_jid = 'bob@localhost/Bob' self_full_jid = 'test@localhost/Resource' # Send Bob presence and his tube caps presence = domish.Element(('jabber:client', 'presence')) presence['from'] = bob_full_jid presence['to'] = self_full_jid c = presence.addElement('c') c['xmlns'] = 'http://jabber.org/protocol/caps' c['node'] = 'http://example.com/ICantBelieveItsNotTelepathy' c['ver'] = '1.2.3' stream.send(presence) event = q.expect('stream-iq', iq_type='get', query_ns='http://jabber.org/protocol/disco#info', to=bob_full_jid) result = event.stanza result['type'] = 'result' assert event.query['node'] == \ 'http://example.com/ICantBelieveItsNotTelepathy#1.2.3' feature = event.query.addElement('feature') feature['var'] = ns.TUBES stream.send(result) # A tube request can be done only if the contact has tube capabilities # Ensure that Bob's caps have been received sync_stream(q, stream) # Also ensure that all the new contact list channels have been announced, # so that the NewChannel(s) signals we look for after calling # RequestChannel are the ones we wanted. sync_dbus(bus, q, conn) bob_handle = conn.RequestHandles(cs.HT_CONTACT, ['bob@localhost'])[0] # let's try to accept a D-Bus tube using the new API bytestream = bytestream_cls(stream, q, 'gamma', bob_full_jid, self_full_jid, True) last_tube_id += 1 contact_offer_dbus_tube(bytestream, last_tube_id) def new_chan_predicate(e): path, props = e.args[0][0] return props[cs.CHANNEL_TYPE] == cs.CHANNEL_TYPE_DBUS_TUBE e = q.expect('dbus-signal', signal='NewChannels', predicate=new_chan_predicate) channels = e.args[0] assert len(channels) == 1 path, props = channels[0] assert props[cs.CHANNEL_TYPE] == cs.CHANNEL_TYPE_DBUS_TUBE assert props[cs.INITIATOR_HANDLE] == bob_handle assert props[cs.INITIATOR_ID] == 'bob@localhost' assert props[cs.INTERFACES] == [cs.CHANNEL_IFACE_TUBE] assert props[cs.REQUESTED] == False assert props[cs.TARGET_HANDLE] == bob_handle assert props[cs.TARGET_ID] == 'bob@localhost' assert props[cs.DBUS_TUBE_SERVICE_NAME] == 'com.example.TestCase2' assert props[cs.TUBE_PARAMETERS] == {'login': 'TEST'} assert props[cs.DBUS_TUBE_SUPPORTED_ACCESS_CONTROLS] == [cs.SOCKET_ACCESS_CONTROL_CREDENTIALS, cs.SOCKET_ACCESS_CONTROL_LOCALHOST] assert cs.TUBE_STATE not in props tube_chan = bus.get_object(conn.bus_name, path) tube_chan_iface = dbus.Interface(tube_chan, cs.CHANNEL) dbus_tube_iface = dbus.Interface(tube_chan, cs.CHANNEL_TYPE_DBUS_TUBE) status = tube_chan.Get(cs.CHANNEL_IFACE_TUBE, 'State', dbus_interface=cs.PROPERTIES_IFACE) assert status == cs.TUBE_STATE_LOCAL_PENDING # try to accept using a wrong access control try: dbus_tube_iface.Accept(cs.SOCKET_ACCESS_CONTROL_PORT) except dbus.DBusException, e: assertEquals(e.get_dbus_name(), cs.INVALID_ARGUMENT) else: assert False # accept the tube (new API) call_async(q, dbus_tube_iface, 'Accept', access_control) events = q.expect_many (EventPattern('stream-iq', iq_type='result', query_ns=ns.SI), EventPattern('dbus-return', method='Accept')) iq_event = events[0] bytestream.check_si_reply(iq_event.stanza) tube = xpath.queryForNodes('/iq/si/tube[@xmlns="%s"]' % ns.TUBES, iq_event.stanza) assert len(tube) == 1 return_event = events[1] addr = return_event.value[0] assert len(addr) > 0 # Open the bytestream _, state_event = bytestream.open_bytestream([], [EventPattern('dbus-signal', signal='TubeChannelStateChanged', path=path)]) state_event = state_event[0] assert state_event.args[0] == cs.TUBE_STATE_OPEN # close the tube tube_chan_iface.Close() q.expect_many( EventPattern('dbus-signal', signal='Closed'), EventPattern('dbus-signal', signal='ChannelClosed')) if __name__ == '__main__': t.exec_dbus_tube_test(test) telepathy-gabble-0.18.2/tests/twisted/tubes/accept-muc-stream-tube.py0000644000175000017500000002050312227000321025572 0ustar00smcvsmcv00000000000000"""Test IBB stream tube support in the context of a MUC.""" import sys import dbus from servicetest import call_async, EventPattern, assertEquals, assertSameSets from gabbletest import acknowledge_iq, make_muc_presence, send_error_reply, disconnect_conn import constants as cs import ns import tubetestutil as t from bytestream import create_from_si_offer, announce_socks5_proxy, BytestreamS5BRelay, BytestreamS5BRelayBugged from twisted.words.xish import xpath sample_parameters = dbus.Dictionary({ 's': 'hello', 'ay': dbus.ByteArray('hello'), 'u': dbus.UInt32(123), 'i': dbus.Int32(-123), }, signature='sv') def test(q, bus, conn, stream, bytestream_cls, address_type, access_control, access_control_param): if bytestream_cls in [BytestreamS5BRelay, BytestreamS5BRelayBugged]: # disable SOCKS5 relay tests because proxy can't be used with muc # contacts atm return if access_control == cs.SOCKET_ACCESS_CONTROL_CREDENTIALS: print "Skip Socket_Access_Control_Credentials (fdo #45445)" return iq_event, disco_event = q.expect_many( EventPattern('stream-iq', to=None, query_ns='vcard-temp', query_name='vCard'), EventPattern('stream-iq', to='localhost', query_ns=ns.DISCO_ITEMS)) acknowledge_iq(stream, iq_event.stanza) announce_socks5_proxy(q, stream, disco_event.stanza) call_async(q, conn, 'RequestHandles', 2, ['chat@conf.localhost']) event = q.expect('dbus-return', method='RequestHandles') handles = event.value[0] room_handle = handles[0] # join the muc call_async(q, conn.Requests, 'CreateChannel', { cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_TEXT, cs.TARGET_HANDLE_TYPE: cs.HT_ROOM, cs.TARGET_HANDLE: room_handle}) _, stream_event = q.expect_many( EventPattern('dbus-signal', signal='MembersChanged', args=[u'', [], [], [], [2], 0, 0]), EventPattern('stream-presence', to='chat@conf.localhost/test')) # Send presence for other member of room. stream.send(make_muc_presence('owner', 'moderator', 'chat@conf.localhost', 'bob')) # Send presence for own membership of room. stream.send(make_muc_presence('none', 'participant', 'chat@conf.localhost', 'test')) q.expect('dbus-signal', signal='MembersChanged', args=[u'', [2, 3], [], [], [], 0, 0]) assert conn.InspectHandles(1, [2]) == ['chat@conf.localhost/test'] assert conn.InspectHandles(1, [3]) == ['chat@conf.localhost/bob'] bob_handle = 3 event = q.expect('dbus-return', method='CreateChannel') # Bob offers a stream tube stream_tube_id = 666 presence = make_muc_presence('owner', 'moderator', 'chat@conf.localhost', 'bob') tubes = presence.addElement((ns.TUBES, 'tubes')) tube = tubes.addElement((None, 'tube')) tube['type'] = 'stream' tube['service'] = 'echo' tube['id'] = str(stream_tube_id) parameters = tube.addElement((None, 'parameters')) parameter = parameters.addElement((None, 'parameter')) parameter['name'] = 's' parameter['type'] = 'str' parameter.addContent('hello') parameter = parameters.addElement((None, 'parameter')) parameter['name'] = 'ay' parameter['type'] = 'bytes' parameter.addContent('aGVsbG8=') parameter = parameters.addElement((None, 'parameter')) parameter['name'] = 'u' parameter['type'] = 'uint' parameter.addContent('123') parameter = parameters.addElement((None, 'parameter')) parameter['name'] = 'i' parameter['type'] = 'int' parameter.addContent('-123') stream.send(presence) # text channel event, new_event = q.expect_many( EventPattern('dbus-signal', signal='NewChannel'), EventPattern('dbus-signal', signal='NewChannels')) assert event.args[1] == cs.CHANNEL_TYPE_TEXT, event.args channels = new_event.args[0] assert len(channels) == 1 path, props = channels[0] assert props[cs.CHANNEL_TYPE] == cs.CHANNEL_TYPE_TEXT def new_chan_predicate(e): path, props = e.args[0][0] return props[cs.CHANNEL_TYPE] == cs.CHANNEL_TYPE_STREAM_TUBE # tube channel is announced new_event = q.expect('dbus-signal', signal='NewChannels', predicate=new_chan_predicate) channels = new_event.args[0] assert len(channels) == 1 path, props = channels[0] assert props[cs.CHANNEL_TYPE] == cs.CHANNEL_TYPE_STREAM_TUBE assert props[cs.INITIATOR_HANDLE] == bob_handle assert props[cs.INITIATOR_ID] == 'chat@conf.localhost/bob' assertSameSets([cs.CHANNEL_IFACE_GROUP, cs.CHANNEL_IFACE_TUBE], props[cs.INTERFACES]) assert props[cs.REQUESTED] == False assert props[cs.TARGET_HANDLE] == room_handle assert props[cs.TARGET_ID] == 'chat@conf.localhost' assert props[cs.STREAM_TUBE_SERVICE] == 'echo' assert props[cs.TUBE_PARAMETERS] == {'s': 'hello', 'ay': 'hello', 'u': 123, 'i': -123} assert access_control in \ props[cs.STREAM_TUBE_SUPPORTED_SOCKET_TYPES][address_type] tube_chan = bus.get_object(conn.bus_name, path) tube_props = tube_chan.GetAll(cs.CHANNEL_IFACE_TUBE, dbus_interface=cs.PROPERTIES_IFACE, byte_arrays=True) tube_iface = dbus.Interface(tube_chan, cs.CHANNEL_TYPE_STREAM_TUBE) assert tube_props['Parameters'] == sample_parameters assert tube_props['State'] == cs.TUBE_CHANNEL_STATE_LOCAL_PENDING # Accept the tube call_async(q, tube_iface, 'Accept', address_type, access_control, access_control_param, byte_arrays=True) accept_return_event, _ = q.expect_many( EventPattern('dbus-return', method='Accept'), EventPattern('dbus-signal', signal='TubeChannelStateChanged', args=[2])) address = accept_return_event.value[0] socket_event, si_event, conn_id = t.connect_to_cm_socket(q, 'chat@conf.localhost/bob', address_type, address, access_control, access_control_param) protocol = socket_event.protocol protocol.sendData("hello initiator") def accept_tube_si_connection(): bytestream, profile = create_from_si_offer(stream, q, bytestream_cls, si_event.stanza, 'chat@conf.localhost/test') assert profile == ns.TUBES muc_stream_node = xpath.queryForNodes('/iq/si/muc-stream[@xmlns="%s"]' % ns.TUBES, si_event.stanza)[0] assert muc_stream_node is not None assert muc_stream_node['tube'] == str(stream_tube_id) # set the real jid of the target as 'to' because the XMPP server changes # it when delivering the IQ result, si = bytestream.create_si_reply(si_event.stanza, 'test@localhost/Resource') si.addElement((ns.TUBES, 'tube')) stream.send(result) bytestream.wait_bytestream_open() return bytestream bytestream = accept_tube_si_connection() binary = bytestream.get_data() assert binary == 'hello initiator' # reply on the socket bytestream.send_data('hi joiner!') q.expect('socket-data', protocol=protocol, data="hi joiner!") # peer closes the bytestream bytestream.close() e = q.expect('dbus-signal', signal='ConnectionClosed') assertEquals(conn_id, e.args[0]) assertEquals(cs.CONNECTION_LOST, e.args[1]) # establish another tube connection socket_event, si_event, conn_id = t.connect_to_cm_socket(q, 'chat@conf.localhost/bob', address_type, address, access_control, access_control_param) # bytestream is refused send_error_reply(stream, si_event.stanza) e, _ = q.expect_many( EventPattern('dbus-signal', signal='ConnectionClosed'), EventPattern('socket-disconnected')) assertEquals(conn_id, e.args[0]) assertEquals(cs.CONNECTION_REFUSED, e.args[1]) # establish another tube connection socket_event, si_event, conn_id = t.connect_to_cm_socket(q, 'chat@conf.localhost/bob', address_type, address, access_control, access_control_param) protocol = socket_event.protocol bytestream = accept_tube_si_connection() # disconnect local socket protocol.transport.loseConnection() e, _ = q.expect_many( EventPattern('dbus-signal', signal='ConnectionClosed'), EventPattern('socket-disconnected')) assertEquals(conn_id, e.args[0]) assertEquals(cs.CANCELLED, e.args[1]) # OK, we're done disconnect_conn(q, conn, stream) if __name__ == '__main__': t.exec_stream_tube_test(test) telepathy-gabble-0.18.2/tests/twisted/tubes/accept-muc-dbus-tube.py0000644000175000017500000001136312227000321025240 0ustar00smcvsmcv00000000000000import dbus from servicetest import ( assertEquals, assertNotEquals, assertSameSets, call_async, EventPattern, ) from gabbletest import exec_test, acknowledge_iq, make_muc_presence import constants as cs from twisted.words.xish import xpath import ns from mucutil import join_muc_and_check def test(q, bus, conn, stream, access_control): iq_event = q.expect('stream-iq', to=None, query_ns='vcard-temp', query_name='vCard') acknowledge_iq(stream, iq_event.stanza) muc = 'chat@conf.localhost' _, _, test_handle, bob_handle = \ join_muc_and_check(q, bus, conn, stream, muc) # Bob offers a stream tube bob_bus_name = ':2.Ym9i' presence = make_muc_presence('owner', 'moderator', 'chat@conf.localhost', 'bob') tubes = presence.addElement((ns.TUBES, 'tubes')) tube = tubes.addElement((None, 'tube')) tube['type'] = 'dbus' tube['initiator'] = 'chat@conf.localhost/bob' tube['stream-id'] = '10' tube['id'] = '1' tube['service'] = 'com.example.Test' tube['dbus-name'] = bob_bus_name parameters = tube.addElement((None, 'parameters')) parameter = parameters.addElement((None, 'parameter')) parameter['type'] = 'str' parameter['name'] = 'foo' parameter.addContent('bar') stream.send(presence) # tube channel is created def new_chan_predicate(e): path, props = e.args[0][0] return props[cs.CHANNEL_TYPE] == cs.CHANNEL_TYPE_DBUS_TUBE event = q.expect('dbus-signal', signal='NewChannels', predicate=new_chan_predicate) channels = event.args[0] path, props = channels[0] assertEquals(cs.CHANNEL_TYPE_DBUS_TUBE, props[cs.CHANNEL_TYPE]) assertEquals('chat@conf.localhost/bob', props[cs.INITIATOR_ID]) bob_handle = props[cs.INITIATOR_HANDLE] assertSameSets([cs.CHANNEL_IFACE_GROUP, cs.CHANNEL_IFACE_TUBE], props[cs.INTERFACES]) assertEquals(False, props[cs.REQUESTED]) assertEquals('chat@conf.localhost', props[cs.TARGET_ID]) assertEquals('com.example.Test', props[cs.DBUS_TUBE_SERVICE_NAME]) assertEquals({'foo': 'bar'}, props[cs.TUBE_PARAMETERS]) assertEquals([cs.SOCKET_ACCESS_CONTROL_CREDENTIALS, cs.SOCKET_ACCESS_CONTROL_LOCALHOST], props[cs.DBUS_TUBE_SUPPORTED_ACCESS_CONTROLS]) tube_chan = bus.get_object(conn.bus_name, path) tube_iface = dbus.Interface(tube_chan, cs.CHANNEL_IFACE_TUBE) dbus_tube_iface = dbus.Interface(tube_chan, cs.CHANNEL_TYPE_DBUS_TUBE) tube_chan_iface = dbus.Interface(tube_chan, cs.CHANNEL) # only Bob is in DBusNames dbus_names = tube_chan.Get(cs.CHANNEL_TYPE_DBUS_TUBE, 'DBusNames', dbus_interface=cs.PROPERTIES_IFACE) assertEquals({bob_handle: bob_bus_name}, dbus_names) call_async(q, dbus_tube_iface, 'Accept', access_control) return_event, names_changed1, names_changed2, presence_event = q.expect_many( EventPattern('dbus-return', method='Accept'), EventPattern('dbus-signal', signal='DBusNamesChanged', interface=cs.CHANNEL_TYPE_DBUS_TUBE), EventPattern('dbus-signal', signal='DBusNamesChanged', interface=cs.CHANNEL_TYPE_DBUS_TUBE), EventPattern('stream-presence', to='chat@conf.localhost/test')) tube_addr = return_event.value[0] assert len(tube_addr) > 0 # check presence stanza tube_node = xpath.queryForNodes('/presence/tubes/tube', presence_event.stanza)[0] assertEquals('chat@conf.localhost/bob', tube_node['initiator']) assertEquals('com.example.Test', tube_node['service']) assertEquals('10', tube_node['stream-id']) assertEquals('dbus', tube_node['type']) assertEquals('1', tube_node['id']) self_bus_name = tube_node['dbus-name'] tubes_self_handle = tube_chan.GetSelfHandle(dbus_interface=cs.CHANNEL_IFACE_GROUP) assertNotEquals(0, tubes_self_handle) # both of us are in DBusNames now dbus_names = tube_chan.Get(cs.CHANNEL_TYPE_DBUS_TUBE, 'DBusNames', dbus_interface=cs.PROPERTIES_IFACE) assertEquals({bob_handle: bob_bus_name, tubes_self_handle: self_bus_name}, dbus_names) added, removed = names_changed1.args assertEquals({bob_handle: bob_bus_name}, added) assertEquals([], removed) added, removed = names_changed2.args assertEquals({tubes_self_handle: self_bus_name}, added) assertEquals([], removed) tube_chan_iface.Close() q.expect_many( EventPattern('dbus-signal', signal='Closed'), EventPattern('dbus-signal', signal='ChannelClosed')) if __name__ == '__main__': # We can't use t.exec_dbus_tube_test() as we can use only the muc bytestream exec_test(lambda q, bus, conn, stream: test(q, bus, conn, stream, cs.SOCKET_ACCESS_CONTROL_CREDENTIALS)) exec_test(lambda q, bus, conn, stream: test(q, bus, conn, stream, cs.SOCKET_ACCESS_CONTROL_LOCALHOST)) telepathy-gabble-0.18.2/tests/twisted/tools/0000755000175000017500000000000012312537051021001 5ustar00smcvsmcv00000000000000telepathy-gabble-0.18.2/tests/twisted/tools/run-gabble.sh.in0000755000175000017500000000055212200204333023753 0ustar00smcvsmcv00000000000000#!/bin/sh G_DEBUG=fatal-warnings,fatal-criticals export G_DEBUG GABBLE_TIMING=1 export GABBLE_TIMING GABBLE_PLUGIN_DIR="@gabbletestsdir@/plugins:@pluginexecdir@" export GABBLE_PLUGIN_DIR WOCKY_CAPS_CACHE=:memory: export WOCKY_CAPS_CACHE WOCKY_CAPS_CACHE_SIZE=50 export WOCKY_CAPS_CACHE_SIZE ulimit -c unlimited @gabbletestsdir@/twisted/telepathy-gabble-debug telepathy-gabble-0.18.2/tests/twisted/tools/exec-with-log.sh.in0000644000175000017500000000367612227000321024420 0ustar00smcvsmcv00000000000000#!/bin/sh cd "@abs_top_builddir@/tests/twisted/tools" 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" export GABBLE_PLUGIN_DIR WOCKY_CAPS_CACHE=:memory: export WOCKY_CAPS_CACHE WOCKY_CAPS_CACHE_SIZE=50 export WOCKY_CAPS_CACHE_SIZE G_MESSAGES_DEBUG=all export G_MESSAGES_DEBUG ulimit -c unlimited exec >> gabble-testing.log 2>&1 G_SLICE=debug-blocks export G_SLICE if test -n "$GABBLE_TEST_VALGRIND"; then G_DEBUG=${G_DEBUG:+"${G_DEBUG},"}gc-friendly export G_DEBUG G_SLICE=${G_SLICE},always-malloc export G_SLICE DBUS_DISABLE_MEM_POOLS=1 export DBUS_DISABLE_MEM_POOLS GABBLE_WRAPPER="valgrind --leak-check=full --num-callers=20" GABBLE_WRAPPER="$GABBLE_WRAPPER --show-reachable=yes" GABBLE_WRAPPER="$GABBLE_WRAPPER --gen-suppressions=all" GABBLE_WRAPPER="$GABBLE_WRAPPER --child-silent-after-fork=yes" GABBLE_WRAPPER="$GABBLE_WRAPPER --suppressions=@abs_top_srcdir@/tests/suppressions/tp-glib.supp" GABBLE_WRAPPER="$GABBLE_WRAPPER --suppressions=@abs_top_srcdir@/tests/suppressions/gabble.supp" elif test -n "$GABBLE_TEST_REFDBG"; then if test -z "$REFDBG_OPTIONS" ; then REFDBG_OPTIONS="btnum=10" export REFDBG_OPTIONS fi if test -z "$GABBLE_WRAPPER" ; then GABBLE_WRAPPER="refdbg" fi elif test -n "$GABBLE_TEST_STRACE"; then GABBLE_WRAPPER="strace -o strace.log" elif test -n "$GABBLE_TEST_BACKTRACE"; then GABBLE_WRAPPER="gdb -x run_and_bt.gdb" fi # Prevent libproxy from hitting the network for wpad configuration PX_MODULE_BLACKLIST=config_wpad export PX_MODULE_BLACKLIST G_DEBUG=fatal-warnings,fatal-criticals" ${G_DEBUG}" export G_DEBUG exec @abs_top_builddir@/libtool --mode=execute $GABBLE_WRAPPER ../telepathy-gabble-debug telepathy-gabble-0.18.2/tests/twisted/tools/with-session-bus.sh0000644000175000017500000000555712200204333024562 0ustar00smcvsmcv00000000000000#!/bin/sh # with-session-bus.sh - run a program with a temporary D-Bus session daemon # # The canonical location of this program is the telepathy-glib tools/ # directory, please synchronize any changes with that copy. # # Copyright (C) 2007-2008 Collabora Ltd. # # Copying and distribution of this file, with or without modification, # are permitted in any medium without royalty provided the copyright # notice and this notice are preserved. set -e me=with-session-bus dbus_daemon_args="--print-address=5 --print-pid=6 --fork" sleep=0 usage () { echo "usage: $me [options] -- program [program_options]" >&2 echo "Requires write access to the current directory." >&2 echo "" >&2 echo "If \$WITH_SESSION_BUS_FORK_DBUS_MONITOR is set, fork dbus-monitor" >&2 echo "with the arguments in \$WITH_SESSION_BUS_FORK_DBUS_MONITOR_OPT." >&2 echo "The output of dbus-monitor is saved in $me-.dbus-monitor-logs" >&2 exit 2 } while test "z$1" != "z--"; do case "$1" in --sleep=*) sleep="$1" sleep="${sleep#--sleep=}" shift ;; --session) dbus_daemon_args="$dbus_daemon_args --session" shift ;; --config-file=*) # FIXME: assumes config file doesn't contain any special characters dbus_daemon_args="$dbus_daemon_args $1" shift ;; --also-for-system) with_system_bus=1 shift ;; *) usage ;; esac done shift if test "z$1" = "z"; then usage; fi exec 5> $me-$$.address exec 6> $me-$$.pid cleanup () { pid=`head -n1 $me-$$.pid` if test -n "$pid" ; then if [ -n "$CHECK_TWISTED_VERBOSE" ] || [ -n "$VERBOSE_TESTS" ]; then echo "Killing temporary bus daemon: $pid" >&2 fi kill -INT "$pid" fi rm -f $me-$$.address rm -f $me-$$.pid } trap cleanup INT HUP TERM dbus-daemon $dbus_daemon_args if [ -n "$CHECK_TWISTED_VERBOSE" ] || [ -n "$VERBOSE_TESTS" ]; then { echo -n "Temporary bus daemon is "; cat $me-$$.address; } >&2 { echo -n "Temporary bus daemon PID is "; head -n1 $me-$$.pid; } >&2 fi e=0 # These might be non-null when run from e.g. gnome-terminal 3.8, which uses # an activatable service for its windows; we don't want to inherit them either unset DBUS_STARTER_ADDRESS unset DBUS_STARTER_BUS_TYPE DBUS_SESSION_BUS_ADDRESS="`cat $me-$$.address`" export DBUS_SESSION_BUS_ADDRESS if [ -n "$with_system_bus" ] ; then DBUS_SYSTEM_BUS_ADDRESS="$DBUS_SESSION_BUS_ADDRESS" export DBUS_SYSTEM_BUS_ADDRESS fi if [ -n "$WITH_SESSION_BUS_FORK_DBUS_MONITOR" ] ; then echo "Forking dbus-monitor $WITH_SESSION_BUS_FORK_DBUS_MONITOR_OPT" >&2 dbus-monitor $WITH_SESSION_BUS_FORK_DBUS_MONITOR_OPT \ > $me-$$.dbus-monitor-logs 2>&1 & fi if [ -n "$GABBLE_TEST_BUSTLE" ]; then echo "Forking bustle-dbus-monitor" >&2 bustle-dbus-monitor > tools/$me-$$.bustle-logs 2>&1 & fi "$@" || e=$? if test $sleep != 0; then sleep $sleep fi trap - INT HUP TERM cleanup exit $e telepathy-gabble-0.18.2/tests/twisted/tools/servicedir-uninstalled/0000755000175000017500000000000012312537051025460 5ustar00smcvsmcv00000000000000telepathy-gabble-0.18.2/tests/twisted/tools/servicedir-uninstalled/tmp-session-bus.conf.in0000644000175000017500000000164012200204333031773 0ustar00smcvsmcv00000000000000 session unix:tmpdir=/tmp @abs_top_builddir@/tests/twisted/tools/servicedir-uninstalled telepathy-gabble-0.18.2/tests/twisted/tools/servicedir-uninstalled/gabble.service.in0000644000175000017500000000020512200204333030646 0ustar00smcvsmcv00000000000000[D-BUS Service] Name=org.freedesktop.Telepathy.ConnectionManager.gabble Exec=@abs_top_builddir@/tests/twisted/tools/exec-with-log.sh telepathy-gabble-0.18.2/tests/twisted/tools/servicedir/0000755000175000017500000000000012312537051023140 5ustar00smcvsmcv00000000000000telepathy-gabble-0.18.2/tests/twisted/tools/servicedir/tmp-session-bus.conf.in0000644000175000017500000000161412200204333027454 0ustar00smcvsmcv00000000000000 session unix:tmpdir=/tmp @gabbletestsdir@/twisted/tools/servicedir telepathy-gabble-0.18.2/tests/twisted/tools/servicedir/gabble.service.in0000644000175000017500000000017212200204333026331 0ustar00smcvsmcv00000000000000[D-BUS Service] Name=org.freedesktop.Telepathy.ConnectionManager.gabble Exec=@gabbletestsdir@/twisted/tools/run-gabble.sh telepathy-gabble-0.18.2/tests/twisted/tls/0000755000175000017500000000000012312537051020443 5ustar00smcvsmcv00000000000000telepathy-gabble-0.18.2/tests/twisted/tls/server-tls-channel.py0000644000175000017500000003402212200204333024520 0ustar00smcvsmcv00000000000000# coding=utf-8 """ Copyright © 2010 Collabora Ltd. Copying and distribution of this file, with or without modification, are permitted in any medium without royalty provided the copyright notice and this notice are preserved. """ """ Test the server TLS channel """ import base64 import dbus import os from OpenSSL import crypto from twisted.words.protocols.jabber import xmlstream from twisted.words.protocols.jabber.client import IQ from twisted.words.xish import domish, xpath from twisted.internet import ssl import ns from gabbletest import exec_test, XmppAuthenticator from servicetest import ProxyWrapper, EventPattern from servicetest import assertEquals, assertLength, assertSameSets import constants as cs JID = "test@example.org" # the certificate is for the domain name 'weasel-juice.org'. # the files are copied from wocky/tests/certs/tls-[cert,key].pem CA_CERT_HOSTNAME = 'weasel-juice.org' CA_CERT = os.environ.get('GABBLE_TWISTED_PATH', '.') + '/tls-cert.pem' CA_KEY = os.environ.get('GABBLE_TWISTED_PATH', '.') + '/tls-key.pem' class TlsAuthenticator(XmppAuthenticator): def __init__(self, username, password, resource=None): XmppAuthenticator.__init__(self, username, password, resource) self.tls_encrypted = False def streamTLS(self): features = domish.Element((xmlstream.NS_STREAMS, 'features')) starttls = features.addElement((ns.NS_XMPP_TLS, 'starttls')) starttls.addElement('required') self.xmlstream.send(features) self.xmlstream.addOnetimeObserver("/starttls", self.tlsAuth) def streamStarted(self, root=None): self.streamInitialize(root) if self.authenticated and self.tls_encrypted: self.streamIQ() elif self.tls_encrypted: self.streamSASL() else: self.streamTLS() def tlsAuth(self, auth): with open(CA_KEY, 'rb') as f: pem_key = f.read() pkey = crypto.load_privatekey(crypto.FILETYPE_PEM, pem_key, "") with open(CA_CERT, 'rb') as f: pem_cert = f.read() cert = crypto.load_certificate(crypto.FILETYPE_PEM, pem_cert) tls_ctx = ssl.CertificateOptions(privateKey=pkey, certificate=cert) self.xmlstream.send(domish.Element((ns.NS_XMPP_TLS, 'proceed'))) self.xmlstream.transport.startTLS(tls_ctx) self.xmlstream.reset() self.tls_encrypted = True class ServerTlsChanWrapper(ProxyWrapper): def __init__(self, object, default=cs.CHANNEL, interfaces={ "ServerTLSConnection" : cs.CHANNEL_TYPE_SERVER_TLS_CONNECTION}): ProxyWrapper.__init__(self, object, default, interfaces) class TlsCertificateWrapper(ProxyWrapper): def __init__(self, object, default=cs.AUTH_TLS_CERT, interfaces={ "TLSCertificate" : cs.AUTH_TLS_CERT}): ProxyWrapper.__init__(self, object, default, interfaces) def test_disconnect_inbetween(q, bus, conn, stream): # we don't expect a channel at all in this case, # as we lose the connection before the TLS channel is created conn.Connect() q.expect('dbus-signal', signal='StatusChanged', args=[cs.CONN_STATUS_CONNECTING, cs.CSR_REQUESTED]) conn.Disconnect() q.expect('dbus-signal', signal='StatusChanged', args=[cs.CONN_STATUS_DISCONNECTED, cs.CSR_REQUESTED]) def is_server_tls_chan_event(event): channels = event.args[0]; if len(channels) > 1: return False path, props = channels[0] return props[cs.CHANNEL_TYPE] == cs.CHANNEL_TYPE_SERVER_TLS_CONNECTION def connect_and_get_tls_objects(q, bus, conn, expect_example_jid=True): conn.Connect() q.expect('dbus-signal', signal='StatusChanged', args=[cs.CONN_STATUS_CONNECTING, cs.CSR_REQUESTED]) ev, = q.expect_many( EventPattern('dbus-signal', signal='NewChannels', predicate=is_server_tls_chan_event)) channels = ev.args[0] path, props = channels[0] chan = ServerTlsChanWrapper(bus.get_object(conn.bus_name, path)) hostname = props[cs.TLS_HOSTNAME] certificate_path = props[cs.TLS_CERT_PATH] if expect_example_jid: assertEquals(hostname, 'example.org') return chan, hostname, certificate_path def test_connect_early_close_success(q, bus, conn, stream): chan, hostname, certificate_path = connect_and_get_tls_objects(q, bus, conn) # close the channel early chan.Close() # we expect the fallback verification process to connect successfully, # even if the certificate doesn't match the hostname, as encryption-required is not set q.expect_many( EventPattern('dbus-signal', signal='Closed'), EventPattern('dbus-signal', signal='ChannelClosed'), EventPattern('dbus-signal', signal='StatusChanged', args=[cs.CONN_STATUS_CONNECTED, cs.CSR_REQUESTED]) ) def test_connect_early_close_fail(q, bus, conn, stream): chan, hostname, certificate_path = connect_and_get_tls_objects(q, bus, conn) # close the channel early chan.Close() # we expect the fallback verification process to fail, as there's a hostname mismatch, # encryption-required is set and ignore-ssl-errors is not q.expect_many( EventPattern('dbus-signal', signal='Closed'), EventPattern('dbus-signal', signal='ChannelClosed'), EventPattern('dbus-signal', signal='StatusChanged', args=[cs.CONN_STATUS_DISCONNECTED, cs.CSR_CERT_HOSTNAME_MISMATCH]) ) def rejection_list_match(event): rejections = event.args[0]; if len(rejections) != 1: return False return rejections == [(cs.TLS_REJECT_REASON_UNTRUSTED, cs.CERT_UNTRUSTED, {})] def test_connect_fail(q, bus, conn, stream): chan, hostname, certificate_path = connect_and_get_tls_objects(q, bus, conn) certificate = TlsCertificateWrapper(bus.get_object(conn.bus_name, certificate_path)) certificate.TLSCertificate.Reject([(cs.TLS_REJECT_REASON_UNTRUSTED, cs.CERT_UNTRUSTED, {})]) # we first expect the certificate to be rejected q.expect('dbus-signal', signal='Rejected', predicate=rejection_list_match) # this should trigger a ConnectionError q.expect('dbus-signal', signal='ConnectionError', args=[cs.CERT_UNTRUSTED, {}]) # at this point the channel should be closed q.expect_many( EventPattern('dbus-signal', signal='Closed'), EventPattern('dbus-signal', signal='ChannelClosed') ) # finally, we should receive a StatusChanged signal on the connection q.expect('dbus-signal', signal='StatusChanged', args=[cs.CONN_STATUS_DISCONNECTED, cs.CSR_CERT_UNTRUSTED]) def test_connect_success(q, bus, conn, stream): chan, hostname, certificate_path = connect_and_get_tls_objects(q, bus, conn) certificate = TlsCertificateWrapper(bus.get_object(conn.bus_name, certificate_path)) certificate.TLSCertificate.Accept() q.expect('dbus-signal', signal='Accepted') cert_props = dbus.Interface(certificate, cs.PROPERTIES_IFACE) state = cert_props.Get(cs.AUTH_TLS_CERT, 'State') rejections = cert_props.Get(cs.AUTH_TLS_CERT, 'Rejections') assertLength(0, rejections) chan.Close() q.expect_many( EventPattern('dbus-signal', signal='Closed'), EventPattern('dbus-signal', signal='ChannelClosed'), EventPattern('dbus-signal', signal='StatusChanged', args=[cs.CONN_STATUS_CONNECTED, cs.CSR_REQUESTED]) ) def connect_and_get_tls_properties(q, bus, conn): chan, hostname, certificate_path = connect_and_get_tls_objects(q, bus, conn) chan_props = dbus.Interface(chan, cs.PROPERTIES_IFACE) return chan_props.GetAll(cs.CHANNEL_TYPE_SERVER_TLS_CONNECTION) def test_channel_reference_identity(q, bus, conn, stream): props = connect_and_get_tls_properties (q, bus, conn) reference_identities = props["ReferenceIdentities"] assertSameSets(reference_identities, [ "example.org", "localhost"]) assertEquals(props["Hostname"], "example.org") def test_channel_reference_identity_with_extra(q, bus, conn, stream): props = connect_and_get_tls_properties (q, bus, conn) reference_identities = props["ReferenceIdentities"] assertSameSets(reference_identities, [ "example.org", "hypnotoad.example.org", "localhost" ]) assertEquals(props["Hostname"], "example.org") def test_channel_reference_identity_with_extra_multiple(q, bus, conn, stream): props = connect_and_get_tls_properties (q, bus, conn) reference_identities = props["ReferenceIdentities"] assertSameSets(reference_identities, [ "example.org", "hypnotoad.example.org", "localhost", "other.local" ]) assertEquals(props["Hostname"], "example.org") def test_channel_accept_hostname(q, bus, conn, stream): chan, hostname, certificate_path = connect_and_get_tls_objects(q, bus, conn, False) # close the channel early chan.Close() # we expect the fallback verification process to accept the hostname (because # it matches the JID's hostname or because it's in extra-certificate-identities), # but the certificate verification will fail anyway as we don't trust the CA q.expect_many( EventPattern('dbus-signal', signal='Closed'), EventPattern('dbus-signal', signal='ChannelClosed'), EventPattern('dbus-signal', signal='StatusChanged', args=[cs.CONN_STATUS_DISCONNECTED, cs.CSR_CERT_UNTRUSTED]) ) def test_channel_reject_hostname(q, bus, conn, stream): chan, hostname, certificate_path = connect_and_get_tls_objects(q, bus, conn, False) # close the channel early chan.Close() # we expect the fallback verification process to not accept the hostname; # it doesn't match the JID's hostname nor the hostnames in the # extra-certificate-identities parameter q.expect_many( EventPattern('dbus-signal', signal='Closed'), EventPattern('dbus-signal', signal='ChannelClosed'), EventPattern('dbus-signal', signal='StatusChanged', args=[cs.CONN_STATUS_DISCONNECTED, cs.CSR_CERT_HOSTNAME_MISMATCH]) ) if __name__ == '__main__': exec_test(test_connect_success, { 'account' : JID }, authenticator=TlsAuthenticator(username='test', password='pass'), do_connect=False) exec_test(test_connect_fail, { 'account' : JID }, authenticator=TlsAuthenticator(username='test', password='pass'), do_connect=False) exec_test(test_connect_early_close_success, { 'account' : JID, 'ignore-ssl-errors' : False, 'require-encryption' : False }, authenticator=TlsAuthenticator(username='test', password='pass'), do_connect=False) exec_test(test_connect_early_close_fail, { 'account' : JID, 'ignore-ssl-errors' : False, 'require-encryption' : True }, authenticator=TlsAuthenticator(username='test', password='pass'), do_connect=False) exec_test(test_disconnect_inbetween, { 'account' : JID }, authenticator=TlsAuthenticator(username='test', password='pass'), do_connect=False) # Certificate verification reference identity checks exec_test(test_channel_reference_identity, { 'account' : JID }, authenticator=TlsAuthenticator(username='test', password='pass'), do_connect=False) exec_test(test_channel_reference_identity_with_extra, { 'account' : JID, 'extra-certificate-identities' : [ 'hypnotoad.example.org' ] }, authenticator=TlsAuthenticator(username='test', password='pass'), do_connect=False) exec_test(test_channel_reference_identity_with_extra_multiple, { 'account' : JID, 'extra-certificate-identities' : [ 'hypnotoad.example.org', 'other.local', '' ] }, authenticator=TlsAuthenticator(username='test', password='pass'), do_connect=False) # Hostname verification exec_test(test_channel_accept_hostname, { 'account' : 'test@' + CA_CERT_HOSTNAME, 'ignore-ssl-errors' : False, 'require-encryption' : True }, authenticator=TlsAuthenticator(username='test', password='pass'), do_connect=False) exec_test(test_channel_accept_hostname, { 'account' : 'test@' + CA_CERT_HOSTNAME, 'ignore-ssl-errors' : False, 'require-encryption' : True, 'extra-certificate-identities' : [ 'other.local', CA_CERT_HOSTNAME ] }, authenticator=TlsAuthenticator(username='test', password='pass'), do_connect=False) exec_test(test_channel_accept_hostname, { 'account' : JID, 'ignore-ssl-errors' : False, 'require-encryption' : True, 'extra-certificate-identities' : [ 'other.local', CA_CERT_HOSTNAME ] }, authenticator=TlsAuthenticator(username='test', password='pass'), do_connect=False) exec_test(test_channel_reject_hostname, { 'account' : 'test@alternative.' + CA_CERT_HOSTNAME, 'ignore-ssl-errors' : False, 'require-encryption' : True }, authenticator=TlsAuthenticator(username='test', password='pass'), do_connect=False) exec_test(test_channel_reject_hostname, { 'account' : JID, 'ignore-ssl-errors' : False, 'require-encryption' : True, 'extra-certificate-identities' : [ 'other.local', 'hypnotoad.example.org' ] }, authenticator=TlsAuthenticator(username='test', password='pass'), do_connect=False) exec_test(test_channel_reject_hostname, { 'account' : JID, 'ignore-ssl-errors' : False, 'require-encryption' : True }, authenticator=TlsAuthenticator(username='test', password='pass'), do_connect=False) exec_test(test_channel_reject_hostname, { 'account' : JID, 'ignore-ssl-errors' : False, 'require-encryption' : True, 'extra-certificate-identities' : [ 'alternative.' + CA_CERT_HOSTNAME ] }, authenticator=TlsAuthenticator(username='test', password='pass'), do_connect=False) telepathy-gabble-0.18.2/tests/twisted/tls/legacy-jabber.py0000644000175000017500000000221112200204333023466 0ustar00smcvsmcv00000000000000""" fd.o #65036: connecting to legacy Jabber servers should respect require-encryption """ from servicetest import assertEquals from gabbletest import exec_test, JabberXmlStream, JabberAuthenticator import constants as cs JID = 'alice@example.com' PASSWORD = 's3kr1t' def test_require_encryption(q, bus, conn, stream): conn.Connect() q.expect('dbus-signal', signal='StatusChanged', args=[cs.CONN_STATUS_CONNECTING, cs.CSR_REQUESTED]) # FIXME: arrange to get a better error new = q.expect('dbus-signal', signal='ConnectionError') assertEquals(cs.NETWORK_ERROR, new.args[0]) q.expect('dbus-signal', signal='StatusChanged', args=[cs.CONN_STATUS_DISCONNECTED, cs.CSR_NETWORK_ERROR]) if __name__ == '__main__': exec_test(test_require_encryption, { 'password': PASSWORD, 'account': JID, 'require-encryption': True, 'old-ssl': False, 'resource': 'legacy-require-encryption', }, protocol=JabberXmlStream, authenticator=JabberAuthenticator(JID.split('@')[0], PASSWORD), do_connect=False) telepathy-gabble-0.18.2/tests/twisted/text/0000755000175000017500000000000012312537051020625 5ustar00smcvsmcv00000000000000telepathy-gabble-0.18.2/tests/twisted/text/test-text.py0000644000175000017500000001540212227000321023130 0ustar00smcvsmcv00000000000000 """ Test text channel. """ import dbus from twisted.words.xish import domish from gabbletest import exec_test, elem from servicetest import ( EventPattern, wrap_channel, assertEquals, assertNotEquals, assertLength, ) import constants as cs def test(q, bus, conn, stream): id = '1845a1a9-f7bc-4a2e-a885-633aadc81e1b' # hello m = domish.Element((None, 'message')) m['from'] = 'foo@bar.com/Pidgin' m['id'] = id m['type'] = 'chat' m.addElement('body', content='hello') stream.send(m) event = q.expect('dbus-signal', signal='NewChannel') text_chan = wrap_channel( bus.get_object(conn.bus_name, event.args[0]), 'Text', ['Messages']) assert event.args[1] == cs.CHANNEL_TYPE_TEXT assert event.args[2] == cs.HT_CONTACT foo_at_bar_dot_com_handle = event.args[3] jid = conn.InspectHandles(1, [foo_at_bar_dot_com_handle])[0] assert jid == 'foo@bar.com' assert event.args[4] == False # suppress handler # Exercise basic Channel Properties from spec 0.17.7 channel_props = text_chan.Properties.GetAll(cs.CHANNEL) assert channel_props.get('TargetHandle') == event.args[3],\ (channel_props.get('TargetHandle'), event.args[3]) assert channel_props.get('TargetHandleType') == cs.HT_CONTACT,\ channel_props.get('TargetHandleType') assert channel_props.get('ChannelType') == \ cs.CHANNEL_TYPE_TEXT,\ channel_props.get('ChannelType') assert cs.CHANNEL_IFACE_CHAT_STATE in \ channel_props.get('Interfaces', ()), \ channel_props.get('Interfaces') assert cs.CHANNEL_IFACE_MESSAGES in \ channel_props.get('Interfaces', ()), \ channel_props.get('Interfaces') assert channel_props['TargetID'] == jid,\ (channel_props['TargetID'], jid) assert channel_props['Requested'] == False assert channel_props['InitiatorHandle'] == event.args[3],\ (channel_props['InitiatorHandle'], event.args[3]) assert channel_props['InitiatorID'] == jid,\ (channel_props['InitiatorID'], jid) received, message_received = q.expect_many( EventPattern('dbus-signal', signal='Received'), EventPattern('dbus-signal', signal='MessageReceived'), ) # Check that C.T.Text.Received looks right # message type: normal assert received.args[3] == 0 # flags: none assert received.args[4] == 0 # body assert received.args[5] == 'hello' # Check that C.I.Messages.MessageReceived looks right. message = message_received.args[0] # message should have two parts: the header and one content part assert len(message) == 2, message header, body = message assert header['message-sender'] == foo_at_bar_dot_com_handle, header # the spec says that message-type "MAY be omitted for normal chat # messages." assert 'message-type' not in header or header['message-type'] == 0, header # We don't make any uniqueness guarantees about the tokens on incoming # messages, so we use the id='' provided at the protocol level. assertEquals(id, header['message-token']) assert body['content-type'] == 'text/plain', body assert body['content'] == 'hello', body # Remove the message from the pending message queue, and check that # PendingMessagesRemoved fires. message_id = header['pending-message-id'] text_chan.Text.AcknowledgePendingMessages([message_id]) removed = q.expect('dbus-signal', signal='PendingMessagesRemoved') removed_ids = removed.args[0] assert len(removed_ids) == 1, removed_ids assert removed_ids[0] == message_id, (removed_ids, message_id) # Send a Notice using the Messages API greeting = [ dbus.Dictionary({ 'message-type': 2, # Notice }, signature='sv'), { 'content-type': 'text/plain', 'content': u"what up", } ] sent_token = text_chan.Messages.SendMessage(greeting, dbus.UInt32(0)) stream_message, sent, message_sent = q.expect_many( EventPattern('stream-message'), EventPattern('dbus-signal', signal='Sent'), EventPattern('dbus-signal', signal='MessageSent'), ) elt = stream_message.stanza assert elt.name == 'message' assert elt['type'] == 'normal' body = list(stream_message.stanza.elements())[0] assert body.name == 'body' assert body.children[0] == u'what up' sent_message = message_sent.args[0] assert len(sent_message) == 2, sent_message header = sent_message[0] assert header['message-type'] == 2, header # Notice assert header['message-token'] == sent_token, header assertEquals(conn.GetSelfHandle(), header['message-sender']) assertEquals('test@localhost', header['message-sender-id']) body = sent_message[1] assert body['content-type'] == 'text/plain', body assert body['content'] == u'what up', body assert message_sent.args[2] == sent_token assert sent.args[1] == 2, sent.args # Notice assert sent.args[2] == u'what up', sent.args # Send a message using Channel.Type.Text API text_chan.Text.Send(0, 'goodbye') stream_message, sent, message_sent = q.expect_many( EventPattern('stream-message'), EventPattern('dbus-signal', signal='Sent'), EventPattern('dbus-signal', signal='MessageSent'), ) elt = stream_message.stanza assert elt.name == 'message' assert elt['type'] == 'chat' body = list(stream_message.stanza.elements())[0] assert body.name == 'body' assert body.children[0] == u'goodbye' sent_message = message_sent.args[0] assert len(sent_message) == 2, sent_message header = sent_message[0] # the spec says that message-type "MAY be omitted for normal chat # messages." assert 'message-type' not in header or header['message-type'] == 0, header body = sent_message[1] assert body['content-type'] == 'text/plain', body assert body['content'] == u'goodbye', body assert sent.args[1] == 0, sent.args # message type normal assert sent.args[2] == u'goodbye', sent.args # And now let's try a message with a malformed type='' attribute. malformed = elem( 'message', from_='foo@bar.com/fubber', type="'")( elem('body')(u'Internettt!'), elem('subject')(u'xyzzy'), elem('thread')(u'6666'), ) stream.send(malformed) event = q.expect('dbus-signal', signal='MessageReceived') message, = event.args assertLength(2, message) header, body = message # Gabble should treat the unparseable type as if it were 'normal' or # omitted (not to be confused with Telepathy's Normal, which is 'chat' in # XMPP...) assertEquals(cs.MT_NOTICE, header['message-type']) if __name__ == '__main__': exec_test(test) telepathy-gabble-0.18.2/tests/twisted/text/test-text-no-body.py0000644000175000017500000000170412227000321024475 0ustar00smcvsmcv00000000000000 """ Test that s with a chat state notification but no body don't create a new text channel. """ from twisted.words.xish import domish from gabbletest import exec_test import constants as cs import ns def test(q, bus, conn, stream): # message without body m = domish.Element((None, 'message')) m['from'] = 'alice@foo.com' m['type'] = 'chat' m.addElement((ns.CHAT_STATES, 'composing')) stream.send(m) # message with body m = domish.Element((None, 'message')) m['from'] = 'bob@foo.com' m['type'] = 'chat' m.addElement((ns.CHAT_STATES, 'active')) m.addElement('body', content='hello') stream.send(m) # first message should be from Bob, not Alice event = q.expect('dbus-signal', signal='NewChannel') assert event.args[1] == cs.CHANNEL_TYPE_TEXT jid = conn.InspectHandles(cs.HT_CONTACT, [event.args[3]])[0] assert jid == 'bob@foo.com' if __name__ == '__main__': exec_test(test) telepathy-gabble-0.18.2/tests/twisted/text/test-text-delayed.py0000644000175000017500000000302712227000321024535 0ustar00smcvsmcv00000000000000 """ Test receiving delayed (offline) messages on a text channel. """ import datetime from twisted.words.xish import domish from gabbletest import exec_test from servicetest import EventPattern import constants as cs def test(q, bus, conn, stream): m = domish.Element((None, 'message')) m['from'] = 'foo@bar.com' m['type'] = 'chat' m.addElement('body', content='hello') # add timestamp information x = m.addElement(('jabber:x:delay', 'x')) x['stamp'] = '20070517T16:15:01' stream.send(m) event = q.expect('dbus-signal', signal='NewChannel') assert event.args[1] == cs.CHANNEL_TYPE_TEXT assert event.args[2] == cs.HT_CONTACT jid = conn.InspectHandles(cs.HT_CONTACT, [event.args[3]])[0] assert jid == 'foo@bar.com' received, message_received = q.expect_many( EventPattern('dbus-signal', signal='Received'), EventPattern('dbus-signal', signal='MessageReceived'), ) assert (str(datetime.datetime.utcfromtimestamp(received.args[1])) == '2007-05-17 16:15:01') assert received.args[5] == 'hello' message = message_received.args[0] header = message[0] message_sent_timestamp = header['message-sent'] assert str(datetime.datetime.utcfromtimestamp(message_sent_timestamp) == '2007-05-17 16:15:01'), header message_received_timestamp = header['message-received'] assert message_received_timestamp > message_sent_timestamp, header assert message[1]['content'] == 'hello', message if __name__ == '__main__': exec_test(test) telepathy-gabble-0.18.2/tests/twisted/text/test-chat-state.py0000644000175000017500000003337212227000321024207 0ustar00smcvsmcv00000000000000# coding=utf-8 """ Test that chat state notifications are correctly sent and received on text channels. """ from twisted.words.xish import domish from servicetest import (assertEquals, assertNotEquals, assertLength, wrap_channel, EventPattern, call_async, sync_dbus) from gabbletest import exec_test, make_result_iq, sync_stream, make_presence import constants as cs import ns def check_state_notification(elem, name, allow_body=False): assertEquals('message', elem.name) assertEquals('chat', elem['type']) children = list(elem.elements()) notification = [x for x in children if x.uri == ns.CHAT_STATES][0] assert notification.name == name, notification.toXml() if not allow_body: assert len(children) == 1, elem.toXml() def make_message(jid, body=None, state=None): m = domish.Element((None, 'message')) m['from'] = jid m['type'] = 'chat' if state is not None: m.addElement((ns.CHAT_STATES, state)) if body is not None: m.addElement('body', content=body) return m def test(q, bus, conn, stream): self_handle = conn.GetSelfHandle() jid = 'foo@bar.com' full_jid = 'foo@bar.com/Foo' foo_handle = conn.RequestHandles(cs.HT_CONTACT, [jid])[0] path = conn.Requests.CreateChannel( { cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_TEXT, cs.TARGET_HANDLE_TYPE: cs.HT_CONTACT, cs.TARGET_HANDLE: foo_handle, })[0] chan = wrap_channel(bus.get_object(conn.bus_name, path), 'Text', ['ChatState', 'Destroyable']) presence = make_presence(full_jid, status='hello', caps={ 'node': 'http://telepathy.freedesktop.org/homeopathy', 'ver' : '0.1', }) stream.send(presence) version_event = q.expect('stream-iq', to=full_jid, query_ns=ns.DISCO_INFO, query_node='http://telepathy.freedesktop.org/homeopathy#0.1') result = make_result_iq(stream, version_event.stanza) query = result.firstChildElement() feature = query.addElement('feature') feature['var'] = ns.CHAT_STATES stream.send(result) sync_stream(q, stream) states = chan.Properties.Get(cs.CHANNEL_IFACE_CHAT_STATE, 'ChatStates') assertEquals(cs.CHAT_STATE_INACTIVE, states.get(self_handle, cs.CHAT_STATE_INACTIVE)) assertEquals(cs.CHAT_STATE_INACTIVE, states.get(foo_handle, cs.CHAT_STATE_INACTIVE)) # Receiving chat states: # Composing... stream.send(make_message(full_jid, state='composing')) changed = q.expect('dbus-signal', signal='ChatStateChanged', path=chan.object_path) handle, state = changed.args assertEquals(foo_handle, handle) assertEquals(cs.CHAT_STATE_COMPOSING, state) states = chan.Properties.Get(cs.CHANNEL_IFACE_CHAT_STATE, 'ChatStates') assertEquals(cs.CHAT_STATE_INACTIVE, states.get(self_handle, cs.CHAT_STATE_INACTIVE)) assertEquals(cs.CHAT_STATE_COMPOSING, states.get(foo_handle, cs.CHAT_STATE_INACTIVE)) # Message! stream.send(make_message(full_jid, body='hello', state='active')) changed = q.expect('dbus-signal', signal='ChatStateChanged', path=chan.object_path) handle, state = changed.args assertEquals(foo_handle, handle) assertEquals(cs.CHAT_STATE_ACTIVE, state) states = chan.Properties.Get(cs.CHANNEL_IFACE_CHAT_STATE, 'ChatStates') assertEquals(cs.CHAT_STATE_INACTIVE, states.get(self_handle, cs.CHAT_STATE_INACTIVE)) assertEquals(cs.CHAT_STATE_ACTIVE, states.get(foo_handle, cs.CHAT_STATE_INACTIVE)) # Assert that a redundant chat-state change doesn't emit a signal forbidden = [EventPattern('dbus-signal', signal='ChatStateChanged', args=[foo_handle, cs.CHAT_STATE_ACTIVE])] q.forbid_events(forbidden) m = domish.Element((None, 'message')) m['from'] = 'foo@bar.com/Foo' m['type'] = 'chat' m.addElement((ns.CHAT_STATES, 'active')) m.addElement('body', content='hello') stream.send(m) sync_dbus(bus, q, conn) sync_stream(q, stream) q.unforbid_events(forbidden) # Sending chat states: # Composing... chan.ChatState.SetChatState(cs.CHAT_STATE_COMPOSING) stream_message = q.expect('stream-message') check_state_notification(stream_message.stanza, 'composing') states = chan.Properties.Get(cs.CHANNEL_IFACE_CHAT_STATE, 'ChatStates') assertEquals(cs.CHAT_STATE_COMPOSING, states.get(self_handle, cs.CHAT_STATE_INACTIVE)) assertEquals(cs.CHAT_STATE_ACTIVE, states.get(foo_handle, cs.CHAT_STATE_INACTIVE)) # XEP 0085: # every content message SHOULD contain an notification. chan.Text.Send(0, 'hi.') stream_message = q.expect('stream-message') elem = stream_message.stanza assertEquals('chat', elem['type']) check_state_notification(elem, 'active', allow_body=True) states = chan.Properties.Get(cs.CHANNEL_IFACE_CHAT_STATE, 'ChatStates') assertEquals(cs.CHAT_STATE_ACTIVE, states.get(self_handle, cs.CHAT_STATE_INACTIVE)) assertEquals(cs.CHAT_STATE_ACTIVE, states.get(foo_handle, cs.CHAT_STATE_INACTIVE)) def is_body(e): if e.name == 'body': assert e.children[0] == u'hi.', e.toXml() return True return False assert len([x for x in elem.elements() if is_body(x)]) == 1, elem.toXml() # Close the channel without acking the received message. The peer should # get a notification, and the channel should respawn. chan.Close() gone, _, _ = q.expect_many( EventPattern('stream-message'), EventPattern('dbus-signal', signal='Closed'), EventPattern('dbus-signal', signal='NewChannel'), ) check_state_notification(gone.stanza, 'gone') # Reusing the proxy object because we happen to know it'll be at the same # path... # Destroy the channel. The peer shouldn't get a notification, since # we already said we were gone and haven't sent them any messages to the # contrary. es = [EventPattern('stream-message')] q.forbid_events(es) chan.Destroyable.Destroy() sync_stream(q, stream) # Make the channel anew. path = conn.Requests.CreateChannel( { cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_TEXT, cs.TARGET_HANDLE_TYPE: cs.HT_CONTACT, cs.TARGET_HANDLE: foo_handle, })[0] chan = wrap_channel(bus.get_object(conn.bus_name, path), 'Text', ['ChatState', 'Destroyable']) # Close it immediately; the peer should again not get a # notification, since we haven't sent any notifications on that channel. chan.Close() sync_stream(q, stream) q.unforbid_events(es) # XEP-0085 §5.1 defines how to negotiate support for chat states with a # contact in the absence of capabilities. This is useful when talking to # invisible contacts, for example. # First, if we receive a message from a contact, containing an # notification, they support chat states, so we should send them. jid = 'i@example.com' full_jid = jid + '/GTalk' path = conn.Requests.CreateChannel( { cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_TEXT, cs.TARGET_HANDLE_TYPE: cs.HT_CONTACT, cs.TARGET_ID: jid, })[0] chan = wrap_channel(bus.get_object(conn.bus_name, path), 'Text', ['ChatState']) stream.send(make_message(full_jid, body='i am invisible', state='active')) changed = q.expect('dbus-signal', signal='ChatStateChanged', path=chan.object_path) assertEquals(cs.CHAT_STATE_ACTIVE, changed.args[1]) # We've seen them send a chat state notification, so we should send them # notifications when the UI tells us to. chan.ChatState.SetChatState(cs.CHAT_STATE_COMPOSING) stream_message = q.expect('stream-message', to=full_jid) check_state_notification(stream_message.stanza, 'composing') changed = q.expect('dbus-signal', signal='ChatStateChanged', path=chan.object_path) handle, state = changed.args assertEquals(cs.CHAT_STATE_COMPOSING, state) assertEquals(self_handle, handle) chan.Text.Send(0, 'very convincing') stream_message = q.expect('stream-message', to=full_jid) check_state_notification(stream_message.stanza, 'active', allow_body=True) # Now, test the case where we start the negotiation, and the contact # turns out to support chat state notifications. jid = 'c@example.com' full_jid = jid + '/GTalk' path = conn.Requests.CreateChannel( { cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_TEXT, cs.TARGET_HANDLE_TYPE: cs.HT_CONTACT, cs.TARGET_ID: jid, })[0] chan = wrap_channel(bus.get_object(conn.bus_name, path), 'Text', ['ChatState']) # We shouldn't send any notifications until we actually send a message. # But ChatStateChanged is still emitted locally e = EventPattern('stream-message', to=jid) q.forbid_events([e]) for i in [cs.CHAT_STATE_ACTIVE, cs.CHAT_STATE_COMPOSING, cs.CHAT_STATE_PAUSED, cs.CHAT_STATE_INACTIVE ]: chan.ChatState.SetChatState(i) changed = q.expect('dbus-signal', signal='ChatStateChanged', path=chan.object_path) handle, state = changed.args assertEquals(i, state) assertEquals(self_handle, handle) sync_stream(q, stream) q.unforbid_events([e]) # When we send a message, say we're active. chan.Text.Send(0, 'is anyone there?') stream_message = q.expect('stream-message', to=jid) check_state_notification(stream_message.stanza, 'active', allow_body=True) # The D-Bus property changes, too changed = q.expect('dbus-signal', signal='ChatStateChanged', path=chan.object_path) handle, state = changed.args assertEquals(cs.CHAT_STATE_ACTIVE, state) assertEquals(self_handle, handle) # We get a notification back from our contact. stream.send(make_message(full_jid, state='composing')) # Wait until gabble tells us the chat-state of the remote party has # changed so we know gabble knows chat state notification are supported changed = q.expect('dbus-signal', signal='ChatStateChanged', path=chan.object_path) handle, state = changed.args assertEquals(cs.CHAT_STATE_COMPOSING, state) assertNotEquals(foo_handle, handle) # So now we know they support notification, so should send notifications. chan.ChatState.SetChatState(cs.CHAT_STATE_COMPOSING) # This doesn't check whether we're sending to the bare jid, or the # jid+resource. In fact, the notification is sent to the bare jid, because # we only update which jid we send to when we actually receive a message, # not when we receive a notification. wjt thinks this is less surprising # than the alternative: # # • I'm talking to you on my N900, and signed in on my laptop; # • I enter one character in a tab to you on my laptop, and then delete # it; # • Now your messages to me appear on my laptop (until I send you another # one from my N900)! stream_message = q.expect('stream-message') check_state_notification(stream_message.stanza, 'composing') # The D-Bus property changes, too changed = q.expect('dbus-signal', signal='ChatStateChanged', path=chan.object_path) handle, state = changed.args assertEquals(cs.CHAT_STATE_COMPOSING, state) assertEquals(self_handle, handle) # But! Now they start messaging us from a different client, which *doesn't* # support notifications. other_jid = jid + '/Library' stream.send(make_message(other_jid, body='grr, library computers')) q.expect('dbus-signal', signal='Received') # Okay, we should stop sending typing notifications. e = EventPattern('stream-message', to=other_jid) q.forbid_events([e]) for i in [cs.CHAT_STATE_COMPOSING, cs.CHAT_STATE_INACTIVE, cs.CHAT_STATE_PAUSED, cs.CHAT_STATE_ACTIVE]: chan.ChatState.SetChatState(i) sync_stream(q, stream) q.unforbid_events([e]) # Now, test the case where we start the negotiation, and the contact # does not support chat state notifications jid = 'twitterbot@example.com' full_jid = jid + '/Nonsense' path = conn.Requests.CreateChannel( { cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_TEXT, cs.TARGET_HANDLE_TYPE: cs.HT_CONTACT, cs.TARGET_ID: jid, })[0] chan = wrap_channel(bus.get_object(conn.bus_name, path), 'Text', ['ChatState']) # We shouldn't send any notifications until we actually send a message. e = EventPattern('stream-message', to=jid) q.forbid_events([e]) for i in [cs.CHAT_STATE_COMPOSING, cs.CHAT_STATE_INACTIVE, cs.CHAT_STATE_PAUSED, cs.CHAT_STATE_ACTIVE]: chan.ChatState.SetChatState(i) sync_stream(q, stream) q.unforbid_events([e]) # When we send a message, say we're active. chan.Text.Send(0, '#n900 #maemo #zomg #woo #yay http://bit.ly/n900') stream_message = q.expect('stream-message', to=jid) check_state_notification(stream_message.stanza, 'active', allow_body=True) # They reply without a chat state. stream.send(make_message(full_jid, body="posted.")) q.expect('dbus-signal', signal='Received') # Okay, we shouldn't send any more. e = EventPattern('stream-message', to=other_jid) q.forbid_events([e]) for i in [cs.CHAT_STATE_COMPOSING, cs.CHAT_STATE_INACTIVE, cs.CHAT_STATE_PAUSED, cs.CHAT_STATE_ACTIVE]: chan.ChatState.SetChatState(i) sync_stream(q, stream) q.unforbid_events([e]) chan.Text.Send(0, '@stephenfry simmer down') message = q.expect('stream-message') states = [x for x in message.stanza.elements() if x.uri == ns.CHAT_STATES] assertLength(0, states) if __name__ == '__main__': exec_test(test) telepathy-gabble-0.18.2/tests/twisted/text/send-to-correct-resource.py0000644000175000017500000000437212200204333026030 0ustar00smcvsmcv00000000000000""" Regression test for https://bugs.freedesktop.org/show_bug.cgi?id=22369. """ from twisted.words.xish import domish from servicetest import wrap_channel from gabbletest import exec_test import constants as cs import ns def test(q, bus, conn, stream): # I need a random name generator # Macro-Variable Spin Gel contact = 'macro-variable.spin.gel@example.com' contact_a = '%s/n810' % contact contact_b = '%s/laptop' % contact path, _ = conn.Requests.CreateChannel({ cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_TEXT, cs.TARGET_HANDLE_TYPE: cs.HT_CONTACT, cs.TARGET_ID: contact, }) chan = wrap_channel(bus.get_object(conn.bus_name, path), 'Text') # When we start a conversation, Gabble should send to the bare JID. chan.Text.Send(0, 'hey, you around?') q.expect('stream-message', to=contact) # A particular resource replies. m = domish.Element((None, 'message')) m['from'] = contact_a m['type'] = 'chat' m.addElement('body', content="i'm on a beach at Gran Canaria!") stream.send(m) q.expect('dbus-signal', signal='Received') # Now that we got a reply from a particular resource, Gabble should reply # there. chan.Text.Send(0, 'nice') q.expect('stream-message', to=contact_a) # Now another resource messages us m = domish.Element((None, 'message')) m['from'] = contact_b m['type'] = 'chat' m.addElement('body', content="I brought my laptop to the Empathy hackfest") stream.send(m) q.expect('dbus-signal', signal='Received') # Gabble should have updated the resource it's sending to. chan.Text.Send(0, "don't get sand in the keyboard") e = q.expect('stream-message', to=contact_b) # But actually that resource has gone offline: m = e.stanza m['from'] = contact_b m['type'] = 'error' del m['to'] err = m.addElement((None, 'error')) err['type'] = 'cancel' err.addElement((ns.STANZA, 'item-not-found')) stream.send(m) q.expect('dbus-signal', signal='SendError') # So as a result, Gabble should send the next message to the bare JID. chan.Text.Send(0, "... i guess my warning was too late") q.expect('stream-message', to=contact) if __name__ == '__main__': exec_test(test) telepathy-gabble-0.18.2/tests/twisted/text/send-error.py0000644000175000017500000001533612227000321023255 0ustar00smcvsmcv00000000000000""" Test that an incoming for a contact gives both a SendError and a delivery report on a 1-1 text channel to that contact. """ from twisted.words.xish import domish from gabbletest import exec_test from servicetest import call_async, EventPattern import constants as cs import ns def test_temporary_error(q, bus, conn, stream): self_handle = conn.GetSelfHandle() jid = 'foo@bar.com' call_async(q, conn, 'RequestHandles', 1, [jid]) event = q.expect('dbus-return', method='RequestHandles') foo_handle = event.value[0][0] path = conn.Requests.CreateChannel( { cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_TEXT, cs.TARGET_HANDLE_TYPE: cs.HT_CONTACT, cs.TARGET_HANDLE: foo_handle, })[0] text_chan = bus.get_object(conn.bus_name, path) # # what is up, my good sir? # # # # message_body = 'what is up, my good sir?' m = domish.Element((None, 'message')) m['from'] = 'foo@bar.com' m['id'] = '1845a1a9-f7bc-4a2e-a885-633aadc81e1b' m['type'] = 'error' m.addElement('body', content=message_body) e = domish.Element((None, 'error')) e['type'] = 'wait' e.addElement((ns.STANZA, 'resource-constraint')) m.addChild(e) stream.send(m) send_error, received, message_received = q.expect_many( EventPattern('dbus-signal', signal='SendError'), EventPattern('dbus-signal', signal='Received'), EventPattern('dbus-signal', signal='MessageReceived'), ) expected_send_error = 4 # Too_Long assert send_error.args[0] == expected_send_error, send_error.args # FIXME: It doesn't look like it's possible to know what the original # message type is, given that the type attribute of is 'error' # for error reports. #assert send_error.args[2] == 0, send_error.args assert send_error.args[3] == message_body, send_error.args assert received.args[2] == foo_handle, (received.args, foo_handle) assert received.args[3] == 4, received.args # Channel_Text_Message_Type_Delivery_Report assert received.args[4] == 2, received.args # Channel_Text_Message_Flag_Non_Text_Content assert received.args[5] == '', received.args delivery_report = message_received.args[0] assert len(delivery_report) == 1, delivery_report header = delivery_report[0] assert header['message-sender'] == foo_handle, header assert header['message-type'] == 4, header # Channel_Text_Message_Type_Delivery_Report assert header['delivery-status'] == 2, header # Delivery_Status_Temporarily_Failed assert header['delivery-token'] == '1845a1a9-f7bc-4a2e-a885-633aadc81e1b',\ header assert header['delivery-error'] == expected_send_error, header delivery_echo = header['delivery-echo'] assert len(delivery_echo) == 2, delivery_echo assert delivery_echo[0]['message-sender'] == self_handle, delivery_echo assert delivery_echo[0]['message-token'] == \ '1845a1a9-f7bc-4a2e-a885-633aadc81e1b', delivery_echo # FIXME: see above #assert delivery_echo[0]['message-type'] == 0, delivery_echo assert delivery_echo[1]['content-type'] == "text/plain", delivery_echo assert delivery_echo[1]['content'] == message_body, delivery_echo def test_permanent_error(q, bus, conn, stream): self_handle = conn.GetSelfHandle() jid = 'wee@ninja.jp' call_async(q, conn, 'RequestHandles', 1, [jid]) event = q.expect('dbus-return', method='RequestHandles') ninja_handle = event.value[0][0] path = conn.Requests.CreateChannel( { cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_TEXT, cs.TARGET_HANDLE_TYPE: cs.HT_CONTACT, cs.TARGET_HANDLE: ninja_handle, })[0] text_chan = bus.get_object(conn.bus_name, path) # # hello? is there anyone there? # # # # message_body = 'hello? is there anyone there?' m = domish.Element((None, 'message')) m['from'] = 'wee@ninja.jp' m['type'] = 'error' m.addElement('body', content=message_body) e = domish.Element((None, 'error')) e['type'] = 'cancel' e.addElement((ns.STANZA, 'item-not-found')) m.addChild(e) stream.send(m) send_error, received, message_received = q.expect_many( EventPattern('dbus-signal', signal='SendError'), EventPattern('dbus-signal', signal='Received'), EventPattern('dbus-signal', signal='MessageReceived'), ) expected_send_error = 2 # Invalid_Contact assert send_error.args[0] == expected_send_error, send_error.args # FIXME: It doesn't look like it's possible to know what the original # message type is, given that the type attribute of is 'error' # for error reports. #assert send_error.args[2] == 0, send_error.args assert send_error.args[3] == message_body, send_error.args assert received.args[2] == ninja_handle, (received.args, ninja_handle) assert received.args[3] == 4, received.args # Channel_Text_Message_Type_Delivery_Report assert received.args[4] == 2, received.args # Channel_Text_Message_Flag_Non_Text_Content assert received.args[5] == '', received.args delivery_report = message_received.args[0] assert len(delivery_report) == 1, delivery_report header = delivery_report[0] assert header['message-sender'] == ninja_handle, header assert header['message-type'] == 4, header # Channel_Text_Message_Type_Delivery_Report assert header['delivery-status'] == 3, header # Delivery_Status_Permanently_Failed # the error has no ID, therefore its Telepathy rendition has no # delivery-token assert 'delivery-token' not in header, header assert header['delivery-error'] == expected_send_error, header delivery_echo = header['delivery-echo'] assert len(delivery_echo) == 2, delivery_echo assert delivery_echo[0]['message-sender'] == self_handle, delivery_echo # the error has no ID, therefore the echo's Telepathy rendition has no # message-token assert 'message-token' not in delivery_echo[0], delivery_echo # FIXME: see above #assert delivery_echo[0]['message-type'] == 0, delivery_echo assert delivery_echo[1]['content-type'] == "text/plain", delivery_echo assert delivery_echo[1]['content'] == message_body, delivery_echo def test(q, bus, conn, stream): test_temporary_error(q, bus, conn, stream) test_permanent_error(q, bus, conn, stream) if __name__ == '__main__': exec_test(test) telepathy-gabble-0.18.2/tests/twisted/text/respawn.py0000644000175000017500000001305112227000321022644 0ustar00smcvsmcv00000000000000""" Test text channel being recreated because there are still pending messages. """ import dbus from twisted.words.xish import domish from gabbletest import exec_test from servicetest import call_async, EventPattern, assertEquals import constants as cs def test(q, bus, conn, stream): self_handle = conn.GetSelfHandle() jid = 'foo@bar.com' foo_handle = conn.RequestHandles(cs.HT_CONTACT, [jid])[0] call_async(q, conn, 'RequestChannel', cs.CHANNEL_TYPE_TEXT, cs.HT_CONTACT, foo_handle, True) ret, old_sig, new_sig = q.expect_many( EventPattern('dbus-return', method='RequestChannel'), EventPattern('dbus-signal', signal='NewChannel'), EventPattern('dbus-signal', signal='NewChannels'), ) text_chan = bus.get_object(conn.bus_name, ret.value[0]) chan_iface = dbus.Interface(text_chan, cs.CHANNEL) text_iface = dbus.Interface(text_chan, cs.CHANNEL_TYPE_TEXT) assert old_sig.args[0] == ret.value[0] assert old_sig.args[1] == cs.CHANNEL_TYPE_TEXT assert old_sig.args[2] == cs.HT_CONTACT assert old_sig.args[3] == foo_handle assert old_sig.args[4] == True # suppress handler assert len(new_sig.args) == 1 assert len(new_sig.args[0]) == 1 # one channel assert len(new_sig.args[0][0]) == 2 # two struct members assert new_sig.args[0][0][0] == ret.value[0] emitted_props = new_sig.args[0][0][1] assert emitted_props[cs.CHANNEL_TYPE] == cs.CHANNEL_TYPE_TEXT assert emitted_props[cs.TARGET_HANDLE_TYPE] == cs.HT_CONTACT assert emitted_props[cs.TARGET_HANDLE] == foo_handle assert emitted_props[cs.TARGET_ID] == jid assert emitted_props[cs.REQUESTED] == True assert emitted_props[cs.INITIATOR_HANDLE] == self_handle assert emitted_props[cs.INITIATOR_ID] == 'test@localhost' channel_props = text_chan.GetAll( cs.CHANNEL, dbus_interface=dbus.PROPERTIES_IFACE) assert channel_props['TargetID'] == jid,\ (channel_props['TargetID'], jid) assert channel_props['Requested'] == True assert channel_props['InitiatorHandle'] == self_handle,\ (channel_props['InitiatorHandle'], self_handle) assert channel_props['InitiatorID'] == 'test@localhost',\ channel_props['InitiatorID'] text_iface.Send(0, 'hey') event = q.expect('stream-message') elem = event.stanza assert elem.name == 'message' assert elem['type'] == 'chat' body = list(event.stanza.elements())[0] assert body.name == 'body' assert body.children[0] == u'hey' # hello m = domish.Element((None, 'message')) m['from'] = 'foo@bar.com/Pidgin' m['type'] = 'chat' m.addElement('body', content='hello') stream.send(m) event = q.expect('dbus-signal', signal='Received') hello_message_id = event.args[0] hello_message_time = event.args[1] assert event.args[2] == foo_handle # message type: normal assert event.args[3] == 0 # flags: none assert event.args[4] == 0 # body assert event.args[5] == 'hello' messages = text_chan.ListPendingMessages(False, dbus_interface=cs.CHANNEL_TYPE_TEXT) assert messages == \ [(hello_message_id, hello_message_time, foo_handle, 0, 0, 'hello')], messages # close the channel without acking the message; it comes back call_async(q, chan_iface, 'Close') old, new = q.expect_many( EventPattern('dbus-signal', signal='Closed'), EventPattern('dbus-signal', signal='ChannelClosed'), ) assert old.path == text_chan.object_path,\ (old.path, text_chan.object_path) assert new.args[0] == text_chan.object_path,\ (new.args[0], text_chan.object_path) event = q.expect('dbus-signal', signal='NewChannel') assert event.args[0] == text_chan.object_path assert event.args[1] == cs.CHANNEL_TYPE_TEXT assert event.args[2] == cs.HT_CONTACT assert event.args[3] == foo_handle assert event.args[4] == False # suppress handler event = q.expect('dbus-return', method='Close') # it now behaves as if the message had initiated it channel_props = text_chan.GetAll( cs.CHANNEL, dbus_interface=dbus.PROPERTIES_IFACE) assert channel_props['TargetID'] == jid,\ (channel_props['TargetID'], jid) assert channel_props['Requested'] == False assert channel_props['InitiatorHandle'] == foo_handle,\ (channel_props['InitiatorHandle'], foo_handle) assert channel_props['InitiatorID'] == 'foo@bar.com',\ channel_props['InitiatorID'] # the message is still there messages = text_chan.ListPendingMessages(False, dbus_interface=cs.CHANNEL_TYPE_TEXT) assertEquals( [(hello_message_id, hello_message_time, foo_handle, 0, 8, 'hello')], messages) # acknowledge it text_chan.AcknowledgePendingMessages([hello_message_id], dbus_interface=cs.CHANNEL_TYPE_TEXT) messages = text_chan.ListPendingMessages(False, dbus_interface=cs.CHANNEL_TYPE_TEXT) assert messages == [] # close the channel again call_async(q, chan_iface, 'Close') event = q.expect('dbus-signal', signal='Closed') assert event.path == text_chan.object_path,\ (event.path, text_chan.object_path) event = q.expect('dbus-return', method='Close') # assert that it stays dead this time! try: chan_iface.GetChannelType() except dbus.DBusException: pass else: raise AssertionError("Why won't it die?") if __name__ == '__main__': exec_test(test) telepathy-gabble-0.18.2/tests/twisted/text/receipts.py0000644000175000017500000001506412200204333023011 0ustar00smcvsmcv00000000000000# coding=utf-8 """ Test XEP-0184 receipts. """ from servicetest import ( EventPattern, assertEquals, assertLength, sync_dbus, wrap_channel, ) from gabbletest import exec_test, elem, sync_stream, acknowledge_iq import constants as cs import ns import caps_helper import rostertest from presence.invisible_helper import Xep0186Stream GUYBRUSH = 'guybrush@mi.lit' GUYBRUSH_FULL_JID = GUYBRUSH + '/Sea Cucumber' def send_received_report(stream, jid, received_id): stream.send( elem('message', from_=jid)( elem(ns.RECEIPTS, 'received', id=received_id) )) def report_received_on_open_channel(q, bus, conn, stream, chan): received_id = 'fine-leather-jackets' send_received_report(stream, GUYBRUSH, received_id) e = q.expect('dbus-signal', signal='MessageReceived', path=chan.object_path) message, = e.args header, = message assertEquals(cs.MT_DELIVERY_REPORT, header['message-type']) assertEquals(cs.DELIVERY_STATUS_DELIVERED, header['delivery-status']) assertEquals(received_id, header['delivery-token']) def report_ignored_without_channel(q, bus, conn, stream): q.forbid_events([EventPattern('dbus-signal', signal='MessageReceived')]) send_received_report(stream, 'marley@mi.gov', 'only-one-candidate') sync_dbus(bus, q, conn) q.unforbid_all() def not_sending_request_to_contact(q, bus, conn, stream, chan): message = [ { 'message-type': cs.MT_NORMAL, }, { 'content-type': 'text/plain', 'content': 'Mancomb Seepgood?', }] chan.Messages.SendMessage(message, 0) e = q.expect('stream-message', to=GUYBRUSH) assertLength(0, list(e.stanza.elements(uri=ns.RECEIPTS, name='request'))) def sending_request_to_presenceless_contact(q, bus, conn, stream, chan): """ Initially we know nothing of Guybrush's presence, so should just try our level best if asked to. """ message = [ { 'message-type': cs.MT_NORMAL, }, { 'content-type': 'text/plain', 'content': 'Thriftweed?', }] chan.Messages.SendMessage(message, cs.MSG_SENDING_FLAGS_REPORT_DELIVERY) e = q.expect('stream-message', to=GUYBRUSH) assertLength(1, list(e.stanza.elements(uri=ns.RECEIPTS, name='request'))) def sending_request_to_cappy_contact(q, bus, conn, stream, chan): """ Test that Gabble requests a receipt from a contact whom we know supports this extension, but only if asked. """ # Convince Gabble that Guybrush supports this extension caps = { 'node': 'http://whatever', 'ver': caps_helper.compute_caps_hash([], [ns.RECEIPTS], {}), 'hash': 'sha-1', } caps_helper.presence_and_disco(q, conn, stream, GUYBRUSH_FULL_JID, disco=True, client=caps['node'], caps=caps, features=[ns.RECEIPTS]) sync_stream(q, stream) # Don't ask, don't tell — even if we know Guybrush does support this. not_sending_request_to_contact(q, bus, conn, stream, chan) # Ask, tell. message = [ { 'message-type': cs.MT_NORMAL, }, { 'content-type': 'text/plain', 'content': 'Ulysses?', }] chan.Messages.SendMessage(message, cs.MSG_SENDING_FLAGS_REPORT_DELIVERY) e = q.expect('stream-message', to=GUYBRUSH) assertLength(1, list(e.stanza.elements(uri=ns.RECEIPTS, name='request'))) def replying_to_requests(q, bus, conn, stream): jid = 'lechuck@lucasarts.lit' # We shouldn't send receipts to people who aren't on our roster. q.forbid_events([EventPattern('stream-message', to=jid)]) stream.send( elem('message', from_=jid, type='chat', id='alpha')( elem('body')( u"You didn't kill me, you moron!" ), elem(ns.RECEIPTS, 'request') )) q.expect('dbus-signal', signal='MessageReceived') sync_stream(q, stream) q.unforbid_all() # We should send receipts to people on our roster, seeing as we're not # invisible. rostertest.send_roster_push(stream, jid, subscription='from') stream.send( elem('message', from_=jid, type='chat', id='beta')( elem('body')( u"You've just destroyed my spiritual essences." ), elem(ns.RECEIPTS, 'request') )) q.expect('dbus-signal', signal='MessageReceived') e = q.expect('stream-message', to=jid) receipt = e.stanza.elements(uri=ns.RECEIPTS, name='received').next() assertEquals('beta', receipt['id']) # We would like requests in messages without id=''s not to crash Gabble, # and also for it not to send a reply. q.forbid_events([EventPattern('stream-message', to=jid)]) stream.send( elem('message', from_=jid, type='chat')( # NB. no id='' attribute elem('body')( u"A favor that I shall now return!" ), elem(ns.RECEIPTS, 'request') )) q.expect('dbus-signal', signal='MessageReceived') sync_stream(q, stream) q.unforbid_all() # If we're invisible, LeChuck shouldn't get receipts. conn.SimplePresence.SetPresence("hidden", "") event = q.expect('stream-iq', query_name='invisible') acknowledge_iq(stream, event.stanza) q.forbid_events([EventPattern('stream-message', to=jid)]) stream.send( elem('message', from_=jid, type='chat', id='epsilon')( elem('body')( u"… but where am I going to find a duck wearing burlap chaps?" ), elem(ns.RECEIPTS, 'request') )) q.expect('dbus-signal', signal='MessageReceived') sync_stream(q, stream) q.unforbid_all() def test(q, bus, conn, stream): path = conn.Requests.CreateChannel( { cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_TEXT, cs.TARGET_HANDLE_TYPE: cs.HT_CONTACT, cs.TARGET_ID: GUYBRUSH, })[0] chan = wrap_channel(bus.get_object(conn.bus_name, path), 'Text', ['Messages']) # Let's start out with an empty roster, eh? e = q.expect('stream-iq', iq_type='get', query_ns=ns.ROSTER) e.stanza['type'] = 'result' stream.send(e.stanza) report_received_on_open_channel(q, bus, conn, stream, chan) report_ignored_without_channel(q, bus, conn, stream) not_sending_request_to_contact(q, bus, conn, stream, chan) # FIXME: This test is disabled because of stupidity in the presence cache. # See the comment in receipts_conceivably_supported(). #sending_request_to_presenceless_contact(q, bus, conn, stream, chan) sending_request_to_cappy_contact(q, bus, conn, stream, chan) replying_to_requests(q, bus, conn, stream) if __name__ == '__main__': exec_test(test, protocol=Xep0186Stream) telepathy-gabble-0.18.2/tests/twisted/text/initiate-requestotron.py0000644000175000017500000000534312227000321025550 0ustar00smcvsmcv00000000000000""" Test text channel initiated by me, using Requests. """ import dbus from gabbletest import exec_test from servicetest import (call_async, EventPattern, assertContains, assertEquals) import constants as cs def test(q, bus, conn, stream): self_handle = conn.GetSelfHandle() jid = 'foo@bar.com' foo_handle = conn.RequestHandles(cs.HT_CONTACT, [jid])[0] properties = conn.GetAll( cs.CONN_IFACE_REQUESTS, dbus_interface=dbus.PROPERTIES_IFACE) assert properties.get('Channels') == [], properties['Channels'] assert ({cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_TEXT, cs.TARGET_HANDLE_TYPE: cs.HT_CONTACT, }, [cs.TARGET_HANDLE, cs.TARGET_ID], ) in properties.get('RequestableChannelClasses'),\ properties['RequestableChannelClasses'] call_async(q, conn.Requests, 'CreateChannel', { cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_TEXT, cs.TARGET_HANDLE_TYPE: cs.HT_CONTACT, cs.TARGET_HANDLE: foo_handle, }) ret, old_sig, new_sig = q.expect_many( EventPattern('dbus-return', method='CreateChannel'), EventPattern('dbus-signal', signal='NewChannel'), EventPattern('dbus-signal', signal='NewChannels'), ) assert len(ret.value) == 2 emitted_props = ret.value[1] assert emitted_props[cs.CHANNEL_TYPE] == cs.CHANNEL_TYPE_TEXT assert emitted_props[cs.TARGET_HANDLE_TYPE] == cs.HT_CONTACT assert emitted_props[cs.TARGET_HANDLE] == foo_handle assert emitted_props[cs.TARGET_ID] == jid assert emitted_props[cs.REQUESTED] == True assert emitted_props[cs.INITIATOR_HANDLE] == self_handle assert emitted_props[cs.INITIATOR_ID] == 'test@localhost' assertContains('text/plain', emitted_props[cs.SUPPORTED_CONTENT_TYPES]) assertEquals(0, emitted_props[cs.MESSAGE_PART_SUPPORT_FLAGS]) assertEquals(cs.DELIVERY_REPORTING_SUPPORT_FLAGS_RECEIVE_FAILURES, emitted_props[cs.DELIVERY_REPORTING_SUPPORT]) assert old_sig.args[0] == ret.value[0] assert old_sig.args[1] == cs.CHANNEL_TYPE_TEXT assert old_sig.args[2] == cs.HT_CONTACT assert old_sig.args[3] == foo_handle assert old_sig.args[4] == True # suppress handler assert len(new_sig.args) == 1 assert len(new_sig.args[0]) == 1 # one channel assert len(new_sig.args[0][0]) == 2 # two struct members assert new_sig.args[0][0][0] == ret.value[0] assert new_sig.args[0][0][1] == ret.value[1] properties = conn.GetAll( cs.CONN_IFACE_REQUESTS, dbus_interface=dbus.PROPERTIES_IFACE) assert new_sig.args[0][0] in properties['Channels'], \ (new_sig.args[0][0], properties['Channels']) if __name__ == '__main__': exec_test(test) telepathy-gabble-0.18.2/tests/twisted/text/initiate.py0000644000175000017500000000557112227000321023003 0ustar00smcvsmcv00000000000000""" Test text channel initiated by me. """ import dbus from twisted.words.xish import domish from gabbletest import exec_test from servicetest import call_async, EventPattern import constants as cs def test(q, bus, conn, stream): self_handle = conn.GetSelfHandle() jid = 'foo@bar.com' call_async(q, conn, 'RequestHandles', cs.HT_CONTACT, [jid]) event = q.expect('dbus-return', method='RequestHandles') foo_handle = event.value[0][0] call_async(q, conn, 'RequestChannel', cs.CHANNEL_TYPE_TEXT, cs.HT_CONTACT, foo_handle, True) ret, sig = q.expect_many( EventPattern('dbus-return', method='RequestChannel'), EventPattern('dbus-signal', signal='NewChannel'), ) text_chan = bus.get_object(conn.bus_name, ret.value[0]) assert sig.args[0] == ret.value[0], \ (sig.args[0], ret.value[0]) assert sig.args[1] == cs.CHANNEL_TYPE_TEXT, sig.args[1] # check that handle type == contact handle assert sig.args[2] == 1, sig.args[1] assert sig.args[3] == foo_handle, (sig.args[3], foo_handle) assert sig.args[4] == True # suppress handler # Exercise basic Channel Properties from spec 0.17.7 channel_props = text_chan.GetAll( cs.CHANNEL, dbus_interface=dbus.PROPERTIES_IFACE) assert channel_props.get('TargetHandle') == foo_handle,\ (channel_props.get('TargetHandle'), foo_handle) assert channel_props.get('TargetHandleType') == 1,\ channel_props.get('TargetHandleType') assert channel_props.get('ChannelType') == \ cs.CHANNEL_TYPE_TEXT,\ channel_props.get('ChannelType') assert cs.CHANNEL_IFACE_CHAT_STATE in \ channel_props.get('Interfaces', ()), \ channel_props.get('Interfaces') assert channel_props['TargetID'] == jid,\ (channel_props['TargetID'], jid) assert channel_props['Requested'] == True assert channel_props['InitiatorHandle'] == self_handle,\ (channel_props['InitiatorHandle'], self_handle) assert channel_props['InitiatorID'] == 'test@localhost',\ channel_props['InitiatorID'] dbus.Interface(text_chan, cs.CHANNEL_TYPE_TEXT).Send(0, 'hey') event = q.expect('stream-message') elem = event.stanza assert elem.name == 'message' assert elem['type'] == 'chat' body = list(event.stanza.elements())[0] assert body.name == 'body' assert body.children[0] == u'hey' # hello m = domish.Element((None, 'message')) m['from'] = 'foo@bar.com/Pidgin' m['type'] = 'chat' m.addElement('body', content='hello') stream.send(m) event = q.expect('dbus-signal', signal='Received') # message type: normal assert event.args[3] == 0 # flags: none assert event.args[4] == 0 # body assert event.args[5] == 'hello' if __name__ == '__main__': exec_test(test) telepathy-gabble-0.18.2/tests/twisted/text/facebook-own-message.py0000644000175000017500000000515312227000321025165 0ustar00smcvsmcv00000000000000""" 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'}) telepathy-gabble-0.18.2/tests/twisted/text/ensure.py0000644000175000017500000001306512227000321022473 0ustar00smcvsmcv00000000000000""" Test text channel initiated by me, using Requests.EnsureChannel """ import dbus from gabbletest import exec_test from servicetest import call_async, EventPattern import constants as cs def test(q, bus, conn, stream): self_handle = conn.GetSelfHandle() jids = ['foo@bar.com', 'truc@cafe.fr'] call_async(q, conn, 'RequestHandles', 1, jids) event = q.expect('dbus-return', method='RequestHandles') handles = event.value[0] properties = conn.GetAll( cs.CONN_IFACE_REQUESTS, dbus_interface=cs.PROPERTIES_IFACE) assert properties.get('Channels') == [], properties['Channels'] assert ({cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_TEXT, cs.TARGET_HANDLE_TYPE: cs.HT_CONTACT, }, [cs.TARGET_HANDLE, cs.TARGET_ID], ) in properties.get('RequestableChannelClasses'),\ properties['RequestableChannelClasses'] test_ensure_ensure(q, conn, self_handle, jids[0], handles[0]) test_request_ensure(q, conn, self_handle, jids[1], handles[1]) def test_ensure_ensure(q, conn, self_handle, jid, handle): """ Test ensuring a non-existant channel twice. The first call should succeed with Yours=True; the subsequent call should succeed with Yours=False """ # Check that Ensuring a channel that doesn't exist succeeds call_async(q, conn.Requests, 'EnsureChannel', request_props (handle)) ret, old_sig, new_sig = q.expect_many( EventPattern('dbus-return', method='EnsureChannel'), EventPattern('dbus-signal', signal='NewChannel'), EventPattern('dbus-signal', signal='NewChannels'), ) assert len(ret.value) == 3 yours, path, emitted_props = ret.value # The channel was created in response to the call, and we were the only # requestor, so we should get Yours=True assert yours, ret.value check_props(emitted_props, self_handle, handle, jid) assert len(old_sig.args) == 5 old_path, old_ct, old_ht, old_h, old_sh = old_sig.args assert old_path == path assert old_ct == cs.CHANNEL_TYPE_TEXT assert old_ht == cs.HT_CONTACT assert old_h == handle assert old_sh == True # suppress handler assert len(new_sig.args) == 1 assert len(new_sig.args[0]) == 1 # one channel assert len(new_sig.args[0][0]) == 2 # two struct members assert new_sig.args[0][0][0] == path assert new_sig.args[0][0][1] == emitted_props properties = conn.GetAll( cs.CONN_IFACE_REQUESTS, dbus_interface=dbus.PROPERTIES_IFACE) assert new_sig.args[0][0] in properties['Channels'], \ (new_sig.args[0][0], properties['Channels']) # Now try Ensuring a channel which already exists call_async(q, conn.Requests, 'EnsureChannel', request_props(handle)) ret_ = q.expect('dbus-return', method='EnsureChannel') assert len(ret_.value) == 3 yours_, path_, emitted_props_ = ret_.value # Someone's already responsible for this channel, so we should get # Yours=False assert not yours_, ret_.value assert path == path_, (path, path_) assert emitted_props == emitted_props_, (emitted_props, emitted_props_) def test_request_ensure(q, conn, self_handle, jid, handle): """ Test Creating a non-existant channel, then Ensuring the same channel. The call to Ensure should succeed with Yours=False. """ call_async(q, conn.Requests, 'CreateChannel', request_props(handle)) ret, old_sig, new_sig = q.expect_many( EventPattern('dbus-return', method='CreateChannel'), EventPattern('dbus-signal', signal='NewChannel'), EventPattern('dbus-signal', signal='NewChannels'), ) assert len(ret.value) == 2 path, emitted_props = ret.value check_props(emitted_props, self_handle, handle, jid) assert len(old_sig.args) == 5 old_path, old_ct, old_ht, old_h, old_sh = old_sig.args assert old_path == path assert old_ct == cs.CHANNEL_TYPE_TEXT assert old_ht == cs.HT_CONTACT assert old_h == handle assert old_sh == True # suppress handler assert len(new_sig.args) == 1 assert len(new_sig.args[0]) == 1 # one channel assert len(new_sig.args[0][0]) == 2 # two struct members assert new_sig.args[0][0][0] == path assert new_sig.args[0][0][1] == emitted_props properties = conn.GetAll( cs.CONN_IFACE_REQUESTS, dbus_interface=dbus.PROPERTIES_IFACE) assert new_sig.args[0][0] in properties['Channels'], \ (new_sig.args[0][0], properties['Channels']) # Now try Ensuring that same channel. call_async(q, conn.Requests, 'EnsureChannel', request_props(handle)) ret_ = q.expect('dbus-return', method='EnsureChannel') assert len(ret_.value) == 3 yours_, path_, emitted_props_ = ret_.value # Someone's already responsible for this channel, so we should get # Yours=False assert not yours_, ret_.value assert path == path_, (path, path_) assert emitted_props == emitted_props_, (emitted_props, emitted_props_) def check_props(props, self_handle, handle, jid): assert props[cs.CHANNEL_TYPE] == cs.CHANNEL_TYPE_TEXT assert props[cs.TARGET_HANDLE_TYPE] == cs.HT_CONTACT assert props[cs.TARGET_HANDLE] == handle assert props[cs.TARGET_ID] == jid assert props[cs.REQUESTED] == True assert props[cs.INITIATOR_HANDLE] == self_handle assert props[cs.INITIATOR_ID] == 'test@localhost' def request_props(handle): return { cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_TEXT, cs.TARGET_HANDLE_TYPE: cs.HT_CONTACT, cs.TARGET_HANDLE: handle, } if __name__ == '__main__': exec_test(test) telepathy-gabble-0.18.2/tests/twisted/text/destroy.py0000644000175000017500000000777712227000321022700 0ustar00smcvsmcv00000000000000""" Test text channel not being recreated because although there were still pending messages, we destroyed it with extreme prejudice. """ import dbus from twisted.words.xish import domish from gabbletest import exec_test from servicetest import call_async, EventPattern import constants as cs def test(q, bus, conn, stream): self_handle = conn.GetSelfHandle() jid = 'foo@bar.com' call_async(q, conn, 'RequestHandles', 1, [jid]) event = q.expect('dbus-return', method='RequestHandles') foo_handle = event.value[0][0] call_async(q, conn, 'RequestChannel', cs.CHANNEL_TYPE_TEXT, cs.HT_CONTACT, foo_handle, True) ret, old_sig, new_sig = q.expect_many( EventPattern('dbus-return', method='RequestChannel'), EventPattern('dbus-signal', signal='NewChannel'), EventPattern('dbus-signal', signal='NewChannels'), ) text_chan = bus.get_object(conn.bus_name, ret.value[0]) chan_iface = dbus.Interface(text_chan, cs.CHANNEL) text_iface = dbus.Interface(text_chan, cs.CHANNEL_TYPE_TEXT) destroyable_iface = dbus.Interface(text_chan, cs.CHANNEL_IFACE_DESTROYABLE) assert old_sig.args[0] == ret.value[0] assert old_sig.args[1] == cs.CHANNEL_TYPE_TEXT assert old_sig.args[2] == cs.HT_CONTACT assert old_sig.args[3] == foo_handle assert old_sig.args[4] == True # suppress handler assert len(new_sig.args) == 1 assert len(new_sig.args[0]) == 1 # one channel assert len(new_sig.args[0][0]) == 2 # two struct members assert new_sig.args[0][0][0] == ret.value[0] emitted_props = new_sig.args[0][0][1] assert emitted_props[cs.CHANNEL_TYPE] == cs.CHANNEL_TYPE_TEXT assert emitted_props[cs.TARGET_HANDLE_TYPE] == cs.HT_CONTACT assert emitted_props[cs.TARGET_HANDLE] == foo_handle assert emitted_props[cs.TARGET_ID] == jid assert emitted_props[cs.REQUESTED] == True assert emitted_props[cs.INITIATOR_HANDLE] == self_handle assert emitted_props[cs.INITIATOR_ID] == 'test@localhost' channel_props = text_chan.GetAll( cs.CHANNEL, dbus_interface=dbus.PROPERTIES_IFACE) assert channel_props['TargetID'] == jid, (channel_props['TargetID'], jid) assert channel_props['Requested'] == True assert channel_props['InitiatorHandle'] == self_handle,\ (channel_props['InitiatorHandle'], self_handle) assert channel_props['InitiatorID'] == 'test@localhost',\ channel_props['InitiatorID'] text_iface.Send(0, 'hey') event = q.expect('stream-message') elem = event.stanza assert elem.name == 'message' assert elem['type'] == 'chat' body = list(event.stanza.elements())[0] assert body.name == 'body' assert body.children[0] == u'hey' # hello m = domish.Element((None, 'message')) m['from'] = 'foo@bar.com/Pidgin' m['type'] = 'chat' m.addElement('body', content='hello') stream.send(m) event = q.expect('dbus-signal', signal='Received') hello_message_id = event.args[0] hello_message_time = event.args[1] assert event.args[2] == foo_handle # message type: normal assert event.args[3] == 0 # flags: none assert event.args[4] == 0 # body assert event.args[5] == 'hello' messages = text_chan.ListPendingMessages(False, dbus_interface=cs.CHANNEL_TYPE_TEXT) assert messages == \ [(hello_message_id, hello_message_time, foo_handle, 0, 0, 'hello')], messages # destroy the channel without acking the message; it does not come back call_async(q, destroyable_iface, 'Destroy') event = q.expect('dbus-signal', signal='Closed') assert event.path == text_chan.object_path,\ (event.path, text_chan.object_path) event = q.expect('dbus-return', method='Destroy') # assert that it stays dead try: chan_iface.GetChannelType() except dbus.DBusException: pass else: raise AssertionError("Why won't it die?") if __name__ == '__main__': exec_test(test) telepathy-gabble-0.18.2/tests/twisted/search/0000755000175000017500000000000012312537051021106 5ustar00smcvsmcv00000000000000telepathy-gabble-0.18.2/tests/twisted/search/search_helper.py0000644000175000017500000000766212200204333024265 0ustar00smcvsmcv00000000000000import dbus from twisted.words.protocols.jabber.client import IQ from servicetest import call_async, EventPattern from gabbletest import make_result_iq import constants as cs import ns def call_create(q, conn, server): request = dbus.Dictionary( { cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_CONTACT_SEARCH, }, signature='sv') if server is not None: request[cs.CONTACT_SEARCH_SERVER] = server call_async(q, conn.Requests, 'CreateChannel', request) def _wait_for_server_query(q, stream, server): # Gabble asks the server what search fields it supports iq_event = q.expect('stream-iq', to=server, query_ns=ns.SEARCH) iq = iq_event.stanza result = make_result_iq(stream, iq) query = result.firstChildElement() query.addElement("instructions", content="cybar?") return result, query def _send_server_reply(q, stream, result): stream.send(result) ret = q.expect('dbus-return', method='CreateChannel') nc_sig = q.expect('dbus-signal', signal='NewChannels') return (ret, nc_sig) def answer_field_query(q, stream, server): result, query = _wait_for_server_query(q, stream, server) # The server says it supports all the fields in unextended XEP 0055 for f in ["first", "last", "nick", "email"]: query.addElement(f) return _send_server_reply(q, stream, result) def answer_extended_field_query(q, stream, server, fields): result, query = _wait_for_server_query(q, stream, server) x = query.addElement((ns.X_DATA, 'x')) x['type'] = 'form' x.addElement('title', content="User Directory Search") x.addElement('instructions', content="mooh?") # add FORM_TYPE field = x.addElement('field') field['type'] = 'hidden' field['var'] = 'FORM_TYPE' field.addElement('value', content=ns.SEARCH) # add fields for var, type, label, options in fields: field = x.addElement('field') field['var'] = var field['type'] = type field['label'] = label # add options (if any) for value, label in options: option = field.addElement('option') option['label'] = label v = option.addElement('value', content=value) return _send_server_reply(q, stream, result) def make_search(q, c_search, c_props, server, terms): call_async(q, c_search, 'Search', terms) _, ssc_event, iq_event = q.expect_many( EventPattern('dbus-return', method='Search'), EventPattern('dbus-signal', signal='SearchStateChanged'), EventPattern('stream-iq', to=server, query_ns=ns.SEARCH, iq_type='set'), ) assert ssc_event.args[0] == cs.SEARCH_IN_PROGRESS state = c_props.Get(cs.CHANNEL_TYPE_CONTACT_SEARCH, 'SearchState') assert state == cs.SEARCH_IN_PROGRESS, state return iq_event.stanza def send_results(stream, iq, results): result = make_result_iq(stream, iq) query = result.firstChildElement() for jid, first, last, nick in results: item = query.addElement('item') item['jid'] = jid item.addElement('first', content=first) item.addElement('last', content=last) item.addElement('nick', content=nick) item.addElement('email', content=jid) stream.send(result) def send_results_extended(stream, iq, results, fields): result = make_result_iq(stream, iq) query = result.firstChildElement() x = query.addElement((ns.X_DATA, 'x')) x['type'] = 'result' x.addElement('title', content='Search result') # add reported fields reported = x.addElement('reported') for var, type, label, options in fields: field = reported.addElement('field') field['var'] = var field['label'] = label # add results for r in results: item = x.addElement('item') for var, value in r.items(): field = item.addElement('field') field['var'] = var field.addElement('value', content=value) stream.send(result) telepathy-gabble-0.18.2/tests/twisted/search/unextended.py0000644000175000017500000001276712200204333023626 0ustar00smcvsmcv00000000000000""" Tests Contact Search channels to a simulated XEP-0055 service, without extensibility via Data Forms """ import dbus from twisted.words.protocols.jabber.client import IQ from gabbletest import exec_test, sync_stream from servicetest import ( call_async, unwrap, make_channel_proxy, EventPattern, assertSameSets, ) from search_helper import call_create, answer_field_query, make_search, send_results from pprint import pformat import constants as cs import ns g_jid = 'guybrush.threepwood@lucasarts.example.com' f_jid = 'freddiet@pgwodehouse.example.com' g_results = (g_jid, 'Guybrush', 'Threepwood', 'Fancy Pants') f_results = (f_jid, 'Frederick', 'Threepwood', 'Freddie') results = { g_jid: g_results, f_jid: f_results } def test(q, bus, conn, stream): for f in [complete_search, cancelled_while_in_progress]: f(q, bus, conn, stream, 'jud.localhost') def complete_search(q, bus, conn, stream, server): call_create(q, conn, server) # the channel is not yet in conn.Requests.Channels as it's not ready yet channels = conn.Get(cs.CONN_IFACE_REQUESTS, 'Channels', dbus_interface=cs.PROPERTIES_IFACE) for path, props in channels: assert props[cs.CHANNEL_TYPE] != cs.CHANNEL_TYPE_CONTACT_SEARCH ret, nc_sig = answer_field_query(q, stream, server) path, props = ret.value props = unwrap(props) expected_search_keys = ['email', 'nickname', 'x-n-family', 'x-n-given'] assert props[cs.CONTACT_SEARCH_SERVER] == server, pformat(props) assert sorted(props[cs.CONTACT_SEARCH_ASK]) == expected_search_keys, \ pformat(props) assert cs.CONTACT_SEARCH_STATE not in props, pformat(props) # check that channel is listed in conn.Requests.Channels channels = conn.Get(cs.CONN_IFACE_REQUESTS, 'Channels', dbus_interface=cs.PROPERTIES_IFACE) assert (path, props) in channels c = make_channel_proxy(conn, path, 'Channel') c_props = dbus.Interface(c, cs.PROPERTIES_IFACE) c_search = dbus.Interface(c, cs.CHANNEL_TYPE_CONTACT_SEARCH) state = c_props.Get(cs.CHANNEL_TYPE_CONTACT_SEARCH, 'SearchState') assert state == cs.SEARCH_NOT_STARTED, state # We make a search. iq = make_search(q, c_search, c_props, server, { 'x-n-family': 'Threepwood' }) query = iq.firstChildElement() i = 0 for field in query.elements(): assert field.name == 'last', field.toXml() assert field.children[0] == u'Threepwood', field.children[0] i += 1 assert i == 1, query # Server sends the results of the search. send_results(stream, iq, results.values()) r = q.expect('dbus-signal', signal='SearchResultReceived') infos = r.args[0] assertSameSets(results.keys(), infos.keys()) for id in results.keys(): i = infos[id] r = results[id] i_ = pformat(unwrap(i)) assert ("n", [], [r[2], r[1], "", "", ""]) in i, i_ assert ("nickname", [], [r[3]]) in i, i_ assert ("email", [], [r[0]]) in i, i_ assert ("x-n-family", [], [r[2]]) in i, i_ assert ("x-n-given", [], [r[1]]) in i, i_ assert len(i) == 5, i_ ssc = q.expect('dbus-signal', signal='SearchStateChanged') assert ssc.args[0] == cs.SEARCH_COMPLETED, ssc.args # We call Stop after the search has completed; it should succeed, but leave # the channel in state Completed rather than changing it to Failed for # reason Cancelled. call_async(q, c_search, 'Stop') event = q.expect('dbus-return', method='Stop') state = c_props.Get(cs.CHANNEL_TYPE_CONTACT_SEARCH, 'SearchState') assert state == cs.SEARCH_COMPLETED, (state, cs.SEARCH_COMPLETED) c.Close() q.expect_many( EventPattern('dbus-signal', signal='Closed'), EventPattern('dbus-signal', signal='ChannelClosed'), ) def cancelled_while_in_progress(q, bus, conn, stream, server): call_create(q, conn, server) ret, _ = answer_field_query(q, stream, server) path, props = ret.value c = make_channel_proxy(conn, path, 'Channel') c_props = dbus.Interface(c, cs.PROPERTIES_IFACE) c_search = dbus.Interface(c, cs.CHANNEL_TYPE_CONTACT_SEARCH) iq = make_search(q, c_search, c_props, server, { 'x-n-family': 'Threepwood' }) # Before the server sends back the results, the client cancels the search. call_async(q, c_search, 'Stop') ret, ssc = q.expect_many( EventPattern('dbus-return', method='Stop'), EventPattern('dbus-signal', signal='SearchStateChanged'), ) assert ssc.args[0] == cs.SEARCH_FAILED, ssc.args assert ssc.args[1] == cs.CANCELLED, ssc.args state = c_props.Get(cs.CHANNEL_TYPE_CONTACT_SEARCH, 'SearchState') assert state == cs.SEARCH_FAILED, (state, cs.SEARCH_FAILED) # Now the server sends us the results; SearchResultReceived shouldn't fire search_result_received_event = EventPattern('dbus-signal', signal='SearchResultReceived') q.forbid_events([search_result_received_event]) send_results(stream, iq, results.values()) # Make sure Gabble's received the results. sync_stream(q, stream) # Hooray! We survived. Now let's call Stop again; it should succeed but do # nothing. search_state_changed_event = EventPattern('dbus-signal', signal='SearchStateChanged') q.forbid_events([search_state_changed_event]) call_async(q, c_search, 'Stop') ssc = q.expect('dbus-return', method='Stop') c.Close() q.unforbid_events([search_result_received_event, search_state_changed_event]) if __name__ == '__main__': exec_test(test) telepathy-gabble-0.18.2/tests/twisted/search/no-server-property.py0000644000175000017500000001017712200204333025256 0ustar00smcvsmcv00000000000000""" Tests Contact Search channels to a simulated XEP-0055 service, without passing the Server property """ import dbus from twisted.words.xish import xpath from functools import partial from gabbletest import exec_test, sync_stream, make_result_iq, acknowledge_iq, elem_iq, elem, disconnect_conn from servicetest import EventPattern, assertEquals from search_helper import call_create, answer_field_query import constants as cs import ns JUD_SERVER = 'jud.localhost' def server_discovered(q, bus, conn, stream, server=None): iq_event, disco_event = q.expect_many( EventPattern('stream-iq', to=None, query_ns='vcard-temp', query_name='vCard'), EventPattern('stream-iq', to='localhost', query_ns=ns.DISCO_ITEMS)) acknowledge_iq(stream, iq_event.stanza) # no search server has been discovered yet. The CreateChannel operation # will be completed once the disco process is finished. call_create(q, conn, server=server) # reply to IQ query reply = make_result_iq(stream, disco_event.stanza) query = xpath.queryForNodes('/iq/query', reply)[0] item = query.addElement((None, 'item')) item['jid'] = JUD_SERVER stream.send(reply) # wait for the disco#info query event = q.expect('stream-iq', to=JUD_SERVER, query_ns=ns.DISCO_INFO) reply = elem_iq(stream, 'result', id=event.stanza['id'], from_=JUD_SERVER)( elem(ns.DISCO_INFO, 'query')( elem('identity', category='directory', type='user', name='vCard User Search')(), elem('feature', var=ns.SEARCH)())) stream.send(reply) # JUD_SERVER is used as default, and shows up as the Server property on the # resulting channel. ret, _ = answer_field_query(q, stream, JUD_SERVER) _, properties = ret.value assertEquals(JUD_SERVER, properties[cs.CONTACT_SEARCH_SERVER]) # Now that the search server has been discovered, it is used right away. call_create(q, conn, server=server) ret, _ = answer_field_query(q, stream, JUD_SERVER) _, properties = ret.value assertEquals(JUD_SERVER, properties[cs.CONTACT_SEARCH_SERVER]) def no_server_discovered(q, bus, conn, stream, server=None): iq_event, disco_event = q.expect_many( EventPattern('stream-iq', to=None, query_ns='vcard-temp', query_name='vCard'), EventPattern('stream-iq', to='localhost', query_ns=ns.DISCO_ITEMS)) acknowledge_iq(stream, iq_event.stanza) # no search server has been discovered yet. The CreateChannel operation # will fail once the disco process is finished. call_create(q, conn, server=server) # reply to IQ query. No search server is present reply = make_result_iq(stream, disco_event.stanza) stream.send(reply) # creation of the channel failed e = q.expect('dbus-error', method='CreateChannel', name=cs.INVALID_ARGUMENT) # This server doesn't have a search server. We can't create Search channel # without specifying a Server property call_create(q, conn, server=server) e = q.expect('dbus-error', method='CreateChannel') assertEquals(cs.INVALID_ARGUMENT, e.error.get_dbus_name()) def disconnect_before_disco(q, bus, conn, stream, server=None): iq_event, disco_event = q.expect_many( EventPattern('stream-iq', to=None, query_ns='vcard-temp', query_name='vCard'), EventPattern('stream-iq', to='localhost', query_ns=ns.DISCO_ITEMS)) acknowledge_iq(stream, iq_event.stanza) # try to create a channel before the disco process is completed. # This creation will fail call_create(q, conn, server=server) # connection is disconnected. CreateChannel fails disconnect_conn(q, conn, stream, [ EventPattern('dbus-error', method='CreateChannel', name=cs.DISCONNECTED)]) if __name__ == '__main__': exec_test(server_discovered) exec_test(no_server_discovered) exec_test(disconnect_before_disco) # We also test that Gabble does something sensible if you set Server to the # empty string, rather than omitting it. exec_test(partial(server_discovered, server='')) exec_test(partial(no_server_discovered, server='')) exec_test(partial(disconnect_before_disco, server='')) telepathy-gabble-0.18.2/tests/twisted/search/extended.py0000644000175000017500000002161312200204333023251 0ustar00smcvsmcv00000000000000""" Tests Contact Search channels to a simulated XEP-0055 service, with extensibility via Data Forms """ import dbus from twisted.words.xish import xpath from gabbletest import exec_test, acknowledge_iq from servicetest import ( call_async, unwrap, make_channel_proxy, EventPattern, assertEquals, assertSameSets, ) from search_helper import call_create, answer_extended_field_query, make_search, send_results_extended from pprint import pformat import constants as cs import ns server = 'jud.localhost' g_jid = 'guybrush.threepwood@lucasarts.example.com' f_jid = 'freddiet@pgwodehouse.example.com' def test(q, bus, conn, stream): for f in [complete_search, complete_search2, openfire_search, double_nick, no_x_in_reply]: f(q, bus, conn, stream) def do_one_search(q, bus, conn, stream, fields, expected_search_keys, terms, results): call_create(q, conn, server) ret, nc_sig = answer_extended_field_query(q, stream, server, fields) path, props = ret.value props = unwrap(props) assert props[cs.CONTACT_SEARCH_SERVER] == server, pformat(props) assert sorted(props[cs.CONTACT_SEARCH_ASK]) == expected_search_keys, \ sorted(props[cs.CONTACT_SEARCH_ASK]) assert cs.CONTACT_SEARCH_STATE not in props, pformat(props) c = make_channel_proxy(conn, path, 'Channel') c_props = dbus.Interface(c, cs.PROPERTIES_IFACE) c_search = dbus.Interface(c, cs.CHANNEL_TYPE_CONTACT_SEARCH) state = c_props.Get(cs.CHANNEL_TYPE_CONTACT_SEARCH, 'SearchState') assert state == cs.SEARCH_NOT_STARTED, state # We make a search. iq = make_search(q, c_search, c_props, server, terms) query = iq.firstChildElement() fields_sent = xpath.queryForNodes( '/iq/query[@xmlns="%s"]/x[@xmlns="%s"][@type="submit"]/field' % (ns.SEARCH, ns.X_DATA), iq) assert fields_sent is not None # check FORM_TYPE f = fields_sent[0] assert f['type'] == 'hidden' assert f['var'] == 'FORM_TYPE' value = f.firstChildElement() assert value.name == 'value' assert value.children[0] == ns.SEARCH # extract search fields search_fields = [] for f in fields_sent[1:]: value = f.firstChildElement() assert value.name == 'value' search_fields.append((f['var'], value.children[0])) # Server sends the results of the search. send_results_extended(stream, iq, results, fields) return search_fields, c, c_search, c_props def search_done(q, c, c_search, c_props): ssc = q.expect('dbus-signal', signal='SearchStateChanged') assert ssc.args[0] == cs.SEARCH_COMPLETED, ssc.args # We call Stop after the search has completed; it should succeed, but leave # the channel in state Completed rather than changing it to Failed for # reason Cancelled. call_async(q, c_search, 'Stop') event = q.expect('dbus-return', method='Stop') state = c_props.Get(cs.CHANNEL_TYPE_CONTACT_SEARCH, 'SearchState') assert state == cs.SEARCH_COMPLETED, (state, cs.SEARCH_COMPLETED) c.Close() q.expect_many( EventPattern('dbus-signal', signal='Closed'), EventPattern('dbus-signal', signal='ChannelClosed'), ) def complete_search(q, bus, conn, stream): fields = [('first', 'text-single', 'Given Name', []), ('last', 'text-single', 'Family Name', []), ('x-gender', 'list-single', 'Gender', [('male', 'Male'), ('female', 'Female')])] expected_search_keys = ['x-gender', 'x-n-family', 'x-n-given'] terms = { 'x-n-family': 'Threepwood' } g_results = { 'jid': g_jid, 'first': 'Guybrush', 'last': 'Threepwood', 'nick': 'Fancy Pants', 'x-gender': 'Male', 'email': g_jid } f_results = { 'jid': f_jid, 'first': 'Frederick', 'last': 'Threepwood', 'nick': 'Freddie', 'x-gender': 'Male', 'email': f_jid } results = { g_jid: g_results, f_jid: f_results } search_fields, chan, c_search, c_props = do_one_search (q, bus, conn, stream, fields, expected_search_keys, terms, results.values()) assert len(search_fields) == 1 assert ('last', 'Threepwood') in search_fields, search_fields e = q.expect('dbus-signal', signal='SearchResultReceived') infos = e.args[0] assertSameSets(results.keys(), infos.keys()) for id in results.keys(): i = infos[id] r = results[id] i_ = pformat(unwrap(i)) assert ("n", [], [r['last'], r['first'], "", "", ""]) in i, i_ assert ("nickname", [], [r['nick']]) in i, i_ assert ("email", [], [r['email']]) in i, i_ assert ("x-gender", [], [r['x-gender']]) in i, i_ assert ("x-n-family", [], [r['last']]) in i, i_ assert ("x-n-given", [], [r['first']]) in i, i_ assert len(i) == 6, i_ search_done(q, chan, c_search, c_props) def complete_search2(q, bus, conn, stream): # uses other, dataform specific, fields fields = [('given', 'text-single', 'Name', []), ('family', 'text-single', 'Family Name', []), ('nickname', 'text-single', 'Nickname', [])] expected_search_keys = ['nickname', 'x-n-family', 'x-n-given'] terms = { 'x-n-family': 'Threepwood' } g_results = { 'jid': g_jid, 'given': 'Guybrush', 'family': 'Threepwood', 'nickname': 'Fancy Pants', 'email': g_jid } f_results = { 'jid': f_jid, 'given': 'Frederick', 'family': 'Threepwood', 'nickname': 'Freddie', 'email': f_jid } results = { g_jid: g_results, f_jid: f_results } search_fields, chan, c_search, c_props = do_one_search (q, bus, conn, stream, fields, expected_search_keys, terms, results.values()) assert len(search_fields) == 1 assert ('family', 'Threepwood') in search_fields, search_fields e = q.expect('dbus-signal', signal='SearchResultReceived') infos = e.args[0] assertSameSets(results.keys(), infos.keys()) for id in results.keys(): i = infos[id] r = results[id] i_ = pformat(unwrap(i)) assert ("n", [], [r['family'], r['given'], "", "", ""]) in i, i_ assert ("nickname", [], [r['nickname']]) in i, i_ assert ("email", [], [r['email']]) in i, i_ assert ("x-n-family", [], [r['family']]) in i, i_ assert ("x-n-given", [], [r['given']]) in i, i_ assert len(i) == 5, i_ search_done(q, chan, c_search, c_props) def openfire_search(q, bus, conn, stream): # Openfire only supports one text field and a bunch of checkboxes fields = [('search', 'text-single', 'Search', []), ('Username', 'boolean', 'Username', []), ('Name', 'boolean', 'Name', []), ('Email', 'boolean', 'Email', [])] expected_search_keys = [''] terms = { '': '*badger*' } jid = 'badger@mushroom.org' results = {jid : { 'jid': jid, 'Name': 'Badger Badger', 'Email': jid, 'Username': 'badger'}} search_fields, chan, c_search, c_props = do_one_search (q, bus, conn, stream, fields, expected_search_keys, terms, results.values()) assert len(search_fields) == 4 assert ('search', '*badger*') in search_fields, search_fields assert ('Username', '1') in search_fields, search_fields assert ('Name', '1') in search_fields, search_fields assert ('Email', '1') in search_fields, search_fields r = q.expect('dbus-signal', signal='SearchResultReceived') infos = r.args[0] assertSameSets(results.keys(), infos.keys()) for id in results.keys(): i = infos[id] r = results[id] i_ = pformat(unwrap(i)) assert ("fn", [], [r['Name']]) in i, i_ assert ("email", [], [r['Email']]) in i, i_ assert len(i) == 2 # Server supports 'nickname' and 'nick' which are both mapped to the # "nickname" in Telepathy def double_nick(q, bus, conn, stream): fields = [('nickname', 'text-single', 'NickName', []), ('nick', 'text-single', 'Nick', []),] # ensure that 'nickname' is not added twice expected_search_keys = ['nickname'] terms = { 'nickname': 'Badger' } results = { } search_fields, chan, c_search, c_props = do_one_search (q, bus, conn, stream, fields, expected_search_keys, terms, results.values()) def no_x_in_reply(q, bus, conn, stream): fields = [('nickname', 'text-single', 'NickName', []), ('nick', 'text-single', 'Nick', []),] terms = { 'nickname': 'Badger' } call_create(q, conn, server) ret, nc_sig = answer_extended_field_query(q, stream, server, fields) path, _ = ret.value c = make_channel_proxy(conn, path, 'Channel') c_props = dbus.Interface(c, cs.PROPERTIES_IFACE) c_search = dbus.Interface(c, cs.CHANNEL_TYPE_CONTACT_SEARCH) iq = make_search(q, c_search, c_props, server, terms) # The server sends back an IQ with a but no inside the query. acknowledge_iq(stream, iq) # Gabble should tell us the query failed, and not crash. event = q.expect('dbus-signal', signal='SearchStateChanged') state = event.args[0] assertEquals(cs.SEARCH_FAILED, state) if __name__ == '__main__': exec_test(test) telepathy-gabble-0.18.2/tests/twisted/search/ceci-nest-pas-un-serveur.py0000644000175000017500000001423312200204333026215 0ustar00smcvsmcv00000000000000""" Tests requesting search channels to, and performing contact searches against, fake servers which are broken in various ways. """ import dbus from twisted.words.protocols.jabber.client import IQ from twisted.words.xish import domish from gabbletest import exec_test, send_error_reply, make_result_iq from servicetest import ( call_async, unwrap, make_channel_proxy, EventPattern, assertDBusError ) from pprint import pformat import constants as cs import ns def call_create(q, conn, server): """ Calls CreateChannel for the given contact search server, and returns the IQ stanza received by the server. """ request = dbus.Dictionary( { cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_CONTACT_SEARCH, cs.CONTACT_SEARCH_SERVER: server, }, signature='sv') call_async(q, conn.Requests, 'CreateChannel', request) iq_event = q.expect('stream-iq', to=server, query_ns=ns.SEARCH) return iq_event.stanza def not_a_search_server(q, stream, conn): iq = call_create(q, conn, 'notajud.localhost') e = domish.Element((None, 'error')) e['type'] = 'cancel' e.addElement((ns.STANZA, 'service-unavailable')) send_error_reply(stream, iq, e) event = q.expect('dbus-error', method='CreateChannel') assertDBusError(cs.NOT_AVAILABLE, event.error) def returns_invalid_fields(q, stream, conn): iq = call_create(q, conn, 'broken.localhost') result = make_result_iq(stream, iq) query = result.firstChildElement() for f in ["first", "shoe-size", "nick", "star-sign"]: query.addElement(f) stream.send(result) event = q.expect('dbus-error', method='CreateChannel') assertDBusError(cs.NOT_AVAILABLE, event.error) def returns_error_from_search(q, stream, conn): server = 'nofunforyou.localhost' iq = call_create(q, conn, server) result = make_result_iq(stream, iq) query = result.firstChildElement() query.addElement("first") stream.send(result) event = q.expect('dbus-return', method='CreateChannel') c = make_channel_proxy(conn, event.value[0], 'Channel') c_search = dbus.Interface(c, cs.CHANNEL_TYPE_CONTACT_SEARCH) call_async(q, c_search, 'Search', {'x-n-given': 'World of Goo'}) iq_event, _ = q.expect_many( EventPattern('stream-iq', to=server, query_ns=ns.SEARCH), EventPattern('dbus-signal', signal='SearchStateChanged'), ) iq = iq_event.stanza error = domish.Element((None, 'error')) error['type'] = 'modify' error.addElement((ns.STANZA, 'not-acceptable')) error.addElement((ns.STANZA, 'text'), content="We don't believe in games here.") send_error_reply(stream, iq, error) ssc = q.expect('dbus-signal', signal='SearchStateChanged') new_state, reason, details = ssc.args assert new_state == cs.SEARCH_FAILED, new_state assert reason == cs.PERMISSION_DENIED, reason # We call stop after the search has failed; it should succeed and do nothing. call_async(q, c_search, 'Stop') event = q.expect('dbus-return', method='Stop') c.Close() def returns_bees_from_search(q, stream, conn): server = 'hivemind.localhost' iq = call_create(q, conn, server) result = make_result_iq(stream, iq) query = result.firstChildElement() query.addElement("nick") stream.send(result) event = q.expect('dbus-return', method='CreateChannel') c = make_channel_proxy(conn, event.value[0], 'Channel') c_search = dbus.Interface(c, cs.CHANNEL_TYPE_CONTACT_SEARCH) call_async(q, c_search, 'Search', {'nickname': 'Buzzy'}) iq_event, _ = q.expect_many( EventPattern('stream-iq', to=server, query_ns=ns.SEARCH), EventPattern('dbus-signal', signal='SearchStateChanged'), ) iq = iq_event.stanza result = IQ(stream, 'result') result['id'] = iq['id'] result['from'] = iq['to'] result.addElement((ns.SEARCH, 'bees')).addElement('bzzzzzzz') stream.send(result) ssc = q.expect('dbus-signal', signal='SearchStateChanged') new_state, reason, details = ssc.args assert new_state == cs.SEARCH_FAILED, new_state assert reason == cs.NOT_AVAILABLE, reason # We call stop after the search has failed; it should succeed and do nothing. call_async(q, c_search, 'Stop') event = q.expect('dbus-return', method='Stop') c.Close() def disconnected_before_reply(q, stream, conn): iq = call_create(q, conn, 'slow.localhost') call_async(q, conn, 'Disconnect') event = q.expect('dbus-error', method='CreateChannel') assertDBusError(cs.DISCONNECTED, event.error) def forbidden(q, stream, conn): iq = call_create(q, conn, 'notforyou.localhost') e = domish.Element((None, 'error')) e['type'] = 'cancel' e.addElement((ns.STANZA, 'forbidden')) send_error_reply(stream, iq, e) event = q.expect('dbus-error', method='CreateChannel') assertDBusError(cs.PERMISSION_DENIED, event.error) def invalid_jid(q, stream, conn): iq = call_create(q, conn, 'invalid.localhost') e = domish.Element((None, 'error')) e['type'] = 'cancel' e.addElement((ns.STANZA, 'jid-malformed')) send_error_reply(stream, iq, e) event = q.expect('dbus-error', method='CreateChannel') assertDBusError(cs.INVALID_ARGUMENT, event.error) def really_invalid_jid(q, stream, conn): request = dbus.Dictionary( { cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_CONTACT_SEARCH, cs.CONTACT_SEARCH_SERVER: 'this is literally bullshit', }, signature='sv') call_async(q, conn.Requests, 'CreateChannel', request) # If the JID is actually malformed, we shouldn't even get as far as trying # to talk to it. event = q.expect('dbus-error', method='CreateChannel') assertDBusError(cs.INVALID_ARGUMENT, event.error) def test(q, bus, conn, stream): not_a_search_server(q, stream, conn) returns_invalid_fields(q, stream, conn) returns_error_from_search(q, stream, conn) returns_bees_from_search(q, stream, conn) forbidden(q, stream, conn) invalid_jid(q, stream, conn) really_invalid_jid(q, stream, conn) disconnected_before_reply(q, stream, conn) stream.sendFooter() q.expect('dbus-return', method='Disconnect') if __name__ == '__main__': exec_test(test) telepathy-gabble-0.18.2/tests/twisted/sasl/0000755000175000017500000000000012312537051020603 5ustar00smcvsmcv00000000000000telepathy-gabble-0.18.2/tests/twisted/sasl/saslutil.py0000644000175000017500000001147312227000321023011 0ustar00smcvsmcv00000000000000# hey, Python: encoding: utf-8 from gabbletest import XmppAuthenticator from base64 import b64decode, b64encode from twisted.words.xish import domish import constants as cs import ns from servicetest import (ProxyWrapper, EventPattern, assertEquals, assertLength, Event) class SaslChannelWrapper(ProxyWrapper): def __init__(self, object, default=cs.CHANNEL, interfaces={ "ServerAuthentication" : cs.CHANNEL_TYPE_SERVER_AUTHENTICATION, "SASLAuthentication" : cs.CHANNEL_IFACE_SASL_AUTH}): ProxyWrapper.__init__(self, object, default, interfaces) class SaslEventAuthenticator(XmppAuthenticator): def __init__(self, jid, mechanisms): XmppAuthenticator.__init__(self, jid, '') self._mechanisms = mechanisms def streamSASL(self): XmppAuthenticator.streamSASL(self) self.xmlstream.addObserver("/response", self._response) self.xmlstream.addObserver("/abort", self._abort) def failure(self, fail_str): reply = domish.Element((ns.NS_XMPP_SASL, 'failure')) reply.addElement(fail_str) self.xmlstream.send(reply) self.xmlstream.reset() def abort(self): self.failure('abort') def not_authorized(self): self.failure('not-authorized') def success(self, data=None): reply = domish.Element((ns.NS_XMPP_SASL, 'success')) if data is not None: reply.addContent(b64encode(data)) self.xmlstream.send(reply) self.authenticated=True self.xmlstream.reset() def challenge(self, data): reply = domish.Element((ns.NS_XMPP_SASL, 'challenge')) reply.addContent(b64encode(data)) self.xmlstream.send(reply) def auth(self, auth): # Special case in XMPP: '=' means a zero-byte blob, whereas an empty # or self-terminating XML element means no initial response. # (RFC 3920 §6.2 (3)) if str(auth) == '': self._event_func(Event('sasl-auth', authenticator=self, has_initial_response=False, initial_response=None, xml=auth)) elif str(auth) == '=': self._event_func(Event('sasl-auth', authenticator=self, has_initial_response=False, initial_response=None, xml=auth)) else: self._event_func(Event('sasl-auth', authenticator=self, has_initial_response=True, initial_response=b64decode(str(auth)), xml=auth)) def _response(self, response): self._event_func(Event('sasl-response', authenticator=self, response=b64decode(str(response)), xml=response)) def _abort(self, abort): self._event_func(Event('sasl-abort', authenticator=self, xml=abort)) def connect_and_get_sasl_channel(q, bus, conn): conn.Connect() q.expect('dbus-signal', signal='StatusChanged', args=[cs.CONN_STATUS_CONNECTING, cs.CSR_REQUESTED]) return expect_sasl_channel(q, bus, conn) def expect_sasl_channel(q, bus, conn): old_signal, new_signal = q.expect_many( EventPattern('dbus-signal', signal='NewChannel', predicate=lambda e: e.args[1] == cs.CHANNEL_TYPE_SERVER_AUTHENTICATION), EventPattern('dbus-signal', signal='NewChannels', predicate=lambda e: e.args[0][0][1].get(cs.CHANNEL_TYPE) == cs.CHANNEL_TYPE_SERVER_AUTHENTICATION), ) path, type, handle_type, handle, suppress_handler = old_signal.args chan = SaslChannelWrapper(bus.get_object(conn.bus_name, path)) assertLength(1, new_signal.args[0]) assertEquals(path, new_signal.args[0][0][0]) props = new_signal.args[0][0][1] assertEquals(cs.CHANNEL_IFACE_SASL_AUTH, props.get(cs.AUTH_METHOD)) return chan, props def abort_auth(q, chan, reason, message): reason_err_map = { cs.SASL_ABORT_REASON_USER_ABORT : cs.CANCELLED, cs.SASL_ABORT_REASON_INVALID_CHALLENGE : cs.SERVICE_CONFUSED } mapped_error = reason_err_map.get(reason, cs.CANCELLED) chan.SASLAuthentication.AbortSASL(reason, message) ssc, ce, _ = q.expect_many( EventPattern( 'dbus-signal', signal='SASLStatusChanged', interface=cs.CHANNEL_IFACE_SASL_AUTH, predicate=lambda e: e.args[0] == cs.SASL_STATUS_CLIENT_FAILED), EventPattern('dbus-signal', signal='ConnectionError'), EventPattern( 'dbus-signal', signal="StatusChanged", args=[cs.CONN_STATUS_DISCONNECTED, cs.CSR_AUTHENTICATION_FAILED])) assertEquals(cs.SASL_STATUS_CLIENT_FAILED, ssc.args[0]) assertEquals(mapped_error, ssc.args[1]) assertEquals(message, ssc.args[2].get('debug-message')), assertEquals(mapped_error, ce.args[0]) telepathy-gabble-0.18.2/tests/twisted/sasl/telepathy-password.py0000644000175000017500000000376112227000321025011 0ustar00smcvsmcv00000000000000""" Test the server sasl channel with the X-TELEPATHY-PASSWORD mechanism. """ from servicetest import call_async from gabbletest import exec_test import constants as cs from saslutil import connect_and_get_sasl_channel PASSWORD = "pass" def test_close_straight_after_accept(q, bus, conn, stream): chan, props = connect_and_get_sasl_channel(q, bus, conn) call_async(q, chan.SASLAuthentication, 'StartMechanismWithData', 'X-TELEPATHY-PASSWORD', PASSWORD) # In_Progress appears before StartMechanismWithData returns q.expect('dbus-signal', signal='SASLStatusChanged', interface=cs.CHANNEL_IFACE_SASL_AUTH, args=[cs.SASL_STATUS_IN_PROGRESS, '', {}]) q.expect('dbus-return', method='StartMechanismWithData') q.expect('dbus-signal', signal='SASLStatusChanged', interface=cs.CHANNEL_IFACE_SASL_AUTH, args=[cs.SASL_STATUS_SERVER_SUCCEEDED, '', {}]) # fd.o#32278: # When this was breaking, gabble received AcceptSASL and told the # success_async GAsyncResult to complete in an idle. But, before # the result got its callback called, Close was also received and # the auth manager cleared its channel. When the idle function was # finally reached it saw it no longer had a channel (it had been # cleared in the closed callback) and thought it should be # chaining up to the wocky auth registry but of course it should # be calling the channel finish function. call_async(q, chan.SASLAuthentication, 'AcceptSASL') call_async(q, chan, 'Close') q.expect('dbus-signal', signal='SASLStatusChanged', interface=cs.CHANNEL_IFACE_SASL_AUTH, args=[cs.SASL_STATUS_SUCCEEDED, '', {}]) e = q.expect('dbus-signal', signal='StatusChanged', args=[cs.CONN_STATUS_CONNECTED, cs.CSR_REQUESTED]) if __name__ == '__main__': exec_test(test_close_straight_after_accept, {'password': None, 'account' : "test@example.org"}, do_connect=False) telepathy-gabble-0.18.2/tests/twisted/sasl/plain.py0000644000175000017500000002150712227000321022253 0ustar00smcvsmcv00000000000000""" Test the server sasl channel with the PLAIN mechanism """ from twisted.words.xish import domish from twisted.words.protocols.jabber import xmlstream from base64 import b64decode import dbus from servicetest import EventPattern, assertEquals, assertContains, call_async from gabbletest import exec_test import constants as cs from saslutil import SaslEventAuthenticator, connect_and_get_sasl_channel, \ abort_auth JID = "test@example.org" PASSWORD = "pass" INITIAL_RESPONSE = '\0' + JID.split('@')[0] + '\0' + PASSWORD def test_plain_success(q, bus, conn, stream): chan, props = connect_and_get_sasl_channel(q, bus, conn) assertEquals(JID, props.get(cs.SASL_AUTHORIZATION_IDENTITY)) # On some servers we can't do DIGEST auth without this information. assertEquals('example.org', props.get(cs.SASL_DEFAULT_REALM)) # We can't necessarily do PLAIN auth without this information. assertEquals('test', props.get(cs.SASL_DEFAULT_USERNAME)) chan.SASLAuthentication.StartMechanismWithData('PLAIN', INITIAL_RESPONSE) e, _ = q.expect_many( EventPattern('sasl-auth', initial_response=INITIAL_RESPONSE), EventPattern('dbus-signal', signal='SASLStatusChanged', interface=cs.CHANNEL_IFACE_SASL_AUTH, args=[cs.SASL_STATUS_IN_PROGRESS, '', {}]), ) authenticator = e.authenticator authenticator.success(None) q.expect('dbus-signal', signal='SASLStatusChanged', interface=cs.CHANNEL_IFACE_SASL_AUTH, args=[cs.SASL_STATUS_SERVER_SUCCEEDED, '', {}]) chan.SASLAuthentication.AcceptSASL() q.expect('dbus-signal', signal='SASLStatusChanged', interface=cs.CHANNEL_IFACE_SASL_AUTH, args=[cs.SASL_STATUS_SUCCEEDED, '', {}]) e = q.expect('dbus-signal', signal='StatusChanged', args=[cs.CONN_STATUS_CONNECTED, cs.CSR_REQUESTED]) def test_plain_no_account(q, bus, conn, stream): chan, props = connect_and_get_sasl_channel(q, bus, conn) assertEquals('example.com', props.get(cs.SASL_DEFAULT_REALM)) assertEquals('', props.get(cs.SASL_DEFAULT_USERNAME)) chan.SASLAuthentication.StartMechanismWithData('PLAIN', INITIAL_RESPONSE) e, _ = q.expect_many( EventPattern('sasl-auth', initial_response=INITIAL_RESPONSE), EventPattern('dbus-signal', signal='SASLStatusChanged', interface=cs.CHANNEL_IFACE_SASL_AUTH, args=[cs.SASL_STATUS_IN_PROGRESS, '', {}]), ) authenticator = e.authenticator authenticator.success(None) q.expect('dbus-signal', signal='SASLStatusChanged', interface=cs.CHANNEL_IFACE_SASL_AUTH, args=[cs.SASL_STATUS_SERVER_SUCCEEDED, '', {}]) chan.SASLAuthentication.AcceptSASL() q.expect('dbus-signal', signal='SASLStatusChanged', interface=cs.CHANNEL_IFACE_SASL_AUTH, args=[cs.SASL_STATUS_SUCCEEDED, '', {}]) e = q.expect('dbus-signal', signal='StatusChanged', args=[cs.CONN_STATUS_CONNECTED, cs.CSR_REQUESTED]) def test_plain_fail_helper(q, bus, conn, stream, element, error, csr): chan, props = connect_and_get_sasl_channel(q, bus, conn) chan.SASLAuthentication.StartMechanismWithData('PLAIN', INITIAL_RESPONSE) e, _ = q.expect_many( EventPattern('sasl-auth', initial_response=INITIAL_RESPONSE), EventPattern('dbus-signal', signal='SASLStatusChanged', interface=cs.CHANNEL_IFACE_SASL_AUTH, args=[cs.SASL_STATUS_IN_PROGRESS, '', {}]), ) authenticator = e.authenticator authenticator.failure(element) e = q.expect('dbus-signal', signal='SASLStatusChanged', interface=cs.CHANNEL_IFACE_SASL_AUTH) assertEquals([cs.SASL_STATUS_SERVER_FAILED, error], e.args[:2]) assertContains('debug-message', e.args[2]) e = q.expect('dbus-signal', signal='ConnectionError') assertEquals(error, e.args[0]) q.expect('dbus-signal', signal='StatusChanged', args=[cs.CONN_STATUS_DISCONNECTED, csr]) def test_plain_fail(q, bus, conn, stream): test_plain_fail_helper(q, bus, conn, stream, 'not-authorized', cs.AUTHENTICATION_FAILED, cs.CSR_AUTHENTICATION_FAILED) def test_plain_bad_encoding(q, bus, conn, stream): test_plain_fail_helper(q, bus, conn, stream, 'incorrect-encoding', cs.AUTHENTICATION_FAILED, cs.CSR_AUTHENTICATION_FAILED) def test_plain_weak(q, bus, conn, stream): test_plain_fail_helper(q, bus, conn, stream, 'mechanism-too-weak', cs.AUTHENTICATION_FAILED, cs.CSR_AUTHENTICATION_FAILED) def test_plain_bad_authzid(q, bus, conn, stream): test_plain_fail_helper(q, bus, conn, stream, 'invalid-authzid', cs.AUTHENTICATION_FAILED, cs.CSR_AUTHENTICATION_FAILED) def test_plain_bad_mech(q, bus, conn, stream): test_plain_fail_helper(q, bus, conn, stream, 'invalid-mechanism', cs.AUTHENTICATION_FAILED, cs.CSR_AUTHENTICATION_FAILED) def test_plain_tempfail(q, bus, conn, stream): test_plain_fail_helper(q, bus, conn, stream, 'temporary-failure', cs.AUTHENTICATION_FAILED, cs.CSR_AUTHENTICATION_FAILED) def test_plain_abort(q, bus, conn, stream): chan, props = connect_and_get_sasl_channel(q, bus, conn) chan.SASLAuthentication.StartMechanismWithData('PLAIN', INITIAL_RESPONSE) e, _ = q.expect_many( EventPattern('sasl-auth', initial_response=INITIAL_RESPONSE), EventPattern('dbus-signal', signal='SASLStatusChanged', interface=cs.CHANNEL_IFACE_SASL_AUTH, args=[cs.SASL_STATUS_IN_PROGRESS, '', {}]), ) authenticator = e.authenticator authenticator.success(None) q.expect('dbus-signal', signal='SASLStatusChanged', interface=cs.CHANNEL_IFACE_SASL_AUTH, args=[cs.SASL_STATUS_SERVER_SUCCEEDED, '', {}]) abort_auth(q, chan, cs.SASL_ABORT_REASON_INVALID_CHALLENGE, "Something is fishy") def test_bad_usage(q, bus, conn, stream): chan, props = connect_and_get_sasl_channel(q, bus, conn) call_async(q, chan.SASLAuthentication, 'Respond', 'This is uncalled for') q.expect('dbus-error', method='Respond', name=cs.NOT_AVAILABLE) chan.SASLAuthentication.StartMechanismWithData('PLAIN', INITIAL_RESPONSE) e, _ = q.expect_many( EventPattern('sasl-auth', initial_response=INITIAL_RESPONSE), EventPattern('dbus-signal', signal='SASLStatusChanged', interface=cs.CHANNEL_IFACE_SASL_AUTH, args=[cs.SASL_STATUS_IN_PROGRESS, '', {}]), ) authenticator = e.authenticator call_async(q, chan.SASLAuthentication, 'StartMechanismWithData', 'PLAIN', 'foo') q.expect('dbus-error', method='StartMechanismWithData', name=cs.NOT_AVAILABLE) authenticator.success(None) q.expect('dbus-signal', signal='SASLStatusChanged', interface=cs.CHANNEL_IFACE_SASL_AUTH, args=[cs.SASL_STATUS_SERVER_SUCCEEDED, '', {}]) call_async(q, chan.SASLAuthentication, 'Respond', 'Responding after success') q.expect('dbus-error', method='Respond', name=cs.NOT_AVAILABLE) if __name__ == '__main__': exec_test( test_plain_success, {'password': None, 'account' : JID}, authenticator=SaslEventAuthenticator('test', ['PLAIN']), do_connect=False) exec_test( test_plain_no_account, {'password': None, 'account' : 'example.com'}, authenticator=SaslEventAuthenticator('test', ['PLAIN']), do_connect=False) exec_test( test_plain_fail, {'password': None, 'account' : JID}, authenticator=SaslEventAuthenticator('test', ['PLAIN']), do_connect=False) exec_test( test_plain_bad_encoding, {'password': None, 'account' : JID}, authenticator=SaslEventAuthenticator('test', ['PLAIN']), do_connect=False) exec_test( test_plain_weak, {'password': None, 'account' : JID}, authenticator=SaslEventAuthenticator('test', ['PLAIN']), do_connect=False) exec_test( test_plain_bad_authzid, {'password': None, 'account' : JID}, authenticator=SaslEventAuthenticator('test', ['PLAIN']), do_connect=False) exec_test( test_plain_bad_mech, {'password': None, 'account' : JID}, authenticator=SaslEventAuthenticator('test', ['PLAIN']), do_connect=False) exec_test( test_plain_tempfail, {'password': None, 'account' : JID}, authenticator=SaslEventAuthenticator('test', ['PLAIN']), do_connect=False) exec_test( test_plain_abort, {'password': None, 'account' : JID}, authenticator=SaslEventAuthenticator('test', ['PLAIN']), do_connect=False) exec_test( test_bad_usage, {'password': None, 'account' : JID}, authenticator=SaslEventAuthenticator('test', ['PLAIN']), do_connect=False) telepathy-gabble-0.18.2/tests/twisted/sasl/jabber_auth.py0000644000175000017500000001045312227000321023414 0ustar00smcvsmcv00000000000000""" Test the server sasl channel with Jabber auth pseudomechanisms """ from twisted.words.xish import domish from twisted.words.protocols.jabber import xmlstream from twisted.words.protocols.jabber.client import IQ import hashlib import dbus from servicetest import (EventPattern, assertEquals, assertSameSets, assertContains) from gabbletest import exec_test, JabberXmlStream, JabberAuthenticator import constants as cs import ns from saslutil import expect_sasl_channel, abort_auth JID = "test@localhost" PASSWORD = "pass" def test_jabber_pass_success(q, bus, conn, stream): conn.Connect() q.expect('dbus-signal', signal='StatusChanged', args=[cs.CONN_STATUS_CONNECTING, cs.CSR_REQUESTED]) e = q.expect('auth-initial-iq') authenticator = e.authenticator authenticator.respondToInitialIq(e.iq) chan, props = expect_sasl_channel(q, bus, conn) assertSameSets(['X-TELEPATHY-PASSWORD'], props.get(cs.SASL_AVAILABLE_MECHANISMS)) chan.SASLAuthentication.StartMechanismWithData('X-TELEPATHY-PASSWORD', PASSWORD) e = q.expect('auth-second-iq') authenticator.respondToSecondIq(e.iq) q.expect('dbus-signal', signal='SASLStatusChanged', interface=cs.CHANNEL_IFACE_SASL_AUTH, args=[cs.SASL_STATUS_SERVER_SUCCEEDED, '', {}]) chan.SASLAuthentication.AcceptSASL() q.expect('dbus-signal', signal='SASLStatusChanged', interface=cs.CHANNEL_IFACE_SASL_AUTH, args=[cs.SASL_STATUS_SUCCEEDED, '', {}]) e = q.expect('dbus-signal', signal='StatusChanged', args=[cs.CONN_STATUS_CONNECTED, cs.CSR_REQUESTED]) CODES = { 'not-authorized': 401, 'conflict': 409, 'not-acceptable': 406 } TYPES = { 'not-authorized': 'auth', 'conflict': 'cancel', 'not-acceptable': 'modify' } CSRS = { 'not-authorized': cs.CSR_AUTHENTICATION_FAILED, 'conflict': cs.CSR_NAME_IN_USE, 'not-acceptable': cs.CSR_AUTHENTICATION_FAILED } ERRORS = { 'not-authorized': cs.AUTHENTICATION_FAILED, 'conflict': cs.ALREADY_CONNECTED, 'not-acceptable': cs.AUTHENTICATION_FAILED } def test_jabber_pass_fail(q, bus, conn, stream, which): conn.Connect() q.expect('dbus-signal', signal='StatusChanged', args=[cs.CONN_STATUS_CONNECTING, cs.CSR_REQUESTED]) e = q.expect('auth-initial-iq') authenticator = e.authenticator authenticator.respondToInitialIq(e.iq) chan, props = expect_sasl_channel(q, bus, conn) assertSameSets(['X-TELEPATHY-PASSWORD'], props.get(cs.SASL_AVAILABLE_MECHANISMS)) chan.SASLAuthentication.StartMechanismWithData('X-TELEPATHY-PASSWORD', PASSWORD) e = q.expect('auth-second-iq') result = IQ(stream, 'error') result['id'] = e.id error = result.addElement('error') error['code'] = str(CODES[which]) error['type'] = TYPES[which] error.addElement((ns.STANZA, which)) stream.send(result) e = q.expect('dbus-signal', signal='SASLStatusChanged', interface=cs.CHANNEL_IFACE_SASL_AUTH, predicate=lambda e: e.args[0] == cs.SASL_STATUS_SERVER_FAILED) assertEquals(ERRORS[which], e.args[1]) assertContains('debug-message', e.args[2]) e = q.expect('dbus-signal', signal='ConnectionError') assertEquals(ERRORS[which], e.args[0]) assertContains('debug-message', e.args[1]) e = q.expect('dbus-signal', signal='StatusChanged') assertEquals(cs.CONN_STATUS_DISCONNECTED, e.args[0]) assertEquals(CSRS[which], e.args[1]) def test_jabber_pass_not_authorized(q, bus, conn, stream): test_jabber_pass_fail(q, bus, conn, stream, 'not-authorized') def test_jabber_pass_conflict(q, bus, conn, stream): test_jabber_pass_fail(q, bus, conn, stream, 'conflict') def test_jabber_pass_not_acceptable(q, bus, conn, stream): test_jabber_pass_fail(q, bus, conn, stream, 'not-acceptable') if __name__ == '__main__': for test in ( # these are Examples 5 to 8 of XEP-0078 test_jabber_pass_success, test_jabber_pass_not_authorized, test_jabber_pass_conflict, test_jabber_pass_not_acceptable): exec_test( test, {'password': None, 'account' : JID}, protocol=JabberXmlStream, authenticator=JabberAuthenticator(JID.split('@')[0], PASSWORD, emit_events=True), do_connect=False) telepathy-gabble-0.18.2/tests/twisted/sasl/complex.py0000644000175000017500000001277312227000321022624 0ustar00smcvsmcv00000000000000""" Test the server sasl channel with the PLAIN mechanism """ import dbus from servicetest import EventPattern, assertEquals, assertSameSets, call_async from gabbletest import exec_test import constants as cs from saslutil import SaslEventAuthenticator, connect_and_get_sasl_channel JID = "test@example.org" INITIAL_RESPONSE = 'Thunder and lightning. Enter three Witches.' CR_PAIRS = [ ('When shall we three meet again?', 'Ere the set of sun.'), ('Where the place?', 'Upon the heath.'), ] SUCCESS_DATA = 'Exeunt.' MECHANISMS = ["PLAIN", "DIGEST-MD5", "SCOTTISH-PLAY"] def test_complex_success(q, bus, conn, stream, with_extra_data=True, accept_early=False): chan, props = connect_and_get_sasl_channel(q, bus, conn) assertSameSets(MECHANISMS + ['X-TELEPATHY-PASSWORD'], props.get(cs.SASL_AVAILABLE_MECHANISMS)) call_async(q, chan.SASLAuthentication, 'StartMechanismWithData', "FOO", "") q.expect('dbus-error', method='StartMechanismWithData', name=cs.NOT_IMPLEMENTED) if with_extra_data: chan.SASLAuthentication.StartMechanismWithData("SCOTTISH-PLAY", INITIAL_RESPONSE) e = q.expect('sasl-auth', initial_response=INITIAL_RESPONSE) else: chan.SASLAuthentication.StartMechanism("SCOTTISH-PLAY") e = q.expect('sasl-auth', has_initial_response=False) authenticator = e.authenticator q.expect('dbus-signal', signal='SASLStatusChanged', interface=cs.CHANNEL_IFACE_SASL_AUTH, args=[cs.SASL_STATUS_IN_PROGRESS, '', {}]) if not with_extra_data: # send the stage directions in-band instead authenticator.challenge('') e = q.expect('dbus-signal', signal='NewChallenge', interface=cs.CHANNEL_IFACE_SASL_AUTH) # this ought to be '' but dbus-python has fd.o #28131 assert e.args in ([''], ['None']) chan.SASLAuthentication.Respond(INITIAL_RESPONSE) q.expect('sasl-response', response=INITIAL_RESPONSE) for challenge, response in CR_PAIRS: authenticator.challenge(challenge) q.expect('dbus-signal', signal='NewChallenge', interface=cs.CHANNEL_IFACE_SASL_AUTH, args=[challenge]) chan.SASLAuthentication.Respond(response) q.expect('sasl-response', response=response) if with_extra_data: authenticator.success(SUCCESS_DATA) else: # The success data is sent in-band as a challenge authenticator.challenge(SUCCESS_DATA) q.expect('dbus-signal', signal='NewChallenge', interface=cs.CHANNEL_IFACE_SASL_AUTH, args=[SUCCESS_DATA]) if accept_early: # the UI can tell that this challenge isn't actually a challenge, # it's a success in disguise chan.SASLAuthentication.AcceptSASL() q.expect('dbus-signal', signal='SASLStatusChanged', interface=cs.CHANNEL_IFACE_SASL_AUTH, args=[cs.SASL_STATUS_CLIENT_ACCEPTED, '', {}]) else: chan.SASLAuthentication.Respond(dbus.ByteArray('')) if with_extra_data: # Wocky removes the distinction between a challenge containing # success data followed by a plain success, and a success # containing initial data, so we won't get to Server_Succeeded # til we "respond" to the "challenge". However, at the XMPP level, # we shouldn't get a response to a success. q.forbid_events([EventPattern('sasl-response')]) else: q.expect('sasl-response', response='') authenticator.success(None) if not accept_early: # *now* we accept q.expect('dbus-signal', signal='SASLStatusChanged', interface=cs.CHANNEL_IFACE_SASL_AUTH, args=[cs.SASL_STATUS_SERVER_SUCCEEDED, '', {}]) # We're willing to accept this SASL transaction chan.SASLAuthentication.AcceptSASL() q.expect('dbus-signal', signal='SASLStatusChanged', interface=cs.CHANNEL_IFACE_SASL_AUTH, args=[cs.SASL_STATUS_SUCCEEDED, '', {}]) q.expect('dbus-signal', signal='StatusChanged', args=[cs.CONN_STATUS_CONNECTED, cs.CSR_REQUESTED]) chan.Close() # ... and check that the Connection is still OK conn.GetSelfHandle() def test_complex_success_data(q, bus, conn, stream): test_complex_success(q, bus, conn, stream, True) def test_complex_success_no_data(q, bus, conn, stream): test_complex_success(q, bus, conn, stream, False) def test_complex_success_data_accept(q, bus, conn, stream): test_complex_success(q, bus, conn, stream, True, True) def test_complex_success_no_data_accept(q, bus, conn, stream): test_complex_success(q, bus, conn, stream, False, True) if __name__ == '__main__': exec_test( test_complex_success_data, {'password': None, 'account' : JID}, authenticator=SaslEventAuthenticator(JID.split('@')[0], MECHANISMS), do_connect=False) exec_test( test_complex_success_no_data, {'password': None, 'account' : JID}, authenticator=SaslEventAuthenticator(JID.split('@')[0], MECHANISMS), do_connect=False) exec_test( test_complex_success_data_accept, {'password': None, 'account' : JID}, authenticator=SaslEventAuthenticator(JID.split('@')[0], MECHANISMS), do_connect=False) exec_test( test_complex_success_no_data_accept, {'password': None, 'account' : JID}, authenticator=SaslEventAuthenticator(JID.split('@')[0], MECHANISMS), do_connect=False) telepathy-gabble-0.18.2/tests/twisted/sasl/close.py0000644000175000017500000000212112227000321022244 0ustar00smcvsmcv00000000000000"""Test the SASL channel being undispatchable.""" import dbus from servicetest import EventPattern from gabbletest import exec_test import constants as cs from saslutil import connect_and_get_sasl_channel JID = 'weaver@crobuzon.fic' def test_no_password(q, bus, conn, stream): chan, props = connect_and_get_sasl_channel(q, bus, conn) chan.Close() q.expect_many( EventPattern('dbus-signal', path=chan.object_path, signal='Closed'), EventPattern('dbus-signal', path=conn.object_path, signal='ChannelClosed', args=[chan.object_path]), EventPattern('dbus-signal', path=conn.object_path, signal='ConnectionError', predicate=lambda e: e.args[0] == cs.AUTHENTICATION_FAILED), EventPattern('dbus-signal', path=conn.object_path, signal='StatusChanged', args=[cs.CONN_STATUS_DISCONNECTED, cs.CSR_AUTHENTICATION_FAILED]), ) if __name__ == '__main__': exec_test(test_no_password, {'password': None,'account' : JID}, do_connect=False) telepathy-gabble-0.18.2/tests/twisted/sasl/abort.py0000644000175000017500000001726512227000321022265 0ustar00smcvsmcv00000000000000""" Test the server sasl aborting at different stages """ import dbus from servicetest import EventPattern, assertEquals from gabbletest import exec_test, call_async import constants as cs from saslutil import SaslEventAuthenticator, connect_and_get_sasl_channel, \ abort_auth JID = "test@example.org" PASSWORD = "pass" EXCHANGE = [("", "remote challenge"), ("Another step", "Here we go"), ("local response", "")] MECHANISMS = ["PLAIN", "DIGEST-MD5", "ABORT-TEST"] def test_abort_early(q, bus, conn, stream): chan, props = connect_and_get_sasl_channel(q, bus, conn) abort_auth(q, chan, 31337, "maybe if I use an undefined code you'll crash") def start_mechanism(q, bus, conn, mechanism="ABORT-TEST", initial_response=EXCHANGE[0][1]): chan, props = connect_and_get_sasl_channel(q, bus, conn) chan.SASLAuthentication.StartMechanismWithData(mechanism, initial_response) q.expect('dbus-signal', signal='SASLStatusChanged', interface=cs.CHANNEL_IFACE_SASL_AUTH, args=[cs.SASL_STATUS_IN_PROGRESS, '', {}]) e = q.expect('sasl-auth', initial_response=initial_response) return chan, e.authenticator def test_abort_mid(q, bus, conn, stream): chan, authenticator = start_mechanism(q, bus, conn) authenticator.challenge(EXCHANGE[1][0]) q.expect('dbus-signal', signal='NewChallenge', interface=cs.CHANNEL_IFACE_SASL_AUTH, args=[EXCHANGE[1][0]]) abort_auth(q, chan, cs.SASL_ABORT_REASON_INVALID_CHALLENGE, "wrong data from server") def test_disconnect_mid(q, bus, conn, stream): chan, authenticator = start_mechanism(q, bus, conn) authenticator.challenge(EXCHANGE[1][0]) q.expect('dbus-signal', signal='NewChallenge', interface=cs.CHANNEL_IFACE_SASL_AUTH, args=[EXCHANGE[1][0]]) call_async(q, conn, 'Disconnect') q.expect_many(EventPattern('dbus-signal', signal='StatusChanged', args=[cs.CONN_STATUS_DISCONNECTED, cs.CSR_REQUESTED]), EventPattern('dbus-return', method='Disconnect')) def test_abort_connected(q, bus, conn, stream): initial_response = '\0' + JID.split('@')[0] + '\0' + PASSWORD chan, authenticator = start_mechanism(q, bus, conn, mechanism='PLAIN', initial_response=initial_response) authenticator.success(None) q.expect('dbus-signal', signal='SASLStatusChanged', interface=cs.CHANNEL_IFACE_SASL_AUTH, args=[cs.SASL_STATUS_SERVER_SUCCEEDED, '', {}]) chan.SASLAuthentication.AcceptSASL() q.expect('dbus-signal', signal='SASLStatusChanged', interface=cs.CHANNEL_IFACE_SASL_AUTH, args=[cs.SASL_STATUS_SUCCEEDED, '', {}]) q.expect('dbus-signal', signal='StatusChanged', args=[cs.CONN_STATUS_CONNECTED, cs.CSR_REQUESTED]) call_async(q, chan.SASLAuthentication, 'AbortSASL', cs.SASL_ABORT_REASON_USER_ABORT, "aborting too late") q.expect('dbus-error', method='AbortSASL', name=cs.NOT_AVAILABLE) chan.Close() def test_give_up_while_waiting(q, bus, conn, stream, channel_method, authenticator_method): """Regression test for https://bugs.freedesktop.org/show_bug.cgi?id=52146 where closing the auth channel while waiting for a challenge from the server would not abort the SASL exchange, and the challenge subsequently arriving would make Gabble assert. """ chan, authenticator = start_mechanism(q, bus, conn) # While Gabble is waiting for waiting for the server to make the next move, # a Telepathy client does something to try to end the authentication # process. channel_method(chan) # And then we hear something back from the server. authenticator_method(authenticator) # FIXME: Gabble should probably send and wait for the server to # say rather than unceremoniously closing the # connection. # # In the bug we're testing for, the stream connection would indeed be lost, # but Gabble would also crash and leave a core dump behind. So this test # would appear to pass, but 'make check' would fail as we want. q.expect_many( EventPattern('stream-connection-lost'), EventPattern('dbus-signal', signal='ConnectionError'), EventPattern( 'dbus-signal', signal="StatusChanged", args=[cs.CONN_STATUS_DISCONNECTED, cs.CSR_AUTHENTICATION_FAILED]), ) def test_close_then_challenge(q, bus, conn, stream): """This is the specific scenario for which 52146 was reported. The channel was being closed because its handler crashed. """ test_give_up_while_waiting(q, bus, conn, stream, lambda chan: chan.Close(), lambda authenticator: authenticator.challenge(EXCHANGE[1][0])) def test_close_then_success(q, bus, conn, stream): test_give_up_while_waiting(q, bus, conn, stream, lambda chan: chan.Close(), lambda authenticator: authenticator.success()) def test_close_then_failure(q, bus, conn, stream): test_give_up_while_waiting(q, bus, conn, stream, lambda chan: chan.Close(), lambda authenticator: authenticator.not_authorized()) def test_abort_then_challenge(q, bus, conn, stream): test_give_up_while_waiting(q, bus, conn, stream, lambda chan: chan.SASLAuthentication.AbortSASL( cs.SASL_ABORT_REASON_USER_ABORT, "bored now"), lambda authenticator: authenticator.challenge(EXCHANGE[1][0])) def test_abort_then_success(q, bus, conn, stream): """FIXME: this test fails because the channel changes its state from TP_SASL_STATUS_CLIENT_FAILED to TP_SASL_STATUS_SERVER_SUCCEEDED and waits for the client to ack it when arrives, which is dumb. """ test_give_up_while_waiting(q, bus, conn, stream, lambda chan: chan.SASLAuthentication.AbortSASL( cs.SASL_ABORT_REASON_USER_ABORT, "bored now"), lambda authenticator: authenticator.success()) def test_abort_then_failure(q, bus, conn, stream): test_give_up_while_waiting(q, bus, conn, stream, lambda chan: chan.SASLAuthentication.AbortSASL( cs.SASL_ABORT_REASON_USER_ABORT, "bored now"), lambda authenticator: authenticator.not_authorized()) def abort_and_close(chan): chan.SASLAuthentication.AbortSASL( cs.SASL_ABORT_REASON_USER_ABORT, "bored now") chan.Close() def test_abort_and_close_then_challenge(q, bus, conn, stream): test_give_up_while_waiting(q, bus, conn, stream, abort_and_close, lambda authenticator: authenticator.challenge(EXCHANGE[1][0])) def test_abort_and_close_then_failure(q, bus, conn, stream): test_give_up_while_waiting(q, bus, conn, stream, abort_and_close, lambda authenticator: authenticator.not_authorized()) def exec_test_(func): # Can't use functools.partial, because the authenticator is stateful. authenticator = SaslEventAuthenticator(JID.split('@')[0], MECHANISMS) exec_test(func, do_connect=False, authenticator=authenticator, params={'password': None, 'account' : JID, }) if __name__ == '__main__': exec_test_(test_abort_early) exec_test_(test_abort_mid) exec_test_(test_disconnect_mid) exec_test_(test_abort_connected) exec_test_(test_close_then_challenge) exec_test_(test_close_then_success) exec_test_(test_close_then_failure) exec_test_(test_abort_then_challenge) # exec_test_(test_abort_then_success) exec_test_(test_abort_then_failure) exec_test_(test_abort_and_close_then_challenge) exec_test_(test_abort_and_close_then_failure) telepathy-gabble-0.18.2/tests/twisted/roster/0000755000175000017500000000000012312537051021157 5ustar00smcvsmcv00000000000000telepathy-gabble-0.18.2/tests/twisted/roster/test-save-alias-to-roster.py0000644000175000017500000001576512227000321026473 0ustar00smcvsmcv00000000000000 """ Test that updating an alias saves it to the roster. """ import dbus from servicetest import EventPattern, call_async, assertEquals from gabbletest import ( acknowledge_iq, exec_test, make_result_iq, sync_stream, elem ) import constants as cs import ns from rostertest import expect_contact_list_signals, send_roster_push from pubsub import make_pubsub_event def send_pep_nick_reply(stream, stanza, nickname): result = make_result_iq(stream, stanza) pubsub = result.firstChildElement() items = pubsub.addElement('items') items['node'] = ns.NICK item = items.addElement('item') item.addElement('nick', ns.NICK, content=nickname) stream.send(result) def check_roster_write(stream, event, jid, name): item = event.query.firstChildElement() assertEquals(jid, item['jid']) # This copes with name=None assertEquals(name, item.getAttribute('name')) acknowledge_iq(stream, event.stanza) # RFC 3921 requires the server to send a roster push to all connected # resources whenever a resource updates the roster. Gabble depends on this # and pays no attention to its own nick update until the server sends a # push. send_roster_push(stream, jid, 'none', name=name) def expect_AliasesChanged_and_roster_write(q, stream, handle, jid, nick): roster_write = q.expect('stream-iq', iq_type='set', query_ns=ns.ROSTER) check_roster_write(stream, roster_write, jid, nick) q.expect('dbus-signal', signal='AliasesChanged', args=[[(handle, nick if nick else jid)]]) def test(q, bus, conn, stream): event, event2 = q.expect_many( EventPattern('stream-iq', to=None, query_ns='vcard-temp', query_name='vCard'), EventPattern('stream-iq', query_ns=ns.ROSTER)) acknowledge_iq(stream, event.stanza) acknowledge_iq(stream, event2.stanza) signals = expect_contact_list_signals(q, bus, conn, lists=['subscribe']) old_signal, new_signal = signals[0] path = old_signal.args[0] # request subscription chan = bus.get_object(conn.bus_name, path) group_iface = dbus.Interface(chan, cs.CHANNEL_IFACE_GROUP) assert group_iface.GetMembers() == [] handle = conn.RequestHandles(1, ['bob@foo.com'])[0] call_async(q, group_iface, 'AddMembers', [handle], '') event = q.expect('stream-iq', iq_type='set', query_ns=ns.ROSTER) item = event.query.firstChildElement() acknowledge_iq(stream, event.stanza) q.expect('dbus-return', method='AddMembers') call_async(q, conn.Aliasing, 'RequestAliases', [handle]) event = q.expect('stream-iq', iq_type='get', query_ns='http://jabber.org/protocol/pubsub', to='bob@foo.com') send_pep_nick_reply(stream, event.stanza, 'Bobby') event, _ = q.expect_many( EventPattern('stream-iq', iq_type='set', query_ns=ns.ROSTER), EventPattern('dbus-return', method='RequestAliases', value=(['Bobby'],))) check_roster_write(stream, event, 'bob@foo.com', 'Bobby') # We get a roster push for a contact who for some reason has their alias # set on our roster to the empty string (maybe a buggy client?). It's never # useful for Gabble to say that someone's alias is the empty string (given # the current semantics where the alias is always meant to be something you # could show, even if it's just their JID), so let's forbid that. jid = 'parts@labor.lit' handle = conn.RequestHandles(cs.HT_CONTACT, [jid])[0] q.forbid_events([EventPattern('dbus-signal', signal='AliasesChanged', args=[[(handle, '')]])]) send_roster_push(stream, jid, 'both', name='') # I don't really have very strong opinions on whether Gabble should be # signalling that this contact's alias has *changed* per se, so am not # explicitly expecting that. q.expect('dbus-signal', signal='MembersChanged') # But if we ask for it, Gabble should probably send a PEP query. assertEquals(jid, conn.Aliasing.GetAliases([handle])[handle]) event = q.expect('stream-iq', iq_type='get', query_ns=ns.PUBSUB, to=jid) nick = 'Constant Future' send_pep_nick_reply(stream, event.stanza, nick) expect_AliasesChanged_and_roster_write(q, stream, handle, jid, nick) # Here's another contact, whose alias is set on our roster to their JID: # because we've cached that they have no alias. Gabble shouldn't make # unsolicited PEP or vCard queries to them. jid = 'friendly@faith.plate' handle = conn.RequestHandles(cs.HT_CONTACT, [jid])[0] q.forbid_events([ EventPattern('stream-iq', query_ns=ns.PUBSUB, to=jid), EventPattern('stream-iq', query_ns=ns.VCARD_TEMP, to=jid), ]) send_roster_push(stream, jid, 'both', name=jid) q.expect('dbus-signal', signal='AliasesChanged', args=[[(handle, jid)]]) sync_stream(q, stream) # But if we get a PEP nickname update for this contact, Gabble should use # the new nickname, and write it back to the roster. nick = u'The Friendly Faith Plate' stream.send(make_pubsub_event(jid, ns.NICK, elem(ns.NICK, 'nick')(nick))) expect_AliasesChanged_and_roster_write(q, stream, handle, jid, nick) # As an undocumented extension, we treat setting the alias to the empty # string to mean "whatever the contact says their nickname is". (The rest # of this test is a regression test for # .) # # So first up, let's change the Friendly Faith Plate's nickname to # something else. custom_nick = u'I saw a deer today' conn.Aliasing.SetAliases({handle: custom_nick}) expect_AliasesChanged_and_roster_write(q, stream, handle, jid, custom_nick) assertEquals([custom_nick], conn.Aliasing.RequestAliases([handle])) # And now set it to the empty string. Since Gabble happens to have a # nickname this contact specified cached, it should switch over to that one. conn.Aliasing.SetAliases({handle: ''}) expect_AliasesChanged_and_roster_write(q, stream, handle, jid, nick) assertEquals([nick], conn.Aliasing.RequestAliases([handle])) # Here's a contact we haven't seen before, pushed to our roster with a # nickname already there. jid = 'glados@aperture.lit' handle = conn.RequestHandles(cs.HT_CONTACT, [jid])[0] nick = 'Potato' send_roster_push(stream, jid, 'both', name=nick) q.expect('dbus-signal', signal='AliasesChanged', args=[[(handle, nick)]]) # If the user clears their alias, we should expect Gabble to say over D-Bus # that their nickname is their jid, and send a roster push removing the # name='' attribute... conn.Aliasing.SetAliases({handle: ''}) expect_AliasesChanged_and_roster_write(q, stream, handle, jid, None) # ...and also send a PEP query to find a better nickname; when the contact # replies, Gabble should update the roster accordingly. event = q.expect('stream-iq', iq_type='get', query_ns=ns.PUBSUB, to=jid) send_pep_nick_reply(stream, event.stanza, 'GLaDOS') expect_AliasesChanged_and_roster_write(q, stream, handle, jid, 'GLaDOS') if __name__ == '__main__': exec_test(test) telepathy-gabble-0.18.2/tests/twisted/roster/test-roster-subscribe.py0000644000175000017500000002403412227000321025774 0ustar00smcvsmcv00000000000000# -*- encoding:utf-8 -*- """ Test subscribing to a contact's presence. """ from twisted.words.xish import domish from servicetest import (EventPattern, assertLength, assertEquals, call_async, wrap_channel, sync_dbus) from gabbletest import (acknowledge_iq, exec_test, sync_stream) from rostertest import send_roster_push import constants as cs import ns def test_ancient(q, bus, conn, stream): test(q, bus, conn, stream, False) def test_modern(q, bus, conn, stream): test(q, bus, conn, stream, True) def test_ancient_remove(q, bus, conn, stream): test(q, bus, conn, stream, False, True) def test_modern_remove(q, bus, conn, stream): test(q, bus, conn, stream, True, True) def test_ancient_reject(q, bus, conn, stream): test(q, bus, conn, stream, False, 'reject') def test_modern_reject(q, bus, conn, stream): test(q, bus, conn, stream, True, 'reject') def test_ancient_reject_remove(q, bus, conn, stream): test(q, bus, conn, stream, False, True, 'reject') def test_modern_reject_remove(q, bus, conn, stream): test(q, bus, conn, stream, True, True, 'reject') def test_ancient_revoke(q, bus, conn, stream): test(q, bus, conn, stream, False, 'revoke') def test_modern_revoke(q, bus, conn, stream): test(q, bus, conn, stream, True, 'revoke') def test_ancient_revoke_remove(q, bus, conn, stream): test(q, bus, conn, stream, False, True, 'revoke') def test_modern_revoke_remove(q, bus, conn, stream): test(q, bus, conn, stream, True, True, 'revoke') def test(q, bus, conn, stream, modern=True, remove=False, remote='accept'): event = q.expect('stream-iq', query_ns=ns.ROSTER) # send back empty roster event.stanza['type'] = 'result' stream.send(event.stanza) while True: event = q.expect('dbus-signal', signal='NewChannel') path, type, handle_type, handle, suppress_handler = event.args if type != cs.CHANNEL_TYPE_CONTACT_LIST: continue chan_name = conn.InspectHandles(handle_type, [handle])[0] if chan_name == 'subscribe': break chan = wrap_channel(bus.get_object(conn.bus_name, path), 'ContactList') assertLength(0, chan.Group.GetMembers()) stored_path = conn.Requests.EnsureChannel({ cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_CONTACT_LIST, cs.TARGET_HANDLE_TYPE: cs.HT_LIST, cs.TARGET_ID: 'stored', })[1] stored = wrap_channel(bus.get_object(conn.bus_name, stored_path), 'ContactList') # request subscription alice, bob = conn.RequestHandles(cs.HT_CONTACT, ['alice@foo.com', 'bob@foo.com']) # Repeated subscription requests are *not* idempotent: the second request # should nag the contact again. for first_time in True, False, False: if modern: call_async(q, conn.ContactList, 'RequestSubscription', [bob], 'plz add kthx') else: call_async(q, chan.Group, 'AddMembers', [bob], 'plz add kthx') if first_time: event = q.expect('stream-iq', iq_type='set', query_ns=ns.ROSTER) item = event.query.firstChildElement() assertEquals('bob@foo.com', item["jid"]) acknowledge_iq(stream, event.stanza) expectations = [ EventPattern('stream-presence', presence_type='subscribe'), ] if modern: expectations.append(EventPattern('dbus-return', method='RequestSubscription')) event = q.expect_many(*expectations)[0] assertEquals('plz add kthx', event.presence_status) if first_time: # Our server sends a roster push indicating that yes, we added him send_roster_push(stream, 'bob@foo.com', 'none') q.expect('stream-iq', iq_type='result', iq_id='push') # Our server will also send a roster push with the ask=subscribe # sub-state, in response to our . # (RFC 3921 §8.2.4) send_roster_push(stream, 'bob@foo.com', 'none', True) q.expect('stream-iq', iq_type='result', iq_id='push') if remote == 'reject': # Bob rejects our request. presence = domish.Element(('jabber:client', 'presence')) presence['from'] = 'bob@foo.com' presence['type'] = 'unsubscribed' stream.send(presence) q.expect_many( EventPattern('dbus-signal', signal='MembersChanged', args=['', [], [bob], [], [], bob, cs.GC_REASON_PERMISSION_DENIED]), #EventPattern('stream-presence'), EventPattern('dbus-signal', signal='ContactsChanged', args=[{bob: (cs.SUBSCRIPTION_STATE_REMOVED_REMOTELY, cs.SUBSCRIPTION_STATE_NO, ''), }, []]), ) send_roster_push(stream, 'bob@foo.com', 'to') q.expect('stream-iq', iq_type='result', iq_id='push') else: # Bob accepts presence = domish.Element(('jabber:client', 'presence')) presence['from'] = 'bob@foo.com' presence['type'] = 'subscribed' stream.send(presence) q.expect_many( EventPattern('dbus-signal', signal='MembersChanged', args=['', [bob], [], [], [], bob, 0]), EventPattern('stream-presence'), EventPattern('dbus-signal', signal='ContactsChanged', args=[{bob: (cs.SUBSCRIPTION_STATE_YES, cs.SUBSCRIPTION_STATE_NO, ''), }, []]), ) send_roster_push(stream, 'bob@foo.com', 'to') q.expect('stream-iq', iq_type='result', iq_id='push') # Doing the same again is a successful no-op forbidden = [EventPattern('stream-iq', query_ns=ns.ROSTER), EventPattern('stream-presence')] sync_stream(q, stream) sync_dbus(bus, q, conn) q.forbid_events(forbidden) call_async(q, conn.ContactList, 'RequestSubscription', [bob], 'moo') q.expect('dbus-return', method='RequestSubscription') # Alice is not on the list call_async(q, conn.ContactList, 'Unsubscribe', [alice]) q.expect('dbus-return', method='Unsubscribe') call_async(q, conn.ContactList, 'RemoveContacts', [alice]) q.expect('dbus-return', method='RemoveContacts') sync_stream(q, stream) sync_dbus(bus, q, conn) q.unforbid_events(forbidden) if remote == 'revoke': # After accepting us, Bob then removes us. presence = domish.Element(('jabber:client', 'presence')) presence['from'] = 'bob@foo.com' presence['type'] = 'unsubscribed' stream.send(presence) q.expect_many( EventPattern('dbus-signal', signal='MembersChanged', args=['', [], [bob], [], [], bob, cs.GC_REASON_PERMISSION_DENIED]), EventPattern('stream-presence'), EventPattern('dbus-signal', signal='ContactsChanged', args=[{bob: (cs.SUBSCRIPTION_STATE_REMOVED_REMOTELY, cs.SUBSCRIPTION_STATE_NO, ''), }, []]), ) # Else, Bob isn't actually as interesting as we thought. Never mind, # we can unsubscribe or remove him (below), with the same APIs we'd # use to acknowledge remote removal. # (Unsubscribing from pending-subscribe is tested in # roster/removed-from-rp-subscribe.py so we don't test it here.) # If Bob removed us, we have to use modern APIs from now on, because from # the point of view of the old Group interface, removed remotely and # removed locally are synonymous. if remote in ('reject', 'revoke'): modern = True if modern: if remove: returning_method = 'RemoveContacts' call_async(q, conn.ContactList, 'RemoveContacts', [bob]) else: returning_method = 'Unsubscribe' call_async(q, conn.ContactList, 'Unsubscribe', [bob]) else: returning_method = 'RemoveMembers' if remove: call_async(q, stored.Group, 'RemoveMembers', [bob], '') else: call_async(q, chan.Group, 'RemoveMembers', [bob], '') if remove: iq = q.expect('stream-iq', iq_type='set', query_ns=ns.ROSTER, query_name='query') acknowledge_iq(stream, iq.stanza) if modern: q.expect('dbus-return', method='RemoveContacts') # FIXME: when we depend on a new enough tp-glib, expect RemoveMembers # to return here too send_roster_push(stream, 'bob@foo.com', 'remove') q.expect_many( EventPattern('stream-iq', iq_type='result', iq_id='push'), EventPattern('dbus-signal', signal='ContactsChanged', args=[{}, [bob]]), ) else: q.expect_many( EventPattern('dbus-return', method=returning_method), EventPattern('stream-presence', presence_type='unsubscribe', to='bob@foo.com'), ) send_roster_push(stream, 'bob@foo.com', 'none') q.expect_many( EventPattern('stream-iq', iq_type='result', iq_id='push'), EventPattern('dbus-signal', signal='ContactsChanged', args=[{bob: (cs.SUBSCRIPTION_STATE_NO, cs.SUBSCRIPTION_STATE_NO, ''), }, []]), ) if __name__ == '__main__': exec_test(test_ancient) exec_test(test_modern) exec_test(test_ancient_remove) exec_test(test_modern_remove) exec_test(test_ancient_revoke) exec_test(test_modern_revoke) exec_test(test_ancient_revoke_remove) exec_test(test_modern_revoke_remove) exec_test(test_ancient_reject) exec_test(test_modern_reject) exec_test(test_ancient_reject_remove) exec_test(test_modern_reject_remove) telepathy-gabble-0.18.2/tests/twisted/roster/test-roster.py0000644000175000017500000000673312227000321024023 0ustar00smcvsmcv00000000000000""" Test basic roster functionality. """ from gabbletest import exec_test from rostertest import expect_contact_list_signals, check_contact_list_signals from servicetest import (assertEquals, assertLength, call_async) import constants as cs import ns def test(q, bus, conn, stream): call_async(q, conn.ContactList, 'GetContactListAttributes', [], False) q.expect('dbus-error', method='GetContactListAttributes', name=cs.NOT_YET) event = q.expect('stream-iq', query_ns=ns.ROSTER) event.stanza['type'] = 'result' item = event.query.addElement('item') item['jid'] = 'amy@foo.com' item['subscription'] = 'both' item = event.query.addElement('item') item['jid'] = 'bob@foo.com' item['subscription'] = 'from' item = event.query.addElement('item') item['jid'] = 'che@foo.com' item['subscription'] = 'to' stream.send(event.stanza) # Regression test for : # some super-buggy XMPP server running on vk.com sends its reply to our # roster query twice. This used to crash Gabble. stream.send(event.stanza) # slight implementation detail: TpBaseContactList emits ContactsChanged # before it announces its channels s = q.expect('dbus-signal', signal='ContactsChanged', interface=cs.CONN_IFACE_CONTACT_LIST, path=conn.object_path) amy, bob, che = conn.RequestHandles(cs.HT_CONTACT, ['amy@foo.com', 'bob@foo.com', 'che@foo.com']) assertEquals([{ amy: (cs.SUBSCRIPTION_STATE_YES, cs.SUBSCRIPTION_STATE_YES, ''), bob: (cs.SUBSCRIPTION_STATE_NO, cs.SUBSCRIPTION_STATE_YES, ''), che: (cs.SUBSCRIPTION_STATE_YES, cs.SUBSCRIPTION_STATE_NO, ''), }, []], s.args) pairs = expect_contact_list_signals(q, bus, conn, ['publish', 'subscribe', 'stored']) # this is emitted last, so clients can tell when the initial state dump # has finished q.expect('dbus-signal', signal='ContactListStateChanged', args=[cs.CONTACT_LIST_STATE_SUCCESS]) call_async(q, conn.ContactList, 'GetContactListAttributes', [], False) r = q.expect('dbus-return', method='GetContactListAttributes') assertEquals(({ amy: { cs.CONN_IFACE_CONTACT_LIST + '/subscribe': cs.SUBSCRIPTION_STATE_YES, cs.CONN_IFACE_CONTACT_LIST + '/publish': cs.SUBSCRIPTION_STATE_YES, cs.CONN + '/contact-id': 'amy@foo.com', }, bob: { cs.CONN_IFACE_CONTACT_LIST + '/subscribe': cs.SUBSCRIPTION_STATE_NO, cs.CONN_IFACE_CONTACT_LIST + '/publish': cs.SUBSCRIPTION_STATE_YES, cs.CONN + '/contact-id': 'bob@foo.com', }, che: { cs.CONN_IFACE_CONTACT_LIST + '/subscribe': cs.SUBSCRIPTION_STATE_YES, cs.CONN_IFACE_CONTACT_LIST + '/publish': cs.SUBSCRIPTION_STATE_NO, cs.CONN + '/contact-id': 'che@foo.com', }, },), r.value) check_contact_list_signals(q, bus, conn, pairs.pop(0), cs.HT_LIST, 'publish', ['amy@foo.com', 'bob@foo.com']) check_contact_list_signals(q, bus, conn, pairs.pop(0), cs.HT_LIST, 'subscribe', ['amy@foo.com', 'che@foo.com']) check_contact_list_signals(q, bus, conn, pairs.pop(0), cs.HT_LIST, 'stored', ['amy@foo.com', 'bob@foo.com', 'che@foo.com']) assertLength(0, pairs) # i.e. we've checked all of them if __name__ == '__main__': exec_test(test) telepathy-gabble-0.18.2/tests/twisted/roster/test-roster-item-deletion.py0000644000175000017500000000720512227000321026553 0ustar00smcvsmcv00000000000000""" Regression test for http://bugs.freedesktop.org/show_bug.cgi?id=19524 """ from gabbletest import exec_test, acknowledge_iq from rostertest import (expect_contact_list_signals, check_contact_list_signals, send_roster_push) from servicetest import (assertLength, assertEquals, EventPattern, call_async) import constants as cs import ns def test_ancient(q, bus, conn, stream): test(q, bus, conn, stream, False) def test_modern(q, bus, conn, stream): test(q, bus, conn, stream, True) def test_ancient_queued(q, bus, conn, stream): test(q, bus, conn, stream, False, True) def test_modern_queued(q, bus, conn, stream): test(q, bus, conn, stream, True, True) def test(q, bus, conn, stream, modern=True, queued=False): event = q.expect('stream-iq', query_ns=ns.ROSTER) event.stanza['type'] = 'result' item = event.query.addElement('item') item['jid'] = 'quux@foo.com' item['subscription'] = 'none' quux_handle = conn.RequestHandles(cs.HT_CONTACT, ['quux@foo.com'])[0] stream.send(event.stanza) # slight implementation detail: TpBaseContactList emits ContactsChanged # before it announces its channels q.expect('dbus-signal', signal='ContactsChanged', interface=cs.CONN_IFACE_CONTACT_LIST, path=conn.object_path, args=[{quux_handle: (cs.SUBSCRIPTION_STATE_NO, cs.SUBSCRIPTION_STATE_NO, '')}, []]) pairs = expect_contact_list_signals(q, bus, conn, ['publish', 'subscribe', 'stored']) check_contact_list_signals(q, bus, conn, pairs.pop(0), cs.HT_LIST, 'publish', []) check_contact_list_signals(q, bus, conn, pairs.pop(0), cs.HT_LIST, 'subscribe', []) stored = check_contact_list_signals(q, bus, conn, pairs.pop(0), cs.HT_LIST, 'stored', ['quux@foo.com']) assertLength(0, pairs) # i.e. we've checked all of them if queued: conn.Aliasing.SetAliases({quux_handle: 'Quux'}) set_aliases = q.expect('stream-iq', query_ns=ns.ROSTER) item = set_aliases.query.firstChildElement() assertEquals('quux@foo.com', item['jid']) assertEquals('Quux', item['name']) expectations = [ EventPattern('stream-iq', iq_type='set', query_ns=ns.ROSTER), ] if modern: call_async(q, conn.ContactList, 'RemoveContacts', [quux_handle]) else: call_async(q, stored.Group, 'RemoveMembers', [quux_handle], '') if queued: # finish off the previous thing we were doing, so removal can proceed acknowledge_iq(stream, set_aliases.stanza) event = q.expect_many(*expectations)[0] item = event.query.firstChildElement() assertEquals('quux@foo.com', item['jid']) assertEquals('remove', item['subscription']) send_roster_push(stream, 'quux@foo.com', 'remove') q.expect_many( EventPattern('dbus-signal', interface=cs.CHANNEL_IFACE_GROUP, path=stored.object_path, signal='MembersChanged', args=['', [], [quux_handle], [], [], 0, 0]), EventPattern('dbus-signal', interface=cs.CONN_IFACE_CONTACT_LIST, path=conn.object_path, signal='ContactsChanged', args=[{}, [quux_handle]]), EventPattern('stream-iq', iq_id='push', iq_type='result'), ) acknowledge_iq(stream, event.stanza) if modern: q.expect('dbus-return', method='RemoveContacts') # FIXME: when we depend on a new enough tp-glib, RemoveMembers should # return at this point too if __name__ == '__main__': exec_test(test_ancient) exec_test(test_modern) exec_test(test_ancient_queued) exec_test(test_modern_queued) telepathy-gabble-0.18.2/tests/twisted/roster/test-google-roster.py0000644000175000017500000005577712227000321025311 0ustar00smcvsmcv00000000000000# vim: set fileencoding=utf-8 : """ Test workarounds for gtalk """ from gabbletest import ( acknowledge_iq, exec_test, sync_stream, make_result_iq, GoogleXmlStream, ) from rostertest import ( expect_contact_list_signals, check_contact_list_signals, ) from servicetest import ( call_async, sync_dbus, EventPattern, assertLength, assertEquals, assertContains, assertDoesNotContain, ) import constants as cs import ns from twisted.words.protocols.jabber.client import IQ from twisted.words.xish import domish def make_set_roster_iq(stream, user, contact, state, ask, attrs={}): iq = IQ(stream, 'set') query = iq.addElement((ns.ROSTER, 'query')) add_gr_attributes(query) add_roster_item(query, contact, state, ask, attrs=attrs) return iq def add_gr_attributes(query): query['xmlns:gr'] = ns.GOOGLE_ROSTER query['gr:ext'] = '2' def add_roster_item(query, contact, state, ask, attrs={}): item = query.addElement('item') item['jid'] = contact item['subscription'] = state if ask: item['ask'] = 'subscribe' for k, v in attrs.iteritems(): item[k] = v return item def is_stored(event): return event.path.endswith('/stored') def is_subscribe(event): return event.path.endswith('/subscribe') def is_publish(event): return event.path.endswith('/publish') def is_deny(event): return event.path.endswith('/deny') def test_inital_roster(q, bus, conn, stream): """ This part of the test checks that Gabble correctly alters on which lists contacts appear based on the google:roster attributes and special-cases. """ event = q.expect('stream-iq', query_ns=ns.ROSTER) query = event.query assertContains('gr', query.localPrefixes) assertEquals(ns.GOOGLE_ROSTER, query.localPrefixes['gr']) # We support version 2 of Google's extensions. assertEquals('2', query[(ns.GOOGLE_ROSTER, 'ext')]) result = make_result_iq(stream, event.stanza) query = result.firstChildElement() add_gr_attributes(query) # Gabble suppresses contacts labelled as "hidden" from all roster channels. add_roster_item(query, 'should-be-hidden@example.com', 'both', False, {'gr:t': 'H'}) # Gabble should hide contacts on the Google roster with subscription="none" # and ask!="subscribe", to hide contacts which are actually just email # addresses. (This is in line with Pidgin; the code there was added by Sean # Egan, who worked on Google Talk for Google at the time.) add_roster_item(query, 'probably-an-email-address@badger.com', 'none', False) # This contact is remote pending, so we shouldn't suppress it. add_roster_item(query, 'this-is-a-jid@badger.com', 'none', True) add_roster_item(query, 'lp-bug-398293@gmail.com', 'both', False, {'gr:autosub': 'true'}) # These contacts are blocked but we're subscribed to them, so they should # show up in all of the lists. add_roster_item(query, 'blocked-but-subscribed@boards.ca', 'both', False, {'gr:t': 'B'}) add_roster_item(query, 'music-is-math@boards.ca', 'both', False, {'gr:t': 'B'}) # This contact is blocked, and we have no other subscription to them; so, # they should not show up in 'stored'. add_roster_item(query, 'blocked-and-no-sub@boards.ca', 'none', False, {'gr:t': 'B'}) # Send back the roster stream.send(result) # Since s-b-h had the "hidden" flag set, we don't expect them to be on any # lists. But we do want the "autosub" contact to be visible; see # , # where Gabble was incorrectly hiding valid contacts. mutually_subscribed_contacts = ['lp-bug-398293@gmail.com', 'blocked-but-subscribed@boards.ca', 'music-is-math@boards.ca'] rp_contacts = ['this-is-a-jid@badger.com'] blocked_contacts = ['blocked-but-subscribed@boards.ca', 'blocked-and-no-sub@boards.ca', 'music-is-math@boards.ca'] pairs = expect_contact_list_signals(q, bus, conn, ['publish', 'subscribe', 'stored', 'deny']) publish = check_contact_list_signals(q, bus, conn, pairs.pop(0), cs.HT_LIST, 'publish', mutually_subscribed_contacts) subscribe = check_contact_list_signals(q, bus, conn, pairs.pop(0), cs.HT_LIST, 'subscribe', mutually_subscribed_contacts, rp_contacts=rp_contacts) stored = check_contact_list_signals(q, bus, conn, pairs.pop(0), cs.HT_LIST, 'stored', mutually_subscribed_contacts + rp_contacts) deny = check_contact_list_signals(q, bus, conn, pairs.pop(0), cs.HT_LIST, 'deny', blocked_contacts) assertLength(0, pairs) # i.e. we've checked all of them return (publish, subscribe, stored, deny) def test_flickering(q, bus, conn, stream, subscribe): """ Google's server is buggy; when asking to subscribe to somebody, the subscription state transitions "flicker" sometimes. Here, we test that Gabble is suppressing the flickers. """ self_handle = conn.GetSelfHandle() contact = 'bob@foo.com' handle = conn.RequestHandles(cs.HT_CONTACT, ['bob@foo.com'])[0] # request subscription call_async(q, subscribe.Group, 'AddMembers', [handle], "") event = q.expect('stream-iq', iq_type='set', query_ns=ns.ROSTER) item = event.query.firstChildElement() assertEquals(contact, item['jid']) acknowledge_iq(stream, event.stanza) # FIXME: when we depend on a new enough tp-glib we could expect # AddMembers to return at this point # send empty roster item iq = make_set_roster_iq(stream, 'test@localhost/Resource', contact, "none", False) stream.send(iq) # We don't expect the stored list to be updated here, because Gabble # ignores Google Talk roster items with subscription="none" and # ask!="subscribe" as described above. event = q.expect('stream-presence', presence_type='subscribe') # Google's server appears to be buggy. If you send # # it sends: # 1. A roster update with ask="subscribe"; # 2. Another roster update, without ask="subscribe"; # 3. A third roster update, with ask="subscribe". # Gabble should work around this, to avoid spuriously informing the UI that # the subscription request was declined. # Send roster update 1: none, ask=subscribe iq = make_set_roster_iq(stream, 'test@localhost/Resource', contact, "none", True) stream.send(iq) # Gabble should report this update to the UI. q.expect_many( EventPattern('dbus-signal', signal='MembersChanged', args=['', [handle], [], [], [], 0, cs.GC_REASON_NONE], predicate=is_stored), EventPattern('dbus-signal', signal='MembersChanged', args=['', [], [], [], [handle], self_handle, cs.GC_REASON_NONE], predicate=is_subscribe), EventPattern('dbus-signal', signal='ContactsChanged', args=[{handle: (cs.SUBSCRIPTION_STATE_ASK, cs.SUBSCRIPTION_STATE_NO, ''), }, []]), ) # Gabble shouldn't report any changes to subscribe or stored's members in # response to the next two roster updates. change_events = [ EventPattern('dbus-signal', signal='MembersChanged', predicate=is_subscribe), EventPattern('dbus-signal', signal='MembersChanged', predicate=is_stored), EventPattern('dbus-signal', signal='ContactsChanged'), ] q.forbid_events(change_events) # Send roster update 2: none iq = make_set_roster_iq(stream, 'test@localhost/Resource', contact, "none", False) stream.send(iq) # Send roster update 3: none, ask=subscribe iq = make_set_roster_iq(stream, 'test@localhost/Resource', contact, "none", True) stream.send(iq) # Neither of those should have been signalled as a change to the subscribe # list sync_stream(q, stream) sync_dbus(bus, q, conn) q.unforbid_events(change_events) # Also, when the contact accepts the subscription request, they flicker # similarly: # 1. subscription='to' # 2. subscription='none' ask='subscribe' # 3. subscription='to' # Again, Gabble should work around this rather than informing the UI that a # subscription request was accepted twice. # Send roster update 1: subscription=to (accepted) iq = make_set_roster_iq(stream, 'test@localhost/Resource', contact, "to", False) stream.send(iq) presence = domish.Element(('jabber:client', 'presence')) presence['from'] = 'bob@foo.com' presence['type'] = 'subscribed' stream.send(presence) # Gabble should report this update to the UI. q.expect_many( EventPattern('dbus-signal', signal='MembersChanged', args=['', [handle], [], [], [], handle, cs.GC_REASON_NONE], predicate=is_subscribe), EventPattern('dbus-signal', signal='ContactsChanged', args=[{handle: (cs.SUBSCRIPTION_STATE_YES, cs.SUBSCRIPTION_STATE_NO, ''), }, []]), ) # Gabble shouldn't report any changes to subscribe or stored's members in # response to the next two roster updates. q.forbid_events(change_events) # Send roster update 2: subscription=none, ask=subscribe (pending again) iq = make_set_roster_iq(stream, 'test@localhost/Resource', contact, "none", True) stream.send(iq) # Send roster update 3: subscript=to (accepted again) iq = make_set_roster_iq(stream, 'test@localhost/Resource', contact, "to", False) stream.send(iq) # Neither of those should have been signalled as a change to the subscribe # list sync_stream(q, stream) sync_dbus(bus, q, conn) q.unforbid_events(change_events) def test_local_pending(q, bus, conn, stream, subscribe): """ When somebody asks to subscribe to us, Google sends the subscription request and then a roster update saying there is no subscription. This causes the contact to appear in local pending and then disappear. Here, we test that Gabble is suppressing the flickers. """ contact = 'alice@foo.com' handle = conn.RequestHandles(cs.HT_CONTACT, [contact])[0] # Alice asks to subscribes to us presence = domish.Element(('jabber:client', 'presence')) presence['from'] = contact presence['type'] = 'subscribe' stream.send(presence) q.expect_many( EventPattern('dbus-signal', signal='MembersChanged', args=['', [], [], [handle], [], handle, cs.GC_REASON_NONE], predicate=is_publish), EventPattern('dbus-signal', signal='ContactsChanged', args=[{handle: (cs.SUBSCRIPTION_STATE_NO, cs.SUBSCRIPTION_STATE_ASK, '')}, []]), ) # Now we send the spurious roster update with subscribe="none" and verify # that nothing happens to her publish state in reaction to that change_event = EventPattern('dbus-signal', signal='MembersChanged', predicate=is_publish) q.forbid_events([change_event]) iq = make_set_roster_iq(stream, 'test@localhost/Resource', contact, "none", False) stream.send(iq) sync_stream(q, stream) sync_dbus(bus, q, conn) q.unforbid_events([change_event]) # Now we cancel alice's subscription request and verify that if the # redundant IQ is sent again, it's safely handled presence = domish.Element(('jabber:client', 'presence')) presence['from'] = contact presence['type'] = 'unsubscribe' stream.send(presence) q.expect_many( EventPattern('dbus-signal', signal='MembersChanged', args=['', [], [handle], [], [], handle, cs.GC_REASON_NONE], predicate=is_publish), EventPattern('dbus-signal', signal='ContactsChanged', args=[{handle: (cs.SUBSCRIPTION_STATE_NO, cs.SUBSCRIPTION_STATE_REMOVED_REMOTELY, '')}, []]), ) # Now we send a roster roster update with subscribe="none" again (which # doesn't change anything, it just confirms what we already knew) and # verify that nothing happens to her publish state in reaction to that. q.forbid_events([change_event]) iq = make_set_roster_iq(stream, 'test@localhost/Resource', contact, "none", False) stream.send(iq) sync_stream(q, stream) sync_dbus(bus, q, conn) q.unforbid_events([change_event]) # This event is forbidden in all of the deny tests! remove_events = [ EventPattern('stream-iq', query_ns=ns.ROSTER, predicate=(lambda event: event.query.firstChildElement()['subscription'] == 'remove'), ) ] def test_deny_simple(q, bus, conn, stream, stored, deny): """ If we remove a blocked contact from 'stored', they shouldn't actually be removed from the roster: rather, we should cancel both subscription directions, at which point they will vanish from 'stored', while remaining on 'deny'. """ contact = 'blocked-but-subscribed@boards.ca' handle = conn.RequestHandles(cs.HT_CONTACT, [contact])[0] assertContains(handle, stored.Properties.Get(cs.CHANNEL_IFACE_GROUP, "Members")) call_async(q, stored.Group, 'RemoveMembers', [handle], "") q.forbid_events(remove_events) q.expect_many( EventPattern('stream-presence', to=contact, presence_type='unsubscribe'), EventPattern('stream-presence', to=contact, presence_type='unsubscribed'), EventPattern('dbus-return', method='RemoveMembers'), ) # Our server sends roster pushes in response to our unsubscribe and # unsubscribed commands. stream.send(make_set_roster_iq(stream, 'test@localhost/Resource', contact, "from", False, attrs={'gr:t': 'B'})) stream.send(make_set_roster_iq(stream, 'test@localhost/Resource', contact, "none", False, attrs={'gr:t': 'B'})) # As a result they should drop off all three non-deny lists, but not fall # off deny: q.expect_many( EventPattern('dbus-signal', signal='ContactsChanged', args=[{}, [handle]]), *[ EventPattern('dbus-signal', signal='MembersChanged', args=['', [], [handle], [], [], 0, cs.GC_REASON_NONE], predicate=p) for p in [is_stored, is_subscribe, is_publish] ]) assertContains(handle, deny.Properties.Get(cs.CHANNEL_IFACE_GROUP, "Members")) q.unforbid_events(remove_events) def test_deny_overlap_one(q, bus, conn, stream, subscribe, stored, deny): """ Here's a tricker case: blocking a contact, and then removing them before the server's responded to the block request. """ self_handle = conn.GetSelfHandle() # As we saw in test_flickering(), we have a subscription to Bob, # everything's peachy. contact = 'bob@foo.com' handle = conn.RequestHandles(cs.HT_CONTACT, ['bob@foo.com'])[0] assertContains(handle, stored.Properties.Get(cs.CHANNEL_IFACE_GROUP, "Members")) assertContains(handle, subscribe.Properties.Get(cs.CHANNEL_IFACE_GROUP, "Members")) q.forbid_events(remove_events) # But then we have a falling out. In a blind rage, I block Bob: call_async(q, deny.Group, 'AddMembers', [handle], "") event = q.expect('stream-iq', query_ns=ns.ROSTER) item = event.query.firstChildElement() assertEquals(contact, item['jid']) assertEquals('B', item[(ns.GOOGLE_ROSTER, 't')]) # Then — *before the server has replied* — I remove him from stored. call_async(q, stored.Group, 'RemoveMembers', [handle], "") # subscription='remove' is still forbidden from above. So we sync to ensure # that Gabble's received RemoveMembers, and if it's going to send us a # remove (or premature ) we catch it. sync_dbus(bus, q, conn) sync_stream(q, stream) # So now we send a roster push and reply for the block request. stream.send(make_set_roster_iq(stream, 'test@localhost/Resource', contact, 'to', False, attrs={ 'gr:t': 'B' })) acknowledge_iq(stream, event.stanza) # At which point, Bob should appear on 'deny', and Gabble should send an # unsubscribe, but *not* an unsubscribe*d* because Bob wasn't subscribed to # us! unsubscribed_events = [ EventPattern('stream-presence', presence_type='unsubscribed') ] q.forbid_events(unsubscribed_events) q.expect_many( EventPattern('dbus-signal', signal='MembersChanged', predicate=is_deny, args=["", [handle], [], [], [], self_handle, 0]), EventPattern('stream-presence', to=contact, presence_type='unsubscribe'), ) # And our server sends us a roster push in response to unsubscribe: stream.send(make_set_roster_iq(stream, 'test@localhost/Resource', contact, "none", False, attrs={'gr:t': 'B'})) # As a result, Gabble makes Bob fall off subscribe and stored. q.expect_many( EventPattern('dbus-signal', signal='MembersChanged', predicate=is_subscribe, args=["", [], [handle], [], [], 0, 0]), EventPattern('dbus-signal', signal='MembersChanged', predicate=is_stored, args=["", [], [handle], [], [], 0, 0]), EventPattern('dbus-signal', signal='ContactsChanged', args=[{}, [handle]]), ) # And he should definitely still be on deny. That rascal. assertContains(handle, deny.Properties.Get(cs.CHANNEL_IFACE_GROUP, "Members")) q.unforbid_events(unsubscribed_events) q.unforbid_events(remove_events) def test_deny_overlap_two(q, bus, conn, stream, subscribe, publish, stored, deny): """ Here's another tricky case: editing a contact (setting an alias, say), and then while that edit's in flight, blocking and remove the contact. """ # This contact was on our roster when we started. contact = 'lp-bug-398293@gmail.com' handle = conn.RequestHandles(cs.HT_CONTACT, [contact])[0] assertContains(handle, stored.Properties.Get(cs.CHANNEL_IFACE_GROUP, "Members")) assertContains(handle, subscribe.Properties.Get(cs.CHANNEL_IFACE_GROUP, "Members")) assertContains(handle, publish.Properties.Get(cs.CHANNEL_IFACE_GROUP, "Members")) # Once again, at no point in this test should anyone be removed outright. q.forbid_events(remove_events) # First up, we edit the contact's alias, triggering a roster update from # the client. conn.Aliasing.SetAliases({handle: 'oh! the huge manatee!'}) event = q.expect('stream-iq', query_ns=ns.ROSTER) item = event.query.firstChildElement() assertEquals(contact, item['jid']) assertEquals('oh! the huge manatee!', item['name']) # Before the server responds, we block and remove the contact. The edits # should be queued... patterns = [ EventPattern('stream-iq', query_ns=ns.ROSTER), EventPattern('stream-presence', presence_type='unsubscribed'), EventPattern('stream-presence', presence_type='unsubscribe'), ] q.forbid_events(patterns) call_async(q, deny.Group, 'AddMembers', [handle], "") call_async(q, stored.Group, 'RemoveMembers', [handle], "") # Make sure if the edits are sent prematurely, we've got them. sync_stream(q, stream) q.unforbid_events(patterns) # Okay, now we respond to the alias update. At this point we expect an # update to gr:t=B, leaving subscription=both intact, and subscription # cancellations. acknowledge_iq(stream, event.stanza) roster_event, _, _ = q.expect_many(*patterns) item = roster_event.query.firstChildElement() assertEquals(contact, item['jid']) assertEquals('B', item[(ns.GOOGLE_ROSTER, 't')]) # And we're done. Clean up. q.unforbid_events(remove_events) def test_deny_unblock_remove(q, bus, conn, stream, stored, deny): """ Test unblocking a contact, and, while that request is pending, deleting them. """ self_handle = conn.GetSelfHandle() # This contact was on our roster, blocked and subscribed, when we started. contact = 'music-is-math@boards.ca' handle = conn.RequestHandles(cs.HT_CONTACT, [contact])[0] # They're blocked, and we have a bidi subscription, so they should be on # deny and stored. (We already checked this earlier, but we've been messing # with the roster so let's be sure the preconditions are okay...) assertContains(handle, deny.Properties.Get(cs.CHANNEL_IFACE_GROUP, "Members")) assertContains(handle, stored.Properties.Get(cs.CHANNEL_IFACE_GROUP, "Members")) # Unblock them. call_async(q, deny.Group, 'RemoveMembers', [handle], "") roster_event = q.expect('stream-iq', query_ns=ns.ROSTER) item = roster_event.query.firstChildElement() assertEquals(contact, item['jid']) assertDoesNotContain((ns.GOOGLE_ROSTER, 't'), item.attributes) # If we now remove them from stored, the edit shouldn't be sent until the # unblock event has had a reply. q.forbid_events(remove_events) call_async(q, stored.Group, 'RemoveMembers', [handle], "") # Make sure if the remove is sent prematurely, we catch it. sync_stream(q, stream) q.unforbid_events(remove_events) # So now we send a roster push and reply for the unblock request. stream.send(make_set_roster_iq(stream, 'test@localhost/Resource', contact, 'both', False, attrs={})) acknowledge_iq(stream, roster_event.stanza) # And on receiving the push and reply, Gabble should show them being # removed from deny, and send a remove. _, roster_event = q.expect_many( EventPattern('dbus-signal', signal='MembersChanged', args=['', [], [handle], [], [], self_handle, cs.GC_REASON_NONE], predicate=is_deny), remove_events[0], ) item = roster_event.query.firstChildElement() assertEquals(contact, item['jid']) stream.send(make_set_roster_iq(stream, 'test@localhost/Resource', contact, 'remove', False, attrs={})) acknowledge_iq(stream, roster_event.stanza) q.expect('dbus-signal', signal='MembersChanged', args=['', [], [handle], [], [], 0, cs.GC_REASON_NONE], predicate=is_stored) def test_contact_blocking(q, bus, conn, stream, stored, deny): """test ContactBlocking API""" assertContains(cs.CONN_IFACE_CONTACT_BLOCKING, conn.Properties.Get(cs.CONN, "Interfaces")) # 3 contacts are blocked blocked = conn.RequestBlockedContacts(dbus_interface=cs.CONN_IFACE_CONTACT_BLOCKING) assertLength(3, blocked) def test(q, bus, conn, stream): publish, subscribe, stored, deny = test_inital_roster(q, bus, conn, stream) test_flickering(q, bus, conn, stream, subscribe) test_local_pending(q, bus, conn, stream, subscribe) test_deny_simple(q, bus, conn, stream, stored, deny) test_deny_overlap_one(q, bus, conn, stream, subscribe, stored, deny) test_deny_overlap_two(q, bus, conn, stream, subscribe, publish, stored, deny) test_deny_unblock_remove(q, bus, conn, stream, stored, deny) test_contact_blocking(q, bus, conn, stream, stored, deny) if __name__ == '__main__': exec_test(test, protocol=GoogleXmlStream) telepathy-gabble-0.18.2/tests/twisted/roster/request-group-before-roster.py0000644000175000017500000000341512227000321027120 0ustar00smcvsmcv00000000000000""" Regression test for a bug where RequestChannel times out when requesting a group channel if the roster hasn't been received at the time of the call. """ from gabbletest import exec_test, sync_stream from servicetest import sync_dbus, call_async import constants as cs import ns def test(q, bus, conn, stream): roster_event = q.expect('stream-iq', query_ns=ns.ROSTER) roster_event.stanza['type'] = 'result' call_async(q, conn, "RequestHandles", cs.HT_GROUP, ['test']) event = q.expect('dbus-return', method='RequestHandles') test_handle = event.value[0][0] call_async(q, conn, 'RequestChannel', cs.CHANNEL_TYPE_CONTACT_LIST, cs.HT_GROUP, test_handle, True) # A previous incarnation of this test --- written with the intention that # RequestChannel would be called before the roster was received, to expose # a bug in Gabble triggered by that ordering --- was racy: if the D-Bus # daemon happened to be particularly busy, the call to RequestChannel # reached Gabble after the roster stanza. (The race was discovered when # that reversed order triggered a newly-introduced instance of the # opposite bug to the one the test was targetting!) So we sync the XMPP # stream and D-Bus queue here. sync_stream(q, stream) sync_dbus(bus, q, conn) # send an empty roster stream.send(roster_event.stanza) event = q.expect('dbus-return', method='RequestChannel') path = event.value[0] while True: event = q.expect('dbus-signal', signal='NewChannel') assert event.args[0] == path, (event.args, path) _, type, handle_type, handle, suppress_handler = event.args if handle_type == cs.HT_GROUP and handle == test_handle: break if __name__ == '__main__': exec_test(test) telepathy-gabble-0.18.2/tests/twisted/roster/request-group-after-roster.py0000644000175000017500000000271212227000321026756 0ustar00smcvsmcv00000000000000""" Regression test for a bug where CreateChannel times out when requesting a group channel after the roster has been received. """ from gabbletest import exec_test, sync_stream from servicetest import sync_dbus, call_async import constants as cs import ns def test(q, bus, conn, stream): roster_event = q.expect('stream-iq', query_ns=ns.ROSTER) roster_event.stanza['type'] = 'result' call_async(q, conn, "RequestHandles", cs.HT_GROUP, ['test']) event = q.expect('dbus-return', method='RequestHandles') test_handle = event.value[0][0] # send an empty roster stream.send(roster_event.stanza) sync_stream(q, stream) sync_dbus(bus, q, conn) call_async(q, conn.Requests, 'CreateChannel', { cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_CONTACT_LIST, cs.TARGET_HANDLE_TYPE: cs.HT_GROUP, cs.TARGET_HANDLE: test_handle, }) event = q.expect('dbus-return', method='CreateChannel') ret_path, ret_props = event.value event = q.expect('dbus-signal', signal='NewChannels') path, props = event.args[0][0] assert props[cs.CHANNEL_TYPE] == cs.CHANNEL_TYPE_CONTACT_LIST, props assert props[cs.TARGET_HANDLE_TYPE] == cs.HT_GROUP, props assert props[cs.TARGET_HANDLE] == test_handle, props assert props[cs.TARGET_ID] == 'test', props assert ret_path == path, (ret_path, path) assert ret_props == props, (ret_props, props) if __name__ == '__main__': exec_test(test) telepathy-gabble-0.18.2/tests/twisted/roster/removed-from-rp-subscribe.py0000644000175000017500000002076212227000321026526 0ustar00smcvsmcv00000000000000""" Regression tests for rescinding outstanding subscription requests. """ from twisted.words.protocols.jabber.client import IQ from servicetest import EventPattern, assertEquals, assertLength, call_async from gabbletest import exec_test, acknowledge_iq from rostertest import expect_contact_list_signals, check_contact_list_signals import constants as cs import ns jid = 'marco@barisione.lit' def test(q, bus, conn, stream, remove, local, modern): # Gabble asks for the roster; the server sends back an empty roster. event = q.expect('stream-iq', query_ns=ns.ROSTER) event.stanza['type'] = 'result' stream.send(event.stanza) pairs = expect_contact_list_signals(q, bus, conn, ['publish', 'subscribe', 'stored']) check_contact_list_signals(q, bus, conn, pairs.pop(0), cs.HT_LIST, 'publish', []) subscribe = check_contact_list_signals(q, bus, conn, pairs.pop(0), cs.HT_LIST, 'subscribe', []) stored = check_contact_list_signals(q, bus, conn, pairs.pop(0), cs.HT_LIST, 'stored', []) assertLength(0, pairs) # i.e. we've checked all of them self_handle = conn.GetSelfHandle() h = conn.RequestHandles(cs.HT_CONTACT, [jid])[0] # Another client logged into our account (Gajim, say) wants to subscribe to # Marco's presence. First, per RFC 3921 it 'SHOULD perform a "roster set" # for the new roster item': # # # # # # # # 'As a result, the user's server (1) MUST initiate a roster push for the # new roster item to all available resources associated with this user that # have requested the roster, setting the 'subscription' attribute to a # value of "none"': iq = IQ(stream, "set") item = iq.addElement((ns.ROSTER, 'query')).addElement('item') item['jid'] = jid item['subscription'] = 'none' stream.send(iq) # In response, Gabble should add Marco to stored: q.expect_many( EventPattern('dbus-signal', signal='MembersChanged', args=['', [h], [], [], [], 0, 0], path=stored.object_path), EventPattern('dbus-signal', signal='ContactsChanged', args=[{ h: (cs.SUBSCRIPTION_STATE_NO, cs.SUBSCRIPTION_STATE_NO, ''), }, []], ), ) # Gajim sends a to Marco. 'As a result, the # user's server MUST initiate a second roster push to all of the user's # available resources that have requested the roster, setting [...] # ask='subscribe' attribute in the roster item [for Marco]: iq = IQ(stream, "set") item = iq.addElement((ns.ROSTER, 'query')).addElement('item') item['jid'] = jid item['subscription'] = 'none' item['ask'] = 'subscribe' stream.send(iq) # In response, Gabble should add Marco to subscribe:remote-pending: q.expect_many( EventPattern('dbus-signal', signal='MembersChanged', args=['', [], [], [], [h], self_handle, 0], path=subscribe.object_path), EventPattern('dbus-signal', signal='ContactsChanged', args=[{ h: (cs.SUBSCRIPTION_STATE_ASK, cs.SUBSCRIPTION_STATE_NO, ''), }, []], ), ) # The user decides that they don't care what Marco's baking after all # (maybe they read his blog instead?) and: if remove: # ...removes him from the roster... if local: # ...by telling Gabble to remove him from stored. if modern: call_async(q, conn.ContactList, 'RemoveContacts', [h]) else: call_async(q, stored.Group, 'RemoveMembers', [h], '') event = q.expect('stream-iq', iq_type='set', query_ns=ns.ROSTER) item = event.query.firstChildElement() assertEquals(jid, item['jid']) assertEquals('remove', item['subscription']) else: # ...using the other client. pass # The server must 'inform all of the user's available resources that # have requested the roster of the roster item removal': iq = IQ(stream, "set") item = iq.addElement((ns.ROSTER, 'query')).addElement('item') item['jid'] = jid item['subscription'] = 'remove' # When Marco found this bug, this roster update included: item['ask'] = 'subscribe' # which is a bit weird: I don't think the server should send that when # the contact's being removed. I think Gabble should ignore it, so I'm # including it in the test. stream.send(iq) # In response, Gabble should announce that Marco has been removed from # subscribe:remote-pending and stored:members: q.expect_many( EventPattern('dbus-signal', signal='MembersChanged', args=['', [], [h], [], [], 0, 0], path=subscribe.object_path), EventPattern('dbus-signal', signal='MembersChanged', args=['', [], [h], [], [], 0, 0], path=stored.object_path), EventPattern('dbus-signal', signal='ContactsChanged', args=[{}, [h]], ), ) if local and modern: acknowledge_iq(stream, event.stanza) q.expect('dbus-return', method='RemoveContacts') # FIXME: when we depend on a new enough tp-glib we can expect # RemoveMembers to return here in the local case, too else: # ...rescinds the subscription request... if local: # ...by telling Gabble to remove him from 'subscribe'. if modern: call_async(q, conn.ContactList, 'Unsubscribe', [h]) else: subscribe.Group.RemoveMembers([h], '') events = [EventPattern('stream-presence', to=jid, presence_type='unsubscribe')] if modern: events.append(EventPattern('dbus-return', method='Unsubscribe')) event = q.expect_many(*events)[0] else: # ...in the other client. pass # In response, the server sends a roster update: iq = IQ(stream, "set") item = iq.addElement((ns.ROSTER, 'query')).addElement('item') item['jid'] = jid item['subscription'] = 'none' # no ask='subscribe' any more. stream.send(iq) # In response, Gabble should announce that Marco has been removed from # subscribe:remote-pending. It shouldn't wait for the ack before doing so: empirical tests reveal # that it's never delivered. q.expect_many( EventPattern('dbus-signal', signal='MembersChanged', args=['', [], [h], [], [], 0, 0], path=subscribe.object_path), EventPattern('dbus-signal', signal='ContactsChanged', args=[{ h: (cs.SUBSCRIPTION_STATE_NO, cs.SUBSCRIPTION_STATE_NO, ''), }, []], ), ) def test_remove_local(q, bus, conn, stream): test(q, bus, conn, stream, remove=True, local=True, modern=True) def test_unsubscribe_local(q, bus, conn, stream): test(q, bus, conn, stream, remove=False, local=True, modern=True) def test_remove_remote(q, bus, conn, stream): test(q, bus, conn, stream, remove=True, local=False, modern=True) def test_unsubscribe_remote(q, bus, conn, stream): test(q, bus, conn, stream, remove=False, local=False, modern=True) def test_remove_local_old(q, bus, conn, stream): test(q, bus, conn, stream, remove=True, local=True, modern=False) def test_unsubscribe_local_old(q, bus, conn, stream): test(q, bus, conn, stream, remove=False, local=True, modern=False) def test_remove_remote_old(q, bus, conn, stream): test(q, bus, conn, stream, remove=True, local=False, modern=False) def test_unsubscribe_remote_old(q, bus, conn, stream): test(q, bus, conn, stream, remove=False, local=False, modern=False) if __name__ == '__main__': exec_test(test_remove_local) exec_test(test_unsubscribe_local) exec_test(test_remove_remote) exec_test(test_unsubscribe_remote) exec_test(test_remove_local_old) exec_test(test_unsubscribe_local_old) exec_test(test_remove_remote_old) exec_test(test_unsubscribe_remote_old) telepathy-gabble-0.18.2/tests/twisted/roster/push-without-id.py0000644000175000017500000000436412227000321024600 0ustar00smcvsmcv00000000000000""" Ensure that Gabble correctly handles broken roster pushes from servers that omit id='', in flagrant violation of XMPP Core. (Sadly these servers exist, so we should interop with them. Think of it of like No_Reply in D-Bus... """ from servicetest import EventPattern, sync_dbus from gabbletest import exec_test, acknowledge_iq, sync_stream from rostertest import ( make_roster_push, expect_contact_list_signals, check_contact_list_signals, ) import ns import constants as cs jid = 'moonboots@xsf.lit' def test(q, bus, conn, stream): # Gabble asks for the roster; the server sends back an empty roster. event = q.expect('stream-iq', query_ns=ns.ROSTER) acknowledge_iq(stream, event.stanza) pairs = expect_contact_list_signals(q, bus, conn, ['stored']) stored = check_contact_list_signals(q, bus, conn, pairs.pop(0), cs.HT_LIST, 'stored', []) # The server sends us a roster push without an id=''. WTF! iq = make_roster_push(stream, jid, 'both') del iq['id'] stream.send(iq) h = conn.RequestHandles(cs.HT_CONTACT, [jid])[0] q.expect_many( EventPattern('dbus-signal', signal='MembersChanged', args=['', [h], [], [], [], 0, 0], path=stored.object_path), EventPattern('dbus-signal', signal='ContactsChanged', args=[{ h: (cs.SUBSCRIPTION_STATE_YES, cs.SUBSCRIPTION_STATE_YES, ''), }, []], ), ) # Verify that Gabble didn't crash while trying to ack the push. sync_stream(q, stream) # Just for completeness, let's repeat this test with a malicious roster # push from a contact (rather than from our server). Our server's *really* # broken if it allows this. Nonetheless... iq = make_roster_push(stream, 'silvio@gov.it', 'both') del iq['id'] iq['from'] = 'silvio@gov.it' stream.send(iq) q.forbid_events( [ EventPattern('dbus-signal', signal='MembersChanged', path=stored.object_path), EventPattern('dbus-signal', signal='ContactsChanged'), ]) # Make sure Gabble's got the evil push... sync_stream(q, stream) # ...and make sure it's not emitted anything. sync_dbus(bus, q, conn) if __name__ == '__main__': exec_test(test) telepathy-gabble-0.18.2/tests/twisted/roster/push-from-contact.py0000644000175000017500000000234212227000321025071 0ustar00smcvsmcv00000000000000""" Ensure that Gabble correctly ignores roster pushes from contacts. """ from servicetest import EventPattern from gabbletest import exec_test, acknowledge_iq from rostertest import ( make_roster_push, expect_contact_list_signals, check_contact_list_signals, ) import ns import constants as cs jid = 'moonboots@xsf.lit' def test(q, bus, conn, stream): # Gabble asks for the roster; the server sends back an empty roster. event = q.expect('stream-iq', query_ns=ns.ROSTER) acknowledge_iq(stream, event.stanza) pairs = expect_contact_list_signals(q, bus, conn, ['stored']) stored = check_contact_list_signals(q, bus, conn, pairs.pop(0), cs.HT_LIST, 'stored', []) # Some malicious peer sends us a roster push to try to trick us into # showing them on our roster. Gabble should know better than to trust it. iq = make_roster_push(stream, jid, 'both') iq['from'] = jid stream.send(iq) q.forbid_events( [ EventPattern('dbus-signal', signal='MembersChanged', path=stored.object_path), EventPattern('dbus-signal', signal='ContactsChanged'), ]) e = q.expect('stream-iq', iq_type='error') if __name__ == '__main__': exec_test(test) telepathy-gabble-0.18.2/tests/twisted/roster/initial-aliases.py0000644000175000017500000000277112227000321024576 0ustar00smcvsmcv00000000000000""" Test retrieving the aliases after connection. """ from servicetest import ( assertContains, assertLength ) from gabbletest import ( exec_test, make_result_iq ) import constants as cs import ns def add_contact(iq, jid, alias): query = iq.firstChildElement() item = query.addElement('item') item['jid'] = jid item['name'] = alias item['subscription'] = 'both' def test(q, bus, conn, stream): """ Check that when we receive a roster update we emit a single AliasesChanged for all the contacts. """ conn.Connect() # Gabble asks the server for the initial roster event = q.expect('stream-iq', iq_type='get', query_ns=ns.ROSTER) result = make_result_iq(stream, event.stanza) # We reply with a roster with two contacts bob_jid = 'bob.smith@example.com' bob_alias = 'Bob Smith' add_contact(result, bob_jid, bob_alias) alice_jid = 'alice@example.com' alice_alias = 'Alice' add_contact(result, alice_jid, alice_alias) stream.send(result) # Check we get a single AliasesChanged for both contacts event = q.expect('dbus-signal', signal='AliasesChanged') added = event.args[0] bob_handle, alice_handle = conn.RequestHandles(cs.HT_CONTACT, [bob_jid, alice_jid]) assertLength(2, added) assertContains((bob_handle, bob_alias), added) assertContains((alice_handle, alice_alias), added) if __name__ == '__main__': exec_test(test, do_connect=False) telepathy-gabble-0.18.2/tests/twisted/roster/groups.py0000644000175000017500000002100312227000321023032 0ustar00smcvsmcv00000000000000""" Test basic roster group functionality. """ from gabbletest import exec_test, acknowledge_iq, sync_stream from rostertest import expect_contact_list_signals, check_contact_list_signals from servicetest import (assertLength, EventPattern, assertEquals, call_async, sync_dbus, assertContains, assertDoesNotContain) import constants as cs import ns from twisted.words.protocols.jabber.client import IQ from twisted.words.xish import xpath def parse_roster_change_request(query, iq): item = query.firstChildElement() groups = set() for gn in xpath.queryForNodes('/iq/query/item/group', iq): groups.add(str(gn)) return item['jid'], groups def send_roster_push(stream, jid, groups): iq = IQ(stream, 'set') query = iq.addElement((ns.ROSTER, 'query')) item = query.addElement('item') item['jid'] = jid item['subscription'] = 'both' for group in groups: item.addElement('group', content=group) stream.send(iq) def test(q, bus, conn, stream): event = q.expect('stream-iq', query_ns=ns.ROSTER) event.stanza['type'] = 'result' item = event.query.addElement('item') item['jid'] = 'amy@foo.com' item['subscription'] = 'both' item.addElement('group', content='women') item = event.query.addElement('item') item['jid'] = 'bob@foo.com' item['subscription'] = 'from' item.addElement('group', content='men') item = event.query.addElement('item') item['jid'] = 'che@foo.com' item['subscription'] = 'to' item.addElement('group', content='men') stream.send(event.stanza) # Avoid relying on the implementation detail of exactly when # TpBaseContactList emits ContactsChanged, relative to when it # announces its channels. Prior to 0.20.3, 0.21.1 it would # announce the channels, emit GroupsChanged, then announce the channels # again... which was a bug, but it turned out this test relied on it. # # We do still rely on the implementation detail that we emit GroupsChanged # once per group with all of its members, not once per contact with all # of their groups. On a typical contact list, there are more contacts # than groups, so that'll work out smaller. pairs, groups_changed = expect_contact_list_signals(q, bus, conn, [], ['men', 'women'], [ EventPattern('dbus-signal', signal='GroupsChanged', interface=cs.CONN_IFACE_CONTACT_GROUPS, path=conn.object_path, predicate=lambda e: 'women' in e.args[1]), EventPattern('dbus-signal', signal='GroupsChanged', interface=cs.CONN_IFACE_CONTACT_GROUPS, path=conn.object_path, predicate=lambda e: 'men' in e.args[1]), ]) amy, bob, che = conn.RequestHandles(cs.HT_CONTACT, ['amy@foo.com', 'bob@foo.com', 'che@foo.com']) assertEquals([[amy], ['women'], []], groups_changed[0].args) assertEquals([[bob, che], ['men'], []], groups_changed[1].args) q.expect('dbus-signal', signal='ContactListStateChanged', args=[cs.CONTACT_LIST_STATE_SUCCESS]) check_contact_list_signals(q, bus, conn, pairs.pop(0), cs.HT_GROUP, 'men', ['bob@foo.com', 'che@foo.com']) check_contact_list_signals(q, bus, conn, pairs.pop(0), cs.HT_GROUP, 'women', ['amy@foo.com']) assertLength(0, pairs) # i.e. we've checked all of them # change Amy's groups call_async(q, conn.ContactGroups, 'SetContactGroups', amy, ['ladies', 'people starting with A']) s, iq = q.expect_many( EventPattern('dbus-signal', signal='GroupsCreated'), EventPattern('stream-iq', iq_type='set', query_name='query', query_ns=ns.ROSTER), ) assertEquals(set(('ladies', 'people starting with A')), set(s.args[0])) jid, groups = parse_roster_change_request(iq.query, iq.stanza) assertEquals('amy@foo.com', jid) assertEquals(set(('ladies', 'people starting with A')), groups) acknowledge_iq(stream, iq.stanza) q.expect('dbus-return', method='SetContactGroups') # Now the server sends us a roster push. send_roster_push(stream, 'amy@foo.com', ['people starting with A', 'ladies']) # We get a single signal corresponding to that roster push e = q.expect('dbus-signal', signal='GroupsChanged', predicate=lambda e: e.args[0] == [amy]) assertEquals(set(['ladies', 'people starting with A']), set(e.args[1])) assertEquals(['women'], e.args[2]) # check that Amy's state is what we expected attrs = conn.Contacts.GetContactAttributes([amy], [cs.CONN_IFACE_CONTACT_GROUPS], False)[amy] # make the group list order-independent attrs[cs.CONN_IFACE_CONTACT_GROUPS + '/groups'] = \ set(attrs[cs.CONN_IFACE_CONTACT_GROUPS + '/groups']) assertEquals({ cs.CONN_IFACE_CONTACT_GROUPS + '/groups': set(['ladies', 'people starting with A']), cs.CONN + '/contact-id': 'amy@foo.com' }, attrs) for it_worked in (False, True): # remove a group with a member (the old API couldn't do this) call_async(q, conn.ContactGroups, 'RemoveGroup', 'people starting with A') iq = q.expect('stream-iq', iq_type='set', query_name='query', query_ns=ns.ROSTER) jid, groups = parse_roster_change_request(iq.query, iq.stanza) assertEquals('amy@foo.com', jid) assertEquals(set(('ladies',)), groups) acknowledge_iq(stream, iq.stanza) # we emit these as soon as the IQ is ack'd, so that we can indicate # group removal... q.expect('dbus-signal', signal='GroupsRemoved', args=[['people starting with A']]) q.expect('dbus-signal', signal='GroupsChanged', args=[[amy], [], ['people starting with A']]) q.expect('dbus-return', method='RemoveGroup') if it_worked: # ... although in fact this is what *actually* removes Amy from the # group send_roster_push(stream, 'amy@foo.com', ['ladies']) else: # if the change didn't "stick", this message will revert it send_roster_push(stream, 'amy@foo.com', ['ladies', 'people starting with A']) q.expect('dbus-signal', signal='GroupsCreated', args=[['people starting with A']]) q.expect('dbus-signal', signal='GroupsChanged', args=[[amy], ['people starting with A'], []]) sync_dbus(bus, q, conn) sync_stream(q, stream) assertEquals({ cs.CONN_IFACE_CONTACT_GROUPS + '/groups': ['ladies', 'people starting with A'], cs.CONN + '/contact-id': 'amy@foo.com' }, conn.Contacts.GetContactAttributes([amy], [cs.CONN_IFACE_CONTACT_GROUPS], False)[amy]) # sanity check: after all that, we expect Amy to be in group 'ladies' only sync_dbus(bus, q, conn) sync_stream(q, stream) assertEquals({ cs.CONN_IFACE_CONTACT_GROUPS + '/groups': ['ladies'], cs.CONN + '/contact-id': 'amy@foo.com' }, conn.Contacts.GetContactAttributes([amy], [cs.CONN_IFACE_CONTACT_GROUPS], False)[amy]) # Rename group 'ladies' to 'girls' call_async(q, conn.ContactGroups, 'RenameGroup', 'ladies', 'girls') # Amy is added to 'girls' e = q.expect('stream-iq', iq_type='set', query_name='query', query_ns=ns.ROSTER) jid, groups = parse_roster_change_request(e.query, e.stanza) assertEquals('amy@foo.com', jid) assertEquals(set(['girls', 'ladies']), groups) send_roster_push(stream, 'amy@foo.com', ['girls', 'ladies']) acknowledge_iq(stream, e.stanza) # Amy is removed from 'ladies' e = q.expect('stream-iq', iq_type='set', query_name='query', query_ns=ns.ROSTER) jid, groups = parse_roster_change_request(e.query, e.stanza) assertEquals('amy@foo.com', jid) assertEquals(set(['girls']), groups) send_roster_push(stream, 'amy@foo.com', ['girls']) acknowledge_iq(stream, e.stanza) q.expect('dbus-return', method='RenameGroup') # check everything has been updated groups = conn.Properties.Get(cs.CONN_IFACE_CONTACT_GROUPS, 'Groups') assertContains('girls', groups) assertDoesNotContain('ladies', groups) contacts = conn.ContactList.GetContactListAttributes([cs.CONN_IFACE_CONTACT_GROUPS], False) assertEquals(['girls'], contacts[amy][cs.CONN_IFACE_CONTACT_GROUPS + '/groups']) if __name__ == '__main__': exec_test(test) telepathy-gabble-0.18.2/tests/twisted/roster/groups-12791.py0000644000175000017500000000647312227000321023531 0ustar00smcvsmcv00000000000000""" Test broken groups on the roster (regression test for fd.o #12791) """ import dbus from gabbletest import exec_test from rostertest import expect_contact_list_signals, check_contact_list_signals from servicetest import assertLength import constants as cs import ns def _expect_group_channel(q, bus, conn, name, contacts): event = q.expect('dbus-signal', signal='NewChannel') path, type, handle_type, handle, suppress_handler = event.args assert type == cs.CHANNEL_TYPE_CONTACT_LIST, type assert handle_type == cs.HT_GROUP, handle_type inspected = conn.InspectHandles(handle_type, [handle])[0] assert inspected == name, (inspected, name) chan = bus.get_object(conn.bus_name, path) group_iface = dbus.Interface(chan, cs.CHANNEL_IFACE_GROUP) inspected = conn.InspectHandles(cs.HT_CONTACT, group_iface.GetMembers()) assert inspected == contacts, (inspected, contacts) def test(q, bus, conn, stream): event = q.expect('stream-iq', query_ns=ns.ROSTER) event.stanza['type'] = 'result' item = event.query.addElement('item') item['jid'] = 'amy@foo.com' item['subscription'] = 'both' item.addElement('group', content='women') item.addElement('group', content='affected-by-fdo-12791') # This is a broken roster - Amy appears twice. This should only happen # if the server is somehow buggy. This was my initial attempt at # reproducing fd.o #12791 - I doubt it's very realistic, but we shouldn't # assert, regardless of what input we get! item = event.query.addElement('item') item['jid'] = 'amy@foo.com' item['subscription'] = 'both' item.addElement('group', content='women') item = event.query.addElement('item') item['jid'] = 'bob@foo.com' item['subscription'] = 'from' item.addElement('group', content='men') # This is what was *actually* strange about the #12791 submitter's roster - # Bob appears, fully subscribed, but also there's an attempt to subscribe # to one of Bob's resources. We now ignore such items item = event.query.addElement('item') item['jid'] = 'bob@foo.com/Resource' item['subscription'] = 'none' item['ask'] = 'subscribe' item = event.query.addElement('item') item['jid'] = 'che@foo.com' item['subscription'] = 'to' item.addElement('group', content='men') stream.send(event.stanza) pairs = expect_contact_list_signals(q, bus, conn, ['publish', 'subscribe', 'stored'], ['men', 'women', 'affected-by-fdo-12791']) check_contact_list_signals(q, bus, conn, pairs.pop(0), cs.HT_LIST, 'publish', ['amy@foo.com', 'bob@foo.com']) check_contact_list_signals(q, bus, conn, pairs.pop(0), cs.HT_LIST, 'subscribe', ['amy@foo.com', 'che@foo.com']) check_contact_list_signals(q, bus, conn, pairs.pop(0), cs.HT_LIST, 'stored', ['amy@foo.com', 'bob@foo.com', 'che@foo.com']) check_contact_list_signals(q, bus, conn, pairs.pop(0), cs.HT_GROUP, 'men', ['bob@foo.com', 'che@foo.com']) check_contact_list_signals(q, bus, conn, pairs.pop(0), cs.HT_GROUP, 'women', ['amy@foo.com']) check_contact_list_signals(q, bus, conn, pairs.pop(0), cs.HT_GROUP, 'affected-by-fdo-12791', []) assertLength(0, pairs) # i.e. we've checked all of them if __name__ == '__main__': exec_test(test) telepathy-gabble-0.18.2/tests/twisted/roster/ensure.py0000644000175000017500000000330412227000321023020 0ustar00smcvsmcv00000000000000""" Test ensuring roster channels """ from gabbletest import exec_test from servicetest import call_async import constants as cs import ns def test(q, bus, conn, stream): roster_event = q.expect('stream-iq', query_ns=ns.ROSTER) roster_event.stanza['type'] = 'result' call_async(q, conn, "RequestHandles", cs.HT_GROUP, ['test']) event = q.expect('dbus-return', method='RequestHandles') test_handle = event.value[0][0] # send an empty roster stream.send(roster_event.stanza) call_async(q, conn.Requests, 'EnsureChannel', { cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_CONTACT_LIST, cs.TARGET_HANDLE_TYPE: cs.HT_GROUP, cs.TARGET_HANDLE: test_handle, }) call_async(q, conn.Requests, 'EnsureChannel', { cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_CONTACT_LIST, cs.TARGET_HANDLE_TYPE: cs.HT_GROUP, cs.TARGET_HANDLE: test_handle, }) ret = q.expect('dbus-return', method='EnsureChannel') ret2 = q.expect('dbus-return', method='EnsureChannel') # We don't test the NewChannels signal here - depending on exact timing, # it might happen between the two EnsureChannel calls, or after the second # one. yours, path, props = ret.value yours2, path2, props2 = ret2.value assert props[cs.CHANNEL_TYPE] == cs.CHANNEL_TYPE_CONTACT_LIST, props assert props[cs.TARGET_HANDLE_TYPE] == cs.HT_GROUP, props assert props[cs.TARGET_HANDLE] == test_handle, props assert props[cs.TARGET_ID] == 'test', props assert yours != yours2, (yours, yours2) assert path == path2, (path, path2) assert props == props2, (props, props2) if __name__ == '__main__': exec_test(test) telepathy-gabble-0.18.2/tests/twisted/roster/edit-before-roster.py0000644000175000017500000001043312227000321025221 0ustar00smcvsmcv00000000000000""" Test editing the roster before we've received it. """ from gabbletest import exec_test, acknowledge_iq, sync_stream from servicetest import (EventPattern, assertEquals, call_async, sync_dbus) import constants as cs import ns from twisted.words.protocols.jabber.client import IQ from twisted.words.xish import xpath def test(q, bus, conn, stream): event = q.expect('stream-iq', query_ns=ns.ROSTER) event.stanza['type'] = 'result' item = event.query.addElement('item') item['jid'] = 'amy@foo.com' item['subscription'] = 'both' item.addElement('group', content='women') item = event.query.addElement('item') item['jid'] = 'bob@foo.com' item['subscription'] = 'from' item.addElement('group', content='men') item = event.query.addElement('item') item['jid'] = 'che@foo.com' item['subscription'] = 'to' item.addElement('group', content='men') # Before we get the roster, try to change something. It won't work. amy, bob, che = conn.RequestHandles(cs.HT_CONTACT, ['amy@foo.com', 'bob@foo.com', 'che@foo.com']) call_async(q, conn.ContactGroups, 'AddToGroup', 'Amy & Bob', [amy, bob]) q.expect('dbus-error', method='AddToGroup', name=cs.NOT_YET) # Now send the roster, and things will happen. stream.send(event.stanza) def mentions_amy(e): return bool(xpath.queryForNodes('/iq/query/item[@jid = "amy@foo.com"]', e.stanza)) def mentions_bob(e): return bool(xpath.queryForNodes('/iq/query/item[@jid = "bob@foo.com"]', e.stanza)) s1, s2, _ = q.expect_many( EventPattern('dbus-signal', signal='GroupsChanged', interface=cs.CONN_IFACE_CONTACT_GROUPS, path=conn.object_path, predicate=lambda e: 'women' in e.args[1]), EventPattern('dbus-signal', signal='GroupsChanged', interface=cs.CONN_IFACE_CONTACT_GROUPS, path=conn.object_path, predicate=lambda e: 'men' in e.args[1]), EventPattern('dbus-signal', signal='ContactListStateChanged', args=[cs.CONTACT_LIST_STATE_SUCCESS]), ) call_async(q, conn.ContactGroups, 'AddToGroup', 'Amy & Bob', [amy, bob]) set1, set2 = q.expect_many( EventPattern('stream-iq', query_ns=ns.ROSTER, query_name='query', iq_type='set', predicate=mentions_amy), EventPattern('stream-iq', query_ns=ns.ROSTER, query_name='query', iq_type='set', predicate=mentions_bob), ) assertEquals([[amy], ['women'], []], s1.args) assertEquals([[bob, che], ['men'], []], s2.args) item = set1.query.firstChildElement() assertEquals('amy@foo.com', item['jid']) groups = set() for gn in xpath.queryForNodes('/iq/query/item/group', set1.stanza): groups.add(str(gn)) assertEquals(set(('Amy & Bob', 'women')), groups) item = set2.query.firstChildElement() assertEquals('bob@foo.com', item['jid']) groups = set() for gn in xpath.queryForNodes('/iq/query/item/group', set2.stanza): groups.add(str(gn)) assertEquals(set(('Amy & Bob', 'men')), groups) # Send a couple of roster pushes to reflect Amy and Bob's new states. iq = IQ(stream, 'set') query = iq.addElement((ns.ROSTER, 'query')) item = query.addElement('item') item['jid'] = 'amy@foo.com' item['subscription'] = 'both' item.addElement('group', content='women') item.addElement('group', content='Amy & Bob') stream.send(iq) q.expect('dbus-signal', signal='GroupsChanged', args=[[amy], ['Amy & Bob'], []]) iq = IQ(stream, 'set') query = iq.addElement((ns.ROSTER, 'query')) item = query.addElement('item') item['jid'] = 'bob@foo.com' item['subscription'] = 'both' item.addElement('group', content='men') item.addElement('group', content='Amy & Bob') stream.send(iq) q.expect('dbus-signal', signal='GroupsChanged', args=[[bob], ['Amy & Bob'], []]) # Acknowledge Amy's IQ, but make sure we don't see the acknowledgement # until Bob's IQ has replied too acknowledge_iq(stream, set1.stanza) sync_dbus(bus, q, conn) sync_stream(q, stream) # When we get the reply for Bob, it actually happens. acknowledge_iq(stream, set2.stanza) q.expect('dbus-return', method='AddToGroup') if __name__ == '__main__': exec_test(test) telepathy-gabble-0.18.2/tests/twisted/roster/authorize.py0000644000175000017500000002664412227000321023545 0ustar00smcvsmcv00000000000000""" Test receiving and authorizing publish requests, including "pre-authorization" (authorizing publication before someone asks for it). """ from gabbletest import (exec_test, sync_stream, acknowledge_iq) from rostertest import (expect_contact_list_signals, check_contact_list_signals, send_roster_push) from servicetest import (assertEquals, assertLength, call_async, EventPattern, sync_dbus) import constants as cs import ns from twisted.words.xish import domish def test(q, bus, conn, stream, modern=True, remove=False): call_async(q, conn.ContactList, 'GetContactListAttributes', [], False) q.expect('dbus-error', method='GetContactListAttributes', name=cs.NOT_YET) event = q.expect('stream-iq', query_ns=ns.ROSTER) item = event.query.addElement('item') item['jid'] = 'holly@example.com' item['subscription'] = 'both' event.stanza['type'] = 'result' stream.send(event.stanza) holly, dave, arnold, kristine, cat = conn.RequestHandles(cs.HT_CONTACT, ['holly@example.com', 'dave@example.com', 'arnold@example.com', 'kristine@example.com', 'cat@example.com']) # slight implementation detail: TpBaseContactList emits ContactsChanged # before it announces its channels s = q.expect('dbus-signal', signal='ContactsChanged', interface=cs.CONN_IFACE_CONTACT_LIST, path=conn.object_path) assertEquals([{ holly: (cs.SUBSCRIPTION_STATE_YES, cs.SUBSCRIPTION_STATE_YES, ''), }, []], s.args) pairs = expect_contact_list_signals(q, bus, conn, ['publish', 'subscribe', 'stored']) # this is emitted last, so clients can tell when the initial state dump # has finished q.expect('dbus-signal', signal='ContactListStateChanged', args=[cs.CONTACT_LIST_STATE_SUCCESS]) call_async(q, conn.ContactList, 'GetContactListAttributes', [], False) r = q.expect('dbus-return', method='GetContactListAttributes') assertEquals(({ holly: { cs.CONN_IFACE_CONTACT_LIST + '/publish': cs.SUBSCRIPTION_STATE_YES, cs.CONN_IFACE_CONTACT_LIST + '/subscribe': cs.SUBSCRIPTION_STATE_YES, cs.CONN + '/contact-id': 'holly@example.com', } },), r.value) # check that the channels were as we expected too publish = check_contact_list_signals(q, bus, conn, pairs.pop(0), cs.HT_LIST, 'publish', ['holly@example.com']) check_contact_list_signals(q, bus, conn, pairs.pop(0), cs.HT_LIST, 'subscribe', ['holly@example.com']) stored = check_contact_list_signals(q, bus, conn, pairs.pop(0), cs.HT_LIST, 'stored', ['holly@example.com']) assertLength(0, pairs) # i.e. we've checked all of them # publication authorized for Dave, Holly (the former is pre-authorization, # the latter is a no-op) if modern: call_async(q, conn.ContactList, 'AuthorizePublication', [dave, holly]) event = q.expect('dbus-return', method='AuthorizePublication') else: call_async(q, publish.Group, 'AddMembers', [dave, holly], '') event = q.expect('dbus-return', method='AddMembers') # Receive authorization requests from the contacts # We pre-authorized Dave, so this is automatically approved presence = domish.Element(('jabber:client', 'presence')) presence['type'] = 'subscribe' presence['from'] = 'dave@example.com' stream.send(presence) q.expect_many( EventPattern('dbus-signal', signal='ContactsChanged', args=[{dave: (cs.SUBSCRIPTION_STATE_NO, cs.SUBSCRIPTION_STATE_ASK, '')}, []]), EventPattern('stream-presence', presence_type='subscribed', to='dave@example.com'), ) # Our server responds to Dave being authorized send_roster_push(stream, 'dave@example.com', 'from') q.expect_many( EventPattern('stream-iq', iq_type='result', iq_id='push'), EventPattern('dbus-signal', signal='ContactsChanged', args=[{dave: (cs.SUBSCRIPTION_STATE_NO, cs.SUBSCRIPTION_STATE_YES, '')}, []]), ) # The request from Kristine needs authorization (below) presence['from'] = 'kristine@example.com' stream.send(presence) q.expect('dbus-signal', signal='ContactsChanged', args=[{kristine: (cs.SUBSCRIPTION_STATE_NO, cs.SUBSCRIPTION_STATE_ASK, '')}, []]) # This request from Arnold is dealt with below presence['from'] = 'arnold@example.com' stream.send(presence) q.expect('dbus-signal', signal='ContactsChanged', args=[{arnold: (cs.SUBSCRIPTION_STATE_NO, cs.SUBSCRIPTION_STATE_ASK, '')}, []]) if modern: returning_method = 'AuthorizePublication' call_async(q, conn.ContactList, 'AuthorizePublication', [kristine, holly]) else: returning_method = 'AddMembers' call_async(q, publish.Group, 'AddMembers', [kristine, holly], '') q.expect_many( EventPattern('dbus-return', method=returning_method), EventPattern('stream-presence', presence_type='subscribed', to='kristine@example.com'), ) # Our server acknowledges that we authorized Kristine. Holly's state # does not change. send_roster_push(stream, 'kristine@example.com', 'from') q.expect_many( EventPattern('dbus-signal', signal='ContactsChanged', args=[{kristine: (cs.SUBSCRIPTION_STATE_NO, cs.SUBSCRIPTION_STATE_YES, '')}, []]), EventPattern('stream-iq', iq_type='result', iq_id='push'), ) # Arnold gives up waiting for us, and cancels his request presence['from'] = 'arnold@example.com' presence['type'] = 'unsubscribe' stream.send(presence) q.expect_many( EventPattern('dbus-signal', signal='ContactsChanged', args=[{arnold: (cs.SUBSCRIPTION_STATE_NO, cs.SUBSCRIPTION_STATE_REMOVED_REMOTELY, '')}, []]), EventPattern('stream-presence', presence_type='unsubscribed', to='arnold@example.com'), ) # We can acknowledge that with RemoveContacts or with Unpublish. # The old Chan.T.ContactList API can't acknowledge RemovedRemotely, # because it sees it as "not there at all" and the group logic drops # the "redundant" request. if remove: returning_method = 'RemoveContacts' call_async(q, conn.ContactList, 'RemoveContacts', [arnold]) else: returning_method = 'Unpublish' call_async(q, conn.ContactList, 'Unpublish', [arnold]) # Even if we Unpublish() here, Arnold was never on our XMPP roster, # so setting his publish state to SUBSCRIPTION_STATE_NO should result # in his removal. q.expect_many( EventPattern('dbus-return', method=returning_method), EventPattern('dbus-signal', signal='ContactsChanged', args=[{}, [arnold]]), ) # Rejecting an authorization request also works presence = domish.Element(('jabber:client', 'presence')) presence['type'] = 'subscribe' presence['from'] = 'cat@example.com' stream.send(presence) q.expect('dbus-signal', signal='ContactsChanged', args=[{cat: (cs.SUBSCRIPTION_STATE_NO, cs.SUBSCRIPTION_STATE_ASK, '')}, []]) if modern: if remove: returning_method = 'RemoveContacts' call_async(q, conn.ContactList, 'RemoveContacts', [cat]) else: returning_method = 'Unpublish' call_async(q, conn.ContactList, 'Unpublish', [cat]) else: returning_method = 'RemoveMembers' if remove: call_async(q, stored.Group, 'RemoveMembers', [cat], '') else: call_async(q, publish.Group, 'RemoveMembers', [cat], '') # As above, the only reason the Cat is on our contact list is the pending # publish request, so Unpublish really results in removal. q.expect_many( EventPattern('dbus-return', method=returning_method), EventPattern('dbus-signal', signal='ContactsChanged', args=[{}, [cat]]), ) # Redundant API calls (removing an absent contact, etc.) cause no network # traffic, and succeed. forbidden = [EventPattern('stream-iq', query_ns=ns.ROSTER), EventPattern('stream-presence')] sync_stream(q, stream) sync_dbus(bus, q, conn) q.forbid_events(forbidden) call_async(q, conn.ContactList, 'AuthorizePublication', [kristine, holly, dave]) call_async(q, conn.ContactList, 'Unpublish', [arnold, cat]) call_async(q, conn.ContactList, 'RemoveContacts', [arnold, cat]) q.expect_many( EventPattern('dbus-return', method='AuthorizePublication'), EventPattern('dbus-return', method='Unpublish'), EventPattern('dbus-return', method='RemoveContacts'), ) sync_stream(q, stream) sync_dbus(bus, q, conn) q.unforbid_events(forbidden) # There's one more case: revoking the publish permission of someone who is # genuinely on the roster. if modern: if remove: returning_method = 'RemoveContacts' call_async(q, conn.ContactList, 'RemoveContacts', [holly]) else: returning_method = 'Unpublish' call_async(q, conn.ContactList, 'Unpublish', [holly]) else: returning_method = 'RemoveMembers' if remove: call_async(q, stored.Group, 'RemoveMembers', [holly], '') else: call_async(q, publish.Group, 'RemoveMembers', [holly], '') if remove: iq = q.expect('stream-iq', iq_type='set', query_ns=ns.ROSTER, query_name='query') acknowledge_iq(stream, iq.stanza) if modern: q.expect('dbus-return', method='RemoveContacts') # FIXME: when we depend on a new enough tp-glib, expect RemoveMembers # to return here too send_roster_push(stream, 'holly@example.com', 'remove') q.expect_many( EventPattern('stream-iq', iq_type='result', iq_id='push'), EventPattern('dbus-signal', signal='ContactsChanged', args=[{}, [holly]]), ) else: q.expect_many( EventPattern('dbus-return', method=returning_method), EventPattern('stream-presence', presence_type='unsubscribed', to='holly@example.com'), ) send_roster_push(stream, 'holly@example.com', 'to') q.expect_many( EventPattern('stream-iq', iq_type='result', iq_id='push'), EventPattern('dbus-signal', signal='ContactsChanged', args=[{holly: (cs.SUBSCRIPTION_STATE_YES, cs.SUBSCRIPTION_STATE_NO, ''), }, []]), ) def test_ancient(q, bus, conn, stream): test(q, bus, conn, stream, modern=False) def test_ancient_remove(q, bus, conn, stream): test(q, bus, conn, stream, modern=False, remove=True) def test_modern(q, bus, conn, stream): test(q, bus, conn, stream, modern=True) def test_modern_remove(q, bus, conn, stream): test(q, bus, conn, stream, modern=True, remove=True) if __name__ == '__main__': exec_test(test_ancient) exec_test(test_ancient_remove) exec_test(test_modern) exec_test(test_modern_remove) telepathy-gabble-0.18.2/tests/twisted/presence/0000755000175000017500000000000012312537051021445 5ustar00smcvsmcv00000000000000telepathy-gabble-0.18.2/tests/twisted/presence/invisible_helper.py0000644000175000017500000001556012200204333025337 0ustar00smcvsmcv00000000000000from gabbletest import ( XmppXmlStream, elem, elem_iq, send_error_reply, acknowledge_iq ) import ns from twisted.words.xish import xpath from time import time class ManualPrivacyListStream(XmppXmlStream): """Unlike the base class, which automatically responds to privacy list requests in the negative, this stream class does not automatically respond. Instead it provides helper methods to let your test expect requests and respond to them manually.""" handle_privacy_lists = False def send_privacy_list_list(self, iq_id, lists=[]): list_elements = [elem('list', name=l) for l in lists] iq = elem_iq(self, "result", id=iq_id)( elem(ns.PRIVACY, 'query')(*list_elements)) self.send(iq) def handle_get_all_privacy_lists(self, q, bus, conn, lists=[]): e = q.expect('stream-iq', query_ns=ns.PRIVACY, iq_type='get') self.send_privacy_list_list(e.iq_id, lists) def send_privacy_list_push_iq(self, list_name): iq = elem_iq(self, 'set')( elem(ns.PRIVACY, 'query')( elem('list', name=list_name) ) ) self.send(iq) return iq["id"] def send_privacy_list(self, req_iq, list_items): req_list = xpath.queryForNodes('//list', req_iq)[0] iq = elem_iq(self, "result", id=req_iq["id"])( elem(ns.PRIVACY, 'query')( elem('list', name=req_list["name"])(*list_items) ) ) self.send(iq) class ValidInvisibleListStream(ManualPrivacyListStream): """This stream class pretends to be a server which supports privacy lists. It has exactly one stored list, named 'invisible', which satisfies Gabble's idea of what an invisible list should look like. Activating that list, or disabling the active list, will succeed; any attempts to activate or modify other lists will fail. The intention is that this class could be used to run presence tests unrelated to invisibility against a server which supports invisibility.""" def __init__(self, event_func, authenticator): ManualPrivacyListStream.__init__(self, event_func, authenticator) self.addObserver("/iq/query[@xmlns='%s']" % ns.PRIVACY, self.privacy_list_iq_cb) def privacy_list_iq_cb(self, iq): if iq.getAttribute("type") == 'set': active = xpath.queryForNodes("//active", iq) if active and ('name' not in active[0].attributes or active[0]['name'] == 'invisible'): acknowledge_iq(self, iq) else: # Don't allow other lists to be activated; and don't allow # modifications. send_error_reply(self, iq) else: requested_lists = xpath.queryForNodes('//list', iq) if not requested_lists: self.send_privacy_list_list(iq['id'], ['invisible']) elif requested_lists[0]['name'] == 'invisible': self.send_privacy_list(iq, [elem('item', action='deny', order='1')( elem('presence-out') ) ]) else: send_error_reply(self, iq, elem(ns.STANZA, 'item-not-found')) class Xep0186Stream(XmppXmlStream): disco_features = [ns.INVISIBLE] class Xep0186AndValidInvisibleListStream(ValidInvisibleListStream): disco_features = [ns.INVISIBLE] class Xep0186AndManualPrivacyListStream(ManualPrivacyListStream): disco_features = [ns.INVISIBLE] class SharedStatusStream(XmppXmlStream): disco_features = [ns.GOOGLE_SHARED_STATUS] handle_privacy_lists = False def __init__(self, event_func, authenticator): XmppXmlStream.__init__(self, event_func, authenticator) self.addObserver("/iq/query[@xmlns='%s']" % ns.GOOGLE_SHARED_STATUS, self.shared_status_iq_cb) self.shared_status_lists = {u'default' : [u'Pining away', u'Wherefore indeed', u'Thinking about the sun'], u'dnd' : [u'Chilling with Mercutio', u'Visiting the monk']} self.shared_status = (u'Pining away', u'default', u'false') self.min_version = '2' self.max_status_message_length = '512' self.max_statuses = '5' def set_shared_status_lists(self, shared_status_lists=None, status=None, show=None, invisible=None, min_version=None): self.shared_status_lists = shared_status_lists or \ self.shared_status_lists _status, _show, _invisible = self.shared_status self.shared_status = (status or _status, show or _show, invisible or _invisible) self.min_version = min_version or self.min_version self._send_status_list() def _send_status_list(self, iq_id=None): if iq_id is None: iq_id = str(int(time())) iq_type = "set" else: iq_type = "result" status, show, invisible = self.shared_status elems = [] elems.append(elem('status')(unicode(status))) elems.append(elem('show')(unicode(show))) for show, statuses in self.shared_status_lists.items(): lst = [] for _status in statuses: lst.append(elem('status')(unicode(_status))) elems.append(elem('status-list', show=show)(*lst)) elems.append(elem('invisible', value=invisible)()) attribs = {'status-max' : self.max_status_message_length, 'status-list-max' : '3', 'status-list-contents-max' : self.max_statuses, 'status-min-ver' : self.min_version} iq = elem_iq(self, iq_type, id=iq_id)( elem(ns.GOOGLE_SHARED_STATUS, 'query', **attribs)(*elems)) self.send(iq) def _store_shared_statuses(self, iq): _status = xpath.queryForNodes('//status', iq)[0] _show = xpath.queryForNodes('//show', iq)[0] _invisible = xpath.queryForNodes('//invisible', iq)[0] self.shared_status = (str(_status), str(_show), _invisible.getAttribute('value')) _status_lists = xpath.queryForNodes('//status-list', iq) self.shared_status_lists = {} for s in _status_lists: self.shared_status_lists[s.getAttribute('show')] = \ [str(e) for e in xpath.queryForNodes('//status', s)] def shared_status_iq_cb(self, req_iq): if req_iq.getAttribute("type") == 'get': self._send_status_list(req_iq['id']) if req_iq.getAttribute("type") == 'set': self._store_shared_statuses(req_iq) self.send(elem_iq(self, "result", id=req_iq['id'])()) telepathy-gabble-0.18.2/tests/twisted/presence/__init__.py0000644000175000017500000000000012200204333023532 0ustar00smcvsmcv00000000000000telepathy-gabble-0.18.2/tests/twisted/presence/shared-status.py0000644000175000017500000004065212227000321024603 0ustar00smcvsmcv00000000000000# coding=utf-8 """ A simple smoke-test for Google Shared Status. See: http://code.google.com/apis/talk/jep_extensions/shared_status.html """ from gabbletest import ( exec_test, XmppXmlStream, acknowledge_iq, send_error_reply, disconnect_conn, elem, elem_iq ) from servicetest import ( EventPattern, assertEquals, assertNotEquals, assertContains, assertDoesNotContain, sync_dbus ) import ns import copy import constants as cs import dbus from twisted.words.xish import xpath, domish from invisible_helper import SharedStatusStream presence_types = {'available' : cs.PRESENCE_AVAILABLE, 'away' : cs.PRESENCE_AWAY, 'xa' : cs.PRESENCE_EXTENDED_AWAY, 'hidden' : cs.PRESENCE_HIDDEN, 'dnd' : cs.PRESENCE_BUSY} def _show_to_shared_status_show(show): # Away and extended away don't use shared status. shared_show = 'default' if show == 'dnd': shared_show = 'dnd' shared_invisible = 'false' if show == 'hidden': shared_invisible = 'true' return shared_show, shared_invisible def _test_remote_status(q, bus, conn, stream, msg, show, list_attrs): self = conn.GetSelfHandle() presence = conn.SimplePresence.GetPresences([self])[self] is_away = presence[0] in (cs.PRESENCE_AWAY, cs.PRESENCE_EXTENDED_AWAY) if is_away: # Away is per connection. If we remotely change the shared status, # we have to stay away. _test_remote_status_away(q, bus, conn, stream, msg, show, list_attrs) else: # If we are not away and the remote status changes, we have to change # also our local presence to reflect that. _test_remote_status_not_away(q, stream, msg, show, list_attrs) def _test_remote_status_away(q, bus, conn, stream, msg, show, list_attrs): events = [EventPattern('dbus-signal', signal='PresencesChanged', interface=cs.CONN_IFACE_SIMPLE_PRESENCE, args=[{1: (presence_types[show], show, msg)}])] q.forbid_events(events) list_attrs['status'] = list_attrs.get('status', msg) stream.set_shared_status_lists(**list_attrs) q.expect('stream-iq', iq_type='result') sync_dbus(bus, q, conn) q.unforbid_events(events) def _test_remote_status_not_away(q, stream, msg, show, list_attrs): list_attrs['status'] = list_attrs.get('status', msg) stream.set_shared_status_lists(**list_attrs) q.expect('stream-iq', iq_type='result') q.expect('dbus-signal', signal='PresencesChanged', interface=cs.CONN_IFACE_SIMPLE_PRESENCE, args=[{1: (presence_types[show], show, msg)}]) def _test_local_status(q, conn, stream, msg, show, expected_show=None): expected_show = expected_show or show away = expected_show in ('away', 'xa') self = conn.GetSelfHandle() prev_presence = conn.SimplePresence.GetPresences([self])[self] was_away = prev_presence[0] in (cs.PRESENCE_AWAY, cs.PRESENCE_EXTENDED_AWAY) was_invisible = (prev_presence[0] == cs.PRESENCE_HIDDEN) if away: # Away and extended away are mapped to idle, that is per connection. # This means we use instead of shared presence... if not was_invisible: # ... so in normal cases we don't expect the shared presence # stuff, but ... wrong_presence_pattern = EventPattern('stream-iq', query_ns=ns.GOOGLE_SHARED_STATUS, iq_type='set') else: # ... when switching from invisible we have to leave invisible # first and then go away. wrong_presence_pattern = None elif was_away: # Non-away status, but we were away previously. Considering that we # went away using , we need to also leave it using # plus the shared status. wrong_presence_pattern = None else: # Normal case without away involvement; we just expect the use of # shared status. wrong_presence_pattern = EventPattern('stream-presence') if wrong_presence_pattern: q.forbid_events([wrong_presence_pattern]) conn.SimplePresence.SetPresence(show, msg) max_status_message_length = int(stream.max_status_message_length) if not away or (away and was_invisible): event = q.expect('stream-iq', query_ns=ns.GOOGLE_SHARED_STATUS, iq_type='set') shared_show, shared_invisible = _show_to_shared_status_show(expected_show) _status = xpath.queryForNodes('//status', event.query)[0] assertEquals(msg[:max_status_message_length], _status.children[0]) _show = xpath.queryForNodes('//show', event.query)[0] assertEquals(shared_show, _show.children[0]) _invisible = xpath.queryForNodes('//invisible', event.query)[0] assertEquals(shared_invisible, _invisible.getAttribute('value')) if was_away or (away and was_invisible): q.expect('stream-presence') else: q.expect('stream-presence') q.expect('dbus-signal', signal='PresencesChanged', interface=cs.CONN_IFACE_SIMPLE_PRESENCE, args=[{1: (presence_types[expected_show], expected_show, msg[:max_status_message_length])}]) if wrong_presence_pattern: q.unforbid_events([wrong_presence_pattern]) def test(q, bus, conn, stream): q.expect_many(EventPattern('stream-iq', query_ns=ns.GOOGLE_SHARED_STATUS, iq_type='get'), EventPattern('stream-iq', query_ns=ns.GOOGLE_SHARED_STATUS, iq_type='set'), EventPattern('stream-presence')) # Set shared status to dnd. _test_local_status(q, conn, stream, "Don't disturb, buddy.", "dnd") # Test maximum status message length max_status_message_length = int(stream.max_status_message_length) _test_local_status(q, conn, stream, "ab" * max_status_message_length, "dnd") # Test invisibility _test_local_status(q, conn, stream, "Peekabo", "hidden") _test_local_status(q, conn, stream, "Here!", "available") # Set shared status to default, local status to away. _test_local_status(q, conn, stream, "I'm away right now", "away") _test_local_status(q, conn, stream, "cd" * max_status_message_length, "away") # Test the transition from away to non-away as GTalk treats it in a # different way. _test_local_status(q, conn, stream, "Here!", "available") _test_local_status(q, conn, stream, "I'm away right now", "away") _test_local_status(q, conn, stream, "I'm away right now", "xa") _test_local_status(q, conn, stream, "Here!", "available") # Test the transition from hidden to away. _test_local_status(q, conn, stream, "Peekabo", "hidden") _test_local_status(q, conn, stream, "I'm away right now", "away") # Test if the status is changed correctly from another client. _test_local_status(q, conn, stream, "Don't disturb, buddy.", "dnd") _test_remote_status(q, bus, conn, stream, "This is me busy, set from another client.", "dnd", {"show" : "dnd"}) _test_remote_status(q, bus, conn, stream, "This is me available, set from another client.", "available", {"show" : "default"}) # Test that our own status as exposed over D-Bus doesn't change when # when we are away and other clients change the shared status. _test_local_status(q, conn, stream, "Lunch!", "away") _test_remote_status(q, bus, conn, stream, "This is me busy, set from another client.", "dnd", {"show" : "dnd"}) _test_remote_status(q, bus, conn, stream, "This is me available, set from another client.", "available", {"show" : "default"}) # Change min version stream.set_shared_status_lists(min_version="1") q.expect('stream-iq', iq_type='result') # Going invisible now should fail, we should just be dnd. _test_local_status(q, conn, stream, "Peekabo", "hidden", "dnd") # Let's go back to version 2 stream.set_shared_status_lists(min_version="2") q.expect('stream-iq', iq_type='result') # "hidden" should work again. _test_local_status(q, conn, stream, "Peekabo", "hidden") # Changing min version mid-flight should make us 'dnd' stream.set_shared_status_lists(min_version="1") q.expect('stream-iq', iq_type='result') q.expect('dbus-signal', signal='PresencesChanged', interface=cs.CONN_IFACE_SIMPLE_PRESENCE, args=[{1: (cs.PRESENCE_BUSY, 'dnd', "Peekabo")}]) def _test_on_connect(q, bus, conn, stream, shared_status, show, msg, expected_show=None, min_version=None): expected_show = expected_show or show _status, _show, _invisible = shared_status stream.shared_status = shared_status if min_version is not None: stream.min_version = min_version forbidden_event_patterns = [EventPattern('stream-presence'), EventPattern('stream-iq', query_ns=ns.PRIVACY, iq_type='get')] q.forbid_events(forbidden_event_patterns) conn.SimplePresence.SetPresence(show, msg) conn.Connect() _, event = q.expect_many(EventPattern('stream-iq', query_ns=ns.GOOGLE_SHARED_STATUS, iq_type='get'), EventPattern('stream-iq', query_ns=ns.GOOGLE_SHARED_STATUS, iq_type='set')) shared_show, shared_invisible = _show_to_shared_status_show(show) _status = xpath.queryForNodes('//status', event.query)[0] assertEquals(msg, _status.children[0]) _show = xpath.queryForNodes('//show', event.query)[0] assertEquals(shared_show, _show.children[0]) _invisible = xpath.queryForNodes('//invisible', event.query)[0] assertEquals(shared_invisible, _invisible.getAttribute('value')) q.expect_many( EventPattern('dbus-signal', signal='PresencesChanged', interface=cs.CONN_IFACE_SIMPLE_PRESENCE, args=[{1: (presence_types[expected_show], expected_show, msg)}]), EventPattern('dbus-signal', signal='StatusChanged', args=[cs.CONN_STATUS_CONNECTED, cs.CSR_REQUESTED])) q.unforbid_events(forbidden_event_patterns) def test_connect_available(q, bus, conn, stream): _test_on_connect(q, bus, conn, stream, ("I'm busy, buddy.", 'dnd', 'false'), 'available', "I'm here, baby.") def test_connect_chat(q, bus, conn, stream): _test_on_connect(q, bus, conn, stream, ("I'm busy, buddy.", 'dnd', 'false'), 'chat', "Do you want to chat?", 'available') def test_connect_dnd(q, bus, conn, stream): _test_on_connect(q, bus, conn, stream, ("Chat with me.", 'default', 'false'), 'dnd', "I'm busy, buddy.") def test_connect_hidden(q, bus, conn, stream): _test_on_connect(q, bus, conn, stream, ("Chat with me.", 'default', 'false'), 'hidden', "I see, but I can't be seen") def test_connect_hidden_future_version(q, bus, conn, stream): _test_on_connect(q, bus, conn, stream, ("Chat with me.", 'default', 'false'), 'hidden', "I see, but I can't be seen", min_version='42') def test_connect_hidden_not_available(q, bus, conn, stream): """Fall back to DND if you try to connect while invisible, but shared status is not completely supported.""" _status, _show, _invisible = "Chat with me.", 'default', 'false' msg = "I see, but I don't think I can be seen" show = "hidden" stream.shared_status = (_status, _show, _invisible) stream.min_version = "1" presence_event_pattern = EventPattern('stream-presence') q.forbid_events([presence_event_pattern]) conn.SimplePresence.SetPresence(show, msg) conn.Connect() _, event = q.expect_many(EventPattern('stream-iq', query_ns=ns.GOOGLE_SHARED_STATUS, iq_type='get'), EventPattern('stream-iq', query_ns=ns.GOOGLE_SHARED_STATUS, iq_type='set')) _status = xpath.queryForNodes('//status', event.query)[0] assertEquals(msg, _status.children[0]) _show = xpath.queryForNodes('//show', event.query)[0] assertEquals("dnd", _show.children[0]) _invisible = xpath.queryForNodes('//invisible', event.query)[0] assertEquals("false", _invisible.getAttribute('value')) q.expect_many( EventPattern('dbus-signal', signal='PresencesChanged', interface=cs.CONN_IFACE_SIMPLE_PRESENCE, args=[{1: (cs.PRESENCE_BUSY, "dnd", msg)}]), EventPattern('dbus-signal', signal='StatusChanged', args=[cs.CONN_STATUS_CONNECTED, cs.CSR_REQUESTED])) q.unforbid_events([presence_event_pattern]) def test_shared_status_list(q, bus, conn, stream): '''Test the shared status list usage''' test_statuses = {"dnd" : ['I am not available now', 'I am busy with real work', 'I have a life, you know...', 'I am actually playing Duke Nukem', 'It is important to me'], "available" : ['I am twiddling my thumbs', 'Please chat me up', 'I am here for you', 'Message me already!']} max_statuses = int(stream.max_statuses) q.expect_many(EventPattern('stream-iq', query_ns=ns.GOOGLE_SHARED_STATUS, iq_type='get'), EventPattern('stream-iq', query_ns=ns.GOOGLE_SHARED_STATUS, iq_type='set'), EventPattern('stream-presence')) for show, statuses in test_statuses.items(): shared_show, _ = _show_to_shared_status_show(show) expected_list = stream.shared_status_lists[shared_show] for status in statuses: _test_local_status(q, conn, stream, status, show) expected_list = [status] + expected_list[:max_statuses - 1] assertEquals(expected_list, stream.shared_status_lists[shared_show]) def test_shared_status_away(q, bus, conn, stream): '''Test the shared status lists with away statuses''' q.expect_many(EventPattern('stream-iq', query_ns=ns.GOOGLE_SHARED_STATUS, iq_type='get'), EventPattern('stream-iq', query_ns=ns.GOOGLE_SHARED_STATUS, iq_type='set'), EventPattern('stream-presence')) expected_list = copy.deepcopy(stream.shared_status_lists) for show in ('away', 'xa'): for status in ('not going to', 'be actually set'): _test_local_status(q, conn, stream, status, show) assertEquals(expected_list, stream.shared_status_lists) def test_shared_status_chat(q, bus, conn, stream): '''Test that 'chat' is not supported with shared status''' q.expect_many(EventPattern('stream-iq', query_ns=ns.GOOGLE_SHARED_STATUS, iq_type='get'), EventPattern('stream-iq', query_ns=ns.GOOGLE_SHARED_STATUS, iq_type='set'), EventPattern('stream-presence')) try: conn.SimplePresence.SetPresence('chat', 'This is not going to work') except dbus.DBusException, e: assert e.get_dbus_name() == cs.NOT_AVAILABLE else: assert False if __name__ == '__main__': exec_test(test, protocol=SharedStatusStream) exec_test(test_connect_available, protocol=SharedStatusStream, do_connect=False) exec_test(test_connect_chat, protocol=SharedStatusStream, do_connect=False) exec_test(test_connect_dnd, protocol=SharedStatusStream, do_connect=False) exec_test(test_connect_hidden, protocol=SharedStatusStream, do_connect=False) exec_test(test_connect_hidden_future_version, protocol=SharedStatusStream, do_connect=False) exec_test(test_connect_hidden_not_available, protocol=SharedStatusStream, do_connect=False) exec_test(test_shared_status_list, protocol=SharedStatusStream) exec_test(test_shared_status_away, protocol=SharedStatusStream) exec_test(test_shared_status_chat, protocol=SharedStatusStream) telepathy-gabble-0.18.2/tests/twisted/presence/set-idempotence.py0000644000175000017500000000733012227000321025075 0ustar00smcvsmcv00000000000000""" Test that redundant calls to SetPresence don't cause anything to happen. """ import dbus from servicetest import EventPattern from gabbletest import exec_test import constants as cs from invisible_helper import Xep0186Stream, ValidInvisibleListStream, \ Xep0186AndValidInvisibleListStream def run_test(q, bus, conn, stream): # Expect an initial presence push from the client. q.expect('stream-presence') # Set presence to away. This should cause PresencesChanged to be emitted, # and a new stanza to be sent to the server. conn.SimplePresence.SetPresence('away', 'gone') simple_signal, presence = q.expect_many ( EventPattern('dbus-signal', signal='PresencesChanged'), EventPattern('stream-presence')) assert simple_signal.args == [{1L: (3L, u'away', u'gone')}] assert conn.Contacts.GetContactAttributes([1], [cs.CONN_IFACE_SIMPLE_PRESENCE], False) == { 1L: { cs.CONN_IFACE_SIMPLE_PRESENCE + "/presence": (3L, u'away', u'gone'), 'org.freedesktop.Telepathy.Connection/contact-id': 'test@localhost'}} children = list(presence.stanza.elements()) assert children[0].name == 'show' assert str(children[0]) == 'away' assert children[1].name == 'status' assert str(children[1]) == 'gone' # Set presence a second time. Since this call is redundant, there should # be no PresencesChanged or sent to the server. conn.SimplePresence.SetPresence('away', 'gone') assert conn.Contacts.GetContactAttributes([1], [cs.CONN_IFACE_SIMPLE_PRESENCE], False) == { 1L: { cs.CONN_IFACE_SIMPLE_PRESENCE + "/presence": (3L, u'away', u'gone'), 'org.freedesktop.Telepathy.Connection/contact-id': 'test@localhost'}} # Set presence a third time. This call is not redundant, and should # generate a signal/message. conn.SimplePresence.SetPresence('available', 'yo') simple_signal, presence = q.expect_many ( EventPattern('dbus-signal', signal='PresencesChanged'), EventPattern('stream-presence')) assert simple_signal.args == [{1L: (2L, u'available', u'yo')}] children = list(presence.stanza.elements()) assert children[0].name == 'status' assert str(children[0]) == 'yo' assert conn.Contacts.GetContactAttributes([1], [cs.CONN_IFACE_SIMPLE_PRESENCE], False) == { 1L: { cs.CONN_IFACE_SIMPLE_PRESENCE + "/presence": (2L, u'available', u'yo'), 'org.freedesktop.Telepathy.Connection/contact-id': 'test@localhost'}} # call SetPresence with an empty message, as this used to cause a # crash in tp-glib conn.SimplePresence.SetPresence('available', '') simple_signal, presence = q.expect_many ( EventPattern('dbus-signal', signal='PresencesChanged'), EventPattern('stream-presence')) assert simple_signal.args == [{1L: (2L, u'available', u'')}] assert conn.Contacts.GetContactAttributes([1], [cs.CONN_IFACE_SIMPLE_PRESENCE], False) == { 1L: { cs.CONN_IFACE_SIMPLE_PRESENCE + "/presence": (2L, u'available', u''), 'org.freedesktop.Telepathy.Connection/contact-id': 'test@localhost'}} if __name__ == '__main__': exec_test(run_test) # Run this test against some invisibility-capable servers, even though we # don't use invisibility, to check that invisibility support doesn't break # us. This is a regression test for # . It turned out that # XEP-0126 support meant that our own presence changes were never # signalled. for protocol in [Xep0186AndValidInvisibleListStream, Xep0186Stream, ValidInvisibleListStream]: exec_test(run_test, protocol=protocol) exec_test(run_test, protocol=protocol) telepathy-gabble-0.18.2/tests/twisted/presence/presence.py0000644000175000017500000000207112227000321023611 0ustar00smcvsmcv00000000000000""" A simple smoke-test for C.I.SimplePresence FIXME: test C.I.Presence too """ from twisted.words.xish import domish from gabbletest import exec_test, make_presence from servicetest import EventPattern import ns import constants as cs def test(q, bus, conn, stream): event = q.expect('stream-iq', query_ns=ns.ROSTER) amy_handle = conn.RequestHandles(1, ['amy@foo.com'])[0] event.stanza['type'] = 'result' item = event.query.addElement('item') item['jid'] = 'amy@foo.com' item['subscription'] = 'both' stream.send(event.stanza) stream.send(make_presence('amy@foo.com', show='away', status='At the pub')) q.expect('dbus-signal', signal='PresencesChanged', args=[{amy_handle: (cs.PRESENCE_AWAY, 'away', 'At the pub')}]) stream.send(make_presence( 'amy@foo.com', show='chat', status='I may have been drinking')) q.expect('dbus-signal', signal='PresencesChanged', args=[{amy_handle: (cs.PRESENCE_AVAILABLE, 'chat', 'I may have been drinking')}]) if __name__ == '__main__': exec_test(test) telepathy-gabble-0.18.2/tests/twisted/presence/plugins.py0000644000175000017500000001053512200204333023472 0ustar00smcvsmcv00000000000000""" Test Gabble support for plugins adding presence statuses. """ from gabbletest import ( XmppXmlStream, exec_test, make_presence, elem, elem_iq, acknowledge_iq, send_error_reply ) from servicetest import ( EventPattern, assertEquals, assertNotEquals, assertContains, assertDoesNotContain, call_async ) import ns import constants as cs from twisted.words.xish import xpath, domish from invisible_helper import Xep0186AndManualPrivacyListStream, \ ManualPrivacyListStream def test(q, bus, conn, stream): statuses = conn.Properties.Get(cs.CONN_IFACE_SIMPLE_PRESENCE, 'Statuses') # testbusy and testaway are provided by test plugin assertContains('testbusy', statuses) assertContains('testaway', statuses) assertEquals(statuses['testbusy'][0], cs.PRESENCE_BUSY) assertEquals(statuses['testaway'][0], cs.PRESENCE_AWAY) conn.SimplePresence.SetPresence('testbusy', '') conn.Connect() # ... gabble asks for all the available lists on the server ... stream.handle_get_all_privacy_lists(q, bus, conn, lists=["foo-list", "test-busy-list", "bar-list"]) # ... gabble checks whether there's usable invisible list on the server ... get_list = q.expect('stream-iq', query_ns=ns.PRIVACY, iq_type='get') list_node = xpath.queryForNodes('//list', get_list.query)[0] assertEquals('invisible', list_node['name']) error = domish.Element((None, 'error')) error['type'] = 'cancel' error.addElement((ns.STANZA, 'item-not-found')) send_error_reply (stream, get_list.stanza, error) # ... since there is none, Gabble creates it ... create_list = q.expect('stream-iq', query_ns=ns.PRIVACY, iq_type='set') list_node = xpath.queryForNodes('//list', create_list.query)[0] assertEquals('invisible', list_node['name']) assertNotEquals([], xpath.queryForNodes('/query/list/item/presence-out', create_list.query)) acknowledge_iq(stream, create_list.stanza) get_list = q.expect('stream-iq', query_ns=ns.PRIVACY, iq_type='set') list_node = xpath.queryForNodes('//active', get_list.query)[0] # ... and then activates the one linked with the requested status # Note: testbusy status is linked to test-busy-list by test plugin assertEquals('test-busy-list', list_node['name']) acknowledge_iq(stream, get_list.stanza) q.expect('dbus-signal', signal='PresencesChanged', args=[{1L: (cs.PRESENCE_BUSY, u'testbusy', '')}]) q.expect('dbus-signal', signal='StatusChanged', args=[cs.CONN_STATUS_CONNECTED, cs.CSR_REQUESTED]) # ... testaway is not supposed to be settable on us call_async(q, conn.SimplePresence, 'SetPresence', 'testaway', '') q.expect('dbus-error', method='SetPresence', name=cs.INVALID_ARGUMENT) def test_with_xep0186(q, bus, conn, stream): statuses = conn.Properties.Get(cs.CONN_IFACE_SIMPLE_PRESENCE, 'Statuses') # testbusy and testaway are provided by test plugin assertContains('testbusy', statuses) assertContains('testaway', statuses) assertEquals(statuses['testbusy'][0], cs.PRESENCE_BUSY) assertEquals(statuses['testaway'][0], cs.PRESENCE_AWAY) conn.SimplePresence.SetPresence('testbusy', '') conn.Connect() # ... gabble asks for all the available lists on the server ... stream.handle_get_all_privacy_lists(q, bus, conn, lists=["foo-list", "test-busy-list", "bar-list"]) get_list = q.expect('stream-iq', query_ns=ns.PRIVACY, iq_type='set') list_node = xpath.queryForNodes('//active', get_list.query)[0] # ... and then activates the one linked with the requested status # Note: testbusy status is linked to test-busy-list by test plugin assertEquals('test-busy-list', list_node['name']) acknowledge_iq(stream, get_list.stanza) q.expect('dbus-signal', signal='PresencesChanged', args=[{1L: (cs.PRESENCE_BUSY, u'testbusy', '')}]) q.expect('dbus-signal', signal='StatusChanged', args=[cs.CONN_STATUS_CONNECTED, cs.CSR_REQUESTED]) # ... testaway is not supposed to be settable on us call_async(q, conn.SimplePresence, 'SetPresence', 'testaway', '') q.expect('dbus-error', method='SetPresence', name=cs.INVALID_ARGUMENT) if __name__ == '__main__': exec_test(test, protocol=ManualPrivacyListStream, do_connect=False) exec_test(test_with_xep0186, protocol=Xep0186AndManualPrivacyListStream, do_connect=False) telepathy-gabble-0.18.2/tests/twisted/presence/invisible_xep_0186.py0000644000175000017500000000726312200204333025333 0ustar00smcvsmcv00000000000000""" A simple smoke-test for XEP-0186 invisibility """ from gabbletest import exec_test, acknowledge_iq, send_error_reply from servicetest import ( EventPattern, assertEquals, assertNotEquals, assertContains, ) import ns import constants as cs from invisible_helper import Xep0186AndValidInvisibleListStream, Xep0186Stream def test_invisible_on_connect(q, bus, conn, stream): props = conn.Properties.GetAll(cs.CONN_IFACE_SIMPLE_PRESENCE) assertNotEquals({}, props['Statuses']) presence_event_pattern = EventPattern('stream-presence') q.forbid_events([presence_event_pattern]) conn.SimplePresence.SetPresence("hidden", "") conn.Connect() event = q.expect('stream-iq', query_name='invisible') acknowledge_iq(stream, event.stanza) q.unforbid_events([presence_event_pattern]) q.expect('dbus-signal', signal='StatusChanged', args=[cs.CONN_STATUS_CONNECTED, cs.CSR_REQUESTED]) def test_invisible_on_connect_fails(q, bus, conn, stream): conn.SimplePresence.SetPresence("hidden", "") conn.Connect() event = q.expect('stream-iq', query_name='invisible') send_error_reply(stream, event.stanza) # Darn! At least we should have our presence set to DND. q.expect_many( EventPattern('dbus-signal', signal='PresencesChanged', interface=cs.CONN_IFACE_SIMPLE_PRESENCE, args=[{1: (6, 'dnd', '')}]), EventPattern('dbus-signal', signal='StatusChanged', args=[cs.CONN_STATUS_CONNECTED, cs.CSR_REQUESTED])) def test_invisible(q, bus, conn, stream): assertContains("hidden", conn.Properties.Get(cs.CONN_IFACE_SIMPLE_PRESENCE, "Statuses")) conn.SimplePresence.SetPresence("hidden", "") # First we send an command. event = q.expect('stream-iq', query_name='invisible') acknowledge_iq(stream, event.stanza) # When that's returned successfully, we can signal the change on D-Bus. q.expect('dbus-signal', signal='PresencesChanged', interface=cs.CONN_IFACE_SIMPLE_PRESENCE, args=[{1: (5, 'hidden', '')}]) conn.SimplePresence.SetPresence("away", "gone") # First Gabble sends a command. event = q.expect('stream-iq', query_name='visible') acknowledge_iq(stream, event.stanza) # Then: "It is the responsibility of the client to send an undirected # presence notification to the server". Plus, we should signal the change # on D-Bus. q.expect_many( EventPattern('stream-presence', to=None), EventPattern('dbus-signal', signal='PresencesChanged', interface=cs.CONN_IFACE_SIMPLE_PRESENCE, args=[{1: (3, 'away', 'gone')}])) def test_invisible_fails(q, bus, conn, stream): assertContains("hidden", conn.Properties.Get(cs.CONN_IFACE_SIMPLE_PRESENCE, "Statuses")) conn.SimplePresence.SetPresence("hidden", "") # First we send an command. event = q.expect('stream-iq', query_name='invisible') send_error_reply(stream, event.stanza) # When that fails, we should expect our status to change to dnd. q.expect('dbus-signal', signal='PresencesChanged', interface=cs.CONN_IFACE_SIMPLE_PRESENCE, args=[{1: (6, 'dnd', '')}]) if __name__ == '__main__': for protocol in [Xep0186Stream, Xep0186AndValidInvisibleListStream]: exec_test(test_invisible_on_connect, protocol=protocol, do_connect=False) exec_test(test_invisible_on_connect_fails, protocol=protocol, do_connect=False) exec_test(test_invisible, protocol=protocol) exec_test(test_invisible_fails, protocol=protocol) telepathy-gabble-0.18.2/tests/twisted/presence/invisible_xep_0126.py0000644000175000017500000003463712200204333025332 0ustar00smcvsmcv00000000000000# coding=utf-8 """ A simple smoke-test for XEP-0126 invisibility We are deliberately not advertising support for privacy lists on connection disco, this is because it is not explicitly required by XEP-0016 or XEP-0126. Some servers silently support it, like certain version of Ejabberd and all released versions of Prosody (as of 7.0). """ from gabbletest import ( exec_test, acknowledge_iq, send_error_reply, elem, elem_iq ) from servicetest import ( EventPattern, assertEquals, assertNotEquals, assertContains, assertDoesNotContain ) import ns import constants as cs from twisted.words.xish import xpath, domish from invisible_helper import ManualPrivacyListStream from functools import partial def test_create_invisible_list(q, bus, conn, stream): conn.SimplePresence.SetPresence("away", "") conn.Connect() stream.handle_get_all_privacy_lists(q, bus, conn) get_list = q.expect('stream-iq', query_ns=ns.PRIVACY, iq_type='get') list_node = xpath.queryForNodes('//list', get_list.query)[0] assertEquals('invisible', list_node['name']) error = domish.Element((None, 'error')) error['type'] = 'cancel' error.addElement((ns.STANZA, 'item-not-found')) send_error_reply (stream, get_list.stanza, error) create_list = q.expect('stream-iq', query_ns=ns.PRIVACY, iq_type='set') list_node = xpath.queryForNodes('//list', create_list.query)[0] assertEquals('invisible', list_node['name']) assertNotEquals([], xpath.queryForNodes('/query/list/item/presence-out', create_list.query)) acknowledge_iq(stream, create_list.stanza) q.expect('dbus-signal', signal='StatusChanged', args=[cs.CONN_STATUS_CONNECTED, cs.CSR_REQUESTED]) assertContains("hidden", conn.Properties.Get(cs.CONN_IFACE_SIMPLE_PRESENCE, "Statuses")) def test_create_invisible_list_failed(q, bus, conn, stream): conn.SimplePresence.SetPresence("away", "") conn.Connect() stream.handle_get_all_privacy_lists(q, bus, conn) get_list = q.expect('stream-iq', query_ns=ns.PRIVACY, iq_type='get') list_node = xpath.queryForNodes('//list', get_list.query)[0] assertEquals('invisible', list_node['name']) error = domish.Element((None, 'error')) error['type'] = 'cancel' error.addElement((ns.STANZA, 'item-not-found')) send_error_reply (stream, get_list.stanza, error) create_list = q.expect('stream-iq', query_ns=ns.PRIVACY, iq_type='set') list_node = xpath.queryForNodes('//list', create_list.query)[0] assertEquals('invisible', list_node['name']) assertNotEquals([], xpath.queryForNodes('/query/list/item/presence-out', create_list.query)) send_error_reply(stream, create_list.stanza) q.expect('dbus-signal', signal='StatusChanged', args=[cs.CONN_STATUS_CONNECTED, cs.CSR_REQUESTED]) assertDoesNotContain("hidden", conn.Properties.Get(cs.CONN_IFACE_SIMPLE_PRESENCE, "Statuses")) def test_invisible_on_connect_fail_no_list(q, bus, conn, stream): props = conn.Properties.GetAll(cs.CONN_IFACE_SIMPLE_PRESENCE) assertNotEquals({}, props['Statuses']) presence_event_pattern = EventPattern('stream-presence') q.forbid_events([presence_event_pattern]) conn.SimplePresence.SetPresence("hidden", "") conn.Connect() stream.handle_get_all_privacy_lists(q, bus, conn) get_list = q.expect('stream-iq', query_ns=ns.PRIVACY, iq_type='get') list_node = xpath.queryForNodes('//list', get_list.query)[0] assertEquals('invisible', list_node['name']) send_error_reply(stream, get_list.stanza) q.unforbid_events([presence_event_pattern]) # Darn! At least we should have our presence set to DND. q.expect_many( EventPattern('dbus-signal', signal='PresencesChanged', interface=cs.CONN_IFACE_SIMPLE_PRESENCE, args=[{1: (6, 'dnd', '')}]), EventPattern('dbus-signal', signal='StatusChanged', args=[cs.CONN_STATUS_CONNECTED, cs.CSR_REQUESTED])) # 'hidden' should not be an available status. assertDoesNotContain("hidden", conn.Properties.Get(cs.CONN_IFACE_SIMPLE_PRESENCE, "Statuses")) def test_invisible_on_connect_fail_invalid_list(q, bus, conn, stream, really_invalid=False): props = conn.Properties.GetAll(cs.CONN_IFACE_SIMPLE_PRESENCE) assertNotEquals({}, props['Statuses']) presence_event_pattern = EventPattern('stream-presence') q.forbid_events([presence_event_pattern]) conn.SimplePresence.SetPresence("hidden", "") conn.Connect() stream.handle_get_all_privacy_lists(q, bus, conn, ['invisible']) get_list = q.expect('stream-iq', query_ns=ns.PRIVACY, iq_type='get') list_node = xpath.queryForNodes('//list', get_list.query)[0] assertEquals('invisible', list_node['name']) if really_invalid: # At one point Gabble would crash if the reply was of type 'result' but # wasn't well-formed. acknowledge_iq(stream, get_list.stanza) else: stream.send_privacy_list(get_list.stanza, [elem('item', type='jid', value='tybalt@example.com', action='allow', order='1')(elem('presence-out')), elem('item', action='deny', order='2')(elem('presence-out'))]) create_list = q.expect('stream-iq', query_ns=ns.PRIVACY, iq_type='set') created = xpath.queryForNodes('//list', create_list.stanza)[0] assertEquals(created["name"], 'invisible-gabble') acknowledge_iq (stream, create_list.stanza) q.unforbid_events([presence_event_pattern]) activate_list = q.expect('stream-iq', query_ns=ns.PRIVACY, iq_type='set') active = xpath.queryForNodes('//active', activate_list.stanza)[0] assertEquals(active["name"], 'invisible-gabble') acknowledge_iq (stream, activate_list.stanza) q.expect_many( EventPattern('dbus-signal', signal='PresencesChanged', interface=cs.CONN_IFACE_SIMPLE_PRESENCE, args=[{1: (5, 'hidden', '')}]), EventPattern('dbus-signal', signal='StatusChanged', args=[cs.CONN_STATUS_CONNECTED, cs.CSR_REQUESTED])) # 'hidden' should not be an available status. assertContains("hidden", conn.Properties.Get(cs.CONN_IFACE_SIMPLE_PRESENCE, "Statuses")) def test_invisible_on_connect_fail(q, bus, conn, stream): props = conn.Properties.GetAll(cs.CONN_IFACE_SIMPLE_PRESENCE) assertNotEquals({}, props['Statuses']) presence_event_pattern = EventPattern('stream-presence') q.forbid_events([presence_event_pattern]) conn.SimplePresence.SetPresence("hidden", "") conn.Connect() stream.handle_get_all_privacy_lists(q, bus, conn) create_list = q.expect('stream-iq', query_ns=ns.PRIVACY, iq_type='set') # Check its name assertNotEquals([], xpath.queryForNodes('/query/list/item/presence-out', create_list.query)) acknowledge_iq(stream, create_list.stanza) set_active = q.expect('stream-iq', query_ns=ns.PRIVACY, iq_type='set') active = xpath.queryForNodes('//active', set_active.query)[0] assertEquals('invisible', active['name']) send_error_reply(stream, set_active.stanza) q.unforbid_events([presence_event_pattern]) # Darn! At least we should have our presence set to DND. q.expect_many( EventPattern('dbus-signal', signal='PresencesChanged', interface=cs.CONN_IFACE_SIMPLE_PRESENCE, args=[{1: (6, 'dnd', '')}]), EventPattern('dbus-signal', signal='StatusChanged', args=[cs.CONN_STATUS_CONNECTED, cs.CSR_REQUESTED])) def test_invisible_on_connect(q, bus, conn, stream): props = conn.Properties.GetAll(cs.CONN_IFACE_SIMPLE_PRESENCE) assertNotEquals({}, props['Statuses']) presence_event_pattern = EventPattern('stream-presence') q.forbid_events([presence_event_pattern]) conn.SimplePresence.SetPresence("hidden", "") conn.Connect() stream.handle_get_all_privacy_lists(q, bus, conn, ['invisible']) get_list = q.expect('stream-iq', query_ns=ns.PRIVACY, iq_type='get') list_node = xpath.queryForNodes('//list', get_list.query)[0] assertEquals('invisible', list_node['name']) stream.send_privacy_list(get_list.stanza, [elem('item', action='deny', order='1')(elem('presence-out'))]) set_active = q.expect('stream-iq', query_ns=ns.PRIVACY, iq_type='set') active = xpath.queryForNodes('//active', set_active.query)[0] assertEquals('invisible', active['name']) acknowledge_iq(stream, set_active.stanza) q.unforbid_events([presence_event_pattern]) q.expect('dbus-signal', signal='StatusChanged', args=[cs.CONN_STATUS_CONNECTED, cs.CSR_REQUESTED]) def test_invisible(q, bus, conn, stream): conn.Connect() stream.handle_get_all_privacy_lists(q, bus, conn, ['invisible']) get_list = q.expect('stream-iq', query_ns=ns.PRIVACY, iq_type='get') stream.send_privacy_list(get_list.stanza, [elem('item', action='deny', order='1')(elem('presence-out'))]) q.expect('dbus-signal', signal='StatusChanged', args=[cs.CONN_STATUS_CONNECTED, cs.CSR_REQUESTED]) assertContains("hidden", conn.Properties.Get(cs.CONN_IFACE_SIMPLE_PRESENCE, "Statuses")) conn.SimplePresence.SetPresence("hidden", "") # §3.5 Become Globally Invisible # # # First, the user sends unavailable presence for broadcasting to all # contacts: q.expect('stream-presence', to=None, presence_type='unavailable') # Second, the user sets as active the global invisibility list previously # defined: event = q.expect('stream-iq', query_ns=ns.PRIVACY, iq_type='set') active = xpath.queryForNodes('//active', event.query)[0] assertEquals('invisible', active['name']) acknowledge_iq(stream, event.stanza) # In order to appear globally invisible, the client MUST now re-send the # user's presence for broadcasting to all contacts, which the active rule # will block to all contacts: q.expect_many( EventPattern('stream-presence', to=None, presence_type=None), EventPattern('dbus-signal', signal='PresencesChanged', interface=cs.CONN_IFACE_SIMPLE_PRESENCE, args=[{1: (5, 'hidden', '')}])) conn.SimplePresence.SetPresence("away", "gone") # §3.3 Become Globally Visible # # # Because globally allowing outbound presence notifications is most likely # the default behavior of any server, a more straightforward way to become # globally visible is to decline the use of any active rule (the # equivalent, as it were, of taking off a magic invisibility ring): event = q.expect('stream-iq', query_ns=ns.PRIVACY, iq_type='set') active = xpath.queryForNodes('//active', event.query)[0] assert (not active.compareAttribute('name', 'invisible')) acknowledge_iq(stream, event.stanza) # In order to ensure synchronization of presence notifications, the client # SHOULD now re-send the user's presence for broadcasting to all contacts. # # At this point, we also signal our presence change on D-Bus: q.expect_many( EventPattern('stream-presence', to=None, presence_type=None), EventPattern('dbus-signal', signal='PresencesChanged', interface=cs.CONN_IFACE_SIMPLE_PRESENCE, args=[{1: (3, 'away', 'gone')}])) def test_privacy_list_push_conflict(q, bus, conn, stream): test_invisible_on_connect(q, bus, conn, stream) set_id = stream.send_privacy_list_push_iq("invisible") _, req_list = q.expect_many( EventPattern('stream-iq', iq_type='result', iq_id=set_id), EventPattern('stream-iq', query_ns=ns.PRIVACY, iq_type="get")) stream.send_privacy_list(req_list.stanza, [elem('item', type='jid', value='tybalt@example.com', action='allow', order='1')(elem('presence-out')), elem('item', action='deny', order='2')(elem('presence-out'))]) create_list = q.expect('stream-iq', query_ns=ns.PRIVACY, iq_type='set') created = xpath.queryForNodes('//list', create_list.stanza)[0] assertEquals(created["name"], 'invisible-gabble') acknowledge_iq(stream, create_list.stanza) set_active = q.expect('stream-iq', query_ns=ns.PRIVACY, iq_type='set') active = xpath.queryForNodes('//active', set_active.query)[0] assertEquals('invisible-gabble', active['name']) acknowledge_iq(stream, set_active.stanza) def test_privacy_list_push_valid(q, bus, conn, stream): test_invisible_on_connect(q, bus, conn, stream) set_id = stream.send_privacy_list_push_iq("invisible") _, req_list = q.expect_many( EventPattern('stream-iq', iq_type='result', iq_id=set_id), EventPattern('stream-iq', query_ns=ns.PRIVACY, iq_type="get")) stream.send_privacy_list(req_list.stanza, [elem('item', action='deny', order='1')(elem(u'presence-out')), elem('item', type='jid', value='tybalt@example.com', action='deny', order='2')(elem(u'message'))]) # We redundantly re-activate the 'invisible' list. These lines also # check to see that we didn't switch over to 'invisible-gabble' activate_list = q.expect('stream-iq', query_ns=ns.PRIVACY, iq_type='set') active = xpath.queryForNodes('//active', activate_list.stanza)[0] assertEquals(active["name"], 'invisible') acknowledge_iq (stream, activate_list.stanza) if __name__ == '__main__': exec_test(test_invisible, protocol=ManualPrivacyListStream, do_connect=False) exec_test(test_invisible_on_connect, protocol=ManualPrivacyListStream, do_connect=False) exec_test(test_create_invisible_list, protocol=ManualPrivacyListStream, do_connect=False) exec_test(test_create_invisible_list_failed, protocol=ManualPrivacyListStream, do_connect=False) exec_test(test_invisible_on_connect_fail_no_list, protocol=ManualPrivacyListStream, do_connect=False) exec_test(test_invisible_on_connect_fail_invalid_list, protocol=ManualPrivacyListStream, do_connect=False) exec_test(partial(test_invisible_on_connect_fail_invalid_list, really_invalid=True), protocol=ManualPrivacyListStream, do_connect=False) exec_test(test_privacy_list_push_valid, protocol=ManualPrivacyListStream, do_connect=False) exec_test(test_privacy_list_push_conflict, protocol=ManualPrivacyListStream, do_connect=False) telepathy-gabble-0.18.2/tests/twisted/presence/initial-presence.py0000644000175000017500000000251212200204333025240 0ustar00smcvsmcv00000000000000""" Tests setting your own presence before calling Connect(), allowing the user to sign in as Busy/Invisible/whatever rather than available. """ from twisted.words.xish import domish from gabbletest import exec_test from servicetest import EventPattern, assertEquals, assertNotEquals import ns import constants as cs from invisible_helper import ValidInvisibleListStream, Xep0186Stream, \ Xep0186AndValidInvisibleListStream def test(q, bus, conn, stream): props = conn.Properties.GetAll(cs.CONN_IFACE_SIMPLE_PRESENCE) assertNotEquals({}, props['Statuses']) conn.SimplePresence.SetPresence("away", "watching bees") conn.Connect() _, presence = q.expect_many( EventPattern('dbus-signal', signal='StatusChanged', args=[cs.CONN_STATUS_CONNECTED, cs.CSR_REQUESTED]), EventPattern('stream-presence'), ) children = list(presence.stanza.elements()) assertEquals('show', children[0].name) assertEquals('away', children[0].children[0]) assertEquals('status', children[1].name) assertEquals('watching bees', children[1].children[0]) if __name__ == '__main__': exec_test(test, do_connect=False) for protocol in [ValidInvisibleListStream, Xep0186Stream, Xep0186AndValidInvisibleListStream]: exec_test(test, protocol=protocol, do_connect=False) telepathy-gabble-0.18.2/tests/twisted/presence/initial-contact-presence.py0000644000175000017500000000763212227000321026701 0ustar00smcvsmcv00000000000000""" Test that contacts we're subscribed to have their presence go from unknown to offline when we get the roster, even if we've got (unavailable) presence for them before we receive the roster; and that receiving available presence from a contact before we get the roster also works. This serves as a regression test for , among other bugs. """ from gabbletest import exec_test, make_presence, sync_stream, elem from servicetest import assertEquals, EventPattern, sync_dbus import constants as cs import ns from twisted.words.xish import domish AVAILABLE = (cs.PRESENCE_AVAILABLE, u'available', u'') OFFLINE = (cs.PRESENCE_OFFLINE, u'offline', u'') UNKNOWN = (cs.PRESENCE_UNKNOWN, u'unknown', u'') def make_roster_item(jid, subscription): item = domish.Element((None, 'item')) item['jid'] = jid item['subscription'] = subscription return item def test(q, bus, conn, stream): event = q.expect('stream-iq', query_ns=ns.ROSTER) amy, bob, che, dre, eve = conn.RequestHandles(cs.HT_CONTACT, ['amy@foo.com', 'bob@foo.com', 'che@foo.com', 'dre@foo.com', 'eve@foo.com']) assertEquals({amy: UNKNOWN, bob: UNKNOWN, che: UNKNOWN, dre: UNKNOWN, eve: UNKNOWN, }, conn.SimplePresence.GetPresences([amy, bob, che, dre, eve])) # Before the server sends Gabble the roster, it relays an 'unavailable' # presence for one of the contacts we're subscribed to. This seems to # happen in practice when using Prosody with a shared roster: the presence # probes start coming back negatively before the shared roster is retrieved # and returned to the client. stream.send(make_presence('dre@foo.com', type='unavailable')) # Dre's presence is still unknown, since we don't have the roster. This # isn't a change per se---we checked above, and Dre's presence was # unknown---so it shouldn't be signalled. q.forbid_events([EventPattern('dbus-signal', signal='PresencesChanged', args=[{dre: UNKNOWN}])]) # We also receive an available presence from Eve before the roster arrives: # this presence should behave normally. stream.send(make_presence('eve@foo.com')) q.expect('dbus-signal', signal='PresencesChanged', args=[{eve: AVAILABLE}]) # We also get a message from a contact before we get the roster (presumably # they sent this while we were offline?). This shouldn't affect the contact # being reported as offline when we finally do get the roster, but it used # to: . stream.send( elem('message', from_='amy@foo.com', type='chat')( elem('body')(u'why are you never online?') )) q.expect('dbus-signal', signal='MessageReceived') event.stanza['type'] = 'result' event.query.addChild(make_roster_item('amy@foo.com', 'both')) event.query.addChild(make_roster_item('bob@foo.com', 'from')) event.query.addChild(make_roster_item('che@foo.com', 'to')) event.query.addChild(make_roster_item('dre@foo.com', 'both')) event.query.addChild(make_roster_item('eve@foo.com', 'both')) stream.send(event.stanza) # The presence for contacts on the roster whose subscription is 'to' or # 'both' but for whom we haven't already received presence should change # from 'unknown' (as checked above) to 'offline'. e = q.expect('dbus-signal', signal='PresencesChanged') changed_presences, = e.args assertEquals( {amy: OFFLINE, che: OFFLINE, dre: OFFLINE, }, changed_presences) assertEquals({amy: OFFLINE, bob: UNKNOWN, che: OFFLINE, dre: OFFLINE, eve: AVAILABLE, }, conn.SimplePresence.GetPresences([amy, bob, che, dre, eve])) if __name__ == '__main__': exec_test(test) telepathy-gabble-0.18.2/tests/twisted/presence/error.py0000644000175000017500000000445312227000321023144 0ustar00smcvsmcv00000000000000""" Tests that Gabble provides at least some useful information from error presences. """ from gabbletest import exec_test, make_presence, elem from servicetest import assertEquals import ns import constants as cs def test(q, bus, conn, stream): jids = ['gregory@unreachable.example.com', 'thehawk@unreachable.example.net', ] gregory, hawk = jids gregory_handle, hawk_handle = conn.RequestHandles(cs.HT_CONTACT, jids) event = q.expect('stream-iq', query_ns=ns.ROSTER) event.stanza['type'] = 'result' for jid in jids: item = event.query.addElement('item') item['jid'] = jid item['subscription'] = 'both' stream.send(event.stanza) q.expect('dbus-signal', signal='PresencesChanged', args=[{gregory_handle: (cs.PRESENCE_OFFLINE, 'offline', ''), hawk_handle: (cs.PRESENCE_OFFLINE, 'offline', ''), } ]) # Our server can't resolve unreachable.example.com so it sends us an error # presence for Gregory. (This is what Prosody actually does.) presence = make_presence(gregory, type='error') error_text = u'Connection failed: DNS resolution failed' presence.addChild( elem('error', type='cancel')( elem(ns.STANZA, 'remote-server-not-found'), elem(ns.STANZA, 'text')( error_text ) )) stream.send(presence) e = q.expect('dbus-signal', signal='PresencesChanged') presences, = e.args type_, status, message = presences[gregory_handle] assertEquals(cs.PRESENCE_ERROR, type_) assertEquals('error', status) assertEquals(error_text, message) # How about maybe the hawk's server is busted? presence = make_presence(hawk, type='error') presence.addChild( elem('error', type='cancel')( elem(ns.STANZA, 'internal-server-error'), )) stream.send(presence) e = q.expect('dbus-signal', signal='PresencesChanged') presences, = e.args type_, status, message = presences[hawk_handle] assertEquals(cs.PRESENCE_ERROR, type_) assertEquals('error', status) # FIXME: It might be less user-hostile to give some kind of readable # description of the error in future. assertEquals('internal-server-error', message) if __name__ == '__main__': exec_test(test) telepathy-gabble-0.18.2/tests/twisted/presence/decloak.py0000644000175000017500000000511012227000321023404 0ustar00smcvsmcv00000000000000from twisted.words.xish import domish from gabbletest import exec_test, make_presence from servicetest import EventPattern, assertEquals import ns import constants as cs def test(q, bus, conn, stream, should_decloak=False): event = q.expect('stream-iq', query_ns=ns.ROSTER) event.stanza['type'] = 'result' stream.send(event.stanza) # First test is to use the CM param's value worker(q, bus, conn, stream, should_decloak) # We can change it at runtime, so flip it to the other value and retry should_decloak = not should_decloak conn.Set(cs.CONN_IFACE_GABBLE_DECLOAK, 'DecloakAutomatically', should_decloak, dbus_interface=cs.PROPERTIES_IFACE) worker(q, bus, conn, stream, should_decloak) # Trivial test for SendDirectedPresence() bob_handle = conn.RequestHandles(1, ['bob@foo.com'])[0] conn.SendDirectedPresence(bob_handle, False, dbus_interface=cs.CONN_IFACE_GABBLE_DECLOAK) q.expect('stream-presence', to='bob@foo.com') def worker(q, bus, conn, stream, should_decloak): decloak_automatically = conn.Get(cs.CONN_IFACE_GABBLE_DECLOAK, 'DecloakAutomatically', dbus_interface=cs.PROPERTIES_IFACE) assertEquals(should_decloak, decloak_automatically) amy_handle = conn.RequestHandles(1, ['amy@foo.com'])[0] # Amy directs presence to us presence = make_presence('amy@foo.com/panopticon') decloak = presence.addElement((ns.TEMPPRES, 'temppres')) decloak['reason'] = 'media' stream.send(presence) events = [ EventPattern('dbus-signal', signal='PresencesChanged', args=[{amy_handle: (cs.PRESENCE_AVAILABLE, 'available', '')}]), EventPattern('dbus-signal', signal='DecloakRequested', args=[amy_handle, 'media', should_decloak]), ] forbidden = [] if should_decloak: events.append(EventPattern('stream-presence', to='amy@foo.com/panopticon')) else: forbidden = [EventPattern('stream-presence')] q.forbid_events(forbidden) q.expect_many(*events) presence = make_presence('amy@foo.com/panopticon', type='unavailable') stream.send(presence) q.expect('dbus-signal', signal='PresencesChanged', args=[{amy_handle: (cs.PRESENCE_OFFLINE, 'offline', '')}]) q.unforbid_events(forbidden) if __name__ == '__main__': exec_test(test, params={cs.CONN_IFACE_GABBLE_DECLOAK + '.DecloakAutomatically': False}) exec_test(lambda q, b, c, s: test(q, b, c, s, should_decloak=True), params={cs.CONN_IFACE_GABBLE_DECLOAK + '.DecloakAutomatically': True}) telepathy-gabble-0.18.2/tests/twisted/olpc/0000755000175000017500000000000012312537051020576 5ustar00smcvsmcv00000000000000telepathy-gabble-0.18.2/tests/twisted/olpc/util.py0000644000175000017500000000543412200204333022121 0ustar00smcvsmcv00000000000000import dbus from servicetest import call_async, EventPattern from gabbletest import make_result_iq, elem, elem_iq from twisted.words.xish import domish, xpath from twisted.words.protocols.jabber.client import IQ import constants as cs import ns def properties_to_xml(properties): result = [] for key, (type, value) in properties.iteritems(): property = domish.Element((None, 'property')) property['type'] = type property['name'] = key property.addContent(value) result.append(property) return result def _make_pubsub_event_msg(from_, node): # manually create the item node as we need a ref on it item = domish.Element((None, 'item')) message = elem('message', from_=from_, to='test@localhost')( elem(ns.PUBSUB_EVENT, 'event')( elem('items', node=node)(item))) return message, item def send_buddy_changed_properties_msg(stream, from_, props): message, item = _make_pubsub_event_msg(from_, ns.OLPC_BUDDY_PROPS) properties = item.addElement((ns.OLPC_BUDDY_PROPS, 'properties')) for child in properties_to_xml(props): properties.addChild(child) stream.send(message) def send_buddy_changed_current_act_msg(stream, from_, id, room): message, item = _make_pubsub_event_msg(from_, ns.OLPC_CURRENT_ACTIVITY) activity = item.addElement((ns.OLPC_CURRENT_ACTIVITY, 'activity')) activity['room'] = room activity['type'] = id stream.send(message) def answer_to_current_act_pubsub_request(stream, request, id, room): # check request structure assert request['type'] == 'get' items = xpath.queryForNodes( '/iq/pubsub[@xmlns="%s"]/items' % ns.PUBSUB, request)[0] assert items['node'] == ns.OLPC_CURRENT_ACTIVITY reply = make_result_iq(stream, request) reply['from'] = request['to'] pubsub = reply.firstChildElement() items = pubsub.addElement((None, 'items')) items['node'] = ns.OLPC_CURRENT_ACTIVITY item = items.addElement((None, 'item')) item['id'] = 'itemID' activity = item.addElement((ns.OLPC_CURRENT_ACTIVITY, 'activity')) activity['room'] = room activity['type'] = id reply.send() def answer_error_to_pubsub_request(stream, request): # look for node's name in the request items = xpath.queryForNodes('/iq/pubsub/items', request)[0] node = items['node'] reply = IQ(stream, "error") reply['id'] = request['id'] reply['from'] = request['to'] pubsub = reply.addElement((ns.PUBSUB, 'pubsub')) items = pubsub.addElement((None, 'items')) items['node'] = node error = reply.addElement((None, 'error')) error['type'] = 'auth' error.addElement((ns.STANZA, 'not-authorized')) error.addElement(("%s#errors" % ns.PUBSUB, 'presence-subscription-required')) stream.send(reply) telepathy-gabble-0.18.2/tests/twisted/olpc/test-olpc-set-props-preload.py0000644000175000017500000000304512200204333026430 0ustar00smcvsmcv00000000000000 """ Test connecting to a server. """ import dbus from twisted.words.xish import xpath from gabbletest import exec_test import ns def test(q, bus, conn, stream): buddy_info_iface = dbus.Interface(conn, 'org.laptop.Telepathy.BuddyInfo') buddy_info_iface.SetProperties({'color': '#ff0000,#0000ff'}) conn.Connect() # buddy activities event = q.expect('stream-iq', iq_type='set', query_name='pubsub') assert xpath.queryForNodes( "/iq[@type='set']/pubsub[@xmlns='%s']" "/publish[@node='%s']" % (ns.PUBSUB, ns.OLPC_ACTIVITIES), event.stanza) # activity properties event = q.expect('stream-iq', iq_type='set', query_name='pubsub') assert xpath.queryForNodes( "/iq[@type='set']/pubsub[@xmlns='%s']" "/publish[@node='%s']" % (ns.PUBSUB, ns.OLPC_ACTIVITY_PROPS), event.stanza) # buddy properties event = q.expect('stream-iq', iq_type='set', query_name='pubsub') iq = event.stanza nodes = xpath.queryForNodes( "/iq[@type='set']/pubsub[@xmlns='%s']" "/publish[@node='%s']" % (ns.PUBSUB, ns.OLPC_BUDDY_PROPS), iq) assert nodes nodes = xpath.queryForNodes( "/publish/item" "/properties[@xmlns='%s']" "/property" % (ns.OLPC_BUDDY_PROPS), nodes[0]) assert len(nodes) == 1 assert nodes[0]['type'] == 'str' assert nodes[0]['name'] == 'color' text = str(nodes[0]) assert text == '#ff0000,#0000ff', text iq['type'] = 'result' stream.send(iq) if __name__ == '__main__': exec_test(test, do_connect=False) telepathy-gabble-0.18.2/tests/twisted/olpc/test-olpc-bundle.py0000644000175000017500000000431712200204333024324 0ustar00smcvsmcv00000000000000"""test OLPC bundle. We shouldn't announce OLPC features until we use the OLPC interface""" import dbus from servicetest import call_async, EventPattern from gabbletest import exec_test, acknowledge_iq from twisted.words.xish import domish, xpath import ns import constants as cs olpc_features = set([ns.OLPC_BUDDY_PROPS_NOTIFY, ns.OLPC_ACTIVITIES_NOTIFY, ns.OLPC_CURRENT_ACTIVITY_NOTIFY, ns.OLPC_ACTIVITY_PROPS_NOTIFY]) def test(q, bus, conn, stream): iq_event = q.expect('stream-iq', to=None, query_ns='vcard-temp', query_name='vCard') acknowledge_iq(stream, iq_event.stanza) # send diso request m = domish.Element((None, 'iq')) m['from'] = 'alice@jabber.laptop.org' m['id'] = '1' m['type'] = 'get' query = m.addElement('query') query['xmlns'] = ns.DISCO_INFO stream.send(m) # wait for disco response event = q.expect('stream-iq', iq_type='result', query_ns=ns.DISCO_INFO, to='alice@jabber.laptop.org') features = set([str(f['var']) for f in xpath.queryForNodes('/iq/query/feature', event.stanza)]) # OLPC NS aren't announced assert len(olpc_features.intersection(features)) == 0 # Use OLPC interface buddy_info_iface = dbus.Interface(conn, 'org.laptop.Telepathy.BuddyInfo') call_async(q, buddy_info_iface, 'SetProperties', {'color': '#ff0000,#0000ff'}) # wait for stanza event = q.expect('stream-presence') c_nodes = xpath.queryForNodes('/presence/c', event.stanza) assert c_nodes is not None assert len(c_nodes) == 1 # send diso request m = domish.Element((None, 'iq')) m['from'] = 'alice@jabber.laptop.org' m['id'] = '2' m['type'] = 'get' query = m.addElement('query') query['xmlns'] = ns.DISCO_INFO stream.send(m) # wait for disco response event = q.expect('stream-iq', iq_type='result', query_ns=ns.DISCO_INFO, to='alice@jabber.laptop.org') assert event.stanza['id'] == '2' # OLPC NS are now announced features = set([str(f['var']) for f in xpath.queryForNodes('/iq/query/feature', event.stanza)]) assert olpc_features.issubset(features) if __name__ == '__main__': exec_test(test) telepathy-gabble-0.18.2/tests/twisted/olpc/olpc-muc-prop-change.py0000644000175000017500000003517712227000321025073 0ustar00smcvsmcv00000000000000""" Test OLPC MUC properties. """ import dbus from twisted.words.xish import domish, xpath from gabbletest import exec_test, acknowledge_iq, make_muc_presence from servicetest import call_async, EventPattern import constants as cs import ns from mucutil import echo_muc_presence def test(q, bus, conn, stream): iq_event = q.expect('stream-iq', to=None, query_ns='vcard-temp', query_name='vCard') acknowledge_iq(stream, iq_event.stanza) buddy_iface = dbus.Interface(conn, 'org.laptop.Telepathy.BuddyInfo') act_prop_iface = dbus.Interface(conn, 'org.laptop.Telepathy.ActivityProperties') bob_handle = conn.RequestHandles(1, ['bob@localhost'])[0] # Bob invites us to a chatroom, pre-seeding properties message = domish.Element(('jabber:client', 'message')) message['from'] = 'bob@localhost' message['to'] = 'test@localhost' properties = message.addElement( (ns.OLPC_ACTIVITY_PROPS, 'properties')) properties['room'] = 'chat@conf.localhost' properties['activity'] = 'foo_id' property = properties.addElement((None, 'property')) property['type'] = 'str' property['name'] = 'title' property.addContent('From the invitation') property = properties.addElement((None, 'property')) property['type'] = 'bool' property['name'] = 'private' property.addContent('1') stream.send(message) message = domish.Element((None, 'message')) message['from'] = 'chat@conf.localhost' message['to'] = 'test@localhost' x = message.addElement((ns.MUC_USER, 'x')) invite = x.addElement((None, 'invite')) invite['from'] = 'bob@localhost' reason = invite.addElement((None, 'reason')) reason.addContent('No good reason') stream.send(message) event = q.expect('dbus-signal', signal='NewChannel') assert event.args[1] == cs.CHANNEL_TYPE_TEXT assert event.args[2] == 2 # handle type assert event.args[3] == 1 # handle room_handle = 1 text_chan = bus.get_object(conn.bus_name, event.args[0]) chan_iface = dbus.Interface(text_chan, cs.CHANNEL) group_iface = dbus.Interface(text_chan, cs.CHANNEL_IFACE_GROUP) members = group_iface.GetAllMembers()[0] local_pending = group_iface.GetAllMembers()[1] remote_pending = group_iface.GetAllMembers()[2] assert len(members) == 1 assert conn.InspectHandles(1, members)[0] == 'bob@localhost' bob_handle = members[0] assert len(local_pending) == 1 # FIXME: the username-part-is-nickname assumption assert conn.InspectHandles(1, local_pending)[0] == \ 'chat@conf.localhost/test' assert len(remote_pending) == 0 room_self_handle = group_iface.GetSelfHandle() assert room_self_handle == local_pending[0] # by now, we should have picked up the extra activity properties buddy_iface = dbus.Interface(conn, 'org.laptop.Telepathy.BuddyInfo') call_async(q, buddy_iface, 'GetActivities', bob_handle) event = q.expect('stream-iq', iq_type='get', to='bob@localhost') # Bob still has no (public) activities event.stanza['type'] = 'result' event.stanza['to'] = 'test@localhost' event.stanza['from'] = 'bob@localhost' stream.send(event.stanza) event = q.expect('dbus-return', method='GetActivities') assert event.value == ([('foo_id', room_handle)],) props = act_prop_iface.GetProperties(room_handle) assert len(props) == 2 assert props['title'] == 'From the invitation' assert props['private'] == True # Now Bob changes the properties message = domish.Element(('jabber:client', 'message')) message['from'] = 'bob@localhost' message['to'] = 'test@localhost' properties = message.addElement( (ns.OLPC_ACTIVITY_PROPS, 'properties')) properties['room'] = 'chat@conf.localhost' properties['activity'] = 'foo_id' property = properties.addElement((None, 'property')) property['type'] = 'str' property['name'] = 'title' property.addContent('Mushroom, mushroom') property = properties.addElement((None, 'property')) property['type'] = 'bool' property['name'] = 'private' property.addContent('0') stream.send(message) event = q.expect('dbus-signal', signal='ActivityPropertiesChanged') assert event.args == [room_handle, {'title': 'Mushroom, mushroom', 'private': False }] assert act_prop_iface.GetProperties(room_handle) == \ event.args[1] # OK, now accept the invitation call_async(q, group_iface, 'AddMembers', [room_self_handle], 'Oh, OK then') q.expect_many( EventPattern('stream-presence', to='chat@conf.localhost/test'), EventPattern('dbus-signal', signal='MembersChanged', args=['', [], [bob_handle], [], [room_self_handle], 0, cs.GC_REASON_INVITED]), EventPattern('dbus-return', method='AddMembers'), ) # Send presence for own membership of room. stream.send(make_muc_presence('owner', 'moderator', 'chat@conf.localhost', 'test')) event = q.expect('dbus-signal', signal='MembersChanged') assert event.args == ['', [room_self_handle], [], [], [], 0, 0] call_async(q, buddy_iface, 'SetActivities', [('foo_id', room_handle)]) event = q.expect('stream-iq', iq_type='set') # Now that it's not private, it'll go in my PEP event.stanza['type'] = 'result' event.stanza['to'] = 'test@localhost' event.stanza['from'] = 'test@localhost' stream.send(event.stanza) q.expect('dbus-return', method='SetActivities') # Bob changes the properties and tells the room he's done so message = domish.Element(('jabber:client', 'message')) message['from'] = 'chat@conf.localhost/bob' message['to'] = 'chat@conf.localhost' properties = message.addElement( (ns.OLPC_ACTIVITY_PROPS, 'properties')) properties['activity'] = 'foo_id' property = properties.addElement((None, 'property')) property['type'] = 'str' property['name'] = 'title' property.addContent('Badger badger badger') property = properties.addElement((None, 'property')) property['type'] = 'bool' property['name'] = 'private' property.addContent('0') stream.send(message) event = q.expect('stream-iq', iq_type='set') message = event.stanza activities = xpath.queryForNodes('/iq/pubsub/publish/item/activities', message) assert (activities is not None and len(activities) == 1), repr(activities) assert activities[0].uri == ns.OLPC_ACTIVITY_PROPS properties = xpath.queryForNodes('/activities/properties', activities[0]) assert (properties is not None and len(properties) == 1), repr(properties) assert properties[0].uri == ns.OLPC_ACTIVITY_PROPS assert properties[0]['room'] == 'chat@conf.localhost' assert properties[0]['activity'] == 'foo_id' property = xpath.queryForNodes('/properties/property', properties[0]) assert (property is not None and len(property) == 2), repr(property) seen = set() for p in property: seen.add(p['name']) if p['name'] == 'title': assert p['type'] == 'str' assert str(p) == 'Badger badger badger' elif p['name'] == 'private': assert p['type'] == 'bool' assert str(p) == '0' else: assert False, 'Unexpected property %s' % p['name'] assert 'title' in seen, seen assert 'private' in seen, seen event.stanza['type'] = 'result' event.stanza['to'] = 'test@localhost' event.stanza['from'] = 'test@localhost' stream.send(event.stanza) act_prop_iface = dbus.Interface(conn, 'org.laptop.Telepathy.ActivityProperties') # test sets the title and sets private back to True call_async(q, act_prop_iface, 'SetProperties', room_handle, {'title': 'I can set the properties too', 'private': True}) event = q.expect('stream-message', to='chat@conf.localhost') message = event.stanza properties = xpath.queryForNodes('/message/properties', message) assert (properties is not None and len(properties) == 1), repr(properties) assert properties[0].uri == ns.OLPC_ACTIVITY_PROPS assert properties[0]['room'] == 'chat@conf.localhost' assert properties[0]['activity'] == 'foo_id' property = xpath.queryForNodes('/properties/property', properties[0]) assert (property is not None and len(property) == 2), repr(property) seen = set() for p in property: seen.add(p['name']) if p['name'] == 'title': assert p['type'] == 'str' assert str(p) == 'I can set the properties too' elif p['name'] == 'private': assert p['type'] == 'bool' assert str(p) == '1' else: assert False, 'Unexpected property %s' % p['name'] assert 'title' in seen, seen assert 'private' in seen, seen event = q.expect('stream-iq', iq_type='set') event.stanza['type'] = 'result' event.stanza['to'] = 'test@localhost' event.stanza['from'] = 'test@localhost' stream.send(event.stanza) message = event.stanza activities = xpath.queryForNodes('/iq/pubsub/publish/item/activities', message) assert (activities is not None and len(activities) == 1), repr(activities) assert activities[0].uri == ns.OLPC_ACTIVITY_PROPS properties = xpath.queryForNodes('/activities/properties', activities[0]) assert properties is None, repr(properties) event = q.expect('stream-iq', iq_type='set') event.stanza['type'] = 'result' event.stanza['to'] = 'test@localhost' event.stanza['from'] = 'test@localhost' stream.send(event.stanza) message = event.stanza activities = xpath.queryForNodes('/iq/pubsub/publish/item/activities', message) assert (activities is not None and len(activities) == 1), repr(activities) assert activities[0].uri == ns.OLPC_ACTIVITIES activity = xpath.queryForNodes('/activities/activity', activities[0]) assert activity is None, repr(activity) q.expect('dbus-return', method='SetProperties') # test sets the title and sets private back to True call_async(q, act_prop_iface, 'SetProperties', room_handle, {'title': 'I can set the properties too', 'private': False}) event = q.expect('stream-message', to='chat@conf.localhost') message = event.stanza properties = xpath.queryForNodes('/message/properties', message) assert (properties is not None and len(properties) == 1), repr(properties) assert properties[0].uri == ns.OLPC_ACTIVITY_PROPS assert properties[0]['room'] == 'chat@conf.localhost' assert properties[0]['activity'] == 'foo_id' property = xpath.queryForNodes('/properties/property', properties[0]) assert (property is not None and len(property) == 2), repr(property) seen = set() for p in property: seen.add(p['name']) if p['name'] == 'title': assert p['type'] == 'str' assert str(p) == 'I can set the properties too' elif p['name'] == 'private': assert p['type'] == 'bool' assert str(p) == '0' else: assert False, 'Unexpected property %s' % p['name'] assert 'title' in seen, seen assert 'private' in seen, seen event = q.expect('stream-iq', iq_type='set') event.stanza['type'] = 'result' event.stanza['to'] = 'test@localhost' event.stanza['from'] = 'test@localhost' stream.send(event.stanza) message = event.stanza activities = xpath.queryForNodes('/iq/pubsub/publish/item/activities', message) assert (activities is not None and len(activities) == 1), repr(activities) assert activities[0].uri == ns.OLPC_ACTIVITY_PROPS properties = xpath.queryForNodes('/activities/properties', activities[0]) assert (properties is not None and len(properties) == 1), repr(properties) assert properties[0].uri == ns.OLPC_ACTIVITY_PROPS assert properties[0]['room'] == 'chat@conf.localhost' assert properties[0]['activity'] == 'foo_id' property = xpath.queryForNodes('/properties/property', properties[0]) assert (property is not None and len(property) == 2), repr(property) seen = set() for p in property: seen.add(p['name']) if p['name'] == 'title': assert p['type'] == 'str' assert str(p) == 'I can set the properties too' elif p['name'] == 'private': assert p['type'] == 'bool' assert str(p) == '0' else: assert False, 'Unexpected property %s' % p['name'] assert 'title' in seen, seen assert 'private' in seen, seen event = q.expect('stream-iq', iq_type='set') event.stanza['type'] = 'result' event.stanza['to'] = 'test@localhost' event.stanza['from'] = 'test@localhost' stream.send(event.stanza) message = event.stanza activities = xpath.queryForNodes('/iq/pubsub/publish/item/activities', message) assert (activities is not None and len(activities) == 1), repr(activities) assert activities[0].uri == ns.OLPC_ACTIVITIES activity = xpath.queryForNodes('/activities/activity', activities[0]) assert (activity is not None and len(activity) == 1), repr(activity) assert activity[0]['room'] == 'chat@conf.localhost' assert activity[0]['type'] == 'foo_id' # sic q.expect('dbus-return', method='SetProperties') chan_iface.Close() # we must echo the MUC presence so the room will actually close event = q.expect('stream-presence', to='chat@conf.localhost/test', presence_type='unavailable') echo_muc_presence(q, stream, event.stanza, 'none', 'participant') event = q.expect('stream-iq', iq_type='set') event.stanza['type'] = 'result' event.stanza['to'] = 'test@localhost' event.stanza['from'] = 'test@localhost' stream.send(event.stanza) message = event.stanza activities = xpath.queryForNodes('/iq/pubsub/publish/item/activities', message) assert (activities is not None and len(activities) == 1), repr(activities) assert activities[0].uri == ns.OLPC_ACTIVITIES activity = xpath.queryForNodes('/activities/activity', activities[0]) assert activity is None, repr(activity) event = q.expect('stream-iq', iq_type='set') event.stanza['type'] = 'result' event.stanza['to'] = 'test@localhost' event.stanza['from'] = 'test@localhost' stream.send(event.stanza) message = event.stanza activities = xpath.queryForNodes('/iq/pubsub/publish/item/activities', message) assert (activities is not None and len(activities) == 1), repr(activities) assert activities[0].uri == ns.OLPC_ACTIVITY_PROPS properties = xpath.queryForNodes('/activities/properties', activities[0]) assert properties is None, repr(properties) if __name__ == '__main__': exec_test(test) telepathy-gabble-0.18.2/tests/twisted/olpc/olpc-muc-invitation.py0000644000175000017500000002110512227000321025036 0ustar00smcvsmcv00000000000000""" Test OLPC extensions to MUC invitations. """ import dbus from twisted.words.xish import domish, xpath from gabbletest import exec_test, make_muc_presence from servicetest import call_async, EventPattern import constants as cs import ns def test(q, bus, conn, stream): handles = {} handles['bob'] = conn.RequestHandles(1, ['bob@localhost'])[0] buddy_iface = dbus.Interface(conn, 'org.laptop.Telepathy.BuddyInfo') act_prop_iface = dbus.Interface(conn, 'org.laptop.Telepathy.ActivityProperties') call_async(q, buddy_iface, 'GetActivities', handles['bob']) event = q.expect('stream-iq', iq_type='get', to='bob@localhost') # Bob has no activities event.stanza['type'] = 'result' event.stanza['to'] = 'test@localhost' event.stanza['from'] = 'bob@localhost' stream.send(event.stanza) event = q.expect('dbus-return', method='GetActivities') # initially, Bob has no activities assert event.value == ([],) # Bob sends an activity properties message message = domish.Element(('jabber:client', 'message')) message['from'] = 'bob@localhost' message['to'] = 'test@localhost' properties = message.addElement((ns.OLPC_ACTIVITY_PROPS, 'properties')) properties['room'] = 'chat@conf.localhost' properties['activity'] = 'foo_id' property = properties.addElement((None, 'property')) property['type'] = 'str' property['name'] = 'color' property.addContent('#ffff00,#00ffff') property = properties.addElement((None, 'property')) property['type'] = 'bool' property['name'] = 'private' property.addContent('1') stream.send(message) event = q.expect('dbus-signal', signal='ActivityPropertiesChanged') handles['chat'], props = event.args assert props == {'color': '#ffff00,#00ffff', 'private' : True} event = q.expect('dbus-signal', signal='ActivitiesChanged') assert event.args[0] == handles['bob'] acts = event.args[1] assert len(acts) == 1 assert acts[0] == ('foo_id', handles['chat']) props = act_prop_iface.GetProperties(handles['chat']) assert props == {'color': '#ffff00,#00ffff', 'private' : True} # Bobs invites us to the activity message = domish.Element((None, 'message')) message['from'] = 'chat@conf.localhost' message['to'] = 'test@localhost' x = message.addElement((ns.MUC_USER, 'x')) invite = x.addElement((None, 'invite')) invite['from'] = 'bob@localhost' reason = invite.addElement((None, 'reason')) reason.addContent('No good reason') stream.send(message) event = q.expect('dbus-signal', signal='NewChannel') assert event.args[1] == cs.CHANNEL_TYPE_TEXT assert event.args[2] == 2 # handle type assert event.args[3] == handles['chat'] # handle text_chan = bus.get_object(conn.bus_name, event.args[0]) group_iface = dbus.Interface(text_chan, cs.CHANNEL_IFACE_GROUP) members = group_iface.GetAllMembers()[0] local_pending = group_iface.GetAllMembers()[1] remote_pending = group_iface.GetAllMembers()[2] assert len(members) == 1 assert conn.InspectHandles(1, members)[0] == 'bob@localhost' assert len(local_pending) == 1 # FIXME: the username-part-is-nickname assumption assert conn.InspectHandles(1, local_pending)[0] == \ 'chat@conf.localhost/test' assert len(remote_pending) == 0 handles['chat_self'] = group_iface.GetSelfHandle() assert handles['chat_self'] == local_pending[0] # by now, we should have picked up the extra activity properties call_async(q, buddy_iface, 'GetActivities', handles['bob']) event = q.expect('stream-iq', iq_type='get', to='bob@localhost') # Bob still has no (public) activities event.stanza['type'] = 'result' event.stanza['to'] = 'test@localhost' event.stanza['from'] = 'bob@localhost' stream.send(event.stanza) event = q.expect('dbus-return', method='GetActivities') assert event.value == ([('foo_id', handles['chat'])],) # OK, now accept the invitation call_async(q, group_iface, 'AddMembers', [handles['chat_self']], 'Oh, OK then') _, event, _ = q.expect_many( EventPattern('stream-presence', to='chat@conf.localhost/test'), EventPattern('dbus-signal', signal='MembersChanged'), EventPattern('dbus-return', method='AddMembers') ) assert event.args == ['', [], [handles['bob']], [], [handles['chat_self']], 0, cs.GC_REASON_INVITED] # Send presence for own membership of room. stream.send(make_muc_presence('owner', 'moderator', 'chat@conf.localhost', 'test')) event = q.expect('dbus-signal', signal='MembersChanged') assert event.args == ['', [handles['chat_self']], [], [], [], 0, 0] call_async(q, buddy_iface, 'SetActivities', [('foo_id', handles['chat'])]) event = q.expect('stream-iq', iq_type='set') event.stanza['type'] = 'result' event.stanza['to'] = 'test@localhost' event.stanza['from'] = 'test@localhost' stream.send(event.stanza) q.expect('dbus-return', method='SetActivities') call_async(q, act_prop_iface, 'SetProperties', handles['chat'], {'color': '#ffff00,#00ffff', 'private': True}) event = q.expect('dbus-signal', signal='ActivityPropertiesChanged') chat_handle, props = event.args assert chat_handle == handles['chat'] assert props == {'color': '#ffff00,#00ffff', 'private' : True} q.expect('dbus-return', method='SetProperties') # Test sending an invitation handles['alice'] = conn.RequestHandles(1, ['alice@localhost'])[0] call_async(q, group_iface, 'AddMembers', [handles['alice']], 'I want to test invitations') event = q.expect('stream-message', to='alice@localhost') message = event.stanza properties = xpath.queryForNodes('/message/properties', message) assert (properties is not None and len(properties) == 1), repr(properties) assert properties[0].uri == ns.OLPC_ACTIVITY_PROPS assert properties[0]['room'] == 'chat@conf.localhost' assert properties[0]['activity'] == 'foo_id' property = xpath.queryForNodes('/properties/property', properties[0]) assert (property is not None and len(property) == 2), repr(property) seen = set() for p in property: seen.add(p['name']) if p['name'] == 'color': assert p['type'] == 'str' assert str(p) == '#ffff00,#00ffff' elif p['name'] == 'private': assert p['type'] == 'bool' assert str(p) == '1' else: assert False, 'Unexpected property %s' % p['name'] assert 'color' in seen, seen assert 'private' in seen, seen event = q.expect('stream-message', to='chat@conf.localhost') message = event.stanza x = xpath.queryForNodes('/message/x', message) assert (x is not None and len(x) == 1), repr(x) assert x[0].uri == ns.MUC_USER invites = xpath.queryForNodes('/x/invite', x[0]) assert (invites is not None and len(invites) == 1), repr(invites) assert invites[0]['to'] == 'alice@localhost' reasons = xpath.queryForNodes('/invite/reason', invites[0]) assert (reasons is not None and len(reasons) == 1), repr(reasons) assert str(reasons[0]) == 'I want to test invitations' call_async(q, act_prop_iface, 'SetProperties', handles['chat'], {'color': '#f00baa,#f00baa', 'private': True}) event, apc_event, _ = q.expect_many( EventPattern('stream-message', to='alice@localhost'), EventPattern('dbus-signal', signal='ActivityPropertiesChanged'), EventPattern('dbus-return', method='SetProperties'), ) message = event.stanza properties = xpath.queryForNodes('/message/properties', message) assert (properties is not None and len(properties) == 1), repr(properties) assert properties[0].uri == ns.OLPC_ACTIVITY_PROPS assert properties[0]['room'] == 'chat@conf.localhost' assert properties[0]['activity'] == 'foo_id' property = xpath.queryForNodes('/properties/property', properties[0]) assert (property is not None and len(property) == 2), repr(property) seen = set() for p in property: seen.add(p['name']) if p['name'] == 'color': assert p['type'] == 'str' assert str(p) == '#f00baa,#f00baa' elif p['name'] == 'private': assert p['type'] == 'bool' assert str(p) == '1' else: assert False, 'Unexpected property %s' % p['name'] assert 'color' in seen, seen assert 'private' in seen, seen chat_handle, props = apc_event.args assert chat_handle == handles['chat'] assert props == {'color': '#f00baa,#f00baa', 'private' : True} if __name__ == '__main__': exec_test(test) telepathy-gabble-0.18.2/tests/twisted/olpc/current-activity.py0000644000175000017500000000454712227000321024464 0ustar00smcvsmcv00000000000000""" test OLPC Buddy properties current activity """ import dbus from servicetest import call_async, EventPattern from gabbletest import exec_test, acknowledge_iq import constants as cs from util import (send_buddy_changed_current_act_msg, answer_to_current_act_pubsub_request, answer_error_to_pubsub_request) import ns def test(q, bus, conn, stream): iq_event, disco_event = q.expect_many( EventPattern('stream-iq', to=None, query_ns='vcard-temp', query_name='vCard'), EventPattern('stream-iq', to='localhost', query_ns=ns.DISCO_ITEMS)) acknowledge_iq(stream, iq_event.stanza) buddy_info_iface = dbus.Interface(conn, 'org.laptop.Telepathy.BuddyInfo') handles = {} # Alice is one of our friend so we receive her PEP notifications handles['alice'] = conn.RequestHandles(1, ['alice@localhost'])[0] # Try to get Alice's currrent-activity call_async(q, buddy_info_iface, "GetCurrentActivity", handles['alice']) # Alice's current-activity is not in the cache so Gabble sends a PEP query event = q.expect('stream-iq', iq_type='get', query_name='pubsub') answer_to_current_act_pubsub_request(stream, event.stanza, 'activity1', 'room1@conference.localhost') event = q.expect('dbus-return', method='GetCurrentActivity') id, handles['room1'] = event.value assert id == 'activity1' assert conn.InspectHandles(2, [handles['room1']]) == \ ['room1@conference.localhost'] # Retry to get Alice's current-activity # Alice's current-activity is now in the cache so Gabble doesn't # send PEP query assert buddy_info_iface.GetCurrentActivity(handles['alice']) == \ ('activity1', handles['room1']) # Alice changed her current-activity send_buddy_changed_current_act_msg(stream, 'alice@localhost', 'activity2', 'room2@conference.localhost') event = q.expect('dbus-signal', signal='CurrentActivityChanged') contact, id, handles['room2'] = event.args assert contact == handles['alice'] assert id == 'activity2' assert conn.InspectHandles(2, [handles['room2']]) == \ ['room2@conference.localhost'] # Get Alice's current-activity as the cache have to be updated assert buddy_info_iface.GetCurrentActivity(handles['alice']) == \ ('activity2', handles['room2']) if __name__ == '__main__': exec_test(test) telepathy-gabble-0.18.2/tests/twisted/olpc/change-notifications.py0000644000175000017500000000175412227000321025241 0ustar00smcvsmcv00000000000000""" test OLPC Buddy properties change notifications """ # FIXME: merge this file to other tests ? from servicetest import EventPattern from gabbletest import exec_test, acknowledge_iq import constants as cs from util import send_buddy_changed_properties_msg import ns def test(q, bus, conn, stream): iq_event, disco_event = q.expect_many( EventPattern('stream-iq', to=None, query_ns='vcard-temp', query_name='vCard'), EventPattern('stream-iq', to='localhost', query_ns=ns.DISCO_ITEMS)) acknowledge_iq(stream, iq_event.stanza) handles = {} handles['alice'] = conn.RequestHandles(1, ['alice@localhost'])[0] # Alice, one our friends changed her properties send_buddy_changed_properties_msg(stream, 'alice@localhost', {'color': ('str', '#005FE4,#00A0FF')}) event = q.expect('dbus-signal', signal='PropertiesChanged', args=[handles['alice'], {'color' : '#005FE4,#00A0FF'}]) if __name__ == '__main__': exec_test(test) telepathy-gabble-0.18.2/tests/twisted/muc/0000755000175000017500000000000012312537051020425 5ustar00smcvsmcv00000000000000telepathy-gabble-0.18.2/tests/twisted/muc/test-muc.py0000644000175000017500000002446412227000321022540 0ustar00smcvsmcv00000000000000# vim: set fileencoding=utf-8 : Python sucks! """ Test MUC support. """ import dbus from twisted.words.xish import domish, xpath from gabbletest import exec_test from servicetest import ( EventPattern, assertEquals, assertLength, assertContains, assertDoesNotContain, ) import constants as cs import ns from mucutil import join_muc_and_check def test(q, bus, conn, stream): room = 'chat@conf.localhost' room_handle, chan, test_handle, bob_handle = \ join_muc_and_check(q, bus, conn, stream, room) # Exercise basic Channel Properties from spec 0.17.7 channel_props = chan.Properties.GetAll(cs.CHANNEL) assertEquals(room_handle, channel_props.get('TargetHandle')) assertEquals(cs.HT_ROOM, channel_props.get('TargetHandleType')) assertEquals(cs.CHANNEL_TYPE_TEXT, channel_props.get('ChannelType')) interfaces = channel_props.get('Interfaces') assertContains(cs.CHANNEL_IFACE_GROUP, interfaces) assertContains(cs.CHANNEL_IFACE_PASSWORD, interfaces) assertDoesNotContain(cs.TP_AWKWARD_PROPERTIES, interfaces) assertContains(cs.CHANNEL_IFACE_CHAT_STATE, interfaces) assertContains(cs.CHANNEL_IFACE_MESSAGES, interfaces) assert channel_props['TargetID'] == 'chat@conf.localhost', channel_props assert channel_props['Requested'] == True assert channel_props['InitiatorID'] == 'test@localhost' assert channel_props['InitiatorHandle'] == conn.GetSelfHandle() # Exercise Group Properties from spec 0.17.6 (in a basic way) group_props = chan.Properties.GetAll(cs.CHANNEL_IFACE_GROUP) assert 'HandleOwners' in group_props, group_props assert 'Members' in group_props, group_props assert 'LocalPendingMembers' in group_props, group_props assert 'RemotePendingMembers' in group_props, group_props assert 'GroupFlags' in group_props, group_props # Test receiving a message from Bob in the MUC message = domish.Element((None, 'message')) message['from'] = 'chat@conf.localhost/bob' message['type'] = 'groupchat' body = message.addElement('body', content='hello') stream.send(message) received, message_received = q.expect_many( EventPattern('dbus-signal', signal='Received'), EventPattern('dbus-signal', signal='MessageReceived'), ) # Check Channel.Type.Text.Received: # sender: bob assert received.args[2] == bob_handle # message type: normal assert received.args[3] == 0 # flags: none assert received.args[4] == 0 # body assert received.args[5] == 'hello' # Check Channel.Interface.Messages.MessageReceived: message = message_received.args[0] # message should have two parts: the header and one content part assert len(message) == 2, message header, body = message assert header['message-sender'] == bob_handle, header # the spec says that message-type "SHOULD be omitted for normal chat # messages." assert 'message-type' not in header, header assert body['content-type'] == 'text/plain', body assert body['content'] == 'hello', body # Remove the message from the pending message queue, and check that # PendingMessagesRemoved fires. message_id = header['pending-message-id'] chan.Text.AcknowledgePendingMessages([message_id]) removed = q.expect('dbus-signal', signal='PendingMessagesRemoved') removed_ids = removed.args[0] assert len(removed_ids) == 1, removed_ids assert removed_ids[0] == message_id, (removed_ids, message_id) # Send an action using the Messages API greeting = [ dbus.Dictionary({ 'message-type': 1, # Action }, signature='sv'), { 'content-type': 'text/plain', 'content': u"peers through a gap in the curtains", } ] # We ask for delivery reports (which MUCs provide) and read reports (which # MUCs do not provide). sent_token = chan.Messages.SendMessage(greeting, cs.MSG_SENDING_FLAGS_REPORT_DELIVERY | cs.MSG_SENDING_FLAGS_REPORT_READ) assert sent_token stream_message, sent, message_sent = q.expect_many( EventPattern('stream-message'), EventPattern('dbus-signal', signal='Sent'), EventPattern('dbus-signal', signal='MessageSent'), ) sent_message, flags, token = message_sent.args assert len(sent_message) == 2, sent_message header = sent_message[0] assert header['message-type'] == 1, header # Action assertEquals(test_handle, header['message-sender']) assertEquals('chat@conf.localhost/test', header['message-sender-id']) body = sent_message[1] assert body['content-type'] == 'text/plain', body assert body['content'] == u'peers through a gap in the curtains', body # Of the flags passed to SendMessage, Gabble should only report the # DELIVERY flag, since the other is not supported. assertEquals(cs.MSG_SENDING_FLAGS_REPORT_DELIVERY, flags) assertEquals(sent_token, token) assert sent.args[1] == 1, sent.args # Action assert sent.args[2] == u'peers through a gap in the curtains', sent.args assert message_sent.args[2] == sent_token elem = stream_message.stanza assert elem.name == 'message' assert elem['type'] == 'groupchat', repr(elem) assert elem['id'] == sent_token, repr(elem) assert elem['to'] == 'chat@conf.localhost', repr(elem) for sub_elem in stream_message.stanza.elements(): if sub_elem.name == 'body': found_body = True assert sub_elem.children[0] == u'/me peers through a gap in the curtains' break assert found_body # reflect the sent message back to the MUC elem['from'] = 'chat@conf.localhost/test' stream.send(elem) # Check that we got the corresponding delivery report report, old_received = q.expect_many( EventPattern('dbus-signal', signal='MessageReceived'), EventPattern('dbus-signal', signal='Received'), ) assert len(report.args) == 1, report.args parts = report.args[0] # The delivery report should just be a header, no body. assert len(parts) == 1, parts part = parts[0] # The intended recipient was the MUC, so there's no contact handle # suitable for being 'message-sender'. assert 'message-sender' not in part or part['message-sender'] == 0, part assert part['message-type'] == 4, part # Message_Type_Delivery_Report assert part['delivery-status'] == 1, part # Delivery_Status_Delivered assert part['delivery-token'] == sent_token, part assert 'delivery-error' not in part, part assert 'delivery-echo' in part, part # Check that the included echo is from us, and matches all the keys in the # message we sent. echo = part['delivery-echo'] assert len(echo) == len(greeting), (echo, greeting) assert echo[0]['message-sender'] == test_handle, echo[0] assert echo[0]['message-token'] == sent_token, echo[0] for i in range(0, len(echo)): for key in greeting[i]: assert key in echo[i], (i, key, echo) assert echo[i][key] == greeting[i][key], (i, key, echo, greeting) # The Text.Received signal should be a "you're not tall enough" stub id, timestamp, sender, type, flags, text = old_received.args assert sender == 0, old_received.args assert type == 4, old_received.args # Message_Type_Delivery_Report assert flags == 2, old_received.args # Non_Text_Content assert text == '', old_received.args # Send a normal message using the Channel.Type.Text API chan.Text.Send(0, 'goodbye') event, sent, message_sent = q.expect_many( EventPattern('stream-message'), EventPattern('dbus-signal', signal='Sent'), EventPattern('dbus-signal', signal='MessageSent'), ) sent_message, flags, _ = message_sent.args assert len(sent_message) == 2, sent_message header = sent_message[0] assert 'message-type' not in header, header # Normal body = sent_message[1] assert body['content-type'] == 'text/plain', body assert body['content'] == u'goodbye', body # The caller didn't ask for delivery reports (how could they? they're using # the old API), but the server's going to send us an echo anyway, so # Gabble's within its rights to pretend that the caller asked. assert flags in [0, cs.MSG_SENDING_FLAGS_REPORT_DELIVERY], flags assert sent.args[1] == 0, sent.args # Normal assert sent.args[2] == u'goodbye', sent.args sent_token = message_sent.args[2] elem = event.stanza assert elem.name == 'message' assert elem['type'] == 'groupchat' assert elem['id'] == message_sent.args[2] body = list(event.stanza.elements())[0] assert body.name == 'body' assert body.children[0] == u'goodbye' # reflect the sent message back to the MUC elem['from'] = 'chat@conf.localhost/test' stream.send(elem) # TODO: check for a delivery report. # test that presence changes are sent via the MUC conn.SimplePresence.SetPresence('away', 'hurrah') event = q.expect('stream-presence', to='chat@conf.localhost/test') elem = event.stanza show = [e for e in elem.elements() if e.name == 'show'][0] assert show assert show.children[0] == u'away' status = [e for e in elem.elements() if e.name == 'status'][0] assert status assert status.children[0] == u'hurrah' # Check that there's no element in the # stanza when we're just updating our presence, as opposed to joining the # MUC in the first place. This is a regression test for # . XEP-0045 §7.4 shows # that you do not need to include this element in presence updates; if we # erroneously include it, some implementations take this to mean that we're # trying to join the MUC again and helpfully send us all the scrollback # again. x_muc_nodes = xpath.queryForNodes('/presence/x[@xmlns="%s"]' % ns.MUC, elem) assert x_muc_nodes is None, elem.toXml() # test that leaving the channel results in an unavailable message chan.Group.RemoveMembers([chan.Group.GetSelfHandle()], 'booo') event = q.expect('stream-presence', to='chat@conf.localhost/test') elem = event.stanza assert elem['type'] == 'unavailable' status = [e for e in elem.elements() if e.name == 'status'] assertLength(1, status) assertEquals(status[0].children[0], u'booo') if __name__ == '__main__': exec_test(test) telepathy-gabble-0.18.2/tests/twisted/muc/test-muc-ownership.py0000644000175000017500000000770212227000321024550 0ustar00smcvsmcv00000000000000 """ Test support for the HANDLE_OWNERS_NOT_AVAILABLE group flag, and calling GetHandleOwners on MUC members. By default, MUC channels should have the flag set. The flag should be unset when presence is received that includes the MUC JID's owner JID. """ import dbus from gabbletest import make_result_iq, exec_test, make_muc_presence from servicetest import ( call_async, EventPattern, assertEquals, assertFlagsSet, assertFlagsUnset, wrap_channel, ) import constants as cs def test(q, bus, conn, stream): self_handle = conn.GetSelfHandle() room_handle = conn.RequestHandles(cs.HT_ROOM, ['chat@conf.localhost'])[0] call_async(q, conn, 'RequestChannel', cs.CHANNEL_TYPE_TEXT, cs.HT_ROOM, room_handle, True) gfc, _, _, _ = q.expect_many( # Initial group flags EventPattern('dbus-signal', signal='GroupFlagsChanged', predicate=lambda e: e.args[0] != 0), EventPattern('dbus-signal', signal='MembersChanged', args=[u'', [], [], [], [2], 0, 0]), # Removing CAN_ADD EventPattern('dbus-signal', signal='GroupFlagsChanged', args = [0, cs.GF_CAN_ADD], predicate=lambda e: e.args[0] == 0), EventPattern('stream-presence', to='chat@conf.localhost/test')) assert gfc.args[1] == 0 # Send presence for anonymous other member of room. stream.send(make_muc_presence('owner', 'moderator', 'chat@conf.localhost', 'bob')) # Send presence for anonymous other member of room (2) stream.send(make_muc_presence('owner', 'moderator', 'chat@conf.localhost', 'brian')) # Send presence for nonymous other member of room. stream.send(make_muc_presence('none', 'participant', 'chat@conf.localhost', 'che', 'che@foo.com')) # Send presence for nonymous other member of room (2) stream.send(make_muc_presence('none', 'participant', 'chat@conf.localhost', 'chris', 'chris@foo.com')) # Send presence for own membership of room. stream.send(make_muc_presence('none', 'participant', 'chat@conf.localhost', 'test')) # Since we received MUC presence that contains an owner JID, the # OWNERS_NOT_AVAILABLE flag should be removed. event = q.expect('dbus-signal', signal='GroupFlagsChanged', args = [0, cs.GF_HANDLE_OWNERS_NOT_AVAILABLE ]) event = q.expect('dbus-signal', signal='HandleOwnersChanged') owners = event.args[0] event = q.expect('dbus-signal', signal='MembersChanged') added = event.args[1] [test, bob, brian, che, che_owner, chris, chris_owner] = \ conn.RequestHandles(cs.HT_CONTACT, [ 'chat@conf.localhost/test', 'chat@conf.localhost/bob', 'chat@conf.localhost/brian', 'chat@conf.localhost/che', 'che@foo.com', 'chat@conf.localhost/chris', 'chris@foo.com', ]) expected_members = sorted([test, bob, brian, che, chris]) expected_owners = { test: self_handle, bob: 0, brian: 0, che: che_owner, chris: chris_owner } assertEquals(expected_members, sorted(added)) assertEquals(expected_owners, owners) event = q.expect('dbus-return', method='RequestChannel') chan = wrap_channel(bus.get_object(conn.bus_name, event.value[0]), 'Text') # Exercise GetHandleOwners assertEquals([che_owner, chris_owner], chan.Group.GetHandleOwners([che, chris])) # Exercise D-Bus properties all = chan.Properties.GetAll(cs.CHANNEL_IFACE_GROUP) assert all[u'LocalPendingMembers'] == [], all assert sorted(all[u'Members']) == expected_members, all assert all[u'RemotePendingMembers'] == [], all assert all[u'SelfHandle'] == test, all assert all[u'HandleOwners'] == expected_owners, all flags = all[u'GroupFlags'] assertFlagsSet(cs.GF_PROPERTIES | cs.GF_CHANNEL_SPECIFIC_HANDLES, flags) assertFlagsUnset(cs.GF_HANDLE_OWNERS_NOT_AVAILABLE, flags) if __name__ == '__main__': exec_test(test) telepathy-gabble-0.18.2/tests/twisted/muc/test-muc-invitation.py0000644000175000017500000001073512227000321024716 0ustar00smcvsmcv00000000000000""" Test MUC invitations. """ import dbus from twisted.words.xish import domish, xpath from gabbletest import exec_test, make_muc_presence from servicetest import call_async, EventPattern import constants as cs def test(q, bus, conn, stream): # Bob has invited us to an activity. message = domish.Element((None, 'message')) message['from'] = 'chat@conf.localhost' message['to'] = 'test@localhost' x = message.addElement(('http://jabber.org/protocol/muc#user', 'x')) invite = x.addElement((None, 'invite')) invite['from'] = 'bob@localhost' reason = invite.addElement((None, 'reason')) reason.addContent('No good reason') stream.send(message) event = q.expect('dbus-signal', signal='NewChannel') assert event.args[1] == cs.CHANNEL_TYPE_TEXT assert event.args[2] == 2 # handle type assert event.args[3] == 1 # handle room_handle = 1 text_chan = bus.get_object(conn.bus_name, event.args[0]) group_iface = dbus.Interface(text_chan, cs.CHANNEL_IFACE_GROUP) members = group_iface.GetMembers() local_pending = group_iface.GetLocalPendingMembers() remote_pending = group_iface.GetRemotePendingMembers() assert len(members) == 1 assert conn.InspectHandles(1, members)[0] == 'bob@localhost' bob_handle = members[0] assert len(local_pending) == 1 # FIXME: the username-part-is-nickname assumption assert conn.InspectHandles(1, local_pending)[0] == \ 'chat@conf.localhost/test' assert len(remote_pending) == 0 room_self_handle = group_iface.GetSelfHandle() assert room_self_handle == local_pending[0] channel_props = text_chan.GetAll( cs.CHANNEL, dbus_interface=dbus.PROPERTIES_IFACE) assert channel_props['TargetID'] == 'chat@conf.localhost', channel_props assert channel_props['Requested'] == False assert channel_props['InitiatorID'] == 'bob@localhost' assert channel_props['InitiatorHandle'] == bob_handle # set ourselves to away and back again, to check that we don't send any # presence to the MUC before the invite has been accepted conn.SimplePresence.SetPresence('away', 'failure') conn.SimplePresence.SetPresence('available', 'success') # accept the invitation call_async(q, group_iface, 'AddMembers', [room_self_handle], 'Oh, OK then') event, event2, _ = q.expect_many( EventPattern('stream-presence', to='chat@conf.localhost/test'), EventPattern('dbus-signal', signal='MembersChanged'), EventPattern('dbus-return', method='AddMembers') ) # check that the status we joined with was available / success elem = event.stanza show = [e for e in elem.elements() if e.name == 'show'] assert not show status = [e for e in elem.elements() if e.name == 'status'][0] assert status assert status.children[0] == u'success' # We are added as remote pending while joining the room. The inviter (Bob) # is removed for now. It will be re-added with his channel specific handle # once we have joined. assert event2.args == ['', [], [bob_handle], [], [room_self_handle], 0, cs.GC_REASON_INVITED] # Send presence for Bob's membership of room. stream.send(make_muc_presence('owner', 'moderator', 'chat@conf.localhost', 'bob')) # Send presence for own membership of room. stream.send(make_muc_presence('owner', 'moderator', 'chat@conf.localhost', 'test')) event = q.expect('dbus-signal', signal='MembersChanged') room_bob_handle = conn.RequestHandles(cs.HT_CONTACT, ['chat@conf.localhost/bob'])[0] assert event.args == ['', [room_self_handle, room_bob_handle], [], [], [], 0, 0] # Test sending an invitation alice_handle = conn.RequestHandles(1, ['alice@localhost'])[0] call_async(q, group_iface, 'AddMembers', [alice_handle], 'I want to test invitations') event = q.expect('stream-message', to='chat@conf.localhost') message = event.stanza x = xpath.queryForNodes('/message/x', message) assert (x is not None and len(x) == 1), repr(x) assert x[0].uri == 'http://jabber.org/protocol/muc#user' invites = xpath.queryForNodes('/x/invite', x[0]) assert (invites is not None and len(invites) == 1), repr(invites) assert invites[0]['to'] == 'alice@localhost' reasons = xpath.queryForNodes('/invite/reason', invites[0]) assert (reasons is not None and len(reasons) == 1), repr(reasons) assert str(reasons[0]) == 'I want to test invitations' if __name__ == '__main__': exec_test(test) telepathy-gabble-0.18.2/tests/twisted/muc/test-muc-alias.py0000644000175000017500000000323412227000321023617 0ustar00smcvsmcv00000000000000""" Test that our alias is used to create MUC JIDs. """ from gabbletest import exec_test, make_muc_presence, request_muc_handle, \ expect_and_handle_get_vcard, expect_and_handle_set_vcard from servicetest import call_async, EventPattern import constants as cs def test(q, bus, conn, stream): expect_and_handle_get_vcard(q, stream) self_handle = conn.GetSelfHandle() conn.Aliasing.SetAliases({self_handle: 'lala'}) expect_and_handle_set_vcard(q, stream) event = q.expect('dbus-signal', signal='AliasesChanged', args=[[(self_handle, u'lala')]]) room_jid = 'chat@conf.localhost' room_handle = request_muc_handle(q, conn, stream, room_jid) call_async(q, conn, 'RequestChannel', cs.CHANNEL_TYPE_TEXT, cs.HT_ROOM, room_handle, True) gfc, _, _ = q.expect_many( EventPattern('dbus-signal', signal='GroupFlagsChanged'), EventPattern('dbus-signal', signal='MembersChanged', args=[u'', [], [], [], [2], 0, 0]), EventPattern('stream-presence', to='%s/lala' % room_jid)) assert gfc.args[1] == 0 # Send presence for other member of room. stream.send(make_muc_presence('owner', 'moderator', room_jid, 'bob')) # Send presence for own membership of room. stream.send(make_muc_presence('none', 'participant', room_jid, 'lala')) event = q.expect('dbus-signal', signal='MembersChanged', args=[u'', [2, 3], [], [], [], 0, 0]) assert conn.InspectHandles(1, [2]) == ['chat@conf.localhost/lala'] assert conn.InspectHandles(1, [3]) == ['chat@conf.localhost/bob'] event = q.expect('dbus-return', method='RequestChannel') if __name__ == '__main__': exec_test(test) telepathy-gabble-0.18.2/tests/twisted/muc/test-ensure.py0000644000175000017500000001231412227000321023244 0ustar00smcvsmcv00000000000000""" Test that EnsureChannel works for MUCs, particularly in the case when there are several pending requests for the same MUC. """ from gabbletest import make_result_iq, exec_test, make_muc_presence from servicetest import (call_async, EventPattern, assertContains, assertEquals) import constants as cs def test(q, bus, conn, stream): # Need to call this asynchronously as it involves Gabble sending us a # query. jids = ['chat@conf.localhost', 'chien@conf.localhost'] call_async(q, conn, 'RequestHandles', 2, jids) event = q.expect('dbus-return', method='RequestHandles') room_handles = event.value[0] test_create_ensure(q, conn, bus, stream, jids[0], room_handles[0]) test_ensure_ensure(q, conn, bus, stream, jids[1], room_handles[1]) def test_create_ensure(q, conn, bus, stream, room_jid, room_handle): # Call both Create and Ensure for the same channel. call_async(q, conn.Requests, 'CreateChannel', { cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_TEXT, cs.TARGET_HANDLE_TYPE: cs.HT_ROOM, cs.TARGET_HANDLE: room_handle, }) call_async(q, conn.Requests, 'EnsureChannel', { cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_TEXT, cs.TARGET_HANDLE_TYPE: cs.HT_ROOM, cs.TARGET_HANDLE: room_handle, }) mc, _ = q.expect_many( EventPattern('dbus-signal', signal='MembersChanged'), EventPattern('stream-presence', to=('%s/test' % room_jid))) msg, added, removed, local_pending, remote_pending, actor, reason = mc.args assert added == [], mc.args assert removed == [], mc.args assert local_pending == [], mc.args assert len(remote_pending) == 1, mc.args # Send presence for other member of room. stream.send(make_muc_presence('owner', 'moderator', room_jid, 'bob')) # Send presence for own membership of room. stream.send(make_muc_presence('none', 'participant', room_jid, 'test')) mc = q.expect('dbus-signal', signal='MembersChanged') msg, added, removed, local_pending, remote_pending, actor, reason = mc.args assert len(added) == 2, mc.args assert removed == [], mc.args assert local_pending == [], mc.args assert remote_pending == [], mc.args members = conn.InspectHandles(1, added) members.sort() assert members == ['%s/bob' % room_jid, '%s/test' % room_jid], members create_event, ensure_event = q.expect_many( EventPattern('dbus-return', method='CreateChannel'), EventPattern('dbus-return', method='EnsureChannel')) assert len(create_event.value) == 2 c_path, c_props = create_event.value assert len(ensure_event.value) == 3 yours, e_path, e_props = ensure_event.value assert c_path == e_path, (c_path, e_path) assert c_props == e_props, (c_props, e_props) assert not yours assertContains('text/plain', c_props[cs.SUPPORTED_CONTENT_TYPES]) assertEquals(0, c_props[cs.MESSAGE_PART_SUPPORT_FLAGS]) assertEquals( cs.DELIVERY_REPORTING_SUPPORT_FLAGS_RECEIVE_FAILURES | cs.DELIVERY_REPORTING_SUPPORT_FLAGS_RECEIVE_SUCCESSES, c_props[cs.DELIVERY_REPORTING_SUPPORT]) def test_ensure_ensure(q, conn, bus, stream, room_jid, room_handle): # Call Ensure twice for the same channel. call_async(q, conn.Requests, 'EnsureChannel', { cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_TEXT, cs.TARGET_HANDLE_TYPE: cs.HT_ROOM, cs.TARGET_HANDLE: room_handle, }) call_async(q, conn.Requests, 'EnsureChannel', { cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_TEXT, cs.TARGET_HANDLE_TYPE: cs.HT_ROOM, cs.TARGET_HANDLE: room_handle, }) mc, _ = q.expect_many( EventPattern('dbus-signal', signal='MembersChanged'), EventPattern('stream-presence', to=('%s/test' % room_jid))) msg, added, removed, local_pending, remote_pending, actor, reason = mc.args assert added == [], mc.args assert removed == [], mc.args assert local_pending == [], mc.args assert len(remote_pending) == 1, mc.args # Send presence for other member of room. stream.send(make_muc_presence('owner', 'moderator', room_jid, 'bob')) # Send presence for own membership of room. stream.send(make_muc_presence('none', 'participant', room_jid, 'test')) mc = q.expect('dbus-signal', signal='MembersChanged') msg, added, removed, local_pending, remote_pending, actor, reason = mc.args assert len(added) == 2, mc.args assert removed == [], mc.args assert local_pending == [], mc.args assert remote_pending == [], mc.args members = conn.InspectHandles(1, added) members.sort() assert members == ['%s/bob' % room_jid, '%s/test' % room_jid], members # We should get two EnsureChannel returns es = [] while len(es) < 2: e = q.expect('dbus-return', method='EnsureChannel') es.append(e) e1, e2 = es assert len(e1.value) == 3 yours1, path1, props1 = e1.value assert len(e2.value) == 3 yours2, path2, props2 = e2.value # Exactly one Ensure should get Yours=True. assert (yours1 == (not yours2)) assert path1 == path2, (path1, path2) assert props1 == props2, (props1, props2) if __name__ == '__main__': exec_test(test) telepathy-gabble-0.18.2/tests/twisted/muc/subject.py0000644000175000017500000001513212227000321022426 0ustar00smcvsmcv00000000000000""" Test Channel.Interface.Subject on MUC channels """ import dbus from twisted.words.xish import domish from gabbletest import exec_test, make_result_iq, make_muc_presence from servicetest import (EventPattern, assertEquals, assertLength, assertContains, call_async) import constants as cs import ns from mucutil import join_muc def test(q, bus, conn, stream): # 3x2x2 possible combinations of change_subject, send_first, moderator: # unrolling the loop here so we'll get better Python tracebacks on failure test_subject(q, bus, conn, stream, None, False, False) test_subject(q, bus, conn, stream, None, False, True) test_subject(q, bus, conn, stream, None, True, False) test_subject(q, bus, conn, stream, None, False, True) test_subject(q, bus, conn, stream, True, False, False) test_subject(q, bus, conn, stream, True, False, True) test_subject(q, bus, conn, stream, True, True, False) test_subject(q, bus, conn, stream, True, False, True) test_subject(q, bus, conn, stream, False, False, False) test_subject(q, bus, conn, stream, False, False, True) test_subject(q, bus, conn, stream, False, True, False) test_subject(q, bus, conn, stream, False, False, True) def check_subject_props(chan, subject_str, actor, flags, signal=None): if signal is not None: assertEquals(subject_str, signal.args[0]) assertEquals(actor, signal.args[1]) assertEquals(flags, signal.args[3]) props = chan.GetAll(cs.CHANNEL_IFACE_SUBJECT, dbus_interface=dbus.PROPERTIES_IFACE) subject = props['Subject'] subject_actor = props['Actor'] subject_can_set = props['CanSet'] def test_subject(q, bus, conn, stream, change_subject, send_first, moderator): room = 'test@conf.localhost' room_handle, chan, path, props, disco = join_muc(q, bus, conn, stream, room, also_capture=[EventPattern('stream-iq', iq_type='get', query_name='query', query_ns=ns.DISCO_INFO, to=room)], role=(moderator and 'moderator' or 'participant')) assert chan.Properties.Get(cs.CHANNEL_IFACE_SUBJECT, "CanSet") if send_first: # Someone sets a subject. message = domish.Element((None, 'message')) message['from'] = room + '/bob' message['type'] = 'groupchat' message.addElement('subject', content='Testing') stream.send(message) q.expect('dbus-signal', interface=cs.PROPERTIES_IFACE, signal='PropertiesChanged', predicate=lambda e: e.args[0] == cs.CHANNEL_IFACE_SUBJECT) check_subject_props(chan, 'Testing', room + '/bob', True) # Reply to the disco iq = make_result_iq(stream, disco.stanza) query = iq.firstChildElement() feat = query.addElement('feature') feat['var'] = 'muc_public' x = query.addElement((ns.X_DATA, 'x')) x['type'] = 'result' if change_subject is not None: # When fd.o #13157 has been fixed, this will actually do something. field = x.addElement('field') field['var'] = 'muc#roomconfig_changesubject' field.addElement('value', content=(change_subject and 'true' or 'false')) stream.send(iq) # Someone sets a subject. message = domish.Element((None, 'message')) message['from'] = room + '/bob' message['type'] = 'groupchat' message.addElement('subject', content='lalala') stream.send(message) q.expect('dbus-signal', interface=cs.PROPERTIES_IFACE, signal='PropertiesChanged', predicate=lambda e: e.args[0] == cs.CHANNEL_IFACE_SUBJECT) check_subject_props(chan, 'lalala', room + '/bob', True) # test changing the subject call_async(q, chan, 'SetSubject', 'le lolz', dbus_interface=cs.CHANNEL_IFACE_SUBJECT) e = q.expect('stream-message', to=room) elem = e.stanza assertEquals('groupchat', elem['type']) assertEquals(1, len(elem.children)) assertEquals(elem.children[0].name, 'subject') assertEquals(str(elem.children[0]), 'le lolz') elem['from'] = room + '/test' stream.send(elem) q.expect_many(EventPattern('dbus-signal', interface=cs.PROPERTIES_IFACE, signal='PropertiesChanged', predicate=lambda e: e.args[0] == cs.CHANNEL_IFACE_SUBJECT), EventPattern('dbus-return', method='SetSubject'), ) check_subject_props(chan, 'le lolz', room + '/test', True) # Test changing the subject and getting an error back. call_async(q, chan, 'SetSubject', 'CHICKEN MAN', dbus_interface=cs.CHANNEL_IFACE_SUBJECT) e = q.expect('stream-message', to=room) elem = e.stanza elem['from'] = room elem['type'] = 'error' error = elem.addElement((None, 'error')) error['type'] = 'auth' error.addElement((ns.STANZA, 'forbidden')) stream.send(elem) q.expect('dbus-error', method='SetSubject', name=cs.PERMISSION_DENIED) # Test changing the subject and getting an error back which doesn't echo # the element. call_async(q, chan, 'SetSubject', 'CHICKEN MAN', dbus_interface=cs.CHANNEL_IFACE_SUBJECT) e = q.expect('stream-message', to=room) message = domish.Element((None, 'message')) message['from'] = room message['id'] = e.stanza['id'] message['type'] = 'error' error = message.addElement((None, 'error')) error.addElement((ns.STANZA, 'forbidden')) stream.send(message) q.expect('dbus-error', method='SetSubject', name=cs.PERMISSION_DENIED) # Test changing the subject just before we leave the room (and hence not # getting a reply). While we're here, check that you can't have more than # one call in flight at a time. call_async(q, chan, 'SetSubject', 'le lolz', dbus_interface=cs.CHANNEL_IFACE_SUBJECT) e = q.expect('stream-message', to=room) call_async(q, chan, 'SetSubject', 'le lolz', dbus_interface=cs.CHANNEL_IFACE_SUBJECT) q.expect('dbus-error', method='SetSubject', name=cs.NOT_AVAILABLE) chan.Close() event = q.expect('stream-presence', to=room + '/test') elem = event.stanza assertEquals('unavailable', elem['type']) q.expect('dbus-error', method='SetSubject', name=cs.CANCELLED) call_async(q, chan, 'SetSubject', 'how about now?', dbus_interface=cs.CHANNEL_IFACE_SUBJECT) q.expect('dbus-error', method='SetSubject', name=cs.NOT_AVAILABLE) # The MUC confirms that we've left the room. echo = make_muc_presence('member', 'none', room, 'test') echo['type'] = 'unavailable' stream.send(echo) q.expect('dbus-signal', signal='ChannelClosed') if __name__ == '__main__': exec_test(test) telepathy-gabble-0.18.2/tests/twisted/muc/send-error.py0000644000175000017500000001357512227000321023060 0ustar00smcvsmcv00000000000000""" Test incoming error messages in MUC channels. """ import warnings import dbus from gabbletest import exec_test from servicetest import EventPattern, assertEquals, assertLength, assertContains import constants as cs import ns from mucutil import join_muc_and_check def test(q, bus, conn, stream): muc = 'chat@conf.localhost' _, text_chan, test_handle, bob_handle = \ join_muc_and_check(q, bus, conn, stream, muc) # Suppose we don't have permission to speak in this MUC. Send a message to # the channel, and have the MUC reject it as unauthorized. send_message_and_expect_error(q, stream, text_chan, test_handle, bob_handle, u"hi r ther ne warez n this chanel?", '401', 'auth', 'not-authorized', delivery_status=cs.DELIVERY_STATUS_PERMANENTLY_FAILED, send_error_value=cs.SendError.PERMISSION_DENIED) # This time, we get rate-limited. # send_message_and_expect_error(q, stream, text_chan, test_handle, bob_handle, "faster faster", '500', 'wait', 'resource-constraint', delivery_status=cs.DELIVERY_STATUS_TEMPORARILY_FAILED, # Yuck this isn't a very good name is it? send_error_value=cs.SendError.TOO_LONG) # How about an error message in the reply? This is from Prosody. See # https://bugs.freedesktop.org/show_bug.cgi?id=43166#c9 send_message_and_expect_error(q, stream, text_chan, test_handle, bob_handle, content=u"fair enough", code=None, type_='wait', element='policy-violation', error_message='The room is currently overactive, please try again later', delivery_status=cs.DELIVERY_STATUS_TEMPORARILY_FAILED, # Maybe we should expand the SendError codes some day, because this one # is l-a-m-e. send_error_value=cs.SendError.PERMISSION_DENIED) def send_message_and_expect_error(q, stream, text_chan, test_handle, bob_handle, content, code=None, type_=None, element=None, error_message=None, delivery_status=None, send_error_value=None): greeting = [ dbus.Dictionary({ }, signature='sv'), { 'content-type': 'text/plain', 'content': content, } ] sent_token = text_chan.Messages.SendMessage(greeting, dbus.UInt32(0)) stream_message, _, _ = q.expect_many( EventPattern('stream-message'), EventPattern('dbus-signal', signal='Sent'), EventPattern('dbus-signal', signal='MessageSent'), ) # computer says no elem = stream_message.stanza elem['from'] = 'chat@conf.localhost' elem['to'] = 'chat@conf.localhost/test' elem['type'] = 'error' error = elem.addElement('error') if code is not None: error['code'] = code if type_ is not None: error['type'] = type_ if element is not None: error.addElement((ns.STANZA, element)) if error_message is not None: error.addElement((ns.STANZA, 'text')).addContent(error_message) stream.send(elem) # check that we got a failed delivery report and a SendError send_error, received, message_received = q.expect_many( EventPattern('dbus-signal', signal='SendError'), EventPattern('dbus-signal', signal='Received'), EventPattern('dbus-signal', signal='MessageReceived'), ) err, timestamp, type, text = send_error.args assertEquals(send_error_value, err) # there's no way to tell when the original message was sent from the error stanza assertEquals(0, timestamp) # Gabble can't determine the type of the original message; see muc/test-muc.py # assert type == 0, send_error.args assertEquals(content, text) # The Text.Received signal should be a "you're not tall enough" stub id, timestamp, sender, type, flags, text = received.args assertEquals(0, sender) assertEquals(type, cs.MT_DELIVERY_REPORT) if flags == 0: warnings.warn("ignoring tp-glib bug #61254") else: assertEquals(cs.MessageFlag.NON_TEXT_CONTENT, flags) if error_message is None: assertEquals('', text) else: assertEquals(error_message, text) # Check that the Messages.MessageReceived signal was a failed delivery report assertLength(1, message_received.args) parts = message_received.args[0] if error_message is None: # The delivery report should just be a header, no body. assertLength(1, parts) else: assertLength(2, parts) part = parts[0] # The intended recipient was the MUC, so there's no contact handle # suitable for being 'message-sender'. assertEquals(0, part.get('message-sender', 0)) assertEquals(cs.MT_DELIVERY_REPORT, part['message-type']) assertEquals(delivery_status, part['delivery-status']) assertEquals(send_error_value, part['delivery-error']) assertEquals(sent_token, part['delivery-token']) # Check that the included echo is from us, and matches all the keys in the # message we sent. assertContains('delivery-echo', part) echo = part['delivery-echo'] assertLength(len(greeting), echo) echo_header = echo[0] assertEquals(test_handle, echo_header['message-sender']) assertEquals(sent_token, echo_header['message-token']) for i in range(0, len(echo)): for key in greeting[i]: assert key in echo[i], (i, key, echo) assert echo[i][key] == greeting[i][key], (i, key, echo, greeting) if error_message is not None: body = parts[1] assertEquals('text/plain', body['content-type']) assertEquals(error_message, body['content']) if __name__ == '__main__': exec_test(test) telepathy-gabble-0.18.2/tests/twisted/muc/scrollback.py0000644000175000017500000000441012227000321023103 0ustar00smcvsmcv00000000000000""" Regression test for https://bugs.freedesktop.org/show_bug.cgi?id=27913 where scrollback messages from contacts not currently in the MUC don't show up in the MUC; instead, they seemed to show up on IM channels whose target is the MUC's bare JID. Also acts as a scrollback messages in general! """ import dbus from servicetest import assertEquals, assertContains from gabbletest import exec_test, elem import constants as cs import ns from mucutil import join_muc_and_check def test(q, bus, conn, stream): room = 'chat@conf.localhost' our_jid = room + '/test' bob_jid = room + '/bob' marco_jid = room + '/marco' room_handle, chan, test_handle, bob_handle = \ join_muc_and_check(q, bus, conn, stream, room) # Here are a few scrollback messages. One from us; one from bob; and one # from marco, who's no longer in the room. stream.send( elem('message', from_=our_jid, type='groupchat')( elem('body')( u'i really hate the muc xep' ), elem(ns.X_DELAY, 'x', from_=room, stamp='20090910T12:34:56') ) ) stream.send( elem('message', from_=bob_jid, type='groupchat')( elem('body')( u'yeah, it totally sucks' ), elem(ns.X_DELAY, 'x', from_=room, stamp='20090910T12:45:56') ) ) stream.send( elem('message', from_=marco_jid, type='groupchat')( elem('body')( u'we should start a riot' ), elem(ns.X_DELAY, 'x', from_=room, stamp='20090910T12:56:56') ) ) m1 = q.expect('dbus-signal', signal='MessageReceived') m2 = q.expect('dbus-signal', signal='MessageReceived') m3 = q.expect('dbus-signal', signal='MessageReceived') def badger(event): assertEquals(chan.object_path, event.path) message, = event.args header = message[0] assertContains('scrollback', header) assert header['scrollback'] assertContains('message-sender', header) return header['message-sender'] me = badger(m1) bob = badger(m2) marco = badger(m3) assertEquals([our_jid, bob_jid, marco_jid], conn.InspectHandles(cs.HT_CONTACT, [ me, bob, marco ])) if __name__ == '__main__': exec_test(test) telepathy-gabble-0.18.2/tests/twisted/muc/room.py0000644000175000017500000001654612227000321021755 0ustar00smcvsmcv00000000000000""" Test the different ways to request a channel using the Room interface """ from gabbletest import exec_test, make_muc_presence from servicetest import (call_async, EventPattern, assertEquals, assertContains) import constants as cs import dbus import re def create_muc(q, conn, stream, props): call_async(q, conn.Requests, 'CreateChannel', props) r = q.expect('stream-presence') muc_name = r.to.split('/', 2)[0] stream.send(make_muc_presence('owner', 'moderator', muc_name, 'test')) r = q.expect('dbus-return', method='CreateChannel') assertEquals(2, len(r.value)) return r.value[1] def test(q, bus, conn, stream): q.expect('stream-presence') # First create a channel with human-readable name like normal. jid = 'booyakasha@conf.localhost' props = create_muc(q, conn, stream, { cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_TEXT, cs.TARGET_HANDLE_TYPE: cs.HT_ROOM, cs.TARGET_ID: jid, }) assertEquals(cs.CHANNEL_TYPE_TEXT, props[cs.CHANNEL_TYPE]) assertEquals(cs.HT_ROOM, props[cs.TARGET_HANDLE_TYPE]) assertEquals(jid, props[cs.TARGET_ID]) assertEquals(jid.split('@')[0], props[cs.ROOM_NAME]) assertEquals(jid.split('@')[1], props[cs.ROOM_SERVER]) # Next create a similar human-readable channel but using the new # properties. jid = 'indahouse@conf.localhost' props = create_muc(q, conn, stream, { cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_TEXT, cs.ROOM_NAME: jid.split('@')[0], cs.ROOM_SERVER: jid.split('@')[1], }) assertEquals(cs.CHANNEL_TYPE_TEXT, props[cs.CHANNEL_TYPE]) assertEquals(cs.HT_ROOM, props[cs.TARGET_HANDLE_TYPE]) assertEquals(jid, props[cs.TARGET_ID]) assertEquals(jid.split('@')[0], props[cs.ROOM_NAME]) assertEquals(jid.split('@')[1], props[cs.ROOM_SERVER]) # Next create a similar human-readable channel but using the new # RoomName property and leave out Server. jid = 'indahouse@fallback.conf.localhost' props = create_muc(q, conn, stream, { cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_TEXT, cs.ROOM_NAME: jid.split('@')[0], }) assertEquals(cs.CHANNEL_TYPE_TEXT, props[cs.CHANNEL_TYPE]) assertEquals(cs.HT_ROOM, props[cs.TARGET_HANDLE_TYPE]) assertEquals(jid, props[cs.TARGET_ID]) assertEquals(jid.split('@')[0], props[cs.ROOM_NAME]) assertEquals(jid.split('@')[1], props[cs.ROOM_SERVER]) # Now create a uniquely-named channel. conf_server = 'conf.localhost' props = create_muc(q, conn, stream, { cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_TEXT, cs.ROOM_NAME: '', cs.ROOM_SERVER: conf_server, }) assertEquals(cs.CHANNEL_TYPE_TEXT, props[cs.CHANNEL_TYPE]) assertEquals(cs.HT_ROOM, props[cs.TARGET_HANDLE_TYPE]) assert re.match( r'^private-chat-\w{8}-\w{4}-\w{4}-\w{4}-\w{12}@' + conf_server + '$', props[cs.TARGET_ID]), props[cs.TARGET_ID] assertEquals('', props[cs.ROOM_NAME]) assertEquals(conf_server, props[cs.ROOM_SERVER]) # Now create a uniquely-named channel with no server. conf_server = 'fallback.conf.localhost' props = create_muc(q, conn, stream, { cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_TEXT, cs.ROOM_NAME: '', }) assertEquals(cs.CHANNEL_TYPE_TEXT, props[cs.CHANNEL_TYPE]) assertEquals(cs.HT_ROOM, props[cs.TARGET_HANDLE_TYPE]) assert re.match( r'^private-chat-\w{8}-\w{4}-\w{4}-\w{4}-\w{12}@' + conf_server + '$', props[cs.TARGET_ID]), props[cs.TARGET_ID] assertEquals('', props[cs.ROOM_NAME]) assertEquals(conf_server, props[cs.ROOM_SERVER]) # Now a channel with non-human-readable name that we set ourselves. jid = 'asdf@conf.localhost' props = create_muc(q, conn, stream, { cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_TEXT, cs.TARGET_HANDLE_TYPE: cs.HT_ROOM, cs.TARGET_ID: jid, cs.ROOM_NAME: '', cs.ROOM_SERVER: 'conf.localhost', }) assertEquals(cs.CHANNEL_TYPE_TEXT, props[cs.CHANNEL_TYPE]) assertEquals(cs.HT_ROOM, props[cs.TARGET_HANDLE_TYPE]) assertEquals(jid, props[cs.TARGET_ID]) assertEquals('', props[cs.ROOM_NAME]) assertEquals(jid.split('@')[1], props[cs.ROOM_SERVER]) # Now a channel with non-human-readable name that we set ourselves # and no server property. jid = 'hjkl@conf.localhost' props = create_muc(q, conn, stream, { cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_TEXT, cs.TARGET_HANDLE_TYPE: cs.HT_ROOM, cs.TARGET_ID: jid, cs.ROOM_NAME: '', }) assertEquals(cs.CHANNEL_TYPE_TEXT, props[cs.CHANNEL_TYPE]) assertEquals(cs.HT_ROOM, props[cs.TARGET_HANDLE_TYPE]) assertEquals(jid, props[cs.TARGET_ID]) assertEquals('', props[cs.ROOM_NAME]) assertEquals(jid.split('@')[1], props[cs.ROOM_SERVER]) # Now a channel with non-human-readable name (with no conf server # in the TargetID) that we set ourselves and no server property. jid = 'qwerty' props = create_muc(q, conn, stream, { cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_TEXT, cs.TARGET_HANDLE_TYPE: cs.HT_ROOM, cs.TARGET_ID: jid, cs.ROOM_NAME: '', }) assertEquals(cs.CHANNEL_TYPE_TEXT, props[cs.CHANNEL_TYPE]) assertEquals(cs.HT_ROOM, props[cs.TARGET_HANDLE_TYPE]) assertEquals(jid + '@fallback.conf.localhost', props[cs.TARGET_ID]) assertEquals('', props[cs.ROOM_NAME]) assertEquals('fallback.conf.localhost', props[cs.ROOM_SERVER]) # Now a channel which already exists (any of the above) with # RoomName set to a non-harmful value jid = 'booyakasha@conf.localhost' call_async(q, conn.Requests, 'EnsureChannel', { cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_TEXT, cs.TARGET_HANDLE_TYPE: cs.HT_ROOM, cs.TARGET_ID: jid, cs.ROOM_NAME: '', }) q.expect('dbus-return', method='EnsureChannel') # Now a channel which already exists (any of the above) with # a conflicting RoomName set. jid = 'booyakasha@conf.localhost' call_async(q, conn.Requests, 'EnsureChannel', { cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_TEXT, cs.TARGET_HANDLE_TYPE: cs.HT_ROOM, cs.TARGET_ID: jid, cs.ROOM_NAME: 'happynewyear', }) q.expect('dbus-error', name=cs.INVALID_ARGUMENT, method='EnsureChannel') # Now a channel which already exists (any of the above) with # a non-conflicting Server set. jid = 'booyakasha@conf.localhost' call_async(q, conn.Requests, 'EnsureChannel', { cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_TEXT, cs.TARGET_HANDLE_TYPE: cs.HT_ROOM, cs.TARGET_ID: jid, cs.ROOM_SERVER: 'conf.localhost', }) q.expect('dbus-return', method='EnsureChannel') # Now a channel which already exists (any of the above) with # a conflicting Server set. jid = 'booyakasha@conf.localhost' call_async(q, conn.Requests, 'EnsureChannel', { cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_TEXT, cs.TARGET_HANDLE_TYPE: cs.HT_ROOM, cs.TARGET_ID: jid, cs.ROOM_SERVER: 'lol.conf.localhost', }) q.expect('dbus-error', name=cs.INVALID_ARGUMENT, method='EnsureChannel') if __name__ == '__main__': exec_test(test, params={ 'fallback-conference-server': 'fallback.conf.localhost' } ) telepathy-gabble-0.18.2/tests/twisted/muc/roomlist.py0000644000175000017500000001541712227000321022645 0ustar00smcvsmcv00000000000000 """ Test MUC support. """ import dbus from gabbletest import make_result_iq, exec_test, sync_stream, disconnect_conn from servicetest import call_async, EventPattern, tp_name_prefix import constants as cs def test(q, bus, conn, stream): event = q.expect('stream-iq', to='localhost', query_ns='http://jabber.org/protocol/disco#items') result = make_result_iq(stream, event.stanza) item = result.firstChildElement().addElement('item') item['jid'] = 'conf.localhost' stream.send(result) event = q.expect('stream-iq', to='conf.localhost', query_ns='http://jabber.org/protocol/disco#info') result = make_result_iq(stream, event.stanza) feature = result.firstChildElement().addElement('feature') feature['var'] = 'http://jabber.org/protocol/muc' identity = result.firstChildElement().addElement('identity') identity['category'] = 'conference' identity['name'] = 'conference service' identity['type'] = 'text' stream.send(result) # Make sure the stream has been processed sync_stream(q, stream) properties = conn.GetAll( tp_name_prefix + '.Connection.Interface.Requests', dbus_interface=dbus.PROPERTIES_IFACE) assert properties.get('Channels') == [], properties['Channels'] assert ({tp_name_prefix + '.Channel.ChannelType': tp_name_prefix + '.Channel.Type.RoomList', tp_name_prefix + '.Channel.TargetHandleType': 0, }, [tp_name_prefix + '.Channel.Type.RoomList.Server'], ) in properties.get('RequestableChannelClasses'),\ properties['RequestableChannelClasses'] call_async(q, conn, 'RequestChannel', tp_name_prefix + '.Channel.Type.RoomList', 0, 0, True) ret, old_sig, new_sig = q.expect_many( EventPattern('dbus-return', method='RequestChannel'), EventPattern('dbus-signal', signal='NewChannel'), EventPattern('dbus-signal', signal='NewChannels'), ) bus = dbus.SessionBus() path1 = ret.value[0] chan = bus.get_object(conn.bus_name, path1) assert new_sig.args[0][0][0] == path1 props = new_sig.args[0][0][1] assert props[tp_name_prefix + '.Channel.ChannelType'] ==\ tp_name_prefix + '.Channel.Type.RoomList' assert props[tp_name_prefix + '.Channel.TargetHandleType'] == 0 assert props[tp_name_prefix + '.Channel.TargetHandle'] == 0 assert props[tp_name_prefix + '.Channel.TargetID'] == '' assert props[tp_name_prefix + '.Channel.Requested'] == True assert props[tp_name_prefix + '.Channel.InitiatorHandle'] \ == conn.GetSelfHandle() assert props[tp_name_prefix + '.Channel.InitiatorID'] \ == 'test@localhost' assert props[tp_name_prefix + '.Channel.Type.RoomList.Server'] == \ 'conf.localhost' assert old_sig.args[0] == path1 assert old_sig.args[1] == tp_name_prefix + '.Channel.Type.RoomList' assert old_sig.args[2] == 0 # handle type assert old_sig.args[3] == 0 # handle assert old_sig.args[4] == 1 # suppress handler # Exercise basic Channel Properties from spec 0.17.7 channel_props = chan.GetAll( tp_name_prefix + '.Channel', dbus_interface=dbus.PROPERTIES_IFACE) assert channel_props.get('TargetHandle') == 0,\ channel_props.get('TargetHandle') assert channel_props['TargetID'] == '', channel_props assert channel_props.get('TargetHandleType') == 0,\ channel_props.get('TargetHandleType') assert channel_props.get('ChannelType') == \ tp_name_prefix + '.Channel.Type.RoomList',\ channel_props.get('ChannelType') assert channel_props['Requested'] == True assert channel_props['InitiatorID'] == 'test@localhost' assert channel_props['InitiatorHandle'] == conn.GetSelfHandle() assert chan.Get( tp_name_prefix + '.Channel.Type.RoomList', 'Server', dbus_interface=dbus.PROPERTIES_IFACE) == \ 'conf.localhost' # FIXME: actually list the rooms! call_async(q, conn.Requests, 'CreateChannel', { tp_name_prefix + '.Channel.ChannelType': tp_name_prefix + '.Channel.Type.RoomList', tp_name_prefix + '.Channel.TargetHandleType': 0, tp_name_prefix + '.Channel.Type.RoomList.Server': 'conference.example.net', }) ret, old_sig, new_sig = q.expect_many( EventPattern('dbus-return', method='CreateChannel'), EventPattern('dbus-signal', signal='NewChannel'), EventPattern('dbus-signal', signal='NewChannels'), ) path2 = ret.value[0] chan = bus.get_object(conn.bus_name, path2) props = ret.value[1] assert props[tp_name_prefix + '.Channel.ChannelType'] ==\ tp_name_prefix + '.Channel.Type.RoomList' assert props[tp_name_prefix + '.Channel.TargetHandleType'] == 0 assert props[tp_name_prefix + '.Channel.TargetHandle'] == 0 assert props[tp_name_prefix + '.Channel.TargetID'] == '' assert props[tp_name_prefix + '.Channel.Requested'] == True assert props[tp_name_prefix + '.Channel.InitiatorHandle'] \ == conn.GetSelfHandle() assert props[tp_name_prefix + '.Channel.InitiatorID'] \ == 'test@localhost' assert props[tp_name_prefix + '.Channel.Type.RoomList.Server'] == \ 'conference.example.net' assert new_sig.args[0][0][0] == path2 assert new_sig.args[0][0][1] == props assert old_sig.args[0] == path2 assert old_sig.args[1] == tp_name_prefix + '.Channel.Type.RoomList' assert old_sig.args[2] == 0 # handle type assert old_sig.args[3] == 0 # handle assert old_sig.args[4] == 1 # suppress handler assert chan.Get( tp_name_prefix + '.Channel.Type.RoomList', 'Server', dbus_interface=dbus.PROPERTIES_IFACE) == \ 'conference.example.net' # FIXME: actually list the rooms! call_async(q, conn.Requests, 'EnsureChannel', { tp_name_prefix + '.Channel.ChannelType': tp_name_prefix + '.Channel.Type.RoomList', tp_name_prefix + '.Channel.TargetHandleType': 0, tp_name_prefix + '.Channel.Type.RoomList.Server': 'conference.example.net', }) ret = q.expect('dbus-return', method='EnsureChannel') yours, ensured_path, ensured_props = ret.value assert not yours assert ensured_path == path2, (ensured_path, path2) disconnect_conn(q, conn, stream, [ EventPattern('dbus-signal', signal='Closed', path=path1), EventPattern('dbus-signal', signal='Closed', path=path2), EventPattern('dbus-signal', signal='ChannelClosed', args=[path1]), EventPattern('dbus-signal', signal='ChannelClosed', args=[path2])]) if __name__ == '__main__': exec_test(test) telepathy-gabble-0.18.2/tests/twisted/muc/room-config.py0000644000175000017500000003501412227000321023207 0ustar00smcvsmcv00000000000000# vim: fileencoding=utf-8 : """ Test the RoomConfig interface on MUC channels. """ import dbus from twisted.words.xish import xpath from gabbletest import ( exec_test, make_result_iq, acknowledge_iq, make_muc_presence, request_muc_handle, sync_stream, disconnect_conn) from servicetest import ( call_async, wrap_channel, EventPattern, assertEquals, assertSameSets, assertContains, ) from mucutil import join_muc import constants as cs import ns ROOM_NAME = "A place to bury strangers" ROOM_DESCRIPTION = "I hate noise-rock." def get_default_form(): return { 'password': [''], 'password_protected': ['0'], 'muc#roomconfig_persistentroom': ['0'], # We have to include this field here to convince Gabble that the # description can be modified by owners. As far as wjt can # determine, this is a question of uneven server support: see # 6f20080. 'muc#roomconfig_roomdesc': [ROOM_DESCRIPTION], # Gabble doesn't understand this field; we include it to verify # that Gabble can correctly echo multi-value fields. 'muc#roomconfig_presencebroadcast': ['moderator', 'participant', 'visitor'], } def parse_form(stanza): fields = xpath.queryForNodes('/iq/query/x/field', stanza) form = {} for field in fields: values = xpath.queryForNodes('/field/value', field) form[field['var']] = [str(v) for v in values] return form def add_field(elem, type, var, value): field = elem.addElement('field') if type is not None: field['type'] = type field['var'] = var value = field.addElement('value', content=value) def handle_muc_owner_get_iq(stream, stanza): iq = make_result_iq(stream, stanza) query = iq.firstChildElement() x = query.addElement(('jabber:x:data', 'x')) x['type'] = 'form' for var, values in get_default_form().iteritems(): if len(values) > 1: field = x.addElement('field') field['type'] = 'list-multi' field['var'] = var for v in values: field.addElement('value', content=v) field.addElement('option', content=v) elif values[0] == '0' or values[0] == '1': add_field(x, 'boolean', var, values[0]) else: add_field(x, 'text', var, values[0]) stream.send(iq) def handle_muc_owner_set_iq(stream, stanza, fields): form = parse_form(stanza) # Check that Gabble echoed back the fields it didn't understand (or want to # change) with their previous values. expected_form = get_default_form() expected_form.update(fields) assertEquals(expected_form, form) acknowledge_iq(stream, stanza) def handle_disco_info_iq(stream, stanza): iq = make_result_iq(stream, stanza) query = iq.firstChildElement() # Title identity = query.addElement('identity') identity['category'] = 'conference' identity['type'] = 'text' identity['name'] = ROOM_NAME for var in [ns.MUC, 'muc_anonymous', # Anonymous 'muc_open', # ¬InviteOnly # Limit lives in the data form 'muc_moderated', # Moderated # Title is above # Description is below 'muc_temporary', # ¬Persistent 'muc_hidden', # Private 'muc_unsecure', # ¬PasswordProtected # Password is in the owner form. ]: f = query.addElement('feature') f['var'] = var # Description x = query.addElement((ns.X_DATA, 'x')) x['type'] = 'result' add_field(x, 'hidden', 'FORM_TYPE', ns.MUC_ROOMINFO) add_field(x, None, 'muc#roominfo_description', ROOM_DESCRIPTION) stream.send(iq) def test_some_stuff(q, bus, conn, stream): _, text_chan, _, _, disco_iq, owner_iq, _ = join_muc(q, bus, conn, stream, 'chat@conf.localhost', role='moderator', affiliation='owner', also_capture=[ EventPattern('stream-iq', to='chat@conf.localhost', iq_type='get', query_ns=ns.DISCO_INFO), EventPattern('stream-iq', to='chat@conf.localhost', iq_type='get', query_ns=ns.MUC_OWNER), # We discovered that we're an owner. Emitting a signal seems # acceptable, although technically this happens before the channel # request finishes so the channel could just as well not be on the bus. EventPattern('dbus-signal', signal='PropertiesChanged', args=[cs.CHANNEL_IFACE_ROOM_CONFIG, {'CanUpdateConfiguration': True}, [] ]), ]) # This tells Gabble that the MUC is well-behaved and lets owners modify the # room description. Technically we could also pull the description out of # here, but as an implementation detail we only read configuration out of # the disco reply. handle_muc_owner_get_iq(stream, owner_iq.stanza) pc = q.expect('dbus-signal', signal='PropertiesChanged', predicate=lambda e: e.args[0] == cs.CHANNEL_IFACE_ROOM_CONFIG) _, changed, invalidated = pc.args assertEquals(['MutableProperties'], changed.keys()) assertContains('Description', changed['MutableProperties']) handle_disco_info_iq(stream, disco_iq.stanza) pc = q.expect('dbus-signal', signal='PropertiesChanged', predicate=lambda e: e.args[0] == cs.CHANNEL_IFACE_ROOM_CONFIG) q.expect('dbus-signal', signal='PropertiesChanged', args=[cs.CHANNEL_IFACE_ROOM_CONFIG, {'ConfigurationRetrieved': True}, [] ]) _, changed, invalidated = pc.args assertEquals( { 'Anonymous': True, 'Moderated': True, 'Title': ROOM_NAME, 'Description': ROOM_DESCRIPTION, 'Private': True, }, changed) assertEquals([], invalidated) config = text_chan.Properties.GetAll(cs.CHANNEL_IFACE_ROOM_CONFIG) # Verify that all of the config properties (besides the password ones) # correspond to the flags set in handle_disco_info_iq(). assertEquals(True, config['Anonymous']) assertEquals(False, config['InviteOnly']) assertEquals(0, config['Limit']) assertEquals(True, config['Moderated']) assertEquals(ROOM_NAME, config['Title']) assertEquals(ROOM_DESCRIPTION, config['Description']) assertEquals(False, config['Persistent']) assertEquals(True, config['Private']) # This is affirmed to be false both by the disco reply and by the muc#owner # reply. assertEquals(False, config['PasswordProtected']) # This comes from the muc#owner reply. assertEquals('', config['Password']) # We're a room owner, so we should be able to modify the room configuration assertEquals(True, config['CanUpdateConfiguration']) assertSameSets( ['Anonymous', 'InviteOnly', # TODO: when we understand member limit fields, add Limit 'Moderated', 'Title', 'Description', 'Persistent', 'Private', 'PasswordProtected', 'Password', ], config['MutableProperties']) props = dbus.Dictionary( { 'Password': 'foo', 'PasswordProtected': True, }, signature='sv') call_async(q, text_chan.RoomConfig1, 'UpdateConfiguration', props) event = q.expect('stream-iq', to='chat@conf.localhost', iq_type='get', query_ns=ns.MUC_OWNER) handle_muc_owner_get_iq(stream, event.stanza) event = q.expect('stream-iq', to='chat@conf.localhost', iq_type='set', query_ns=ns.MUC_OWNER) handle_muc_owner_set_iq(stream, event.stanza, {'password': ['foo'], 'password_protected': ['1'], }) pc, _ = q.expect_many( EventPattern('dbus-signal', signal='PropertiesChanged', predicate=lambda e: e.args[0] == cs.CHANNEL_IFACE_ROOM_CONFIG), EventPattern('dbus-return', method='UpdateConfiguration'), ) _, changed, invalidated = pc.args assertEquals(props, changed) assertEquals([], invalidated) config = text_chan.Properties.GetAll(cs.CHANNEL_IFACE_ROOM_CONFIG) assertEquals(True, config['PasswordProtected']) assertEquals('foo', config['Password']) # Check unknown fields are rejected. props = dbus.Dictionary( { 'PasswordProtected': True, 'Riding on a donkey': True, }, signature='sv') call_async(q, text_chan.RoomConfig1, 'UpdateConfiguration', props) q.expect('dbus-error', name=cs.INVALID_ARGUMENT) # Check that mis-typed fields are rejected. props = dbus.Dictionary( { 'PasswordProtected': 'foo', 'Password': True, }, signature='sv') call_async(q, text_chan.RoomConfig1, 'UpdateConfiguration', props) q.expect('dbus-error', name=cs.INVALID_ARGUMENT) # Updating no fields should be a no-op, and not wait on any network # traffic. text_chan.RoomConfig1.UpdateConfiguration({}) def test_role_changes(q, bus, conn, stream): # The test user joins a room. Bob is an owner (and moderator); the test # user starts out with no affiliation and the rôle of participant. MUC = 'aoeu@snth' _, chan, _, immutable_props, disco = join_muc(q, bus, conn, stream, MUC, role='participant', also_capture=[ EventPattern('stream-iq', to=MUC, iq_type='get', query_ns=ns.DISCO_INFO), ]) assertContains(cs.CHANNEL_IFACE_ROOM_CONFIG, immutable_props[cs.INTERFACES]) handle_disco_info_iq(stream, disco.stanza) q.expect('dbus-signal', signal='PropertiesChanged', args=[cs.CHANNEL_IFACE_ROOM_CONFIG, {'ConfigurationRetrieved': True}, [] ]) # If we try to change the configuration, Gabble should say no: it knows # we're not allowed to do that. call_async(q, chan.RoomConfig1, 'UpdateConfiguration', {}) q.expect('dbus-error', name=cs.PERMISSION_DENIED) config = chan.Properties.GetAll(cs.CHANNEL_IFACE_ROOM_CONFIG) assert not config['CanUpdateConfiguration'], config # If we acquire affiliation='owner', this should be signalled as our # becoming able to modify the channel configuration. stream.send(make_muc_presence('owner', 'moderator', MUC, 'test')) q.expect('dbus-signal', signal='PropertiesChanged', args=[cs.CHANNEL_IFACE_ROOM_CONFIG, {'CanUpdateConfiguration': True}, [] ]) # Due to silliness, Gabble has to grab the owner configuration form to see # whether it's possible to change the room description. owner_iq = q.expect('stream-iq', to=MUC, iq_type='get', query_ns=ns.MUC_OWNER) handle_muc_owner_get_iq(stream, owner_iq.stanza) # Bob's ownership rights being taken away should have no effect. stream.send(make_muc_presence('none', 'participant', MUC, 'bob')) # So now we're an owner, and CanUpdateConfiguration is True, we should be # able to change some configuration. props = dbus.Dictionary( { 'Persistent': True, }, signature='sv') call_async(q, chan.RoomConfig1, 'UpdateConfiguration', props) owner_iq = q.expect('stream-iq', to=MUC, iq_type='get', query_ns=ns.MUC_OWNER) handle_muc_owner_get_iq(stream, owner_iq.stanza) event = q.expect('stream-iq', to=MUC, iq_type='set', query_ns=ns.MUC_OWNER) handle_muc_owner_set_iq(stream, event.stanza, {'muc#roomconfig_persistentroom': ['1']}) q.expect_many( EventPattern('dbus-return', method='UpdateConfiguration'), EventPattern('dbus-signal', signal='PropertiesChanged', args=[cs.CHANNEL_IFACE_ROOM_CONFIG, {'Persistent': True}, [] ])) # If we lose our affiliation, that should be signalled too. stream.send(make_muc_presence('none', 'participant', MUC, 'test')) q.expect('dbus-signal', signal='PropertiesChanged', args=[cs.CHANNEL_IFACE_ROOM_CONFIG, {'CanUpdateConfiguration': False}, [] ]) # Gabble should once again reject attempts to change the configuration call_async(q, chan.RoomConfig1, 'UpdateConfiguration', {}) q.expect('dbus-error', name=cs.PERMISSION_DENIED) def test_broken_server(q, bus, conn, stream): MUC = 'bro@ken' _, chan, _ , _ = join_muc(q, bus, conn, stream, MUC, affiliation='owner') owner_iq = q.expect('stream-iq', to=MUC, iq_type='get', query_ns=ns.MUC_OWNER) handle_muc_owner_get_iq(stream, owner_iq.stanza) call_async(q, chan.RoomConfig1, 'UpdateConfiguration', {'Private': False}) e = q.expect('stream-iq', to=MUC, iq_type='get', query_ns=ns.MUC_OWNER) handle_muc_owner_get_iq(stream, e.stanza) # The server doesn't actually have a form field for configuring whether the # room is private or not. q.expect('dbus-error', method='UpdateConfiguration', name=cs.SERVICE_CONFUSED) def test_disconnect_during_update_configuration(q, bus, conn, stream): """ Test disconnecting while a pair of UpdateConfiguration requests are in flight: one waiting for the muc#owner form, and the other waiting for its changes to be acked. """ def join_me_up_buttercup(muc): _, chan, _, _ = join_muc(q, bus, conn, stream, muc, affiliation='owner') # Gabble grabs the owner configuration form to see whether it's # possible to change the room description. owner_iq = q.expect('stream-iq', to=muc, iq_type='get', query_ns=ns.MUC_OWNER) handle_muc_owner_get_iq(stream, owner_iq.stanza) call_async(q, chan.RoomConfig1, 'UpdateConfiguration', {'Persistent': True}) e = q.expect('stream-iq', to=muc, iq_type='get', query_ns=ns.MUC_OWNER) return chan, e ONE = '1@ttt' one, _ = join_me_up_buttercup(ONE) TWO = '2@ttt' two, e = join_me_up_buttercup(TWO) handle_muc_owner_get_iq(stream, e.stanza) q.expect('stream-iq', to=TWO, iq_type='set', query_ns=ns.MUC_OWNER) disconnect_conn(q, conn, stream, expected_after=[ # Buh. We can't match on paths or on message serials … but we know # there are two of them! EventPattern('dbus-error', method='UpdateConfiguration', name=cs.CANCELLED), EventPattern('dbus-error', method='UpdateConfiguration', name=cs.CANCELLED), ]) def test(q, bus, conn, stream): test_some_stuff(q, bus, conn, stream) test_role_changes(q, bus, conn, stream) test_broken_server(q, bus, conn, stream) test_disconnect_during_update_configuration(q, bus, conn, stream) if __name__ == '__main__': exec_test(test) telepathy-gabble-0.18.2/tests/twisted/muc/renamed.py0000644000175000017500000000574512227000321022413 0ustar00smcvsmcv00000000000000""" Test dealing with the server giving you a nick you didn't ask for. """ import dbus from gabbletest import ( exec_test, make_muc_presence, request_muc_handle ) from servicetest import call_async, unwrap from constants import ( HT_CONTACT, HT_ROOM, CONN_IFACE_REQUESTS, CHANNEL_TYPE_TEXT, CHANNEL_IFACE_GROUP, CHANNEL_TYPE, TARGET_HANDLE_TYPE, TARGET_HANDLE, ) import constants as cs def test(q, bus, conn, stream): self_handle = conn.GetSelfHandle() requests = dbus.Interface(conn, CONN_IFACE_REQUESTS) room_jid = 'chat@conf.localhost' room_handle = request_muc_handle(q, conn, stream, room_jid) call_async(q, requests, 'CreateChannel', dbus.Dictionary({ CHANNEL_TYPE: CHANNEL_TYPE_TEXT, TARGET_HANDLE_TYPE: HT_ROOM, TARGET_HANDLE: room_handle, }, signature='sv')) expected_jid = '%s/%s' % (room_jid, 'test') q.expect('stream-presence', to=expected_jid) # Send presence for another member of the MUC stream.send(make_muc_presence('owner', 'moderator', room_jid, 'liz')) # This is a themed discussion, so the MUC server forces you to have an # appropriate name. self_presence = make_muc_presence('none', 'participant', room_jid, 'toofer') x = [elt for elt in self_presence.elements() if elt.name == 'x'][0] status = x.addElement('status') status['code'] = '110' # "This is you" status = x.addElement('status') status['code'] = '210' # "I renamed you. Muahaha." stream.send(self_presence) # Gabble should figure out from 110 that it's in the room, and from 210 # that we've been renamed. event = q.expect('dbus-return', method='CreateChannel') path, props = event.value text_chan = bus.get_object(conn.bus_name, path) group_props = unwrap(text_chan.GetAll(CHANNEL_IFACE_GROUP, dbus_interface=dbus.PROPERTIES_IFACE)) liz, toofer, expected_handle = conn.RequestHandles(HT_CONTACT, ["%s/%s" % (room_jid, m) for m in ['liz', 'toofer', 'test']]) # Check that Gabble think our nickname in the room is toofer not test muc_self_handle = group_props['SelfHandle'] assert muc_self_handle == toofer, (muc_self_handle, toofer, expected_handle) members = group_props['Members'] # Check there are exactly two members (liz and toofer) expected_members = [liz, toofer] assert sorted(members) == sorted(expected_members), \ (members, expected_members) # There should be no pending members. assert len(group_props['LocalPendingMembers']) == 0, group_props assert len(group_props['RemotePendingMembers']) == 0, group_props # Check that toofer's handle owner is us, and that liz has # no owner. handle_owners = group_props['HandleOwners'] assert handle_owners[toofer] == self_handle, \ (handle_owners, toofer, handle_owners[toofer], self_handle) assert handle_owners[liz] == 0, (handle_owners, liz) if __name__ == '__main__': exec_test(test) telepathy-gabble-0.18.2/tests/twisted/muc/presence-before-closing.py0000644000175000017500000001231712227000321025471 0ustar00smcvsmcv00000000000000""" Test for fd.o#19930. """ import dbus from twisted.words.xish import domish from gabbletest import ( exec_test, make_result_iq, request_muc_handle, wrap_channel, elem, ) from servicetest import (EventPattern, assertEquals, assertLength, assertContains, sync_dbus, call_async) import constants as cs import ns from mucutil import join_muc, echo_muc_presence def test(q, bus, conn, stream): room = 'test@conf.localhost' room_handle, chan, path, props, disco = join_muc(q, bus, conn, stream, room, also_capture=[EventPattern('stream-iq', iq_type='get', query_name='query', query_ns=ns.DISCO_INFO, to=room)]) sync_dbus(bus, q, conn) # we call Close... call_async(q, chan, 'Close') q.expect('dbus-return', method='Close') # ...so gabble announces our unavailable presence to the MUC. event = q.expect('stream-presence', to=room + '/test') elem = event.stanza assertEquals('unavailable', elem['type']) # while we wait for the conference server to echo our unavailable # presence, we try and create the same channel again... call_async(q, conn.Requests, 'CreateChannel', { cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_TEXT, cs.TARGET_HANDLE_TYPE: cs.HT_ROOM, cs.TARGET_ID: room }) # ...which should fail because the channel hasn't closed yet. q.expect('dbus-error', method='CreateChannel', name=cs.NOT_AVAILABLE) # the conference server finally gets around to echoing our # unavailable presence... echo_muc_presence(q, stream, elem, 'none', 'participant') # ...and only now is the channel closed. q.expect_many(EventPattern('dbus-signal', signal='Closed'), EventPattern('dbus-signal', signal='ChannelClosed')) # now that the channel has finally closed, let's try and request # it again which should succeed! _, chan, _, _ = join_muc(q, bus, conn, stream, room) # let's clear up though. chan.Close() event = q.expect('stream-presence', to=room + '/test') echo_muc_presence(q, stream, event.stanza, 'none', 'participant') q.expect_many(EventPattern('dbus-signal', signal='Closed'), EventPattern('dbus-signal', signal='ChannelClosed')) def test_then_disconnect(q, bus, conn, stream): room = 'test@conf.localhost' room_handle, chan, path, props, disco = join_muc(q, bus, conn, stream, room, also_capture=[EventPattern('stream-iq', iq_type='get', query_name='query', query_ns=ns.DISCO_INFO, to=room)]) sync_dbus(bus, q, conn) # we call Close... call_async(q, chan, 'Close') q.expect('dbus-return', method='Close') # ...so gabble announces our unavailable presence to the MUC. event = q.expect('stream-presence', to=room + '/test') elem = event.stanza assertEquals('unavailable', elem['type']) # oh no, but now we want to disconnect. call_async(q, conn, 'Disconnect') # the muc factory is told to close everything, so it does so # without announcing it to the channel because it does it # forcibly, so the channels disappear. q.expect_many(EventPattern('dbus-signal', signal='Closed'), EventPattern('dbus-signal', signal='ChannelClosed')) # now echo the unavailable presence; this shouldn't be handled # because the channel has already closed. echo_muc_presence(q, stream, elem, 'none', 'participant') # send the stream footer so that the connection thinks it's # property disconnected now. stream.sendFooter() # finally, Disconnect returns q.expect('dbus-return', method='Disconnect') def test_with_password(q, bus, conn, stream): room = 'chat@conf.localhost' handle = request_muc_handle(q, conn, stream, room) call_async(q, conn.Requests, 'CreateChannel', { cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_TEXT, cs.TARGET_HANDLE_TYPE: cs.HT_ROOM, cs.TARGET_HANDLE: handle}) expected_muc_jid = '%s/%s' % (room, 'test') q.expect('stream-presence', to=expected_muc_jid) # tell gabble the room needs a password stream.send( elem('jabber:client', 'presence', from_=expected_muc_jid, type='error')( elem(ns.MUC, 'x'), elem('error', type='auth')( elem(ns.STANZA, 'not-authorized'), ), )) cc, _ = q.expect_many( EventPattern('dbus-return', method='CreateChannel'), EventPattern('dbus-signal', signal='PasswordFlagsChanged', args=[cs.PASSWORD_FLAG_PROVIDE, 0])) chan = wrap_channel(bus.get_object(conn.bus_name, cc.value[0]), 'Text') forbidden = [EventPattern('stream-presence', to=expected_muc_jid)] q.forbid_events(forbidden) # we call Close... call_async(q, chan, 'Close') # ...but this time no unavailable presence because we were in the # auth state, so the channel closes immediately. q.expect_many(EventPattern('dbus-return', method='Close'), EventPattern('dbus-signal', signal='Closed'), EventPattern('dbus-signal', signal='ChannelClosed')) q.unforbid_events(forbidden) if __name__ == '__main__': exec_test(test) exec_test(test_then_disconnect) exec_test(test_with_password) telepathy-gabble-0.18.2/tests/twisted/muc/password.py0000644000175000017500000000600112227000321022624 0ustar00smcvsmcv00000000000000from gabbletest import exec_test, elem, request_muc_handle, make_muc_presence from servicetest import call_async, EventPattern, wrap_channel, assertEquals import constants as cs import ns def expect_attempt(q, expected_muc_jid, expected_password): e = q.expect('stream-presence', to=expected_muc_jid) x = e.stanza.elements(uri=ns.MUC, name='x').next() p = x.firstChildElement() assertEquals(expected_password, str(p)) def test(q, bus, conn, stream): room = 'chat@conf.localhost' handle = request_muc_handle(q, conn, stream, room) call_async(q, conn.Requests, 'CreateChannel', { cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_TEXT, cs.TARGET_HANDLE_TYPE: cs.HT_ROOM, cs.TARGET_HANDLE: handle}) expected_muc_jid = '%s/%s' % (room, 'test') q.expect('stream-presence', to=expected_muc_jid) # tell gabble the room needs a password denied = \ elem('jabber:client', 'presence', from_=expected_muc_jid, type='error')( elem(ns.MUC, 'x'), elem('error', type='auth')( elem(ns.STANZA, 'not-authorized'), ), ) stream.send(denied) cc, _, _ = q.expect_many( EventPattern('dbus-return', method='CreateChannel'), EventPattern('dbus-signal', signal='NewChannels'), EventPattern('dbus-signal', signal='PasswordFlagsChanged', args=[cs.PASSWORD_FLAG_PROVIDE, 0])) chan = wrap_channel(bus.get_object(conn.bus_name, cc.value[0]), 'Text', ['Password']) flags = chan.Password.GetPasswordFlags() assertEquals(cs.PASSWORD_FLAG_PROVIDE, flags) call_async(q, chan.Password, 'ProvidePassword', 'brand new benz') expect_attempt(q, expected_muc_jid, 'brand new benz') # Try again while the first attempt is outstanding. Gabble should say no. call_async(q, chan.Password, 'ProvidePassword', 'faster faster') q.expect('dbus-error', method='ProvidePassword') # Sorry, wrong password. stream.send(denied) ret = q.expect('dbus-return', method='ProvidePassword') assert not ret.value[0] call_async(q, chan.Password, 'ProvidePassword', 'bougie friends') expect_attempt(q, expected_muc_jid, 'bougie friends') # Well, this may be the right password, but actually that nick is in use. presence = elem('presence', from_=expected_muc_jid, type='error')( elem(ns.MUC, 'x'), elem('error', type='cancel')( elem(ns.STANZA, 'conflict'), )) stream.send(presence) # Okay, so Gabble tries again, with a new JID *and the same password*. expected_muc_jid = expected_muc_jid + '_' expect_attempt(q, expected_muc_jid, 'bougie friends') # Hey this worked. stream.send(make_muc_presence('none', 'participant', room, 'test_')) ret, _ = q.expect_many( EventPattern('dbus-return', method='ProvidePassword'), EventPattern('dbus-signal', signal='PasswordFlagsChanged', args=[0, cs.PASSWORD_FLAG_PROVIDE])) assert ret.value[0] if __name__ == '__main__': exec_test(test) telepathy-gabble-0.18.2/tests/twisted/muc/name-conflict.py0000644000175000017500000002045612227000321023513 0ustar00smcvsmcv00000000000000# vim: fileencoding=utf-8 : """ Test gabble trying alternative nicknames when the nick you wanted is already in use in a MUC you try to join. """ import dbus from gabbletest import ( exec_test, make_muc_presence, sync_stream, elem, ) from servicetest import ( call_async, unwrap, sync_dbus, assertEquals, assertSameSets, wrap_channel, EventPattern, ) import constants as cs import ns def test(q, bus, conn, stream): test_join(q, bus, conn, stream, 'chat@conf.localhost', False) test_join(q, bus, conn, stream, 'chien@conf.localhost', True) test_gtalk_weirdness(q, bus, conn, stream, 'private-chat-massive-uuid@groupchat.google.com') def test_join(q, bus, conn, stream, room_jid, transient_conflict): """ Tells Gabble to join a MUC, but make the first nick it tries conflict with an existing member of the MUC. If transient_conflict is True, then when Gabble successfully joins with a different nick the originally conflicting user turns out not actually to be in the room (they left while we were retrying). """ # Implementation detail: Gabble uses the first part of your jid (if you # don't have an alias) as your room nickname, and appends an underscore a # few times before giving up. member, member_ = [room_jid + '/' + x for x in ['test', 'test_']] call_async(q, conn.Requests, 'CreateChannel', dbus.Dictionary({ cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_TEXT, cs.TARGET_HANDLE_TYPE: cs.HT_ROOM, cs.TARGET_ID: room_jid, }, signature='sv')) # Gabble first tries to join as test q.expect('stream-presence', to=member) # MUC says no: there's already someone called test in room_jid presence = elem('presence', from_=member, type='error')( elem(ns.MUC, 'x'), elem('error', type='cancel')( elem(ns.STANZA, 'conflict'), )) stream.send(presence) # Gabble tries again as test_ q.expect('stream-presence', to=member_) # MUC says yes! if not transient_conflict: # Send the other member of the room's presence. This is the nick we # originally wanted. stream.send(make_muc_presence('owner', 'moderator', room_jid, 'test')) # If gabble erroneously thinks the other user's presence is our own, it'll # think that it's got the whole userlist now. If so, syncing here will make # CreateChannel incorrectly return here. sync_stream(q, stream) sync_dbus(bus, q, conn) # Send presence for own membership of room. stream.send(make_muc_presence('none', 'participant', room_jid, 'test_')) # Only now should we have finished joining the room. event = q.expect('dbus-return', method='CreateChannel') path, props = event.value text_chan = wrap_channel(bus.get_object(conn.bus_name, path), 'Text') group_props = unwrap(text_chan.Properties.GetAll(cs.CHANNEL_IFACE_GROUP)) t, t_ = conn.RequestHandles(cs.HT_CONTACT, [member, member_]) # Check that Gabble think our nickname in the room is test_, not test muc_self_handle = group_props['SelfHandle'] assert muc_self_handle == t_, (muc_self_handle, t_, t) members = group_props['Members'] if transient_conflict: # The user we originally conflicted with isn't actually here; check # there's exactly one member (test_). assert members == [t_], (members, t_, t) else: # Check there are exactly two members (test and test_) assertSameSets([t, t_], members) # In either case, there should be no pending members. assert len(group_props['LocalPendingMembers']) == 0, group_props assert len(group_props['RemotePendingMembers']) == 0, group_props # Check that test_'s handle owner is us, and that test (if it's there) has # no owner. handle_owners = group_props['HandleOwners'] assertEquals (conn.GetSelfHandle(), handle_owners[t_]) if not transient_conflict: assertEquals (0, handle_owners[t]) # test that closing the channel results in an unavailable message to the # right jid text_chan.Close() event = q.expect('stream-presence', to=member_) assertEquals('unavailable', event.stanza['type']) def test_gtalk_weirdness(q, bus, conn, stream, room_jid): """ There's a strange bug in the Google Talk MUC server where it sends the stanza twice. This has been reported to their server team; but in any case it triggered a crazy bug in Gabble, so here's a regression test. """ # Implementation detail: Gabble uses the first part of your jid (if you # don't have an alias) as your room nickname, and appends an underscore a # few times before giving up. jids = ['%s/test%s' % (room_jid, x) for x in ['', '_', '__']] member, member_, member__ = jids # Gabble should never get as far as trying to join as 'test__' since # joining as 'test_' will succeed. q.forbid_events([ EventPattern('stream-presence', to=member__) ]) call_async(q, conn.Requests, 'CreateChannel', dbus.Dictionary({ cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_TEXT, cs.TARGET_HANDLE_TYPE: cs.HT_ROOM, cs.TARGET_ID: room_jid, }, signature='sv')) # Gabble first tries to join as test q.expect('stream-presence', to=member) # Google Talk says no from 'test', twice. presence = elem('presence', from_=member, type='error')( elem(ns.MUC, 'x'), elem('error', type='cancel')( elem(ns.STANZA, 'conflict'), )) stream.send(presence) stream.send(presence) # Gabble should try to join again as test_ q.expect('stream-presence', to=member_) # Since 'test_' is not in use in the MUC, joining should succeed. According # to XEP-0045 §7.1.3 : # The service MUST first send the complete list of the existing occupants # to the new occupant and only then send the new occupant's own presence # to the new occupant # but groupchat.google.com cheerfully violates this. stream.send(make_muc_presence('none', 'participant', room_jid, 'test_')) # Here's some other random person, who owns the MUC. stream.send(make_muc_presence('owner', 'moderator', room_jid, 'foobar_gmail.com')) # And here's our hypothetical other self. stream.send(make_muc_presence('none', 'participant', room_jid, 'test')) # The Gabble bug makes this time out: because Gabble thinks it's joining as # test__ it ignores the presence for test_, since it's not flagged with # code='210' to say “this is you”. (This is acceptable behaviour by the # server: it only needs to include code='210' if it's assigned the client a # name other than the one it asked for. # # The forbidden stream-presence event above doesn't blow up here because # servicetest doesn't process events on the 'stream-*' queue at all when # we're not waiting for one. But during disconnection in the test clean-up, # the forbidden event is encountered and correctly flagged up. event = q.expect('dbus-return', method='CreateChannel') path, _ = event.value text_chan = wrap_channel(bus.get_object(conn.bus_name, path), 'Text') # As far as Gabble's concerned, the two other participants joined # immediately after we did. We can't request handles for them before we # try to join the MUC, because until we do so, Gabble doesn't know that # room_jid is a MUC, and so considers these three JIDs to be different # resources of the same contact. There is no race between this method # returning and MembersChangedDetailed firing, because libdbus reorders # messages when you make blocking calls. handle, handle_, handle__, foobar_handle = conn.RequestHandles( cs.HT_CONTACT, jids + ['%s/foobar_gmail.com' % room_jid]) q.expect('dbus-signal', signal='MembersChangedDetailed', predicate=lambda e: e.args[0:4] == [[foobar_handle], [], [], []]) q.expect('dbus-signal', signal='MembersChangedDetailed', predicate=lambda e: e.args[0:4] == [[handle], [], [], []]) group_props = text_chan.Properties.GetAll(cs.CHANNEL_IFACE_GROUP) assertEquals(handle_, group_props['SelfHandle']) assertSameSets([handle, handle_, foobar_handle], group_props['Members']) if __name__ == '__main__': exec_test(test) telepathy-gabble-0.18.2/tests/twisted/muc/kicked.py0000644000175000017500000000321712227000321022222 0ustar00smcvsmcv00000000000000""" Tests the user being kicked from a MUC. Another symptom of the underlying bug behind was that this would crash. """ from servicetest import assertEquals, assertContains from gabbletest import exec_test, elem from mucutil import join_muc import constants as cs import ns MUC = 'deerhoof@evil.lit' def test(q, bus, conn, stream): # The user happily joins a MUC _, chan, _, _ = join_muc(q, bus, conn, stream, MUC) muc_self_handle = chan.Group.GetSelfHandle() muc_self_jid, = conn.InspectHandles(cs.HT_CONTACT, [muc_self_handle]) # But then Bob kicks us. bob_jid = '%s/bob' % MUC bob_handle, = conn.RequestHandles(cs.HT_CONTACT, [bob_jid]) stream.send( elem('presence', from_=muc_self_jid, type='unavailable')( elem(ns.MUC_USER, 'x')( elem('item', affiliation='none', role='none')( elem('actor', jid=bob_jid), elem('reason')( u'bye' ) ), elem('status', code='307'), ) )) mcd_event = q.expect('dbus-signal', signal='MembersChangedDetailed') added, removed, local_pending, remote_pending, details = mcd_event.args assertEquals([], added) assertEquals([muc_self_handle], removed) assertEquals([], local_pending) assertEquals([], remote_pending) assertContains('actor', details) assertEquals(bob_handle, details['actor']) assertEquals(cs.GC_REASON_KICKED, details['change-reason']) assertEquals('bye', details['message']) q.expect('dbus-signal', signal='ChannelClosed') if __name__ == '__main__': exec_test(test) telepathy-gabble-0.18.2/tests/twisted/muc/conference.py0000644000175000017500000000534212227000321023100 0ustar00smcvsmcv00000000000000""" Test the different ways to request a channel using the Conference interface """ from gabbletest import exec_test, make_muc_presence from servicetest import (call_async, EventPattern, assertEquals, assertContains) import constants as cs import dbus import re def test(q, bus, conn, stream): # Wait for us to be fully logged in q.expect('stream-presence') test_create_pmuc(q, conn, stream) test_create_pmuc_with_invitee(q, conn, stream) def create_pmuc(q, conn, stream, extra_props=None): """ Request a PMUC just for ourselves. """ props = { cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_TEXT, cs.TARGET_HANDLE_TYPE: cs.HT_NONE, cs.CONFERENCE_INITIAL_CHANNELS: dbus.Array([], signature='o'), } if extra_props: props.update(extra_props) call_async(q, conn.Requests, 'CreateChannel', props) # wait for the MUC name, so we can inject a reply r = q.expect('stream-presence') pmuc_name = r.to.split('/', 2)[0] assert re.match( r'^private-chat-\w{8}-\w{4}-\w{4}-\w{4}-\w{12}@conf.localhost$', pmuc_name), pmuc_name stream.send(make_muc_presence('owner', 'moderator', pmuc_name, 'test')) # wait for the method return r = q.expect('dbus-return', method='CreateChannel') assert len(r.value) == 2 path, out_props = r.value assert out_props[cs.CHANNEL_TYPE] == cs.CHANNEL_TYPE_TEXT assert out_props[cs.TARGET_HANDLE_TYPE] == cs.HT_ROOM assert out_props[cs.TARGET_ID] == pmuc_name assertContains(cs.CHANNEL_IFACE_CONFERENCE, out_props[cs.INTERFACES]) assertEquals(props[cs.CONFERENCE_INITIAL_CHANNELS], out_props[cs.CONFERENCE_INITIAL_CHANNELS]) return pmuc_name, path, out_props def test_create_pmuc(q, conn, stream): pmuc_name, path, props = create_pmuc(q, conn, stream) assertEquals([], props[cs.CONFERENCE_INITIAL_INVITEE_IDS]) assertEquals([], props[cs.CONFERENCE_INITIAL_INVITEE_HANDLES]) def test_create_pmuc_with_invitee(q, conn, stream): # Open an initial 1-to-1 connection props = { cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_TEXT, cs.TARGET_HANDLE_TYPE: cs.HT_CONTACT, cs.TARGET_ID: 'bob@localhost', } call_async(q, conn.Requests, 'EnsureChannel', props) r = q.expect('dbus-return', method='EnsureChannel') assert len(r.value) == 3 yours, path, props = r.value pmuc_name, path, props = create_pmuc(q, conn, stream, { cs.CONFERENCE_INITIAL_CHANNELS: dbus.Array([path], signature='o'), }) # FIXME: check for stream-message containing invite for Bob assertEquals(['bob@localhost'], props[cs.CONFERENCE_INITIAL_INVITEE_IDS]) if __name__ == '__main__': exec_test(test, params={ 'fallback-conference-server': 'conf.localhost' } ) telepathy-gabble-0.18.2/tests/twisted/muc/chat-states.py0000644000175000017500000001215612227000321023212 0ustar00smcvsmcv00000000000000""" Regression test for , wherein chat states in MUCs were misparsed, and MUC chat states in general. """ from servicetest import assertEquals, assertLength, EventPattern from gabbletest import exec_test, elem, make_muc_presence, sync_stream from mucutil import join_muc_and_check import ns import constants as cs MUC = 'ohai@groupchat.google.com' BOB = MUC + '/bob' def get_state_notification(stanza): for x in stanza.elements(): if x.uri == ns.CHAT_STATES: return x return None def check_state_notification(elem, name, allow_body=False): assertEquals('message', elem.name) assertEquals('groupchat', elem['type']) notification = get_state_notification(elem) assert notification is not None, elem.toXml() assert notification.name == name, notification.toXml() if not allow_body: assert len(elem.children) == 1, elem.toXml() def test(q, bus, conn, stream): (muc_handle, chan, user, bob) = join_muc_and_check(q, bus, conn, stream, MUC) states = chan.Properties.Get(cs.CHANNEL_IFACE_CHAT_STATE, 'ChatStates') assertEquals(cs.CHAT_STATE_INACTIVE, states.get(user, cs.CHAT_STATE_INACTIVE)) assertEquals(cs.CHAT_STATE_INACTIVE, states.get(bob, cs.CHAT_STATE_INACTIVE)) stream.send( elem('message', from_=BOB, to='test@localhost/Resource', type='groupchat', jid='bob@bob.bob')( elem(ns.CHAT_STATES, 'composing'), elem('google:nosave', 'x', value='disabled'), elem('http://jabber.org/protocol/archive', 'record', otr='false'), )) e = q.expect('dbus-signal', signal='ChatStateChanged') contact, state = e.args assertEquals(bob, contact) assertEquals(cs.CHAT_STATE_COMPOSING, state) states = chan.Properties.Get(cs.CHANNEL_IFACE_CHAT_STATE, 'ChatStates') assertEquals(cs.CHAT_STATE_INACTIVE, states.get(user, cs.CHAT_STATE_INACTIVE)) assertEquals(cs.CHAT_STATE_COMPOSING, states.get(bob, cs.CHAT_STATE_INACTIVE)) stream.send( elem('message', from_=BOB, to='test@localhost/Resource', type='groupchat', jid='bob@bob.bob')( elem(ns.CHAT_STATES, 'paused'), elem('google:nosave', 'x', value='disabled'), elem('http://jabber.org/protocol/archive', 'record', otr='false'), )) e = q.expect('dbus-signal', signal='ChatStateChanged') contact, state = e.args assertEquals(bob, contact) assertEquals(cs.CHAT_STATE_PAUSED, state) states = chan.Properties.Get(cs.CHANNEL_IFACE_CHAT_STATE, 'ChatStates') assertEquals(cs.CHAT_STATE_INACTIVE, states.get(user, cs.CHAT_STATE_INACTIVE)) assertEquals(cs.CHAT_STATE_PAUSED, states.get(bob, cs.CHAT_STATE_INACTIVE)) # Bob leaves presence = make_muc_presence('owner', 'none', MUC, 'bob') presence['type'] = 'unavailable' stream.send(presence) e = q.expect('dbus-signal', signal='ChatStateChanged') contact, state = e.args assertEquals(bob, contact) assertEquals(cs.CHAT_STATE_GONE, state) states = chan.Properties.Get(cs.CHANNEL_IFACE_CHAT_STATE, 'ChatStates') assertEquals(cs.CHAT_STATE_INACTIVE, states.get(user, cs.CHAT_STATE_INACTIVE)) # Bob no longer has any chat state at all assertEquals(None, states.get(bob, None)) # Sending chat states: # Composing... chan.ChatState.SetChatState(cs.CHAT_STATE_COMPOSING) stream_message = q.expect('stream-message') check_state_notification(stream_message.stanza, 'composing') states = chan.Properties.Get(cs.CHANNEL_IFACE_CHAT_STATE, 'ChatStates') assertEquals(cs.CHAT_STATE_COMPOSING, states.get(user, cs.CHAT_STATE_INACTIVE)) # XEP 0085: # every content message SHOULD contain an notification. chan.Text.Send(0, 'hi.') stream_message = q.expect('stream-message') stanza = stream_message.stanza check_state_notification(stanza, 'active', allow_body=True) states = chan.Properties.Get(cs.CHANNEL_IFACE_CHAT_STATE, 'ChatStates') assertEquals(cs.CHAT_STATE_ACTIVE, states.get(user, cs.CHAT_STATE_INACTIVE)) bodies = list(stanza.elements(uri=ns.CLIENT, name='body')) assertLength(1, bodies) assertEquals(u'hi.', bodies[0].children[0]) # If we get an error with type='wait', stop sending chat states. stanza['type'] = 'error' stanza['from'] = MUC stanza['to'] = 'test@localhost/Resource' error = stanza.addElement('error') error['type'] = 'wait' error.addElement((ns.STANZA, 'resource-constraint')) stream.send(stanza) q.expect('dbus-signal', signal='MessageReceived', predicate=lambda e: e.args[0][0]['message-type'] == cs.MT_DELIVERY_REPORT) q.forbid_events([ EventPattern('stream-message', to=MUC, predicate=lambda e: get_state_notification(e.stanza) is not None) ]) # User starts typing again but nothing should be seen or heard on the stream. chan.ChatState.SetChatState(cs.CHAT_STATE_COMPOSING) sync_stream(q, stream) if __name__ == '__main__': exec_test(test) telepathy-gabble-0.18.2/tests/twisted/muc/banned.py0000644000175000017500000000147512200204333022223 0ustar00smcvsmcv00000000000000""" Tests the server refusing to let us join a MUC. This is a regression test for where Gabble would crash in this situation. """ from gabbletest import exec_test, elem from mucutil import try_to_join_muc import constants as cs import ns MUC = 'deerhoof@evil.lit' def test(q, bus, conn, stream): try_to_join_muc(q, bus, conn, stream, MUC) stream.send( elem('presence', from_=MUC, type='error')( elem(ns.MUC, 'x'), elem('error', code='403', type='auth')( elem(ns.STANZA, 'forbidden'), elem(ns.STANZA, 'text')( u'Access denied by service policy', ) ) )) q.expect('dbus-error', method='CreateChannel', name=cs.BANNED) if __name__ == '__main__': exec_test(test) telepathy-gabble-0.18.2/tests/twisted/muc/avatars.py0000644000175000017500000001445212227000321022434 0ustar00smcvsmcv00000000000000# vim: set fileencoding=utf-8 # Tests publishing an avatar in MUCs, and getting tokens for ourselves and # others. Serves as a regression test for # , where our MUC-specific # self handle would have an empty avatar token even though we're publishing our # avatar on the wire correctly. import hashlib from servicetest import ( call_async, EventPattern, assertEquals, assertLength, sync_dbus, wrap_channel, ) from gabbletest import ( exec_test, expect_and_handle_get_vcard, expect_and_handle_set_vcard, make_muc_presence, elem, ) from twisted.words.xish import xpath import ns import constants as cs from mucutil import try_to_join_muc AVATAR_1_DATA = 'nyan' AVATAR_1_SHA1 = hashlib.sha1(AVATAR_1_DATA).hexdigest() AVATAR_1_MIME_TYPE = 'image/x-pop-tart' AVATAR_2_DATA = 'NYAN' AVATAR_2_SHA1 = hashlib.sha1(AVATAR_2_DATA).hexdigest() AVATAR_2_MIME_TYPE = 'image/x-pop-tart' MUC = 'taco-dog@nyan.cat' def extract_hash_from_presence(stanza): return xpath.queryForString( '/presence/x[@xmlns="%s"]/photo' % ns.VCARD_TEMP_UPDATE, stanza) def test(q, bus, conn, stream): self_handle = conn.GetSelfHandle() # When Gabble initially requests its avatar from the server, it discovers # it has none. expect_and_handle_get_vcard(q, stream) handle, signalled_token = q.expect('dbus-signal', signal='AvatarUpdated').args assertEquals(self_handle, handle) assertEquals('', signalled_token) # The user sets an avatar. call_async(q, conn.Avatars, 'SetAvatar', AVATAR_1_DATA, AVATAR_1_MIME_TYPE) expect_and_handle_get_vcard(q, stream) expect_and_handle_set_vcard(q, stream) # It's signalled on D-Bus … set_ret, avatar_updated = q.expect_many( EventPattern('dbus-return', method='SetAvatar'), EventPattern('dbus-signal', signal='AvatarUpdated'), ) returned_token, = set_ret.value handle, signalled_token = avatar_updated.args assertEquals(self_handle, handle) assertEquals(returned_token, signalled_token) # … and also on XMPP. broadcast = q.expect('stream-presence', to=None) broadcast_hash = extract_hash_from_presence(broadcast.stanza) assertEquals(AVATAR_1_SHA1, broadcast_hash) # If applications ask Gabble for information about the user's own avatar, # it should be able to answer. (Strictly speaking, expecting Gabble to know # the avatar data is risky because Gabble discards cached vCards after a # while, but we happen to know it takes 20 seconds or so for that to # happen.) known = conn.Avatars.GetKnownAvatarTokens([self_handle]) assertEquals({self_handle: signalled_token}, known) conn.Avatars.RequestAvatars([self_handle]) retrieved = q.expect('dbus-signal', signal='AvatarRetrieved') handle, token, data, mime_type = retrieved.args assertEquals(self_handle, handle) assertEquals(signalled_token, token) assertEquals(AVATAR_1_DATA, data) assertEquals(AVATAR_1_MIME_TYPE, mime_type) # Well, that was quite easy. How about we join a MUC? XEP-0153 §4.1 says: # If a client supports the protocol defined herein, it […] SHOULD # also include the update child in directed presence stanzas (e.g., # directed presence sent when joining Multi-User Chat [5] rooms). # — http://xmpp.org/extensions/xep-0153.html#bizrules-presence join_event = try_to_join_muc(q, bus, conn, stream, MUC) directed_hash = extract_hash_from_presence(join_event.stanza) assertEquals(AVATAR_1_SHA1, directed_hash) # There are two others in the MUC: fredrik has no avatar, wendy has an # avatar. We, of course, have our own avatar. stream.send(make_muc_presence('none', 'participant', MUC, 'fredrik')) stream.send(make_muc_presence('none', 'participant', MUC, 'wendy', photo=AVATAR_2_SHA1)) stream.send(make_muc_presence('owner', 'moderator', MUC, 'test', photo=AVATAR_1_SHA1)) path, _ = q.expect('dbus-return', method='CreateChannel').value chan = wrap_channel(bus.get_object(conn.bus_name, path), 'Text', ['Messages']) members = chan.Properties.Get(cs.CHANNEL_IFACE_GROUP, 'Members') assertLength(3, members) fredrik, wendy, muc_self_handle = conn.RequestHandles(cs.HT_CONTACT, ['%s/%s' % (MUC, x) for x in ["fredrik", "wendy", "test"]]) known = conn.Avatars.GetKnownAvatarTokens(members) # : this assertion # failed, the MUC self handle's token was the empty string. assertEquals(AVATAR_1_SHA1, known[muc_self_handle]) assertEquals(AVATAR_2_SHA1, known[wendy]) assertEquals('', known[fredrik]) # 'k, cool. Wendy loves our avatar and switches to it. stream.send(make_muc_presence('none', 'participant', MUC, 'wendy', photo=AVATAR_1_SHA1)) # Okay this is technically assuming that we just expose the SHA1 sums # directly which is not guaranteed … but we do. q.expect('dbus-signal', signal='AvatarUpdated', args=[wendy, AVATAR_1_SHA1]) # Fredrik switches too. stream.send(make_muc_presence('none', 'participant', MUC, 'fredrik', photo=AVATAR_1_SHA1)) q.expect('dbus-signal', signal='AvatarUpdated', args=[fredrik, AVATAR_1_SHA1]) # And we switch to some other avatar. Gabble should update its vCard, and # then update its MUC presence (which the test, acting as the MUC server, # must echo). call_async(q, conn.Avatars, 'SetAvatar', AVATAR_2_DATA, AVATAR_2_MIME_TYPE) expect_and_handle_get_vcard(q, stream) expect_and_handle_set_vcard(q, stream) muc_presence = q.expect('stream-presence', to=('%s/test' % MUC)) directed_hash = extract_hash_from_presence(muc_presence.stanza) stream.send(make_muc_presence('owner', 'moderator', MUC, 'test', photo=directed_hash)) # Gabble should signal an avatar update for both our global self-handle and # our MUC self-handle. (The first of these of course does not need to wait # for the MUC server to echo our presence.) q.expect_many( EventPattern('dbus-signal', signal='AvatarUpdated', args=[self_handle, AVATAR_2_SHA1]), EventPattern('dbus-signal', signal='AvatarUpdated', args=[muc_self_handle, AVATAR_2_SHA1]), ) if __name__ == '__main__': exec_test(test) telepathy-gabble-0.18.2/tests/twisted/jingle-share/0000755000175000017500000000000012312537051022211 5ustar00smcvsmcv00000000000000telepathy-gabble-0.18.2/tests/twisted/jingle-share/jingleshareutils.py0000644000175000017500000000764612227000321026142 0ustar00smcvsmcv00000000000000import dbus from twisted.words.xish import xpath from servicetest import (assertEquals, EventPattern) from gabbletest import exec_test, make_result_iq, sync_stream, make_presence import constants as cs from caps_helper import compute_caps_hash, \ text_fixed_properties, text_allowed_properties, \ ft_fixed_properties, ft_allowed_properties import ns run = 0 def test_ft_caps_from_contact(q, bus, conn, stream, contact, contact_handle, client): global run run += 1 conn_caps_iface = dbus.Interface(conn, cs.CONN_IFACE_CONTACT_CAPS) conn_contacts_iface = dbus.Interface(conn, cs.CONN_IFACE_CONTACTS) # send presence with no FT cap presence = make_presence(contact, status='hello') c = presence.addElement((ns.CAPS, 'c')) c['node'] = client c['ver'] = compute_caps_hash(['client/pc//jingleshareutils-%d' % run], [], {}) c['ext'] = "" stream.send(presence) # Gabble looks up our capabilities event = q.expect('stream-iq', to=contact, query_ns=ns.DISCO_INFO) query_node = xpath.queryForNodes('/iq/query', event.stanza)[0] assert query_node.attributes['node'] == \ client + '#' + c['ver'] # send good reply result = make_result_iq(stream, event.stanza) query = result.firstChildElement() query['node'] = client + '#' + c['ver'] stream.send(result) # no change in ContactCapabilities, so no signal ContactCapabilitiesChanged sync_stream(q, stream) # no special capabilities basic_caps = dbus.Dictionary({contact_handle: [(text_fixed_properties, text_allowed_properties)]}) caps = conn_caps_iface.GetContactCapabilities([contact_handle]) assert caps == basic_caps, caps # test again, to check GetContactCapabilities does not have side effect caps = conn_caps_iface.GetContactCapabilities([contact_handle]) assert caps == basic_caps, caps # check the Contacts interface give the same caps caps_via_contacts_iface = conn_contacts_iface.GetContactAttributes( [contact_handle], [cs.CONN_IFACE_CONTACT_CAPS], False) \ [contact_handle][cs.ATTR_CONTACT_CAPABILITIES] assert caps_via_contacts_iface == caps[contact_handle], \ caps_via_contacts_iface # send presence with ft capa presence = make_presence(contact, status='hello') c = presence.addElement((ns.CAPS, 'c')) c['node'] = client c['ext'] = "share-v1" c['ver'] = '1.1' stream.send(presence) # Gabble looks up our capabilities event = q.expect('stream-iq', to=contact, query_ns=ns.DISCO_INFO) query_node = xpath.queryForNodes('/iq/query', event.stanza)[0] assertEquals(client + '#' + c['ver'], query_node.attributes['node']) # send good reply result = make_result_iq(stream, event.stanza) query = result.firstChildElement() query['node'] = client + '#' + c['ver'] feature = query.addElement('feature') feature['var'] = ns.GOOGLE_FEAT_SHARE stream.send(result) generic_ft_caps = dbus.Dictionary({contact_handle: [(text_fixed_properties, text_allowed_properties), (ft_fixed_properties, ft_allowed_properties)]}) event = q.expect('dbus-signal', signal='ContactCapabilitiesChanged') assert len(event.args) == 1 assert event.args[0] == generic_ft_caps caps = conn_caps_iface.GetContactCapabilities([contact_handle]) assert caps == generic_ft_caps, caps # test again, to check GetContactCapabilities does not have side effect caps = conn_caps_iface.GetContactCapabilities([contact_handle]) assert caps == generic_ft_caps, caps # check the Contacts interface give the same caps caps_via_contacts_iface = conn_contacts_iface.GetContactAttributes( [contact_handle], [cs.CONN_IFACE_CONTACT_CAPS], False) \ [contact_handle][cs.ATTR_CONTACT_CAPABILITIES] assert caps_via_contacts_iface == caps[contact_handle], \ caps_via_contacts_iface telepathy-gabble-0.18.2/tests/twisted/jingle-share/file_transfer_helper.py0000644000175000017500000005754612227000321026754 0ustar00smcvsmcv00000000000000import dbus import socket import hashlib import time import datetime from servicetest import EventPattern, assertEquals, assertLength, assertSameSets from gabbletest import exec_test, sync_stream, make_result_iq, elem_iq, elem import ns from caps_helper import text_fixed_properties, text_allowed_properties, \ stream_tube_fixed_properties, stream_tube_allowed_properties, \ dbus_tube_fixed_properties, dbus_tube_allowed_properties, \ ft_fixed_properties, ft_allowed_properties, compute_caps_hash, \ extract_disco_parts from twisted.words.xish import domish, xpath import constants as cs import sys class File(object): DEFAULT_DATA = "What a nice file" DEFAULT_NAME = "The foo.txt" DEFAULT_CONTENT_TYPE = 'text/plain' DEFAULT_DESCRIPTION = "A nice file to test" def __init__(self, data=DEFAULT_DATA, name=DEFAULT_NAME, content_type=DEFAULT_CONTENT_TYPE, description=DEFAULT_DESCRIPTION, hash_type=cs.FILE_HASH_TYPE_MD5): self.data = data self.size = len(self.data) self.name = name self.content_type = content_type self.description = description self.date = int(time.time()) self.compute_hash(hash_type) self.offset = 0 def compute_hash(self, hash_type): assert hash_type == cs.FILE_HASH_TYPE_MD5 self.hash_type = hash_type self.hash = hashlib.md5(self.data).hexdigest() generic_ft_caps = [(text_fixed_properties, text_allowed_properties), (stream_tube_fixed_properties, \ stream_tube_allowed_properties), (dbus_tube_fixed_properties, dbus_tube_allowed_properties), (ft_fixed_properties, ft_allowed_properties)] generic_caps = [(text_fixed_properties, text_allowed_properties), (stream_tube_fixed_properties, \ stream_tube_allowed_properties), (dbus_tube_fixed_properties, dbus_tube_allowed_properties)] class FileTransferTest(object): caps_identities = None caps_features = None caps_dataforms = None caps_ft = None def __init__(self, file, address_type, access_control, access_control_param): self.file = file self.address_type = address_type self.access_control = access_control self.access_control_param = access_control_param self.closed = True def connect(self): self.conn.Connect() self.q.expect('dbus-signal', signal='StatusChanged', args=[cs.CONN_STATUS_CONNECTED, cs.CSR_REQUESTED], path=self.conn.object.object_path) self.self_handle = self.conn.GetSelfHandle() self.self_handle_name = self.conn.InspectHandles(cs.HT_CONTACT, [self.self_handle])[0] def set_target(self, jid): self.target = jid self.handle = self.conn.RequestHandles(cs.HT_CONTACT, [jid])[0] def set_ft_caps(self): caps_iface = dbus.Interface(self.conn, cs.CONN_IFACE_CONTACT_CAPS) caps_iface.UpdateCapabilities([("self", [ft_fixed_properties], dbus.Array([], signature="s"))]) self.q.expect('dbus-signal', signal='ContactCapabilitiesChanged', path=self.conn.object.object_path, args=[{self.self_handle:generic_ft_caps}]) def wait_for_ft_caps(self): conn_caps_iface = dbus.Interface(self.conn, cs.CONN_IFACE_CONTACT_CAPS) caps = conn_caps_iface.GetContactCapabilities([self.handle]) if caps != dbus.Dictionary({self.handle:generic_ft_caps}): self.q.expect('dbus-signal', signal='ContactCapabilitiesChanged', path=self.conn.object.object_path, args=[{self.handle:generic_ft_caps}]) caps = conn_caps_iface.GetContactCapabilities([self.handle]) assert caps == dbus.Dictionary({self.handle:generic_ft_caps}), caps def create_ft_channel(self): ft_chan = self.bus.get_object(self.conn.object.bus_name, self.ft_path) self.channel = dbus.Interface(ft_chan, cs.CHANNEL) self.ft_channel = dbus.Interface(ft_chan, cs.CHANNEL_TYPE_FILE_TRANSFER) self.ft_props = dbus.Interface(ft_chan, cs.PROPERTIES_IFACE) self.closed = False def channel_closed_cb(): self.closed = True self.channel.connect_to_signal('Closed', channel_closed_cb) def close_channel(self): if self.closed is False: self.channel.Close() self.q.expect('dbus-signal', signal='Closed', path=self.channel.object_path) def done(self): pass def test(self, q, bus, conn, stream): self.q = q self.bus = bus self.conn = conn self.stream = stream self.stream.addObserver( "//presence", self._cb_presence_iq, priority=1) self.stream.addObserver( "/iq/query[@xmlns='http://jabber.org/protocol/disco#info']", self._cb_disco_iq, priority=1) def _cb_presence_iq(self, stanza): nodes = xpath.queryForNodes("/presence/c", stanza) c = nodes[0] if 'share-v1' in c.getAttribute('ext'): assert FileTransferTest.caps_identities is not None and \ FileTransferTest.caps_features is not None and \ FileTransferTest.caps_dataforms is not None new_hash = compute_caps_hash(FileTransferTest.caps_identities, FileTransferTest.caps_features + \ [ns.GOOGLE_FEAT_SHARE], FileTransferTest.caps_dataforms) # Replace ver hash from one with file-transfer ns to one without FileTransferTest.caps_ft = c.attributes['ver'] c.attributes['ver'] = new_hash else: node = c.attributes['node'] ver = c.attributes['ver'] # ask for raw caps request = elem_iq(self.stream, 'get', from_='fake_contact@jabber.org/resource')( elem(ns.DISCO_INFO, 'query', node=(node + '#' + ver))) self.stream.send(request) def _cb_disco_iq(self, iq): nodes = xpath.queryForNodes("/iq/query", iq) query = nodes[0] if query.getAttribute('node') is None: return node = query.attributes['node'] ver = node.replace("http://telepathy.freedesktop.org/caps#", "") if iq.getAttribute('type') == 'result': if FileTransferTest.caps_identities is None or \ FileTransferTest.caps_features is None or \ FileTransferTest.caps_dataforms is None: # create our own identity identity_nodes = xpath.queryForNodes('/iq/query/identity', iq) assertLength(1, identity_nodes) identity_node = identity_nodes[0] identity_category = identity_node['category'] identity_type = identity_node['type'] identity_name = identity_node['name'] identity = '%s/%s//%s' % (identity_category, identity_type, identity_name) _, features, dataforms = extract_disco_parts(iq) FileTransferTest.caps_identities = [identity] FileTransferTest.caps_features = features FileTransferTest.caps_dataforms = dataforms # Check if the hash matches the announced capabilities assertEquals(compute_caps_hash(FileTransferTest.caps_identities, FileTransferTest.caps_features, FileTransferTest.caps_dataforms), ver) if ver == FileTransferTest.caps_ft: caps_share = compute_caps_hash(FileTransferTest.caps_identities, FileTransferTest.caps_features + \ [ns.GOOGLE_FEAT_SHARE], FileTransferTest.caps_dataforms) n = query.attributes['node'].replace(ver, caps_share) query.attributes['node'] = n for feature in xpath.queryForNodes('/iq/query/feature', iq): query.children.remove(feature) for f in FileTransferTest.caps_features + [ns.GOOGLE_FEAT_SHARE]: el = domish.Element((None, 'feature')) el['var'] = f query.addChild(el) elif iq.getAttribute('type') == 'get': caps_share = compute_caps_hash(FileTransferTest.caps_identities, FileTransferTest.caps_features + \ [ns.GOOGLE_FEAT_SHARE], FileTransferTest.caps_dataforms) if ver == caps_share: n = query.attributes['node'].replace(ver, FileTransferTest.caps_ft) query.attributes['node'] = n def create_socket(self): if self.address_type == cs.SOCKET_ADDRESS_TYPE_UNIX: return socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) elif self.address_type == cs.SOCKET_ADDRESS_TYPE_IPV4: return socket.socket(socket.AF_INET, socket.SOCK_STREAM) elif self.address_type == cs.SOCKET_ADDRESS_TYPE_IPV6: return socket.socket(socket.AF_INET6, socket.SOCK_STREAM) else: assert False class ReceiveFileTest(FileTransferTest): def __init__(self, file, address_type, access_control, access_control_param): FileTransferTest.__init__(self, file, address_type, access_control, access_control_param) self._actions = [self.connect, self.set_ft_caps, None, self.wait_for_ft_caps, None, self.check_new_channel, self.accept_file, None, self.receive_file, None, self.close_channel, self.done] def check_new_channel(self): def is_ft_channel_event(event): channels, = event.args if len(channels) > 1: return False path, props = channels[0] return props[cs.CHANNEL_TYPE] == cs.CHANNEL_TYPE_FILE_TRANSFER e = self.q.expect('dbus-signal', signal='NewChannels', path=self.conn.object.object_path, predicate=is_ft_channel_event) channels, = e.args path, props = channels[0] # check channel properties # org.freedesktop.Telepathy.Channel D-Bus properties assert props[cs.CHANNEL_TYPE] == cs.CHANNEL_TYPE_FILE_TRANSFER, props assertSameSets( [ cs.CHANNEL_IFACE_FILE_TRANSFER_METADATA, cs.CHANNEL_TYPE_FILE_TRANSFER + '.FUTURE', ], props[cs.INTERFACES]) assert props[cs.TARGET_HANDLE] == self.handle, props assert props[cs.TARGET_ID] == self.target, props assert props[cs.TARGET_HANDLE_TYPE] == cs.HT_CONTACT, props assert props[cs.REQUESTED] == False, props assert props[cs.INITIATOR_HANDLE] == self.handle, props assert props[cs.INITIATOR_ID] == self.target, props # org.freedesktop.Telepathy.Channel.Type.FileTransfer D-Bus properties assert props[cs.FT_STATE] == cs.FT_STATE_PENDING, props assert props[cs.FT_CONTENT_TYPE] == '', props assert props[cs.FT_FILENAME].encode('utf-8') == self.file.name, props assert props[cs.FT_SIZE] == self.file.size, props # FT's protocol doesn't allow us the send the hash info assert props[cs.FT_CONTENT_HASH_TYPE] == cs.FILE_HASH_TYPE_NONE, props assert props[cs.FT_CONTENT_HASH] == '', props assert props[cs.FT_DESCRIPTION] == '', props assert props[cs.FT_DATE] == 0, props assert props[cs.FT_AVAILABLE_SOCKET_TYPES] == \ {cs.SOCKET_ADDRESS_TYPE_UNIX: [cs.SOCKET_ACCESS_CONTROL_LOCALHOST], cs.SOCKET_ADDRESS_TYPE_IPV4: [cs.SOCKET_ACCESS_CONTROL_LOCALHOST], cs.SOCKET_ADDRESS_TYPE_IPV6: [cs.SOCKET_ACCESS_CONTROL_LOCALHOST]}, \ props[cs.FT_AVAILABLE_SOCKET_TYPES] assert props[cs.FT_TRANSFERRED_BYTES] == 0, props assert props[cs.FT_INITIAL_OFFSET] == 0, props self.ft_path = path self.create_ft_channel() def accept_file(self): self.address = self.ft_channel.AcceptFile(self.address_type, self.access_control, self.access_control_param, self.file.offset, byte_arrays=True) state_event = self.q.expect('dbus-signal', signal='FileTransferStateChanged', path=self.channel.object_path) state, reason = state_event.args assert state == cs.FT_STATE_ACCEPTED assert reason == cs.FT_STATE_CHANGE_REASON_REQUESTED state_event, offset_event = self.q.expect_many( EventPattern ('dbus-signal', signal='FileTransferStateChanged', path=self.channel.object_path), EventPattern ('dbus-signal', signal='InitialOffsetDefined', path=self.channel.object_path)) offset = offset_event.args[0] assert offset == 0 state, reason = state_event.args assert state == cs.FT_STATE_OPEN assert reason == cs.FT_STATE_CHANGE_REASON_NONE def receive_file(self): # Connect to Gabble's socket s = self.create_socket() s.connect(self.address) self._read_file_from_socket(s) def _read_file_from_socket(self, s): # Read the file from Gabble's socket data = '' read = 0 to_receive = self.file.size e = self.q.expect('dbus-signal', signal='TransferredBytesChanged', path=self.channel.object_path) count = e.args[0] while True: received = s.recv(1024) if len(received) == 0: break data += received assert data == self.file.data while count < to_receive: # Catch TransferredBytesChanged until we transfered all the data e = self.q.expect('dbus-signal', signal='TransferredBytesChanged', path=self.channel.object_path) count = e.args[0] e = self.q.expect('dbus-signal', signal='FileTransferStateChanged', path=self.channel.object_path) state, reason = e.args assert state == cs.FT_STATE_COMPLETED assert reason == cs.FT_STATE_CHANGE_REASON_NONE class SendFileTest(FileTransferTest): def __init__(self, file, address_type, access_control, acces_control_param): FileTransferTest.__init__(self, file, address_type, access_control, acces_control_param) self._actions = [self.connect, self.set_ft_caps, self.check_ft_available, None, self.wait_for_ft_caps, None, self.request_ft_channel, self.provide_file, None, self.send_file, self.wait_for_completion, None, self.close_channel, self.done] def check_ft_available(self): properties = self.conn.GetAll(cs.CONN_IFACE_REQUESTS, dbus_interface=cs.PROPERTIES_IFACE) # general FT class assert ({cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_FILE_TRANSFER, cs.TARGET_HANDLE_TYPE: cs.HT_CONTACT}, [cs.FT_CONTENT_HASH_TYPE, cs.TARGET_HANDLE, cs.TARGET_ID, cs.FT_CONTENT_TYPE, cs.FT_FILENAME, cs.FT_SIZE, cs.FT_CONTENT_HASH, cs.FT_DESCRIPTION, cs.FT_DATE, cs.FT_URI, cs.FT_SERVICE_NAME, cs.FT_METADATA] ) in properties.get('RequestableChannelClasses'),\ properties['RequestableChannelClasses'] # FT class with MD5 as HashType assert ({cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_FILE_TRANSFER, cs.TARGET_HANDLE_TYPE: cs.HT_CONTACT, cs.FT_CONTENT_HASH_TYPE: cs.FILE_HASH_TYPE_MD5}, [cs.TARGET_HANDLE, cs.TARGET_ID, cs.FT_CONTENT_TYPE, cs.FT_FILENAME, cs.FT_SIZE, cs.FT_CONTENT_HASH, cs.FT_DESCRIPTION, cs.FT_DATE, cs.FT_URI, cs.FT_SERVICE_NAME, cs.FT_METADATA] ) in properties.get('RequestableChannelClasses'),\ properties['RequestableChannelClasses'] def request_ft_channel(self): self.ft_path, props = self.conn.Requests.CreateChannel({ cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_FILE_TRANSFER, cs.TARGET_HANDLE_TYPE: cs.HT_CONTACT, cs.TARGET_HANDLE: self.handle, cs.FT_CONTENT_TYPE: self.file.content_type, cs.FT_FILENAME: self.file.name, cs.FT_SIZE: self.file.size, cs.FT_CONTENT_HASH_TYPE: self.file.hash_type, cs.FT_CONTENT_HASH: self.file.hash, cs.FT_DESCRIPTION: self.file.description, cs.FT_DATE: self.file.date, cs.FT_INITIAL_OFFSET: 0, }) # org.freedesktop.Telepathy.Channel D-Bus properties assert props[cs.CHANNEL_TYPE] == cs.CHANNEL_TYPE_FILE_TRANSFER assertSameSets( [ cs.CHANNEL_IFACE_FILE_TRANSFER_METADATA, cs.CHANNEL_TYPE_FILE_TRANSFER + '.FUTURE', ], props[cs.INTERFACES]) assert props[cs.TARGET_HANDLE] == self.handle assert props[cs.TARGET_ID] == self.target assert props[cs.TARGET_HANDLE_TYPE] == cs.HT_CONTACT assert props[cs.REQUESTED] == True assert props[cs.INITIATOR_HANDLE] == self.self_handle assert props[cs.INITIATOR_ID] == self.self_handle_name # org.freedesktop.Telepathy.Channel.Type.FileTransfer D-Bus properties assert props[cs.FT_STATE] == cs.FT_STATE_PENDING assert props[cs.FT_CONTENT_TYPE] == self.file.content_type assert props[cs.FT_FILENAME].encode('utf-8') == self.file.name, props assert props[cs.FT_SIZE] == self.file.size assert props[cs.FT_CONTENT_HASH_TYPE] == self.file.hash_type assert props[cs.FT_CONTENT_HASH] == self.file.hash assert props[cs.FT_DESCRIPTION] == self.file.description assert props[cs.FT_DATE] == self.file.date assert props[cs.FT_AVAILABLE_SOCKET_TYPES] == \ {cs.SOCKET_ADDRESS_TYPE_UNIX: [cs.SOCKET_ACCESS_CONTROL_LOCALHOST], cs.SOCKET_ADDRESS_TYPE_IPV4: [cs.SOCKET_ACCESS_CONTROL_LOCALHOST], cs.SOCKET_ADDRESS_TYPE_IPV6: [cs.SOCKET_ACCESS_CONTROL_LOCALHOST]}, \ props[cs.FT_AVAILABLE_SOCKET_TYPES] assert props[cs.FT_TRANSFERRED_BYTES] == 0 assert props[cs.FT_INITIAL_OFFSET] == 0 self.create_ft_channel() self.open = False self.offset_defined = False def initial_offset_defined_cb(offset): self.offset_defined = True assert offset == 0, offset self.ft_channel.connect_to_signal('InitialOffsetDefined', initial_offset_defined_cb) # Make sure the file transfer is of type jingle-share event = self.q.expect('stream-iq', stream=self.stream, query_name = 'session', query_ns = ns.GOOGLE_SESSION) description_node = xpath.queryForNodes('/iq/session/description', event.stanza)[0] assert description_node.uri == ns.GOOGLE_SESSION_SHARE, \ description_node.uri def provide_file(self): # try to accept our outgoing file transfer try: self.ft_channel.AcceptFile(self.address_type, self.access_control, self.access_control_param, self.file.offset, byte_arrays=True) except dbus.DBusException, e: assert e.get_dbus_name() == cs.NOT_AVAILABLE else: assert False # In case a unit test accepts the FT before we ProvideFile # then the ProvideFile will result in an OPEN state with reason state = self.ft_props.Get(cs.CHANNEL_TYPE_FILE_TRANSFER, 'State') if state == cs.FT_STATE_ACCEPTED: self.open_reason = cs.FT_STATE_CHANGE_REASON_REQUESTED else: self.open_reason = cs.FT_STATE_CHANGE_REASON_NONE self.address = self.ft_channel.ProvideFile(self.address_type, self.access_control, self.access_control_param, byte_arrays=True) def send_file(self): if self.open is False: self.q.expect('dbus-signal', signal='FileTransferStateChanged', path=self.channel.object_path, args=[cs.FT_STATE_OPEN, self.open_reason]) assert self.offset_defined == True s = self.create_socket() s.connect(self.address) s.send(self.file.data) def wait_for_completion(self): to_send = self.file.size self.count = 0 def bytes_changed_cb(bytes): self.count = bytes self.ft_channel.connect_to_signal('TransferredBytesChanged', bytes_changed_cb) # FileTransferStateChanged can be fired while we are receiving data self.completed = False def ft_state_changed_cb(state, reason): if state == cs.FT_STATE_COMPLETED: self.completed = True self.ft_channel.connect_to_signal('FileTransferStateChanged', ft_state_changed_cb) # If not all the bytes transferred have been announced using # TransferredBytesChanged, wait for them while self.count < to_send: self.q.expect('dbus-signal', signal='TransferredBytesChanged', path=self.channel.object_path) assert self.count == to_send def exec_file_transfer_test(send_cls, recv_cls, file = None): addr_type = cs.SOCKET_ADDRESS_TYPE_IPV4 access_control = cs.SOCKET_ACCESS_CONTROL_LOCALHOST access_control_param = "" if file is None: file = File() def test(q, bus, conns, streams): q.timeout = 15 conn1, conn2 = conns stream1, stream2 = streams send = send_cls(file, addr_type, access_control, access_control_param) recv = recv_cls(file, addr_type, access_control, access_control_param) send.test(q, bus, conn1, stream1) recv.test(q, bus, conn2, stream2) send_action = 0 recv_action = 0 target_set = False done = False while send_action < len(send._actions) or \ recv_action < len(recv._actions): for i in range(send_action, len(send._actions)): action = send._actions[i] if action is None: break done = action() if done is True: break send_action = i + 1 if done is True: break for i in range(recv_action, len(recv._actions)): action = recv._actions[i] if action is None: break done = action() if done is True: break recv_action = i + 1 if done is True: break if target_set == False: send.set_target(recv.self_handle_name) recv.set_target(send.self_handle_name) target_set = True exec_test(test, num_instances=2, do_connect=False) telepathy-gabble-0.18.2/tests/twisted/jingle-share/test-send-file-wait-to-provide.py0000644000175000017500000000313412200204333030425 0ustar00smcvsmcv00000000000000import dbus import constants as cs from servicetest import EventPattern from file_transfer_helper import SendFileTest, ReceiveFileTest, \ FileTransferTest, exec_file_transfer_test from config import JINGLE_FILE_TRANSFER_ENABLED if not JINGLE_FILE_TRANSFER_ENABLED: print "NOTE: built with --disable-file-transfer or --disable-voip" raise SystemExit(77) class SendFileAndWaitToProvide (SendFileTest): def __init__(self, file, address_type, access_control, acces_control_param): FileTransferTest.__init__(self, file, address_type, access_control, acces_control_param) self._actions = [self.connect, self.set_ft_caps, self.check_ft_available, None, self.wait_for_ft_caps, None, self.request_ft_channel, self.check_pending_state, None, self.check_accepted_state, self.provide_file, self.send_file, self.wait_for_completion, None, self.close_channel, self.done] def check_pending_state(self): # state is still Pending as remote didn't accept the transfer yet state = self.ft_props.Get(cs.CHANNEL_TYPE_FILE_TRANSFER, 'State') assert state == cs.FT_STATE_PENDING def check_accepted_state(self): # Remote accepted the transfer state = self.ft_props.Get(cs.CHANNEL_TYPE_FILE_TRANSFER, 'State') assert state == cs.FT_STATE_ACCEPTED, state if __name__ == '__main__': exec_file_transfer_test(SendFileAndWaitToProvide, ReceiveFileTest) telepathy-gabble-0.18.2/tests/twisted/jingle-share/test-send-file-send-before-accept.py0000644000175000017500000000241712200204333031024 0ustar00smcvsmcv00000000000000from file_transfer_helper import SendFileTest, FileTransferTest, \ ReceiveFileTest, exec_file_transfer_test from config import JINGLE_FILE_TRANSFER_ENABLED if not JINGLE_FILE_TRANSFER_ENABLED: print "NOTE: built with --disable-file-transfer or --disable-voip" raise SystemExit(77) print("FIXME: test is not stable enough.\n" + " https://bugs.freedesktop.org/show_bug.cgi?id=49595") raise SystemExit(77) class SendFileBeforeAccept(SendFileTest): def __init__(self, file, address_type, access_control, acces_control_param): FileTransferTest.__init__(self, file, address_type, access_control, acces_control_param) self._actions = [self.connect, self.set_ft_caps, self.check_ft_available, None, self.wait_for_ft_caps, None, self.request_ft_channel, self.provide_file, self.set_open, self.send_file, None, self.wait_for_completion, None, self.close_channel, self.done] def set_open(self): self.open = True self.offset_defined = True if __name__ == '__main__': exec_file_transfer_test(SendFileBeforeAccept, ReceiveFileTest) telepathy-gabble-0.18.2/tests/twisted/jingle-share/test-send-file.py0000644000175000017500000000074712200204333025404 0ustar00smcvsmcv00000000000000# -*- coding: utf-8 -*- from file_transfer_helper import SendFileTest, ReceiveFileTest, \ exec_file_transfer_test, File from config import JINGLE_FILE_TRANSFER_ENABLED if not JINGLE_FILE_TRANSFER_ENABLED: print "NOTE: built with --disable-file-transfer or --disable-voip" raise SystemExit(77) if __name__ == '__main__': file = File() file.offset = 5 file.name = "The greek foo δοκιμή.txt" exec_file_transfer_test(SendFileTest, ReceiveFileTest, file) telepathy-gabble-0.18.2/tests/twisted/jingle-share/test-send-file-and-cancel-immediately.py0000644000175000017500000000525512200204333031667 0ustar00smcvsmcv00000000000000import dbus import constants as cs from servicetest import EventPattern from file_transfer_helper import SendFileTest, ReceiveFileTest, \ FileTransferTest, exec_file_transfer_test from config import JINGLE_FILE_TRANSFER_ENABLED if not JINGLE_FILE_TRANSFER_ENABLED: print "NOTE: built with --disable-file-transfer or --disable-voip" raise SystemExit(77) class ReceiveFileStopped(ReceiveFileTest): def __init__(self, file, address_type, access_control, access_control_param): FileTransferTest.__init__(self, file, address_type, access_control, access_control_param) self._actions = [self.connect, self.set_ft_caps, None, self.wait_for_ft_caps, None, self.check_new_channel, None, self.check_stopped, None, self.close_channel, self.done] def check_stopped(self): state_event = self.q.expect ('dbus-signal', signal='FileTransferStateChanged', path=self.channel.object_path) state, reason = state_event.args assert state == cs.FT_STATE_CANCELLED assert reason == cs.FT_STATE_CHANGE_REASON_REMOTE_STOPPED # try to provide the file try: self.accept_file() except dbus.DBusException, e: assert e.get_dbus_name() == cs.NOT_AVAILABLE else: assert False class SendFileAndClose (SendFileTest): def __init__(self, file, address_type, access_control, acces_control_param): FileTransferTest.__init__(self, file, address_type, access_control, acces_control_param) self._actions = [self.connect, self.set_ft_caps, self.check_ft_available, None, self.wait_for_ft_caps, None, self.request_ft_channel, self.provide_file, None, self.close_and_check, None, self.close_channel, self.done] def close_and_check(self): self.channel.Close() state_event, _ = self.q.expect_many( EventPattern('dbus-signal', signal='FileTransferStateChanged', path=self.channel.object_path), EventPattern('dbus-signal', signal='Closed', path=self.channel.object_path)) state, reason = state_event.args assert state == cs.FT_STATE_CANCELLED assert reason == cs.FT_STATE_CHANGE_REASON_LOCAL_STOPPED if __name__ == '__main__': exec_file_transfer_test(SendFileAndClose, ReceiveFileStopped) telepathy-gabble-0.18.2/tests/twisted/jingle-share/test-receive-file-decline.py0000644000175000017500000000614112200204333027470 0ustar00smcvsmcv00000000000000import dbus import constants as cs from servicetest import EventPattern from file_transfer_helper import SendFileTest, ReceiveFileTest, \ FileTransferTest, exec_file_transfer_test from config import JINGLE_FILE_TRANSFER_ENABLED if not JINGLE_FILE_TRANSFER_ENABLED: print "NOTE: built with --disable-file-transfer or --disable-voip" raise SystemExit(77) class ReceiveFileDecline(ReceiveFileTest): def __init__(self, file, address_type, access_control, access_control_param): FileTransferTest.__init__(self, file, address_type, access_control, access_control_param) self._actions = [self.connect, self.set_ft_caps, None, self.wait_for_ft_caps, None, self.check_new_channel, self.close_and_check, self.done] def close_and_check(self): self.channel.Close() state_event, event, _ = self.q.expect_many( EventPattern('dbus-signal', signal='FileTransferStateChanged', path=self.channel.object_path), EventPattern('stream-iq', stream=self.stream, iq_type='set', query_name='session'), EventPattern('dbus-signal', signal='Closed', path=self.channel.object_path)) state, reason = state_event.args assert state == cs.FT_STATE_CANCELLED assert reason == cs.FT_STATE_CHANGE_REASON_LOCAL_STOPPED while event.query.getAttribute('type') != 'terminate': event = self.q.expect('stream-iq', stream=self.stream, iq_type='set', query_name='session') class SendFileDeclined (SendFileTest): def __init__(self, file, address_type, access_control, acces_control_param): FileTransferTest.__init__(self, file, address_type, access_control, acces_control_param) self._actions = [self.connect, self.set_ft_caps, self.check_ft_available, None, self.wait_for_ft_caps, None, self.request_ft_channel, self.provide_file, None, self.check_declined, self.close_channel, self.done] def check_declined(self): state_event = self.q.expect('dbus-signal', signal='FileTransferStateChanged', path=self.channel.object_path) state, reason = state_event.args assert state == cs.FT_STATE_CANCELLED assert reason == cs.FT_STATE_CHANGE_REASON_REMOTE_STOPPED transferred = self.ft_props.Get(cs.CHANNEL_TYPE_FILE_TRANSFER, 'TransferredBytes') # no byte has been transferred as the file was declined assert transferred == 0 # try to provide the file try: self.provide_file() except dbus.DBusException, e: assert e.get_dbus_name() == cs.NOT_AVAILABLE else: assert False if __name__ == '__main__': exec_file_transfer_test(SendFileDeclined, ReceiveFileDecline) test-receive-file-and-sender-disconnect-while-transfering.py0000644000175000017500000000321312200204333035600 0ustar00smcvsmcv00000000000000telepathy-gabble-0.18.2/tests/twisted/jingle-shareimport dbus import constants as cs from file_transfer_helper import SendFileTest, ReceiveFileTest, \ FileTransferTest, exec_file_transfer_test from config import JINGLE_FILE_TRANSFER_ENABLED if not JINGLE_FILE_TRANSFER_ENABLED: print "NOTE: built with --disable-file-transfer or --disable-voip" raise SystemExit(77) class ReceiveFileAndSenderDisconnectWhileTransfering(ReceiveFileTest): def receive_file(self): self.q.expect('dbus-signal', signal='FileTransferStateChanged', path = self.channel.object_path, args=[cs.FT_STATE_CANCELLED, \ cs.FT_STATE_CHANGE_REASON_REMOTE_STOPPED]) self.close_channel() # stop the test return True class SendFileAndDisconnect (SendFileTest): def __init__(self, file, address_type, access_control, acces_control_param): FileTransferTest.__init__(self, file, address_type, access_control, acces_control_param) self._actions = [self.connect, self.set_ft_caps, self.check_ft_available, None, self.wait_for_ft_caps, None, self.request_ft_channel, self.provide_file, None, self.send_file, self.wait_for_completion, self.disconnect, None, self.close_channel, self.done] def disconnect(self): self.conn.Disconnect() if __name__ == '__main__': exec_file_transfer_test(SendFileAndDisconnect, \ ReceiveFileAndSenderDisconnectWhileTransfering) test-receive-file-and-sender-disconnect-while-pending.py0000644000175000017500000000361612200204333034711 0ustar00smcvsmcv00000000000000telepathy-gabble-0.18.2/tests/twisted/jingle-shareimport dbus import constants as cs from file_transfer_helper import SendFileTest, ReceiveFileTest, \ FileTransferTest, exec_file_transfer_test from config import JINGLE_FILE_TRANSFER_ENABLED if not JINGLE_FILE_TRANSFER_ENABLED: print "NOTE: built with --disable-file-transfer or --disable-voip" raise SystemExit(77) class ReceiveFileAndSenderDisconnectWhilePendingTest(ReceiveFileTest): def accept_file(self): e = self.q.expect('dbus-signal', signal='FileTransferStateChanged', path = self.channel.object_path, args=[cs.FT_STATE_CANCELLED, \ cs.FT_STATE_CHANGE_REASON_REMOTE_STOPPED]) # We can't accept the transfer now try: self.ft_channel.AcceptFile(cs.SOCKET_ADDRESS_TYPE_UNIX, cs.SOCKET_ACCESS_CONTROL_LOCALHOST, "", 0) except dbus.DBusException, e: assert e.get_dbus_name() == cs.NOT_AVAILABLE else: assert False self.close_channel() # stop the test return True class SendFileAndDisconnect (SendFileTest): def __init__(self, file, address_type, access_control, acces_control_param): FileTransferTest.__init__(self, file, address_type, access_control, acces_control_param) self._actions = [self.connect, self.set_ft_caps, self.check_ft_available, None, self.wait_for_ft_caps, None, self.request_ft_channel, self.provide_file, self.disconnect, None, self.close_channel, self.done] def disconnect(self): self.conn.Disconnect() if __name__ == '__main__': exec_file_transfer_test(SendFileAndDisconnect, \ ReceiveFileAndSenderDisconnectWhilePendingTest) telepathy-gabble-0.18.2/tests/twisted/jingle-share/test-receive-file-and-disconnect.py0000644000175000017500000000117612200204333030761 0ustar00smcvsmcv00000000000000 from file_transfer_helper import SendFileTest, ReceiveFileTest, \ exec_file_transfer_test from config import JINGLE_FILE_TRANSFER_ENABLED if not JINGLE_FILE_TRANSFER_ENABLED: print "NOTE: built with --disable-file-transfer or --disable-voip" raise SystemExit(77) class ReceiveFileAndDisconnectTest(ReceiveFileTest): def receive_file(self): s = self.create_socket() s.connect(self.address) # return True so the test will be ended and the connection # disconnected return True if __name__ == '__main__': exec_file_transfer_test(SendFileTest, ReceiveFileAndDisconnectTest) test-receive-file-and-close-socket-while-receiving.py0000644000175000017500000000146212200204333034221 0ustar00smcvsmcv00000000000000telepathy-gabble-0.18.2/tests/twisted/jingle-share import constants as cs from file_transfer_helper import SendFileTest, ReceiveFileTest, \ exec_file_transfer_test from config import JINGLE_FILE_TRANSFER_ENABLED if not JINGLE_FILE_TRANSFER_ENABLED: print "NOTE: built with --disable-file-transfer or --disable-voip" raise SystemExit(77) class ReceiveFileAndCancelWhileReceiving(ReceiveFileTest): def receive_file(self): # Connect to Gabble's socket s = self.create_socket() s.connect(self.address) # for some reason the socket is closed s.close() self.q.expect('dbus-signal', signal='FileTransferStateChanged', args=[cs.FT_STATE_CANCELLED, cs.FT_STATE_CHANGE_REASON_LOCAL_ERROR]) if __name__ == '__main__': exec_file_transfer_test(SendFileTest, ReceiveFileAndCancelWhileReceiving) telepathy-gabble-0.18.2/tests/twisted/jingle-share/test-multift.py0000644000175000017500000001347512227000321025224 0ustar00smcvsmcv00000000000000import dbus from twisted.words.protocols.jabber.client import IQ from servicetest import assertEquals, assertSameSets, EventPattern from gabbletest import exec_test, sync_stream import constants as cs from jingleshareutils import test_ft_caps_from_contact from config import JINGLE_FILE_TRANSFER_ENABLED if not JINGLE_FILE_TRANSFER_ENABLED: print "NOTE: built with --disable-file-transfer or --disable-voip" raise SystemExit(77) def test(q, bus, conn, stream): client = 'http://telepathy.freedesktop.org/fake-client' contact = 'bilbo1@foo.com/Resource' files = [("file", "File.txt", 12345, False), ("file", "Image.txt", 54321, True), ("folder", "Folder", 123, False), ("folder", "Folder no size", None, True)] test_ft_caps_from_contact(q, bus, conn, stream, contact, 2L, client) self_handle = conn.GetSelfHandle() jid = conn.InspectHandles(cs.HT_CONTACT, [self_handle])[0] iq = IQ(stream, "set") iq['to'] = jid iq['from'] = contact session = iq.addElement("session", "http://www.google.com/session") session['type'] = "initiate" session['id'] = "2156517633" session['initiator'] = contact session.addElement("transport", "http://www.google.com/transport/p2p") description = session.addElement("description", "http://www.google.com/session/share") manifest = description.addElement("manifest") for f in files: type, name, size, image = f file = manifest.addElement(type) if size is not None: file['size'] = str(size) file.addElement("name", None, name) if image: image = file.addElement("image") image['width'] = '1200' image['height'] = '1024' protocol = description.addElement("protocol") http = protocol.addElement("http") url = http.addElement("url", None, "/temporary/ade15194140cf7b7bceafe/") url['name'] = 'source-path' url = http.addElement("url", None, "/temporary/578d715be25ddc28870d3f/") url['name'] = 'preview-path' stream.send(iq) event = q.expect('dbus-signal', signal="NewChannels") channels = event.args[0] # Make sure we get the right amout of channels assert len(channels) == len(files) # Make sure every file transfer has a channel associated with it found = [False for i in files] file_collection = None for channel in channels: path, props = channel # Get the FileCollection and make sure it exists if file_collection is None: file_collection = props[cs.FT_FILE_COLLECTION] assert file_collection != '' assert file_collection is not None # FileCollection must be the same for every channel assert props[cs.FT_FILE_COLLECTION] == file_collection, props for i, f in enumerate(files): type, name, size, image = f if type == "folder": name = "%s.tar" % name if size is None: size = 0 if props[cs.FT_FILENAME].encode('utf=8') == name: assert found[i] == False found[i] = True assert props[cs.FT_SIZE] == size, props assert props[cs.CHANNEL_TYPE] == cs.CHANNEL_TYPE_FILE_TRANSFER, props assertSameSets( [ cs.CHANNEL_IFACE_FILE_TRANSFER_METADATA, cs.CHANNEL_TYPE_FILE_TRANSFER + '.FUTURE', ], props[cs.INTERFACES]) assert props[cs.TARGET_HANDLE] == 2L, props assert props[cs.TARGET_ID] == contact.replace("/Resource", ""), props assert props[cs.TARGET_HANDLE_TYPE] == cs.HT_CONTACT, props assert props[cs.REQUESTED] == False, props assert props[cs.INITIATOR_HANDLE] == 2L, props assert props[cs.INITIATOR_ID] == contact.replace("/Resource", ""), props assert props[cs.FT_STATE] == cs.FT_STATE_PENDING, props assert props[cs.FT_CONTENT_TYPE] == '', props # FT's protocol doesn't allow us the send the hash info assert props[cs.FT_CONTENT_HASH_TYPE] == cs.FILE_HASH_TYPE_NONE, props assert props[cs.FT_CONTENT_HASH] == '', props assert props[cs.FT_DESCRIPTION] == '', props assert props[cs.FT_DATE] == 0, props assert props[cs.FT_AVAILABLE_SOCKET_TYPES] == \ {cs.SOCKET_ADDRESS_TYPE_UNIX: [cs.SOCKET_ACCESS_CONTROL_LOCALHOST], cs.SOCKET_ADDRESS_TYPE_IPV4: [cs.SOCKET_ACCESS_CONTROL_LOCALHOST], cs.SOCKET_ADDRESS_TYPE_IPV6: [cs.SOCKET_ACCESS_CONTROL_LOCALHOST]}, \ props[cs.FT_AVAILABLE_SOCKET_TYPES] assert props[cs.FT_TRANSFERRED_BYTES] == 0, props assert props[cs.FT_INITIAL_OFFSET] == 0, props assert False not in found event = q.expect('stream-iq', to=contact, iq_type='set', query_name='session') session_node = event.query assert session_node.attributes['type'] == 'transport-accept' # Close all but one of the channels, and make sure Gabble doesn't cancel # the multi-FT yet. terminate_pattern = EventPattern('stream-iq', to=contact, iq_type='set', query_name='session', predicate=lambda event: event.query['type'] == 'terminate') q.forbid_events([terminate_pattern]) for path, props in channels[:-1]: ft_chan = bus.get_object(conn.object.bus_name, path) channel = dbus.Interface(ft_chan, cs.CHANNEL) channel.Close() q.expect('dbus-signal', signal='Closed', path=path) sync_stream(q, stream) q.unforbid_all() # Now close the final channel, and make sure Gabble terminates the session. last_path, props = channels[-1] ft_chan = bus.get_object(conn.object.bus_name, last_path) channel = dbus.Interface(ft_chan, cs.CHANNEL) channel.Close() q.expect_many(terminate_pattern) if __name__ == '__main__': exec_test(test) telepathy-gabble-0.18.2/tests/twisted/jingle-share/test-caps-file-transfer.py0000644000175000017500000001437312227000321027223 0ustar00smcvsmcv00000000000000import dbus from twisted.words.xish import xpath from servicetest import (assertEquals, EventPattern) from gabbletest import exec_test, make_result_iq, sync_stream, make_presence import constants as cs from caps_helper import compute_caps_hash, \ text_fixed_properties, text_allowed_properties, \ stream_tube_fixed_properties, stream_tube_allowed_properties, \ dbus_tube_fixed_properties, dbus_tube_allowed_properties, \ ft_fixed_properties, ft_allowed_properties_with_metadata import ns from jingleshareutils import test_ft_caps_from_contact from config import FILE_TRANSFER_ENABLED if not FILE_TRANSFER_ENABLED: print "NOTE: built with --disable-file-transfer" raise SystemExit(77) def test(q, bus, conn, stream): client = 'http://telepathy.freedesktop.org/fake-client' test_ft_caps_from_contact(q, bus, conn, stream, 'bilbo1@foo.com/Foo', 2L, client) # our own capabilities, formerly tested here, are now in # tests/twisted/caps/advertise-contact-capabilities.py generic_ft_caps = [(text_fixed_properties, text_allowed_properties), (stream_tube_fixed_properties, \ stream_tube_allowed_properties), (dbus_tube_fixed_properties, dbus_tube_allowed_properties), (ft_fixed_properties, ft_allowed_properties_with_metadata)] generic_caps = [(text_fixed_properties, text_allowed_properties), (stream_tube_fixed_properties, \ stream_tube_allowed_properties), (dbus_tube_fixed_properties, dbus_tube_allowed_properties)] def check_contact_caps(conn, handle, with_ft): conn_caps_iface = dbus.Interface(conn, cs.CONN_IFACE_CONTACT_CAPS) conn_contacts_iface = dbus.Interface(conn, cs.CONN_IFACE_CONTACTS) if with_ft: expected_caps = dbus.Dictionary({handle: generic_ft_caps}) else: expected_caps = dbus.Dictionary({handle: generic_caps}) caps = conn_caps_iface.GetContactCapabilities([handle]) assert caps == expected_caps, caps # check the Contacts interface give the same caps caps_via_contacts_iface = conn_contacts_iface.GetContactAttributes( [handle], [cs.CONN_IFACE_CONTACT_CAPS], False) \ [handle][cs.ATTR_CONTACT_CAPABILITIES] assert caps_via_contacts_iface == caps[handle], \ caps_via_contacts_iface def test2(q, bus, connections, streams): conn1, conn2 = connections stream1, stream2 = streams conn1_handle = conn1.Properties.Get(cs.CONN, 'SelfHandle') conn1_jid = conn1.InspectHandles(cs.HT_CONTACT, [conn1_handle])[0] conn2_handle = conn2.Properties.Get(cs.CONN, 'SelfHandle') conn2_jid = conn2.InspectHandles(cs.HT_CONTACT, [conn2_handle])[0] handle1 = conn2.RequestHandles(cs.HT_CONTACT, [conn1_jid])[0] handle2 = conn1.RequestHandles(cs.HT_CONTACT, [conn2_jid])[0] q.expect_many(EventPattern('dbus-signal', signal='ContactCapabilitiesChanged', path=conn1.object.object_path), EventPattern('dbus-signal', signal='ContactCapabilitiesChanged', path=conn2.object.object_path)) check_contact_caps (conn1, handle2, False) check_contact_caps (conn2, handle1, False) caps_iface = dbus.Interface(conn1, cs.CONN_IFACE_CONTACT_CAPS) caps_iface.UpdateCapabilities([("self", [ft_fixed_properties], dbus.Array([], signature="s"))]) _, presence, disco, _ = \ q.expect_many(EventPattern('dbus-signal', signal='ContactCapabilitiesChanged', path=conn1.object.object_path, args=[{conn1_handle:generic_ft_caps}]), EventPattern('stream-presence', stream=stream1), EventPattern('stream-iq', stream=stream1, query_ns=ns.DISCO_INFO, iq_type = 'result'), EventPattern('dbus-signal', signal='ContactCapabilitiesChanged', path=conn2.object.object_path, args=[{handle1:generic_ft_caps}])) presence_c = xpath.queryForNodes('/presence/c', presence.stanza)[0] assert "share-v1" in presence_c.attributes['ext'] conn1_ver = presence_c.attributes['ver'] found_share = False for feature in xpath.queryForNodes('/iq/query/feature', disco.stanza): if feature.attributes['var'] == ns.GOOGLE_FEAT_SHARE: found_share = True assert found_share check_contact_caps (conn2, handle1, True) caps_iface = dbus.Interface(conn2, cs.CONN_IFACE_CONTACT_CAPS) caps_iface.UpdateCapabilities([("self", [ft_fixed_properties], dbus.Array([], signature="s"))]) _, presence, _ = \ q.expect_many(EventPattern('dbus-signal', signal='ContactCapabilitiesChanged', path=conn2.object.object_path, args=[{conn2_handle:generic_ft_caps}]), EventPattern('stream-presence', stream=stream2), EventPattern('dbus-signal', signal='ContactCapabilitiesChanged', path=conn1.object.object_path, args=[{handle2:generic_ft_caps}])) presence_c = xpath.queryForNodes('/presence/c', presence.stanza)[0] assert "share-v1" in presence_c.attributes['ext'] # We will have the same capabilities on both sides, so we can't check for # a cap disco since the hash will be the same, so we need to make sure the # hash is indeed the same assert presence_c.attributes['ver'] == conn1_ver found_share = False for feature in xpath.queryForNodes('/iq/query/feature', disco.stanza): if feature.attributes['var'] == ns.GOOGLE_FEAT_SHARE: found_share = True assert found_share check_contact_caps (conn1, handle2, True) if __name__ == '__main__': exec_test(test) exec_test(test2, num_instances=2) telepathy-gabble-0.18.2/tests/twisted/jingle/0000755000175000017500000000000012312537051021111 5ustar00smcvsmcv00000000000000telepathy-gabble-0.18.2/tests/twisted/jingle/jingletest2.py0000644000175000017500000007651012227000321023714 0ustar00smcvsmcv00000000000000# New API for making it easier to write Jingle tests. The idea # is not so much to hide away the details (this makes tests # unreadable), but to make the expressions denser and more concise. # Helper classes support different dialects so the test can # be invoked for different (possibly all) dialects. from functools import partial from twisted.words.xish import domish, xpath import random from gabbletest import sync_stream, exec_test from servicetest import EventPattern import dbus import ns import os import constants as cs class JingleProtocol(object): """ Defines a simple DSL for constructing Jingle messages. """ def __init__(self, dialect): self.dialect = dialect self.id_seq = 0 def _simple_xml(self, node): "Construct domish.Element tree from tree of tuples" name, namespace, attribs, children = node el = domish.Element((namespace, name)) for key, val in attribs.items(): el[key] = val for c in children: if isinstance(c, tuple): el.addChild(self._simple_xml(c)) elif isinstance(c, unicode): el.addContent(c) else: raise ValueError("invalid child object %r of type %r" % (c, type(c))) return el def xml(self, node): "Returns XML from tree of tuples" return self._simple_xml(node).toXml() def Iq(self, type, id, frm, to, children): "Creates an IQ element" if not id: id = 'seq%d' % self.id_seq self.id_seq += 1 return ('iq', 'jabber:client', { 'type': type, 'from': frm, 'to': to, 'id': id }, children) def SetIq(self, frm, to, children): "Creates a set IQ element" return self.Iq('set', None, frm, to, children) def ResultIq(self, to, iq, children): "Creates a result IQ element" return self.Iq('result', iq['id'], iq['to'], to, children) def ErrorIq(self, iq, errtype, errchild): "Creates an error IQ element, and includes the original stanza" return self.Iq('error', iq['id'], iq['to'], iq['from'], [ iq.firstChildElement(), ('error', None, { 'type': errtype, 'xmlns': ns.STANZA, }, [ errchild ]) ]) def PayloadType(self, name, rate, id, parameters={}, **kw): "Creates a element" kw['name'] = name kw['rate'] = rate kw['id'] = id children = [self.Parameter(name, value) for name, value in parameters.iteritems()] return ('payload-type', None, kw, children) def Parameter(self, name, value): "Creates a element" return ('parameter', None, {'name': name, 'value': value}, []) def TransportGoogleP2PCall (self, username, password, call_remote_candidates=[]): candidates = [] for (component, host, port, props) in call_remote_candidates: candidates.append(("candidate", None, { "name": "rtp", "address": host, "port": str(port), "protocol": "udp", "preference": str(props["priority"] / 65536.0), "type": ["INVALID NONE", "local", "stun", "INVALID PEER RFLX", "relay"][props["type"]], "network": "0", "generation": "0",# Increment this yourself if you care. "component": str(component), # 1 is rtp, 2 is rtcp "username": props.get("username", username), "password": props.get("password", password), }, [])) #NOTE: subtype and profile are unused return ('transport', ns.GOOGLE_P2P, {}, candidates) def TransportGoogleP2P(self, remote_transports=[]): """ Creates a element for Google P2P transport. If remote_transports is present, and of the form [(host, port, proto, subtype, profile, pref, transtype, user, pwd)] (basically a list of Media_Stream_Handler_Transport without the component number) then it will be converted to xml and added. """ candidates = [] for i, (host, port, proto, subtype, profile, pref, transtype, user, pwd ) in enumerate(remote_transports): candidates.append(("candidate", None, { "name": "rtp", "address": host, "port": str(port), "protocol": ["udp", "tcp"][proto], "preference": str(pref), "type": ["local", "stun", "relay"][transtype], "network": "0", "generation": "0",# Increment this yourself if you care. "component": "1", # 1 is rtp, 2 is rtcp "username": user, "password": pwd, }, [])) #NOTE: subtype and profile are unused return ('transport', ns.GOOGLE_P2P, {}, candidates) def TransportIceUdp(self, remote_transports=[]): """ Creates a element for ICE-UDP transport. If remote_transports is present, and of the form [(host, port, proto, subtype, profile, pref, transtype, user, pwd)] (basically a list of Media_Stream_Handler_Transport without the component number) then it will be converted to xml and added. """ candidates = [] attrs = {} for (host, port, proto, subtype, profile, pref, transtype, user, pwd ) in remote_transports: if "ufrag" not in attrs: attrs = {"ufrag": user, "pwd": pwd} else: assert (user == attrs["ufrag"] and pwd == attrs["pwd"] ), "user and pwd should be the same across all candidates." node = ("candidate", None, { # ICE-CORE says it can be arbitrary string, even though XEP # gives an int as an example. "foundation": "fake", "ip": host, "port": str(port), "protocol": ["udp", "tcp"][proto], # Gabble multiplies by 65536 so we should too. "priority": str(int(pref * 65536)), "type": ["host", "srflx", "srflx"][transtype], "network": "0", "generation": "0",# Increment this yourself if you care. "component": "1", # 1 is rtp, 2 is rtcp }, []) #NOTE: subtype and profile are unused candidates.append(node) return ('transport', ns.JINGLE_TRANSPORT_ICEUDP, attrs, candidates) def Presence(self, frm, to, caps): "Creates stanza with specified capabilities" children = [] if caps: children = [ ('c', ns.CAPS, caps, []) ] return ('presence', 'jabber:client', { 'from': frm, 'to': to }, children) def Query(self, node, xmlns, children): "Creates element" attrs = {} if node: attrs['node'] = node return ('query', xmlns, attrs, children) def Feature(self, var): "Creates element" return ('feature', None, { 'var': var }, []) def action_predicate(self, action): def f(e): return self.match_jingle_action(e.query, action) return f def match_jingle_action(self, q, action): return q is not None and q.name == 'jingle' and q['action'] == action def _extract_session_id(self, query): return query['sid'] def validate_session_initiate(self, query): raise NotImplementedError() def can_do_video(self): return True def can_do_video_only(self): return self.can_do_video() def separate_contents(self): return True def has_mutable_streams(self): return True def is_modern_jingle(self): return False def rtp_info_event(self, name): return None def rtp_info_event_list(self, name): e = self.rtp_info_event(name) return [e] if e is not None else [] class GtalkProtocol03(JingleProtocol): features = [ ns.GOOGLE_FEAT_VOICE, ns.GOOGLE_FEAT_VIDEO ] def __init__(self): JingleProtocol.__init__(self, 'gtalk-v0.3') def _action_map(self, action): map = { 'session-initiate': 'initiate', 'session-terminate': 'terminate', 'session-accept': 'accept', 'transport-info': 'candidates' } if action in map: return map[action] else: return action def Jingle(self, sid, initiator, action, children): action = self._action_map(action) return ('session', ns.GOOGLE_SESSION, { 'type': action, 'initiator': initiator, 'id': sid }, children) def PayloadType(self, name, rate, id, parameters={}, **kw): p = JingleProtocol.PayloadType(self, name, rate, id, parameters, **kw) if "type" in kw: namespaces = { "audio": ns.GOOGLE_SESSION_PHONE, "video": ns.GOOGLE_SESSION_VIDEO, } p = p[:1] + (namespaces[kw["type"]],) + p[2:] return p # Gtalk has only one content, and node is implicit. Also it # never mixes payloads and transport information. It's up to the call of # this function to ensure it never calls it with both mixed def Content(self, name, creator, senders=None, description=None, transport=None): # Normally has and , but we only # use unless has candidates. assert description == None or len(transport[3]) == 0 if description != None: return description else: assert len(transport[3]) == 1, \ "gtalk 0.3 only lets you send one candidate at a time." \ "You sent %r" % [transport] return transport[3][0] def Description(self, type, children): if type == 'audio': namespace = ns.GOOGLE_SESSION_PHONE elif type == 'video': namespace = ns.GOOGLE_SESSION_VIDEO else: namespace = 'unexistent-namespace' return ('description', namespace, {}, children) def match_jingle_action(self, q, action): action = self._action_map(action) return q is not None and q.name == 'session' and q['type'] == action def _extract_session_id(self, query): return query['id'] def can_do_video_only(self): return False def validate_session_initiate(self, query): sid = self._extract_session_id(query) # No transport in GTalk03 assert xpath.queryForNodes('/session/transport', query) == None # Exactly one description in Gtalk03 descs = xpath.queryForNodes('/session/description', query) assert len(descs) == 1 desc = descs[0] # the ds is either audio or video assert desc.uri in [ ns.GOOGLE_SESSION_PHONE, ns.GOOGLE_SESSION_VIDEO ] if desc.uri == ns.GOOGLE_SESSION_VIDEO: # If it's a video call there should be some audio codecs as well assert xpath.queryForNodes( '/session/description/payload-type[@xmlns="%s"]' % ns.GOOGLE_SESSION_PHONE, query) return (sid, ['fake-audio'], ['fake-video']) else: return (sid, ['fake-audio'], []) def separate_contents(self): return False def has_mutable_streams(self): return False class GtalkProtocol04(JingleProtocol): features = [ ns.GOOGLE_FEAT_VOICE, ns.GOOGLE_P2P ] def __init__(self): JingleProtocol.__init__(self, 'gtalk-v0.4') def _action_map(self, action): map = { 'session-initiate': 'initiate', 'session-terminate': 'terminate', 'session-accept': 'accept', } if action in map: return map[action] else: return action def Jingle(self, sid, initiator, action, children): # ignore Content and go straight for its children if len(children) == 1 and children[0][0] == 'dummy-content': # Either have just a transport or a description + transport # without candidates children = children[0][3] action = self._action_map(action) return ('session', ns.GOOGLE_SESSION, { 'type': action, 'initiator': initiator, 'id': sid }, children) # hacky: parent Jingle node should just pick up our children def Content(self, name, creator, senders=None, description=None, transport=None): return ('dummy-content', None, {}, [node for node in [description, transport] if node != None]) def Description(self, type, children): return ('description', ns.GOOGLE_SESSION_PHONE, {}, children) def match_jingle_action(self, q, action): action = self._action_map(action) return q is not None and q.name == 'session' and q['type'] == action def _extract_session_id(self, query): return query['id'] def validate_session_initiate(self, query): # FIXME: validate it! return (self._extract_session_id(query), ['fake-audio'], []) def can_do_video(self): return False class JingleProtocol015(JingleProtocol): features = [ ns.GOOGLE_P2P, ns.JINGLE_015, ns.JINGLE_015_AUDIO, ns.JINGLE_015_VIDEO ] def __init__(self): JingleProtocol.__init__(self, 'jingle-v0.15') def Jingle(self, sid, initiator, action, children): return ('jingle', ns.JINGLE_015, { 'action': action, 'initiator': initiator, 'sid': sid }, children) # Note: senders weren't mandatory in this dialect def Content(self, name, creator, senders = None, description=None, transport=None): attribs = { 'name': name, 'creator': creator } if senders: attribs['senders'] = senders return ('content', None, attribs, [node for node in [description, transport] if node != None]) def Description(self, type, children): if type == 'audio': namespace = ns.JINGLE_015_AUDIO elif type == 'video': namespace = ns.JINGLE_015_VIDEO else: namespace = 'unexistent-namespace' return ('description', namespace, { 'type': type }, children) def validate_session_initiate(self, query): contents = xpath.queryForNodes( '/jingle[@xmlns="%s"]/content' % ns.JINGLE_015, query) audio, video = [], [] for c in contents: a_desc = xpath.queryForNodes( '/content/description[@xmlns="%s"]' % ns.JINGLE_015_AUDIO, c) v_desc = xpath.queryForNodes( '/content/description[@xmlns="%s"]' % ns.JINGLE_015_VIDEO, c) if a_desc is not None: assert len(a_desc) == 1, c.toXml() assert v_desc is None audio.append(c['name']) elif v_desc is not None: assert len(v_desc) == 1, c.toXml() assert a_desc is None video.append(c['name']) else: assert False, c.toXml() assert len(audio) + len(video) > 0, query.toXml() return (self._extract_session_id(query), audio, video) class JingleProtocol031(JingleProtocol): features = [ ns.JINGLE, ns.JINGLE_RTP, ns.JINGLE_RTP_AUDIO, ns.JINGLE_RTP_VIDEO, ns.GOOGLE_P2P ] def __init__(self): JingleProtocol.__init__(self, 'jingle-v0.31') def Jingle(self, sid, initiator, action, children): return ('jingle', ns.JINGLE, { 'action': action, 'initiator': initiator, 'sid': sid }, children) def Content(self, name, creator, senders=None, description=None, transport=None): if not senders: senders = 'both' return ('content', None, { 'name': name, 'creator': creator, 'senders': senders }, [node for node in [description, transport] if node != None]) def Description(self, type, children): return ('description', ns.JINGLE_RTP, { 'media': type }, children) def is_modern_jingle(self): return True def rtp_info_event(self, name): def p(e): query = e.query if not self.match_jingle_action(query, 'session-info'): return False n = query.firstChildElement() return n is not None and n.uri == ns.JINGLE_RTP_INFO_1 and \ n.name == name return EventPattern('stream-iq', predicate=p) def validate_session_initiate(self, query): contents = xpath.queryForNodes( '/jingle[@xmlns="%s"]/content' % ns.JINGLE, query) audio, video = [], [] for c in contents: descs = xpath.queryForNodes( '/content/description[@xmlns="%s"]' % ns.JINGLE_RTP, c) assert len(descs) == 1, c.toXml() d = descs[0] if d['media'] == 'audio': audio.append(c['name']) elif d['media'] == 'video': video.append(c['name']) else: assert False, c.toXml() assert len(audio) + len(video) > 0, query.toXml() return (self._extract_session_id(query), audio, video) class JingleTest2(object): # Default caps for the remote end remote_caps = { 'ext': '', 'ver': '0.0.0', 'node': 'http://example.com/fake-client0' } # Default audio codecs for the remote end audio_codecs = [ ('GSM', 3, 8000, {}), ('PCMA', 8, 8000, {}), ('PCMU', 0, 8000, {}) ] # Default video codecs for the remote end. I have no idea what's # a suitable value here... video_codecs = [ ('WTF', 96, 90000, {}) ] ufrag = "SessionUfrag" pwd = "SessionPwd" # Default candidates for the remote end remote_call_candidates = [# Local candidates (1, "192.168.0.1", 666, {"type": cs.CALL_STREAM_CANDIDATE_TYPE_HOST, #"Foundation":, "protocol": cs.MEDIA_STREAM_BASE_PROTO_UDP, "priority": 10000, #"base-ip": }), (2, "192.168.0.1", 667, {"type": cs.CALL_STREAM_CANDIDATE_TYPE_HOST, #"Foundation":, "protocol": cs.MEDIA_STREAM_BASE_PROTO_UDP, "priority": 10000, #"base-ip": }), # STUN candidates have their own ufrag (1, "168.192.0.1", 10666, {"type": cs.CALL_STREAM_CANDIDATE_TYPE_SERVER_REFLEXIVE, #"Foundation":, "protocol": cs.MEDIA_STREAM_BASE_PROTO_UDP, "priority": 100, #"base-ip":, "username": "STUNRTPUfrag", "password": "STUNRTPPwd" }), (2, "168.192.0.1", 10667, {"type": cs.CALL_STREAM_CANDIDATE_TYPE_SERVER_REFLEXIVE, #"Foundation":, "protocol": cs.MEDIA_STREAM_BASE_PROTO_UDP, "priority": 100, #"base-ip":, "username": "STUNRTCPUfrag", "password": "STUNRTCPPwd" }), # Candidates found using UPnP or somesuch? (1, "131.111.12.50", 10666, {"type": cs.CALL_STREAM_CANDIDATE_TYPE_HOST, #"Foundation":, "protocol": cs.MEDIA_STREAM_BASE_PROTO_UDP, "priority": 1000, #"base-ip": }), (2, "131.111.12.50", 10667, {"type": cs.CALL_STREAM_CANDIDATE_TYPE_HOST, #"Foundation":, "protocol": cs.MEDIA_STREAM_BASE_PROTO_UDP, "priority": 1000, #"base-ip": }), ] remote_transports = [ ( "192.168.0.1", # host 666, # port 0, # protocol = TP_MEDIA_STREAM_BASE_PROTO_UDP "RTP", # protocol subtype "AVP", # profile 1.0, # preference 0, # transport type = TP_CALL_STREAM_CANDIDATE_TYPE_HOST, "username", "password" ) ] def __init__(self, jp, conn, q, stream, jid, peer): self.jp = jp self.conn = conn self.q = q self.jid = jid self.peer = peer self.peer_bare_jid = peer.split('/', 1)[0] self.stream = stream self.sid = 'sess' + str(int(random.random() * 10000)) def prepare(self, send_presence=True, send_roster=True, events=None): # If we need to override remote caps, feats, codecs or caps, # we should do it prior to calling this method. if events is None: # Catch events: authentication, our presence update, # status connected, vCard query # If we don't catch the vCard query here, it can trip us up later: # http://bugs.freedesktop.org/show_bug.cgi?id=19161 events = self.q.expect_many( EventPattern('stream-iq', to=None, query_ns='vcard-temp', query_name='vCard'), EventPattern('stream-iq', query_ns=ns.ROSTER), ) # some Jingle tests care about our roster relationship to the peer if send_roster: roster = events[-1] roster.stanza['type'] = 'result' item = roster.query.addElement('item') item['jid'] = 'publish@foo.com' item['subscription'] = 'from' item = roster.query.addElement('item') item['jid'] = 'subscribe@foo.com' item['subscription'] = 'to' item = roster.query.addElement('item') item['jid'] = 'publish-subscribe@foo.com' item['subscription'] = 'both' self.stream.send(roster.stanza) if send_presence: self.send_presence_and_caps() def send_presence(self): # We need remote end's presence for capabilities self.stream.send(self.jp.xml( self.jp.Presence(self.peer, self.jid, self.remote_caps))) # Gabble doesn't trust it, so makes a disco return self.q.expect('stream-iq', query_ns=ns.DISCO_INFO, to=self.peer) def send_remote_disco_reply(self, query_stanza): self.stream.send(self.jp.xml(self.jp.ResultIq(self.jid, query_stanza, [ self.jp.Query(None, ns.DISCO_INFO, [ self.jp.Feature(x) for x in self.jp.features ]) ]) )) def send_presence_and_caps(self): event = self.send_presence() self.send_remote_disco_reply(event.stanza) # Force Gabble to process the caps before doing any more Jingling sync_stream(self.q, self.stream) def generate_payloads(self, codecs, **kwargs): return [ self.jp.PayloadType(payload_name, str(rate), str(id), parameters, **kwargs) for (payload_name, id, rate, parameters) in codecs ] def generate_contents(self, transports=[]): assert len(self.audio_names + self.video_names) > 0 jp = self.jp assert len(self.video_names) == 0 or jp.can_do_video() contents = [] if not jp.separate_contents() and self.video_names: assert jp.can_do_video() assert self.audio_names payload = self.generate_payloads (self.video_codecs) + \ self.generate_payloads (self.audio_codecs, type="audio") contents.append( jp.Content('stream0', 'initiator', 'both', jp.Description('video', payload), jp.TransportGoogleP2P(transports)) ) else: def mk_content(name, media, codecs): payload = self.generate_payloads (codecs) contents.append( jp.Content(name, 'initiator', 'both', jp.Description(media, payload), jp.TransportGoogleP2P(transports)) ) for name in self.audio_names: mk_content(name, 'audio', self.audio_codecs) for name in self.video_names: mk_content(name, 'video', self.video_codecs) return contents def incoming_call(self, audio = "audio1", video = None): jp = self.jp self.audio_names = [ audio ] if audio != None else [] self.video_names = [ video ] if video != None else [] contents = self.generate_contents() node = jp.SetIq(self.peer, self.jid, [ jp.Jingle(self.sid, self.peer, 'session-initiate', contents), ]) self.stream.send(jp.xml(node)) def parse_session_initiate (self, query): # Validate the session initiate and get some useful info from it self.sid, self.audio_names, self.video_names = \ self.jp.validate_session_initiate(query) def accept(self): jp = self.jp contents = self.generate_contents() node = jp.SetIq(self.peer, self.jid, [ jp.Jingle(self.sid, self.peer, 'session-accept', contents) ]) self.stream.send(jp.xml(node)) def content_accept(self, query, media): """ Accepts a content-add stanza containing a single of the given media type. """ jp = self.jp assert jp.separate_contents() c = query.firstChildElement() if media == 'audio': codecs = self.audio_codecs elif media == 'video': codecs = self.video_codecs else: assert False # Remote end finally accepts node = jp.SetIq(self.peer, self.jid, [ jp.Jingle(self.sid, self.peer, 'content-accept', [ jp.Content(c['name'], c['creator'], c['senders'], jp.Description(media, [ jp.PayloadType(name, str(rate), str(id), parameters) for (name, id, rate, parameters) in codecs ]), jp.TransportGoogleP2P()) ]) ]) self.stream.send(jp.xml(node)) def content_modify(self, name, creator, senders): jp = self.jp assert jp.separate_contents() node = jp.SetIq(self.peer, self.jid, [ jp.Jingle(self.sid, self.peer, 'content-modify', [ jp.Content(name, creator, senders)])]) self.stream.send(jp.xml(node)) def terminate(self, reason=None, text=""): jp = self.jp if reason is not None and jp.is_modern_jingle(): body = [("reason", None, {}, [(reason, None, {}, []), ("text", None, {}, [text]), ] )] else: body = [] iq = jp.SetIq(self.peer, self.jid, [ jp.Jingle(self.sid, self.peer, 'session-terminate', body) ]) self.stream.send(jp.xml(iq)) def result_iq(self, iniq, children = []): jp = self.jp iq = jp.ResultIq(self.peer, {'id': iniq.iq_id, 'to': self.peer}, children) self.stream.send(jp.xml(iq)) def send_remote_candidates_call_xmpp(self, name, creator, candidates=None): jp = self.jp if candidates is None: candidates = self.remote_call_candidates node = jp.SetIq(self.peer, self.jid, [ jp.Jingle(self.sid, self.peer, 'transport-info', [ jp.Content(name, creator, transport=jp.TransportGoogleP2PCall (self.ufrag, self.pwd, candidates)) ] ) ]) self.stream.send(jp.xml(node)) def remote_candidates(self, name, creator): jp = self.jp node = jp.SetIq(self.peer, self.jid, [ jp.Jingle(self.sid, self.peer, 'transport-info', [ jp.Content(name, creator, transport=jp.TransportGoogleP2P (self.remote_transports)) ] ) ]) self.stream.send(jp.xml(node)) def dbusify_codecs(self, codecs): dbussed_codecs = [ (id, name, 0, rate, 0, params ) for (name, id, rate, params) in codecs ] return dbus.Array(dbussed_codecs, signature='(usuuua{ss})') def dbusify_codecs_with_params (self, codecs): return self.dbusify_codecs(codecs) def get_audio_codecs_dbus(self): return self.dbusify_codecs(self.audio_codecs) def get_video_codecs_dbus(self): return self.dbusify_codecs(self.video_codecs) def dbusify_call_codecs(self, codecs): dbussed_codecs = [ (id, name, rate, 0, False, params) for (name, id, rate, params) in codecs ] return dbus.Array(dbussed_codecs, signature='(usuuba{ss})') def dbusify_call_codecs_with_params(self, codecs): return dbusify_call_codecs (self, codecs) def __get_call_audio_codecs_dbus(self): return self.dbusify_call_codecs(self.audio_codecs) def __get_call_video_codecs_dbus(self): return self.dbusify_call_codecs(self.video_codecs) def get_call_audio_md_dbus(self, handle = 0): d = dbus.Dictionary( { cs.CALL_CONTENT_MEDIADESCRIPTION + '.Codecs': self.__get_call_audio_codecs_dbus(), }, signature='sv') if handle != 0: d[cs.CALL_CONTENT_MEDIADESCRIPTION + '.RemoteContact'] = dbus.UInt32 (handle) return d def get_call_video_md_dbus(self, handle = 0): d = dbus.Dictionary( { cs.CALL_CONTENT_MEDIADESCRIPTION + '.Codecs': self.__get_call_video_codecs_dbus(), }, signature='sv') if handle != 0: d[cs.CALL_CONTENT_MEDIADESCRIPTION + '.RemoteContact'] = dbus.UInt32 (handle) return d def get_remote_transports_dbus(self): return dbus.Array([ (dbus.UInt32(1 + i), host, port, proto, subtype, profile, pref, transtype, user, pwd) for i, (host, port, proto, subtype, profile, pref, transtype, user, pwd) in enumerate(self.remote_transports) ], signature='(usuussduss)') def get_call_remote_transports_dbus(self): return dbus.Array(self.remote_call_candidates, signature='(usqa{sv})') def test_dialects(f, dialects, params=None, protocol=None): for dialect in dialects: exec_test(partial(f, dialect()), params=params, protocol=protocol) def test_all_dialects(f, params=None, protocol=None): dialectmap = { "jingle015": JingleProtocol015, "jingle031": JingleProtocol031, "gtalk03": GtalkProtocol03, "gtalk04": GtalkProtocol04 } dialects = [] jd = os.getenv("JINGLE_DIALECTS") if jd == None: dialects = dialectmap.values() else: for d in jd.split (','): dialects.append(dialectmap[d]) test_dialects(f, dialects, params=params, protocol=protocol) telepathy-gabble-0.18.2/tests/twisted/jingle/__init__.py0000644000175000017500000000040512200204333023207 0ustar00smcvsmcv00000000000000# Without this, caps/jingle-caps.py can't include jingle.jingletest2. # # I guess the rationale for requiring an __init__.py at # makes sense, but it # was still kind of a pain. __all__ = ['jingletest2'] telepathy-gabble-0.18.2/tests/twisted/jingle/callutils.py0000644000175000017500000000541312227000321023450 0ustar00smcvsmcv00000000000000""" Utilities for the call channel class """ import dbus from dbus.exceptions import DBusException from twisted.words.xish import xpath from gabbletest import exec_test from servicetest import ( make_channel_proxy, wrap_channel, EventPattern, call_async, assertEquals, assertContains, assertLength, assertNotEquals ) import constants as cs from jingletest2 import JingleTest2, test_all_dialects import ns from mucutil import echo_muc_presence def check_state (q, chan, state, wait = False): if wait: q.expect('dbus-signal', signal='CallStateChanged', interface = cs.CHANNEL_TYPE_CALL, predicate = lambda e: e.args[0] == state) properties = chan.GetAll(cs.CHANNEL_TYPE_CALL, dbus_interface=dbus.PROPERTIES_IFACE) assertEquals (state, properties["CallState"]) def check_and_accept_offer (q, bus, conn, content, md, in_remote_handle = 0, offer_path = None, md_changed = True): [path, remote_md] = content.Get(cs.CALL_CONTENT_IFACE_MEDIA, "MediaDescriptionOffer", dbus_interface=dbus.PROPERTIES_IFACE) remote_handle = remote_md[cs.CALL_CONTENT_MEDIADESCRIPTION + '.RemoteContact'] if in_remote_handle != 0: assertEquals(remote_handle, in_remote_handle) if offer_path != None: assertEquals (offer_path, path) assertNotEquals ("/", path) offer = bus.get_object (conn.bus_name, path) codecmap_property = offer.Get (cs.CALL_CONTENT_MEDIADESCRIPTION, "Codecs", dbus_interface=dbus.PROPERTIES_IFACE) assertEquals (remote_md[cs.CALL_CONTENT_MEDIADESCRIPTION + '.Codecs'], codecmap_property) offer.Accept (md, dbus_interface=cs.CALL_CONTENT_MEDIADESCRIPTION) current_md = content.Get(cs.CALL_CONTENT_IFACE_MEDIA, "LocalMediaDescriptions", dbus_interface=dbus.PROPERTIES_IFACE) assertEquals (md, current_md[remote_handle]) if md_changed: o = q.expect ('dbus-signal', signal='LocalMediaDescriptionChanged') assertEquals ([md], o.args) def no_muji_presences (muc): return EventPattern ('stream-presence', to = muc + "/test", predicate = lambda x: xpath.queryForNodes("/presence/muji", x.stanza)) def create_muji_channel (q, conn, stream, muc, in_muc = False): call_async (q, conn.Requests, 'CreateChannel', { cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_CALL, cs.TARGET_HANDLE_TYPE: cs.HT_ROOM, cs.TARGET_ID: muc, cs.CALL_INITIAL_AUDIO: True, cs.CALL_INITIAL_AUDIO_NAME: "Audio", }, byte_arrays = True) if not in_muc: e = q.expect('stream-presence', to = muc + "/test") echo_muc_presence (q, stream, e.stanza, 'none', 'participant') e = q.expect ('dbus-return', method='CreateChannel') return e.value telepathy-gabble-0.18.2/tests/twisted/jingle/call_helper.py0000644000175000017500000010231312312320035023725 0ustar00smcvsmcv00000000000000""" Base classes for Call tests """ import config if not config.CHANNEL_TYPE_CALL_ENABLED: print "NOTE: built with --disable-channel-type-call" raise SystemExit(77) import dbus from dbus.exceptions import DBusException from functools import partial from servicetest import ( EventPattern, call_async, wrap_channel, wrap_content, assertEquals, assertDoesNotContain, assertContains, assertLength, assertNotEquals, DictionarySupersetOf) from gabbletest import sync_stream, make_result_iq from jingletest2 import JingleTest2, test_all_dialects import constants as cs import ns from config import VOIP_ENABLED if not VOIP_ENABLED: print "NOTE: built with --disable-voip" raise SystemExit(77) class CallTest(object): SELF_JID = 'test@localhost' PEER_JID = 'foo@bar.com/Foo' # These can be changed as needed by base class initial_audio = True initial_video = False # The following will be set after initiate_call() chan = None audio_content = None audio_content_name = None audio_stream = None video_content = None video_content_name = None video_stream = None def __init__(self, jp, q, bus, conn, stream, incoming, params): self.jp = jp self.q = q self.bus = bus self.conn = conn self.stream = stream self.incoming = incoming self.params = params self.jt2 = JingleTest2(jp, conn, q, stream, self.SELF_JID, self.PEER_JID) self.can_change_direction = (jp.dialect not in ['gtalk-v0.3', 'gtalk-v0.4']) self.self_handle = conn.GetSelfHandle() self.peer_handle = conn.RequestHandles(1, ["foo@bar.com/Foo"])[0] def check_channel_state(self, state, wait = False): """Optionnally wait for channel state to be reached and check that the property has the right value""" if wait: self.q.expect('dbus-signal', signal='CallStateChanged', interface = cs.CHANNEL_TYPE_CALL, predicate = lambda e: e.args[0] == state) assertEquals(state, self.chan.Get(cs.CHANNEL_TYPE_CALL, 'CallState', dbus_interface=dbus.PROPERTIES_IFACE)) def check_stream_recv_state(self, stream, state): assertEquals(state, stream.Get(cs.CALL_STREAM_IFACE_MEDIA, 'ReceivingState', dbus_interface=dbus.PROPERTIES_IFACE)) def check_stream_send_state(self, stream, state): assertEquals(state, stream.Get(cs.CALL_STREAM_IFACE_MEDIA, 'SendingState', dbus_interface=dbus.PROPERTIES_IFACE)) def complete_receiving_state(self, stream): if stream is None: return self.check_stream_recv_state(stream, cs.CALL_STREAM_FLOW_STATE_PENDING_START) stream.CompleteReceivingStateChange(cs.CALL_STREAM_FLOW_STATE_STARTED, dbus_interface = cs.CALL_STREAM_IFACE_MEDIA) self.q.expect('dbus-signal', signal='ReceivingStateChanged', args = [cs.CALL_STREAM_FLOW_STATE_STARTED], interface = cs.CALL_STREAM_IFACE_MEDIA) def check_and_accept_offer(self, content, md, md_changed = True, offer_path = None): [path, remote_md] = content.Get(cs.CALL_CONTENT_IFACE_MEDIA, "MediaDescriptionOffer", dbus_interface=dbus.PROPERTIES_IFACE) if offer_path != None: assertEquals(offer_path, path) assertNotEquals("/", path) offer = self.bus.get_object(self.conn.bus_name, path) codecmap_property = offer.Get(cs.CALL_CONTENT_MEDIADESCRIPTION, "Codecs", dbus_interface=dbus.PROPERTIES_IFACE) assertEquals(remote_md[cs.CALL_CONTENT_MEDIADESCRIPTION + '.Codecs'], codecmap_property) offer.Accept(md, dbus_interface=cs.CALL_CONTENT_MEDIADESCRIPTION) current_md = content.Get(cs.CALL_CONTENT_IFACE_MEDIA, "LocalMediaDescriptions", dbus_interface=dbus.PROPERTIES_IFACE) assertEquals(md, current_md[self.peer_handle]) if md_changed: o = self.q.expect('dbus-signal', signal='LocalMediaDescriptionChanged') assertEquals([md], o.args) def store_content(self, content_path, initial = True, incoming = None): if incoming is None: incoming = self.incoming content = wrap_content(self.bus.get_object(self.conn.bus_name, content_path), ['DTMF', 'Media']) content_props = content.GetAll(cs.CALL_CONTENT, dbus_interface=dbus.PROPERTIES_IFACE) # Has one stream assertLength(1, content_props["Streams"]) if initial: assertEquals(cs.CALL_DISPOSITION_INITIAL, content_props["Disposition"]) else: assertEquals(cs.CALL_DISPOSITION_NONE, content_props["Disposition"]) # Implements Content.Interface.Media assertContains(cs.CALL_CONTENT_IFACE_MEDIA, content_props["Interfaces"]) if content_props['Type'] == cs.CALL_MEDIA_TYPE_AUDIO: # Implements Content.Interface.DTMF assertContains(cs.CALL_CONTENT_IFACE_DTMF, content_props["Interfaces"]) assertContains("Name", content_props.keys()) content_name = content_props["Name"] stream = self.bus.get_object(self.conn.bus_name, content_props["Streams"][0]) stream_props = stream.GetAll(cs.CALL_STREAM, dbus_interface = dbus.PROPERTIES_IFACE) assertDoesNotContain(self.self_handle, stream_props["RemoteMembers"].keys()) assertContains(self.peer_handle, stream_props["RemoteMembers"].keys()) assertEquals([cs.CALL_STREAM_IFACE_MEDIA], stream_props["Interfaces"]) assertEquals(self.can_change_direction, stream_props["CanRequestReceiving"]) if incoming: assertEquals(cs.CALL_SENDING_STATE_PENDING_SEND, stream_props["LocalSendingState"]) assertEquals(cs.CALL_SENDING_STATE_SENDING, stream_props["RemoteMembers"][self.peer_handle]) else: if initial: assertEquals(cs.CALL_SENDING_STATE_PENDING_SEND, stream_props["RemoteMembers"][self.peer_handle]) else: assertEquals(cs.CALL_SENDING_STATE_SENDING, stream_props["RemoteMembers"][self.peer_handle]) assertEquals(cs.CALL_SENDING_STATE_SENDING, stream_props["LocalSendingState"]) # Packetization should be RTP content_media_props = content.GetAll(cs.CALL_CONTENT_IFACE_MEDIA, dbus_interface=dbus.PROPERTIES_IFACE) assertEquals(cs.CALL_CONTENT_PACKETIZATION_RTP, content_media_props["Packetization"]) # Check the directions stream_media_props = stream.GetAll(cs.CALL_STREAM_IFACE_MEDIA, dbus_interface=dbus.PROPERTIES_IFACE) if initial or incoming: assertEquals(cs.CALL_STREAM_FLOW_STATE_STOPPED, stream_media_props["SendingState"]) else: assertEquals(cs.CALL_STREAM_FLOW_STATE_PENDING_START, stream_media_props["SendingState"]) if initial: assertEquals(cs.CALL_STREAM_FLOW_STATE_STOPPED, stream_media_props["ReceivingState"]) else: assertEquals(cs.CALL_STREAM_FLOW_STATE_PENDING_START, stream_media_props["ReceivingState"]) assertEquals(False, stream_media_props["ICERestartPending"]) # Store the content and stream if content_props['Type'] == cs.CALL_MEDIA_TYPE_AUDIO: assert self.initial_audio == initial assert self.audio_content == None assert self.audio_stream == None self.audio_content = content self.audio_content_name = content_name self.audio_stream = stream elif content_props['Type'] == cs.CALL_MEDIA_TYPE_VIDEO: assert self.initial_video == initial assert self.video_content == None assert self.video_stream == None self.video_content = content self.video_content_name = content_name self.video_stream = stream else: assert not 'Bad content type value' def enable_endpoint(self, endpoint): endpoint.SetEndpointState(cs.CALL_STREAM_COMPONENT_DATA, cs.CALL_STREAM_ENDPOINT_STATE_FULLY_CONNECTED, dbus_interface=cs.CALL_STREAM_ENDPOINT) self.q.expect('dbus-signal', signal='EndpointStateChanged', interface=cs.CALL_STREAM_ENDPOINT) endpoint.SetEndpointState(cs.CALL_STREAM_COMPONENT_CONTROL, cs.CALL_STREAM_ENDPOINT_STATE_FULLY_CONNECTED, dbus_interface=cs.CALL_STREAM_ENDPOINT) self.q.expect('dbus-signal', signal='EndpointStateChanged', interface=cs.CALL_STREAM_ENDPOINT) state = endpoint.Get(cs.CALL_STREAM_ENDPOINT, "EndpointState", dbus_interface=dbus.PROPERTIES_IFACE) assertEquals(cs.CALL_STREAM_ENDPOINT_STATE_FULLY_CONNECTED, state[1]) assertEquals(cs.CALL_STREAM_ENDPOINT_STATE_FULLY_CONNECTED, state[2]) def advertise(self, initial_audio = True, initial_video = True): """Advertise that Call is supported""" self.conn.ContactCapabilities.UpdateCapabilities([ (cs.CLIENT + ".CallHandler", [ { cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_CALL, cs.CALL_INITIAL_AUDIO: initial_audio}, { cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_CALL, cs.CALL_INITIAL_VIDEO: initial_video}, ], [ cs.CHANNEL_TYPE_CALL + '/gtalk-p2p', cs.CHANNEL_TYPE_CALL + '/ice', cs.CHANNEL_TYPE_CALL + '/video/h264', ]), ]) def prepare(self, events=None): """Prepare the JingleTest2 object. This method can be override to trap special event linke jingleinfo.""" self.jt2.prepare(events=events) def initiate(self): """Brind the call to INITIALISING state. This method will fill the channel, contents and streams members.""" # Ensure a channel that doesn't exist yet. if self.incoming: if self.initial_audio and self.initial_video: self.jt2.incoming_call(audio='audio1', video='video1') elif self.initial_audio: self.jt2.incoming_call(audio='audio1', video=None) else: self.jt2.incoming_call(audio=None, video='video1') else: ret = self.conn.Requests.CreateChannel( { cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_CALL, cs.TARGET_HANDLE_TYPE: cs.HT_CONTACT, cs.TARGET_HANDLE: self.peer_handle, cs.CALL_INITIAL_AUDIO: self.initial_audio, cs.CALL_INITIAL_VIDEO: self.initial_video, }) signal = self.q.expect('dbus-signal', signal='NewChannels', predicate=lambda e: cs.CHANNEL_TYPE_CONTACT_LIST not in e.args[0][0][1].values()) assertLength(1, signal.args) assertLength(1, signal.args[0]) # one channel assertLength(2, signal.args[0][0]) # two struct members emitted_props = signal.args[0][0][1] assertEquals( cs.CHANNEL_TYPE_CALL, emitted_props[cs.CHANNEL_TYPE]) peer_bare_jid = self.PEER_JID.split('/')[0] assertEquals(self.peer_handle, emitted_props[cs.TARGET_HANDLE]) assertEquals(cs.HT_CONTACT, emitted_props[cs.TARGET_HANDLE_TYPE]) assertEquals(peer_bare_jid, emitted_props[cs.TARGET_ID]) assertEquals(not self.incoming, emitted_props[cs.REQUESTED]) if self.incoming: assertEquals(self.peer_handle, emitted_props[cs.INITIATOR_HANDLE]) assertEquals(peer_bare_jid, emitted_props[cs.INITIATOR_ID]) else: assertEquals(self.self_handle, emitted_props[cs.INITIATOR_HANDLE]) assertEquals(self.SELF_JID, emitted_props[cs.INITIATOR_ID]) assertEquals(self.initial_audio, emitted_props[cs.CALL_INITIAL_AUDIO]) assertEquals(self.initial_video, emitted_props[cs.CALL_INITIAL_VIDEO]) chan_path = signal.args[0][0][0] self.chan = wrap_channel( self.bus.get_object(self.conn.bus_name, chan_path), 'Call', ['Hold']) properties = self.chan.GetAll(cs.CHANNEL_TYPE_CALL, dbus_interface=dbus.PROPERTIES_IFACE) # Check if all the properties are there assertEquals(sorted([ "Contents", "CallMembers", "CallState", "CallFlags", "CallStateReason", "CallStateDetails", "HardwareStreaming", "InitialAudio", "InitialAudioName", "InitialVideo", "InitialVideoName", "MutableContents", "InitialTransport", "MemberIdentifiers" ]), sorted(properties.keys())) # Remote member is the target assertEquals([self.peer_handle], properties["CallMembers"].keys()) assertEquals(0, properties["CallMembers"][self.peer_handle]) # No Hardware Streaming for you assertEquals(False, properties["HardwareStreaming"]) # Store content and stream nb_contents = self.initial_audio + self.initial_video assertLength(nb_contents, properties["Contents"]) for content_path in properties["Contents"]: self.store_content(content_path) if self.initial_audio: assert self.audio_content if self.initial_video: assert self.video_content def accept_outgoing(self): """If call is incoming, accept the channel and complete the receiving state change. Then do state check. This method shall be called even if receiving a call to execute the state sanity checks.""" # Check if the channel is in the right pending state if not self.incoming: self.check_channel_state(cs.CALL_STATE_PENDING_INITIATOR) self.chan.Accept(dbus_interface=cs.CHANNEL_TYPE_CALL) if self.initial_audio: self.complete_receiving_state(self.audio_stream) # Don't start sending before the call is accepted locally or # remotely self.check_stream_send_state(self.audio_stream, cs.CALL_STREAM_FLOW_STATE_STOPPED) if self.initial_video: self.complete_receiving_state(self.video_stream) self.check_stream_send_state(self.video_stream, cs.CALL_STREAM_FLOW_STATE_STOPPED) # All Direction should be both now for outgoing if self.initial_audio: stream_props = self.audio_stream.GetAll(cs.CALL_STREAM, dbus_interface = dbus.PROPERTIES_IFACE) if self.incoming: assertEquals({self.peer_handle: cs.CALL_SENDING_STATE_SENDING}, stream_props["RemoteMembers"]) assertEquals(cs.CALL_SENDING_STATE_PENDING_SEND, stream_props["LocalSendingState"]) else: assertEquals( {self.peer_handle: cs.CALL_SENDING_STATE_PENDING_SEND}, stream_props["RemoteMembers"]) assertEquals(cs.CALL_SENDING_STATE_SENDING, stream_props["LocalSendingState"]) if self.initial_video: stream_props = self.video_stream.GetAll(cs.CALL_STREAM, dbus_interface = dbus.PROPERTIES_IFACE) if self.incoming: assertEquals({self.peer_handle: cs.CALL_SENDING_STATE_SENDING}, stream_props["RemoteMembers"]) assertEquals(cs.CALL_SENDING_STATE_PENDING_SEND, stream_props["LocalSendingState"]) else: assertEquals( {self.peer_handle: cs.CALL_SENDING_STATE_PENDING_SEND}, stream_props["RemoteMembers"]) assertEquals(cs.CALL_SENDING_STATE_SENDING, stream_props["LocalSendingState"]) self.check_channel_state(cs.CALL_STATE_INITIALISING) def connect_streams(self, contents, streams, mds, expect_after_si=None): # Expected to fail since we did not said we are controlling side try: contents[0].UpdateLocalMediaDescription(mds[0], dbus_interface=cs.CALL_CONTENT_IFACE_MEDIA) except DBusException, e: if e.get_dbus_name() != cs.NOT_AVAILABLE: raise e else: assert False expected = [] candidates = self.jt2.get_call_remote_transports_dbus() for i in range(len(contents)): self.check_and_accept_offer(contents[i], mds[i], md_changed=False) expected.append(EventPattern('dbus-signal', signal='LocalMediaDescriptionChanged', args=[mds[i]])) current_md = contents[i].Get(cs.CALL_CONTENT_IFACE_MEDIA, "LocalMediaDescriptions", dbus_interface=dbus.PROPERTIES_IFACE) assertEquals(mds[i], current_md[self.peer_handle]) streams[i].SetCredentials(self.jt2.ufrag, self.jt2.pwd, dbus_interface=cs.CALL_STREAM_IFACE_MEDIA) expected.append(EventPattern('dbus-signal', signal='LocalCredentialsChanged', args=[self.jt2.ufrag, self.jt2.pwd])) credentials = streams[i].GetAll(cs.CALL_STREAM_IFACE_MEDIA, dbus_interface=dbus.PROPERTIES_IFACE)["LocalCredentials"] assertEquals((self.jt2.ufrag, self.jt2.pwd), credentials) # Add candidates streams[i].AddCandidates(candidates, dbus_interface=cs.CALL_STREAM_IFACE_MEDIA) expected.append(EventPattern('dbus-signal', signal='LocalCandidatesAdded')) if not self.incoming: expected.append(EventPattern('stream-iq', predicate=self.jp.action_predicate('session-initiate'))) ret = self.q.expect_many(*expected) # Check the first LocalCandidatesAdded signal (third in the array) assertEquals(candidates, ret[2].args[0]) if not self.incoming: if expect_after_si is not None: sync_stream(self.q, self.stream) self.q.unforbid_events(expect_after_si) self.stream.send(make_result_iq(self.stream, ret[-1].stanza)) if expect_after_si is not None: self.q.expect_many(*expect_after_si) self.jt2.parse_session_initiate(ret[-1].query) endpoints = [] for stream in streams: stream.FinishInitialCandidates( dbus_interface=cs.CALL_STREAM_IFACE_MEDIA) local_candidates = stream.Get(cs.CALL_STREAM_IFACE_MEDIA, "LocalCandidates", dbus_interface=dbus.PROPERTIES_IFACE) assertEquals(candidates, local_candidates) endpoint_paths = stream.Get(cs.CALL_STREAM_IFACE_MEDIA, "Endpoints", dbus_interface=dbus.PROPERTIES_IFACE) assertLength(1, endpoint_paths) # There doesn't seem to be a good way to get the transport type from # the JP used, for now assume we prefer gtalk p2p and always pick # that.. transport = stream.Get(cs.CALL_STREAM_IFACE_MEDIA, "Transport", dbus_interface=dbus.PROPERTIES_IFACE) assertEquals(cs.CALL_STREAM_TRANSPORT_GTALK_P2P, transport) endpoint = self.bus.get_object(self.conn.bus_name, endpoint_paths[0]) endpoints.append(endpoint) endpoint_props = endpoint.GetAll(cs.CALL_STREAM_ENDPOINT, dbus_interface=dbus.PROPERTIES_IFACE) transport = endpoint_props["Transport"] assertEquals(cs.CALL_STREAM_TRANSPORT_GTALK_P2P, transport) remote_candidates = endpoint.Get(cs.CALL_STREAM_ENDPOINT, "RemoteCandidates", dbus_interface=dbus.PROPERTIES_IFACE) assertEquals([], remote_candidates) selected_candidate = endpoint.Get(cs.CALL_STREAM_ENDPOINT, "SelectedCandidatePairs", dbus_interface=dbus.PROPERTIES_IFACE) assertEquals([], selected_candidate) state = endpoint.Get(cs.CALL_STREAM_ENDPOINT, "EndpointState", dbus_interface=dbus.PROPERTIES_IFACE) assertEquals({}, state) names = [] for content in contents: if content is self.audio_content: names.append(self.jt2.audio_names[0]) else: names.append(self.jt2.video_names[0]) for name in names: if self.jp.dialect == 'gtalk-v0.3': # Candidates must be sent one at a time. for candidate in self.jt2.get_call_remote_transports_dbus(): component, addr, port, props = candidate self.jt2.send_remote_candidates_call_xmpp( name, "initiator", [candidate]) self.q.expect('dbus-signal', signal='RemoteCandidatesAdded', interface=cs.CALL_STREAM_ENDPOINT, args=[[(component, addr, port, DictionarySupersetOf(props))]]) elif self.jp.dialect == 'gtalk-v0.4' and not self.incoming: # Don't test this case at all. pass else: self.jt2.send_remote_candidates_call_xmpp(name, "initiator") candidates = [] for component, addr, port, props in \ self.jt2.get_call_remote_transports_dbus(): candidates.append((component, addr, port, DictionarySupersetOf(props))) self.q.expect('dbus-signal', signal='RemoteCandidatesAdded', interface=cs.CALL_STREAM_ENDPOINT, args=[candidates]) # FIXME: makes sense to have same local and remote candidate? candidate1 = self.jt2.get_call_remote_transports_dbus()[0] candidate2 = self.jt2.get_call_remote_transports_dbus()[1] for endpoint in endpoints: # Expected to fail since we did not said we are controlling side try: endpoint.SetSelectedCandidatePair(candidate1, candidate1, dbus_interface=cs.CALL_STREAM_ENDPOINT) except DBusException, e: if e.get_dbus_name() != cs.INVALID_ARGUMENT: raise e else: assert false endpoint.SetControlling(True, dbus_interface=cs.CALL_STREAM_ENDPOINT) endpoint.SetSelectedCandidatePair(candidate1, candidate1, dbus_interface=cs.CALL_STREAM_ENDPOINT) pair = self.q.expect('dbus-signal', signal='CandidatePairSelected', interface=cs.CALL_STREAM_ENDPOINT) assertEquals(candidate1, pair.args[0]) assertEquals(candidate1, pair.args[1]) endpoint.SetSelectedCandidatePair(candidate2, candidate2, dbus_interface=cs.CALL_STREAM_ENDPOINT) # We have an RTCP candidate as well, so we should set this as # selected too. pair = self.q.expect('dbus-signal', signal='CandidatePairSelected', interface=cs.CALL_STREAM_ENDPOINT) assertEquals(candidate2, pair.args[0]) assertEquals(candidate2, pair.args[1]) pairs = endpoint.Get(cs.CALL_STREAM_ENDPOINT, "SelectedCandidatePairs", dbus_interface=dbus.PROPERTIES_IFACE) assertEquals(len(pairs), 2) assertEquals(pairs[0][0], pairs[0][1]) assertEquals(pairs[1][0], pairs[1][1]) if pairs[0][0] == candidate1: assertEquals(pairs[1][0], candidate2) else: assertEquals(pairs[0][0], candidate2) assertEquals(pairs[1][0], candidate1) # setting endpoints to CONNECTED should make the call state move # from INITIALISING to INITIALISED self.enable_endpoint(endpoint) self.check_channel_state(cs.CALL_STATE_INITIALISED) def connect(self, expect_after_si=None): """Negotiate all the codecs, bringing the channel to INITIALISED state""" contents = [] streams = [] mds = [] if self.initial_audio: # Setup media description contents.append(self.audio_content) streams.append(self.audio_stream) mds.append(self.jt2.get_call_audio_md_dbus(self.peer_handle)) if self.initial_video: contents.append(self.video_content) streams.append(self.video_stream) mds.append(self.jt2.get_call_video_md_dbus(self.peer_handle)) self.connect_streams(contents, streams, mds, expect_after_si=expect_after_si) def pickup(self, held=False): if self.initial_audio: self.check_stream_send_state(self.audio_stream, cs.CALL_STREAM_FLOW_STATE_STOPPED) if self.initial_video: self.check_stream_send_state(self.video_stream, cs.CALL_STREAM_FLOW_STATE_STOPPED) if self.incoming: # Act as if we're ringing self.chan.SetRinging(dbus_interface=cs.CHANNEL_TYPE_CALL) signal = self.q.expect('dbus-signal', signal='CallStateChanged') assertEquals(cs.CALL_FLAG_LOCALLY_RINGING, signal.args[1] & cs.CALL_FLAG_LOCALLY_RINGING) # And now pickup the call self.chan.Accept(dbus_interface=cs.CHANNEL_TYPE_CALL) expected = [ EventPattern('dbus-signal', signal='CallStateChanged'), EventPattern('stream-iq', predicate=self.jp.action_predicate('session-accept'))] if self.initial_audio: # SendingStateChanged is caused by chan.Accept expected.append(EventPattern('dbus-signal', signal='SendingStateChanged')) recv_state = self.audio_stream.GetAll( cs.CALL_STREAM_IFACE_MEDIA, dbus_interface=dbus.PROPERTIES_IFACE)["ReceivingState"] assertEquals(cs.CALL_STREAM_FLOW_STATE_PENDING_START, recv_state) self.audio_stream.CompleteReceivingStateChange( cs.CALL_STREAM_FLOW_STATE_STARTED, dbus_interface = cs.CALL_STREAM_IFACE_MEDIA) expected.append(EventPattern('dbus-signal', signal='ReceivingStateChanged')) if self.initial_video: # SendingStateChanged is caused by chan.Accept expected.append(EventPattern('dbus-signal', signal='SendingStateChanged')) recv_state = self.video_stream.GetAll( cs.CALL_STREAM_IFACE_MEDIA, dbus_interface=dbus.PROPERTIES_IFACE)["ReceivingState"] assertEquals(cs.CALL_STREAM_FLOW_STATE_PENDING_START, recv_state) self.video_stream.CompleteReceivingStateChange( cs.CALL_STREAM_FLOW_STATE_STARTED, dbus_interface = cs.CALL_STREAM_IFACE_MEDIA) expected.append(EventPattern('dbus-signal', signal='ReceivingStateChanged')) ret = self.q.expect_many(*expected) assertEquals(0, ret[0].args[1] & cs.CALL_FLAG_LOCALLY_RINGING) if self.initial_audio and self.initial_video: assertEquals(cs.CALL_STREAM_FLOW_STATE_PENDING_START, ret[2].args[0]) assertEquals(cs.CALL_STREAM_FLOW_STATE_PENDING_START, ret[3].args[0]) else: assertEquals(cs.CALL_STREAM_FLOW_STATE_PENDING_START, ret[2].args[0]) self.jt2.result_iq(ret[1]) else: if self.jp.is_modern_jingle(): # The other person's client starts ringing, and tells us so! node = self.jp.SetIq(self.jt2.peer, self.jt2.jid, [ self.jp.Jingle(self.jt2.sid, self.jt2.jid, 'session-info', [ ('ringing', ns.JINGLE_RTP_INFO_1, {}, []) ]) ]) self.stream.send(self.jp.xml(node)) o = self.q.expect('dbus-signal', signal="CallMembersChanged") assertEquals(cs.CALL_MEMBER_FLAG_RINGING, o.args[0][self.peer_handle]) self.jt2.accept() expected = [EventPattern('dbus-signal', signal='NewMediaDescriptionOffer')] if not held: if self.initial_audio: expected.append(EventPattern('dbus-signal', signal='SendingStateChanged')) if self.initial_video: expected.append(EventPattern('dbus-signal', signal='SendingStateChanged')) ret = self.q.expect_many(*expected) if not held: # Checking one of sending states assertEquals(cs.CALL_STREAM_FLOW_STATE_PENDING_START, ret[1].args[0]) if self.initial_audio: md = self.jt2.get_call_audio_md_dbus(self.peer_handle) self.check_and_accept_offer(self.audio_content, md, md_changed = False) if self.initial_video: md = self.jt2.get_call_video_md_dbus(self.peer_handle) self.check_and_accept_offer(self.video_content, md, md_changed = False) self.check_channel_state(cs.CALL_STATE_ACTIVE) # All Direction should be both sending now if self.initial_audio and not held: stream_props = self.audio_stream.GetAll(cs.CALL_STREAM, dbus_interface = dbus.PROPERTIES_IFACE) assertEquals({self.peer_handle: cs.CALL_SENDING_STATE_SENDING}, stream_props["RemoteMembers"]) assertEquals(cs.CALL_SENDING_STATE_SENDING, stream_props["LocalSendingState"]) assertEquals(cs.CALL_STREAM_FLOW_STATE_PENDING_START, self.audio_stream.Get(cs.CALL_STREAM_IFACE_MEDIA, "SendingState", dbus_interface = dbus.PROPERTIES_IFACE)) self.audio_stream.CompleteSendingStateChange( cs.CALL_STREAM_FLOW_STATE_STARTED, dbus_interface = cs.CALL_STREAM_IFACE_MEDIA) o = self.q.expect('dbus-signal', signal='SendingStateChanged', interface = cs.CALL_STREAM_IFACE_MEDIA) assertEquals(cs.CALL_STREAM_FLOW_STATE_STARTED, o.args[0]) if self.initial_video and not held: stream_props = self.video_stream.GetAll(cs.CALL_STREAM, dbus_interface = dbus.PROPERTIES_IFACE) assertEquals({self.peer_handle: cs.CALL_SENDING_STATE_SENDING}, stream_props["RemoteMembers"]) assertEquals(cs.CALL_SENDING_STATE_SENDING, stream_props["LocalSendingState"]) assertEquals(cs.CALL_STREAM_FLOW_STATE_PENDING_START, self.video_stream.Get(cs.CALL_STREAM_IFACE_MEDIA, "SendingState", dbus_interface = dbus.PROPERTIES_IFACE)) self.video_stream.CompleteSendingStateChange( cs.CALL_STREAM_FLOW_STATE_STARTED, dbus_interface = cs.CALL_STREAM_IFACE_MEDIA) o = self.q.expect('dbus-signal', signal='SendingStateChanged', interface = cs.CALL_STREAM_IFACE_MEDIA) assertEquals(cs.CALL_STREAM_FLOW_STATE_STARTED, o.args[0]) def hangup(self): if self.incoming: self.jt2.terminate() else: self.chan.Hangup(0, "", "", dbus_interface=cs.CHANNEL_TYPE_CALL) self.check_channel_state(cs.CALL_STATE_ENDED, wait = True) def run(self): if self.initial_video: if not self.initial_audio and not self.jp.can_do_video_only(): return elif not self.jp.can_do_video(): return self.advertise() self.prepare() self.initiate() self.accept_outgoing() self.connect() self.pickup() self.hangup() def run_call_test(jp, q, bus, conn, stream, klass=CallTest, incoming=False, params={}): test = klass(jp, q, bus, conn, stream, incoming, params) test.run() if __name__ == '__main__': test_all_dialects(partial(run_call_test, incoming=False)) test_all_dialects(partial(run_call_test, incoming=True)) telepathy-gabble-0.18.2/tests/twisted/jingle/unknown-session.py0000644000175000017500000000224312200204333024632 0ustar00smcvsmcv00000000000000""" Tests that Gabble doesn't explode if it gets Jingle stanzas for unknown sessions. """ from gabbletest import exec_test from servicetest import assertEquals from jingletest2 import JingleProtocol031 import ns def assertHasChild(node, uri, name): try: node.elements(uri=uri, name=name).next() except StopIteration: raise AssertionError( "Expected <%s xmlns='%s'> to be a child of\n %s" % ( name, uri, node.toXml())) def test_send_action_for_unknown_session(q, bus, conn, stream): jp = JingleProtocol031() peer = 'guybrush@threepwo.od' iq = jp.SetIq(peer, 'test@localhost', [ jp.Jingle('fine-leather-jackets', peer, 'session-info', []) ]) stream.send(jp.xml(iq)) e = q.expect('stream-iq', iq_type='error', iq_id=iq[2]['id']) stanza = e.stanza error_node = stanza.children[-1] assertEquals('error', error_node.name) # http://xmpp.org/extensions/xep-0166.html#example-29 assertHasChild(error_node, ns.STANZA, 'item-not-found') assertHasChild(error_node, ns.JINGLE_ERRORS, 'unknown-session') if __name__ == '__main__': exec_test(test_send_action_for_unknown_session) telepathy-gabble-0.18.2/tests/twisted/jingle/transport-info-parsing.py0000644000175000017500000000511612227000321026102 0ustar00smcvsmcv00000000000000""" Test whether we parse the transport info with multiple contents correctly """ from gabbletest import exec_test from servicetest import ( make_channel_proxy, EventPattern, assertEquals, assertNotEquals ) from jingletest2 import * from config import VOIP_ENABLED if not VOIP_ENABLED: print "NOTE: built with --disable-voip" raise SystemExit(77) def test(q, bus, conn, stream, peer='foo@bar.com/Foo'): jp = JingleProtocol031() jt = JingleTest2(jp, conn, q, stream, 'test@localhost', peer) jt.prepare() # Remote end calls us jt.incoming_call(audio = "Audio", video = "Video") e = q.expect ('dbus-signal', signal='NewSessionHandler') handler = make_channel_proxy(conn, e.args[0], 'Media.SessionHandler') handler.Ready() events = q.expect_many ( EventPattern('dbus-signal', signal='NewStreamHandler'), EventPattern('dbus-signal', signal='NewStreamHandler') ) for e in events: handler = make_channel_proxy(conn, e.args[0], 'Media.StreamHandler') handler.Ready([]) candidate0 = ( "1.2.3.4", # host 666, # port 0, # protocol = TP_MEDIA_STREAM_BASE_PROTO_UDP "RTP", # protocol subtype "AVP", # profile 1.0, # preference 0, # transport type = TP_MEDIA_STREAM_TRANSPORT_TYPE_LOCAL, "username", "password" ) candidate1 = ( "5.6.7.8", # host 999, # port 0, # protocol = TP_MEDIA_STREAM_BASE_PROTO_UDP "RTP", # protocol subtype "AVP", # profile 1.0, # preference 0, # transport type = TP_MEDIA_STREAM_TRANSPORT_TYPE_LOCAL, "username", "password" ) node = jp.SetIq(jt.peer, jt.jid, [ jp.Jingle(jt.sid, jt.peer, 'transport-info', [ jp.Content('Audio', 'initiator', 'both', transport = jp.TransportGoogleP2P([candidate0])), jp.Content('Video', 'initiator', 'both', transport = jp.TransportGoogleP2P([candidate1])), ] ) ]) stream.send(jp.xml(node)) q.expect ('stream-iq', iq_type='result') (c0, c1) = q.expect_many( EventPattern('dbus-signal', signal='AddRemoteCandidate'), EventPattern('dbus-signal', signal='AddRemoteCandidate')) assertNotEquals(c0.path, c1.path) mapping = { 666: candidate0, 999: candidate1} # Candidate without component number to compare candidate = c0.args[1][0][1:] assertEquals(mapping[candidate[1]], candidate) candidate = c1.args[1][0][1:] assertEquals(mapping[candidate[1]], candidate) if __name__ == '__main__': exec_test(test) telepathy-gabble-0.18.2/tests/twisted/jingle/test-wait-for-caps.py0000644000175000017500000000671112227000321025107 0ustar00smcvsmcv00000000000000""" Test use-case when client requests going online and immediately attempts to call a contact. Gabble should delay the RequestStreams call until caps have arrived. """ from functools import partial from gabbletest import exec_test from servicetest import make_channel_proxy, call_async, sync_dbus import jingletest2 import dbus import constants as cs from config import VOIP_ENABLED if not VOIP_ENABLED: print "NOTE: built with --disable-voip" raise SystemExit(77) def test(q, bus, conn, stream, channel_type): jp = jingletest2.JingleProtocol031() jt = jingletest2.JingleTest2(jp, conn, q, stream, 'test@localhost', 'foo@bar.com/Foo') jt2 = jingletest2.JingleTest2(jp, conn, q, stream, 'test@localhost', 'foo2@bar.com/Foo') # Make gabble think this is a different client jt2.remote_caps['node'] = 'http://example.com/fake-client1' run_test(q, bus, conn, stream, jt, True, channel_type) run_test(q, bus, conn, stream, jt2, False, channel_type) def run_test(q, bus, conn, stream, jt, request_before_presence, channel_type): """ Requests streams on a media channel to jt.peer, either before their presence is received (if request_before_presence is True) or after their presence is received but before we've got a disco response for their capabilities (otherwise). """ # We intentionally DON'T set remote presence yet. Since Gabble is still # unsure whether to treat contact as offline for this purpose, it # will tentatively allow channel creation and contact handle addition request = dbus.Dictionary({ cs.CHANNEL_TYPE: channel_type, cs.TARGET_HANDLE_TYPE: cs.HT_CONTACT, cs.TARGET_ID: jt.peer, }, signature='sv') if channel_type == cs.CHANNEL_TYPE_CALL: request[cs.CALL_INITIAL_AUDIO] = True if channel_type == cs.CHANNEL_TYPE_STREAMED_MEDIA: path, props = conn.CreateChannel(request, dbus_interface=cs.CONN_IFACE_REQUESTS) media_iface = make_channel_proxy(conn, path, 'Channel.Type.StreamedMedia') handle = props[cs.TARGET_HANDLE] sync_dbus(bus, q, conn) def call_request_streams(): if channel_type == cs.CHANNEL_TYPE_STREAMED_MEDIA: call_async(q, media_iface, 'RequestStreams', handle, [cs.MEDIA_STREAM_TYPE_AUDIO]) else: call_async(q, conn.Requests, 'CreateChannel', request) if request_before_presence: # Request streams before either or caps have arrived. Gabble # should wait for both to arrive before returning from RequestStreams. call_request_streams() # Ensure Gabble's received the method call. sync_dbus(bus, q, conn) # Now send the presence. info_event = jt.send_presence() else: info_event = jt.send_presence() # Now call RequestStreams; it should wait for the disco reply. call_request_streams() jt.send_remote_disco_reply(info_event.stanza) # RequestStreams should now happily complete if channel_type == cs.CHANNEL_TYPE_STREAMED_MEDIA: q.expect('dbus-return', method='RequestStreams') else: q.expect('dbus-return', method='CreateChannel') if __name__ == '__main__': exec_test(partial(test, channel_type=cs.CHANNEL_TYPE_STREAMED_MEDIA), timeout=10) exec_test(partial(test, channel_type=cs.CHANNEL_TYPE_CALL), timeout=10) telepathy-gabble-0.18.2/tests/twisted/jingle/test-wait-for-caps-incomplete.py0000644000175000017500000000545412227000321027247 0ustar00smcvsmcv00000000000000""" Test that Gabble properly cleans up delayed RequestStream contexts and returns an error when Disconnect is called and there are incomplete requests. """ from functools import partial from gabbletest import exec_test, disconnect_conn from servicetest import make_channel_proxy, call_async, sync_dbus, EventPattern import constants as cs from config import VOIP_ENABLED if not VOIP_ENABLED: print "NOTE: built with --disable-voip" raise SystemExit(77) def test(q, bus, conn, stream, channel_type): peer = 'foo@bar.com/Foo' # We intentionally DON'T set remote presence yet. Since Gabble is still # unsure whether to treat contact as offline for this purpose, it # will tentatively allow channel creation and contact handle addition handle = conn.RequestHandles(cs.HT_CONTACT, [peer])[0] if channel_type == cs.CHANNEL_TYPE_STREAMED_MEDIA: path = conn.RequestChannel(cs.CHANNEL_TYPE_STREAMED_MEDIA, cs.HT_CONTACT, handle, True) media_iface = make_channel_proxy(conn, path, 'Channel.Type.StreamedMedia') # So it turns out that the calls to RequestStreams and Disconnect could be # reordered while the first waits on the result of introspecting the # channel's object which is kicked off by making a proxy object for it, # whereas the connection proxy is long ago introspected. Isn't dbus-python # great? Syncing here forces that introspection to finish so we can rely on # the ordering of RequestStreams and Disconnect. Yay. sync_dbus(bus, q, conn) # Now we request streams before either or caps have arrived if channel_type == cs.CHANNEL_TYPE_STREAMED_MEDIA: call_async(q, media_iface, 'RequestStreams', handle, [cs.MEDIA_STREAM_TYPE_AUDIO]) before_events, after_events = disconnect_conn(q, conn, stream, [EventPattern('dbus-error', method='RequestStreams')]) # RequestStreams should now return NotAvailable assert before_events[0].error.get_dbus_name() == cs.NOT_AVAILABLE, \ before_events[0].error else: call_async(q, conn.Requests, 'CreateChannel', { cs.CHANNEL_TYPE: channel_type, cs.TARGET_HANDLE_TYPE: cs.HT_CONTACT, cs.TARGET_ID: peer, cs.CALL_INITIAL_AUDIO: True }) before_events, after_events = disconnect_conn(q, conn, stream, [EventPattern('dbus-error', method='CreateChannel')]) # CreateChannel should now return Disconnected assert before_events[0].error.get_dbus_name() == cs.DISCONNECTED, \ before_events[0].error if __name__ == '__main__': exec_test(partial(test, channel_type=cs.CHANNEL_TYPE_STREAMED_MEDIA), timeout=10) exec_test(partial(test, channel_type=cs.CHANNEL_TYPE_CALL), timeout=10) telepathy-gabble-0.18.2/tests/twisted/jingle/test-outgoing-iceudp.py0000644000175000017500000001341012227000321025527 0ustar00smcvsmcv00000000000000""" Test outgoing call using ICE-UDP transport mechanism. """ from gabbletest import exec_test, sync_stream from servicetest import ( wrap_channel, make_channel_proxy, EventPattern, call_async, assertEquals) import gabbletest import dbus import time from twisted.words.xish import xpath import ns import constants as cs from jingletest2 import * from config import VOIP_ENABLED if not VOIP_ENABLED: print "NOTE: built with --disable-voip" raise SystemExit(77) def worker(jp, q, bus, conn, stream): jp.features.remove(ns.GOOGLE_P2P) jp.features.append(ns.JINGLE_TRANSPORT_ICEUDP) jt2 = JingleTest2(jp, conn, q, stream, 'test@localhost', 'foo@bar.com/Foo') jt2.prepare() remote_handle = conn.RequestHandles(cs.HT_CONTACT, ["foo@bar.com/Foo"])[0] call_async(q, conn, 'RequestChannel', cs.CHANNEL_TYPE_STREAMED_MEDIA, 0, 0, True) ret, old_sig, new_sig = q.expect_many( EventPattern('dbus-return', method='RequestChannel'), EventPattern('dbus-signal', signal='NewChannel', predicate=lambda e: cs.CHANNEL_TYPE_CONTACT_LIST not in e.args), EventPattern('dbus-signal', signal='NewChannels', predicate=lambda e: cs.CHANNEL_TYPE_CONTACT_LIST not in e.args[0][0][1].values()), ) path = ret.value[0] chan = wrap_channel(bus.get_object(conn.bus_name, path), 'StreamedMedia') chan.StreamedMedia.RequestStreams(remote_handle, [cs.MEDIA_STREAM_TYPE_AUDIO]) # S-E gets notified about new session handler, and calls Ready on it e = q.expect('dbus-signal', signal='NewSessionHandler') assert e.args[1] == 'rtp' # The 'nat-traversal' tp property should be "ice-udp" hrggh = chan.ListProperties(dbus_interface=cs.TP_AWKWARD_PROPERTIES) id = [x for x, name, _, _ in hrggh if name == 'nat-traversal'][0] nrgrg = chan.GetProperties([id], dbus_interface=cs.TP_AWKWARD_PROPERTIES) _, nat_traversal = nrgrg[0] assertEquals('ice-udp', nat_traversal) session_handler = make_channel_proxy(conn, e.args[0], 'Media.SessionHandler') session_handler.Ready() e = q.expect('dbus-signal', signal='NewStreamHandler') stream_handler = make_channel_proxy(conn, e.args[0], 'Media.StreamHandler') stream_handler.NewNativeCandidate("fake", jt2.get_remote_transports_dbus()) stream_handler.Ready(jt2.get_audio_codecs_dbus()) stream_handler.StreamState(2) e = q.expect('stream-iq', predicate=jp.action_predicate('session-initiate')) # The session-initiate "MUST include a child element qualified # by the [ice-udp] namespace" node = xpath.queryForNodes("/iq/jingle/content/transport[@xmlns='%s']" % ns.JINGLE_TRANSPORT_ICEUDP, e.stanza)[0] jt2.parse_session_initiate(e.query) # ...which SHOULD contain the higher-priority ICE candidates. We supplied # one candidate, so... assertEquals('username', node['ufrag']) assertEquals('password', node['pwd']) node = [ x for x in node.children if type(x) != unicode ][0] assertEquals('candidate', node.name) assert node['foundation'] is not None stream.send(jp.xml(jp.ResultIq('test@localhost', e.stanza, []))) # Make sure that it doesn't send a duplicate of our one ICE candidate here. ti_event = [ EventPattern('stream-iq', predicate=jp.action_predicate('transport-info')) ] q.forbid_events(ti_event) sync_stream(q, stream) q.unforbid_events(ti_event) # XEP-0166 6.4 Negotiation: "The allowable negotiations include: # Exchanging particular transport candidates via the transport-info action." candidate = ( "192.168.0.69", # host 668, # port 0, # protocol = TP_MEDIA_STREAM_BASE_PROTO_UDP "RTP", # protocol subtype "AVP", # profile 1.0, # preference 0, # transport type = TP_MEDIA_STREAM_TRANSPORT_TYPE_LOCAL, "username", "password" ) transport = jp.TransportIceUdp([candidate]) node = jp.SetIq(jt2.peer, jt2.jid, [ jp.Jingle(jt2.sid, jt2.peer, 'transport-info', [ jp.Content('Audio', 'initiator', 'both', transport = transport) ]) ]) stream.send(jp.xml(node)) candidate_e, result_e = q.expect_many( EventPattern('dbus-signal', signal='AddRemoteCandidate'), EventPattern('stream-iq', iq_type='result')) fake_, (returned_candidate,) = candidate_e.args assertEquals(candidate, returned_candidate[1:]) candidate = ( "192.168.0.69", # host 670, # port 0, # protocol = TP_MEDIA_STREAM_BASE_PROTO_UDP "RTP", # protocol subtype "AVP", # profile 1.0, # preference 0, # transport type = TP_MEDIA_STREAM_TRANSPORT_TYPE_LOCAL, "username", "password" ) transport = jp.TransportIceUdp([candidate]) # It is also valid to send transports in the accept. # This is what pidgin does. node = jp.SetIq(jt2.peer, jt2.jid, [ jp.Jingle(jt2.sid, jt2.peer, 'session-accept', [ jp.Content('Audio', 'initiator', 'both', jp.Description('audio', [ jp.PayloadType(name, str(rate), str(id), parameters) for (name, id, rate, parameters) in jt2.audio_codecs ]), transport) ]) ]) stream.send(jp.xml(node)) candidate_e, result_e = q.expect_many( EventPattern('dbus-signal', signal='AddRemoteCandidate'), EventPattern('stream-iq', iq_type='result')) fake_, (returned_candidate,) = candidate_e.args assertEquals(candidate, returned_candidate[1:]) chan.Close() e = q.expect('stream-iq', predicate=jp.action_predicate('session-terminate')) def test031(q, bus, conn, stream): return worker(JingleProtocol031(),q, bus, conn, stream) if __name__ == '__main__': exec_test(test031) telepathy-gabble-0.18.2/tests/twisted/jingle/test-outgoing-call-rejected.py0000644000175000017500000000600012227000321026751 0ustar00smcvsmcv00000000000000""" Test outgoing call handling. This tests the case when the remote party rejects our call because they're busy. """ from gabbletest import make_result_iq from servicetest import make_channel_proxy, assertEquals import constants as cs from jingletest2 import JingleTest2, test_all_dialects from config import VOIP_ENABLED if not VOIP_ENABLED: print "NOTE: built with --disable-voip" raise SystemExit(77) def _test(jp, q, bus, conn, stream, jingle_reason, group_change_reason, stream_error): remote_jid = 'foo@bar.com/Foo' jt = JingleTest2(jp, conn, q, stream, 'test@localhost', remote_jid) jt.prepare() self_handle = conn.GetSelfHandle() remote_handle = conn.RequestHandles(cs.HT_CONTACT, [remote_jid])[0] path = conn.RequestChannel(cs.CHANNEL_TYPE_STREAMED_MEDIA, cs.HT_CONTACT, remote_handle, True) signalling_iface = make_channel_proxy(conn, path, 'Channel.Interface.MediaSignalling') media_iface = make_channel_proxy(conn, path, 'Channel.Type.StreamedMedia') media_iface.RequestStreams(remote_handle, [cs.MEDIA_STREAM_TYPE_AUDIO]) # S-E gets notified about new session handler, and calls Ready on it e = q.expect('dbus-signal', signal='NewSessionHandler') assert e.args[1] == 'rtp' session_handler = make_channel_proxy(conn, e.args[0], 'Media.SessionHandler') session_handler.Ready() e = q.expect('dbus-signal', signal='NewStreamHandler') stream_handler = make_channel_proxy(conn, e.args[0], 'Media.StreamHandler') stream_handler.NewNativeCandidate("fake", jt.get_remote_transports_dbus()) stream_handler.Ready(jt.get_audio_codecs_dbus()) stream_handler.StreamState(cs.MEDIA_STREAM_STATE_CONNECTED) e = q.expect('stream-iq', predicate=jp.action_predicate('session-initiate')) stream.send(make_result_iq(stream, e.stanza)) text = u"begone!" jt.parse_session_initiate(e.query) jt.terminate(reason=jingle_reason, text=text) mc = q.expect('dbus-signal', signal='MembersChanged') message, added, removed, lp, rp, actor, reason = mc.args assert added == [], added assert set(removed) == set([self_handle, remote_handle]), \ (removed, self_handle, remote_handle) assert lp == [], lp assert rp == [], rp assert actor == remote_handle, (actor, remote_handle) if jp.is_modern_jingle(): assertEquals(text, message) assertEquals(group_change_reason, reason) if jp.is_modern_jingle() and stream_error: se = q.expect('dbus-signal', signal='StreamError') assertEquals(stream_error, se.args[1]) q.expect('dbus-signal', signal='Close') #XXX - match against the path def test_busy(jp, q, bus, conn, stream): _test(jp, q, bus, conn, stream, "busy", cs.GC_REASON_BUSY, None) def test_codec_fail(jp, q, bus, conn, stream): _test(jp, q, bus, conn, stream, "failed-application", cs.GC_REASON_ERROR, cs.MEDIA_STREAM_ERROR_CODEC_NEGOTIATION_FAILED) if __name__ == '__main__': test_all_dialects(test_busy) test_all_dialects(test_codec_fail) telepathy-gabble-0.18.2/tests/twisted/jingle/test-incoming-iceudp.py0000644000175000017500000000677112227000321025513 0ustar00smcvsmcv00000000000000""" Test usage of ICE-UDP in incoming calls. """ from twisted.words.xish import xpath from gabbletest import exec_test from servicetest import ( make_channel_proxy, wrap_channel, assertEquals, EventPattern, assertLength, ) import ns import constants as cs from jingletest2 import * from config import VOIP_ENABLED if not VOIP_ENABLED: print "NOTE: built with --disable-voip" raise SystemExit(77) def worker(jp, q, bus, conn, stream): jp.features.append(ns.JINGLE_TRANSPORT_ICEUDP) jt2 = JingleTest2(jp, conn, q, stream, 'test@localhost', 'foo@bar.com/Foo') jt2.prepare() remote_handle = conn.RequestHandles(cs.HT_CONTACT, ["foo@bar.com/Foo"])[0] # Remote end calls us node = jp.SetIq(jt2.peer, jt2.jid, [ jp.Jingle(jt2.sid, jt2.peer, 'session-initiate', [ jp.Content('stream1', 'initiator', 'both', jp.Description('audio', [ jp.PayloadType(name, str(rate), str(id), parameters) for (name, id, rate, parameters) in jt2.audio_codecs ]), jp.TransportIceUdp()) ]) ]) stream.send(jp.xml(node)) nc, e = q.expect_many( EventPattern('dbus-signal', signal='NewChannel', predicate=lambda e: cs.CHANNEL_TYPE_CONTACT_LIST not in e.args), EventPattern('dbus-signal', signal='NewSessionHandler'), ) path = nc.args[0] chan = wrap_channel(bus.get_object(conn.bus_name, path), 'StreamedMedia') hrggh = chan.ListProperties(dbus_interface=cs.TP_AWKWARD_PROPERTIES) id = [x for x, name, _, _ in hrggh if name == 'nat-traversal'][0] nrgrg = chan.GetProperties([id], dbus_interface=cs.TP_AWKWARD_PROPERTIES) _, nat_traversal = nrgrg[0] assertEquals('ice-udp', nat_traversal) session_handler = make_channel_proxy(conn, e.args[0], 'Media.SessionHandler') session_handler.Ready() chan.Group.AddMembers([conn.GetSelfHandle()], 'accepted') # S-E gets notified about a newly-created stream e = q.expect('dbus-signal', signal='NewStreamHandler') stream_handler = make_channel_proxy(conn, e.args[0], 'Media.StreamHandler') stream_handler.NewNativeCandidate("fake", jt2.get_remote_transports_dbus()) stream_handler.Ready(jt2.get_audio_codecs_dbus()) stream_handler.StreamState(2) # First one is transport-info e = q.expect('stream-iq', predicate=jp.action_predicate('transport-info')) transport_stanza = xpath.queryForNodes( "/iq/jingle/content/transport[@xmlns='%s']" % ns.JINGLE_TRANSPORT_ICEUDP, e.stanza)[0] # username assertEquals(transport_stanza['ufrag'], jt2.remote_transports[0][7]) # password assertEquals(transport_stanza['pwd'], jt2.remote_transports[0][8]) children = list(transport_stanza.elements()) assertLength(1, children) stream.send(jp.xml(jp.ResultIq('test@localhost', e.stanza, []))) # Set codec intersection so gabble can accept the session stream_handler.SupportedCodecs(jt2.get_audio_codecs_dbus()) # Second one is session-accept e = q.expect('stream-iq', predicate=jp.action_predicate('session-accept')) assert xpath.queryForNodes("/iq/jingle/content/transport[@xmlns='%s']" % ns.JINGLE_TRANSPORT_ICEUDP, e.stanza) stream.send(jp.xml(jp.ResultIq('test@localhost', e.stanza, []))) # Connected! Blah, blah, ... jt2.terminate() e = q.expect('dbus-signal', signal='Close') def test031(q, bus, conn, stream): return worker(JingleProtocol031(),q, bus, conn, stream) if __name__ == '__main__': exec_test(test031) telepathy-gabble-0.18.2/tests/twisted/jingle/test-incoming-call-reject.py0000644000175000017500000000742312227000321026422 0ustar00smcvsmcv00000000000000""" Test incoming call handling - reject a call because we're busy, and for no reason. """ from twisted.words.xish import xpath from servicetest import make_channel_proxy, EventPattern, call_async from jingletest2 import JingleTest2, test_all_dialects import constants as cs from config import VOIP_ENABLED if not VOIP_ENABLED: print "NOTE: built with --disable-voip" raise SystemExit(77) def test_busy(jp, q, bus, conn, stream): test(jp, q, bus, conn, stream, True) def test_no_reason(jp, q, bus, conn, stream): test(jp, q, bus, conn, stream, False) def test(jp, q, bus, conn, stream, busy): remote_jid = 'foo@bar.com/Foo' jt = JingleTest2(jp, conn, q, stream, 'test@localhost', remote_jid) jt.prepare() self_handle = conn.GetSelfHandle() remote_handle = conn.RequestHandles(cs.HT_CONTACT, [remote_jid])[0] # Remote end calls us jt.incoming_call() # FIXME: these signals are not observable by real clients, since they # happen before NewChannels. # The caller is in members e = q.expect('dbus-signal', signal='MembersChanged', args=[u'', [remote_handle], [], [], [], 0, 0]) # We're pending because of remote_handle e = q.expect('dbus-signal', signal='MembersChanged', args=[u'', [], [], [self_handle], [], remote_handle, cs.GC_REASON_INVITED]) # S-E gets notified about new session handler, and calls Ready on it e = q.expect('dbus-signal', signal='NewSessionHandler') assert e.args[1] == 'rtp' session_handler = make_channel_proxy(conn, e.args[0], 'Media.SessionHandler') session_handler.Ready() media_chan = make_channel_proxy(conn, e.path, 'Channel.Interface.Group') # Exercise channel properties channel_props = media_chan.GetAll(cs.CHANNEL, dbus_interface=cs.PROPERTIES_IFACE) assert channel_props['TargetHandle'] == remote_handle assert channel_props['TargetHandleType'] == 1 assert channel_props['TargetID'] == 'foo@bar.com' assert channel_props['Requested'] == False assert channel_props['InitiatorID'] == 'foo@bar.com' assert channel_props['InitiatorHandle'] == remote_handle if busy: # First, try using a reason that doesn't make any sense call_async(q, media_chan, 'RemoveMembersWithReason', [self_handle], "what kind of a reason is Separated?!", cs.GC_REASON_SEPARATED) e = q.expect('dbus-error', method='RemoveMembersWithReason') assert e.error.get_dbus_name() == cs.INVALID_ARGUMENT # Now try a more sensible reason. media_chan.RemoveMembersWithReason([self_handle], "which part of 'Do Not Disturb' don't you understand?", cs.GC_REASON_BUSY) else: media_chan.RemoveMembers([self_handle], 'rejected') iq, mc, _ = q.expect_many( EventPattern('stream-iq', predicate=jp.action_predicate('session-terminate')), EventPattern('dbus-signal', signal='MembersChanged'), EventPattern('dbus-signal', signal='Closed'), ) _, added, removed, lp, rp, actor, reason = mc.args assert added == [], added assert set(removed) == set([self_handle, remote_handle]), \ (removed, self_handle, remote_handle) assert lp == [], lp assert rp == [], rp assert actor == self_handle, (actor, self_handle) if busy: assert reason == cs.GC_REASON_BUSY, reason else: assert reason == cs.GC_REASON_NONE, reason if jp.is_modern_jingle(): jingle = iq.query if busy: r = "/jingle/reason/busy" else: r = "/jingle/reason/cancel" assert xpath.queryForNodes(r, jingle) is not None, (jingle.toXml(), r) if __name__ == '__main__': test_all_dialects(test_busy) test_all_dialects(test_no_reason) telepathy-gabble-0.18.2/tests/twisted/jingle/test-description-info.py0000644000175000017500000002037712227000321025713 0ustar00smcvsmcv00000000000000""" Test emition and handling of codec update using description-info """ from gabbletest import exec_test, sync_stream from servicetest import ( wrap_channel, assertEquals, make_channel_proxy, unwrap, EventPattern, call_async) from jingletest2 import JingleTest2, JingleProtocol031 import constants as cs from twisted.words.xish import xpath from config import VOIP_ENABLED if not VOIP_ENABLED: print "NOTE: built with --disable-voip" raise SystemExit(77) def extract_params(payload_type): ret = {} for node in payload_type.elements(): assert node.name == 'parameter' ret[node['name']] = node['value'] return ret def early_description_info(q, bus, conn, stream): test(q, bus, conn, stream, send_early_description_info=True) def test(q, bus, conn, stream, send_early_description_info=False): jp = JingleProtocol031() jt2 = JingleTest2(jp, conn, q, stream, 'test@localhost', 'foo@bar.com/Foo') jt2.prepare() self_handle = conn.GetSelfHandle() remote_handle = conn.RequestHandles(cs.HT_CONTACT, ["foo@bar.com/Foo"])[0] # Remote end calls us jt2.incoming_call() # FIXME: these signals are not observable by real clients, since they # happen before NewChannels. # The caller is in members e = q.expect('dbus-signal', signal='MembersChanged', args=[u'', [remote_handle], [], [], [], 0, 0]) # We're pending because of remote_handle e = q.expect('dbus-signal', signal='MembersChanged', args=[u'', [], [], [self_handle], [], remote_handle, cs.GC_REASON_INVITED]) chan = wrap_channel(bus.get_object(conn.bus_name, e.path), 'StreamedMedia') # S-E gets notified about new session handler, and calls Ready on it e = q.expect('dbus-signal', signal='NewSessionHandler') assert e.args[1] == 'rtp' if send_early_description_info: """ Regression test for a bug where Gabble would crash if you sent it description-info before calling Ready() on the relevant StreamHandler, and then for a bug where Gabble would never accept the call if a description-info was received before all StreamHandlers were Ready(). """ node = jp.SetIq(jt2.peer, jt2.jid, [ jp.Jingle(jt2.sid, jt2.peer, 'description-info', [ jp.Content('stream1', 'initiator', 'both', jp.Description('audio', [ ])) ]) ]) stream.send(jp.xml(node)) sync_stream(q, stream) session_handler = make_channel_proxy(conn, e.args[0], 'Media.SessionHandler') session_handler.Ready() chan.Group.AddMembers([self_handle], 'accepted') # S-E gets notified about a newly-created stream e = q.expect('dbus-signal', signal='NewStreamHandler') id1 = e.args[1] stream_handler = make_channel_proxy(conn, e.args[0], 'Media.StreamHandler') # We are now in members too e = q.expect('dbus-signal', signal='MembersChanged', args=[u'', [self_handle], [], [], [], self_handle, cs.GC_REASON_NONE]) # we are now both in members members = chan.Group.GetMembers() assert set(members) == set([self_handle, remote_handle]), members local_codecs = [('GSM', 3, 8000, {}), ('PCMA', 8, 8000, {'helix':'woo yay'}), ('PCMU', 0, 8000, {}) ] local_codecs_dbus = jt2.dbusify_codecs_with_params(local_codecs) stream_handler.NewNativeCandidate("fake", jt2.get_remote_transports_dbus()) stream_handler.Ready(local_codecs_dbus) stream_handler.StreamState(cs.MEDIA_STREAM_STATE_CONNECTED) stream_handler.CodecsUpdated(local_codecs_dbus) local_codecs = [('GSM', 3, 8000, {}), ('PCMA', 8, 8000, {'gstreamer':'rock on'}), ('PCMU', 0, 8000, {}) ] local_codecs_dbus = jt2.dbusify_codecs_with_params(local_codecs) stream_handler.CodecsUpdated(local_codecs_dbus) # First IQ is transport-info; also, we expect to be told what codecs the # other end wants. e, src = q.expect_many( EventPattern('stream-iq', predicate=jp.action_predicate('transport-info')), EventPattern('dbus-signal', signal='SetRemoteCodecs') ) assertEquals('foo@bar.com/Foo', e.query['initiator']) assert jt2.audio_codecs == [ (name, id, rate, parameters) for id, name, type, rate, channels, parameters in unwrap(src.args[0]) ], \ (jt2.audio_codecs, unwrap(src.args[0])) stream.send(jp.xml(jp.ResultIq('test@localhost', e.stanza, []))) # S-E reports codec intersection, after which gabble can send acceptance stream_handler.SupportedCodecs(local_codecs_dbus) # Second one is session-accept e = q.expect('stream-iq', predicate=jp.action_predicate('session-accept')) # farstream is buggy, and tells tp-fs to tell Gabble to change the third # codec's clockrate. This isn't legal, so Gabble says no. new_codecs = [ ('GSM', 3, 8000, {}), ('PCMA', 8, 8000, {}), ('PCMU', 0, 4000, {}) ] call_async(q, stream_handler, 'CodecsUpdated', jt2.dbusify_codecs(new_codecs)) event = q.expect('dbus-error', method='CodecsUpdated') assert event.error.get_dbus_name() == cs.INVALID_ARGUMENT, \ event.error.get_dbus_name() # With its tail between its legs, tp-fs decides it wants to add some # parameters to the first two codecs, not changing the third. new_codecs = [ ('GSM', 3, 8000, {'type': 'banana'}), ('PCMA', 8, 8000, {'helix': 'BUFFERING'}), ('PCMU', 0, 8000, {}) ] stream_handler.CodecsUpdated(jt2.dbusify_codecs_with_params(new_codecs)) audio_content = jt2.audio_names[0] e = q.expect('stream-iq', iq_type='set', predicate=lambda x: xpath.queryForNodes("/iq/jingle[@action='description-info']", x.stanza)) payload_types = xpath.queryForNodes( "/iq/jingle/content[@name='%s']/description/payload-type" % audio_content, e.stanza) # Gabble SHOULD only include the changed codecs in description-info assert len(payload_types) == 2, payload_types payload_types_tupled = [ (pt['name'], int(pt['id']), int(pt['clockrate']), extract_params(pt)) for pt in payload_types ] assert sorted(payload_types_tupled) == sorted(new_codecs[0:2]), \ (payload_types_tupled, new_codecs[0:2]) # The remote end decides it wants to change the number of channels in the # third codec. This is not meant to happen, so Gabble should send it an IQ # error back. node = jp.SetIq(jt2.peer, jt2.jid, [ jp.Jingle(jt2.sid, jt2.peer, 'description-info', [ jp.Content(audio_content, 'initiator', 'both', jp.Description('audio', [ jp.PayloadType('PCMU', '1600', '0') ])) ]) ]) stream.send(jp.xml(node)) q.expect('stream-iq', iq_type='error', predicate=lambda x: x.stanza['id'] == node[2]['id']) # Instead, the remote end decides to add a parameter to the third codec. new_codecs = [ ('GSM', 3, 8000, {}), ('PCMA', 8, 8000, {}), ('PCMU', 0, 8000, {'choppy': 'false'}), ] # As per the XEP, it only sends the ones which have changed. c = new_codecs[2] node = jp.SetIq(jt2.peer, jt2.jid, [ jp.Jingle(jt2.sid, jt2.peer, 'description-info', [ jp.Content(audio_content, 'initiator', 'both', jp.Description('audio', [ jp.PayloadType(c[0], str(c[2]), str(c[1]), c[3]) ])) ]) ]) stream.send(jp.xml(node)) # Gabble should patch its idea of the remote codecs with the update it just # got, and emit SetRemoteCodecs for them all. e = q.expect('dbus-signal', signal='SetRemoteCodecs') new_codecs_dbus = unwrap(jt2.dbusify_codecs_with_params(new_codecs)) announced = unwrap(e.args[0]) assert new_codecs_dbus == announced, (new_codecs_dbus, announced) # We close the session by removing the stream chan.StreamedMedia.RemoveStreams([id1]) e = q.expect('stream-iq', iq_type='set', predicate=lambda x: xpath.queryForNodes("/iq/jingle[@action='session-terminate']", x.stanza)) if __name__ == '__main__': exec_test(test) exec_test(early_description_info) telepathy-gabble-0.18.2/tests/twisted/jingle/test-content-complex.py0000644000175000017500000002410612227000321025550 0ustar00smcvsmcv00000000000000""" Test everything related to contents """ from gabbletest import sync_stream from servicetest import ( make_channel_proxy, assertEquals, EventPattern) import constants as cs from jingletest2 import ( JingleTest2, JingleProtocol015, JingleProtocol031, test_dialects) from twisted.words.xish import xpath from config import VOIP_ENABLED if not VOIP_ENABLED: print "NOTE: built with --disable-voip" raise SystemExit(77) def worker(jp, q, bus, conn, stream): def make_stream_request(stream_type): media_iface.RequestStreams(remote_handle, [stream_type]) e = q.expect('dbus-signal', signal='NewStreamHandler') stream_id = e.args[1] stream_handler = make_channel_proxy(conn, e.args[0], 'Media.StreamHandler') stream_handler.NewNativeCandidate("fake", jt2.get_remote_transports_dbus()) stream_handler.Ready(jt2.get_audio_codecs_dbus()) stream_handler.StreamState(cs.MEDIA_STREAM_STATE_CONNECTED) return (stream_handler, stream_id) jt2 = JingleTest2(jp, conn, q, stream, 'test@localhost', 'foo@bar.com/Foo') jt2.prepare() self_handle = conn.GetSelfHandle() remote_handle = conn.RequestHandles(cs.HT_CONTACT, ["foo@bar.com/Foo"])[0] # Remote end calls us jt2.incoming_call() # FIXME: these signals are not observable by real clients, since they # happen before NewChannels. # The caller is in members e = q.expect('dbus-signal', signal='MembersChanged', args=[u'', [remote_handle], [], [], [], 0, 0]) # We're pending because of remote_handle e = q.expect('dbus-signal', signal='MembersChanged', args=[u'', [], [], [self_handle], [], remote_handle, cs.GC_REASON_INVITED]) media_chan = make_channel_proxy(conn, e.path, 'Channel.Interface.Group') signalling_iface = make_channel_proxy(conn, e.path, 'Channel.Interface.MediaSignalling') media_iface = make_channel_proxy(conn, e.path, 'Channel.Type.StreamedMedia') # S-E gets notified about new session handler, and calls Ready on it e = q.expect('dbus-signal', signal='NewSessionHandler') assert e.args[1] == 'rtp' session_handler = make_channel_proxy(conn, e.args[0], 'Media.SessionHandler') session_handler.Ready() media_chan.AddMembers([self_handle], 'accepted') # S-E gets notified about a newly-created stream e = q.expect('dbus-signal', signal='NewStreamHandler') id1 = e.args[1] stream_handler = make_channel_proxy(conn, e.args[0], 'Media.StreamHandler') # We are now in members too e = q.expect('dbus-signal', signal='MembersChanged', args=[u'', [self_handle], [], [], [], self_handle, cs.GC_REASON_NONE]) # we are now both in members members = media_chan.GetMembers() assert set(members) == set([self_handle, remote_handle]), members stream_handler.NewNativeCandidate("fake", jt2.get_remote_transports_dbus()) stream_handler.Ready(jt2.get_audio_codecs_dbus()) stream_handler.StreamState(cs.MEDIA_STREAM_STATE_CONNECTED) # First one is transport-info e = q.expect('stream-iq', predicate=jp.action_predicate('transport-info')) assertEquals('foo@bar.com/Foo', e.query['initiator']) # stream.send(gabbletest.make_result_iq(stream, e.stanza)) stream.send(jp.xml(jp.ResultIq('test@localhost', e.stanza, []))) # S-E reports codec intersection, after which gabble can send acceptance stream_handler.SupportedCodecs(jt2.get_audio_codecs_dbus()) # Second one is session-accept e = q.expect('stream-iq', predicate=jp.action_predicate('session-accept')) # stream.send(gabbletest.make_result_iq(stream, e.stanza)) stream.send(jp.xml(jp.ResultIq('test@localhost', e.stanza, []))) # Here starts the interesting part of this test # Remote end tries to create a content we can't handle node = jp.SetIq(jt2.peer, jt2.jid, [ jp.Jingle(jt2.sid, jt2.peer, 'content-add', [ jp.Content('bogus', 'initiator', 'both', jp.Description('hologram', [ jp.PayloadType(name, str(rate), str(id), parameters) for (name, id, rate, parameters) in jt2.audio_codecs ]), jp.TransportGoogleP2P()) ]) ]) stream.send(jp.xml(node)) # In older Jingle, this is a separate namespace, which isn't # recognized, but it's a valid request, so it gets ackd and rejected if jp.dialect == 'jingle-v0.15': # Gabble should acknowledge content-add q.expect('stream-iq', iq_type='result') # .. and then send content-reject for the bogus content e = q.expect('stream-iq', iq_type='set', predicate=lambda x: xpath.queryForNodes("/iq/jingle[@action='content-reject']/content[@name='bogus']", x.stanza)) # In new Jingle, this is a bogus subtype of recognized namespace, # so Gabble returns a bad request error else: q.expect('stream-iq', iq_type='error') # Remote end then tries to create a content with a name it's already used node = jp.SetIq(jt2.peer, jt2.jid, [ jp.Jingle(jt2.sid, jt2.peer, 'content-add', [ jp.Content(jt2.audio_names[0], 'initiator', 'both', jp.Description('audio', [ jp.PayloadType(name, str(rate), str(id), parameters) for (name, id, rate, parameters) in jt2.audio_codecs ]), jp.TransportGoogleP2P()) ]) ]) stream.send(jp.xml(node)) # Gabble should return error (content already exists) q.expect('stream-iq', iq_type='error') # We try to add a stream (stream_handler2, id2) = make_stream_request(cs.MEDIA_STREAM_TYPE_VIDEO) # Gabble should now send content-add e = q.expect('stream-iq', iq_type='set', predicate=lambda x: xpath.queryForNodes("/iq/jingle[@action='content-add']", x.stanza)) c = e.query.firstChildElement() assert c['creator'] == 'responder', c['creator'] stream.send(jp.xml(jp.ResultIq('test@localhost', e.stanza, []))) # We try to add yet another stream (stream_handler3, id3) = make_stream_request(cs.MEDIA_STREAM_TYPE_VIDEO) # Gabble should send another content-add e = q.expect('stream-iq', iq_type='set', predicate=lambda x: xpath.queryForNodes("/iq/jingle[@action='content-add']", x.stanza)) d = e.query.firstChildElement() assertEquals('responder', d['creator']) stream.send(jp.xml(jp.ResultIq('test@localhost', e.stanza, []))) # Remote end rejects the first stream we tried to add. node = jp.SetIq(jt2.peer, jt2.jid, [ jp.Jingle(jt2.sid, jt2.peer, 'content-reject', [ jp.Content(c['name'], c['creator'], c['senders']) ]) ]) stream.send(jp.xml(node)) # Gabble removes the stream q.expect('dbus-signal', signal='StreamRemoved', interface=cs.CHANNEL_TYPE_STREAMED_MEDIA) # Remote end tries to add a content with the same name as the second one we # just added node = jp.SetIq(jt2.peer, jt2.jid, [ jp.Jingle(jt2.sid, jt2.peer, 'content-add', [ jp.Content(d['name'], 'initiator', 'both', jp.Description('audio', [ jp.PayloadType(name, str(rate), str(id), parameters) for (name, id, rate, parameters) in jt2.audio_codecs ]), jp.TransportGoogleP2P()) ]) ]) stream.send(jp.xml(node)) # Because stream names are namespaced by creator, Gabble should be okay # with that. q.expect_many( EventPattern('stream-iq', iq_type='result', iq_id=node[2]['id']), EventPattern('dbus-signal', signal='StreamAdded'), ) # Remote end thinks better of that, and removes the similarly-named stream # it tried to add. node = jp.SetIq(jt2.peer, jt2.jid, [ jp.Jingle(jt2.sid, jt2.peer, 'content-remove', [ jp.Content(d['name'], 'initiator', d['senders']) ]) ]) stream.send(jp.xml(node)) q.expect_many( EventPattern('stream-iq', iq_type='result', iq_id=node[2]['id']), EventPattern('dbus-signal', signal='StreamRemoved'), ) # Remote end finally accepts. When Gabble did not namespace contents by # their creator, it would NAK this IQ: # - Gabble (responder) created a stream called 'foo'; # - test suite (initiator) created a stream called 'foo', which Gabble # decided would replace its own stream called 'foo'; # - test suite removed its 'foo'; # - test suite accepted Gabble's 'foo', but Gabble didn't believe a stream # called 'foo' existed any more. node = jp.SetIq(jt2.peer, jt2.jid, [ jp.Jingle(jt2.sid, jt2.peer, 'content-accept', [ jp.Content(d['name'], d['creator'], d['senders'], jp.Description('video', [ jp.PayloadType(name, str(rate), str(id), parameters) for (name, id, rate, parameters ) in jt2.audio_codecs ]), jp.TransportGoogleP2P()) ]) ]) stream.send(jp.xml(node)) # We get remote codecs e = q.expect('dbus-signal', signal='SetRemoteCodecs') # Now, both we and remote peer try to remove the content simultaneously: # Telepathy client calls RemoveStreams... media_iface.RemoveStreams([id3]) # ...so Gabble sends a content-remove... e = q.expect('stream-iq', iq_type='set', predicate=lambda x: xpath.queryForNodes("/iq/jingle[@action='content-remove']", x.stanza)) # ...but before it's acked the peer sends its own content-remove... node = jp.SetIq(jt2.peer, jt2.jid, [ jp.Jingle(jt2.sid, jt2.peer, 'content-remove', [ jp.Content(c['name'], c['creator'], c['senders']) ]) ]) stream.send(jp.xml(node)) # ...and we don't want Gabble to break when that happens. sync_stream(q, stream) # Now we want to remove the first stream media_iface.RemoveStreams([id1]) # Since this is the last stream, Gabble will just terminate the session. e = q.expect('stream-iq', iq_type='set', predicate=lambda x: xpath.queryForNodes("/iq/jingle[@action='session-terminate']", x.stanza)) if __name__ == '__main__': test_dialects(worker, [JingleProtocol015, JingleProtocol031]) telepathy-gabble-0.18.2/tests/twisted/jingle/test-content-adding-removal.py0000644000175000017500000001474212227000321026777 0ustar00smcvsmcv00000000000000""" Test content adding and removal during the session. We start session with only one stream, then add one more, then remove the first one and lastly remove the second stream, which closes the session. """ from gabbletest import make_result_iq, sync_stream from servicetest import ( wrap_channel, make_channel_proxy, assertEquals, EventPattern) from jingletest2 import ( JingleTest2, test_dialects, JingleProtocol031, JingleProtocol015, ) import constants as cs from config import VOIP_ENABLED if not VOIP_ENABLED: print "NOTE: built with --disable-voip" raise SystemExit(77) def gabble_terminates(jp, q, bus, conn, stream): test(jp, q, bus, conn, stream, False) def peer_terminates(jp, q, bus, conn, stream): test(jp, q, bus, conn, stream, True) def test(jp, q, bus, conn, stream, peer_removes_final_content): jt = JingleTest2(jp, conn, q, stream, 'test@localhost', 'foo@bar.com/Foo') jt.prepare() handle = conn.RequestHandles(cs.HT_CONTACT, [jt.peer])[0] path = conn.RequestChannel( cs.CHANNEL_TYPE_STREAMED_MEDIA, cs.HT_CONTACT, handle, True) chan = wrap_channel(bus.get_object(conn.bus_name, path), 'StreamedMedia') chan.StreamedMedia.RequestStreams(handle, [cs.MEDIA_STREAM_TYPE_AUDIO]) # S-E gets notified about new session handler, and calls Ready on it e = q.expect('dbus-signal', signal='NewSessionHandler') assert e.args[1] == 'rtp' session_handler = make_channel_proxy(conn, e.args[0], 'Media.SessionHandler') session_handler.Ready() e = q.expect('dbus-signal', signal='NewStreamHandler') stream_id = e.args[1] stream_handler = make_channel_proxy(conn, e.args[0], 'Media.StreamHandler') stream_handler.NewNativeCandidate("fake", jt.get_remote_transports_dbus()) # Before sending the initiate, request another stream chan.StreamedMedia.RequestStreams(handle, [cs.MEDIA_STREAM_TYPE_VIDEO]) e = q.expect('dbus-signal', signal='NewStreamHandler') stream_id2 = e.args[1] stream_handler2 = make_channel_proxy(conn, e.args[0], 'Media.StreamHandler') stream_handler2.NewNativeCandidate("fake", jt.get_remote_transports_dbus()) # Before the CM can initiate session, we modify a stream direction. This # should result in a no-op since there's no need to inform the peer of # change. chan.StreamedMedia.RequestStreamDirection(stream_id2, cs.MEDIA_STREAM_DIRECTION_RECEIVE) # We set both streams as ready, which will trigger the session initiate stream_handler.Ready(jt.get_audio_codecs_dbus()) stream_handler.StreamState(cs.MEDIA_STREAM_STATE_CONNECTED) stream_handler2.Ready(jt.get_audio_codecs_dbus()) stream_handler2.StreamState(cs.MEDIA_STREAM_STATE_CONNECTED) # We changed our mind locally, don't want video chan.StreamedMedia.RemoveStreams([stream_id2]) e = q.expect('stream-iq', predicate=jp.action_predicate('session-initiate')) stream.send(make_result_iq(stream, e.stanza)) jt.parse_session_initiate(e.query) # Gabble sends content-remove for the video stream... e2 = q.expect('stream-iq', predicate=jp.action_predicate('content-remove')) # ...but before the peer notices, they accept the call. jt.accept() # Only now the remote end removes the video stream; if gabble mistakenly # marked it as accepted on session acceptance, it'll crash right about # now. If it's good, stream will be really removed, and # we can proceed. stream.send(make_result_iq(stream, e2.stanza)) q.expect('dbus-signal', signal='StreamRemoved') # Actually, we *do* want video! chan.StreamedMedia.RequestStreams(handle, [cs.MEDIA_STREAM_TYPE_VIDEO]) e = q.expect('dbus-signal', signal='NewStreamHandler') stream2_id = e.args[1] stream_handler2 = make_channel_proxy(conn, e.args[0], 'Media.StreamHandler') stream_handler2.NewNativeCandidate("fake", jt.get_remote_transports_dbus()) stream_handler2.Ready(jt.get_audio_codecs_dbus()) stream_handler2.StreamState(cs.MEDIA_STREAM_STATE_CONNECTED) e = q.expect('stream-iq', predicate=jp.action_predicate('content-add')) c = e.query.firstChildElement() assertEquals('initiator', c['creator']) stream.send(make_result_iq(stream, e.stanza)) # Peer accepts jt.content_accept(e.query, 'video') # Let's start sending and receiving video! q.expect_many( EventPattern('dbus-signal', signal='SetStreamPlaying', args=[True]), EventPattern('dbus-signal', signal='SetStreamSending', args=[True]), ) # Now, the call draws to a close. # We first remove the original stream chan.StreamedMedia.RemoveStreams([stream_id]) e = q.expect('stream-iq', predicate=jp.action_predicate('content-remove')) content_remove_ack = make_result_iq(stream, e.stanza) if peer_removes_final_content: # The peer removes the final countdo^W content. From a footnote (!) in # XEP 0166: # If the content-remove results in zero content definitions for the # session, the entity that receives the content-remove SHOULD send # a session-terminate action to the other party (since a session # with no content definitions is void). # So, Gabble should respond to the content-remove with a # session-terminate. node = jp.SetIq(jt.peer, jt.jid, [ jp.Jingle(jt.sid, jt.peer, 'content-remove', [ jp.Content(c['name'], c['creator'], c['senders']) ]) ]) stream.send(jp.xml(node)) else: # The Telepathy client removes the second stream; Gabble should # terminate the session rather than sending a content-remove. chan.StreamedMedia.RemoveStreams([stream2_id]) st, closed = q.expect_many( EventPattern('stream-iq', predicate=jp.action_predicate('session-terminate')), # Gabble shouldn't wait for the peer to ack the terminate before # considering the call finished. EventPattern('dbus-signal', signal='Closed', path=path)) # Only now does the peer ack the content-remove. This serves as a # regression test for contents outliving the session; if the content didn't # die properly, this crashed Gabble. stream.send(content_remove_ack) sync_stream(q, stream) # The peer can ack the terminate too, just for completeness. stream.send(make_result_iq(stream, st.stanza)) if __name__ == '__main__': test_dialects(gabble_terminates, [JingleProtocol015, JingleProtocol031]) test_dialects(peer_terminates, [JingleProtocol015, JingleProtocol031]) telepathy-gabble-0.18.2/tests/twisted/jingle/stun-server.py0000644000175000017500000003272112312320035023755 0ustar00smcvsmcv00000000000000""" Test getting STUN server from Google jingleinfo """ from functools import partial import dbus import socket from gabbletest import make_result_iq, GoogleXmlStream, elem_iq, elem from servicetest import ( make_channel_proxy, EventPattern, assertEquals, assertLength, assertNotEquals, assertEquals ) from jingletest2 import test_all_dialects, JingleTest2 import constants as cs import ns from config import CHANNEL_TYPE_CALL_ENABLED, GOOGLE_RELAY_ENABLED, VOIP_ENABLED if not VOIP_ENABLED: print "NOTE: built with --disable-voip" raise SystemExit(77) def test_stun_server(stun_server_prop, expected_stun_servers=None): if expected_stun_servers is None: # If there is no stun server set, and it can't discover some from the # network, then gabble should fallback on the default fallback stun # server (stun.telepathy.im) # # This test uses the test-resolver which is set to # have 'stun.telepathy.im' resolve to '6.7.8.9' expected_stun_servers=[('6.7.8.9', 3478)] assertEquals(expected_stun_servers, stun_server_prop) def add_jingle_info(jingleinfo, stun_server, stun_port): stun = jingleinfo.firstChildElement().addElement('stun') server = stun.addElement('server') server['host'] = stun_server server['udp'] = stun_port relay = jingleinfo.firstChildElement().addElement('relay') relay.addElement('token', content='jingle all the way') def handle_jingle_info_query(q, stream, stun_server, stun_port): # See: http://code.google.com/apis/talk/jep_extensions/jingleinfo.html event = q.expect('stream-iq', query_ns=ns.GOOGLE_JINGLE_INFO, to=stream.authenticator.bare_jid) jingleinfo = make_result_iq(stream, event.stanza) add_jingle_info(jingleinfo, stun_server, stun_port) stream.send(jingleinfo) def push_jingle_info(q, stream, stun_server, stun_port): iq = elem_iq(stream, 'set')(elem(ns.GOOGLE_JINGLE_INFO, 'query')) add_jingle_info(iq, stun_server, stun_port) stream.send(iq) q.expect('stream-iq', iq_type='result', iq_id=iq['id']) def init_test(jp, q, conn, stream, google=False, google_push_replacements=None): jt = JingleTest2(jp, conn, q, stream, 'test@localhost', 'foo@bar.com/Foo') # If we need to override remote caps, feats, codecs or caps, # this is a good time to do it if google: handle_jingle_info_query(q, stream, 'resolves-to-1.2.3.4', '12345') if google_push_replacements is not None: # oh no! the server changed its mind! server, port = google_push_replacements push_jingle_info(q, stream, server, port) else: # We shouldn't be sending google:jingleinfo queries if the server # doesn't support it. q.forbid_events([ EventPattern('stream-iq', query_ns=ns.GOOGLE_JINGLE_INFO), ]) jt.send_presence_and_caps() remote_handle = conn.RequestHandles(1, ["foo@bar.com/Foo"])[0] return jt, remote_handle def test_streamed_media(jp, q, bus, conn, stream, expected_stun_servers=None, google=False, google_push_replacements=None, expected_relays=[]): # Initialize the test values jt, remote_handle = init_test(jp, q, conn, stream, google, google_push_replacements) # Remote end calls us jt.incoming_call() # FIXME: these signals are not observable by real clients, since they # happen before NewChannels. # The caller is in members e = q.expect('dbus-signal', signal='MembersChanged', args=[u'', [remote_handle], [], [], [], 0, 0]) # We're pending because of remote_handle e = q.expect('dbus-signal', signal='MembersChanged', args=[u'', [], [], [1L], [], remote_handle, cs.GC_REASON_INVITED]) # S-E gets notified about new session handler, and calls Ready on it e = q.expect('dbus-signal', signal='NewSessionHandler') assert e.args[1] == 'rtp' session_handler = make_channel_proxy(conn, e.args[0], 'Media.SessionHandler') session_handler.Ready() e = q.expect('dbus-signal', signal='NewStreamHandler') stream_handler = make_channel_proxy(conn, e.args[0], 'Media.StreamHandler') media_chan = make_channel_proxy(conn, e.path, 'Channel.Interface.Group') # Exercise channel properties channel_props = media_chan.GetAll( cs.CHANNEL, dbus_interface=dbus.PROPERTIES_IFACE) assert channel_props['TargetHandle'] == remote_handle assert channel_props['TargetHandleType'] == 1 assert channel_props['TargetID'] == 'foo@bar.com' assert channel_props['Requested'] == False assert channel_props['InitiatorID'] == 'foo@bar.com' assert channel_props['InitiatorHandle'] == remote_handle # The new API for STUN servers etc. sh_props = stream_handler.GetAll( 'org.freedesktop.Telepathy.Media.StreamHandler', dbus_interface=dbus.PROPERTIES_IFACE) assert sh_props['NATTraversal'] == 'gtalk-p2p' assert sh_props['CreatedLocally'] == False test_stun_server(sh_props['STUNServers'], expected_stun_servers) assert sh_props['RelayInfo'] == expected_relays # consistency check, since we currently reimplement Get separately for k in sh_props: assert sh_props[k] == stream_handler.Get( 'org.freedesktop.Telepathy.Media.StreamHandler', k, dbus_interface=dbus.PROPERTIES_IFACE), k # The old API for STUN servers etc. still needs supporting, for farsight 1 tp_prop_list = media_chan.ListProperties(dbus_interface=cs.TP_AWKWARD_PROPERTIES) tp_props = {} tp_prop_ids = {} for spec in tp_prop_list: tp_prop_ids[spec[0]] = spec[1] tp_props[spec[1]] = { 'id': spec[0], 'sig': spec[2], 'flags': spec[3] } assert 'nat-traversal' in tp_props assert tp_props['nat-traversal']['sig'] == 's' assert tp_props['nat-traversal']['flags'] == cs.PROPERTY_FLAG_READ assert 'stun-server' in tp_props assert tp_props['stun-server']['sig'] == 's' assert 'stun-port' in tp_props assert tp_props['stun-port']['sig'] in ('u', 'q') assert 'gtalk-p2p-relay-token' in tp_props assert tp_props['gtalk-p2p-relay-token']['sig'] == 's' assert tp_props['stun-server']['flags'] == cs.PROPERTY_FLAG_READ assert tp_props['stun-port']['flags'] == cs.PROPERTY_FLAG_READ if google: assert tp_props['gtalk-p2p-relay-token']['flags'] == cs.PROPERTY_FLAG_READ else: assert tp_props['gtalk-p2p-relay-token']['flags'] == 0 tp_prop_values = media_chan.GetProperties( [tp_props[k]['id'] for k in tp_props if tp_props[k]['flags']], dbus_interface=cs.TP_AWKWARD_PROPERTIES) for value in tp_prop_values: assert value[0] in tp_prop_ids tp_props[tp_prop_ids[value[0]]]['value'] = value[1] assert tp_props['nat-traversal']['value'] == 'gtalk-p2p' if expected_stun_servers is not None: expected_stun_server, expected_stun_port = expected_stun_servers[0] assert tp_props['stun-server']['value'] == expected_stun_server assert tp_props['stun-port']['value'] == expected_stun_port if google: assert tp_props['gtalk-p2p-relay-token']['value'] == 'jingle all the way' media_chan.RemoveMembers([dbus.UInt32(1)], 'rejected') q.expect_many( EventPattern('stream-iq', predicate=jp.action_predicate('session-terminate')), EventPattern('dbus-signal', signal='Closed'), ) def test_call(jp, q, bus, conn, stream, expected_stun_servers=None, google=False, google_push_replacements=None, expected_relays=[]): # Initialize the test values jt, remote_handle = init_test(jp, q, conn, stream, google, google_push_replacements) # Advertise that we can do new style calls conn.ContactCapabilities.UpdateCapabilities([ (cs.CLIENT + ".CallHandler", [ { cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_CALL, cs.CALL_INITIAL_AUDIO: True}, { cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_CALL, cs.CALL_INITIAL_VIDEO: True}, ], [ cs.CHANNEL_TYPE_CALL + '/gtalk-p2p', cs.CHANNEL_TYPE_CALL + '/ice', cs.CHANNEL_TYPE_CALL + '/video/h264', ]), ]) # Remote end calls us jt.incoming_call() e = q.expect('dbus-signal', signal='ServerInfoRetrieved') assertLength(0, e.args) assertEquals(e.interface, cs.CALL_STREAM_IFACE_MEDIA) e = q.expect('dbus-signal', signal='NewChannels', predicate=lambda e: cs.CHANNEL_TYPE_CONTACT_LIST not in e.args) assert e.args[0][0][0] call_chan = make_channel_proxy(conn, e.args[0][0][0], 'Channel') # Exercise channel properties channel_props = call_chan.GetAll( cs.CHANNEL, dbus_interface=dbus.PROPERTIES_IFACE) assertEquals(remote_handle, channel_props['TargetHandle']) assertEquals(1, channel_props['TargetHandleType']) assertEquals('foo@bar.com', channel_props['TargetID']) assertEquals(False, channel_props['Requested']) assertEquals('foo@bar.com', channel_props['InitiatorID']) assertEquals(remote_handle, channel_props['InitiatorHandle']) # Get the call's Content object channel_props = call_chan.Get(cs.CHANNEL_TYPE_CALL, 'Contents', dbus_interface=dbus.PROPERTIES_IFACE) assertLength(1, channel_props) assert len(channel_props[0]) > 0 assertNotEquals('/', channel_props[0]) # Get the call's Stream object call_content = make_channel_proxy(conn, channel_props[0], 'Call.Content.Draft') content_props = call_content.Get(cs.CALL_CONTENT, 'Streams', dbus_interface=dbus.PROPERTIES_IFACE) assertLength(1, content_props) assert len(content_props[0]) > 0 assertNotEquals('/', content_props[0]) # Test the call's Stream's properties call_stream = make_channel_proxy(conn, content_props[0], 'Call.Stream.Interface.Media.Draft') stream_props = call_stream.GetAll(cs.CALL_STREAM_IFACE_MEDIA, dbus_interface=dbus.PROPERTIES_IFACE) assertEquals(cs.CALL_STREAM_TRANSPORT_GTALK_P2P, stream_props['Transport']) test_stun_server(stream_props['STUNServers'], expected_stun_servers) assertEquals(expected_relays, stream_props['RelayInfo']) assertEquals(True, stream_props['HasServerInfo']) if __name__ == '__main__': # StreamedMedia tests test_all_dialects(partial(test_streamed_media, google=False)) test_all_dialects(partial(test_streamed_media, google=False, expected_stun_servers=[('5.4.3.2', 54321)]), params={'fallback-stun-server': 'resolves-to-5.4.3.2', 'fallback-stun-port': dbus.UInt16(54321)}) test_all_dialects(partial(test_streamed_media, google=False, expected_stun_servers=[('5.4.3.2', 1)]), params={'account': 'test@stunning.localhost'}) if GOOGLE_RELAY_ENABLED: test_all_dialects(partial(test_streamed_media, google=True, expected_stun_servers=[('1.2.3.4', 12345)]), protocol=GoogleXmlStream) test_all_dialects(partial(test_streamed_media, google=True, expected_stun_servers=[('5.4.3.2', 54321)]), protocol=GoogleXmlStream, params={'stun-server': 'resolves-to-5.4.3.2', 'stun-port': dbus.UInt16(54321)}) test_all_dialects(partial(test_streamed_media, google=True, expected_stun_servers=[('1.2.3.4', 12345)]), protocol=GoogleXmlStream, params={'fallback-stun-server': 'resolves-to-5.4.3.2', 'fallback-stun-port': dbus.UInt16(54321)}) test_all_dialects(partial(test_streamed_media, google=True, google_push_replacements=('resolves-to-5.4.3.2', '3838'), expected_stun_servers=[('5.4.3.2', 3838)]), protocol=GoogleXmlStream) else: print "NOTE: built with --disable-google-relay; omitting StreamedMedia tests with Google relay" # Call tests if CHANNEL_TYPE_CALL_ENABLED: test_all_dialects(partial(test_call, google=False)) test_all_dialects(partial(test_call, google=False, expected_stun_servers=[('5.4.3.2', 54321)]), params={'fallback-stun-server': 'resolves-to-5.4.3.2', 'fallback-stun-port': dbus.UInt16(54321)}) test_all_dialects(partial(test_call, google=False, expected_stun_servers=[('5.4.3.2', 1)]), params={'account': 'test@stunning.localhost'}) else: print "NOTE: built with --disable-channel-type-call; omitting Call tests" if CHANNEL_TYPE_CALL_ENABLED and GOOGLE_RELAY_ENABLED: test_all_dialects(partial(test_call, google=True, expected_stun_servers=[('1.2.3.4', 12345)]), protocol=GoogleXmlStream) test_all_dialects(partial(test_call, google=True, expected_stun_servers=[('5.4.3.2', 54321)]), protocol=GoogleXmlStream, params={'stun-server': 'resolves-to-5.4.3.2', 'stun-port': dbus.UInt16(54321)}) test_all_dialects(partial(test_call, google=True, expected_stun_servers=[('1.2.3.4', 12345)]), protocol=GoogleXmlStream, params={'fallback-stun-server': 'resolves-to-5.4.3.2', 'fallback-stun-port': dbus.UInt16(54321)}) test_all_dialects(partial(test_call, google=True, google_push_replacements=('resolves-to-5.4.3.2', '3838'), expected_stun_servers=[('5.4.3.2', 3838)]), protocol=GoogleXmlStream) else: print "NOTE: built with --disable-channel-type-call or with --disable-google-relay; omitting Call tests with Google relay" telepathy-gabble-0.18.2/tests/twisted/jingle/stream-handler-error.py0000644000175000017500000000411612227000321025510 0ustar00smcvsmcv00000000000000""" Test handling of errors from StreamHandler during calls. This is a regression test for a bug introduced by 54021cee0ad38 which removed an idle callback masking refcounting assumptions. """ from functools import partial from gabbletest import exec_test from servicetest import make_channel_proxy import jingletest2 import constants as cs from config import VOIP_ENABLED if not VOIP_ENABLED: print "NOTE: built with --disable-voip" raise SystemExit(77) def test(q, bus, conn, stream, call_error_on): jp = jingletest2.JingleProtocol031() jt = jingletest2.JingleTest2(jp, conn, q, stream, 'test@localhost', 'foo@bar.com/Foo') remote_handle = conn.RequestHandles(1, [jt.peer])[0] # Remote end calls us jt.incoming_call() # FIXME: these signals are not observable by real clients, since they # happen before NewChannels. # The caller is in members e = q.expect('dbus-signal', signal='MembersChanged', args=[u'', [remote_handle], [], [], [], 0, 0]) # We're pending because of remote_handle e = q.expect('dbus-signal', signal='MembersChanged', args=[u'', [], [], [1L], [], remote_handle, cs.GC_REASON_INVITED]) media_chan_suffix = e.path e = q.expect('dbus-signal', signal='NewSessionHandler') session_handler = make_channel_proxy(conn, e.args[0], 'Media.SessionHandler') if call_error_on == 'session': session_handler.Error(0, "this has been deprecated for years") else: session_handler.Ready() e = q.expect('dbus-signal', signal='NewStreamHandler') # S-E gets notified about a newly-created stream stream_handler = make_channel_proxy(conn, e.args[0], 'Media.StreamHandler') # Something goes wrong immediately! stream_handler.Error(0, "i'll have the eggs tostada please") # Gabble doesn't fall over, and the channel closes nicely. e = q.expect('dbus-signal', signal='Closed', path=media_chan_suffix) if __name__ == '__main__': exec_test(partial(test, call_error_on='stream')) exec_test(partial(test, call_error_on='session')) telepathy-gabble-0.18.2/tests/twisted/jingle/stream-errors-on-terminate.py0000644000175000017500000001213612227000321026661 0ustar00smcvsmcv00000000000000""" Test StreamError events and on session terminate, both directions. """ import dbus from gabbletest import make_result_iq, sync_stream, exec_test from servicetest import ( make_channel_proxy, unwrap, EventPattern, assertEquals, assertLength) from jingletest2 import JingleTest2, JingleProtocol031 import constants as cs from twisted.words.xish import xpath from config import VOIP_ENABLED if not VOIP_ENABLED: print "NOTE: built with --disable-voip" raise SystemExit(77) def _session_terminate_predicate(event, msg): reason = xpath.queryForNodes("/iq" "/jingle[@action='session-terminate']" "/reason/failed-application", event.stanza) reason_text = xpath.queryForString("/iq/jingle/reason/text", event.stanza) return reason is not None and reason_text == msg def _test_terminate_reason(jp, q, bus, conn, stream, incoming): jt = JingleTest2(jp, conn, q, stream, 'test@localhost', 'foo@bar.com/Foo') jt.prepare() self_handle = conn.GetSelfHandle() remote_handle = conn.RequestHandles(cs.HT_CONTACT, [jt.peer])[0] if incoming: jt.incoming_call() else: ret = conn.Requests.CreateChannel( { cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_STREAMED_MEDIA, cs.TARGET_HANDLE_TYPE: cs.HT_CONTACT, cs.TARGET_HANDLE: remote_handle, cs.INITIAL_AUDIO: True }) nc, e = q.expect_many( EventPattern('dbus-signal', signal='NewChannel', predicate=lambda e: cs.CHANNEL_TYPE_CONTACT_LIST not in e.args), EventPattern('dbus-signal', signal='NewSessionHandler')) path = nc.args[0] media_chan = make_channel_proxy(conn, path, 'Channel.Interface.Group') media_iface = make_channel_proxy(conn, path, 'Channel.Type.StreamedMedia') # S-E was notified about new session handler, and calls Ready on it session_handler = make_channel_proxy(conn, e.args[0], 'Media.SessionHandler') session_handler.Ready() nsh_event = q.expect('dbus-signal', signal='NewStreamHandler') # S-E gets notified about a newly-created stream stream_handler = make_channel_proxy(conn, nsh_event.args[0], 'Media.StreamHandler') group_props = media_chan.GetAll( cs.CHANNEL_IFACE_GROUP, dbus_interface=dbus.PROPERTIES_IFACE) if incoming: assertEquals([remote_handle], group_props['Members']) assertEquals(unwrap(group_props['LocalPendingMembers']), [(self_handle, remote_handle, cs.GC_REASON_INVITED, '')]) else: assertEquals([self_handle], group_props['Members']) streams = media_chan.ListStreams( dbus_interface=cs.CHANNEL_TYPE_STREAMED_MEDIA) stream_id = streams[0][0] stream_handler.NewNativeCandidate("fake", jt.get_remote_transports_dbus()) stream_handler.Ready(jt.dbusify_codecs([("FOO", 5, 8000, {})])) msg = u"None of the codecs are good for us, damn!" expected_events = [] if incoming: q.expect('dbus-signal', signal='SetRemoteCodecs') stream_handler.Error(cs.MEDIA_STREAM_ERROR_CODEC_NEGOTIATION_FAILED, msg) expected_events = [EventPattern( "stream-iq", iq_type="set", predicate=lambda x: _session_terminate_predicate(x, msg))] rejector = self_handle else: stream_handler.StreamState(cs.MEDIA_STREAM_STATE_CONNECTED) session_initiate = q.expect( 'stream-iq', predicate=jp.action_predicate('session-initiate')) q.expect('dbus-signal', signal='MembersChanged', path=path, args=['', [], [], [], [remote_handle], self_handle, cs.GC_REASON_INVITED]) jt.parse_session_initiate(session_initiate.query) stream.send(jp.xml(jp.ResultIq('test@localhost', session_initiate.stanza, []))) jt.terminate('failed-application', msg) rejector = remote_handle expected_events += [ EventPattern('dbus-signal', signal='StreamError', interface=cs.CHANNEL_TYPE_STREAMED_MEDIA, path=path, args=[stream_id, cs.MEDIA_STREAM_ERROR_CODEC_NEGOTIATION_FAILED, msg]), EventPattern('dbus-signal', signal='MembersChanged', interface=cs.CHANNEL_IFACE_GROUP, path=path, args=[msg, [], [self_handle, remote_handle], [], [], rejector, cs.GC_REASON_ERROR])] q.expect_many(*expected_events) q.expect('dbus-signal', signal='Closed', path=path) def test_terminate_outgoing(jp, q, bus, conn, stream): _test_terminate_reason(jp, q, bus, conn, stream, False) def test_terminate_incoming(jp, q, bus, conn, stream): _test_terminate_reason(jp, q, bus, conn, stream, True) if __name__ == '__main__': for f in (test_terminate_incoming, test_terminate_outgoing): exec_test( lambda q, b, c, s: f(JingleProtocol031(), q, b, c, s)) telepathy-gabble-0.18.2/tests/twisted/jingle/stream-errors-on-content-reject.py0000644000175000017500000002263012227000321027615 0ustar00smcvsmcv00000000000000""" Test StreamError events when new content is rejected in-call. """ import dbus from gabbletest import make_result_iq, sync_stream, exec_test from servicetest import ( make_channel_proxy, unwrap, EventPattern, assertEquals, assertLength) from jingletest2 import JingleTest2, JingleProtocol031 import constants as cs from twisted.words.xish import xpath from config import VOIP_ENABLED if not VOIP_ENABLED: print "NOTE: built with --disable-voip" raise SystemExit(77) def _content_reject_predicate(event): reason = xpath.queryForNodes("/iq" "/jingle[@action='content-reject']" "/reason/failed-application", event.stanza) return bool(reason) def _start_audio_session(jp, q, bus, conn, stream, incoming): jt = JingleTest2(jp, conn, q, stream, 'test@localhost', 'foo@bar.com/Foo') jt.prepare() self_handle = conn.GetSelfHandle() remote_handle = conn.RequestHandles(cs.HT_CONTACT, [jt.peer])[0] if incoming: jt.incoming_call() else: ret = conn.Requests.CreateChannel( { cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_STREAMED_MEDIA, cs.TARGET_HANDLE_TYPE: cs.HT_CONTACT, cs.TARGET_HANDLE: remote_handle, cs.INITIAL_AUDIO: True }) nc, e = q.expect_many( EventPattern('dbus-signal', signal='NewChannel', predicate=lambda e: cs.CHANNEL_TYPE_CONTACT_LIST not in e.args), EventPattern('dbus-signal', signal='NewSessionHandler')) path = nc.args[0] media_chan = make_channel_proxy(conn, path, 'Channel.Interface.Group') media_iface = make_channel_proxy(conn, path, 'Channel.Type.StreamedMedia') # S-E was notified about new session handler, and calls Ready on it session_handler = make_channel_proxy(conn, e.args[0], 'Media.SessionHandler') session_handler.Ready() nsh_event = q.expect('dbus-signal', signal='NewStreamHandler') # S-E gets notified about a newly-created stream stream_handler = make_channel_proxy(conn, nsh_event.args[0], 'Media.StreamHandler') group_props = media_chan.GetAll( cs.CHANNEL_IFACE_GROUP, dbus_interface=dbus.PROPERTIES_IFACE) if incoming: assertEquals([remote_handle], group_props['Members']) assertEquals(unwrap(group_props['LocalPendingMembers']), [(self_handle, remote_handle, cs.GC_REASON_INVITED, '')]) else: assertEquals([self_handle], group_props['Members']) streams = media_chan.ListStreams( dbus_interface=cs.CHANNEL_TYPE_STREAMED_MEDIA) stream_id = streams[0][0] stream_handler.NewNativeCandidate("fake", jt.get_remote_transports_dbus()) stream_handler.Ready(jt.dbusify_codecs([("FOO", 5, 8000, {})])) msg = u"None of the codecs are good for us, damn!" expected_events = [] if incoming: stream_handler.StreamState(cs.MEDIA_STREAM_STATE_CONNECTED) stream_handler.SupportedCodecs(jt.get_audio_codecs_dbus()) e = q.expect('stream-iq', predicate=jp.action_predicate('transport-info')) assertEquals(jt.peer, e.query['initiator']) content = xpath.queryForNodes('/iq/jingle/content', e.stanza)[0] assertEquals('initiator', content['creator']) stream.send(make_result_iq(stream, e.stanza)) media_chan.AddMembers([self_handle], 'accepted') memb, acc, _, _, _ = q.expect_many( EventPattern('dbus-signal', signal='MembersChanged', args=[u'', [self_handle], [], [], [], self_handle, cs.GC_REASON_NONE]), EventPattern('stream-iq', predicate=jp.action_predicate('session-accept')), EventPattern('dbus-signal', signal='SetStreamSending', args=[True]), EventPattern('dbus-signal', signal='SetStreamPlaying', args=[True]), EventPattern('dbus-signal', signal='StreamDirectionChanged', args=[stream_id, cs.MEDIA_STREAM_DIRECTION_BIDIRECTIONAL, 0])) stream.send(make_result_iq(stream, acc.stanza)) active_event = jp.rtp_info_event("active") if active_event is not None: q.expect_many(active_event) members = media_chan.GetMembers() assert set(members) == set([self_handle, remote_handle]), members else: stream_handler.StreamState(cs.MEDIA_STREAM_STATE_CONNECTED) session_initiate = q.expect( 'stream-iq', predicate=jp.action_predicate('session-initiate')) q.expect('dbus-signal', signal='MembersChanged', path=path, args=['', [], [], [], [remote_handle], self_handle, cs.GC_REASON_INVITED]) jt.parse_session_initiate(session_initiate.query) stream.send(jp.xml(jp.ResultIq('test@localhost', session_initiate.stanza, []))) jt.accept() q.expect_many( EventPattern('stream-iq', iq_type='result'), # Call accepted EventPattern('dbus-signal', signal='MembersChanged', args=['', [remote_handle], [], [], [], remote_handle, cs.GC_REASON_NONE]), ) return jt, media_iface def _start_audio_session_outgoing(jp, q, bus, conn, stream): return _start_audio_session(jp, q, bus, conn, stream, False) def _start_audio_session_incoming(jp, q, bus, conn, stream): return _start_audio_session(jp, q, bus, conn, stream, True) def _remote_content_add(jp, q, bus, conn, stream, initiate_call_func): jt, chan = initiate_call_func(jp, q, bus, conn, stream) video_codecs = [ jp.PayloadType(name, str(rate), str(id), parameters) \ for (name, id, rate, parameters) in jt.video_codecs] node = jp.SetIq(jt.peer, jt.jid, [ jp.Jingle(jt.sid, jt.peer, 'content-add', [ jp.Content( 'videostream', 'initiator', 'both', jp.Description('video', video_codecs), jp.TransportGoogleP2P()) ]) ]) stream.send(jp.xml(node)) _, nsh = q.expect_many( EventPattern('dbus-signal', signal='StreamAdded'), EventPattern('dbus-signal', signal='NewStreamHandler')) stream_handler_path, stream_id, media_type, direction = nsh.args video_handler = make_channel_proxy(conn, stream_handler_path, 'Media.StreamHandler') video_handler.NewNativeCandidate("fake", jt.get_remote_transports_dbus()) video_handler.Ready(jt.dbusify_codecs([("FOO", 5, 8000, {})])) msg = u"None of the codecs are good for us, damn!" video_handler.Error(cs.MEDIA_STREAM_ERROR_CODEC_NEGOTIATION_FAILED, msg) q.expect_many( EventPattern('dbus-signal', signal='StreamError', args=[stream_id, cs.MEDIA_STREAM_ERROR_CODEC_NEGOTIATION_FAILED, msg]), EventPattern('stream-iq', predicate=_content_reject_predicate)) def _local_content_add(jp, q, bus, conn, stream, initiate_call_func): jt, chan = initiate_call_func(jp, q, bus, conn, stream) remote_handle = conn.RequestHandles(cs.HT_CONTACT, [jt.peer])[0] chan.RequestStreams(remote_handle, [cs.MEDIA_STREAM_TYPE_VIDEO]) nsh = q.expect('dbus-signal', signal='NewStreamHandler') stream_handler_path, stream_id, media_type, direction = nsh.args video_handler = make_channel_proxy(conn, stream_handler_path, 'Media.StreamHandler') video_handler.NewNativeCandidate("fake", jt.get_remote_transports_dbus()) video_handler.Ready(jt.get_audio_codecs_dbus()) video_handler.StreamState(cs.MEDIA_STREAM_STATE_CONNECTED) e = q.expect('stream-iq', predicate=jp.action_predicate('content-add')) c = e.query.firstChildElement() stream.send(make_result_iq(stream, e.stanza)) node = jp.SetIq(jt.peer, jt.jid, [ jp.Jingle(jt.sid, jt.peer, 'content-reject', [ ('reason', None, {}, [ ('failed-application', None, {}, [])]), jp.Content(c['name'], c['creator'], c['senders']) ]) ]) stream.send(jp.xml(node)) q.expect('dbus-signal', signal='StreamError', args=[stream_id, cs.MEDIA_STREAM_ERROR_CODEC_NEGOTIATION_FAILED, ""]), def test_remote_content_add_incoming(jp, q, bus, conn, stream): _remote_content_add(jp, q, bus, conn, stream, _start_audio_session_incoming) def test_remote_content_add_outgoing(jp, q, bus, conn, stream): _remote_content_add(jp, q, bus, conn, stream, _start_audio_session_outgoing) def test_local_content_add_incoming(jp, q, bus, conn, stream): _local_content_add(jp, q, bus, conn, stream, _start_audio_session_incoming) def test_local_content_add_outgoing(jp, q, bus, conn, stream): _local_content_add(jp, q, bus, conn, stream, _start_audio_session_outgoing) if __name__ == '__main__': for f in (test_local_content_add_incoming, test_local_content_add_outgoing, test_remote_content_add_incoming, test_remote_content_add_outgoing): exec_test( lambda q, b, c, s: f(JingleProtocol031(), q, b, c, s)) telepathy-gabble-0.18.2/tests/twisted/jingle/session-id-collision.py0000644000175000017500000000265612227000321025530 0ustar00smcvsmcv00000000000000""" Regression test for a bug where Gabble did not namespace session IDs by the peer, leading to hilarity (and possible DOSing) if two peers picked the same sid. """ from jingletest2 import JingleTest2, test_all_dialects import constants as cs from config import VOIP_ENABLED if not VOIP_ENABLED: print "NOTE: built with --disable-voip" raise SystemExit(77) def test(jp, q, bus, conn, stream): jt1 = JingleTest2(jp, conn, q, stream, 'test@localhost', 'edgar@collabora.co.uk/Monitor') jt2 = JingleTest2(jp, conn, q, stream, 'test@localhost', 'wcc@collabora.co.uk/Pillow') jt1.prepare() jt2.send_presence_and_caps() # Two peers happen to pick the same Jingle session ID jt1.sid = '1' jt2.sid = '1' jt1.incoming_call() q.expect('dbus-signal', signal='NewChannel', predicate=lambda e: cs.CHANNEL_TYPE_CONTACT_LIST not in e.args) # If Gabble confuses the two sessions, it'll NAK the IQ rather than # realising this is a new call. jt2.incoming_call() q.expect('dbus-signal', signal='NewChannel', predicate=lambda e: cs.CHANNEL_TYPE_CONTACT_LIST not in e.args) # On the other hand, if the same person calls twice with the same sid, # Gabble _should_ NAK the second s-i. jt2.incoming_call() q.expect('stream-iq', iq_type='error', predicate=jp.action_predicate('session-initiate')) if __name__ == '__main__': test_all_dialects(test) telepathy-gabble-0.18.2/tests/twisted/jingle/preload-caps-crash.py0000644000175000017500000000302212200204333025116 0ustar00smcvsmcv00000000000000""" Regression test for a crash in which PRESENCE_CAP_GOOGLE_VOICE is preloaded and we don't have any per_channel_manager_caps by the time we receive a call, when called by gtalk2voip.com on behalf of a sipphone.com user. """ import dbus from gabbletest import make_result_iq, exec_test, sync_stream from servicetest import ( make_channel_proxy, unwrap, EventPattern, assertEquals, assertLength) from jingletest2 import JingleTest2, GtalkProtocol03 import constants as cs import ns from twisted.words.xish import xpath class MyJingleTest(JingleTest2): remote_caps = { 'ext': 'sidebar voice-v1', 'node': 'http://www.google.com/xmpp/client/caps', 'ver': '1.0.0.104' } def test(q, bus, conn, stream): jp = GtalkProtocol03() jt = MyJingleTest(jp, conn, q, stream, 'test@localhost', 'foo@gtalk2voip.com') jt.prepare(send_presence=False) # Send the presence from a bare JID to our bare JID... stream.send(jp.xml(jp.Presence('foo@gtalk2voip.com', 'test@localhost', jt.remote_caps))) event = q.expect('stream-iq', query_ns=ns.DISCO_INFO, to='foo@gtalk2voip.com') # ... then immediately send from a bare JID to our full JID stream.send(jp.xml(jp.Presence('foo@gtalk2voip.com', 'test@localhost/test', jt.remote_caps))) stream.send(jp.xml(jp.ResultIq('test@localhost/test', event.stanza, [ jp.Query(None, ns.DISCO_INFO, [ jp.Feature(x) for x in jp.features ]) ]) )) sync_stream(q, stream) if __name__ == '__main__': exec_test(test) telepathy-gabble-0.18.2/tests/twisted/jingle/payload-types.py0000644000175000017500000000716012227000321024250 0ustar00smcvsmcv00000000000000""" Regression test for https://bugs.freedesktop.org/show_bug.cgi?id=18918 """ import dbus from gabbletest import exec_test from servicetest import wrap_channel, make_channel_proxy import jingletest2 import constants as cs from config import VOIP_ENABLED if not VOIP_ENABLED: print "NOTE: built with --disable-voip" raise SystemExit(77) def test(q, bus, conn, stream): jp = jingletest2.JingleProtocol031() jt = jingletest2.JingleTest2(jp, conn, q, stream, 'test@localhost', 'foo@bar.com/Foo') self_handle = conn.GetSelfHandle() jt.send_presence_and_caps() handle = conn.RequestHandles(cs.HT_CONTACT, [jt.peer])[0] path = conn.RequestChannel( cs.CHANNEL_TYPE_STREAMED_MEDIA, cs.HT_CONTACT, handle, True) channel = wrap_channel(bus.get_object(conn.bus_name, path), 'StreamedMedia') # Test that codec parameters are correctly sent in children of # rather than as attributes of the latter. channel.StreamedMedia.RequestStreams(handle, [cs.MEDIA_STREAM_TYPE_AUDIO]) # S-E gets notified about new session handler, and calls Ready on it e = q.expect('dbus-signal', signal='NewSessionHandler') assert e.args[1] == 'rtp' session_handler = make_channel_proxy(conn, e.args[0], 'Media.SessionHandler') session_handler.Ready() e = q.expect('dbus-signal', signal='NewStreamHandler') stream_id = e.args[1] stream_handler = make_channel_proxy(conn, e.args[0], 'Media.StreamHandler') stream_handler.NewNativeCandidate("fake", jt.get_remote_transports_dbus()) codecs = dbus.Array( [ (96, 'speex', 0, 16000, 0, {'vbr': 'on'}) ], signature='(usuuua{ss})') stream_handler.Ready(codecs) stream_handler.StreamState(cs.MEDIA_STREAM_STATE_CONNECTED) e = q.expect('stream-iq') content = list(e.query.elements())[0] assert content.name == 'content' for child in content.elements(): if child.name == 'description': description = child break assert description is not None # there should be one tag for speex: assert len(list(description.elements())) == 1 payload_type = list(description.elements())[0] assert payload_type.name == 'payload-type' assert payload_type['name'] == 'speex' # the vbr parameter should not be an attribute on the , but # a child tag assert 'vbr' not in payload_type.attributes assert len(list(payload_type.elements())) == 1 parameter = list(payload_type.elements())[0] assert parameter.name == 'parameter' assert parameter['name'] == 'vbr' assert parameter['value'] == 'on' channel.Close() # Test that codec parameters are correctly extracted from # children of rather than from attributes of the latter. jt.audio_codecs = [ ('GSM', 3, 8000, {'misc': 'other'}) ] jt.incoming_call() e = q.expect('dbus-signal', signal='NewSessionHandler') assert e.args[1] == 'rtp' session_handler = make_channel_proxy(conn, e.args[0], 'Media.SessionHandler') session_handler.Ready() e = q.expect('dbus-signal', signal='NewStreamHandler') stream_id = e.args[1] stream_handler = make_channel_proxy(conn, e.args[0], 'Media.StreamHandler') stream_handler.Ready( dbus.Array( [], signature='(usuuua{ss})')) e = q.expect('dbus-signal', signal='SetRemoteCodecs') for codec in e.args[0]: id, name, type, rate, channels, parameters = codec assert len(parameters) == 1, parameters assert parameters['misc'] == 'other', parameters if __name__ == '__main__': exec_test(test) telepathy-gabble-0.18.2/tests/twisted/jingle/outgoing-many-streams.py0000644000175000017500000002072612227000321025731 0ustar00smcvsmcv00000000000000 """ Test making outgoing call using CreateChannel. This tests the happy scenario when the remote party accepts the call. """ import dbus from gabbletest import exec_test, sync_stream from servicetest import ( make_channel_proxy, call_async, EventPattern) import jingletest2 import gabbletest import constants as cs from config import VOIP_ENABLED if not VOIP_ENABLED: print "NOTE: built with --disable-voip" raise SystemExit(77) def test(q, bus, conn, stream): worker(q, bus, conn, stream, 'foo@bar.com/Foo') worker(q, bus, conn, stream, 'foo@sip.bar.com') def worker(q, bus, conn, stream, peer): jp = jingletest2.JingleProtocol031() jt = jingletest2.JingleTest2(jp, conn, q, stream, 'test@localhost', peer) self_handle = conn.GetSelfHandle() jt.send_presence_and_caps() handle = conn.RequestHandles(cs.HT_CONTACT, [jt.peer])[0] call_async(q, conn.Requests, 'CreateChannel', { cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_STREAMED_MEDIA, cs.TARGET_HANDLE_TYPE: cs.HT_CONTACT, cs.TARGET_HANDLE: handle, }) ret, old_sig, new_sig = q.expect_many( EventPattern('dbus-return', method='CreateChannel'), EventPattern('dbus-signal', signal='NewChannel', predicate=lambda e: cs.CHANNEL_TYPE_CONTACT_LIST not in e.args), EventPattern('dbus-signal', signal='NewChannels', predicate=lambda e: cs.CHANNEL_TYPE_CONTACT_LIST not in e.args[0][0][1].values()), ) path = ret.value[0] sig_path, sig_ct, sig_ht, sig_h, sig_sh = old_sig.args assert sig_path == path, (sig_path, path) assert sig_ct == cs.CHANNEL_TYPE_STREAMED_MEDIA, sig_ct assert sig_ht == cs.HT_CONTACT, sig_ht assert sig_h == handle, sig_h assert sig_sh == True # suppress handler assert len(new_sig.args) == 1 assert len(new_sig.args[0]) == 1 # one channel assert len(new_sig.args[0][0]) == 2 # two struct members assert new_sig.args[0][0][0] == path emitted_props = new_sig.args[0][0][1] assert emitted_props[cs.CHANNEL_TYPE] == cs.CHANNEL_TYPE_STREAMED_MEDIA assert emitted_props[cs.TARGET_HANDLE_TYPE] == cs.HT_CONTACT assert emitted_props[cs.TARGET_HANDLE] == handle assert emitted_props[cs.TARGET_ID] == jt.peer_bare_jid, emitted_props assert emitted_props[cs.REQUESTED] == True assert emitted_props[cs.INITIATOR_HANDLE] == self_handle assert emitted_props[cs.INITIATOR_ID] == 'test@localhost' signalling_iface = make_channel_proxy(conn, path, 'Channel.Interface.MediaSignalling') media_iface = make_channel_proxy(conn, path, 'Channel.Type.StreamedMedia') group_iface = make_channel_proxy(conn, path, 'Channel.Interface.Group') # Exercise basic Channel Properties from spec 0.17.7 channel_props = group_iface.GetAll(cs.CHANNEL, dbus_interface=dbus.PROPERTIES_IFACE) assert channel_props.get('TargetHandle') == handle, \ channel_props.get('TargetHandle') assert channel_props.get('TargetHandleType') == cs.HT_CONTACT,\ channel_props.get('TargetHandleType') assert media_iface.GetHandle(dbus_interface=cs.CHANNEL) == (cs.HT_CONTACT, handle) assert channel_props.get('ChannelType') == cs.CHANNEL_TYPE_STREAMED_MEDIA,\ channel_props.get('ChannelType') interfaces = channel_props['Interfaces'] for i in [cs.CHANNEL_IFACE_GROUP, cs.CHANNEL_IFACE_MEDIA_SIGNALLING, cs.TP_AWKWARD_PROPERTIES, cs.CHANNEL_IFACE_HOLD]: assert i in interfaces, (i, interfaces) assert channel_props['TargetID'] == jt.peer_bare_jid, channel_props assert channel_props['Requested'] == True assert channel_props['InitiatorID'] == 'test@localhost' assert channel_props['InitiatorHandle'] == self_handle # Exercise Group Properties from spec 0.17.6 (in a basic way) group_props = group_iface.GetAll(cs.CHANNEL_IFACE_GROUP, dbus_interface=dbus.PROPERTIES_IFACE) assert 'HandleOwners' in group_props, group_props assert 'Members' in group_props, group_props assert 'LocalPendingMembers' in group_props, group_props assert 'RemotePendingMembers' in group_props, group_props assert 'GroupFlags' in group_props, group_props # The remote contact shouldn't be in remote pending yet (nor should it be # in members!) assert handle not in group_props['RemotePendingMembers'], group_props assert handle not in group_props['Members'], group_props list_streams_result = media_iface.ListStreams() assert len(list_streams_result) == 0, list_streams_result # Asking for 4 audio and 3 video streams is pathological, but we claim to # support up to 99 streams, so we should test a decent number of them. # # More practically, the success of this test implies that the simpler case # of one audio stream and one video stream should easily work. streams = media_iface.RequestStreams(handle, [cs.MEDIA_STREAM_TYPE_AUDIO, cs.MEDIA_STREAM_TYPE_VIDEO, cs.MEDIA_STREAM_TYPE_VIDEO, cs.MEDIA_STREAM_TYPE_AUDIO, cs.MEDIA_STREAM_TYPE_AUDIO, cs.MEDIA_STREAM_TYPE_VIDEO, cs.MEDIA_STREAM_TYPE_AUDIO]) assert len(streams) == 7, streams streams_by_id = {} for s in streams: streams_by_id[s[0]] = s assert len(s) == 6, s assert s[1] == handle, (s, handle) assert s[2] in (cs.MEDIA_STREAM_TYPE_AUDIO, cs.MEDIA_STREAM_TYPE_VIDEO), s # We haven't connected yet assert s[3] == cs.MEDIA_STREAM_STATE_DISCONNECTED, s # In Gabble, requested streams start off bidirectional assert s[4] == cs.MEDIA_STREAM_DIRECTION_BIDIRECTIONAL, s assert s[5] == 0, s # no pending send # the streams should all have unique IDs stream_ids = streams_by_id.keys() assert len(stream_ids) == 7 # the streams should come out in the same order as the requests assert streams[0][2] == cs.MEDIA_STREAM_TYPE_AUDIO, streams[0] assert streams[1][2] == cs.MEDIA_STREAM_TYPE_VIDEO, streams[0] assert streams[2][2] == cs.MEDIA_STREAM_TYPE_VIDEO, streams[0] assert streams[3][2] == cs.MEDIA_STREAM_TYPE_AUDIO, streams[0] assert streams[4][2] == cs.MEDIA_STREAM_TYPE_AUDIO, streams[0] assert streams[5][2] == cs.MEDIA_STREAM_TYPE_VIDEO, streams[0] assert streams[6][2] == cs.MEDIA_STREAM_TYPE_AUDIO, streams[0] # The ListStreams() result must be the streams we got from RequestStreams, # but this time the order is unimportant list_streams_result = media_iface.ListStreams() listed_streams_by_id = {} for s in list_streams_result: listed_streams_by_id[s[0]] = s assert listed_streams_by_id == streams_by_id, (listed_streams_by_id, streams_by_id) # S-E gets notified about new session handler, and calls Ready on it e = q.expect('dbus-signal', signal='NewSessionHandler') assert e.args[1] == 'rtp' session_handler_path = e.args[0] session_handler = make_channel_proxy(conn, session_handler_path, 'Media.SessionHandler') session_handler.Ready() stream_handler_paths = [] # give all 7 streams some candidates for i in xrange(7): e = q.expect('dbus-signal', signal='NewStreamHandler') stream_handler_paths.append(e.args[0]) stream_handler = make_channel_proxy(conn, e.args[0], 'Media.StreamHandler') stream_handler.NewNativeCandidate("fake", jt.get_remote_transports_dbus()) stream_handler.Ready(jt.get_audio_codecs_dbus()) stream_handler.StreamState(cs.MEDIA_STREAM_STATE_CONNECTED) e = q.expect('stream-iq') assert e.query.name == 'jingle' assert e.query['action'] == 'session-initiate' stream.send(gabbletest.make_result_iq(stream, e.stanza)) jt.parse_session_initiate(e.query) jt.accept() q.expect('stream-iq', iq_type='result') # Time passes ... afterwards we close the chan group_iface.RemoveMembers([self_handle], 'closed') # Everything closes closes = [ EventPattern('dbus-signal', signal='Close', path=stream_handler_paths[i]) for i in range(0,7) ] removeds = [ EventPattern('dbus-signal', signal='StreamRemoved', args=[stream_ids[i]], path=path) for i in range(0,7) ] q.expect_many( EventPattern('dbus-signal', signal='ChannelClosed', args=[path]), *(closes + removeds) ) if __name__ == '__main__': exec_test(test) telepathy-gabble-0.18.2/tests/twisted/jingle/outgoing-ensure.py0000644000175000017500000001746512227000321024620 0ustar00smcvsmcv00000000000000""" Test making outgoing calls using EnsureChannel, and retrieving existing calls using EnsureChannel. This also exercises calls to a contact on a SIP gateway, who has no resource, only a bare JID. """ from functools import partial from gabbletest import exec_test from servicetest import ( wrap_channel, call_async, EventPattern, assertEquals, assertLength, ) import constants as cs from jingletest2 import JingleProtocol031, JingleTest2 from config import VOIP_ENABLED if not VOIP_ENABLED: print "NOTE: built with --disable-voip" raise SystemExit(77) def test(q, bus, conn, stream, channel_type): jt = JingleTest2(JingleProtocol031(), conn, q, stream, 'test@localhost', 'foo@sip.bar.com') jt.prepare() self_handle = conn.GetSelfHandle() handle = conn.RequestHandles(cs.HT_CONTACT, [jt.peer])[0] request = { cs.CHANNEL_TYPE: channel_type, cs.TARGET_HANDLE_TYPE: cs.HT_CONTACT, cs.TARGET_HANDLE: handle} if channel_type == cs.CHANNEL_TYPE_CALL: request[cs.CALL_INITIAL_AUDIO] = True # Ensure a channel that doesn't exist yet. call_async(q, conn.Requests, 'EnsureChannel', request) ret, old_sig, new_sig = q.expect_many( EventPattern('dbus-return', method='EnsureChannel'), EventPattern('dbus-signal', signal='NewChannel', predicate=lambda e: cs.CHANNEL_TYPE_CONTACT_LIST not in e.args), EventPattern('dbus-signal', signal='NewChannels', predicate=lambda e: cs.CHANNEL_TYPE_CONTACT_LIST not in e.args[0][0][1].values()), ) yours, path, props = ret.value # this channel was created in response to our EnsureChannel call, so it # should be ours. assert yours, ret.value sig_path, sig_ct, sig_ht, sig_h, sig_sh = old_sig.args assertEquals(sig_path, path) assertEquals(channel_type, sig_ct) assertEquals(cs.HT_CONTACT, sig_ht) assertEquals(handle, sig_h) assert sig_sh # suppress handler assertLength(1, new_sig.args) assertLength(1, new_sig.args[0]) # one channel assertLength(2, new_sig.args[0][0]) # two struct members assertEquals(path, new_sig.args[0][0][0]) emitted_props = new_sig.args[0][0][1] assertEquals(channel_type, emitted_props[cs.CHANNEL_TYPE]) assertEquals(cs.HT_CONTACT, emitted_props[cs.TARGET_HANDLE_TYPE]) assertEquals(handle, emitted_props[cs.TARGET_HANDLE]) assertEquals(jt.peer_bare_jid, emitted_props[cs.TARGET_ID]) assert emitted_props[cs.REQUESTED] assertEquals(self_handle, emitted_props[cs.INITIATOR_HANDLE]) assertEquals('test@localhost', emitted_props[cs.INITIATOR_ID]) # Now ensure a media channel with the same contact, and check it's the # same. call_async(q, conn.Requests, 'EnsureChannel', request) event = q.expect('dbus-return', method='EnsureChannel') yours2, path2, props2 = event.value # We should have got back the same channel we created a page or so ago. assertEquals(path2, path) # It's not been created for this call, so Yours should be False. assert not yours2 # Time passes ... afterwards we close the chan chan = wrap_channel(bus.get_object(conn.bus_name, path), 'StreamedMedia') chan.Close() # Ensure a channel that doesn't exist yet. call_async(q, conn.Requests, 'EnsureChannel', request) # Re-ensure a channel that is hopefully still pending creation. call_async(q, conn.Requests, 'EnsureChannel', request) ret, ret2, old_sig, new_sig = q.expect_many( EventPattern('dbus-return', method='EnsureChannel'), EventPattern('dbus-return', method='EnsureChannel'), EventPattern('dbus-signal', signal='NewChannel', predicate=lambda e: cs.CHANNEL_TYPE_CONTACT_LIST not in e.args), EventPattern('dbus-signal', signal='NewChannels', predicate=lambda e: cs.CHANNEL_TYPE_CONTACT_LIST not in e.args[0][0][1].values()), ) yours, path, props = ret.value # this channel was created in response to our EnsureChannel call, so it # should be ours. assert yours, ret.value sig_path, sig_ct, sig_ht, sig_h, sig_sh = old_sig.args assertEquals(sig_path, path) assertEquals(channel_type, sig_ct) assertEquals(cs.HT_CONTACT, sig_ht) assertEquals(handle, sig_h) assert sig_sh # suppress handler assertLength(1, new_sig.args) assertLength(1, new_sig.args[0]) # one channel assertLength(2, new_sig.args[0][0]) # two struct members assertEquals(path, new_sig.args[0][0][0]) emitted_props = new_sig.args[0][0][1] assertEquals(channel_type, emitted_props[cs.CHANNEL_TYPE]) assertEquals(cs.HT_CONTACT, emitted_props[cs.TARGET_HANDLE_TYPE]) assertEquals(handle, emitted_props[cs.TARGET_HANDLE]) assertEquals(jt.peer_bare_jid, emitted_props[cs.TARGET_ID]) assert emitted_props[cs.REQUESTED] assertEquals(self_handle, emitted_props[cs.INITIATOR_HANDLE]) assertEquals('test@localhost', emitted_props[cs.INITIATOR_ID]) yours2, path2, props2 = ret2.value # We should have got back the same channel we created a page or so ago. assertEquals(path2, path) # It's not been created for this call, so Yours should be False. assert not yours2 # Time passes ... afterwards we close the chan chan = wrap_channel(bus.get_object(conn.bus_name, path), 'StreamedMedia') chan.Close() # The remaining checks don't apply to calls if channel_type == cs.CHANNEL_TYPE_CALL: return # Now, create an anonymous channel with RequestChannel, add the other # person to it with RequestStreams, then Ensure a media channel with that # person. We should get the anonymous channel back. call_async( q, conn, 'RequestChannel', channel_type, 0, 0, True) ret, old_sig, new_sig = q.expect_many( EventPattern('dbus-return', method='RequestChannel'), EventPattern('dbus-signal', signal='NewChannel', predicate=lambda e: cs.CHANNEL_TYPE_CONTACT_LIST not in e.args), EventPattern('dbus-signal', signal='NewChannels', predicate=lambda e: cs.CHANNEL_TYPE_CONTACT_LIST not in e.args[0][0][1].values()), ) path = ret.value[0] assertEquals( [path, channel_type, cs.HT_NONE, 0, True], old_sig.args) assertLength(1, new_sig.args) assertLength(1, new_sig.args[0]) # one channel assertLength(2, new_sig.args[0][0]) # two struct members assertEquals(path, new_sig.args[0][0][0]) emitted_props = new_sig.args[0][0][1] assertEquals(channel_type, emitted_props[cs.CHANNEL_TYPE]) assertEquals(cs.HT_NONE, emitted_props[cs.TARGET_HANDLE_TYPE]) assertEquals(0, emitted_props[cs.TARGET_HANDLE]) assertEquals('', emitted_props[cs.TARGET_ID]) assert emitted_props[cs.REQUESTED] assertEquals(self_handle, emitted_props[cs.INITIATOR_HANDLE]) assertEquals('test@localhost', emitted_props[cs.INITIATOR_ID]) chan = wrap_channel(bus.get_object(conn.bus_name, path), 'StreamedMedia') # Request streams with the other person. This should make them the # channel's "peer" property. chan.StreamedMedia.RequestStreams(handle, [cs.MEDIA_STREAM_TYPE_AUDIO]) # Now, Ensuring a media channel with handle should yield the channel just # created. call_async(q, conn.Requests, 'EnsureChannel', request) event = q.expect('dbus-return', method='EnsureChannel') yours, path2, _ = event.value # we should have got back the anonymous channel we got with requestchannel # and called RequestStreams(handle) on. assertEquals(path2, path) # It's not been created for this call, so Yours should be False. assert not yours chan.Close() if __name__ == '__main__': exec_test(partial(test, channel_type=cs.CHANNEL_TYPE_STREAMED_MEDIA)) exec_test(partial(test, channel_type=cs.CHANNEL_TYPE_CALL)) telepathy-gabble-0.18.2/tests/twisted/jingle/outgoing-basics.py0000644000175000017500000003240112227000321024546 0ustar00smcvsmcv00000000000000""" Test basic outgoing call handling, using CreateChannel and all three variations of RequestChannel. """ from functools import partial import dbus from twisted.words.xish import xpath from gabbletest import exec_test from servicetest import ( make_channel_proxy, wrap_channel, EventPattern, call_async, assertEquals, assertContains, assertLength, ) import constants as cs from jingletest2 import JingleTest2, test_all_dialects from config import VOIP_ENABLED if not VOIP_ENABLED: print "NOTE: built with --disable-voip" raise SystemExit(77) # There are various deprecated APIs for requesting calls, documented at # . # These are ordered from most recent to most deprecated. CREATE = 0 # CreateChannel({TargetHandleType: Contact, TargetHandle: h}); # RequestStreams() REQUEST_ANONYMOUS = 1 # RequestChannel(HandleTypeNone, 0); RequestStreams() REQUEST_ANONYMOUS_AND_ADD = 2 # RequestChannel(HandleTypeNone, 0); # AddMembers([h], ...); RequestStreams(h,...) REQUEST_NONYMOUS = 3 # RequestChannel(HandleTypeContact, h); # RequestStreams(h, ...) def create(jp, q, bus, conn, stream, peer='foo@bar.com/Res'): worker(jp, q, bus, conn, stream, CREATE, peer) def request_anonymous(jp, q, bus, conn, stream, peer='publish@foo.com/Res'): worker(jp, q, bus, conn, stream, REQUEST_ANONYMOUS, peer) def request_anonymous_and_add(jp, q, bus, conn, stream, peer='publish-subscribe@foo.com/Res'): worker(jp, q, bus, conn, stream, REQUEST_ANONYMOUS_AND_ADD, peer) def request_nonymous(jp, q, bus, conn, stream, peer='subscribe@foo.com/Res'): worker(jp, q, bus, conn, stream, REQUEST_NONYMOUS, peer) def worker(jp, q, bus, conn, stream, variant, peer): jt2 = JingleTest2(jp, conn, q, stream, 'test@localhost', peer) jt2.prepare(send_presence=True, send_roster=True) self_handle = conn.GetSelfHandle() remote_handle = conn.RequestHandles(1, [jt2.peer])[0] if variant == REQUEST_NONYMOUS: path = conn.RequestChannel(cs.CHANNEL_TYPE_STREAMED_MEDIA, cs.HT_CONTACT, remote_handle, True) elif variant == CREATE: path = conn.Requests.CreateChannel({ cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_STREAMED_MEDIA, cs.TARGET_HANDLE_TYPE: cs.HT_CONTACT, cs.TARGET_HANDLE: remote_handle, })[0] else: path = conn.RequestChannel(cs.CHANNEL_TYPE_STREAMED_MEDIA, cs.HT_NONE, 0, True) old_sig, new_sig = q.expect_many( EventPattern('dbus-signal', signal='NewChannel', predicate=lambda e: cs.CHANNEL_TYPE_CONTACT_LIST not in e.args), EventPattern('dbus-signal', signal='NewChannels', predicate=lambda e: cs.CHANNEL_TYPE_CONTACT_LIST not in e.args[0][0][1].values()), ) if variant == REQUEST_NONYMOUS or variant == CREATE: assertEquals( [path, cs.CHANNEL_TYPE_STREAMED_MEDIA, cs.HT_CONTACT, remote_handle, True], old_sig.args) else: assertEquals( [path, cs.CHANNEL_TYPE_STREAMED_MEDIA, cs.HT_NONE, 0, True], old_sig.args) assertLength(1, new_sig.args) assertLength(1, new_sig.args[0]) # one channel assertLength(2, new_sig.args[0][0]) # two struct members emitted_props = new_sig.args[0][0][1] assertEquals( cs.CHANNEL_TYPE_STREAMED_MEDIA, emitted_props[cs.CHANNEL_TYPE]) if variant == REQUEST_NONYMOUS or variant == CREATE: assertEquals(remote_handle, emitted_props[cs.TARGET_HANDLE]) assertEquals(cs.HT_CONTACT, emitted_props[cs.TARGET_HANDLE_TYPE]) assertEquals(jt2.peer_bare_jid, emitted_props[cs.TARGET_ID]) else: assertEquals(0, emitted_props[cs.TARGET_HANDLE]) assertEquals(cs.HT_NONE, emitted_props[cs.TARGET_HANDLE_TYPE]) assertEquals('', emitted_props[cs.TARGET_ID]) assertEquals(True, emitted_props[cs.REQUESTED]) assertEquals(self_handle, emitted_props[cs.INITIATOR_HANDLE]) assertEquals('test@localhost', emitted_props[cs.INITIATOR_ID]) chan = wrap_channel(bus.get_object(conn.bus_name, path), 'StreamedMedia', ['MediaSignalling']) # Exercise basic Channel Properties channel_props = chan.Properties.GetAll(cs.CHANNEL) assertEquals(cs.CHANNEL_TYPE_STREAMED_MEDIA, channel_props.get('ChannelType')) if variant == REQUEST_NONYMOUS or variant == CREATE: assertEquals(remote_handle, channel_props['TargetHandle']) assertEquals(cs.HT_CONTACT, channel_props['TargetHandleType']) assertEquals(jt2.peer_bare_jid, channel_props['TargetID']) assertEquals((cs.HT_CONTACT, remote_handle), chan.GetHandle()) else: assertEquals(0, channel_props['TargetHandle']) assertEquals(cs.HT_NONE, channel_props['TargetHandleType']) assertEquals('', channel_props['TargetID']) assertEquals((cs.HT_NONE, 0), chan.GetHandle()) for interface in [ cs.CHANNEL_IFACE_GROUP, cs.CHANNEL_IFACE_MEDIA_SIGNALLING, cs.TP_AWKWARD_PROPERTIES, cs.CHANNEL_IFACE_HOLD]: assertContains(interface, channel_props['Interfaces']) assertEquals(True, channel_props['Requested']) assertEquals('test@localhost', channel_props['InitiatorID']) assertEquals(conn.GetSelfHandle(), channel_props['InitiatorHandle']) # Exercise Group Properties group_props = chan.Properties.GetAll(cs.CHANNEL_IFACE_GROUP) assertEquals([self_handle], group_props['Members']) assertEquals([], group_props['LocalPendingMembers']) if variant == REQUEST_NONYMOUS: # In this variant, they're meant to be in RP even though we've sent # nothing assertEquals([remote_handle], group_props['RemotePendingMembers']) else: # For an anonymous channel, the peer isn't yet known; for a Create-d # channel, the peer only appears in RP when we actually send them the # session-initiate assertEquals([], group_props['RemotePendingMembers']) if variant == REQUEST_ANONYMOUS_AND_ADD: # but we should be allowed to add the peer. chan.Group.AddMembers([remote_handle], 'I love backwards compat') base_flags = cs.GF_MEMBERS_CHANGED_DETAILED | cs.GF_PROPERTIES | cs.GF_MESSAGE_REMOVE \ | cs.GF_MESSAGE_REJECT | cs.GF_MESSAGE_RESCIND if variant == REQUEST_ANONYMOUS_AND_ADD or variant == REQUEST_ANONYMOUS: expected_flags = base_flags | cs.GF_CAN_ADD else: expected_flags = base_flags assertEquals(expected_flags, group_props['GroupFlags']) assertEquals({}, group_props['HandleOwners']) assertEquals([], chan.StreamedMedia.ListStreams()) streams = chan.StreamedMedia.RequestStreams(remote_handle, [cs.MEDIA_STREAM_TYPE_AUDIO]) assertEquals(streams, chan.StreamedMedia.ListStreams()) assertLength(1, streams) # streams[0][0] is the stream identifier, which in principle we can't # make any assertion about (although in practice it's probably 1) assertEquals(( remote_handle, cs.MEDIA_STREAM_TYPE_AUDIO, # We haven't connected yet cs.MEDIA_STREAM_STATE_DISCONNECTED, # In Gabble, requested streams start off bidirectional cs.MEDIA_STREAM_DIRECTION_BIDIRECTIONAL, 0), streams[0][1:]) # S-E gets notified about new session handler, and calls Ready on it e = q.expect('dbus-signal', signal='NewSessionHandler') assert e.args[1] == 'rtp' session_handler = make_channel_proxy(conn, e.args[0], 'Media.SessionHandler') session_handler.Ready() e = q.expect('dbus-signal', signal='NewStreamHandler') stream_handler = make_channel_proxy(conn, e.args[0], 'Media.StreamHandler') stream_handler.NewNativeCandidate("fake", jt2.get_remote_transports_dbus()) stream_handler.Ready(jt2.get_audio_codecs_dbus()) stream_handler.StreamState(cs.MEDIA_STREAM_STATE_CONNECTED) sh_props = stream_handler.GetAll( cs.STREAM_HANDLER, dbus_interface=dbus.PROPERTIES_IFACE) assertEquals('gtalk-p2p', sh_props['NATTraversal']) assertEquals(True, sh_props['CreatedLocally']) if variant == CREATE: # When we actually send XML to the peer, they should pop up in remote # pending. session_initiate, _ = q.expect_many( EventPattern('stream-iq', iq_type='set', predicate=lambda e: jp.match_jingle_action(e.query, 'session-initiate')), EventPattern('dbus-signal', signal='MembersChanged', args=["", [], [], [], [remote_handle], self_handle, cs.GC_REASON_INVITED]), ) else: forbidden = [] if peer.split('/', 1)[0] in ( 'publish@foo.com', 'publish-subscribe@foo.com'): forbidden = [EventPattern('stream-presence')] q.forbid_events(forbidden) else: # we're calling someone not on our roster, so we'll send directed # presence first presence = q.expect('stream-presence') assert (xpath.queryForNodes('/presence/c', presence.stanza) is not None) assert (xpath.queryForNodes( '/presence/x[@xmlns="vcard-temp:x:update"]', presence.stanza) is not None) session_initiate = q.expect('stream-iq', predicate=jp.action_predicate('session-initiate')) if forbidden: q.unforbid_events(forbidden) jt2.parse_session_initiate(session_initiate.query) stream.send(jp.xml(jp.ResultIq('test@localhost', session_initiate.stanza, []))) # Check the Group interface's properties again. Regardless of the call # requesting API in use, the state should be the same here: group_props = chan.Properties.GetAll(cs.CHANNEL_IFACE_GROUP) assertContains('HandleOwners', group_props) assertEquals([self_handle], group_props['Members']) assertEquals([], group_props['LocalPendingMembers']) assertEquals([remote_handle], group_props['RemotePendingMembers']) if jp.dialect == 'gtalk-v0.4': node = jp.SetIq(jt2.peer, jt2.jid, [ jp.Jingle(jt2.sid, jt2.peer, 'transport-accept', [ jp.TransportGoogleP2P() ]) ]) stream.send(jp.xml(node)) # FIXME: expect transport-info, then if we're gtalk3, send # candidates, and check that gabble resends transport-info as # candidates jt2.accept() q.expect_many( EventPattern('stream-iq', iq_type='result'), # Call accepted EventPattern('dbus-signal', signal='MembersChanged', args=['', [remote_handle], [], [], [], remote_handle, cs.GC_REASON_NONE]), ) # Time passes ... afterwards we close the chan chan.Group.RemoveMembers([self_handle], 'closed') # Make sure gabble sends proper terminate action if jp.dialect.startswith('gtalk'): terminate = EventPattern('stream-iq', predicate=lambda x: xpath.queryForNodes("/iq/session[@type='terminate']", x.stanza)) else: terminate = EventPattern('stream-iq', predicate=lambda x: xpath.queryForNodes("/iq/jingle[@action='session-terminate']", x.stanza)) mc_event, _, _ = q.expect_many( EventPattern('dbus-signal', signal='MembersChanged'), EventPattern('dbus-signal', signal='Close'), terminate, ) # Check that we're the actor assertEquals(self_handle, mc_event.args[5]) def rccs(q, bus, conn, stream): """ Tests that the connection's RequestableChannelClasses for StreamedMedia are sane. """ rccs = conn.Properties.Get(cs.CONN_IFACE_REQUESTS, 'RequestableChannelClasses') # Test Channel.Type.StreamedMedia media_classes = [ rcc for rcc in rccs if rcc[0][cs.CHANNEL_TYPE] == cs.CHANNEL_TYPE_STREAMED_MEDIA ] assertLength(1, media_classes) fixed, allowed = media_classes[0] assertEquals(cs.HT_CONTACT, fixed[cs.TARGET_HANDLE_TYPE]) expected_allowed = [ cs.TARGET_ID, cs.TARGET_HANDLE, cs.INITIAL_VIDEO, cs.INITIAL_AUDIO ] allowed.sort() expected_allowed.sort() assertEquals(expected_allowed, allowed) # Test Channel.Type.Call media_classes = [ rcc for rcc in rccs if rcc[0][cs.CHANNEL_TYPE] == cs.CHANNEL_TYPE_CALL ] assertLength(2, media_classes) hts = [] for mc in media_classes: fixed, allowed = mc hts.append (fixed[cs.TARGET_HANDLE_TYPE]) expected_allowed = [ cs.TARGET_ID, cs.TARGET_HANDLE, cs.CALL_INITIAL_VIDEO, cs.CALL_INITIAL_AUDIO, cs.CALL_INITIAL_VIDEO_NAME, cs.CALL_INITIAL_AUDIO_NAME, cs.CALL_MUTABLE_CONTENTS ] allowed.sort() expected_allowed.sort() assertEquals(expected_allowed, allowed) assertEquals(sorted([cs.HT_CONTACT, cs.HT_ROOM]), sorted(hts)) if __name__ == '__main__': exec_test(rccs) test_all_dialects(create) test_all_dialects(request_anonymous) test_all_dialects(request_anonymous_and_add) test_all_dialects(request_nonymous) test_all_dialects(partial(create, peer='foo@gw.bar.com')) test_all_dialects(partial(request_anonymous, peer='foo@gw.bar.com')) test_all_dialects(partial(request_anonymous_and_add, peer='foo@gw.bar.com')) test_all_dialects(partial(request_nonymous, peer='foo@gw.bar.com')) telepathy-gabble-0.18.2/tests/twisted/jingle/misuse.py0000644000175000017500000000317212227000321022761 0ustar00smcvsmcv00000000000000""" Test misuse of the streamed media API, which should return error messages rather than asserting Gabble. """ from dbus import DBusException from servicetest import make_channel_proxy, wrap_channel, assertEquals from gabbletest import exec_test from jingletest2 import JingleTest2, JingleProtocol031 import constants as cs from config import VOIP_ENABLED if not VOIP_ENABLED: print "NOTE: built with --disable-voip" raise SystemExit(77) def test(q, bus, conn, stream): jp = JingleProtocol031() remote_jid = 'foo@example.com/misc' jt = JingleTest2(jp, conn, q, stream, 'test@localhost', remote_jid) jt.prepare() self_handle = conn.GetSelfHandle() remote_handle = conn.RequestHandles(cs.HT_CONTACT, [remote_jid])[0] path, _ = conn.Requests.CreateChannel({ cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_STREAMED_MEDIA, cs.TARGET_HANDLE_TYPE: cs.HT_CONTACT, cs.TARGET_HANDLE: remote_handle}) chan = wrap_channel(bus.get_object(conn.bus_name, path), 'StreamedMedia') # In Gabble, the StreamedMedia channel is secretly also the SessionHandler. # Let's make up a proxy and call some methods on it. They should fail # gracefully, rather than crashing Gabble. session_handler = make_channel_proxy(conn, path, 'Media.SessionHandler') try: session_handler.Ready() except DBusException, e: assertEquals(cs.NOT_AVAILABLE, e.get_dbus_name()) try: session_handler.Error(0, "slowing down but with a sense of speeding up") except DBusException, e: assertEquals(cs.NOT_AVAILABLE, e.get_dbus_name()) if __name__ == '__main__': exec_test(test) telepathy-gabble-0.18.2/tests/twisted/jingle/initial-audio-video.py0000644000175000017500000001625112227000321025312 0ustar00smcvsmcv00000000000000""" Tests outgoing calls created with InitialAudio and/or InitialVideo, and exposing the initial contents of incoming calls as values of InitialAudio and InitialVideo """ import operator from servicetest import ( assertContains, assertEquals, assertLength, wrap_channel, EventPattern, call_async, make_channel_proxy) from jingletest2 import JingleTest2, test_all_dialects import constants as cs from config import VOIP_ENABLED if not VOIP_ENABLED: print "NOTE: built with --disable-voip" raise SystemExit(77) def outgoing(jp, q, bus, conn, stream): remote_jid = 'flames@cold.mountain/beyond' jt = JingleTest2(jp, conn, q, stream, 'test@localhost', remote_jid) jt.prepare() self_handle = conn.GetSelfHandle() remote_handle = conn.RequestHandles(cs.HT_CONTACT, [remote_jid])[0] rccs = conn.Properties.Get(cs.CONN_IFACE_REQUESTS, 'RequestableChannelClasses') media_classes = [ rcc for rcc in rccs if rcc[0][cs.CHANNEL_TYPE] == cs.CHANNEL_TYPE_STREAMED_MEDIA ] assertLength(1, media_classes) fixed, allowed = media_classes[0] assertContains(cs.INITIAL_AUDIO, allowed) assertContains(cs.INITIAL_VIDEO, allowed) check_neither(q, conn, bus, stream, remote_handle) check_iav(jt, q, conn, bus, stream, remote_handle, True, False) check_iav(jt, q, conn, bus, stream, remote_handle, False, True) check_iav(jt, q, conn, bus, stream, remote_handle, True, True) def check_neither(q, conn, bus, stream, remote_handle): """ Make a channel without specifying InitialAudio or InitialVideo; check that it's announced with both False, and that they're both present and false in GetAll(). """ path, props = conn.Requests.CreateChannel({ cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_STREAMED_MEDIA, cs.TARGET_HANDLE_TYPE: cs.HT_CONTACT, cs.TARGET_HANDLE: remote_handle}) assertContains((cs.INITIAL_AUDIO, False), props.items()) assertContains((cs.INITIAL_VIDEO, False), props.items()) chan = wrap_channel(bus.get_object(conn.bus_name, path), cs.CHANNEL_TYPE_STREAMED_MEDIA, ['MediaSignalling']) props = chan.Properties.GetAll(cs.CHANNEL_TYPE_STREAMED_MEDIA) assertContains(('InitialAudio', False), props.items()) assertContains(('InitialVideo', False), props.items()) # We shouldn't have started a session yet, so there shouldn't be any # session handlers. Strictly speaking, there could be a session handler # with no stream handlers, but... session_handlers = chan.MediaSignalling.GetSessionHandlers() assertLength(0, session_handlers) def check_iav(jt, q, conn, bus, stream, remote_handle, initial_audio, initial_video): """ Make a channel and check that its InitialAudio and InitialVideo properties come out correctly. """ call_async(q, conn.Requests, 'CreateChannel', { cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_STREAMED_MEDIA, cs.TARGET_HANDLE_TYPE: cs.HT_CONTACT, cs.TARGET_HANDLE: remote_handle, cs.INITIAL_AUDIO: initial_audio, cs.INITIAL_VIDEO: initial_video, }) if initial_video and (not jt.jp.can_do_video() or (not initial_audio and not jt.jp.can_do_video_only ())): # Some protocols can't do video event = q.expect('dbus-error', method='CreateChannel') assertEquals(cs.NOT_CAPABLE, event.error.get_dbus_name()) else: path, props = q.expect('dbus-return', method='CreateChannel').value assertContains((cs.INITIAL_AUDIO, initial_audio), props.items()) assertContains((cs.INITIAL_VIDEO, initial_video), props.items()) chan = wrap_channel(bus.get_object(conn.bus_name, path), cs.CHANNEL_TYPE_STREAMED_MEDIA, ['MediaSignalling']) props = chan.Properties.GetAll(cs.CHANNEL_TYPE_STREAMED_MEDIA) assertContains(('InitialAudio', initial_audio), props.items()) assertContains(('InitialVideo', initial_video), props.items()) session_handlers = chan.MediaSignalling.GetSessionHandlers() assertLength(1, session_handlers) path, type = session_handlers[0] assertEquals('rtp', type) session_handler = make_channel_proxy(conn, path, 'Media.SessionHandler') session_handler.Ready() stream_handler_paths = [] stream_handler_types = [] for x in [initial_audio, initial_video]: if x: e = q.expect('dbus-signal', signal='NewStreamHandler') stream_handler_paths.append(e.args[0]) stream_handler_types.append(e.args[2]) if initial_audio: assertContains(cs.MEDIA_STREAM_TYPE_AUDIO, stream_handler_types) if initial_video: assertContains(cs.MEDIA_STREAM_TYPE_VIDEO, stream_handler_types) for x in xrange (0, len(stream_handler_paths)): p = stream_handler_paths[x] t = stream_handler_types[x] sh = make_channel_proxy(conn, p, 'Media.StreamHandler') sh.NewNativeCandidate("fake", jt.get_remote_transports_dbus()) if t == cs.MEDIA_STREAM_TYPE_AUDIO: sh.Ready(jt.get_audio_codecs_dbus()) else: sh.Ready(jt.get_video_codecs_dbus()) sh.StreamState(cs.MEDIA_STREAM_STATE_CONNECTED) e = q.expect('stream-iq', predicate=jt.jp.action_predicate('session-initiate')) jt.parse_session_initiate (e.query) jt.accept() events = reduce(operator.concat, [ [ EventPattern('dbus-signal', signal='SetRemoteCodecs', path=p), EventPattern('dbus-signal', signal='SetStreamPlaying', path=p), ] for p in stream_handler_paths ], []) q.expect_many(*events) chan.Close() def incoming(jp, q, bus, conn, stream): remote_jid = 'skinny.fists@heaven/antennas' jt = JingleTest2(jp, conn, q, stream, 'test@localhost', remote_jid) jt.prepare() self_handle = conn.GetSelfHandle() remote_handle = conn.RequestHandles(cs.HT_CONTACT, [remote_jid])[0] for a, v in [("audio1", None), (None, "video1"), ("audio1", "video1")]: if v!= None and not jp.can_do_video(): continue if a == None and v != None and not jp.can_do_video_only(): continue jt.incoming_call(audio=a, video=v) e = q.expect('dbus-signal', signal='NewChannels', predicate=lambda e: cs.CHANNEL_TYPE_CONTACT_LIST not in e.args[0][0][1].values()) chans = e.args[0] assertLength(1, chans) path, props = chans[0] assertEquals(cs.CHANNEL_TYPE_STREAMED_MEDIA, props[cs.CHANNEL_TYPE]) assertEquals(a != None, props[cs.INITIAL_AUDIO]) assertEquals(v != None, props[cs.INITIAL_VIDEO]) # FIXME: This doesn't check non-Google contacts that can only do one # media type, as such contacts as simulated by JingleTest2 can always # do both. assertEquals(not jp.can_do_video() or not jp.can_do_video_only(), props[cs.IMMUTABLE_STREAMS]) chan = wrap_channel(bus.get_object(conn.bus_name, path), cs.CHANNEL_TYPE_STREAMED_MEDIA) chan.Close() if __name__ == '__main__': test_all_dialects(outgoing) test_all_dialects(incoming) telepathy-gabble-0.18.2/tests/twisted/jingle/incoming-gmail-modern-jingle.py0000644000175000017500000001636512227000321027106 0ustar00smcvsmcv00000000000000""" Tests workarounds for calls with the GMail client, which supports a (currently quirky) variation on the theme of modern Jingle. """ from servicetest import EventPattern, wrap_channel, make_channel_proxy, assertEquals from gabbletest import elem, elem_iq, exec_test from jingletest2 import JingleTest2, JingleProtocol031 import ns import constants as cs from twisted.words.xish import xpath from config import VOIP_ENABLED if not VOIP_ENABLED: print "NOTE: built with --disable-voip" raise SystemExit(77) class GMail(JingleTest2): remote_caps = { 'ext': 'pmuc-v1 sms-v1 camera-v1 video-v1 voice-v1', 'ver': '1.1', 'node': 'http://mail.google.com/xmpp/client/caps', } def test(q, bus, conn, stream): peer = 'foo@gmail.com/gmail.7E1F07D0' self = 'test@localhost/test' jp = JingleProtocol031() jt = GMail(jp, conn, q, stream, 'test@localhost', peer) jt.prepare(send_roster=False) sid = 'c1025763497' iq_id = 'session_init_iq' si = elem_iq(stream, 'set', from_=peer, to=self, id=iq_id)( elem(ns.JINGLE, 'jingle', action='session-initiate', sid=sid, initiator=peer)( elem('content', name='video')( elem(ns.JINGLE_RTP, 'description', media='video')( elem('payload-type', id='99', name='H264-SVC')( elem('parameter', name='width', value='320'), elem('parameter', name='height', value='200'), elem('parameter', name='framerate', value='30'), ), # ... other codecs elided ... elem('encryption'), ), elem(ns.GOOGLE_P2P, 'transport'), ), elem('content', name='audio')( elem(ns.JINGLE_RTP, 'description', media='audio')( elem('payload-type', id='103', name='ISAC', clockrate='16000')( elem('parameter', name='bitrate', value='32000'), ), # ... other codecs elided ... elem('encryption'), ), elem(ns.GOOGLE_P2P, 'transport'), ) ), elem(ns.GOOGLE_SESSION, 'session', action='initiate', sid='c1025763497', initiator=peer)( elem(ns.GOOGLE_SESSION_VIDEO, 'description')( elem('payload-type', id='99', name='H264-SVC', width='320', height='200', framerate='30'), # ... other codecs elided ... elem(ns.JINGLE_RTP, 'encryption')( elem(ns.GOOGLE_SESSION_VIDEO, 'usage'), ), elem(ns.GOOGLE_SESSION_PHONE, 'payload-type', id='103', name='ISAC', bitrate='32000', clockrate='16000'), # ... other codecs elided ... elem(ns.JINGLE_RTP, 'encryption')( elem(ns.GOOGLE_SESSION_PHONE, 'usage'), ), ), ), ) stream.send(si) ok, nc, nsh = q.expect_many( # fd.o #65131: we have to tell Google which dialect we're speaking EventPattern('stream-iq', iq_type='result', query_name='jingle', query_ns=ns.JINGLE, iq_id=iq_id), EventPattern('dbus-signal', signal='NewChannels'), EventPattern('dbus-signal', signal='NewSessionHandler'), ) path, properties = nc.args[0][0] # It's an audio+video call assert properties[cs.INITIAL_AUDIO] assert properties[cs.INITIAL_VIDEO] # Google can't add and remove streams on the fly. We special-case GMail. assert properties[cs.IMMUTABLE_STREAMS] chan = wrap_channel(bus.get_object(conn.bus_name, path), 'StreamedMedia') session_handler = make_channel_proxy(conn, nsh.args[0], 'Media.SessionHandler') session_handler.Ready() path, _, _, _ = q.expect('dbus-signal', signal='NewStreamHandler').args stream1 = make_channel_proxy(conn, path, 'Media.StreamHandler') path, _, _, _ = q.expect('dbus-signal', signal='NewStreamHandler').args stream2 = make_channel_proxy(conn, path, 'Media.StreamHandler') stream1.Ready([]) stream2.Ready([]) # Audio rtcp stream.send( elem_iq(stream, from_=peer, to=self, type='set')( elem(ns.JINGLE, 'jingle', action='transport-info', sid=sid)( elem('content', name='audio')( elem(ns.GOOGLE_P2P, 'transport')( elem('candidate', address='172.22.64.192', port='54335', name='rtcp', username='+wJqkmRVYotCz+Rd', password='POWPzg5Pks4+ywAz', preference='1', protocol='udp', generation='0', network='1', type='local') ) ) ) ) ) q.expect('dbus-signal', signal='AddRemoteCandidate', path=stream1.object_path) # audio rtp stream.send( elem_iq(stream, from_=peer, to=self, type='set')( elem(ns.JINGLE, 'jingle', action='transport-info', sid=sid)( elem('content', name='audio')( elem(ns.GOOGLE_P2P, 'transport')( elem('candidate', address='172.22.64.192', port='54337', name='rtp', username='F7rgdWcCgH3Q/HgE', password='ioh2IDwd3iZEZHzM', preference='1', protocol='udp', generation='0', network='1', type='local') ) ) ) ) ) q.expect('dbus-signal', signal='AddRemoteCandidate', path=stream1.object_path) # video rtcp: note the weird name='' field which Gabble has to work around stream.send( elem_iq(stream, from_=peer, to=self, type='set')( elem(ns.JINGLE, 'jingle', action='transport-info', sid=sid)( elem('content', name='video')( elem(ns.GOOGLE_P2P, 'transport')( elem('candidate', address='172.22.64.192', port='54339', name='video_rtcp', username='fnLduEIu6VHsSOqh', password='IYeNu/HWzMpx2zrS', preference='1', protocol='udp', generation='0', network='1', type='local') ) ) ) ) ) q.expect('dbus-signal', signal='AddRemoteCandidate', path=stream2.object_path) # video rtp: ditto stream.send( elem_iq(stream, from_=peer, to=self, type='set')( elem(ns.JINGLE, 'jingle', action='transport-info', sid=sid)( elem('content', name='video')( elem(ns.GOOGLE_P2P, 'transport')( elem('candidate', address='172.22.64.192', port='54341', name='video_rtp', username='mZVBFdQ2LyAP6oyE', password='3uoyCHP8zwE+/Ylw', preference='1', protocol='udp', generation='0', network='1', type='local') ) ) ) ) ) q.expect('dbus-signal', signal='AddRemoteCandidate', path=stream2.object_path) # Test that we're sending with name='video_rtp' as well, but only for the video stream. stream1.NewNativeCandidate("fake", jt.get_remote_transports_dbus()) e = q.expect('stream-iq', predicate=jp.action_predicate('transport-info')) candidate = xpath.queryForNodes( '/iq/jingle/content[@name="audio"]/transport/candidate', e.stanza)[0] assertEquals('rtp', candidate['name']) stream2.NewNativeCandidate("fake", jt.get_remote_transports_dbus()) e = q.expect('stream-iq', predicate=jp.action_predicate('transport-info')) candidate = xpath.queryForNodes( '/iq/jingle/content[@name="video"]/transport/candidate', e.stanza)[0] assertEquals('video_rtp', candidate['name']) if __name__ == '__main__': exec_test(test) telepathy-gabble-0.18.2/tests/twisted/jingle/incoming-call-stream-error.py0000644000175000017500000000777412227000321026624 0ustar00smcvsmcv00000000000000 """ Test handling of Error() call on stream handler. This tests a regression in which MembersChanged was emitted with reason other than GC_REASON_ERROR. """ from servicetest import EventPattern, assertEquals, make_channel_proxy from jingletest2 import JingleTest2, test_all_dialects import constants as cs from twisted.words.xish import xpath from config import VOIP_ENABLED if not VOIP_ENABLED: print "NOTE: built with --disable-voip" raise SystemExit(77) def _session_terminate_predicate(event, reason, msg, jp): matches = jp.match_jingle_action(event.query, 'session-terminate') if matches and jp.is_modern_jingle(): reason = xpath.queryForNodes("/iq" "/jingle[@action='session-terminate']" "/reason/%s" % reason, event.stanza) reason_text = xpath.queryForString("/iq/jingle/reason/text", event.stanza) return bool(reason) and reason_text == msg return matches def _test(jp, q, bus, conn, stream, jingle_reason, group_change_reason, stream_error): jt = JingleTest2(jp, conn, q, stream, 'test@localhost', 'foo@bar.com/Foo') jt.prepare() self_handle = conn.GetSelfHandle() remote_handle = conn.RequestHandles(cs.HT_CONTACT, ["foo@bar.com/Foo"])[0] # Ring ring! jt.incoming_call() new_channel, new_session_handler = q.expect_many( EventPattern('dbus-signal', signal='NewChannel', predicate=lambda e: cs.CHANNEL_TYPE_CONTACT_LIST not in e.args), EventPattern('dbus-signal', signal='NewSessionHandler')) assertEquals(cs.CHANNEL_TYPE_STREAMED_MEDIA, new_channel.args[1]) assertEquals(cs.HT_CONTACT, new_channel.args[2]) assertEquals(remote_handle, new_channel.args[3]) assertEquals('rtp', new_session_handler.args[1]) channel_path = new_channel.args[0] # Client calls Ready on new session handler. session_handler = make_channel_proxy( conn, new_session_handler.args[0], 'Media.SessionHandler') session_handler.Ready() # Client gets notified about a newly created stream... new_stream_handler = q.expect('dbus-signal', signal='NewStreamHandler') stream_id = new_stream_handler.args[1] stream_handler = make_channel_proxy( conn, new_stream_handler.args[0], 'Media.StreamHandler') stream_handler.NewNativeCandidate("fake", jt.get_remote_transports_dbus()) stream_handler.Ready(jt.dbusify_codecs([("FOO", 5, 8000, {})])) q.expect('dbus-signal', signal='SetRemoteCodecs') msg = u"o noes" # ...but something goes wrong. stream_handler.Error(stream_error, msg) q.expect("stream-iq", iq_type="set", predicate=lambda x: _session_terminate_predicate(x, jingle_reason, msg, jp)) # Bye bye members. mc = q.expect('dbus-signal', signal='MembersChanged', interface=cs.CHANNEL_IFACE_GROUP, path=channel_path, args=[msg, [], [self_handle, remote_handle], [], [], self_handle, group_change_reason]) q.expect('dbus-signal', signal='StreamError', interface=cs.CHANNEL_TYPE_STREAMED_MEDIA, args=[stream_id, stream_error, msg]) # Bye bye stream q.expect('dbus-signal', signal='Close') q.expect('dbus-signal', signal='StreamRemoved') # Bye bye channel. q.expect('dbus-signal', signal='Closed') q.expect('dbus-signal', signal='ChannelClosed') def test_connection_error(jp, q, bus, conn, stream): _test(jp, q, bus, conn, stream, "connectivity-error", cs.GC_REASON_ERROR, cs.MEDIA_STREAM_ERROR_NETWORK_ERROR) def test_codec_negotiation_fail(jp, q, bus, conn, stream): _test(jp, q, bus, conn, stream, "failed-application", cs.GC_REASON_ERROR, cs.MEDIA_STREAM_ERROR_CODEC_NEGOTIATION_FAILED) if __name__ == '__main__': test_all_dialects(test_connection_error) test_all_dialects(test_codec_negotiation_fail) telepathy-gabble-0.18.2/tests/twisted/jingle/incoming-basics.py0000644000175000017500000001717212227000321024526 0ustar00smcvsmcv00000000000000""" Test incoming call handling. """ import dbus from gabbletest import make_result_iq from servicetest import ( make_channel_proxy, unwrap, EventPattern, assertEquals, assertLength) from jingletest2 import JingleTest2, test_all_dialects import constants as cs from twisted.words.xish import xpath from config import VOIP_ENABLED if not VOIP_ENABLED: print "NOTE: built with --disable-voip" raise SystemExit(77) def test(jp, q, bus, conn, stream, peer='foo@bar.com/Foo'): jt = JingleTest2(jp, conn, q, stream, 'test@localhost', peer) jt.prepare() self_handle = conn.GetSelfHandle() remote_handle = conn.RequestHandles(cs.HT_CONTACT, [jt.peer])[0] # Remote end calls us jt.incoming_call() # If this is a Jingle dialect that supports it, Gabble should send a # notification when it gets the session-initiate until Telepathy # has a way for the UI to do this. # https://bugs.freedesktop.org/show_bug.cgi?id=21964 ringing_event = jp.rtp_info_event_list("ringing") if jp.dialect == 'gtalk-v0.4': # With gtalk4, apparently we have to send transport-accept immediately, # not even just before we send our transport-info. wjt tested this, and # indeed if we don't send this for incoming calls, the call never # connects. ta_event = [ EventPattern('stream-iq', predicate=lambda x: xpath.queryForNodes("/iq/session[@type='transport-accept']", x.stanza)), ] else: ta_event = [] nc, e = q.expect_many( EventPattern('dbus-signal', signal='NewChannel', predicate=lambda e: cs.CHANNEL_TYPE_CONTACT_LIST not in e.args), EventPattern('dbus-signal', signal='NewSessionHandler'), *(ringing_event + ta_event) )[0:2] path, ct, ht, h, _ = nc.args assert ct == cs.CHANNEL_TYPE_STREAMED_MEDIA, ct assert ht == cs.HT_CONTACT, ht assert h == remote_handle, h media_chan = make_channel_proxy(conn, path, 'Channel.Interface.Group') media_iface = make_channel_proxy(conn, path, 'Channel.Type.StreamedMedia') # S-E was notified about new session handler, and calls Ready on it assert e.args[1] == 'rtp' session_handler = make_channel_proxy(conn, e.args[0], 'Media.SessionHandler') session_handler.Ready() nsh_event = q.expect('dbus-signal', signal='NewStreamHandler') # S-E gets notified about a newly-created stream stream_handler = make_channel_proxy(conn, nsh_event.args[0], 'Media.StreamHandler') streams = media_iface.ListStreams() assertLength(1, streams) stream_id, stream_handle, stream_type, _, stream_direction, pending_flags =\ streams[0] assertEquals(remote_handle, stream_handle) assertEquals(cs.MEDIA_STREAM_TYPE_AUDIO, stream_type) assertEquals(cs.MEDIA_STREAM_DIRECTION_RECEIVE, stream_direction) assertEquals(cs.MEDIA_STREAM_PENDING_LOCAL_SEND, pending_flags) # Exercise channel properties channel_props = media_chan.GetAll( cs.CHANNEL, dbus_interface=dbus.PROPERTIES_IFACE) assertEquals(remote_handle, channel_props['TargetHandle']) assertEquals(cs.HT_CONTACT, channel_props['TargetHandleType']) assertEquals((cs.HT_CONTACT, remote_handle), media_chan.GetHandle(dbus_interface=cs.CHANNEL)) assertEquals(jt.peer_bare_jid, channel_props['TargetID']) assertEquals(jt.peer_bare_jid, channel_props['InitiatorID']) assertEquals(remote_handle, channel_props['InitiatorHandle']) assertEquals(False, channel_props['Requested']) group_props = media_chan.GetAll( cs.CHANNEL_IFACE_GROUP, dbus_interface=dbus.PROPERTIES_IFACE) assert group_props['SelfHandle'] == self_handle, \ (group_props['SelfHandle'], self_handle) flags = group_props['GroupFlags'] assert flags & cs.GF_PROPERTIES, flags # Changing members in any way other than adding or removing yourself is # meaningless for incoming calls, and the flags need not be sent to change # your own membership. assert not flags & cs.GF_CAN_ADD, flags assert not flags & cs.GF_CAN_REMOVE, flags assert not flags & cs.GF_CAN_RESCIND, flags assert group_props['Members'] == [remote_handle], group_props['Members'] assert group_props['RemotePendingMembers'] == [], \ group_props['RemotePendingMembers'] # We're local pending because remote_handle invited us. assert group_props['LocalPendingMembers'] == \ [(self_handle, remote_handle, cs.GC_REASON_INVITED, '')], \ unwrap(group_props['LocalPendingMembers']) streams = media_chan.ListStreams( dbus_interface=cs.CHANNEL_TYPE_STREAMED_MEDIA) assert len(streams) == 1, streams assert len(streams[0]) == 6, streams[0] # streams[0][0] is the stream identifier, which in principle we can't # make any assertion about (although in practice it's probably 1) assert streams[0][1] == remote_handle, (streams[0], remote_handle) assert streams[0][2] == cs.MEDIA_STREAM_TYPE_AUDIO, streams[0] # We haven't connected yet assert streams[0][3] == cs.MEDIA_STREAM_STATE_DISCONNECTED, streams[0] # In Gabble, incoming streams start off with remote send enabled, and # local send requested assert streams[0][4] == cs.MEDIA_STREAM_DIRECTION_RECEIVE, streams[0] assert streams[0][5] == cs.MEDIA_STREAM_PENDING_LOCAL_SEND, streams[0] # Connectivity checks happen before we have accepted the call stream_handler.NewNativeCandidate("fake", jt.get_remote_transports_dbus()) stream_handler.Ready(jt.get_audio_codecs_dbus()) stream_handler.StreamState(cs.MEDIA_STREAM_STATE_CONNECTED) stream_handler.SupportedCodecs(jt.get_audio_codecs_dbus()) # peer gets the transport e = q.expect('stream-iq', predicate=jp.action_predicate('transport-info')) assertEquals(jt.peer, e.query['initiator']) if jp.dialect in ['jingle-v0.15', 'jingle-v0.31']: content = xpath.queryForNodes('/iq/jingle/content', e.stanza)[0] assertEquals('initiator', content['creator']) stream.send(make_result_iq(stream, e.stanza)) # At last, accept the call media_chan.AddMembers([self_handle], 'accepted') # Call is accepted, we become a member, and the stream that was pending # local send is now sending. memb, acc, _, _, _ = q.expect_many( EventPattern('dbus-signal', signal='MembersChanged', args=[u'', [self_handle], [], [], [], self_handle, cs.GC_REASON_NONE]), EventPattern('stream-iq', predicate=jp.action_predicate('session-accept')), EventPattern('dbus-signal', signal='SetStreamSending', args=[True]), EventPattern('dbus-signal', signal='SetStreamPlaying', args=[True]), EventPattern('dbus-signal', signal='StreamDirectionChanged', args=[stream_id, cs.MEDIA_STREAM_DIRECTION_BIDIRECTIONAL, 0]), ) stream.send(make_result_iq(stream, acc.stanza)) # Also, if this is a Jingle dialect that supports it, Gabble should send an # notification when the session-accept is acked (until the # Telepathy spec lets the UI say it's not ringing any more). active_event = jp.rtp_info_event("active") if active_event is not None: q.expect_many(active_event) # we are now both in members members = media_chan.GetMembers() assert set(members) == set([self_handle, remote_handle]), members # Connected! Blah, blah, ... # 'Nuff said jt.terminate() q.expect('dbus-signal', signal='Closed', path=path) if __name__ == '__main__': test_all_dialects(test) test_all_dialects(lambda jp, q, bus, conn, stream: test(jp, q, bus, conn, stream, 'foo@sip.bar.com')) telepathy-gabble-0.18.2/tests/twisted/jingle/hold-av.py0000644000175000017500000004202612227000321023007 0ustar00smcvsmcv00000000000000""" Test the Hold API. """ from gabbletest import make_result_iq, sync_stream from servicetest import ( assertEquals, wrap_channel, make_channel_proxy, call_async, EventPattern, sync_dbus) import constants as cs from jingletest2 import JingleTest2, test_all_dialects from config import VOIP_ENABLED if not VOIP_ENABLED: print "NOTE: built with --disable-voip" raise SystemExit(77) def mutable_stream_tests(jp, jt, q, bus, conn, stream, chan, handle): # ---- Test 13: while the call's on hold, we add a new stream --- # We shouldn't go off hold locally as a result, and the new StreamHandler # should tell s-e to hold the stream. pending_hold = [ EventPattern('dbus-signal', signal='HoldStateChanged', predicate=lambda e: e.args[0] == cs.HS_PENDING_HOLD), ] q.forbid_events(pending_hold) call_async(q, chan.StreamedMedia, 'RequestStreams', handle, [cs.MEDIA_STREAM_TYPE_AUDIO]) e = q.expect('dbus-signal', signal='NewStreamHandler') audio_stream_path = e.args[0] audio_stream_handler = make_channel_proxy(conn, e.args[0], 'Media.StreamHandler') # Syncing here to make sure SetStreamHeld happens after Ready... sync_dbus(bus, q, conn) audio_stream_handler.Ready(jt.get_audio_codecs_dbus()) audio_stream_handler.NewNativeCandidate("fake", jt.get_remote_transports_dbus()) audio_stream_handler.StreamState(cs.MEDIA_STREAM_STATE_CONNECTED) q.expect_many( EventPattern('dbus-signal', signal='SetStreamHeld', args=[True], path=audio_stream_path), EventPattern('dbus-signal', signal='SetStreamSending', args=[False], path=audio_stream_path), ) assertEquals(cs.HS_HELD, chan.Hold.GetHoldState()[0]) sync_dbus(bus, q, conn) # ---- Test 14: while the call's on hold, the peer adds a new stream ---- # Again, we shouldn't go off hold locally as a result, and the new # StreamHandler should tell s-e to hold the stream. node = jp.SetIq(jt.peer, jt.jid, [ jp.Jingle(jt.sid, jt.peer, 'content-add', [ jp.Content('videostream', 'initiator', 'both', jp.Description('video', [ jp.PayloadType(name, str(rate), str(id), parameters) for (name, id, rate, parameters) in jt.video_codecs ]), jp.TransportGoogleP2P()) ]) ]) stream.send(jp.xml(node)) e = q.expect('dbus-signal', signal='NewStreamHandler') video_stream_path = e.args[0] video_stream_handler = make_channel_proxy(conn, e.args[0], 'Media.StreamHandler') # Syncing here to make sure SetStreamHeld happens after Ready... sync_dbus(bus, q, conn) video_stream_handler.Ready(jt.get_video_codecs_dbus()) video_stream_handler.NewNativeCandidate("fake", jt.get_remote_transports_dbus()) video_stream_handler.StreamState(cs.MEDIA_STREAM_STATE_CONNECTED) q.expect_many( EventPattern('dbus-signal', signal='SetStreamHeld', args=[True], path=video_stream_path), EventPattern('dbus-signal', signal='SetStreamSending', args=[False], path=video_stream_path), ) assertEquals(cs.HS_HELD, chan.Hold.GetHoldState()[0]) sync_dbus(bus, q, conn) q.unforbid_events(pending_hold) def test(jp, q, bus, conn, stream): # These are 0- (for old dialects) or 1- (for new dialects) element lists # that can be splatted into expect_many with * hold_event = jp.rtp_info_event_list("hold") unhold_event = jp.rtp_info_event_list("unhold") # Let's forbid them until we're ready to start holding, to check that # Gabble doesn't send spurious notifications. q.forbid_events(hold_event) q.forbid_events(unhold_event) jt = JingleTest2(jp, conn, q, stream, 'test@localhost', 'foo@bar.com/Foo') jt.prepare() self_handle = conn.GetSelfHandle() handle = conn.RequestHandles(cs.HT_CONTACT, [jt.peer])[0] path = conn.RequestChannel(cs.CHANNEL_TYPE_STREAMED_MEDIA, cs.HT_CONTACT, handle, True) chan = wrap_channel(bus.get_object(conn.bus_name, path), 'StreamedMedia', ['Hold']) call_async(q, chan.StreamedMedia, 'RequestStreams', handle, [cs.MEDIA_STREAM_TYPE_AUDIO, cs.MEDIA_STREAM_TYPE_VIDEO]) if not jp.can_do_video(): # Video on GTalk? Not so much. e = q.expect('dbus-error', method='RequestStreams') # The spec and implemention say this should be NotAvailable, but wjt # thinks it should be NotCapable. The spec bug is #20920. name = e.error.get_dbus_name() #assert name == cs.NOT_CAPABLE, name return # S-E gets notified about new session handler, and calls Ready on it e = q.expect('dbus-signal', signal='NewSessionHandler') assert e.args[1] == 'rtp' session_handler = make_channel_proxy(conn, e.args[0], 'Media.SessionHandler') session_handler.Ready() e = q.expect('dbus-signal', signal='NewStreamHandler') # FIXME: we assume this one's the audio stream, just because we requested # that first audio_stream_path = e.args[0] audio_stream_handler = make_channel_proxy(conn, e.args[0], 'Media.StreamHandler') audio_stream_handler.NewNativeCandidate("fake", jt.get_remote_transports_dbus()) audio_stream_handler.Ready(jt.get_audio_codecs_dbus()) audio_stream_handler.StreamState(cs.MEDIA_STREAM_STATE_CONNECTED) e = q.expect('dbus-signal', signal='NewStreamHandler') video_stream_path = e.args[0] video_stream_handler = make_channel_proxy(conn, e.args[0], 'Media.StreamHandler') video_stream_handler.NewNativeCandidate("fake", jt.get_remote_transports_dbus()) video_stream_handler.Ready(jt.get_video_codecs_dbus()) video_stream_handler.StreamState(cs.MEDIA_STREAM_STATE_CONNECTED) e = q.expect('stream-iq', predicate=jp.action_predicate('session-initiate')) stream.send(make_result_iq(stream, e.stanza)) jt.parse_session_initiate(e.query) jt.accept() q.expect('stream-iq', iq_type='result') # ---- Test 1: GetHoldState returns unheld and unhold is a no-op ---- hold_state = chan.Hold.GetHoldState() assert hold_state[0] == cs.HS_UNHELD, hold_state chan.Hold.RequestHold(False) # We're about to start holding, so remove the ban on . sync_stream(q, stream) q.unforbid_events(hold_event) # ---- Test 2: successful hold ---- call_async(q, chan.Hold, 'RequestHold', True) q.expect_many( EventPattern('dbus-signal', signal='HoldStateChanged', args=[cs.HS_PENDING_HOLD, cs.HSR_REQUESTED]), EventPattern('dbus-signal', signal='SetStreamHeld', args=[True]), EventPattern('dbus-return', method='RequestHold', value=()), *hold_event ) call_async(q, audio_stream_handler, 'HoldState', True) call_async(q, video_stream_handler, 'HoldState', True) q.expect_many( EventPattern('dbus-return', method='HoldState', value=()), EventPattern('dbus-signal', signal='HoldStateChanged', args=[cs.HS_HELD, cs.HSR_REQUESTED]), ) # ---- Test 3: GetHoldState returns held and hold is a no-op ---- q.forbid_events(hold_event) hold_state = chan.Hold.GetHoldState() assert hold_state[0] == cs.HS_HELD, hold_state chan.Hold.RequestHold(True) sync_stream(q, stream) q.unforbid_events(hold_event) # ---- Test 4: successful unhold ---- call_async(q, chan.Hold, 'RequestHold', False) q.expect_many( EventPattern('dbus-signal', signal='HoldStateChanged', args=[cs.HS_PENDING_UNHOLD, cs.HSR_REQUESTED]), EventPattern('dbus-signal', signal='SetStreamHeld', args=[False]), EventPattern('dbus-return', method='RequestHold', value=()), ) sync_stream(q, stream) q.unforbid_events(unhold_event) call_async(q, audio_stream_handler, 'HoldState', False) call_async(q, video_stream_handler, 'HoldState', False) q.expect_many( EventPattern('dbus-return', method='HoldState', value=()), EventPattern('dbus-signal', signal='HoldStateChanged', args=[cs.HS_UNHELD, cs.HSR_REQUESTED]), *unhold_event ) # ---- Test 5: GetHoldState returns False and unhold is a no-op ---- hold_state = chan.Hold.GetHoldState() assert hold_state[0] == cs.HS_UNHELD, hold_state chan.Hold.RequestHold(False) # ---- Test 6: 3 parallel calls to hold ---- hold_state = chan.Hold.GetHoldState() assert hold_state[0] == cs.HS_UNHELD, hold_state call_async(q, chan.Hold, 'RequestHold', True) call_async(q, chan.Hold, 'RequestHold', True) call_async(q, chan.Hold, 'RequestHold', True) q.expect_many( EventPattern('dbus-signal', signal='HoldStateChanged', args=[cs.HS_PENDING_HOLD, cs.HSR_REQUESTED]), EventPattern('dbus-signal', signal='SetStreamHeld', args=[True]), EventPattern('dbus-return', method='RequestHold', value=()), *hold_event ) call_async(q, audio_stream_handler, 'HoldState', True) call_async(q, video_stream_handler, 'HoldState', True) q.expect_many( EventPattern('dbus-return', method='HoldState', value=()), EventPattern('dbus-signal', signal='HoldStateChanged', args=[cs.HS_HELD, cs.HSR_REQUESTED]), ) # ---- Test 7: 3 parallel calls to unhold ---- q.forbid_events(unhold_event) call_async(q, chan.Hold, 'RequestHold', False) call_async(q, chan.Hold, 'RequestHold', False) call_async(q, chan.Hold, 'RequestHold', False) q.expect_many( EventPattern('dbus-signal', signal='HoldStateChanged', args=[cs.HS_PENDING_UNHOLD, cs.HSR_REQUESTED]), EventPattern('dbus-signal', signal='SetStreamHeld', args=[False]), EventPattern('dbus-return', method='RequestHold', value=()), ) sync_stream(q, stream) q.unforbid_events(unhold_event) call_async(q, audio_stream_handler, 'HoldState', False) call_async(q, video_stream_handler, 'HoldState', False) q.expect_many( EventPattern('dbus-return', method='HoldState', value=()), EventPattern('dbus-signal', signal='HoldStateChanged', args=[cs.HS_UNHELD, cs.HSR_REQUESTED]), *unhold_event ) # ---- Test 8: hold, then change our minds before s-e has responded ---- hold_state = chan.Hold.GetHoldState() assert hold_state[0] == cs.HS_UNHELD, hold_state call_async(q, chan.Hold, 'RequestHold', True) q.expect_many( EventPattern('dbus-signal', signal='HoldStateChanged', args=[cs.HS_PENDING_HOLD, cs.HSR_REQUESTED]), EventPattern('dbus-signal', signal='SetStreamHeld', args=[True]), *hold_event ) # Gabble can't send until s-e confirms it has the resources q.forbid_events(unhold_event) call_async(q, chan.Hold, 'RequestHold', False) q.expect_many( EventPattern('dbus-signal', signal='HoldStateChanged', args=[cs.HS_PENDING_UNHOLD, cs.HSR_REQUESTED]), EventPattern('dbus-signal', signal='SetStreamHeld', args=[False]), ) call_async(q, audio_stream_handler, 'HoldState', True) call_async(q, video_stream_handler, 'HoldState', True) call_async(q, audio_stream_handler, 'HoldState', False) sync_stream(q, stream) q.unforbid_events(unhold_event) call_async(q, video_stream_handler, 'HoldState', False) q.expect_many( EventPattern('dbus-return', method='HoldState', value=()), EventPattern('dbus-signal', signal='HoldStateChanged', args=[cs.HS_UNHELD, cs.HSR_REQUESTED]), *unhold_event ) hold_state = chan.Hold.GetHoldState() assert hold_state[0] == cs.HS_UNHELD, hold_state # ---- Test 9: unhold, then change our minds before s-e has responded ---- # Go to state "held" first call_async(q, chan.Hold, 'RequestHold', True) q.expect_many( EventPattern('dbus-signal', signal='HoldStateChanged', args=[cs.HS_PENDING_HOLD, cs.HSR_REQUESTED]), EventPattern('dbus-signal', signal='SetStreamHeld', args=[True]), EventPattern('dbus-return', method='RequestHold', value=()), *hold_event ) call_async(q, audio_stream_handler, 'HoldState', True) call_async(q, video_stream_handler, 'HoldState', True) q.expect_many( EventPattern('dbus-return', method='HoldState', value=()), EventPattern('dbus-signal', signal='HoldStateChanged', args=[cs.HS_HELD, cs.HSR_REQUESTED]), ) # Actually do test 9 q.forbid_events(hold_event + unhold_event) hold_state = chan.Hold.GetHoldState() assert hold_state[0] == cs.HS_HELD, hold_state call_async(q, chan.Hold, 'RequestHold', False) call_async(q, chan.Hold, 'RequestHold', True) q.expect_many( EventPattern('dbus-signal', signal='HoldStateChanged', args=[cs.HS_PENDING_UNHOLD, cs.HSR_REQUESTED]), EventPattern('dbus-signal', signal='SetStreamHeld', args=[False]), ) q.expect_many( EventPattern('dbus-signal', signal='HoldStateChanged', args=[cs.HS_PENDING_HOLD, cs.HSR_REQUESTED]), EventPattern('dbus-signal', signal='SetStreamHeld', args=[True]), ) call_async(q, audio_stream_handler, 'HoldState', False) call_async(q, video_stream_handler, 'HoldState', False) call_async(q, audio_stream_handler, 'HoldState', True) call_async(q, video_stream_handler, 'HoldState', True) q.expect_many( EventPattern('dbus-return', method='HoldState', value=()), EventPattern('dbus-signal', signal='HoldStateChanged', args=[cs.HS_HELD, cs.HSR_REQUESTED]), ) sync_stream(q, stream) q.unforbid_events(hold_event + unhold_event) hold_state = chan.Hold.GetHoldState() assert hold_state[0] == cs.HS_HELD, hold_state # ---- Test 10: attempting to unhold fails (both streams) ---- q.forbid_events(hold_event + unhold_event) call_async(q, chan.Hold, 'RequestHold', False) q.expect_many( EventPattern('dbus-signal', signal='HoldStateChanged', args=[cs.HS_PENDING_UNHOLD, cs.HSR_REQUESTED]), EventPattern('dbus-signal', signal='SetStreamHeld', args=[False]), EventPattern('dbus-return', method='RequestHold', value=()), ) call_async(q, audio_stream_handler, 'UnholdFailure') call_async(q, video_stream_handler, 'UnholdFailure') q.expect_many( EventPattern('dbus-return', method='UnholdFailure', value=()), EventPattern('dbus-signal', signal='HoldStateChanged', args=[cs.HS_HELD, cs.HSR_RESOURCE_NOT_AVAILABLE]), ) sync_stream(q, stream) q.unforbid_events(hold_event + unhold_event) # ---- Test 11: attempting to unhold fails (first stream) ---- q.forbid_events(hold_event + unhold_event) call_async(q, chan.Hold, 'RequestHold', False) q.expect_many( EventPattern('dbus-signal', signal='HoldStateChanged', args=[cs.HS_PENDING_UNHOLD, cs.HSR_REQUESTED]), EventPattern('dbus-signal', signal='SetStreamHeld', args=[False]), EventPattern('dbus-return', method='RequestHold', value=()), ) call_async(q, audio_stream_handler, 'UnholdFailure') q.expect_many( EventPattern('dbus-return', method='UnholdFailure', value=()), EventPattern('dbus-signal', signal='HoldStateChanged', args=[cs.HS_HELD, cs.HSR_RESOURCE_NOT_AVAILABLE]), ) sync_stream(q, stream) q.unforbid_events(hold_event + unhold_event) # ---- Test 12: attempting to unhold partially fails, so roll back ---- q.forbid_events(hold_event + unhold_event) call_async(q, chan.Hold, 'RequestHold', False) q.expect_many( EventPattern('dbus-signal', signal='HoldStateChanged', args=[cs.HS_PENDING_UNHOLD, cs.HSR_REQUESTED]), EventPattern('dbus-signal', signal='SetStreamHeld', args=[False]), EventPattern('dbus-return', method='RequestHold', value=()), ) call_async(q, audio_stream_handler, 'HoldState', False) q.expect('dbus-return', method='HoldState', value=()) call_async(q, video_stream_handler, 'UnholdFailure') q.expect_many( EventPattern('dbus-signal', signal='SetStreamHeld', args=[True]), EventPattern('dbus-return', method='UnholdFailure', value=()), EventPattern('dbus-signal', signal='HoldStateChanged', args=[cs.HS_PENDING_HOLD, cs.HSR_RESOURCE_NOT_AVAILABLE]), ) call_async(q, audio_stream_handler, 'HoldState', True) q.expect_many( EventPattern('dbus-return', method='HoldState', value=()), EventPattern('dbus-signal', signal='HoldStateChanged', args=[cs.HS_HELD, cs.HSR_RESOURCE_NOT_AVAILABLE]), ) sync_stream(q, stream) q.unforbid_events(hold_event + unhold_event) if jp.has_mutable_streams(): mutable_stream_tests(jp, jt, q, bus, conn, stream, chan, handle) # ---- The end ---- chan.Group.RemoveMembers([self_handle], 'closed') # Test completed, close the connection e = q.expect('dbus-signal', signal='Close') #XXX - match against the path if __name__ == '__main__': test_all_dialects(test) telepathy-gabble-0.18.2/tests/twisted/jingle/hold-audio.py0000644000175000017500000003617312227000321023510 0ustar00smcvsmcv00000000000000""" Test the Hold API. """ from gabbletest import make_result_iq, sync_stream from servicetest import ( assertEquals, sync_dbus, make_channel_proxy, call_async, EventPattern, wrap_channel, ) import constants as cs from jingletest2 import JingleTest2, test_all_dialects from config import VOIP_ENABLED if not VOIP_ENABLED: print "NOTE: built with --disable-voip" raise SystemExit(77) def test(jp, q, bus, conn, stream): remote_jid = 'foo@bar.com/Foo' jt = JingleTest2(jp, conn, q, stream, 'test@localhost', remote_jid) jt.prepare() self_handle = conn.GetSelfHandle() handle = conn.RequestHandles(cs.HT_CONTACT, [remote_jid])[0] path = conn.RequestChannel(cs.CHANNEL_TYPE_STREAMED_MEDIA, cs.HT_CONTACT, handle, True) chan = wrap_channel(bus.get_object(conn.bus_name, path), 'StreamedMedia', ['Hold']) # These are 0- (for old dialects) or 1- (for new dialects) element lists # that can be splatted into expect_many with * hold_event = jp.rtp_info_event_list("hold") unhold_event = jp.rtp_info_event_list("unhold") # Before we have any streams, GetHoldState returns Unheld and unhold is a # no-op. assertEquals((cs.HS_UNHELD, cs.HSR_NONE), chan.Hold.GetHoldState()) chan.Hold.RequestHold(False) # Before we have any streams, RequestHold(True) should work; because there # are no streams, it should take effect at once. It certainly shouldn't # send anything to the peer. q.forbid_events(hold_event) q.forbid_events(unhold_event) call_async(q, chan.Hold, 'RequestHold', True) q.expect('dbus-signal', signal='HoldStateChanged', args=[cs.HS_PENDING_HOLD, cs.HSR_REQUESTED]) q.expect('dbus-signal', signal='HoldStateChanged', args=[cs.HS_HELD, cs.HSR_REQUESTED]) assertEquals((cs.HS_HELD, cs.HSR_REQUESTED), chan.Hold.GetHoldState()) # If we unhold, it should succeed immediately again, because there are no # resources to reclaim. call_async(q, chan.Hold, 'RequestHold', False) q.expect('dbus-signal', signal='HoldStateChanged', args=[cs.HS_PENDING_UNHOLD, cs.HSR_REQUESTED]) q.expect('dbus-signal', signal='HoldStateChanged', args=[cs.HS_UNHELD, cs.HSR_REQUESTED]) assertEquals((cs.HS_UNHELD, cs.HSR_REQUESTED), chan.Hold.GetHoldState()) # Put the call back on hold ... call_async(q, chan.Hold, 'RequestHold', True) q.expect('dbus-signal', signal='HoldStateChanged', args=[cs.HS_PENDING_HOLD, cs.HSR_REQUESTED]) q.expect('dbus-signal', signal='HoldStateChanged', args=[cs.HS_HELD, cs.HSR_REQUESTED]) assertEquals((cs.HS_HELD, cs.HSR_REQUESTED), chan.Hold.GetHoldState()) # ... and request a stream. chan.StreamedMedia.RequestStreams(handle, [cs.MEDIA_STREAM_TYPE_AUDIO]) # S-E gets notified about new session handler, and calls Ready on it e = q.expect('dbus-signal', signal='NewSessionHandler') assert e.args[1] == 'rtp' session_handler = make_channel_proxy(conn, e.args[0], 'Media.SessionHandler') session_handler.Ready() e = q.expect('dbus-signal', signal='NewStreamHandler') stream_handler = make_channel_proxy(conn, e.args[0], 'Media.StreamHandler') # Syncing here to make sure SetStreamHeld happens after Ready... sync_dbus(bus, q, conn) stream_handler.Ready(jt.get_audio_codecs_dbus()) stream_handler.NewNativeCandidate("fake", jt.get_remote_transports_dbus()) stream_handler.StreamState(cs.MEDIA_STREAM_STATE_CONNECTED) # Now Gabble tells the streaming implementation to go on hold (because it # said it was Ready), and the session is initiated. e = q.expect_many( EventPattern('dbus-signal', signal='SetStreamHeld', args=[True]), EventPattern('stream-iq', predicate=jp.action_predicate('session-initiate')), )[1] # Ensure that if Gabble sent the stanza too early it's already # arrived. sync_stream(q, stream) q.unforbid_events(hold_event) stream.send(make_result_iq(stream, e.stanza)) # We've acked the s-i, so we do speak Jingle; Gabble should send the # notification. q.expect_many(*hold_event) # The call's still on hold, both before and after the streaming # implementation says it's okay with that (re-entering PENDING_HOLD seems # silly). assertEquals((cs.HS_HELD, cs.HSR_REQUESTED), chan.Hold.GetHoldState()) stream_handler.HoldState(True) assertEquals((cs.HS_HELD, cs.HSR_REQUESTED), chan.Hold.GetHoldState()) # The peer answers the call; they're still on hold. jt.parse_session_initiate(e.query) jt.accept() q.expect('stream-iq', iq_type='result') assertEquals((cs.HS_HELD, cs.HSR_REQUESTED), chan.Hold.GetHoldState()) # Now we decide we do actually want to speak to them, and unhold. # Ensure that if Gabble sent the stanza too early it's already # arrived. sync_stream(q, stream) q.forbid_events(unhold_event) call_async(q, chan.Hold, 'RequestHold', False) q.expect_many( EventPattern('dbus-signal', signal='HoldStateChanged', args=[cs.HS_PENDING_UNHOLD, cs.HSR_REQUESTED]), EventPattern('dbus-signal', signal='SetStreamHeld', args=[False]), EventPattern('dbus-return', method='RequestHold', value=()), ) # Ensure that if Gabble sent the stanza too early it's already # arrived. sync_stream(q, stream) q.unforbid_events(unhold_event) call_async(q, stream_handler, 'HoldState', False) q.expect_many( EventPattern('dbus-return', method='HoldState', value=()), EventPattern('dbus-signal', signal='HoldStateChanged', args=[cs.HS_UNHELD, cs.HSR_REQUESTED]), *unhold_event ) # Hooray! Now let's check that Hold works properly once the call's fully # established. # ---- Test 1: GetHoldState returns unheld and unhold is a no-op ---- hold_state = chan.Hold.GetHoldState() assert hold_state[0] == cs.HS_UNHELD, hold_state chan.Hold.RequestHold(False) # ---- Test 2: successful hold ---- call_async(q, chan.Hold, 'RequestHold', True) q.expect_many( EventPattern('dbus-signal', signal='HoldStateChanged', args=[cs.HS_PENDING_HOLD, cs.HSR_REQUESTED]), EventPattern('dbus-signal', signal='SetStreamHeld', args=[True]), EventPattern('dbus-return', method='RequestHold', value=()), *hold_event ) call_async(q, stream_handler, 'HoldState', True) q.expect_many( EventPattern('dbus-return', method='HoldState', value=()), EventPattern('dbus-signal', signal='HoldStateChanged', args=[cs.HS_HELD, cs.HSR_REQUESTED]), ) # ---- Test 3: GetHoldState returns held and hold is a no-op ---- hold_state = chan.Hold.GetHoldState() assert hold_state[0] == cs.HS_HELD, hold_state chan.Hold.RequestHold(True) # ---- Test 4: successful unhold ---- q.forbid_events(unhold_event) call_async(q, chan.Hold, 'RequestHold', False) q.expect_many( EventPattern('dbus-signal', signal='HoldStateChanged', args=[cs.HS_PENDING_UNHOLD, cs.HSR_REQUESTED]), EventPattern('dbus-signal', signal='SetStreamHeld', args=[False]), EventPattern('dbus-return', method='RequestHold', value=()), ) # Ensure that if Gabble sent the stanza too early it's already # arrived. sync_stream(q, stream) q.unforbid_events(unhold_event) call_async(q, stream_handler, 'HoldState', False) q.expect_many( EventPattern('dbus-return', method='HoldState', value=()), EventPattern('dbus-signal', signal='HoldStateChanged', args=[cs.HS_UNHELD, cs.HSR_REQUESTED]), *unhold_event ) # ---- Test 5: GetHoldState returns False and unhold is a no-op ---- hold_state = chan.Hold.GetHoldState() assert hold_state[0] == cs.HS_UNHELD, hold_state chan.Hold.RequestHold(False) # ---- Test 6: 3 parallel calls to hold ---- hold_state = chan.Hold.GetHoldState() assert hold_state[0] == cs.HS_UNHELD, hold_state call_async(q, chan.Hold, 'RequestHold', True) call_async(q, chan.Hold, 'RequestHold', True) call_async(q, chan.Hold, 'RequestHold', True) q.expect_many( EventPattern('dbus-signal', signal='HoldStateChanged', args=[cs.HS_PENDING_HOLD, cs.HSR_REQUESTED]), EventPattern('dbus-signal', signal='SetStreamHeld', args=[True]), EventPattern('dbus-return', method='RequestHold', value=()), *hold_event ) call_async(q, stream_handler, 'HoldState', True) q.expect_many( EventPattern('dbus-return', method='HoldState', value=()), EventPattern('dbus-signal', signal='HoldStateChanged', args=[cs.HS_HELD, cs.HSR_REQUESTED]), ) # ---- Test 7: 3 parallel calls to unhold ---- q.forbid_events(unhold_event) call_async(q, chan.Hold, 'RequestHold', False) call_async(q, chan.Hold, 'RequestHold', False) call_async(q, chan.Hold, 'RequestHold', False) q.expect_many( EventPattern('dbus-signal', signal='HoldStateChanged', args=[cs.HS_PENDING_UNHOLD, cs.HSR_REQUESTED]), EventPattern('dbus-signal', signal='SetStreamHeld', args=[False]), EventPattern('dbus-return', method='RequestHold', value=()), ) # Ensure that if Gabble sent the stanza too early it's already # arrived. sync_stream(q, stream) q.unforbid_events(unhold_event) call_async(q, stream_handler, 'HoldState', False) q.expect_many( EventPattern('dbus-return', method='HoldState', value=()), EventPattern('dbus-signal', signal='HoldStateChanged', args=[cs.HS_UNHELD, cs.HSR_REQUESTED]), *unhold_event ) # ---- Test 8: hold, then change our minds before s-e has responded ---- hold_state = chan.Hold.GetHoldState() assert hold_state[0] == cs.HS_UNHELD, hold_state call_async(q, chan.Hold, 'RequestHold', True) q.expect_many( EventPattern('dbus-signal', signal='HoldStateChanged', args=[cs.HS_PENDING_HOLD, cs.HSR_REQUESTED]), EventPattern('dbus-signal', signal='SetStreamHeld', args=[True]), *hold_event ) q.forbid_events(unhold_event) call_async(q, chan.Hold, 'RequestHold', False) q.expect_many( EventPattern('dbus-signal', signal='HoldStateChanged', args=[cs.HS_PENDING_UNHOLD, cs.HSR_REQUESTED]), EventPattern('dbus-signal', signal='SetStreamHeld', args=[False]), # Gabble shouldn't send here because s-e might have already # relinquished the audio hardware. ) sync_stream(q, stream) q.unforbid_events(unhold_event) call_async(q, stream_handler, 'HoldState', True) q.expect('dbus-return', method='HoldState', value=()) call_async(q, stream_handler, 'HoldState', False) q.expect_many( EventPattern('dbus-return', method='HoldState', value=()), EventPattern('dbus-signal', signal='HoldStateChanged', args=[cs.HS_UNHELD, cs.HSR_REQUESTED]), *unhold_event ) hold_state = chan.Hold.GetHoldState() assert hold_state[0] == cs.HS_UNHELD, hold_state # ---- Test 9: unhold, then change our minds before s-e has responded ---- # Go to state "held" first call_async(q, chan.Hold, 'RequestHold', True) q.expect_many( EventPattern('dbus-signal', signal='HoldStateChanged', args=[cs.HS_PENDING_HOLD, cs.HSR_REQUESTED]), EventPattern('dbus-signal', signal='SetStreamHeld', args=[True]), EventPattern('dbus-return', method='RequestHold', value=()), *hold_event ) call_async(q, stream_handler, 'HoldState', True) q.expect_many( EventPattern('dbus-return', method='HoldState', value=()), EventPattern('dbus-signal', signal='HoldStateChanged', args=[cs.HS_HELD, cs.HSR_REQUESTED]), ) # Actually do test 9 hold_state = chan.Hold.GetHoldState() assert hold_state[0] == cs.HS_HELD, hold_state # Check that Gabble doesn't send another , or send before # we change our minds. q.forbid_events(unhold_event + hold_event) call_async(q, chan.Hold, 'RequestHold', False) q.expect_many( EventPattern('dbus-signal', signal='HoldStateChanged', args=[cs.HS_PENDING_UNHOLD, cs.HSR_REQUESTED]), EventPattern('dbus-signal', signal='SetStreamHeld', args=[False]), ) call_async(q, chan.Hold, 'RequestHold', True) q.expect_many( EventPattern('dbus-signal', signal='HoldStateChanged', args=[cs.HS_PENDING_HOLD, cs.HSR_REQUESTED]), EventPattern('dbus-signal', signal='SetStreamHeld', args=[True]), ) call_async(q, stream_handler, 'HoldState', False) call_async(q, stream_handler, 'HoldState', True) q.expect_many( EventPattern('dbus-return', method='HoldState', value=()), EventPattern('dbus-signal', signal='HoldStateChanged', args=[cs.HS_HELD, cs.HSR_REQUESTED]), ) hold_state = chan.Hold.GetHoldState() assert hold_state[0] == cs.HS_HELD, hold_state sync_stream(q, stream) q.unforbid_events(unhold_event + hold_event) # ---- Test 10: attempting to unhold fails ---- # Check that Gabble doesn't send another , or send even # though unholding fails. q.forbid_events(unhold_event + hold_event) call_async(q, chan.Hold, 'RequestHold', False) q.expect_many( EventPattern('dbus-signal', signal='HoldStateChanged', args=[cs.HS_PENDING_UNHOLD, cs.HSR_REQUESTED]), EventPattern('dbus-signal', signal='SetStreamHeld', args=[False]), EventPattern('dbus-return', method='RequestHold', value=()), ) call_async(q, stream_handler, 'UnholdFailure') q.expect_many( EventPattern('dbus-return', method='UnholdFailure', value=()), EventPattern('dbus-signal', signal='HoldStateChanged', args=[cs.HS_HELD, cs.HSR_RESOURCE_NOT_AVAILABLE]), ) sync_stream(q, stream) q.unforbid_events(unhold_event + hold_event) # ---- Test 11: when we successfully unhold, the peer gets --- q.forbid_events(unhold_event) call_async(q, chan.Hold, 'RequestHold', False) q.expect_many( EventPattern('dbus-signal', signal='HoldStateChanged', args=[cs.HS_PENDING_UNHOLD, cs.HSR_REQUESTED]), EventPattern('dbus-signal', signal='SetStreamHeld', args=[False]), EventPattern('dbus-return', method='RequestHold', value=()), ) # Ensure that if Gabble sent the stanza too early it's already # arrived. sync_stream(q, stream) q.unforbid_events(unhold_event) call_async(q, stream_handler, 'HoldState', False) q.expect_many( EventPattern('dbus-return', method='HoldState', value=()), EventPattern('dbus-signal', signal='HoldStateChanged', args=[cs.HS_UNHELD, cs.HSR_REQUESTED]), *unhold_event ) # ---- The end ---- chan.Group.RemoveMembers([self_handle], 'closed') # Test completed, close the connection e = q.expect('dbus-signal', signal='Close') #XXX - match against the path if __name__ == '__main__': test_all_dialects(test) telepathy-gabble-0.18.2/tests/twisted/jingle/google-relay.py0000644000175000017500000003624612312320035024054 0ustar00smcvsmcv00000000000000""" Test getting relay from Google jingleinfo """ import config if not config.GOOGLE_RELAY_ENABLED: print "NOTE: built with --disable-google-relay" raise SystemExit(77) from functools import partial from gabbletest import exec_test, make_result_iq, sync_stream, \ GoogleXmlStream, disconnect_conn from servicetest import make_channel_proxy, \ EventPattern, call_async, sync_dbus, assertEquals, assertLength import jingletest2 import gabbletest import constants as cs import dbus import ns import config from twisted.words.protocols.jabber.client import IQ from twisted.web import http from httptest import listen_http from config import VOIP_ENABLED if not VOIP_ENABLED: print "NOTE: built with --disable-voip" raise SystemExit(77) # A real request/response looks like this: # # GET /create_session HTTP/1.1 # Connection: Keep-Alive # Content-Length: 0 # Host: relay.l.google.com # User-Agent: farsight-libjingle # X-Google-Relay-Auth: censored # X-Talk-Google-Relay-Auth: censored # # HTTP/1.1 200 OK # Content-Type: text/plain # Date: Tue, 03 Mar 2009 18:33:28 GMT # Server: MediaProxy # Cache-Control: private, x-gzip-ok="" # Transfer-Encoding: chunked # # c3 # relay.ip=74.125.47.126 # relay.udp_port=19295 # relay.tcp_port=19294 # relay.ssltcp_port=443 # stun.ip=74.125.47.126 # stun.port=19302 # username=censored # password=censored # magic_cookie=censored # # 0 response_template = """c3 relay.ip=127.0.0.1 relay.udp_port=11111 relay.tcp_port=22222 relay.ssltcp_port=443 stun.ip=1.2.3.4 stun.port=12345 username=UUUUUUUU%d password=PPPPPPPP%d magic_cookie=MMMMMMMM """ def handle_request(req, n): req.setResponseCode(http.OK) req.setHeader("Content-Type", "text/plain") req.write(response_template % (n, n)) req.finish() TOO_SLOW_CLOSE = 1 TOO_SLOW_REMOVE_SELF = 2 TOO_SLOW_DISCONNECT = 3 TOO_SLOW_DISCONNECT_IMMEDIATELY = 4 def test(q, bus, conn, stream, incoming=True, too_slow=None, use_call=False): jp = jingletest2.JingleProtocol031() jt = jingletest2.JingleTest2(jp, conn, q, stream, 'test@localhost', 'foo@bar.com/Foo') if use_call: # wjt only updated just about enough of this test for Call to check for # one specific crash, not to verify that it all works... assert incoming assert too_slow in [TOO_SLOW_CLOSE, TOO_SLOW_DISCONNECT] # Tell Gabble we want to use Call. conn.ContactCapabilities.UpdateCapabilities([ (cs.CLIENT + ".CallHandler", [ { cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_CALL, cs.CALL_INITIAL_AUDIO: True}, { cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_CALL, cs.CALL_INITIAL_VIDEO: True}, ], [ cs.CHANNEL_TYPE_CALL + '/gtalk-p2p', cs.CHANNEL_TYPE_CALL + '/ice', cs.CHANNEL_TYPE_CALL + '/video/h264', ]), ]) # See: http://code.google.com/apis/talk/jep_extensions/jingleinfo.html ji_event = q.expect('stream-iq', query_ns=ns.GOOGLE_JINGLE_INFO, to='test@localhost') # Regression test for a bug where Gabble would crash if it disconnected # before receiving a reply to the google:jingleinfo query. if too_slow == TOO_SLOW_DISCONNECT_IMMEDIATELY: disconnect_conn(q, conn, stream, []) return listen_port = listen_http(q, 0) jingleinfo = make_result_iq(stream, ji_event.stanza) stun = jingleinfo.firstChildElement().addElement('stun') server = stun.addElement('server') server['host'] = 'resolves-to-1.2.3.4' server['udp'] = '12345' expected_stun_server = '1.2.3.4' expected_stun_port = 12345 # This bit is undocumented... but it has the same format as what we get # from Google Talk servers: # # # # # # # # # # # censored # # # # relay = jingleinfo.firstChildElement().addElement('relay') relay.addElement('token', content='jingle all the way') server = relay.addElement('server') server['host'] = '127.0.0.1' server['udp'] = '11111' server['tcp'] = '22222' server['tcpssl'] = '443' # The special regression-test build of Gabble parses this attribute, # because we can't listen on port 80 server['gabble-test-http-port'] = str(listen_port.getHost().port) stream.send(jingleinfo) jingleinfo = None # Spoof some jingle info. This is a regression test for # . We assert that # Gabble has ignored this stuff later. iq = IQ(stream, 'set') iq['from'] = "evil@evil.net" query = iq.addElement((ns.GOOGLE_JINGLE_INFO, "query")) stun = query.addElement('stun') server = stun.addElement('server') server['host'] = '6.6.6.6' server['udp'] = '6666' relay = query.addElement('relay') relay.addElement('token', content='mwohahahahaha') server = relay.addElement('server') server['host'] = '127.0.0.1' server['udp'] = '666' server['tcp'] = '999' server['tcpssl'] = '666' stream.send(iq) jt.send_presence_and_caps() remote_handle = conn.RequestHandles(cs.HT_CONTACT, ["foo@bar.com/Foo"])[0] self_handle = conn.GetSelfHandle() req_pattern = EventPattern('http-request', method='GET', path='/create_session') if incoming: # Remote end calls us jt.incoming_call() if use_call: def looks_like_a_call_to_me(event): channels, = event.args path, props = channels[0] return props[cs.CHANNEL_TYPE] == cs.CHANNEL_TYPE_CALL new_channels = q.expect('dbus-signal', signal='NewChannels', predicate=looks_like_a_call_to_me) path = new_channels.args[0][0][0] media_chan = bus.get_object(conn.bus_name, path) else: # FIXME: these signals are not observable by real clients, since they # happen before NewChannels. # The caller is in members # We're pending because of remote_handle mc, _, e = q.expect_many( EventPattern('dbus-signal', signal='MembersChanged', args=[u'', [remote_handle], [], [], [], 0, 0]), EventPattern('dbus-signal', signal='MembersChanged', args=[u'', [], [], [self_handle], [], remote_handle, cs.GC_REASON_INVITED]), EventPattern('dbus-signal', signal='NewSessionHandler')) media_chan = make_channel_proxy(conn, mc.path, 'Channel.Interface.Group') media_iface = make_channel_proxy(conn, mc.path, 'Channel.Type.StreamedMedia') else: call_async(q, conn.Requests, 'CreateChannel', { cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_STREAMED_MEDIA, cs.TARGET_HANDLE_TYPE: cs.HT_CONTACT, cs.TARGET_HANDLE: remote_handle, }) ret, old_sig, new_sig = q.expect_many( EventPattern('dbus-return', method='CreateChannel'), EventPattern('dbus-signal', signal='NewChannel', predicate=lambda e: cs.CHANNEL_TYPE_CONTACT_LIST not in e.args), EventPattern('dbus-signal', signal='NewChannels', predicate=lambda e: cs.CHANNEL_TYPE_CONTACT_LIST not in e.args[0][0][1].values()), ) path = ret.value[0] media_chan = make_channel_proxy(conn, path, 'Channel.Interface.Group') media_iface = make_channel_proxy(conn, path, 'Channel.Type.StreamedMedia') call_async(q, media_iface, 'RequestStreams', remote_handle, [cs.MEDIA_STREAM_TYPE_AUDIO]) e = q.expect('dbus-signal', signal='NewSessionHandler') req1 = q.expect('http-request', method='GET', path='/create_session') req2 = q.expect('http-request', method='GET', path='/create_session') if too_slow is not None: test_too_slow(q, bus, conn, stream, req1, req2, media_chan, too_slow) return if incoming: assertLength(0, media_iface.ListStreams()) # Accept the call. media_chan.AddMembers([self_handle], '') # In response to the streams call, we now have two HTTP requests # (for RTP and RTCP) handle_request(req1.request, 0) handle_request(req2.request, 1) if incoming: # We accepted the call, and it should get a new, bidirectional stream # now that the relay info request has finished. This tests against a # regression of bug #24023. q.expect('dbus-signal', signal='StreamAdded', args=[1, remote_handle, cs.MEDIA_STREAM_TYPE_AUDIO]) q.expect('dbus-signal', signal='StreamDirectionChanged', args=[1, cs.MEDIA_STREAM_DIRECTION_BIDIRECTIONAL, 0]) else: # Now that we have the relay info, RequestStreams can return q.expect('dbus-return', method='RequestStreams') session_handler = make_channel_proxy(conn, e.args[0], 'Media.SessionHandler') session_handler.Ready() e = q.expect('dbus-signal', signal='NewStreamHandler') stream_handler = make_channel_proxy(conn, e.args[0], 'Media.StreamHandler') # Exercise channel properties channel_props = media_chan.GetAll( cs.CHANNEL, dbus_interface=dbus.PROPERTIES_IFACE) assert channel_props['TargetHandle'] == remote_handle assert channel_props['TargetHandleType'] == cs.HT_CONTACT assert channel_props['TargetID'] == 'foo@bar.com' assert channel_props['Requested'] == (not incoming) # The new API for STUN servers etc. sh_props = stream_handler.GetAll( cs.STREAM_HANDLER, dbus_interface=dbus.PROPERTIES_IFACE) assert sh_props['NATTraversal'] == 'gtalk-p2p' assert sh_props['CreatedLocally'] == (not incoming) # If Gabble has erroneously paid attention to the contact evil@evil.net who # sent us a google:jingleinfo stanza, this assertion will fail. assertEquals([(expected_stun_server, expected_stun_port)], sh_props['STUNServers']) credentials_used = {} credentials = {} for relay in sh_props['RelayInfo']: assert relay['ip'] == '127.0.0.1', sh_props['RelayInfo'] assert relay['type'] in ('udp', 'tcp', 'tls') assert relay['component'] in (1, 2) if relay['type'] == 'udp': assert relay['port'] == 11111, sh_props['RelayInfo'] elif relay['type'] == 'tcp': assert relay['port'] == 22222, sh_props['RelayInfo'] elif relay['type'] == 'tls': assert relay['port'] == 443, sh_props['RelayInfo'] assert relay['username'][:8] == 'UUUUUUUU', sh_props['RelayInfo'] assert relay['password'][:8] == 'PPPPPPPP', sh_props['RelayInfo'] assert relay['password'][8:] == relay['username'][8:], \ sh_props['RelayInfo'] assert (relay['password'][8:], relay['type']) not in credentials_used credentials_used[(relay['password'][8:], relay['type'])] = 1 credentials[(relay['component'], relay['type'])] = relay['password'][8:] assert (1, 'udp') in credentials assert (1, 'tcp') in credentials assert (1, 'tls') in credentials assert (2, 'udp') in credentials assert (2, 'tcp') in credentials assert (2, 'tls') in credentials assert ('0', 'udp') in credentials_used assert ('0', 'tcp') in credentials_used assert ('0', 'tls') in credentials_used assert ('1', 'udp') in credentials_used assert ('1', 'tcp') in credentials_used assert ('1', 'tls') in credentials_used # consistency check, since we currently reimplement Get separately for k in sh_props: assert sh_props[k] == stream_handler.Get( 'org.freedesktop.Telepathy.Media.StreamHandler', k, dbus_interface=dbus.PROPERTIES_IFACE), k media_chan.RemoveMembers([self_handle], '') if incoming: q.expect_many( EventPattern('stream-iq', predicate=lambda e: e.query is not None and e.query.name == 'jingle' and e.query['action'] == 'session-terminate'), EventPattern('dbus-signal', signal='Closed'), ) else: # We haven't sent a session-initiate, so we shouldn't expect to send a # session-terminate. q.expect('dbus-signal', signal='Closed') # Tests completed, close the connection def test_too_slow(q, bus, conn, stream, req1, req2, media_chan, too_slow): """ Regression test for a bug where if the channel was closed before the HTTP responses arrived, the responses finally arriving crashed Gabble. """ # User gets bored, and ends the call. e = EventPattern('dbus-signal', signal='Closed', path=media_chan.object_path) if too_slow == TOO_SLOW_CLOSE: call_async(q, media_chan, 'Close', dbus_interface=cs.CHANNEL) elif too_slow == TOO_SLOW_REMOVE_SELF: media_chan.RemoveMembers([conn.GetSelfHandle()], "", dbus_interface=cs.CHANNEL_IFACE_GROUP) elif too_slow == TOO_SLOW_DISCONNECT: disconnect_conn(q, conn, stream, [e]) try: media_chan.GetMembers() except dbus.DBusException, e: # This should fail because the object's gone away, not because # Gabble's crashed. assert cs.UNKNOWN_METHOD == e.get_dbus_name(), \ "maybe Gabble crashed? %s" % e else: # Gabble will probably also crash in a moment, because the http # request callbacks will be called after the channel's meant to # have died, which will cause the channel to try to call methods on # the (finalized) connection. assert False, "the channel should be dead by now" return q.expect_many(e) # Now Google answers! handle_request(req1.request, 2) handle_request(req2.request, 3) # Make a misc method call to check that Gabble's still alive. sync_dbus(bus, q, conn) def exec_relay_test(incoming, too_slow=None, use_call=False): exec_test( partial(test, incoming=incoming, too_slow=too_slow, use_call=use_call), protocol=GoogleXmlStream) if __name__ == '__main__': exec_relay_test(True) exec_relay_test(False) exec_relay_test(True, TOO_SLOW_CLOSE) exec_relay_test(False, TOO_SLOW_CLOSE) exec_relay_test(True, TOO_SLOW_REMOVE_SELF) exec_relay_test(False, TOO_SLOW_REMOVE_SELF) exec_relay_test(True, TOO_SLOW_DISCONNECT) exec_relay_test(False, TOO_SLOW_DISCONNECT) exec_relay_test(True, TOO_SLOW_DISCONNECT_IMMEDIATELY) if config.CHANNEL_TYPE_CALL_ENABLED: exec_relay_test(True, TOO_SLOW_CLOSE, use_call=True) exec_relay_test(True, TOO_SLOW_DISCONNECT, use_call=True) telepathy-gabble-0.18.2/tests/twisted/jingle/dtmf.py0000644000175000017500000001572112227000321022411 0ustar00smcvsmcv00000000000000""" Test DTMF events. """ from twisted.words.xish import xpath from gabbletest import make_result_iq from servicetest import (call_async, wrap_channel, make_channel_proxy, EventPattern, sync_dbus, assertEquals) import constants as cs from jingletest2 import JingleTest2, test_all_dialects from config import VOIP_ENABLED if not VOIP_ENABLED: print "NOTE: built with --disable-voip" raise SystemExit(77) def test(jp, q, bus, conn, stream): # this test uses multiple streams if not jp.is_modern_jingle(): return remote_jid = 'foo@bar.com/Foo' jt = JingleTest2(jp, conn, q, stream, 'test@localhost', remote_jid) jt.prepare() self_handle = conn.GetSelfHandle() handle = conn.RequestHandles(cs.HT_CONTACT, [remote_jid])[0] chan_path = conn.RequestChannel(cs.CHANNEL_TYPE_STREAMED_MEDIA, cs.HT_CONTACT, handle, True) chan = wrap_channel(bus.get_object(conn.bus_name, chan_path), 'StreamedMedia', ['MediaSignalling', 'Group', 'CallState', 'DTMF']) chan_props = chan.Properties.GetAll(cs.CHANNEL) assert cs.CHANNEL_IFACE_DTMF in chan_props['Interfaces'], \ chan_props['Interfaces'] assertEquals('', chan.Properties.Get(cs.CHANNEL_IFACE_DTMF, 'InitialTones')) assertEquals('', chan.Properties.Get(cs.CHANNEL_IFACE_DTMF, 'DeferredTones')) chan.StreamedMedia.RequestStreams(handle, [cs.MEDIA_STREAM_TYPE_AUDIO]) # S-E gets notified about new session handler, and calls Ready on it e = q.expect('dbus-signal', signal='NewSessionHandler') assert e.args[1] == 'rtp' session_handler = make_channel_proxy(conn, e.args[0], 'Media.SessionHandler') session_handler.Ready() e = q.expect('dbus-signal', signal='NewStreamHandler') audio_path = e.args[0] stream_handler = make_channel_proxy(conn, audio_path, 'Media.StreamHandler') stream_handler.NewNativeCandidate("fake", jt.get_remote_transports_dbus()) stream_handler.Ready(jt.get_audio_codecs_dbus()) stream_handler.StreamState(cs.MEDIA_STREAM_STATE_CONNECTED) e = q.expect('stream-iq', predicate=jp.action_predicate('session-initiate')) stream.send(make_result_iq(stream, e.stanza)) jt.parse_session_initiate(e.query) jt.accept() # Gabble tells s-e to start sending q.expect('dbus-signal', signal='SetStreamSending', args=[True], path=audio_path) chan.StreamedMedia.RequestStreams(handle, [cs.MEDIA_STREAM_TYPE_AUDIO]) e = q.expect('dbus-signal', signal='NewStreamHandler') audio2_path = e.args[0] # The Stream_ID is specified to be ignored; we use 666 here. call_async(q, chan.DTMF, 'StartTone', 666, 3) q.expect_many( EventPattern('dbus-signal', signal='StartTelephonyEvent', path=audio2_path), EventPattern('dbus-signal', signal='StartTelephonyEvent', path=audio_path), EventPattern('dbus-signal', signal='SendingTones', args=['3'], path=chan_path), EventPattern('dbus-return', method='StartTone'), ) call_async(q, chan.DTMF, 'StopTone', 666) q.expect_many( EventPattern('dbus-signal', signal='StopTelephonyEvent', path=audio_path), EventPattern('dbus-signal', signal='StopTelephonyEvent', path=audio2_path), EventPattern('dbus-signal', signal='StoppedTones', args=[True], path=chan_path), EventPattern('dbus-return', method='StopTone'), ) call_async(q, chan.DTMF, 'MultipleTones', '123w*#') q.expect_many( EventPattern('dbus-signal', signal='StartTelephonyEvent', path=audio_path, args=[1]), EventPattern('dbus-signal', signal='StartTelephonyEvent', path=audio2_path, args=[1]), EventPattern('dbus-signal', signal='SendingTones', args=['123w*#'], path=chan_path), EventPattern('dbus-return', method='MultipleTones'), ) q.expect_many( EventPattern('dbus-signal', signal='StopTelephonyEvent', path=audio_path), EventPattern('dbus-signal', signal='StopTelephonyEvent', path=audio2_path), ) q.expect_many( EventPattern('dbus-signal', signal='StartTelephonyEvent', path=audio_path, args=[2]), EventPattern('dbus-signal', signal='StartTelephonyEvent', path=audio2_path, args=[2]), ) q.expect_many( EventPattern('dbus-signal', signal='StopTelephonyEvent', path=audio_path), EventPattern('dbus-signal', signal='StopTelephonyEvent', path=audio2_path), ) q.expect_many( EventPattern('dbus-signal', signal='StartTelephonyEvent', path=audio_path, args=[3]), EventPattern('dbus-signal', signal='StartTelephonyEvent', path=audio2_path, args=[3]), ) q.expect_many( EventPattern('dbus-signal', signal='StopTelephonyEvent', path=audio_path), EventPattern('dbus-signal', signal='StopTelephonyEvent', path=audio2_path), EventPattern('dbus-signal', signal='StoppedTones', args=[False], path=chan_path), EventPattern('dbus-signal', signal='TonesDeferred', args=['*#']), ) assertEquals('*#', chan.Properties.Get(cs.CHANNEL_IFACE_DTMF, 'DeferredTones')) forbidden = [EventPattern('dbus-signal', signal='StartTelephonyEvent', args=[9])] q.forbid_events(forbidden) # This is technically a race condition, but this dialstring is almost # certainly long enough that the Python script will win the race, i.e. # cancel before Gabble processes the whole dialstring. call_async(q, chan.DTMF, 'MultipleTones', '1,1' * 100) q.expect('dbus-return', method='MultipleTones') call_async(q, chan.DTMF, 'MultipleTones', '9') q.expect('dbus-error', method='MultipleTones', name=cs.SERVICE_BUSY) call_async(q, chan.DTMF, 'StartTone', 666, 9) q.expect('dbus-error', method='StartTone', name=cs.SERVICE_BUSY) call_async(q, chan.DTMF, 'StopTone', 666) q.expect_many( EventPattern('dbus-signal', signal='StopTelephonyEvent', path=audio_path), EventPattern('dbus-signal', signal='StopTelephonyEvent', path=audio2_path), EventPattern('dbus-signal', signal='StoppedTones', args=[True], path=chan_path), EventPattern('dbus-return', method='StopTone'), ) # emitting any sound resets TonesDeferred assertEquals('', chan.Properties.Get(cs.CHANNEL_IFACE_DTMF, 'DeferredTones')) q.unforbid_events(forbidden) chan.Group.RemoveMembers([self_handle], 'closed') e = q.expect('dbus-signal', signal='Closed', path=chan_path) if __name__ == '__main__': test_all_dialects(test) telepathy-gabble-0.18.2/tests/twisted/jingle/dtmf-no-audio.py0000644000175000017500000000546712227000321024130 0ustar00smcvsmcv00000000000000""" Test inability to send DTMF events on a video-only call. """ from twisted.words.xish import xpath from gabbletest import make_result_iq from servicetest import (call_async, wrap_channel, make_channel_proxy, EventPattern, sync_dbus) import constants as cs from jingletest2 import JingleTest2, test_all_dialects from config import VOIP_ENABLED if not VOIP_ENABLED: print "NOTE: built with --disable-voip" raise SystemExit(77) def test(jp, q, bus, conn, stream): if not jp.can_do_video_only(): return remote_jid = 'foo@bar.com/Foo' jt = JingleTest2(jp, conn, q, stream, 'test@localhost', remote_jid) jt.prepare() self_handle = conn.GetSelfHandle() handle = conn.RequestHandles(cs.HT_CONTACT, [remote_jid])[0] chan_path = conn.RequestChannel(cs.CHANNEL_TYPE_STREAMED_MEDIA, cs.HT_CONTACT, handle, True) chan = wrap_channel(bus.get_object(conn.bus_name, chan_path), 'StreamedMedia', ['MediaSignalling', 'Group', 'CallState', 'DTMF']) chan_props = chan.Properties.GetAll(cs.CHANNEL) assert cs.CHANNEL_IFACE_DTMF in chan_props['Interfaces'], \ chan_props['Interfaces'] chan.StreamedMedia.RequestStreams(handle, [cs.MEDIA_STREAM_TYPE_VIDEO]) # S-E gets notified about new session handler, and calls Ready on it e = q.expect('dbus-signal', signal='NewSessionHandler') assert e.args[1] == 'rtp' session_handler = make_channel_proxy(conn, e.args[0], 'Media.SessionHandler') session_handler.Ready() e = q.expect('dbus-signal', signal='NewStreamHandler') video_path = e.args[0] stream_handler = make_channel_proxy(conn, video_path, 'Media.StreamHandler') stream_handler.NewNativeCandidate("fake", jt.get_remote_transports_dbus()) stream_handler.Ready(jt.get_video_codecs_dbus()) stream_handler.StreamState(cs.MEDIA_STREAM_STATE_CONNECTED) e = q.expect('stream-iq', predicate=jp.action_predicate('session-initiate')) stream.send(make_result_iq(stream, e.stanza)) jt.parse_session_initiate(e.query) jt.accept() # Gabble tells s-e to start sending q.expect('dbus-signal', signal='SetStreamSending', args=[True], path=video_path) # We don't actually have an audio stream, so this is a non-starter. call_async(q, chan.DTMF, 'StartTone', 666, 3) q.expect('dbus-error', method='StartTone', name=cs.NOT_AVAILABLE) call_async(q, chan.DTMF, 'MultipleTones', '**666##') q.expect('dbus-error', method='MultipleTones', name=cs.NOT_AVAILABLE) # We can still stop all the tones that are playing (a no-op). call_async(q, chan.DTMF, 'StopTone', 666) q.expect('dbus-return', method='StopTone') chan.Group.RemoveMembers([self_handle], 'closed') e = q.expect('dbus-signal', signal='Closed', path=chan_path) if __name__ == '__main__': test_all_dialects(test) telepathy-gabble-0.18.2/tests/twisted/jingle/decloak-peer.py0000644000175000017500000000427212227000321024011 0ustar00smcvsmcv00000000000000""" Test use-case when client attempts to call an unsubscribed contact. Gabble should ask them to "de-cloak". """ from gabbletest import exec_test from servicetest import (make_channel_proxy, call_async, sync_dbus, assertEquals, assertLength) from jingletest2 import JingleProtocol031, JingleTest2 import dbus import constants as cs import ns from config import VOIP_ENABLED if not VOIP_ENABLED: print "NOTE: built with --disable-voip" raise SystemExit(77) def test(q, bus, conn, stream): jp = JingleProtocol031() jt = JingleTest2(jp, conn, q, stream, 'test@localhost', 'foo@bar.com/Foo') jt2 = JingleTest2(jp, conn, q, stream, 'test@localhost', 'foo2@bar.com/Foo') # Make gabble think this is a different client jt2.remote_caps['node'] = 'http://example.com/fake-client1' run_test(q, bus, conn, stream, jt, True) run_test(q, bus, conn, stream, jt2, False) def run_test(q, bus, conn, stream, jt, decloak_allowed): """ Requests streams on a media channel to jt.remote_jid without having their presence at all. """ request = dbus.Dictionary({ cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_STREAMED_MEDIA, cs.TARGET_HANDLE_TYPE: cs.HT_CONTACT, cs.TARGET_ID: jt.peer, }, signature='sv') path, props = conn.CreateChannel(request, dbus_interface=cs.CONN_IFACE_REQUESTS) media_iface = make_channel_proxy(conn, path, 'Channel.Type.StreamedMedia') handle = props[cs.TARGET_HANDLE] call_async(q, media_iface, 'RequestStreams', handle, [cs.MEDIA_STREAM_TYPE_AUDIO]) e = q.expect('stream-presence', to=jt.peer_bare_jid, presence_type=None) nodes = [ node for node in e.stanza.elements(uri=ns.TEMPPRES, name='temppres') ] assertLength(1, nodes) assertEquals('media', nodes[0].getAttribute('reason')) if decloak_allowed: jt.send_presence_and_caps() # RequestStreams should now happily complete q.expect('dbus-return', method='RequestStreams') else: q.expect('dbus-error', method='RequestStreams', name=cs.OFFLINE) if __name__ == '__main__': exec_test(test, timeout=10) telepathy-gabble-0.18.2/tests/twisted/jingle/call-state.py0000644000175000017500000002605112227000321023506 0ustar00smcvsmcv00000000000000""" Test exposing incoming , and notifications via the CallState interface. """ from twisted.words.xish import xpath from gabbletest import make_result_iq from servicetest import ( wrap_channel, make_channel_proxy, EventPattern, sync_dbus) import ns import constants as cs from jingletest2 import JingleTest2, test_all_dialects from config import VOIP_ENABLED if not VOIP_ENABLED: print "NOTE: built with --disable-voip" raise SystemExit(77) def test(jp, q, bus, conn, stream): remote_jid = 'foo@bar.com/Foo' jt = JingleTest2(jp, conn, q, stream, 'test@localhost', remote_jid) jt.prepare() self_handle = conn.GetSelfHandle() handle = conn.RequestHandles(cs.HT_CONTACT, [remote_jid])[0] path = conn.RequestChannel(cs.CHANNEL_TYPE_STREAMED_MEDIA, cs.HT_CONTACT, handle, True) chan = wrap_channel(bus.get_object(conn.bus_name, path), 'StreamedMedia', ['MediaSignalling', 'Group', 'CallState']) chan_props = chan.Properties.GetAll(cs.CHANNEL) assert cs.CHANNEL_IFACE_CALL_STATE in chan_props['Interfaces'], \ chan_props['Interfaces'] call_states = chan.CallState.GetCallStates() assert call_states == { handle: 0 } or \ call_states == {}, call_states chan.StreamedMedia.RequestStreams(handle, [cs.MEDIA_STREAM_TYPE_AUDIO]) # S-E gets notified about new session handler, and calls Ready on it e = q.expect('dbus-signal', signal='NewSessionHandler') assert e.args[1] == 'rtp' session_handler = make_channel_proxy(conn, e.args[0], 'Media.SessionHandler') session_handler.Ready() e = q.expect('dbus-signal', signal='NewStreamHandler') audio_path = e.args[0] stream_handler = make_channel_proxy(conn, audio_path, 'Media.StreamHandler') stream_handler.NewNativeCandidate("fake", jt.get_remote_transports_dbus()) stream_handler.Ready(jt.get_audio_codecs_dbus()) stream_handler.StreamState(cs.MEDIA_STREAM_STATE_CONNECTED) e = q.expect('stream-iq', predicate=jp.action_predicate('session-initiate')) stream.send(make_result_iq(stream, e.stanza)) jt.parse_session_initiate(e.query) if jp.is_modern_jingle(): # The other person's client starts ringing, and tells us so! node = jp.SetIq(jt.peer, jt.jid, [ jp.Jingle(jt.sid, jt.jid, 'session-info', [ ('ringing', ns.JINGLE_RTP_INFO_1, {}, []) ]) ]) stream.send(jp.xml(node)) # If this is an old Jingle dialect, Gabble should treat the # session-initiate ack as ringing notification; if it's modern Jingle, we # just sent a ringing notification. q.expect('dbus-signal', signal='CallStateChanged', args=[handle, cs.CALL_STATE_RINGING]) call_states = chan.CallState.GetCallStates() assert call_states == { handle: cs.CALL_STATE_RINGING }, call_states if jp.is_modern_jingle(): # We're waiting in a queue, so the other person's client tells us we're on # hold. Gabble should ack the IQ, and set the call state to Ringing | Held. # Also, Gabble certainly shouldn't tell s-e to start sending. (Although it # might tell it not to; we don't mind.) node = jp.SetIq(jt.peer, jt.jid, [ jp.Jingle(jt.sid, jt.jid, 'session-info', [ ('hold', ns.JINGLE_RTP_INFO_1, {}, []) ]) ]) stream.send(jp.xml(node)) forbidden = [ EventPattern('dbus-signal', signal='SetStreamSending', args=[True], path=audio_path), ] q.forbid_events(forbidden) q.expect_many( EventPattern('stream-iq', iq_type='result', iq_id=node[2]['id']), EventPattern('dbus-signal', signal='CallStateChanged', args=[handle, cs.CALL_STATE_RINGING | cs.CALL_STATE_HELD]), ) call_states = chan.CallState.GetCallStates() assert call_states == { handle: cs.CALL_STATE_RINGING | cs.CALL_STATE_HELD }, call_states # We're at the head of a queue, so the other person's client tells us we're # no longer on hold. The call centre phone's still ringing, though. s-e # still shouldn't start sending. node = jp.SetIq(jt.peer, jt.jid, [ jp.Jingle(jt.sid, jt.jid, 'session-info', [ ('unhold', ns.JINGLE_RTP_INFO_1, {}, []) ]) ]) stream.send(jp.xml(node)) q.expect_many( EventPattern('stream-iq', iq_type='result', iq_id=node[2]['id']), EventPattern('dbus-signal', signal='CallStateChanged', args=[handle, cs.CALL_STATE_RINGING]), ) call_states = chan.CallState.GetCallStates() assert call_states == { handle: cs.CALL_STATE_RINGING }, call_states sync_dbus(bus, q, conn) q.unforbid_events(forbidden) jt.accept() if jp.is_modern_jingle(): # The other person's client decides it's not ringing any more node = jp.SetIq(jt.peer, jt.jid, [ jp.Jingle(jt.sid, jt.jid, 'session-info', [ ('active', ns.JINGLE_RTP_INFO_1, {}, []) ]) ]) stream.send(jp.xml(node)) # Gabble tells s-e to start sending, and removes the Ringing flag, either # because we got or because of the session-accept in ye olde # Jingle. q.expect_many( EventPattern('dbus-signal', signal='SetStreamSending', args=[True], path=audio_path), EventPattern('dbus-signal', signal='CallStateChanged', args=[ handle, 0 ]), ) call_states = chan.CallState.GetCallStates() assert call_states == { handle: 0 } or call_states == {}, call_states # The rest of the test concerns things we only support in the glorious # modern Jingle future. if not jp.is_modern_jingle(): return # The other person puts us on hold. Gabble should ack the session-info IQ, # tell s-e to stop sending on the held stream, and set the call state. node = jp.SetIq(jt.peer, jt.jid, [ jp.Jingle(jt.sid, jt.jid, 'session-info', [ ('hold', ns.JINGLE_RTP_INFO_1, {}, []) ]) ]) stream.send(jp.xml(node)) q.expect_many( EventPattern('stream-iq', iq_type='result', iq_id=node[2]['id']), EventPattern('dbus-signal', signal='SetStreamSending', args=[False], path=audio_path), EventPattern('dbus-signal', signal='CallStateChanged', args=[handle, cs.CALL_STATE_HELD]), ) call_states = chan.CallState.GetCallStates() assert call_states == { handle: cs.CALL_STATE_HELD }, call_states # The peer pings us with an empty session-info; Gabble should just ack it. node = jp.SetIq(jt.peer, jt.jid, [ jp.Jingle(jt.sid, jt.jid, 'session-info', [])]) stream.send(jp.xml(node)) q.expect('stream-iq', iq_type='result', iq_id=node[2]['id']) call_states = chan.CallState.GetCallStates() assert call_states == { handle: cs.CALL_STATE_HELD }, call_states # The peer sends us some unknown-namespaced misc in a session-info; Gabble # should nak it with node = jp.SetIq(jt.peer, jt.jid, [ jp.Jingle(jt.sid, jt.jid, 'session-info', [ ('boiling', 'com.example.Kettle', {}, []) ]) ]) stream.send(jp.xml(node)) e = q.expect('stream-iq', iq_type='error', iq_id=node[2]['id']) xpath.queryForNodes("/jingle/error/unsupported-info", e.query) call_states = chan.CallState.GetCallStates() assert call_states == { handle: cs.CALL_STATE_HELD }, call_states # The other person unholds us; Gabble should ack the session-info IQ, tell # s-e to start sending on the now-active stream, and set the call state. node = jp.SetIq(jt.peer, jt.jid, [ jp.Jingle(jt.sid, jt.jid, 'session-info', [ ('active', ns.JINGLE_RTP_INFO_1, {}, []) ]) ]) stream.send(jp.xml(node)) q.expect_many( EventPattern('stream-iq', iq_type='result', iq_id=node[2]['id']), EventPattern('dbus-signal', signal='SetStreamSending', args=[True], path=audio_path), EventPattern('dbus-signal', signal='CallStateChanged', args=[handle, 0]), ) call_states = chan.CallState.GetCallStates() assert call_states == { handle: 0 } or call_states == {}, call_states # Okay, let's get a second stream involved! chan.StreamedMedia.RequestStreams(handle, [cs.MEDIA_STREAM_TYPE_VIDEO]) e = q.expect('dbus-signal', signal='NewStreamHandler') video_path = e.args[0] stream_handler2 = make_channel_proxy(conn, video_path, 'Media.StreamHandler') stream_handler2.NewNativeCandidate("fake", jt.get_remote_transports_dbus()) stream_handler2.Ready(jt.get_video_codecs_dbus()) stream_handler2.StreamState(cs.MEDIA_STREAM_STATE_CONNECTED) e = q.expect('stream-iq', predicate=jp.action_predicate('content-add')) stream.send(make_result_iq(stream, e.stanza)) jt.content_accept(e.query, 'video') q.expect('dbus-signal', signal='SetStreamSending', args=[True], path=video_path) call_states = chan.CallState.GetCallStates() assert call_states == { handle: 0 } or call_states == {}, call_states # The other person puts us on hold. Gabble should ack the session-info IQ, # tell s-e to stop sending on both streams, and set the call state. node = jp.SetIq(jt.peer, jt.jid, [ jp.Jingle(jt.sid, jt.jid, 'session-info', [ ('hold', ns.JINGLE_RTP_INFO_1, {}, []) ]) ]) stream.send(jp.xml(node)) q.expect_many( EventPattern('stream-iq', iq_type='result', iq_id=node[2]['id']), EventPattern('dbus-signal', signal='SetStreamSending', args=[False], path=audio_path), EventPattern('dbus-signal', signal='SetStreamSending', args=[False], path=video_path), EventPattern('dbus-signal', signal='CallStateChanged', args=[handle, cs.CALL_STATE_HELD]), ) call_states = chan.CallState.GetCallStates() assert call_states == { handle: cs.CALL_STATE_HELD }, call_states # Now the other person sets the audio stream to mute. We can't represent # mute yet, but Gabble shouldn't take this to mean the call is active, as # one stream being muted doesn't change the fact that the call's on hold. # FIXME: hardcoded stream id node = jp.SetIq(jt.peer, jt.jid, [ jp.Jingle(jt.sid, jt.jid, 'session-info', [ ('mute', ns.JINGLE_RTP_INFO_1, {'name': 'Audio', 'creator': 'initiator'}, []) ]) ]) stream.send(jp.xml(node)) forbidden = [ EventPattern('dbus-signal', signal='SetStreamSending', args=[True], path=audio_path), EventPattern('dbus-signal', signal='CallStateChanged', args=[ handle, 0 ]), ] q.forbid_events(forbidden) q.expect('stream-iq', iq_type='result', iq_id=node[2]['id']) call_states = chan.CallState.GetCallStates() assert call_states == { handle: cs.CALL_STATE_HELD }, call_states sync_dbus(bus, q, conn) q.unforbid_events(forbidden) # That'll do, pig. chan.Group.RemoveMembers([self_handle], 'closed') e = q.expect('dbus-signal', signal='Close') #XXX - match against the path if __name__ == '__main__': test_all_dialects(test) telepathy-gabble-0.18.2/tests/twisted/jingle/call-muc-re-re-request.py0000644000175000017500000000322312200204333025644 0ustar00smcvsmcv00000000000000""" Test basic outgoing and incoming call handling """ from gabbletest import exec_test from servicetest import call_async, assertEquals, assertNotEquals from jingletest2 import JingleProtocol031 import constants as cs from callutils import * from config import VOIP_ENABLED if not VOIP_ENABLED: print "NOTE: built with --disable-voip" raise SystemExit(77) muc = "muji@test" def run_cancel_test(q, bus, conn, stream): jp = JingleProtocol031 () jt = JingleTest2(jp, conn, q, stream, 'test@localhost', muc + '/bob') jt.prepare() for x in xrange (0, 10): (path, props) = create_muji_channel (q, conn, stream, muc, x > 0) channel = bus.get_object (conn.bus_name, path) contents = channel.Get (cs.CHANNEL_TYPE_CALL, "Contents", dbus_interface = dbus.PROPERTIES_IFACE) content = bus.get_object (conn.bus_name, contents[0]) md = jt.get_call_audio_md_dbus() check_and_accept_offer (q, bus, conn, content, md) # Accept the channel channel.Accept() def preparing(e): node = xpath.queryForNodes("/presence/muji/preparing", e.stanza) return node is not None q.expect('stream-presence', to = muc + "/test", predicate=preparing) channel.Hangup(0, "", "", dbus_interface=cs.CHANNEL_TYPE_CALL) def notpreparing(e): node = xpath.queryForNodes("/presence/muji/preparing", e.stanza) return node is None q.expect('stream-presence', to = muc + "/test", predicate=notpreparing) if x % 2 == 0: channel.Close() if __name__ == '__main__': exec_test (run_cancel_test) telepathy-gabble-0.18.2/tests/twisted/jingle/call-muc.py0000644000175000017500000003100612227000321023146 0ustar00smcvsmcv00000000000000""" Test basic outgoing and incoming call handling """ import dbus from dbus.exceptions import DBusException from twisted.words.xish import xpath from gabbletest import exec_test, sync_stream from servicetest import ( EventPattern, call_async, assertEquals, assertContains, assertLength ) import constants as cs from jingletest2 import JingleTest2, test_all_dialects, JingleProtocol031 import ns from gabbletest import make_muc_presence from mucutil import * from callutils import * from config import VOIP_ENABLED if not VOIP_ENABLED: print "NOTE: built with --disable-voip" raise SystemExit(77) muc = "muji@test" def run_incoming_test(q, bus, conn, stream, bob_leaves_room = False): jp = JingleProtocol031 () jt = JingleTest2(jp, conn, q, stream, 'test@localhost', muc + "/bob") jt.prepare() forbidden = [ no_muji_presences (muc) ] self_handle = conn.GetSelfHandle() _, _, test_handle, bob_handle = \ join_muc_and_check(q, bus, conn, stream, muc) presence = make_muc_presence('owner', 'moderator', muc, 'bob') muji = ('muji', ns.MUJI, {}, [('content', ns.MUJI, { "name": "Voice" }, [( 'description', ns.JINGLE_RTP, {"media": "audio"}, jt.generate_payloads(jt.audio_codecs))])]) presence.addChild(jp._simple_xml(muji)) stream.send(presence) e = q.expect ('dbus-signal', signal='NewChannels', predicate=lambda e: \ e.args[0][0][1][cs.CHANNEL_TYPE] == cs.CHANNEL_TYPE_CALL ) (path, props) = e.args[0][0] assertContains((cs.CHANNEL_TYPE_CALL + '.InitialAudio', True), props.items()) assertContains((cs.CHANNEL_TYPE_CALL + '.InitialVideo', False), props.items()) general_tests (jp, q, bus, conn, stream, path, props) channel = bus.get_object (conn.bus_name, path) props = channel.GetAll (cs.CHANNEL_TYPE_CALL, dbus_interface = dbus.PROPERTIES_IFACE) content = bus.get_object (conn.bus_name, props['Contents'][0]) check_state (q, channel, cs.CALL_STATE_PENDING_RECEIVER) md = jt.get_call_audio_md_dbus() check_and_accept_offer (q, bus, conn, self_handle, content, md) channel.Accept (dbus_interface=cs.CHANNEL_TYPE_CALL) # Preparing stanza e = q.expect('stream-presence', to = muc + "/test") echo_muc_presence (q, stream, e.stanza, 'none', 'participant') # Codecs stanza e = q.expect('stream-presence', to = muc + "/test") echo_muc_presence (q, stream, e.stanza, 'none', 'participant') # Gabble shouldn't send new presences for a while q.forbid_events(forbidden) e = q.expect ('dbus-signal', signal = 'StreamsAdded') cstream = bus.get_object (conn.bus_name, e.args[0][0]) cstream.SetCredentials(jt.ufrag, jt.pwd, dbus_interface=cs.CALL_STREAM_IFACE_MEDIA) candidates = jt.get_call_remote_transports_dbus () cstream.AddCandidates (candidates, dbus_interface=cs.CALL_STREAM_IFACE_MEDIA) e = q.expect('stream-iq', predicate=jp.action_predicate('session-initiate')) jt.parse_session_initiate (e.query) jt.accept() # Bob adds a Video content presence = make_muc_presence('owner', 'moderator', muc, 'bob') presence.addElement ((ns.MUJI, 'muji')).addElement('preparing') stream.send(presence) presence = make_muc_presence('owner', 'moderator', muc, 'bob') muji = ('muji', ns.MUJI, {}, [('content', ns.MUJI, { "name": "Voice" }, [( 'description', ns.JINGLE_RTP, {"media": "audio"}, jt.generate_payloads(jt.audio_codecs))]), ('content', ns.MUJI, { "name": "Camera" }, [( 'description', ns.JINGLE_RTP, {"media": "video"}, jt.generate_payloads(jt.video_codecs))]), ]) presence.addChild(jp._simple_xml(muji)) stream.send(presence) # Gabble noticed bob added a content e = q.expect('dbus-signal', signal = 'ContentAdded') q.unforbid_events (forbidden) content = bus.get_object (conn.bus_name, e.args[0]) check_and_accept_offer (q, bus, conn, self_handle, content, jt.get_call_video_codecs_dbus(), check_codecs_changed = False) # Gabble sends a presence to prepare e = q.expect('stream-presence', to = muc + "/test") echo_muc_presence (q, stream, e.stanza, 'none', 'participant') # Gabble sends a presence with the video codecs e = q.expect('stream-presence', to = muc + "/test") echo_muc_presence (q, stream, e.stanza, 'none', 'participant') # Gabble adds a content to the jingle session and thus a stream is added e = q.expect ('dbus-signal', signal = 'StreamsAdded') cstream = bus.get_object (conn.bus_name, e.args[0][0]) candidates = jt.get_call_remote_transports_dbus () cstream.AddCandidates (candidates, dbus_interface=cs.CALL_STREAM_IFACE_MEDIA) # And now the content-add on the jingle streams e = q.expect('stream-iq', to = muc + "/bob", predicate = lambda x: \ xpath.queryForNodes("/iq/jingle[@action='content-add']", x.stanza)) # Bob leaves the call, bye bob if bob_leaves_room: presence = make_muc_presence('owner', 'moderator', muc, 'bob') presence['type'] = 'unavailable' else: presence = make_muc_presence('owner', 'moderator', muc, 'bob') stream.send(presence) (cmembers, _, _) = q.expect_many( EventPattern ('dbus-signal', signal = 'CallMembersChanged'), # Audio and video stream EventPattern ('dbus-signal', signal = 'StreamsRemoved'), EventPattern ('dbus-signal', signal = 'StreamsRemoved')) # Just bob left assertLength (1, cmembers.args[1]) def run_outgoing_test(q, bus, conn, stream, close_channel=False): jp = JingleProtocol031 () jt = JingleTest2(jp, conn, q, stream, 'test@localhost', muc + '/bob') jt.prepare() self_handle = conn.GetSelfHandle() # Not allowed to have muji related presences before we accept the channel forbidden = [ no_muji_presences (muc) ] (path, props) = create_muji_channel (q, conn, stream, muc) q.forbid_events(forbidden) general_tests (jp, q, bus, conn, stream, path, props) channel = bus.get_object (conn.bus_name, path) props = channel.GetAll (cs.CHANNEL_TYPE_CALL, dbus_interface = dbus.PROPERTIES_IFACE) content = bus.get_object (conn.bus_name, props['Contents'][0]) md = jt.get_call_audio_md_dbus() check_and_accept_offer (q, bus, conn, content, md) # Accept the channel, which means we can get muji presences q.unforbid_events (forbidden) channel.Accept() e = q.expect('stream-presence', to = muc + "/test") echo_muc_presence (q, stream, e.stanza, 'none', 'participant') mujinode = xpath.queryForNodes("/presence/muji", e.stanza) assertLength (1, mujinode) # The one with the codecs e = q.expect('stream-presence', to = muc + "/test") echo_muc_presence (q, stream, e.stanza, 'none', 'participant') # Gabble shouldn't send new presences for a while q.forbid_events(forbidden) presence = make_muc_presence('owner', 'moderator', muc, 'bob') presence.addElement ((ns.MUJI, 'muji')).addElement('preparing') stream.send(presence) presence = make_muc_presence('owner', 'moderator', muc, 'bob') muji = ('muji', ns.MUJI, {}, [('content', ns.MUJI, { "name": "Audio" }, [( 'description', ns.JINGLE_RTP, {"media": "audio"}, jt.generate_payloads(jt.audio_codecs))])]) presence.addChild(jp._simple_xml(muji)) stream.send(presence) q.expect('dbus-signal', signal = 'CallStateChanged') # Bob appears and starts a session right afterwards q.expect('dbus-signal', signal = 'CallMembersChanged') q.unforbid_events(forbidden) e = q.expect('dbus-signal', signal = 'NewMediaDescriptionOffer') offer = bus.get_object (conn.bus_name, e.args[0]) offer.Accept(md, dbus_interface=cs.CALL_CONTENT_MEDIADESCRIPTION) jt.incoming_call(audio = "Audio") e = q.expect ('dbus-signal', signal = 'StreamsAdded') cstream = bus.get_object (conn.bus_name, e.args[0][0]) candidates = jt.get_call_remote_transports_dbus () cstream.AddCandidates (candidates, dbus_interface=cs.CALL_STREAM_IFACE_MEDIA) # Fake our endpoint being connected endpoints = cstream.Get(cs.CALL_STREAM_IFACE_MEDIA, "Endpoints", dbus_interface=dbus.PROPERTIES_IFACE) assertLength (1, endpoints) endpoint = bus.get_object (conn.bus_name, endpoints[0]) endpoint.SetEndpointState (1, cs.MEDIA_STREAM_STATE_CONNECTED, dbus_interface=cs.CALL_STREAM_ENDPOINT) endpoint.SetEndpointState (2, cs.MEDIA_STREAM_STATE_CONNECTED, dbus_interface=cs.CALL_STREAM_ENDPOINT) e = q.expect ('stream-iq', predicate = jp.action_predicate ('session-accept')) stream.send(jp.xml(jp.ResultIq(jt.peer, e.stanza, []))) # But we want video as well ! c = channel.AddContent ("Camera!", cs.MEDIA_STREAM_TYPE_VIDEO, dbus_interface=cs.CHANNEL_TYPE_CALL) q.expect('dbus-signal', signal = 'ContentAdded') content = bus.get_object (conn.bus_name, c) # wait for the CodecOffer and Accept it q.expect('dbus-signal', signal = 'NewMediaDescriptionOffer') md = jt.get_call_video_md_dbus() check_and_accept_offer (q, bus, conn, content, md) # preparing e = q.expect('stream-presence', to = muc + "/test") echo_muc_presence (q, stream, e.stanza, 'none', 'participant') #codecs e = q.expect('stream-presence', to = muc + "/test") echo_muc_presence (q, stream, e.stanza, 'none', 'participant') # Bob would like to join our video party presence = make_muc_presence('owner', 'moderator', muc, 'bob') muji = ('muji', ns.MUJI, {}, [('content', ns.MUJI, { "name": "Audio" }, [( 'description', ns.JINGLE_RTP, {"media": "audio"}, jt.generate_payloads(jt.audio_codecs))]), ('content', ns.MUJI, { "name": "Camera!" }, [( 'description', ns.JINGLE_RTP, {"media": "video"}, jt.generate_payloads(jt.video_codecs))]), ]) presence.addChild(jp._simple_xml(muji)) stream.send(presence) # new codec offer as bob threw in some codecs q.expect('dbus-signal', signal='NewMediaDescriptionOffer') check_and_accept_offer (q, bus, conn, self_handle, content, codecs, check_codecs_changed = False) # Bob sends a content node = jp.SetIq(jt.peer, jt.jid, [ jp.Jingle(jt.sid, jt.peer, 'content-add', [ jp.Content('videostream', 'initiator', 'both', jp.Description('video', [ jp.PayloadType(name, str(rate), str(id), parameters) for (name, id, rate, parameters) in jt.video_codecs ]), jp.TransportGoogleP2P()) ]) ]) stream.send(jp.xml(node)) # We get a new stream q.expect('dbus-signal', signal = 'StreamsAdded') # Sync up the stream to ensure we sent out all the xmpp traffic that was # the result of a stream being added sync_stream (q, stream) # happiness.. Now let's hang up if close_channel: channel.Close() hangup_event = EventPattern ('dbus-signal', signal = "Closed", path = path) else: channel.Hangup (0, "", "", dbus_interface=cs.CHANNEL_TYPE_CALL) hangup_event = EventPattern ('dbus-signal', signal='CallStateChanged') # Should change the call state to ended, send a session-terminate to our # only peer and send a muc presence without any mention of muji q.forbid_events(forbidden) q.expect_many (EventPattern ('stream-presence', to = muc + "/test"), EventPattern ('stream-iq', predicate=jp.action_predicate ('session-terminate')), hangup_event) if not close_channel: channel.Close() q.expect ('dbus-signal', signal="Closed", path = path) try: channel.Close() raise AssertionError ("Channel didn't actually close") except DBusException: pass def general_tests (jp, q, bus, conn, stream, path, props): assertEquals (cs.HT_ROOM, props[cs.TARGET_HANDLE_TYPE]) channel = bus.get_object (conn.bus_name, path) chan_props = channel.GetAll (cs.CHANNEL_TYPE_CALL, dbus_interface = dbus.PROPERTIES_IFACE) contents = chan_props['Contents'] assertLength(1, contents) if __name__ == '__main__': print "FIXME: needs to be ported to Call1" raise SystemExit(77) exec_test (run_outgoing_test) exec_test (lambda q,b, c, s: run_outgoing_test (q, b, c, s, True)) exec_test (run_incoming_test) exec_test (lambda q,b, c, s: run_incoming_test (q, b, c, s, True)) telepathy-gabble-0.18.2/tests/twisted/jingle/call-muc-cancel.py0000644000175000017500000000137412200204333024376 0ustar00smcvsmcv00000000000000""" Test closing the muc channel while a muji request is in flight """ from gabbletest import exec_test, disconnect_conn from servicetest import call_async import constants as cs from config import VOIP_ENABLED if not VOIP_ENABLED: print "NOTE: built with --disable-voip" raise SystemExit(77) muc = "muji@test" def run_cancel_test(q, bus, conn, stream): call_async (q, conn.Requests, 'CreateChannel', { cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_CALL, cs.TARGET_HANDLE_TYPE: cs.HT_ROOM, cs.TARGET_ID: muc, cs.CALL_INITIAL_AUDIO: True, }, byte_arrays = True) q.expect('stream-presence', to = muc + "/test") disconnect_conn(q, conn, stream) if __name__ == '__main__': exec_test (run_cancel_test) telepathy-gabble-0.18.2/tests/twisted/jingle/call-hold-av.py0000644000175000017500000013531012227000321023717 0ustar00smcvsmcv00000000000000""" Test the Hold API. """ import dbus from dbus.exceptions import DBusException from functools import partial from servicetest import (call_async, EventPattern, assertEquals, assertLength, sync_dbus) from jingletest2 import test_all_dialects from gabbletest import sync_stream from call_helper import CallTest, run_call_test import constants as cs from config import VOIP_ENABLED if not VOIP_ENABLED: print "NOTE: built with --disable-voip" raise SystemExit(77) class CallHoldAVTest(CallTest): # Audio and Video test initial_audio = True initial_video = True def mutable_stream_tests(self): jp = self.jp jt = self.jt2 q = self.q bus = self.bus conn = self.conn stream = self.stream chan = self.chan # ---- Test 13: while the call's on hold, we add a new content --- # We shouldn't go off hold locally as a result, and the new # StreamHandler should tell s-e to hold the stream. pending_hold = [ EventPattern('dbus-signal', signal='HoldStateChanged', predicate=lambda e: e.args[0] == cs.HS_PENDING_HOLD), ] q.forbid_events(pending_hold) content_path = chan.AddContent('added_audio', cs.MEDIA_STREAM_TYPE_AUDIO, cs.MEDIA_STREAM_DIRECTION_BIDIRECTIONAL, dbus_interface=cs.CHANNEL_TYPE_CALL) q.expect ('dbus-signal', signal='ContentAdded') content = bus.get_object (conn.bus_name, content_path) assertEquals (cs.MEDIA_STREAM_TYPE_AUDIO, content.Get(cs.CALL_CONTENT, 'Type', dbus_interface=dbus.PROPERTIES_IFACE)) stream_paths = content.Get(cs.CALL_CONTENT, "Streams", dbus_interface=dbus.PROPERTIES_IFACE) assertLength (1, stream_paths) cstream = bus.get_object (conn.bus_name, stream_paths[0]) md = jt.get_call_audio_md_dbus() [path, _] = content.Get(cs.CALL_CONTENT_IFACE_MEDIA, "MediaDescriptionOffer", dbus_interface=dbus.PROPERTIES_IFACE) offer = bus.get_object (conn.bus_name, path) offer.Accept (md, dbus_interface=cs.CALL_CONTENT_MEDIADESCRIPTION) assertEquals(cs.CALL_STREAM_FLOW_STATE_STOPPED, cstream.Get(cs.CALL_STREAM_IFACE_MEDIA, 'SendingState', dbus_interface=dbus.PROPERTIES_IFACE)) assertEquals(cs.CALL_STREAM_FLOW_STATE_STOPPED, cstream.Get(cs.CALL_STREAM_IFACE_MEDIA, 'ReceivingState', dbus_interface=dbus.PROPERTIES_IFACE)) content_paths = chan.Get(cs.CHANNEL_TYPE_CALL, 'Contents', dbus_interface=dbus.PROPERTIES_IFACE) assertEquals(cs.HS_HELD, chan.Hold.GetHoldState()[0]) sync_dbus(bus, q, conn) # --- Test 14: while the call's on hold, the peer adds a new stream ---- # Again, we shouldn't go off hold locally as a result, and the new # StreamHandler should tell s-e to hold the stream. node = jp.SetIq(jt.peer, jt.jid, [ jp.Jingle(jt.sid, jt.peer, 'content-add', [ jp.Content('videostream', 'initiator', 'both', jp.Description('video', [ jp.PayloadType(name, str(rate), str(id), parameters) for (name, id, rate, parameters) in jt.video_codecs ]), jp.TransportGoogleP2P()) ]) ]) stream.send(jp.xml(node)) e = q.expect ('dbus-signal', signal='ContentAdded') content = bus.get_object (conn.bus_name, e.args[0]) assertEquals (cs.MEDIA_STREAM_TYPE_VIDEO, content.Get(cs.CALL_CONTENT, 'Type', dbus_interface=dbus.PROPERTIES_IFACE)) stream_paths = content.Get(cs.CALL_CONTENT, "Streams", dbus_interface=dbus.PROPERTIES_IFACE) assertLength (1, stream_paths) cstream = bus.get_object (conn.bus_name, stream_paths[0]) md = jt.get_call_video_md_dbus() [path, _] = content.Get(cs.CALL_CONTENT_IFACE_MEDIA, "MediaDescriptionOffer", dbus_interface=dbus.PROPERTIES_IFACE) offer = bus.get_object (conn.bus_name, path) offer.Accept (md, dbus_interface=cs.CALL_CONTENT_MEDIADESCRIPTION) assertEquals(cs.CALL_STREAM_FLOW_STATE_STOPPED, cstream.Get(cs.CALL_STREAM_IFACE_MEDIA, 'SendingState', dbus_interface=dbus.PROPERTIES_IFACE)) assertEquals(cs.CALL_STREAM_FLOW_STATE_STOPPED, cstream.Get(cs.CALL_STREAM_IFACE_MEDIA, 'ReceivingState', dbus_interface=dbus.PROPERTIES_IFACE)) content_paths = chan.Get(cs.CHANNEL_TYPE_CALL, 'Contents', dbus_interface=dbus.PROPERTIES_IFACE) assertEquals(cs.HS_HELD, chan.Hold.GetHoldState()[0]) sync_dbus(bus, q, conn) q.unforbid_events(pending_hold) def initiate(self): CallTest.initiate(self) q = self.q jp = self.jp cstream = self.audio_stream chan = self.chan audio_cstream = self.audio_stream recv_state = audio_cstream.GetAll(cs.CALL_STREAM_IFACE_MEDIA, dbus_interface=dbus.PROPERTIES_IFACE)["ReceivingState"] send_state = audio_cstream.GetAll(cs.CALL_STREAM_IFACE_MEDIA, dbus_interface=dbus.PROPERTIES_IFACE)["SendingState"] assertEquals (cs.CALL_STREAM_FLOW_STATE_STOPPED, recv_state) assertEquals (cs.CALL_STREAM_FLOW_STATE_STOPPED, send_state) video_cstream = self.video_stream recv_state = video_cstream.GetAll(cs.CALL_STREAM_IFACE_MEDIA, dbus_interface=dbus.PROPERTIES_IFACE)["ReceivingState"] send_state = video_cstream.GetAll(cs.CALL_STREAM_IFACE_MEDIA, dbus_interface=dbus.PROPERTIES_IFACE)["SendingState"] assertEquals (cs.CALL_STREAM_FLOW_STATE_STOPPED, recv_state) assertEquals (cs.CALL_STREAM_FLOW_STATE_STOPPED, send_state) # These are 0- (for old dialects) or 1- (for new dialects) element lists # that can be splatted into expect_many with * self.hold_event = jp.rtp_info_event_list("hold") self.unhold_event = jp.rtp_info_event_list("unhold") # Before we have accepted any streams, GetHoldState returns Unheld and # unhold is a no-op. assertEquals((cs.HS_UNHELD, cs.HSR_REQUESTED), chan.Hold.GetHoldState()) chan.Hold.RequestHold(False) q.forbid_events(self.hold_event) q.forbid_events(self.unhold_event) assertEquals((cs.HS_UNHELD, cs.HSR_REQUESTED), chan.Hold.GetHoldState()) chan.Hold.RequestHold(False) # Before we have any streams, RequestHold(True) should work because # there are no streams, it should take effect at once. It certainly # shouldn't send anything to the peer. q.forbid_events(self.hold_event) q.forbid_events(self.unhold_event) call_async(q, chan.Hold, 'RequestHold', True) q.expect('dbus-signal', signal='HoldStateChanged', args=[cs.HS_PENDING_HOLD, cs.HSR_REQUESTED]) q.expect('dbus-signal', signal='HoldStateChanged', args=[cs.HS_HELD, cs.HSR_REQUESTED]) assertEquals((cs.HS_HELD, cs.HSR_REQUESTED), chan.Hold.GetHoldState()) # If we unhold, it should succeed immediately again, because there are # no resources to reclaim. call_async(q, chan.Hold, 'RequestHold', False) q.expect('dbus-signal', signal='HoldStateChanged', args=[cs.HS_PENDING_UNHOLD, cs.HSR_REQUESTED]) q.expect('dbus-signal', signal='HoldStateChanged', args=[cs.HS_UNHELD, cs.HSR_REQUESTED]) assertEquals((cs.HS_UNHELD, cs.HSR_REQUESTED), chan.Hold.GetHoldState()) # Put the call back on hold ... call_async(q, chan.Hold, 'RequestHold', True) q.expect('dbus-signal', signal='HoldStateChanged', args=[cs.HS_PENDING_HOLD, cs.HSR_REQUESTED]) q.expect('dbus-signal', signal='HoldStateChanged', args=[cs.HS_HELD, cs.HSR_REQUESTED]) assertEquals((cs.HS_HELD, cs.HSR_REQUESTED), chan.Hold.GetHoldState()) def connect(self): assertEquals((cs.HS_HELD, cs.HSR_REQUESTED), self.chan.Hold.GetHoldState()) assertEquals((cs.HS_HELD, cs.HSR_REQUESTED), self.chan.Hold.GetHoldState()) CallTest.connect(self, expect_after_si=self.hold_event) def accept_outgoing(self): # We are on hold, no states to complete here self.check_channel_state(cs.CALL_STATE_PENDING_INITIATOR) self.chan.Accept(dbus_interface=cs.CHANNEL_TYPE_CALL) self.check_channel_state(cs.CALL_STATE_INITIALISING) def pickup(self): CallTest.pickup(self, held=True) q = self.q stream = self.stream chan = self.chan audio_cstream = self.audio_stream video_cstream = self.video_stream assertEquals((cs.HS_HELD, cs.HSR_REQUESTED), chan.Hold.GetHoldState()) recv_state = audio_cstream.Get(cs.CALL_STREAM_IFACE_MEDIA, "ReceivingState", dbus_interface=dbus.PROPERTIES_IFACE) assertEquals (cs.CALL_STREAM_FLOW_STATE_STOPPED, recv_state) send_state = audio_cstream.Get(cs.CALL_STREAM_IFACE_MEDIA, "SendingState", dbus_interface=dbus.PROPERTIES_IFACE) assertEquals (cs.CALL_STREAM_FLOW_STATE_STOPPED, send_state) recv_state = video_cstream.Get(cs.CALL_STREAM_IFACE_MEDIA, "ReceivingState", dbus_interface=dbus.PROPERTIES_IFACE) assertEquals (cs.CALL_STREAM_FLOW_STATE_STOPPED, recv_state) send_state = video_cstream.Get(cs.CALL_STREAM_IFACE_MEDIA, "SendingState", dbus_interface=dbus.PROPERTIES_IFACE) assertEquals (cs.CALL_STREAM_FLOW_STATE_STOPPED, send_state) # Now we decide we do actually want to speak to them, and unhold. # Ensure that if Gabble sent the stanza too early it's already # arrived. sync_stream(q, stream) q.forbid_events(self.unhold_event) call_async(q, chan.Hold, 'RequestHold', False) q.expect_many( EventPattern('dbus-signal', signal='HoldStateChanged', args=[cs.HS_PENDING_UNHOLD, cs.HSR_REQUESTED]), EventPattern('dbus-signal', signal='ReceivingStateChanged', args = [cs.CALL_STREAM_FLOW_STATE_PENDING_START], interface = cs.CALL_STREAM_IFACE_MEDIA), EventPattern('dbus-signal', signal='ReceivingStateChanged', args = [cs.CALL_STREAM_FLOW_STATE_PENDING_START], interface = cs.CALL_STREAM_IFACE_MEDIA), EventPattern('dbus-signal', signal='SendingStateChanged', args = [cs.CALL_STREAM_FLOW_STATE_PENDING_START], interface = cs.CALL_STREAM_IFACE_MEDIA), EventPattern('dbus-signal', signal='SendingStateChanged', args = [cs.CALL_STREAM_FLOW_STATE_PENDING_START], interface = cs.CALL_STREAM_IFACE_MEDIA), EventPattern('dbus-return', method='RequestHold', value=()), ) # Ensure that if Gabble sent the stanza too early it's already # arrived. sync_stream(q, stream) q.unforbid_events(self.unhold_event) audio_cstream.CompleteReceivingStateChange( cs.CALL_STREAM_FLOW_STATE_STARTED, dbus_interface = cs.CALL_STREAM_IFACE_MEDIA) audio_cstream.CompleteSendingStateChange( cs.CALL_STREAM_FLOW_STATE_STARTED, dbus_interface = cs.CALL_STREAM_IFACE_MEDIA) video_cstream.CompleteReceivingStateChange( cs.CALL_STREAM_FLOW_STATE_STARTED, dbus_interface = cs.CALL_STREAM_IFACE_MEDIA) video_cstream.CompleteSendingStateChange( cs.CALL_STREAM_FLOW_STATE_STARTED, dbus_interface = cs.CALL_STREAM_IFACE_MEDIA) q.expect_many( EventPattern('dbus-signal', signal='HoldStateChanged', args=[cs.HS_UNHELD, cs.HSR_REQUESTED]), EventPattern('dbus-signal', signal='SendingStateChanged', args = [cs.CALL_STREAM_FLOW_STATE_STARTED], interface = cs.CALL_STREAM_IFACE_MEDIA), EventPattern('dbus-signal', signal='SendingStateChanged', args = [cs.CALL_STREAM_FLOW_STATE_STARTED], interface = cs.CALL_STREAM_IFACE_MEDIA), EventPattern('dbus-signal', signal='ReceivingStateChanged', args = [cs.CALL_STREAM_FLOW_STATE_STARTED], interface = cs.CALL_STREAM_IFACE_MEDIA), EventPattern('dbus-signal', signal='ReceivingStateChanged', args = [cs.CALL_STREAM_FLOW_STATE_STARTED], interface = cs.CALL_STREAM_IFACE_MEDIA), *self.unhold_event ) # Hooray! Now let's check that Hold works properly once the call's fully # established. # ---- Test 1: GetHoldState returns unheld and unhold is a no-op ---- hold_state = chan.Hold.GetHoldState() assert hold_state[0] == cs.HS_UNHELD, hold_state chan.Hold.RequestHold(False) # ---- Test 2: successful hold ---- call_async(q, chan.Hold, 'RequestHold', True) q.expect_many( EventPattern('dbus-signal', signal='HoldStateChanged', args=[cs.HS_PENDING_HOLD, cs.HSR_REQUESTED]), EventPattern('dbus-signal', signal='SendingStateChanged', args = [cs.CALL_STREAM_FLOW_STATE_PENDING_STOP], interface = cs.CALL_STREAM_IFACE_MEDIA), EventPattern('dbus-signal', signal='SendingStateChanged', args = [cs.CALL_STREAM_FLOW_STATE_PENDING_STOP], interface = cs.CALL_STREAM_IFACE_MEDIA), EventPattern('dbus-signal', signal='ReceivingStateChanged', args = [cs.CALL_STREAM_FLOW_STATE_PENDING_STOP], interface = cs.CALL_STREAM_IFACE_MEDIA), EventPattern('dbus-signal', signal='ReceivingStateChanged', args = [cs.CALL_STREAM_FLOW_STATE_PENDING_STOP], interface = cs.CALL_STREAM_IFACE_MEDIA), EventPattern('dbus-return', method='RequestHold', value=()), *self.hold_event ) audio_cstream.CompleteReceivingStateChange( cs.CALL_STREAM_FLOW_STATE_STOPPED, dbus_interface = cs.CALL_STREAM_IFACE_MEDIA) audio_cstream.CompleteSendingStateChange( cs.CALL_STREAM_FLOW_STATE_STOPPED, dbus_interface = cs.CALL_STREAM_IFACE_MEDIA) video_cstream.CompleteReceivingStateChange( cs.CALL_STREAM_FLOW_STATE_STOPPED, dbus_interface = cs.CALL_STREAM_IFACE_MEDIA) video_cstream.CompleteSendingStateChange( cs.CALL_STREAM_FLOW_STATE_STOPPED, dbus_interface = cs.CALL_STREAM_IFACE_MEDIA) q.expect_many( EventPattern('dbus-signal', signal='HoldStateChanged', args=[cs.HS_HELD, cs.HSR_REQUESTED]), EventPattern('dbus-signal', signal='SendingStateChanged', args = [cs.CALL_STREAM_FLOW_STATE_STOPPED], interface = cs.CALL_STREAM_IFACE_MEDIA), EventPattern('dbus-signal', signal='SendingStateChanged', args = [cs.CALL_STREAM_FLOW_STATE_STOPPED], interface = cs.CALL_STREAM_IFACE_MEDIA), EventPattern('dbus-signal', signal='ReceivingStateChanged', args = [cs.CALL_STREAM_FLOW_STATE_STOPPED], interface = cs.CALL_STREAM_IFACE_MEDIA), EventPattern('dbus-signal', signal='ReceivingStateChanged', args = [cs.CALL_STREAM_FLOW_STATE_STOPPED], interface = cs.CALL_STREAM_IFACE_MEDIA), ) # ---- Test 3: GetHoldState returns held and hold is a no-op ---- hold_state = chan.Hold.GetHoldState() assert hold_state[0] == cs.HS_HELD, hold_state chan.Hold.RequestHold(True) # ---- Test 4: successful unhold ---- q.forbid_events(self.unhold_event) call_async(q, chan.Hold, 'RequestHold', False) q.expect_many( EventPattern('dbus-signal', signal='HoldStateChanged', args=[cs.HS_PENDING_UNHOLD, cs.HSR_REQUESTED]), EventPattern('dbus-signal', signal='ReceivingStateChanged', args = [cs.CALL_STREAM_FLOW_STATE_PENDING_START], interface = cs.CALL_STREAM_IFACE_MEDIA), EventPattern('dbus-signal', signal='ReceivingStateChanged', args = [cs.CALL_STREAM_FLOW_STATE_PENDING_START], interface = cs.CALL_STREAM_IFACE_MEDIA), EventPattern('dbus-signal', signal='SendingStateChanged', args = [cs.CALL_STREAM_FLOW_STATE_PENDING_START], interface = cs.CALL_STREAM_IFACE_MEDIA), EventPattern('dbus-signal', signal='SendingStateChanged', args = [cs.CALL_STREAM_FLOW_STATE_PENDING_START], interface = cs.CALL_STREAM_IFACE_MEDIA), EventPattern('dbus-return', method='RequestHold', value=()), ) # Ensure that if Gabble sent the stanza too early it's already # arrived. sync_stream(q, stream) q.unforbid_events(self.unhold_event) audio_cstream.CompleteReceivingStateChange( cs.CALL_STREAM_FLOW_STATE_STARTED, dbus_interface = cs.CALL_STREAM_IFACE_MEDIA) audio_cstream.CompleteSendingStateChange( cs.CALL_STREAM_FLOW_STATE_STARTED, dbus_interface = cs.CALL_STREAM_IFACE_MEDIA) video_cstream.CompleteReceivingStateChange( cs.CALL_STREAM_FLOW_STATE_STARTED, dbus_interface = cs.CALL_STREAM_IFACE_MEDIA) video_cstream.CompleteSendingStateChange( cs.CALL_STREAM_FLOW_STATE_STARTED, dbus_interface = cs.CALL_STREAM_IFACE_MEDIA) q.expect_many( EventPattern('dbus-signal', signal='HoldStateChanged', args=[cs.HS_UNHELD, cs.HSR_REQUESTED]), EventPattern('dbus-signal', signal='SendingStateChanged', args = [cs.CALL_STREAM_FLOW_STATE_STARTED], interface = cs.CALL_STREAM_IFACE_MEDIA), EventPattern('dbus-signal', signal='SendingStateChanged', args = [cs.CALL_STREAM_FLOW_STATE_STARTED], interface = cs.CALL_STREAM_IFACE_MEDIA), EventPattern('dbus-signal', signal='ReceivingStateChanged', args = [cs.CALL_STREAM_FLOW_STATE_STARTED], interface = cs.CALL_STREAM_IFACE_MEDIA), EventPattern('dbus-signal', signal='ReceivingStateChanged', args = [cs.CALL_STREAM_FLOW_STATE_STARTED], interface = cs.CALL_STREAM_IFACE_MEDIA), *self.unhold_event ) # ---- Test 5: GetHoldState returns False and unhold is a no-op ---- hold_state = chan.Hold.GetHoldState() assert hold_state[0] == cs.HS_UNHELD, hold_state chan.Hold.RequestHold(False) # ---- Test 6: 3 parallel calls to hold ---- hold_state = chan.Hold.GetHoldState() assert hold_state[0] == cs.HS_UNHELD, hold_state call_async(q, chan.Hold, 'RequestHold', True) call_async(q, chan.Hold, 'RequestHold', True) call_async(q, chan.Hold, 'RequestHold', True) q.expect_many( EventPattern('dbus-signal', signal='HoldStateChanged', args=[cs.HS_PENDING_HOLD, cs.HSR_REQUESTED]), EventPattern('dbus-signal', signal='SendingStateChanged', args = [cs.CALL_STREAM_FLOW_STATE_PENDING_STOP], interface = cs.CALL_STREAM_IFACE_MEDIA), EventPattern('dbus-signal', signal='SendingStateChanged', args = [cs.CALL_STREAM_FLOW_STATE_PENDING_STOP], interface = cs.CALL_STREAM_IFACE_MEDIA), EventPattern('dbus-signal', signal='ReceivingStateChanged', args = [cs.CALL_STREAM_FLOW_STATE_PENDING_STOP], interface = cs.CALL_STREAM_IFACE_MEDIA), EventPattern('dbus-signal', signal='ReceivingStateChanged', args = [cs.CALL_STREAM_FLOW_STATE_PENDING_STOP], interface = cs.CALL_STREAM_IFACE_MEDIA), EventPattern('dbus-return', method='RequestHold', value=()), *self.hold_event ) audio_cstream.CompleteReceivingStateChange( cs.CALL_STREAM_FLOW_STATE_STOPPED, dbus_interface = cs.CALL_STREAM_IFACE_MEDIA) audio_cstream.CompleteSendingStateChange( cs.CALL_STREAM_FLOW_STATE_STOPPED, dbus_interface = cs.CALL_STREAM_IFACE_MEDIA) video_cstream.CompleteReceivingStateChange( cs.CALL_STREAM_FLOW_STATE_STOPPED, dbus_interface = cs.CALL_STREAM_IFACE_MEDIA) video_cstream.CompleteSendingStateChange( cs.CALL_STREAM_FLOW_STATE_STOPPED, dbus_interface = cs.CALL_STREAM_IFACE_MEDIA) q.expect_many( EventPattern('dbus-signal', signal='SendingStateChanged', args = [cs.CALL_STREAM_FLOW_STATE_STOPPED], interface = cs.CALL_STREAM_IFACE_MEDIA), EventPattern('dbus-signal', signal='SendingStateChanged', args = [cs.CALL_STREAM_FLOW_STATE_STOPPED], interface = cs.CALL_STREAM_IFACE_MEDIA), EventPattern('dbus-signal', signal='ReceivingStateChanged', args = [cs.CALL_STREAM_FLOW_STATE_STOPPED], interface = cs.CALL_STREAM_IFACE_MEDIA), EventPattern('dbus-signal', signal='ReceivingStateChanged', args = [cs.CALL_STREAM_FLOW_STATE_STOPPED], interface = cs.CALL_STREAM_IFACE_MEDIA), EventPattern('dbus-signal', signal='HoldStateChanged', args=[cs.HS_HELD, cs.HSR_REQUESTED]), ) # ---- Test 7: 3 parallel calls to unhold ---- q.forbid_events(self.unhold_event) call_async(q, chan.Hold, 'RequestHold', False) call_async(q, chan.Hold, 'RequestHold', False) call_async(q, chan.Hold, 'RequestHold', False) q.expect_many( EventPattern('dbus-signal', signal='HoldStateChanged', args=[cs.HS_PENDING_UNHOLD, cs.HSR_REQUESTED]), EventPattern('dbus-signal', signal='SendingStateChanged', args = [cs.CALL_STREAM_FLOW_STATE_PENDING_START], interface = cs.CALL_STREAM_IFACE_MEDIA), EventPattern('dbus-signal', signal='SendingStateChanged', args = [cs.CALL_STREAM_FLOW_STATE_PENDING_START], interface = cs.CALL_STREAM_IFACE_MEDIA), EventPattern('dbus-signal', signal='ReceivingStateChanged', args = [cs.CALL_STREAM_FLOW_STATE_PENDING_START], interface = cs.CALL_STREAM_IFACE_MEDIA), EventPattern('dbus-signal', signal='ReceivingStateChanged', args = [cs.CALL_STREAM_FLOW_STATE_PENDING_START], interface = cs.CALL_STREAM_IFACE_MEDIA), EventPattern('dbus-return', method='RequestHold', value=()), ) # Ensure that if Gabble sent the stanza too early it's already # arrived. sync_stream(q, stream) q.unforbid_events(self.unhold_event) audio_cstream.CompleteReceivingStateChange( cs.CALL_STREAM_FLOW_STATE_STARTED, dbus_interface = cs.CALL_STREAM_IFACE_MEDIA) audio_cstream.CompleteSendingStateChange( cs.CALL_STREAM_FLOW_STATE_STARTED, dbus_interface = cs.CALL_STREAM_IFACE_MEDIA) video_cstream.CompleteReceivingStateChange( cs.CALL_STREAM_FLOW_STATE_STARTED, dbus_interface = cs.CALL_STREAM_IFACE_MEDIA) video_cstream.CompleteSendingStateChange( cs.CALL_STREAM_FLOW_STATE_STARTED, dbus_interface = cs.CALL_STREAM_IFACE_MEDIA) q.expect_many( EventPattern('dbus-signal', signal='HoldStateChanged', args=[cs.HS_UNHELD, cs.HSR_REQUESTED]), EventPattern('dbus-signal', signal='SendingStateChanged', args = [cs.CALL_STREAM_FLOW_STATE_STARTED], interface = cs.CALL_STREAM_IFACE_MEDIA), EventPattern('dbus-signal', signal='SendingStateChanged', args = [cs.CALL_STREAM_FLOW_STATE_STARTED], interface = cs.CALL_STREAM_IFACE_MEDIA), EventPattern('dbus-signal', signal='ReceivingStateChanged', args = [cs.CALL_STREAM_FLOW_STATE_STARTED], interface = cs.CALL_STREAM_IFACE_MEDIA), EventPattern('dbus-signal', signal='ReceivingStateChanged', args = [cs.CALL_STREAM_FLOW_STATE_STARTED], interface = cs.CALL_STREAM_IFACE_MEDIA), *self.unhold_event ) # ---- Test 8: hold, then change our minds before s-e has responded ---- hold_state = chan.Hold.GetHoldState() assert hold_state[0] == cs.HS_UNHELD, hold_state call_async(q, chan.Hold, 'RequestHold', True) q.expect_many( EventPattern('dbus-signal', signal='HoldStateChanged', args=[cs.HS_PENDING_HOLD, cs.HSR_REQUESTED]), EventPattern('dbus-signal', signal='SendingStateChanged', args = [cs.CALL_STREAM_FLOW_STATE_PENDING_STOP], interface = cs.CALL_STREAM_IFACE_MEDIA), EventPattern('dbus-signal', signal='ReceivingStateChanged', args = [cs.CALL_STREAM_FLOW_STATE_PENDING_STOP], interface = cs.CALL_STREAM_IFACE_MEDIA), *self.hold_event ) q.forbid_events(self.unhold_event) call_async(q, chan.Hold, 'RequestHold', False) q.expect_many( EventPattern('dbus-signal', signal='HoldStateChanged', args=[cs.HS_PENDING_UNHOLD, cs.HSR_REQUESTED]), EventPattern('dbus-signal', signal='SendingStateChanged', args = [cs.CALL_STREAM_FLOW_STATE_PENDING_START], interface = cs.CALL_STREAM_IFACE_MEDIA), EventPattern('dbus-signal', signal='ReceivingStateChanged', args = [cs.CALL_STREAM_FLOW_STATE_PENDING_START], interface = cs.CALL_STREAM_IFACE_MEDIA), # Gabble shouldn't send here because s-e might have # already relinquished the audio hardware. ) sync_stream(q, stream) q.unforbid_events(self.unhold_event) try: audio_cstream.CompleteReceivingStateChange( cs.CALL_STREAM_FLOW_STATE_STOPPED, dbus_interface = cs.CALL_STREAM_IFACE_MEDIA) except dbus.DBusException, e: assertEquals (cs.INVALID_ARGUMENT, e.get_dbus_name ()) try: audio_cstream.CompleteSendingStateChange( cs.CALL_STREAM_FLOW_STATE_STOPPED, dbus_interface = cs.CALL_STREAM_IFACE_MEDIA) except dbus.DBusException, e: assertEquals (cs.INVALID_ARGUMENT, e.get_dbus_name ()) try: video_cstream.CompleteReceivingStateChange( cs.CALL_STREAM_FLOW_STATE_STOPPED, dbus_interface = cs.CALL_STREAM_IFACE_MEDIA) except dbus.DBusException, e: assertEquals (cs.INVALID_ARGUMENT, e.get_dbus_name ()) try: video_cstream.CompleteSendingStateChange( cs.CALL_STREAM_FLOW_STATE_STOPPED, dbus_interface = cs.CALL_STREAM_IFACE_MEDIA) except dbus.DBusException, e: assertEquals (cs.INVALID_ARGUMENT, e.get_dbus_name ()) audio_cstream.CompleteReceivingStateChange( cs.CALL_STREAM_FLOW_STATE_STARTED, dbus_interface = cs.CALL_STREAM_IFACE_MEDIA) audio_cstream.CompleteSendingStateChange( cs.CALL_STREAM_FLOW_STATE_STARTED, dbus_interface = cs.CALL_STREAM_IFACE_MEDIA) video_cstream.CompleteReceivingStateChange( cs.CALL_STREAM_FLOW_STATE_STARTED, dbus_interface = cs.CALL_STREAM_IFACE_MEDIA) video_cstream.CompleteSendingStateChange( cs.CALL_STREAM_FLOW_STATE_STARTED, dbus_interface = cs.CALL_STREAM_IFACE_MEDIA) q.expect_many( EventPattern('dbus-signal', signal='HoldStateChanged', args=[cs.HS_UNHELD, cs.HSR_REQUESTED]), EventPattern('dbus-signal', signal='SendingStateChanged', args = [cs.CALL_STREAM_FLOW_STATE_STARTED], interface = cs.CALL_STREAM_IFACE_MEDIA), EventPattern('dbus-signal', signal='SendingStateChanged', args = [cs.CALL_STREAM_FLOW_STATE_STARTED], interface = cs.CALL_STREAM_IFACE_MEDIA), EventPattern('dbus-signal', signal='ReceivingStateChanged', args = [cs.CALL_STREAM_FLOW_STATE_STARTED], interface = cs.CALL_STREAM_IFACE_MEDIA), EventPattern('dbus-signal', signal='ReceivingStateChanged', args = [cs.CALL_STREAM_FLOW_STATE_STARTED], interface = cs.CALL_STREAM_IFACE_MEDIA), *self.unhold_event ) hold_state = chan.Hold.GetHoldState() assert hold_state[0] == cs.HS_UNHELD, hold_state # --- Test 9: unhold, then change our minds before s-e has responded ---- # Go to state "held" first call_async(q, chan.Hold, 'RequestHold', True) q.expect_many( EventPattern('dbus-signal', signal='HoldStateChanged', args=[cs.HS_PENDING_HOLD, cs.HSR_REQUESTED]), EventPattern('dbus-signal', signal='SendingStateChanged', args = [cs.CALL_STREAM_FLOW_STATE_PENDING_STOP], interface = cs.CALL_STREAM_IFACE_MEDIA), EventPattern('dbus-signal', signal='SendingStateChanged', args = [cs.CALL_STREAM_FLOW_STATE_PENDING_STOP], interface = cs.CALL_STREAM_IFACE_MEDIA), EventPattern('dbus-signal', signal='ReceivingStateChanged', args = [cs.CALL_STREAM_FLOW_STATE_PENDING_STOP], interface = cs.CALL_STREAM_IFACE_MEDIA), EventPattern('dbus-signal', signal='ReceivingStateChanged', args = [cs.CALL_STREAM_FLOW_STATE_PENDING_STOP], interface = cs.CALL_STREAM_IFACE_MEDIA), EventPattern('dbus-return', method='RequestHold', value=()), *self.hold_event ) audio_cstream.CompleteReceivingStateChange( cs.CALL_STREAM_FLOW_STATE_STOPPED, dbus_interface = cs.CALL_STREAM_IFACE_MEDIA) audio_cstream.CompleteSendingStateChange( cs.CALL_STREAM_FLOW_STATE_STOPPED, dbus_interface = cs.CALL_STREAM_IFACE_MEDIA) video_cstream.CompleteReceivingStateChange( cs.CALL_STREAM_FLOW_STATE_STOPPED, dbus_interface = cs.CALL_STREAM_IFACE_MEDIA) video_cstream.CompleteSendingStateChange( cs.CALL_STREAM_FLOW_STATE_STOPPED, dbus_interface = cs.CALL_STREAM_IFACE_MEDIA) q.expect_many( EventPattern('dbus-signal', signal='SendingStateChanged', args = [cs.CALL_STREAM_FLOW_STATE_STOPPED], interface = cs.CALL_STREAM_IFACE_MEDIA), EventPattern('dbus-signal', signal='SendingStateChanged', args = [cs.CALL_STREAM_FLOW_STATE_STOPPED], interface = cs.CALL_STREAM_IFACE_MEDIA), EventPattern('dbus-signal', signal='ReceivingStateChanged', args = [cs.CALL_STREAM_FLOW_STATE_STOPPED], interface = cs.CALL_STREAM_IFACE_MEDIA), EventPattern('dbus-signal', signal='ReceivingStateChanged', args = [cs.CALL_STREAM_FLOW_STATE_STOPPED], interface = cs.CALL_STREAM_IFACE_MEDIA), EventPattern('dbus-signal', signal='HoldStateChanged', args=[cs.HS_HELD, cs.HSR_REQUESTED]), ) # Actually do test 9 hold_state = chan.Hold.GetHoldState() assert hold_state[0] == cs.HS_HELD, hold_state # Check that Gabble doesn't send another , or send # before we change our minds. q.forbid_events(self.unhold_event + self.hold_event) call_async(q, chan.Hold, 'RequestHold', False) q.expect_many( EventPattern('dbus-signal', signal='HoldStateChanged', args=[cs.HS_PENDING_UNHOLD, cs.HSR_REQUESTED]), EventPattern('dbus-signal', signal='SendingStateChanged', args = [cs.CALL_STREAM_FLOW_STATE_PENDING_START], interface = cs.CALL_STREAM_IFACE_MEDIA), EventPattern('dbus-signal', signal='SendingStateChanged', args = [cs.CALL_STREAM_FLOW_STATE_PENDING_START], interface = cs.CALL_STREAM_IFACE_MEDIA), EventPattern('dbus-signal', signal='ReceivingStateChanged', args = [cs.CALL_STREAM_FLOW_STATE_PENDING_START], interface = cs.CALL_STREAM_IFACE_MEDIA), EventPattern('dbus-signal', signal='ReceivingStateChanged', args = [cs.CALL_STREAM_FLOW_STATE_PENDING_START], interface = cs.CALL_STREAM_IFACE_MEDIA), ) call_async(q, chan.Hold, 'RequestHold', True) q.expect_many( EventPattern('dbus-signal', signal='HoldStateChanged', args=[cs.HS_PENDING_HOLD, cs.HSR_REQUESTED]), EventPattern('dbus-signal', signal='SendingStateChanged', args = [cs.CALL_STREAM_FLOW_STATE_PENDING_STOP], interface = cs.CALL_STREAM_IFACE_MEDIA), EventPattern('dbus-signal', signal='SendingStateChanged', args = [cs.CALL_STREAM_FLOW_STATE_PENDING_STOP], interface = cs.CALL_STREAM_IFACE_MEDIA), EventPattern('dbus-signal', signal='ReceivingStateChanged', args = [cs.CALL_STREAM_FLOW_STATE_PENDING_STOP], interface = cs.CALL_STREAM_IFACE_MEDIA), EventPattern('dbus-signal', signal='ReceivingStateChanged', args = [cs.CALL_STREAM_FLOW_STATE_PENDING_STOP], interface = cs.CALL_STREAM_IFACE_MEDIA), ) audio_cstream.CompleteReceivingStateChange( cs.CALL_STREAM_FLOW_STATE_STOPPED, dbus_interface = cs.CALL_STREAM_IFACE_MEDIA) audio_cstream.CompleteSendingStateChange( cs.CALL_STREAM_FLOW_STATE_STOPPED, dbus_interface = cs.CALL_STREAM_IFACE_MEDIA) video_cstream.CompleteReceivingStateChange( cs.CALL_STREAM_FLOW_STATE_STOPPED, dbus_interface = cs.CALL_STREAM_IFACE_MEDIA) video_cstream.CompleteSendingStateChange( cs.CALL_STREAM_FLOW_STATE_STOPPED, dbus_interface = cs.CALL_STREAM_IFACE_MEDIA) q.expect_many( EventPattern('dbus-signal', signal='SendingStateChanged', args = [cs.CALL_STREAM_FLOW_STATE_STOPPED], interface = cs.CALL_STREAM_IFACE_MEDIA), EventPattern('dbus-signal', signal='SendingStateChanged', args = [cs.CALL_STREAM_FLOW_STATE_STOPPED], interface = cs.CALL_STREAM_IFACE_MEDIA), EventPattern('dbus-signal', signal='ReceivingStateChanged', args = [cs.CALL_STREAM_FLOW_STATE_STOPPED], interface = cs.CALL_STREAM_IFACE_MEDIA), EventPattern('dbus-signal', signal='ReceivingStateChanged', args = [cs.CALL_STREAM_FLOW_STATE_STOPPED], interface = cs.CALL_STREAM_IFACE_MEDIA), EventPattern('dbus-signal', signal='HoldStateChanged', args=[cs.HS_HELD, cs.HSR_REQUESTED]), ) hold_state = chan.Hold.GetHoldState() assert hold_state[0] == cs.HS_HELD, hold_state sync_stream(q, stream) # ---- Test 10: attempting to unhold fails (both streams) ---- call_async(q, chan.Hold, 'RequestHold', False) q.expect_many( EventPattern('dbus-signal', signal='HoldStateChanged', args=[cs.HS_PENDING_UNHOLD, cs.HSR_REQUESTED]), EventPattern('dbus-signal', signal='SendingStateChanged', args = [cs.CALL_STREAM_FLOW_STATE_PENDING_START], interface = cs.CALL_STREAM_IFACE_MEDIA), EventPattern('dbus-signal', signal='SendingStateChanged', args = [cs.CALL_STREAM_FLOW_STATE_PENDING_START], interface = cs.CALL_STREAM_IFACE_MEDIA), EventPattern('dbus-signal', signal='ReceivingStateChanged', args = [cs.CALL_STREAM_FLOW_STATE_PENDING_START], interface = cs.CALL_STREAM_IFACE_MEDIA), EventPattern('dbus-signal', signal='ReceivingStateChanged', args = [cs.CALL_STREAM_FLOW_STATE_PENDING_START], interface = cs.CALL_STREAM_IFACE_MEDIA), EventPattern('dbus-return', method='RequestHold', value=()), ) audio_cstream.ReportSendingFailure(0, "", "", dbus_interface = cs.CALL_STREAM_IFACE_MEDIA) video_cstream.ReportSendingFailure(0, "", "", dbus_interface = cs.CALL_STREAM_IFACE_MEDIA) q.expect_many( EventPattern('dbus-signal', signal='HoldStateChanged', args=[cs.HS_PENDING_HOLD, cs.HSR_RESOURCE_NOT_AVAILABLE]), EventPattern('dbus-signal', signal='SendingStateChanged', args = [cs.CALL_STREAM_FLOW_STATE_STOPPED], interface = cs.CALL_STREAM_IFACE_MEDIA), EventPattern('dbus-signal', signal='SendingStateChanged', args = [cs.CALL_STREAM_FLOW_STATE_STOPPED], interface = cs.CALL_STREAM_IFACE_MEDIA), EventPattern('dbus-signal', signal='SendingStateChanged', args = [cs.CALL_STREAM_FLOW_STATE_PENDING_STOP], interface = cs.CALL_STREAM_IFACE_MEDIA), EventPattern('dbus-signal', signal='ReceivingStateChanged', args = [cs.CALL_STREAM_FLOW_STATE_PENDING_STOP], interface = cs.CALL_STREAM_IFACE_MEDIA), EventPattern('dbus-signal', signal='ReceivingStateChanged', args = [cs.CALL_STREAM_FLOW_STATE_PENDING_STOP], interface = cs.CALL_STREAM_IFACE_MEDIA), ) audio_cstream.CompleteReceivingStateChange( cs.CALL_STREAM_FLOW_STATE_STOPPED, dbus_interface = cs.CALL_STREAM_IFACE_MEDIA) video_cstream.CompleteReceivingStateChange( cs.CALL_STREAM_FLOW_STATE_STOPPED, dbus_interface = cs.CALL_STREAM_IFACE_MEDIA) q.expect_many( EventPattern('dbus-signal', signal='ReceivingStateChanged', args = [cs.CALL_STREAM_FLOW_STATE_STOPPED], interface = cs.CALL_STREAM_IFACE_MEDIA), EventPattern('dbus-signal', signal='ReceivingStateChanged', args = [cs.CALL_STREAM_FLOW_STATE_STOPPED], interface = cs.CALL_STREAM_IFACE_MEDIA), EventPattern('dbus-signal', signal='HoldStateChanged', args=[cs.HS_HELD, cs.HSR_RESOURCE_NOT_AVAILABLE]), ) # ---- Test 11: attempting to unhold fails (audio stream) ---- call_async(q, chan.Hold, 'RequestHold', False) q.expect_many( EventPattern('dbus-signal', signal='HoldStateChanged', args=[cs.HS_PENDING_UNHOLD, cs.HSR_REQUESTED]), EventPattern('dbus-signal', signal='SendingStateChanged', args = [cs.CALL_STREAM_FLOW_STATE_PENDING_START], interface = cs.CALL_STREAM_IFACE_MEDIA), EventPattern('dbus-signal', signal='SendingStateChanged', args = [cs.CALL_STREAM_FLOW_STATE_PENDING_START], interface = cs.CALL_STREAM_IFACE_MEDIA), EventPattern('dbus-signal', signal='ReceivingStateChanged', args = [cs.CALL_STREAM_FLOW_STATE_PENDING_START], interface = cs.CALL_STREAM_IFACE_MEDIA), EventPattern('dbus-signal', signal='ReceivingStateChanged', args = [cs.CALL_STREAM_FLOW_STATE_PENDING_START], interface = cs.CALL_STREAM_IFACE_MEDIA), EventPattern('dbus-return', method='RequestHold', value=()), ) audio_cstream.ReportReceivingFailure(0, "", "", dbus_interface = cs.CALL_STREAM_IFACE_MEDIA) q.expect_many( EventPattern('dbus-signal', signal='HoldStateChanged', args=[cs.HS_PENDING_HOLD, cs.HSR_RESOURCE_NOT_AVAILABLE]), EventPattern('dbus-signal', signal='SendingStateChanged', args = [cs.CALL_STREAM_FLOW_STATE_PENDING_STOP], interface = cs.CALL_STREAM_IFACE_MEDIA), EventPattern('dbus-signal', signal='SendingStateChanged', args = [cs.CALL_STREAM_FLOW_STATE_PENDING_STOP], interface = cs.CALL_STREAM_IFACE_MEDIA), EventPattern('dbus-signal', signal='ReceivingStateChanged', args = [cs.CALL_STREAM_FLOW_STATE_PENDING_STOP], interface = cs.CALL_STREAM_IFACE_MEDIA), EventPattern('dbus-signal', signal='ReceivingStateChanged', args = [cs.CALL_STREAM_FLOW_STATE_STOPPED], interface = cs.CALL_STREAM_IFACE_MEDIA), ) audio_cstream.CompleteSendingStateChange( cs.CALL_STREAM_FLOW_STATE_STOPPED, dbus_interface = cs.CALL_STREAM_IFACE_MEDIA) video_cstream.CompleteReceivingStateChange( cs.CALL_STREAM_FLOW_STATE_STOPPED, dbus_interface = cs.CALL_STREAM_IFACE_MEDIA) video_cstream.CompleteSendingStateChange( cs.CALL_STREAM_FLOW_STATE_STOPPED, dbus_interface = cs.CALL_STREAM_IFACE_MEDIA) q.expect_many( EventPattern('dbus-signal', signal='HoldStateChanged', args=[cs.HS_HELD, cs.HSR_RESOURCE_NOT_AVAILABLE]), EventPattern('dbus-signal', signal='SendingStateChanged', args = [cs.CALL_STREAM_FLOW_STATE_STOPPED], interface = cs.CALL_STREAM_IFACE_MEDIA), EventPattern('dbus-signal', signal='SendingStateChanged', args = [cs.CALL_STREAM_FLOW_STATE_STOPPED], interface = cs.CALL_STREAM_IFACE_MEDIA), EventPattern('dbus-signal', signal='ReceivingStateChanged', args = [cs.CALL_STREAM_FLOW_STATE_STOPPED], interface = cs.CALL_STREAM_IFACE_MEDIA), ) sync_stream(q, stream) # ---- Test 12: attempting to unhold partially fails (video stream) ---- call_async(q, chan.Hold, 'RequestHold', False) q.expect_many( EventPattern('dbus-signal', signal='HoldStateChanged', args=[cs.HS_PENDING_UNHOLD, cs.HSR_REQUESTED]), EventPattern('dbus-signal', signal='SendingStateChanged', args = [cs.CALL_STREAM_FLOW_STATE_PENDING_START], interface = cs.CALL_STREAM_IFACE_MEDIA), EventPattern('dbus-signal', signal='SendingStateChanged', args = [cs.CALL_STREAM_FLOW_STATE_PENDING_START], interface = cs.CALL_STREAM_IFACE_MEDIA), EventPattern('dbus-signal', signal='ReceivingStateChanged', args = [cs.CALL_STREAM_FLOW_STATE_PENDING_START], interface = cs.CALL_STREAM_IFACE_MEDIA), EventPattern('dbus-signal', signal='ReceivingStateChanged', args = [cs.CALL_STREAM_FLOW_STATE_PENDING_START], interface = cs.CALL_STREAM_IFACE_MEDIA), EventPattern('dbus-return', method='RequestHold', value=()), ) video_cstream.ReportReceivingFailure(0, "", "", dbus_interface = cs.CALL_STREAM_IFACE_MEDIA) q.expect_many( EventPattern('dbus-signal', signal='HoldStateChanged', args=[cs.HS_PENDING_HOLD, cs.HSR_RESOURCE_NOT_AVAILABLE]), EventPattern('dbus-signal', signal='SendingStateChanged', args = [cs.CALL_STREAM_FLOW_STATE_PENDING_STOP], interface = cs.CALL_STREAM_IFACE_MEDIA), EventPattern('dbus-signal', signal='SendingStateChanged', args = [cs.CALL_STREAM_FLOW_STATE_PENDING_STOP], interface = cs.CALL_STREAM_IFACE_MEDIA), EventPattern('dbus-signal', signal='ReceivingStateChanged', args = [cs.CALL_STREAM_FLOW_STATE_PENDING_STOP], interface = cs.CALL_STREAM_IFACE_MEDIA), EventPattern('dbus-signal', signal='ReceivingStateChanged', args = [cs.CALL_STREAM_FLOW_STATE_STOPPED], interface = cs.CALL_STREAM_IFACE_MEDIA), ) audio_cstream.CompleteReceivingStateChange( cs.CALL_STREAM_FLOW_STATE_STOPPED, dbus_interface = cs.CALL_STREAM_IFACE_MEDIA) audio_cstream.CompleteSendingStateChange( cs.CALL_STREAM_FLOW_STATE_STOPPED, dbus_interface = cs.CALL_STREAM_IFACE_MEDIA) video_cstream.CompleteSendingStateChange( cs.CALL_STREAM_FLOW_STATE_STOPPED, dbus_interface = cs.CALL_STREAM_IFACE_MEDIA) q.expect_many( EventPattern('dbus-signal', signal='HoldStateChanged', args=[cs.HS_HELD, cs.HSR_RESOURCE_NOT_AVAILABLE]), EventPattern('dbus-signal', signal='SendingStateChanged', args = [cs.CALL_STREAM_FLOW_STATE_STOPPED], interface = cs.CALL_STREAM_IFACE_MEDIA), EventPattern('dbus-signal', signal='SendingStateChanged', args = [cs.CALL_STREAM_FLOW_STATE_STOPPED], interface = cs.CALL_STREAM_IFACE_MEDIA), EventPattern('dbus-signal', signal='ReceivingStateChanged', args = [cs.CALL_STREAM_FLOW_STATE_STOPPED], interface = cs.CALL_STREAM_IFACE_MEDIA), ) sync_stream(q, stream) q.unforbid_events(self.unhold_event + self.hold_event) if self.jp.has_mutable_streams(): self.mutable_stream_tests() if __name__ == '__main__': test_all_dialects(partial(run_call_test, klass=CallHoldAVTest, incoming=False)) telepathy-gabble-0.18.2/tests/twisted/jingle/call-hold-audio.py0000644000175000017500000006750412200204333024423 0ustar00smcvsmcv00000000000000""" Test the Hold API. """ import dbus from dbus.exceptions import DBusException from functools import partial from servicetest import call_async, EventPattern, assertEquals, assertLength from jingletest2 import test_all_dialects from gabbletest import sync_stream from call_helper import CallTest, run_call_test import constants as cs from config import VOIP_ENABLED if not VOIP_ENABLED: print "NOTE: built with --disable-voip" raise SystemExit(77) class CallHoldAudioTest(CallTest): def initiate(self): CallTest.initiate(self) q = self.q jp = self.jp cstream = self.audio_stream chan = self.chan recv_state = cstream.GetAll(cs.CALL_STREAM_IFACE_MEDIA, dbus_interface=dbus.PROPERTIES_IFACE)["ReceivingState"] send_state = cstream.GetAll(cs.CALL_STREAM_IFACE_MEDIA, dbus_interface=dbus.PROPERTIES_IFACE)["SendingState"] assertEquals (cs.CALL_STREAM_FLOW_STATE_STOPPED, recv_state) assertEquals (cs.CALL_STREAM_FLOW_STATE_STOPPED, send_state) # These are 0- (for old dialects) or 1- (for new dialects) element lists # that can be splatted into expect_many with * self.hold_event = jp.rtp_info_event_list("hold") self.unhold_event = jp.rtp_info_event_list("unhold") # Before we have accepted any streams, GetHoldState returns Unheld and # unhold is a no-op. assertEquals((cs.HS_UNHELD, cs.HSR_REQUESTED), chan.Hold.GetHoldState()) chan.Hold.RequestHold(False) q.forbid_events(self.hold_event) q.forbid_events(self.unhold_event) assertEquals((cs.HS_UNHELD, cs.HSR_REQUESTED), chan.Hold.GetHoldState()) chan.Hold.RequestHold(False) # Before we have any streams, RequestHold(True) should work; because # there are no streams, it should take effect at once. It certainly # should't send anything to the peer. q.forbid_events(self.hold_event) q.forbid_events(self.unhold_event) call_async(q, chan.Hold, 'RequestHold', True) q.expect('dbus-signal', signal='HoldStateChanged', args=[cs.HS_PENDING_HOLD, cs.HSR_REQUESTED]) q.expect('dbus-signal', signal='HoldStateChanged', args=[cs.HS_HELD, cs.HSR_REQUESTED]) assertEquals((cs.HS_HELD, cs.HSR_REQUESTED), chan.Hold.GetHoldState()) # If we unhold, it should succeed immediately again, because there are # no resources to reclaim. call_async(q, chan.Hold, 'RequestHold', False) q.expect('dbus-signal', signal='HoldStateChanged', args=[cs.HS_PENDING_UNHOLD, cs.HSR_REQUESTED]) q.expect('dbus-signal', signal='HoldStateChanged', args=[cs.HS_UNHELD, cs.HSR_REQUESTED]) assertEquals((cs.HS_UNHELD, cs.HSR_REQUESTED), chan.Hold.GetHoldState()) # Put the call back on hold ... call_async(q, chan.Hold, 'RequestHold', True) q.expect('dbus-signal', signal='HoldStateChanged', args=[cs.HS_PENDING_HOLD, cs.HSR_REQUESTED]) q.expect('dbus-signal', signal='HoldStateChanged', args=[cs.HS_HELD, cs.HSR_REQUESTED]) assertEquals((cs.HS_HELD, cs.HSR_REQUESTED), chan.Hold.GetHoldState()) def connect(self): assertEquals((cs.HS_HELD, cs.HSR_REQUESTED), self.chan.Hold.GetHoldState()) assertEquals((cs.HS_HELD, cs.HSR_REQUESTED), self.chan.Hold.GetHoldState()) CallTest.connect(self, expect_after_si=self.hold_event) def accept_outgoing(self): # We are on hold, no states to complete here self.check_channel_state(cs.CALL_STATE_PENDING_INITIATOR) self.chan.Accept(dbus_interface=cs.CHANNEL_TYPE_CALL) self.check_channel_state(cs.CALL_STATE_INITIALISING) def pickup(self): CallTest.pickup(self, held=True) q = self.q stream = self.stream chan = self.chan cstream = self.audio_stream assertEquals((cs.HS_HELD, cs.HSR_REQUESTED), chan.Hold.GetHoldState()) recv_state = cstream.Get(cs.CALL_STREAM_IFACE_MEDIA, "ReceivingState", dbus_interface=dbus.PROPERTIES_IFACE) assertEquals (cs.CALL_STREAM_FLOW_STATE_STOPPED, recv_state) send_state = cstream.Get(cs.CALL_STREAM_IFACE_MEDIA, "SendingState", dbus_interface=dbus.PROPERTIES_IFACE) assertEquals (cs.CALL_STREAM_FLOW_STATE_STOPPED, send_state) # Now we decide we do actually want to speak to them, and unhold. # Ensure that if Gabble sent the stanza too early it's already # arrived. sync_stream(q, stream) q.forbid_events(self.unhold_event) call_async(q, chan.Hold, 'RequestHold', False) q.expect_many( EventPattern('dbus-signal', signal='HoldStateChanged', args=[cs.HS_PENDING_UNHOLD, cs.HSR_REQUESTED]), EventPattern('dbus-signal', signal='ReceivingStateChanged', args = [cs.CALL_STREAM_FLOW_STATE_PENDING_START], interface = cs.CALL_STREAM_IFACE_MEDIA), EventPattern('dbus-signal', signal='SendingStateChanged', args = [cs.CALL_STREAM_FLOW_STATE_PENDING_START], interface = cs.CALL_STREAM_IFACE_MEDIA), EventPattern('dbus-return', method='RequestHold', value=()), ) # Ensure that if Gabble sent the stanza too early it's already # arrived. sync_stream(q, stream) q.unforbid_events(self.unhold_event) cstream.CompleteReceivingStateChange( cs.CALL_STREAM_FLOW_STATE_STARTED, dbus_interface = cs.CALL_STREAM_IFACE_MEDIA) cstream.CompleteSendingStateChange( cs.CALL_STREAM_FLOW_STATE_STARTED, dbus_interface = cs.CALL_STREAM_IFACE_MEDIA) q.expect_many( EventPattern('dbus-signal', signal='HoldStateChanged', args=[cs.HS_UNHELD, cs.HSR_REQUESTED]), EventPattern('dbus-signal', signal='SendingStateChanged', args = [cs.CALL_STREAM_FLOW_STATE_STARTED], interface = cs.CALL_STREAM_IFACE_MEDIA), EventPattern('dbus-signal', signal='ReceivingStateChanged', args = [cs.CALL_STREAM_FLOW_STATE_STARTED], interface = cs.CALL_STREAM_IFACE_MEDIA), *self.unhold_event ) # Hooray! Now let's check that Hold works properly once the call's fully # established. # ---- Test 1: GetHoldState returns unheld and unhold is a no-op ---- hold_state = chan.Hold.GetHoldState() assert hold_state[0] == cs.HS_UNHELD, hold_state chan.Hold.RequestHold(False) # ---- Test 2: successful hold ---- call_async(q, chan.Hold, 'RequestHold', True) q.expect_many( EventPattern('dbus-signal', signal='HoldStateChanged', args=[cs.HS_PENDING_HOLD, cs.HSR_REQUESTED]), EventPattern('dbus-signal', signal='SendingStateChanged', args = [cs.CALL_STREAM_FLOW_STATE_PENDING_STOP], interface = cs.CALL_STREAM_IFACE_MEDIA), EventPattern('dbus-signal', signal='ReceivingStateChanged', args = [cs.CALL_STREAM_FLOW_STATE_PENDING_STOP], interface = cs.CALL_STREAM_IFACE_MEDIA), EventPattern('dbus-return', method='RequestHold', value=()), *self.hold_event ) cstream.CompleteReceivingStateChange( cs.CALL_STREAM_FLOW_STATE_STOPPED, dbus_interface = cs.CALL_STREAM_IFACE_MEDIA) cstream.CompleteSendingStateChange( cs.CALL_STREAM_FLOW_STATE_STOPPED, dbus_interface = cs.CALL_STREAM_IFACE_MEDIA) q.expect_many( EventPattern('dbus-signal', signal='HoldStateChanged', args=[cs.HS_HELD, cs.HSR_REQUESTED]), EventPattern('dbus-signal', signal='SendingStateChanged', args = [cs.CALL_STREAM_FLOW_STATE_STOPPED], interface = cs.CALL_STREAM_IFACE_MEDIA), EventPattern('dbus-signal', signal='ReceivingStateChanged', args = [cs.CALL_STREAM_FLOW_STATE_STOPPED], interface = cs.CALL_STREAM_IFACE_MEDIA), ) # ---- Test 3: GetHoldState returns held and hold is a no-op ---- hold_state = chan.Hold.GetHoldState() assert hold_state[0] == cs.HS_HELD, hold_state chan.Hold.RequestHold(True) # ---- Test 4: successful unhold ---- q.forbid_events(self.unhold_event) call_async(q, chan.Hold, 'RequestHold', False) q.expect_many( EventPattern('dbus-signal', signal='HoldStateChanged', args=[cs.HS_PENDING_UNHOLD, cs.HSR_REQUESTED]), EventPattern('dbus-signal', signal='ReceivingStateChanged', args = [cs.CALL_STREAM_FLOW_STATE_PENDING_START], interface = cs.CALL_STREAM_IFACE_MEDIA), EventPattern('dbus-signal', signal='SendingStateChanged', args = [cs.CALL_STREAM_FLOW_STATE_PENDING_START], interface = cs.CALL_STREAM_IFACE_MEDIA), EventPattern('dbus-return', method='RequestHold', value=()), ) # Ensure that if Gabble sent the stanza too early it's already # arrived. sync_stream(q, stream) q.unforbid_events(self.unhold_event) cstream.CompleteReceivingStateChange( cs.CALL_STREAM_FLOW_STATE_STARTED, dbus_interface = cs.CALL_STREAM_IFACE_MEDIA) cstream.CompleteSendingStateChange( cs.CALL_STREAM_FLOW_STATE_STARTED, dbus_interface = cs.CALL_STREAM_IFACE_MEDIA) q.expect_many( EventPattern('dbus-signal', signal='HoldStateChanged', args=[cs.HS_UNHELD, cs.HSR_REQUESTED]), EventPattern('dbus-signal', signal='SendingStateChanged', args = [cs.CALL_STREAM_FLOW_STATE_STARTED], interface = cs.CALL_STREAM_IFACE_MEDIA), EventPattern('dbus-signal', signal='ReceivingStateChanged', args = [cs.CALL_STREAM_FLOW_STATE_STARTED], interface = cs.CALL_STREAM_IFACE_MEDIA), *self.unhold_event ) # ---- Test 5: GetHoldState returns False and unhold is a no-op ---- hold_state = chan.Hold.GetHoldState() assert hold_state[0] == cs.HS_UNHELD, hold_state chan.Hold.RequestHold(False) # ---- Test 6: 3 parallel calls to hold ---- hold_state = chan.Hold.GetHoldState() assert hold_state[0] == cs.HS_UNHELD, hold_state call_async(q, chan.Hold, 'RequestHold', True) call_async(q, chan.Hold, 'RequestHold', True) call_async(q, chan.Hold, 'RequestHold', True) q.expect_many( EventPattern('dbus-signal', signal='HoldStateChanged', args=[cs.HS_PENDING_HOLD, cs.HSR_REQUESTED]), EventPattern('dbus-signal', signal='SendingStateChanged', args = [cs.CALL_STREAM_FLOW_STATE_PENDING_STOP], interface = cs.CALL_STREAM_IFACE_MEDIA), EventPattern('dbus-signal', signal='ReceivingStateChanged', args = [cs.CALL_STREAM_FLOW_STATE_PENDING_STOP], interface = cs.CALL_STREAM_IFACE_MEDIA), EventPattern('dbus-return', method='RequestHold', value=()), *self.hold_event ) cstream.CompleteReceivingStateChange( cs.CALL_STREAM_FLOW_STATE_STOPPED, dbus_interface = cs.CALL_STREAM_IFACE_MEDIA) cstream.CompleteSendingStateChange( cs.CALL_STREAM_FLOW_STATE_STOPPED, dbus_interface = cs.CALL_STREAM_IFACE_MEDIA) q.expect_many( EventPattern('dbus-signal', signal='SendingStateChanged', args = [cs.CALL_STREAM_FLOW_STATE_STOPPED], interface = cs.CALL_STREAM_IFACE_MEDIA), EventPattern('dbus-signal', signal='ReceivingStateChanged', args = [cs.CALL_STREAM_FLOW_STATE_STOPPED], interface = cs.CALL_STREAM_IFACE_MEDIA), EventPattern('dbus-signal', signal='HoldStateChanged', args=[cs.HS_HELD, cs.HSR_REQUESTED]), ) # ---- Test 7: 3 parallel calls to unhold ---- q.forbid_events(self.unhold_event) call_async(q, chan.Hold, 'RequestHold', False) call_async(q, chan.Hold, 'RequestHold', False) call_async(q, chan.Hold, 'RequestHold', False) q.expect_many( EventPattern('dbus-signal', signal='HoldStateChanged', args=[cs.HS_PENDING_UNHOLD, cs.HSR_REQUESTED]), EventPattern('dbus-signal', signal='SendingStateChanged', args = [cs.CALL_STREAM_FLOW_STATE_PENDING_START], interface = cs.CALL_STREAM_IFACE_MEDIA), EventPattern('dbus-signal', signal='ReceivingStateChanged', args = [cs.CALL_STREAM_FLOW_STATE_PENDING_START], interface = cs.CALL_STREAM_IFACE_MEDIA), EventPattern('dbus-return', method='RequestHold', value=()), ) # Ensure that if Gabble sent the stanza too early it's already # arrived. sync_stream(q, stream) q.unforbid_events(self.unhold_event) cstream.CompleteReceivingStateChange( cs.CALL_STREAM_FLOW_STATE_STARTED, dbus_interface = cs.CALL_STREAM_IFACE_MEDIA) cstream.CompleteSendingStateChange( cs.CALL_STREAM_FLOW_STATE_STARTED, dbus_interface = cs.CALL_STREAM_IFACE_MEDIA) q.expect_many( EventPattern('dbus-signal', signal='HoldStateChanged', args=[cs.HS_UNHELD, cs.HSR_REQUESTED]), EventPattern('dbus-signal', signal='SendingStateChanged', args = [cs.CALL_STREAM_FLOW_STATE_STARTED], interface = cs.CALL_STREAM_IFACE_MEDIA), EventPattern('dbus-signal', signal='ReceivingStateChanged', args = [cs.CALL_STREAM_FLOW_STATE_STARTED], interface = cs.CALL_STREAM_IFACE_MEDIA), *self.unhold_event ) # ---- Test 8: hold, then change our minds before s-e has responded ---- hold_state = chan.Hold.GetHoldState() assert hold_state[0] == cs.HS_UNHELD, hold_state call_async(q, chan.Hold, 'RequestHold', True) q.expect_many( EventPattern('dbus-signal', signal='HoldStateChanged', args=[cs.HS_PENDING_HOLD, cs.HSR_REQUESTED]), EventPattern('dbus-signal', signal='SendingStateChanged', args = [cs.CALL_STREAM_FLOW_STATE_PENDING_STOP], interface = cs.CALL_STREAM_IFACE_MEDIA), EventPattern('dbus-signal', signal='ReceivingStateChanged', args = [cs.CALL_STREAM_FLOW_STATE_PENDING_STOP], interface = cs.CALL_STREAM_IFACE_MEDIA), *self.hold_event ) q.forbid_events(self.unhold_event) call_async(q, chan.Hold, 'RequestHold', False) q.expect_many( EventPattern('dbus-signal', signal='HoldStateChanged', args=[cs.HS_PENDING_UNHOLD, cs.HSR_REQUESTED]), EventPattern('dbus-signal', signal='SendingStateChanged', args = [cs.CALL_STREAM_FLOW_STATE_PENDING_START], interface = cs.CALL_STREAM_IFACE_MEDIA), EventPattern('dbus-signal', signal='ReceivingStateChanged', args = [cs.CALL_STREAM_FLOW_STATE_PENDING_START], interface = cs.CALL_STREAM_IFACE_MEDIA), # Gabble shouldn't send here because s-e might have # already relinquished the audio hardware. ) sync_stream(q, stream) q.unforbid_events(self.unhold_event) try: cstream.CompleteReceivingStateChange( cs.CALL_STREAM_FLOW_STATE_STOPPED, dbus_interface = cs.CALL_STREAM_IFACE_MEDIA) except dbus.DBusException, e: assertEquals (cs.INVALID_ARGUMENT, e.get_dbus_name ()) try: cstream.CompleteSendingStateChange( cs.CALL_STREAM_FLOW_STATE_STOPPED, dbus_interface = cs.CALL_STREAM_IFACE_MEDIA) except dbus.DBusException, e: assertEquals (cs.INVALID_ARGUMENT, e.get_dbus_name ()) cstream.CompleteReceivingStateChange( cs.CALL_STREAM_FLOW_STATE_STARTED, dbus_interface = cs.CALL_STREAM_IFACE_MEDIA) cstream.CompleteSendingStateChange( cs.CALL_STREAM_FLOW_STATE_STARTED, dbus_interface = cs.CALL_STREAM_IFACE_MEDIA) q.expect_many( EventPattern('dbus-signal', signal='HoldStateChanged', args=[cs.HS_UNHELD, cs.HSR_REQUESTED]), EventPattern('dbus-signal', signal='SendingStateChanged', args = [cs.CALL_STREAM_FLOW_STATE_STARTED], interface = cs.CALL_STREAM_IFACE_MEDIA), EventPattern('dbus-signal', signal='ReceivingStateChanged', args = [cs.CALL_STREAM_FLOW_STATE_STARTED], interface = cs.CALL_STREAM_IFACE_MEDIA), *self.unhold_event ) hold_state = chan.Hold.GetHoldState() assert hold_state[0] == cs.HS_UNHELD, hold_state # ---- Test 9: unhold, then change our minds before s-e has responded -- # Go to state "held" first call_async(q, chan.Hold, 'RequestHold', True) q.expect_many( EventPattern('dbus-signal', signal='HoldStateChanged', args=[cs.HS_PENDING_HOLD, cs.HSR_REQUESTED]), EventPattern('dbus-signal', signal='SendingStateChanged', args = [cs.CALL_STREAM_FLOW_STATE_PENDING_STOP], interface = cs.CALL_STREAM_IFACE_MEDIA), EventPattern('dbus-signal', signal='ReceivingStateChanged', args = [cs.CALL_STREAM_FLOW_STATE_PENDING_STOP], interface = cs.CALL_STREAM_IFACE_MEDIA), EventPattern('dbus-return', method='RequestHold', value=()), *self.hold_event ) cstream.CompleteReceivingStateChange( cs.CALL_STREAM_FLOW_STATE_STOPPED, dbus_interface = cs.CALL_STREAM_IFACE_MEDIA) cstream.CompleteSendingStateChange( cs.CALL_STREAM_FLOW_STATE_STOPPED, dbus_interface = cs.CALL_STREAM_IFACE_MEDIA) q.expect_many( EventPattern('dbus-signal', signal='SendingStateChanged', args = [cs.CALL_STREAM_FLOW_STATE_STOPPED], interface = cs.CALL_STREAM_IFACE_MEDIA), EventPattern('dbus-signal', signal='ReceivingStateChanged', args = [cs.CALL_STREAM_FLOW_STATE_STOPPED], interface = cs.CALL_STREAM_IFACE_MEDIA), EventPattern('dbus-signal', signal='HoldStateChanged', args=[cs.HS_HELD, cs.HSR_REQUESTED]), ) # Actually do test 9 hold_state = chan.Hold.GetHoldState() assert hold_state[0] == cs.HS_HELD, hold_state # Check that Gabble doesn't send another , or send # before we change our minds. q.forbid_events(self.unhold_event + self.hold_event) call_async(q, chan.Hold, 'RequestHold', False) q.expect_many( EventPattern('dbus-signal', signal='HoldStateChanged', args=[cs.HS_PENDING_UNHOLD, cs.HSR_REQUESTED]), EventPattern('dbus-signal', signal='SendingStateChanged', args = [cs.CALL_STREAM_FLOW_STATE_PENDING_START], interface = cs.CALL_STREAM_IFACE_MEDIA), EventPattern('dbus-signal', signal='ReceivingStateChanged', args = [cs.CALL_STREAM_FLOW_STATE_PENDING_START], interface = cs.CALL_STREAM_IFACE_MEDIA), ) call_async(q, chan.Hold, 'RequestHold', True) q.expect_many( EventPattern('dbus-signal', signal='HoldStateChanged', args=[cs.HS_PENDING_HOLD, cs.HSR_REQUESTED]), EventPattern('dbus-signal', signal='SendingStateChanged', args = [cs.CALL_STREAM_FLOW_STATE_PENDING_STOP], interface = cs.CALL_STREAM_IFACE_MEDIA), EventPattern('dbus-signal', signal='ReceivingStateChanged', args = [cs.CALL_STREAM_FLOW_STATE_PENDING_STOP], interface = cs.CALL_STREAM_IFACE_MEDIA), ) cstream.CompleteReceivingStateChange( cs.CALL_STREAM_FLOW_STATE_STOPPED, dbus_interface = cs.CALL_STREAM_IFACE_MEDIA) cstream.CompleteSendingStateChange( cs.CALL_STREAM_FLOW_STATE_STOPPED, dbus_interface = cs.CALL_STREAM_IFACE_MEDIA) q.expect_many( EventPattern('dbus-signal', signal='SendingStateChanged', args = [cs.CALL_STREAM_FLOW_STATE_STOPPED], interface = cs.CALL_STREAM_IFACE_MEDIA), EventPattern('dbus-signal', signal='ReceivingStateChanged', args = [cs.CALL_STREAM_FLOW_STATE_STOPPED], interface = cs.CALL_STREAM_IFACE_MEDIA), EventPattern('dbus-signal', signal='HoldStateChanged', args=[cs.HS_HELD, cs.HSR_REQUESTED]), ) hold_state = chan.Hold.GetHoldState() assert hold_state[0] == cs.HS_HELD, hold_state sync_stream(q, stream) q.unforbid_events(self.unhold_event + self.hold_event) # ---- Test 10: attempting to unhold fails in the sending bit ---- # Check that Gabble doesn't send another , or send even # though unholding fails. q.forbid_events(self.unhold_event + self.hold_event) call_async(q, chan.Hold, 'RequestHold', False) q.expect_many( EventPattern('dbus-signal', signal='HoldStateChanged', args=[cs.HS_PENDING_UNHOLD, cs.HSR_REQUESTED]), EventPattern('dbus-signal', signal='SendingStateChanged', args = [cs.CALL_STREAM_FLOW_STATE_PENDING_START], interface = cs.CALL_STREAM_IFACE_MEDIA), EventPattern('dbus-signal', signal='ReceivingStateChanged', args = [cs.CALL_STREAM_FLOW_STATE_PENDING_START], interface = cs.CALL_STREAM_IFACE_MEDIA), EventPattern('dbus-return', method='RequestHold', value=()), ) cstream.ReportSendingFailure(0, "", "", dbus_interface = cs.CALL_STREAM_IFACE_MEDIA) q.expect_many( EventPattern('dbus-signal', signal='HoldStateChanged', args=[cs.HS_PENDING_HOLD, cs.HSR_RESOURCE_NOT_AVAILABLE]), EventPattern('dbus-signal', signal='SendingStateChanged', args = [cs.CALL_STREAM_FLOW_STATE_STOPPED], interface = cs.CALL_STREAM_IFACE_MEDIA), EventPattern('dbus-signal', signal='ReceivingStateChanged', args = [cs.CALL_STREAM_FLOW_STATE_PENDING_STOP], interface = cs.CALL_STREAM_IFACE_MEDIA), ) cstream.CompleteReceivingStateChange( cs.CALL_STREAM_FLOW_STATE_STOPPED, dbus_interface = cs.CALL_STREAM_IFACE_MEDIA) q.expect_many( EventPattern('dbus-signal', signal='HoldStateChanged', args=[cs.HS_HELD, cs.HSR_RESOURCE_NOT_AVAILABLE]), EventPattern('dbus-signal', signal='ReceivingStateChanged', args = [cs.CALL_STREAM_FLOW_STATE_STOPPED], interface = cs.CALL_STREAM_IFACE_MEDIA), ) # ---- Test 11: attempting to unhold fails in the receiving bit ---- call_async(q, chan.Hold, 'RequestHold', False) q.expect_many( EventPattern('dbus-signal', signal='HoldStateChanged', args=[cs.HS_PENDING_UNHOLD, cs.HSR_REQUESTED]), EventPattern('dbus-signal', signal='SendingStateChanged', args = [cs.CALL_STREAM_FLOW_STATE_PENDING_START], interface = cs.CALL_STREAM_IFACE_MEDIA), EventPattern('dbus-signal', signal='ReceivingStateChanged', args = [cs.CALL_STREAM_FLOW_STATE_PENDING_START], interface = cs.CALL_STREAM_IFACE_MEDIA), EventPattern('dbus-return', method='RequestHold', value=()), ) cstream.ReportReceivingFailure(0, "", "", dbus_interface = cs.CALL_STREAM_IFACE_MEDIA) q.expect_many( EventPattern('dbus-signal', signal='HoldStateChanged', args=[cs.HS_PENDING_HOLD, cs.HSR_RESOURCE_NOT_AVAILABLE]), EventPattern('dbus-signal', signal='SendingStateChanged', args = [cs.CALL_STREAM_FLOW_STATE_PENDING_STOP], interface = cs.CALL_STREAM_IFACE_MEDIA), EventPattern('dbus-signal', signal='ReceivingStateChanged', args = [cs.CALL_STREAM_FLOW_STATE_STOPPED], interface = cs.CALL_STREAM_IFACE_MEDIA), ) cstream.CompleteSendingStateChange( cs.CALL_STREAM_FLOW_STATE_STOPPED, dbus_interface = cs.CALL_STREAM_IFACE_MEDIA) q.expect_many( EventPattern('dbus-signal', signal='HoldStateChanged', args=[cs.HS_HELD, cs.HSR_RESOURCE_NOT_AVAILABLE]), EventPattern('dbus-signal', signal='SendingStateChanged', args = [cs.CALL_STREAM_FLOW_STATE_STOPPED], interface = cs.CALL_STREAM_IFACE_MEDIA), ) sync_stream(q, stream) q.unforbid_events(self.unhold_event + self.hold_event) # ---- Test 12: when we successfully unhold, the peer gets --- q.forbid_events(self.unhold_event) call_async(q, chan.Hold, 'RequestHold', False) q.expect_many( EventPattern('dbus-signal', signal='HoldStateChanged', args=[cs.HS_PENDING_UNHOLD, cs.HSR_REQUESTED]), EventPattern('dbus-signal', signal='SendingStateChanged', args = [cs.CALL_STREAM_FLOW_STATE_PENDING_START], interface = cs.CALL_STREAM_IFACE_MEDIA), EventPattern('dbus-signal', signal='ReceivingStateChanged', args = [cs.CALL_STREAM_FLOW_STATE_PENDING_START], interface = cs.CALL_STREAM_IFACE_MEDIA), EventPattern('dbus-return', method='RequestHold', value=()), ) # Ensure that if Gabble sent the stanza too early it's already # arrived. sync_stream(q, stream) q.unforbid_events(self.unhold_event) cstream.CompleteReceivingStateChange( cs.CALL_STREAM_FLOW_STATE_STARTED, dbus_interface = cs.CALL_STREAM_IFACE_MEDIA) cstream.CompleteSendingStateChange( cs.CALL_STREAM_FLOW_STATE_STARTED, dbus_interface = cs.CALL_STREAM_IFACE_MEDIA) q.expect_many( EventPattern('dbus-signal', signal='HoldStateChanged', args=[cs.HS_UNHELD, cs.HSR_REQUESTED]), EventPattern('dbus-signal', signal='SendingStateChanged', args = [cs.CALL_STREAM_FLOW_STATE_STARTED], interface = cs.CALL_STREAM_IFACE_MEDIA), EventPattern('dbus-signal', signal='ReceivingStateChanged', args = [cs.CALL_STREAM_FLOW_STATE_STARTED], interface = cs.CALL_STREAM_IFACE_MEDIA), *self.unhold_event ) if __name__ == '__main__': test_all_dialects(partial(run_call_test, klass=CallHoldAudioTest, incoming=False)) telepathy-gabble-0.18.2/tests/twisted/jingle/call-google-relay.py0000644000175000017500000002470212227000321024755 0ustar00smcvsmcv00000000000000""" Test getting relay from Google jingleinfo """ import config if not config.GOOGLE_RELAY_ENABLED: print "NOTE: built with --disable-google-relay" raise SystemExit(77) import dbus from dbus.exceptions import DBusException from functools import partial from servicetest import call_async, EventPattern, assertEquals from jingletest2 import GtalkProtocol04 from gabbletest import (exec_test, disconnect_conn, GoogleXmlStream, make_result_iq, sync_stream) from call_helper import CallTest, run_call_test import constants as cs import ns from twisted.words.protocols.jabber.client import IQ from twisted.web import http from httptest import listen_http TOO_SLOW_CLOSE = 1 TOO_SLOW_REMOVE_SELF = 2 TOO_SLOW_DISCONNECT = 3 class CallGoogleRelayTest(CallTest): initial_audio = True initial_video = True # A real request/response looks like this: # # GET /create_session HTTP/1.1 # Connection: Keep-Alive # Content-Length: 0 # Host: relay.l.google.com # User-Agent: farsight-libjingle # X-Google-Relay-Auth: censored # X-Talk-Google-Relay-Auth: censored # # HTTP/1.1 200 OK # Content-Type: text/plain # Date: Tue, 03 Mar 2009 18:33:28 GMT # Server: MediaProxy # Cache-Control: private, x-gzip-ok="" # Transfer-Encoding: chunked # # c3 # relay.ip=74.125.47.126 # relay.udp_port=19295 # relay.tcp_port=19294 # relay.ssltcp_port=443 # stun.ip=74.125.47.126 # stun.port=19302 # username=censored # password=censored # magic_cookie=censored # # 0 response_template = """c3 relay.ip=127.0.0.1 relay.udp_port=11111 relay.tcp_port=22222 relay.ssltcp_port=443 stun.ip=1.2.3.4 stun.port=12345 username=UUUUUUUU%d password=PPPPPPPP%d magic_cookie=MMMMMMMM """ def handle_request(req, n): req.setResponseCode(http.OK) req.setHeader("Content-Type", "text/plain") req.write(self.response_template % (n, n)) req.finish() def prepare(self): events = self.q.expect_many( EventPattern('stream-iq', query_ns=ns.GOOGLE_JINGLE_INFO), EventPattern('stream-iq', to=None, query_ns='vcard-temp', query_name='vCard'), EventPattern('stream-iq', query_ns=ns.ROSTER), ) CallTest.prepare(self, events=events) ji_event = events[0] listen_port = listen_http(self.q, 0) jingleinfo = make_result_iq(self.stream, ji_event.stanza) stun = jingleinfo.firstChildElement().addElement('stun') server = stun.addElement('server') server['host'] = 'resolves-to-1.2.3.4' server['udp'] = '12345' self.expected_stun_server = '1.2.3.4' self.expected_stun_port = 12345 # This bit is undocumented... but it has the same format as what we get # from Google Talk servers: # # # # # # # # # # # censored # # # # relay = jingleinfo.firstChildElement().addElement('relay') relay.addElement('token', content='jingle all the way') server = relay.addElement('server') server['host'] = '127.0.0.1' server['udp'] = '11111' server['tcp'] = '22222' server['tcpssl'] = '443' # The special regression-test build of Gabble parses this attribute, # because we can't listen on port 80 server['gabble-test-http-port'] = str(listen_port.getHost().port) self.stream.send(jingleinfo) jingleinfo = None # Spoof some jingle info. This is a regression test for # . We assert that # Gabble has ignored this stuff later. iq = IQ(self.stream, 'set') iq['from'] = "evil@evil.net" query = iq.addElement((ns.GOOGLE_JINGLE_INFO, "query")) stun = query.addElement('stun') server = stun.addElement('server') server['host'] = '6.6.6.6' server['udp'] = '6666' relay = query.addElement('relay') relay.addElement('token', content='mwohahahahaha') server = relay.addElement('server') server['host'] = '127.0.0.1' server['udp'] = '666' server['tcp'] = '999' server['tcpssl'] = '666' self.stream.send(iq) # Force Gabble to process the capabilities sync_stream(self.q, self.stream) def connect(self): CallTest.connect(self) req_pattern = EventPattern('http-request', method='GET', path='/create_session') req1, req2 = self.q.expect_many(req_pattern, req_pattern) if self.params['too-slow'] is not None: test_too_slow(req1, req2, too_slow) return def pickup(self): if self.params['too-slow'] is not None: return CallTest.pickup(self) # The new API for STUN servers etc. cstream_props = self.audio_stream.GetAll( cs.CALL_STREAM_IFACE_MEDIA, dbus_interface=dbus.PROPERTIES_IFACE) assert cstream_props['Transport'] == cs.CALL_STREAM_TRANSPORT_GTALK_P2P # If Gabble has erroneously paid attention to the contact # evil@evil.net who sent us a google:jingleinfo stanza, this assertion # will fail. assertEquals([(self.expected_stun_server, self.expected_stun_port)], cstream_props['STUNServers']) credentials_used = {} credentials = {} for relay in cstream_props['RelayInfo']: assert relay['ip'] == '127.0.0.1', cstream_props['RelayInfo'] assert relay['type'] in ('udp', 'tcp', 'tls') assert relay['component'] in (1, 2) if relay['type'] == 'udp': assert relay['port'] == 11111, cstream_props['RelayInfo'] elif relay['type'] == 'tcp': assert relay['port'] == 22222, cstream_props['RelayInfo'] elif relay['type'] == 'tls': assert relay['port'] == 443, cstream_props['RelayInfo'] assert relay['username'][:8] == 'UUUUUUUU', \ cstream_props['RelayInfo'] assert relay['password'][:8] == 'PPPPPPPP', \ cstream_props['RelayInfo'] assert relay['password'][8:] == relay['username'][8:], \ cstream_props['RelayInfo'] assert (relay['password'][8:], relay['type']) \ not in credentials_used credentials_used[(relay['password'][8:], relay['type'])] = 1 credentials[(relay['component'], relay['type'])] = \ relay['password'][8:] assert (1, 'udp') in credentials assert (1, 'tcp') in credentials assert (1, 'tls') in credentials assert (2, 'udp') in credentials assert (2, 'tcp') in credentials assert (2, 'tls') in credentials assert ('0', 'udp') in credentials_used assert ('0', 'tcp') in credentials_used assert ('0', 'tls') in credentials_used assert ('1', 'udp') in credentials_used assert ('1', 'tcp') in credentials_used assert ('1', 'tls') in credentials_used # consistency check, since we currently reimplement Get separately for k in cstream_props: assert cstream_props[k] == self.audio_stream.Get( cs.CALL_STREAM_IFACE_MEDIA, k, dbus_interface=dbus.PROPERTIES_IFACE) def test_too_slow(self, req1, req2, too_slow): """ Regression test for a bug where if the channel was closed before the HTTP responses arrived, the responses finally arriving crashed Gabble. """ # User gets bored, and ends the call. e = EventPattern('dbus-signal', signal='Closed', path=chan.object_path) if too_slow == TOO_SLOW_CLOSE: call_async(self.q, self.chan, 'Close', dbus_interface=cs.CHANNEL) elif too_slow == TOO_SLOW_REMOVE_SELF: self.chan.Hangup (0, "", "", dbus_interface=cs.CHANNEL_TYPE_CALL) elif too_slow == TOO_SLOW_DISCONNECT: disconnect_conn(q, conn, stream, [e]) try: chan.GetMembers() except dbus.DBusException, e: # This should fail because the object's gone away, not because # Gabble's crashed. assert cs.UNKNOWN_METHOD == e.get_dbus_name(), \ "maybe Gabble crashed? %s" % e else: # Gabble will probably also crash in a moment, because the http # request callbacks will be called after the channel's meant to # have died, which will cause the channel to try to call # methods on the (finalized) connection. assert False, "the channel should be dead by now" return # Hangup does not cause the channel to close # except if local user hangup if too_slow != TOO_SLOW_REMOVE_SELF: q.expect_many(e) # Now Google answers! self.handle_request(req1.request, 2) self.handle_request(req2.request, 3) # Make a misc method call to check that Gabble's still alive. sync_dbus(self.bus, self.q, self.conn) def exec_relay_test(incoming, too_slow=None): exec_test(partial(run_call_test, GtalkProtocol04(), incoming=incoming, klass=CallGoogleRelayTest, params={'too-slow': too_slow}), protocol=GoogleXmlStream) if __name__ == '__main__': exec_relay_test(True) exec_relay_test(False) exec_relay_test(True, TOO_SLOW_CLOSE) exec_relay_test(False, TOO_SLOW_CLOSE) exec_relay_test(True, TOO_SLOW_REMOVE_SELF) exec_relay_test(False, TOO_SLOW_REMOVE_SELF) exec_relay_test(True, TOO_SLOW_DISCONNECT) exec_relay_test(False, TOO_SLOW_DISCONNECT) telepathy-gabble-0.18.2/tests/twisted/jingle/call-dtmf.py0000644000175000017500000001675612200204333023333 0ustar00smcvsmcv00000000000000""" Test DMTF events in a Call channel """ import dbus from dbus.exceptions import DBusException from functools import partial from servicetest import call_async, EventPattern, assertEquals from jingletest2 import test_all_dialects from call_helper import CallTest, run_call_test import constants as cs from config import VOIP_ENABLED if not VOIP_ENABLED: print "NOTE: built with --disable-voip" raise SystemExit(77) class CallDtmfTest(CallTest): def test_dtmf(self): content = self.audio_content q = self.q # The Stream_ID is specified to be ignored; we use 666 here. assertEquals(False, content.Get(cs.CALL_CONTENT_IFACE_DTMF, 'CurrentlySendingTones', dbus_interface=dbus.PROPERTIES_IFACE)); call_async(q, content.DTMF, 'StartTone', 3) q.expect_many( EventPattern('dbus-signal', signal='DTMFChangeRequested', args = [3, cs.CALL_SENDING_STATE_PENDING_SEND]), EventPattern('dbus-return', method='StartTone'), ) assertEquals(True, content.Get(cs.CALL_CONTENT_IFACE_DTMF, 'CurrentlySendingTones', dbus_interface=dbus.PROPERTIES_IFACE)); content.Media.AcknowledgeDTMFChange(3, cs.CALL_SENDING_STATE_SENDING) q.expect('dbus-signal', signal='SendingTones', args=['3']) call_async(q, content.DTMF, 'StopTone') q.expect_many( EventPattern('dbus-signal', signal='DTMFChangeRequested', args = [3, cs.CALL_SENDING_STATE_PENDING_STOP_SENDING]), EventPattern('dbus-return', method='StopTone'), ) call_async(q, content.Media, 'AcknowledgeDTMFChange', 3, cs.CALL_SENDING_STATE_NONE) q.expect_many( EventPattern('dbus-signal', signal='StoppedTones', args=[False]), EventPattern('dbus-return', method='AcknowledgeDTMFChange'), ) assertEquals(False, content.Get(cs.CALL_CONTENT_IFACE_DTMF, 'CurrentlySendingTones', dbus_interface=dbus.PROPERTIES_IFACE)); call_async(q, content.DTMF, 'MultipleTones', '123') q.expect_many( EventPattern('dbus-return', method='MultipleTones'), EventPattern('dbus-signal', signal='DTMFChangeRequested', args = [1, cs.CALL_SENDING_STATE_PENDING_SEND]), ) content.Media.AcknowledgeDTMFChange(1, cs.CALL_SENDING_STATE_SENDING) q.expect('dbus-signal', signal='SendingTones', args=['123']) q.expect('dbus-signal', signal='DTMFChangeRequested', args = [1, cs.CALL_SENDING_STATE_PENDING_STOP_SENDING]) content.Media.AcknowledgeDTMFChange(1, cs.CALL_SENDING_STATE_NONE) q.expect_many( EventPattern('dbus-signal', signal='DTMFChangeRequested', args = [2, cs.CALL_SENDING_STATE_PENDING_SEND]), ) content.Media.AcknowledgeDTMFChange(2, cs.CALL_SENDING_STATE_SENDING) q.expect('dbus-signal', signal='SendingTones', args=['23']), q.expect('dbus-signal', signal='DTMFChangeRequested', args = [2, cs.CALL_SENDING_STATE_PENDING_STOP_SENDING]) content.Media.AcknowledgeDTMFChange(2, cs.CALL_SENDING_STATE_NONE) q.expect_many( EventPattern('dbus-signal', signal='DTMFChangeRequested', args = [3, cs.CALL_SENDING_STATE_PENDING_SEND]), ) content.Media.AcknowledgeDTMFChange(3, cs.CALL_SENDING_STATE_SENDING) q.expect('dbus-signal', signal='SendingTones', args=['3']), q.expect('dbus-signal', signal='DTMFChangeRequested', args = [3, cs.CALL_SENDING_STATE_PENDING_STOP_SENDING]) content.Media.AcknowledgeDTMFChange(3, cs.CALL_SENDING_STATE_NONE) q.expect_many( EventPattern('dbus-signal', signal='StoppedTones', args=[False]) ) call_async(q, content.DTMF, 'MultipleTones', '1,1' * 100) q.expect_many( EventPattern('dbus-signal', signal='DTMFChangeRequested', args = [1, cs.CALL_SENDING_STATE_PENDING_SEND]), EventPattern('dbus-return', method='MultipleTones'), ) call_async(q, content.DTMF, 'MultipleTones', '9') q.expect('dbus-error', method='MultipleTones', name=cs.SERVICE_BUSY) call_async(q, content.DTMF, 'StartTone', 9) q.expect('dbus-error', method='StartTone', name=cs.SERVICE_BUSY) call_async(q, content.DTMF, 'StopTone') q.expect_many( EventPattern('dbus-signal', signal='DTMFChangeRequested', args = [1, cs.CALL_SENDING_STATE_PENDING_STOP_SENDING]), EventPattern('dbus-return', method='StopTone'), ) call_async(q, content.Media, 'AcknowledgeDTMFChange', 1, cs.CALL_SENDING_STATE_NONE) q.expect_many( EventPattern('dbus-signal', signal='StoppedTones', args=[True]), EventPattern('dbus-return', method='AcknowledgeDTMFChange'), ) call_async(q, content.DTMF, 'MultipleTones', '1w2') q.expect_many( EventPattern('dbus-signal', signal='DTMFChangeRequested', args = [1, cs.CALL_SENDING_STATE_PENDING_SEND]), EventPattern('dbus-return', method='MultipleTones'), ) content.Media.AcknowledgeDTMFChange(1, cs.CALL_SENDING_STATE_SENDING) q.expect('dbus-signal', signal='SendingTones', args=['1w2']), q.expect('dbus-signal', signal='DTMFChangeRequested', args = [1, cs.CALL_SENDING_STATE_PENDING_STOP_SENDING]) call_async(q, content.Media, 'AcknowledgeDTMFChange', 1, cs.CALL_SENDING_STATE_NONE) q.expect_many( EventPattern('dbus-signal', signal='TonesDeferred', args=['2']), EventPattern('dbus-signal', signal='StoppedTones', args=[False]), EventPattern('dbus-return', method='AcknowledgeDTMFChange'), ) assertEquals('2', content.Get(cs.CALL_CONTENT_IFACE_DTMF, 'DeferredTones', dbus_interface=dbus.PROPERTIES_IFACE)); call_async(q, content.DTMF, 'StartTone', 7) q.expect_many( EventPattern('dbus-signal', signal='DTMFChangeRequested', args = [7, cs.CALL_SENDING_STATE_PENDING_SEND]), EventPattern('dbus-return', method='StartTone'), ) # Checked that DeferredTones is properly reset assertEquals('', content.Get(cs.CALL_CONTENT_IFACE_DTMF, 'DeferredTones', dbus_interface=dbus.PROPERTIES_IFACE)); content.Media.AcknowledgeDTMFChange(7, cs.CALL_SENDING_STATE_SENDING) q.expect('dbus-signal', signal='SendingTones', args=['7']), call_async(q, content.DTMF, 'StopTone') q.expect_many( EventPattern('dbus-signal', signal='DTMFChangeRequested', args = [7, cs.CALL_SENDING_STATE_PENDING_STOP_SENDING]), EventPattern('dbus-return', method='StopTone'), ) content.Media.AcknowledgeDTMFChange(7, cs.CALL_SENDING_STATE_NONE) q.expect('dbus-signal', signal='StoppedTones', args=[False]) def pickup(self): CallTest.pickup(self) self.test_dtmf() if __name__ == '__main__': test_all_dialects( partial(run_call_test, klass=CallDtmfTest, incoming=False)) telepathy-gabble-0.18.2/tests/twisted/jingle/call-content-adding-removal.py0000644000175000017500000001357412200204333026735 0ustar00smcvsmcv00000000000000""" Test content adding and removal during the session. We start session with only one stream, then add one more, then remove the first one and lastly remove the second stream, which closes the session. """ from functools import partial from servicetest import call_async, assertEquals, assertLength, EventPattern from gabbletest import make_result_iq, sync_stream from jingletest2 import test_dialects, JingleProtocol015, JingleProtocol031 from call_helper import CallTest, run_call_test import constants as cs import dbus from config import VOIP_ENABLED if not VOIP_ENABLED: print "NOTE: built with --disable-voip" raise SystemExit(77) class CallContentAddingRemovalTest(CallTest): # A/V initial_audio = True initial_video = True def pickup(self): peer_removes_final_content = self.params['peer-removes-final-content'] # Remove video content before remote pick the call self.video_content.Remove() e = self.q.expect('dbus-signal', signal='ContentRemoved') assertEquals(e.args[1][0], self.self_handle) assertEquals(e.args[1][1], cs.CALL_STATE_CHANGE_REASON_USER_REQUESTED) assertEquals(e.args[1][2], '') self.initial_video = False self.video_content = None self.video_content_name = None self.video_stream = None # ...but before the peer notices, they accept the call. CallTest.pickup(self) # Gabble sends content-remove for the video stream... e = self.q.expect('stream-iq', predicate=self.jp.action_predicate('content-remove')) # Only now the remote end removes the video stream; if gabble mistakenly # marked it as accepted on session acceptance, it'll crash right about # now. If it's good, stream will be really removed, and # we can proceed. self.stream.send(make_result_iq(self.stream, e.stanza)) # Actually, we *do* want video! content_path = self.chan.AddContent( "video1", cs.CALL_MEDIA_TYPE_VIDEO, cs.MEDIA_STREAM_DIRECTION_BIDIRECTIONAL, dbus_interface=cs.CHANNEL_TYPE_CALL); self.q.expect('dbus-signal', signal='ContentAdded') self.store_content(content_path, initial=False, incoming=False) md = self.jt2.get_call_video_md_dbus() self.check_and_accept_offer(self.video_content, md) candidates = self.jt2.get_call_remote_transports_dbus() self.video_stream.AddCandidates(candidates, dbus_interface=cs.CALL_STREAM_IFACE_MEDIA) e = self.q.expect('stream-iq', predicate=self.jp.action_predicate('content-add')) c = e.query.firstChildElement() assertEquals('initiator', c['creator']) endpoints = self.video_stream.Get(cs.CALL_STREAM_IFACE_MEDIA, "Endpoints", dbus_interface=dbus.PROPERTIES_IFACE) assertLength(1, endpoints) endpoint = self.bus.get_object(self.conn.bus_name, endpoints[0]) self.enable_endpoint(endpoint) # Now, the call draws to a close. # We first remove the original stream self.audio_content.Remove() self.initial_audio = False self.audio_content = None self.audio_content_name = None self.audio_stream = None e = self.q.expect('stream-iq', predicate=self.jp.action_predicate('content-remove')) content_remove_ack = make_result_iq(self.stream, e.stanza) if peer_removes_final_content: # The peer removes the final countdo content. From a footnote (!) in # XEP 0166: # If the content-remove results in zero content definitions for the # session, the entity that receives the content-remove SHOULD send # a session-terminate action to the other party (since a session # with no content definitions is void). # So, Gabble should respond to the content-remove with a # session-terminate. node = self.jp.SetIq(self.jt2.peer, self.jt2.jid, [ self.jp.Jingle(self.jt2.sid, self.jt2.peer, 'content-remove', [ self.jp.Content(c['name'], c['creator'], c['senders']) ]) ]) self.stream.send(self.jp.xml(node)) else: # The Telepathy client removes the second stream; Gabble should # terminate the session rather than sending a content-remove. self.video_content.Remove() self.initial_video = False self.video_content = None self.video_content_name = None self.video_stream = None st, ended = self.q.expect_many( EventPattern('stream-iq', predicate=self.jp.action_predicate('session-terminate')), # Gabble shouldn't wait for the peer to ack the terminate before # considering the call finished. EventPattern('dbus-signal', signal='CallStateChanged')) assertEquals(ended.args[0], cs.CALL_STATE_ENDED) # Only now does the peer ack the content-remove. This serves as a # regression test for contents outliving the session; if the content did # did't die properly, this crashed Gabble. self.stream.send(content_remove_ack) sync_stream(self.q, self.stream) # The peer can ack the terminate too, just for completeness. self.stream.send(make_result_iq(self.stream, st.stanza)) def hangup(self): pass if __name__ == '__main__': dialects = [JingleProtocol015, JingleProtocol031] test_dialects( partial(run_call_test, klass=CallContentAddingRemovalTest, incoming=False, params={'peer-removes-final-content': True}), dialects) test_dialects( partial(run_call_test, klass=CallContentAddingRemovalTest, incoming=False, params={'peer-removes-final-content': False}), dialects) telepathy-gabble-0.18.2/tests/twisted/jingle/call-codecoffer.py0000644000175000017500000002437212312320035024473 0ustar00smcvsmcv00000000000000""" Testing different methods related to the CodecOffer interface. """ import dbus from dbus.exceptions import DBusException from servicetest import (EventPattern, assertEquals, assertLength, assertNotEquals ) import ns import constants as cs from jingletest2 import JingleTest2, test_dialects, JingleProtocol031 from config import CHANNEL_TYPE_CALL_ENABLED, VOIP_ENABLED if not CHANNEL_TYPE_CALL_ENABLED: print "NOTE: built with --disable-channel-type-call" raise SystemExit(77) if not VOIP_ENABLED: print "NOTE: built with --disable-voip" raise SystemExit(77) def check_offer (bus, conn, content): [path, md] = content.Get(cs.CALL_CONTENT_IFACE_MEDIA, "MediaDescriptionOffer", dbus_interface=dbus.PROPERTIES_IFACE) assertNotEquals ("/", path) offer = bus.get_object (conn.bus_name, path) md_property = offer.Get (cs.CALL_CONTENT_MEDIADESCRIPTION, "Codecs", dbus_interface=dbus.PROPERTIES_IFACE) assertEquals (md[cs.CALL_CONTENT_MEDIADESCRIPTION + ".Codecs"], md_property) def accept_offer (q, bus, conn, self_handle, remote_handle, content, md_props, offer_path = None, codecs_changed = True): [path, _] = content.Get (cs.CALL_CONTENT_IFACE_MEDIA, "MediaDescriptionOffer", dbus_interface=dbus.PROPERTIES_IFACE) offer = bus.get_object (conn.bus_name, path) offer.Accept (md_props, dbus_interface=cs.CALL_CONTENT_MEDIADESCRIPTION) current_mds = content.Get (cs.CALL_CONTENT_IFACE_MEDIA, "RemoteMediaDescriptions", dbus_interface=dbus.PROPERTIES_IFACE) assertEquals (md_props[cs.CALL_CONTENT_MEDIADESCRIPTION + '.Codecs'], current_mds[remote_handle][cs.CALL_CONTENT_MEDIADESCRIPTION + '.Codecs']) if codecs_changed: o = q.expect ('dbus-signal', signal='RemoteMediaDescriptionsChanged') assertEquals (md_props[cs.CALL_CONTENT_MEDIADESCRIPTION + '.Codecs'], o.args[0][remote_handle][cs.CALL_CONTENT_MEDIADESCRIPTION + '.Codecs']) def reject_offer (q, bus, conn, content, codecs, offer_path = None): [path, _] = content.Get(cs.CALL_CONTENT_IFACE_MEDIA, "MediaDescriptionOffer", dbus_interface=dbus.PROPERTIES_IFACE) offer = bus.get_object (conn.bus_name, path) offer.Reject ((0, 0, "", ""), dbus_interface=cs.CALL_CONTENT_MEDIADESCRIPTION) def update_codecs(jt2): contents = jt2.generate_contents() node = jt2.jp.SetIq(jt2.peer, jt2.jid, [ jt2.jp.Jingle(jt2.sid, jt2.peer, 'description-info', contents), ]) jt2.stream.send(jt2.jp.xml(node)) def prepare_test(jp, q, bus, conn, stream): remote_jid = 'foo@bar.com/Foo' jt2 = JingleTest2(jp, conn, q, stream, 'test@localhost', remote_jid) jt2.prepare() self_handle = conn.GetSelfHandle() remote_handle = conn.RequestHandles(1, ["foo@bar.com/Foo"])[0] # Advertise that we can do new style calls conn.ContactCapabilities.UpdateCapabilities([ (cs.CLIENT + ".CallHandler", [ { cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_CALL, cs.CALL_INITIAL_AUDIO: True}, { cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_CALL, cs.CALL_INITIAL_VIDEO: True}, ], [ cs.CHANNEL_TYPE_CALL + '/gtalk-p2p', cs.CHANNEL_TYPE_CALL + '/ice', cs.CHANNEL_TYPE_CALL + '/video/h264', ]), ]) return remote_jid, jt2, self_handle, remote_handle def try_to_access_old_offer(conn, path): try: offer = bus.get_object (conn.bus_name, path) ret = offer.GetAll (cs.CALL_CONTENT_MEDIADESCRIPTION, dbus_interface=dbus.PROPERTIES_IFACE) except Exception, e: pass else: assert False, 'Offer still exists' def test_incoming(jp, q, bus, conn, stream): remote_jid, jt2, self_handle, remote_handle = prepare_test(jp, q, bus, conn, stream) jt2.incoming_call() ret = q.expect_many(EventPattern('dbus-signal', signal='NewChannels', predicate=lambda e: cs.CHANNEL_TYPE_CALL in e.args[0][0][1].values()), EventPattern('dbus-signal', signal='NewMediaDescriptionOffer')) chan = bus.get_object(conn.bus_name, ret[0].args[0][0][0]) properties = chan.GetAll(cs.CHANNEL_TYPE_CALL, dbus_interface=dbus.PROPERTIES_IFACE) content = bus.get_object (conn.bus_name, properties["Contents"][0]) md = jt2.get_call_audio_md_dbus() check_offer(bus, conn, content) accept_offer(q, bus, conn, self_handle, remote_handle, content, md) update_codecs(jt2) signal = q.expect('dbus-signal', signal='NewMediaDescriptionOffer') check_offer(bus, conn, content) reject_offer(q, bus, conn, content, md) update_codecs(jt2) signal = q.expect('dbus-signal', signal='NewMediaDescriptionOffer') check_offer(bus, conn, content) accept_offer(q, bus, conn, self_handle, remote_handle, content, md, codecs_changed = False) update_codecs(jt2) signal = q.expect('dbus-signal', signal='NewMediaDescriptionOffer') check_offer(bus, conn, content) [path, _] = content.Get (cs.CALL_CONTENT_IFACE_MEDIA, "MediaDescriptionOffer", dbus_interface=dbus.PROPERTIES_IFACE) chan.Close(dbus_interface=cs.CHANNEL) signal = q.expect('dbus-signal', signal='ChannelClosed') try_to_access_old_offer(conn, path) try: ret = conn.GetAll (cs.CONN, dbus_interface=dbus.PROPERTIES_IFACE) except Exception, e: print 'Gabble probably crashed' raise e else: # depending on the age of our telepathy-glib, we have at least # SelfHandle, and might also have Interfaces and Status assert len(ret) > 0 def test_outgoing(jp, q, bus, conn, stream): remote_jid, jt2, self_handle, remote_handle = prepare_test(jp, q, bus, conn, stream) # make a new outgoing call conn.CreateChannel({ cs.TARGET_HANDLE_TYPE: cs.HT_CONTACT, cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_CALL, cs.TARGET_HANDLE: remote_handle, cs.CALL_INITIAL_AUDIO: True, cs.CALL_INITIAL_VIDEO: False }, dbus_interface=cs.CONN_IFACE_REQUESTS) ret = q.expect_many(EventPattern('dbus-signal', signal='NewChannels', predicate=lambda e: cs.CHANNEL_TYPE_CALL in e.args[0][0][1].values()), # a codec offer appears already! EventPattern('dbus-signal', signal='NewMediaDescriptionOffer')) # all the basic stuff is already tested in call-basics.py chan = bus.get_object(conn.bus_name, ret[0].args[0][0][0]) # there is no remote codec information, so this should be empty assertEquals(ret[1].args[1][cs.CALL_CONTENT_MEDIADESCRIPTION + ".Codecs"], []) # get a list of audio codecs we can support md = jt2.get_call_audio_md_dbus(remote_handle) # make sure UpdateCodecs fails props = chan.GetAll(cs.CHANNEL_TYPE_CALL, dbus_interface=dbus.PROPERTIES_IFACE) content = bus.get_object(conn.bus_name, props["Contents"][0]) try: content.UpdateLocalMediaDescription(md, dbus_interface=cs.CALL_CONTENT_IFACE_MEDIA) except DBusException, e: # this should fail now that there is a codec offer around if e.get_dbus_name() != cs.NOT_AVAILABLE: raise e else: assert false # we'll need these later content_props = content.GetAll(cs.CALL_CONTENT, dbus_interface=dbus.PROPERTIES_IFACE) # make an offer they can't refuse offer = bus.get_object(conn.bus_name, ret[1].args[0]) props = offer.GetAll(cs.CALL_CONTENT_MEDIADESCRIPTION, dbus_interface=dbus.PROPERTIES_IFACE) # this also needs to be empty assertEquals(props["Codecs"], []) offer.Accept(md, dbus_interface=cs.CALL_CONTENT_MEDIADESCRIPTION) o = q.expect('dbus-signal', signal='RemoteMediaDescriptionsChanged') chan.Accept(dbus_interface=cs.CHANNEL_TYPE_CALL) cstream = bus.get_object(conn.bus_name, content_props["Streams"][0]) recv_state = cstream.Get(cs.CALL_STREAM_IFACE_MEDIA, "ReceivingState", dbus_interface=dbus.PROPERTIES_IFACE) assertEquals (cs.CALL_STREAM_FLOW_STATE_PENDING_START, recv_state) cstream.CompleteReceivingStateChange( cs.CALL_STREAM_FLOW_STATE_STARTED, dbus_interface = cs.CALL_STREAM_IFACE_MEDIA) # add candidates candidates = jt2.get_call_remote_transports_dbus () cstream.AddCandidates (candidates, dbus_interface=cs.CALL_STREAM_IFACE_MEDIA) ret = q.expect_many(EventPattern('dbus-signal', signal='LocalCandidatesAdded'), EventPattern('stream-iq', predicate=jp.action_predicate('session-initiate'))) assertEquals (candidates, ret[0].args[0]) jt2.parse_session_initiate(ret[1].query) cstream.FinishInitialCandidates (dbus_interface=cs.CALL_STREAM_IFACE_MEDIA) local_candidates = cstream.Get(cs.CALL_STREAM_IFACE_MEDIA, "LocalCandidates", dbus_interface=dbus.PROPERTIES_IFACE) assertEquals (candidates, local_candidates) endpoints = cstream.Get(cs.CALL_STREAM_IFACE_MEDIA, "Endpoints", dbus_interface=dbus.PROPERTIES_IFACE) assertLength (1, endpoints) # accept with a subset of the codecs old_codecs = jt2.audio_codecs jt2.audio_codecs = jt2.audio_codecs[:-1] # all but the last one # session-accept jt2.accept() ret = q.expect('dbus-signal', signal='NewMediaDescriptionOffer') # make sure the codec offer has the updated codecs assertEquals(ret.args[1][cs.CALL_CONTENT_MEDIADESCRIPTION + ".Codecs"], md[cs.CALL_CONTENT_MEDIADESCRIPTION + ".Codecs"][:-1]) # accept new offer offer = bus.get_object(conn.bus_name, ret.args[0]) md[cs.CALL_CONTENT_MEDIADESCRIPTION + ".Codecs"].pop() offer.Accept(md, dbus_interface=cs.CALL_CONTENT_MEDIADESCRIPTION) # now we should both have the smaller set of codecs, easy o = q.expect ('dbus-signal', signal='RemoteMediaDescriptionsChanged') assertEquals (md[cs.CALL_CONTENT_MEDIADESCRIPTION + ".Codecs"], o.args[0][remote_handle][cs.CALL_CONTENT_MEDIADESCRIPTION + ".Codecs"]) chan.Close(dbus_interface=cs.CHANNEL) signal = q.expect('dbus-signal', signal='ChannelClosed') try_to_access_old_offer(conn, ret.args[0]) if __name__ == '__main__': test_dialects(test_incoming, [JingleProtocol031]) test_dialects(test_outgoing, [JingleProtocol031]) telepathy-gabble-0.18.2/tests/twisted/jingle/call-basics.py0000644000175000017500000003730112227000321023632 0ustar00smcvsmcv00000000000000""" Test basic outgoing and incoming call handling """ import config if not config.CHANNEL_TYPE_CALL_ENABLED: print "NOTE: built with --disable-channel-type-call" raise SystemExit(77) import dbus from dbus.exceptions import DBusException from functools import partial from servicetest import EventPattern, assertEquals, assertContains, assertLength from call_helper import CallTest, run_call_test from jingletest2 import test_all_dialects import constants as cs import ns from config import VOIP_ENABLED if not VOIP_ENABLED: print "NOTE: built with --disable-voip" raise SystemExit(77) class CallBasicsTest(CallTest): def test_connect_disconnect_endpoint(self): endpoints = self.audio_stream.Get(cs.CALL_STREAM_IFACE_MEDIA, "Endpoints", dbus_interface=dbus.PROPERTIES_IFACE) assertLength(1, endpoints) endpoint = self.bus.get_object(self.conn.bus_name, endpoints[0]) # Lets try disconnecting one endpoint.SetEndpointState(1, cs.CALL_STREAM_ENDPOINT_STATE_CONNECTING, dbus_interface=cs.CALL_STREAM_ENDPOINT) ret = self.q.expect_many( EventPattern('dbus-signal', signal='EndpointStateChanged'), EventPattern('dbus-signal', signal='CallStateChanged')) assertEquals(1, ret[0].args[0]) assertEquals(cs.CALL_STREAM_ENDPOINT_STATE_CONNECTING, ret[0].args[1]) assertEquals(cs.CALL_STATE_ACCEPTED, ret[1].args[0]) state = endpoint.Get(cs.CALL_STREAM_ENDPOINT, "EndpointState", dbus_interface=dbus.PROPERTIES_IFACE) assertEquals(cs.CALL_STREAM_ENDPOINT_STATE_CONNECTING, state[1]) # And reconnecting it endpoint.SetEndpointState(1, cs.CALL_STREAM_ENDPOINT_STATE_FULLY_CONNECTED, dbus_interface=cs.CALL_STREAM_ENDPOINT) ret = self.q.expect_many( EventPattern('dbus-signal', signal='EndpointStateChanged'), EventPattern('dbus-signal', signal='CallStateChanged')) assertEquals(1,ret[0].args[0]) assertEquals(cs.CALL_STREAM_ENDPOINT_STATE_FULLY_CONNECTED, ret[0].args[1]) assertEquals(cs.CALL_STATE_ACTIVE, ret[1].args[0]) state = endpoint.Get(cs.CALL_STREAM_ENDPOINT, "EndpointState", dbus_interface=dbus.PROPERTIES_IFACE) assertEquals(cs.CALL_STREAM_ENDPOINT_STATE_FULLY_CONNECTED, state[1]) # All Direction should still be both now stream_props = self.audio_stream.GetAll(cs.CALL_STREAM, dbus_interface = dbus.PROPERTIES_IFACE) assertEquals({self.peer_handle: cs.CALL_SENDING_STATE_SENDING}, stream_props["RemoteMembers"]) assertEquals(cs.CALL_SENDING_STATE_SENDING, stream_props["LocalSendingState"]) def test_content_addition(self): path = self.chan.AddContent("Webcam", cs.CALL_MEDIA_TYPE_VIDEO, cs.MEDIA_STREAM_DIRECTION_BIDIRECTIONAL, dbus_interface=cs.CHANNEL_TYPE_CALL) content = self.bus.get_object(self.conn.bus_name, path) content_properties = content.GetAll(cs.CALL_CONTENT, dbus_interface=dbus.PROPERTIES_IFACE) assertEquals(cs.CALL_DISPOSITION_NONE, content_properties["Disposition"]) #assertEquals(self_handle, content_properties["Creator"]) assertContains("Webcam", content_properties["Name"]) md = self.jt2.get_call_video_md_dbus() self.check_and_accept_offer(content, md) cstream = self.bus.get_object(self.conn.bus_name, content_properties["Streams"][0]) candidates = self.jt2.get_call_remote_transports_dbus() cstream.AddCandidates(candidates, dbus_interface=cs.CALL_STREAM_IFACE_MEDIA) self.q.expect('stream-iq', predicate=self.jp.action_predicate('content-add')) content.Remove(dbus_interface=cs.CALL_CONTENT) self.q.expect('stream-iq', predicate=self.jp.action_predicate('content-remove')) def pickup(self): jt2 = self.jt2 jp = self.jp q = self.q cstream = self.audio_stream remote_handle = self.peer_handle can_change_direction = self.can_change_direction incoming = self.incoming # We pickup the call as we need active state to run this test CallTest.pickup(self) self.test_connect_disconnect_endpoint() # FIXME: This should eventually be break down in smaller test methods # Turn sending off and on again # but first, let's try direction changes requested by the other side if can_change_direction: content_name = jt2.audio_names[0] if incoming: jt2.content_modify(content_name, "initiator", "initiator") else: jt2.content_modify(content_name, "initiator", "responder") o = q.expect('dbus-signal', signal='LocalSendingStateChanged') assertEquals(cs.CALL_SENDING_STATE_PENDING_STOP_SENDING, o.args[0]) cstream.SetSending(False, dbus_interface = cs.CALL_STREAM) o = q.expect('dbus-signal', signal='SendingStateChanged') assertEquals(cs.CALL_STREAM_FLOW_STATE_PENDING_STOP, o.args[0]) cstream.CompleteSendingStateChange( cs.CALL_STREAM_FLOW_STATE_STOPPED, dbus_interface = cs.CALL_STREAM_IFACE_MEDIA) o = q.expect('dbus-signal', signal='SendingStateChanged', interface = cs.CALL_STREAM_IFACE_MEDIA) assertEquals(cs.CALL_STREAM_FLOW_STATE_STOPPED, o.args[0]) jt2.content_modify(content_name, "initiator", "both") o = q.expect('dbus-signal', signal='LocalSendingStateChanged') assertEquals(cs.CALL_SENDING_STATE_PENDING_SEND, o.args[0]) cstream.SetSending(True, dbus_interface = cs.CALL_STREAM) ret = q.expect_many( EventPattern('dbus-signal', signal='SendingStateChanged'), EventPattern('dbus-signal', signal='LocalSendingStateChanged')) assertEquals(cs.CALL_STREAM_FLOW_STATE_PENDING_START, ret[0].args[0]) assertEquals(cs.CALL_SENDING_STATE_SENDING, ret[1].args[0]) cstream.CompleteSendingStateChange( cs.CALL_STREAM_FLOW_STATE_STARTED, dbus_interface = cs.CALL_STREAM_IFACE_MEDIA) o = q.expect('dbus-signal', signal='SendingStateChanged', interface = cs.CALL_STREAM_IFACE_MEDIA) assertEquals(cs.CALL_STREAM_FLOW_STATE_STARTED, o.args[0]) stream_props = cstream.GetAll(cs.CALL_STREAM, dbus_interface = dbus.PROPERTIES_IFACE) assertEquals({remote_handle: cs.CALL_SENDING_STATE_SENDING}, stream_props["RemoteMembers"]) assertEquals(cs.CALL_SENDING_STATE_SENDING, stream_props["LocalSendingState"]) cstream.SetSending(False, dbus_interface = cs.CALL_STREAM) ret = q.expect_many( EventPattern('dbus-signal', signal='SendingStateChanged'), EventPattern('dbus-signal', signal='LocalSendingStateChanged')) assertEquals(cs.CALL_STREAM_FLOW_STATE_PENDING_STOP, ret[0].args[0]) assertEquals(cs.CALL_SENDING_STATE_NONE, ret[1].args[0]) cstream.CompleteSendingStateChange( cs.CALL_STREAM_FLOW_STATE_STOPPED, dbus_interface = cs.CALL_STREAM_IFACE_MEDIA) o = q.expect('dbus-signal', signal='SendingStateChanged', interface = cs.CALL_STREAM_IFACE_MEDIA) assertEquals(cs.CALL_STREAM_FLOW_STATE_STOPPED, o.args[0]) stream_props = cstream.GetAll(cs.CALL_STREAM, dbus_interface = dbus.PROPERTIES_IFACE) assertEquals({remote_handle: cs.CALL_SENDING_STATE_SENDING}, stream_props["RemoteMembers"]) assertEquals(cs.CALL_SENDING_STATE_NONE, stream_props["LocalSendingState"]) # If possible, test the other side asking us to start then stop sending if can_change_direction: jt2.content_modify(content_name, "initiator", "both") o = q.expect('dbus-signal', signal='LocalSendingStateChanged') assertEquals(cs.CALL_SENDING_STATE_PENDING_SEND, o.args[0]) if incoming: jt2.content_modify(content_name, "initiator", "initiator") else: jt2.content_modify(content_name, "initiator", "responder") o = q.expect('dbus-signal', signal='LocalSendingStateChanged') assertEquals(cs.CALL_SENDING_STATE_NONE, o.args[0]) jt2.content_modify(content_name, "initiator", "both") o = q.expect('dbus-signal', signal='LocalSendingStateChanged') assertEquals(cs.CALL_SENDING_STATE_PENDING_SEND, o.args[0]) cstream.SetSending(True, dbus_interface = cs.CALL_STREAM) ret = q.expect_many( EventPattern('dbus-signal', signal='SendingStateChanged'), EventPattern('dbus-signal', signal='LocalSendingStateChanged')) assertEquals(cs.CALL_STREAM_FLOW_STATE_PENDING_START, ret[0].args[0]) assertEquals(cs.CALL_SENDING_STATE_SENDING, ret[1].args[0]) cstream.CompleteSendingStateChange( cs.CALL_STREAM_FLOW_STATE_STARTED, dbus_interface = cs.CALL_STREAM_IFACE_MEDIA) stream_props = cstream.GetAll(cs.CALL_STREAM, dbus_interface = dbus.PROPERTIES_IFACE) assertEquals({remote_handle: cs.CALL_SENDING_STATE_SENDING}, stream_props["RemoteMembers"]) assertEquals(cs.CALL_SENDING_STATE_SENDING, stream_props["LocalSendingState"]) # Turn receiving off and on again try: cstream.RequestReceiving(remote_handle, False, dbus_interface = cs.CALL_STREAM) assert can_change_direction except dbus.DBusException, e: assertEquals(cs.NOT_CAPABLE, e.get_dbus_name()) assert not can_change_direction if can_change_direction: ret = q.expect_many( EventPattern('dbus-signal', signal='ReceivingStateChanged'), EventPattern('dbus-signal', signal='RemoteMembersChanged'), EventPattern('stream-iq', predicate=jp.action_predicate('content-modify'))) assertEquals(cs.CALL_STREAM_FLOW_STATE_PENDING_STOP, ret[0].args[0]) assert ret[1].args[0].has_key(remote_handle) cstream.CompleteReceivingStateChange( cs.CALL_STREAM_FLOW_STATE_STOPPED, dbus_interface = cs.CALL_STREAM_IFACE_MEDIA) o = q.expect('dbus-signal', signal='ReceivingStateChanged', interface = cs.CALL_STREAM_IFACE_MEDIA) assertEquals(cs.CALL_STREAM_FLOW_STATE_STOPPED, o.args[0]) stream_props = cstream.GetAll(cs.CALL_STREAM, dbus_interface = dbus.PROPERTIES_IFACE) assertEquals({remote_handle: cs.CALL_SENDING_STATE_NONE}, stream_props["RemoteMembers"]) assertEquals(cs.CALL_SENDING_STATE_SENDING, stream_props["LocalSendingState"]) else: stream_props = cstream.GetAll(cs.CALL_STREAM, dbus_interface = dbus.PROPERTIES_IFACE) assertEquals({remote_handle: cs.CALL_SENDING_STATE_SENDING}, stream_props["RemoteMembers"]) assertEquals(cs.CALL_SENDING_STATE_SENDING, stream_props["LocalSendingState"]) try: cstream.RequestReceiving(remote_handle, True, dbus_interface = cs.CALL_STREAM) assert can_change_direction except dbus.DBusException, e: assertEquals(cs.NOT_CAPABLE, e.get_dbus_name()) assert not can_change_direction if can_change_direction: ret = q.expect_many( EventPattern('dbus-signal', signal='ReceivingStateChanged'), EventPattern('dbus-signal', signal='RemoteMembersChanged'), EventPattern('stream-iq', predicate=jp.action_predicate('content-modify'))) assertEquals(cs.CALL_STREAM_FLOW_STATE_PENDING_START, ret[0].args[0]) assert ret[1].args[0].has_key(remote_handle) assertEquals(cs.CALL_SENDING_STATE_PENDING_SEND, ret[1].args[0][remote_handle]) cstream.CompleteReceivingStateChange( cs.CALL_STREAM_FLOW_STATE_STARTED, dbus_interface = cs.CALL_STREAM_IFACE_MEDIA) ret = q.expect_many( EventPattern('dbus-signal', signal='ReceivingStateChanged'), EventPattern('dbus-signal', signal='RemoteMembersChanged')) assertEquals(cs.CALL_STREAM_FLOW_STATE_STARTED, ret[0].args[0]) assert ret[1].args[0].has_key(remote_handle) assertEquals(cs.CALL_SENDING_STATE_SENDING, ret[1].args[0][remote_handle]) stream_props = cstream.GetAll(cs.CALL_STREAM, dbus_interface = dbus.PROPERTIES_IFACE) assertEquals({remote_handle: cs.CALL_SENDING_STATE_SENDING}, stream_props["RemoteMembers"]) assertEquals(cs.CALL_SENDING_STATE_SENDING, stream_props["LocalSendingState"]) if can_change_direction: if incoming: jt2.content_modify(content_name, "initiator", "responder") else: jt2.content_modify(content_name, "initiator", "initiator") ret = q.expect_many( EventPattern('dbus-signal', signal='ReceivingStateChanged'), EventPattern('dbus-signal', signal='RemoteMembersChanged')) assertEquals(cs.CALL_STREAM_FLOW_STATE_PENDING_STOP, ret[0].args[0]) assert ret[1].args[0].has_key(remote_handle) assertEquals(cs.CALL_SENDING_STATE_NONE, ret[1].args[0][remote_handle]) cstream.CompleteReceivingStateChange( cs.CALL_STREAM_FLOW_STATE_STOPPED, dbus_interface = cs.CALL_STREAM_IFACE_MEDIA) o = q.expect('dbus-signal', signal='ReceivingStateChanged') assertEquals(cs.CALL_STREAM_FLOW_STATE_STOPPED, o.args[0]) jt2.content_modify(content_name, "initiator", "both") ret = q.expect_many( EventPattern('dbus-signal', signal='ReceivingStateChanged'), EventPattern('dbus-signal', signal='RemoteMembersChanged')) assertEquals(cs.CALL_STREAM_FLOW_STATE_PENDING_START, ret[0].args[0]) assert ret[1].args[0].has_key(remote_handle) assertEquals(cs.CALL_SENDING_STATE_SENDING, ret[1].args[0][remote_handle]) cstream.CompleteReceivingStateChange( cs.CALL_STREAM_FLOW_STATE_STARTED, dbus_interface = cs.CALL_STREAM_IFACE_MEDIA) o = q.expect('dbus-signal', signal='ReceivingStateChanged') assertEquals(cs.CALL_STREAM_FLOW_STATE_STARTED, o.args[0]) try: self.test_content_addition() except DBusException, e: assertEquals(cs.NOT_AVAILABLE, e.get_dbus_name()) assert not jp.can_do_video() if __name__ == '__main__': test_all_dialects( partial(run_call_test, klass=CallBasicsTest, incoming=True)) test_all_dialects( partial(run_call_test, klass=CallBasicsTest, incoming=False)) telepathy-gabble-0.18.2/tests/twisted/jingle/accept-extra-stream.py0000644000175000017500000002010712227000321025322 0ustar00smcvsmcv00000000000000""" Test that we can accept streams added after the call has been accepted. """ from servicetest import ( make_channel_proxy, EventPattern, sync_dbus, call_async, assertEquals, ) from gabbletest import exec_test, make_result_iq, sync_stream import constants as cs from jingletest2 import JingleProtocol031, JingleTest2 from config import VOIP_ENABLED if not VOIP_ENABLED: print "NOTE: built with --disable-voip" raise SystemExit(77) def test(q, bus, conn, stream): worker(q, bus, conn, stream, remote_jid='foo@bar.com/Foo') def test_bare_jid(q, bus, conn, stream): worker(q, bus, conn, stream, remote_jid='foo@sip.bar.com') def worker(q, bus, conn, stream, remote_jid): jp = JingleProtocol031() jt2 = JingleTest2(jp, conn, q, stream, 'test@localhost', remote_jid) jt2.prepare() self_handle = conn.GetSelfHandle() remote_handle = conn.RequestHandles(cs.HT_CONTACT, [jt2.peer])[0] # Remote end calls us node = jp.SetIq(jt2.peer, jt2.jid, [ jp.Jingle(jt2.sid, jt2.peer, 'session-initiate', [ jp.Content('audiostream', 'initiator', 'both', jp.Description('audio', [ jp.PayloadType(name, str(rate), str(id), parameters) for (name, id, rate, parameters) in jt2.audio_codecs ]), jp.TransportGoogleP2P()) ]) ]) stream.send(jp.xml(node)) nc = q.expect('dbus-signal', signal='NewChannel', predicate=lambda e: cs.CHANNEL_TYPE_CONTACT_LIST not in e.args) path, ct, ht, h, sh = nc.args assert ct == cs.CHANNEL_TYPE_STREAMED_MEDIA, ct assert ht == cs.HT_CONTACT, ht assert h == remote_handle, h group = make_channel_proxy(conn, path, 'Channel.Interface.Group') sm = make_channel_proxy(conn, path, 'Channel.Type.StreamedMedia') ms = make_channel_proxy(conn, path, 'Channel.Interface.MediaSignalling') streams = sm.ListStreams() assert len(streams) == 1, streams audio_stream_id, h, media_type, state, direction, pending = streams[0] assert h == remote_handle, (h, remote_handle) assert media_type == cs.MEDIA_STREAM_TYPE_AUDIO, media_type assert state == cs.MEDIA_STREAM_STATE_DISCONNECTED, state # FIXME: This turns out to be Bidirectional; wjt thinks this sounds wrong # since the stream is (we hope) pending local send. #assert direction == cs.MEDIA_STREAM_DIRECTION_RECEIVE, direction assert pending == cs.MEDIA_STREAM_PENDING_LOCAL_SEND, pending session_handlers = ms.GetSessionHandlers() assert len(session_handlers) == 1, session_handlers session_handler = make_channel_proxy(conn, session_handlers[0][0], 'Media.SessionHandler') session_handler.Ready() nsh = q.expect('dbus-signal', signal='NewStreamHandler') stream_handler_path, stream_id, media_type, direction = nsh.args assert stream_id == audio_stream_id, (stream_id, audio_stream_id) assert media_type == cs.MEDIA_STREAM_TYPE_AUDIO, media_type # FIXME: As above #assert direction == cs.MEDIA_STREAM_DIRECTION_RECEIVE, direction stream_handler = make_channel_proxy(conn, stream_handler_path, 'Media.StreamHandler') stream_handler.NewNativeCandidate("fake", jt2.get_remote_transports_dbus()) stream_handler.Ready(jt2.get_audio_codecs_dbus()) stream_handler.StreamState(cs.MEDIA_STREAM_STATE_CONNECTED) stream_handler.SupportedCodecs(jt2.get_audio_codecs_dbus()) # peer gets the transport e = q.expect('stream-iq', predicate=jp.action_predicate('transport-info')) assertEquals(jt2.peer, e.query['initiator']) stream.send(make_result_iq(stream, e.stanza)) # Make sure all the above's happened. sync_stream(q, stream) sync_dbus(bus, q, conn) # At last, accept the call group.AddMembers([self_handle], 'accepted') # Call is accepted, we become a member, and the stream that was pending # local send is now sending. memb, acc, _, _, _, = q.expect_many( EventPattern('dbus-signal', signal='MembersChanged', args=[u'', [self_handle], [], [], [], self_handle, cs.GC_REASON_NONE]), EventPattern('stream-iq', predicate=lambda e: (e.query.name == 'jingle' and e.query['action'] == 'session-accept')), EventPattern('dbus-signal', signal='SetStreamSending', args=[True]), EventPattern('dbus-signal', signal='SetStreamPlaying', args=[True]), EventPattern('dbus-signal', signal='StreamDirectionChanged', args=[audio_stream_id, cs.MEDIA_STREAM_DIRECTION_BIDIRECTIONAL, 0]), ) # Respond to session-accept # FIXME: wjt thinks Gabble should accept the content-add without this. stream.send(jp.xml(jp.ResultIq('test@localhost', acc.stanza, []))) # Foo would like to gaze upon our beautiful complexion node = jp.SetIq(jt2.peer, jt2.jid, [ jp.Jingle(jt2.sid, jt2.peer, 'content-add', [ jp.Content('videostream', 'initiator', 'both', jp.Description('video', [ jp.PayloadType(name, str(rate), str(id), parameters) for (name, id, rate, parameters) in jt2.video_codecs ]), jp.TransportGoogleP2P()) ]) ]) stream.send(jp.xml(node)) added, nsh = q.expect_many( EventPattern('dbus-signal', signal='StreamAdded'), EventPattern('dbus-signal', signal='NewStreamHandler'), ) video_stream_id, h, type = added.args assert h == remote_handle, (h, remote_handle) assert type == cs.MEDIA_STREAM_TYPE_VIDEO, type stream_handler_path, stream_id, media_type, direction = nsh.args assert stream_id == video_stream_id, (stream_id, video_stream_id) assert media_type == cs.MEDIA_STREAM_TYPE_VIDEO, type # FIXME: As above #assert direction == cs.MEDIA_STREAM_DIRECTION_RECEIVE, direction video_handler = make_channel_proxy(conn, stream_handler_path, 'Media.StreamHandler') video_handler.NewNativeCandidate("fake", jt2.get_remote_transports_dbus()) video_handler.Ready(jt2.get_video_codecs_dbus()) video_handler.StreamState(cs.MEDIA_STREAM_STATE_CONNECTED) video_handler.SupportedCodecs(jt2.get_video_codecs_dbus()) ti, _, _, _ = q.expect_many( # Peer gets the transport EventPattern('stream-iq', predicate=jp.action_predicate('transport-info')), # Gabble tells the peer we accepted EventPattern('stream-iq', predicate=jp.action_predicate('content-accept')), EventPattern('dbus-signal', signal='SetStreamPlaying', args=[True]), # It's not entirely clear that this *needs* to fire here... EventPattern('dbus-signal', signal='SetStreamSending', args=[False]), ) assertEquals(jt2.peer, ti.query['initiator']) stream.send(make_result_iq(stream, e.stanza)) # Okay, so now the stream's playing but not sending, and we should be still # pending local send: streams = sm.ListStreams() assert len(streams) == 2, streams video_streams = [s for s in streams if s[2] == cs.MEDIA_STREAM_TYPE_VIDEO] assert len(video_streams) == 1, streams stream_id, h, _, state, direction, pending = video_streams[0] assert stream_id == video_stream_id, (stream_id, video_stream_id) assert h == remote_handle, (h, remote_handle) assert state == cs.MEDIA_STREAM_STATE_CONNECTED, state assert direction == cs.MEDIA_STREAM_DIRECTION_RECEIVE, direction assert pending == cs.MEDIA_STREAM_PENDING_LOCAL_SEND, pending # Let's accept the stream; the direction should change, and we should be # told to start sending. call_async(q, sm, 'RequestStreamDirection', video_stream_id, cs.MEDIA_STREAM_DIRECTION_BIDIRECTIONAL) # The stream's direction should change, and we should be told to start # playing. q.expect_many( EventPattern('dbus-signal', signal='StreamDirectionChanged', args=[video_stream_id, cs.MEDIA_STREAM_DIRECTION_BIDIRECTIONAL, 0]), EventPattern('dbus-signal', signal='SetStreamSending', args=[True]), ) # That'll do, pig. That'll do. if __name__ == '__main__': exec_test(test) exec_test(test_bare_jid) telepathy-gabble-0.18.2/tests/twisted/file-transfer/0000755000175000017500000000000012312537051022402 5ustar00smcvsmcv00000000000000telepathy-gabble-0.18.2/tests/twisted/file-transfer/file_transfer_helper.py0000644000175000017500000005576312227000321027144 0ustar00smcvsmcv00000000000000import dbus import socket import hashlib import time import datetime import os from servicetest import EventPattern, assertEquals, assertSameSets, call_async from gabbletest import exec_test, sync_stream, make_result_iq import ns from bytestream import create_from_si_offer, announce_socks5_proxy import bytestream from caps_helper import extract_data_forms, add_data_forms from twisted.words.xish import domish, xpath import constants as cs class File(object): DEFAULT_DATA = "What a nice file" DEFAULT_NAME = "The foo.txt" DEFAULT_CONTENT_TYPE = 'text/plain' DEFAULT_DESCRIPTION = "A nice file to test" def __init__(self, data=DEFAULT_DATA, name=DEFAULT_NAME, content_type=DEFAULT_CONTENT_TYPE, description=DEFAULT_DESCRIPTION, hash_type=cs.FILE_HASH_TYPE_MD5): self.data = data self.size = len(self.data) self.name = name self.content_type = content_type self.description = description self.date = int(time.time()) self.compute_hash(hash_type) self.offset = 0 self.uri = 'file:///tmp/%s' % self.name def compute_hash(self, hash_type): assert hash_type == cs.FILE_HASH_TYPE_MD5 self.hash_type = hash_type self.hash = hashlib.md5(self.data).hexdigest() class FileTransferTest(object): CONTACT_NAME = 'test-ft@localhost' CONTACT_FULL_JID = 'test-ft@localhost/Telepathy' service_name = 'a.wacky.service.name' metadata = {'loads': ['of', 'blahblah', 'stuff'], 'mental': ['data', 'sidf']} def __init__(self, bytestream_cls, file, address_type, access_control, access_control_param): self.file = file self.bytestream_cls = bytestream_cls self.address_type = address_type self.access_control = access_control self.access_control_param = access_control_param def check_platform_socket_types(self, sock_types): assertEquals(sock_types.get(cs.SOCKET_ADDRESS_TYPE_IPV4), [cs.SOCKET_ACCESS_CONTROL_LOCALHOST]) assertEquals(sock_types.get(cs.SOCKET_ADDRESS_TYPE_IPV6), [cs.SOCKET_ACCESS_CONTROL_LOCALHOST]) if os.name == 'posix': # true on at least Linux assertEquals(sock_types.get(cs.SOCKET_ADDRESS_TYPE_UNIX), [cs.SOCKET_ACCESS_CONTROL_LOCALHOST]) def connect(self): vcard_event, roster_event, disco_event = self.q.expect_many( EventPattern('stream-iq', to=None, query_ns='vcard-temp', query_name='vCard'), EventPattern('stream-iq', query_ns=ns.ROSTER), EventPattern('stream-iq', to='localhost', query_ns=ns.DISCO_ITEMS)) roster = make_result_iq(self.stream, roster_event.stanza) query = roster.firstChildElement() item = query.addElement('item') item['jid'] = self.CONTACT_FULL_JID item['subscription'] = 'both' self.stream.send(roster) announce_socks5_proxy(self.q, self.stream, disco_event.stanza) self.self_handle = self.conn.GetSelfHandle() self.self_handle_name = self.conn.InspectHandles(cs.HT_CONTACT, [self.self_handle])[0] def announce_contact(self, name=CONTACT_NAME, metadata=True): self.contact_name = name self.contact_full_jid = '%s/Telepathy' % name self.handle = self.conn.RequestHandles(cs.HT_CONTACT, [name])[0] presence = domish.Element(('jabber:client', 'presence')) presence['from'] = self.contact_full_jid presence['to'] = 'test@localhost/Resource' c = presence.addElement('c') c['xmlns'] = 'http://jabber.org/protocol/caps' c['node'] = 'http://example.com/ISupportFT' c['ver'] = '1.0' self.stream.send(presence) disco_event, presence_event = self.q.expect_many( EventPattern('stream-iq', iq_type='get', query_ns='http://jabber.org/protocol/disco#info', to=self.contact_full_jid), EventPattern('dbus-signal', signal='PresencesChanged', args=[ {self.handle: (cs.PRESENCE_AVAILABLE, u'available', u'')}])) assert disco_event.query['node'] == \ 'http://example.com/ISupportFT#1.0' result = make_result_iq(self.stream, disco_event.stanza) query = result.firstChildElement() feature = query.addElement('feature') feature['var'] = ns.FILE_TRANSFER if metadata: feature = query.addElement('feature') feature['var'] = ns.TP_FT_METADATA self.stream.send(result) sync_stream(self.q, self.stream) def create_ft_channel(self): ft_chan = self.bus.get_object(self.conn.object.bus_name, self.ft_path) self.channel = dbus.Interface(ft_chan, cs.CHANNEL) self.ft_channel = dbus.Interface(ft_chan, cs.CHANNEL_TYPE_FILE_TRANSFER) self.ft_props = dbus.Interface(ft_chan, cs.PROPERTIES_IFACE) def close_channel(self): self.channel.Close() self.q.expect('dbus-signal', signal='Closed') def done(self): pass def test(self, q, bus, conn, stream): self.q = q self.bus = bus self.conn = conn self.stream = stream for fct in self._actions: # stop if a function returns True if fct(): break def create_socket(self): if self.address_type == cs.SOCKET_ADDRESS_TYPE_UNIX: return socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) elif self.address_type == cs.SOCKET_ADDRESS_TYPE_IPV4: return socket.socket(socket.AF_INET, socket.SOCK_STREAM) elif self.address_type == cs.SOCKET_ADDRESS_TYPE_IPV6: return socket.socket(socket.AF_INET6, socket.SOCK_STREAM) else: assert False class ReceiveFileTest(FileTransferTest): def __init__(self, bytestream_cls, file, address_type, access_control, access_control_param): FileTransferTest.__init__(self, bytestream_cls, file, address_type, access_control, access_control_param) self._actions = [self.connect, self.announce_contact, self.send_ft_offer_iq, self.check_new_channel, self.create_ft_channel, self.set_uri, self.accept_file, self.receive_file, self.close_channel, self.done] def send_ft_offer_iq(self): self.bytestream = self.bytestream_cls(self.stream, self.q, 'alpha', self.contact_full_jid, 'test@localhost/Resource', True) iq, si = self.bytestream.create_si_offer(ns.FILE_TRANSFER) file_node = si.addElement((ns.FILE_TRANSFER,'file')) file_node['name'] = self.file.name file_node['size'] = str(self.file.size) file_node['mime-type'] = self.file.content_type file_node['hash'] = self.file.hash date = datetime.datetime.utcfromtimestamp(self.file.date).strftime('%FT%H:%M:%SZ') file_node['date'] = date file_node.addElement('desc', content=self.file.description) # we support range transfer file_node.addElement('range') # Metadata if self.service_name: service_form = {ns.TP_FT_METADATA_SERVICE: {'ServiceName': [self.service_name]}} add_data_forms(file_node, service_form) if self.metadata: metadata_form = {ns.TP_FT_METADATA: self.metadata} add_data_forms(file_node, metadata_form) # so... lunch? iq.send() def check_new_channel(self): def is_ft_channel_event(event): channels, = event.args if len(channels) > 1: return False path, props = channels[0] return props[cs.CHANNEL_TYPE] == cs.CHANNEL_TYPE_FILE_TRANSFER e = self.q.expect('dbus-signal', signal='NewChannels', path=self.conn.object.object_path, predicate=is_ft_channel_event) channels = e.args[0] assert len(channels) == 1 path, props = channels[0] # check channel properties # org.freedesktop.Telepathy.Channel D-Bus properties assert props[cs.CHANNEL_TYPE] == cs.CHANNEL_TYPE_FILE_TRANSFER assertSameSets( [ cs.CHANNEL_IFACE_FILE_TRANSFER_METADATA, cs.CHANNEL_TYPE_FILE_TRANSFER + '.FUTURE', ], props[cs.INTERFACES]) assert props[cs.TARGET_HANDLE] == self.handle assert props[cs.TARGET_ID] == self.contact_name assert props[cs.TARGET_HANDLE_TYPE] == cs.HT_CONTACT assert props[cs.REQUESTED] == False assert props[cs.INITIATOR_HANDLE] == self.handle assert props[cs.INITIATOR_ID] == self.contact_name # org.freedesktop.Telepathy.Channel.Type.FileTransfer D-Bus properties assert props[cs.FT_STATE] == cs.FT_STATE_PENDING assert props[cs.FT_CONTENT_TYPE] == self.file.content_type assert props[cs.FT_FILENAME] == self.file.name assert props[cs.FT_SIZE] == self.file.size # FT's protocol doesn't allow us the send the hash info assert props[cs.FT_CONTENT_HASH_TYPE] == cs.FILE_HASH_TYPE_MD5 assert props[cs.FT_CONTENT_HASH] == self.file.hash assert props[cs.FT_DESCRIPTION] == self.file.description assert props[cs.FT_DATE] == self.file.date assert props[cs.FT_TRANSFERRED_BYTES] == 0 assert props[cs.FT_INITIAL_OFFSET] == 0 self.check_platform_socket_types(props[cs.FT_AVAILABLE_SOCKET_TYPES]) assertEquals(self.service_name, props[cs.FT_SERVICE_NAME]) assertEquals(self.metadata, props[cs.FT_METADATA]) self.ft_path = path def set_uri(self): ft_props = dbus.Interface(self.ft_channel, cs.PROPERTIES_IFACE) # URI is not set yet uri = ft_props.Get(cs.CHANNEL_TYPE_FILE_TRANSFER, 'URI') assertEquals('', uri) # Setting URI call_async(self.q, ft_props, 'Set', cs.CHANNEL_TYPE_FILE_TRANSFER, 'URI', self.file.uri) self.q.expect('dbus-signal', signal='URIDefined', args=[self.file.uri]) self.q.expect('dbus-return', method='Set') # Check it has the right value now uri = ft_props.Get(cs.CHANNEL_TYPE_FILE_TRANSFER, 'URI') assertEquals(self.file.uri, uri) # We can't change it once it has been set call_async(self.q, ft_props, 'Set', cs.CHANNEL_TYPE_FILE_TRANSFER, 'URI', 'badger://snake') self.q.expect('dbus-error', method='Set', name=cs.INVALID_ARGUMENT) def accept_file(self): try: self.address = self.ft_channel.AcceptFile(self.address_type, self.access_control, self.access_control_param, self.file.offset, byte_arrays=True) except dbus.DBusException, e: if self.address_type == cs.SOCKET_ADDRESS_TYPE_IPV6 and \ e.get_dbus_name() == cs.NOT_AVAILABLE and \ e.get_dbus_message() == "Could not set up local socket": print "Ignoring error for ipv6 address space" return True else: raise e state_event, iq_event = self.q.expect_many( EventPattern('dbus-signal', signal='FileTransferStateChanged'), EventPattern('stream-iq', iq_type='result')) state, reason = state_event.args assert state == cs.FT_STATE_ACCEPTED assert reason == cs.FT_STATE_CHANGE_REASON_REQUESTED # Got SI reply self.bytestream.check_si_reply(iq_event.stanza) if self.file.offset != 0: range = xpath.queryForNodes('/iq/si/file/range', iq_event.stanza)[0] assert range['offset'] == str(self.file.offset) _, events = self.bytestream.open_bytestream([], [ EventPattern('dbus-signal', signal='InitialOffsetDefined'), EventPattern('dbus-signal', signal='FileTransferStateChanged')]) offset_event, state_event = events offset = offset_event.args[0] assert offset == self.file.offset state, reason = state_event.args assert state == cs.FT_STATE_OPEN assert reason == cs.FT_STATE_CHANGE_REASON_NONE # send the beginning of the file (client didn't connect to socket yet) self.bytestream.send_data(self.file.data[self.file.offset:self.file.offset + 2]) def receive_file(self): # Connect to Gabble's socket s = self.create_socket() s.connect(self.address) # send the rest of the file i = self.file.offset + 2 self.bytestream.send_data(self.file.data[i:]) self._read_file_from_socket(s) def _read_file_from_socket(self, s): # Read the file from Gabble's socket data = '' read = 0 to_receive = self.file.size - self.file.offset e = self.q.expect('dbus-signal', signal='TransferredBytesChanged') count = e.args[0] while True: received = s.recv(1024) if len(received) == 0: break data += received assert data == self.file.data[self.file.offset:] while count < to_receive: # Catch TransferredBytesChanged until we transfered all the data e = self.q.expect('dbus-signal', signal='TransferredBytesChanged') count = e.args[0] e = self.q.expect('dbus-signal', signal='FileTransferStateChanged') state, reason = e.args assert state == cs.FT_STATE_COMPLETED assert reason == cs.FT_STATE_CHANGE_REASON_NONE class SendFileTest(FileTransferTest): def __init__(self, bytestream_cls, file, address_type, access_control, acces_control_param): FileTransferTest.__init__(self, bytestream_cls, file, address_type, access_control, acces_control_param) self._actions = [self.connect, self.announce_contact, self.check_ft_available, self.request_ft_channel, self.create_ft_channel, self.got_send_iq, self.provide_file, self.client_accept_file, self.send_file, self.close_channel, self.done] def check_ft_available(self): properties = self.conn.GetAll(cs.CONN_IFACE_REQUESTS, dbus_interface=cs.PROPERTIES_IFACE) # general FT class assert ({cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_FILE_TRANSFER, cs.TARGET_HANDLE_TYPE: cs.HT_CONTACT}, [cs.FT_CONTENT_HASH_TYPE, cs.TARGET_HANDLE, cs.TARGET_ID, cs.FT_CONTENT_TYPE, cs.FT_FILENAME, cs.FT_SIZE, cs.FT_CONTENT_HASH, cs.FT_DESCRIPTION, cs.FT_DATE, cs.FT_URI, cs.FT_SERVICE_NAME, cs.FT_METADATA] ) in properties.get('RequestableChannelClasses'),\ properties['RequestableChannelClasses'] # FT class with MD5 as HashType assert ({cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_FILE_TRANSFER, cs.TARGET_HANDLE_TYPE: cs.HT_CONTACT, cs.FT_CONTENT_HASH_TYPE: cs.FILE_HASH_TYPE_MD5}, [cs.TARGET_HANDLE, cs.TARGET_ID, cs.FT_CONTENT_TYPE, cs.FT_FILENAME, cs.FT_SIZE, cs.FT_CONTENT_HASH, cs.FT_DESCRIPTION, cs.FT_DATE, cs.FT_URI, cs.FT_SERVICE_NAME, cs.FT_METADATA] ) in properties.get('RequestableChannelClasses'),\ properties['RequestableChannelClasses'] def request_ft_channel(self, uri=True): request = { cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_FILE_TRANSFER, cs.TARGET_HANDLE_TYPE: cs.HT_CONTACT, cs.TARGET_HANDLE: self.handle, cs.FT_CONTENT_TYPE: self.file.content_type, cs.FT_FILENAME: self.file.name, cs.FT_SIZE: self.file.size, cs.FT_CONTENT_HASH_TYPE: self.file.hash_type, cs.FT_CONTENT_HASH: self.file.hash, cs.FT_DESCRIPTION: self.file.description, cs.FT_DATE: self.file.date, cs.FT_INITIAL_OFFSET: 0, cs.FT_SERVICE_NAME: self.service_name, cs.FT_METADATA: dbus.Dictionary(self.metadata, signature='sas')} if uri: request[cs.FT_URI] = self.file.uri self.ft_path, props = self.conn.Requests.CreateChannel(request) # org.freedesktop.Telepathy.Channel D-Bus properties assertEquals(cs.CHANNEL_TYPE_FILE_TRANSFER, props[cs.CHANNEL_TYPE]) assertSameSets( [ cs.CHANNEL_IFACE_FILE_TRANSFER_METADATA, cs.CHANNEL_TYPE_FILE_TRANSFER + '.FUTURE', ], props[cs.INTERFACES]) assertEquals(self.handle, props[cs.TARGET_HANDLE]) assertEquals(self.contact_name, props[cs.TARGET_ID]) assertEquals(cs.HT_CONTACT, props[cs.TARGET_HANDLE_TYPE]) assert props[cs.REQUESTED] assertEquals(self.self_handle, props[cs.INITIATOR_HANDLE]) assertEquals(self.self_handle_name, props[cs.INITIATOR_ID]) # org.freedesktop.Telepathy.Channel.Type.FileTransfer D-Bus properties assertEquals(cs.FT_STATE_PENDING, props[cs.FT_STATE]) assertEquals(self.file.content_type, props[cs.FT_CONTENT_TYPE]) assertEquals(self.file.name, props[cs.FT_FILENAME]) assertEquals(self.file.size, props[cs.FT_SIZE]) assertEquals(self.file.hash_type, props[cs.FT_CONTENT_HASH_TYPE]) assertEquals(self.file.hash, props[cs.FT_CONTENT_HASH]) assertEquals(self.file.description, props[cs.FT_DESCRIPTION]) assertEquals(self.file.date, props[cs.FT_DATE]) assertEquals(0, props[cs.FT_TRANSFERRED_BYTES]) assertEquals(0, props[cs.FT_INITIAL_OFFSET]) assertEquals(self.service_name, props[cs.FT_SERVICE_NAME]) assertEquals(self.metadata, props[cs.FT_METADATA]) if uri: assertEquals(self.file.uri, props[cs.FT_URI]) else: assertEquals('', props[cs.FT_URI]) self.check_platform_socket_types(props[cs.FT_AVAILABLE_SOCKET_TYPES]) def got_send_iq(self): iq_event = self.q.expect('stream-iq', to=self.contact_full_jid) self._check_file_transfer_offer_iq(iq_event) def _check_file_transfer_offer_iq(self, iq_event): self.iq = iq_event.stanza self.bytestream, profile = create_from_si_offer(self.stream, self.q, self.bytestream_cls, iq_event.stanza, 'test@localhost/Resource') assert self.iq['to'] == self.contact_full_jid assert profile == ns.FILE_TRANSFER file_node = xpath.queryForNodes('/iq/si/file', self.iq)[0] assert file_node['name'] == self.file.name assert file_node['size'] == str(self.file.size) assert file_node['mime-type'] == self.file.content_type assert file_node['hash'] == self.file.hash date = datetime.datetime.utcfromtimestamp(self.file.date).strftime('%FT%H:%M:%SZ') assert file_node['date'] == date, file_node['date'] desc_node = xpath.queryForNodes("/iq/si/file/desc", self.iq)[0] self.desc = desc_node.children[0] assert self.desc == self.file.description # Gabble supports resume range = xpath.queryForNodes('/iq/si/file/range', self.iq)[0] assert range is not None # Metadata forms forms = extract_data_forms(xpath.queryForNodes('/iq/si/file/x', self.iq)) if self.service_name: assertEquals({'ServiceName': [self.service_name]}, forms[ns.TP_FT_METADATA_SERVICE]) else: assert ns.TP_FT_METADATA_SERVICE not in forms if self.metadata: assertEquals(self.metadata, forms[ns.TP_FT_METADATA]) else: assert ns.TP_FT_METADATA not in forms def provide_file(self): try: self.address = self.ft_channel.ProvideFile(self.address_type, self.access_control, self.access_control_param, byte_arrays=True) except dbus.DBusException, e: if self.address_type == cs.SOCKET_ADDRESS_TYPE_IPV6 and \ e.get_dbus_name() == cs.NOT_AVAILABLE and \ e.get_dbus_message() == "Could not set up local socket": print "Ignoring error for ipv6 address space" return True else: raise e def client_accept_file(self): # accept SI offer result, si = self.bytestream.create_si_reply(self.iq) file_node = si.addElement((ns.FILE_TRANSFER, 'file')) range = file_node.addElement('range') range['offset'] = str(self.file.offset) self.stream.send(result) self.bytestream.wait_bytestream_open() def send_file(self): s = self.create_socket() s.connect(self.address) s.send(self.file.data[self.file.offset:]) to_receive = self.file.size - self.file.offset self.count = 0 def bytes_changed_cb(bytes): self.count = bytes self.ft_channel.connect_to_signal('TransferredBytesChanged', bytes_changed_cb) # FileTransferStateChanged can be fired while we are receiving data # (in the SOCKS5 case for example) self.completed = False def ft_state_changed_cb(state, reason): if state == cs.FT_STATE_COMPLETED: self.completed = True self.ft_channel.connect_to_signal('FileTransferStateChanged', ft_state_changed_cb) # get data from bytestream data = '' while len(data) < to_receive: data += self.bytestream.get_data() assert data == self.file.data[self.file.offset:] if self.completed: # FileTransferStateChanged has already been received waiting = [] else: waiting = [EventPattern('dbus-signal', signal='FileTransferStateChanged')] events = self.bytestream.wait_bytestream_closed(waiting) # If not all the bytes transferred have been announced using # TransferredBytesChanged, wait for them while self.count < to_receive: self.q.expect('dbus-signal', signal='TransferredBytesChanged') assert self.count == to_receive if len(waiting) > 1: state, reason = events[0].args assert state == cs.FT_STATE_COMPLETED assert reason == cs.FT_STATE_CHANGE_REASON_NONE def platform_impls(): impls = [ (cs.SOCKET_ADDRESS_TYPE_IPV4, cs.SOCKET_ACCESS_CONTROL_LOCALHOST, ""), (cs.SOCKET_ADDRESS_TYPE_IPV6, cs.SOCKET_ACCESS_CONTROL_LOCALHOST, ""), ] if os.name == 'posix': impls.append((cs.SOCKET_ADDRESS_TYPE_UNIX, cs.SOCKET_ACCESS_CONTROL_LOCALHOST, "")) return impls def exec_file_transfer_test(test_cls, one_run=False): for bytestream_cls in [ bytestream.BytestreamIBBMsg, bytestream.BytestreamS5B, bytestream.BytestreamS5BPidgin, bytestream.BytestreamSIFallbackS5CannotConnect, bytestream.BytestreamSIFallbackS5WrongHash, bytestream.BytestreamS5BRelay, bytestream.BytestreamS5BRelayBugged]: for addr_type, access_control, access_control_param in platform_impls(): file = File() test = test_cls(bytestream_cls, file, addr_type, access_control, access_control_param) exec_test(test.test) # test resume file.offset = 5 test = test_cls(bytestream_cls, file, addr_type, access_control, access_control_param) exec_test(test.test) if one_run: return telepathy-gabble-0.18.2/tests/twisted/file-transfer/ft-client-caps.py0000644000175000017500000003133112227000321025554 0ustar00smcvsmcv00000000000000 """ Test FT capabilities with Connection.Interface.ContactCapabilities 1. Receive presence and caps from contacts and check that GetContactCapabilities works correctly and that ContactCapabilitiesChanged is correctly received. Also check that GetContactAttributes gives the same results. - no FT cap at all - FT caps without metadata extension - FT caps with metadata extension - 1 FT cap with a service name - 2 FT caps with service names - 1 FT cap again, to test whether the caps cache works with FT services 2. Test UpdateCapabilities and test that a presence stanza is sent to the contacts, test that the D-Bus signal ContactCapabilitiesChanged is fired for the self handle, ask Gabble for its caps with an iq request, check the reply is correct, and ask Gabble for its caps using D-Bus method GetContactCapabilities. Also check that GetContactAttributes gives the same results. Ensure that just a Requested=True channel class in a client filter doesn't make a FT service advertised as a cap. - no FT cap at all - 1 FT cap with no service name - 1 Requested=True FT cap with service name - 1 FT cap with service name - 1 FT cap with service name + 1 FT cap with no service name - 2 FT caps with service names - 1 FT cap with service name again, just for fun """ import dbus from twisted.words.xish import xpath from servicetest import assertEquals, assertLength, assertContains,\ assertDoesNotContain, sync_dbus from gabbletest import exec_test, make_result_iq, sync_stream, make_presence import constants as cs from caps_helper import compute_caps_hash, text_fixed_properties,\ text_allowed_properties, stream_tube_fixed_properties, stream_tube_allowed_properties,\ dbus_tube_fixed_properties, dbus_tube_allowed_properties, receive_presence_and_ask_caps,\ caps_contain, ft_fixed_properties, ft_allowed_properties, ft_allowed_properties_with_metadata, \ presence_and_disco import ns from config import FILE_TRANSFER_ENABLED if not FILE_TRANSFER_ENABLED: print "NOTE: built with --disable-file-transfer" raise SystemExit(77) def dict_union(a, b): return dbus.Dictionary(a.items() + b.items(), signature='sv') no_service_fixed_properties = { cs.TARGET_HANDLE_TYPE: cs.HT_CONTACT, cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_FILE_TRANSFER, } bidir_daap_fixed_properties = dict_union(no_service_fixed_properties, { cs.FT_SERVICE_NAME: 'daap' }) outgoing_daap_fixed_properties = dict_union(bidir_daap_fixed_properties, { cs.REQUESTED : True, }) incoming_daap_fixed_properties = dict_union(bidir_daap_fixed_properties, { cs.REQUESTED : False, }) http_fixed_properties = dict_union(no_service_fixed_properties, { cs.FT_SERVICE_NAME: 'http', }) xiangqi_fixed_properties = dict_union(no_service_fixed_properties, { cs.FT_SERVICE_NAME: 'com.example.Xiangqi', }) go_fixed_properties = dict_union(no_service_fixed_properties, { cs.FT_SERVICE_NAME: 'com.example.Go', }) client = 'http://telepathy.freedesktop.org/another-fake-client' def assertSameElements(a, b): assertEquals(sorted(a), sorted(b)) def receive_caps(q, conn, stream, contact, contact_handle, features, expected_caps, expect_disco=True, expect_ccc=True): caps = {'node': client, 'ver': compute_caps_hash([], features, {}), 'hash': 'sha-1'} presence_and_disco(q, conn, stream, contact, expect_disco, client, caps, features, initial=False) if expect_ccc: event = q.expect('dbus-signal', signal='ContactCapabilitiesChanged') announced_ccs, = event.args assertSameElements(expected_caps, announced_ccs[contact_handle]) else: # Make sure Gabble's got the caps sync_stream(q, stream) caps = conn.ContactCapabilities.GetContactCapabilities([contact_handle]) assertSameElements(expected_caps, caps[contact_handle]) # test again, to check GetContactCapabilities does not have side effect caps = conn.ContactCapabilities.GetContactCapabilities([contact_handle]) assertSameElements(expected_caps, caps[contact_handle]) # check the Contacts interface give the same caps caps_via_contacts_iface = conn.Contacts.GetContactAttributes( [contact_handle], [cs.CONN_IFACE_CONTACT_CAPS], False) \ [contact_handle][cs.ATTR_CONTACT_CAPABILITIES] assertSameElements(caps[contact_handle], caps_via_contacts_iface) def test_ft_caps_from_contact(q, bus, conn, stream, contact): contact_handle = conn.RequestHandles(cs.HT_CONTACT, [contact])[0] # Check that we don't crash if we haven't seen any caps/presence for this # contact yet. caps = conn.ContactCapabilities.GetContactCapabilities([contact_handle]) basic_caps = [(text_fixed_properties, text_allowed_properties)] # Since we don't know their caps, they should be omitted from the dict, # rather than present with no caps, but all contacts have text chat caps. assertEquals(basic_caps, caps[contact_handle]) # send presence with no FT cap # We don't expect ContactCapabilitiesChanged to be emitted here: we always # assume people can do text channels. receive_caps(q, conn, stream, contact, contact_handle, [], basic_caps, expect_ccc=False) # send presence with no mention of metadata no_metadata_ft_caps = [ (text_fixed_properties, text_allowed_properties), (ft_fixed_properties, ft_allowed_properties) ] receive_caps(q, conn, stream, contact, contact_handle, [ns.FILE_TRANSFER], no_metadata_ft_caps) # send presence with generic FT caps including metadata from now on generic_ft_caps = [ (text_fixed_properties, text_allowed_properties), (ft_fixed_properties, ft_allowed_properties_with_metadata) ] generic_ft_features = [ns.FILE_TRANSFER, ns.TP_FT_METADATA] receive_caps(q, conn, stream, contact, contact_handle, generic_ft_features, generic_ft_caps) # send presence with 1 FT cap with a service daap_caps = generic_ft_caps + [ (bidir_daap_fixed_properties, ft_allowed_properties + [cs.FT_METADATA])] receive_caps(q, conn, stream, contact, contact_handle, generic_ft_features + [ns.TP_FT_METADATA + '#daap'], daap_caps) # send presence with 2 FT caps daap_xiangqi_caps = daap_caps + [ (xiangqi_fixed_properties, ft_allowed_properties + [cs.FT_METADATA])] receive_caps(q, conn, stream, contact, contact_handle, generic_ft_features + [ns.TP_FT_METADATA + '#com.example.Xiangqi', ns.TP_FT_METADATA + '#daap', ], daap_xiangqi_caps) # send presence with 1 FT cap again # Gabble does not look up our capabilities because of the cache receive_caps(q, conn, stream, contact, contact_handle, generic_ft_features + [ns.TP_FT_METADATA + '#daap'], daap_caps, expect_disco=False) def advertise_caps(q, bus, conn, stream, filters, expected_features, unexpected_features, expected_caps): # make sure nothing from a previous update is still running sync_dbus(bus, q, conn) self_handle = conn.GetSelfHandle() ret_caps = conn.ContactCapabilities.UpdateCapabilities( [(cs.CLIENT + '.Foo', filters, [])]) # Expect Gabble to reply with the correct caps event, namespaces, _, signaled_caps = receive_presence_and_ask_caps(q, stream) assertSameElements(expected_caps, signaled_caps[self_handle]) assertContains(ns.TP_FT_METADATA, namespaces) for var in expected_features: assertContains(var, namespaces) for var in unexpected_features: assertDoesNotContain(var, namespaces) # Check our own caps caps = conn.ContactCapabilities.GetContactCapabilities([self_handle]) assertSameElements(expected_caps, caps[self_handle]) # check the Contacts interface give the same caps caps_via_contacts_iface = conn.Contacts.GetContactAttributes( [self_handle], [cs.CONN_IFACE_CONTACT_CAPS], False) \ [self_handle][cs.ATTR_CONTACT_CAPABILITIES] assertSameElements(caps[self_handle], caps_via_contacts_iface) def test_ft_caps_to_contact(q, bus, conn, stream): self_handle = conn.GetSelfHandle() basic_caps = [ (text_fixed_properties, text_allowed_properties), (stream_tube_fixed_properties, stream_tube_allowed_properties), (dbus_tube_fixed_properties, dbus_tube_allowed_properties), ] simple_ft_caps = basic_caps + [ (ft_fixed_properties, ft_allowed_properties_with_metadata), ] daap_caps = simple_ft_caps + [ (bidir_daap_fixed_properties, ft_allowed_properties + [cs.FT_METADATA]), ] xiangqi_caps = simple_ft_caps + [ (xiangqi_fixed_properties, ft_allowed_properties + [cs.FT_METADATA]), ] xiangqi_go_caps = xiangqi_caps + [ (go_fixed_properties, ft_allowed_properties + [cs.FT_METADATA]), ] go_caps = simple_ft_caps + [ (go_fixed_properties, ft_allowed_properties + [cs.FT_METADATA]), ] # # Check our own caps; we should have no FT caps # caps = conn.ContactCapabilities.GetContactCapabilities([self_handle]) assertEquals(basic_caps, caps[self_handle]) # check the Contacts interface give the same caps caps_via_contacts_iface = conn.Contacts.GetContactAttributes( [self_handle], [cs.CONN_IFACE_CONTACT_CAPS], False) \ [self_handle][cs.ATTR_CONTACT_CAPABILITIES] assertEquals(caps[self_handle], caps_via_contacts_iface) # # Advertise nothing # conn.ContactCapabilities.UpdateCapabilities( [(cs.CLIENT + '.Foo', {}, [])]) # Check our own caps caps = conn.ContactCapabilities.GetContactCapabilities([self_handle]) assertLength(1, caps) assertEquals(basic_caps, caps[self_handle]) # check the Contacts interface give the same caps caps_via_contacts_iface = conn.Contacts.GetContactAttributes( [self_handle], [cs.CONN_IFACE_CONTACT_CAPS], False) \ [self_handle][cs.ATTR_CONTACT_CAPABILITIES] assertEquals(caps[self_handle], caps_via_contacts_iface) sync_stream(q, stream) # # Advertise FT but with no service name # conn.ContactCapabilities.UpdateCapabilities( [(cs.CLIENT + '.Foo', [no_service_fixed_properties], [])]) # Check our own caps caps = conn.ContactCapabilities.GetContactCapabilities([self_handle]) assertLength(1, caps) assertEquals(simple_ft_caps, caps[self_handle]) # check the Contacts interface give the same caps caps_via_contacts_iface = conn.Contacts.GetContactAttributes( [self_handle], [cs.CONN_IFACE_CONTACT_CAPS], False) \ [self_handle][cs.ATTR_CONTACT_CAPABILITIES] assertEquals(caps[self_handle], caps_via_contacts_iface) sync_stream(q, stream) # # Advertise a Requested=True FT cap # conn.ContactCapabilities.UpdateCapabilities( [(cs.CLIENT + '.Foo', [outgoing_daap_fixed_properties], [])]) # Check our own caps caps = conn.ContactCapabilities.GetContactCapabilities([self_handle]) assertLength(1, caps) assertEquals(simple_ft_caps, caps[self_handle]) # check the Contacts interface give the same caps caps_via_contacts_iface = conn.Contacts.GetContactAttributes( [self_handle], [cs.CONN_IFACE_CONTACT_CAPS], False) \ [self_handle][cs.ATTR_CONTACT_CAPABILITIES] assertEquals(caps[self_handle], caps_via_contacts_iface) advertise_caps(q, bus, conn, stream, [bidir_daap_fixed_properties], [ns.TP_FT_METADATA + '#daap'], [ns.TP_FT_METADATA + '#http', ns.TP_FT_METADATA + '#com.example.Go', ns.TP_FT_METADATA + '#com.example.Xiangqi', ], daap_caps) advertise_caps(q, bus, conn, stream, [xiangqi_fixed_properties, no_service_fixed_properties], [ns.TP_FT_METADATA + '#com.example.Xiangqi'], [ns.TP_FT_METADATA + '#daap', ns.TP_FT_METADATA + '#http', ns.TP_FT_METADATA + '#com.example.Go', ], xiangqi_caps) advertise_caps(q, bus, conn, stream, [xiangqi_fixed_properties, go_fixed_properties], [ns.TP_FT_METADATA + '#com.example.Xiangqi', ns.TP_FT_METADATA + '#com.example.Go', ], [ns.TP_FT_METADATA + '#http', ns.TP_FT_METADATA + '#daap', ], xiangqi_go_caps) advertise_caps(q, bus, conn, stream, [go_fixed_properties], [ns.TP_FT_METADATA + '#com.example.Go', ], [ns.TP_FT_METADATA + '#http', ns.TP_FT_METADATA + '#daap', ns.TP_FT_METADATA + '#com.example.Xiangqi', ], go_caps) def test(q, bus, conn, stream): test_ft_caps_from_contact(q, bus, conn, stream, 'bilbo1@foo.com/Foo') test_ft_caps_to_contact(q, bus, conn, stream) if __name__ == '__main__': exec_test(test) telepathy-gabble-0.18.2/tests/twisted/file-transfer/metadata.py0000644000175000017500000000643512227000321024532 0ustar00smcvsmcv00000000000000# The 'normal' cases are tested with test-receive-file.py and test-send-file-provide-immediately.py # This file tests some corner cases import dbus from file_transfer_helper import exec_file_transfer_test, ReceiveFileTest, SendFileTest from servicetest import assertEquals, call_async import constants as cs from config import FILE_TRANSFER_ENABLED if not FILE_TRANSFER_ENABLED: print "NOTE: built with --disable-file-transfer" raise SystemExit(77) class SendFileNoMetadata(SendFileTest): # this is basically the equivalent of calling CreateChannel # without these two properties service_name = '' metadata = {} class ReceiveFileNoMetadata(ReceiveFileTest): service_name = '' metadata = {} class SendFileOddMetadata(SendFileTest): service_name = '' metadata = {'loldongs': []} class ReceiveFileOddMetadata(ReceiveFileTest): service_name = '' metadata = {'loldongs': []} class SendFileBadProps(SendFileTest): metadata = {'FORM_TYPE': ['this shouldnt be allowed']} def request_ft_channel(self): request = { cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_FILE_TRANSFER, cs.TARGET_HANDLE_TYPE: cs.HT_CONTACT, cs.TARGET_HANDLE: self.handle, cs.FT_CONTENT_TYPE: self.file.content_type, cs.FT_FILENAME: self.file.name, cs.FT_SIZE: self.file.size, cs.FT_CONTENT_HASH_TYPE: self.file.hash_type, cs.FT_CONTENT_HASH: self.file.hash, cs.FT_DESCRIPTION: self.file.description, cs.FT_DATE: self.file.date, cs.FT_INITIAL_OFFSET: 0, cs.FT_SERVICE_NAME: self.service_name, cs.FT_METADATA: dbus.Dictionary(self.metadata, signature='sas')} call_async(self.q, self.conn.Requests, 'CreateChannel', request) # FORM_TYPE is not allowed, soz self.q.expect('dbus-error', method='CreateChannel', name=cs.INVALID_ARGUMENT) return True class SendFileBadContact(SendFileTest): def announce_contact(self): SendFileTest.announce_contact(self, metadata=False) def request_ft_channel(self): request = { cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_FILE_TRANSFER, cs.TARGET_HANDLE_TYPE: cs.HT_CONTACT, cs.TARGET_HANDLE: self.handle, cs.FT_CONTENT_TYPE: self.file.content_type, cs.FT_FILENAME: self.file.name, cs.FT_SIZE: self.file.size, cs.FT_CONTENT_HASH_TYPE: self.file.hash_type, cs.FT_CONTENT_HASH: self.file.hash, cs.FT_DESCRIPTION: self.file.description, cs.FT_DATE: self.file.date, cs.FT_INITIAL_OFFSET: 0, cs.FT_SERVICE_NAME: self.service_name, cs.FT_METADATA: dbus.Dictionary(self.metadata, signature='sas')} call_async(self.q, self.conn.Requests, 'CreateChannel', request) # no support for metadata, soz self.q.expect('dbus-error', method='CreateChannel', name=cs.NOT_CAPABLE) return True if __name__ == '__main__': exec_file_transfer_test(SendFileNoMetadata, True) exec_file_transfer_test(ReceiveFileNoMetadata, True) exec_file_transfer_test(SendFileOddMetadata, True) exec_file_transfer_test(ReceiveFileOddMetadata, True) exec_file_transfer_test(SendFileBadProps, True) exec_file_transfer_test(SendFileBadContact, True) telepathy-gabble-0.18.2/tests/twisted/file-transfer/test-uri.py0000644000175000017500000000546312200204333024526 0ustar00smcvsmcv00000000000000# The 'normal' cases are tested with test-receive-file.py and test-send-file-provide-immediately.py # This file tests some corner cases import dbus from file_transfer_helper import exec_file_transfer_test, ReceiveFileTest, SendFileTest from servicetest import assertEquals, call_async import constants as cs from config import FILE_TRANSFER_ENABLED if not FILE_TRANSFER_ENABLED: print "NOTE: built with --disable-file-transfer" raise SystemExit(77) def assertNoURI(ft_channel): ft_props = dbus.Interface(ft_channel, cs.PROPERTIES_IFACE) uri = ft_props.Get(cs.CHANNEL_TYPE_FILE_TRANSFER, 'URI') assertEquals('', uri) class SetURIAfterAccepting(ReceiveFileTest): def __init__(self, bytestream_cls, file, address_type, access_control, access_control_param): ReceiveFileTest.__init__(self, bytestream_cls, file, address_type, access_control, access_control_param) self._actions = [self.connect, self.announce_contact, self.send_ft_offer_iq, self.check_new_channel, self.create_ft_channel, self.accept_file, self.set_uri, self.done] def set_uri(self): ft_props = dbus.Interface(self.ft_channel, cs.PROPERTIES_IFACE) # URI is not set yet assertNoURI(self.ft_channel) # Setting URI call_async(self.q, ft_props, 'Set', cs.CHANNEL_TYPE_FILE_TRANSFER, 'URI', self.file.uri) # too late... self.q.expect('dbus-error', method='Set', name=cs.INVALID_ARGUMENT) class ReceiveFileTestNoURI(ReceiveFileTest): def __init__(self, bytestream_cls, file, address_type, access_control, access_control_param): ReceiveFileTest.__init__(self, bytestream_cls, file, address_type, access_control, access_control_param) self._actions = [self.connect, self.announce_contact, self.send_ft_offer_iq, self.check_new_channel, self.create_ft_channel, self.accept_file, self.receive_file, self.close_channel, self.done] def accept_file(self): # URI is not set assertNoURI(self.ft_channel) ReceiveFileTest.accept_file(self) def close_channel(self): # Still no URI assertNoURI(self.ft_channel) ReceiveFileTest.close_channel(self) class SendFileNoURI(SendFileTest): def request_ft_channel(self): SendFileTest.request_ft_channel(self, False) def close_channel(self): # Still no URI assertNoURI(self.ft_channel) SendFileTest.close_channel(self) if __name__ == '__main__': # We don't define an URI before accepting the file and try to set it after exec_file_transfer_test(SetURIAfterAccepting, True) # Don't define any URI when receiving a file exec_file_transfer_test(ReceiveFileTestNoURI, True) # Don't define any URI when sending a file exec_file_transfer_test(SendFileNoURI, True) telepathy-gabble-0.18.2/tests/twisted/file-transfer/test-send-file-wait-to-provide.py0000644000175000017500000000357612200204333030630 0ustar00smcvsmcv00000000000000import constants as cs from file_transfer_helper import SendFileTest, exec_file_transfer_test from config import FILE_TRANSFER_ENABLED if not FILE_TRANSFER_ENABLED: print "NOTE: built with --disable-file-transfer" raise SystemExit(77) class SendFileTransferWaitToProvideTest(SendFileTest): def __init__(self, bytestream_cls, file, address_type, access_control, acces_control_param): SendFileTest.__init__(self, bytestream_cls, file, address_type, access_control, acces_control_param) self._actions = [self.connect, self.check_ft_available, self.announce_contact, self.check_ft_available, self.request_ft_channel, self.create_ft_channel, self.got_send_iq, self.client_accept_file, self.provide_file, self.send_file, self.close_channel] def client_accept_file(self): # state is still Pending as remote didn't accept the transfer yet state = self.ft_props.Get(cs.CHANNEL_TYPE_FILE_TRANSFER, 'State') assert state == cs.FT_STATE_PENDING SendFileTest.client_accept_file(self) # Remote accepted the transfer e = self.q.expect('dbus-signal', signal='FileTransferStateChanged') state, reason = e.args assert state == cs.FT_STATE_ACCEPTED, state assert reason == cs.FT_STATE_CHANGE_REASON_NONE def provide_file(self): if SendFileTest.provide_file(self): return True e = self.q.expect('dbus-signal', signal='InitialOffsetDefined') offset = e.args[0] assert offset == self.file.offset # Channel is open. We can start to send the file e = self.q.expect('dbus-signal', signal='FileTransferStateChanged') state, reason = e.args assert state == cs.FT_STATE_OPEN assert reason == cs.FT_STATE_CHANGE_REASON_REQUESTED if __name__ == '__main__': exec_file_transfer_test(SendFileTransferWaitToProvideTest) telepathy-gabble-0.18.2/tests/twisted/file-transfer/test-send-file-to-unknown-contact.py0000644000175000017500000000204312227000321031332 0ustar00smcvsmcv00000000000000import dbus import constants as cs from file_transfer_helper import SendFileTest, exec_file_transfer_test from config import FILE_TRANSFER_ENABLED if not FILE_TRANSFER_ENABLED: print "NOTE: built with --disable-file-transfer" raise SystemExit(77) class SendFileTransferToUnknownContactTest(SendFileTest): def __init__(self, bytestream_cls, file, address_type, access_control, acces_control_param): SendFileTest.__init__(self, bytestream_cls, file, address_type, access_control, acces_control_param) self._actions = [self.connect, self.check_ft_available, self.my_request_ft_channel] def my_request_ft_channel(self): self.contact_name = 'jean@localhost' self.handle = self.conn.RequestHandles(cs.HT_CONTACT, [self.contact_name])[0] try: self.request_ft_channel() except dbus.DBusException, e: assert e.get_dbus_name() == cs.OFFLINE else: assert False if __name__ == '__main__': exec_file_transfer_test(SendFileTransferToUnknownContactTest) telepathy-gabble-0.18.2/tests/twisted/file-transfer/test-send-file-send-before-accept.py0000644000175000017500000000460512227000321031216 0ustar00smcvsmcv00000000000000import dbus import constants as cs from servicetest import EventPattern from file_transfer_helper import SendFileTest, exec_file_transfer_test from twisted.words.xish import domish import ns from config import FILE_TRANSFER_ENABLED if not FILE_TRANSFER_ENABLED: print "NOTE: built with --disable-file-transfer" raise SystemExit(77) class SendFileSendBeforeAccept(SendFileTest): def client_accept_file_empty(self): return False client_accept_file_orig = SendFileTest.client_accept_file client_accept_file = client_accept_file_empty def send_file(self): s = self.create_socket() s.connect(self.address) s.send(self.file.data[self.file.offset:]) # Accept the file now that we've sent our data... self.client_accept_file_orig() to_receive = self.file.size - self.file.offset self.count = 0 def bytes_changed_cb(bytes): self.count = bytes self.ft_channel.connect_to_signal('TransferredBytesChanged', bytes_changed_cb) # FileTransferStateChanged can be fired while we are receiving data # (in the SOCKS5 case for example) self.completed = False def ft_state_changed_cb(state, reason): if state == cs.FT_STATE_COMPLETED: self.completed = True self.ft_channel.connect_to_signal('FileTransferStateChanged', ft_state_changed_cb) # get data from bytestream data = '' while len(data) < to_receive: data += self.bytestream.get_data() assert data == self.file.data[self.file.offset:] if self.completed: # FileTransferStateChanged has already been received waiting = [] else: waiting = [EventPattern('dbus-signal', signal='FileTransferStateChanged')] events = self.bytestream.wait_bytestream_closed(waiting) # If not all the bytes transferred have been announced using # TransferredBytesChanged, wait for them while self.count < to_receive: self.q.expect('dbus-signal', signal='TransferredBytesChanged') assert self.count == to_receive if len(waiting) > 1: state, reason = events[0].args assert state == cs.FT_STATE_COMPLETED assert reason == cs.FT_STATE_CHANGE_REASON_NONE if __name__ == '__main__': exec_file_transfer_test(SendFileSendBeforeAccept) telepathy-gabble-0.18.2/tests/twisted/file-transfer/test-send-file-provide-immediately.py0000644000175000017500000000302212200204333031531 0ustar00smcvsmcv00000000000000import dbus import constants as cs from file_transfer_helper import SendFileTest, exec_file_transfer_test from config import FILE_TRANSFER_ENABLED if not FILE_TRANSFER_ENABLED: print "NOTE: built with --disable-file-transfer" raise SystemExit(77) class SendFileTransferProvideImmediately(SendFileTest): def provide_file(self): # try to accept our outgoing file transfer try: self.ft_channel.AcceptFile(self.address_type, self.access_control, self.access_control_param, self.file.offset, byte_arrays=True) except dbus.DBusException, e: assert e.get_dbus_name() == cs.NOT_AVAILABLE else: assert False if SendFileTest.provide_file(self): return True # state is still Pending as remote didn't accept the transfer yet state = self.ft_props.Get(cs.CHANNEL_TYPE_FILE_TRANSFER, 'State') assert state == cs.FT_STATE_PENDING def client_accept_file(self): SendFileTest.client_accept_file(self) e = self.q.expect('dbus-signal', signal='InitialOffsetDefined') offset = e.args[0] assert offset == self.file.offset # Channel is open. We can start to send the file e = self.q.expect('dbus-signal', signal='FileTransferStateChanged') state, reason = e.args assert state == cs.FT_STATE_OPEN assert reason == cs.FT_STATE_CHANGE_REASON_NONE if __name__ == '__main__': exec_file_transfer_test(SendFileTransferProvideImmediately) telepathy-gabble-0.18.2/tests/twisted/file-transfer/test-send-file-declined.py0000644000175000017500000000341612227000321027336 0ustar00smcvsmcv00000000000000import dbus import constants as cs from file_transfer_helper import SendFileTest, exec_file_transfer_test from twisted.words.xish import domish import ns from config import FILE_TRANSFER_ENABLED if not FILE_TRANSFER_ENABLED: print "NOTE: built with --disable-file-transfer" raise SystemExit(77) class SendFileDeclinedTest(SendFileTest): def got_send_iq(self): SendFileTest.got_send_iq(self) # Receiver declines the file offer reply = domish.Element(('jabber:client', 'iq')) reply['to'] = 'test@localhost/Resource' reply['from'] = self.iq['to'] reply['type'] = 'error' reply['id'] = self.iq['id'] error = reply.addElement((None, 'error')) error['code'] = '403' error['type'] = 'cancel' forbidden = error.addElement((ns.STANZA, 'forbidden')) text = error.addElement((ns.STANZA, 'text'), content='Offer Declined') self.stream.send(reply) e = self.q.expect('dbus-signal', signal='FileTransferStateChanged') state, reason = e.args assert state == cs.FT_STATE_CANCELLED, state assert reason == cs.FT_STATE_CHANGE_REASON_REMOTE_STOPPED transferred = self.ft_props.Get(cs.CHANNEL_TYPE_FILE_TRANSFER, 'TransferredBytes') # no byte has been transferred as the file was declined assert transferred == 0 # try to provide the file, assert that this finishes the test (e.g. # couldn't go further because of ipv6) or that it raises # cs.NOT_AVAILABLE try: assert self.provide_file() except dbus.DBusException, e: assert e.get_dbus_name() == cs.NOT_AVAILABLE # stop test return True if __name__ == '__main__': exec_file_transfer_test(SendFileDeclinedTest) telepathy-gabble-0.18.2/tests/twisted/file-transfer/test-send-file-and-cancel-immediately.py0000644000175000017500000000165412200204333032057 0ustar00smcvsmcv00000000000000import constants as cs from file_transfer_helper import SendFileTest, exec_file_transfer_test from config import FILE_TRANSFER_ENABLED if not FILE_TRANSFER_ENABLED: print "NOTE: built with --disable-file-transfer" raise SystemExit(77) class SendFileAndCancelImmediatelyTest(SendFileTest): def provide_file(self): SendFileTest.provide_file(self) # cancel the transfer before the receiver accepts it self.channel.Close() e = self.q.expect('dbus-signal', signal='FileTransferStateChanged') state, reason = e.args assert state == cs.FT_STATE_CANCELLED assert reason == cs.FT_STATE_CHANGE_REASON_LOCAL_STOPPED self.q.expect('dbus-signal', signal='Closed') # XEP-0096 doesn't have a way to inform receiver we cancelled the # transfer... return True if __name__ == '__main__': exec_file_transfer_test(SendFileAndCancelImmediatelyTest) telepathy-gabble-0.18.2/tests/twisted/file-transfer/test-receive-file.py0000644000175000017500000000045212200204333026257 0ustar00smcvsmcv00000000000000from file_transfer_helper import exec_file_transfer_test, ReceiveFileTest from config import FILE_TRANSFER_ENABLED if not FILE_TRANSFER_ENABLED: print "NOTE: built with --disable-file-transfer" raise SystemExit(77) if __name__ == '__main__': exec_file_transfer_test(ReceiveFileTest) telepathy-gabble-0.18.2/tests/twisted/file-transfer/test-receive-file-decline.py0000644000175000017500000000211412200204333027655 0ustar00smcvsmcv00000000000000from servicetest import EventPattern from twisted.words.xish import xpath import constants as cs from file_transfer_helper import ReceiveFileTest, exec_file_transfer_test from config import FILE_TRANSFER_ENABLED if not FILE_TRANSFER_ENABLED: print "NOTE: built with --disable-file-transfer" raise SystemExit(77) class ReceiveFileDeclineTest(ReceiveFileTest): def accept_file(self): # decline FT self.channel.Close() state_event, iq_event, _ = self.q.expect_many( EventPattern('dbus-signal', signal='FileTransferStateChanged'), EventPattern('stream-iq', iq_type='error'), EventPattern('dbus-signal', signal='Closed'), ) error_node = xpath.queryForNodes('/iq/error', iq_event.stanza)[0] assert error_node['code'] == '403' state, reason = state_event.args assert state == cs.FT_STATE_CANCELLED assert reason == cs.FT_STATE_CHANGE_REASON_LOCAL_STOPPED # stop test return True if __name__ == '__main__': exec_file_transfer_test(ReceiveFileDeclineTest) test-receive-file-and-sender-disconnect-while-transfering.py0000644000175000017500000000176712200204333036005 0ustar00smcvsmcv00000000000000telepathy-gabble-0.18.2/tests/twisted/file-transferfrom twisted.words.xish import domish import constants as cs from file_transfer_helper import exec_file_transfer_test, ReceiveFileTest from config import FILE_TRANSFER_ENABLED if not FILE_TRANSFER_ENABLED: print "NOTE: built with --disable-file-transfer" raise SystemExit(77) class ReceiveFileAndSenderDisconnectWhileTransfering(ReceiveFileTest): def accept_file(self): ReceiveFileTest.accept_file(self) presence = domish.Element(('jabber:client', 'presence')) presence['from'] = self.contact_full_jid presence['to'] = 'test@localhost/Resource' presence['type'] = 'unavailable' self.stream.send(presence) e = self.q.expect('dbus-signal', signal='FileTransferStateChanged') state, reason = e.args assert state == cs.FT_STATE_CANCELLED assert reason == cs.FT_STATE_CHANGE_REASON_REMOTE_STOPPED return True if __name__ == '__main__': exec_file_transfer_test(ReceiveFileAndSenderDisconnectWhileTransfering) test-receive-file-and-sender-disconnect-while-pending.py0000644000175000017500000000266212200204333035102 0ustar00smcvsmcv00000000000000telepathy-gabble-0.18.2/tests/twisted/file-transferimport dbus from twisted.words.xish import domish import constants as cs from file_transfer_helper import exec_file_transfer_test, ReceiveFileTest from config import FILE_TRANSFER_ENABLED if not FILE_TRANSFER_ENABLED: print "NOTE: built with --disable-file-transfer" raise SystemExit(77) class ReceiveFileAndSenderDisconnectWhilePendingTest(ReceiveFileTest): def accept_file(self): # The sender of the file disconnects presence = domish.Element(('jabber:client', 'presence')) presence['from'] = self.contact_full_jid presence['to'] = 'test@localhost/Resource' presence['type'] = 'unavailable' self.stream.send(presence) e = self.q.expect('dbus-signal', signal='FileTransferStateChanged') state, reason = e.args assert state == cs.FT_STATE_CANCELLED assert reason == cs.FT_STATE_CHANGE_REASON_REMOTE_STOPPED # We can't accept the transfer now try: # IPv4 is always guaranteed to be available self.ft_channel.AcceptFile(cs.SOCKET_ADDRESS_TYPE_IPV4, cs.SOCKET_ACCESS_CONTROL_LOCALHOST, "", 0) except dbus.DBusException, e: assert e.get_dbus_name() == cs.NOT_AVAILABLE else: assert False self.close_channel() # stop the test return True if __name__ == '__main__': exec_file_transfer_test(ReceiveFileAndSenderDisconnectWhilePendingTest) telepathy-gabble-0.18.2/tests/twisted/file-transfer/test-receive-file-and-disconnect.py0000644000175000017500000000107212200204333031145 0ustar00smcvsmcv00000000000000 from file_transfer_helper import exec_file_transfer_test, ReceiveFileTest from config import FILE_TRANSFER_ENABLED if not FILE_TRANSFER_ENABLED: print "NOTE: built with --disable-file-transfer" raise SystemExit(77) class ReceiveFileAndDisconnectTest(ReceiveFileTest): def receive_file(self): s = self.create_socket() s.connect(self.address) # return True so the test will be ended and the connection # disconnected return True if __name__ == '__main__': exec_file_transfer_test(ReceiveFileAndDisconnectTest) test-receive-file-and-close-socket-while-receiving.py0000644000175000017500000000170212200204333034407 0ustar00smcvsmcv00000000000000telepathy-gabble-0.18.2/tests/twisted/file-transfer import constants as cs from file_transfer_helper import exec_file_transfer_test, ReceiveFileTest from config import FILE_TRANSFER_ENABLED if not FILE_TRANSFER_ENABLED: print "NOTE: built with --disable-file-transfer" raise SystemExit(77) class ReceiveFileAndCancelWhileReceiving(ReceiveFileTest): def receive_file(self): # Connect to Gabble's socket s = self.create_socket() s.connect(self.address) # for some reason the socket is closed s.close() # we receive one more byte from the sender self.bytestream.send_data(self.file.data[2:3]) self.q.expect('dbus-signal', signal='FileTransferStateChanged', args=[cs.FT_STATE_CANCELLED, cs.FT_STATE_CHANGE_REASON_LOCAL_ERROR]) self.channel.Close() self.q.expect('dbus-signal', signal='Closed') return True if __name__ == '__main__': exec_file_transfer_test(ReceiveFileAndCancelWhileReceiving) telepathy-gabble-0.18.2/tests/twisted/file-transfer/test-ibb-too-early.py0000644000175000017500000000174512227000321026373 0ustar00smcvsmcv00000000000000from gabbletest import exec_test import bytestream from file_transfer_helper import ReceiveFileTest, File from servicetest import EventPattern import constants as cs from config import FILE_TRANSFER_ENABLED if not FILE_TRANSFER_ENABLED: print "NOTE: built with --disable-file-transfer" raise SystemExit(77) class IbbTooEarlyTest (ReceiveFileTest): def __init__ (self): ReceiveFileTest.__init__ (self, bytestream.BytestreamIBBMsg, File (), cs.SOCKET_ADDRESS_TYPE_UNIX, cs.SOCKET_ACCESS_CONTROL_LOCALHOST, "") def accept_file (self): # Instead of us accepting the other side starts sending the iq open # skip the open step explicitely self.bytestream.checked = True event = self.bytestream.open_bytestream( expected_after = [ EventPattern ('stream-iq', iq_type = 'error') ] ) return True if __name__ == '__main__': exec_test (IbbTooEarlyTest().test) telepathy-gabble-0.18.2/tests/twisted/file-transfer/test-caps-file-transfer.py0000644000175000017500000001077512227000321027416 0ustar00smcvsmcv00000000000000import dbus from twisted.words.xish import xpath from servicetest import assertEquals from gabbletest import exec_test, make_result_iq, sync_stream, make_presence import constants as cs from caps_helper import compute_caps_hash, text_fixed_properties,\ text_allowed_properties, stream_tube_fixed_properties,\ stream_tube_allowed_properties, dbus_tube_fixed_properties,\ dbus_tube_allowed_properties, ft_fixed_properties, ft_allowed_properties import ns from config import FILE_TRANSFER_ENABLED if not FILE_TRANSFER_ENABLED: print "NOTE: built with --disable-file-transfer" raise SystemExit(77) def test_ft_caps_from_contact(q, bus, conn, stream, contact, contact_handle, client): conn_caps_iface = dbus.Interface(conn, cs.CONN_IFACE_CONTACT_CAPS) conn_contacts_iface = dbus.Interface(conn, cs.CONN_IFACE_CONTACTS) # send presence with no FT cap presence = make_presence(contact, status='hello') c = presence.addElement((ns.CAPS, 'c')) c['node'] = client c['ver'] = compute_caps_hash([], [], {}) c['hash'] = 'sha-1' stream.send(presence) # Gabble looks up our capabilities event = q.expect('stream-iq', to=contact, query_ns=ns.DISCO_INFO) query_node = xpath.queryForNodes('/iq/query', event.stanza)[0] assert query_node.attributes['node'] == \ client + '#' + c['ver'] # send good reply result = make_result_iq(stream, event.stanza) query = result.firstChildElement() query['node'] = client + '#' + c['ver'] stream.send(result) # no change in ContactCapabilities, so no signal ContactCapabilitiesChanged sync_stream(q, stream) # no special capabilities basic_caps = dbus.Dictionary({contact_handle: [(text_fixed_properties, text_allowed_properties)]}) caps = conn_caps_iface.GetContactCapabilities([contact_handle]) assert caps == basic_caps, caps # test again, to check GetContactCapabilities does not have side effect caps = conn_caps_iface.GetContactCapabilities([contact_handle]) assert caps == basic_caps, caps # check the Contacts interface give the same caps caps_via_contacts_iface = conn_contacts_iface.GetContactAttributes( [contact_handle], [cs.CONN_IFACE_CONTACT_CAPS], False) \ [contact_handle][cs.ATTR_CONTACT_CAPABILITIES] assert caps_via_contacts_iface == caps[contact_handle], \ caps_via_contacts_iface # send presence with ft capa presence = make_presence(contact, status='hello') c = presence.addElement((ns.CAPS, 'c')) c['node'] = client c['ver'] = compute_caps_hash([], [ns.FILE_TRANSFER], {}) c['hash'] = 'sha-1' stream.send(presence) # Gabble looks up our capabilities event = q.expect('stream-iq', to=contact, query_ns=ns.DISCO_INFO) query_node = xpath.queryForNodes('/iq/query', event.stanza)[0] assert query_node.attributes['node'] == \ client + '#' + c['ver'] # send good reply result = make_result_iq(stream, event.stanza) query = result.firstChildElement() query['node'] = client + '#' + c['ver'] feature = query.addElement('feature') feature['var'] = ns.FILE_TRANSFER stream.send(result) generic_tubes_caps = dbus.Dictionary({contact_handle: [(text_fixed_properties, text_allowed_properties), (ft_fixed_properties, ft_allowed_properties)]}) event = q.expect('dbus-signal', signal='ContactCapabilitiesChanged') assert len(event.args) == 1 assert event.args[0] == generic_tubes_caps caps = conn_caps_iface.GetContactCapabilities([contact_handle]) assert caps == generic_tubes_caps, caps # test again, to check GetContactCapabilities does not have side effect caps = conn_caps_iface.GetContactCapabilities([contact_handle]) assert caps == generic_tubes_caps, caps # check the Contacts interface give the same caps caps_via_contacts_iface = conn_contacts_iface.GetContactAttributes( [contact_handle], [cs.CONN_IFACE_CONTACT_CAPS], False) \ [contact_handle][cs.ATTR_CONTACT_CAPABILITIES] assert caps_via_contacts_iface == caps[contact_handle], \ caps_via_contacts_iface def test(q, bus, conn, stream): client = 'http://telepathy.freedesktop.org/fake-ft-client' test_ft_caps_from_contact(q, bus, conn, stream, 'bilbo1@foo.com/Foo', 2L, client) # our own capabilities, formerly tested here, are now in # tests/twisted/caps/advertise-contact-capabilities.py if __name__ == '__main__': exec_test(test) telepathy-gabble-0.18.2/tests/twisted/connect/0000755000175000017500000000000012312537051021272 5ustar00smcvsmcv00000000000000telepathy-gabble-0.18.2/tests/twisted/connect/torture.py0000644000175000017500000000130412200204333023334 0ustar00smcvsmcv00000000000000""" This test does nothing besides connect, and then disconnect as soon as the session is established, two thousand times. It was used to smoke out a bug where connections were leaked (which ultimately meant that new connections could not be established, since the file descriptors were leaked too); it may also be useful for profiling the connection process (and test framework). """ from gabbletest import exec_test import os if os.environ.get('REALLY_TORTURE', '') != 'yes': raise SystemExit(77) def test(q, bus, conn, stream): pass def main(): for i in xrange(0, 2000): print i exec_test(test) print "we partied like it's %i" % i if __name__ == '__main__': main() telepathy-gabble-0.18.2/tests/twisted/connect/test-twice.py0000644000175000017500000000500712227000321023724 0ustar00smcvsmcv00000000000000""" Test connecting to a server with 2 accounts, testing XmppAuthenticator and JabberAuthenticator """ import os import sys import dbus import constants as cs from gabbletest import exec_test from rostertest import expect_contact_list_signals, check_contact_list_signals from servicetest import assertLength def test(q, bus, conns, streams): conn1, conn2 = conns stream1, stream2 = streams # Connection 1 conn1.Connect() q.expect('dbus-signal', signal='StatusChanged', args=[cs.CONN_STATUS_CONNECTING, cs.CSR_REQUESTED], path=conn1.object.object_path) q.expect('stream-authenticated') q.expect('dbus-signal', signal='PresencesChanged', args=[{1L: (cs.PRESENCE_AVAILABLE, 'available', '')}], path=conn1.object.object_path) q.expect('dbus-signal', signal='StatusChanged', args=[cs.CONN_STATUS_CONNECTED, cs.CSR_REQUESTED], path=conn1.object.object_path) pairs = expect_contact_list_signals(q, bus, conn1, ['publish', 'subscribe', 'stored']) check_contact_list_signals(q, bus, conn1, pairs.pop(0), cs.HT_LIST, 'publish', []) check_contact_list_signals(q, bus, conn1, pairs.pop(0), cs.HT_LIST, 'subscribe', []) check_contact_list_signals(q, bus, conn1, pairs.pop(0), cs.HT_LIST, 'stored', []) assertLength(0, pairs) # i.e. we popped and checked all of them # Connection 2 conn2.Connect() q.expect('dbus-signal', signal='StatusChanged', args=[cs.CONN_STATUS_CONNECTING, cs.CSR_REQUESTED], path=conn2.object.object_path) q.expect('stream-authenticated') q.expect('dbus-signal', signal='PresencesChanged', args=[{1L: (cs.PRESENCE_AVAILABLE, 'available', '')}], path=conn2.object.object_path) q.expect('dbus-signal', signal='StatusChanged', args=[cs.CONN_STATUS_CONNECTED, cs.CSR_REQUESTED], path=conn2.object.object_path) pairs = expect_contact_list_signals(q, bus, conn2, ['publish', 'subscribe', 'stored']) check_contact_list_signals(q, bus, conn2, pairs.pop(0), cs.HT_LIST, 'publish', []) check_contact_list_signals(q, bus, conn2, pairs.pop(0), cs.HT_LIST, 'subscribe', []) check_contact_list_signals(q, bus, conn2, pairs.pop(0), cs.HT_LIST, 'stored', []) assertLength(0, pairs) # i.e. we popped and checked all of them if __name__ == '__main__': exec_test(test, num_instances=2, do_connect=False) telepathy-gabble-0.18.2/tests/twisted/connect/test-success.py0000644000175000017500000000110012200204333024247 0ustar00smcvsmcv00000000000000 """ Test connecting to a server. """ from gabbletest import exec_test import constants as cs def test(q, bus, conn, stream): conn.Connect() q.expect('dbus-signal', signal='StatusChanged', args=[cs.CONN_STATUS_CONNECTING, cs.CSR_REQUESTED]) q.expect('stream-authenticated') q.expect('dbus-signal', signal='PresencesChanged', args=[{1L: (cs.PRESENCE_AVAILABLE, 'available', '')}]) q.expect('dbus-signal', signal='StatusChanged', args=[cs.CONN_STATUS_CONNECTED, cs.CSR_REQUESTED]) if __name__ == '__main__': exec_test(test, do_connect=False) telepathy-gabble-0.18.2/tests/twisted/connect/test-nonblocking-tls.py0000644000175000017500000001133012200204333025710 0ustar00smcvsmcv00000000000000 """ Test connecting to a server with 2 accounts. Check one account does not block the second account. """ import os import sys import dbus import servicetest from twisted.words.xish import domish from twisted.words.protocols.jabber import xmlstream import twisted.internet.protocol from twisted.internet import reactor from servicetest import (Event, unwrap) from gabbletest import ( make_connection, make_stream, XmppAuthenticator, XmppXmlStream, disconnect_conn, GabbleAuthenticator) import constants as cs NS_XMPP_TLS = 'urn:ietf:params:xml:ns:xmpp-tls' NS_XMPP_SASL = 'urn:ietf:params:xml:ns:xmpp-sasl' class BlockForeverTlsAuthenticator(GabbleAuthenticator): """A TLS stream authenticator that is deliberately broken. It sends to the client but then do nothing, so the TLS handshake will not work. Useful for testing regression of bug #14341.""" def __init__(self, username, password): GabbleAuthenticator.__init__(self, username, password) self.username = username self.password = password self.authenticated = False def streamStarted(self, root=None): if root: self.xmlstream.sid = root.getAttribute('id') self.xmlstream.sendHeader() features = domish.Element((xmlstream.NS_STREAMS, 'features')) mechanisms = features.addElement((NS_XMPP_SASL, 'mechanisms')) mechanism = mechanisms.addElement('mechanism', content='DIGEST-MD5') starttls = features.addElement((NS_XMPP_TLS, 'starttls')) starttls.addElement('required') self.xmlstream.send(features) self.xmlstream.addOnetimeObserver("/starttls", self.auth) def auth(self, auth): proceed = domish.Element((NS_XMPP_TLS, 'proceed')) self.xmlstream.send(proceed) return; # auth blocks self.xmlstream.reset() self.authenticated = True def test(q, bus, conn1, conn2, stream1, stream2): # Connection 1 conn1.Connect() q.expect('dbus-signal', signal='StatusChanged', args=[cs.CONN_STATUS_CONNECTING, cs.CSR_REQUESTED]) # Connection 1 blocks because the fake jabber server behind conn1 does not # proceed to the tls handshake. The second connection is independant and # should work. # Connection 2 conn2.Connect() q.expect('dbus-signal', signal='StatusChanged', args=[cs.CONN_STATUS_CONNECTING, cs.CSR_REQUESTED]) q.expect('stream-authenticated') q.expect('dbus-signal', signal='PresencesChanged', args=[{1L: (cs.PRESENCE_AVAILABLE, 'available', '')}]) q.expect('dbus-signal', signal='StatusChanged', args=[cs.CONN_STATUS_CONNECTED, cs.CSR_REQUESTED]) # Disconnection 2 disconnect_conn(q, conn2, stream2) if __name__ == '__main__': queue = servicetest.IteratingEventQueue(None) queue.verbose = ( os.environ.get('CHECK_TWISTED_VERBOSE', '') != '' or '-v' in sys.argv) bus = dbus.SessionBus() params = { 'account': 'test1@localhost/Resource', 'password': 'pass', 'resource': 'Resource', 'server': 'localhost', 'port': dbus.UInt32(4242), } conn1, jid1 = make_connection(bus, queue.append, params) authenticator = BlockForeverTlsAuthenticator('test1', 'pass') stream1 = make_stream(queue.append, authenticator, protocol=XmppXmlStream) factory = twisted.internet.protocol.Factory() factory.protocol = lambda:stream1 port1 = reactor.listenTCP(4242, factory, interface='localhost') params = { 'account': 'test2@localhost/Resource', 'password': 'pass', 'resource': 'Resource', 'server': 'localhost', 'port': dbus.UInt32(4343), } conn2, jid2 = make_connection(bus, queue.append, params) authenticator = XmppAuthenticator('test2', 'pass') stream2 = make_stream(queue.append, authenticator, protocol=XmppXmlStream) factory = twisted.internet.protocol.Factory() factory.protocol = lambda:stream2 port1 = reactor.listenTCP(4343, factory, interface='localhost') bus.add_signal_receiver( lambda *args, **kw: queue.append(Event('dbus-signal', path=unwrap(kw['path']), signal=kw['member'], args=map(unwrap, args), interface=kw['interface'])), None, # signal name None, # interface None, path_keyword='path', member_keyword='member', interface_keyword='interface', byte_arrays=True ) try: test(queue, bus, conn1, conn2, stream1, stream2) finally: try: conn1.Disconnect() conn2.Disconnect() except dbus.DBusException, e: pass telepathy-gabble-0.18.2/tests/twisted/connect/test-fail.py0000644000175000017500000000640412200204333023526 0ustar00smcvsmcv00000000000000""" Test various ways in which connections can fail. """ from twisted.words.xish import domish from twisted.words.protocols.jabber import xmlstream import dbus from servicetest import assertEquals from gabbletest import exec_test, GabbleAuthenticator import constants as cs import ns def test_network_error(q, bus, conn, stream): conn.Connect() q.expect('dbus-signal', signal='StatusChanged', args=[cs.CONN_STATUS_CONNECTING, cs.CSR_REQUESTED]) # FIXME: this is G_IO_ERROR_FAILED, which we can't really map to anything # better than NetworkError. The debug message says "Connection refused", # so something, somewhere, ought to be able to do better, and give us # enough information to produce cs.CONNECTION_REFUSED. new = q.expect('dbus-signal', signal='ConnectionError') assertEquals(cs.NETWORK_ERROR, new.args[0]) q.expect('dbus-signal', signal='StatusChanged', args=[cs.CONN_STATUS_DISCONNECTED, cs.CSR_NETWORK_ERROR]) def test_conflict_after_connect(q, bus, conn, stream): stream.send_stream_error('conflict') new = q.expect('dbus-signal', signal='ConnectionError') assertEquals(cs.CONNECTION_REPLACED, new.args[0]) q.expect('dbus-signal', signal='StatusChanged', args=[cs.CONN_STATUS_DISCONNECTED, cs.CSR_NAME_IN_USE]) class StreamErrorAuthenticator(GabbleAuthenticator): def __init__(self, stream_error): GabbleAuthenticator.__init__(self, username='n/a', password='n/a') self.__stream_error = stream_error def streamStarted(self, root=None): if root: self.xmlstream.sid = root.getAttribute('id') self.xmlstream.sendHeader() no = domish.Element((xmlstream.NS_STREAMS, 'error')) no.addElement((ns.STREAMS, self.__stream_error)) self.xmlstream.send(no) def test_stream_conflict_during_connect(q, bus, conn, stream): conn.Connect() q.expect('dbus-signal', signal='StatusChanged', args=[cs.CONN_STATUS_CONNECTING, cs.CSR_REQUESTED]) new = q.expect('dbus-signal', signal='ConnectionError') assertEquals(cs.ALREADY_CONNECTED, new.args[0]) old = q.expect('dbus-signal', signal='StatusChanged') status, reason = old.args assertEquals(cs.CONN_STATUS_DISCONNECTED, status) assertEquals(cs.CSR_NAME_IN_USE, reason) def test_host_unknown(q, bus, conn, stream): conn.Connect() q.expect('dbus-signal', signal='StatusChanged', args=[cs.CONN_STATUS_CONNECTING, cs.CSR_REQUESTED]) new = q.expect('dbus-signal', signal='ConnectionError') assertEquals(cs.AUTHENTICATION_FAILED, new.args[0]) old = q.expect('dbus-signal', signal='StatusChanged') status, reason = old.args assertEquals(cs.CONN_STATUS_DISCONNECTED, status) assertEquals(cs.CSR_AUTHENTICATION_FAILED, reason) if __name__ == '__main__': exec_test(test_network_error, {'port': dbus.UInt32(4243)}, do_connect=False) exec_test(test_conflict_after_connect) exec_test(test_stream_conflict_during_connect, authenticator=StreamErrorAuthenticator('conflict'), do_connect=False) exec_test(test_host_unknown, {'server': 'localhost', 'account': 'test@example.org', }, authenticator=StreamErrorAuthenticator('host-unknown'), do_connect=False) telepathy-gabble-0.18.2/tests/twisted/connect/test-connection-params.py0000644000175000017500000000327212200204333026233 0ustar00smcvsmcv00000000000000 """ Test connecting with different ContactList.DownloadAtConnection values """ import dbus from servicetest import EventPattern from gabbletest import exec_test, sync_stream, call_async import constants as cs import ns forbidden = [EventPattern('stream-iq', query_ns=ns.ROSTER)] def test_get_roster(q, bus, conn, stream): # DownloadAtConnection = True, so gabble should get the roster # automatically q.expect('stream-iq', query_ns=ns.ROSTER) # but calling ContactList.Download should not try and get the # roster again q.forbid_events(forbidden) conn.ContactList.Download() sync_stream(q, stream) q.unforbid_events(forbidden) def test_dont_get_roster(q, bus, conn, stream): # DownloadAtConnection = False, so let's make sure the roster # isn't fetched automatically q.forbid_events(forbidden) conn.Connect() q.expect_many(EventPattern('dbus-signal', signal='StatusChanged', args=[cs.CONN_STATUS_CONNECTED, cs.CSR_REQUESTED])) sync_stream(q, stream) q.unforbid_events(forbidden) # seems fine, now calling Download should try and get the roster # successfully. call_async(q, conn.ContactList, 'Download') q.expect_many(EventPattern('stream-iq', query_ns=ns.ROSTER), EventPattern('dbus-return', method='Download')) if __name__ == '__main__': # Parameter DownloadAtConnection = True exec_test(test_get_roster, params={cs.CONN_IFACE_CONTACT_LIST + '.DownloadAtConnection': True}) # Parameter DownloadAtConnection = False exec_test(test_dont_get_roster, params={cs.CONN_IFACE_CONTACT_LIST + '.DownloadAtConnection': False}, do_connect=False) telepathy-gabble-0.18.2/tests/twisted/connect/stream-closed.py0000644000175000017500000000104212200204333024371 0ustar00smcvsmcv00000000000000 """ Connection is disconnected because server closes its XMPP stream. """ from gabbletest import exec_test from servicetest import EventPattern import constants as cs def test(q, bus, conn, stream): # server closes its stream stream.sendFooter() # Gabble disconnect and close its connection q.expect_many( EventPattern('dbus-signal', signal='StatusChanged', args=[cs.CONN_STATUS_DISCONNECTED, cs.CSR_NETWORK_ERROR]), EventPattern('stream-closed')) if __name__ == '__main__': exec_test(test) telepathy-gabble-0.18.2/tests/twisted/connect/network-error.py0000644000175000017500000000141112200204333024447 0ustar00smcvsmcv00000000000000 """ Connection is disconnected because server closes its TCP stream abruptly. """ from gabbletest import exec_test from servicetest import EventPattern import constants as cs import sys def test(q, bus, conn, stream): # server closes its stream stream.transport.loseConnection() # Gabble disconnect and close its connection q.expect('dbus-signal', signal='StatusChanged', args=[cs.CONN_STATUS_DISCONNECTED, cs.CSR_NONE_SPECIFIED]) q.expect('dbus-signal', signal='NameOwnerChanged', predicate=lambda e: cs.CONN + '.gabble.jabber' in str(e.args[0]) and str(e.args[1]) != '' and str(e.args[2]) == '') if __name__ == '__main__': exec_test(test) telepathy-gabble-0.18.2/tests/twisted/connect/disco-no-reply.py0000644000175000017500000000172512200204333024503 0ustar00smcvsmcv00000000000000""" Test that Gabble disconnects connection if it doesn't receive a response to its initial service discovery request to the server """ from gabbletest import exec_test, XmppXmlStream import constants as cs def test(q, bus, conn, stream): conn.Connect() # connecting q.expect('dbus-signal', signal='StatusChanged', args=[cs.CONN_STATUS_CONNECTING, cs.CSR_REQUESTED]), # We are disconnected with Connection_Status_Reason_Network_Error as the # disco request timed out q.expect('dbus-signal', signal='StatusChanged', args=[cs.CONN_STATUS_DISCONNECTED, cs.CSR_NETWORK_ERROR]), class JabberXmlStreamNoDiscoReply(XmppXmlStream): """Subclass XmppXmlStream not to respond to disco requests to the server.""" def _cb_disco_iq (self, iq): pass if __name__ == '__main__': # telepathy-gabble-debug has been tweaked to time out after 3 seconds exec_test(test, protocol=JabberXmlStreamNoDiscoReply, do_connect=False) telepathy-gabble-0.18.2/tests/twisted/connect/disconnect-timeout.py0000644000175000017500000000125012200204333025445 0ustar00smcvsmcv00000000000000""" Disconnect the connection but the server doesn't send its stream close stanza. After a while Gabble gives up, force the closing and the Disconnect D-Bus call returns. """ from gabbletest import exec_test from servicetest import call_async, EventPattern import constants as cs def test(q, bus, conn, stream): call_async(q, conn, 'Disconnect') q.expect_many( EventPattern('dbus-signal', signal='StatusChanged', args=[cs.CONN_STATUS_DISCONNECTED, cs.CSR_REQUESTED]), EventPattern('stream-closed')) q.expect('dbus-return', method='Disconnect') if __name__ == '__main__': # Gabble will time out after 5 seconds exec_test(test, timeout=10) telepathy-gabble-0.18.2/tests/twisted/connect/disco-facebook.py0000644000175000017500000000302612213334426024516 0ustar00smcvsmcv00000000000000""" Test that Gabble is tolerant of non-RFC-compliance from Facebook. https://bugs.freedesktop.org/show_bug.cgi?id=68829 """ from gabbletest import exec_test, XmppXmlStream import constants as cs import ns from twisted.words.xish import xpath def test(q, bus, conn, stream): conn.Connect() # everything is fine and actually very boring q.expect('dbus-signal', signal='StatusChanged', args=[cs.CONN_STATUS_CONNECTING, cs.CSR_REQUESTED]), q.expect('dbus-signal', signal='StatusChanged', args=[cs.CONN_STATUS_CONNECTED, cs.CSR_REQUESTED]) class XmppXmlStreamFacebook201309(XmppXmlStream): """As of 2013-09, a new version of Facebook's XMPP server (used consistently for beta.chat.facebook.com, and gradually being rolled out for chat.facebook.com users) omits the 'from' attribute in its disco reply. The disco reply is otherwise correct. """ def _cb_disco_iq(self, iq): nodes = xpath.queryForNodes( "/iq/query[@xmlns='" + ns.DISCO_INFO + "']", iq) query = nodes[0] for feature in self.disco_features: query.addChild(elem('feature', var=feature)) iq['type'] = 'result' # The Facebook server's IQ responses have neither 'from' nor 'to' try: del iq['from'] except KeyError: pass try: del iq['to'] except KeyError: pass self.send(iq) if __name__ == '__main__': exec_test(test, protocol=XmppXmlStreamFacebook201309, do_connect=False) telepathy-gabble-0.18.2/tests/twisted/connect/disco-error-from-bare-jid.py0000644000175000017500000000257512200204333026507 0ustar00smcvsmcv00000000000000""" When we connect, Gabble sends a disco request both to the server, and to our own bare JID (to find out whether we support PEP). Some servers return errors from the latter; Gabble should ignore the error, and connect anyway. This tests """ from gabbletest import exec_test, XmppXmlStream, elem, send_error_reply import constants as cs import ns def test(q, bus, conn, stream): conn.Connect() # We start connecting... q.expect('dbus-signal', signal='StatusChanged', args=[cs.CONN_STATUS_CONNECTING, cs.CSR_REQUESTED]), # ...and then we finish connecting, despite the server having got upset # when we auto-discoed. Party on! q.expect('dbus-signal', signal='StatusChanged', args=[cs.CONN_STATUS_CONNECTED, cs.CSR_REQUESTED]) class BareJidDiscoErrorXmlStream(XmppXmlStream): def _cb_bare_jid_disco_iq(self, iq): """ \o\ /o/ /o> ... "Crap! It's the cops! Turn the music off!" """ send_error_reply(self, iq, error_stanza=elem('error')( elem(ns.STANZA, 'feature-not-implemented'), elem(ns.STANZA, 'text')( u'No, officer! This is certainly not an illegal party.') )) if __name__ == '__main__': exec_test(test, protocol=BareJidDiscoErrorXmlStream, do_connect=False) telepathy-gabble-0.18.2/tests/twisted/cm/0000755000175000017500000000000012312537051020240 5ustar00smcvsmcv00000000000000telepathy-gabble-0.18.2/tests/twisted/cm/protocol.py0000644000175000017500000000670512227000321022451 0ustar00smcvsmcv00000000000000""" Test Gabble's o.T.Protocol implementation """ import dbus from servicetest import unwrap, tp_path_prefix, assertEquals from gabbletest import exec_test, call_async import constants as cs import time def test(q, bus, conn, stream): cm = bus.get_object(cs.CM + '.gabble', tp_path_prefix + '/ConnectionManager/gabble') cm_iface = dbus.Interface(cm, cs.CM) cm_prop_iface = dbus.Interface(cm, cs.PROPERTIES_IFACE) protocols = unwrap(cm_prop_iface.Get(cs.CM, 'Protocols')) assertEquals(set(['jabber']), set(protocols.keys())) protocol_names = unwrap(cm_iface.ListProtocols()) assertEquals(set(['jabber']), set(protocol_names)) cm_params = cm_iface.GetParameters('jabber') jabber_props = protocols['jabber'] jabber_params = jabber_props[cs.PROTOCOL + '.Parameters'] assertEquals(cm_params, jabber_params) proto = bus.get_object(cm.bus_name, cm.object_path + '/jabber') proto_iface = dbus.Interface(proto, cs.PROTOCOL) proto_prop_iface = dbus.Interface(proto, cs.PROPERTIES_IFACE) proto_props = unwrap(proto_prop_iface.GetAll(cs.PROTOCOL)) for key in ['Parameters', 'Interfaces', 'ConnectionInterfaces', 'RequestableChannelClasses', u'VCardField', u'EnglishName', u'Icon']: a = jabber_props[cs.PROTOCOL + '.' + key] b = proto_props[key] assertEquals(a, b) assertEquals('foo@mit.edu', unwrap(proto_iface.NormalizeContact('foo@MIT.Edu/Telepathy'))) # org.freedesktop.Telepathy.Protocol.Interface.Presence expected_status = {'available': (cs.PRESENCE_AVAILABLE, True, True), 'dnd' : (cs.PRESENCE_BUSY, True, True), 'unknown' : (cs.PRESENCE_UNKNOWN, False, False), 'away' : (cs.PRESENCE_AWAY, True, True), 'xa' : (cs.PRESENCE_EXTENDED_AWAY, True, True), 'chat' : (cs.PRESENCE_AVAILABLE, True, True), 'error' : (cs.PRESENCE_ERROR, False, False), 'offline' : (cs.PRESENCE_OFFLINE, False, False), 'testaway' : (cs.PRESENCE_AWAY, False, False), 'testbusy' : (cs.PRESENCE_BUSY, True, False), 'hidden' : (cs.PRESENCE_HIDDEN, True, True)} presences = proto_prop_iface.Get(cs.PROTOCOL_IFACE_PRESENCES, 'Statuses'); # Plugins could add additional statuses, so we check if expected_status is # included in presences rather than equality. for k, v in expected_status.iteritems(): assertEquals(expected_status[k], unwrap(presences[k])) # (Only) 'account' is mandatory for IdentifyAccount() call_async(q, proto_iface, 'IdentifyAccount', {}) q.expect('dbus-error', method='IdentifyAccount', name=cs.INVALID_ARGUMENT) test_params = { 'account': 'test@localhost' } acc_name = unwrap(proto_iface.IdentifyAccount(test_params)) assertEquals(test_params['account'], acc_name) conn.Connect() q.expect('dbus-signal', signal='StatusChanged', args=[cs.CONN_STATUS_CONNECTING, cs.CSR_REQUESTED]) q.expect('stream-authenticated') q.expect('dbus-signal', signal='PresencesChanged', args=[{1L: (cs.PRESENCE_AVAILABLE, 'available', '')}]) q.expect('dbus-signal', signal='StatusChanged', args=[cs.CONN_STATUS_CONNECTED, cs.CSR_REQUESTED]) return if __name__ == '__main__': exec_test(test, do_connect=False) telepathy-gabble-0.18.2/tests/twisted/caps/0000755000175000017500000000000012312537051020567 5ustar00smcvsmcv00000000000000telepathy-gabble-0.18.2/tests/twisted/caps/tube-caps.py0000644000175000017500000003741512227000321023024 0ustar00smcvsmcv00000000000000 """ Test tubes capabilities with Connection.Interface.ContactCapabilities 1. Receive presence and caps from contacts and check that GetContactCapabilities works correctly and that ContactCapabilitiesChanged is correctly received. Also check that GetContactAttributes gives the same results. - no tube cap at all - 1 stream tube cap - 1 D-Bus tube cap - 1 stream tube + 1 D-Bus tube caps - 2 stream tube + 2 D-Bus tube caps - 1 stream tube + 1 D-Bus tube caps, again, to test whether the caps cache works with tubes 2. Test UpdateCapabilities and test that a presence stanza is sent to the contacts, test that the D-Bus signal ContactCapabilitiesChanged is fired for the self handle, ask Gabble for its caps with an iq request, check the reply is correct, and ask Gabble for its caps using D-Bus method GetContactCapabilities. Also check that GetContactAttributes gives the same results. Ensure that just a Requested=True channel class in a client filter doesn't make a tube service advertised as a cap. - no tube cap at all - 1 Requested=True stream tube cap - 1 stream tube cap - 1 D-Bus tube cap + 1 Requested=True cap - 1 stream tube + 1 D-Bus tube caps + 1 Requested=True cap - 2 stream tube + 2 D-Bus tube caps - 1 stream tube + 1 D-Bus tube caps, again, just for the fun """ import dbus from twisted.words.xish import xpath from servicetest import assertEquals, assertLength, assertContains,\ assertDoesNotContain from gabbletest import exec_test, make_result_iq, sync_stream, make_presence import constants as cs from caps_helper import compute_caps_hash, text_fixed_properties,\ text_allowed_properties, stream_tube_fixed_properties, stream_tube_allowed_properties,\ dbus_tube_fixed_properties, dbus_tube_allowed_properties, receive_presence_and_ask_caps,\ caps_contain, ft_fixed_properties, ft_allowed_properties import ns specialized_tube_allowed_properties = dbus.Array([cs.TARGET_HANDLE, cs.TARGET_ID]) bidir_daap_fixed_properties = dbus.Dictionary({ cs.TARGET_HANDLE_TYPE: cs.HT_CONTACT, cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_STREAM_TUBE, cs.STREAM_TUBE_SERVICE: 'daap' }) outgoing_daap_fixed_properties = dbus.Dictionary({ cs.TARGET_HANDLE_TYPE: cs.HT_CONTACT, cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_STREAM_TUBE, cs.REQUESTED : True, cs.STREAM_TUBE_SERVICE: 'daap' }) incoming_daap_fixed_properties = dbus.Dictionary({ cs.TARGET_HANDLE_TYPE: cs.HT_CONTACT, cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_STREAM_TUBE, cs.REQUESTED : False, cs.STREAM_TUBE_SERVICE: 'daap' }) http_fixed_properties = dbus.Dictionary({ cs.TARGET_HANDLE_TYPE: cs.HT_CONTACT, cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_STREAM_TUBE, cs.STREAM_TUBE_SERVICE: 'http' }) xiangqi_fixed_properties = dbus.Dictionary({ cs.TARGET_HANDLE_TYPE: cs.HT_CONTACT, cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_DBUS_TUBE, cs.DBUS_TUBE_SERVICE_NAME: 'com.example.Xiangqi' }) go_fixed_properties = dbus.Dictionary({ cs.TARGET_HANDLE_TYPE: cs.HT_CONTACT, cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_DBUS_TUBE, cs.DBUS_TUBE_SERVICE_NAME: 'com.example.Go' }) client = 'http://telepathy.freedesktop.org/fake-client' def assertSameElements(a, b): assertEquals(sorted(a), sorted(b)) def receive_caps(q, conn, stream, contact, contact_handle, features, expected_caps, expect_disco=True, expect_ccc=True): presence = make_presence(contact, status='hello') c = presence.addElement((ns.CAPS, 'c')) c['node'] = client c['ver'] = compute_caps_hash([], features if features is not None else [], {}) c['hash'] = 'sha-1' stream.send(presence) if expect_disco: # Gabble looks up our capabilities event = q.expect('stream-iq', to=contact, query_ns=ns.DISCO_INFO) query_node = xpath.queryForNodes('/iq/query', event.stanza)[0] assert query_node.attributes['node'] == \ client + '#' + c['ver'] # send good reply result = make_result_iq(stream, event.stanza) query = result.firstChildElement() query['node'] = client + '#' + c['ver'] for f in features: feature = query.addElement('feature') feature['var'] = f stream.send(result) if expect_ccc: event = q.expect('dbus-signal', signal='ContactCapabilitiesChanged') announced_ccs, = event.args assertSameElements(expected_caps, announced_ccs) else: # Make sure Gabble's got the caps sync_stream(q, stream) caps = conn.ContactCapabilities.GetContactCapabilities([contact_handle]) assertSameElements(expected_caps, caps) # test again, to check GetContactCapabilities does not have side effect caps = conn.ContactCapabilities.GetContactCapabilities([contact_handle]) assertSameElements(expected_caps, caps) # check the Contacts interface give the same caps caps_via_contacts_iface = conn.Contacts.GetContactAttributes( [contact_handle], [cs.CONN_IFACE_CONTACT_CAPS], False) \ [contact_handle][cs.ATTR_CONTACT_CAPABILITIES] assertSameElements(caps[contact_handle], caps_via_contacts_iface) def test_tube_caps_from_contact(q, bus, conn, stream, contact): contact_handle = conn.RequestHandles(cs.HT_CONTACT, [contact])[0] # Check that we don't crash if we haven't seen any caps/presence for this # contact yet. caps = conn.ContactCapabilities.GetContactCapabilities([contact_handle]) basic_caps = dbus.Dictionary({contact_handle: [(text_fixed_properties, text_allowed_properties)]}) # Since we don't know their caps, they should be omitted from the dict, # rather than present with no caps, but all contacts have text chat caps. assertEquals(basic_caps, caps) # send presence with no tube cap # We don't expect ContactCapabilitiesChanged to be emitted here: we always # assume people can do text channels. receive_caps(q, conn, stream, contact, contact_handle, [], basic_caps, expect_ccc=False) # send presence with generic tubes caps generic_tubes_caps = dbus.Dictionary({contact_handle: [(text_fixed_properties, text_allowed_properties), (stream_tube_fixed_properties, stream_tube_allowed_properties), (dbus_tube_fixed_properties, dbus_tube_allowed_properties)]}) receive_caps(q, conn, stream, contact, contact_handle, [ns.TUBES], generic_tubes_caps) # send presence with 1 stream tube cap daap_caps = dbus.Dictionary({contact_handle: [(text_fixed_properties, text_allowed_properties), (stream_tube_fixed_properties, stream_tube_allowed_properties), (dbus_tube_fixed_properties, dbus_tube_allowed_properties), (incoming_daap_fixed_properties, specialized_tube_allowed_properties)]}) receive_caps(q, conn, stream, contact, contact_handle, [ns.TUBES + '/stream#daap'], daap_caps) # send presence with 1 D-Bus tube cap xiangqi_caps = dbus.Dictionary({contact_handle: [(text_fixed_properties, text_allowed_properties), (stream_tube_fixed_properties, stream_tube_allowed_properties), (dbus_tube_fixed_properties, dbus_tube_allowed_properties), (xiangqi_fixed_properties, specialized_tube_allowed_properties)]}) receive_caps(q, conn, stream, contact, contact_handle, [ns.TUBES + '/dbus#com.example.Xiangqi'], xiangqi_caps) # send presence with both D-Bus and stream tube caps daap_xiangqi_caps = dbus.Dictionary({contact_handle: [(text_fixed_properties, text_allowed_properties), (stream_tube_fixed_properties, stream_tube_allowed_properties), (dbus_tube_fixed_properties, dbus_tube_allowed_properties), (incoming_daap_fixed_properties, specialized_tube_allowed_properties), (xiangqi_fixed_properties, specialized_tube_allowed_properties)]}) receive_caps(q, conn, stream, contact, contact_handle, [ns.TUBES + '/dbus#com.example.Xiangqi', ns.TUBES + '/stream#daap', ], daap_xiangqi_caps) # send presence with 4 tube caps all_tubes_caps = dbus.Dictionary({contact_handle: [(text_fixed_properties, text_allowed_properties), (stream_tube_fixed_properties, stream_tube_allowed_properties), (dbus_tube_fixed_properties, dbus_tube_allowed_properties), (incoming_daap_fixed_properties, specialized_tube_allowed_properties), (http_fixed_properties, specialized_tube_allowed_properties), (xiangqi_fixed_properties, specialized_tube_allowed_properties), (go_fixed_properties, specialized_tube_allowed_properties)]}) receive_caps(q, conn, stream, contact, contact_handle, [ns.TUBES + '/dbus#com.example.Xiangqi', ns.TUBES + '/dbus#com.example.Go', ns.TUBES + '/stream#daap', ns.TUBES + '/stream#http', ], all_tubes_caps) # send presence with both D-Bus and stream tube caps # Gabble does not look up our capabilities because of the cache receive_caps(q, conn, stream, contact, contact_handle, [ns.TUBES + '/dbus#com.example.Xiangqi', ns.TUBES + '/stream#daap', ], daap_xiangqi_caps, expect_disco=False) def advertise_caps(q, conn, stream, filters, expected_features, unexpected_features, expected_caps): self_handle = conn.GetSelfHandle() ret_caps = conn.ContactCapabilities.UpdateCapabilities( [(cs.CLIENT + '.Foo', filters, [])]) # Expect Gabble to reply with the correct caps event, namespaces, _, signaled_caps = receive_presence_and_ask_caps(q, stream) assertSameElements(expected_caps, signaled_caps) assertContains(ns.TUBES, namespaces) for var in expected_features: assertContains(var, namespaces) for var in unexpected_features: assertDoesNotContain(var, namespaces) # Check our own caps caps = conn.ContactCapabilities.GetContactCapabilities([self_handle]) assertSameElements(expected_caps, caps) # check the Contacts interface give the same caps caps_via_contacts_iface = conn.Contacts.GetContactAttributes( [self_handle], [cs.CONN_IFACE_CONTACT_CAPS], False) \ [self_handle][cs.ATTR_CONTACT_CAPABILITIES] assertSameElements(caps[self_handle], caps_via_contacts_iface) def test_tube_caps_to_contact(q, bus, conn, stream): self_handle = conn.GetSelfHandle() basic_caps = dbus.Dictionary({self_handle: [(text_fixed_properties, text_allowed_properties), (stream_tube_fixed_properties, stream_tube_allowed_properties), (dbus_tube_fixed_properties, dbus_tube_allowed_properties), ]}) daap_caps = dbus.Dictionary({self_handle: [(text_fixed_properties, text_allowed_properties), (stream_tube_fixed_properties, stream_tube_allowed_properties), (dbus_tube_fixed_properties, dbus_tube_allowed_properties), (incoming_daap_fixed_properties, specialized_tube_allowed_properties), (ft_fixed_properties, ft_allowed_properties)]}) xiangqi_caps = dbus.Dictionary({self_handle: [(text_fixed_properties, text_allowed_properties), (stream_tube_fixed_properties, stream_tube_allowed_properties), (dbus_tube_fixed_properties, dbus_tube_allowed_properties), (xiangqi_fixed_properties, specialized_tube_allowed_properties), (ft_fixed_properties, ft_allowed_properties)]}) daap_xiangqi_caps = dbus.Dictionary({self_handle: [(text_fixed_properties, text_allowed_properties), (stream_tube_fixed_properties, stream_tube_allowed_properties), (dbus_tube_fixed_properties, dbus_tube_allowed_properties), (incoming_daap_fixed_properties, specialized_tube_allowed_properties), (xiangqi_fixed_properties, specialized_tube_allowed_properties), (ft_fixed_properties, ft_allowed_properties)]}) all_tubes_caps = dbus.Dictionary({self_handle: [(text_fixed_properties, text_allowed_properties), (stream_tube_fixed_properties, stream_tube_allowed_properties), (dbus_tube_fixed_properties, dbus_tube_allowed_properties), (incoming_daap_fixed_properties, specialized_tube_allowed_properties), (http_fixed_properties, specialized_tube_allowed_properties), (xiangqi_fixed_properties, specialized_tube_allowed_properties), (go_fixed_properties, specialized_tube_allowed_properties), (ft_fixed_properties, ft_allowed_properties)]}) # Check our own caps caps = conn.ContactCapabilities.GetContactCapabilities([self_handle]) assertEquals(basic_caps, caps) # check the Contacts interface give the same caps caps_via_contacts_iface = conn.Contacts.GetContactAttributes( [self_handle], [cs.CONN_IFACE_CONTACT_CAPS], False) \ [self_handle][cs.ATTR_CONTACT_CAPABILITIES] assertEquals(caps[self_handle], caps_via_contacts_iface) # Advertise nothing conn.ContactCapabilities.UpdateCapabilities( [(cs.CLIENT + '.Foo', {}, [])]) # Check our own caps caps = conn.ContactCapabilities.GetContactCapabilities([self_handle]) assertLength(1, caps) assertEquals(basic_caps, caps) # check the Contacts interface give the same caps caps_via_contacts_iface = conn.Contacts.GetContactAttributes( [self_handle], [cs.CONN_IFACE_CONTACT_CAPS], False) \ [self_handle][cs.ATTR_CONTACT_CAPABILITIES] assertEquals(caps[self_handle], caps_via_contacts_iface) sync_stream(q, stream) # Advertise a Requested=True tube cap conn.ContactCapabilities.UpdateCapabilities( [(cs.CLIENT + '.Foo', [outgoing_daap_fixed_properties], [])]) # Check our own caps caps = conn.ContactCapabilities.GetContactCapabilities([self_handle]) assertLength(1, caps) assertEquals(basic_caps, caps) # check the Contacts interface give the same caps caps_via_contacts_iface = conn.Contacts.GetContactAttributes( [self_handle], [cs.CONN_IFACE_CONTACT_CAPS], False) \ [self_handle][cs.ATTR_CONTACT_CAPABILITIES] assertEquals(caps[self_handle], caps_via_contacts_iface) sync_stream(q, stream) advertise_caps(q, conn, stream, [bidir_daap_fixed_properties], [ns.TUBES + '/stream#daap'], [ns.TUBES + '/stream#http', ns.TUBES + '/dbus#com.example.Go', ns.TUBES + '/dbus#com.example.Xiangqi', ], daap_caps) advertise_caps(q, conn, stream, [outgoing_daap_fixed_properties, xiangqi_fixed_properties], [ns.TUBES + '/dbus#com.example.Xiangqi'], [ns.TUBES + '/stream#daap', ns.TUBES + '/stream#http', ns.TUBES + '/dbus#com.example.Go', ], xiangqi_caps) advertise_caps(q, conn, stream, [incoming_daap_fixed_properties, outgoing_daap_fixed_properties, xiangqi_fixed_properties], [ns.TUBES + '/dbus#com.example.Xiangqi', ns.TUBES + '/stream#daap', ], [ns.TUBES + '/stream#http', ns.TUBES + '/dbus#com.example.Go', ], daap_xiangqi_caps) advertise_caps(q, conn, stream, [incoming_daap_fixed_properties, http_fixed_properties, go_fixed_properties, xiangqi_fixed_properties], [ns.TUBES + '/dbus#com.example.Xiangqi', ns.TUBES + '/stream#daap', ns.TUBES + '/stream#http', ns.TUBES + '/dbus#com.example.Go', ], [], all_tubes_caps) # test daap + xiangqi again for some reason advertise_caps(q, conn, stream, [incoming_daap_fixed_properties, xiangqi_fixed_properties], [ns.TUBES + '/dbus#com.example.Xiangqi', ns.TUBES + '/stream#daap', ], [ns.TUBES + '/stream#http', ns.TUBES + '/dbus#com.example.Go', ], daap_xiangqi_caps) def test(q, bus, conn, stream): test_tube_caps_from_contact(q, bus, conn, stream, 'bilbo1@foo.com/Foo') test_tube_caps_to_contact(q, bus, conn, stream) if __name__ == '__main__': exec_test(test) telepathy-gabble-0.18.2/tests/twisted/caps/trust-thyself.py0000644000175000017500000000533212227000321023767 0ustar00smcvsmcv00000000000000""" Test that we cache our own capabilities, so that we don't disco other people with the same caps hash or ext='' bundles. """ from twisted.words.xish import xpath from twisted.words.protocols.jabber.client import IQ from gabbletest import exec_test, make_presence, sync_stream from servicetest import EventPattern, assertEquals, assertNotEquals import ns import constants as cs from config import VOIP_ENABLED if not VOIP_ENABLED: print "NOTE: built with --disable-voip" raise SystemExit(77) def test(q, bus, conn, stream): self_presence = q.expect('stream-presence') c = xpath.queryForNodes('/presence/c', self_presence.stanza)[0] jid = 'lol@great.big/omg' # Gabble shouldn't send any disco requests to our contact during this test. q.forbid_events([ EventPattern('stream-iq', to=jid, iq_type='get', query_ns=ns.DISCO_INFO), ]) # Check that Gabble doesn't disco other clients with the same caps hash. p = make_presence(jid, caps={'node': c['node'], 'hash': c['hash'], 'ver': c['ver'], }) stream.send(p) sync_stream(q, stream) # Check that Gabble doesn't disco its own ext='' bundles (well, its own # bundles as advertised by Gabbles that don't do hashed caps) p = make_presence(jid, caps={'node': c['node'], 'ver': c['ver'], # omitting hash='' so Gabble doesn't ignore ext='' 'ext': 'voice-v1 video-v1', }) stream.send(p) sync_stream(q, stream) # Advertise some different capabilities, to change our own caps hash. add = [(cs.CHANNEL_TYPE_STREAMED_MEDIA, 2L**32-1), (cs.CHANNEL_TYPE_STREAM_TUBE, 2L**32-1), (cs.CHANNEL_TYPE_STREAM_TUBE, 2L**32-1)] remove = [] caps = conn.Capabilities.AdvertiseCapabilities(add, remove) self_presence = q.expect('stream-presence') c_ = xpath.queryForNodes('/presence/c', self_presence.stanza)[0] assertNotEquals(c['ver'], c_['ver']) for suffix in [c['ver'], 'voice-v1', 'video-v1', 'camera-v1', 'share-v1', 'pmuc-v1'] + list(c_['ext'].split()): # But then someone asks us for our old caps iq = IQ(stream, 'get') iq['from'] = jid query = iq.addElement((ns.DISCO_INFO, 'query')) query['node'] = c['node'] + '#' + suffix stream.send(iq) # Gabble should still know what they are, and reply. This is # actually quite important: there's a bug in iChat where if you # return an error to a disco query, it just asks again, and again, # and again... reply = q.expect('stream-iq', to=jid) assertEquals('result', reply.iq_type) if __name__ == '__main__': exec_test(test) telepathy-gabble-0.18.2/tests/twisted/caps/receive-jingle.py0000644000175000017500000001341512227000321024023 0ustar00smcvsmcv00000000000000""" Test receiving another contact's capabilities. """ import dbus from servicetest import EventPattern, assertEquals, sync_dbus from gabbletest import exec_test, make_result_iq, make_presence, sync_stream import constants as cs from config import VOIP_ENABLED if not VOIP_ENABLED: print "NOTE: built with --disable-voip" raise SystemExit(77) icaps_attr = cs.CONN_IFACE_CAPS + "/caps" basic_caps = [(2, cs.CHANNEL_TYPE_TEXT, 3, 0)] def test(q, bus, conn, stream): presence = make_presence('bob@foo.com/Foo', status='hello') stream.send(presence) q.expect('dbus-signal', signal='PresencesChanged', args=[{2L: (2, u'available', 'hello')}]) # FIXME: throughout this test, Bob's handle is assumed to be 2. # no special capabilities assert conn.Capabilities.GetCapabilities([2]) == basic_caps # holding the handle here: see below assert conn.Contacts.GetContactAttributes( [2], [cs.CONN_IFACE_CAPS], True) == \ { 2L: { icaps_attr: basic_caps, cs.CONN + '/contact-id': 'bob@foo.com'}} # send updated presence with Jingle audio/video caps info. we turn on both # audio and video at the same time to test that all of the capabilities are # discovered before any capabilities change signal is emitted presence = make_presence('bob@foo.com/Foo', status='hello', caps={ 'node': 'http://telepathy.freedesktop.org/fake-client', 'ver' : '0.1', 'ext' : 'video', }) stream.send(presence) # Gabble looks up both the version and the video bundles, in any order (version_event, video_event) = q.expect_many( EventPattern('stream-iq', to='bob@foo.com/Foo', query_ns='http://jabber.org/protocol/disco#info', query_node='http://telepathy.freedesktop.org/fake-client#0.1'), EventPattern('stream-iq', to='bob@foo.com/Foo', query_ns='http://jabber.org/protocol/disco#info', query_node='http://telepathy.freedesktop.org/fake-client#video')) # reply to the video bundle query first - this capability alone is not # sufficient to make us callable result = make_result_iq(stream, video_event.stanza) query = result.firstChildElement() feature = query.addElement('feature') feature['var'] = 'http://jabber.org/protocol/jingle/description/video' stream.send(result) # reply to the version bundle query, which should make us audio and # video callable result = make_result_iq(stream, version_event.stanza) query = result.firstChildElement() feature = query.addElement('feature') feature['var'] = 'http://jabber.org/protocol/jingle' feature = query.addElement('feature') feature['var'] = 'http://jabber.org/protocol/jingle/description/audio' feature = query.addElement('feature') feature['var'] = 'http://www.google.com/transport/p2p' stream.send(result) # we can now do audio and video calls event = q.expect('dbus-signal', signal='CapabilitiesChanged', args=[[(2, cs.CHANNEL_TYPE_STREAMED_MEDIA, 0, 3, 0, cs.MEDIA_CAP_AUDIO | cs.MEDIA_CAP_VIDEO)]]) caps = conn.Contacts.GetContactAttributes([2], [cs.CONN_IFACE_CAPS], False) assert caps.keys() == [2L] assert icaps_attr in caps[2L] assert len(caps[2L][icaps_attr]) == 2 assert basic_caps[0] in caps[2L][icaps_attr] assert (2, cs.CHANNEL_TYPE_STREAMED_MEDIA, 3, 3) in caps[2L][icaps_attr] # send updated presence without video support presence = make_presence('bob@foo.com/Foo', status='hello', caps={ 'node': 'http://telepathy.freedesktop.org/fake-client', 'ver' : '0.1', }) stream.send(presence) # we can now do only audio calls (and as a result have the ImmutableStreams # cap) event = q.expect('dbus-signal', signal='CapabilitiesChanged', args=[[(2, cs.CHANNEL_TYPE_STREAMED_MEDIA, 3, 3, cs.MEDIA_CAP_AUDIO | cs.MEDIA_CAP_VIDEO, cs.MEDIA_CAP_AUDIO | cs.MEDIA_CAP_IMMUTABLE_STREAMS)]]) caps = conn.Contacts.GetContactAttributes([2], [cs.CONN_IFACE_CAPS], False) assert caps.keys() == [2L] assert icaps_attr in caps[2L] assert len(caps[2L][icaps_attr]) == 2 assert basic_caps[0] in caps[2L][icaps_attr] assert (2, cs.CHANNEL_TYPE_STREAMED_MEDIA, 3, cs.MEDIA_CAP_AUDIO | cs.MEDIA_CAP_IMMUTABLE_STREAMS) \ in caps[2L][icaps_attr] # go offline presence = make_presence('bob@foo.com/Foo', type='unavailable') stream.send(presence) # can't do audio calls any more q.expect_many( EventPattern('dbus-signal', signal='CapabilitiesChanged', args=[[(2, cs.CHANNEL_TYPE_STREAMED_MEDIA, 3, 0, cs.MEDIA_CAP_AUDIO | cs.MEDIA_CAP_IMMUTABLE_STREAMS, 0)]], ), EventPattern('dbus-signal', signal='PresencesChanged', args=[{2: (cs.PRESENCE_OFFLINE, 'offline', '')}]), ) # Contact went offline. Previously, this test asserted that the handle # became invalid, but that's not guaranteed to happen immediately; so we # now hold the handle (above), to guarantee that it does *not* become # invalid. assert conn.Contacts.GetContactAttributes( [2], [cs.CONN_IFACE_CAPS], False) == \ { 2L: { icaps_attr: basic_caps, cs.CONN + '/contact-id': 'bob@foo.com'}} # What about a handle that's not valid? assertEquals({}, conn.Contacts.GetContactAttributes( [31337], [cs.CONN_IFACE_CAPS], False)) # regression test for fd.o #15198: getting caps of invalid handle crashed try: conn.Capabilities.GetCapabilities([31337]) except dbus.DBusException, e: pass else: assert False, "Should have had an error!" if __name__ == '__main__': exec_test(test) telepathy-gabble-0.18.2/tests/twisted/caps/offline.py0000644000175000017500000000254712227000321022561 0ustar00smcvsmcv00000000000000""" Test for fd.o#32874 -- Offline contacts do not have capabilities. """ from gabbletest import exec_test from servicetest import assertEquals, assertSameSets, assertLength import constants as cs import ns def test(q, bus, conn, stream): conn.Connect() # bob is offline jid = 'bob@foo.com' event = q.expect('stream-iq', query_ns=ns.ROSTER) event.stanza['type'] = 'result' item = event.query.addElement('item') item['jid'] = jid item['subscription'] = 'from' stream.send(event.stanza) q.expect('dbus-signal', signal='StatusChanged', args=[cs.CONN_STATUS_CONNECTED, cs.CSR_REQUESTED]), bob_handle = conn.RequestHandles(cs.HT_CONTACT, [jid])[0] # new ContactCapabilities ccaps_map = conn.ContactCapabilities.GetContactCapabilities([bob_handle]) assertLength(1, ccaps_map) assertLength(1, ccaps_map[bob_handle]) fixed, allowed = ccaps_map[bob_handle][0] assertEquals({cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_TEXT, cs.TARGET_HANDLE_TYPE: cs.HT_CONTACT}, fixed) assertSameSets([cs.TARGET_HANDLE], allowed) # old Capabilities all_caps = conn.Capabilities.GetCapabilities([bob_handle]) assertLength(1, all_caps) caps = all_caps[0] assertEquals((bob_handle, cs.CHANNEL_TYPE_TEXT, 3, 0), caps) if __name__ == '__main__': exec_test(test, do_connect=False) telepathy-gabble-0.18.2/tests/twisted/caps/jingle-caps.py0000644000175000017500000002222612227000321023327 0ustar00smcvsmcv00000000000000""" Test several different permutations of features that should a client audio and/or video capable """ from functools import partial from itertools import permutations from gabbletest import exec_test, make_presence, sync_stream from servicetest import ( assertContains, assertDoesNotContain, assertEquals, EventPattern, make_channel_proxy ) import constants as cs import ns from caps_helper import presence_and_disco, compute_caps_hash, send_presence from jingle.jingletest2 import JingleTest2, JingleProtocol031 from config import VOIP_ENABLED if not VOIP_ENABLED: print "NOTE: built with --disable-voip" raise SystemExit(77) client = 'http://telepathy.freedesktop.org/fake-client' caps = { 'node': client, 'ver': "dummy", 'hash': 'sha-1' } all_transports = [ ns.JINGLE_TRANSPORT_ICEUDP, ns.JINGLE_TRANSPORT_RAWUDP, ns.GOOGLE_P2P ] def check_contact_caps (caps, channel_type, expected_media_caps): [media_caps] = [ c for c in caps if c[0][cs.CHANNEL_TYPE] == channel_type ] assertEquals (expected_media_caps, media_caps[1]) def test_caps(q, conn, stream, contact, features, audio, video, google=False): caps['ver'] = compute_caps_hash ([], features, {}) h = presence_and_disco(q, conn, stream, contact, True, client, caps, features) cflags = 0 stream_expected_media_caps = [] call_expected_media_caps = [] if audio: cflags |= cs.MEDIA_CAP_AUDIO stream_expected_media_caps.append (cs.INITIAL_AUDIO) call_expected_media_caps.append (cs.CALL_INITIAL_AUDIO) call_expected_media_caps.append (cs.CALL_INITIAL_AUDIO_NAME) if video: cflags |= cs.MEDIA_CAP_VIDEO stream_expected_media_caps.append (cs.INITIAL_VIDEO) call_expected_media_caps.append (cs.CALL_INITIAL_VIDEO) call_expected_media_caps.append (cs.CALL_INITIAL_VIDEO_NAME) # If the contact can only do one of audio or video, or uses a Google # client, they'll have the ImmutableStreams cap. if cflags < (cs.MEDIA_CAP_AUDIO | cs.MEDIA_CAP_VIDEO) or google: cflags |= cs.MEDIA_CAP_IMMUTABLE_STREAMS stream_expected_media_caps.append(cs.IMMUTABLE_STREAMS) else: call_expected_media_caps.append(cs.CALL_MUTABLE_CONTENTS) _, event = q.expect_many( EventPattern('dbus-signal', signal='CapabilitiesChanged', args = [[ ( h, cs.CHANNEL_TYPE_STREAMED_MEDIA, 0, # old generic 3, # new generic (can create and receive these) 0, # old specific cflags ) ]] # new specific ), EventPattern('dbus-signal', signal='ContactCapabilitiesChanged') ) assertContains((h, cs.CHANNEL_TYPE_STREAMED_MEDIA, 3, cflags), conn.Capabilities.GetCapabilities([h])) # Check Contact capabilities for streamed media assertEquals(len(event.args), 1) assertEquals (event.args[0], conn.ContactCapabilities.GetContactCapabilities([h])) check_contact_caps (event.args[0][h], cs.CHANNEL_TYPE_STREAMED_MEDIA, stream_expected_media_caps) check_contact_caps (event.args[0][h], cs.CHANNEL_TYPE_CALL, call_expected_media_caps) def test_all_transports(q, conn, stream, contact, features, audio, video): for t in all_transports: test_caps(q, conn, stream, contact, features + [t] , audio, video) contact += "a" def test(q, bus, conn, stream): # Fully capable jingle clients with one transport each features = [ ns.JINGLE_RTP, ns.JINGLE_RTP_AUDIO, ns.JINGLE_RTP_VIDEO ] test_all_transports(q, conn, stream, "full@a", features, True, True) # video capable jingle clients with one transport each features = [ ns.JINGLE_RTP, ns.JINGLE_RTP_VIDEO ] test_all_transports (q, conn, stream, "video@a", features, False, True) # audio capable jingle clients with one transport each features = [ ns.JINGLE_RTP, ns.JINGLE_RTP_AUDIO ] test_all_transports(q, conn, stream, "audio@a", features, True, False) # old jingle client fully capable features = [ ns.JINGLE_015, ns.JINGLE_015_AUDIO, ns.JINGLE_015_VIDEO ] test_all_transports(q, conn, stream, "oldfull@a", features, True, True) # old jingle client video capable features = [ ns.JINGLE_015, ns.JINGLE_015_VIDEO ] test_all_transports(q, conn, stream, "oldvideo@a", features, False, True) # old jingle client audio capable features = [ ns.JINGLE_015, ns.JINGLE_015_AUDIO ] test_all_transports(q, conn, stream, "oldaudio@a", features, True, False) # Google media doesn't need a transport at all features = [ ns.GOOGLE_FEAT_VOICE, ns.GOOGLE_FEAT_VIDEO ] test_caps(q, conn, stream, "full@google", features, True, True, google=True) # Google video only features = [ ns.GOOGLE_FEAT_VIDEO ] test_caps(q, conn, stream, "video@google", features, False, True, google=True) # Google audio only features = [ ns.GOOGLE_FEAT_VOICE ] test_caps(q, conn, stream, "audio@google", features, True, False, google=True) def test_prefer_phones(q, bus, conn, stream, expect_disco): cat = 'cat@windowsill' def sign_in_a_cat(jid, identities, show=None): caps['ver'] = compute_caps_hash(identities, features, {}) presence_and_disco(q, conn, stream, jid, expect_disco, client, caps, features, identities=identities, initial=False, show=show) # Make sure Gabble's got the caps sync_stream(q, stream) def make_call(expected_recipient): jp = JingleProtocol031() jt = JingleTest2(jp, conn, q, stream, 'test@localhost', 'dummy') conn.Requests.CreateChannel({ cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_STREAMED_MEDIA, cs.TARGET_HANDLE_TYPE: cs.HT_CONTACT, cs.TARGET_ID: cat, cs.INITIAL_AUDIO: True, }) e = q.expect('dbus-signal', signal='NewSessionHandler') session = make_channel_proxy(conn, e.args[0], 'Media.SessionHandler') session.Ready() e = q.expect('dbus-signal', signal='NewStreamHandler') stream_handler = make_channel_proxy(conn, e.args[0], 'Media.StreamHandler') stream_handler.NewNativeCandidate("fake", jt.get_remote_transports_dbus()) stream_handler.Ready(jt.get_audio_codecs_dbus()) stream_handler.StreamState(cs.MEDIA_STREAM_STATE_CONNECTED) e = q.expect('stream-iq', predicate=jp.action_predicate('session-initiate')) assertEquals(expected_recipient, e.to) features = [ ns.JINGLE_RTP, ns.JINGLE_RTP_AUDIO, ns.JINGLE_RTP_VIDEO ] + all_transports # My cat is signed in with their laptop (which is available)... laptop_jid = 'cat@windowsill/Laptop' sign_in_a_cat(laptop_jid, ['client/pc//clocks']) # ...and a web client, which is away. cloud_jid = 'cat@windowsill/Cloud' sign_in_a_cat(cloud_jid, ['client/web//zomg'], show='away') # The laptop is more available, so the call should go there. make_call(expected_recipient=laptop_jid) # But if my cat signs in with a phone, also set to away... phone_jid = 'cat@windowsill/Fido' sign_in_a_cat(phone_jid, ['client/phone//mars rover'], show='away') # ...then calls should go there, even though the laptop is more available. make_call(expected_recipient=phone_jid) def test_google_caps(q, bus, conn, stream): i = 1 # we want to make sure all permutations of voice-v1 and video-v1 # result in the correct caps, so let's do exactly that. for j in (1, 2): for ext_set in permutations(['voice-v1', 'video-v1'], j): jid = 'larry%s@page/mountainview' % i i += 1 # order of these ext values shouldn't matter gcaps = { 'node': 'blahblahthiskeepsonchanging', 'ver': '1.1', 'ext': ' '.join(ext_set) } handle = conn.RequestHandles(cs.HT_CONTACT, [jid])[0] send_presence(q, conn, stream, jid, gcaps, initial=True) e = q.expect('dbus-signal', signal='ContactCapabilitiesChanged', predicate=lambda e: handle in e.args[0]) assertEquals(1, len(e.args[0])) rccs = e.args[0][handle] found = False for fixed, allowed in rccs: if fixed[cs.CHANNEL_TYPE] != cs.CHANNEL_TYPE_CALL: continue # we should only have InitialAudio or InitialVideo if # voice-v1 or video-v1 is present respectively for a, b in [('voice-v1' in ext_set, cs.CALL_INITIAL_AUDIO), ('video-v1' in ext_set, cs.CALL_INITIAL_VIDEO)]: if a: assertContains(b, allowed) else: assertDoesNotContain(b, allowed) found = True assert found if __name__ == '__main__': exec_test(test) exec_test(partial(test_prefer_phones, expect_disco=True)) # And again, this time pulling the caps from the cache. This tests that the # quirk is cached! exec_test(partial(test_prefer_phones, expect_disco=False)) exec_test(test_google_caps) telepathy-gabble-0.18.2/tests/twisted/caps/initial-caps.py0000644000175000017500000000154212200204333023506 0ustar00smcvsmcv00000000000000""" Test initial capabilities """ import dbus from twisted.words.xish import xpath, domish from servicetest import EventPattern from gabbletest import exec_test, sync_stream from caps_helper import check_caps, disco_caps import constants as cs import ns from config import VOIP_ENABLED def run_test(q, bus, conn, stream): initial_presence = q.expect('stream-presence') _, namespaces, _ = disco_caps(q, stream, initial_presence) # For some reason, until we advertise any capabilities, these caps turn # up in our presence if VOIP_ENABLED: caps = [ ns.JINGLE, ns.JINGLE_015, ns.JINGLE_TRANSPORT_ICEUDP, ns.JINGLE_TRANSPORT_RAWUDP, ns.GOOGLE_P2P ] else: caps = [] check_caps(namespaces, caps) if __name__ == '__main__': exec_test(run_test) telepathy-gabble-0.18.2/tests/twisted/caps/hashed-caps.py0000644000175000017500000002722612227000321023320 0ustar00smcvsmcv00000000000000# coding=utf-8 """ Test the verification string introduced in version 1.5 of XEP-0115 This test changes the caps several times: - Initial presence to be online - Change presence to handle audio calls, using XEP-0115-v1.3. Check that 'CapabilitiesChanged' *is* fired - Change presence *not* to handle audio calls, using XEP-0115-v1.5, but with a *bogus* hash. Check that 'CapabilitiesChanged' is *not* fired - Change presence *not* to handle audio calls, using XEP-0115-v1.5, with a *good* hash. Check that 'CapabilitiesChanged' *is* fired - Change presence to handle audio calls, using XEP-0115-v1.5, with a XEP-0128 dataform. Check that 'CapabilitiesChanged' is fired This is done for 2 contacts Then, this test announce 2 contacts with the same hash. - Gabble must ask only once for the hash and update the caps for both contacts - When the caps advertised by the first contact does not match, Gabble asks the second and update only the caps of the second contact """ import dbus from twisted.words.xish import xpath from gabbletest import ( exec_test, make_result_iq, make_presence, sync_stream, elem, ) from servicetest import sync_dbus, EventPattern, assertLength import constants as cs import ns from caps_helper import ( compute_caps_hash, make_caps_disco_reply, send_disco_reply, fake_client_dataforms) from config import VOIP_ENABLED if not VOIP_ENABLED: print "NOTE: built with --disable-voip" raise SystemExit(77) caps_changed_flag = False some_identities = [ 'client/pc/fr/le gabble', 'client/pc/en/gabble', ] jingle_av_features = [ ns.JINGLE_015, ns.JINGLE_015_AUDIO, ns.JINGLE_015_VIDEO, ns.GOOGLE_P2P, ] def caps_changed_cb(dummy): # Workaround to bug 9980: do not raise an error but use a flag # https://bugs.freedesktop.org/show_bug.cgi?id=9980 global caps_changed_flag caps_changed_flag = True def test_hash(q, bus, conn, stream, contact, contact_handle, client): global caps_changed_flag presence = make_presence(contact, status='hello') stream.send(presence) q.expect('dbus-signal', signal='PresencesChanged', args=[{contact_handle: (2, u'available', 'hello')}]) # no special capabilities basic_caps = [(contact_handle, cs.CHANNEL_TYPE_TEXT, 3, 0)] assert conn.Capabilities.GetCapabilities([contact_handle]) == basic_caps # send updated presence with Jingle caps info presence = make_presence(contact, status='hello', caps={'node': client, 'ver': '0.1', }) stream.send(presence) # Gabble looks up our capabilities event = q.expect('stream-iq', to=contact, query_ns='http://jabber.org/protocol/disco#info') query_node = xpath.queryForNodes('/iq/query', event.stanza)[0] assert query_node.attributes['node'] == \ client + '#' + '0.1' # send good reply send_disco_reply(stream, event.stanza, [], jingle_av_features) # we can now do audio calls event = q.expect('dbus-signal', signal='CapabilitiesChanged') caps_diff = event.args[0] media_diff = [c for c in caps_diff if c[1] == cs.CHANNEL_TYPE_STREAMED_MEDIA][0] assert media_diff[5] & cs.MEDIA_CAP_AUDIO, media_diff[5] caps_changed_flag = False # Send presence without any capabilities. XEP-0115 §8.4 Caps Optimization # says “receivers of presence notifications MUST NOT expect an annotation # on every presence notification they receive”, so the contact should still # be media-capable afterwards. stream.send(make_presence(contact, status='very capable')) q.expect('dbus-signal', signal='PresencesChanged', args=[{contact_handle: (2, u'available', 'very capable')}]) ye_olde_caps = conn.Capabilities.GetCapabilities([contact_handle]) assertLength(1, [c for c in ye_olde_caps if c[1] == cs.CHANNEL_TYPE_STREAMED_MEDIA and c[3] & cs.MEDIA_CAP_AUDIO]) # send bogus presence caps = { 'node': client, 'ver': 'ceci=nest=pas=un=hash', 'hash': 'sha-1', } presence = make_presence(contact, status='hello', caps=caps) stream.send(presence) # Gabble looks up our capabilities event = q.expect('stream-iq', to=contact, query_ns='http://jabber.org/protocol/disco#info') query_node = xpath.queryForNodes('/iq/query', event.stanza)[0] assert query_node.attributes['node'] == \ client + '#' + caps['ver'] # send bogus reply send_disco_reply(stream, event.stanza, [], ['http://jabber.org/protocol/bogus-feature']) # don't receive any D-Bus signal sync_dbus(bus, q, conn) sync_stream(q, stream) assert caps_changed_flag == False # send presence with empty caps presence = make_presence(contact, status='hello', caps={'node': client, 'ver': '0.0', }) stream.send(presence) # Gabble looks up our capabilities event = q.expect('stream-iq', to=contact, query_ns='http://jabber.org/protocol/disco#info') query_node = xpath.queryForNodes('/iq/query', event.stanza)[0] assert query_node.attributes['node'] == \ client + '#' + '0.0' # still don't receive any D-Bus signal sync_dbus(bus, q, conn) assert caps_changed_flag == False # send good reply result = make_result_iq(stream, event.stanza) query = result.firstChildElement() stream.send(result) # we can now do nothing event = q.expect('dbus-signal', signal='CapabilitiesChanged') assert caps_changed_flag == True caps_changed_flag = False # send correct presence ver = compute_caps_hash(some_identities, jingle_av_features, fake_client_dataforms) caps = { 'node': client, 'ver': ver, 'hash': 'sha-1', } presence = make_presence(contact, status='hello', caps=caps) stream.send(presence) # Gabble looks up our capabilities event = q.expect('stream-iq', to=contact, query_ns='http://jabber.org/protocol/disco#info') query_node = xpath.queryForNodes('/iq/query', event.stanza)[0] assert query_node.attributes['node'] == \ client + '#' + caps['ver'] # don't receive any D-Bus signal sync_dbus(bus, q, conn) assert caps_changed_flag == False # send good reply send_disco_reply( stream, event.stanza, some_identities, jingle_av_features, fake_client_dataforms) # we can now do audio calls event = q.expect('dbus-signal', signal='CapabilitiesChanged', ) assert caps_changed_flag == True caps_changed_flag = False def test_two_clients(q, bus, conn, stream, contact1, contact2, contact_handle1, contact_handle2, client, broken_hash): global caps_changed_flag presence = make_presence(contact1, status='hello') stream.send(presence) q.expect('dbus-signal', signal='PresencesChanged', args=[{contact_handle1: (2, u'available', 'hello')}]) presence = make_presence(contact2, status='hello') stream.send(presence) q.expect('dbus-signal', signal='PresencesChanged', args=[{contact_handle2: (2, u'available', 'hello')}]) # no special capabilities basic_caps = [(contact_handle1, cs.CHANNEL_TYPE_TEXT, 3, 0)] assert conn.Capabilities.GetCapabilities([contact_handle1]) == basic_caps basic_caps = [(contact_handle2, cs.CHANNEL_TYPE_TEXT, 3, 0)] assert conn.Capabilities.GetCapabilities([contact_handle2]) == basic_caps # send updated presence with Jingle caps info ver = compute_caps_hash(some_identities, jingle_av_features, {}) caps = { 'node': client, 'ver': ver, 'hash': 'sha-1', } presence = make_presence(contact1, status='hello', caps=caps) stream.send(presence) presence = make_presence(contact2, status='hello', caps=caps) stream.send(presence) # Gabble looks up our capabilities event = q.expect('stream-iq', to=contact1, query_ns='http://jabber.org/protocol/disco#info') query_node = xpath.queryForNodes('/iq/query', event.stanza)[0] assert query_node.attributes['node'] == \ client + '#' + ver # don't receive any D-Bus signal sync_dbus(bus, q, conn) assert caps_changed_flag == False result = make_caps_disco_reply( stream, event.stanza, some_identities, jingle_av_features) if broken_hash: # make the hash break! query = result.firstChildElement() query.addElement('feature')['var'] = 'http://example.com/another-feature' stream.send(result) if broken_hash: # Gabble looks up our capabilities again because the first contact # failed to provide a valid hash event = q.expect('stream-iq', to=contact2, query_ns='http://jabber.org/protocol/disco#info') query_node = xpath.queryForNodes('/iq/query', event.stanza)[0] assert query_node.attributes['node'] == \ client + '#' + ver # don't receive any D-Bus signal sync_dbus(bus, q, conn) assert caps_changed_flag == False # send good reply send_disco_reply(stream, event.stanza, some_identities, jingle_av_features) # we can now do audio calls with both contacts event = q.expect('dbus-signal', signal='CapabilitiesChanged', args=[[(contact_handle2, cs.CHANNEL_TYPE_STREAMED_MEDIA, 0, 3, 0, cs.MEDIA_CAP_AUDIO | cs.MEDIA_CAP_VIDEO)]]) if not broken_hash: # if the first contact failed to provide a good hash, it does not # deserve its capabilities to be understood by Gabble! event = q.expect('dbus-signal', signal='CapabilitiesChanged', args=[[(contact_handle1, cs.CHANNEL_TYPE_STREAMED_MEDIA, 0, 3, 0, cs.MEDIA_CAP_AUDIO | cs.MEDIA_CAP_VIDEO)]]) caps_changed_flag = False # don't receive any D-Bus signal sync_dbus(bus, q, conn) assert caps_changed_flag == False def test_39464(q, bus, conn, stream): """ Regression test for an issue where a form with no type='' attribute on the node would crash Gabble. """ client = 'fake:qutim' hash = 'blahblah' contact = 'bug-39464@example.com/foo' caps = { 'node': client, 'ver': hash, 'hash': 'sha-1', } presence = make_presence(contact, status='hello', caps=caps) stream.send(presence) # Gabble looks up our capabilities event = q.expect('stream-iq', to=contact, query_ns=ns.DISCO_INFO) # Send a reply with a form without a type='' result = make_result_iq(stream, event.stanza, add_query_node=False) result.addChild( elem(ns.DISCO_INFO, 'query', node='%s#%s' % (client, hash))( # NB. no type='' attribute elem(ns.X_DATA, 'x') ) ) stream.send(result) # We don't really care what Gabble does, as long as it doesn't crash. sync_stream(q, stream) def test(q, bus, conn, stream): # be notified when the signal CapabilitiesChanged is fired conn_caps_iface = dbus.Interface(conn, cs.CONN_IFACE_CAPS) conn_caps_iface.connect_to_signal('CapabilitiesChanged', caps_changed_cb) test_hash(q, bus, conn, stream, 'bob@foo.com/Foo', 2L, 'http://telepathy.freedesktop.org/fake-client') test_hash(q, bus, conn, stream, 'bob2@foo.com/Foo', 3L, 'http://telepathy.freedesktop.org/fake-client2') test_two_clients(q, bus, conn, stream, 'user1@example.com/Res', 'user2@example.com/Res', 4L, 5L, 'http://telepathy.freedesktop.org/fake-client3', 0) test_two_clients(q, bus, conn, stream, 'user3@example.com/Res', 'user4@example.com/Res', 6L, 7L, 'http://telepathy.freedesktop.org/fake-client4', 1) test_39464(q, bus, conn, stream) if __name__ == '__main__': exec_test(test) telepathy-gabble-0.18.2/tests/twisted/caps/from-bare-jid.py0000644000175000017500000000775312227000321023561 0ustar00smcvsmcv00000000000000""" Tests receiving capabilities from bare JIDs. """ from twisted.words.xish import xpath from servicetest import ( assertEquals, assertContains, assertDoesNotContain, EventPattern, ) from gabbletest import make_presence, exec_test from caps_helper import compute_caps_hash, send_disco_reply import constants as cs import ns from config import VOIP_ENABLED if not VOIP_ENABLED: print "NOTE: built with --disable-voip" raise SystemExit(77) def test(q, bus, conn, stream): client = 'http://example.com/perverse-client' contact_bare_jid = 'edgecase@example.com' contact_with_resource = 'edgecase@example.com/hi' contact_handle = conn.RequestHandles(cs.HT_CONTACT, [contact_bare_jid])[0] # Gabble gets a presence stanza from a bare JID, which is a tad surprising. features = [ ns.JINGLE_015, ns.JINGLE_015_AUDIO, ns.JINGLE_015_VIDEO, ns.GOOGLE_P2P, ] caps = {'node': client, 'hash': 'sha-1', 'ver': compute_caps_hash([], features, {}), } p = make_presence(contact_bare_jid, status='Hello', caps=caps) stream.send(p) # Gabble looks up the hash event = q.expect('stream-iq', to=contact_bare_jid, query_ns='http://jabber.org/protocol/disco#info') query_node = xpath.queryForNodes('/iq/query', event.stanza)[0] assertEquals(client + '#' + caps['ver'], query_node.attributes['node']) # The bare jid replies send_disco_reply(stream, event.stanza, [], features) # Gabble lets us know their caps have changed. (Gabble used to ignore the # reply.) streamed_media_caps = (contact_handle, cs.CHANNEL_TYPE_STREAMED_MEDIA, 0, 3, 0, cs.MEDIA_CAP_AUDIO | cs.MEDIA_CAP_VIDEO) e = q.expect('dbus-signal', signal='CapabilitiesChanged') assertContains(streamed_media_caps, e.args[0]) # Gabble gets another presence stanza from the bare JID, with different # caps. features.append(ns.TUBES) caps = {'node': client, 'hash': 'sha-1', 'ver': compute_caps_hash([], features, {}), } p = make_presence(contact_bare_jid, status='Get out the abacus', caps=caps) stream.send(p) # Gabble looks up the new hash disco2 = q.expect('stream-iq', to=contact_bare_jid, query_ns='http://jabber.org/protocol/disco#info') query_node = xpath.queryForNodes('/iq/query', disco2.stanza)[0] assertEquals(client + '#' + caps['ver'], query_node.attributes['node']) # This time, before the bare JID replies, Gabble gets a presence from the # resourceful jid. features_ = features + [ns.CHAT_STATES] caps = {'node': client, 'hash': 'sha-1', 'ver': compute_caps_hash([], features_, {}), } p = make_presence(contact_with_resource, status='Count this', caps=caps) stream.send(p) # Gabble throws away presence from the bare JID when it gets presence from # a resource (and vice versa), so it should now say the contact is # incapable. Gabble also looks up the resourceful JID's hash. cc, disco3 = q.expect_many( EventPattern('dbus-signal', signal='CapabilitiesChanged'), EventPattern('stream-iq', to=contact_with_resource, query_ns='http://jabber.org/protocol/disco#info'), ) assertDoesNotContain(streamed_media_caps, cc.args[0]) query_node = xpath.queryForNodes('/iq/query', disco3.stanza)[0] assertEquals(client + '#' + caps['ver'], query_node.attributes['node']) # The bare jid replies! Getting a disco reply from a bare JID when we've # got presence from resources used to crash Gabble, but now it just ignores # it. send_disco_reply(stream, disco2.stanza, [], features) # Now the resourceful JID replies: send_disco_reply(stream, disco3.stanza, [], features_) # Gabble should announce that the contact has acquired some caps. e = q.expect('dbus-signal', signal='CapabilitiesChanged') assertContains(streamed_media_caps, e.args[0]) if __name__ == '__main__': exec_test(test) telepathy-gabble-0.18.2/tests/twisted/caps/double-disco.py0000644000175000017500000000322412200204333023501 0ustar00smcvsmcv00000000000000 from twisted.words.xish import xpath from servicetest import EventPattern from gabbletest import exec_test, make_presence, sync_stream import constants as cs import ns def test(q, bus, conn, stream): contact = 'grapes@graze.box/delicious' presence = make_presence(contact, status='eat me!', caps={ 'node': 'oh:hai', 'ver': 'thar', }) thar_disco = EventPattern('stream-iq', to=contact, query_ns=ns.DISCO_INFO, query_node='oh:hai#thar') stream.send(presence) q.expect_many(thar_disco) # Okay, all good so far. But if we get the same caps node again from the # same contact, we shouldn't disco it again: we won't get any more trust # that way. This matters in practice, because Google's clients send a whole # bunch of presence stanzas in quick succession when they sign on. q.forbid_events([thar_disco]) stream.send(presence) sync_stream(q, stream) # If we get a presence update from this contact with some new ext='' # bundles, we should disco those, but not the nodes we're already querying. presence = make_presence(contact, status='eat me!', caps={ 'node': 'oh:hai', 'ver': 'thar', 'ext': 'good-sir', }) good_sir_disco = EventPattern('stream-iq', to=contact, query_ns=ns.DISCO_INFO, query_node='oh:hai#good-sir') stream.send(presence) q.expect_many(good_sir_disco) sync_stream(q, stream) # We should only disco ext='' attributes once per jid, too. q.forbid_events([good_sir_disco]) stream.send(presence) sync_stream(q, stream) if __name__ == '__main__': exec_test(test) telepathy-gabble-0.18.2/tests/twisted/caps/disco-without-node.py0000644000175000017500000000101112200204333024645 0ustar00smcvsmcv00000000000000""" Test that Gabble responds to disco#info queries without a node='' attribute. """ from servicetest import assertEquals from gabbletest import exec_test, elem_iq, elem import constants as cs import ns def test(q, bus, conn, stream): jid = 'foo@bar.com' iq = elem_iq(stream, 'get', from_=jid)(elem(ns.DISCO_INFO, 'query')) stream.send(iq) event = q.expect('stream-iq', iq_type='result', to='foo@bar.com') assertEquals(iq['id'], event.stanza['id']) if __name__ == '__main__': exec_test(test) telepathy-gabble-0.18.2/tests/twisted/caps/compat-bundles.py0000644000175000017500000000320412200204333024043 0ustar00smcvsmcv00000000000000""" Check that Gabble always responds to disco for voice-v1 and video-v1. They have hard-coded contents, because they only exist for compatibility with Google Talk, Google Video Chat, and old versions of Gabble. In particular, if the appropriate capabilities are not enabled (as in this test), doing disco on the bundles still gives their contents. """ import dbus from twisted.words.xish import xpath, domish from servicetest import EventPattern, assertEquals from gabbletest import exec_test, elem, elem_iq import constants as cs import ns from config import VOIP_ENABLED if not VOIP_ENABLED: print "NOTE: built with --disable-voip" raise SystemExit(77) def disco_bundle(q, bus, conn, stream, node, features): request = \ elem_iq(stream, 'get', from_='fake_contact@jabber.org/resource')( elem(ns.DISCO_INFO, 'query', node=node) ) stream.send(request) disco_response = q.expect('stream-iq', query_ns=ns.DISCO_INFO, iq_id=request['id']) nodes = xpath.queryForNodes('/iq/query/feature', disco_response.stanza) vars = [n["var"] for n in nodes] assertEquals(set(features), set(vars)) def run_test(q, bus, conn, stream): event_stream = q.expect('stream-presence') c_nodes = xpath.queryForNodes('/presence/c', event_stream.stanza) assert c_nodes is not None assert len(c_nodes) == 1 node = c_nodes[0].attributes['node'] disco_bundle(q, bus, conn, stream, node + '#voice-v1', set([ns.GOOGLE_FEAT_VOICE])) disco_bundle(q, bus, conn, stream, node + '#video-v1', set([ns.GOOGLE_FEAT_VIDEO])) if __name__ == '__main__': exec_test(run_test) telepathy-gabble-0.18.2/tests/twisted/caps/caps-persistent-cache.py0000644000175000017500000000767212227000321025330 0ustar00smcvsmcv00000000000000 from twisted.words.xish import xpath from servicetest import ( assertEquals, assertContains, assertDoesNotContain, EventPattern, ) from gabbletest import make_presence, exec_test from caps_helper import compute_caps_hash, send_disco_reply import constants as cs import ns from config import VOIP_ENABLED if not VOIP_ENABLED: print "NOTE: built with --disable-voip" raise SystemExit(77) contact_bare_jid = 'macbeth@glamis' contact_jid = 'macbeth@glamis/hall' client = 'http://telepathy.freedesktop.org/zomg-ponies' features = [ ns.JINGLE_015, ns.JINGLE_015_AUDIO, ns.JINGLE_015_VIDEO, ns.GOOGLE_P2P, ns.TUBES + '/dbus#com.example.Xiangqi', ] xiangqi_tube_cap = ( {cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_DBUS_TUBE, cs.TARGET_HANDLE_TYPE: cs.HT_CONTACT, cs.DBUS_TUBE_SERVICE_NAME: u'com.example.Xiangqi'}, [cs.TARGET_HANDLE, cs.TARGET_ID]) def send_presence(q, stream, contact_jid, identity): ver = compute_caps_hash([identity], features, {}) stream.send(make_presence(contact_jid, status='Hello', caps={'node': client, 'hash': 'sha-1', 'ver': ver})) def handle_disco(q, stream, contact_jid, identity): # Gabble tries to resolve a caps hash. ver = compute_caps_hash([identity], features, {}) event = q.expect('stream-iq', to=contact_jid, query_ns=ns.DISCO_INFO) assertEquals(client + '#' + ver, event.query.attributes['node']) # The bare jid replies. send_disco_reply(stream, event.stanza, [identity], features) def capabilities_changed(q, contact_handle): streamed_media_caps = (contact_handle, cs.CHANNEL_TYPE_STREAMED_MEDIA, 0, 3, 0, cs.MEDIA_CAP_AUDIO | cs.MEDIA_CAP_VIDEO) e = q.expect('dbus-signal', signal='CapabilitiesChanged') assertContains(streamed_media_caps, e.args[0]) e = q.expect('dbus-signal', signal='ContactCapabilitiesChanged') assertContains(contact_handle, e.args[0]) assertContains(xiangqi_tube_cap, e.args[0][contact_handle]) def test1(q, bus, conn, stream): contact_handle = conn.RequestHandles(cs.HT_CONTACT, [contact_bare_jid])[0] send_presence(q, stream, contact_jid, 'client/pc//thane') handle_disco(q, stream, contact_jid, 'client/pc//thane') capabilities_changed(q, contact_handle) def test2(q, bus, conn, stream): # The second time around, the capabilities are retrieved from the cache, # so no disco request is sent. contact_handle = conn.RequestHandles(cs.HT_CONTACT, [contact_bare_jid])[0] send_presence(q, stream, contact_jid, 'client/pc//thane') capabilities_changed(q, contact_handle) # Overflow the cache. GC is considered every 50 inserts, and then only # performed if the cache has more entries than a threshold which is set to # 50 in the test suite, reducing the cache to 0.95 * that threshold, which # is 47 in the test suite. # # We want to ensure that Macbeth is removed from the cache. In the worst # case, GC is considered and performed when we insert the 46th witch, which # will leave Macbeth in the cache. Inserting a further 50 witches will # ensure that Macbeth is flushed even in this worst case. Let's round up to # 100 witches. for i in range(100): overflow_contact_jid = 'witch%d@forest/cauldron' % i overflow_identity = 'client/pc//prophecy%d' % i send_presence(q, stream, overflow_contact_jid, overflow_identity) handle_disco(q, stream, overflow_contact_jid, overflow_identity) if __name__ == '__main__': # We run test1. The capabilities for macbeth@glamis's client # need to be fetched via disco and are then stored in the cache. exec_test(test1) # We run test2 again. The capabilities are retrieved from the cache, so no # disco request is sent. Then, a bunch of other clients turn up and force # the entry for Macbeth's client out of the cache. exec_test(test2) # We run test1 again. The caps are no longer in the cache, so a disco # request is sent again. exec_test(test1) telepathy-gabble-0.18.2/tests/twisted/caps/caps-cache.py0000644000175000017500000000763012227000321023124 0ustar00smcvsmcv00000000000000 """ Test that requesting a caps set 1 time is enough with hash and that we need 5 confirmation without hash. """ from twisted.words.xish import xpath from servicetest import EventPattern, assertEquals, assertContains from gabbletest import exec_test, make_presence, sync_stream import constants as cs import ns from caps_helper import ( compute_caps_hash, fake_client_dataforms, presence_and_disco, send_presence, expect_disco, send_disco_reply) from config import VOIP_ENABLED if not VOIP_ENABLED: print "NOTE: built with --disable-voip" raise SystemExit(77) client = 'http://telepathy.freedesktop.org/fake-client/caps-cache' features = [ ns.JINGLE_015, ns.JINGLE_015_AUDIO, ns.JINGLE_015_VIDEO, ns.GOOGLE_P2P, ] def expect_caps(q, conn, h): # we can now do audio and video calls event = q.expect('dbus-signal', signal='CapabilitiesChanged') check_caps(conn, h) def check_caps(conn, h): assertContains((h, cs.CHANNEL_TYPE_STREAMED_MEDIA, 3, cs.MEDIA_CAP_AUDIO | cs.MEDIA_CAP_VIDEO), conn.Capabilities.GetCapabilities([h])) def update_contact_caps(q, conn, stream, contact, caps, disco = True, dataforms = {}, initial = True): h = presence_and_disco (q, conn, stream, contact, disco, client, caps, features, dataforms=dataforms, initial = initial) expect_caps (q, conn, h) def test(q, bus, conn, stream): caps = { 'node': client, 'ver': '0.1', } update_contact_caps(q, conn, stream, 'bob1@foo.com/Foo', caps) update_contact_caps(q, conn, stream, 'bob2@foo.com/Foo', caps) # Meredith signs in from one resource. update_contact_caps(q, conn, stream, 'meredith@foo.com/One', caps) # Meredith signs in from another resource with the same client. We don't # need to disco her, even though we don't trust this caps node in general # yet, because she's already told us what it means. meredith_two = 'meredith@foo.com/Two' q.forbid_events([ EventPattern('stream-iq', to=meredith_two, query_ns=ns.DISCO_INFO) ]) stream.send(make_presence(meredith_two, 'hello', caps=caps)) sync_stream(q, stream) # Jens signs in from one resource, which is slow to answer the disco query. jens_one = 'jens@foo.com/One' j = send_presence(q, conn, stream, jens_one, caps) j_stanza = expect_disco(q, jens_one, client, caps) # Jens now signs in elsewhere with the same client; we disco it (maybe # it'll reply sooner? Maybe his first client's network connection went away # and the server hasn't noticed yet?) and it replies immediately. update_contact_caps (q, conn, stream, 'jens@foo.com/Two', caps, initial=False) # Jens' first client replies. We don't expect any caps changes here, and # this shouldn't count as a second point towards the five we need to trust # this caps node. send_disco_reply(stream, j_stanza, [], features) check_caps (conn, j) update_contact_caps (q, conn, stream, 'bob5@foo.com/Foo', caps) # Now five distinct contacts have told us what this caps node means, we # trust it. update_contact_caps (q, conn, stream, 'bob6@foo.com/Foo', caps, disco = False) update_contact_caps (q, conn, stream, 'bob7@foo.com/Foo', caps, disco = False) caps = { 'node': client, 'ver': compute_caps_hash([], features, fake_client_dataforms), 'hash': 'sha-1', } update_contact_caps(q, conn, stream, 'bilbo1@foo.com/Foo', caps, dataforms = fake_client_dataforms) # We can verify the reply for these caps against the hash, and thus never # need to disco it again. update_contact_caps(q, conn, stream, 'bilbo2@foo.com/Foo', caps, disco = False, dataforms = fake_client_dataforms) update_contact_caps(q, conn, stream, 'bilbo3@foo.com/Foo', caps, disco = False, dataforms = fake_client_dataforms) if __name__ == '__main__': exec_test(test) telepathy-gabble-0.18.2/tests/twisted/caps/broken-reply.py0000644000175000017500000000114212200204333023536 0ustar00smcvsmcv00000000000000""" Tests that disco replies whose node is missing don't crash Gabble. """ from gabbletest import exec_test, sync_stream, make_result_iq import caps_helper def test(q, bus, conn, stream): jid = 'crashy@cra.shy/hi' caps = { 'node': 'oh:hi', 'ver': "dere", } h = caps_helper.send_presence(q, conn, stream, jid, caps, initial=False) request = caps_helper.expect_disco(q, jid, caps['node'], caps) result = make_result_iq(stream, request, add_query_node=False) stream.send(result) sync_stream(q, stream) if __name__ == '__main__': exec_test(test) telepathy-gabble-0.18.2/tests/twisted/caps/advertise-legacy.py0000644000175000017500000001022112227000321024353 0ustar00smcvsmcv00000000000000""" Test AdvertiseCapabilities. """ import dbus from twisted.words.xish import xpath, domish from servicetest import EventPattern from gabbletest import exec_test from caps_helper import caps_contain, receive_presence_and_ask_caps,\ check_caps, JINGLE_CAPS import constants as cs import ns from config import VOIP_ENABLED if not VOIP_ENABLED: print "NOTE: built with --disable-voip" raise SystemExit(77) def run_test(q, bus, conn, stream): initial_presence = q.expect('stream-presence') # This method call looks wrong, but it's "the other side" of # test/twisted/capabilities/legacy-caps.py in MC 5.1 - MC doesn't know # how to map Client capabilities into the old Capabilities interface. # # Also, MC sometimes puts the same channel type in the list twice, so # make sure Gabble copes. add = [(cs.CHANNEL_TYPE_STREAMED_MEDIA, 2L**32-1), (cs.CHANNEL_TYPE_STREAM_TUBE, 2L**32-1), (cs.CHANNEL_TYPE_STREAM_TUBE, 2L**32-1)] remove = [] caps = conn.Capabilities.AdvertiseCapabilities(add, remove) (disco_response, namespaces, _, _) = receive_presence_and_ask_caps(q, stream, False) check_caps(namespaces, JINGLE_CAPS) # Remove all our caps again add = [] remove = [cs.CHANNEL_TYPE_STREAMED_MEDIA, cs.CHANNEL_TYPE_STREAM_TUBE] caps = conn.Capabilities.AdvertiseCapabilities(add, remove) (disco_response, namespaces, _, _) = receive_presence_and_ask_caps(q, stream, False) check_caps(namespaces, []) # Add caps selectively (i.e. what a client that actually understood the # old Capabilities interface would do). With AUDIO and GTALK_P2P, we're # callable, but not via ICE-UDP, and not with video. # # (Jingle and raw UDP need no special client support, so are automatically # enabled whenever we can do audio or video.) add = [(cs.CHANNEL_TYPE_STREAMED_MEDIA, cs.MEDIA_CAP_AUDIO | cs.MEDIA_CAP_GTALKP2P)] remove = [] caps = conn.Capabilities.AdvertiseCapabilities(add, remove) (disco_response, namespaces, _, _) = receive_presence_and_ask_caps(q, stream, False) check_caps(namespaces, [ns.GOOGLE_P2P, ns.JINGLE_TRANSPORT_RAWUDP, ns.JINGLE, ns.JINGLE_015, ns.GOOGLE_FEAT_VOICE, ns.JINGLE_RTP_AUDIO, ns.JINGLE_RTP, ns.JINGLE_015_AUDIO]) # Remove all our caps again add = [] remove = [cs.CHANNEL_TYPE_STREAMED_MEDIA, cs.CHANNEL_TYPE_STREAM_TUBE] caps = conn.Capabilities.AdvertiseCapabilities(add, remove) (disco_response, namespaces, _, _) = receive_presence_and_ask_caps(q, stream, False) check_caps(namespaces, []) # With AUDIO but no transport, we are only callable via raw UDP, which # Google clients cannot do. add = [(cs.CHANNEL_TYPE_STREAMED_MEDIA, cs.MEDIA_CAP_AUDIO)] remove = [] caps = conn.Capabilities.AdvertiseCapabilities(add, remove) (disco_response, namespaces, _, _) = receive_presence_and_ask_caps(q, stream, False) check_caps(namespaces, [ns.JINGLE_TRANSPORT_RAWUDP, ns.JINGLE, ns.JINGLE_015, ns.JINGLE_RTP_AUDIO, ns.JINGLE_RTP, ns.JINGLE_015_AUDIO]) # Remove all our caps again add = [] remove = [cs.CHANNEL_TYPE_STREAMED_MEDIA, cs.CHANNEL_TYPE_STREAM_TUBE] caps = conn.Capabilities.AdvertiseCapabilities(add, remove) (disco_response, namespaces, _, _) = receive_presence_and_ask_caps(q, stream, False) check_caps(namespaces, []) # With VIDEO and ICE-UDP only, we are very futuristic indeed. # Google clients cannot interop with us. add = [(cs.CHANNEL_TYPE_STREAMED_MEDIA, cs.MEDIA_CAP_VIDEO | cs.MEDIA_CAP_ICEUDP)] remove = [] caps = conn.Capabilities.AdvertiseCapabilities(add, remove) (disco_response, namespaces, _, _) = receive_presence_and_ask_caps(q, stream, False) check_caps(namespaces, [ns.JINGLE_TRANSPORT_ICEUDP, ns.JINGLE_TRANSPORT_RAWUDP, ns.JINGLE, ns.JINGLE_015, ns.JINGLE_RTP_VIDEO, ns.JINGLE_RTP, ns.JINGLE_015_VIDEO]) if __name__ == '__main__': exec_test(run_test) telepathy-gabble-0.18.2/tests/twisted/caps/advertise-contact-caps.py0000644000175000017500000002340612312320035025501 0ustar00smcvsmcv00000000000000""" Test UpdateCapabilities. """ from functools import partial import dbus from twisted.words.xish import xpath, domish from servicetest import EventPattern from gabbletest import exec_test, sync_stream from caps_helper import caps_contain, receive_presence_and_ask_caps, \ FIXED_CAPS, JINGLE_CAPS, VARIABLE_CAPS, check_caps, disco_caps import constants as cs import ns from config import FILE_TRANSFER_ENABLED, VOIP_ENABLED if not VOIP_ENABLED: print "NOTE: built with --disable-voip" raise SystemExit(77) def noop_presence_update(q, stream): # At the moment Gabble does not optimize away presence updates that # have no effect. When it does, we can forbid those events here. #events = [EventPattern('stream-presence')] #q.forbid_events(events) sync_stream(q, stream) #q.unforbid_events(events) JINGLE_CAPS_EXCEPT_GVIDEO = [n for n in JINGLE_CAPS if n != ns.GOOGLE_FEAT_VIDEO] def run_test(q, bus, conn, stream, media_channel_type, media_interface, initial_audio, initial_video): conn.ContactCapabilities.UpdateCapabilities([ (cs.CLIENT + '.AbiWord', [ { cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_STREAM_TUBE, cs.TARGET_HANDLE_TYPE: cs.HT_CONTACT, cs.STREAM_TUBE_SERVICE: 'x-abiword' }, { cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_STREAM_TUBE, cs.TARGET_HANDLE_TYPE: cs.HT_ROOM, cs.STREAM_TUBE_SERVICE: 'x-abiword' }, ], []), ]) conn.Connect() _, initial_presence = q.expect_many( EventPattern('dbus-signal', signal='StatusChanged', args=[cs.CONN_STATUS_CONNECTED, cs.CSR_REQUESTED]), EventPattern('stream-presence'), ) (disco_response, namespaces, _) = disco_caps(q, stream, initial_presence) check_caps(namespaces, [ns.TUBES + '/stream#x-abiword']) conn.ContactCapabilities.UpdateCapabilities([ (cs.CLIENT + '.AbiWord', [ { cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_STREAM_TUBE, cs.TARGET_HANDLE_TYPE: cs.HT_CONTACT, cs.STREAM_TUBE_SERVICE: 'x-abiword' }, { cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_STREAM_TUBE, cs.TARGET_HANDLE_TYPE: cs.HT_ROOM, cs.STREAM_TUBE_SERVICE: 'x-abiword' }, ], []), (cs.CLIENT + '.KCall', [ { cs.CHANNEL_TYPE: media_channel_type }, { cs.CHANNEL_TYPE: media_channel_type, initial_audio: True}, { cs.CHANNEL_TYPE: media_channel_type, initial_video: True}, ], [ media_interface + '/gtalk-p2p', media_interface + '/ice-udp', media_interface + '/video/h264', ]), ]) (disco_response, namespaces, _, _) = receive_presence_and_ask_caps(q, stream, False) check_caps(namespaces, JINGLE_CAPS + [ns.TUBES + '/stream#x-abiword']) # Removing our H264 codec removes our ability to do Google Video conn.ContactCapabilities.UpdateCapabilities([ (cs.CLIENT + '.KCall', [ { cs.CHANNEL_TYPE: media_channel_type }, { cs.CHANNEL_TYPE: media_channel_type, initial_audio: True}, { cs.CHANNEL_TYPE: media_channel_type, initial_video: True}, ], [ media_interface + '/gtalk-p2p', media_interface + '/ice-udp', ]), ]) (disco_response, namespaces, _, _) = receive_presence_and_ask_caps(q, stream, False) check_caps(namespaces, JINGLE_CAPS_EXCEPT_GVIDEO + [ns.TUBES + '/stream#x-abiword']) # Remove AbiWord's caps conn.ContactCapabilities.UpdateCapabilities([ (cs.CLIENT + '.AbiWord', [], []), ]) (disco_response, namespaces, _, _) = receive_presence_and_ask_caps(q, stream, False) check_caps(namespaces, JINGLE_CAPS_EXCEPT_GVIDEO) # Remove KCall's caps too conn.ContactCapabilities.UpdateCapabilities([ (cs.CLIENT + '.KCall', [], []), ]) (disco_response, namespaces, _, _) = receive_presence_and_ask_caps(q, stream, False) check_caps(namespaces, []) # If AbiWord claims that it can do MediaSignalling things on its Tubes # channels, then it's wrong, and that still doesn't make us callable. conn.ContactCapabilities.UpdateCapabilities([ (cs.CLIENT + '.AbiWord', [ { cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_STREAM_TUBE, cs.TARGET_HANDLE_TYPE: cs.HT_CONTACT, cs.STREAM_TUBE_SERVICE: 'x-abiword' }, { cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_STREAM_TUBE, cs.TARGET_HANDLE_TYPE: cs.HT_ROOM, cs.STREAM_TUBE_SERVICE: 'x-abiword' }, ], [ media_interface + '/gtalk-p2p', media_interface + '/ice-udp', media_interface + '/video/h264', ]), ]) (disco_response, namespaces, _, _) = receive_presence_and_ask_caps(q, stream, False) check_caps(namespaces, [ns.TUBES + '/stream#x-abiword']) # Remove the broken version of AbiWord's caps conn.ContactCapabilities.UpdateCapabilities([ (cs.CLIENT + '.AbiWord', [], []), ]) (disco_response, namespaces, _, _) = receive_presence_and_ask_caps(q, stream, False) check_caps(namespaces, []) # Add caps selectively. Here we're callable, but not via ICE-UDP, and not # with video. # # (Jingle and raw UDP need no special client support, so are automatically # enabled whenever we can do audio or video.) conn.ContactCapabilities.UpdateCapabilities([ (cs.CLIENT + '.KCall', [ { cs.CHANNEL_TYPE: media_channel_type }, { cs.CHANNEL_TYPE: media_channel_type, initial_audio: True}, ], [media_interface + '/gtalk-p2p']), ]) (disco_response, namespaces, _, _) = receive_presence_and_ask_caps(q, stream, False) check_caps(namespaces, [ns.GOOGLE_P2P, ns.JINGLE_TRANSPORT_RAWUDP, ns.JINGLE, ns.JINGLE_015, ns.GOOGLE_FEAT_VOICE, ns.JINGLE_RTP_AUDIO, ns.JINGLE_RTP, ns.JINGLE_015_AUDIO]) # With AUDIO but no transport, we are only callable via raw UDP, which # Google clients cannot do. conn.ContactCapabilities.UpdateCapabilities([ (cs.CLIENT + '.KCall', [ { cs.CHANNEL_TYPE: media_channel_type }, { cs.CHANNEL_TYPE: media_channel_type, initial_audio: True}, ], []) ]) (disco_response, namespaces, _, _) = receive_presence_and_ask_caps(q, stream, False) check_caps(namespaces, [ns.JINGLE_TRANSPORT_RAWUDP, ns.JINGLE, ns.JINGLE_015, ns.JINGLE_RTP_AUDIO, ns.JINGLE_RTP, ns.JINGLE_015_AUDIO]) # With VIDEO and ICE-UDP only, we are very futuristic indeed. # Google clients cannot interop with us. conn.ContactCapabilities.UpdateCapabilities([ (cs.CLIENT + '.KCall', [ { cs.CHANNEL_TYPE: media_channel_type }, { cs.CHANNEL_TYPE: media_channel_type, initial_video: True}, ], [ media_interface + '/ice-udp', media_interface + '/video/theora', ]), ]) (disco_response, namespaces, _, _) = receive_presence_and_ask_caps(q, stream, False) check_caps(namespaces, [ns.JINGLE_TRANSPORT_ICEUDP, ns.JINGLE_TRANSPORT_RAWUDP, ns.JINGLE, ns.JINGLE_015, ns.JINGLE_RTP_VIDEO, ns.JINGLE_RTP, ns.JINGLE_015_VIDEO]) # Remove KCall to simplify subsequent checks conn.ContactCapabilities.UpdateCapabilities([ (cs.CLIENT + '.KCall', [], []), ]) (disco_response, namespaces, _, _) = receive_presence_and_ask_caps(q, stream, False) check_caps(namespaces, []) # Support file transfer if FILE_TRANSFER_ENABLED: conn.ContactCapabilities.UpdateCapabilities([ (cs.CLIENT + '.FileReceiver', [{ cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_FILE_TRANSFER, cs.TARGET_HANDLE_TYPE: cs.HT_CONTACT, }], []), ]) (disco_response, namespaces, _, _) = receive_presence_and_ask_caps(q, stream, False) check_caps(namespaces, [ns.FILE_TRANSFER]) def run_mixed_test (q, bus, conn, stream): conn.Connect() conn.ContactCapabilities.UpdateCapabilities([ (cs.CLIENT + '.SquareWheel', [ { cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_STREAMED_MEDIA, cs.INITIAL_AUDIO: True}, { cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_STREAMED_MEDIA, cs.INITIAL_VIDEO: True}, ], [ cs.CHANNEL_IFACE_MEDIA_SIGNALLING + '/gtalk-p2p', cs.CHANNEL_IFACE_MEDIA_SIGNALLING + '/ice-udp', cs.CHANNEL_IFACE_MEDIA_SIGNALLING + '/video/h264', ]), (cs.CLIENT + '.FlyingCar', [ { cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_CALL, cs.CALL_INITIAL_AUDIO: True}, { cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_CALL, cs.CALL_INITIAL_VIDEO: True}, ], [ cs.CHANNEL_TYPE_CALL + '/gtalk-p2p', cs.CHANNEL_TYPE_CALL + '/ice', cs.CHANNEL_TYPE_CALL + '/video/h264', ]), ]) (disco_response, namespaces, _, _) = receive_presence_and_ask_caps(q, stream, False) check_caps(namespaces, JINGLE_CAPS) if __name__ == '__main__': exec_test( partial(run_test, media_channel_type=cs.CHANNEL_TYPE_STREAMED_MEDIA, media_interface=cs.CHANNEL_IFACE_MEDIA_SIGNALLING, initial_audio=cs.INITIAL_AUDIO, initial_video=cs.INITIAL_VIDEO), do_connect=False) exec_test( partial(run_test, media_channel_type=cs.CHANNEL_TYPE_CALL, media_interface=cs.CHANNEL_TYPE_CALL, initial_audio=cs.CALL_INITIAL_AUDIO, initial_video=cs.CALL_INITIAL_VIDEO), do_connect=False) exec_test(run_mixed_test, do_connect=False) telepathy-gabble-0.18.2/tests/tp-error-from-wocky.c0000644000175000017500000000646412200204333022167 0ustar00smcvsmcv00000000000000#include "config.h" #include static void test_remap (GQuark domain, gint code, const gchar *message, TpConnectionStatus prev_status, gint exp_code, const gchar *exp_message, TpConnectionStatusReason exp_reason) { GError *error = NULL; GError wocky_error = { domain, code, (gchar *) message }; TpConnectionStatusReason conn_reason; gabble_set_tp_conn_error_from_wocky (&wocky_error, prev_status, &conn_reason, &error); g_assert (error != NULL); g_assert_cmpstr (g_quark_to_string (error->domain), ==, g_quark_to_string (TP_ERROR)); g_assert_cmpint (error->code, ==, exp_code); g_assert_cmpint (conn_reason, ==, exp_reason); if (exp_message != NULL) g_assert_cmpstr (error->message, ==, exp_message); g_error_free (error); } #define TEST_XMPP(nick, n, message, exp_code, exp_reason) \ test_remap (WOCKY_XMPP_ERROR, WOCKY_XMPP_ERROR_ ## nick, message, \ TP_CONNECTION_STATUS_CONNECTED, \ exp_code, \ "WOCKY_XMPP_ERROR_" #nick " (#" #n "): " \ message, \ exp_reason) int main (void) { g_type_init (); test_remap (WOCKY_XMPP_ERROR, WOCKY_XMPP_ERROR_FORBIDDEN, "computer says no", TP_CONNECTION_STATUS_CONNECTED, TP_ERROR_PERMISSION_DENIED, "WOCKY_XMPP_ERROR_FORBIDDEN (#8): computer says no", TP_CONNECTION_STATUS_REASON_AUTHENTICATION_FAILED); /* shorthand version of the above */ TEST_XMPP (FORBIDDEN, 8, "computer says no", TP_ERROR_PERMISSION_DENIED, TP_CONNECTION_STATUS_REASON_AUTHENTICATION_FAILED); /* more mappings */ TEST_XMPP (RESOURCE_CONSTRAINT, 19, "shut up!", TP_ERROR_SERVICE_BUSY, TP_CONNECTION_STATUS_REASON_NONE_SPECIFIED); TEST_XMPP (FEATURE_NOT_IMPLEMENTED, 20, "what?", TP_ERROR_NOT_AVAILABLE, TP_CONNECTION_STATUS_REASON_NONE_SPECIFIED); /* ConnectionReplaced, AlreadyConnected are mapped depending on the * connection status */ test_remap (WOCKY_XMPP_STREAM_ERROR, WOCKY_XMPP_STREAM_ERROR_CONFLICT, "go away", TP_CONNECTION_STATUS_CONNECTED, TP_ERROR_CONNECTION_REPLACED, "WOCKY_XMPP_STREAM_ERROR_CONFLICT (#2): go away", TP_CONNECTION_STATUS_REASON_NAME_IN_USE); test_remap (WOCKY_XMPP_STREAM_ERROR, WOCKY_XMPP_STREAM_ERROR_CONFLICT, "no you don't", TP_CONNECTION_STATUS_CONNECTING, TP_ERROR_ALREADY_CONNECTED, "WOCKY_XMPP_STREAM_ERROR_CONFLICT (#2): no you don't", TP_CONNECTION_STATUS_REASON_NAME_IN_USE); /* out-of-range is handled gracefully */ test_remap (WOCKY_XMPP_ERROR, 12345678, "lalala I am broken", TP_CONNECTION_STATUS_CONNECTED, TP_ERROR_NOT_AVAILABLE, "unknown WockyXmppError code (#12345678): lalala I am broken", TP_CONNECTION_STATUS_REASON_NONE_SPECIFIED); /* GIOError is NetworkError, for now */ test_remap (G_IO_ERROR, G_IO_ERROR_TIMED_OUT, "network fail", TP_CONNECTION_STATUS_CONNECTED, TP_ERROR_NETWORK_ERROR, "G_IO_ERROR_TIMED_OUT (#24): network fail", TP_CONNECTION_STATUS_REASON_NETWORK_ERROR); /* other domains do something basically sane (the message will be something * vaguely helpful) */ test_remap (G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_PARSE, "what's this doing here?", TP_CONNECTION_STATUS_CONNECTED, TP_ERROR_NOT_AVAILABLE, NULL, TP_CONNECTION_STATUS_REASON_NONE_SPECIFIED); return 0; } telepathy-gabble-0.18.2/tests/test-presence.c0000644000175000017500000001752512200204333021103 0ustar00smcvsmcv00000000000000 #include "config.h" #include #ifdef HAVE_UNISTD_H # include #endif #include #include "src/debug.h" #include "src/presence.h" #include "src/namespaces.h" static void big_test_of_doom (void) { const gchar *resource; GabblePresence *presence; GabbleCapabilitySet *cap_set; time_t now = time (NULL); GPtrArray *data_forms = g_ptr_array_new (); /* When we create a new presence, we know nothing about the contact in * question's presence. */ presence = gabble_presence_new (); g_assert (GABBLE_PRESENCE_UNKNOWN == presence->status); g_assert (NULL == presence->status_message); /* offline presence from no resource: we now know something about this * contact's presence. */ g_assert (TRUE == gabble_presence_update (presence, NULL, GABBLE_PRESENCE_OFFLINE, NULL, 0, NULL, now)); g_assert (GABBLE_PRESENCE_OFFLINE == presence->status); g_assert (NULL == presence->status_message); /* offline presence from unknown resource: no change */ g_assert (FALSE == gabble_presence_update (presence, "foo", GABBLE_PRESENCE_OFFLINE, NULL, 0, NULL, now)); /* available presence from unknown resource: change */ g_assert (TRUE == gabble_presence_update (presence, "foo", GABBLE_PRESENCE_AVAILABLE, NULL, 0, NULL, now)); /* accumulated presence has changed; status message unchanged */ g_assert (GABBLE_PRESENCE_AVAILABLE == presence->status); g_assert (NULL == presence->status_message); /* available presence again; no change */ g_assert (FALSE == gabble_presence_update (presence, "foo", GABBLE_PRESENCE_AVAILABLE, NULL, 0, NULL, now)); /* available presence again, but with status message: change */ g_assert (TRUE == gabble_presence_update (presence, "foo", GABBLE_PRESENCE_AVAILABLE, "status message", 0, NULL, now)); /* accumulated presence unchanged; status message changed */ g_assert (GABBLE_PRESENCE_AVAILABLE == presence->status); g_assert (0 == strcmp ("status message", presence->status_message)); /* same presence again; no change */ g_assert (FALSE == gabble_presence_update (presence, "foo", GABBLE_PRESENCE_AVAILABLE, "status message", 0, NULL, now)); /* time passes */ now++; /* presence from different resource, but equal present-ness and equal * status message; unchanged */ g_assert (FALSE == gabble_presence_update (presence, "bar", GABBLE_PRESENCE_AVAILABLE, "status message", 0, NULL, now)); g_assert (GABBLE_PRESENCE_AVAILABLE == presence->status); g_assert (0 == strcmp ("status message", presence->status_message)); /* but if we were to make a voip call, we would prefer the newer one */ g_assert (0 == strcmp ("bar", gabble_presence_pick_resource_by_caps (presence, 0, NULL, NULL))); /* mountain ranges form */ now++; /* presence from different resource, but equal present-ness and different * status message; changed */ g_assert (TRUE == gabble_presence_update (presence, "baz", GABBLE_PRESENCE_AVAILABLE, "dingbats", 0, NULL, now)); g_assert (GABBLE_PRESENCE_AVAILABLE == presence->status); g_assert (0 == strcmp ("dingbats", presence->status_message)); /* babies are born */ now++; /* presence with higher priority; presence and message changed */ g_assert (TRUE == gabble_presence_update (presence, "bar", GABBLE_PRESENCE_AVAILABLE, "dingoes", 1, NULL, now)); g_assert (GABBLE_PRESENCE_AVAILABLE == presence->status); g_assert (0 == strcmp ("dingoes", presence->status_message)); /* third-world dictators are deposed */ now++; /* now foo is newer, so the next voip call would prefer that */ g_assert (FALSE == gabble_presence_update (presence, "foo", GABBLE_PRESENCE_AVAILABLE, "status message", 0, NULL, now)); g_assert (0 == strcmp ("foo", gabble_presence_pick_resource_by_caps (presence, 0, NULL, NULL))); /* portal 2 is released */ now++; /* presence from first resource with greated present-ness: change */ g_assert (TRUE == gabble_presence_update (presence, "foo", GABBLE_PRESENCE_CHAT, "status message", 0, NULL, now)); /* seasons change */ now++; /* make bar be the latest presence: no change, since foo is more present */ g_assert (FALSE == gabble_presence_update (presence, "bar", GABBLE_PRESENCE_AVAILABLE, "dingoes", 1, NULL, now)); /* we still prefer foo for the voip calls, because it's more present */ g_assert (0 == strcmp ("foo", gabble_presence_pick_resource_by_caps (presence, 0, NULL, NULL))); g_assert (GABBLE_PRESENCE_CHAT == presence->status); g_assert (0 == strcmp ("status message", presence->status_message)); /* no resource has the Google voice cap */ resource = gabble_presence_pick_resource_by_caps (presence, 0, gabble_capability_set_predicate_has, NS_GOOGLE_FEAT_VOICE); g_assert (NULL == resource); /* give voice cap to second resource, but make priority negative */ g_assert (FALSE == gabble_presence_update (presence, "bar", GABBLE_PRESENCE_AVAILABLE, "dingoes", -1, NULL, now)); cap_set = gabble_capability_set_new (); gabble_capability_set_add (cap_set, NS_GOOGLE_FEAT_VOICE); gabble_presence_set_capabilities (presence, "bar", cap_set, data_forms, 0); gabble_capability_set_free (cap_set); /* no resource with non-negative priority has the Google voice cap */ resource = gabble_presence_pick_resource_by_caps (presence, 0, gabble_capability_set_predicate_has, NS_GOOGLE_FEAT_VOICE); g_assert (NULL == resource); /* give voice cap to first resource */ cap_set = gabble_capability_set_new (); gabble_capability_set_add (cap_set, NS_GOOGLE_FEAT_VOICE); gabble_presence_set_capabilities (presence, "foo", cap_set, data_forms, 0); gabble_capability_set_free (cap_set); /* resource has voice cap */ resource = gabble_presence_pick_resource_by_caps (presence, 0, gabble_capability_set_predicate_has, NS_GOOGLE_FEAT_VOICE); g_assert (0 == strcmp ("foo", resource)); /* presence turns up from null resource; it trumps other presence regardless * of whether status is more present or not */ g_assert (TRUE == gabble_presence_update (presence, NULL, GABBLE_PRESENCE_OFFLINE, "gone", 0, NULL, now)); g_assert (GABBLE_PRESENCE_OFFLINE == presence->status); g_assert (0 == strcmp ("gone", presence->status_message)); /* caps are gone too */ resource = gabble_presence_pick_resource_by_caps (presence, 0, gabble_capability_set_predicate_has, NS_GOOGLE_FEAT_VOICE); g_assert (NULL == resource); g_ptr_array_unref (data_forms); g_object_unref (presence); } /* * prefer_higher_priority_resources: * * This is a regression test for a bug which didn't actually happen, but would * have happened (and would not have been caught by the other tests in this * file) had a series of apparently-tautological if(){}s been turned into * if(){}else if(){} as suggested in a review comment * . */ static void prefer_higher_priority_resources (void) { GabblePresence *presence = gabble_presence_new (); time_t now = time (NULL); /* 'foo' and 'bar' are equally available, at the same time, but bar has a * lower priority. */ gabble_presence_update (presence, "foo", GABBLE_PRESENCE_AVAILABLE, "foo", 10, NULL, now); gabble_presence_update (presence, "bar", GABBLE_PRESENCE_AVAILABLE, "bar", 5, NULL, now); /* We should be sure to prefer "foo"'s status message to "bar"'s. */ g_assert_cmpstr (presence->status_message, ==, "foo"); g_object_unref (presence); } int main (int argc, char **argv) { int ret; g_type_init (); gabble_capabilities_init (NULL); gabble_debug_set_flags_from_env (); g_test_init (&argc, &argv, NULL); g_test_add_func ("/presence/big-test-of-doom", big_test_of_doom); g_test_add_func ("/presence/prefer-higher-priority-resources", prefer_higher_priority_resources); ret = g_test_run (); gabble_capabilities_finalize (NULL); gabble_debug_free (); return ret; } telepathy-gabble-0.18.2/tests/test-parse-message.c0000644000175000017500000002241112200204333022021 0ustar00smcvsmcv00000000000000 #include "config.h" #include #include #include "src/util.h" #include "src/message-util.h" /* Test the most basic possible. */ static void test1 (void) { WockyStanza *msg; gboolean ret; const gchar *from; time_t stamp; TpChannelTextMessageType type; TpChannelTextSendError send_error; TpDeliveryStatus delivery_status; const gchar *id; const gchar *body; gint state; msg = wocky_stanza_build (WOCKY_STANZA_TYPE_MESSAGE, WOCKY_STANZA_SUB_TYPE_NONE, "foo@bar.com", NULL, '@', "id", "a867c060-bd3f-4ecc-a38f-3e306af48e4c", NULL); ret = gabble_message_util_parse_incoming_message ( msg, &from, &stamp, &type, &id, &body, &state, &send_error, &delivery_status); g_assert (ret); g_assert_cmpstr (id, ==, "a867c060-bd3f-4ecc-a38f-3e306af48e4c"); g_assert_cmpstr (from, ==, "foo@bar.com"); g_assert_cmpuint (stamp, ==, 0); g_assert_cmpuint (type, ==, TP_CHANNEL_TEXT_MESSAGE_TYPE_NOTICE); g_assert_cmpstr (body, ==, NULL); g_assert_cmpuint (state, ==, -1); g_assert_cmpuint (send_error, ==, GABBLE_TEXT_CHANNEL_SEND_NO_ERROR); g_object_unref (msg); } /* A with a simple body. Parsed as a NOTICE because it doesn't have * a 'type' attribute. */ static void test2 (void) { WockyStanza *msg; gboolean ret; const gchar *from; time_t stamp; TpChannelTextMessageType type; TpChannelTextSendError send_error; TpDeliveryStatus delivery_status; const gchar *id; const gchar *body; gint state; msg = wocky_stanza_build (WOCKY_STANZA_TYPE_MESSAGE, WOCKY_STANZA_SUB_TYPE_NONE, "foo@bar.com", NULL, '@', "id", "a867c060-bd3f-4ecc-a38f-3e306af48e4c", '(', "body", '$', "hello", ')', NULL); ret = gabble_message_util_parse_incoming_message ( msg, &from, &stamp, &type, &id, &body, &state, &send_error, &delivery_status); g_assert (ret); g_assert_cmpstr (id, ==, "a867c060-bd3f-4ecc-a38f-3e306af48e4c"); g_assert_cmpstr (from, ==, "foo@bar.com"); g_assert_cmpuint (stamp, ==, 0); g_assert_cmpuint (type, ==, TP_CHANNEL_TEXT_MESSAGE_TYPE_NOTICE); g_assert_cmpstr (body, ==, "hello"); g_assert_cmpuint (state, ==, -1); g_assert_cmpuint (send_error, ==, GABBLE_TEXT_CHANNEL_SEND_NO_ERROR); g_object_unref (msg); } /* Simple type="chat" message. */ static void test3 (void) { WockyStanza *msg; gboolean ret; const gchar *from; time_t stamp; TpChannelTextMessageType type; TpChannelTextSendError send_error; TpDeliveryStatus delivery_status; const gchar *id; const gchar *body; gint state; msg = wocky_stanza_build (WOCKY_STANZA_TYPE_MESSAGE, WOCKY_STANZA_SUB_TYPE_CHAT, "foo@bar.com", NULL, '@', "id", "a867c060-bd3f-4ecc-a38f-3e306af48e4c", '(', "body", '$', "hello", ')', NULL); ret = gabble_message_util_parse_incoming_message ( msg, &from, &stamp, &type, &id, &body, &state, &send_error, &delivery_status); g_assert (ret); g_assert_cmpstr (id, ==, "a867c060-bd3f-4ecc-a38f-3e306af48e4c"); g_assert_cmpstr (from, ==, "foo@bar.com"); g_assert_cmpuint (stamp, ==, 0); g_assert_cmpuint (type, ==, TP_CHANNEL_TEXT_MESSAGE_TYPE_NORMAL); g_assert_cmpstr (body, ==, "hello"); g_assert_cmpuint (state, ==, -1); g_assert_cmpuint (send_error, ==, GABBLE_TEXT_CHANNEL_SEND_NO_ERROR); g_object_unref (msg); } /* A simple error. */ static void test_error (void) { WockyStanza *msg; gboolean ret; const gchar *from; time_t stamp; TpChannelTextMessageType type; TpChannelTextSendError send_error; TpDeliveryStatus delivery_status; const gchar *id; const gchar *body; gint state; msg = wocky_stanza_build ( WOCKY_STANZA_TYPE_MESSAGE, WOCKY_STANZA_SUB_TYPE_ERROR, "foo@bar.com", NULL, '@', "id", "a867c060-bd3f-4ecc-a38f-3e306af48e4c", '(', "error", '$', "oops", ')', NULL); ret = gabble_message_util_parse_incoming_message ( msg, &from, &stamp, &type, &id, &body, &state, &send_error, &delivery_status); g_assert (ret); g_assert_cmpstr (id, ==, "a867c060-bd3f-4ecc-a38f-3e306af48e4c"); g_assert_cmpstr (from, ==, "foo@bar.com"); g_assert_cmpuint (stamp, ==, 0); g_assert_cmpuint (type, ==, TP_CHANNEL_TEXT_MESSAGE_TYPE_NOTICE); g_assert_cmpstr (body, ==, NULL); g_assert_cmpuint (state, ==, -1); g_assert_cmpuint (send_error, ==, TP_CHANNEL_TEXT_SEND_ERROR_UNKNOWN); g_assert_cmpuint (delivery_status, ==, TP_DELIVERY_STATUS_PERMANENTLY_FAILED); g_object_unref (msg); } /* A more complicated error, described in XEP-0086 as a "simple error response". */ static void test_another_error (void) { WockyStanza *msg; gboolean ret; const gchar *from; time_t stamp; TpChannelTextMessageType type; TpChannelTextSendError send_error; TpDeliveryStatus delivery_status; const gchar *id; const gchar *body; gint state; const gchar *message = "Wherefore art thou, Romeo?"; msg = wocky_stanza_build ( WOCKY_STANZA_TYPE_MESSAGE, WOCKY_STANZA_SUB_TYPE_ERROR, "romeo@montague.net/garden", "juliet@capulet.com/balcony", '@', "id", "a867c060-bd3f-4ecc-a38f-3e306af48e4c", '(', "body", '$', message, ')', '(', "error", '@', "code", "404", '@', "type", "cancel", '(', "item-not-found", ':', "urn:ietf:params:xml:ns:xmpp-stanzas", ')', ')', NULL); ret = gabble_message_util_parse_incoming_message ( msg, &from, &stamp, &type, &id, &body, &state, &send_error, &delivery_status); g_assert (ret); g_assert_cmpstr (id, ==, "a867c060-bd3f-4ecc-a38f-3e306af48e4c"); g_assert_cmpstr (from, ==, "romeo@montague.net/garden"); g_assert_cmpuint (stamp, ==, 0); g_assert_cmpuint (type, ==, TP_CHANNEL_TEXT_MESSAGE_TYPE_NOTICE); g_assert_cmpstr (body, ==, message); g_assert_cmpuint (state, ==, -1); g_assert_cmpuint (send_error, ==, TP_CHANNEL_TEXT_SEND_ERROR_INVALID_CONTACT); g_assert_cmpuint (delivery_status, ==, TP_DELIVERY_STATUS_PERMANENTLY_FAILED); g_object_unref (msg); } /* One million, seven hundred seventy-one thousand, five hundred sixty-one * errors. */ static void test_yet_another_error (void) { WockyStanza *msg; gboolean ret; const gchar *from; time_t stamp; TpChannelTextMessageType type; TpChannelTextSendError send_error; TpDeliveryStatus delivery_status; const gchar *id; const gchar *body; gint state; const gchar *message = "Its trilling seems to have a tranquilizing effect on " "the human nervous system."; msg = wocky_stanza_build ( WOCKY_STANZA_TYPE_MESSAGE, WOCKY_STANZA_SUB_TYPE_ERROR, "other@starfleet.us/Enterprise", "spock@starfleet.us/Enterprise", '@', "id", "a867c060-bd3f-4ecc-a38f-3e306af48e4c", '(', "body", '$', message, ')', '(', "error", '@', "code", "404", '@', "type", "wait", '(', "recipient-unavailable", ':', "urn:ietf:params:xml:ns:xmpp-stanzas", ')', ')', NULL); ret = gabble_message_util_parse_incoming_message ( msg, &from, &stamp, &type, &id, &body, &state, &send_error, &delivery_status); g_assert (ret); g_assert_cmpstr (id, ==, "a867c060-bd3f-4ecc-a38f-3e306af48e4c"); g_assert_cmpstr (from, ==, "other@starfleet.us/Enterprise"); g_assert_cmpuint (stamp, ==, 0); g_assert_cmpuint (type, ==, TP_CHANNEL_TEXT_MESSAGE_TYPE_NOTICE); g_assert_cmpstr (body, ==, message); g_assert_cmpuint (state, ==, -1); g_assert_cmpuint (send_error, ==, TP_CHANNEL_TEXT_SEND_ERROR_OFFLINE); g_assert_cmpuint (delivery_status, ==, TP_DELIVERY_STATUS_TEMPORARILY_FAILED); g_object_unref (msg); } static void test_google_offline (void) { WockyStanza *msg; gboolean ret; const gchar *from; time_t stamp; TpChannelTextMessageType type; TpChannelTextSendError send_error; TpDeliveryStatus delivery_status; const gchar *id; const gchar *body; gint state; msg = wocky_stanza_build (WOCKY_STANZA_TYPE_MESSAGE, WOCKY_STANZA_SUB_TYPE_NONE, "foo@bar.com", NULL, '@', "id", "a867c060-bd3f-4ecc-a38f-3e306af48e4c", '(', "body", '$', "hello", ')', '(', "x", ':', "jabber:x:delay", '@', "stamp", "20070927T13:24:14", ')', '(', "time", ':', "google:timestamp", '@', "ms", "1190899454656", ')', NULL); ret = gabble_message_util_parse_incoming_message ( msg, &from, &stamp, &type, &id, &body, &state, &send_error, &delivery_status); g_assert (ret); g_assert_cmpstr (id, ==, "a867c060-bd3f-4ecc-a38f-3e306af48e4c"); g_assert_cmpstr (from, ==, "foo@bar.com"); g_assert_cmpuint (stamp, ==, 1190899454); g_assert_cmpuint (type, ==, TP_CHANNEL_TEXT_MESSAGE_TYPE_NORMAL); g_assert_cmpstr (body, ==, "hello"); g_assert_cmpuint (state, ==, -1); g_assert_cmpuint (send_error, ==, GABBLE_TEXT_CHANNEL_SEND_NO_ERROR); g_object_unref (msg); } int main ( int argc, char *argv[]) { g_type_init (); g_test_init (&argc, &argv, NULL); g_test_add_func ("/parse-message/1", test1); g_test_add_func ("/parse-message/2", test2); g_test_add_func ("/parse-message/3", test3); g_test_add_func ("/parse-message/error", test_error); g_test_add_func ("/parse-message/another-error", test_another_error); g_test_add_func ("/parse-message/yet-another-error", test_yet_another_error); g_test_add_func ("/parse-message/google-offline", test_google_offline); return g_test_run (); } telepathy-gabble-0.18.2/tests/test-jid-decode.c0000644000175000017500000000246212200204333021260 0ustar00smcvsmcv00000000000000 #include "config.h" #include #include #include "src/util.h" static void test_pass ( const gchar *jid, const gchar *expected_node, const gchar *expected_domain, const gchar *expected_resource) { gchar *node = NULL; gchar *domain = NULL; gchar *resource = NULL; g_assert (wocky_decode_jid (jid, &node, &domain, &resource)); g_assert (!tp_strdiff (expected_node, node)); g_assert (!tp_strdiff (expected_domain, domain)); g_assert (!tp_strdiff (expected_resource, resource)); g_free (node); g_free (domain); g_free (resource); } static void test_fail (const gchar *jid) { gchar *node = NULL; gchar *domain = NULL; gchar *resource = NULL; g_assert (!wocky_decode_jid (jid, &node, &domain, &resource)); g_assert (node == NULL); g_assert (domain == NULL); g_assert (resource == NULL); } int main (void) { test_fail (""); test_pass ("bar", NULL, "bar", NULL); test_pass ("foo@bar", "foo", "bar", NULL); test_pass ("foo@bar/baz", "foo", "bar", "baz"); test_fail ("@bar"); test_fail ("foo@bar/"); test_pass ("Foo@Bar/Baz", "foo", "bar", "Baz"); test_fail ("foo@@"); test_fail ("foo&bar@baz"); test_pass ("foo/bar@baz", NULL, "foo", "bar@baz"); test_pass ("foo@bar/foo@bar/foo@bar", "foo", "bar", "foo@bar/foo@bar"); return 0; } telepathy-gabble-0.18.2/tests/test-handles.c0000644000175000017500000000424712227000321020712 0ustar00smcvsmcv00000000000000 #include "config.h" #include #include #include #include #include #include "src/connection.h" static void test_handles (guint handle_type) { TpHandleRepoIface *repos[NUM_TP_HANDLE_TYPES]; TpHandleRepoIface *tp_repo = NULL; GError *error = NULL; guint i; TpHandle handle = 0; const gchar *jid = "handle.test@foobar"; const gchar *return_jid; for (i = 0; i < NUM_TP_HANDLE_TYPES; i++) { repos[i] = NULL; } _gabble_connection_create_handle_repos (NULL, repos); tp_repo = repos[handle_type]; g_assert (tp_repo != NULL); /* Handle zero is never valid */ g_assert (tp_handle_is_valid (tp_repo, 0, &error) == FALSE); /* this should probably be InvalidHandle, but it was InvalidArgument in * older telepathy-glib */ g_assert (error->code == TP_ERROR_INVALID_ARGUMENT || error->code == TP_ERROR_INVALID_HANDLE); g_error_free (error); error = NULL; /* Properly return error when handle isn't in the repo */ g_assert (tp_handle_is_valid (tp_repo, 65536, &error) == FALSE); /* this should really be InvalidHandle, but it was InvalidArgument in * older telepathy-glib */ g_assert (error->code == TP_ERROR_INVALID_ARGUMENT || error->code == TP_ERROR_INVALID_HANDLE); g_error_free (error); error = NULL; /* Properly return when error out argument isn't provided */ g_assert (tp_handle_is_valid (tp_repo, 65536, NULL) == FALSE); /* It's not there to start with, unless we're using the static repo */ handle = tp_handle_lookup (tp_repo, jid, NULL, NULL); g_assert (handle == 0); /* ... but when we call tp_handle_ensure we get a new ref to it */ handle = tp_handle_ensure (tp_repo, jid, NULL, NULL); g_assert (handle != 0); /* Try to inspect it */ return_jid = tp_handle_inspect (tp_repo, handle); g_assert (!strcmp (return_jid, jid)); for (i = 0; i < NUM_TP_HANDLE_TYPES; i++) { if (repos[i]) g_object_unref ((GObject *) repos[i]); } } int main (int argc, char **argv) { g_type_init (); test_handles (TP_HANDLE_TYPE_CONTACT); test_handles (TP_HANDLE_TYPE_ROOM); return 0; } telepathy-gabble-0.18.2/tests/test-gabble-idle-weak.c0000644000175000017500000000316212200204333022343 0ustar00smcvsmcv00000000000000#include "config.h" #include #include "src/util.h" static gboolean idle_quit_called = FALSE; static GMainLoop *loop = NULL; static GObject *object; static gboolean idle_quit (gpointer data) { /* This callback should be called exactly once per test. */ g_assert (idle_quit_called == FALSE); idle_quit_called = TRUE; g_assert (data == object); g_main_loop_quit (loop); return FALSE; } /* Test 1: Source removed before object finalised. */ static void test_1 (void) { idle_quit_called = FALSE; object = g_object_new (G_TYPE_OBJECT, NULL); g_object_set_data (object, "foo", GUINT_TO_POINTER (42)); loop = g_main_loop_new (NULL, FALSE); gabble_idle_add_weak (idle_quit, object); g_main_loop_run (loop); g_object_unref (object); g_main_loop_unref (loop); g_assert (idle_quit_called == TRUE); } static gboolean idle_unref (gpointer data) { g_object_unref (G_OBJECT (data)); return FALSE; } /* Test 2: Object finalised before source removed. */ static void test_2 (void) { idle_quit_called = FALSE; object = g_object_new (G_TYPE_OBJECT, NULL); g_object_set_data (object, "foo", GUINT_TO_POINTER (42)); loop = g_main_loop_new (NULL, FALSE); g_idle_add (idle_unref, object); /* This idle quit shouldn't be called because the previous idle will unref * the object and trigger the weak notify. */ gabble_idle_add_weak (idle_quit, object); /* This one will be called, however. */ g_idle_add (idle_quit, object); g_main_loop_run (loop); g_main_loop_unref (loop); g_assert (idle_quit_called == TRUE); } int main (void) { g_type_init (); test_1(); test_2(); return 0; } telepathy-gabble-0.18.2/tests/test-dtube-unique-names.c0000644000175000017500000000435312200204333023002 0ustar00smcvsmcv00000000000000 #include "config.h" #include #ifdef HAVE_UNISTD_H # include #endif #include #include "src/tube-dbus.h" int main (int argc, char **argv) { guint i; const gchar *nicknames[] = { "short", "FirstWitch", "Second witch", "012345678901234567890123456789012345678901234567890123456789" "01234567890123456789012345678901234567890123456789" "01234567890123456789012345678901234567890123456789" "01234567890123456789012345", /* 186 */ "012345678901234567890123456789012345678901234567890123456789" "01234567890123456789012345678901234567890123456789" "01234567890123456789012345678901234567890123456789" "012345678901234567890123456", /* 187 */ "012345678901234567890123456789012345678901234567890123456789" "01234567890123456789012345678901234567890123456789" "01234567890123456789012345678901234567890123456789" "0123456789012345678901234567890123456789", /* 200 */ NULL }; const gchar *results[] = { ":2.c2hvcnQA", ":2.Rmlyc3RXaXRjaAAA", ":2.U2Vjb25kIHdpdGNo", ":2.MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkw" "MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTIzNDU2Nzg5MDEyMzQ1Njc4OTAxM" "jM0NTY3ODkwMTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTIzNDU2Nzg5MDEyMz" "Q1Njc4OTAxMjM0NTY3ODkwMTIzNDU2Nzg5MDEyMzQ1", ":2.MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkw" "MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTIzNDU2Nzg5MDEyMzQ1Njc4OTAxM" "jM0NTY3ODkwMTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTIzNDU2Nzg5MDEyMz" "Q1Njc4OTAxMjM0NTY3OEVd9C5NgmmRD6jp1ftG6XUEc11x", ":2.MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkw" "MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTIzNDU2Nzg5MDEyMzQ1Njc4OTAxM" "jM0NTY3ODkwMTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTIzNDU2Nzg5MDEyMz" "Q1Njc4OTAxMjM0NTY3OO-utwRnwcoUFhnJVMKg5pm9Hxal", NULL }; for (i = 0; nicknames[i] != NULL; i++) { gchar *name; name = _gabble_generate_dbus_unique_name (nicknames[i]); g_assert (strcmp (name, results[i]) == 0); g_free (name); } return 0; } telepathy-gabble-0.18.2/tests/README0000644000175000017500000000533212200204333017027 0ustar00smcvsmcv00000000000000To run all tests: make check or with coverage info: ./configure --enable-compiler-coverage make lcov-check == C tests == To run all C tests (assuming the current directory is $top_srcdir): make -C tests check-TESTS To run an individual test: make -C tests check-TESTS TESTS=test-handles To run tests under Valgrind: make -C tests check-valgrind To run an individual test under Valgrind: make -C tests check-valgrind TESTS=test-handles == Twisted tests == To run all Twisted tests: make check-twisted To run an individual Twisted test: make -C tests/twisted check-twisted TWISTED_TESTS=connect/test-success.py or: cd tests/twisted sh tools/with-session-bus.sh --config-file=tools/servicedir-uninstalled/tmp-session-bus.conf \ -- python connect/test-success.py To run with debug information: make -C tests/twisted check-twisted TWISTED_TESTS=connect/test-success.py \ CHECK_TWISTED_VERBOSE=1 or: cd tests/twisted sh tools/with-session-bus.sh --config-file=tools/servicedir-uninstalled/tmp-session-bus.conf \ -- python connect/test-success.py -v To debug an individual test you can set one of the following env variable: * GABBLE_TEST_VALGRIND : to run Gabble inside valgrind. The report is added to tools/gabble-testing.log. export GABBLE_TEST_VALGRIND=1 * GABBLE_TEST_REFDBG : to run Gabble inside refdbg. The report is written to tools/refdbg.log. You can change GABBLE_WRAPPER to use an alternative refdbg and change REFDBG_OPTIONS to set your own parameters. Example: export GABBLE_TEST_REFDBG=1 export GABBLE_WRAPPER="/path/to/refdbg" export REFDBG_OPTIONS="btnum=16" * GABBLE_WRAPPER="nemiver" : to run Gabble inside the graphical debugger nemiver. You'll be able to set up breakpoints; then hit the "continue" button to launch Gabble. * GABBLE_TEST_STRACE : to run Gabble inside strace. The report is written to tools/strace.log. export GABBLE_TEST_STRACE=1 * GABBLE_TEST_BUSTLE : to run bustle-dbus-monitor in the temporary bus daemon. The logs are saved to tools/*bustle-logs. export GABBLE_TEST_BUSTLE=1 * GABBLE_NODELAY : to run any Gabble test with TCP_NODELAY set on both Wocky and Twisted's socket. This can speed up tests significantly. export GABBLE_NODELAY=1 == Jingle tests == Various jingle tests run the same tests with different dialects. To only test certain dialects use the JINGLE_DIALECTS environment variable. Currently supported are jingle015, jingle031, gtalk03 and gtalk04. For example to only run generic tests with the two jingle dialects you can do: make -C tests/twisted check-twisted TWISTED_TESTS=jingle/\*.py \ JINGLE_DIALECTS=jingle015,jingle031 telepathy-gabble-0.18.2/tests/Makefile.am0000644000175000017500000000323712200204333020205 0ustar00smcvsmcv00000000000000SUBDIRS = twisted suppressions tests_list = \ test-dtube-unique-names \ test-gabble-idle-weak \ test-handles \ test-jid-decode \ test-parse-message \ test-presence \ test-tp-error-from-wocky gabble-C-tests.list: $(AM_V_GEN)echo $(tests_list) > $@ if ENABLE_INSTALLED_TESTS gabbletests_PROGRAMS = $(tests_list) gabbletests_DATA = gabble-C-tests.list else noinst_PROGRAMS = $(tests_list) endif LDADD = $(top_builddir)/src/libgabble-convenience.la AM_CFLAGS = $(ERROR_CFLAGS) @DBUS_CFLAGS@ @GLIB_CFLAGS@ @WOCKY_CFLAGS@ \ @TP_GLIB_CFLAGS@ \ -I $(top_srcdir) -I $(top_builddir) \ -I $(top_srcdir)/lib -I $(top_builddir)/lib TESTS = $(tests_list) TESTS_ENVIRONMENT = \ abs_top_builddir=@abs_top_builddir@ \ G_DEBUG=fatal-warnings,fatal-criticals \ sh $(top_srcdir)/tests/twisted/tools/with-session-bus.sh \ --config-file=$(top_builddir)/tests/twisted/tools/servicedir-uninstalled/tmp-session-bus.conf -- check-valgrind: $(TESTS) G_SLICE=always-malloc \ G_DEBUG=gc-friendly \ $(MAKE) \ TESTS_ENVIRONMENT="$(TESTS_ENVIRONMENT) \ libtool --mode=execute valgrind \ --leak-check=full \ --show-reachable=no \ --gen-suppressions=all \ --num-callers=20 \ --suppressions=@abs_top_srcdir@/tests/suppressions/gabble.supp \ --suppressions=@abs_top_srcdir@/tests/suppressions/tp-glib.supp \ --error-exitcode=1" \ check-TESTS check_c_sources = \ $(dbus_test_sources) \ test-dtube-unique-names.c \ test-presence.c \ test-jid-decode.c \ test-handles.c \ test-parse-message.c \ tp-error-from-wocky.c test_tp_error_from_wocky_SOURCES = tp-error-from-wocky.c include $(top_srcdir)/tools/check-coding-style.mk check-local: check-coding-style telepathy-gabble-0.18.2/tests/Makefile.in0000644000175000017500000013623112312536074020235 0ustar00smcvsmcv00000000000000# Makefile.in generated by automake 1.14.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2013 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ VPATH = @srcdir@ am__is_gnu_make = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)' am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ @ENABLE_INSTALLED_TESTS_TRUE@gabbletests_PROGRAMS = $(am__EXEEXT_1) @ENABLE_INSTALLED_TESTS_FALSE@noinst_PROGRAMS = $(am__EXEEXT_1) TESTS = $(am__EXEEXT_1) DIST_COMMON = $(top_srcdir)/tools/check-coding-style.mk \ $(srcdir)/Makefile.in $(srcdir)/Makefile.am \ $(top_srcdir)/depcomp $(top_srcdir)/test-driver README subdir = tests ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/ax_config_dir.m4 \ $(top_srcdir)/m4/compiler.m4 $(top_srcdir)/m4/libtool.m4 \ $(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \ $(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \ $(top_srcdir)/m4/tp-compiler-flag.m4 \ $(top_srcdir)/m4/tp-compiler-warnings.m4 \ $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = am__EXEEXT_1 = test-dtube-unique-names$(EXEEXT) \ test-gabble-idle-weak$(EXEEXT) test-handles$(EXEEXT) \ test-jid-decode$(EXEEXT) test-parse-message$(EXEEXT) \ test-presence$(EXEEXT) test-tp-error-from-wocky$(EXEEXT) am__installdirs = "$(DESTDIR)$(gabbletestsdir)" \ "$(DESTDIR)$(gabbletestsdir)" PROGRAMS = $(gabbletests_PROGRAMS) $(noinst_PROGRAMS) test_dtube_unique_names_SOURCES = test-dtube-unique-names.c test_dtube_unique_names_OBJECTS = test-dtube-unique-names.$(OBJEXT) test_dtube_unique_names_LDADD = $(LDADD) test_dtube_unique_names_DEPENDENCIES = \ $(top_builddir)/src/libgabble-convenience.la AM_V_lt = $(am__v_lt_@AM_V@) am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) am__v_lt_0 = --silent am__v_lt_1 = test_gabble_idle_weak_SOURCES = test-gabble-idle-weak.c test_gabble_idle_weak_OBJECTS = test-gabble-idle-weak.$(OBJEXT) test_gabble_idle_weak_LDADD = $(LDADD) test_gabble_idle_weak_DEPENDENCIES = \ $(top_builddir)/src/libgabble-convenience.la test_handles_SOURCES = test-handles.c test_handles_OBJECTS = test-handles.$(OBJEXT) test_handles_LDADD = $(LDADD) test_handles_DEPENDENCIES = \ $(top_builddir)/src/libgabble-convenience.la test_jid_decode_SOURCES = test-jid-decode.c test_jid_decode_OBJECTS = test-jid-decode.$(OBJEXT) test_jid_decode_LDADD = $(LDADD) test_jid_decode_DEPENDENCIES = \ $(top_builddir)/src/libgabble-convenience.la test_parse_message_SOURCES = test-parse-message.c test_parse_message_OBJECTS = test-parse-message.$(OBJEXT) test_parse_message_LDADD = $(LDADD) test_parse_message_DEPENDENCIES = \ $(top_builddir)/src/libgabble-convenience.la test_presence_SOURCES = test-presence.c test_presence_OBJECTS = test-presence.$(OBJEXT) test_presence_LDADD = $(LDADD) test_presence_DEPENDENCIES = \ $(top_builddir)/src/libgabble-convenience.la am_test_tp_error_from_wocky_OBJECTS = tp-error-from-wocky.$(OBJEXT) test_tp_error_from_wocky_OBJECTS = \ $(am_test_tp_error_from_wocky_OBJECTS) test_tp_error_from_wocky_LDADD = $(LDADD) test_tp_error_from_wocky_DEPENDENCIES = \ $(top_builddir)/src/libgabble-convenience.la AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) depcomp = $(SHELL) $(top_srcdir)/depcomp am__depfiles_maybe = depfiles am__mv = mv -f COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ $(AM_CFLAGS) $(CFLAGS) AM_V_CC = $(am__v_CC_@AM_V@) am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) am__v_CC_0 = @echo " CC " $@; am__v_CC_1 = CCLD = $(CC) LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ $(AM_LDFLAGS) $(LDFLAGS) -o $@ AM_V_CCLD = $(am__v_CCLD_@AM_V@) am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) am__v_CCLD_0 = @echo " CCLD " $@; am__v_CCLD_1 = SOURCES = test-dtube-unique-names.c test-gabble-idle-weak.c \ test-handles.c test-jid-decode.c test-parse-message.c \ test-presence.c $(test_tp_error_from_wocky_SOURCES) DIST_SOURCES = test-dtube-unique-names.c test-gabble-idle-weak.c \ test-handles.c test-jid-decode.c test-parse-message.c \ test-presence.c $(test_tp_error_from_wocky_SOURCES) RECURSIVE_TARGETS = all-recursive check-recursive cscopelist-recursive \ ctags-recursive dvi-recursive html-recursive info-recursive \ install-data-recursive install-dvi-recursive \ install-exec-recursive install-html-recursive \ install-info-recursive install-pdf-recursive \ install-ps-recursive install-recursive installcheck-recursive \ installdirs-recursive pdf-recursive ps-recursive \ tags-recursive uninstall-recursive am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; am__vpath_adj = case $$p in \ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ *) f=$$p;; \ esac; am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; am__install_max = 40 am__nobase_strip_setup = \ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` am__nobase_strip = \ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" am__nobase_list = $(am__nobase_strip_setup); \ for p in $$list; do echo "$$p $$p"; done | \ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ if (++n[$$2] == $(am__install_max)) \ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ END { for (dir in files) print dir, files[dir] }' am__base_list = \ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' am__uninstall_files_from_dir = { \ test -z "$$files" \ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ $(am__cd) "$$dir" && rm -f $$files; }; \ } DATA = $(gabbletests_DATA) RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive \ distclean-recursive maintainer-clean-recursive am__recursive_targets = \ $(RECURSIVE_TARGETS) \ $(RECURSIVE_CLEAN_TARGETS) \ $(am__extra_recursive_targets) AM_RECURSIVE_TARGETS = $(am__recursive_targets:-recursive=) TAGS CTAGS \ check recheck distdir am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) # Read a list of newline-separated strings from the standard input, # and print each of them once, without duplicates. Input order is # *not* preserved. am__uniquify_input = $(AWK) '\ BEGIN { nonempty = 0; } \ { items[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in items) print i; }; } \ ' # Make sure the list of sources is unique. This is necessary because, # e.g., the same source file might be shared among _SOURCES variables # for different programs/libraries. am__define_uniq_tagged_files = \ list='$(am__tagged_files)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | $(am__uniquify_input)` ETAGS = etags CTAGS = ctags am__tty_colors_dummy = \ mgn= red= grn= lgn= blu= brg= std=; \ am__color_tests=no am__tty_colors = { \ $(am__tty_colors_dummy); \ if test "X$(AM_COLOR_TESTS)" = Xno; then \ am__color_tests=no; \ elif test "X$(AM_COLOR_TESTS)" = Xalways; then \ am__color_tests=yes; \ elif test "X$$TERM" != Xdumb && { test -t 1; } 2>/dev/null; then \ am__color_tests=yes; \ fi; \ if test $$am__color_tests = yes; then \ red=''; \ grn=''; \ lgn=''; \ blu=''; \ mgn=''; \ brg=''; \ std=''; \ fi; \ } am__recheck_rx = ^[ ]*:recheck:[ ]* am__global_test_result_rx = ^[ ]*:global-test-result:[ ]* am__copy_in_global_log_rx = ^[ ]*:copy-in-global-log:[ ]* # A command that, given a newline-separated list of test names on the # standard input, print the name of the tests that are to be re-run # upon "make recheck". am__list_recheck_tests = $(AWK) '{ \ recheck = 1; \ while ((rc = (getline line < ($$0 ".trs"))) != 0) \ { \ if (rc < 0) \ { \ if ((getline line2 < ($$0 ".log")) < 0) \ recheck = 0; \ break; \ } \ else if (line ~ /$(am__recheck_rx)[nN][Oo]/) \ { \ recheck = 0; \ break; \ } \ else if (line ~ /$(am__recheck_rx)[yY][eE][sS]/) \ { \ break; \ } \ }; \ if (recheck) \ print $$0; \ close ($$0 ".trs"); \ close ($$0 ".log"); \ }' # A command that, given a newline-separated list of test names on the # standard input, create the global log from their .trs and .log files. am__create_global_log = $(AWK) ' \ function fatal(msg) \ { \ print "fatal: making $@: " msg | "cat >&2"; \ exit 1; \ } \ function rst_section(header) \ { \ print header; \ len = length(header); \ for (i = 1; i <= len; i = i + 1) \ printf "="; \ printf "\n\n"; \ } \ { \ copy_in_global_log = 1; \ global_test_result = "RUN"; \ while ((rc = (getline line < ($$0 ".trs"))) != 0) \ { \ if (rc < 0) \ fatal("failed to read from " $$0 ".trs"); \ if (line ~ /$(am__global_test_result_rx)/) \ { \ sub("$(am__global_test_result_rx)", "", line); \ sub("[ ]*$$", "", line); \ global_test_result = line; \ } \ else if (line ~ /$(am__copy_in_global_log_rx)[nN][oO]/) \ copy_in_global_log = 0; \ }; \ if (copy_in_global_log) \ { \ rst_section(global_test_result ": " $$0); \ while ((rc = (getline line < ($$0 ".log"))) != 0) \ { \ if (rc < 0) \ fatal("failed to read from " $$0 ".log"); \ print line; \ }; \ printf "\n"; \ }; \ close ($$0 ".trs"); \ close ($$0 ".log"); \ }' # Restructured Text title. am__rst_title = { sed 's/.*/ & /;h;s/./=/g;p;x;s/ *$$//;p;g' && echo; } # Solaris 10 'make', and several other traditional 'make' implementations, # pass "-e" to $(SHELL), and POSIX 2008 even requires this. Work around it # by disabling -e (using the XSI extension "set +e") if it's set. am__sh_e_setup = case $$- in *e*) set +e;; esac # Default flags passed to test drivers. am__common_driver_flags = \ --color-tests "$$am__color_tests" \ --enable-hard-errors "$$am__enable_hard_errors" \ --expect-failure "$$am__expect_failure" # To be inserted before the command running the test. Creates the # directory for the log if needed. Stores in $dir the directory # containing $f, in $tst the test, in $log the log. Executes the # developer- defined test setup AM_TESTS_ENVIRONMENT (if any), and # passes TESTS_ENVIRONMENT. Set up options for the wrapper that # will run the test scripts (or their associated LOG_COMPILER, if # thy have one). am__check_pre = \ $(am__sh_e_setup); \ $(am__vpath_adj_setup) $(am__vpath_adj) \ $(am__tty_colors); \ srcdir=$(srcdir); export srcdir; \ case "$@" in \ */*) am__odir=`echo "./$@" | sed 's|/[^/]*$$||'`;; \ *) am__odir=.;; \ esac; \ test "x$$am__odir" = x"." || test -d "$$am__odir" \ || $(MKDIR_P) "$$am__odir" || exit $$?; \ if test -f "./$$f"; then dir=./; \ elif test -f "$$f"; then dir=; \ else dir="$(srcdir)/"; fi; \ tst=$$dir$$f; log='$@'; \ if test -n '$(DISABLE_HARD_ERRORS)'; then \ am__enable_hard_errors=no; \ else \ am__enable_hard_errors=yes; \ fi; \ case " $(XFAIL_TESTS) " in \ *[\ \ ]$$f[\ \ ]* | *[\ \ ]$$dir$$f[\ \ ]*) \ am__expect_failure=yes;; \ *) \ am__expect_failure=no;; \ esac; \ $(AM_TESTS_ENVIRONMENT) $(TESTS_ENVIRONMENT) # A shell command to get the names of the tests scripts with any registered # extension removed (i.e., equivalently, the names of the test logs, with # the '.log' extension removed). The result is saved in the shell variable # '$bases'. This honors runtime overriding of TESTS and TEST_LOGS. Sadly, # we cannot use something simpler, involving e.g., "$(TEST_LOGS:.log=)", # since that might cause problem with VPATH rewrites for suffix-less tests. # See also 'test-harness-vpath-rewrite.sh' and 'test-trs-basic.sh'. am__set_TESTS_bases = \ bases='$(TEST_LOGS)'; \ bases=`for i in $$bases; do echo $$i; done | sed 's/\.log$$//'`; \ bases=`echo $$bases` RECHECK_LOGS = $(TEST_LOGS) TEST_SUITE_LOG = test-suite.log TEST_EXTENSIONS = @EXEEXT@ .test LOG_DRIVER = $(SHELL) $(top_srcdir)/test-driver LOG_COMPILE = $(LOG_COMPILER) $(AM_LOG_FLAGS) $(LOG_FLAGS) am__set_b = \ case '$@' in \ */*) \ case '$*' in \ */*) b='$*';; \ *) b=`echo '$@' | sed 's/\.log$$//'`; \ esac;; \ *) \ b='$*';; \ esac am__test_logs1 = $(TESTS:=.log) am__test_logs2 = $(am__test_logs1:@EXEEXT@.log=.log) TEST_LOGS = $(am__test_logs2:.test.log=.log) TEST_LOG_DRIVER = $(SHELL) $(top_srcdir)/test-driver TEST_LOG_COMPILE = $(TEST_LOG_COMPILER) $(AM_TEST_LOG_FLAGS) \ $(TEST_LOG_FLAGS) DIST_SUBDIRS = $(SUBDIRS) DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) am__relativize = \ dir0=`pwd`; \ sed_first='s,^\([^/]*\)/.*$$,\1,'; \ sed_rest='s,^[^/]*/*,,'; \ sed_last='s,^.*/\([^/]*\)$$,\1,'; \ sed_butlast='s,/*[^/]*$$,,'; \ while test -n "$$dir1"; do \ first=`echo "$$dir1" | sed -e "$$sed_first"`; \ if test "$$first" != "."; then \ if test "$$first" = ".."; then \ dir2=`echo "$$dir0" | sed -e "$$sed_last"`/"$$dir2"; \ dir0=`echo "$$dir0" | sed -e "$$sed_butlast"`; \ else \ first2=`echo "$$dir2" | sed -e "$$sed_first"`; \ if test "$$first2" = "$$first"; then \ dir2=`echo "$$dir2" | sed -e "$$sed_rest"`; \ else \ dir2="../$$dir2"; \ fi; \ dir0="$$dir0"/"$$first"; \ fi; \ fi; \ dir1=`echo "$$dir1" | sed -e "$$sed_rest"`; \ done; \ reldir="$$dir2" ACLOCAL = @ACLOCAL@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ AR = @AR@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CFLAGS = @CFLAGS@ CLIENT_TYPE = @CLIENT_TYPE@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CYGPATH_W = @CYGPATH_W@ DBUS_CFLAGS = @DBUS_CFLAGS@ DBUS_LIBS = @DBUS_LIBS@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DLLTOOL = @DLLTOOL@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ ENABLE_CODING_STYLE_CHECKS = @ENABLE_CODING_STYLE_CHECKS@ ENABLE_PLUGINS = @ENABLE_PLUGINS@ ERROR_CFLAGS = @ERROR_CFLAGS@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ GLIB_CFLAGS = @GLIB_CFLAGS@ GLIB_GENMARSHAL = @GLIB_GENMARSHAL@ GLIB_LIBS = @GLIB_LIBS@ GMODULE_CFLAGS = @GMODULE_CFLAGS@ GMODULE_LIBS = @GMODULE_LIBS@ GREP = @GREP@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ LD = @LD@ LDFLAGS = @LDFLAGS@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBTOOL = @LIBTOOL@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBOBJS = @LTLIBOBJS@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ NICE_CFLAGS = @NICE_CFLAGS@ NICE_LIBS = @NICE_LIBS@ NM = @NM@ NMEDIT = @NMEDIT@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ PYTHON = @PYTHON@ PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@ PYTHON_PLATFORM = @PYTHON_PLATFORM@ PYTHON_PREFIX = @PYTHON_PREFIX@ PYTHON_VERSION = @PYTHON_VERSION@ RANLIB = @RANLIB@ SED = @SED@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ SOUP_CFLAGS = @SOUP_CFLAGS@ SOUP_LIBS = @SOUP_LIBS@ STRIP = @STRIP@ TEST_PYTHON = @TEST_PYTHON@ TP_GLIB_CFLAGS = @TP_GLIB_CFLAGS@ TP_GLIB_LIBS = @TP_GLIB_LIBS@ VERSION = @VERSION@ WOCKY_CFLAGS = @WOCKY_CFLAGS@ WOCKY_LIBS = @WOCKY_LIBS@ XSLTPROC = @XSLTPROC@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ gabbletestsdir = @gabbletestsdir@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ mandir = @mandir@ mkdir_p = @mkdir_p@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ pkgpyexecdir = @pkgpyexecdir@ pkgpythondir = @pkgpythondir@ pluginexecdir = @pluginexecdir@ pluginexeclibdir = @pluginexeclibdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ pyexecdir = @pyexecdir@ pythondir = @pythondir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ srcdir = @srcdir@ sysconfdir = @sysconfdir@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ SUBDIRS = twisted suppressions tests_list = \ test-dtube-unique-names \ test-gabble-idle-weak \ test-handles \ test-jid-decode \ test-parse-message \ test-presence \ test-tp-error-from-wocky @ENABLE_INSTALLED_TESTS_TRUE@gabbletests_DATA = gabble-C-tests.list LDADD = $(top_builddir)/src/libgabble-convenience.la AM_CFLAGS = $(ERROR_CFLAGS) @DBUS_CFLAGS@ @GLIB_CFLAGS@ @WOCKY_CFLAGS@ \ @TP_GLIB_CFLAGS@ \ -I $(top_srcdir) -I $(top_builddir) \ -I $(top_srcdir)/lib -I $(top_builddir)/lib TESTS_ENVIRONMENT = \ abs_top_builddir=@abs_top_builddir@ \ G_DEBUG=fatal-warnings,fatal-criticals \ sh $(top_srcdir)/tests/twisted/tools/with-session-bus.sh \ --config-file=$(top_builddir)/tests/twisted/tools/servicedir-uninstalled/tmp-session-bus.conf -- check_c_sources = \ $(dbus_test_sources) \ test-dtube-unique-names.c \ test-presence.c \ test-jid-decode.c \ test-handles.c \ test-parse-message.c \ tp-error-from-wocky.c test_tp_error_from_wocky_SOURCES = tp-error-from-wocky.c all: all-recursive .SUFFIXES: .SUFFIXES: .c .lo .log .o .obj .test .test$(EXEEXT) .trs $(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(top_srcdir)/tools/check-coding-style.mk $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu tests/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --gnu tests/Makefile .PRECIOUS: Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ esac; $(top_srcdir)/tools/check-coding-style.mk: $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): install-gabbletestsPROGRAMS: $(gabbletests_PROGRAMS) @$(NORMAL_INSTALL) @list='$(gabbletests_PROGRAMS)'; test -n "$(gabbletestsdir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(gabbletestsdir)'"; \ $(MKDIR_P) "$(DESTDIR)$(gabbletestsdir)" || exit 1; \ fi; \ for p in $$list; do echo "$$p $$p"; done | \ sed 's/$(EXEEXT)$$//' | \ while read p p1; do if test -f $$p \ || test -f $$p1 \ ; then echo "$$p"; echo "$$p"; else :; fi; \ done | \ sed -e 'p;s,.*/,,;n;h' \ -e 's|.*|.|' \ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \ sed 'N;N;N;s,\n, ,g' | \ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \ if ($$2 == $$4) files[d] = files[d] " " $$1; \ else { print "f", $$3 "/" $$4, $$1; } } \ END { for (d in files) print "f", d, files[d] }' | \ while read type dir files; do \ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \ test -z "$$files" || { \ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(gabbletestsdir)$$dir'"; \ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(gabbletestsdir)$$dir" || exit $$?; \ } \ ; done uninstall-gabbletestsPROGRAMS: @$(NORMAL_UNINSTALL) @list='$(gabbletests_PROGRAMS)'; test -n "$(gabbletestsdir)" || list=; \ files=`for p in $$list; do echo "$$p"; done | \ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \ -e 's/$$/$(EXEEXT)/' \ `; \ test -n "$$list" || exit 0; \ echo " ( cd '$(DESTDIR)$(gabbletestsdir)' && rm -f" $$files ")"; \ cd "$(DESTDIR)$(gabbletestsdir)" && rm -f $$files clean-gabbletestsPROGRAMS: @list='$(gabbletests_PROGRAMS)'; test -n "$$list" || exit 0; \ echo " rm -f" $$list; \ rm -f $$list || exit $$?; \ test -n "$(EXEEXT)" || exit 0; \ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \ echo " rm -f" $$list; \ rm -f $$list clean-noinstPROGRAMS: @list='$(noinst_PROGRAMS)'; test -n "$$list" || exit 0; \ echo " rm -f" $$list; \ rm -f $$list || exit $$?; \ test -n "$(EXEEXT)" || exit 0; \ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \ echo " rm -f" $$list; \ rm -f $$list test-dtube-unique-names$(EXEEXT): $(test_dtube_unique_names_OBJECTS) $(test_dtube_unique_names_DEPENDENCIES) $(EXTRA_test_dtube_unique_names_DEPENDENCIES) @rm -f test-dtube-unique-names$(EXEEXT) $(AM_V_CCLD)$(LINK) $(test_dtube_unique_names_OBJECTS) $(test_dtube_unique_names_LDADD) $(LIBS) test-gabble-idle-weak$(EXEEXT): $(test_gabble_idle_weak_OBJECTS) $(test_gabble_idle_weak_DEPENDENCIES) $(EXTRA_test_gabble_idle_weak_DEPENDENCIES) @rm -f test-gabble-idle-weak$(EXEEXT) $(AM_V_CCLD)$(LINK) $(test_gabble_idle_weak_OBJECTS) $(test_gabble_idle_weak_LDADD) $(LIBS) test-handles$(EXEEXT): $(test_handles_OBJECTS) $(test_handles_DEPENDENCIES) $(EXTRA_test_handles_DEPENDENCIES) @rm -f test-handles$(EXEEXT) $(AM_V_CCLD)$(LINK) $(test_handles_OBJECTS) $(test_handles_LDADD) $(LIBS) test-jid-decode$(EXEEXT): $(test_jid_decode_OBJECTS) $(test_jid_decode_DEPENDENCIES) $(EXTRA_test_jid_decode_DEPENDENCIES) @rm -f test-jid-decode$(EXEEXT) $(AM_V_CCLD)$(LINK) $(test_jid_decode_OBJECTS) $(test_jid_decode_LDADD) $(LIBS) test-parse-message$(EXEEXT): $(test_parse_message_OBJECTS) $(test_parse_message_DEPENDENCIES) $(EXTRA_test_parse_message_DEPENDENCIES) @rm -f test-parse-message$(EXEEXT) $(AM_V_CCLD)$(LINK) $(test_parse_message_OBJECTS) $(test_parse_message_LDADD) $(LIBS) test-presence$(EXEEXT): $(test_presence_OBJECTS) $(test_presence_DEPENDENCIES) $(EXTRA_test_presence_DEPENDENCIES) @rm -f test-presence$(EXEEXT) $(AM_V_CCLD)$(LINK) $(test_presence_OBJECTS) $(test_presence_LDADD) $(LIBS) test-tp-error-from-wocky$(EXEEXT): $(test_tp_error_from_wocky_OBJECTS) $(test_tp_error_from_wocky_DEPENDENCIES) $(EXTRA_test_tp_error_from_wocky_DEPENDENCIES) @rm -f test-tp-error-from-wocky$(EXEEXT) $(AM_V_CCLD)$(LINK) $(test_tp_error_from_wocky_OBJECTS) $(test_tp_error_from_wocky_LDADD) $(LIBS) mostlyclean-compile: -rm -f *.$(OBJEXT) distclean-compile: -rm -f *.tab.c @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test-dtube-unique-names.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test-gabble-idle-weak.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test-handles.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test-jid-decode.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test-parse-message.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test-presence.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tp-error-from-wocky.Po@am__quote@ .c.o: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< .c.obj: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` .c.lo: @am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs install-gabbletestsDATA: $(gabbletests_DATA) @$(NORMAL_INSTALL) @list='$(gabbletests_DATA)'; test -n "$(gabbletestsdir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(gabbletestsdir)'"; \ $(MKDIR_P) "$(DESTDIR)$(gabbletestsdir)" || exit 1; \ fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; \ done | $(am__base_list) | \ while read files; do \ echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(gabbletestsdir)'"; \ $(INSTALL_DATA) $$files "$(DESTDIR)$(gabbletestsdir)" || exit $$?; \ done uninstall-gabbletestsDATA: @$(NORMAL_UNINSTALL) @list='$(gabbletests_DATA)'; test -n "$(gabbletestsdir)" || list=; \ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ dir='$(DESTDIR)$(gabbletestsdir)'; $(am__uninstall_files_from_dir) # This directory's subdirectories are mostly independent; you can cd # into them and run 'make' without going through this Makefile. # To change the values of 'make' variables: instead of editing Makefiles, # (1) if the variable is set in 'config.status', edit 'config.status' # (which will cause the Makefiles to be regenerated when you run 'make'); # (2) otherwise, pass the desired values on the 'make' command line. $(am__recursive_targets): @fail=; \ if $(am__make_keepgoing); then \ failcom='fail=yes'; \ else \ failcom='exit 1'; \ fi; \ dot_seen=no; \ target=`echo $@ | sed s/-recursive//`; \ case "$@" in \ distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \ *) list='$(SUBDIRS)' ;; \ esac; \ for subdir in $$list; do \ echo "Making $$target in $$subdir"; \ if test "$$subdir" = "."; then \ dot_seen=yes; \ local_target="$$target-am"; \ else \ local_target="$$target"; \ fi; \ ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ || eval $$failcom; \ done; \ if test "$$dot_seen" = "no"; then \ $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \ fi; test -z "$$fail" ID: $(am__tagged_files) $(am__define_uniq_tagged_files); mkid -fID $$unique tags: tags-recursive TAGS: tags tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) set x; \ here=`pwd`; \ if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \ include_option=--etags-include; \ empty_fix=.; \ else \ include_option=--include; \ empty_fix=; \ fi; \ list='$(SUBDIRS)'; for subdir in $$list; do \ if test "$$subdir" = .; then :; else \ test ! -f $$subdir/TAGS || \ set "$$@" "$$include_option=$$here/$$subdir/TAGS"; \ fi; \ done; \ $(am__define_uniq_tagged_files); \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ if test $$# -gt 0; then \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ "$$@" $$unique; \ else \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ $$unique; \ fi; \ fi ctags: ctags-recursive CTAGS: ctags ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) $(am__define_uniq_tagged_files); \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" cscopelist: cscopelist-recursive cscopelist-am: $(am__tagged_files) list='$(am__tagged_files)'; \ case "$(srcdir)" in \ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ *) sdir=$(subdir)/$(srcdir) ;; \ esac; \ for i in $$list; do \ if test -f "$$i"; then \ echo "$(subdir)/$$i"; \ else \ echo "$$sdir/$$i"; \ fi; \ done >> $(top_builddir)/cscope.files distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags # Recover from deleted '.trs' file; this should ensure that # "rm -f foo.log; make foo.trs" re-run 'foo.test', and re-create # both 'foo.log' and 'foo.trs'. Break the recipe in two subshells # to avoid problems with "make -n". .log.trs: rm -f $< $@ $(MAKE) $(AM_MAKEFLAGS) $< # Leading 'am--fnord' is there to ensure the list of targets does not # expand to empty, as could happen e.g. with make check TESTS=''. am--fnord $(TEST_LOGS) $(TEST_LOGS:.log=.trs): $(am__force_recheck) am--force-recheck: @: $(TEST_SUITE_LOG): $(TEST_LOGS) @$(am__set_TESTS_bases); \ am__f_ok () { test -f "$$1" && test -r "$$1"; }; \ redo_bases=`for i in $$bases; do \ am__f_ok $$i.trs && am__f_ok $$i.log || echo $$i; \ done`; \ if test -n "$$redo_bases"; then \ redo_logs=`for i in $$redo_bases; do echo $$i.log; done`; \ redo_results=`for i in $$redo_bases; do echo $$i.trs; done`; \ if $(am__make_dryrun); then :; else \ rm -f $$redo_logs && rm -f $$redo_results || exit 1; \ fi; \ fi; \ if test -n "$$am__remaking_logs"; then \ echo "fatal: making $(TEST_SUITE_LOG): possible infinite" \ "recursion detected" >&2; \ else \ am__remaking_logs=yes $(MAKE) $(AM_MAKEFLAGS) $$redo_logs; \ fi; \ if $(am__make_dryrun); then :; else \ st=0; \ errmsg="fatal: making $(TEST_SUITE_LOG): failed to create"; \ for i in $$redo_bases; do \ test -f $$i.trs && test -r $$i.trs \ || { echo "$$errmsg $$i.trs" >&2; st=1; }; \ test -f $$i.log && test -r $$i.log \ || { echo "$$errmsg $$i.log" >&2; st=1; }; \ done; \ test $$st -eq 0 || exit 1; \ fi @$(am__sh_e_setup); $(am__tty_colors); $(am__set_TESTS_bases); \ ws='[ ]'; \ results=`for b in $$bases; do echo $$b.trs; done`; \ test -n "$$results" || results=/dev/null; \ all=` grep "^$$ws*:test-result:" $$results | wc -l`; \ pass=` grep "^$$ws*:test-result:$$ws*PASS" $$results | wc -l`; \ fail=` grep "^$$ws*:test-result:$$ws*FAIL" $$results | wc -l`; \ skip=` grep "^$$ws*:test-result:$$ws*SKIP" $$results | wc -l`; \ xfail=`grep "^$$ws*:test-result:$$ws*XFAIL" $$results | wc -l`; \ xpass=`grep "^$$ws*:test-result:$$ws*XPASS" $$results | wc -l`; \ error=`grep "^$$ws*:test-result:$$ws*ERROR" $$results | wc -l`; \ if test `expr $$fail + $$xpass + $$error` -eq 0; then \ success=true; \ else \ success=false; \ fi; \ br='==================='; br=$$br$$br$$br$$br; \ result_count () \ { \ if test x"$$1" = x"--maybe-color"; then \ maybe_colorize=yes; \ elif test x"$$1" = x"--no-color"; then \ maybe_colorize=no; \ else \ echo "$@: invalid 'result_count' usage" >&2; exit 4; \ fi; \ shift; \ desc=$$1 count=$$2; \ if test $$maybe_colorize = yes && test $$count -gt 0; then \ color_start=$$3 color_end=$$std; \ else \ color_start= color_end=; \ fi; \ echo "$${color_start}# $$desc $$count$${color_end}"; \ }; \ create_testsuite_report () \ { \ result_count $$1 "TOTAL:" $$all "$$brg"; \ result_count $$1 "PASS: " $$pass "$$grn"; \ result_count $$1 "SKIP: " $$skip "$$blu"; \ result_count $$1 "XFAIL:" $$xfail "$$lgn"; \ result_count $$1 "FAIL: " $$fail "$$red"; \ result_count $$1 "XPASS:" $$xpass "$$red"; \ result_count $$1 "ERROR:" $$error "$$mgn"; \ }; \ { \ echo "$(PACKAGE_STRING): $(subdir)/$(TEST_SUITE_LOG)" | \ $(am__rst_title); \ create_testsuite_report --no-color; \ echo; \ echo ".. contents:: :depth: 2"; \ echo; \ for b in $$bases; do echo $$b; done \ | $(am__create_global_log); \ } >$(TEST_SUITE_LOG).tmp || exit 1; \ mv $(TEST_SUITE_LOG).tmp $(TEST_SUITE_LOG); \ if $$success; then \ col="$$grn"; \ else \ col="$$red"; \ test x"$$VERBOSE" = x || cat $(TEST_SUITE_LOG); \ fi; \ echo "$${col}$$br$${std}"; \ echo "$${col}Testsuite summary for $(PACKAGE_STRING)$${std}"; \ echo "$${col}$$br$${std}"; \ create_testsuite_report --maybe-color; \ echo "$$col$$br$$std"; \ if $$success; then :; else \ echo "$${col}See $(subdir)/$(TEST_SUITE_LOG)$${std}"; \ if test -n "$(PACKAGE_BUGREPORT)"; then \ echo "$${col}Please report to $(PACKAGE_BUGREPORT)$${std}"; \ fi; \ echo "$$col$$br$$std"; \ fi; \ $$success || exit 1 check-TESTS: @list='$(RECHECK_LOGS)'; test -z "$$list" || rm -f $$list @list='$(RECHECK_LOGS:.log=.trs)'; test -z "$$list" || rm -f $$list @test -z "$(TEST_SUITE_LOG)" || rm -f $(TEST_SUITE_LOG) @set +e; $(am__set_TESTS_bases); \ log_list=`for i in $$bases; do echo $$i.log; done`; \ trs_list=`for i in $$bases; do echo $$i.trs; done`; \ log_list=`echo $$log_list`; trs_list=`echo $$trs_list`; \ $(MAKE) $(AM_MAKEFLAGS) $(TEST_SUITE_LOG) TEST_LOGS="$$log_list"; \ exit $$?; recheck: all @test -z "$(TEST_SUITE_LOG)" || rm -f $(TEST_SUITE_LOG) @set +e; $(am__set_TESTS_bases); \ bases=`for i in $$bases; do echo $$i; done \ | $(am__list_recheck_tests)` || exit 1; \ log_list=`for i in $$bases; do echo $$i.log; done`; \ log_list=`echo $$log_list`; \ $(MAKE) $(AM_MAKEFLAGS) $(TEST_SUITE_LOG) \ am__force_recheck=am--force-recheck \ TEST_LOGS="$$log_list"; \ exit $$? test-dtube-unique-names.log: test-dtube-unique-names$(EXEEXT) @p='test-dtube-unique-names$(EXEEXT)'; \ b='test-dtube-unique-names'; \ $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ --log-file $$b.log --trs-file $$b.trs \ $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ "$$tst" $(AM_TESTS_FD_REDIRECT) test-gabble-idle-weak.log: test-gabble-idle-weak$(EXEEXT) @p='test-gabble-idle-weak$(EXEEXT)'; \ b='test-gabble-idle-weak'; \ $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ --log-file $$b.log --trs-file $$b.trs \ $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ "$$tst" $(AM_TESTS_FD_REDIRECT) test-handles.log: test-handles$(EXEEXT) @p='test-handles$(EXEEXT)'; \ b='test-handles'; \ $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ --log-file $$b.log --trs-file $$b.trs \ $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ "$$tst" $(AM_TESTS_FD_REDIRECT) test-jid-decode.log: test-jid-decode$(EXEEXT) @p='test-jid-decode$(EXEEXT)'; \ b='test-jid-decode'; \ $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ --log-file $$b.log --trs-file $$b.trs \ $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ "$$tst" $(AM_TESTS_FD_REDIRECT) test-parse-message.log: test-parse-message$(EXEEXT) @p='test-parse-message$(EXEEXT)'; \ b='test-parse-message'; \ $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ --log-file $$b.log --trs-file $$b.trs \ $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ "$$tst" $(AM_TESTS_FD_REDIRECT) test-presence.log: test-presence$(EXEEXT) @p='test-presence$(EXEEXT)'; \ b='test-presence'; \ $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ --log-file $$b.log --trs-file $$b.trs \ $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ "$$tst" $(AM_TESTS_FD_REDIRECT) test-tp-error-from-wocky.log: test-tp-error-from-wocky$(EXEEXT) @p='test-tp-error-from-wocky$(EXEEXT)'; \ b='test-tp-error-from-wocky'; \ $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ --log-file $$b.log --trs-file $$b.trs \ $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ "$$tst" $(AM_TESTS_FD_REDIRECT) .test.log: @p='$<'; \ $(am__set_b); \ $(am__check_pre) $(TEST_LOG_DRIVER) --test-name "$$f" \ --log-file $$b.log --trs-file $$b.trs \ $(am__common_driver_flags) $(AM_TEST_LOG_DRIVER_FLAGS) $(TEST_LOG_DRIVER_FLAGS) -- $(TEST_LOG_COMPILE) \ "$$tst" $(AM_TESTS_FD_REDIRECT) @am__EXEEXT_TRUE@.test$(EXEEXT).log: @am__EXEEXT_TRUE@ @p='$<'; \ @am__EXEEXT_TRUE@ $(am__set_b); \ @am__EXEEXT_TRUE@ $(am__check_pre) $(TEST_LOG_DRIVER) --test-name "$$f" \ @am__EXEEXT_TRUE@ --log-file $$b.log --trs-file $$b.trs \ @am__EXEEXT_TRUE@ $(am__common_driver_flags) $(AM_TEST_LOG_DRIVER_FLAGS) $(TEST_LOG_DRIVER_FLAGS) -- $(TEST_LOG_COMPILE) \ @am__EXEEXT_TRUE@ "$$tst" $(AM_TESTS_FD_REDIRECT) distdir: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done @list='$(DIST_SUBDIRS)'; for subdir in $$list; do \ if test "$$subdir" = .; then :; else \ $(am__make_dryrun) \ || test -d "$(distdir)/$$subdir" \ || $(MKDIR_P) "$(distdir)/$$subdir" \ || exit 1; \ dir1=$$subdir; dir2="$(distdir)/$$subdir"; \ $(am__relativize); \ new_distdir=$$reldir; \ dir1=$$subdir; dir2="$(top_distdir)"; \ $(am__relativize); \ new_top_distdir=$$reldir; \ echo " (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) top_distdir="$$new_top_distdir" distdir="$$new_distdir" \\"; \ echo " am__remove_distdir=: am__skip_length_check=: am__skip_mode_fix=: distdir)"; \ ($(am__cd) $$subdir && \ $(MAKE) $(AM_MAKEFLAGS) \ top_distdir="$$new_top_distdir" \ distdir="$$new_distdir" \ am__remove_distdir=: \ am__skip_length_check=: \ am__skip_mode_fix=: \ distdir) \ || exit 1; \ fi; \ done check-am: all-am $(MAKE) $(AM_MAKEFLAGS) check-TESTS check-local check: check-recursive all-am: Makefile $(PROGRAMS) $(DATA) installdirs: installdirs-recursive installdirs-am: for dir in "$(DESTDIR)$(gabbletestsdir)" "$(DESTDIR)$(gabbletestsdir)"; do \ test -z "$$dir" || $(MKDIR_P) "$$dir"; \ done install: install-recursive install-exec: install-exec-recursive install-data: install-data-recursive uninstall: uninstall-recursive install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-recursive install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: -test -z "$(TEST_LOGS)" || rm -f $(TEST_LOGS) -test -z "$(TEST_LOGS:.log=.trs)" || rm -f $(TEST_LOGS:.log=.trs) -test -z "$(TEST_SUITE_LOG)" || rm -f $(TEST_SUITE_LOG) clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." clean: clean-recursive clean-am: clean-gabbletestsPROGRAMS clean-generic clean-libtool \ clean-noinstPROGRAMS mostlyclean-am distclean: distclean-recursive -rm -rf ./$(DEPDIR) -rm -f Makefile distclean-am: clean-am distclean-compile distclean-generic \ distclean-tags dvi: dvi-recursive dvi-am: html: html-recursive html-am: info: info-recursive info-am: install-data-am: install-gabbletestsDATA install-gabbletestsPROGRAMS install-dvi: install-dvi-recursive install-dvi-am: install-exec-am: install-html: install-html-recursive install-html-am: install-info: install-info-recursive install-info-am: install-man: install-pdf: install-pdf-recursive install-pdf-am: install-ps: install-ps-recursive install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-recursive -rm -rf ./$(DEPDIR) -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-recursive mostlyclean-am: mostlyclean-compile mostlyclean-generic \ mostlyclean-libtool pdf: pdf-recursive pdf-am: ps: ps-recursive ps-am: uninstall-am: uninstall-gabbletestsDATA uninstall-gabbletestsPROGRAMS .MAKE: $(am__recursive_targets) check-am install-am install-strip .PHONY: $(am__recursive_targets) CTAGS GTAGS TAGS all all-am check \ check-TESTS check-am check-local clean \ clean-gabbletestsPROGRAMS clean-generic clean-libtool \ clean-noinstPROGRAMS cscopelist-am ctags ctags-am distclean \ distclean-compile distclean-generic distclean-libtool \ distclean-tags distdir dvi dvi-am html html-am info info-am \ install install-am install-data install-data-am install-dvi \ install-dvi-am install-exec install-exec-am \ install-gabbletestsDATA install-gabbletestsPROGRAMS \ install-html install-html-am install-info install-info-am \ install-man install-pdf install-pdf-am install-ps \ install-ps-am install-strip installcheck installcheck-am \ installdirs installdirs-am maintainer-clean \ maintainer-clean-generic mostlyclean mostlyclean-compile \ mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ recheck tags tags-am uninstall uninstall-am \ uninstall-gabbletestsDATA uninstall-gabbletestsPROGRAMS gabble-C-tests.list: $(AM_V_GEN)echo $(tests_list) > $@ check-valgrind: $(TESTS) G_SLICE=always-malloc \ G_DEBUG=gc-friendly \ $(MAKE) \ TESTS_ENVIRONMENT="$(TESTS_ENVIRONMENT) \ libtool --mode=execute valgrind \ --leak-check=full \ --show-reachable=no \ --gen-suppressions=all \ --num-callers=20 \ --suppressions=@abs_top_srcdir@/tests/suppressions/gabble.supp \ --suppressions=@abs_top_srcdir@/tests/suppressions/tp-glib.supp \ --error-exitcode=1" \ check-TESTS check-coding-style: @fail=0; \ if test -n "$(check_misc_sources)"; then \ tools_dir=$(top_srcdir)/tools \ sh $(top_srcdir)/tools/check-misc.sh \ $(addprefix $(srcdir)/,$(check_misc_sources)) || fail=1; \ fi; \ if test -n "$(check_c_sources)"; then \ tools_dir=$(top_srcdir)/tools \ sh $(top_srcdir)/tools/check-c-style.sh \ $(addprefix $(srcdir)/,$(check_c_sources)) || fail=1; \ fi;\ if test yes = "$(ENABLE_CODING_STYLE_CHECKS)"; then \ exit "$$fail";\ else \ exit 0;\ fi check-local: check-coding-style # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: telepathy-gabble-0.18.2/plugins/0000755000175000017500000000000012312537051016475 5ustar00smcvsmcv00000000000000telepathy-gabble-0.18.2/plugins/test.h0000644000175000017500000001173012200204333017615 0ustar00smcvsmcv00000000000000#include #include #include #include #include /* Plugin */ typedef struct _TestPluginClass TestPluginClass; typedef struct _TestPlugin TestPlugin; struct _TestPluginClass { GObjectClass parent; }; struct _TestPlugin { GObject parent; }; GType test_plugin_get_type (void); #define TEST_TYPE_PLUGIN \ (test_plugin_get_type ()) #define TEST_PLUGIN(obj) \ (G_TYPE_CHECK_INSTANCE_CAST((obj), TEST_TYPE_PLUGIN, TestPlugin)) #define TEST_PLUGIN_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST((klass), TEST_TYPE_PLUGIN, \ TestPluginClass)) #define TEST_IS_PLUGIN(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE((obj), TEST_TYPE_PLUGIN)) #define TEST_IS_PLUGIN_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE((klass), TEST_TYPE_PLUGIN)) #define TEST_PLUGIN_GET_CLASS(obj) \ (G_TYPE_INSTANCE_GET_CLASS ((obj), TEST_TYPE_PLUGIN, \ TestPluginClass)) /* Sidecar */ typedef struct _TestSidecarClass TestSidecarClass; typedef struct _TestSidecar TestSidecar; struct _TestSidecarClass { GObjectClass parent; }; struct _TestSidecar { GObject parent; }; GType test_sidecar_get_type (void); #define TEST_TYPE_SIDECAR \ (test_sidecar_get_type ()) #define TEST_SIDECAR(obj) \ (G_TYPE_CHECK_INSTANCE_CAST((obj), TEST_TYPE_SIDECAR, TestSidecar)) #define TEST_SIDECAR_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST((klass), TEST_TYPE_SIDECAR, \ TestSidecarClass)) #define TEST_IS_SIDECAR(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE((obj), TEST_TYPE_SIDECAR)) #define TEST_IS_SIDECAR_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE((klass), TEST_TYPE_SIDECAR)) #define TEST_SIDECAR_GET_CLASS(obj) \ (G_TYPE_INSTANCE_GET_CLASS ((obj), TEST_TYPE_SIDECAR, \ TestSidecarClass)) /* Sidecar with properties */ typedef struct _TestSidecarPropsClass TestSidecarPropsClass; typedef struct _TestSidecarProps TestSidecarProps; struct _TestSidecarPropsClass { GObjectClass parent; }; struct _TestSidecarProps { GObject parent; GHashTable *props; }; GType test_sidecar_props_get_type (void); #define TEST_TYPE_SIDECAR_PROPS \ (test_sidecar_props_get_type ()) #define TEST_SIDECAR_PROPS(obj) \ (G_TYPE_CHECK_INSTANCE_CAST((obj), TEST_TYPE_SIDECAR_PROPS, TestSidecarProps)) #define TEST_SIDECAR_PROPS_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST((klass), TEST_TYPE_SIDECAR_PROPS, \ TestSidecarPropsClass)) #define TEST_IS_SIDECAR_PROPS(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE((obj), TEST_TYPE_SIDECAR_PROPS)) #define TEST_IS_SIDECAR_PROPS_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE((klass), TEST_TYPE_SIDECAR_PROPS)) #define TEST_SIDECAR_PROPS_GET_CLASS(obj) \ (G_TYPE_INSTANCE_GET_CLASS ((obj), TEST_TYPE_SIDECAR_PROPS, \ TestSidecarPropsClass)) /* Sidecar which sends an IQ and waits for a reply before being ready. */ typedef struct _TestSidecarIQClass TestSidecarIQClass; typedef struct _TestSidecarIQ TestSidecarIQ; struct _TestSidecarIQClass { GObjectClass parent; }; struct _TestSidecarIQ { GObject parent; GSimpleAsyncResult *result; WockySession *session; GabblePluginConnection *connection; }; GType test_sidecar_iq_get_type (void); #define TEST_TYPE_SIDECAR_IQ \ (test_sidecar_iq_get_type ()) #define TEST_SIDECAR_IQ(obj) \ (G_TYPE_CHECK_INSTANCE_CAST((obj), TEST_TYPE_SIDECAR_IQ, TestSidecarIQ)) #define TEST_SIDECAR_IQ_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST((klass), TEST_TYPE_SIDECAR_IQ, \ TestSidecarIQClass)) #define TEST_IS_SIDECAR_IQ(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE((obj), TEST_TYPE_SIDECAR_IQ)) #define TEST_IS_SIDECAR_IQ_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE((klass), TEST_TYPE_SIDECAR_IQ)) #define TEST_SIDECAR_IQ_GET_CLASS(obj) \ (G_TYPE_INSTANCE_GET_CLASS ((obj), TEST_TYPE_SIDECAR_IQ, \ TestSidecarIQClass)) /* Test channel manager */ typedef struct _TestChannelManager TestChannelManager; typedef struct _TestChannelManagerClass TestChannelManagerClass; struct _TestChannelManagerClass { GObjectClass parent_class; }; struct _TestChannelManager { GObject parent; GabblePluginConnection *plugin_connection; }; GType test_channel_manager_get_type (void); /* TYPE MACROS */ #define TEST_TYPE_CHANNEL_MANAGER \ (test_channel_manager_get_type ()) #define TEST_CHANNEL_MANAGER(obj) \ (G_TYPE_CHECK_INSTANCE_CAST((obj), TEST_TYPE_CHANNEL_MANAGER, TestChannelManager)) #define TEST_CHANNEL_MANAGER_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST((klass), TEST_TYPE_CHANNEL_MANAGER,\ TestChannelManagerClass)) #define TEST_IS_CHANNEL_MANAGER(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE((obj), TEST_TYPE_CHANNEL_MANAGER)) #define TEST_IS_CHANNEL_MANAGER_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE((klass), TEST_TYPE_CHANNEL_MANAGER)) #define TEST_CHANNEL_MANAGER_GET_CLASS(obj) \ (G_TYPE_INSTANCE_GET_CLASS ((obj), TEST_TYPE_CHANNEL_MANAGER,\ TestChannelManagerClass)) telepathy-gabble-0.18.2/plugins/test.c0000644000175000017500000004503612200204333017616 0ustar00smcvsmcv00000000000000#include "config.h" #include "test.h" #include #include #include #include "extensions/extensions.h" #include #include #define DEBUG(msg, ...) \ g_debug ("%s: " msg, G_STRFUNC, ##__VA_ARGS__) /***************************** * TestPlugin implementation * *****************************/ static void plugin_iface_init ( gpointer g_iface, gpointer data); #define IFACE_TEST "org.freedesktop.Telepathy.Gabble.Plugin.Test" #define IFACE_TEST_PROPS IFACE_TEST ".Props" #define IFACE_TEST_BUGGY IFACE_TEST ".Buggy" #define IFACE_TEST_IQ IFACE_TEST ".IQ" static const gchar * const sidecar_interfaces[] = { IFACE_TEST, IFACE_TEST_PROPS, IFACE_TEST_BUGGY, IFACE_TEST_IQ, NULL }; G_DEFINE_TYPE_WITH_CODE (TestPlugin, test_plugin, G_TYPE_OBJECT, G_IMPLEMENT_INTERFACE (GABBLE_TYPE_PLUGIN, plugin_iface_init); ) static void test_plugin_init (TestPlugin *object) { DEBUG ("%p", object); } static void test_plugin_class_init (TestPluginClass *klass) { } static void sidecar_iq_created_cb ( GObject *source, GAsyncResult *new_result, gpointer user_data) { GSimpleAsyncResult *result = user_data; GabbleSidecar *sidecar = GABBLE_SIDECAR (source); GError *error = NULL; if (g_async_initable_init_finish (G_ASYNC_INITABLE (source), new_result, &error)) { g_simple_async_result_set_op_res_gpointer (result, sidecar, g_object_unref); } else { g_simple_async_result_set_from_error (result, error); g_clear_error (&error); g_object_unref (sidecar); } g_simple_async_result_complete (result); g_object_unref (result); } static void test_plugin_create_sidecar_async ( GabblePlugin *plugin, const gchar *sidecar_interface, GabblePluginConnection *plugin_connection, WockySession *session, GAsyncReadyCallback callback, gpointer user_data) { GSimpleAsyncResult *result = g_simple_async_result_new (G_OBJECT (plugin), callback, user_data, test_plugin_create_sidecar_async); GabbleSidecar *sidecar = NULL; if (!tp_strdiff (sidecar_interface, IFACE_TEST)) { sidecar = g_object_new (TEST_TYPE_SIDECAR, NULL); } else if (!tp_strdiff (sidecar_interface, IFACE_TEST_PROPS)) { sidecar = g_object_new (TEST_TYPE_SIDECAR_PROPS, NULL); } else if (!tp_strdiff (sidecar_interface, IFACE_TEST_IQ)) { g_async_initable_new_async (TEST_TYPE_SIDECAR_IQ, G_PRIORITY_DEFAULT, NULL, sidecar_iq_created_cb, result, "session", session, "plugin-connection", plugin_connection, NULL); return; } else { /* This deliberately doesn't check for IFACE_TEST_BUGGY, to test Gabble's * reactions to buggy plugins. :) */ g_simple_async_result_set_error (result, TP_ERROR, TP_ERROR_NOT_IMPLEMENTED, "'%s' not implemented", sidecar_interface); } if (sidecar != NULL) g_simple_async_result_set_op_res_gpointer (result, sidecar, g_object_unref); g_simple_async_result_complete_in_idle (result); g_object_unref (result); } static GabbleSidecar * test_plugin_create_sidecar_finish ( GabblePlugin *plugin, GAsyncResult *result, GError **error) { GabbleSidecar *sidecar; if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), error)) return NULL; g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (plugin), test_plugin_create_sidecar_async), NULL); sidecar = GABBLE_SIDECAR (g_simple_async_result_get_op_res_gpointer ( G_SIMPLE_ASYNC_RESULT (result))); return g_object_ref (sidecar); } static GPtrArray * test_plugin_create_channel_managers (GabblePlugin *plugin, GabblePluginConnection *plugin_connection) { GPtrArray *ret = g_ptr_array_new (); DEBUG ("plugin %p on connection %p", plugin, plugin_connection); g_ptr_array_add (ret, g_object_new (TEST_TYPE_CHANNEL_MANAGER, "plugin-connection", plugin_connection, NULL)); return ret; } static TpPresenceStatusSpec test_presences[] = { { "testbusy", TP_CONNECTION_PRESENCE_TYPE_BUSY, TRUE, NULL, NULL, NULL }, { "testaway", TP_CONNECTION_PRESENCE_TYPE_AWAY, FALSE, NULL, NULL, NULL }, { NULL, 0, FALSE, NULL, NULL, NULL } }; static GabblePluginPrivacyListMap privacy_list_map[] = { { "testbusy", "test-busy-list" }, { NULL, NULL }, }; static void plugin_iface_init ( gpointer g_iface, gpointer data G_GNUC_UNUSED) { GabblePluginInterface *iface = g_iface; iface->name = "Sidecar test plugin"; iface->version = PACKAGE_VERSION; iface->sidecar_interfaces = sidecar_interfaces; iface->create_sidecar_async = test_plugin_create_sidecar_async; iface->create_sidecar_finish = test_plugin_create_sidecar_finish; iface->create_channel_managers = test_plugin_create_channel_managers; iface->presence_statuses = test_presences; iface->privacy_list_map = privacy_list_map; } GabblePlugin * gabble_plugin_create () { return g_object_new (test_plugin_get_type (), NULL); } /****************************** * TestSidecar implementation * ******************************/ static void sidecar_iface_init ( gpointer g_iface, gpointer data); G_DEFINE_TYPE_WITH_CODE (TestSidecar, test_sidecar, G_TYPE_OBJECT, G_IMPLEMENT_INTERFACE (GABBLE_TYPE_SIDECAR, sidecar_iface_init); G_IMPLEMENT_INTERFACE (GABBLE_TYPE_SVC_GABBLE_PLUGIN_TEST, NULL); ) static void test_sidecar_init (TestSidecar *object) { DEBUG ("%p", object); } static void test_sidecar_class_init (TestSidecarClass *klass) { } static void sidecar_iface_init ( gpointer g_iface, gpointer data) { GabbleSidecarInterface *iface = g_iface; iface->interface = IFACE_TEST; iface->get_immutable_properties = NULL; } /*********************************** * TestSidecarProps implementation * ***********************************/ static void sidecar_props_iface_init ( gpointer g_iface, gpointer data); G_DEFINE_TYPE_WITH_CODE (TestSidecarProps, test_sidecar_props, G_TYPE_OBJECT, G_IMPLEMENT_INTERFACE (GABBLE_TYPE_SIDECAR, sidecar_props_iface_init); G_IMPLEMENT_INTERFACE (GABBLE_TYPE_SVC_GABBLE_PLUGIN_TEST, NULL); ) static void test_sidecar_props_init (TestSidecarProps *object) { DEBUG ("%p", object); object->props = tp_asv_new ( IFACE_TEST_PROPS ".Greeting", G_TYPE_STRING, "oh hai", NULL); } static void test_sidecar_props_finalize (GObject *object) { TestSidecarProps *self = TEST_SIDECAR_PROPS (object); void (*chain_up) (GObject *) = G_OBJECT_CLASS (test_sidecar_props_parent_class)->finalize; g_hash_table_unref (self->props); if (chain_up != NULL) chain_up (object); } static void test_sidecar_props_class_init (TestSidecarPropsClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->finalize = test_sidecar_props_finalize; } static GHashTable * sidecar_props_get_immutable_properties (GabbleSidecar *sidecar) { TestSidecarProps *self = TEST_SIDECAR_PROPS (sidecar); return g_hash_table_ref (self->props); } static void sidecar_props_iface_init ( gpointer g_iface, gpointer data) { GabbleSidecarInterface *iface = g_iface; iface->interface = IFACE_TEST_PROPS; iface->get_immutable_properties = sidecar_props_get_immutable_properties; } /******************************** * TestSidecarIQ implementation * ********************************/ static void sidecar_iq_iface_init ( gpointer g_iface, gpointer data); static void async_initable_iface_init ( gpointer g_iface, gpointer data); G_DEFINE_TYPE_WITH_CODE (TestSidecarIQ, test_sidecar_iq, G_TYPE_OBJECT, G_IMPLEMENT_INTERFACE (GABBLE_TYPE_SIDECAR, sidecar_iq_iface_init); G_IMPLEMENT_INTERFACE (GABBLE_TYPE_SVC_GABBLE_PLUGIN_TEST, NULL); G_IMPLEMENT_INTERFACE (G_TYPE_ASYNC_INITABLE, async_initable_iface_init); ) enum { PROP_SESSION = 1, PROP_CONNECTION = 2 }; static void test_sidecar_iq_init (TestSidecarIQ *object) { DEBUG ("%p", object); } static void test_sidecar_iq_set_property ( GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { TestSidecarIQ *self = TEST_SIDECAR_IQ (object); switch (property_id) { case PROP_SESSION: self->session = g_value_dup_object (value); break; case PROP_CONNECTION: { self->connection = g_value_dup_object (value); if (self->connection) { GabbleCapabilitySet *features; const gchar *applications[] = { "com.example.test1", "com.example.test2", NULL }; GPtrArray *identities; WockyDiscoIdentity *identity; gchar *hash; guint i; features = gabble_capability_set_new (); for (i = 0; applications[i] != NULL; i++) gabble_capability_set_add (features, applications[i]); identities = wocky_disco_identity_array_new (); identity = wocky_disco_identity_new ("test", "app-list", NULL, "Test"); g_ptr_array_add (identities, identity); /* set own caps so we proper reply to disco#info */ hash = gabble_plugin_connection_add_sidecar_own_caps (self->connection, features, identities); g_free (hash); wocky_disco_identity_array_free (identities); gabble_capability_set_free (features); } } break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); } } static void test_sidecar_iq_dispose (GObject *object) { TestSidecarIQ *self = TEST_SIDECAR_IQ (object); DEBUG ("called for %p", object); tp_clear_object (&self->session); tp_clear_object (&self->connection); } static void test_sidecar_iq_class_init (TestSidecarIQClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->set_property = test_sidecar_iq_set_property; object_class->dispose = test_sidecar_iq_dispose; g_object_class_install_property (object_class, PROP_SESSION, g_param_spec_object ("session", "SESSION", "THIS IS A WOCKY SESSION YOU CAN TELL BY THE TYPE", WOCKY_TYPE_SESSION, G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (object_class, PROP_CONNECTION, g_param_spec_object ("plugin-connection", "Gabble Plugin Connection", "Gabble Plugin Connection", GABBLE_TYPE_PLUGIN_CONNECTION, G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); } static void sidecar_iq_iface_init ( gpointer g_iface, gpointer data) { GabbleSidecarInterface *iface = g_iface; iface->interface = IFACE_TEST_IQ; iface->get_immutable_properties = NULL; } static void iq_cb ( GObject *source, GAsyncResult *nested_result, gpointer user_data) { GSimpleAsyncResult *result = user_data; GError *error = NULL; WockyStanza *reply; reply = wocky_porter_send_iq_finish (WOCKY_PORTER (source), nested_result, &error); if (reply == NULL) { g_simple_async_result_set_from_error (result, error); g_clear_error (&error); } else { WockyStanzaSubType t; wocky_stanza_get_type_info (reply, NULL, &t); if (t == WOCKY_STANZA_SUB_TYPE_RESULT) g_simple_async_result_set_op_res_gboolean (result, TRUE); else g_simple_async_result_set_error (result, TP_ERROR, TP_ERROR_NOT_AVAILABLE, "server said no!"); g_object_unref (reply); } g_simple_async_result_complete (result); } static void sidecar_iq_init_async ( GAsyncInitable *initable, int io_priority, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { TestSidecarIQ *self = TEST_SIDECAR_IQ (initable); GSimpleAsyncResult *result = g_simple_async_result_new (G_OBJECT (self), callback, user_data, sidecar_iq_init_async); WockyPorter *porter = wocky_session_get_porter (self->session); WockyStanza *iq; iq = wocky_stanza_build (WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_GET, NULL, "sidecar.example.com", '(', "query", ':', "http://example.com/sidecar", '(', "oh-hai", ')', ')', NULL); wocky_porter_send_iq_async (porter, iq, cancellable, iq_cb, result); } static gboolean sidecar_iq_init_finish ( GAsyncInitable *initable, GAsyncResult *result, GError **error) { GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (result); if (g_simple_async_result_propagate_error (simple, error)) return FALSE; g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (initable), sidecar_iq_init_async), FALSE); return g_simple_async_result_get_op_res_gboolean (simple); } static void async_initable_iface_init ( gpointer g_iface, gpointer data) { GAsyncInitableIface *iface = g_iface; iface->init_async = sidecar_iq_init_async; iface->init_finish = sidecar_iq_init_finish; } /*********************************** * TestChannelManager implementation * ***********************************/ static void channel_manager_iface_init (gpointer, gpointer); static void caps_channel_manager_iface_init (gpointer g_iface, gpointer iface_data); G_DEFINE_TYPE_WITH_CODE (TestChannelManager, test_channel_manager, G_TYPE_OBJECT, G_IMPLEMENT_INTERFACE (TP_TYPE_CHANNEL_MANAGER, channel_manager_iface_init) G_IMPLEMENT_INTERFACE (GABBLE_TYPE_CAPS_CHANNEL_MANAGER, caps_channel_manager_iface_init)); static void test_channel_manager_init (TestChannelManager *self) { } static void test_channel_manager_set_property ( GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { TestChannelManager *self = TEST_CHANNEL_MANAGER (object); switch (property_id) { case PROP_CONNECTION: /* Not reffing this: the connection owns all channel managers, so it * must outlive us. Taking a reference leads to a cycle. */ self->plugin_connection = g_value_get_object (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); } } static void test_channel_manager_get_property ( GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { TestChannelManager *self = TEST_CHANNEL_MANAGER (object); switch (property_id) { case PROP_CONNECTION: g_value_set_object (value, self->plugin_connection); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); } } static void test_channel_manager_porter_available_cb (GabbleConnection *connection, WockyPorter *porter, gpointer user_data) { DEBUG ("now we have a porter: %p", porter); /* so now we can call things like wocky_porter_register_handler_* * and get some stanzas. */ } static void test_channel_manager_constructed (GObject *object) { TestChannelManager *self = TEST_CHANNEL_MANAGER (object); if (G_OBJECT_CLASS (test_channel_manager_parent_class)->constructed != NULL) G_OBJECT_CLASS (test_channel_manager_parent_class)->constructed (object); tp_g_signal_connect_object (self->plugin_connection, "porter-available", G_CALLBACK (test_channel_manager_porter_available_cb), self, 0); } static void test_channel_manager_class_init (TestChannelManagerClass *klass) { GObjectClass *oclass = G_OBJECT_CLASS (klass); oclass->set_property = test_channel_manager_set_property; oclass->get_property = test_channel_manager_get_property; oclass->constructed = test_channel_manager_constructed; g_object_class_install_property (oclass, PROP_CONNECTION, g_param_spec_object ("plugin-connection", "Gabble Plugin Connection", "Gabble Plugin Connection", GABBLE_TYPE_PLUGIN_CONNECTION, G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); } static void test_channel_manager_type_foreach_channel_class (GType type, TpChannelManagerTypeChannelClassFunc func, gpointer user_data) { GHashTable *table = tp_asv_new ( TP_PROP_CHANNEL_CHANNEL_TYPE, G_TYPE_STRING, "com.jonnylamb.lolbags", TP_PROP_CHANNEL_TARGET_HANDLE_TYPE, G_TYPE_UINT, TP_HANDLE_TYPE_NONE, NULL); const gchar * const chock_a_block_full_of_strings[] = { "com.jonnylamb.omg", "com.jonnylamb.brokethebuild", NULL }; func (type, table, chock_a_block_full_of_strings, user_data); g_hash_table_unref (table); } static void test_channel_manager_represent_client ( GabbleCapsChannelManager *manager, const gchar *client_name, const GPtrArray *filters, const gchar * const *cap_tokens, GabbleCapabilitySet *cap_set, GPtrArray *data_forms) { WockyStanza *stanza; WockyDataForm *form; if (tp_strdiff (client_name, "dataformtest")) return; stanza = wocky_stanza_build (WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_NONE, NULL, "badger", '(', "x", ':', "jabber:x:data", '@', "type", "result", '(', "field", '@', "var", "FORM_TYPE", '@', "type", "hidden", '(', "value", '$', "gabble:test:channel:manager:data:form", ')', ')', '(', "field", '@', "var", "animal", '(', "value", '$', "badger", ')', '(', "value", '$', "snake", ')', '(', "value", '$', "weasel", ')', ')', '(', "field", '@', "var", "cheese", '(', "value", '$', "omgnothorriblecheese", ')', ')', '(', "field", '@', "var", "favourite_crane", '(', "value", '$', "a tall one", ')', '(', "value", '$', "a short one", ')', ')', '(', "field", '@', "var", "running_out_of", '(', "value", '$', "ideas", ')', '(', "value", '$', "cake", ')', ')', ')', NULL); form = wocky_data_form_new_from_node ( wocky_node_get_first_child (wocky_stanza_get_top_node (stanza)), NULL); g_ptr_array_add (data_forms, form); g_object_unref (stanza); } static void channel_manager_iface_init (gpointer g_iface, gpointer iface_data) { TpChannelManagerIface *iface = g_iface; iface->type_foreach_channel_class = test_channel_manager_type_foreach_channel_class; /* not requestable. */ iface->ensure_channel = NULL; iface->create_channel = NULL; iface->request_channel = NULL; iface->foreach_channel_class = NULL; } static void caps_channel_manager_iface_init (gpointer g_iface, gpointer iface_data) { GabbleCapsChannelManagerInterface *iface = g_iface; iface->represent_client = test_channel_manager_represent_client; } telepathy-gabble-0.18.2/plugins/gateways.h0000644000175000017500000000637412200204333020472 0ustar00smcvsmcv00000000000000/* Gateway registration plugin * * Copyright © 2010 Collabora Ltd. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 #include #include typedef struct _GabbleGatewayPlugin GabbleGatewayPlugin; typedef struct _GabbleGatewayPluginClass GabbleGatewayPluginClass; typedef struct _GabbleGatewayPluginPrivate GabbleGatewayPluginPrivate; struct _GabbleGatewayPlugin { GObject parent; GabbleGatewayPluginPrivate *priv; }; struct _GabbleGatewayPluginClass { GObjectClass parent; }; GType gabble_gateway_plugin_get_type (void); #define GABBLE_TYPE_GATEWAY_PLUGIN \ (gabble_gateway_plugin_get_type ()) #define GABBLE_GATEWAY_PLUGIN(obj) \ (G_TYPE_CHECK_INSTANCE_CAST ((obj), GABBLE_TYPE_GATEWAY_PLUGIN, \ GabbleGatewayPlugin)) #define GABBLE_GATEWAY_PLUGIN_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST ((klass), GABBLE_TYPE_GATEWAY_PLUGIN, \ GabbleGatewayPluginClass)) #define GABBLE_IS_GATEWAY_PLUGIN(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GABBLE_TYPE_GATEWAY_PLUGIN)) #define GABBLE_IS_GATEWAY_PLUGIN_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE ((klass), GABBLE_TYPE_GATEWAY_PLUGIN)) #define GABBLE_GATEWAY_PLUGIN_GET_CLASS(obj) \ (G_TYPE_INSTANCE_GET_CLASS ((obj), GABBLE_TYPE_GATEWAY_PLUGIN, \ GabbleGatewayPluginClass)) typedef struct _GabbleGatewaySidecar GabbleGatewaySidecar; typedef struct _GabbleGatewaySidecarClass GabbleGatewaySidecarClass; typedef struct _GabbleGatewaySidecarPrivate GabbleGatewaySidecarPrivate; struct _GabbleGatewaySidecar { GObject parent; GabbleGatewaySidecarPrivate *priv; }; struct _GabbleGatewaySidecarClass { GObjectClass parent; }; GType gabble_gateway_sidecar_get_type (void); #define GABBLE_TYPE_GATEWAY_SIDECAR \ (gabble_gateway_sidecar_get_type ()) #define GABBLE_GATEWAY_SIDECAR(obj) \ (G_TYPE_CHECK_INSTANCE_CAST ((obj), GABBLE_TYPE_GATEWAY_SIDECAR, \ GabbleGatewaySidecar)) #define GABBLE_GATEWAY_SIDECAR_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST ((klass), GABBLE_TYPE_GATEWAY_SIDECAR, \ GabbleGatewaySidecarClass)) #define GABBLE_IS_GATEWAY_SIDECAR(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GABBLE_TYPE_GATEWAY_SIDECAR)) #define GABBLE_IS_GATEWAY_SIDECAR_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE ((klass), GABBLE_TYPE_GATEWAY_SIDECAR)) #define GABBLE_GATEWAY_SIDECAR_GET_CLASS(obj) \ (G_TYPE_INSTANCE_GET_CLASS ((obj), GABBLE_TYPE_GATEWAY_SIDECAR, \ GabbleGatewaySidecarClass)) telepathy-gabble-0.18.2/plugins/gateways.c0000644000175000017500000003723512200204333020465 0ustar00smcvsmcv00000000000000/* Gateway registration plugin * * Copyright © 2010 Collabora Ltd. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 "config.h" #include "gateways.h" #include #include #include #include "extensions/extensions.h" #include /************************* * Plugin implementation * *************************/ static guint debug = 0; #define DEBUG(format, ...) \ G_STMT_START { \ if (debug != 0) \ g_debug ("%s: " format, G_STRFUNC, ## __VA_ARGS__); \ } G_STMT_END static const GDebugKey debug_keys[] = { { "gateways", 1 }, { NULL, 0 } }; static void plugin_iface_init ( gpointer g_iface, gpointer data); static const gchar * const sidecar_interfaces[] = { GABBLE_IFACE_GABBLE_PLUGIN_GATEWAYS, NULL }; G_DEFINE_TYPE_WITH_CODE (GabbleGatewayPlugin, gabble_gateway_plugin, G_TYPE_OBJECT, G_IMPLEMENT_INTERFACE (GABBLE_TYPE_PLUGIN, plugin_iface_init); ) static void gabble_gateway_plugin_init (GabbleGatewayPlugin *self) { } static void gabble_gateway_plugin_class_init (GabbleGatewayPluginClass *klass) { } static void gabble_gateway_plugin_create_sidecar_async ( GabblePlugin *plugin, const gchar *sidecar_interface, GabblePluginConnection *connection, WockySession *session, GAsyncReadyCallback callback, gpointer user_data) { GSimpleAsyncResult *result = g_simple_async_result_new (G_OBJECT (plugin), callback, user_data, gabble_gateway_plugin_create_sidecar_async); GabbleSidecar *sidecar = NULL; if (!tp_strdiff (sidecar_interface, GABBLE_IFACE_GABBLE_PLUGIN_GATEWAYS)) { sidecar = g_object_new (GABBLE_TYPE_GATEWAY_SIDECAR, "connection", connection, "session", session, NULL); } else { g_simple_async_result_set_error (result, TP_ERROR, TP_ERROR_NOT_IMPLEMENTED, "'%s' not implemented", sidecar_interface); } if (sidecar != NULL) g_simple_async_result_set_op_res_gpointer (result, sidecar, g_object_unref); g_simple_async_result_complete_in_idle (result); g_object_unref (result); } static GabbleSidecar * gabble_gateway_plugin_create_sidecar_finish ( GabblePlugin *plugin, GAsyncResult *result, GError **error) { GabbleSidecar *sidecar; if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), error)) return NULL; g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (plugin), gabble_gateway_plugin_create_sidecar_async), NULL); sidecar = GABBLE_SIDECAR (g_simple_async_result_get_op_res_gpointer ( G_SIMPLE_ASYNC_RESULT (result))); return g_object_ref (sidecar); } static void plugin_iface_init ( gpointer g_iface, gpointer data G_GNUC_UNUSED) { GabblePluginInterface *iface = g_iface; iface->name = "Gateway registration plugin"; iface->version = PACKAGE_VERSION; iface->sidecar_interfaces = sidecar_interfaces; iface->create_sidecar_async = gabble_gateway_plugin_create_sidecar_async; iface->create_sidecar_finish = gabble_gateway_plugin_create_sidecar_finish; } GabblePlugin * gabble_plugin_create (void) { debug = g_parse_debug_string (g_getenv ("GABBLE_DEBUG"), debug_keys, G_N_ELEMENTS (debug_keys) - 1); DEBUG ("loaded"); return g_object_new (GABBLE_TYPE_GATEWAY_PLUGIN, NULL); } /************************** * Sidecar implementation * **************************/ enum { PROP_0, PROP_CONNECTION, PROP_SESSION }; struct _GabbleGatewaySidecarPrivate { WockySession *session; TpBaseConnection *connection; guint subscribe_id; guint subscribed_id; GHashTable *gateways; }; static void sidecar_iface_init ( gpointer g_iface, gpointer data); static void gateway_iface_init ( gpointer g_iface, gpointer data); G_DEFINE_TYPE_WITH_CODE (GabbleGatewaySidecar, gabble_gateway_sidecar, G_TYPE_OBJECT, G_IMPLEMENT_INTERFACE (GABBLE_TYPE_SIDECAR, sidecar_iface_init); G_IMPLEMENT_INTERFACE (GABBLE_TYPE_SVC_GABBLE_PLUGIN_GATEWAYS, gateway_iface_init); ) static void gabble_gateway_sidecar_init (GabbleGatewaySidecar *self) { self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, GABBLE_TYPE_GATEWAY_SIDECAR, GabbleGatewaySidecarPrivate); self->priv->gateways = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); } static void gabble_gateway_sidecar_set_property ( GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { GabbleGatewaySidecar *self = GABBLE_GATEWAY_SIDECAR (object); switch (property_id) { case PROP_CONNECTION: g_assert (self->priv->connection == NULL); /* construct-only */ self->priv->connection = g_value_dup_object (value); break; case PROP_SESSION: g_assert (self->priv->session == NULL); /* construct-only */ self->priv->session = g_value_dup_object (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); } } static void gabble_gateway_sidecar_dispose (GObject *object) { void (*chain_up) (GObject *) = G_OBJECT_CLASS (gabble_gateway_sidecar_parent_class)->dispose; GabbleGatewaySidecar *self = GABBLE_GATEWAY_SIDECAR (object); tp_clear_object (&self->priv->connection); if (self->priv->session != NULL) { WockyPorter *porter = wocky_session_get_porter (self->priv->session); wocky_porter_unregister_handler (porter, self->priv->subscribe_id); wocky_porter_unregister_handler (porter, self->priv->subscribed_id); } tp_clear_object (&self->priv->session); if (chain_up != NULL) chain_up (object); } static gboolean presence_cb (WockyPorter *porter, WockyStanza *stanza, gpointer user_data) { GabbleGatewaySidecar *self = GABBLE_GATEWAY_SIDECAR (user_data); const gchar *from; gchar *normalized = NULL; gboolean ret = FALSE; WockyStanzaSubType subtype; wocky_stanza_get_type_info (stanza, NULL, &subtype); switch (subtype) { case WOCKY_STANZA_SUB_TYPE_SUBSCRIBED: /* Someone has allowed us to subscribe to them */ break; case WOCKY_STANZA_SUB_TYPE_SUBSCRIBE: /* Someone wants to subscribe to us */ break; default: g_return_val_if_reached (FALSE); } from = wocky_node_get_attribute ( wocky_stanza_get_top_node (stanza), "from"); if (from == NULL || strchr (from, '@') != NULL || strchr (from, '/') != NULL) goto finally; normalized = wocky_normalise_jid (from); if (g_hash_table_lookup (self->priv->gateways, normalized) == NULL) goto finally; if (subtype == WOCKY_STANZA_SUB_TYPE_SUBSCRIBE) { WockyStanza *reply; /* It's a gateway we've registered with during this session, and they * want to subscribe to us. OK, let them. */ DEBUG ("Allowing gateway '%s' to subscribe to us", normalized); reply = wocky_stanza_build (WOCKY_STANZA_TYPE_PRESENCE, WOCKY_STANZA_SUB_TYPE_SUBSCRIBED, NULL, normalized, NULL); wocky_porter_send (porter, reply); g_object_unref (reply); } else { /* It's a gateway we've registered with during this session, letting us * know that yes, we may subscribe to them. Good. */ DEBUG ("Gateway '%s' allowed us to subscribe to it", normalized); /* Eventually, we'll return success from the D-Bus method call here. */ } ret = TRUE; finally: g_free (normalized); return ret; } static void gabble_gateway_sidecar_constructed (GObject *object) { void (*chain_up) (GObject *) = G_OBJECT_CLASS (gabble_gateway_sidecar_parent_class)->constructed; GabbleGatewaySidecar *self = GABBLE_GATEWAY_SIDECAR (object); WockyPorter *porter; if (chain_up != NULL) chain_up (object); g_assert (self->priv->session != NULL); g_assert (self->priv->connection != NULL); porter = wocky_session_get_porter (self->priv->session); self->priv->subscribe_id = wocky_porter_register_handler_from_anyone (porter, WOCKY_STANZA_TYPE_PRESENCE, WOCKY_STANZA_SUB_TYPE_SUBSCRIBE, WOCKY_PORTER_HANDLER_PRIORITY_MAX, presence_cb, self, NULL); self->priv->subscribed_id = wocky_porter_register_handler_from_anyone (porter, WOCKY_STANZA_TYPE_PRESENCE, WOCKY_STANZA_SUB_TYPE_SUBSCRIBED, WOCKY_PORTER_HANDLER_PRIORITY_MAX, presence_cb, self, NULL); } static void gabble_gateway_sidecar_class_init (GabbleGatewaySidecarClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->set_property = gabble_gateway_sidecar_set_property; object_class->dispose = gabble_gateway_sidecar_dispose; object_class->constructed = gabble_gateway_sidecar_constructed; g_type_class_add_private (klass, sizeof (GabbleGatewaySidecarPrivate)); g_object_class_install_property (object_class, PROP_CONNECTION, g_param_spec_object ("connection", "Connection", "Gabble connection", GABBLE_TYPE_PLUGIN_CONNECTION, G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (object_class, PROP_SESSION, g_param_spec_object ("session", "Session", "Wocky session", WOCKY_TYPE_SESSION, G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); } static void sidecar_iface_init ( gpointer g_iface, gpointer data) { GabbleSidecarInterface *iface = g_iface; iface->interface = GABBLE_IFACE_GABBLE_PLUGIN_GATEWAYS; iface->get_immutable_properties = NULL; } typedef struct { DBusGMethodInvocation *context; gchar *gateway; } PendingRegistration; static PendingRegistration * pending_registration_new (DBusGMethodInvocation *context, const gchar *gateway) { PendingRegistration *pr = g_slice_new (PendingRegistration); pr->context = context; pr->gateway = g_strdup (gateway); return pr; } static void pending_registration_free (PendingRegistration *pr) { g_assert (pr->context == NULL); g_free (pr->gateway); g_slice_free (PendingRegistration, pr); } #define NON_NULL (((int *) NULL) + 1) static void register_cb (GObject *source, GAsyncResult *result, gpointer user_data) { WockyPorter *porter = WOCKY_PORTER (source); PendingRegistration *pr = user_data; WockyStanza *reply; GError *error = NULL; reply = wocky_porter_send_iq_finish (porter, result, &error); if (reply == NULL || wocky_stanza_extract_errors (reply, NULL, &error, NULL, NULL)) { GError *tp_error = NULL; /* specific error cases for registration: 'conflict' and * 'not-acceptable' are documented */ if (error->domain == WOCKY_XMPP_ERROR) { switch (error->code) { case WOCKY_XMPP_ERROR_CONFLICT: g_set_error (&tp_error, TP_ERROR, TP_ERROR_REGISTRATION_EXISTS, "someone else registered that username: %s", error->message); break; case WOCKY_XMPP_ERROR_NOT_ACCEPTABLE: g_set_error (&tp_error, TP_ERROR, TP_ERROR_NOT_AVAILABLE, "registration not acceptable: %s", error->message); break; default: gabble_set_tp_error_from_wocky (error, &tp_error); break; } } else { /* generic fallback */ gabble_set_tp_error_from_wocky (error, &tp_error); } DEBUG ("Failed to register with '%s': %s", pr->gateway, tp_error->message); dbus_g_method_return_error (pr->context, tp_error); pr->context = NULL; g_error_free (error); g_error_free (tp_error); } else { WockyStanza *request; DEBUG ("Registered with '%s', exchanging presence...", pr->gateway); /* attempt to subscribe to the gateway's presence (FIXME: is this * harmless if we're already subscribed to it?) */ request = wocky_stanza_build (WOCKY_STANZA_TYPE_PRESENCE, WOCKY_STANZA_SUB_TYPE_SUBSCRIBE, NULL, pr->gateway, NULL); wocky_porter_send (porter, request); g_object_unref (request); gabble_svc_gabble_plugin_gateways_return_from_register (pr->context); pr->context = NULL; } tp_clear_object (&reply); pending_registration_free (pr); } static void gateways_register ( GabbleSvcGabblePluginGateways *sidecar, const gchar *gateway, const gchar *username, const gchar *password, DBusGMethodInvocation *context) { GabbleGatewaySidecar *self = GABBLE_GATEWAY_SIDECAR (sidecar); WockyPorter *porter = wocky_session_get_porter (self->priv->session); WockyStanza *stanza; gchar *normalized_gateway; GError *error = NULL; if (strchr (gateway, '@') != NULL) { g_set_error (&error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT, "Gateway names cannot contain '@': %s", gateway); goto error; } if (strchr (gateway, '/') != NULL) { g_set_error (&error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT, "Gateway names cannot contain '/': %s", gateway); goto error; } if (!wocky_decode_jid (gateway, NULL, &normalized_gateway, NULL)) { g_set_error (&error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT, "Invalid gateway name: %s", gateway); goto error; } DEBUG ("Trying to register on '%s' as '%s'", gateway, username); /* steals ownership of normalized_gateway */ g_hash_table_replace (self->priv->gateways, normalized_gateway, NON_NULL); /* This is a *really* minimal implementation. We're meant to ask the gateway * what parameters it supports (a XEP-0077 pseudo-form or a XEP-0004 data * form), then fill in the blanks to actually make a request. * * However, because we're hard-coded to take only a username and a password, * we might as well just fire off a request with those in and see if it * works. If it doesn't, then we couldn't have registered anyway... * * A more general API for service registration could look like this: * * method Plugin.GetService() -> o, a{s(*)} [where * is some * representation of the type of the parameter] * property Service.Parameters: readable a{s(*)} [likewise] * property Service.Registered: readable b * method Service.Register(a{sv}) * method Service.Unregister() * method Service.Release() [distributed refcounting] */ stanza = wocky_stanza_build (WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_SET, NULL, normalized_gateway, '(', "query", ':', WOCKY_XEP77_NS_REGISTER, '(', "username", '$', username, ')', '(', "password", '$', password, ')', ')', NULL); wocky_porter_send_iq_async (porter, stanza, NULL, register_cb, pending_registration_new (context, normalized_gateway)); g_object_unref (stanza); return; error: DEBUG ("%s", error->message); dbus_g_method_return_error (context, error); g_error_free (error); } static void gateway_iface_init ( gpointer klass, gpointer data G_GNUC_UNUSED) { #define IMPLEMENT(x) gabble_svc_gabble_plugin_gateways_implement_##x (\ klass, gateways_##x) IMPLEMENT (register); #undef IMPLEMENT } telepathy-gabble-0.18.2/plugins/console.h0000644000175000017500000000651312200204333020303 0ustar00smcvsmcv00000000000000/* XML console plugin * * Copyright © 2011 Collabora Ltd. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 #include #include #include typedef struct _GabbleConsolePlugin GabbleConsolePlugin; typedef struct _GabbleConsolePluginClass GabbleConsolePluginClass; typedef struct _GabbleConsolePluginPrivate GabbleConsolePluginPrivate; struct _GabbleConsolePlugin { GObject parent; GabbleConsolePluginPrivate *priv; }; struct _GabbleConsolePluginClass { GObjectClass parent; }; GType gabble_console_plugin_get_type (void); #define GABBLE_TYPE_CONSOLE_PLUGIN \ (gabble_console_plugin_get_type ()) #define GABBLE_CONSOLE_PLUGIN(obj) \ (G_TYPE_CHECK_INSTANCE_CAST ((obj), GABBLE_TYPE_CONSOLE_PLUGIN, \ GabbleConsolePlugin)) #define GABBLE_CONSOLE_PLUGIN_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST ((klass), GABBLE_TYPE_CONSOLE_PLUGIN, \ GabbleConsolePluginClass)) #define GABBLE_IS_CONSOLE_PLUGIN(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GABBLE_TYPE_CONSOLE_PLUGIN)) #define GABBLE_IS_CONSOLE_PLUGIN_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE ((klass), GABBLE_TYPE_CONSOLE_PLUGIN)) #define GABBLE_CONSOLE_PLUGIN_GET_CLASS(obj) \ (G_TYPE_INSTANCE_GET_CLASS ((obj), GABBLE_TYPE_CONSOLE_PLUGIN, \ GabbleConsolePluginClass)) typedef struct _GabbleConsoleSidecar GabbleConsoleSidecar; typedef struct _GabbleConsoleSidecarClass GabbleConsoleSidecarClass; typedef struct _GabbleConsoleSidecarPrivate GabbleConsoleSidecarPrivate; struct _GabbleConsoleSidecar { GObject parent; GabbleConsoleSidecarPrivate *priv; }; struct _GabbleConsoleSidecarClass { GObjectClass parent; TpDBusPropertiesMixinClass props_class; }; GType gabble_console_sidecar_get_type (void); #define GABBLE_TYPE_CONSOLE_SIDECAR \ (gabble_console_sidecar_get_type ()) #define GABBLE_CONSOLE_SIDECAR(obj) \ (G_TYPE_CHECK_INSTANCE_CAST ((obj), GABBLE_TYPE_CONSOLE_SIDECAR, \ GabbleConsoleSidecar)) #define GABBLE_CONSOLE_SIDECAR_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST ((klass), GABBLE_TYPE_CONSOLE_SIDECAR, \ GabbleConsoleSidecarClass)) #define GABBLE_IS_CONSOLE_SIDECAR(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GABBLE_TYPE_CONSOLE_SIDECAR)) #define GABBLE_IS_CONSOLE_SIDECAR_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE ((klass), GABBLE_TYPE_CONSOLE_SIDECAR)) #define GABBLE_CONSOLE_SIDECAR_GET_CLASS(obj) \ (G_TYPE_INSTANCE_GET_CLASS ((obj), GABBLE_TYPE_CONSOLE_SIDECAR, \ GabbleConsoleSidecarClass)) telepathy-gabble-0.18.2/plugins/console.c0000644000175000017500000004423612200204333020302 0ustar00smcvsmcv00000000000000/* XML console plugin * * Copyright © 2011 Collabora Ltd. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 "config.h" #include "console.h" #include #include #include #include "extensions/extensions.h" #include /************************* * Plugin implementation * *************************/ static guint debug = 0; #define DEBUG(format, ...) \ G_STMT_START { \ if (debug != 0) \ g_debug ("%s: " format, G_STRFUNC, ## __VA_ARGS__); \ } G_STMT_END static const GDebugKey debug_keys[] = { { "console", 1 }, { NULL, 0 } }; static void plugin_iface_init ( gpointer g_iface, gpointer data); static const gchar * const sidecar_interfaces[] = { GABBLE_IFACE_GABBLE_PLUGIN_CONSOLE, NULL }; G_DEFINE_TYPE_WITH_CODE (GabbleConsolePlugin, gabble_console_plugin, G_TYPE_OBJECT, G_IMPLEMENT_INTERFACE (GABBLE_TYPE_PLUGIN, plugin_iface_init); ) static void gabble_console_plugin_init (GabbleConsolePlugin *self) { } static void gabble_console_plugin_class_init (GabbleConsolePluginClass *klass) { } static void gabble_console_plugin_create_sidecar_async ( GabblePlugin *plugin, const gchar *sidecar_interface, GabblePluginConnection *connection, WockySession *session, GAsyncReadyCallback callback, gpointer user_data) { GSimpleAsyncResult *result = g_simple_async_result_new (G_OBJECT (plugin), callback, user_data, gabble_console_plugin_create_sidecar_async); GabbleSidecar *sidecar = NULL; if (!tp_strdiff (sidecar_interface, GABBLE_IFACE_GABBLE_PLUGIN_CONSOLE)) { sidecar = g_object_new (GABBLE_TYPE_CONSOLE_SIDECAR, "connection", connection, "session", session, NULL); } else { g_simple_async_result_set_error (result, TP_ERROR, TP_ERROR_NOT_IMPLEMENTED, "'%s' not implemented", sidecar_interface); } if (sidecar != NULL) g_simple_async_result_set_op_res_gpointer (result, sidecar, g_object_unref); g_simple_async_result_complete_in_idle (result); g_object_unref (result); } static GabbleSidecar * gabble_console_plugin_create_sidecar_finish ( GabblePlugin *plugin, GAsyncResult *result, GError **error) { GabbleSidecar *sidecar; if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), error)) return NULL; g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (plugin), gabble_console_plugin_create_sidecar_async), NULL); sidecar = GABBLE_SIDECAR (g_simple_async_result_get_op_res_gpointer ( G_SIMPLE_ASYNC_RESULT (result))); return g_object_ref (sidecar); } static void plugin_iface_init ( gpointer g_iface, gpointer data G_GNUC_UNUSED) { GabblePluginInterface *iface = g_iface; iface->name = "XMPP console"; iface->version = PACKAGE_VERSION; iface->sidecar_interfaces = sidecar_interfaces; iface->create_sidecar_async = gabble_console_plugin_create_sidecar_async; iface->create_sidecar_finish = gabble_console_plugin_create_sidecar_finish; } GabblePlugin * gabble_plugin_create (void) { debug = g_parse_debug_string (g_getenv ("GABBLE_DEBUG"), debug_keys, G_N_ELEMENTS (debug_keys) - 1); DEBUG ("loaded"); return g_object_new (GABBLE_TYPE_CONSOLE_PLUGIN, NULL); } /************************** * Sidecar implementation * **************************/ enum { PROP_0, PROP_CONNECTION, PROP_SESSION, PROP_SPEW }; struct _GabbleConsoleSidecarPrivate { WockySession *session; TpBaseConnection *connection; WockyXmppReader *reader; WockyXmppWriter *writer; /* %TRUE if we should emit signals when sending or receiving stanzas */ gboolean spew; /* 0 if spew is FALSE; or a WockyPorter handler id for all incoming stanzas * if spew is TRUE. */ guint incoming_handler; /* 0 if spew is FALSE; a GLib signal handler id for WockyPorter::sending if * spew is TRUE. */ gulong sending_id; }; static void sidecar_iface_init ( gpointer g_iface, gpointer data); static void console_iface_init ( gpointer g_iface, gpointer data); static void gabble_console_sidecar_set_spew ( GabbleConsoleSidecar *self, gboolean spew); G_DEFINE_TYPE_WITH_CODE (GabbleConsoleSidecar, gabble_console_sidecar, G_TYPE_OBJECT, G_IMPLEMENT_INTERFACE (GABBLE_TYPE_SIDECAR, sidecar_iface_init); G_IMPLEMENT_INTERFACE (GABBLE_TYPE_SVC_GABBLE_PLUGIN_CONSOLE, console_iface_init); G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_DBUS_PROPERTIES, tp_dbus_properties_mixin_iface_init); ) static void gabble_console_sidecar_init (GabbleConsoleSidecar *self) { self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, GABBLE_TYPE_CONSOLE_SIDECAR, GabbleConsoleSidecarPrivate); self->priv->reader = wocky_xmpp_reader_new_no_stream_ns ( WOCKY_XMPP_NS_JABBER_CLIENT); self->priv->writer = wocky_xmpp_writer_new_no_stream (); } static void gabble_console_sidecar_get_property ( GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { GabbleConsoleSidecar *self = GABBLE_CONSOLE_SIDECAR (object); switch (property_id) { case PROP_SPEW: g_value_set_boolean (value, self->priv->spew); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); } } static void gabble_console_sidecar_set_property ( GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { GabbleConsoleSidecar *self = GABBLE_CONSOLE_SIDECAR (object); switch (property_id) { case PROP_CONNECTION: g_assert (self->priv->connection == NULL); /* construct-only */ self->priv->connection = g_value_dup_object (value); break; case PROP_SESSION: g_assert (self->priv->session == NULL); /* construct-only */ self->priv->session = g_value_dup_object (value); break; case PROP_SPEW: gabble_console_sidecar_set_spew (self, g_value_get_boolean (value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); } } static void gabble_console_sidecar_dispose (GObject *object) { void (*chain_up) (GObject *) = G_OBJECT_CLASS (gabble_console_sidecar_parent_class)->dispose; GabbleConsoleSidecar *self = GABBLE_CONSOLE_SIDECAR (object); gabble_console_sidecar_set_spew (self, FALSE); tp_clear_object (&self->priv->connection); tp_clear_object (&self->priv->reader); tp_clear_object (&self->priv->writer); tp_clear_object (&self->priv->session); if (chain_up != NULL) chain_up (object); } static void gabble_console_sidecar_class_init (GabbleConsoleSidecarClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); static TpDBusPropertiesMixinPropImpl console_props[] = { { "SpewStanzas", "spew-stanzas", "spew-stanzas" }, { NULL }, }; static TpDBusPropertiesMixinIfaceImpl interfaces[] = { { GABBLE_IFACE_GABBLE_PLUGIN_CONSOLE, tp_dbus_properties_mixin_getter_gobject_properties, /* FIXME: if we were feeling clever, we'd override the setter so that * we can monitor the bus name of any application which sets * SpewStanzas to TRUE and flip it back to false when that application * dies. * * Alternatively, we could just replace this sidecar with a channel. */ tp_dbus_properties_mixin_setter_gobject_properties, console_props }, { NULL }, }; object_class->get_property = gabble_console_sidecar_get_property; object_class->set_property = gabble_console_sidecar_set_property; object_class->dispose = gabble_console_sidecar_dispose; g_type_class_add_private (klass, sizeof (GabbleConsoleSidecarPrivate)); g_object_class_install_property (object_class, PROP_CONNECTION, g_param_spec_object ("connection", "Connection", "Gabble connection", GABBLE_TYPE_PLUGIN_CONNECTION, G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (object_class, PROP_SESSION, g_param_spec_object ("session", "Session", "Wocky session", WOCKY_TYPE_SESSION, G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (object_class, PROP_SPEW, g_param_spec_boolean ("spew-stanzas", "SpewStanzas", "If %TRUE, someone wants us to spit out a tonne of stanzas", FALSE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); klass->props_class.interfaces = interfaces; tp_dbus_properties_mixin_class_init (object_class, G_STRUCT_OFFSET (GabbleConsoleSidecarClass, props_class)); } static void sidecar_iface_init ( gpointer g_iface, gpointer data) { GabbleSidecarInterface *iface = g_iface; iface->interface = GABBLE_IFACE_GABBLE_PLUGIN_CONSOLE; iface->get_immutable_properties = NULL; } static gboolean incoming_cb ( WockyPorter *porter, WockyStanza *stanza, gpointer user_data) { GabbleConsoleSidecar *self = GABBLE_CONSOLE_SIDECAR (user_data); const guint8 *body; gsize length; wocky_xmpp_writer_write_stanza (self->priv->writer, stanza, &body, &length); gabble_svc_gabble_plugin_console_emit_stanza_received (self, (const gchar *) body); return FALSE; } static void sending_cb ( WockyPorter *porter, WockyStanza *stanza, gpointer user_data) { GabbleConsoleSidecar *self = GABBLE_CONSOLE_SIDECAR (user_data); if (stanza != NULL) { const guint8 *body; gsize length; wocky_xmpp_writer_write_stanza (self->priv->writer, stanza, &body, &length); gabble_svc_gabble_plugin_console_emit_stanza_sent (self, (const gchar *) body); } } static void gabble_console_sidecar_set_spew ( GabbleConsoleSidecar *self, gboolean spew) { GabbleConsoleSidecarPrivate *priv = self->priv; if (!spew != !priv->spew) { WockyPorter *porter = wocky_session_get_porter (self->priv->session); const gchar *props[] = { "SpewStanzas", NULL }; priv->spew = spew; tp_dbus_properties_mixin_emit_properties_changed (G_OBJECT (self), GABBLE_IFACE_GABBLE_PLUGIN_CONSOLE, props); if (spew) { g_return_if_fail (priv->incoming_handler == 0); priv->incoming_handler = wocky_porter_register_handler_from_anyone ( porter, WOCKY_STANZA_TYPE_NONE, WOCKY_STANZA_SUB_TYPE_NONE, WOCKY_PORTER_HANDLER_PRIORITY_MAX, incoming_cb, self, NULL); g_return_if_fail (priv->sending_id == 0); priv->sending_id = g_signal_connect (porter, "sending", (GCallback) sending_cb, self); } else { g_return_if_fail (priv->incoming_handler != 0); wocky_porter_unregister_handler (porter, priv->incoming_handler); priv->incoming_handler = 0; g_return_if_fail (priv->sending_id != 0); g_signal_handler_disconnect (porter, priv->sending_id); priv->sending_id = 0; } } } static void return_from_send_iq ( GObject *source, GAsyncResult *result, gpointer user_data) { GabbleConsoleSidecar *self = GABBLE_CONSOLE_SIDECAR (source); DBusGMethodInvocation *context = user_data; GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (result); GError *error = NULL; if (g_simple_async_result_propagate_error (simple, &error)) { dbus_g_method_return_error (context, error); g_error_free (error); } else { WockyStanza *reply = g_simple_async_result_get_op_res_gpointer (simple); WockyStanzaSubType sub_type; const guint8 *body; gsize length; wocky_stanza_get_type_info (reply, NULL, &sub_type); wocky_xmpp_writer_write_stanza (self->priv->writer, reply, &body, &length); /* woop woop */ gabble_svc_gabble_plugin_console_return_from_send_iq (context, sub_type == WOCKY_STANZA_SUB_TYPE_RESULT ? "result" : "error", (const gchar *) body); } } static void console_iq_reply_cb ( GObject *source, GAsyncResult *result, gpointer user_data) { WockyPorter *porter = WOCKY_PORTER (source); GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (user_data); GError *error = NULL; WockyStanza *reply = wocky_porter_send_iq_finish (porter, result, &error); if (reply != NULL) { g_simple_async_result_set_op_res_gpointer (simple, reply, g_object_unref); } else { g_simple_async_result_set_from_error (simple, error); g_error_free (error); } g_simple_async_result_complete (simple); g_object_unref (simple); } static gboolean get_iq_type (const gchar *type_str, WockyStanzaSubType *sub_type_out, GError **error) { if (!wocky_strdiff (type_str, "get")) { *sub_type_out = WOCKY_STANZA_SUB_TYPE_GET; return TRUE; } if (!wocky_strdiff (type_str, "set")) { *sub_type_out = WOCKY_STANZA_SUB_TYPE_SET; return TRUE; } g_set_error (error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT, "Type must be 'get' or 'set', not '%s'", type_str); return FALSE; } static gboolean validate_jid (const gchar **to, GError **error) { if (tp_str_empty (*to)) { *to = NULL; return TRUE; } if (wocky_decode_jid (*to, NULL, NULL, NULL)) return TRUE; g_set_error (error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT, "'%s' is not a valid (or empty) JID", *to); return FALSE; } /* * @xml: doesn't actually have to be a top-level stanza. It can be the body of * an IQ or whatever. If it has no namespace, it's assumed to be in * jabber:client. */ static gboolean parse_me_a_stanza ( GabbleConsoleSidecar *self, const gchar *xml, WockyStanza **stanza_out, GError **error) { GabbleConsoleSidecarPrivate *priv = self->priv; WockyStanza *stanza; wocky_xmpp_reader_reset (priv->reader); wocky_xmpp_reader_push (priv->reader, (const guint8 *) xml, strlen (xml)); *error = wocky_xmpp_reader_get_error (priv->reader); if (*error != NULL) return FALSE; stanza = wocky_xmpp_reader_pop_stanza (priv->reader); if (stanza == NULL) { g_set_error (error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT, "Incomplete stanza! Bad person."); return FALSE; } *stanza_out = stanza; return TRUE; } static void console_send_iq ( GabbleSvcGabblePluginConsole *sidecar, const gchar *type_str, const gchar *to, const gchar *body, DBusGMethodInvocation *context) { GabbleConsoleSidecar *self = GABBLE_CONSOLE_SIDECAR (sidecar); WockyPorter *porter = wocky_session_get_porter (self->priv->session); WockyStanzaSubType sub_type; WockyStanza *fragment; GError *error = NULL; if (get_iq_type (type_str, &sub_type, &error) && validate_jid (&to, &error) && parse_me_a_stanza (self, body, &fragment, &error)) { GSimpleAsyncResult *simple = g_simple_async_result_new (G_OBJECT (self), return_from_send_iq, context, console_send_iq); WockyStanza *stanza = wocky_stanza_build (WOCKY_STANZA_TYPE_IQ, sub_type, NULL, to, NULL); wocky_node_add_node_tree (wocky_stanza_get_top_node (stanza), WOCKY_NODE_TREE (fragment)); wocky_porter_send_iq_async (porter, stanza, NULL, console_iq_reply_cb, simple); g_object_unref (fragment); } else { DEBUG ("%s", error->message); dbus_g_method_return_error (context, error); g_error_free (error); } } static void console_stanza_sent_cb ( GObject *source, GAsyncResult *result, gpointer user_data) { WockyPorter *porter = WOCKY_PORTER (source); DBusGMethodInvocation *context = user_data; GError *error = NULL; if (wocky_porter_send_finish (porter, result, &error)) { gabble_svc_gabble_plugin_console_return_from_send_stanza (context); } else { dbus_g_method_return_error (context, error); g_clear_error (&error); } } static gboolean stanza_looks_coherent ( WockyStanza *stanza, GError **error) { WockyNode *top_node = wocky_stanza_get_top_node (stanza); WockyStanzaType t; WockyStanzaSubType st; wocky_stanza_get_type_info (stanza, &t, &st); if (t == WOCKY_STANZA_TYPE_UNKNOWN) { g_set_error (error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT, "I don't know what a <%s xmlns='%s'/> is", top_node->name, g_quark_to_string (top_node->ns)); return FALSE; } else if (st == WOCKY_STANZA_SUB_TYPE_UNKNOWN) { g_set_error (error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT, "I don't know what type='%s' means", wocky_node_get_attribute (top_node, "type")); return FALSE; } return TRUE; } static void console_send_stanza ( GabbleSvcGabblePluginConsole *sidecar, const gchar *xml, DBusGMethodInvocation *context) { GabbleConsoleSidecar *self = GABBLE_CONSOLE_SIDECAR (sidecar); WockyPorter *porter = wocky_session_get_porter (self->priv->session); WockyStanza *stanza = NULL; GError *error = NULL; if (parse_me_a_stanza (self, xml, &stanza, &error) && stanza_looks_coherent (stanza, &error)) { wocky_porter_send_async (porter, stanza, NULL, console_stanza_sent_cb, context); } else { DEBUG ("%s", error->message); dbus_g_method_return_error (context, error); g_error_free (error); } tp_clear_object (&stanza); } static void console_iface_init ( gpointer klass, gpointer data G_GNUC_UNUSED) { #define IMPLEMENT(x) gabble_svc_gabble_plugin_console_implement_##x (\ klass, console_##x) IMPLEMENT (send_iq); IMPLEMENT (send_stanza); #undef IMPLEMENT } telepathy-gabble-0.18.2/plugins/telepathy-gabble-xmpp-console0000755000175000017500000003321412200204333024247 0ustar00smcvsmcv00000000000000#!/usr/bin/env python # vim: set fileencoding=utf-8 sts=4 sw=4 et : """ The world's worst XMPP console user interface. Pass it the bus name of a Gabble connection; type some words; get minimalistic error reporting. Copyright © 2011 Collabora Ltd. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. 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 """ import sys import re from xml.dom import minidom from gi.repository import Gtk from gi.repository import GLib from gi.repository import Gio from gi.repository import GtkSource PADDING = 6 def pathify(name): return '/' + name.replace('.', '/') def nameify(path): return (path[1:]).replace('/', '.') CONN_FUTURE_IFACE = "org.freedesktop.Telepathy.Connection.FUTURE" CONSOLE_IFACE = "org.freedesktop.Telepathy.Gabble.Plugin.Console" class StanzaViewer(Gtk.ScrolledWindow): def __init__(self): Gtk.ScrolledWindow.__init__(self) self.b = GtkSource.Buffer() self.view = GtkSource.View.new_with_buffer(self.b) self.b.set_language( GtkSource.LanguageManager.get_default().get_language('xml')) self.b.set_highlight_matching_brackets(False) self.view.set_editable(False) self.view.set_wrap_mode(Gtk.WrapMode.WORD_CHAR) self.view.set_property('expand', True) self.add(self.view) def clear(self): self.b.set_text("") def append_stanza(self, xml): pretty = minidom.parseString(xml).toprettyxml() pretty = pretty.replace('\n', '') i = self.b.get_end_iter() self.b.insert(i, pretty + '\n') def append_comment(self, text): i = self.b.get_end_iter() self.b.insert(i, '\n' % text) def tell_me_everything(self): return self.b.get_property('text') class SpinWrapper(Gtk.Notebook): PRIMARY_PAGE = 0 SPINNER_PAGE = 1 def __init__(self, main_widget): Gtk.Notebook.__init__(self) self.set_show_tabs(False) self.set_show_border(False) self.insert_page(main_widget, None, self.PRIMARY_PAGE) self.spinner = Gtk.Spinner() self.spinner.set_property('halign', Gtk.Align.CENTER) self.spinner.set_property('valign', Gtk.Align.CENTER) self.spinner.set_property('width-request', 32) self.spinner.set_property('height-request', 32) self.insert_page(self.spinner, None, self.SPINNER_PAGE) def start_spinning(self): self.set_current_page(self.SPINNER_PAGE) self.spinner.start() def stop_spinning(self): self.spinner.stop() self.set_current_page(self.PRIMARY_PAGE) class Page(Gtk.Grid): def __init__(self, console_proxy): Gtk.Grid.__init__(self) self.console_proxy = console_proxy self.set_column_spacing(PADDING) self.set_row_spacing(PADDING) def add_title(self, title, below=None): label = Gtk.Label() label.set_markup("%s" % title) label.set_property('xalign', 0) if below is None: self.attach(label, 0, 0, 2, 1) else: self.attach_next_to(label, below, Gtk.PositionType.BOTTOM, 2, 1) return label def add_label(self, title, below=None): label = Gtk.Label(title) label.set_property('margin-left', PADDING) label.set_property('xalign', 0) if below is None: self.attach(label, 0, 0, 1, 1) else: self.attach_next_to(label, below, Gtk.PositionType.BOTTOM, 1, 1) return label class IQPage(Page): def __init__(self, console_proxy): Page.__init__(self, console_proxy) request_label = self.add_title("Request") recipient_label, recipient_entry = self.add_label_entry_pair( 'To:', below=request_label) self.recipient_entry = recipient_entry type_label = self.add_label('IQ Type:', below=recipient_label) self.get_button = Gtk.RadioButton.new_with_label([], "get") self.get_button.set_active(True) self.set_button = Gtk.RadioButton.new_with_label_from_widget( self.get_button, "set") box = Gtk.ButtonBox.new(Gtk.Orientation.HORIZONTAL) box.set_layout(Gtk.ButtonBoxStyle.START) box.add(self.get_button) box.add(self.set_button) self.attach_next_to(box, type_label, Gtk.PositionType.RIGHT, 1, 1) body_label, body_entry = self.add_label_entry_pair( 'Body:', below=type_label) body_entry.set_text( "") body_entry.set_icon_from_stock( Gtk.EntryIconPosition.SECONDARY, Gtk.STOCK_GO_FORWARD) body_entry.set_icon_tooltip_text( Gtk.EntryIconPosition.SECONDARY, "Send this IQ") self.body_entry = body_entry reply_label = self.add_title("Reply", below=body_label) self.stanza_viewer = StanzaViewer() self.stanza_viewer.append_comment("send a request to see the reply here") self.result_nb = SpinWrapper(self.stanza_viewer) self.attach_next_to(self.result_nb, reply_label, Gtk.PositionType.BOTTOM, 2, 1) body_entry.connect('activate', self.send_iq) body_entry.connect('icon-release', self.send_iq) def add_label_entry_pair(self, title, below): label = self.add_label(title, below) entry = Gtk.Entry() entry.set_property('margin-right', PADDING) entry.set_property('hexpand', True) self.attach_next_to(entry, label, Gtk.PositionType.RIGHT, 1, 1) return label, entry def send_iq(self, *misc): type = 'get' if self.get_button.get_active() else 'set' to = self.recipient_entry.get_text() body = self.body_entry.get_text() self.console_proxy.SendIQ('(sss)', type, to, body, result_handler=self.send_iq_cb) self.result_nb.start_spinning() def send_iq_cb(self, proxy, result, user_data): self.stanza_viewer.clear() if isinstance(result, Exception): self.stanza_viewer.append_comment("error:\n%s" % result) else: reply_type, reply = result self.stanza_viewer.append_stanza(reply) self.result_nb.stop_spinning() class StanzaPage(Page): def __init__(self, console_proxy): Page.__init__(self, console_proxy) title = self.add_title("Enter a complete stanza:") self.sv = StanzaViewer() self.sv.view.set_editable(True) self.sv.append_stanza("Been on any nice boats recently?") self.spin_wrapper = SpinWrapper(self.sv) self.attach_next_to(self.spin_wrapper, title, Gtk.PositionType.BOTTOM, 2, 1) self.result_label = self.add_label('', self.spin_wrapper) self.result_label.set_property('hexpand', True) self.result_label.set_line_wrap(True) b = Gtk.Button.new_with_mnemonic("_Send") b.connect('clicked', self.__send_stanza) b.set_property('hexpand', False) self.attach_next_to(b, self.result_label, Gtk.PositionType.RIGHT, 1, 1) def __send_stanza(self, button): self.console_proxy.SendStanza('(s)', self.sv.tell_me_everything(), result_handler=self.__send_stanza_cb) self.spin_wrapper.start_spinning() def __send_stanza_cb(self, proxy, result, user_data): if isinstance(result, Exception): # FIXME: this sucks. You can't just get the free text bit without # the D-Bus error bit. t = result.message else: t = "yes sir, captain tightpants" self.result_label.set_text(t) self.spin_wrapper.stop_spinning() class SnoopyPage(Page): def __init__(self, console_proxy): Page.__init__(self, console_proxy) label = self.add_label("Stanza monitor:") label.set_property('hexpand', True) switch = Gtk.Switch() self.attach_next_to(switch, label, Gtk.PositionType.RIGHT, 1, 1) self.stanza_viewer = StanzaViewer() self.attach_next_to(self.stanza_viewer, label, Gtk.PositionType.BOTTOM, 2, 1) switch.set_active(self.get_remote_active()) switch.connect('notify::active', self.__switch_switched_cb) self.console_proxy.connect('g-signal', self.__g_signal_cb) def teardown(self): """Turn off the monitor when we quit.""" self.__set_spew(False) def __set_spew(self, spew): args = GLib.Variant("(ssv)", (CONSOLE_IFACE, "SpewStanzas", GLib.Variant.new_boolean(spew))) self.console_proxy.call_sync( "org.freedesktop.DBus.Properties.Set", args, 0, -1, None) def get_remote_active(self): return self.console_proxy.get_cached_property('SpewStanzas').get_boolean() def __switch_switched_cb(self, switch, pspec): remote = self.get_remote_active() new_local = switch.get_active() if new_local != remote: self.__set_spew(new_local) self.stanza_viewer.append_comment( 'started monitoring' if new_local else 'stopped monitoring') def __g_signal_cb(self, console_proxy, sender_name, signal_name, parameters): if signal_name in ['StanzaSent', 'StanzaReceived']: outgoing = (signal_name == 'StanzaSent') xml, = parameters self.stanza_viewer.append_comment('sent' if outgoing else 'received') self.stanza_viewer.append_stanza(xml) class Window(Gtk.Window): IQ_PAGE = 0 STANZA_PAGE = 1 SNOOPY_PAGE = 2 def __init__(self, bus, connection_bus_name): Gtk.Window.__init__(self) self.set_title('XMPP Console') self.set_default_size(600, 371) conn_future_proxy = Gio.DBusProxy.new_sync(bus, 0, None, connection_bus_name, pathify(connection_bus_name), CONN_FUTURE_IFACE, None) try: sidecar_path, _ = conn_future_proxy.EnsureSidecar('(s)', CONSOLE_IFACE) except Exception, e: print """ Couldn't connect to the XMPP console interface on '%(connection_bus_name)s': %(e)s Check that it's a running Jabber connection, and that you have the console plugin installed.""" % locals() raise SystemExit(2) self.console_proxy = Gio.DBusProxy.new_sync(bus, 0, None, connection_bus_name, sidecar_path, CONSOLE_IFACE, None) # Build up the UI self.nb = Gtk.Notebook() self.add(self.nb) self.iq = IQPage(self.console_proxy) self.nb.insert_page(self.iq, Gtk.Label.new_with_mnemonic("_IQ console"), self.IQ_PAGE) self.stanza = StanzaPage(self.console_proxy) self.nb.insert_page(self.stanza, Gtk.Label.new_with_mnemonic("Send a s_tanza"), self.STANZA_PAGE) self.snoopy = SnoopyPage(self.console_proxy) self.nb.insert_page(self.snoopy, Gtk.Label.new_with_mnemonic("_Monitor network traffic"), self.SNOOPY_PAGE) self.connect('destroy', Window.__destroy_cb) def __destroy_cb(self): self.snoopy.teardown() Gtk.main_quit() GABBLE_PREFIX = 'org.freedesktop.Telepathy.Connection.gabble.jabber.' AM_BUS_NAME = 'org.freedesktop.Telepathy.AccountManager' ACCOUNT_PREFIX = '/org/freedesktop/Telepathy/Account' ACCOUNT_IFACE = 'org.freedesktop.Telepathy.Account' def usage(): print """ Usage: %(arg0)s gabble/jabber/blahblah %(arg0)s %(prefix)sblahblah List account identifiers using `mc-tool list | grep gabble`. List connection bus names using `qdbus | grep gabble`. """ % { 'arg0': sys.argv[0], 'prefix': GABBLE_PREFIX, } raise SystemExit(1) if __name__ == '__main__': bus = Gio.bus_get_sync(Gio.BusType.SESSION, None) if len(sys.argv) != 2: usage() thing = sys.argv[1] if re.match('^gabble/jabber/[a-zA-Z0-9_]+$', thing): # Looks like an account path to me. account_proxy = Gio.DBusProxy.new_sync(bus, 0, None, AM_BUS_NAME, '%s/%s' % (ACCOUNT_PREFIX, thing), ACCOUNT_IFACE, None) path = account_proxy.get_cached_property('Connection').get_string() if path == '/': print "%s is not online" % thing raise SystemExit(1) else: thing = nameify(path) if not re.match('^%s[a-zA-Z0-9_]+$' % GABBLE_PREFIX, thing): usage() win = Window(bus, thing) win.show_all() Gtk.main() """ .,,. ,;;*;;;;, .-'``;-');;. /' .-. /*;; .' \d \;; .;;;, / o ` \; ,__. ,;*;;;*;, \__, _.__,' \_.-') __)--.;;;;;*;;;;, `""`;;;\ /-')_) __) `\' ';;;;;; ;*;;; -') `)_) |\ | ;;;;*; ;;;;| `---` O | | ;;*;;; *;*;\| O / ;;;;;* ;;;;;/| .-------\ / ;*;;;;; ;;;*;/ \ | '. (`. ;;;*;;; ;;;;;'. ; | ) \ | ;;;;;; ,;*;;;;\/ |. / /` | ';;;*; ;;;;;;/ |/ / /__/ ';;; '*jgs/ | / | ;*; `""""` `""""` ;' """ telepathy-gabble-0.18.2/plugins/Makefile.am0000644000175000017500000000314712200204333020524 0ustar00smcvsmcv00000000000000installable_plugins = \ libconsole.la \ libgateways.la libtest_only_plugins = \ libtest.la # libtesting-only plugins if ENABLE_INSTALLED_TESTS noinst_LTLIBRARIES = \ $(NULL) libtestplugindir = $(gabbletestsdir)/plugins libtestplugin_LTLIBRARIES = \ $(libtest_only_plugins) \ $(NULL) libtest_la_LDFLAGS = $(AM_LDFLAGS) else noinst_LTLIBRARIES = \ $(libtest_only_plugins) \ $(NULL) # because libtest.la is not installed, libtool will want to compile it as static # despite -shared (a convenience library), unless we also use -rpath libtest_la_LDFLAGS = $(AM_LDFLAGS) -rpath $(pluginexecdir) endif if ENABLE_PLUGINS pluginexec_LTLIBRARIES = $(installable_plugins) dist_bin_SCRIPTS = \ telepathy-gabble-xmpp-console else # we still compile the plugin (just to make sure it compiles!) but we don't # install it noinst_LTLIBRARIES += $(installable_plugins) EXTRA_DIST = \ telepathy-gabble-xmpp-console endif AM_LDFLAGS = -avoid-version -shared -no-undefined ALL_PLUGIN_LIBS = \ @WOCKY_LIBS@ \ @GLIB_LIBS@ \ @TP_GLIB_LIBS@ \ $(top_builddir)/extensions/libgabble-extensions.la \ $(top_builddir)/src/libgabble-plugins.la libtest_la_LIBADD = $(ALL_PLUGIN_LIBS) libgateways_la_LIBADD = $(ALL_PLUGIN_LIBS) libconsole_la_LIBADD = $(ALL_PLUGIN_LIBS) libtest_la_SOURCES = \ test.c \ test.h libgateways_la_SOURCES = \ gateways.c \ gateways.h libconsole_la_SOURCES = \ console.c \ console.h AM_CFLAGS = $(ERROR_CFLAGS) \ -I $(top_srcdir) -I $(top_builddir) \ @DBUS_CFLAGS@ @GLIB_CFLAGS@ @WOCKY_CFLAGS@ @TP_GLIB_CFLAGS@ \ -I $(top_srcdir)/gabble -I $(top_builddir)/gabble \ -I $(top_srcdir)/plugins telepathy-gabble-0.18.2/plugins/Makefile.in0000644000175000017500000007021012312536074020546 0ustar00smcvsmcv00000000000000# Makefile.in generated by automake 1.14.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2013 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ VPATH = @srcdir@ am__is_gnu_make = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)' am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ # we still compile the plugin (just to make sure it compiles!) but we don't # install it @ENABLE_PLUGINS_FALSE@am__append_1 = $(installable_plugins) subdir = plugins DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am \ $(am__dist_bin_SCRIPTS_DIST) $(top_srcdir)/depcomp ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/ax_config_dir.m4 \ $(top_srcdir)/m4/compiler.m4 $(top_srcdir)/m4/libtool.m4 \ $(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \ $(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \ $(top_srcdir)/m4/tp-compiler-flag.m4 \ $(top_srcdir)/m4/tp-compiler-warnings.m4 \ $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; am__vpath_adj = case $$p in \ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ *) f=$$p;; \ esac; am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; am__install_max = 40 am__nobase_strip_setup = \ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` am__nobase_strip = \ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" am__nobase_list = $(am__nobase_strip_setup); \ for p in $$list; do echo "$$p $$p"; done | \ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ if (++n[$$2] == $(am__install_max)) \ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ END { for (dir in files) print dir, files[dir] }' am__base_list = \ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' am__uninstall_files_from_dir = { \ test -z "$$files" \ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ $(am__cd) "$$dir" && rm -f $$files; }; \ } am__installdirs = "$(DESTDIR)$(libtestplugindir)" \ "$(DESTDIR)$(pluginexecdir)" "$(DESTDIR)$(bindir)" LTLIBRARIES = $(libtestplugin_LTLIBRARIES) $(noinst_LTLIBRARIES) \ $(pluginexec_LTLIBRARIES) am__DEPENDENCIES_1 = \ $(top_builddir)/extensions/libgabble-extensions.la \ $(top_builddir)/src/libgabble-plugins.la libconsole_la_DEPENDENCIES = $(am__DEPENDENCIES_1) am_libconsole_la_OBJECTS = console.lo libconsole_la_OBJECTS = $(am_libconsole_la_OBJECTS) AM_V_lt = $(am__v_lt_@AM_V@) am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) am__v_lt_0 = --silent am__v_lt_1 = @ENABLE_INSTALLED_TESTS_FALSE@@ENABLE_PLUGINS_FALSE@am_libconsole_la_rpath = @ENABLE_INSTALLED_TESTS_TRUE@@ENABLE_PLUGINS_FALSE@am_libconsole_la_rpath = @ENABLE_PLUGINS_TRUE@am_libconsole_la_rpath = -rpath $(pluginexecdir) libgateways_la_DEPENDENCIES = $(am__DEPENDENCIES_1) am_libgateways_la_OBJECTS = gateways.lo libgateways_la_OBJECTS = $(am_libgateways_la_OBJECTS) @ENABLE_INSTALLED_TESTS_FALSE@@ENABLE_PLUGINS_FALSE@am_libgateways_la_rpath = @ENABLE_INSTALLED_TESTS_TRUE@@ENABLE_PLUGINS_FALSE@am_libgateways_la_rpath = @ENABLE_PLUGINS_TRUE@am_libgateways_la_rpath = -rpath $(pluginexecdir) libtest_la_DEPENDENCIES = $(am__DEPENDENCIES_1) am_libtest_la_OBJECTS = test.lo libtest_la_OBJECTS = $(am_libtest_la_OBJECTS) libtest_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ $(libtest_la_LDFLAGS) $(LDFLAGS) -o $@ @ENABLE_INSTALLED_TESTS_FALSE@am_libtest_la_rpath = @ENABLE_INSTALLED_TESTS_TRUE@am_libtest_la_rpath = -rpath \ @ENABLE_INSTALLED_TESTS_TRUE@ $(libtestplugindir) am__dist_bin_SCRIPTS_DIST = telepathy-gabble-xmpp-console SCRIPTS = $(dist_bin_SCRIPTS) AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) depcomp = $(SHELL) $(top_srcdir)/depcomp am__depfiles_maybe = depfiles am__mv = mv -f COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ $(AM_CFLAGS) $(CFLAGS) AM_V_CC = $(am__v_CC_@AM_V@) am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) am__v_CC_0 = @echo " CC " $@; am__v_CC_1 = CCLD = $(CC) LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ $(AM_LDFLAGS) $(LDFLAGS) -o $@ AM_V_CCLD = $(am__v_CCLD_@AM_V@) am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) am__v_CCLD_0 = @echo " CCLD " $@; am__v_CCLD_1 = SOURCES = $(libconsole_la_SOURCES) $(libgateways_la_SOURCES) \ $(libtest_la_SOURCES) DIST_SOURCES = $(libconsole_la_SOURCES) $(libgateways_la_SOURCES) \ $(libtest_la_SOURCES) am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) # Read a list of newline-separated strings from the standard input, # and print each of them once, without duplicates. Input order is # *not* preserved. am__uniquify_input = $(AWK) '\ BEGIN { nonempty = 0; } \ { items[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in items) print i; }; } \ ' # Make sure the list of sources is unique. This is necessary because, # e.g., the same source file might be shared among _SOURCES variables # for different programs/libraries. am__define_uniq_tagged_files = \ list='$(am__tagged_files)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | $(am__uniquify_input)` ETAGS = etags CTAGS = ctags DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ AR = @AR@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CFLAGS = @CFLAGS@ CLIENT_TYPE = @CLIENT_TYPE@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CYGPATH_W = @CYGPATH_W@ DBUS_CFLAGS = @DBUS_CFLAGS@ DBUS_LIBS = @DBUS_LIBS@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DLLTOOL = @DLLTOOL@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ ENABLE_CODING_STYLE_CHECKS = @ENABLE_CODING_STYLE_CHECKS@ ENABLE_PLUGINS = @ENABLE_PLUGINS@ ERROR_CFLAGS = @ERROR_CFLAGS@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ GLIB_CFLAGS = @GLIB_CFLAGS@ GLIB_GENMARSHAL = @GLIB_GENMARSHAL@ GLIB_LIBS = @GLIB_LIBS@ GMODULE_CFLAGS = @GMODULE_CFLAGS@ GMODULE_LIBS = @GMODULE_LIBS@ GREP = @GREP@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ LD = @LD@ LDFLAGS = @LDFLAGS@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBTOOL = @LIBTOOL@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBOBJS = @LTLIBOBJS@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ NICE_CFLAGS = @NICE_CFLAGS@ NICE_LIBS = @NICE_LIBS@ NM = @NM@ NMEDIT = @NMEDIT@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ PYTHON = @PYTHON@ PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@ PYTHON_PLATFORM = @PYTHON_PLATFORM@ PYTHON_PREFIX = @PYTHON_PREFIX@ PYTHON_VERSION = @PYTHON_VERSION@ RANLIB = @RANLIB@ SED = @SED@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ SOUP_CFLAGS = @SOUP_CFLAGS@ SOUP_LIBS = @SOUP_LIBS@ STRIP = @STRIP@ TEST_PYTHON = @TEST_PYTHON@ TP_GLIB_CFLAGS = @TP_GLIB_CFLAGS@ TP_GLIB_LIBS = @TP_GLIB_LIBS@ VERSION = @VERSION@ WOCKY_CFLAGS = @WOCKY_CFLAGS@ WOCKY_LIBS = @WOCKY_LIBS@ XSLTPROC = @XSLTPROC@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ gabbletestsdir = @gabbletestsdir@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ mandir = @mandir@ mkdir_p = @mkdir_p@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ pkgpyexecdir = @pkgpyexecdir@ pkgpythondir = @pkgpythondir@ pluginexecdir = @pluginexecdir@ pluginexeclibdir = @pluginexeclibdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ pyexecdir = @pyexecdir@ pythondir = @pythondir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ srcdir = @srcdir@ sysconfdir = @sysconfdir@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ installable_plugins = \ libconsole.la \ libgateways.la libtest_only_plugins = \ libtest.la @ENABLE_INSTALLED_TESTS_FALSE@noinst_LTLIBRARIES = \ @ENABLE_INSTALLED_TESTS_FALSE@ $(libtest_only_plugins) $(NULL) \ @ENABLE_INSTALLED_TESTS_FALSE@ $(am__append_1) # libtesting-only plugins @ENABLE_INSTALLED_TESTS_TRUE@noinst_LTLIBRARIES = $(NULL) \ @ENABLE_INSTALLED_TESTS_TRUE@ $(am__append_1) @ENABLE_INSTALLED_TESTS_TRUE@libtestplugindir = $(gabbletestsdir)/plugins @ENABLE_INSTALLED_TESTS_TRUE@libtestplugin_LTLIBRARIES = \ @ENABLE_INSTALLED_TESTS_TRUE@ $(libtest_only_plugins) \ @ENABLE_INSTALLED_TESTS_TRUE@ $(NULL) # because libtest.la is not installed, libtool will want to compile it as static # despite -shared (a convenience library), unless we also use -rpath @ENABLE_INSTALLED_TESTS_FALSE@libtest_la_LDFLAGS = $(AM_LDFLAGS) -rpath $(pluginexecdir) @ENABLE_INSTALLED_TESTS_TRUE@libtest_la_LDFLAGS = $(AM_LDFLAGS) @ENABLE_PLUGINS_TRUE@pluginexec_LTLIBRARIES = $(installable_plugins) @ENABLE_PLUGINS_TRUE@dist_bin_SCRIPTS = \ @ENABLE_PLUGINS_TRUE@ telepathy-gabble-xmpp-console @ENABLE_PLUGINS_FALSE@EXTRA_DIST = \ @ENABLE_PLUGINS_FALSE@ telepathy-gabble-xmpp-console AM_LDFLAGS = -avoid-version -shared -no-undefined ALL_PLUGIN_LIBS = \ @WOCKY_LIBS@ \ @GLIB_LIBS@ \ @TP_GLIB_LIBS@ \ $(top_builddir)/extensions/libgabble-extensions.la \ $(top_builddir)/src/libgabble-plugins.la libtest_la_LIBADD = $(ALL_PLUGIN_LIBS) libgateways_la_LIBADD = $(ALL_PLUGIN_LIBS) libconsole_la_LIBADD = $(ALL_PLUGIN_LIBS) libtest_la_SOURCES = \ test.c \ test.h libgateways_la_SOURCES = \ gateways.c \ gateways.h libconsole_la_SOURCES = \ console.c \ console.h AM_CFLAGS = $(ERROR_CFLAGS) \ -I $(top_srcdir) -I $(top_builddir) \ @DBUS_CFLAGS@ @GLIB_CFLAGS@ @WOCKY_CFLAGS@ @TP_GLIB_CFLAGS@ \ -I $(top_srcdir)/gabble -I $(top_builddir)/gabble \ -I $(top_srcdir)/plugins all: all-am .SUFFIXES: .SUFFIXES: .c .lo .o .obj $(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu plugins/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --gnu plugins/Makefile .PRECIOUS: Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): install-libtestpluginLTLIBRARIES: $(libtestplugin_LTLIBRARIES) @$(NORMAL_INSTALL) @list='$(libtestplugin_LTLIBRARIES)'; test -n "$(libtestplugindir)" || list=; \ list2=; for p in $$list; do \ if test -f $$p; then \ list2="$$list2 $$p"; \ else :; fi; \ done; \ test -z "$$list2" || { \ echo " $(MKDIR_P) '$(DESTDIR)$(libtestplugindir)'"; \ $(MKDIR_P) "$(DESTDIR)$(libtestplugindir)" || exit 1; \ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(libtestplugindir)'"; \ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(libtestplugindir)"; \ } uninstall-libtestpluginLTLIBRARIES: @$(NORMAL_UNINSTALL) @list='$(libtestplugin_LTLIBRARIES)'; test -n "$(libtestplugindir)" || list=; \ for p in $$list; do \ $(am__strip_dir) \ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(libtestplugindir)/$$f'"; \ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(libtestplugindir)/$$f"; \ done clean-libtestpluginLTLIBRARIES: -test -z "$(libtestplugin_LTLIBRARIES)" || rm -f $(libtestplugin_LTLIBRARIES) @list='$(libtestplugin_LTLIBRARIES)'; \ locs=`for p in $$list; do echo $$p; done | \ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ sort -u`; \ test -z "$$locs" || { \ echo rm -f $${locs}; \ rm -f $${locs}; \ } clean-noinstLTLIBRARIES: -test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES) @list='$(noinst_LTLIBRARIES)'; \ locs=`for p in $$list; do echo $$p; done | \ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ sort -u`; \ test -z "$$locs" || { \ echo rm -f $${locs}; \ rm -f $${locs}; \ } install-pluginexecLTLIBRARIES: $(pluginexec_LTLIBRARIES) @$(NORMAL_INSTALL) @list='$(pluginexec_LTLIBRARIES)'; test -n "$(pluginexecdir)" || list=; \ list2=; for p in $$list; do \ if test -f $$p; then \ list2="$$list2 $$p"; \ else :; fi; \ done; \ test -z "$$list2" || { \ echo " $(MKDIR_P) '$(DESTDIR)$(pluginexecdir)'"; \ $(MKDIR_P) "$(DESTDIR)$(pluginexecdir)" || exit 1; \ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(pluginexecdir)'"; \ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(pluginexecdir)"; \ } uninstall-pluginexecLTLIBRARIES: @$(NORMAL_UNINSTALL) @list='$(pluginexec_LTLIBRARIES)'; test -n "$(pluginexecdir)" || list=; \ for p in $$list; do \ $(am__strip_dir) \ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(pluginexecdir)/$$f'"; \ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(pluginexecdir)/$$f"; \ done clean-pluginexecLTLIBRARIES: -test -z "$(pluginexec_LTLIBRARIES)" || rm -f $(pluginexec_LTLIBRARIES) @list='$(pluginexec_LTLIBRARIES)'; \ locs=`for p in $$list; do echo $$p; done | \ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ sort -u`; \ test -z "$$locs" || { \ echo rm -f $${locs}; \ rm -f $${locs}; \ } libconsole.la: $(libconsole_la_OBJECTS) $(libconsole_la_DEPENDENCIES) $(EXTRA_libconsole_la_DEPENDENCIES) $(AM_V_CCLD)$(LINK) $(am_libconsole_la_rpath) $(libconsole_la_OBJECTS) $(libconsole_la_LIBADD) $(LIBS) libgateways.la: $(libgateways_la_OBJECTS) $(libgateways_la_DEPENDENCIES) $(EXTRA_libgateways_la_DEPENDENCIES) $(AM_V_CCLD)$(LINK) $(am_libgateways_la_rpath) $(libgateways_la_OBJECTS) $(libgateways_la_LIBADD) $(LIBS) libtest.la: $(libtest_la_OBJECTS) $(libtest_la_DEPENDENCIES) $(EXTRA_libtest_la_DEPENDENCIES) $(AM_V_CCLD)$(libtest_la_LINK) $(am_libtest_la_rpath) $(libtest_la_OBJECTS) $(libtest_la_LIBADD) $(LIBS) install-dist_binSCRIPTS: $(dist_bin_SCRIPTS) @$(NORMAL_INSTALL) @list='$(dist_bin_SCRIPTS)'; test -n "$(bindir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(bindir)'"; \ $(MKDIR_P) "$(DESTDIR)$(bindir)" || exit 1; \ fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ if test -f "$$d$$p"; then echo "$$d$$p"; echo "$$p"; else :; fi; \ done | \ sed -e 'p;s,.*/,,;n' \ -e 'h;s|.*|.|' \ -e 'p;x;s,.*/,,;$(transform)' | sed 'N;N;N;s,\n, ,g' | \ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1; } \ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \ if ($$2 == $$4) { files[d] = files[d] " " $$1; \ if (++n[d] == $(am__install_max)) { \ print "f", d, files[d]; n[d] = 0; files[d] = "" } } \ else { print "f", d "/" $$4, $$1 } } \ END { for (d in files) print "f", d, files[d] }' | \ while read type dir files; do \ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \ test -z "$$files" || { \ echo " $(INSTALL_SCRIPT) $$files '$(DESTDIR)$(bindir)$$dir'"; \ $(INSTALL_SCRIPT) $$files "$(DESTDIR)$(bindir)$$dir" || exit $$?; \ } \ ; done uninstall-dist_binSCRIPTS: @$(NORMAL_UNINSTALL) @list='$(dist_bin_SCRIPTS)'; test -n "$(bindir)" || exit 0; \ files=`for p in $$list; do echo "$$p"; done | \ sed -e 's,.*/,,;$(transform)'`; \ dir='$(DESTDIR)$(bindir)'; $(am__uninstall_files_from_dir) mostlyclean-compile: -rm -f *.$(OBJEXT) distclean-compile: -rm -f *.tab.c @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/console.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gateways.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test.Plo@am__quote@ .c.o: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< .c.obj: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` .c.lo: @am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs ID: $(am__tagged_files) $(am__define_uniq_tagged_files); mkid -fID $$unique tags: tags-am TAGS: tags tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) set x; \ here=`pwd`; \ $(am__define_uniq_tagged_files); \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ if test $$# -gt 0; then \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ "$$@" $$unique; \ else \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ $$unique; \ fi; \ fi ctags: ctags-am CTAGS: ctags ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) $(am__define_uniq_tagged_files); \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" cscopelist: cscopelist-am cscopelist-am: $(am__tagged_files) list='$(am__tagged_files)'; \ case "$(srcdir)" in \ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ *) sdir=$(subdir)/$(srcdir) ;; \ esac; \ for i in $$list; do \ if test -f "$$i"; then \ echo "$(subdir)/$$i"; \ else \ echo "$$sdir/$$i"; \ fi; \ done >> $(top_builddir)/cscope.files distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags distdir: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done check-am: all-am check: check-am all-am: Makefile $(LTLIBRARIES) $(SCRIPTS) installdirs: for dir in "$(DESTDIR)$(libtestplugindir)" "$(DESTDIR)$(pluginexecdir)" "$(DESTDIR)$(bindir)"; do \ test -z "$$dir" || $(MKDIR_P) "$$dir"; \ done install: install-am install-exec: install-exec-am install-data: install-data-am uninstall: uninstall-am install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-am install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." clean: clean-am clean-am: clean-generic clean-libtestpluginLTLIBRARIES clean-libtool \ clean-noinstLTLIBRARIES clean-pluginexecLTLIBRARIES \ mostlyclean-am distclean: distclean-am -rm -rf ./$(DEPDIR) -rm -f Makefile distclean-am: clean-am distclean-compile distclean-generic \ distclean-tags dvi: dvi-am dvi-am: html: html-am html-am: info: info-am info-am: install-data-am: install-libtestpluginLTLIBRARIES install-dvi: install-dvi-am install-dvi-am: install-exec-am: install-dist_binSCRIPTS install-pluginexecLTLIBRARIES install-html: install-html-am install-html-am: install-info: install-info-am install-info-am: install-man: install-pdf: install-pdf-am install-pdf-am: install-ps: install-ps-am install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-am -rm -rf ./$(DEPDIR) -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-am mostlyclean-am: mostlyclean-compile mostlyclean-generic \ mostlyclean-libtool pdf: pdf-am pdf-am: ps: ps-am ps-am: uninstall-am: uninstall-dist_binSCRIPTS \ uninstall-libtestpluginLTLIBRARIES \ uninstall-pluginexecLTLIBRARIES .MAKE: install-am install-strip .PHONY: CTAGS GTAGS TAGS all all-am check check-am clean clean-generic \ clean-libtestpluginLTLIBRARIES clean-libtool \ clean-noinstLTLIBRARIES clean-pluginexecLTLIBRARIES \ cscopelist-am ctags ctags-am distclean distclean-compile \ distclean-generic distclean-libtool distclean-tags distdir dvi \ dvi-am html html-am info info-am install install-am \ install-data install-data-am install-dist_binSCRIPTS \ install-dvi install-dvi-am install-exec install-exec-am \ install-html install-html-am install-info install-info-am \ install-libtestpluginLTLIBRARIES install-man install-pdf \ install-pdf-am install-pluginexecLTLIBRARIES install-ps \ install-ps-am install-strip installcheck installcheck-am \ installdirs maintainer-clean maintainer-clean-generic \ mostlyclean mostlyclean-compile mostlyclean-generic \ mostlyclean-libtool pdf pdf-am ps ps-am tags tags-am uninstall \ uninstall-am uninstall-dist_binSCRIPTS \ uninstall-libtestpluginLTLIBRARIES \ uninstall-pluginexecLTLIBRARIES # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: telepathy-gabble-0.18.2/data/0000755000175000017500000000000012312537051015725 5ustar00smcvsmcv00000000000000telepathy-gabble-0.18.2/data/gabble.service.in0000644000175000017500000000015312200204332021114 0ustar00smcvsmcv00000000000000[D-BUS Service] Name=org.freedesktop.Telepathy.ConnectionManager.gabble Exec=@libexecdir@/telepathy-gabble telepathy-gabble-0.18.2/data/Makefile.am0000644000175000017500000000137312200204332017752 0ustar00smcvsmcv00000000000000EXTRA_DIST = gabble.service.in managerdir = $(datadir)/telepathy/managers manager_DATA = gabble.manager servicedir = $(datadir)/dbus-1/services service_DATA = org.freedesktop.Telepathy.ConnectionManager.gabble.service # We don't use the full filename for the .in because > 99 character filenames # in tarballs are non-portable (and automake 1.8 doesn't let us build # non-archaic tarballs) org.freedesktop.Telepathy.ConnectionManager.gabble.service: gabble.service.in \ Makefile $(AM_V_GEN)sed -e "s|[@]libexecdir[@]|$(libexecdir)|" $< > $@ CLEANFILES = $(service_DATA) $(manager_DATA) $(manager_DATA): ../src/write-mgr-file.c ../src/protocol.c ../src/protocol.h @$(MAKE) -C ../src write-mgr-file$(EXEEXT) $(AM_V_GEN)../src/write-mgr-file$(EXEEXT) > $@ telepathy-gabble-0.18.2/data/Makefile.in0000644000175000017500000004076512312536073020011 0ustar00smcvsmcv00000000000000# Makefile.in generated by automake 1.14.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2013 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ VPATH = @srcdir@ am__is_gnu_make = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)' am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ subdir = data DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/ax_config_dir.m4 \ $(top_srcdir)/m4/compiler.m4 $(top_srcdir)/m4/libtool.m4 \ $(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \ $(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \ $(top_srcdir)/m4/tp-compiler-flag.m4 \ $(top_srcdir)/m4/tp-compiler-warnings.m4 \ $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = SOURCES = DIST_SOURCES = am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; am__vpath_adj = case $$p in \ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ *) f=$$p;; \ esac; am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; am__install_max = 40 am__nobase_strip_setup = \ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` am__nobase_strip = \ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" am__nobase_list = $(am__nobase_strip_setup); \ for p in $$list; do echo "$$p $$p"; done | \ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ if (++n[$$2] == $(am__install_max)) \ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ END { for (dir in files) print dir, files[dir] }' am__base_list = \ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' am__uninstall_files_from_dir = { \ test -z "$$files" \ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ $(am__cd) "$$dir" && rm -f $$files; }; \ } am__installdirs = "$(DESTDIR)$(managerdir)" "$(DESTDIR)$(servicedir)" DATA = $(manager_DATA) $(service_DATA) am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ AR = @AR@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CFLAGS = @CFLAGS@ CLIENT_TYPE = @CLIENT_TYPE@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CYGPATH_W = @CYGPATH_W@ DBUS_CFLAGS = @DBUS_CFLAGS@ DBUS_LIBS = @DBUS_LIBS@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DLLTOOL = @DLLTOOL@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ ENABLE_CODING_STYLE_CHECKS = @ENABLE_CODING_STYLE_CHECKS@ ENABLE_PLUGINS = @ENABLE_PLUGINS@ ERROR_CFLAGS = @ERROR_CFLAGS@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ GLIB_CFLAGS = @GLIB_CFLAGS@ GLIB_GENMARSHAL = @GLIB_GENMARSHAL@ GLIB_LIBS = @GLIB_LIBS@ GMODULE_CFLAGS = @GMODULE_CFLAGS@ GMODULE_LIBS = @GMODULE_LIBS@ GREP = @GREP@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ LD = @LD@ LDFLAGS = @LDFLAGS@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBTOOL = @LIBTOOL@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBOBJS = @LTLIBOBJS@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ NICE_CFLAGS = @NICE_CFLAGS@ NICE_LIBS = @NICE_LIBS@ NM = @NM@ NMEDIT = @NMEDIT@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ PYTHON = @PYTHON@ PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@ PYTHON_PLATFORM = @PYTHON_PLATFORM@ PYTHON_PREFIX = @PYTHON_PREFIX@ PYTHON_VERSION = @PYTHON_VERSION@ RANLIB = @RANLIB@ SED = @SED@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ SOUP_CFLAGS = @SOUP_CFLAGS@ SOUP_LIBS = @SOUP_LIBS@ STRIP = @STRIP@ TEST_PYTHON = @TEST_PYTHON@ TP_GLIB_CFLAGS = @TP_GLIB_CFLAGS@ TP_GLIB_LIBS = @TP_GLIB_LIBS@ VERSION = @VERSION@ WOCKY_CFLAGS = @WOCKY_CFLAGS@ WOCKY_LIBS = @WOCKY_LIBS@ XSLTPROC = @XSLTPROC@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ gabbletestsdir = @gabbletestsdir@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ mandir = @mandir@ mkdir_p = @mkdir_p@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ pkgpyexecdir = @pkgpyexecdir@ pkgpythondir = @pkgpythondir@ pluginexecdir = @pluginexecdir@ pluginexeclibdir = @pluginexeclibdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ pyexecdir = @pyexecdir@ pythondir = @pythondir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ srcdir = @srcdir@ sysconfdir = @sysconfdir@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ EXTRA_DIST = gabble.service.in managerdir = $(datadir)/telepathy/managers manager_DATA = gabble.manager servicedir = $(datadir)/dbus-1/services service_DATA = org.freedesktop.Telepathy.ConnectionManager.gabble.service CLEANFILES = $(service_DATA) $(manager_DATA) all: all-am .SUFFIXES: $(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu data/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --gnu data/Makefile .PRECIOUS: Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs install-managerDATA: $(manager_DATA) @$(NORMAL_INSTALL) @list='$(manager_DATA)'; test -n "$(managerdir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(managerdir)'"; \ $(MKDIR_P) "$(DESTDIR)$(managerdir)" || exit 1; \ fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; \ done | $(am__base_list) | \ while read files; do \ echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(managerdir)'"; \ $(INSTALL_DATA) $$files "$(DESTDIR)$(managerdir)" || exit $$?; \ done uninstall-managerDATA: @$(NORMAL_UNINSTALL) @list='$(manager_DATA)'; test -n "$(managerdir)" || list=; \ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ dir='$(DESTDIR)$(managerdir)'; $(am__uninstall_files_from_dir) install-serviceDATA: $(service_DATA) @$(NORMAL_INSTALL) @list='$(service_DATA)'; test -n "$(servicedir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(servicedir)'"; \ $(MKDIR_P) "$(DESTDIR)$(servicedir)" || exit 1; \ fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; \ done | $(am__base_list) | \ while read files; do \ echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(servicedir)'"; \ $(INSTALL_DATA) $$files "$(DESTDIR)$(servicedir)" || exit $$?; \ done uninstall-serviceDATA: @$(NORMAL_UNINSTALL) @list='$(service_DATA)'; test -n "$(servicedir)" || list=; \ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ dir='$(DESTDIR)$(servicedir)'; $(am__uninstall_files_from_dir) tags TAGS: ctags CTAGS: cscope cscopelist: distdir: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done check-am: all-am check: check-am all-am: Makefile $(DATA) installdirs: for dir in "$(DESTDIR)$(managerdir)" "$(DESTDIR)$(servicedir)"; do \ test -z "$$dir" || $(MKDIR_P) "$$dir"; \ done install: install-am install-exec: install-exec-am install-data: install-data-am uninstall: uninstall-am install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-am install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: clean-generic: -test -z "$(CLEANFILES)" || rm -f $(CLEANFILES) distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." clean: clean-am clean-am: clean-generic clean-libtool mostlyclean-am distclean: distclean-am -rm -f Makefile distclean-am: clean-am distclean-generic dvi: dvi-am dvi-am: html: html-am html-am: info: info-am info-am: install-data-am: install-managerDATA install-serviceDATA install-dvi: install-dvi-am install-dvi-am: install-exec-am: install-html: install-html-am install-html-am: install-info: install-info-am install-info-am: install-man: install-pdf: install-pdf-am install-pdf-am: install-ps: install-ps-am install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-am -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-am mostlyclean-am: mostlyclean-generic mostlyclean-libtool pdf: pdf-am pdf-am: ps: ps-am ps-am: uninstall-am: uninstall-managerDATA uninstall-serviceDATA .MAKE: install-am install-strip .PHONY: all all-am check check-am clean clean-generic clean-libtool \ cscopelist-am ctags-am distclean distclean-generic \ distclean-libtool distdir dvi dvi-am html html-am info info-am \ install install-am install-data install-data-am install-dvi \ install-dvi-am install-exec install-exec-am install-html \ install-html-am install-info install-info-am install-man \ install-managerDATA install-pdf install-pdf-am install-ps \ install-ps-am install-serviceDATA install-strip installcheck \ installcheck-am installdirs maintainer-clean \ maintainer-clean-generic mostlyclean mostlyclean-generic \ mostlyclean-libtool pdf pdf-am ps ps-am tags-am uninstall \ uninstall-am uninstall-managerDATA uninstall-serviceDATA # We don't use the full filename for the .in because > 99 character filenames # in tarballs are non-portable (and automake 1.8 doesn't let us build # non-archaic tarballs) org.freedesktop.Telepathy.ConnectionManager.gabble.service: gabble.service.in \ Makefile $(AM_V_GEN)sed -e "s|[@]libexecdir[@]|$(libexecdir)|" $< > $@ $(manager_DATA): ../src/write-mgr-file.c ../src/protocol.c ../src/protocol.h @$(MAKE) -C ../src write-mgr-file$(EXEEXT) $(AM_V_GEN)../src/write-mgr-file$(EXEEXT) > $@ # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: telepathy-gabble-0.18.2/src/0000755000175000017500000000000012312537051015603 5ustar00smcvsmcv00000000000000telepathy-gabble-0.18.2/src/write-mgr-file.c0000644000175000017500000002705212227000321020575 0ustar00smcvsmcv00000000000000/* * write_mgr_file.c - utility to produce gabble.manager. Part of Gabble. * Copyright (C) 2006-2010 Collabora Ltd. * Copyright (C) 2006-2010 Nokia Corporation * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 "config.h" #include #include #include #include #include #include "extensions/extensions.h" #include "protocol.h" #define WRITE_STR(prop, key) \ { \ const gchar *val = tp_asv_get_string (props, prop); \ g_assert (!tp_str_empty (val)); \ g_key_file_set_string (f, section_name, key, val); \ } static void write_parameters (GKeyFile *f, gchar *section_name, TpBaseProtocol *protocol) { const TpCMParamSpec *parameters = tp_base_protocol_get_parameters (protocol); const TpCMParamSpec *row; for (row = parameters; row->name; row++) { gchar *param_name = g_strdup_printf ("param-%s", row->name); gchar *param_value = g_strdup_printf ("%s%s%s%s", row->dtype, (row->flags & TP_CONN_MGR_PARAM_FLAG_REQUIRED ? " required" : ""), (row->flags & TP_CONN_MGR_PARAM_FLAG_REGISTER ? " register" : ""), (row->flags & TP_CONN_MGR_PARAM_FLAG_SECRET ? " secret" : "")); g_key_file_set_string (f, section_name, param_name, param_value); g_free (param_value); g_free (param_name); } for (row = parameters; row->name; row++) { if (row->flags & TP_CONN_MGR_PARAM_FLAG_HAS_DEFAULT) { gchar *default_name = g_strdup_printf ("default-%s", row->name); switch (row->gtype) { case G_TYPE_STRING: g_key_file_set_string (f, section_name, default_name, row->def); break; case G_TYPE_INT: case G_TYPE_UINT: g_key_file_set_integer (f, section_name, default_name, GPOINTER_TO_INT(row->def)); break; case G_TYPE_BOOLEAN: g_key_file_set_boolean (f, section_name, default_name, GPOINTER_TO_INT(row->def) ? 1 : 0); break; default: /* can't be in the case because G_TYPE_STRV is actually a * function */ if (row->gtype == G_TYPE_STRV) { g_key_file_set_string_list (f, section_name, default_name, (const gchar **) row->def, g_strv_length ((gchar **) row->def)); } } g_free (default_name); } } } static void write_rcc_property (GKeyFile *keyfile, const gchar *group_name, const gchar *key, GValue *val) { switch (G_VALUE_TYPE (val)) { case G_TYPE_BOOLEAN: { gchar *kf_key = g_strconcat (key, " " DBUS_TYPE_BOOLEAN_AS_STRING, NULL); g_key_file_set_boolean (keyfile, group_name, kf_key, g_value_get_boolean (val)); g_free (kf_key); break; } case G_TYPE_STRING: { gchar *kf_key = g_strconcat (key, " " DBUS_TYPE_STRING_AS_STRING, NULL); g_key_file_set_string (keyfile, group_name, kf_key, g_value_get_string (val)); g_free (kf_key); break; } case G_TYPE_UINT: { gchar *kf_key = g_strconcat (key, " " DBUS_TYPE_UINT32_AS_STRING, NULL); gchar *kf_val = g_strdup_printf ("%u", g_value_get_uint (val)); g_key_file_set_value (keyfile, group_name, kf_key, kf_val); g_free (kf_key); g_free (kf_val); break; } /* FIXME: when we depend on Glib 2.26, we can use * g_key_file_set_[u]int64 (g.o #614864). */ case G_TYPE_UINT64: { gchar *kf_key = g_strconcat (key, " " DBUS_TYPE_UINT64_AS_STRING, NULL); gchar *kf_val = g_strdup_printf ("%" G_GUINT64_FORMAT, g_value_get_uint64 (val)); g_key_file_set_value (keyfile, group_name, kf_key, kf_val); g_free (kf_key); g_free (kf_val); break; } case G_TYPE_INT: { gchar *kf_key = g_strconcat (key, " " DBUS_TYPE_INT32_AS_STRING, NULL); g_key_file_set_integer (keyfile, group_name, kf_key, g_value_get_int (val)); g_free (kf_key); break; } case G_TYPE_INT64: { gchar *kf_key = g_strconcat (key, " " DBUS_TYPE_UINT64_AS_STRING, NULL); gchar *kf_val = g_strdup_printf ("%" G_GINT64_FORMAT, g_value_get_int64 (val)); g_key_file_set_value (keyfile, group_name, kf_key, kf_val); g_free (kf_key); g_free (kf_val); break; } default: { if (G_VALUE_TYPE (val) == G_TYPE_STRV) { gchar **list = g_value_get_boxed (val); gchar *kf_key = g_strconcat (key, " " DBUS_TYPE_ARRAY_AS_STRING DBUS_TYPE_STRING_AS_STRING, NULL); g_key_file_set_string_list (keyfile, group_name, kf_key, (const gchar **) list, g_strv_length (list)); g_free (kf_key); break; } /* we'd rather crash than forget to write required rcc property */ g_assert_not_reached (); } } } static gchar * generate_group_name (GHashTable *props) { static guint counter = 0; gchar *retval; gchar *chan_type = g_ascii_strdown (tp_asv_get_string (props, TP_PROP_CHANNEL_CHANNEL_TYPE), -1); gchar *chan_type_suffix; gchar *handle_type_name; guint handle_type = tp_asv_get_uint32 (props, TP_PROP_CHANNEL_TARGET_HANDLE_TYPE, NULL); g_assert (chan_type != NULL); chan_type_suffix = strrchr (chan_type, '.'); g_assert (chan_type_suffix != NULL); chan_type_suffix++; switch (handle_type) { case TP_HANDLE_TYPE_CONTACT: handle_type_name = "-1on1"; break; case TP_HANDLE_TYPE_ROOM: handle_type_name = "-multi"; break; case TP_HANDLE_TYPE_GROUP: handle_type_name = "-group"; break; case TP_HANDLE_TYPE_LIST: handle_type_name = "-list"; break; default: handle_type_name = ""; } retval = g_strdup_printf ("%s%s-%d", chan_type_suffix, handle_type_name, ++counter); g_free (chan_type); return retval; } static void write_rccs (GKeyFile *f, const gchar *section_name, GHashTable *props) { GPtrArray *rcc_list = tp_asv_get_boxed (props, TP_PROP_PROTOCOL_REQUESTABLE_CHANNEL_CLASSES, TP_ARRAY_TYPE_REQUESTABLE_CHANNEL_CLASS_LIST); guint i; gchar **group_names = g_new0 (gchar *, rcc_list->len + 1); for (i = 0; i < rcc_list->len; i++) { gchar **allowed; gchar *group_name; GHashTable *fixed; GHashTableIter iter; gpointer k, v; tp_value_array_unpack (g_ptr_array_index (rcc_list, i), 2, &fixed, &allowed); group_name = generate_group_name (fixed); g_hash_table_iter_init (&iter, fixed); while (g_hash_table_iter_next (&iter, &k, &v)) { const gchar *key = k; GValue *val = v; write_rcc_property (f, group_name, key, val); } /* takes ownership */ group_names[i] = group_name; g_key_file_set_string_list (f, group_name, "allowed", (const gchar **) allowed, g_strv_length (allowed)); } g_key_file_set_string_list (f, section_name, "RequestableChannelClasses", (const gchar **) group_names, rcc_list->len); g_strfreev (group_names); } static gchar * mgr_file_contents (const char *busname, const char *objpath, GSList *protocols, GError **error) { GKeyFile *f = g_key_file_new (); gchar *file_data; g_key_file_set_string (f, "ConnectionManager", "BusName", busname); g_key_file_set_string (f, "ConnectionManager", "ObjectPath", objpath); /* there are no CM interfaces defined yet, so we cheat */ g_key_file_set_string (f, "ConnectionManager", "Interfaces", ""); while (protocols != NULL) { GabbleJabberProtocol *protocol = protocols->data; gchar *section_name; GHashTable *props; const gchar * const *ifaces; const gchar * const *c_ifaces; const gchar * const *addr_vcard_fields; const gchar * const *addr_uri_schemes; const gchar * const *auth_types; g_object_get (G_OBJECT (protocol), "immutable-properties", &props, NULL); section_name = g_strdup_printf ("Protocol %s", tp_base_protocol_get_name (TP_BASE_PROTOCOL (protocol))); ifaces = tp_asv_get_strv (props, TP_PROP_PROTOCOL_INTERFACES); c_ifaces = tp_asv_get_strv (props, TP_PROP_PROTOCOL_CONNECTION_INTERFACES); auth_types = tp_asv_get_strv (props, TP_PROP_PROTOCOL_AUTHENTICATION_TYPES); addr_vcard_fields = tp_asv_get_strv (props, TP_PROP_PROTOCOL_INTERFACE_ADDRESSING_ADDRESSABLE_VCARD_FIELDS); addr_uri_schemes = tp_asv_get_strv (props, TP_PROP_PROTOCOL_INTERFACE_ADDRESSING_ADDRESSABLE_URI_SCHEMES); write_parameters (f, section_name, TP_BASE_PROTOCOL (protocol)); write_rccs (f, section_name, props); g_key_file_set_string_list (f, section_name, "Interfaces", ifaces, g_strv_length ((gchar **) ifaces)); g_key_file_set_string_list (f, section_name, "ConnectionInterfaces", c_ifaces, g_strv_length ((gchar **) c_ifaces)); g_key_file_set_string_list (f, section_name, "AuthenticationTypes", auth_types, g_strv_length ((gchar **) auth_types)); g_key_file_set_string_list (f, section_name, "AddressableVCardFields", addr_vcard_fields, g_strv_length ((gchar **) addr_vcard_fields)); g_key_file_set_string_list (f, section_name, "AddressableURISchemes", addr_uri_schemes, g_strv_length ((gchar **) addr_uri_schemes)); WRITE_STR (TP_PROP_PROTOCOL_VCARD_FIELD, "VCardField"); WRITE_STR (TP_PROP_PROTOCOL_ENGLISH_NAME, "EnglishName"); WRITE_STR (TP_PROP_PROTOCOL_ICON, "Icon"); g_free (section_name); g_hash_table_unref (props); protocols = protocols->next; } file_data = g_key_file_to_data (f, NULL, error); g_key_file_free (f); return file_data; } int main (void) { GError *error = NULL; gchar *s; GSList *protocols = NULL; g_type_init (); dbus_g_type_specialized_init (); protocols = g_slist_prepend (protocols, gabble_jabber_protocol_new ()); s = mgr_file_contents (TP_CM_BUS_NAME_BASE "gabble", TP_CM_OBJECT_PATH_BASE "gabble", protocols, &error); g_object_unref (protocols->data); g_slist_free (protocols); if (!s) { fprintf (stderr, "%s", error->message); g_error_free (error); return 1; } printf ("%s", s); g_free (s); return 0; } telepathy-gabble-0.18.2/src/main.c0000644000175000017500000000202212200204333016655 0ustar00smcvsmcv00000000000000/* * main.c - simple wrapper calling gabble_main * Copyright (C) 2008 Collabora Ltd. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 "config.h" #include "gabble.h" int #ifdef BUILD_AS_ANDROID_SERVICE telepathy_gabble_main #else main #endif (int argc, char **argv) { gabble_init (); return gabble_main (argc, argv); } telepathy-gabble-0.18.2/src/plugin-connection.c0000644000175000017500000001303412200204333021371 0ustar00smcvsmcv00000000000000/* * plugin-connection.c — API for telepathy-gabble plugins * Copyright © 2012 Collabora Ltd. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 "config.h" #include "gabble/plugin-connection.h" #include #include #include #include static guint sig_id_porter_available = 0; /** * SECTION: gabble-plugin-connection * @title: GabblePluginConnection * @short_description: Object representing gabble connection, implemented by * Gabble internals. * * This Object represents Gabble Connection. * * Virtual methods in GabblePluginConnectionInterface interface are implemented * by GabbleConnection object. And only Gabble should implement this interface. */ G_DEFINE_INTERFACE (GabblePluginConnection, gabble_plugin_connection, G_TYPE_OBJECT); static void gabble_plugin_connection_default_init (GabblePluginConnectionInterface *iface) { static gsize once = 0; if (g_once_init_enter (&once)) { /** * @self: a connection interface * @porter: a porter * * Emitted when the WockyPorter becomes available. */ sig_id_porter_available = g_signal_new ( "porter-available", G_OBJECT_CLASS_TYPE (iface), G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED, 0, NULL, NULL, g_cclosure_marshal_VOID__OBJECT, G_TYPE_NONE, 1, WOCKY_TYPE_PORTER); g_once_init_leave (&once, 1); } } gchar * gabble_plugin_connection_add_sidecar_own_caps ( GabblePluginConnection *plugin_connection, const GabbleCapabilitySet *cap_set, const GPtrArray *identities) { GabblePluginConnectionInterface *iface = GABBLE_PLUGIN_CONNECTION_GET_IFACE (plugin_connection); g_return_val_if_fail (iface != NULL, NULL); g_return_val_if_fail (iface->add_sidecar_own_caps != NULL, NULL); return iface->add_sidecar_own_caps (plugin_connection, cap_set, identities); } gchar * gabble_plugin_connection_add_sidecar_own_caps_full ( GabblePluginConnection *plugin_connection, const GabbleCapabilitySet *cap_set, const GPtrArray *identities, GPtrArray *data_forms) { GabblePluginConnectionInterface *iface = GABBLE_PLUGIN_CONNECTION_GET_IFACE (plugin_connection); g_return_val_if_fail (iface != NULL, NULL); g_return_val_if_fail (iface->add_sidecar_own_caps_full != NULL, NULL); return iface->add_sidecar_own_caps_full (plugin_connection, cap_set, identities, data_forms); } WockySession * gabble_plugin_connection_get_session ( GabblePluginConnection *plugin_connection) { GabblePluginConnectionInterface *iface = GABBLE_PLUGIN_CONNECTION_GET_IFACE (plugin_connection); g_return_val_if_fail (iface != NULL, NULL); g_return_val_if_fail (iface->get_session != NULL, NULL); return iface->get_session (plugin_connection); } gchar * gabble_plugin_connection_get_full_jid ( GabblePluginConnection *plugin_connection) { GabblePluginConnectionInterface *iface = GABBLE_PLUGIN_CONNECTION_GET_IFACE (plugin_connection); g_return_val_if_fail (iface != NULL, NULL); g_return_val_if_fail (iface->get_full_jid != NULL, NULL); return iface->get_full_jid (plugin_connection); } const gchar * gabble_plugin_connection_get_jid_for_caps ( GabblePluginConnection *plugin_connection, WockyXep0115Capabilities *caps) { GabblePluginConnectionInterface *iface = GABBLE_PLUGIN_CONNECTION_GET_IFACE (plugin_connection); g_return_val_if_fail (iface != NULL, NULL); g_return_val_if_fail (iface->get_jid_for_caps != NULL, NULL); return iface->get_jid_for_caps (plugin_connection, caps); } const gchar * gabble_plugin_connection_pick_best_resource_for_caps ( GabblePluginConnection *plugin_connection, const gchar *jid, GabbleCapabilitySetPredicate predicate, gconstpointer user_data) { GabblePluginConnectionInterface *iface = GABBLE_PLUGIN_CONNECTION_GET_IFACE (plugin_connection); g_return_val_if_fail (iface != NULL, NULL); g_return_val_if_fail (iface->pick_best_resource_for_caps != NULL, NULL); return iface->pick_best_resource_for_caps (plugin_connection, jid, predicate, user_data); } TpBaseContactList * gabble_plugin_connection_get_contact_list ( GabblePluginConnection *plugin_connection) { GabblePluginConnectionInterface *iface = GABBLE_PLUGIN_CONNECTION_GET_IFACE (plugin_connection); g_return_val_if_fail (iface != NULL, NULL); g_return_val_if_fail (iface->get_contact_list != NULL, NULL); return iface->get_contact_list (plugin_connection); } WockyXep0115Capabilities * gabble_plugin_connection_get_caps ( GabblePluginConnection *plugin_connection, TpHandle handle) { GabblePluginConnectionInterface *iface = GABBLE_PLUGIN_CONNECTION_GET_IFACE (plugin_connection); g_return_val_if_fail (iface != NULL, NULL); g_return_val_if_fail (iface->get_contact_list != NULL, NULL); return iface->get_caps (plugin_connection, handle); } telepathy-gabble-0.18.2/src/plugin.c0000644000175000017500000001452212200204333017237 0ustar00smcvsmcv00000000000000/* * plugin.c — API for telepathy-gabble plugins * Copyright © 2009 Collabora Ltd. * Copyright © 2009 Nokia Corporation * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 "config.h" #include "gabble/plugin.h" #include #define DEBUG_FLAG GABBLE_DEBUG_PLUGINS #include "debug.h" GType gabble_plugin_get_type (void) { static GType type = 0; if (type == 0) { static const GTypeInfo info = { sizeof (GabblePluginInterface), NULL, /* base_init */ NULL, /* base_finalize */ NULL, /* class_init */ NULL, /* class_finalize */ NULL, /* class_data */ 0, 0, /* n_preallocs */ NULL /* instance_init */ }; type = g_type_register_static (G_TYPE_INTERFACE, "GabblePlugin", &info, 0); } return type; } const gchar * gabble_plugin_get_name (GabblePlugin *plugin) { GabblePluginInterface *iface = GABBLE_PLUGIN_GET_INTERFACE (plugin); return iface->name; } const gchar * gabble_plugin_get_version (GabblePlugin *plugin) { GabblePluginInterface *iface = GABBLE_PLUGIN_GET_INTERFACE (plugin); return iface->version; } const gchar * const * gabble_plugin_get_sidecar_interfaces (GabblePlugin *plugin) { GabblePluginInterface *iface = GABBLE_PLUGIN_GET_INTERFACE (plugin); return iface->sidecar_interfaces; } gboolean gabble_plugin_implements_sidecar ( GabblePlugin *plugin, const gchar *sidecar_interface) { GabblePluginInterface *iface = GABBLE_PLUGIN_GET_INTERFACE (plugin); return tp_strv_contains (iface->sidecar_interfaces, sidecar_interface); } /** * gabble_plugin_create_sidecar: * @plugin: a plugin * @sidecar_interface: the primary D-Bus interface implemented by the sidecar, * which must be a member of the list returned by * gabble_plugin_get_sidecar_interfaces () * @callback: function to call when the new sidecar has been created, or an * unrecoverable error has occured * @user_data: data to pass to @callback */ void gabble_plugin_create_sidecar_async ( GabblePlugin *plugin, const gchar *sidecar_interface, GabblePluginConnection *plugin_connection, WockySession *session, GAsyncReadyCallback callback, gpointer user_data) { GabblePluginInterface *iface = GABBLE_PLUGIN_GET_INTERFACE (plugin); if (!gabble_plugin_implements_sidecar (plugin, sidecar_interface)) g_simple_async_report_error_in_idle (G_OBJECT (plugin), callback, user_data, TP_ERROR, TP_ERROR_NOT_IMPLEMENTED, "Gabble is buggy: '%s' doesn't implement sidecar %s", iface->name, sidecar_interface); else if (iface->create_sidecar_async == NULL) g_simple_async_report_error_in_idle (G_OBJECT (plugin), callback, user_data, TP_ERROR, TP_ERROR_NOT_IMPLEMENTED, "'%s' is buggy: it claims to implement %s, but does not implement " "create_sidecar_async", iface->name, sidecar_interface); else if (iface->create_sidecar_finish == NULL) g_simple_async_report_error_in_idle (G_OBJECT (plugin), callback, user_data, TP_ERROR, TP_ERROR_NOT_IMPLEMENTED, "'%s' is buggy: does not imlement create_sidecar_finish", iface->name); else iface->create_sidecar_async (plugin, sidecar_interface, plugin_connection, session, callback, user_data); } GabbleSidecar * gabble_plugin_create_sidecar_finish ( GabblePlugin *plugin, GAsyncResult *result, GError **error) { GabblePluginInterface *iface = GABBLE_PLUGIN_GET_INTERFACE (plugin); GabbleSidecar *sidecar; if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), error)) return NULL; if (iface->create_sidecar_finish == NULL) { WARNING ("'%s' is buggy: does not implement create_sidecar_finish", iface->name); return NULL; } sidecar = iface->create_sidecar_finish (plugin, result, error); return g_object_ref (sidecar); } const TpPresenceStatusSpec * gabble_plugin_get_custom_presence_statuses ( GabblePlugin *plugin) { GabblePluginInterface *iface = GABBLE_PLUGIN_GET_INTERFACE (plugin); return iface->presence_statuses; } gboolean gabble_plugin_implements_presence_status ( GabblePlugin *plugin, const gchar *status) { GabblePluginInterface *iface = GABBLE_PLUGIN_GET_INTERFACE (plugin); gint i; if (iface->presence_statuses == NULL) return FALSE; for (i = 0; iface->presence_statuses[i].name; i++) { if (!tp_strdiff (status, iface->presence_statuses[i].name)) return TRUE; } return FALSE; } const gchar * gabble_plugin_presence_status_for_privacy_list ( GabblePlugin *plugin, const gchar *list_name) { GabblePluginInterface *iface = GABBLE_PLUGIN_GET_INTERFACE (plugin); int i; if (iface->privacy_list_map == NULL) return NULL; for (i = 0; iface->privacy_list_map[i].privacy_list_name; i++) { if (!tp_strdiff (list_name, iface->privacy_list_map[i].privacy_list_name)) { DEBUG ("Plugin %s links presence %s with privacy list %s", iface->name, iface->privacy_list_map[i].privacy_list_name, iface->privacy_list_map[i].presence_status_name); return iface->privacy_list_map[i].presence_status_name; } } DEBUG ("No plugins link presence to privacy list %s", list_name); return NULL; } GPtrArray * gabble_plugin_create_channel_managers (GabblePlugin *plugin, GabblePluginConnection *plugin_connection) { GabblePluginInterface *iface = GABBLE_PLUGIN_GET_INTERFACE (plugin); GabblePluginCreateChannelManagersImpl func = iface->create_channel_managers; GPtrArray *out = NULL; if (func != NULL) out = func (plugin, plugin_connection); return out; } telepathy-gabble-0.18.2/src/gtalk-file-collection.h0000644000175000017500000000726112200204333022120 0ustar00smcvsmcv00000000000000/* * gtalk-file-collection.h - Header for GTalkFileCollection * Copyright (C) 2010 Collabora Ltd. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 __GTALK_FILE_COLLECTION_H__ #define __GTALK_FILE_COLLECTION_H__ #include #include #include "connection.h" typedef struct _GTalkFileCollection GTalkFileCollection; typedef enum { GTALK_FILE_COLLECTION_STATE_PENDING, GTALK_FILE_COLLECTION_STATE_ACCEPTED, GTALK_FILE_COLLECTION_STATE_OPEN, GTALK_FILE_COLLECTION_STATE_TERMINATED, GTALK_FILE_COLLECTION_STATE_CONNECTION_FAILED, GTALK_FILE_COLLECTION_STATE_ERROR, GTALK_FILE_COLLECTION_STATE_COMPLETED } GTalkFileCollectionState; #include "ft-channel.h" G_BEGIN_DECLS typedef struct _GTalkFileCollectionClass GTalkFileCollectionClass; GType gtalk_file_collection_get_type (void); /* TYPE MACROS */ #define GTALK_TYPE_FILE_COLLECTION \ (gtalk_file_collection_get_type ()) #define GTALK_FILE_COLLECTION(obj) \ (G_TYPE_CHECK_INSTANCE_CAST((obj), GTALK_TYPE_FILE_COLLECTION, \ GTalkFileCollection)) #define GTALK_FILE_COLLECTION_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST((klass), GTALK_TYPE_FILE_COLLECTION, \ GTalkFileCollectionClass)) #define GTALK_IS_FILE_COLLECTION(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE((obj), GTALK_TYPE_FILE_COLLECTION)) #define GTALK_IS_FILE_COLLECTION_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE((klass), GTALK_TYPE_FILE_COLLECTION)) #define GTALK_FILE_COLLECTION_GET_CLASS(obj) \ (G_TYPE_INSTANCE_GET_CLASS ((obj), GTALK_TYPE_FILE_COLLECTION, \ GTalkFileCollectionClass)) struct _GTalkFileCollectionClass { GObjectClass parent_class; }; typedef struct _GTalkFileCollectionPrivate GTalkFileCollectionPrivate; struct _GTalkFileCollection { GObject parent; GTalkFileCollectionPrivate *priv; }; GTalkFileCollection *gtalk_file_collection_new ( GabbleFileTransferChannel *channel, WockyJingleFactory *jingle_factory, TpHandle handle, const gchar *jid); GTalkFileCollection *gtalk_file_collection_new_from_session ( WockyJingleFactory *jingle_factory, WockyJingleSession *session); void gtalk_file_collection_add_channel (GTalkFileCollection *self, GabbleFileTransferChannel *channel); void gtalk_file_collection_initiate (GTalkFileCollection *self, GabbleFileTransferChannel *channel); void gtalk_file_collection_accept (GTalkFileCollection *self, GabbleFileTransferChannel *channel); void gtalk_file_collection_terminate (GTalkFileCollection *self, GabbleFileTransferChannel *channel); void gtalk_file_collection_completed (GTalkFileCollection *self, GabbleFileTransferChannel *channel); void gtalk_file_collection_block_reading (GTalkFileCollection *self, GabbleFileTransferChannel *channel, gboolean block); gboolean gtalk_file_collection_send_data (GTalkFileCollection *self, GabbleFileTransferChannel *channel, const gchar *data, guint length); void gtalk_file_collection_set_test_mode (void); #endif /* __GTALK_FILE_COLLECTION_H__ */ telepathy-gabble-0.18.2/src/gtalk-file-collection.c0000644000175000017500000015325412200204333022117 0ustar00smcvsmcv00000000000000/* * gtalk-file-collection.c - Source for GTalkFileCollection * * Copyright (C) 2010 Collabora Ltd. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 "config.h" #include "gtalk-file-collection.h" #include #include #include #include #define DEBUG_FLAG GABBLE_DEBUG_SHARE #include "debug.h" #include "jingle-share.h" #include "namespaces.h" #include "util.h" #include /* * This GTalk compatible file transfer protocol is a bit complicated, so here * is an explanation on how it works : * * A pseudo-good initial source of information is available here : * http://code.google.com/apis/talk/libjingle/file_share.html * * The current object interaction is like this : * * GabbleFileTransferChannelManager * | * | * | * | * * ref * GabbleFileTransferChannel * weakref * * * / \ * / \ * / \ * / \ * / \ * / \ * / \ * ref / \ * GTalkFileCollection \ * | \ * | \ * ref | \ * WockyJingleSession \ * | \ (one at a time) * | \ * ref | \ * GabbleJingleShare ------------------*- ShareChannel -------- NiceAgent * | | * | | * ref | ref | * GabbleTransportGoogle ----------------*- WockyJingleCandidate PseudoTCP * * The protocol works like this : * Once you receive an invitation, the manifest will contain a number of files * and folders. Some files might have image attributes (width/height) that help * specify that they are images. * If there are images in the invitation, a new ShareChannel gets created and * connectivity must be established. Then on that stream, an HTTP GET request * is sent for each image being transfered with the URL as : * GET /filename?width=X&height=Y * where X and Y are the thumbnail's requested width and height. * The peer should at this point scale down the image to the requested width and * height and send the thumbnail for showing the preview of the image in the FT * UI. * Once the invitation is accepted, a new ShareChannel is created, which will * cause a new NiceAgent to be created and connectivity to be established on that * ShareChannel. The resulting stream is then used as an HTTP server/client to * request files on it using the as prefix to the URL. * Simple files are being transferred normally, while directories will be * transferred as a tarball with 'chuncked' Transfer-Encoding since the resulting * size of the tarball isn't known in advance. * Once a file is completely transferred, then the next file is requested on the * same ShareChannel. If all files are transferred, then the info * action is being sent through the jingle signaling, and the session can then * be terminated safely. * * Since telepathy doesn't currently support image previews, so the 'preview' * ShareChannel is never created with gabble. * Also note that we only create one ShareChannel and we serialize the file * transfers one after the other, they do not each get one ShareChannel and they * cannot be downloaded in parallel. * */ static gboolean test_mode = FALSE; void gtalk_file_collection_set_test_mode (void) { test_mode = TRUE; } G_DEFINE_TYPE (GTalkFileCollection, gtalk_file_collection, G_TYPE_OBJECT); /* properties */ enum { PROP_TOKEN = 1, LAST_PROPERTY }; typedef enum { HTTP_SERVER_IDLE, HTTP_SERVER_HEADERS, HTTP_SERVER_SEND, HTTP_CLIENT_IDLE, HTTP_CLIENT_RECEIVE, HTTP_CLIENT_HEADERS, HTTP_CLIENT_CHUNK_SIZE, HTTP_CLIENT_CHUNK_END, HTTP_CLIENT_CHUNK_FINAL, HTTP_CLIENT_BODY, } HttpStatus; typedef struct { NiceAgent *agent; guint stream_id; guint component_id; gboolean agent_attached; GabbleJingleShare *content; guint share_channel_id; HttpStatus http_status; gchar *status_line; gboolean is_chunked; guint64 content_length; gchar *write_buffer; guint write_len; gchar *read_buffer; guint read_len; } ShareChannel; typedef enum { GTALK_FT_STATUS_PENDING, GTALK_FT_STATUS_INITIATED, GTALK_FT_STATUS_ACCEPTED, GTALK_FT_STATUS_TRANSFERRING, GTALK_FT_STATUS_WAITING, GTALK_FT_STATUS_TERMINATED } GtalkFtStatus; struct _GTalkFileCollectionPrivate { gboolean dispose_has_run; GtalkFtStatus status; /* GList of weakreffed GabbleFileTransferChannel */ GList *channels; /* GHashTable of GabbleFileTransferChannel => GINT_TO_POINTER (gboolean) */ /* the weakref to the channel here is held through the GList *channels */ GHashTable *channels_reading; /* GHashTable of GabbleFileTransferChannel => GINT_TO_POINTER (gboolean) */ /* the weakref to the channel here is held through the GList *channels */ GHashTable *channels_usable; GabbleFileTransferChannel *current_channel; WockyJingleFactory *jingle_factory; WockyJingleSession *jingle; /* ICE component id to jingle share channel association GINT_TO_POINTER (candidate->component) => g_slice_new (ShareChannel) */ GHashTable *share_channels; gboolean requested; gchar *token; }; static void free_share_channel (gpointer data); static void nice_data_received_cb (NiceAgent *agent, guint stream_id, guint component_id, guint len, gchar *buffer, gpointer user_data); static void set_current_channel (GTalkFileCollection *self, GabbleFileTransferChannel *channel); static void channel_disposed (gpointer data, GObject *where_the_object_was); static void gtalk_file_collection_init (GTalkFileCollection *self) { GTalkFileCollectionPrivate *priv = G_TYPE_INSTANCE_GET_PRIVATE (self, GTALK_TYPE_FILE_COLLECTION, GTalkFileCollectionPrivate); gchar buf[16]; guint32 *uint_buf = (guint32 *) buf; guint i; DEBUG ("GTalk file collection init called"); self->priv = priv; self->priv->status = GTALK_FT_STATUS_PENDING; self->priv->channels_reading = g_hash_table_new_full (NULL, NULL, NULL, NULL); self->priv->channels_usable = g_hash_table_new_full (NULL, NULL, NULL, NULL); self->priv->share_channels = g_hash_table_new_full (NULL, NULL, NULL, free_share_channel); for (i = 0; i < sizeof (buf); i++) buf[i] = g_random_int_range (0, 256); self->priv->token = g_strdup_printf ("%x%x%x%x", uint_buf[0], uint_buf[1], uint_buf[2], uint_buf[3]); /* FIXME: we should start creating a nice agent already and have it start the candidate gathering.. but we don't know which jingle-share transport channel name to assign it to... */ priv->dispose_has_run = FALSE; } static void gtalk_file_collection_dispose (GObject *object) { GTalkFileCollection *self = GTALK_FILE_COLLECTION (object); GList *i; if (self->priv->dispose_has_run) return; DEBUG ("dispose called"); self->priv->dispose_has_run = TRUE; if (self->priv->jingle != NULL) wocky_jingle_session_terminate (self->priv->jingle, WOCKY_JINGLE_REASON_UNKNOWN, NULL, NULL); tp_clear_object (&self->priv->jingle); set_current_channel (self, NULL); tp_clear_pointer (&self->priv->channels_reading, g_hash_table_unref); tp_clear_pointer (&self->priv->channels_usable, g_hash_table_unref); tp_clear_pointer (&self->priv->share_channels, g_hash_table_unref); for (i = self->priv->channels; i; i = i->next) { GabbleFileTransferChannel *channel = i->data; g_object_weak_unref (G_OBJECT (channel), channel_disposed, self); } g_list_free (self->priv->channels); g_free (self->priv->token); if (G_OBJECT_CLASS (gtalk_file_collection_parent_class)->dispose) G_OBJECT_CLASS (gtalk_file_collection_parent_class)->dispose (object); } static void gtalk_file_collection_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { GTalkFileCollection *self = GTALK_FILE_COLLECTION (object); switch (property_id) { case PROP_TOKEN: g_value_set_string (value, self->priv->token); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void gtalk_file_collection_class_init (GTalkFileCollectionClass *cls) { GObjectClass *object_class = G_OBJECT_CLASS (cls); g_type_class_add_private (cls, sizeof (GTalkFileCollectionPrivate)); object_class->get_property = gtalk_file_collection_get_property; object_class->dispose = gtalk_file_collection_dispose; g_object_class_install_property (object_class, PROP_TOKEN, g_param_spec_string ( "token", "Unique token identifiying the FileCollection", "Token identifying a collection of files", "", G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); } static ShareChannel * get_share_channel (GTalkFileCollection *self, NiceAgent *agent) { GHashTableIter iter; gpointer key, value; ShareChannel *ret = NULL; g_hash_table_iter_init (&iter, self->priv->share_channels); while (g_hash_table_iter_next (&iter, &key, &value)) { ShareChannel *share_channel = (ShareChannel *) value; if (share_channel->agent == agent) { ret = share_channel; break; } } return ret; } static GabbleFileTransferChannel * get_channel_by_filename (GTalkFileCollection *self, gchar *filename) { GList *i; for (i = self->priv->channels; i; i = i->next) { GabbleFileTransferChannel *channel = i->data; gchar *file = NULL; g_object_get (channel, "filename", &file, NULL); if (strcmp (file, filename) == 0) return channel; } return NULL; } static void set_current_channel (GTalkFileCollection *self, GabbleFileTransferChannel *channel) { self->priv->current_channel = channel; if (channel != NULL) { gboolean reading = FALSE; gabble_file_transfer_channel_gtalk_file_collection_state_changed ( channel, GTALK_FILE_COLLECTION_STATE_OPEN, FALSE); reading = GPOINTER_TO_INT (g_hash_table_lookup ( self->priv->channels_reading, channel)); gtalk_file_collection_block_reading (self, channel, !reading); } } static gboolean channel_exists (GTalkFileCollection * self, GabbleFileTransferChannel *channel) { GList *i; for (i = self->priv->channels; i; i = i->next) { if (channel == i->data) return TRUE; } return FALSE; } static void add_channel (GTalkFileCollection * self, GabbleFileTransferChannel *channel) { self->priv->channels = g_list_append (self->priv->channels, channel); g_hash_table_replace (self->priv->channels_reading, channel, GINT_TO_POINTER (FALSE)); g_object_weak_ref (G_OBJECT (channel), channel_disposed, self); } static void del_channel (GTalkFileCollection * self, GabbleFileTransferChannel *channel) { g_return_if_fail (channel_exists (self, channel)); self->priv->channels = g_list_remove (self->priv->channels, channel); g_hash_table_remove (self->priv->channels_reading, channel); g_hash_table_remove (self->priv->channels_usable, channel); g_object_weak_unref (G_OBJECT (channel), channel_disposed, self); if (self->priv->current_channel == channel) set_current_channel (self, NULL); } static void jingle_session_state_changed_cb (WockyJingleSession *session, GParamSpec *arg1, GTalkFileCollection *self) { WockyJingleState state; GList *i; DEBUG ("called"); g_object_get (session, "state", &state, NULL); switch (state) { case WOCKY_JINGLE_STATE_INVALID: case WOCKY_JINGLE_STATE_PENDING_CREATED: break; case WOCKY_JINGLE_STATE_PENDING_INITIATE_SENT: case WOCKY_JINGLE_STATE_PENDING_INITIATED: for (i = self->priv->channels; i;) { GabbleFileTransferChannel *channel = i->data; i = i->next; gabble_file_transfer_channel_gtalk_file_collection_state_changed ( channel, GTALK_FILE_COLLECTION_STATE_PENDING, FALSE); } break; case WOCKY_JINGLE_STATE_PENDING_ACCEPT_SENT: case WOCKY_JINGLE_STATE_ACTIVE: /* Do not set the channels to OPEN unless we're ready to send/receive data from them */ if (self->priv->status == GTALK_FT_STATUS_INITIATED) self->priv->status = GTALK_FT_STATUS_ACCEPTED; for (i = self->priv->channels; i;) { GabbleFileTransferChannel *channel = i->data; gboolean usable; i = i->next; usable = GPOINTER_TO_INT (g_hash_table_lookup ( self->priv->channels_usable, channel)); if (usable) gabble_file_transfer_channel_gtalk_file_collection_state_changed ( channel, GTALK_FILE_COLLECTION_STATE_ACCEPTED, FALSE); } break; case WOCKY_JINGLE_STATE_ENDED: /* Do nothing, let the terminated signal set the correct state depending on the termination reason */ default: break; } } static void jingle_session_terminated_cb (WockyJingleSession *session, gboolean local_terminator, WockyJingleReason reason, const gchar *text, gpointer user_data) { GTalkFileCollection *self = GTALK_FILE_COLLECTION (user_data); GList *i; g_assert (session == self->priv->jingle); self->priv->status = GTALK_FT_STATUS_TERMINATED; for (i = self->priv->channels; i;) { GabbleFileTransferChannel *channel = i->data; i = i->next; gabble_file_transfer_channel_gtalk_file_collection_state_changed ( channel, GTALK_FILE_COLLECTION_STATE_TERMINATED, local_terminator); } } static void content_new_remote_candidates_cb (WockyJingleContent *content, GList *clist, gpointer user_data) { GTalkFileCollection *self = GTALK_FILE_COLLECTION (user_data); GList *li; DEBUG ("Got new remote candidates : %d", g_list_length (clist)); for (li = clist; li; li = li->next) { WockyJingleCandidate *candidate = li->data; NiceCandidate *cand = NULL; ShareChannel *share_channel = NULL; GSList *candidates = NULL; if (candidate->protocol != WOCKY_JINGLE_TRANSPORT_PROTOCOL_UDP) { DEBUG ("Ignoring candidate %s because of non-UDP protocol : %d", candidate->username, candidate->protocol); continue; } share_channel = g_hash_table_lookup (self->priv->share_channels, GINT_TO_POINTER (candidate->component)); if (share_channel == NULL) { DEBUG ("Ignoring candidate %s because of unknown component id %d", candidate->id, candidate->component); continue; } cand = nice_candidate_new ( candidate->type == WOCKY_JINGLE_CANDIDATE_TYPE_LOCAL? NICE_CANDIDATE_TYPE_HOST: candidate->type == WOCKY_JINGLE_CANDIDATE_TYPE_STUN? NICE_CANDIDATE_TYPE_SERVER_REFLEXIVE: NICE_CANDIDATE_TYPE_RELAYED); cand->transport = WOCKY_JINGLE_TRANSPORT_PROTOCOL_UDP; nice_address_init (&cand->addr); if (!nice_address_set_from_string (&cand->addr, candidate->address)) { DEBUG ("invalid address '%s' in candidate, skipping", candidate->address ? candidate->address : ""); nice_candidate_free (cand); continue; } nice_address_set_port (&cand->addr, candidate->port); cand->priority = candidate->preference * 1000; cand->stream_id = share_channel->stream_id; cand->component_id = share_channel->component_id; /* if (c->id == NULL) candidate_id = g_strdup_printf ("R%d", ++priv->remote_candidate_count); else candidate_id = c->id;*/ if (candidate->id != NULL) strncpy (cand->foundation, candidate->id, NICE_CANDIDATE_MAX_FOUNDATION - 1); else if (candidate->username != NULL) strncpy (cand->foundation, candidate->username, NICE_CANDIDATE_MAX_FOUNDATION - 1); cand->username = g_strdup (candidate->username?candidate->username:""); cand->password = g_strdup (candidate->password?candidate->password:""); candidates = g_slist_append (candidates, cand); nice_agent_set_remote_candidates (share_channel->agent, share_channel->stream_id, share_channel->component_id, candidates); g_slist_foreach (candidates, (GFunc)nice_candidate_free, NULL); g_slist_free (candidates); } } static void nice_candidate_gathering_done (NiceAgent *agent, guint stream_id, gpointer user_data) { GTalkFileCollection *self = GTALK_FILE_COLLECTION (user_data); ShareChannel *share_channel = get_share_channel (self, agent); WockyJingleContent *content = WOCKY_JINGLE_CONTENT (share_channel->content); GList *candidates = NULL; GList *remote_candidates = NULL; GSList *local_candidates; GSList *li; DEBUG ("libnice candidate gathering done!!!!"); /* Send remote candidates to libnice and listen to new signal */ remote_candidates = wocky_jingle_content_get_remote_candidates (content); content_new_remote_candidates_cb (content, remote_candidates, self); gabble_signal_connect_weak (content, "new-candidates", (GCallback) content_new_remote_candidates_cb, G_OBJECT (self)); /* Send gathered local candidates to the content */ local_candidates = nice_agent_get_local_candidates (agent, stream_id, share_channel->component_id); for (li = local_candidates; li; li = li->next) { NiceCandidate *cand = li->data; WockyJingleCandidate *candidate; gchar ip[NICE_ADDRESS_STRING_LEN]; nice_address_to_string (&cand->addr, ip); candidate = wocky_jingle_candidate_new ( /* protocol */ cand->transport == NICE_CANDIDATE_TRANSPORT_UDP? WOCKY_JINGLE_TRANSPORT_PROTOCOL_UDP: WOCKY_JINGLE_TRANSPORT_PROTOCOL_TCP, /* candidate type */ cand->type == NICE_CANDIDATE_TYPE_HOST? WOCKY_JINGLE_CANDIDATE_TYPE_LOCAL: cand->type == NICE_CANDIDATE_TYPE_RELAYED? WOCKY_JINGLE_CANDIDATE_TYPE_RELAY: WOCKY_JINGLE_CANDIDATE_TYPE_STUN, /* id */ cand->foundation, /* component */ share_channel->share_channel_id, /* address */ ip, /* port */ nice_address_get_port (&cand->addr), /* generation */ 0, /* preference */ (gfloat) cand->priority / 1000.0, /* username */ cand->username?cand->username:"", /* password */ cand->password?cand->password:"", /* network */ 0); candidates = g_list_prepend (candidates, candidate); } wocky_jingle_content_add_candidates (content, candidates); } static void nice_component_state_changed (NiceAgent *agent, guint stream_id, guint component_id, guint state, gpointer user_data) { GTalkFileCollection *self = GTALK_FILE_COLLECTION (user_data); ShareChannel *share_channel = get_share_channel (self, agent); WockyJingleContent *content = WOCKY_JINGLE_CONTENT (share_channel->content); WockyJingleTransportState ts = WOCKY_JINGLE_TRANSPORT_STATE_DISCONNECTED; DEBUG ("libnice component state changed %d!!!!", state); switch (state) { case NICE_COMPONENT_STATE_DISCONNECTED: case NICE_COMPONENT_STATE_GATHERING: ts = WOCKY_JINGLE_TRANSPORT_STATE_DISCONNECTED; break; case NICE_COMPONENT_STATE_CONNECTING: ts = WOCKY_JINGLE_TRANSPORT_STATE_CONNECTING; break; case NICE_COMPONENT_STATE_CONNECTED: case NICE_COMPONENT_STATE_READY: ts = WOCKY_JINGLE_TRANSPORT_STATE_CONNECTED; break; case NICE_COMPONENT_STATE_FAILED: { GList *i; for (i = self->priv->channels; i;) { GabbleFileTransferChannel *channel = i->data; i = i->next; gabble_file_transfer_channel_gtalk_file_collection_state_changed ( channel, GTALK_FILE_COLLECTION_STATE_CONNECTION_FAILED, TRUE); } /* return because we don't want to use the content after it has been destroyed.. */ return; } } wocky_jingle_content_set_transport_state (content, ts); } static void get_next_manifest_entry (GTalkFileCollection *self, ShareChannel *share_channel, gboolean error) { GabbleJingleShareManifest *manifest = NULL; GabbleJingleShareManifestEntry *entry = NULL; GabbleFileTransferChannel *channel = NULL; GList *i; DEBUG ("called"); if (self->priv->current_channel != NULL) { if (g_list_length (self->priv->channels) == 1) { WockyJingleContent *content = \ WOCKY_JINGLE_CONTENT (share_channel->content); DEBUG ("Received all the files. Transfer is complete"); wocky_jingle_content_send_complete (content); } g_hash_table_replace (self->priv->channels_usable, self->priv->current_channel, GINT_TO_POINTER (FALSE)); gabble_file_transfer_channel_gtalk_file_collection_state_changed ( self->priv->current_channel, error ? GTALK_FILE_COLLECTION_STATE_ERROR: GTALK_FILE_COLLECTION_STATE_COMPLETED, FALSE); set_current_channel (self, NULL); } manifest = gabble_jingle_share_get_manifest (share_channel->content); for (i = manifest->entries; i; i = i->next) { gchar *filename = NULL; gboolean usable; entry = i->data; filename = g_strdup_printf ("%s%s", entry->name, (entry->folder ? ".tar" : "")); channel = get_channel_by_filename (self, filename); g_free (filename); if (channel != NULL) { usable = GPOINTER_TO_INT (g_hash_table_lookup ( self->priv->channels_usable, channel)); if (usable) break; } entry = NULL; } self->priv->status = GTALK_FT_STATUS_WAITING; if (entry != NULL) { gchar *buffer = NULL; gchar *source_url = manifest->source_url; guint url_len = (source_url != NULL? strlen (source_url) : 0); gchar *separator = ""; gchar *filename = NULL; if (source_url != NULL && source_url[url_len -1] != '/') separator = "/"; self->priv->status = GTALK_FT_STATUS_TRANSFERRING; filename = g_uri_escape_string (entry->name, NULL, TRUE); /* The session initiator will always be the full JID of the peer */ buffer = g_strdup_printf ("GET %s%s%s HTTP/1.1\r\n" "Connection: Keep-Alive\r\n" "Content-Length: 0\r\n" "Host: %s:0\r\n" /* e.g. alice@example.com/Empathy:0 */ "User-Agent: %s\r\n\r\n", (source_url != NULL ? source_url : ""), separator, filename, wocky_jingle_session_get_initiator (self->priv->jingle), PACKAGE_STRING); g_free (filename); /* FIXME: check for success */ nice_agent_send (share_channel->agent, share_channel->stream_id, share_channel->component_id, strlen (buffer), buffer); g_free (buffer); share_channel->http_status = HTTP_CLIENT_RECEIVE; /* Block or unblock accordingly */ set_current_channel (self, channel); } } static void nice_component_writable (NiceAgent *agent, guint stream_id, guint component_id, gpointer user_data) { GTalkFileCollection *self = GTALK_FILE_COLLECTION (user_data); ShareChannel *share_channel = get_share_channel (self, agent); if (share_channel->http_status == HTTP_CLIENT_IDLE) { get_next_manifest_entry (self, share_channel, FALSE); } else if (share_channel->http_status == HTTP_SERVER_SEND) { if (self->priv->current_channel == NULL) { GList *i; DEBUG ("Unexpected current_channel == NULL!"); for (i = self->priv->channels; i;) { GabbleFileTransferChannel *channel = i->data; i = i->next; gabble_file_transfer_channel_gtalk_file_collection_state_changed ( channel, GTALK_FILE_COLLECTION_STATE_ERROR, FALSE); } return; } gabble_file_transfer_channel_gtalk_file_collection_write_blocked ( self->priv->current_channel, FALSE); if (share_channel->write_buffer != NULL) { gint ret = nice_agent_send (agent, stream_id, component_id, share_channel->write_len, share_channel->write_buffer); if (ret < 0 || (guint) ret < share_channel->write_len) { gchar *to_free = share_channel->write_buffer; if (ret < 0) ret = 0; share_channel->write_buffer = g_memdup ( share_channel->write_buffer + ret, share_channel->write_len - ret); share_channel->write_len = share_channel->write_len - ret; g_free (to_free); gabble_file_transfer_channel_gtalk_file_collection_write_blocked ( self->priv->current_channel, TRUE); } else { g_free (share_channel->write_buffer); share_channel->write_buffer = NULL; share_channel->write_len = 0; } } } } typedef struct { union { gpointer ptr; GTalkFileCollection *self; } u; ShareChannel *share_channel; } GoogleRelaySessionData; static const NiceRelayType relay_type_map[] = { /* WOCKY_JINGLE_RELAY_TYPE_UDP */ NICE_RELAY_TYPE_TURN_UDP, /* WOCKY_JINGLE_RELAY_TYPE_TCP */ NICE_RELAY_TYPE_TURN_TCP, /* WOCKY_JINGLE_RELAY_TYPE_TLS */ NICE_RELAY_TYPE_TURN_TLS, }; static void set_relay_info (gpointer item, gpointer user_data) { GoogleRelaySessionData *data = user_data; WockyJingleRelay *relay = item; NiceRelayType type; g_return_if_fail (relay->type < WOCKY_N_JINGLE_RELAY_TYPES); type = relay_type_map[relay->type]; nice_agent_set_relay_info (data->share_channel->agent, data->share_channel->stream_id, data->share_channel->component_id, relay->ip, relay->port, relay->username, relay->password, type); } static void google_relay_session_cb (GPtrArray *relays, gpointer user_data) { GoogleRelaySessionData *data = user_data; if (data->u.self == NULL) { DEBUG ("Received relay session callback but self got destroyed"); g_slice_free (GoogleRelaySessionData, data); return; } if (relays != NULL) g_ptr_array_foreach (relays, set_relay_info, user_data); nice_agent_gather_candidates (data->share_channel->agent, data->share_channel->stream_id); g_object_remove_weak_pointer (G_OBJECT (data->u.self), &data->u.ptr); g_slice_free (GoogleRelaySessionData, data); } static void content_new_share_channel_cb (WockyJingleContent *content, const gchar *name, guint share_channel_id, gpointer user_data) { GTalkFileCollection *self = GTALK_FILE_COLLECTION (user_data); ShareChannel *share_channel = g_slice_new0 (ShareChannel); NiceAgent *agent = nice_agent_new_reliable (g_main_context_default (), NICE_COMPATIBILITY_GOOGLE); guint stream_id = nice_agent_add_stream (agent, 1); GList *stun_servers; GoogleRelaySessionData *relay_data = NULL; DEBUG ("New Share channel %s was created and linked to id %d", name, share_channel_id); if (test_mode) g_object_set (agent, "upnp", FALSE, NULL); share_channel->agent = agent; share_channel->stream_id = stream_id; share_channel->component_id = NICE_COMPONENT_TYPE_RTP; share_channel->content = GABBLE_JINGLE_SHARE (content); share_channel->share_channel_id = share_channel_id; if (self->priv->requested) share_channel->http_status = HTTP_SERVER_IDLE; else share_channel->http_status = HTTP_CLIENT_IDLE; gabble_signal_connect_weak (agent, "candidate-gathering-done", G_CALLBACK (nice_candidate_gathering_done), G_OBJECT (self)); gabble_signal_connect_weak (agent, "component-state-changed", G_CALLBACK (nice_component_state_changed), G_OBJECT (self)); gabble_signal_connect_weak (agent, "reliable-transport-writable", G_CALLBACK (nice_component_writable), G_OBJECT (self)); /* Add the agent to the hash table before gathering candidates in case the gathering finishes synchronously, and the callback tries to add local candidates to the content, it needs to find the share channel id.. */ g_hash_table_insert (self->priv->share_channels, GINT_TO_POINTER (share_channel_id), share_channel); share_channel->agent_attached = TRUE; nice_agent_attach_recv (agent, stream_id, share_channel->component_id, g_main_context_default (), nice_data_received_cb, self); stun_servers = wocky_jingle_info_get_stun_servers ( wocky_jingle_factory_get_jingle_info (self->priv->jingle_factory)); if (stun_servers != NULL) { WockyStunServer *stun_server = stun_servers->data; g_object_set (agent, "stun-server", stun_server->address, "stun-server-port", (guint) stun_server->port, NULL); g_list_free (stun_servers); } relay_data = g_slice_new0 (GoogleRelaySessionData); relay_data->u.self = self; relay_data->share_channel = share_channel; g_object_add_weak_pointer (G_OBJECT (relay_data->u.self), &relay_data->u.ptr); wocky_jingle_info_create_google_relay_session ( wocky_jingle_factory_get_jingle_info (self->priv->jingle_factory), 1, google_relay_session_cb, relay_data); } static void content_completed (WockyJingleContent *content, gpointer user_data) { GTalkFileCollection *self = GTALK_FILE_COLLECTION (user_data); GList *i; DEBUG ("Received content completed"); for (i = self->priv->channels; i;) { GabbleFileTransferChannel *channel = i->data; i = i->next; gabble_file_transfer_channel_gtalk_file_collection_state_changed ( channel, GTALK_FILE_COLLECTION_STATE_COMPLETED, FALSE); } } static void free_share_channel (gpointer data) { ShareChannel *share_channel = (ShareChannel *) data; DEBUG ("Freeing jingle Share channel"); tp_clear_pointer (&share_channel->write_buffer, g_free); tp_clear_pointer (&share_channel->read_buffer, g_free); g_object_unref (share_channel->agent); g_slice_free (ShareChannel, share_channel); } /* If buffer contains a line ending, 0-terminate the first line and * return a pointer to the beginning of the next line. Otherwise * return NULL. */ static gchar * http_read_line (gchar *buffer, guint len) { gchar *p = memchr (buffer, '\n', len); if (p != NULL) { *p = 0; if (p > buffer && *(p-1) == '\r') *(p-1) = '\0'; p++; } return p; } static guint http_data_received (GTalkFileCollection *self, ShareChannel *share_channel, gchar *buffer, guint len) { switch (share_channel->http_status) { case HTTP_SERVER_IDLE: { gchar *headers = http_read_line (buffer, len); if (headers == NULL) return 0; share_channel->http_status = HTTP_SERVER_HEADERS; share_channel->status_line = g_strdup (buffer); if (self->priv->current_channel != NULL) { DEBUG ("Received status line with current channel set"); gabble_file_transfer_channel_gtalk_file_collection_state_changed ( self->priv->current_channel, GTALK_FILE_COLLECTION_STATE_COMPLETED, FALSE); set_current_channel (self, NULL); } return headers - buffer; } break; case HTTP_SERVER_HEADERS: { gchar *line = buffer; gchar *next_line = http_read_line (buffer, len); if (next_line == NULL) return 0; DEBUG ("Found server headers line (%" G_GSIZE_FORMAT ") : %s", strlen (line), line); /* FIXME: how about content-length and an actual body ? */ if (line[0] == '\0') { gchar *response = NULL; gchar *get_line = NULL; GabbleJingleShareManifest *manifest = NULL; gchar *source_url = NULL; guint url_len; gchar *separator = ""; gchar *filename = NULL; GabbleFileTransferChannel *channel = NULL; g_assert (self->priv->current_channel == NULL); DEBUG ("Found empty line, received request : %s ", share_channel->status_line); manifest = gabble_jingle_share_get_manifest ( share_channel->content); source_url = manifest->source_url; url_len = (source_url != NULL? strlen (source_url) : 0); if (source_url != NULL && source_url[url_len -1] != '/') separator = "/"; get_line = g_strdup_printf ("GET %s%s%%s HTTP/1.1", (source_url != NULL ? source_url : ""), separator); filename = g_malloc (strlen (share_channel->status_line)); if (sscanf (share_channel->status_line, get_line, filename) == 1) { gchar *unescaped = g_uri_unescape_string (filename, NULL); g_free (filename); filename = unescaped; channel = get_channel_by_filename (self, filename); } if (channel != NULL) { guint64 size; g_object_get (channel, "size", &size, NULL); DEBUG ("Found valid filename, result : 200"); share_channel->http_status = HTTP_SERVER_SEND; response = g_strdup_printf ("HTTP/1.1 200\r\n" "Connection: Keep-Alive\r\n" "Content-Length: %" G_GUINT64_FORMAT "\r\n" "Content-Type: application/octet-stream\r\n\r\n", size); } else { DEBUG ("Unable to find valid filename (%s), result : 404", (filename != NULL? filename : "")); share_channel->http_status = HTTP_SERVER_IDLE; response = g_strdup_printf ("HTTP/1.1 404\r\n" "Connection: Keep-Alive\r\n" "Content-Length: 0\r\n\r\n"); } /* FIXME: check for success of nice_agent_send */ nice_agent_send (share_channel->agent, share_channel->stream_id, share_channel->component_id, strlen (response), response); g_free (response); g_free (filename); g_free (get_line); /* Now that we sent our response, we can assign the current channel which sets it to OPEN (if non NULL) so data can start flowing */ self->priv->status = GTALK_FT_STATUS_TRANSFERRING; set_current_channel (self, channel); } return next_line - buffer; } break; case HTTP_SERVER_SEND: DEBUG ("received data when we're supposed to be sending data.. " "not supposed to happen"); break; case HTTP_CLIENT_IDLE: DEBUG ("received data when we're supposed to be sending the GET.. " "not supposed to happen"); break; case HTTP_CLIENT_RECEIVE: { gchar *headers = http_read_line (buffer, len); if (headers == NULL) return 0; share_channel->http_status = HTTP_CLIENT_HEADERS; share_channel->status_line = g_strdup (buffer); return headers - buffer; } case HTTP_CLIENT_HEADERS: { gchar *line = buffer; gchar *next_line = http_read_line (buffer, len); if (next_line == NULL) return 0; DEBUG ("Found client headers line (%" G_GSIZE_FORMAT ") : %s", strlen (line), line); if (line[0] == '\0') { DEBUG ("Found empty line, GET response : %s", share_channel->status_line); if (g_str_has_prefix (share_channel->status_line, "HTTP/1.1 200")) { if (share_channel->is_chunked) { share_channel->http_status = HTTP_CLIENT_CHUNK_SIZE; } else { share_channel->http_status = HTTP_CLIENT_BODY; if (share_channel->content_length == 0) get_next_manifest_entry (self, share_channel, FALSE); } } else { /* We expect content-length to be 0 and no chunks for non-200 statuses (404 error) */ if (share_channel->is_chunked || share_channel->content_length != 0) { GList *i; DEBUG ("Unexpected body for non-200 error!"); for (i = self->priv->channels; i;) { GabbleFileTransferChannel *channel = i->data; i = i->next; gabble_file_transfer_channel_gtalk_file_collection_state_changed ( channel, GTALK_FILE_COLLECTION_STATE_ERROR, FALSE); } } else { get_next_manifest_entry (self, share_channel, TRUE); } } } else if (!g_ascii_strncasecmp (line, "Content-Length: ", 16)) { share_channel->is_chunked = FALSE; /* Check strtoull read all the length */ share_channel->content_length = g_ascii_strtoull (line + 16, NULL, 10); DEBUG ("Found data length : %" G_GUINT64_FORMAT, share_channel->content_length); } else if (!g_ascii_strcasecmp (line, "Transfer-Encoding: chunked")) { share_channel->is_chunked = TRUE; share_channel->content_length = 0; DEBUG ("Found file is chunked"); } return next_line - buffer; } break; case HTTP_CLIENT_CHUNK_SIZE: { gchar *line = buffer; gchar *next_line = http_read_line (buffer, len); if (next_line == NULL) return 0; /* FIXME : check validity of strtoul */ share_channel->content_length = strtoul (line, NULL, 16); if (share_channel->content_length > 0) share_channel->http_status = HTTP_CLIENT_BODY; else share_channel->http_status = HTTP_CLIENT_CHUNK_FINAL; return next_line - buffer; } break; case HTTP_CLIENT_BODY: { guint consumed = 0; if (len >= share_channel->content_length) { if (self->priv->current_channel == NULL) { GList *i; DEBUG ("Unexpected current_channel == NULL!"); for (i = self->priv->channels; i;) { GabbleFileTransferChannel *channel = i->data; i = i->next; gabble_file_transfer_channel_gtalk_file_collection_state_changed ( channel, GTALK_FILE_COLLECTION_STATE_ERROR, FALSE); } /* FIXME: Who knows what might happen here if we got destroyed It shouldn't crash since our object isn't dereferences anymore, but.. */ return len; } consumed = share_channel->content_length; gabble_file_transfer_channel_gtalk_file_collection_data_received ( self->priv->current_channel, buffer, consumed); share_channel->content_length = 0; if (share_channel->is_chunked) share_channel->http_status = HTTP_CLIENT_CHUNK_END; else get_next_manifest_entry (self, share_channel, FALSE); } else { consumed = len; share_channel->content_length -= len; gabble_file_transfer_channel_gtalk_file_collection_data_received ( self->priv->current_channel, buffer, consumed); } return consumed; } break; case HTTP_CLIENT_CHUNK_END: { gchar *chunk = http_read_line (buffer, len); if (chunk == NULL) return 0; share_channel->http_status = HTTP_CLIENT_CHUNK_SIZE; return chunk - buffer; } break; case HTTP_CLIENT_CHUNK_FINAL: { gchar *end = http_read_line (buffer, len); if (end == NULL) return 0; share_channel->http_status = HTTP_CLIENT_IDLE; get_next_manifest_entry (self, share_channel, FALSE); return end - buffer; } break; } return 0; } static void nice_data_received_cb (NiceAgent *agent, guint stream_id, guint component_id, guint len, gchar *buffer, gpointer user_data) { GTalkFileCollection *self = GTALK_FILE_COLLECTION (user_data); ShareChannel *share_channel = get_share_channel (self, agent); gchar *free_buffer = NULL; if (share_channel->read_buffer != NULL) { gchar *tmp = g_malloc (share_channel->read_len + len); memcpy (tmp, share_channel->read_buffer, share_channel->read_len); memcpy (tmp + share_channel->read_len, buffer, len); free_buffer = buffer = tmp; len += share_channel->read_len; g_free (share_channel->read_buffer); share_channel->read_buffer = NULL; share_channel->read_len = 0; } while (len > 0) { guint consumed = http_data_received (self, share_channel, buffer, len); if (consumed == 0) { share_channel->read_buffer = g_memdup (buffer, len); share_channel->read_len = len; break; } else { /* we assume http_data_received never returns consumed > len */ g_assert (consumed <= len); len -= consumed; buffer += consumed; } } if (free_buffer != NULL) g_free (free_buffer); } static void set_session (GTalkFileCollection * self, WockyJingleSession *session, WockyJingleContent *content) { self->priv->jingle = g_object_ref (session); gabble_signal_connect_weak (session, "notify::state", (GCallback) jingle_session_state_changed_cb, G_OBJECT (self)); gabble_signal_connect_weak (session, "terminated", (GCallback) jingle_session_terminated_cb, G_OBJECT (self)); gabble_signal_connect_weak (content, "new-share-channel", (GCallback) content_new_share_channel_cb, G_OBJECT (self)); gabble_signal_connect_weak (content, "completed", (GCallback) content_completed, G_OBJECT (self)); self->priv->status = GTALK_FT_STATUS_PENDING; } GTalkFileCollection * gtalk_file_collection_new (GabbleFileTransferChannel *channel, WockyJingleFactory *jingle_factory, TpHandle handle, const gchar *jid) { GTalkFileCollection * self = g_object_new (GTALK_TYPE_FILE_COLLECTION, NULL); WockyJingleSession *session = NULL; WockyJingleContent *content = NULL; gchar *filename; guint64 size; self->priv->jingle_factory = jingle_factory; self->priv->requested = TRUE; session = wocky_jingle_factory_create_session (jingle_factory, jid, WOCKY_JINGLE_DIALECT_GTALK4, FALSE); if (session == NULL) { g_object_unref (self); return NULL; } content = wocky_jingle_session_add_content (session, WOCKY_JINGLE_MEDIA_TYPE_NONE, WOCKY_JINGLE_CONTENT_SENDERS_BOTH, "share", NS_GOOGLE_SESSION_SHARE, NS_GOOGLE_TRANSPORT_P2P); if (content == NULL) { g_object_unref (self); g_object_unref (session); return NULL; } g_object_get (channel, "filename", &filename, "size", &size, NULL); g_object_set (content, "filename", filename, "filesize", size, NULL); set_session (self, session, content); add_channel (self, channel); return self; } GTalkFileCollection * gtalk_file_collection_new_from_session (WockyJingleFactory *jingle_factory, WockyJingleSession *session) { GTalkFileCollection * self = NULL; WockyJingleContent *content = NULL; GList *cs; if (wocky_jingle_session_get_content_type (session) != GABBLE_TYPE_JINGLE_SHARE) return NULL; cs = wocky_jingle_session_get_contents (session); if (cs != NULL) { content = WOCKY_JINGLE_CONTENT (cs->data); g_list_free (cs); } if (content == NULL) return NULL; self = g_object_new (GTALK_TYPE_FILE_COLLECTION, NULL); self->priv->jingle_factory = jingle_factory; self->priv->requested = FALSE; set_session (self, session, content); return self; } void gtalk_file_collection_add_channel (GTalkFileCollection *self, GabbleFileTransferChannel *channel) { add_channel (self, channel); } void gtalk_file_collection_initiate (GTalkFileCollection *self, GabbleFileTransferChannel * channel) { if (channel_exists (self, channel)) { g_hash_table_replace (self->priv->channels_reading, channel, GINT_TO_POINTER (TRUE)); g_hash_table_replace (self->priv->channels_usable, channel, GINT_TO_POINTER (TRUE)); } if (self->priv->status == GTALK_FT_STATUS_PENDING) { wocky_jingle_session_accept (self->priv->jingle); self->priv->status = GTALK_FT_STATUS_INITIATED; } else { gabble_file_transfer_channel_gtalk_file_collection_state_changed ( channel, GTALK_FILE_COLLECTION_STATE_ACCEPTED, FALSE); } } void gtalk_file_collection_accept (GTalkFileCollection *self, GabbleFileTransferChannel * channel) { GList *cs = wocky_jingle_session_get_contents (self->priv->jingle); DEBUG ("called"); if (channel_exists (self, channel)) { g_hash_table_replace (self->priv->channels_usable, channel, GINT_TO_POINTER (TRUE)); } if (self->priv->status == GTALK_FT_STATUS_PENDING) { if (cs != NULL) { WockyJingleContent *content = WOCKY_JINGLE_CONTENT (cs->data); guint initial_id = 0; guint share_channel_id; wocky_jingle_session_accept (self->priv->jingle); self->priv->status = GTALK_FT_STATUS_ACCEPTED; /* The new-share-channel signal will take care of the rest.. */ do { gchar *share_channel_name = NULL; share_channel_name = g_strdup_printf ("gabble-%d", ++initial_id); share_channel_id = wocky_jingle_content_create_share_channel ( content, share_channel_name); g_free (share_channel_name); } while (share_channel_id == 0 && initial_id < 10); /* FIXME: not assert but actually cancel the FT? */ g_assert (share_channel_id > 0); g_list_free (cs); } } else { gabble_file_transfer_channel_gtalk_file_collection_state_changed ( channel, GTALK_FILE_COLLECTION_STATE_ACCEPTED, FALSE); } if (self->priv->status == GTALK_FT_STATUS_WAITING) { /* FIXME: this and other lookups should not check for channel '1' */ ShareChannel *share_channel = g_hash_table_lookup ( self->priv->share_channels, GINT_TO_POINTER (1)); get_next_manifest_entry (self, share_channel, FALSE); } } gboolean gtalk_file_collection_send_data (GTalkFileCollection *self, GabbleFileTransferChannel *channel, const gchar *data, guint length) { ShareChannel *share_channel = g_hash_table_lookup (self->priv->share_channels, GINT_TO_POINTER (1)); gint ret; g_return_val_if_fail (self->priv->current_channel == channel, FALSE); ret = nice_agent_send (share_channel->agent, share_channel->stream_id, share_channel->component_id, length, data); if (ret < 0 || (guint) ret < length) { if (ret < 0) ret = 0; share_channel->write_buffer = g_memdup (data + ret, length - ret); share_channel->write_len = length - ret; gabble_file_transfer_channel_gtalk_file_collection_write_blocked (channel, TRUE); } return TRUE; } void gtalk_file_collection_block_reading (GTalkFileCollection *self, GabbleFileTransferChannel *channel, gboolean block) { ShareChannel *share_channel = g_hash_table_lookup (self->priv->share_channels, GINT_TO_POINTER (1)); g_assert (channel_exists (self, channel)); if (self->priv->status != GTALK_FT_STATUS_TRANSFERRING) DEBUG ("Channel %p %s reading ", channel, block?"blocks":"unblocks" ); g_hash_table_replace (self->priv->channels_reading, channel, GINT_TO_POINTER (!block)); if (channel == self->priv->current_channel) { if (block) { if (share_channel && share_channel->agent_attached) { nice_agent_attach_recv (share_channel->agent, share_channel->stream_id, share_channel->component_id, NULL, NULL, NULL); share_channel->agent_attached = FALSE; } } else { if (share_channel && !share_channel->agent_attached) { share_channel->agent_attached = TRUE; nice_agent_attach_recv (share_channel->agent, share_channel->stream_id, share_channel->component_id, g_main_context_default (), nice_data_received_cb, self); } } } } void gtalk_file_collection_completed (GTalkFileCollection *self, GabbleFileTransferChannel * channel) { ShareChannel *share_channel = g_hash_table_lookup (self->priv->share_channels, GINT_TO_POINTER (1)); DEBUG ("called"); g_return_if_fail (self->priv->current_channel == channel); /* We shouldn't set the FT to completed until we receive the 'complete' info or we receive a new HTTP request otherwise we might terminate the session and cause a race condition where the peer thinks it got canceled before it completed. */ share_channel->http_status = HTTP_SERVER_IDLE; self->priv->status = GTALK_FT_STATUS_WAITING; } void gtalk_file_collection_terminate (GTalkFileCollection *self, GabbleFileTransferChannel * channel) { DEBUG ("called"); if (!channel_exists (self, channel)) return; if (self->priv->current_channel == channel) { del_channel (self, channel); /* Cancel the whole thing if we terminate the current channel */ if (self->priv->status == GTALK_FT_STATUS_TRANSFERRING) { /* The terminate should call our terminated_cb callback which should terminate all channels which should unref us which will unref the jingle session */ self->priv->status = GTALK_FT_STATUS_TERMINATED; wocky_jingle_session_terminate (self->priv->jingle, WOCKY_JINGLE_REASON_UNKNOWN, NULL, NULL); return; } return; } else { del_channel (self, channel); /* If this was the last channel, it will cause it to unref us and the dispose will be called, which will call wocky_jingle_session_terminate */ gabble_file_transfer_channel_gtalk_file_collection_state_changed (channel, GTALK_FILE_COLLECTION_STATE_TERMINATED, TRUE); } } static void channel_disposed (gpointer data, GObject *object) { GTalkFileCollection *self = data; GabbleFileTransferChannel *channel = (GabbleFileTransferChannel *) object; DEBUG ("channel %p got destroyed", channel); g_return_if_fail (channel_exists (self, channel)); if (self->priv->current_channel == channel) { del_channel (self, channel); /* Cancel the whole thing if we terminate the current channel */ if (self->priv->status == GTALK_FT_STATUS_TRANSFERRING) { /* The terminate should call our terminated_cb callback which should terminate all channels which should unref us which will unref the jingle session */ self->priv->status = GTALK_FT_STATUS_TERMINATED; wocky_jingle_session_terminate (self->priv->jingle, WOCKY_JINGLE_REASON_UNKNOWN, NULL, NULL); return; } } else { del_channel (self, channel); } } telepathy-gabble-0.18.2/src/media-factory.c0000644000175000017500000012151712312320035020472 0ustar00smcvsmcv00000000000000/* * media-factory.c - Source for GabbleMediaFactory * Copyright (C) 2006 Collabora Ltd. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 "config.h" #include "media-factory.h" #include #include #include #include #include #include #include #define DEBUG_FLAG GABBLE_DEBUG_MEDIA #include "gabble/caps-channel-manager.h" #include "connection.h" #include "debug.h" #include "call-channel.h" #include "media-channel.h" #include "namespaces.h" #include "util.h" typedef struct { GabbleMediaFactory *self; TpExportableChannel *channel; GSList *request_tokens; } MediaChannelRequest; static MediaChannelRequest * media_channel_request_new (GabbleMediaFactory *self, TpExportableChannel *channel, gpointer request_token) { MediaChannelRequest *mcr = g_slice_new0 (MediaChannelRequest); mcr->self = self; mcr->channel = g_object_ref (channel); if (request_token != NULL) mcr->request_tokens = g_slist_prepend (mcr->request_tokens, request_token); return mcr; } static void media_channel_request_free (MediaChannelRequest *mcr) { g_object_unref (mcr->channel); g_slist_free (mcr->request_tokens); g_slice_free (MediaChannelRequest, mcr); } static void media_channel_request_succeeded_cb (MediaChannelRequest *mcr, GPtrArray *streams) { tp_channel_manager_emit_new_channel (mcr->self, mcr->channel, mcr->request_tokens); media_channel_request_free (mcr); } static void media_channel_request_failed_cb (MediaChannelRequest *mcr, GError *error) { GSList *l; for (l = mcr->request_tokens; l != NULL; l = g_slist_next (l)) tp_channel_manager_emit_request_failed (mcr->self, l->data, error->domain, error->code, error->message); media_channel_request_free (mcr); } static void channel_manager_iface_init (gpointer, gpointer); static void caps_channel_manager_iface_init (gpointer, gpointer); G_DEFINE_TYPE_WITH_CODE (GabbleMediaFactory, gabble_media_factory, G_TYPE_OBJECT, G_IMPLEMENT_INTERFACE (TP_TYPE_CHANNEL_MANAGER, channel_manager_iface_init); G_IMPLEMENT_INTERFACE (GABBLE_TYPE_CAPS_CHANNEL_MANAGER, caps_channel_manager_iface_init)); /* properties */ enum { PROP_CONNECTION = 1, LAST_PROPERTY }; struct _GabbleMediaFactoryPrivate { GabbleConnection *conn; gulong status_changed_id; GList *media_channels; GList *call_channels; GList *pending_call_channels; guint channel_index; /* Whether or not we should use call channels for incoming calls */ gboolean use_call_channels; gboolean dispose_has_run; }; static void gabble_media_factory_init (GabbleMediaFactory *fac) { GabbleMediaFactoryPrivate *priv = G_TYPE_INSTANCE_GET_PRIVATE (fac, GABBLE_TYPE_MEDIA_FACTORY, GabbleMediaFactoryPrivate); fac->priv = priv; priv->channel_index = 0; priv->conn = NULL; priv->dispose_has_run = FALSE; } static void gabble_media_factory_close_all (GabbleMediaFactory *fac); static void gabble_media_factory_dispose (GObject *object) { GabbleMediaFactory *fac = GABBLE_MEDIA_FACTORY (object); GabbleMediaFactoryPrivate *priv = fac->priv; if (priv->dispose_has_run) return; DEBUG ("dispose called"); priv->dispose_has_run = TRUE; gabble_media_factory_close_all (fac); g_assert (priv->media_channels == NULL); g_assert (priv->call_channels == NULL); if (G_OBJECT_CLASS (gabble_media_factory_parent_class)->dispose) G_OBJECT_CLASS (gabble_media_factory_parent_class)->dispose (object); } static void gabble_media_factory_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { GabbleMediaFactory *fac = GABBLE_MEDIA_FACTORY (object); GabbleMediaFactoryPrivate *priv = fac->priv; switch (property_id) { case PROP_CONNECTION: g_value_set_object (value, priv->conn); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void gabble_media_factory_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { GabbleMediaFactory *fac = GABBLE_MEDIA_FACTORY (object); GabbleMediaFactoryPrivate *priv = fac->priv; switch (property_id) { case PROP_CONNECTION: priv->conn = g_value_get_object (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void gabble_media_factory_constructed (GObject *object); static void gabble_media_factory_class_init (GabbleMediaFactoryClass *gabble_media_factory_class) { GObjectClass *object_class = G_OBJECT_CLASS (gabble_media_factory_class); GParamSpec *param_spec; g_type_class_add_private (gabble_media_factory_class, sizeof (GabbleMediaFactoryPrivate)); object_class->constructed = gabble_media_factory_constructed; object_class->dispose = gabble_media_factory_dispose; object_class->get_property = gabble_media_factory_get_property; object_class->set_property = gabble_media_factory_set_property; param_spec = g_param_spec_object ("connection", "GabbleConnection object", "Gabble connection object that owns this media channel manager object.", GABBLE_TYPE_CONNECTION, G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, PROP_CONNECTION, param_spec); } /** * media_channel_closed_cb: * * Signal callback for when a media channel is closed. Removes the references * that #GabbleMediaFactory holds to them. */ static void media_channel_closed_cb (GabbleMediaChannel *chan, gpointer user_data) { GabbleMediaFactory *fac = GABBLE_MEDIA_FACTORY (user_data); GabbleMediaFactoryPrivate *priv = fac->priv; tp_channel_manager_emit_channel_closed_for_object (fac, TP_EXPORTABLE_CHANNEL (chan)); DEBUG ("removing media channel %p with ref count %d", chan, G_OBJECT (chan)->ref_count); priv->media_channels = g_list_remove (priv->media_channels, chan); g_object_unref (chan); } /** * new_media_channel * * Creates a new empty GabbleMediaChannel. */ static GabbleMediaChannel * new_media_channel (GabbleMediaFactory *fac, WockyJingleSession *sess, TpHandle maybe_peer, gboolean peer_in_rp, gboolean initial_audio, gboolean initial_video) { GabbleMediaFactoryPrivate *priv; TpBaseConnection *conn; GabbleMediaChannel *chan; gchar *object_path; g_assert (GABBLE_IS_MEDIA_FACTORY (fac)); priv = fac->priv; conn = (TpBaseConnection *) priv->conn; object_path = g_strdup_printf ("%s/MediaChannel%u", tp_base_connection_get_object_path (conn), priv->channel_index); priv->channel_index += 1; chan = g_object_new (GABBLE_TYPE_MEDIA_CHANNEL, "connection", priv->conn, "object-path", object_path, "session", sess, "initial-peer", maybe_peer, "peer-in-rp", peer_in_rp, "initial-audio", initial_audio, "initial-video", initial_video, NULL); DEBUG ("object path %s", object_path); g_signal_connect (chan, "closed", (GCallback) media_channel_closed_cb, fac); priv->media_channels = g_list_prepend (priv->media_channels, chan); g_free (object_path); return chan; } static void call_channel_closed_cb (GabbleCallChannel *chan, gpointer user_data) { GabbleMediaFactory *fac = GABBLE_MEDIA_FACTORY (user_data); GabbleMediaFactoryPrivate *priv = fac->priv; tp_channel_manager_emit_channel_closed_for_object (fac, TP_EXPORTABLE_CHANNEL (chan)); DEBUG ("removing media channel %p with ref count %d", chan, G_OBJECT (chan)->ref_count); priv->call_channels = g_list_remove (priv->call_channels, chan); g_object_unref (chan); } static void call_channel_initialized (GObject *source, GAsyncResult *res, gpointer user_data) { MediaChannelRequest *mcr = user_data; GabbleMediaFactoryPrivate *priv = mcr->self->priv; GError *error = NULL; priv->pending_call_channels = g_list_remove (priv->pending_call_channels, mcr); if (g_async_initable_init_finish (G_ASYNC_INITABLE (source), res, &error)) { priv->call_channels = g_list_prepend (priv->call_channels, g_object_ref (mcr->channel)); tp_channel_manager_emit_new_channel (mcr->self, mcr->channel, mcr->request_tokens); g_signal_connect (mcr->channel, "closed", G_CALLBACK (call_channel_closed_cb), mcr->self); } else { GSList *l; for (l = mcr->request_tokens; l != NULL; l = g_slist_next (l)) tp_channel_manager_emit_request_failed (mcr->self, l->data, error->domain, error->code, error->message); } media_channel_request_free (mcr); } /** * new_call_channel * * Creates and triggers initialisation of a new empty GabbleCallChannel. */ static void new_call_channel (GabbleMediaFactory *self, WockyJingleSession *sess, TpHandle peer, gboolean initial_audio, const gchar *initial_audio_name, gboolean initial_video, const gchar *initial_video_name, gpointer request_token) { GabbleCallChannel *channel; MediaChannelRequest *mcr; gchar *object_path; TpBaseConnection *conn = TP_BASE_CONNECTION (self->priv->conn); TpHandle initiator; if (sess != NULL) initiator = peer; else initiator = tp_base_connection_get_self_handle (conn); object_path = g_strdup_printf ("%s/CallChannel%u", tp_base_connection_get_object_path (conn), self->priv->channel_index); self->priv->channel_index++; channel = g_object_new (GABBLE_TYPE_CALL_CHANNEL, "connection", conn, "object-path", object_path, "session", sess, "handle", peer, "initial-audio", initial_audio, "initial-audio-name", initial_audio_name != NULL ? initial_audio_name : "audio", "initial-video", initial_video, "initial-video-name", initial_video_name != NULL ? initial_video_name : "video", "requested", request_token != NULL, "initiator-handle", initiator, "mutable-contents", TRUE, NULL); g_free (object_path); mcr = media_channel_request_new (self, TP_EXPORTABLE_CHANNEL (channel), request_token); g_async_initable_init_async (G_ASYNC_INITABLE (channel), G_PRIORITY_DEFAULT, NULL, /* FIXME support cancelling the channel creation */ call_channel_initialized, mcr); self->priv->pending_call_channels = g_list_prepend (self->priv->pending_call_channels, mcr); g_object_unref (channel); } static void gabble_media_factory_close_all (GabbleMediaFactory *fac) { GabbleMediaFactoryPrivate *priv = fac->priv; DEBUG ("closing channels"); /* Close will cause the channel to be removed from the list indirectly..*/ while (priv->media_channels != NULL) gabble_media_channel_close ( GABBLE_MEDIA_CHANNEL (priv->media_channels->data)); /* Close will cause the channel to be removed from the list indirectly..*/ while (priv->call_channels != NULL) tp_base_channel_close (TP_BASE_CHANNEL (priv->call_channels->data)); if (priv->status_changed_id != 0) { g_signal_handler_disconnect (priv->conn, priv->status_changed_id); priv->status_changed_id = 0; } } static void new_jingle_session_cb (GabbleJingleMint *jm, WockyJingleSession *sess, gpointer data) { GabbleMediaFactory *self = GABBLE_MEDIA_FACTORY (data); GabbleMediaFactoryPrivate *priv = self->priv; TpHandleRepoIface *contacts; TpHandle peer; if (wocky_jingle_session_get_content_type (sess) != WOCKY_TYPE_JINGLE_MEDIA_RTP) return; if (gabble_muc_factory_handle_jingle_session (priv->conn->muc_factory, sess)) { /* Muji channel the muc factory is taking care of it */ return; } contacts = tp_base_connection_get_handles (TP_BASE_CONNECTION (priv->conn), TP_HANDLE_TYPE_CONTACT); peer = tp_handle_ensure (contacts, wocky_jingle_session_get_peer_jid (sess), NULL, NULL); if (self->priv->use_call_channels) { new_call_channel (self, sess, peer, FALSE, NULL, FALSE, NULL, NULL); } else { GabbleMediaChannel *chan = new_media_channel (self, sess, peer, FALSE, FALSE, FALSE); GList *cs; /* FIXME: we need this detection to properly adjust nat-traversal on * the channel. We hope all contents will have the same transport... */ cs = wocky_jingle_session_get_contents (sess); if (cs != NULL) { WockyJingleContent *c = cs->data; gchar *ns; g_object_get (c, "transport-ns", &ns, NULL); if (!tp_strdiff (ns, NS_JINGLE_TRANSPORT_ICEUDP)) g_object_set (chan, "nat-traversal", "ice-udp", NULL); else if (!tp_strdiff (ns, NS_JINGLE_TRANSPORT_RAWUDP)) g_object_set (chan, "nat-traversal", "none", NULL); g_free (ns); g_list_free (cs); } tp_channel_manager_emit_new_channel (self, TP_EXPORTABLE_CHANNEL (chan), NULL); } } static void connection_status_changed_cb (GabbleConnection *conn, guint status, guint reason, GabbleMediaFactory *self) { GabbleMediaFactoryPrivate *priv = self->priv; switch (status) { case TP_CONNECTION_STATUS_CONNECTING: g_signal_connect (priv->conn->jingle_mint, "incoming-session", G_CALLBACK (new_jingle_session_cb), self); break; case TP_CONNECTION_STATUS_DISCONNECTED: gabble_media_factory_close_all (self); break; } } static void gabble_media_factory_constructed (GObject *object) { void (*chain_up) (GObject *) = G_OBJECT_CLASS (gabble_media_factory_parent_class)->constructed; GabbleMediaFactory *self = GABBLE_MEDIA_FACTORY (object); GabbleMediaFactoryPrivate *priv = self->priv; if (chain_up != NULL) chain_up (object); priv->status_changed_id = g_signal_connect (priv->conn, "status-changed", (GCallback) connection_status_changed_cb, object); } static void gabble_media_factory_foreach_channel (TpChannelManager *manager, TpExportableChannelFunc foreach, gpointer user_data) { GabbleMediaFactory *fac = GABBLE_MEDIA_FACTORY (manager); GabbleMediaFactoryPrivate *priv = fac->priv; GList *l; for (l = priv->media_channels; l != NULL; l = g_list_next (l)) foreach (TP_EXPORTABLE_CHANNEL (l->data), user_data); for (l = priv->call_channels; l != NULL; l = g_list_next (l)) foreach (TP_EXPORTABLE_CHANNEL (l->data), user_data); } static const gchar * const media_channel_fixed_properties[] = { TP_PROP_CHANNEL_CHANNEL_TYPE, TP_PROP_CHANNEL_TARGET_HANDLE_TYPE, NULL }; /* If you change this at all, you'll probably also need to change both_allowed * and video_allowed */ static const gchar * const named_channel_allowed_properties[] = { TP_PROP_CHANNEL_TARGET_HANDLE, TP_PROP_CHANNEL_TARGET_ID, TP_PROP_CHANNEL_TYPE_STREAMED_MEDIA_INITIAL_AUDIO, TP_PROP_CHANNEL_TYPE_STREAMED_MEDIA_INITIAL_VIDEO, NULL }; static const gchar * const * both_allowed = named_channel_allowed_properties + 2; static const gchar * const audio_allowed[] = { TP_PROP_CHANNEL_TYPE_STREAMED_MEDIA_INITIAL_AUDIO, TP_PROP_CHANNEL_TYPE_STREAMED_MEDIA_IMMUTABLE_STREAMS, NULL }; static const gchar * const video_allowed[] = { TP_PROP_CHANNEL_TYPE_STREAMED_MEDIA_INITIAL_VIDEO, TP_PROP_CHANNEL_TYPE_STREAMED_MEDIA_IMMUTABLE_STREAMS, NULL }; static const gchar * const both_allowed_immutable[] = { TP_PROP_CHANNEL_TYPE_STREAMED_MEDIA_INITIAL_AUDIO, TP_PROP_CHANNEL_TYPE_STREAMED_MEDIA_INITIAL_VIDEO, TP_PROP_CHANNEL_TYPE_STREAMED_MEDIA_IMMUTABLE_STREAMS, NULL }; static const gchar * const call_channel_allowed_properties[] = { TP_PROP_CHANNEL_TARGET_HANDLE, TP_PROP_CHANNEL_TARGET_ID, TP_PROP_CHANNEL_TYPE_CALL_INITIAL_AUDIO, TP_PROP_CHANNEL_TYPE_CALL_INITIAL_AUDIO_NAME, TP_PROP_CHANNEL_TYPE_CALL_INITIAL_VIDEO, TP_PROP_CHANNEL_TYPE_CALL_INITIAL_VIDEO_NAME, TP_PROP_CHANNEL_TYPE_CALL_MUTABLE_CONTENTS, NULL }; static const gchar * const call_audio_allowed[] = { TP_PROP_CHANNEL_TYPE_CALL_INITIAL_AUDIO, TP_PROP_CHANNEL_TYPE_CALL_INITIAL_AUDIO_NAME, NULL }; static const gchar * const call_video_allowed[] = { TP_PROP_CHANNEL_TYPE_CALL_INITIAL_VIDEO, TP_PROP_CHANNEL_TYPE_CALL_INITIAL_VIDEO_NAME, NULL }; static const gchar * const call_both_allowed[] = { TP_PROP_CHANNEL_TYPE_CALL_INITIAL_AUDIO, TP_PROP_CHANNEL_TYPE_CALL_INITIAL_AUDIO_NAME, TP_PROP_CHANNEL_TYPE_CALL_INITIAL_VIDEO, TP_PROP_CHANNEL_TYPE_CALL_INITIAL_VIDEO_NAME, TP_PROP_CHANNEL_TYPE_CALL_MUTABLE_CONTENTS, NULL }; static const gchar * const call_both_allowed_immutable[] = { TP_PROP_CHANNEL_TYPE_CALL_INITIAL_AUDIO, TP_PROP_CHANNEL_TYPE_CALL_INITIAL_AUDIO_NAME, TP_PROP_CHANNEL_TYPE_CALL_INITIAL_VIDEO, TP_PROP_CHANNEL_TYPE_CALL_INITIAL_VIDEO_NAME, NULL }; /* not advertised in type_foreach_channel_class - can only be requested with * RequestChannel, not with CreateChannel/EnsureChannel */ static const gchar * const anon_channel_allowed_properties[] = { NULL }; const gchar * const * gabble_media_factory_call_channel_allowed_properties (void) { return call_channel_allowed_properties; } static GHashTable * gabble_media_factory_streamed_media_channel_class (void) { GHashTable *table = tp_asv_new ( TP_PROP_CHANNEL_TARGET_HANDLE_TYPE, G_TYPE_UINT, TP_HANDLE_TYPE_CONTACT, NULL); tp_asv_set_static_string (table, TP_PROP_CHANNEL_CHANNEL_TYPE, TP_IFACE_CHANNEL_TYPE_STREAMED_MEDIA); return table; } static GHashTable * gabble_media_factory_call_channel_class (void) { GHashTable *table = tp_asv_new ( TP_PROP_CHANNEL_TARGET_HANDLE_TYPE, G_TYPE_UINT, TP_HANDLE_TYPE_CONTACT, NULL); tp_asv_set_static_string (table, TP_PROP_CHANNEL_CHANNEL_TYPE, TP_IFACE_CHANNEL_TYPE_CALL); return table; } static void gabble_media_factory_type_foreach_channel_class (GType type, TpChannelManagerTypeChannelClassFunc func, gpointer user_data) { GHashTable *table = gabble_media_factory_streamed_media_channel_class (); func (type, table, named_channel_allowed_properties, user_data); g_hash_table_unref (table); table = gabble_media_factory_call_channel_class (); func (type, table, call_channel_allowed_properties, user_data); g_hash_table_unref (table); } typedef enum { METHOD_REQUEST, METHOD_CREATE, METHOD_ENSURE, } RequestMethod; static gboolean gabble_media_factory_requestotron (TpChannelManager *manager, gpointer request_token, GHashTable *request_properties, RequestMethod method) { GabbleMediaFactory *self = GABBLE_MEDIA_FACTORY (manager); GabbleMediaFactoryPrivate *priv = self->priv; TpHandleType handle_type; TpHandle handle; GabbleMediaChannel *channel = NULL; GError *error = NULL; gboolean require_target_handle, add_peer_to_remote_pending; gboolean initial_audio, initial_video; /* Supported modes of operation: * - RequestChannel(None, 0): * channel is anonymous; * caller may optionally use AddMembers to add the peer to RemotePending * without sending them any XML; * caller uses RequestStreams to set the peer and start the call. * - RequestChannel(Contact, n) where n != 0: * channel has TargetHandle=n; * n is in remote pending; * call is started when caller calls RequestStreams. * - CreateChannel({THT: Contact, TH: n}): * channel has TargetHandle=n * n is not in the group interface at all * call is started when caller calls RequestStreams. * - EnsureChannel({THT: Contact, TH: n}): * look for a channel whose peer is n, and return that if found with * whatever properties and group membership it has; * otherwise the same as into CreateChannel */ switch (method) { case METHOD_REQUEST: require_target_handle = FALSE; add_peer_to_remote_pending = TRUE; break; case METHOD_CREATE: case METHOD_ENSURE: require_target_handle = TRUE; add_peer_to_remote_pending = FALSE; break; default: g_assert_not_reached (); } if (tp_strdiff (tp_asv_get_string (request_properties, TP_PROP_CHANNEL_CHANNEL_TYPE), TP_IFACE_CHANNEL_TYPE_STREAMED_MEDIA)) return FALSE; handle_type = tp_asv_get_uint32 (request_properties, TP_PROP_CHANNEL_TARGET_HANDLE_TYPE, NULL); handle = tp_asv_get_uint32 (request_properties, TP_PROP_CHANNEL_TARGET_HANDLE, NULL); initial_audio = tp_asv_get_boolean (request_properties, TP_PROP_CHANNEL_TYPE_STREAMED_MEDIA_INITIAL_AUDIO, NULL); initial_video = tp_asv_get_boolean (request_properties, TP_PROP_CHANNEL_TYPE_STREAMED_MEDIA_INITIAL_VIDEO, NULL); switch (handle_type) { case TP_HANDLE_TYPE_NONE: /* already checked by TpBaseConnection */ g_assert (handle == 0); if (require_target_handle) { g_set_error (&error, TP_ERROR, TP_ERROR_NOT_IMPLEMENTED, "A valid Contact handle must be provided when requesting a media " "channel"); goto error; } if (tp_channel_manager_asv_has_unknown_properties (request_properties, media_channel_fixed_properties, anon_channel_allowed_properties, &error)) goto error; channel = new_media_channel (self, NULL, 0, FALSE, FALSE, FALSE); break; case TP_HANDLE_TYPE_CONTACT: /* validity already checked by TpBaseConnection */ g_assert (handle != 0); if (tp_channel_manager_asv_has_unknown_properties (request_properties, media_channel_fixed_properties, named_channel_allowed_properties, &error)) goto error; if (method == METHOD_ENSURE) { GList *l; TpHandle peer = 0; for (l = priv->media_channels; l != NULL; l = g_list_next (l)) { channel = GABBLE_MEDIA_CHANNEL (l->data); g_object_get (channel, "peer", &peer, NULL); if (peer == handle) { /* Per the spec, we ignore InitialAudio and InitialVideo when * looking for an existing channel. */ tp_channel_manager_emit_request_already_satisfied (self, request_token, TP_EXPORTABLE_CHANNEL (channel)); return TRUE; } } } channel = new_media_channel (self, NULL, handle, add_peer_to_remote_pending, initial_audio, initial_video); break; default: return FALSE; } g_assert (channel != NULL); gabble_media_channel_request_initial_streams (channel, (GFunc) media_channel_request_succeeded_cb, (GFunc) media_channel_request_failed_cb, media_channel_request_new (self, TP_EXPORTABLE_CHANNEL (channel), request_token)); return TRUE; error: tp_channel_manager_emit_request_failed (self, request_token, error->domain, error->code, error->message); g_error_free (error); return TRUE; } static gboolean gabble_media_factory_create_call (TpChannelManager *manager, gpointer request_token, GHashTable *request_properties, RequestMethod method) { GabbleMediaFactory *self = GABBLE_MEDIA_FACTORY (manager); TpHandle target; GError *error = NULL; gboolean initial_audio, initial_video; const gchar *initial_audio_name, *initial_video_name; DEBUG ("Creating a new call channel"); if (tp_asv_get_uint32 (request_properties, TP_PROP_CHANNEL_TARGET_HANDLE_TYPE, NULL) != TP_HANDLE_TYPE_CONTACT) return FALSE; if (tp_channel_manager_asv_has_unknown_properties (request_properties, media_channel_fixed_properties, call_channel_allowed_properties, &error)) goto error; target = tp_asv_get_uint32 (request_properties, TP_PROP_CHANNEL_TARGET_HANDLE, NULL); if (method == METHOD_ENSURE) { GList *l; TpHandle handle = 0; for (l = self->priv->call_channels; l != NULL; l = g_list_next (l)) { GabbleCallChannel *channel = GABBLE_CALL_CHANNEL (l->data); g_object_get (channel, "handle", &handle, NULL); if (handle == target) { /* Per the spec, we ignore InitialAudio and InitialVideo when * looking for an existing channel. */ tp_channel_manager_emit_request_already_satisfied (self, request_token, TP_EXPORTABLE_CHANNEL (channel)); return TRUE; } } for (l = self->priv->pending_call_channels; l != NULL; l = g_list_next (l)) { MediaChannelRequest *mcr = (MediaChannelRequest *) l->data; g_object_get (mcr->channel, "handle", &handle, NULL); if (handle == target) { /* Per the spec, we ignore InitialAudio and InitialVideo when * looking for an existing channel. */ mcr->request_tokens = g_slist_prepend (mcr->request_tokens, request_token); return TRUE; } } } initial_audio = tp_asv_get_boolean (request_properties, TP_PROP_CHANNEL_TYPE_CALL_INITIAL_AUDIO, NULL); initial_video = tp_asv_get_boolean (request_properties, TP_PROP_CHANNEL_TYPE_CALL_INITIAL_VIDEO, NULL); if (!initial_audio && !initial_video) { g_set_error (&error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT, "Call channel must contain at least " "one of InitialAudio or InitialVideo"); goto error; } /* FIXME creating the channel should check and wait for the capabilities * FIXME need to cope with disconnecting while channels are setting up */ initial_audio_name = tp_asv_get_string (request_properties, TP_PROP_CHANNEL_TYPE_CALL_INITIAL_AUDIO_NAME); initial_video_name = tp_asv_get_string (request_properties, TP_PROP_CHANNEL_TYPE_CALL_INITIAL_VIDEO_NAME); new_call_channel (self, NULL, target, initial_audio, initial_audio_name, initial_video, initial_video_name, request_token); return TRUE; error: tp_channel_manager_emit_request_failed (self, request_token, error->domain, error->code, error->message); g_error_free (error); return TRUE; } static gboolean gabble_media_factory_request_channel (TpChannelManager *manager, gpointer request_token, GHashTable *request_properties) { return gabble_media_factory_requestotron (manager, request_token, request_properties, METHOD_REQUEST); } static gboolean gabble_media_factory_create_channel (TpChannelManager *manager, gpointer request_token, GHashTable *request_properties) { if (!tp_strdiff (tp_asv_get_string (request_properties, TP_PROP_CHANNEL_CHANNEL_TYPE), TP_IFACE_CHANNEL_TYPE_CALL)) return gabble_media_factory_create_call (manager, request_token, request_properties, METHOD_CREATE); else return gabble_media_factory_requestotron (manager, request_token, request_properties, METHOD_CREATE); } static gboolean gabble_media_factory_ensure_channel (TpChannelManager *manager, gpointer request_token, GHashTable *request_properties) { if (!tp_strdiff (tp_asv_get_string (request_properties, TP_PROP_CHANNEL_CHANNEL_TYPE), TP_IFACE_CHANNEL_TYPE_CALL)) return gabble_media_factory_create_call (manager, request_token, request_properties, METHOD_ENSURE); else return gabble_media_factory_requestotron (manager, request_token, request_properties, METHOD_ENSURE); } static void channel_manager_iface_init (gpointer g_iface, gpointer iface_data) { TpChannelManagerIface *iface = g_iface; iface->foreach_channel = gabble_media_factory_foreach_channel; iface->type_foreach_channel_class = gabble_media_factory_type_foreach_channel_class; iface->request_channel = gabble_media_factory_request_channel; iface->create_channel = gabble_media_factory_create_channel; iface->ensure_channel = gabble_media_factory_ensure_channel; } static void gabble_media_factory_add_caps (GabbleCapabilitySet *caps, const gchar *client_name, gboolean audio, gboolean video, gboolean gtalk_p2p, gboolean ice_udp, gboolean h264) { gboolean any_content = audio || video; DEBUG ("Client %s media capabilities:%s%s%s%s%s", client_name, audio ? " audio" : "", video ? " video" : "", gtalk_p2p ? " gtalk-p2p" : "", ice_udp ? " ice-udp" : "", h264 ? " H.264" : ""); if (gtalk_p2p && any_content) gabble_capability_set_add (caps, NS_GOOGLE_TRANSPORT_P2P); if (ice_udp && any_content) gabble_capability_set_add (caps, NS_JINGLE_TRANSPORT_ICEUDP); if (audio) { gabble_capability_set_add (caps, NS_JINGLE_RTP); gabble_capability_set_add (caps, NS_JINGLE_RTP_AUDIO); gabble_capability_set_add (caps, NS_JINGLE_DESCRIPTION_AUDIO); gabble_capability_set_add (caps, NS_JINGLE_RTP_HDREXT); gabble_capability_set_add (caps, NS_JINGLE_RTCP_FB); /* voice-v1 implies that we interop with GTalk, i.e. we have gtalk-p2p * as well as audio */ if (gtalk_p2p) gabble_capability_set_add (caps, NS_GOOGLE_FEAT_VOICE); } if (video) { gabble_capability_set_add (caps, NS_JINGLE_RTP); gabble_capability_set_add (caps, NS_JINGLE_RTP_VIDEO); gabble_capability_set_add (caps, NS_JINGLE_DESCRIPTION_VIDEO); gabble_capability_set_add (caps, NS_JINGLE_RTP_HDREXT); gabble_capability_set_add (caps, NS_JINGLE_RTCP_FB); /* video-v1 implies that we interop with Google Video Chat, i.e. we have * gtalk-p2p and H.264 as well as video */ if (gtalk_p2p && h264) { gabble_capability_set_add (caps, NS_GOOGLE_FEAT_VIDEO); gabble_capability_set_add (caps, NS_GOOGLE_FEAT_CAMERA); } } } /* The switch in gabble_media_factory_get_contact_caps needs to be kept in * sync with the possible returns from this function. */ TpChannelMediaCapabilities _gabble_media_factory_caps_to_typeflags (const GabbleCapabilitySet *caps) { TpChannelMediaCapabilities typeflags = 0; gboolean has_a_transport, just_google, one_media_type; has_a_transport = gabble_capability_set_has_one (caps, gabble_capabilities_get_any_transport ()); if (has_a_transport && gabble_capability_set_has_one (caps, gabble_capabilities_get_any_audio ())) typeflags |= TP_CHANNEL_MEDIA_CAPABILITY_AUDIO; if (has_a_transport && gabble_capability_set_has_one (caps, gabble_capabilities_get_any_video ())) typeflags |= TP_CHANNEL_MEDIA_CAPABILITY_VIDEO; /* The checks below are an intentional asymmetry with the function going the * other way - we don't require the other end to advertise the GTalk-P2P * transport capability separately because old GTalk clients didn't do that. * Having Google voice implied Google session and GTalk-P2P. */ if (gabble_capability_set_has (caps, NS_GOOGLE_FEAT_VOICE)) typeflags |= TP_CHANNEL_MEDIA_CAPABILITY_AUDIO; if (gabble_capability_set_has (caps, NS_GOOGLE_FEAT_VIDEO)) typeflags |= TP_CHANNEL_MEDIA_CAPABILITY_VIDEO; just_google = gabble_capability_set_has_one (caps, gabble_capabilities_get_any_google_av ()) && !gabble_capability_set_has_one (caps, gabble_capabilities_get_any_jingle_av ()); one_media_type = (typeflags == TP_CHANNEL_MEDIA_CAPABILITY_AUDIO) || (typeflags == TP_CHANNEL_MEDIA_CAPABILITY_VIDEO); if (just_google || one_media_type) typeflags |= TP_CHANNEL_MEDIA_CAPABILITY_IMMUTABLE_STREAMS; return typeflags; } void _gabble_media_factory_typeflags_to_caps (TpChannelMediaCapabilities flags, GabbleCapabilitySet *caps) { DEBUG ("adding Jingle caps %u", flags); /* The client name just appears in a debug message, so use something that * won't, in practice, clash with any client that uses ContactCapabilities */ gabble_media_factory_add_caps (caps, "", (flags & TP_CHANNEL_MEDIA_CAPABILITY_AUDIO) != 0, (flags & TP_CHANNEL_MEDIA_CAPABILITY_VIDEO) != 0, (flags & TP_CHANNEL_MEDIA_CAPABILITY_NAT_TRAVERSAL_GTALK_P2P) != 0, (flags & TP_CHANNEL_MEDIA_CAPABILITY_NAT_TRAVERSAL_ICE_UDP) != 0, TRUE /* assume we have H.264 for now */); } static void gabble_media_factory_get_contact_caps (GabbleCapsChannelManager *manager, TpHandle handle, const GabbleCapabilitySet *caps, GPtrArray *arr) { TpChannelMediaCapabilities typeflags = _gabble_media_factory_caps_to_typeflags (caps); GValueArray *va; const gchar * const *streamed_media_allowed; const gchar * const *call_allowed; typeflags &= (TP_CHANNEL_MEDIA_CAPABILITY_AUDIO | TP_CHANNEL_MEDIA_CAPABILITY_VIDEO | TP_CHANNEL_MEDIA_CAPABILITY_IMMUTABLE_STREAMS); /* This switch is over the values of several bits from a * bitfield-represented-as-an-enum, simultaneously, which upsets gcc-4.5; * the guint cast reassures it that we know what we're doing. * _gabble_media_factory_caps_to_typeflags shouldn't return any cases not * handled here. */ switch ((guint) typeflags) { case 0: return; case TP_CHANNEL_MEDIA_CAPABILITY_AUDIO | TP_CHANNEL_MEDIA_CAPABILITY_IMMUTABLE_STREAMS: streamed_media_allowed = audio_allowed; call_allowed = call_audio_allowed; break; case TP_CHANNEL_MEDIA_CAPABILITY_VIDEO | TP_CHANNEL_MEDIA_CAPABILITY_IMMUTABLE_STREAMS: streamed_media_allowed = video_allowed; call_allowed = call_video_allowed; break; case TP_CHANNEL_MEDIA_CAPABILITY_AUDIO | TP_CHANNEL_MEDIA_CAPABILITY_VIDEO: /* both */ streamed_media_allowed = both_allowed; call_allowed = call_both_allowed; break; case TP_CHANNEL_MEDIA_CAPABILITY_AUDIO /* both but immutable */ | TP_CHANNEL_MEDIA_CAPABILITY_VIDEO | TP_CHANNEL_MEDIA_CAPABILITY_IMMUTABLE_STREAMS: streamed_media_allowed = both_allowed_immutable; call_allowed = call_both_allowed_immutable; break; default: g_assert_not_reached (); } /* Streamed Media channel */ va = g_value_array_new (2); g_value_array_append (va, NULL); g_value_array_append (va, NULL); g_value_init (va->values + 0, TP_HASH_TYPE_CHANNEL_CLASS); g_value_init (va->values + 1, G_TYPE_STRV); g_value_take_boxed (va->values + 0, gabble_media_factory_streamed_media_channel_class ()); g_value_set_static_boxed (va->values + 1, streamed_media_allowed); g_ptr_array_add (arr, va); /* Call channel */ va = g_value_array_new (2); g_value_array_append (va, NULL); g_value_array_append (va, NULL); g_value_init (va->values + 0, TP_HASH_TYPE_CHANNEL_CLASS); g_value_init (va->values + 1, G_TYPE_STRV); g_value_take_boxed (va->values + 0, gabble_media_factory_call_channel_class ()); g_value_set_static_boxed (va->values + 1, call_allowed); g_ptr_array_add (arr, va); } static void gabble_media_factory_represent_client (GabbleCapsChannelManager *manager, const gchar *client_name, const GPtrArray *filters, const gchar * const *cap_tokens, GabbleCapabilitySet *cap_set, GPtrArray *data_forms) { static GQuark q_gtalk_p2p = 0, q_ice_udp = 0, q_h264 = 0; static GQuark qc_gtalk_p2p = 0, qc_ice_udp = 0, qc_h264 = 0, qc_ice = 0; gboolean gtalk_p2p = FALSE, h264 = FALSE, audio = FALSE, video = FALSE, ice_udp = FALSE; guint i; /* One-time initialization - turn the tokens we care about into quarks */ if (G_UNLIKELY (q_gtalk_p2p == 0)) { q_gtalk_p2p = g_quark_from_static_string ( TP_TOKEN_CHANNEL_INTERFACE_MEDIA_SIGNALLING_GTALK_P2P); qc_gtalk_p2p = g_quark_from_static_string ( TP_TOKEN_CHANNEL_TYPE_CALL_GTALK_P2P); q_ice_udp = g_quark_from_static_string ( TP_TOKEN_CHANNEL_INTERFACE_MEDIA_SIGNALLING_ICE_UDP); qc_ice = g_quark_from_static_string ( TP_TOKEN_CHANNEL_TYPE_CALL_ICE); /* 'ice-udp' isn't the proper cap name, 'ice' is. We keep supporting * 'ice-udp' for now to not break existing clients. */ qc_ice_udp = g_quark_from_static_string ( TP_IFACE_CHANNEL_TYPE_CALL "/ice-udp"); q_h264 = g_quark_from_static_string ( TP_IFACE_CHANNEL_INTERFACE_MEDIA_SIGNALLING "/video/h264"); qc_h264 = g_quark_from_static_string ( TP_IFACE_CHANNEL_TYPE_CALL "/video/h264"); } if (cap_tokens != NULL) { const gchar * const *token; for (token = cap_tokens; *token != NULL; token++) { GQuark quark = g_quark_try_string (*token); struct { GQuark quark; gboolean *cap; } q2cap[] = { { q_gtalk_p2p, >alk_p2p }, { qc_gtalk_p2p, >alk_p2p }, { q_ice_udp, &ice_udp }, { qc_ice_udp, &ice_udp }, { qc_ice, &ice_udp }, { q_h264, &h264 }, { qc_h264, &h264 }, { 0, NULL }, }; for (i = 0; q2cap[i].quark != 0; i++) { if (quark == q2cap[i].quark) *(q2cap[i].cap) = TRUE; } } } for (i = 0; i < filters->len; i++) { GHashTable *filter = g_ptr_array_index (filters, i); if (tp_asv_size (filter) == 0) { /* a client that claims to be able to do absolutely everything can * presumably do audio, video, smell, etc. etc. */ audio = TRUE; video = TRUE; continue; } if (tp_strdiff (tp_asv_get_string (filter, TP_PROP_CHANNEL_CHANNEL_TYPE), TP_IFACE_CHANNEL_TYPE_STREAMED_MEDIA) && tp_strdiff (tp_asv_get_string (filter, TP_PROP_CHANNEL_CHANNEL_TYPE), TP_IFACE_CHANNEL_TYPE_CALL)) { /* not interesting to this channel manager */ continue; } #ifdef ENABLE_CHANNEL_TYPE_CALL /* If there is a handler that can support Call channels, use those for * incoming channels */ if (!tp_strdiff (tp_asv_get_string (filter, TP_PROP_CHANNEL_CHANNEL_TYPE), TP_IFACE_CHANNEL_TYPE_CALL)) { GabbleMediaFactory *self = GABBLE_MEDIA_FACTORY (manager); self->priv->use_call_channels = TRUE; } #endif if (tp_asv_lookup (filter, TP_PROP_CHANNEL_TARGET_HANDLE_TYPE) != NULL && tp_asv_get_uint32 (filter, TP_PROP_CHANNEL_TARGET_HANDLE_TYPE, NULL) != TP_HANDLE_TYPE_CONTACT) { /* not interesting to this channel manager: we only care about * Jingle calls involving contacts (or about clients that support * all Jingle calls regardless of handle type) */ continue; } if (tp_asv_get_boolean (filter, TP_PROP_CHANNEL_TYPE_STREAMED_MEDIA_INITIAL_AUDIO, NULL) || tp_asv_get_boolean (filter, TP_PROP_CHANNEL_TYPE_CALL_INITIAL_AUDIO, NULL)) audio = TRUE; if (tp_asv_get_boolean (filter, TP_PROP_CHANNEL_TYPE_STREAMED_MEDIA_INITIAL_VIDEO, NULL) || tp_asv_get_boolean (filter, TP_PROP_CHANNEL_TYPE_CALL_INITIAL_VIDEO, NULL)) video = TRUE; /* If we've picked up all the capabilities we're ever going to, then * we don't need to look at the rest of the filters */ if (audio && video) break; } gabble_media_factory_add_caps (cap_set, client_name, audio, video, gtalk_p2p, ice_udp, h264); } static void caps_channel_manager_iface_init (gpointer g_iface, gpointer iface_data) { GabbleCapsChannelManagerInterface *iface = g_iface; iface->get_contact_caps = gabble_media_factory_get_contact_caps; iface->represent_client = gabble_media_factory_represent_client; } telepathy-gabble-0.18.2/src/media-factory.h0000644000175000017500000000537712227000321020502 0ustar00smcvsmcv00000000000000/* * media-factory.h - Header for GabbleMediaFactory * Copyright (C) 2006 Collabora Ltd. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 __MEDIA_FACTORY_H__ #define __MEDIA_FACTORY_H__ #include #include "media-channel.h" G_BEGIN_DECLS typedef struct _GabbleMediaFactory GabbleMediaFactory; typedef struct _GabbleMediaFactoryClass GabbleMediaFactoryClass; typedef struct _GabbleMediaFactoryPrivate GabbleMediaFactoryPrivate; struct _GabbleMediaFactoryClass { GObjectClass parent_class; }; struct _GabbleMediaFactory { GObject parent; GabbleMediaFactoryPrivate *priv; }; GType gabble_media_factory_get_type (void); /* TYPE MACROS */ #define GABBLE_TYPE_MEDIA_FACTORY \ (gabble_media_factory_get_type ()) #define GABBLE_MEDIA_FACTORY(obj) \ (G_TYPE_CHECK_INSTANCE_CAST((obj), GABBLE_TYPE_MEDIA_FACTORY,\ GabbleMediaFactory)) #define GABBLE_MEDIA_FACTORY_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST((klass), GABBLE_TYPE_MEDIA_FACTORY,\ GabbleMediaFactoryClass)) #define GABBLE_IS_MEDIA_FACTORY(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE((obj), GABBLE_TYPE_MEDIA_FACTORY)) #define GABBLE_IS_MEDIA_FACTORY_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE((klass), GABBLE_TYPE_MEDIA_FACTORY)) #define GABBLE_MEDIA_FACTORY_GET_CLASS(obj) \ (G_TYPE_INSTANCE_GET_CLASS ((obj), GABBLE_TYPE_MEDIA_FACTORY,\ GabbleMediaFactoryClass)) const gchar * _gabble_media_factory_allocate_sid (GabbleMediaFactory *fac, GabbleMediaChannel *chan); const gchar * _gabble_media_factory_register_sid (GabbleMediaFactory *fac, const gchar *sid, GabbleMediaChannel *chan); void _gabble_media_factory_free_sid (GabbleMediaFactory *fac, const gchar *sid); void _gabble_media_factory_typeflags_to_caps (TpChannelMediaCapabilities flags, GabbleCapabilitySet *caps); TpChannelMediaCapabilities _gabble_media_factory_caps_to_typeflags (const GabbleCapabilitySet *caps); const gchar * const * gabble_media_factory_call_channel_allowed_properties ( void); G_END_DECLS #endif /* #ifndef __MEDIA_FACTORY_H__ */ telepathy-gabble-0.18.2/src/media-stream.c0000644000175000017500000020100112227000321020277 0ustar00smcvsmcv00000000000000/* * gabble-media-stream.c - Source for GabbleMediaStream * Copyright © 2006-2009 Collabora Ltd. * Copyright © 2006-2009 Nokia Corporation * @author Ole Andre Vadla Ravnaas * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 "config.h" #include "media-stream.h" #include #include #include #include #include #include #define DEBUG_FLAG GABBLE_DEBUG_MEDIA #include "connection.h" #include "debug.h" #include "gabble-signals-marshal.h" #include "media-channel.h" #include "namespaces.h" #include "util.h" static void stream_handler_iface_init (gpointer, gpointer); G_DEFINE_TYPE_WITH_CODE(GabbleMediaStream, gabble_media_stream, G_TYPE_OBJECT, G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_MEDIA_STREAM_HANDLER, stream_handler_iface_init); G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_DBUS_PROPERTIES, tp_dbus_properties_mixin_iface_init); ) /* signal enum */ enum { ERROR, UNHOLD_FAILED, LAST_SIGNAL }; static guint signals[LAST_SIGNAL] = {0}; /* properties */ enum { PROP_DBUS_DAEMON = 1, PROP_OBJECT_PATH, PROP_NAME, PROP_ID, PROP_MEDIA_TYPE, PROP_CONNECTION_STATE, PROP_READY, PROP_PLAYING, PROP_COMBINED_DIRECTION, PROP_LOCAL_HOLD, PROP_CONTENT, PROP_STUN_SERVERS, PROP_RELAY_INFO, PROP_NAT_TRAVERSAL, PROP_CREATED_LOCALLY, LAST_PROPERTY }; /* private structure */ struct _GabbleMediaStreamPrivate { WockyJingleContent *content; TpDBusDaemon *dbus_daemon; gchar *object_path; guint id; guint media_type; gboolean local_codecs_set; /* Whether we're waiting for a codec intersection from the streaming * implementation. If FALSE, SupportedCodecs is a no-op. */ gboolean awaiting_intersection; GValue local_rtp_hdrexts; GValue local_feedback_messages; GValue remote_codecs; GValue remote_rtp_hdrexts; GValue remote_feedback_messages; GValue remote_candidates; guint remote_candidate_count; /* source ID for initial codecs/candidates getter */ gulong initial_getter_id; gchar *nat_traversal; /* GPtrArray(GValueArray(STRING, UINT)) */ GPtrArray *stun_servers; /* GPtrArray(GHashTable(string => GValue)) */ GPtrArray *relay_info; gboolean on_hold; /* These are really booleans, but gboolean is signed. Thanks, GLib */ unsigned closed:1; unsigned dispose_has_run:1; unsigned local_hold:1; unsigned ready:1; unsigned sending:1; unsigned created_locally:1; }; static void push_remote_media_description (GabbleMediaStream *stream); static void push_remote_candidates (GabbleMediaStream *stream); static void push_playing (GabbleMediaStream *stream); static void push_sending (GabbleMediaStream *stream); static void new_remote_candidates_cb (WockyJingleContent *content, GList *clist, GabbleMediaStream *stream); static void new_remote_media_description_cb (WockyJingleContent *content, WockyJingleMediaDescription *md, GabbleMediaStream *stream); static void content_state_changed_cb (WockyJingleContent *c, GParamSpec *pspec, GabbleMediaStream *stream); static void content_senders_changed_cb (WockyJingleContent *c, GParamSpec *pspec, GabbleMediaStream *stream); static void remote_state_changed_cb (WockyJingleSession *session, GabbleMediaStream *stream); static void content_removed_cb (WockyJingleContent *content, GabbleMediaStream *stream); static void update_direction (GabbleMediaStream *stream, WockyJingleContent *c); static void update_sending (GabbleMediaStream *stream, gboolean start_sending); GabbleMediaStream * gabble_media_stream_new ( TpDBusDaemon *dbus_daemon, const gchar *object_path, WockyJingleContent *content, const gchar *name, guint id, const gchar *nat_traversal, const GPtrArray *relay_info, gboolean local_hold) { GPtrArray *empty = NULL; GabbleMediaStream *result; g_return_val_if_fail (WOCKY_IS_JINGLE_MEDIA_RTP (content), NULL); if (relay_info == NULL) { empty = g_ptr_array_sized_new (0); relay_info = empty; } result = g_object_new (GABBLE_TYPE_MEDIA_STREAM, "dbus-daemon", dbus_daemon, "object-path", object_path, "content", content, "name", name, "id", id, "nat-traversal", nat_traversal, "relay-info", relay_info, "local-hold", local_hold, NULL); if (empty != NULL) g_ptr_array_unref (empty); return result; } TpMediaStreamType gabble_media_stream_get_media_type (GabbleMediaStream *self) { return self->priv->media_type; } static void gabble_media_stream_init (GabbleMediaStream *self) { GabbleMediaStreamPrivate *priv = G_TYPE_INSTANCE_GET_PRIVATE (self, GABBLE_TYPE_MEDIA_STREAM, GabbleMediaStreamPrivate); GType candidate_list_type = TP_ARRAY_TYPE_MEDIA_STREAM_HANDLER_CANDIDATE_LIST; GType codec_list_type = TP_ARRAY_TYPE_MEDIA_STREAM_HANDLER_CODEC_LIST; GType rtp_hdrext_list_type = TP_ARRAY_TYPE_RTP_HEADER_EXTENSIONS_LIST; GType fb_msg_map_type = TP_HASH_TYPE_RTCP_FEEDBACK_MESSAGE_MAP; self->priv = priv; g_value_init (&priv->local_rtp_hdrexts, rtp_hdrext_list_type); g_value_init (&priv->local_feedback_messages, fb_msg_map_type); g_value_init (&priv->remote_codecs, codec_list_type); g_value_take_boxed (&priv->remote_codecs, dbus_g_type_specialized_construct (codec_list_type)); g_value_init (&priv->remote_rtp_hdrexts, rtp_hdrext_list_type); g_value_take_boxed (&priv->remote_rtp_hdrexts, dbus_g_type_specialized_construct (rtp_hdrext_list_type)); g_value_init (&priv->remote_feedback_messages, fb_msg_map_type); g_value_take_boxed (&priv->remote_feedback_messages, dbus_g_type_specialized_construct (fb_msg_map_type)); g_value_init (&priv->remote_candidates, candidate_list_type); g_value_take_boxed (&priv->remote_candidates, dbus_g_type_specialized_construct (candidate_list_type)); priv->stun_servers = g_ptr_array_sized_new (1); } static gboolean _get_initial_codecs_and_candidates (gpointer user_data) { GabbleMediaStream *stream = GABBLE_MEDIA_STREAM (user_data); GabbleMediaStreamPrivate *priv = stream->priv; WockyJingleMediaDescription *md; priv->initial_getter_id = 0; /* we can immediately get the codecs if we're responder */ md = wocky_jingle_media_rtp_get_remote_media_description ( WOCKY_JINGLE_MEDIA_RTP (priv->content)); if (md != NULL) new_remote_media_description_cb (priv->content, md, stream); /* if any candidates arrived before idle loop had the chance to excute * us (e.g. specified in session-initiate/content-add), we don't want to * miss them */ new_remote_candidates_cb (priv->content, wocky_jingle_content_get_remote_candidates (priv->content), stream); return FALSE; } static GObject * gabble_media_stream_constructor (GType type, guint n_props, GObjectConstructParam *props) { GObject *obj; GabbleMediaStream *stream; GabbleMediaStreamPrivate *priv; WockyJingleFactory *jf; GList *stun_servers; /* call base class constructor */ obj = G_OBJECT_CLASS (gabble_media_stream_parent_class)-> constructor (type, n_props, props); stream = GABBLE_MEDIA_STREAM (obj); priv = stream->priv; g_assert (priv->content != NULL); /* STUN servers are needed as soon as the stream appears, so there's little * point in waiting for them - either they've already been resolved, or * we're too late to use them for this stream */ jf = wocky_jingle_session_get_factory (priv->content->session); stun_servers = wocky_jingle_info_get_stun_servers ( wocky_jingle_factory_get_jingle_info (jf)); while (stun_servers != NULL) { WockyStunServer *stun_server = stun_servers->data; GValueArray *va = tp_value_array_build (2, G_TYPE_STRING, stun_server->address, G_TYPE_UINT, (guint) stun_server->port, G_TYPE_INVALID); g_ptr_array_add (priv->stun_servers, va); stun_servers = g_list_delete_link (stun_servers, stun_servers); } /* go for the bus */ g_assert (priv->dbus_daemon != NULL); tp_dbus_daemon_register_object (priv->dbus_daemon, priv->object_path, obj); update_direction (stream, priv->content); /* MediaStream is created as soon as WockyJingleContent is * created, but we want to let it parse the initiation (if * initiated by remote end) before we pick up initial * codecs and candidates. * FIXME: add API for ordering IQs rather than using g_idle_add. */ priv->initial_getter_id = g_idle_add (_get_initial_codecs_and_candidates, stream); if (priv->created_locally) { g_object_set (stream, "combined-direction", MAKE_COMBINED_DIRECTION (TP_MEDIA_STREAM_DIRECTION_BIDIRECTIONAL, 0), NULL); } else { priv->awaiting_intersection = TRUE; } return obj; } static void gabble_media_stream_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { GabbleMediaStream *stream = GABBLE_MEDIA_STREAM (object); GabbleMediaStreamPrivate *priv = stream->priv; switch (property_id) { case PROP_DBUS_DAEMON: g_value_set_object (value, priv->dbus_daemon); break; case PROP_OBJECT_PATH: g_value_set_string (value, priv->object_path); break; case PROP_NAME: g_value_set_string (value, stream->name); break; case PROP_ID: g_value_set_uint (value, priv->id); break; case PROP_MEDIA_TYPE: g_value_set_uint (value, priv->media_type); break; case PROP_CONNECTION_STATE: g_value_set_uint (value, stream->connection_state); break; case PROP_READY: g_value_set_boolean (value, priv->ready); break; case PROP_PLAYING: g_value_set_boolean (value, stream->playing); break; case PROP_COMBINED_DIRECTION: g_value_set_uint (value, stream->combined_direction); break; case PROP_LOCAL_HOLD: g_value_set_boolean (value, priv->local_hold); break; case PROP_CONTENT: g_value_set_object (value, priv->content); break; case PROP_STUN_SERVERS: g_value_set_boxed (value, priv->stun_servers); break; case PROP_NAT_TRAVERSAL: g_value_set_string (value, priv->nat_traversal); break; case PROP_CREATED_LOCALLY: g_value_set_boolean (value, priv->created_locally); break; case PROP_RELAY_INFO: g_value_set_boxed (value, priv->relay_info); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void gabble_media_stream_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { GabbleMediaStream *stream = GABBLE_MEDIA_STREAM (object); GabbleMediaStreamPrivate *priv = stream->priv; switch (property_id) { case PROP_OBJECT_PATH: g_free (priv->object_path); priv->object_path = g_value_dup_string (value); break; case PROP_DBUS_DAEMON: g_assert (priv->dbus_daemon == NULL); priv->dbus_daemon = g_value_dup_object (value); break; case PROP_NAME: g_free (stream->name); stream->name = g_value_dup_string (value); break; case PROP_ID: priv->id = g_value_get_uint (value); break; case PROP_CONNECTION_STATE: DEBUG ("stream %s connection state %d", stream->name, stream->connection_state); stream->connection_state = g_value_get_uint (value); break; case PROP_READY: priv->ready = g_value_get_boolean (value); break; case PROP_PLAYING: { gboolean old = stream->playing; stream->playing = g_value_get_boolean (value); if (stream->playing != old) push_playing (stream); } break; case PROP_COMBINED_DIRECTION: DEBUG ("changing combined direction from %u to %u", stream->combined_direction, g_value_get_uint (value)); stream->combined_direction = g_value_get_uint (value); break; case PROP_CONTENT: g_assert (priv->content == NULL); priv->content = g_value_dup_object (value); { guint jtype; gboolean locally_created; g_object_get (priv->content, "media-type", &jtype, "locally-created", &locally_created, NULL); if (jtype == WOCKY_JINGLE_MEDIA_TYPE_VIDEO) priv->media_type = TP_MEDIA_STREAM_TYPE_VIDEO; else priv->media_type = TP_MEDIA_STREAM_TYPE_AUDIO; priv->created_locally = locally_created; } DEBUG ("%p: connecting to content %p signals", stream, priv->content); gabble_signal_connect_weak (priv->content, "new-candidates", (GCallback) new_remote_candidates_cb, object); /* we need this also, if we're the initiator of the stream * (so remote codecs arrive later) */ gabble_signal_connect_weak (priv->content, "remote-media-description", (GCallback) new_remote_media_description_cb, object); gabble_signal_connect_weak (priv->content, "notify::state", (GCallback) content_state_changed_cb, object); gabble_signal_connect_weak (priv->content, "notify::senders", (GCallback) content_senders_changed_cb, object); gabble_signal_connect_weak (priv->content->session, "remote-state-changed", (GCallback) remote_state_changed_cb, object); gabble_signal_connect_weak (priv->content, "removed", (GCallback) content_removed_cb, object); break; case PROP_NAT_TRAVERSAL: g_assert (priv->nat_traversal == NULL); priv->nat_traversal = g_value_dup_string (value); break; case PROP_RELAY_INFO: g_assert (priv->relay_info == NULL); priv->relay_info = g_value_dup_boxed (value); break; case PROP_LOCAL_HOLD: priv->local_hold = g_value_get_boolean (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void gabble_media_stream_dispose (GObject *object); static void gabble_media_stream_finalize (GObject *object); static void gabble_media_stream_class_init (GabbleMediaStreamClass *gabble_media_stream_class) { GObjectClass *object_class = G_OBJECT_CLASS (gabble_media_stream_class); GParamSpec *param_spec; static TpDBusPropertiesMixinPropImpl stream_handler_props[] = { { "RelayInfo", "relay-info", NULL }, { "STUNServers", "stun-servers", NULL }, { "NATTraversal", "nat-traversal", NULL }, { "CreatedLocally", "created-locally", NULL }, { NULL } }; static TpDBusPropertiesMixinIfaceImpl prop_interfaces[] = { { TP_IFACE_MEDIA_STREAM_HANDLER, tp_dbus_properties_mixin_getter_gobject_properties, NULL, stream_handler_props, }, { NULL } }; g_type_class_add_private (gabble_media_stream_class, sizeof (GabbleMediaStreamPrivate)); object_class->constructor = gabble_media_stream_constructor; object_class->get_property = gabble_media_stream_get_property; object_class->set_property = gabble_media_stream_set_property; object_class->dispose = gabble_media_stream_dispose; object_class->finalize = gabble_media_stream_finalize; param_spec = g_param_spec_object ("dbus-daemon", "TpDBusDaemon", "Bus on which to export this object", TP_TYPE_DBUS_DAEMON, G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, PROP_DBUS_DAEMON, param_spec); param_spec = g_param_spec_string ("object-path", "D-Bus object path", "The D-Bus object path used for this " "object on the bus.", NULL, G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_NAME | G_PARAM_STATIC_BLURB); g_object_class_install_property (object_class, PROP_OBJECT_PATH, param_spec); param_spec = g_param_spec_string ("name", "Stream name", "An opaque name for the stream used in the signalling.", NULL, G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_NAME | G_PARAM_STATIC_BLURB); g_object_class_install_property (object_class, PROP_NAME, param_spec); param_spec = g_param_spec_uint ("id", "Stream ID", "A stream number for the stream used in the " "D-Bus API.", 0, G_MAXUINT, 0, G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_NAME | G_PARAM_STATIC_BLURB); g_object_class_install_property (object_class, PROP_ID, param_spec); param_spec = g_param_spec_uint ("media-type", "Stream media type", "A constant indicating which media type the stream carries.", TP_MEDIA_STREAM_TYPE_AUDIO, TP_MEDIA_STREAM_TYPE_VIDEO, TP_MEDIA_STREAM_TYPE_AUDIO, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, PROP_MEDIA_TYPE, param_spec); param_spec = g_param_spec_uint ("connection-state", "Stream connection state", "An integer indicating the state of the" "stream's connection.", TP_MEDIA_STREAM_STATE_DISCONNECTED, TP_MEDIA_STREAM_STATE_CONNECTED, TP_MEDIA_STREAM_STATE_DISCONNECTED, G_PARAM_CONSTRUCT | G_PARAM_READWRITE | G_PARAM_STATIC_NAME | G_PARAM_STATIC_BLURB); g_object_class_install_property (object_class, PROP_CONNECTION_STATE, param_spec); param_spec = g_param_spec_boolean ("ready", "Ready?", "A boolean signifying whether the user " "is ready to handle signals from this " "object.", FALSE, G_PARAM_CONSTRUCT | G_PARAM_READWRITE | G_PARAM_STATIC_NAME | G_PARAM_STATIC_BLURB); g_object_class_install_property (object_class, PROP_READY, param_spec); param_spec = g_param_spec_boolean ("playing", "Set playing", "A boolean signifying whether the stream " "has been set playing yet.", FALSE, G_PARAM_CONSTRUCT | G_PARAM_READWRITE | G_PARAM_STATIC_NAME | G_PARAM_STATIC_BLURB); g_object_class_install_property (object_class, PROP_PLAYING, param_spec); param_spec = g_param_spec_uint ("combined-direction", "Combined direction", "An integer indicating the directions the stream currently sends in, " "and the peers who have been asked to send.", TP_MEDIA_STREAM_DIRECTION_NONE, MAKE_COMBINED_DIRECTION (TP_MEDIA_STREAM_DIRECTION_BIDIRECTIONAL, TP_MEDIA_STREAM_PENDING_LOCAL_SEND | TP_MEDIA_STREAM_PENDING_REMOTE_SEND), TP_MEDIA_STREAM_DIRECTION_NONE, G_PARAM_CONSTRUCT | G_PARAM_READWRITE | G_PARAM_STATIC_NAME | G_PARAM_STATIC_BLURB); g_object_class_install_property (object_class, PROP_COMBINED_DIRECTION, param_spec); param_spec = g_param_spec_boolean ("local-hold", "Local hold?", "True if resources used for this stream have been freed.", FALSE, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_NAME | G_PARAM_STATIC_BLURB | G_PARAM_STATIC_NICK); g_object_class_install_property (object_class, PROP_LOCAL_HOLD, param_spec); param_spec = g_param_spec_object ("content", "WockyJingleContent object", "Jingle content signalling this media stream.", WOCKY_TYPE_JINGLE_CONTENT, G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB); g_object_class_install_property (object_class, PROP_CONTENT, param_spec); param_spec = g_param_spec_boxed ("stun-servers", "STUN servers", "Array of (STRING: address literal, UINT: port) pairs", /* FIXME: use correct macro when available */ tp_type_dbus_array_su (), G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, PROP_STUN_SERVERS, param_spec); param_spec = g_param_spec_boxed ("relay-info", "Relay info", "Array of mappings containing relay server information", TP_ARRAY_TYPE_STRING_VARIANT_MAP_LIST, G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, PROP_RELAY_INFO, param_spec); param_spec = g_param_spec_string ("nat-traversal", "NAT traversal", "NAT traversal mechanism for this stream", NULL, G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, PROP_NAT_TRAVERSAL, param_spec); param_spec = g_param_spec_boolean ("created-locally", "Created locally?", "True if this stream was created by the local user", FALSE, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, PROP_CREATED_LOCALLY, param_spec); /* signals not exported by D-Bus interface */ signals[ERROR] = g_signal_new ("error", G_OBJECT_CLASS_TYPE (gabble_media_stream_class), G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED, 0, NULL, NULL, gabble_marshal_VOID__UINT_STRING, G_TYPE_NONE, 2, G_TYPE_UINT, G_TYPE_STRING); signals[UNHOLD_FAILED] = g_signal_new ("unhold-failed", G_OBJECT_CLASS_TYPE (gabble_media_stream_class), G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED, 0, NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); gabble_media_stream_class->props_class.interfaces = prop_interfaces; tp_dbus_properties_mixin_class_init (object_class, G_STRUCT_OFFSET (GabbleMediaStreamClass, props_class)); } void gabble_media_stream_dispose (GObject *object) { GabbleMediaStream *self = GABBLE_MEDIA_STREAM (object); GabbleMediaStreamPrivate *priv = self->priv; DEBUG ("called"); if (priv->dispose_has_run) return; if (priv->initial_getter_id != 0) { g_source_remove (priv->initial_getter_id); priv->initial_getter_id = 0; } gabble_media_stream_close (self); priv->dispose_has_run = TRUE; tp_clear_object (&priv->content); tp_clear_pointer (&self->name, g_free); g_clear_object (&priv->dbus_daemon); if (G_OBJECT_CLASS (gabble_media_stream_parent_class)->dispose) G_OBJECT_CLASS (gabble_media_stream_parent_class)->dispose (object); } void gabble_media_stream_finalize (GObject *object) { GabbleMediaStream *self = GABBLE_MEDIA_STREAM (object); GabbleMediaStreamPrivate *priv = self->priv; g_free (priv->object_path); g_free (priv->nat_traversal); /* FIXME: use correct macro when available */ if (priv->stun_servers != NULL) g_boxed_free (tp_type_dbus_array_su (), priv->stun_servers); if (priv->relay_info != NULL) g_boxed_free (TP_ARRAY_TYPE_STRING_VARIANT_MAP_LIST, priv->relay_info); g_value_unset (&priv->local_rtp_hdrexts); g_value_unset (&priv->local_feedback_messages); g_value_unset (&priv->remote_codecs); g_value_unset (&priv->remote_rtp_hdrexts); g_value_unset (&priv->remote_feedback_messages); g_value_unset (&priv->remote_candidates); G_OBJECT_CLASS (gabble_media_stream_parent_class)->finalize (object); } /** * gabble_media_stream_codec_choice * * Implements D-Bus method CodecChoice * on interface org.freedesktop.Telepathy.Media.StreamHandler */ static void gabble_media_stream_codec_choice (TpSvcMediaStreamHandler *iface, guint codec_id, DBusGMethodInvocation *context) { tp_svc_media_stream_handler_return_from_codec_choice (context); } gboolean gabble_media_stream_error (GabbleMediaStream *self, guint errnum, const gchar *message, GError **error) { g_assert (GABBLE_IS_MEDIA_STREAM (self)); DEBUG ( "Media.StreamHandler::Error called, error %u (%s) -- emitting signal", errnum, message); g_signal_emit (self, signals[ERROR], 0, errnum, message); return TRUE; } /** * gabble_media_stream_error * * Implements D-Bus method Error * on interface org.freedesktop.Telepathy.Media.StreamHandler */ static void gabble_media_stream_error_async (TpSvcMediaStreamHandler *iface, guint errnum, const gchar *message, DBusGMethodInvocation *context) { GabbleMediaStream *self = GABBLE_MEDIA_STREAM (iface); GError *error = NULL; if (gabble_media_stream_error (self, errnum, message, &error)) { tp_svc_media_stream_handler_return_from_error (context); } else { dbus_g_method_return_error (context, error); g_error_free (error); } } /** * gabble_media_stream_hold: * * Tell streaming clients that the stream is going on hold, so they should * stop streaming and free up any resources they are currently holding * (e.g. close hardware devices); or that the stream is coming off hold, * so they should reacquire those resources. */ void gabble_media_stream_hold (GabbleMediaStream *self, gboolean hold) { tp_svc_media_stream_handler_emit_set_stream_held (self, hold); } /** * gabble_media_stream_hold_state: * * Called by streaming clients when the stream's hold state has been changed * successfully in response to SetStreamHeld. */ static void gabble_media_stream_hold_state (TpSvcMediaStreamHandler *iface, gboolean hold_state, DBusGMethodInvocation *context) { GabbleMediaStream *self = GABBLE_MEDIA_STREAM (iface); GabbleMediaStreamPrivate *priv = self->priv; DEBUG ("%p: %s", self, hold_state ? "held" : "unheld"); priv->local_hold = hold_state; g_object_notify ((GObject *) self, "local-hold"); tp_svc_media_stream_handler_return_from_hold_state (context); } /** * gabble_media_stream_unhold_failure: * * Called by streaming clients when an attempt to reacquire the necessary * hardware or software resources to unhold the stream, in response to * SetStreamHeld, has failed. */ static void gabble_media_stream_unhold_failure (TpSvcMediaStreamHandler *iface, DBusGMethodInvocation *context) { GabbleMediaStream *self = GABBLE_MEDIA_STREAM (iface); GabbleMediaStreamPrivate *priv = self->priv; DEBUG ("%p", self); priv->local_hold = TRUE; g_signal_emit (self, signals[UNHOLD_FAILED], 0); g_object_notify ((GObject *) self, "local-hold"); tp_svc_media_stream_handler_return_from_unhold_failure (context); } /** * gabble_media_stream_native_candidates_prepared * * Implements D-Bus method NativeCandidatesPrepared * on interface org.freedesktop.Telepathy.Media.StreamHandler */ static void gabble_media_stream_native_candidates_prepared (TpSvcMediaStreamHandler *iface, DBusGMethodInvocation *context) { tp_svc_media_stream_handler_return_from_native_candidates_prepared (context); } /** * gabble_media_stream_new_active_candidate_pair * * Implements D-Bus method NewActiveCandidatePair * on interface org.freedesktop.Telepathy.Media.StreamHandler */ static void gabble_media_stream_new_active_candidate_pair (TpSvcMediaStreamHandler *iface, const gchar *native_candidate_id, const gchar *remote_candidate_id, DBusGMethodInvocation *context) { DEBUG ("called (%s, %s); this is a no-op on Jingle", native_candidate_id, remote_candidate_id); tp_svc_media_stream_handler_return_from_new_active_candidate_pair (context); } /** * gabble_media_stream_new_native_candidate * * Implements D-Bus method NewNativeCandidate * on interface org.freedesktop.Telepathy.Media.StreamHandler */ static void gabble_media_stream_new_native_candidate (TpSvcMediaStreamHandler *iface, const gchar *candidate_id, const GPtrArray *transports, DBusGMethodInvocation *context) { GabbleMediaStream *self = GABBLE_MEDIA_STREAM (iface); GabbleMediaStreamPrivate *priv; WockyJingleState state; GList *li = NULL; guint i; g_assert (GABBLE_IS_MEDIA_STREAM (self)); priv = self->priv; g_object_get (priv->content->session, "state", &state, NULL); /* FIXME: maybe this should be an assertion in case the channel * isn't closed early enough right now? */ if (state > WOCKY_JINGLE_STATE_ACTIVE) { DEBUG ("state > WOCKY_JINGLE_STATE_ACTIVE, doing nothing"); tp_svc_media_stream_handler_return_from_new_native_candidate (context); return; } for (i = 0; i < transports->len; i++) { GValueArray *transport; guint component; const gchar *addr; WockyJingleCandidate *c; transport = g_ptr_array_index (transports, i); component = g_value_get_uint (g_value_array_get_nth (transport, 0)); /* Farsight 1 compatibility */ if (component == 0) component = 1; /* We understand RTP and RTCP, and silently ignore the rest */ if ((component != 1) && (component != 2)) continue; addr = g_value_get_string (g_value_array_get_nth (transport, 1)); if (!strcmp (addr, "127.0.0.1")) { DEBUG ("ignoring native localhost candidate"); continue; } c = wocky_jingle_candidate_new ( /* protocol */ g_value_get_uint (g_value_array_get_nth (transport, 3)), /* candidate type, we're relying on 1:1 candidate type mapping */ g_value_get_uint (g_value_array_get_nth (transport, 7)), /* id */ candidate_id, /* component */ component, /* address */ g_value_get_string (g_value_array_get_nth (transport, 1)), /* port */ g_value_get_uint (g_value_array_get_nth (transport, 2)), /* generation */ 0, /* preference */ (int) (g_value_get_double (g_value_array_get_nth (transport, 6)) * 65536), /* username */ g_value_get_string (g_value_array_get_nth (transport, 8)), /* password */ g_value_get_string (g_value_array_get_nth (transport, 9)), /* network */ 0); li = g_list_prepend (li, c); } if (li != NULL) wocky_jingle_content_add_candidates (priv->content, li); tp_svc_media_stream_handler_return_from_new_native_candidate (context); } static void gabble_media_stream_set_local_codecs (TpSvcMediaStreamHandler *, const GPtrArray *codecs, DBusGMethodInvocation *); /** * gabble_media_stream_ready * * Implements D-Bus method Ready * on interface org.freedesktop.Telepathy.Media.StreamHandler */ static void gabble_media_stream_ready (TpSvcMediaStreamHandler *iface, const GPtrArray *codecs, DBusGMethodInvocation *context) { GabbleMediaStream *self = GABBLE_MEDIA_STREAM (iface); GabbleMediaStreamPrivate *priv; g_assert (GABBLE_IS_MEDIA_STREAM (self)); priv = self->priv; DEBUG ("ready called"); if (priv->ready == FALSE) { g_object_set (self, "ready", TRUE, NULL); push_remote_media_description (self); push_remote_candidates (self); push_playing (self); push_sending (self); /* If a new stream is added while the call's on hold, it will have * local_hold set at construct time. So once tp-fs has called Ready(), we * should let it know this stream's on hold. */ if (priv->local_hold) gabble_media_stream_hold (self, priv->local_hold); } else { DEBUG ("Ready called twice, running plain SetLocalCodecs instead"); } /* set_local_codecs and ready return the same thing, so we can do... */ gabble_media_stream_set_local_codecs (iface, codecs, context); } static gboolean pass_local_codecs (GabbleMediaStream *stream, const GPtrArray *codecs, gboolean ready, GError **error) { GabbleMediaStreamPrivate *priv = stream->priv; guint i; WockyJingleMediaDescription *md; const GPtrArray *hdrexts; GHashTable *fbs; GError *wocky_error = NULL; DEBUG ("putting list of %d supported codecs from stream-engine into cache", codecs->len); md = wocky_jingle_media_description_new (); fbs = g_value_get_boxed (&priv->local_feedback_messages); for (i = 0; i < codecs->len; i++) { GType codec_struct_type = TP_STRUCT_TYPE_MEDIA_STREAM_HANDLER_CODEC; GValue codec = { 0, }; guint id, clock_rate, channels; gchar *name; GHashTable *params; WockyJingleCodec *c; GValueArray *fb_codec; g_value_init (&codec, codec_struct_type); g_value_set_static_boxed (&codec, g_ptr_array_index (codecs, i)); dbus_g_type_struct_get (&codec, 0, &id, 1, &name, 3, &clock_rate, 4, &channels, 5, ¶ms, G_MAXUINT); c = jingle_media_rtp_codec_new (id, name, clock_rate, channels, params); if (fbs != NULL) { fb_codec = g_hash_table_lookup (fbs, GUINT_TO_POINTER (id)); if (fb_codec != NULL) { if (G_VALUE_HOLDS_UINT ( g_value_array_get_nth (fb_codec, 0)) && G_VALUE_TYPE (g_value_array_get_nth (fb_codec, 1)) == TP_ARRAY_TYPE_RTCP_FEEDBACK_MESSAGE_LIST) { GValue *val; const GPtrArray *fb_array; guint j; val = g_value_array_get_nth (fb_codec, 0); c->trr_int = g_value_get_uint (val); val = g_value_array_get_nth (fb_codec, 1); fb_array = g_value_get_boxed (val); for (j = 0; j < fb_array->len; j++) { GValueArray *message = g_ptr_array_index (fb_array, j); const gchar *type; const gchar *subtype; val = g_value_array_get_nth (message, 0); type = g_value_get_string (val); val = g_value_array_get_nth (message, 1); subtype = g_value_get_string (val); c->feedback_msgs = g_list_append (c->feedback_msgs, wocky_jingle_feedback_message_new (type, subtype)); } } } } DEBUG ("adding codec %s (%u %u %u)", c->name, c->id, c->clockrate, c->channels); md->codecs = g_list_append (md->codecs, c); g_free (name); g_hash_table_unref (params); } if (fbs != NULL) g_value_reset (&priv->local_feedback_messages); hdrexts = g_value_get_boxed (&priv->local_rtp_hdrexts); if (hdrexts != NULL) { gboolean have_initiator = FALSE; gboolean initiated_by_us; for (i = 0; i < hdrexts->len; i++) { GValueArray *hdrext; guint id; guint direction; WockyJingleContentSenders senders; gchar *uri; gchar *params; hdrext = g_ptr_array_index (hdrexts, i); g_assert (hdrext); g_assert (hdrext->n_values == 4); g_assert (G_VALUE_HOLDS_UINT (g_value_array_get_nth (hdrext, 0))); g_assert (G_VALUE_HOLDS_UINT (g_value_array_get_nth (hdrext, 1))); g_assert (G_VALUE_HOLDS_STRING (g_value_array_get_nth (hdrext, 2))); g_assert (G_VALUE_HOLDS_STRING (g_value_array_get_nth (hdrext, 3))); tp_value_array_unpack (hdrext, 4, &id, &direction, &uri, ¶ms); if (!have_initiator) { g_object_get (priv->content->session, "local-initiator", &initiated_by_us, NULL); have_initiator = TRUE; } switch (direction) { case TP_MEDIA_STREAM_DIRECTION_BIDIRECTIONAL: senders = WOCKY_JINGLE_CONTENT_SENDERS_BOTH; break; case TP_MEDIA_STREAM_DIRECTION_NONE: senders = WOCKY_JINGLE_CONTENT_SENDERS_NONE; break; case TP_MEDIA_STREAM_DIRECTION_SEND: senders = initiated_by_us ? WOCKY_JINGLE_CONTENT_SENDERS_INITIATOR : WOCKY_JINGLE_CONTENT_SENDERS_RESPONDER; break; case TP_MEDIA_STREAM_DIRECTION_RECEIVE: senders = initiated_by_us ? WOCKY_JINGLE_CONTENT_SENDERS_RESPONDER : WOCKY_JINGLE_CONTENT_SENDERS_INITIATOR; break; default: g_assert_not_reached (); } md->hdrexts = g_list_append (md->hdrexts, wocky_jingle_rtp_header_extension_new (id, senders, uri)); } /* Can only be used once */ g_value_reset (&priv->local_rtp_hdrexts); } wocky_jingle_media_description_simplify (md); if (jingle_media_rtp_set_local_media_description ( WOCKY_JINGLE_MEDIA_RTP (priv->content), md, ready, &wocky_error)) return TRUE; g_set_error_literal (error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT, wocky_error->message); g_clear_error (&wocky_error); return FALSE; } /** * gabble_media_stream_set_local_codecs * * Implements D-Bus method SetLocalCodecs * on interface org.freedesktop.Telepathy.Media.StreamHandler */ static void gabble_media_stream_set_local_codecs (TpSvcMediaStreamHandler *iface, const GPtrArray *codecs, DBusGMethodInvocation *context) { GabbleMediaStream *self = GABBLE_MEDIA_STREAM (iface); GabbleMediaStreamPrivate *priv = self->priv; GError *error = NULL; DEBUG ("called"); if (codecs->len == 0) goto done; priv->local_codecs_set = TRUE; if (wocky_jingle_content_is_created_by_us (self->priv->content)) { if (!pass_local_codecs (self, codecs, self->priv->created_locally, &error)) { DEBUG ("failed: %s", error->message); dbus_g_method_return_error (context, error); g_error_free (error); return; } } else { DEBUG ("ignoring local codecs, waiting for codec intersection"); } done: tp_svc_media_stream_handler_return_from_set_local_codecs (context); } /** * gabble_media_stream_stream_state * * Implements D-Bus method StreamState * on interface org.freedesktop.Telepathy.Media.StreamHandler */ static void gabble_media_stream_stream_state (TpSvcMediaStreamHandler *iface, guint connection_state, DBusGMethodInvocation *context) { GabbleMediaStream *self = GABBLE_MEDIA_STREAM (iface); GabbleMediaStreamPrivate *priv = self->priv; WockyJingleTransportState ts = WOCKY_JINGLE_TRANSPORT_STATE_DISCONNECTED; switch (connection_state) { case TP_MEDIA_STREAM_STATE_DISCONNECTED: ts = WOCKY_JINGLE_TRANSPORT_STATE_DISCONNECTED; break; case TP_MEDIA_STREAM_STATE_CONNECTING: ts = WOCKY_JINGLE_TRANSPORT_STATE_CONNECTING; break; case TP_MEDIA_STREAM_STATE_CONNECTED: ts = WOCKY_JINGLE_TRANSPORT_STATE_CONNECTED; break; default: DEBUG ("ignoring unknown connection state %u", connection_state); goto OUT; } g_object_set (self, "connection-state", connection_state, NULL); wocky_jingle_content_set_transport_state (priv->content, ts); OUT: tp_svc_media_stream_handler_return_from_stream_state (context); } /** * gabble_media_stream_supported_codecs * * Implements D-Bus method SupportedCodecs * on interface org.freedesktop.Telepathy.Media.StreamHandler */ static void gabble_media_stream_supported_codecs (TpSvcMediaStreamHandler *iface, const GPtrArray *codecs, DBusGMethodInvocation *context) { GabbleMediaStream *self = GABBLE_MEDIA_STREAM (iface); GabbleMediaStreamPrivate *priv = self->priv; GError *error = NULL; DEBUG ("called"); if (codecs->len == 0) { GError e = { TP_ERROR, TP_ERROR_INVALID_ARGUMENT, "SupportedCodecs must have a non-empty list of codecs" }; dbus_g_method_return_error (context, &e); return; } priv->local_codecs_set = TRUE; if (priv->awaiting_intersection) { if (!pass_local_codecs (self, codecs, TRUE, &error)) { DEBUG ("failed: %s", error->message); dbus_g_method_return_error (context, error); g_error_free (error); return; } priv->awaiting_intersection = FALSE; } else { /* If we created the stream, we don't need to send the intersection. If * we didn't create it, but have already sent the intersection once, we * don't need to send it again. In either case, extra calls to * SupportedCodecs are in response to an incoming description-info, which * can only change parameters and which XEP-0167 §10 says is purely * advisory. */ DEBUG ("we already sent, or don't need to send, our codecs"); } tp_svc_media_stream_handler_return_from_supported_codecs (context); } /** * gabble_media_stream_codecs_updated * * Implements D-Bus method CodecsUpdated * on interface org.freedesktop.Telepathy.Media.StreamHandler */ static void gabble_media_stream_codecs_updated (TpSvcMediaStreamHandler *iface, const GPtrArray *codecs, DBusGMethodInvocation *context) { GabbleMediaStream *self = GABBLE_MEDIA_STREAM (iface); GError *error = NULL; if (!self->priv->local_codecs_set) { GError e = { TP_ERROR, TP_ERROR_NOT_AVAILABLE, "CodecsUpdated may only be called once an initial set of codecs " "has been set" }; dbus_g_method_return_error (context, &e); return; } if (self->priv->awaiting_intersection) { /* When awaiting an intersection the initial set of codecs should be set * by calling SupportedCodecs as that is the canonical set of codecs, * updates are only meaningful afterwards */ tp_svc_media_stream_handler_return_from_codecs_updated (context); return; } if (pass_local_codecs (self, codecs, self->priv->created_locally, &error)) { tp_svc_media_stream_handler_return_from_codecs_updated (context); } else { DEBUG ("failed: %s", error->message); dbus_g_method_return_error (context, error); g_error_free (error); } } /** * gabble_media_stream_supported_header_extensions * * Implements D-Bus method SupportedHeaderExtensions * on interface org.freedesktop.Telepathy.Media.StreamHandler */ static void gabble_media_stream_supported_header_extensions (TpSvcMediaStreamHandler *iface, const GPtrArray *hdrexts, DBusGMethodInvocation *context) { GabbleMediaStream *self = GABBLE_MEDIA_STREAM (iface); g_value_set_boxed (&self->priv->local_rtp_hdrexts, hdrexts); tp_svc_media_stream_handler_return_from_supported_header_extensions (context); } /** * gabble_media_stream_supported_feedback_messages * * Implements D-Bus method SupportedFeedbackMessages * on interface org.freedesktop.Telepathy.Media.StreamHandler */ static void gabble_media_stream_supported_feedback_messages (TpSvcMediaStreamHandler *iface, GHashTable *messages, DBusGMethodInvocation *context) { GabbleMediaStream *self = GABBLE_MEDIA_STREAM (iface); g_value_set_boxed (&self->priv->local_feedback_messages, messages); tp_svc_media_stream_handler_return_from_supported_feedback_messages (context); } void gabble_media_stream_close (GabbleMediaStream *stream) { GabbleMediaStreamPrivate *priv; g_assert (GABBLE_IS_MEDIA_STREAM (stream)); priv = stream->priv; if (!priv->closed) { priv->closed = TRUE; tp_svc_media_stream_handler_emit_close (stream); } } static void insert_feedback_message (WockyJingleFeedbackMessage *fb, GPtrArray *fb_msgs) { GValueArray *msg; msg = tp_value_array_build (3, G_TYPE_STRING, fb->type, G_TYPE_STRING, fb->subtype, G_TYPE_STRING, "", G_TYPE_INVALID); g_ptr_array_add (fb_msgs, msg); } static void new_remote_media_description_cb (WockyJingleContent *content, WockyJingleMediaDescription *md, GabbleMediaStream *stream) { GabbleMediaStreamPrivate *priv; GList *li; GPtrArray *codecs; GPtrArray *hdrexts; GHashTable *fbs; GType codec_struct_type = TP_STRUCT_TYPE_MEDIA_STREAM_HANDLER_CODEC; gboolean have_initiator = FALSE; gboolean initiated_by_us; DEBUG ("called"); g_assert (GABBLE_IS_MEDIA_STREAM (stream)); priv = stream->priv; codecs = g_value_get_boxed (&priv->remote_codecs); if (codecs->len != 0) { /* We already had some codecs; let's free the old list and make a new, * empty one to fill in. */ g_value_reset (&priv->remote_codecs); codecs = dbus_g_type_specialized_construct ( TP_ARRAY_TYPE_MEDIA_STREAM_HANDLER_CODEC_LIST); g_value_take_boxed (&priv->remote_codecs, codecs); } hdrexts = g_value_get_boxed (&priv->remote_rtp_hdrexts); if (hdrexts->len != 0) { /* We already had some rtp hdrext; let's free the old list and make a new, * empty one to fill in. */ g_value_reset (&priv->remote_rtp_hdrexts); hdrexts = dbus_g_type_specialized_construct ( TP_ARRAY_TYPE_RTP_HEADER_EXTENSIONS_LIST); g_value_take_boxed (&priv->remote_rtp_hdrexts, hdrexts); } fbs = g_value_get_boxed (&priv->remote_feedback_messages); if (g_hash_table_size (fbs) != 0) { /* We already had some rtp hdrext; let's free the old list and make a new, * empty one to fill in. */ g_value_reset (&priv->remote_feedback_messages); fbs = dbus_g_type_specialized_construct ( TP_HASH_TYPE_RTCP_FEEDBACK_MESSAGE_MAP); g_value_take_boxed (&priv->remote_feedback_messages, fbs); } for (li = md->codecs; li; li = li->next) { GValue codec = { 0, }; WockyJingleCodec *c = li->data; g_value_init (&codec, codec_struct_type); g_value_take_boxed (&codec, dbus_g_type_specialized_construct (codec_struct_type)); DEBUG ("new remote %s codec: %u '%s' %u %u %u", priv->media_type == TP_MEDIA_STREAM_TYPE_AUDIO ? "audio" : "video", c->id, c->name, priv->media_type, c->clockrate, c->channels); dbus_g_type_struct_set (&codec, 0, c->id, 1, c->name, 2, priv->media_type, 3, c->clockrate, 4, c->channels, 5, c->params, G_MAXUINT); if (md->trr_int != G_MAXUINT || c->trr_int != G_MAXUINT || md->feedback_msgs != NULL || c->feedback_msgs != NULL) { GValueArray *fb_msg_props; guint trr_int; GPtrArray *fb_msgs = g_ptr_array_new (); if (c->trr_int != G_MAXUINT) trr_int = c->trr_int; else trr_int = md->trr_int; g_list_foreach (md->feedback_msgs, (GFunc) insert_feedback_message, fb_msgs); g_list_foreach (c->feedback_msgs, (GFunc) insert_feedback_message, fb_msgs); fb_msg_props = tp_value_array_build (2, G_TYPE_UINT, trr_int, TP_ARRAY_TYPE_RTCP_FEEDBACK_MESSAGE_LIST, fb_msgs, G_TYPE_INVALID); g_boxed_free (TP_ARRAY_TYPE_RTCP_FEEDBACK_MESSAGE_LIST, fb_msgs); g_hash_table_insert (fbs, GUINT_TO_POINTER (c->id), fb_msg_props); } g_ptr_array_add (codecs, g_value_get_boxed (&codec)); } for (li = md->hdrexts; li; li = li->next) { WockyJingleRtpHeaderExtension *h = li->data; TpMediaStreamDirection direction; if (!have_initiator) { g_object_get (priv->content->session, "local-initiator", &initiated_by_us, NULL); have_initiator = TRUE; } switch (h->senders) { case WOCKY_JINGLE_CONTENT_SENDERS_BOTH: direction = TP_MEDIA_STREAM_DIRECTION_BIDIRECTIONAL; break; case WOCKY_JINGLE_CONTENT_SENDERS_NONE: direction = TP_MEDIA_STREAM_DIRECTION_NONE; break; case WOCKY_JINGLE_CONTENT_SENDERS_INITIATOR: direction = initiated_by_us ? TP_MEDIA_STREAM_DIRECTION_SEND : TP_MEDIA_STREAM_DIRECTION_RECEIVE; break; case WOCKY_JINGLE_CONTENT_SENDERS_RESPONDER: direction = initiated_by_us ? TP_MEDIA_STREAM_DIRECTION_RECEIVE : TP_MEDIA_STREAM_DIRECTION_SEND; break; default: g_assert_not_reached (); } DEBUG ("new RTP header ext : %u %s", h->id, h->uri); g_ptr_array_add (hdrexts, tp_value_array_build (4, G_TYPE_UINT, h->id, G_TYPE_UINT, direction, G_TYPE_STRING, h->uri, G_TYPE_STRING, "", /* No protocol defines parameters */ G_TYPE_INVALID)); } DEBUG ("pushing remote codecs"); push_remote_media_description (stream); } static void push_remote_media_description (GabbleMediaStream *stream) { GabbleMediaStreamPrivate *priv; GPtrArray *codecs; GPtrArray *hdrexts; GHashTable *fbs; g_assert (GABBLE_IS_MEDIA_STREAM (stream)); priv = stream->priv; if (!priv->ready) return; codecs = g_value_get_boxed (&priv->remote_codecs); if (codecs->len == 0) return; hdrexts = g_value_get_boxed (&priv->remote_rtp_hdrexts); fbs = g_value_get_boxed (&priv->remote_feedback_messages); DEBUG ("passing %d remote codecs to stream-engine", codecs->len); tp_svc_media_stream_handler_emit_set_remote_header_extensions (stream, hdrexts); tp_svc_media_stream_handler_emit_set_remote_feedback_messages (stream, fbs); tp_svc_media_stream_handler_emit_set_remote_codecs (stream, codecs); } static void new_remote_candidates_cb (WockyJingleContent *content, GList *clist, GabbleMediaStream *stream) { GabbleMediaStreamPrivate *priv = stream->priv; GPtrArray *candidates; GList *li; candidates = g_value_get_boxed (&priv->remote_candidates); DEBUG ("got new remote candidates"); for (li = clist; li; li = li->next) { gchar *candidate_id; GValue candidate = { 0, }; GPtrArray *transports; GValue transport = { 0, }; WockyJingleCandidate *c = li->data; GType transport_struct_type = TP_STRUCT_TYPE_MEDIA_STREAM_HANDLER_TRANSPORT; GType candidate_struct_type = TP_STRUCT_TYPE_MEDIA_STREAM_HANDLER_CANDIDATE; g_value_init (&transport, transport_struct_type); g_value_take_boxed (&transport, dbus_g_type_specialized_construct (transport_struct_type)); dbus_g_type_struct_set (&transport, 0, c->component, 1, c->address, 2, c->port, 3, c->protocol == WOCKY_JINGLE_TRANSPORT_PROTOCOL_UDP ? 0 : 1, 4, "RTP", 5, "AVP", 6, (gdouble) (c->preference / 65536.0), 7, c->type, /* FIXME: we're relying on 1:1 tp/jingle candidate type enums */ 8, c->username, 9, c->password, G_MAXUINT); transports = g_ptr_array_sized_new (1); g_ptr_array_add (transports, g_value_get_boxed (&transport)); g_value_init (&candidate, candidate_struct_type); g_value_take_boxed (&candidate, dbus_g_type_specialized_construct (candidate_struct_type)); if (c->id == NULL) /* FIXME: is this naming scheme sensible? */ candidate_id = g_strdup_printf ("R%d", ++priv->remote_candidate_count); else candidate_id = c->id; dbus_g_type_struct_set (&candidate, 0, candidate_id, 1, transports, G_MAXUINT); g_free (candidate_id); g_value_unset (&transport); g_ptr_array_unref (transports); g_ptr_array_add (candidates, g_value_get_boxed (&candidate)); } push_remote_candidates (stream); } static void content_state_changed_cb (WockyJingleContent *c, GParamSpec *pspec, GabbleMediaStream *stream) { GabbleMediaStreamPrivate *priv = stream->priv; WockyJingleContentState state; g_object_get (c, "state", &state, NULL); DEBUG ("called"); switch (state) { case WOCKY_JINGLE_CONTENT_STATE_ACKNOWLEDGED: /* connected stream means we can play, but sending is determined * by content senders (in update_senders) */ stream->playing = TRUE; update_sending (stream, TRUE); push_playing (stream); push_sending (stream); break; case WOCKY_JINGLE_CONTENT_STATE_REMOVING: stream->playing = FALSE; priv->sending = FALSE; push_playing (stream); break; default: /* so gcc doesn't cry */ break; } } static void push_remote_candidates (GabbleMediaStream *stream) { GabbleMediaStreamPrivate *priv; GPtrArray *candidates; guint i; GType candidate_list_type = TP_ARRAY_TYPE_MEDIA_STREAM_HANDLER_CANDIDATE_LIST; g_assert (GABBLE_IS_MEDIA_STREAM (stream)); priv = stream->priv; candidates = g_value_get_boxed (&priv->remote_candidates); if (candidates->len == 0) return; if (!priv->ready) return; for (i = 0; i < candidates->len; i++) { GValueArray *candidate = g_ptr_array_index (candidates, i); const gchar *candidate_id; const GPtrArray *transports; candidate_id = g_value_get_string (g_value_array_get_nth (candidate, 0)); transports = g_value_get_boxed (g_value_array_get_nth (candidate, 1)); DEBUG ("passing 1 remote candidate to stream engine: %s", candidate_id); tp_svc_media_stream_handler_emit_add_remote_candidate ( stream, candidate_id, transports); } g_value_take_boxed (&priv->remote_candidates, dbus_g_type_specialized_construct (candidate_list_type)); } static void push_playing (GabbleMediaStream *stream) { GabbleMediaStreamPrivate *priv; g_assert (GABBLE_IS_MEDIA_STREAM (stream)); priv = stream->priv; if (!priv->ready) return; DEBUG ("stream %s emitting SetStreamPlaying(%s)", stream->name, stream->playing ? "true" : "false"); tp_svc_media_stream_handler_emit_set_stream_playing ( stream, stream->playing); } static void push_sending (GabbleMediaStream *stream) { GabbleMediaStreamPrivate *priv; gboolean emit; g_assert (GABBLE_IS_MEDIA_STREAM (stream)); priv = stream->priv; if (!priv->ready) return; emit = (priv->sending && !(priv->on_hold)); DEBUG ("stream %s emitting SetStreamSending(%s); sending=%s, on_hold=%s", stream->name, emit ? "true" : "false", priv->sending ? "true" : "false", priv->on_hold ? "true" : "false"); tp_svc_media_stream_handler_emit_set_stream_sending ( stream, emit); } static void update_direction (GabbleMediaStream *stream, WockyJingleContent *c) { CombinedStreamDirection new_combined_dir; TpMediaStreamDirection requested_dir, current_dir; TpMediaStreamPendingSend pending_send; WockyJingleContentSenders senders; gboolean local_initiator; DEBUG ("called"); g_object_get (c, "senders", &senders, NULL); g_object_get (c->session, "local-initiator", &local_initiator, NULL); switch (senders) { case WOCKY_JINGLE_CONTENT_SENDERS_INITIATOR: requested_dir = local_initiator ? TP_MEDIA_STREAM_DIRECTION_SEND : TP_MEDIA_STREAM_DIRECTION_RECEIVE; break; case WOCKY_JINGLE_CONTENT_SENDERS_RESPONDER: requested_dir = local_initiator ? TP_MEDIA_STREAM_DIRECTION_RECEIVE : TP_MEDIA_STREAM_DIRECTION_SEND; break; case WOCKY_JINGLE_CONTENT_SENDERS_BOTH: requested_dir = TP_MEDIA_STREAM_DIRECTION_BIDIRECTIONAL; break; default: requested_dir = TP_MEDIA_STREAM_DIRECTION_NONE; } current_dir = COMBINED_DIRECTION_GET_DIRECTION (stream->combined_direction); pending_send = COMBINED_DIRECTION_GET_PENDING_SEND (stream->combined_direction); /* if local sending has been added, remove it, * and set the pending local send flag */ if (((current_dir & TP_MEDIA_STREAM_DIRECTION_SEND) == 0) && ((requested_dir & TP_MEDIA_STREAM_DIRECTION_SEND) != 0)) { DEBUG ("setting pending local send flag"); requested_dir &= ~TP_MEDIA_STREAM_DIRECTION_SEND; pending_send |= TP_MEDIA_STREAM_PENDING_LOCAL_SEND; } /* make any necessary changes */ new_combined_dir = MAKE_COMBINED_DIRECTION (requested_dir, pending_send); if (new_combined_dir != stream->combined_direction) { g_object_set (stream, "combined-direction", new_combined_dir, NULL); update_sending (stream, FALSE); } } static void content_senders_changed_cb (WockyJingleContent *c, GParamSpec *pspec, GabbleMediaStream *stream) { update_direction (stream, c); } static void remote_state_changed_cb (WockyJingleSession *session, GabbleMediaStream *stream) { GabbleMediaStreamPrivate *priv = stream->priv; gboolean old_hold = priv->on_hold; priv->on_hold = wocky_jingle_session_get_remote_hold (session); if (old_hold != priv->on_hold) push_sending (stream); } static void content_removed_cb (WockyJingleContent *content, GabbleMediaStream *stream) { gabble_media_stream_close (stream); } gboolean gabble_media_stream_change_direction (GabbleMediaStream *stream, guint requested_dir, GError **error) { GabbleMediaStreamPrivate *priv = stream->priv; CombinedStreamDirection new_combined_dir; TpMediaStreamDirection current_dir; TpMediaStreamPendingSend pending_send; WockyJingleContentSenders senders; gboolean local_initiator; current_dir = COMBINED_DIRECTION_GET_DIRECTION (stream->combined_direction); pending_send = COMBINED_DIRECTION_GET_PENDING_SEND (stream->combined_direction); /* if we're awaiting a local decision on sending... */ if ((pending_send & TP_MEDIA_STREAM_PENDING_LOCAL_SEND) != 0) { /* clear the flag */ pending_send &= ~TP_MEDIA_STREAM_PENDING_LOCAL_SEND; /* make our current_dir match what other end thinks (he thinks we're * bidirectional) so that we send the correct transitions */ current_dir ^= TP_MEDIA_STREAM_DIRECTION_SEND; } /* make any necessary changes */ new_combined_dir = MAKE_COMBINED_DIRECTION (requested_dir, pending_send); if (new_combined_dir != stream->combined_direction) { WockyJingleContentState state; gboolean start_sending; g_object_set (stream, "combined-direction", new_combined_dir, NULL); /* We would like to emit SetStreamSending(True) (if appropriate) only if: * - the content was locally created, or * - the user explicitly okayed the content. * This appears to be the meaning of Acknowledged. :-) */ g_object_get (stream->priv->content, "state", &state, NULL); start_sending = (state == WOCKY_JINGLE_CONTENT_STATE_ACKNOWLEDGED); update_sending (stream, start_sending); } DEBUG ("current_dir: %u, requested_dir: %u", current_dir, requested_dir); /* short-circuit sending a request if we're not asking for anything new */ if (current_dir == requested_dir) return TRUE; g_object_get (priv->content->session, "local-initiator", &local_initiator, NULL); switch (requested_dir) { case TP_MEDIA_STREAM_DIRECTION_SEND: senders = local_initiator ? WOCKY_JINGLE_CONTENT_SENDERS_INITIATOR : WOCKY_JINGLE_CONTENT_SENDERS_RESPONDER; break; case TP_MEDIA_STREAM_DIRECTION_RECEIVE: senders = local_initiator ? WOCKY_JINGLE_CONTENT_SENDERS_RESPONDER : WOCKY_JINGLE_CONTENT_SENDERS_INITIATOR; break; case TP_MEDIA_STREAM_DIRECTION_BIDIRECTIONAL: senders = WOCKY_JINGLE_CONTENT_SENDERS_BOTH; break; default: g_assert_not_reached (); } if (!wocky_jingle_content_change_direction (priv->content, senders)) { g_set_error (error, TP_ERROR, TP_ERROR_NOT_AVAILABLE, "stream direction invalid for the Jingle dialect in use"); return FALSE; } return TRUE; } void gabble_media_stream_accept_pending_local_send (GabbleMediaStream *stream) { CombinedStreamDirection combined_dir = stream->combined_direction; TpMediaStreamDirection current_dir; TpMediaStreamPendingSend pending_send; current_dir = COMBINED_DIRECTION_GET_DIRECTION (combined_dir); pending_send = COMBINED_DIRECTION_GET_PENDING_SEND (combined_dir); if ((pending_send & TP_MEDIA_STREAM_PENDING_LOCAL_SEND) != 0) { DEBUG ("accepting pending local send on stream %s", stream->name); gabble_media_stream_change_direction (stream, current_dir | TP_MEDIA_STREAM_DIRECTION_SEND, NULL); } else { DEBUG ("stream %s not pending local send", stream->name); } } static void update_sending (GabbleMediaStream *stream, gboolean start_sending) { GabbleMediaStreamPrivate *priv = stream->priv; gboolean new_sending; new_sending = ((stream->combined_direction & TP_MEDIA_STREAM_DIRECTION_SEND) != 0); if (priv->sending == new_sending) return; if (new_sending && !start_sending) return; priv->sending = new_sending; push_sending (stream); } static void stream_handler_iface_init (gpointer g_iface, gpointer iface_data) { TpSvcMediaStreamHandlerClass *klass = (TpSvcMediaStreamHandlerClass *) g_iface; #define IMPLEMENT(x,suffix) tp_svc_media_stream_handler_implement_##x (\ klass, gabble_media_stream_##x##suffix) IMPLEMENT(codec_choice,); IMPLEMENT(error,_async); IMPLEMENT(hold_state,); IMPLEMENT(native_candidates_prepared,); IMPLEMENT(new_active_candidate_pair,); IMPLEMENT(new_native_candidate,); IMPLEMENT(ready,); IMPLEMENT(set_local_codecs,); IMPLEMENT(stream_state,); IMPLEMENT(supported_codecs,); IMPLEMENT(unhold_failure,); IMPLEMENT(codecs_updated,); IMPLEMENT(supported_header_extensions,); IMPLEMENT(supported_feedback_messages,); #undef IMPLEMENT } WockyJingleMediaRtp * gabble_media_stream_get_content (GabbleMediaStream *self) { /* FIXME: we should fix this whole class up. It relies throughout on * self->priv->content actually secretly being a WockyJingleMediaRtp. */ return WOCKY_JINGLE_MEDIA_RTP (self->priv->content); } void gabble_media_stream_start_telephony_event (GabbleMediaStream *self, guchar event) { DEBUG ("stream %s: %c", self->name, tp_dtmf_event_to_char (event)); tp_svc_media_stream_handler_emit_start_telephony_event ( (TpSvcMediaStreamHandler *) self, event); } void gabble_media_stream_stop_telephony_event (GabbleMediaStream *self) { DEBUG ("stream %s", self->name); tp_svc_media_stream_handler_emit_stop_telephony_event ( (TpSvcMediaStreamHandler *) self); } void gabble_media_stream_add_dtmf_player (GabbleMediaStream *self, TpDTMFPlayer *dtmf_player) { tp_g_signal_connect_object (dtmf_player, "started-tone", G_CALLBACK (gabble_media_stream_start_telephony_event), self, G_CONNECT_SWAPPED); tp_g_signal_connect_object (dtmf_player, "stopped-tone", G_CALLBACK (gabble_media_stream_stop_telephony_event), self, G_CONNECT_SWAPPED); } telepathy-gabble-0.18.2/src/media-stream.h0000644000175000017500000001000112227000321020302 0ustar00smcvsmcv00000000000000/* * gabble-media-stream.h - Header for GabbleMediaStream * Copyright (C) 2006 Collabora Ltd. * Copyright (C) 2006 Nokia Corporation * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 __GABBLE_MEDIA_STREAM_H__ #define __GABBLE_MEDIA_STREAM_H__ #include #include #include G_BEGIN_DECLS typedef enum { STREAM_SIG_STATE_NEW, STREAM_SIG_STATE_SENT, STREAM_SIG_STATE_ACKNOWLEDGED, STREAM_SIG_STATE_REMOVING } StreamSignallingState; typedef guint32 CombinedStreamDirection; typedef struct _GabbleMediaStream GabbleMediaStream; typedef struct _GabbleMediaStreamClass GabbleMediaStreamClass; typedef struct _GabbleMediaStreamPrivate GabbleMediaStreamPrivate; struct _GabbleMediaStreamClass { GObjectClass parent_class; TpDBusPropertiesMixinClass props_class; }; struct _GabbleMediaStream { GObject parent; gchar *name; TpMediaStreamState connection_state; CombinedStreamDirection combined_direction; gboolean playing; GabbleMediaStreamPrivate *priv; }; GType gabble_media_stream_get_type (void); /* TYPE MACROS */ #define GABBLE_TYPE_MEDIA_STREAM \ (gabble_media_stream_get_type ()) #define GABBLE_MEDIA_STREAM(obj) \ (G_TYPE_CHECK_INSTANCE_CAST((obj), GABBLE_TYPE_MEDIA_STREAM, \ GabbleMediaStream)) #define GABBLE_MEDIA_STREAM_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST((klass), GABBLE_TYPE_MEDIA_STREAM, \ GabbleMediaStreamClass)) #define GABBLE_IS_MEDIA_STREAM(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE((obj), GABBLE_TYPE_MEDIA_STREAM)) #define GABBLE_IS_MEDIA_STREAM_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE((klass), GABBLE_TYPE_MEDIA_STREAM)) #define GABBLE_MEDIA_STREAM_GET_CLASS(obj) \ (G_TYPE_INSTANCE_GET_CLASS ((obj), GABBLE_TYPE_MEDIA_STREAM, \ GabbleMediaStreamClass)) #define COMBINED_DIRECTION_GET_DIRECTION(d) \ ((TpMediaStreamDirection) ((d) & TP_MEDIA_STREAM_DIRECTION_BIDIRECTIONAL)) #define COMBINED_DIRECTION_GET_PENDING_SEND(d) \ ((TpMediaStreamPendingSend) ((d) >> 2)) #define MAKE_COMBINED_DIRECTION(d, p) \ ((CombinedStreamDirection) ((d) | ((p) << 2))) gboolean gabble_media_stream_error (GabbleMediaStream *self, guint errnum, const gchar *message, GError **error); void gabble_media_stream_close (GabbleMediaStream *close); void gabble_media_stream_hold (GabbleMediaStream *stream, gboolean hold); gboolean gabble_media_stream_change_direction (GabbleMediaStream *stream, guint requested_dir, GError **error); void gabble_media_stream_accept_pending_local_send (GabbleMediaStream *stream); GabbleMediaStream *gabble_media_stream_new ( TpDBusDaemon *dbus_daemon, const gchar *object_path, WockyJingleContent *content, const gchar *name, guint id, const gchar *nat_traversal, const GPtrArray *relay_info, gboolean local_hold); TpMediaStreamType gabble_media_stream_get_media_type (GabbleMediaStream *self); void gabble_media_stream_add_dtmf_player (GabbleMediaStream *self, TpDTMFPlayer *dtmf_player); WockyJingleMediaRtp *gabble_media_stream_get_content (GabbleMediaStream *self); void gabble_media_stream_start_telephony_event (GabbleMediaStream *self, guchar event); void gabble_media_stream_stop_telephony_event (GabbleMediaStream *self); G_END_DECLS #endif /* #ifndef __GABBLE_MEDIA_STREAM_H__*/ telepathy-gabble-0.18.2/src/media-channel-hold.c0000644000175000017500000003166512227000321021361 0ustar00smcvsmcv00000000000000/* * media-channel-hold.c - Hold and CallState interface implementations * Copyright © 2006–2009 Collabora Ltd. * Copyright © 2006–2009 Nokia Corporation * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 "config.h" #include "media-channel.h" #include "media-channel-internal.h" #include #define DEBUG_FLAG GABBLE_DEBUG_MEDIA #include "debug.h" #include "util.h" /* * Implementation of Channel.Interface.Hold, which deals with placing the peer * on and off hold. */ static void stream_hold_state_changed (GabbleMediaStream *stream G_GNUC_UNUSED, GParamSpec *unused G_GNUC_UNUSED, gpointer data) { GabbleMediaChannel *self = data; GabbleMediaChannelPrivate *priv = self->priv; gboolean all_held = TRUE, any_held = FALSE; guint i; for (i = 0; i < priv->streams->len; i++) { gboolean its_hold; g_object_get (g_ptr_array_index (priv->streams, i), "local-hold", &its_hold, NULL); DEBUG ("Stream at index %u has local-hold=%u", i, (guint) its_hold); all_held = all_held && its_hold; any_held = any_held || its_hold; } DEBUG ("all_held=%u, any_held=%u", (guint) all_held, (guint) any_held); if (all_held && !any_held) { /* There are no streams, move to the desired state immediately */ switch (priv->hold_state) { case TP_LOCAL_HOLD_STATE_PENDING_HOLD: DEBUG ("no streams, moving from pending hold to held"); priv->hold_state = TP_LOCAL_HOLD_STATE_HELD; /* No need to touch the session: send_held (TRUE) is called as soon * as Hold is requested. */ break; case TP_LOCAL_HOLD_STATE_PENDING_UNHOLD: DEBUG ("no streams, moving from pending unhold to unheld"); priv->hold_state = TP_LOCAL_HOLD_STATE_UNHELD; if (priv->session != NULL) wocky_jingle_session_set_local_hold (priv->session, FALSE); break; default: /* nothing to change */ return; } } else if (all_held) { /* Move to state HELD */ switch (priv->hold_state) { case TP_LOCAL_HOLD_STATE_HELD: /* nothing changed */ return; case TP_LOCAL_HOLD_STATE_PENDING_UNHOLD: /* This can happen if the user asks us to hold, then changes their * mind. We make no particular guarantees about stream states when * in PENDING_UNHOLD state, so keep claiming to be in that state */ return; case TP_LOCAL_HOLD_STATE_PENDING_HOLD: /* We wanted to hold, and indeed we have. Yay! Keep whatever * reason code we used for going to PENDING_HOLD */ priv->hold_state = TP_LOCAL_HOLD_STATE_HELD; break; case TP_LOCAL_HOLD_STATE_UNHELD: /* We were previously UNHELD. So why have we gone on hold now? */ DEBUG ("Unexpectedly entered HELD state!"); priv->hold_state = TP_LOCAL_HOLD_STATE_HELD; priv->hold_state_reason = TP_LOCAL_HOLD_STATE_REASON_NONE; break; } } else if (any_held) { switch (priv->hold_state) { case TP_LOCAL_HOLD_STATE_UNHELD: /* The streaming client has spontaneously changed its stream * state. Why? We just don't know */ DEBUG ("Unexpectedly entered PENDING_UNHOLD state!"); priv->hold_state = TP_LOCAL_HOLD_STATE_PENDING_UNHOLD; priv->hold_state_reason = TP_LOCAL_HOLD_STATE_REASON_NONE; break; case TP_LOCAL_HOLD_STATE_HELD: /* Likewise */ DEBUG ("Unexpectedly entered PENDING_HOLD state!"); priv->hold_state = TP_LOCAL_HOLD_STATE_PENDING_HOLD; priv->hold_state_reason = TP_LOCAL_HOLD_STATE_REASON_NONE; break; default: /* nothing particularly interesting - we're trying to change hold * state already, so nothing to signal */ return; } } else { /* Move to state UNHELD */ switch (priv->hold_state) { case TP_LOCAL_HOLD_STATE_UNHELD: /* nothing changed */ return; case TP_LOCAL_HOLD_STATE_PENDING_HOLD: /* This can happen if the user asks us to unhold, then changes their * mind. We make no particular guarantees about stream states when * in PENDING_HOLD state, so keep claiming to be in that state */ return; case TP_LOCAL_HOLD_STATE_PENDING_UNHOLD: /* We wanted to hold, and indeed we have. Yay! Keep whatever * reason code we used for going to PENDING_UNHOLD */ priv->hold_state = TP_LOCAL_HOLD_STATE_UNHELD; break; case TP_LOCAL_HOLD_STATE_HELD: /* We were previously HELD. So why have we gone off hold now? */ DEBUG ("Unexpectedly entered UNHELD state!"); priv->hold_state = TP_LOCAL_HOLD_STATE_UNHELD; priv->hold_state_reason = TP_LOCAL_HOLD_STATE_REASON_NONE; break; } /* Tell the peer what's happened. */ if (priv->session != NULL) wocky_jingle_session_set_local_hold (priv->session, FALSE); } tp_svc_channel_interface_hold_emit_hold_state_changed (self, priv->hold_state, priv->hold_state_reason); } static void stream_unhold_failed (GabbleMediaStream *stream, gpointer data) { GabbleMediaChannel *self = data; GabbleMediaChannelPrivate *priv = self->priv; guint i; DEBUG ("%p: %p", self, stream); /* Unholding failed - let's roll back to Hold state */ priv->hold_state = TP_LOCAL_HOLD_STATE_PENDING_HOLD; priv->hold_state_reason = TP_LOCAL_HOLD_STATE_REASON_RESOURCE_NOT_AVAILABLE; tp_svc_channel_interface_hold_emit_hold_state_changed (self, priv->hold_state, priv->hold_state_reason); /* The stream's state may have changed from unheld to held, so re-poll. * It's possible that all streams are now held, in which case we can stop. */ stream_hold_state_changed (stream, NULL, self); if (priv->hold_state == TP_LOCAL_HOLD_STATE_HELD) return; /* There should be no need to notify the peer, who already thinks they're * on hold, so just tell the streaming client what to do. */ for (i = 0; i < priv->streams->len; i++) { gabble_media_stream_hold (g_ptr_array_index (priv->streams, i), TRUE); } } void gabble_media_channel_hold_stream_closed (GabbleMediaChannel *chan, GabbleMediaStream *stream) { /* A stream closing might cause the "total" hold state to change: * if there's one held and one unheld, and the unheld one closes, * then our state changes from indeterminate to held. */ stream_hold_state_changed (stream, NULL, chan); } /* Implements RequestHold on Telepathy.Channel.Interface.Hold */ static void gabble_media_channel_request_hold (TpSvcChannelInterfaceHold *iface, gboolean hold, DBusGMethodInvocation *context) { GabbleMediaChannel *self = GABBLE_MEDIA_CHANNEL (iface); GabbleMediaChannelPrivate *priv = self->priv; WockyJingleSession *session = priv->session; guint i; TpLocalHoldState old_state = priv->hold_state; DEBUG ("%p: RequestHold(%u)", self, !!hold); if (hold) { if (priv->hold_state == TP_LOCAL_HOLD_STATE_HELD) { DEBUG ("No-op"); tp_svc_channel_interface_hold_return_from_request_hold (context); return; } if (priv->hold_state == TP_LOCAL_HOLD_STATE_UNHELD && session != NULL) wocky_jingle_session_set_local_hold (session, TRUE); priv->hold_state = TP_LOCAL_HOLD_STATE_PENDING_HOLD; } else { if (priv->hold_state == TP_LOCAL_HOLD_STATE_UNHELD) { DEBUG ("No-op"); tp_svc_channel_interface_hold_return_from_request_hold (context); return; } priv->hold_state = TP_LOCAL_HOLD_STATE_PENDING_UNHOLD; } if (old_state != priv->hold_state || priv->hold_state_reason != TP_LOCAL_HOLD_STATE_REASON_REQUESTED) { tp_svc_channel_interface_hold_emit_hold_state_changed (self, priv->hold_state, TP_LOCAL_HOLD_STATE_REASON_REQUESTED); priv->hold_state_reason = TP_LOCAL_HOLD_STATE_REASON_REQUESTED; } if (priv->streams->len == 0) { /* No streams yet! We can go straight to the desired state. */ stream_hold_state_changed (NULL, NULL, self); } else { /* Tell streaming client to release or reacquire resources */ for (i = 0; i < priv->streams->len; i++) { gabble_media_stream_hold (g_ptr_array_index (priv->streams, i), hold); } } tp_svc_channel_interface_hold_return_from_request_hold (context); } /* Implements GetHoldState on Telepathy.Channel.Interface.Hold */ static void gabble_media_channel_get_hold_state (TpSvcChannelInterfaceHold *iface, DBusGMethodInvocation *context) { GabbleMediaChannel *self = (GabbleMediaChannel *) iface; GabbleMediaChannelPrivate *priv = self->priv; tp_svc_channel_interface_hold_return_from_get_hold_state (context, priv->hold_state, priv->hold_state_reason); } void gabble_media_channel_hold_iface_init (gpointer g_iface, gpointer iface_data G_GNUC_UNUSED) { TpSvcChannelInterfaceHoldClass *klass = g_iface; #define IMPLEMENT(x) tp_svc_channel_interface_hold_implement_##x (\ klass, gabble_media_channel_##x) IMPLEMENT(get_hold_state); IMPLEMENT(request_hold); #undef IMPLEMENT } /* * Implementation of Channel.Interface.CallState, which indicates call states * from the peer (such as being put on or off hold, or that the peer's client * is ringing. */ static void remote_state_changed_cb (WockyJingleSession *session, GabbleMediaChannel *self) { GabbleMediaChannelPrivate *priv = self->priv; TpChannelCallStateFlags call_state = 0; if (wocky_jingle_session_get_remote_hold (session)) call_state |= TP_CHANNEL_CALL_STATE_HELD; if (wocky_jingle_session_get_remote_ringing (session)) call_state |= TP_CHANNEL_CALL_STATE_RINGING; DEBUG ("Call state changed to %u (current state %u)", call_state, priv->call_state); if (call_state == priv->call_state) return; priv->call_state = call_state; tp_svc_channel_interface_call_state_emit_call_state_changed (self, priv->peer, call_state); } /* Implements GetCallStates on Channel.Interface.CallState */ static void gabble_media_channel_get_call_states (TpSvcChannelInterfaceCallState *iface, DBusGMethodInvocation *context) { GabbleMediaChannel *self = (GabbleMediaChannel *) iface; GabbleMediaChannelPrivate *priv = self->priv; GHashTable *states = g_hash_table_new (g_direct_hash, g_direct_equal); if (priv->peer != 0) { g_hash_table_insert (states, GUINT_TO_POINTER (priv->peer), GUINT_TO_POINTER (priv->call_state)); } tp_svc_channel_interface_call_state_return_from_get_call_states (context, states); g_hash_table_unref (states); } void gabble_media_channel_call_state_iface_init (gpointer g_iface, gpointer iface_data G_GNUC_UNUSED) { TpSvcChannelInterfaceCallStateClass *klass = g_iface; #define IMPLEMENT(x) tp_svc_channel_interface_call_state_implement_##x (\ klass, gabble_media_channel_##x) IMPLEMENT(get_call_states); #undef IMPLEMENT } /* Called by construct_stream to allow the Hold code to hook itself up to a new * stream. */ void gabble_media_channel_hold_new_stream (GabbleMediaChannel *chan, GabbleMediaStream *stream, WockyJingleMediaRtp *content) { GObject *chan_o = (GObject *) chan; gabble_signal_connect_weak (stream, "unhold-failed", (GCallback) stream_unhold_failed, chan_o); gabble_signal_connect_weak (stream, "notify::local-hold", (GCallback) stream_hold_state_changed, chan_o); /* A stream being added might cause the "total" hold state to change */ stream_hold_state_changed (stream, NULL, chan); } /* Called by _latch_to_session to allow the CallState code to hook itself up to * a new session. */ void gabble_media_channel_hold_latch_to_session (GabbleMediaChannel *chan) { g_assert (chan->priv->session != NULL); /* Watch the active/ringing/held state of the session so we can keep the call * state up to date. */ gabble_signal_connect_weak (chan->priv->session, "remote-state-changed", (GCallback) remote_state_changed_cb, (GObject *) chan); } telepathy-gabble-0.18.2/src/media-channel.c0000644000175000017500000030044112227000321020424 0ustar00smcvsmcv00000000000000/* * gabble-media-channel.c - Source for GabbleMediaChannel * Copyright (C) 2006 Collabora Ltd. * Copyright (C) 2006 Nokia Corporation * @author Ole Andre Vadla Ravnaas * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 "config.h" #include "media-channel.h" #include "media-channel-internal.h" #include #include #include #include #include #define DEBUG_FLAG GABBLE_DEBUG_MEDIA #include "connection.h" #include "debug.h" #include "jingle-tp-util.h" #include "media-factory.h" #include "media-stream.h" #include "namespaces.h" #include "presence-cache.h" #include "presence.h" #include "util.h" #define MAX_STREAMS 99 static void channel_iface_init (gpointer, gpointer); static void dtmf_iface_init (gpointer, gpointer); static void media_signalling_iface_init (gpointer, gpointer); static void streamed_media_iface_init (gpointer, gpointer); static void session_handler_iface_init (gpointer, gpointer); G_DEFINE_TYPE_WITH_CODE (GabbleMediaChannel, gabble_media_channel, G_TYPE_OBJECT, G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CHANNEL, channel_iface_init); G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CHANNEL_INTERFACE_CALL_STATE, gabble_media_channel_call_state_iface_init); G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CHANNEL_INTERFACE_DTMF, dtmf_iface_init); G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CHANNEL_INTERFACE_GROUP, tp_group_mixin_iface_init); G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CHANNEL_INTERFACE_HOLD, gabble_media_channel_hold_iface_init); G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CHANNEL_INTERFACE_MEDIA_SIGNALLING, media_signalling_iface_init); G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CHANNEL_TYPE_STREAMED_MEDIA, streamed_media_iface_init); G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_PROPERTIES_INTERFACE, tp_properties_mixin_iface_init); G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_DBUS_PROPERTIES, tp_dbus_properties_mixin_iface_init); G_IMPLEMENT_INTERFACE (TP_TYPE_EXPORTABLE_CHANNEL, NULL); G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_MEDIA_SESSION_HANDLER, session_handler_iface_init); G_IMPLEMENT_INTERFACE (TP_TYPE_CHANNEL_IFACE, NULL)); static const gchar *gabble_media_channel_interfaces[] = { TP_IFACE_CHANNEL_INTERFACE_CALL_STATE, TP_IFACE_CHANNEL_INTERFACE_DTMF, TP_IFACE_CHANNEL_INTERFACE_GROUP, TP_IFACE_CHANNEL_INTERFACE_HOLD, TP_IFACE_CHANNEL_INTERFACE_MEDIA_SIGNALLING, TP_IFACE_PROPERTIES_INTERFACE, TP_IFACE_MEDIA_SESSION_HANDLER, NULL }; /* properties */ enum { PROP_OBJECT_PATH = 1, PROP_CHANNEL_TYPE, PROP_HANDLE_TYPE, PROP_HANDLE, PROP_TARGET_ID, PROP_INITIAL_PEER, PROP_PEER_IN_RP, PROP_PEER, PROP_REQUESTED, PROP_CONNECTION, PROP_CREATOR, PROP_CREATOR_ID, PROP_INTERFACES, PROP_CHANNEL_DESTROYED, PROP_CHANNEL_PROPERTIES, PROP_INITIAL_AUDIO, PROP_INITIAL_VIDEO, PROP_IMMUTABLE_STREAMS, PROP_CURRENTLY_SENDING_TONES, PROP_INITIAL_TONES, PROP_DEFERRED_TONES, /* TP properties (see also below) */ PROP_NAT_TRAVERSAL, PROP_STUN_SERVER, PROP_STUN_PORT, PROP_GTALK_P2P_RELAY_TOKEN, PROP_SESSION, LAST_PROPERTY }; /* TP properties */ enum { CHAN_PROP_NAT_TRAVERSAL = 0, CHAN_PROP_STUN_SERVER, CHAN_PROP_STUN_PORT, CHAN_PROP_GTALK_P2P_RELAY_TOKEN, NUM_CHAN_PROPS, INVALID_CHAN_PROP }; const TpPropertySignature channel_property_signatures[NUM_CHAN_PROPS] = { { "nat-traversal", G_TYPE_STRING }, { "stun-server", G_TYPE_STRING }, { "stun-port", G_TYPE_UINT }, { "gtalk-p2p-relay-token", G_TYPE_STRING } }; typedef struct { GabbleMediaChannel *self; WockyJingleContent *content; gulong removed_id; gchar *name; const gchar *nat_traversal; gboolean initial; } StreamCreationData; struct _delayed_request_streams_ctx { GabbleMediaChannel *chan; gulong caps_disco_id; gulong unsure_period_ended_id; guint contact_handle; GArray *types; GFunc succeeded_cb; GFunc failed_cb; gpointer context; }; static void destroy_request (struct _delayed_request_streams_ctx *ctx, gpointer user_data); static void tones_deferred_cb (GabbleMediaChannel *self, const gchar *tones, TpDTMFPlayer *dtmf_player) { DEBUG ("waiting for user to continue sending '%s'", tones); g_free (self->priv->deferred_tones); self->priv->deferred_tones = g_strdup (tones); tp_svc_channel_interface_dtmf_emit_tones_deferred (self, tones); } static void gabble_media_channel_init (GabbleMediaChannel *self) { GabbleMediaChannelPrivate *priv = G_TYPE_INSTANCE_GET_PRIVATE (self, GABBLE_TYPE_MEDIA_CHANNEL, GabbleMediaChannelPrivate); self->priv = priv; priv->next_stream_id = 1; priv->delayed_request_streams = g_ptr_array_sized_new (1); priv->streams = g_ptr_array_sized_new (1); /* initialize properties mixin */ tp_properties_mixin_init (G_OBJECT (self), G_STRUCT_OFFSET ( GabbleMediaChannel, properties)); priv->dtmf_player = tp_dtmf_player_new (); tp_g_signal_connect_object (priv->dtmf_player, "finished", G_CALLBACK (tp_svc_channel_interface_dtmf_emit_stopped_tones), self, G_CONNECT_SWAPPED); tp_g_signal_connect_object (priv->dtmf_player, "tones-deferred", G_CALLBACK (tones_deferred_cb), self, G_CONNECT_SWAPPED); } static void session_state_changed_cb (WockyJingleSession *session, GParamSpec *arg1, GabbleMediaChannel *channel); static void session_terminated_cb (WockyJingleSession *session, gboolean local_terminator, WockyJingleReason reason, const gchar *text, gpointer user_data); static void session_new_content_cb (WockyJingleSession *session, WockyJingleContent *c, gpointer user_data); static void create_stream_from_content (GabbleMediaChannel *chan, WockyJingleContent *c, gboolean initial); static gboolean contact_is_media_capable (GabbleMediaChannel *chan, TpHandle peer, gboolean *wait, GError **error); static void stream_creation_data_cancel (gpointer p, gpointer unused); static void session_content_rejected_cb (WockyJingleSession *session, WockyJingleContent *c, WockyJingleReason reason, const gchar *message, gpointer user_data); static void create_initial_streams (GabbleMediaChannel *chan) { GabbleMediaChannelPrivate *priv = chan->priv; GList *contents, *li; contents = wocky_jingle_session_get_contents (priv->session); for (li = contents; li; li = li->next) { WockyJingleContent *c = li->data; /* I'm so sorry. */ if (G_OBJECT_TYPE (c) == WOCKY_TYPE_JINGLE_MEDIA_RTP) { guint media_type; g_object_get (c, "media-type", &media_type, NULL); switch (media_type) { case WOCKY_JINGLE_MEDIA_TYPE_AUDIO: priv->initial_audio = TRUE; break; case WOCKY_JINGLE_MEDIA_TYPE_VIDEO: priv->initial_video = TRUE; break; default: /* smell? */ DEBUG ("unknown rtp media type %u", media_type); } } else { g_assert_not_reached (); } create_stream_from_content (chan, c, TRUE); } DEBUG ("initial_audio: %s, initial_video: %s", priv->initial_audio ? "true" : "false", priv->initial_video ? "true" : "false"); g_list_free (contents); } static void _latch_to_session (GabbleMediaChannel *chan) { GabbleMediaChannelPrivate *priv = chan->priv; g_assert (priv->session != NULL); DEBUG ("%p: Latching onto session %p", chan, priv->session); g_signal_connect (priv->session, "notify::state", (GCallback) session_state_changed_cb, chan); g_signal_connect (priv->session, "new-content", (GCallback) session_new_content_cb, chan); g_signal_connect (priv->session, "terminated", (GCallback) session_terminated_cb, chan); g_signal_connect (priv->session, "content-rejected", (GCallback) session_content_rejected_cb, chan); gabble_media_channel_hold_latch_to_session (chan); g_assert (priv->streams->len == 0); tp_svc_channel_interface_media_signalling_emit_new_session_handler ( G_OBJECT (chan), priv->object_path, "rtp"); } static void create_session (GabbleMediaChannel *chan, const gchar *jid, WockyJingleDialect dialect) { GabbleMediaChannelPrivate *priv = chan->priv; gboolean local_hold = (priv->hold_state != TP_LOCAL_HOLD_STATE_UNHELD); WockyJingleFactory *jf; g_assert (priv->session == NULL); DEBUG ("%p: Creating new outgoing session", chan); jf = gabble_jingle_mint_get_factory (priv->conn->jingle_mint); g_return_if_fail (jf != NULL); priv->session = g_object_ref ( wocky_jingle_factory_create_session (jf, jid, dialect, local_hold)); _latch_to_session (chan); } static GObject * gabble_media_channel_constructor (GType type, guint n_props, GObjectConstructParam *props) { GObject *obj; GabbleMediaChannelPrivate *priv; TpBaseConnection *conn; TpDBusDaemon *bus; TpIntset *set; TpHandleRepoIface *contact_handles; WockyJingleInfo *ji; const gchar *relay_token; GList *stun_servers; obj = G_OBJECT_CLASS (gabble_media_channel_parent_class)-> constructor (type, n_props, props); priv = GABBLE_MEDIA_CHANNEL (obj)->priv; conn = (TpBaseConnection *) priv->conn; contact_handles = tp_base_connection_get_handles (conn, TP_HANDLE_TYPE_CONTACT); /* register object on the bus */ bus = tp_base_connection_get_dbus_daemon (conn); tp_dbus_daemon_register_object (bus, priv->object_path, obj); tp_group_mixin_init (obj, G_STRUCT_OFFSET (GabbleMediaChannel, group), contact_handles, tp_base_connection_get_self_handle (conn)); if (priv->session != NULL) { priv->peer = ensure_handle_from_contact (priv->conn, wocky_jingle_session_get_peer_contact (priv->session)); g_return_val_if_fail (priv->peer != 0, NULL); priv->creator = priv->peer; } else { priv->creator = tp_base_connection_get_self_handle (conn); } /* automatically add creator to channel, but also ref them again (because * priv->creator is the InitiatorHandle) */ g_assert (priv->creator != 0); set = tp_intset_new_containing (priv->creator); tp_group_mixin_change_members (obj, "", set, NULL, NULL, NULL, 0, TP_CHANNEL_GROUP_CHANGE_REASON_NONE); tp_intset_destroy (set); /* We implement the 0.17.6 properties correctly, and can include a message * when ending a call. */ tp_group_mixin_change_flags (obj, TP_CHANNEL_GROUP_FLAG_PROPERTIES | TP_CHANNEL_GROUP_FLAG_MESSAGE_REMOVE | TP_CHANNEL_GROUP_FLAG_MESSAGE_REJECT | TP_CHANNEL_GROUP_FLAG_MESSAGE_RESCIND, 0); /* Set up Google relay related properties */ ji = gabble_jingle_mint_get_info (priv->conn->jingle_mint); stun_servers = wocky_jingle_info_get_stun_servers (ji); if (stun_servers != NULL) { WockyStunServer *stun_server = stun_servers->data; g_object_set (obj, "stun-server", stun_server->address, "stun-port", (guint) stun_server->port, NULL); g_list_free (stun_servers); } relay_token = wocky_jingle_info_get_google_relay_token (ji); if (relay_token != NULL) { g_object_set (obj, "gtalk-p2p-relay-token", relay_token, NULL); } if (priv->session != NULL) { /* This is an incoming call; make us local pending and don't set any * group flags (all we can do is add or remove ourselves, which is always * valid per the spec) */ set = tp_intset_new_containing (tp_base_connection_get_self_handle (conn)); tp_group_mixin_change_members (obj, "", NULL, NULL, set, NULL, priv->peer, TP_CHANNEL_GROUP_CHANGE_REASON_INVITED); tp_intset_destroy (set); /* Set up signal callbacks, emit session handler, initialize streams, * figure out InitialAudio and InitialVideo */ _latch_to_session (GABBLE_MEDIA_CHANNEL (obj)); create_initial_streams (GABBLE_MEDIA_CHANNEL (obj)); } else { /* This is an outgoing call. */ if (priv->initial_peer != 0) { if (priv->peer_in_rp) { /* This channel was created with RequestChannel(SM, Contact, h) * so the peer should start out in remote pending. */ set = tp_intset_new_containing (priv->initial_peer); tp_group_mixin_change_members (obj, "", NULL, NULL, NULL, set, tp_base_connection_get_self_handle (conn), TP_CHANNEL_GROUP_CHANGE_REASON_INVITED); tp_intset_destroy (set); } /* else this channel was created with CreateChannel or EnsureChannel, * so don't. */ } else { /* This channel was created with RequestChannel(SM, None, 0). */ /* The peer can't be in remote pending */ g_assert (!priv->peer_in_rp); /* The UI may call AddMembers([h], "") before calling * RequestStreams(h, [...]). */ tp_group_mixin_change_flags (obj, TP_CHANNEL_GROUP_FLAG_CAN_ADD, 0); } } /* If this is a Google session, let's set ImmutableStreams */ if (priv->session != NULL) { priv->immutable_streams = !wocky_jingle_session_can_modify_contents (priv->session); } /* If there's no session yet, but we know who the peer will be, and we have * presence for them, we can set ImmutableStreams using the same algorithm as * for old-style capabilities. If we don't know who the peer will be, then * the client is using an old calling convention and doesn't need to know * this. */ else if (priv->initial_peer != 0) { GabblePresence *presence = gabble_presence_cache_get ( priv->conn->presence_cache, priv->initial_peer); TpChannelMediaCapabilities flags = 0; if (presence != NULL) flags = _gabble_media_factory_caps_to_typeflags ( gabble_presence_peek_caps (presence)); if (flags & TP_CHANNEL_MEDIA_CAPABILITY_IMMUTABLE_STREAMS) priv->immutable_streams = TRUE; } return obj; } static void gabble_media_channel_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { GabbleMediaChannel *chan = GABBLE_MEDIA_CHANNEL (object); GabbleMediaChannelPrivate *priv = chan->priv; TpBaseConnection *base_conn = (TpBaseConnection *) priv->conn; const gchar *param_name; guint tp_property_id; switch (property_id) { case PROP_OBJECT_PATH: g_value_set_string (value, priv->object_path); break; case PROP_CHANNEL_TYPE: g_value_set_static_string (value, TP_IFACE_CHANNEL_TYPE_STREAMED_MEDIA); break; case PROP_HANDLE_TYPE: /* This is used to implement TargetHandleType, which is immutable. If * the peer was known at channel-creation time, this will be Contact; * otherwise, it must be None even if we subsequently learn who the peer * is. */ if (priv->initial_peer != 0) g_value_set_uint (value, TP_HANDLE_TYPE_CONTACT); else g_value_set_uint (value, TP_HANDLE_TYPE_NONE); break; case PROP_INITIAL_PEER: case PROP_HANDLE: /* As above: TargetHandle is immutable, so non-0 only if the peer handle * was known at creation time. */ g_value_set_uint (value, priv->initial_peer); break; case PROP_TARGET_ID: /* As above. */ if (priv->initial_peer != 0) { TpHandleRepoIface *repo = tp_base_connection_get_handles ( base_conn, TP_HANDLE_TYPE_CONTACT); const gchar *target_id = tp_handle_inspect (repo, priv->initial_peer); g_value_set_string (value, target_id); } else { g_value_set_static_string (value, ""); } break; case PROP_PEER: { TpHandle peer = 0; if (priv->initial_peer != 0) peer = priv->initial_peer; else peer = priv->peer; g_value_set_uint (value, peer); break; } case PROP_CONNECTION: g_value_set_object (value, priv->conn); break; case PROP_CREATOR: g_value_set_uint (value, priv->creator); break; case PROP_CREATOR_ID: { TpHandleRepoIface *repo = tp_base_connection_get_handles ( base_conn, TP_HANDLE_TYPE_CONTACT); g_value_set_string (value, tp_handle_inspect (repo, priv->creator)); } break; case PROP_REQUESTED: g_value_set_boolean (value, (priv->creator == tp_base_connection_get_self_handle (base_conn))); break; case PROP_INTERFACES: g_value_set_boxed (value, gabble_media_channel_interfaces); break; case PROP_CHANNEL_DESTROYED: g_value_set_boolean (value, priv->closed); break; case PROP_CHANNEL_PROPERTIES: g_value_take_boxed (value, tp_dbus_properties_mixin_make_properties_hash (object, TP_IFACE_CHANNEL, "TargetHandle", TP_IFACE_CHANNEL, "TargetHandleType", TP_IFACE_CHANNEL, "ChannelType", TP_IFACE_CHANNEL, "TargetID", TP_IFACE_CHANNEL, "InitiatorHandle", TP_IFACE_CHANNEL, "InitiatorID", TP_IFACE_CHANNEL, "Requested", TP_IFACE_CHANNEL, "Interfaces", TP_IFACE_CHANNEL_TYPE_STREAMED_MEDIA, "InitialAudio", TP_IFACE_CHANNEL_TYPE_STREAMED_MEDIA, "InitialVideo", TP_IFACE_CHANNEL_TYPE_STREAMED_MEDIA, "ImmutableStreams", NULL)); break; case PROP_SESSION: g_value_set_object (value, priv->session); break; case PROP_INITIAL_AUDIO: g_value_set_boolean (value, priv->initial_audio); break; case PROP_INITIAL_VIDEO: g_value_set_boolean (value, priv->initial_video); break; case PROP_IMMUTABLE_STREAMS: g_value_set_boolean (value, priv->immutable_streams); break; case PROP_CURRENTLY_SENDING_TONES: g_value_set_boolean (value, tp_dtmf_player_is_active (priv->dtmf_player)); break; case PROP_INITIAL_TONES: /* FIXME: stub */ g_value_set_static_string (value, ""); break; case PROP_DEFERRED_TONES: if (priv->deferred_tones != NULL) g_value_set_string (value, priv->deferred_tones); else g_value_set_static_string (value, ""); break; default: param_name = g_param_spec_get_name (pspec); if (tp_properties_mixin_has_property (object, param_name, &tp_property_id)) { GValue *tp_property_value = chan->properties.properties[tp_property_id].value; if (tp_property_value) { g_value_copy (tp_property_value, value); return; } } G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void gabble_media_channel_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { GabbleMediaChannel *chan = GABBLE_MEDIA_CHANNEL (object); GabbleMediaChannelPrivate *priv = chan->priv; const gchar *param_name; guint tp_property_id; switch (property_id) { case PROP_OBJECT_PATH: g_free (priv->object_path); priv->object_path = g_value_dup_string (value); break; case PROP_HANDLE_TYPE: case PROP_HANDLE: case PROP_CHANNEL_TYPE: /* these properties are writable in the interface, but not actually * meaningfully changable on this channel, so we do nothing */ break; case PROP_CONNECTION: priv->conn = g_value_get_object (value); break; case PROP_CREATOR: priv->creator = g_value_get_uint (value); break; case PROP_INITIAL_PEER: priv->initial_peer = g_value_get_uint (value); break; case PROP_PEER_IN_RP: priv->peer_in_rp = g_value_get_boolean (value); break; case PROP_SESSION: g_assert (priv->session == NULL); priv->session = g_value_dup_object (value); if (priv->session != NULL) { } break; case PROP_INITIAL_AUDIO: priv->initial_audio = g_value_get_boolean (value); break; case PROP_INITIAL_VIDEO: priv->initial_video = g_value_get_boolean (value); break; default: param_name = g_param_spec_get_name (pspec); if (tp_properties_mixin_has_property (object, param_name, &tp_property_id)) { tp_properties_mixin_change_value (object, tp_property_id, value, NULL); tp_properties_mixin_change_flags (object, tp_property_id, TP_PROPERTY_FLAG_READ, 0, NULL); return; } G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void gabble_media_channel_dispose (GObject *object); static void gabble_media_channel_finalize (GObject *object); static gboolean gabble_media_channel_add_member (GObject *obj, TpHandle handle, const gchar *message, GError **error); static gboolean gabble_media_channel_remove_member (GObject *obj, TpHandle handle, const gchar *message, guint reason, GError **error); static void gabble_media_channel_class_init (GabbleMediaChannelClass *gabble_media_channel_class) { static TpDBusPropertiesMixinPropImpl channel_props[] = { { "TargetHandleType", "handle-type", NULL }, { "TargetHandle", "handle", NULL }, { "TargetID", "target-id", NULL }, { "ChannelType", "channel-type", NULL }, { "Interfaces", "interfaces", NULL }, { "Requested", "requested", NULL }, { "InitiatorHandle", "creator", NULL }, { "InitiatorID", "creator-id", NULL }, { NULL } }; static TpDBusPropertiesMixinPropImpl streamed_media_props[] = { { "ImmutableStreams", "immutable-streams", NULL }, { "InitialAudio", "initial-audio", NULL }, { "InitialVideo", "initial-video", NULL }, { NULL } }; static TpDBusPropertiesMixinPropImpl dtmf_props[] = { { "CurrentlySendingTones", "currently-sending-tones", NULL }, { "InitialTones", "initial-tones", NULL }, { "DeferredTones", "deferred-tones", NULL }, { NULL } }; static TpDBusPropertiesMixinIfaceImpl prop_interfaces[] = { { TP_IFACE_CHANNEL, tp_dbus_properties_mixin_getter_gobject_properties, NULL, channel_props, }, { TP_IFACE_CHANNEL_TYPE_STREAMED_MEDIA, tp_dbus_properties_mixin_getter_gobject_properties, NULL, streamed_media_props, }, { TP_IFACE_CHANNEL_INTERFACE_DTMF, tp_dbus_properties_mixin_getter_gobject_properties, NULL, dtmf_props, }, { NULL } }; GObjectClass *object_class = G_OBJECT_CLASS (gabble_media_channel_class); GParamSpec *param_spec; g_type_class_add_private (gabble_media_channel_class, sizeof (GabbleMediaChannelPrivate)); object_class->constructor = gabble_media_channel_constructor; object_class->get_property = gabble_media_channel_get_property; object_class->set_property = gabble_media_channel_set_property; object_class->dispose = gabble_media_channel_dispose; object_class->finalize = gabble_media_channel_finalize; g_object_class_override_property (object_class, PROP_OBJECT_PATH, "object-path"); g_object_class_override_property (object_class, PROP_CHANNEL_TYPE, "channel-type"); g_object_class_override_property (object_class, PROP_HANDLE_TYPE, "handle-type"); g_object_class_override_property (object_class, PROP_HANDLE, "handle"); g_object_class_override_property (object_class, PROP_CHANNEL_DESTROYED, "channel-destroyed"); g_object_class_override_property (object_class, PROP_CHANNEL_PROPERTIES, "channel-properties"); param_spec = g_param_spec_string ("target-id", "Target JID", "Currently empty, because this channel always has handle 0.", NULL, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, PROP_TARGET_ID, param_spec); param_spec = g_param_spec_uint ("initial-peer", "Other participant", "The TpHandle representing the other participant in the channel if known " "at construct-time; 0 if the other participant was unknown at the time " "of channel creation", 0, G_MAXUINT32, 0, G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, PROP_INITIAL_PEER, param_spec); param_spec = g_param_spec_boolean ("peer-in-rp", "Peer initially in Remote Pending?", "True if the channel was created with the most-deprecated " "RequestChannels form, and so the peer should be in Remote Pending " "before any XML has been sent.", FALSE, G_PARAM_CONSTRUCT_ONLY | G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, PROP_PEER_IN_RP, param_spec); param_spec = g_param_spec_uint ("peer", "Other participant", "The TpHandle representing the other participant in the channel if " "currently known; 0 if this is an anonymous channel on which " "RequestStreams has not yet been called.", 0, G_MAXUINT32, 0, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, PROP_PEER, param_spec); param_spec = g_param_spec_object ("connection", "GabbleConnection object", "Gabble connection object that owns this media channel object.", GABBLE_TYPE_CONNECTION, G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, PROP_CONNECTION, param_spec); param_spec = g_param_spec_uint ("creator", "Channel creator", "The TpHandle representing the contact who created the channel.", 0, G_MAXUINT32, 0, G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, PROP_CREATOR, param_spec); param_spec = g_param_spec_string ("creator-id", "Creator bare JID", "The bare JID obtained by inspecting the creator handle.", NULL, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, PROP_CREATOR_ID, param_spec); param_spec = g_param_spec_boolean ("requested", "Requested?", "True if this channel was requested by the local user", FALSE, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, PROP_REQUESTED, param_spec); param_spec = g_param_spec_boxed ("interfaces", "Extra D-Bus interfaces", "Additional Channel.Interface.* interfaces", G_TYPE_STRV, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, PROP_INTERFACES, param_spec); param_spec = g_param_spec_string ("nat-traversal", "NAT traversal", "NAT traversal mechanism.", "gtalk-p2p", G_PARAM_CONSTRUCT | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, PROP_NAT_TRAVERSAL, param_spec); param_spec = g_param_spec_string ("stun-server", "STUN server", "IP or address of STUN server.", NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, PROP_STUN_SERVER, param_spec); param_spec = g_param_spec_uint ("stun-port", "STUN port", "UDP port of STUN server.", 0, G_MAXUINT16, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, PROP_STUN_PORT, param_spec); param_spec = g_param_spec_string ("gtalk-p2p-relay-token", "GTalk P2P Relay Token", "Magic token to authenticate with the Google Talk relay server.", NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, PROP_GTALK_P2P_RELAY_TOKEN, param_spec); param_spec = g_param_spec_object ("session", "WockyJingleSession object", "Jingle session associated with this media channel object.", WOCKY_TYPE_JINGLE_SESSION, G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB); g_object_class_install_property (object_class, PROP_SESSION, param_spec); param_spec = g_param_spec_boolean ("initial-audio", "InitialAudio", "Whether the channel initially contained an audio stream", FALSE, G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, PROP_INITIAL_AUDIO, param_spec); param_spec = g_param_spec_boolean ("initial-video", "InitialVideo", "Whether the channel initially contained an video stream", FALSE, G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, PROP_INITIAL_VIDEO, param_spec); param_spec = g_param_spec_boolean ("immutable-streams", "ImmutableStreams", "Whether the set of streams on this channel are fixed once requested", FALSE, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, PROP_IMMUTABLE_STREAMS, param_spec); param_spec = g_param_spec_boolean ("currently-sending-tones", "CurrentlySendingTones", "True if a DTMF tone is being sent", FALSE, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, PROP_CURRENTLY_SENDING_TONES, param_spec); param_spec = g_param_spec_string ("initial-tones", "InitialTones", "Initial DTMF tones to be sent in the first audio stream", "", G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, PROP_INITIAL_TONES, param_spec); param_spec = g_param_spec_string ("deferred-tones", "DeferredTones", "DTMF tones that followed a 'w' or 'W', to be resumed on user request", "", G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, PROP_DEFERRED_TONES, param_spec); tp_properties_mixin_class_init (object_class, G_STRUCT_OFFSET (GabbleMediaChannelClass, properties_class), channel_property_signatures, NUM_CHAN_PROPS, NULL); gabble_media_channel_class->dbus_props_class.interfaces = prop_interfaces; tp_dbus_properties_mixin_class_init (object_class, G_STRUCT_OFFSET (GabbleMediaChannelClass, dbus_props_class)); tp_group_mixin_class_init (object_class, G_STRUCT_OFFSET (GabbleMediaChannelClass, group_class), gabble_media_channel_add_member, NULL); tp_group_mixin_class_set_remove_with_reason_func (object_class, gabble_media_channel_remove_member); tp_group_mixin_class_allow_self_removal (object_class); tp_group_mixin_init_dbus_properties (object_class); } void gabble_media_channel_dispose (GObject *object) { GabbleMediaChannel *self = GABBLE_MEDIA_CHANNEL (object); GabbleMediaChannelPrivate *priv = self->priv; GList *l; if (priv->dispose_has_run) return; DEBUG ("called"); priv->dispose_has_run = TRUE; if (!priv->closed) gabble_media_channel_close (self); g_assert (priv->closed); g_assert (priv->session == NULL); /* Since the session's dead, all the stream_creation_datas should have been * cancelled (which is indicated by their 'content' being NULL). */ for (l = priv->stream_creation_datas; l != NULL; l = l->next) { StreamCreationData *d = l->data; g_assert (d->content == NULL); } g_list_free (priv->stream_creation_datas); priv->stream_creation_datas = NULL; if (priv->delayed_request_streams != NULL) { g_ptr_array_foreach (priv->delayed_request_streams, (GFunc) destroy_request, NULL); g_ptr_array_unref (priv->delayed_request_streams); priv->delayed_request_streams = NULL; } /* All of the streams should have closed in response to the contents being * removed when the call ended. */ g_assert (priv->streams->len == 0); g_ptr_array_unref (priv->streams); priv->streams = NULL; if (G_OBJECT_CLASS (gabble_media_channel_parent_class)->dispose) G_OBJECT_CLASS (gabble_media_channel_parent_class)->dispose (object); } void gabble_media_channel_finalize (GObject *object) { GabbleMediaChannel *self = GABBLE_MEDIA_CHANNEL (object); GabbleMediaChannelPrivate *priv = self->priv; g_free (priv->object_path); tp_clear_pointer (&self->priv->deferred_tones, g_free); tp_group_mixin_finalize (object); tp_properties_mixin_finalize (object); G_OBJECT_CLASS (gabble_media_channel_parent_class)->finalize (object); } /** * gabble_media_channel_close_async: * * Implements D-Bus method Close * on interface org.freedesktop.Telepathy.Channel */ static void gabble_media_channel_close_async (TpSvcChannel *iface, DBusGMethodInvocation *context) { GabbleMediaChannel *self = GABBLE_MEDIA_CHANNEL (iface); if (DEBUGGING) { gchar *caller = dbus_g_method_get_sender (context); DEBUG ("called by %s", caller); g_free (caller); } gabble_media_channel_close (self); tp_svc_channel_return_from_close (context); } void gabble_media_channel_close (GabbleMediaChannel *self) { GabbleMediaChannelPrivate *priv = self->priv; DEBUG ("called on %p", self); if (!priv->closed) { priv->closed = TRUE; if (priv->session != NULL) wocky_jingle_session_terminate (priv->session, WOCKY_JINGLE_REASON_UNKNOWN, NULL, NULL); tp_svc_channel_emit_closed (self); } } /** * gabble_media_channel_get_channel_type * * Implements D-Bus method GetChannelType * on interface org.freedesktop.Telepathy.Channel */ static void gabble_media_channel_get_channel_type (TpSvcChannel *iface, DBusGMethodInvocation *context) { tp_svc_channel_return_from_get_channel_type (context, TP_IFACE_CHANNEL_TYPE_STREAMED_MEDIA); } /** * gabble_media_channel_get_handle * * Implements D-Bus method GetHandle * on interface org.freedesktop.Telepathy.Channel */ static void gabble_media_channel_get_handle (TpSvcChannel *iface, DBusGMethodInvocation *context) { GabbleMediaChannel *self = GABBLE_MEDIA_CHANNEL (iface); if (self->priv->initial_peer == 0) tp_svc_channel_return_from_get_handle (context, TP_HANDLE_TYPE_NONE, 0); else tp_svc_channel_return_from_get_handle (context, TP_HANDLE_TYPE_CONTACT, self->priv->initial_peer); } /** * gabble_media_channel_get_interfaces * * Implements D-Bus method GetInterfaces * on interface org.freedesktop.Telepathy.Channel */ static void gabble_media_channel_get_interfaces (TpSvcChannel *iface, DBusGMethodInvocation *context) { tp_svc_channel_return_from_get_interfaces (context, gabble_media_channel_interfaces); } /** * gabble_media_channel_get_session_handlers * * Implements D-Bus method GetSessionHandlers * on interface org.freedesktop.Telepathy.Channel.Interface.MediaSignalling */ static void gabble_media_channel_get_session_handlers (TpSvcChannelInterfaceMediaSignalling *iface, DBusGMethodInvocation *context) { GabbleMediaChannel *self = GABBLE_MEDIA_CHANNEL (iface); GabbleMediaChannelPrivate *priv; GPtrArray *ret; GType info_type = TP_STRUCT_TYPE_MEDIA_SESSION_HANDLER_INFO; g_assert (GABBLE_IS_MEDIA_CHANNEL (self)); priv = self->priv; if (priv->session) { GValue handler = { 0, }; g_value_init (&handler, info_type); g_value_take_boxed (&handler, dbus_g_type_specialized_construct (info_type)); dbus_g_type_struct_set (&handler, 0, priv->object_path, 1, "rtp", G_MAXUINT); ret = g_ptr_array_sized_new (1); g_ptr_array_add (ret, g_value_get_boxed (&handler)); } else { ret = g_ptr_array_sized_new (0); } tp_svc_channel_interface_media_signalling_return_from_get_session_handlers ( context, ret); g_ptr_array_foreach (ret, (GFunc) g_value_array_free, NULL); g_ptr_array_unref (ret); } /** * make_stream_list: * * Creates an array of MediaStreamInfo structs. * * Precondition: priv->session is non-NULL. */ static GPtrArray * make_stream_list (GabbleMediaChannel *self, guint len, GabbleMediaStream **streams) { GabbleMediaChannelPrivate *priv = self->priv; GPtrArray *ret; guint i; GType info_type = TP_STRUCT_TYPE_MEDIA_STREAM_INFO; g_assert (priv->session != NULL); ret = g_ptr_array_sized_new (len); for (i = 0; i < len; i++) { GValue entry = { 0, }; guint id; TpMediaStreamType type; TpMediaStreamState connection_state; CombinedStreamDirection combined_direction; g_object_get (streams[i], "id", &id, "media-type", &type, "connection-state", &connection_state, "combined-direction", &combined_direction, NULL); g_value_init (&entry, info_type); g_value_take_boxed (&entry, dbus_g_type_specialized_construct (info_type)); dbus_g_type_struct_set (&entry, 0, id, 1, priv->peer, 2, type, 3, connection_state, 4, COMBINED_DIRECTION_GET_DIRECTION (combined_direction), 5, COMBINED_DIRECTION_GET_PENDING_SEND (combined_direction), G_MAXUINT); g_ptr_array_add (ret, g_value_get_boxed (&entry)); } return ret; } /** * gabble_media_channel_list_streams * * Implements D-Bus method ListStreams * on interface org.freedesktop.Telepathy.Channel.Type.StreamedMedia */ static void gabble_media_channel_list_streams (TpSvcChannelTypeStreamedMedia *iface, DBusGMethodInvocation *context) { GabbleMediaChannel *self = GABBLE_MEDIA_CHANNEL (iface); GabbleMediaChannelPrivate *priv; GPtrArray *ret; g_assert (GABBLE_IS_MEDIA_CHANNEL (self)); priv = self->priv; /* If the session has not yet started, or has ended, return an empty array. */ if (priv->session == NULL) { ret = g_ptr_array_new (); } else { ret = make_stream_list (self, priv->streams->len, (GabbleMediaStream **) priv->streams->pdata); } tp_svc_channel_type_streamed_media_return_from_list_streams (context, ret); g_ptr_array_foreach (ret, (GFunc) g_value_array_free, NULL); g_ptr_array_unref (ret); } static GabbleMediaStream * _find_stream_by_id (GabbleMediaChannel *chan, guint stream_id, GError **error) { GabbleMediaChannelPrivate *priv; guint i; g_assert (GABBLE_IS_MEDIA_CHANNEL (chan)); priv = chan->priv; for (i = 0; i < priv->streams->len; i++) { GabbleMediaStream *stream = g_ptr_array_index (priv->streams, i); guint id; g_object_get (stream, "id", &id, NULL); if (id == stream_id) return stream; } g_set_error (error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT, "given stream id %u does not exist", stream_id); return NULL; } static GabbleMediaStream * _find_stream_by_content (GabbleMediaChannel *chan, WockyJingleContent *content) { GabbleMediaChannelPrivate *priv; guint i; g_assert (GABBLE_IS_MEDIA_CHANNEL (chan)); priv = chan->priv; for (i = 0; i < priv->streams->len; i++) { GabbleMediaStream *stream = g_ptr_array_index (priv->streams, i); WockyJingleContent *c = WOCKY_JINGLE_CONTENT ( gabble_media_stream_get_content (stream)); if (content == c) return stream; } return NULL; } /** * gabble_media_channel_remove_streams * * Implements DBus method RemoveStreams * on interface org.freedesktop.Telepathy.Channel.Type.StreamedMedia */ static void gabble_media_channel_remove_streams (TpSvcChannelTypeStreamedMedia *iface, const GArray * streams, DBusGMethodInvocation *context) { GabbleMediaChannel *obj = GABBLE_MEDIA_CHANNEL (iface); GabbleMediaChannelPrivate *priv; GPtrArray *stream_objs; GError *error = NULL; guint i; g_assert (GABBLE_IS_MEDIA_CHANNEL (obj)); priv = obj->priv; if (!wocky_jingle_session_can_modify_contents (priv->session)) { GError e = { TP_ERROR, TP_ERROR_NOT_IMPLEMENTED, "Streams can't be removed from Google Talk calls" }; dbus_g_method_return_error (context, &e); return; } stream_objs = g_ptr_array_sized_new (streams->len); /* check that all stream ids are valid and at the same time build an array * of stream objects so we don't have to look them up again after verifying * all stream identifiers. */ for (i = 0; i < streams->len; i++) { guint id = g_array_index (streams, guint, i); GabbleMediaStream *stream; guint j; stream = _find_stream_by_id (obj, id, &error); if (stream == NULL) goto OUT; /* make sure we don't allow the client to repeatedly remove the same stream */ for (j = 0; j < stream_objs->len; j++) { GabbleMediaStream *tmp = g_ptr_array_index (stream_objs, j); if (tmp == stream) { stream = NULL; break; } } if (stream != NULL) g_ptr_array_add (stream_objs, stream); } /* groovy, it's all good dude, let's remove them */ if (stream_objs->len > 0) { GabbleMediaStream *stream; WockyJingleMediaRtp *c; for (i = 0; i < stream_objs->len; i++) { stream = g_ptr_array_index (stream_objs, i); c = gabble_media_stream_get_content (stream); /* FIXME: make sure session emits content-removed, on which we can * delete it from the list */ wocky_jingle_session_remove_content (priv->session, (WockyJingleContent *) c); } } OUT: g_ptr_array_unref (stream_objs); if (error) { dbus_g_method_return_error (context, error); g_error_free (error); } else { tp_svc_channel_type_streamed_media_return_from_remove_streams (context); } } /** * gabble_media_channel_request_stream_direction * * Implements D-Bus method RequestStreamDirection * on interface org.freedesktop.Telepathy.Channel.Type.StreamedMedia */ static void gabble_media_channel_request_stream_direction (TpSvcChannelTypeStreamedMedia *iface, guint stream_id, guint stream_direction, DBusGMethodInvocation *context) { GabbleMediaChannel *self = GABBLE_MEDIA_CHANNEL (iface); GabbleMediaChannelPrivate *priv; GabbleMediaStream *stream; GError *error = NULL; g_assert (GABBLE_IS_MEDIA_CHANNEL (self)); priv = self->priv; if (stream_direction > TP_MEDIA_STREAM_DIRECTION_BIDIRECTIONAL) { g_set_error (&error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT, "given stream direction %u is not valid", stream_direction); dbus_g_method_return_error (context, error); g_error_free (error); return; } stream = _find_stream_by_id (self, stream_id, &error); if (stream == NULL) { dbus_g_method_return_error (context, error); g_error_free (error); return; } DEBUG ("called (stream %s, direction %u)", stream->name, stream_direction); /* streams with no session? I think not... */ g_assert (priv->session != NULL); if (stream_direction == TP_MEDIA_STREAM_DIRECTION_NONE) { if (wocky_jingle_session_can_modify_contents (priv->session)) { WockyJingleMediaRtp *c; DEBUG ("request for NONE direction; removing stream"); c = gabble_media_stream_get_content (stream); wocky_jingle_session_remove_content (priv->session, (WockyJingleContent *) c); tp_svc_channel_type_streamed_media_return_from_request_stream_direction ( context); } else { GError e = { TP_ERROR, TP_ERROR_NOT_IMPLEMENTED, "Stream direction can't be set to None in Google Talk calls" }; DEBUG ("%s", e.message); dbus_g_method_return_error (context, &e); } return; } if (gabble_media_stream_change_direction (stream, stream_direction, &error)) { tp_svc_channel_type_streamed_media_return_from_request_stream_direction ( context); } else { dbus_g_method_return_error (context, error); g_error_free (error); } } typedef struct { /* number of streams requested == number of content objects */ guint len; /* array of @len borrowed pointers */ WockyJingleContent **contents; /* accumulates borrowed pointers to streams. Initially @len NULL pointers; * when the stream for contents[i] is created, it is stored at streams[i]. */ GabbleMediaStream **streams; /* number of non-NULL elements in streams (0 <= satisfied <= contents) */ guint satisfied; /* succeeded_cb(context, GPtrArray) * will be called if the stream request succeeds. */ GFunc succeeded_cb; /* failed_cb(context, GError *) will be called if the stream request fails. */ GFunc failed_cb; gpointer context; } PendingStreamRequest; static PendingStreamRequest * pending_stream_request_new (GPtrArray *contents, GFunc succeeded_cb, GFunc failed_cb, gpointer context) { PendingStreamRequest *p = g_slice_new0 (PendingStreamRequest); g_assert (succeeded_cb); g_assert (failed_cb); p->len = contents->len; p->contents = g_memdup (contents->pdata, contents->len * sizeof (gpointer)); p->streams = g_new0 (GabbleMediaStream *, contents->len); p->satisfied = 0; p->succeeded_cb = succeeded_cb; p->failed_cb = failed_cb; p->context = context; return p; } static gboolean pending_stream_request_maybe_satisfy (PendingStreamRequest *p, GabbleMediaChannel *channel, WockyJingleContent *content, GabbleMediaStream *stream) { guint i; for (i = 0; i < p->len; i++) { if (p->contents[i] == content) { g_assert (p->streams[i] == NULL); p->streams[i] = stream; if (++p->satisfied == p->len && p->context != NULL) { GPtrArray *ret = make_stream_list (channel, p->len, p->streams); p->succeeded_cb (p->context, ret); g_ptr_array_foreach (ret, (GFunc) g_value_array_free, NULL); g_ptr_array_unref (ret); p->context = NULL; return TRUE; } } } return FALSE; } static gboolean pending_stream_request_maybe_fail (PendingStreamRequest *p, GabbleMediaChannel *channel, WockyJingleContent *content) { guint i; for (i = 0; i < p->len; i++) { if (content == p->contents[i]) { GError e = { TP_ERROR, TP_ERROR_NOT_AVAILABLE, "A stream was removed before it could be fully set up" }; /* return early */ p->failed_cb (p->context, &e); p->context = NULL; return TRUE; } } return FALSE; } static void pending_stream_request_free (gpointer data) { PendingStreamRequest *p = data; if (p->context != NULL) { GError e = { TP_ERROR, TP_ERROR_CANCELLED, "The session terminated before the requested streams could be added" }; p->failed_cb (p->context, &e); } g_free (p->contents); g_free (p->streams); g_slice_free (PendingStreamRequest, p); } static gboolean _gabble_media_channel_request_contents (GabbleMediaChannel *chan, TpHandle peer, const GArray *media_types, GPtrArray **ret, GError **error) { GabbleMediaChannelPrivate *priv = chan->priv; gboolean want_audio, want_video; WockyJingleDialect dialect; guint idx; const gchar *peer_resource; const gchar *transport_ns = NULL; DEBUG ("called"); want_audio = want_video = FALSE; for (idx = 0; idx < media_types->len; idx++) { guint media_type = g_array_index (media_types, guint, idx); if (media_type == TP_MEDIA_STREAM_TYPE_AUDIO) { want_audio = TRUE; } else if (media_type == TP_MEDIA_STREAM_TYPE_VIDEO) { want_video = TRUE; } else { g_set_error (error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT, "given media type %u is invalid", media_type); return FALSE; } } /* existing call; the recipient and the mode has already been decided */ if (priv->session != NULL) { peer_resource = wocky_jingle_session_get_peer_resource (priv->session); if (peer_resource[0] != '\0') DEBUG ("existing call, using peer resource %s", peer_resource); else DEBUG ("existing call, using bare JID"); /* is a google call... we have no other option */ if (!wocky_jingle_session_can_modify_contents (priv->session)) { g_set_error (error, TP_ERROR, TP_ERROR_NOT_AVAILABLE, "Streams can't be added to ongoing Google Talk calls"); return FALSE; } /* check if the resource supports it; FIXME - we assume only * one channel type (video or audio) will be added later */ if (NULL == jingle_pick_best_content_type (priv->conn, peer, peer_resource, want_audio ? WOCKY_JINGLE_MEDIA_TYPE_AUDIO : WOCKY_JINGLE_MEDIA_TYPE_VIDEO)) { g_set_error (error, TP_ERROR, TP_ERROR_NOT_AVAILABLE, "member does not have the desired audio/video capabilities"); return FALSE; } /* We assume we already picked the best possible transport ns for the * previous streams, so we just reuse that one */ { GList *contents = wocky_jingle_session_get_contents (priv->session); WockyJingleContent *c; /* If we have a session, we must have at least one content. */ g_assert (contents != NULL); c = contents->data; g_list_free (contents); transport_ns = wocky_jingle_content_get_transport_ns (c); } } /* no existing call; we should choose a recipient and a mode */ else { gchar *jid; DEBUG ("picking the best resource (want audio: %u, want video: %u", want_audio, want_video); g_assert (priv->streams->len == 0); if (!jingle_pick_best_resource (priv->conn, peer, want_audio, want_video, &transport_ns, &dialect, &peer_resource)) { g_set_error (error, TP_ERROR, TP_ERROR_NOT_CAPABLE, "member does not have the desired audio/video capabilities"); return FALSE; } DEBUG ("Picking resource '%s' (transport: %s, dialect: %u)", peer_resource == NULL ? "(null)" : peer_resource, transport_ns, dialect); jid = gabble_peer_to_jid (priv->conn, peer, peer_resource); priv->peer = peer; create_session (chan, jid, dialect); g_free (jid); /* Change nat-traversal if we need to */ if (!tp_strdiff (transport_ns, NS_JINGLE_TRANSPORT_ICEUDP)) { DEBUG ("changing nat-traversal property to ice-udp"); g_object_set (chan, "nat-traversal", "ice-udp", NULL); } else if (!tp_strdiff (transport_ns, NS_JINGLE_TRANSPORT_RAWUDP)) { DEBUG ("changing nat-traversal property to raw-udp"); g_object_set (chan, "nat-traversal", "none", NULL); } } /* check it's not a ridiculous number of streams */ if ((priv->streams->len + media_types->len) > MAX_STREAMS) { g_set_error (error, TP_ERROR, TP_ERROR_NOT_AVAILABLE, "I think that's quite enough streams already"); return FALSE; } /* if we've got here, we're good to make the Jingle contents */ *ret = g_ptr_array_sized_new (media_types->len); for (idx = 0; idx < media_types->len; idx++) { guint media_type = g_array_index (media_types, guint, idx); WockyJingleContent *c; const gchar *content_ns; content_ns = jingle_pick_best_content_type (priv->conn, peer, peer_resource, media_type == TP_MEDIA_STREAM_TYPE_AUDIO ? WOCKY_JINGLE_MEDIA_TYPE_AUDIO : WOCKY_JINGLE_MEDIA_TYPE_VIDEO); /* if we got this far, resource should be capable enough, so we * should not fail in choosing ns */ g_assert (content_ns != NULL); g_assert (transport_ns != NULL); DEBUG ("Creating new jingle content with ns %s : %s", content_ns, transport_ns); c = wocky_jingle_session_add_content (priv->session, media_type == TP_MEDIA_STREAM_TYPE_AUDIO ? WOCKY_JINGLE_MEDIA_TYPE_AUDIO : WOCKY_JINGLE_MEDIA_TYPE_VIDEO, WOCKY_JINGLE_CONTENT_SENDERS_BOTH, NULL, content_ns, transport_ns); /* The stream is created in "new-content" callback, and appended to * priv->streams. This is now guaranteed to happen asynchronously (adding * streams can take time due to the relay info lookup, and if it doesn't, * we use an idle so it does). */ g_assert (c != NULL); g_ptr_array_add (*ret, c); } return TRUE; } /* user_data param is here so we match the GFunc prototype */ static void destroy_request (struct _delayed_request_streams_ctx *ctx, gpointer user_data G_GNUC_UNUSED) { GabbleMediaChannelPrivate *priv = ctx->chan->priv; if (ctx->unsure_period_ended_id) g_signal_handler_disconnect (priv->conn->presence_cache, ctx->unsure_period_ended_id); if (ctx->caps_disco_id) g_signal_handler_disconnect (priv->conn->presence_cache, ctx->caps_disco_id); if (ctx->context != NULL) { GError *error = NULL; g_set_error (&error, TP_ERROR, TP_ERROR_NOT_AVAILABLE, "cannot add streams: peer has insufficient caps"); ctx->failed_cb (ctx->context, error); g_error_free (error); } g_array_unref (ctx->types); g_slice_free (struct _delayed_request_streams_ctx, ctx); } static void destroy_and_remove_request (struct _delayed_request_streams_ctx *ctx) { GabbleMediaChannelPrivate *priv = ctx->chan->priv; destroy_request (ctx, NULL); g_ptr_array_remove_fast (priv->delayed_request_streams, ctx); } static void media_channel_request_streams (GabbleMediaChannel *self, TpHandle contact_handle, const GArray *types, GFunc succeeded_cb, GFunc failed_cb, gpointer context); static gboolean repeat_request (struct _delayed_request_streams_ctx *ctx) { media_channel_request_streams (ctx->chan, ctx->contact_handle, ctx->types, ctx->succeeded_cb, ctx->failed_cb, ctx->context); ctx->context = NULL; destroy_and_remove_request (ctx); return FALSE; } static void capabilities_discovered_cb (GabblePresenceCache *cache, TpHandle handle, struct _delayed_request_streams_ctx *ctx) { /* If this isn't the contact we're waiting for, ignore the signal. */ if (ctx->contact_handle != handle) return; /* If we're still unsure about this contact (most likely because there are * more cache caps pending), wait for them. */ if (gabble_presence_cache_is_unsure (cache, handle)) return; repeat_request (ctx); } static void delay_stream_request (GabbleMediaChannel *chan, guint contact_handle, const GArray *types, GFunc succeeded_cb, GFunc failed_cb, gpointer context) { GabbleMediaChannelPrivate *priv = chan->priv; struct _delayed_request_streams_ctx *ctx = g_slice_new0 (struct _delayed_request_streams_ctx); ctx->chan = chan; ctx->contact_handle = contact_handle; ctx->succeeded_cb = succeeded_cb; ctx->failed_cb = failed_cb; ctx->context = context; ctx->types = g_array_sized_new (FALSE, FALSE, sizeof (guint), types->len); g_array_append_vals (ctx->types, types->data, types->len); ctx->caps_disco_id = g_signal_connect (priv->conn->presence_cache, "capabilities-discovered", G_CALLBACK (capabilities_discovered_cb), ctx); ctx->unsure_period_ended_id = g_signal_connect_swapped ( priv->conn->presence_cache, "unsure-period-ended", G_CALLBACK (repeat_request), ctx); g_ptr_array_add (priv->delayed_request_streams, ctx); } static void media_channel_request_streams (GabbleMediaChannel *self, TpHandle contact_handle, const GArray *types, GFunc succeeded_cb, GFunc failed_cb, gpointer context) { GabbleMediaChannelPrivate *priv = self->priv; GPtrArray *contents; gboolean wait; PendingStreamRequest *psr; GError *error = NULL; if (types->len == 0) { GPtrArray *empty = g_ptr_array_sized_new (0); DEBUG ("no streams to request"); succeeded_cb (context, empty); g_ptr_array_unref (empty); return; } /* If we know the caps haven't arrived yet, delay stream creation * and check again later. Else, give up. */ if (!contact_is_media_capable (self, contact_handle, &wait, &error)) { if (wait) { DEBUG ("Delaying RequestStreams until we get all caps from contact"); delay_stream_request (self, contact_handle, types, succeeded_cb, failed_cb, context); g_error_free (error); return; } goto error; } if (priv->peer != 0 && priv->peer != contact_handle) { g_set_error (&error, TP_ERROR, TP_ERROR_NOT_AVAILABLE, "cannot add streams for %u: this channel's peer is %u", contact_handle, priv->peer); goto error; } if (!_gabble_media_channel_request_contents (self, contact_handle, types, &contents, &error)) goto error; psr = pending_stream_request_new (contents, succeeded_cb, failed_cb, context); priv->pending_stream_requests = g_list_prepend (priv->pending_stream_requests, psr); g_ptr_array_unref (contents); /* signal acceptance */ wocky_jingle_session_accept (priv->session); return; error: DEBUG ("returning error %u: %s", error->code, error->message); failed_cb (context, error); g_error_free (error); } /** * gabble_media_channel_request_streams * * Implements D-Bus method RequestStreams * on interface org.freedesktop.Telepathy.Channel.Type.StreamedMedia */ static void gabble_media_channel_request_streams (TpSvcChannelTypeStreamedMedia *iface, guint contact_handle, const GArray *types, DBusGMethodInvocation *context) { GabbleMediaChannel *self = GABBLE_MEDIA_CHANNEL (iface); TpBaseConnection *base_conn = (TpBaseConnection *) self->priv->conn; TpHandleRepoIface *contact_handles = tp_base_connection_get_handles ( base_conn, TP_HANDLE_TYPE_CONTACT); GError *error = NULL; if (!tp_handle_is_valid (contact_handles, contact_handle, &error)) { DEBUG ("that's not a handle, sonny! (%u)", contact_handle); dbus_g_method_return_error (context, error); g_error_free (error); return; } else { /* FIXME: disallow this if we've put the peer on hold? */ media_channel_request_streams (self, contact_handle, types, (GFunc) tp_svc_channel_type_streamed_media_return_from_request_streams, (GFunc) dbus_g_method_return_error, context); } } /** * gabble_media_channel_request_initial_streams: * @chan: an outgoing call, which must have just been constructed. * @succeeded_cb: called with arguments @user_data and a GPtrArray of * TP_STRUCT_TYPE_MEDIA_STREAM_INFO if the request succeeds. * @failed_cb: called with arguments @user_data and a GError * if the request * fails. * @user_data: context for the callbacks. * * Request streams corresponding to the values of InitialAudio and InitialVideo * in the channel request. */ void gabble_media_channel_request_initial_streams (GabbleMediaChannel *chan, GFunc succeeded_cb, GFunc failed_cb, gpointer user_data) { GabbleMediaChannelPrivate *priv = chan->priv; GArray *types = g_array_sized_new (FALSE, FALSE, sizeof (guint), 2); guint media_type; TpBaseConnection *base_conn = TP_BASE_CONNECTION (priv->conn); /* This has to be an outgoing call... */ g_assert (priv->creator == tp_base_connection_get_self_handle (base_conn)); /* ...which has just been constructed. */ g_assert (priv->session == NULL); if (priv->initial_peer == 0) { /* This is a ye olde anonymous channel, so InitialAudio/Video should be * impossible. */ g_assert (!priv->initial_audio); g_assert (!priv->initial_video); } if (priv->initial_audio) { media_type = TP_MEDIA_STREAM_TYPE_AUDIO; g_array_append_val (types, media_type); } if (priv->initial_video) { media_type = TP_MEDIA_STREAM_TYPE_VIDEO; g_array_append_val (types, media_type); } media_channel_request_streams (chan, priv->initial_peer, types, succeeded_cb, failed_cb, user_data); g_array_unref (types); } static gboolean contact_is_media_capable (GabbleMediaChannel *chan, TpHandle peer, gboolean *wait_ret, GError **error) { GabbleMediaChannelPrivate *priv = chan->priv; GabblePresence *presence; TpBaseConnection *conn = (TpBaseConnection *) priv->conn; TpHandleRepoIface *contact_handles = tp_base_connection_get_handles ( conn, TP_HANDLE_TYPE_CONTACT); gboolean wait = FALSE; presence = gabble_presence_cache_get (priv->conn->presence_cache, peer); if (presence != NULL) { const GabbleCapabilitySet *caps = gabble_presence_peek_caps (presence); if (gabble_capability_set_has_one (caps, gabble_capabilities_get_any_audio_video ())) return TRUE; } /* Okay, they're not capable (yet). Let's figure out whether we should wait, * and return an appropriate error. */ if (gabble_presence_cache_is_unsure (priv->conn->presence_cache, peer)) { DEBUG ("presence cache is still unsure about handle %u", peer); wait = TRUE; } else if (!priv->tried_decloaking && gabble_presence_cache_request_decloaking (priv->conn->presence_cache, peer, "media")) { /* only ask to decloak at most once per call */ priv->tried_decloaking = TRUE; DEBUG ("asked handle %u to decloak, let's see what they do", peer); wait = TRUE; } if (wait_ret != NULL) *wait_ret = wait; if (presence == NULL) g_set_error (error, TP_ERROR, TP_ERROR_OFFLINE, "contact %d (%s) has no presence available", peer, tp_handle_inspect (contact_handles, peer)); else g_set_error (error, TP_ERROR, TP_ERROR_NOT_CAPABLE, "contact %d (%s) doesn't have sufficient media caps", peer, tp_handle_inspect (contact_handles, peer)); return FALSE; } static gboolean gabble_media_channel_add_member (GObject *obj, TpHandle handle, const gchar *message, GError **error) { GabbleMediaChannel *chan = GABBLE_MEDIA_CHANNEL (obj); GabbleMediaChannelPrivate *priv = chan->priv; TpGroupMixin *mixin = TP_GROUP_MIXIN (obj); TpIntset *set; /* did we create this channel? */ if (priv->creator == mixin->self_handle) { GError *error_ = NULL; gboolean wait; /* yes: check we don't have a peer already, and if not add this one to * remote pending (but don't send an invitation yet). */ if (priv->peer != 0 && priv->peer != handle) { g_set_error (error, TP_ERROR, TP_ERROR_NOT_AVAILABLE, "handle %u cannot be added: this channel's peer is %u", handle, priv->peer); return FALSE; } /* We can't delay the request at this time, but if there's a chance * the caps might be available later, we'll add the contact and * hope for the best. */ if (!contact_is_media_capable (chan, handle, &wait, &error_)) { if (wait) { DEBUG ("contact %u caps still pending, adding anyways", handle); g_error_free (error_); } else { DEBUG ("%u: %s", error_->code, error_->message); g_propagate_error (error, error_); return FALSE; } } /* make the peer remote pending */ set = tp_intset_new_containing (handle); tp_group_mixin_change_members (obj, "", NULL, NULL, NULL, set, mixin->self_handle, TP_CHANNEL_GROUP_CHANGE_REASON_INVITED); tp_intset_destroy (set); /* and remove CanAdd, since it was only here to allow this deprecated * API. */ tp_group_mixin_change_flags (obj, 0, TP_CHANNEL_GROUP_FLAG_CAN_ADD); return TRUE; } else { /* no: has a session been created, is the handle being added ours, * and are we in local pending? (call answer) */ if (priv->session && handle == mixin->self_handle && tp_handle_set_is_member (mixin->local_pending, handle)) { /* is the call on hold? */ if (priv->hold_state != TP_LOCAL_HOLD_STATE_UNHELD) { g_set_error (error, TP_ERROR, TP_ERROR_NOT_AVAILABLE, "Can't answer a call while it's on hold"); return FALSE; } /* make us a member */ set = tp_intset_new_containing (handle); tp_group_mixin_change_members (obj, "", set, NULL, NULL, NULL, handle, TP_CHANNEL_GROUP_CHANGE_REASON_NONE); tp_intset_destroy (set); /* accept any local pending sends */ g_ptr_array_foreach (priv->streams, (GFunc) gabble_media_stream_accept_pending_local_send, NULL); /* signal acceptance */ wocky_jingle_session_accept (priv->session); return TRUE; } } g_set_error (error, TP_ERROR, TP_ERROR_NOT_AVAILABLE, "handle %u cannot be added in the current state", handle); return FALSE; } static gboolean gabble_media_channel_remove_member (GObject *obj, TpHandle handle, const gchar *message, TpChannelGroupChangeReason reason, GError **error) { GabbleMediaChannel *chan = GABBLE_MEDIA_CHANNEL (obj); GabbleMediaChannelPrivate *priv = chan->priv; TpGroupMixin *mixin = TP_GROUP_MIXIN (obj); /* We don't set CanRemove, and did allow self removal. So tp-glib should * ensure this. */ g_assert (handle == mixin->self_handle); /* Closing up might make GabbleMediaFactory release its ref. */ g_object_ref (chan); if (priv->session == NULL) { /* The call didn't even start yet; close up. */ gabble_media_channel_close (chan); } else { WockyJingleReason wocky_jingle_reason = WOCKY_JINGLE_REASON_UNKNOWN; switch (reason) { case TP_CHANNEL_GROUP_CHANGE_REASON_NONE: wocky_jingle_reason = WOCKY_JINGLE_REASON_UNKNOWN; break; case TP_CHANNEL_GROUP_CHANGE_REASON_OFFLINE: wocky_jingle_reason = WOCKY_JINGLE_REASON_GONE; break; case TP_CHANNEL_GROUP_CHANGE_REASON_BUSY: wocky_jingle_reason = WOCKY_JINGLE_REASON_BUSY; break; case TP_CHANNEL_GROUP_CHANGE_REASON_ERROR: wocky_jingle_reason = WOCKY_JINGLE_REASON_GENERAL_ERROR; break; case TP_CHANNEL_GROUP_CHANGE_REASON_NO_ANSWER: wocky_jingle_reason = WOCKY_JINGLE_REASON_TIMEOUT; break; default: g_set_error (error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT, "%u doesn't make sense as a reason to end a call", reason); g_object_unref (chan); return FALSE; } wocky_jingle_session_terminate (priv->session, wocky_jingle_reason, message, error); } /* Remove CanAdd if it was there for the deprecated anonymous channel * semantics, since the channel will go away RSN. */ tp_group_mixin_change_flags (obj, 0, TP_CHANNEL_GROUP_FLAG_CAN_ADD); g_object_unref (chan); return TRUE; } /** * copy_stream_list: * * Returns a copy of priv->streams. This is used when applying a function to * all streams that could result in them being closed, to avoid stream_close_cb * modifying the list being iterated. */ static GPtrArray * copy_stream_list (GabbleMediaChannel *channel) { return gabble_g_ptr_array_copy (channel->priv->streams); } /* return TRUE when the jingle reason is reason enough to raise a * StreamError */ static gboolean extract_media_stream_error_from_jingle_reason (WockyJingleReason wocky_jingle_reason, TpMediaStreamError *stream_error) { TpMediaStreamError _stream_error; /* TODO: Make a better mapping with more distinction of possible errors */ switch (wocky_jingle_reason) { case WOCKY_JINGLE_REASON_CONNECTIVITY_ERROR: _stream_error = TP_MEDIA_STREAM_ERROR_NETWORK_ERROR; break; case WOCKY_JINGLE_REASON_MEDIA_ERROR: _stream_error = TP_MEDIA_STREAM_ERROR_MEDIA_ERROR; break; case WOCKY_JINGLE_REASON_FAILED_APPLICATION: _stream_error = TP_MEDIA_STREAM_ERROR_CODEC_NEGOTIATION_FAILED; break; case WOCKY_JINGLE_REASON_GENERAL_ERROR: _stream_error = TP_MEDIA_STREAM_ERROR_UNKNOWN; break; default: { if (stream_error != NULL) *stream_error = TP_MEDIA_STREAM_ERROR_UNKNOWN; return FALSE; } } if (stream_error != NULL) *stream_error = _stream_error; return TRUE; } static WockyJingleReason media_stream_error_to_jingle_reason (TpMediaStreamError stream_error) { switch (stream_error) { case TP_MEDIA_STREAM_ERROR_NETWORK_ERROR: return WOCKY_JINGLE_REASON_CONNECTIVITY_ERROR; case TP_MEDIA_STREAM_ERROR_MEDIA_ERROR: return WOCKY_JINGLE_REASON_MEDIA_ERROR; case TP_MEDIA_STREAM_ERROR_CODEC_NEGOTIATION_FAILED: return WOCKY_JINGLE_REASON_FAILED_APPLICATION; default: return WOCKY_JINGLE_REASON_GENERAL_ERROR; } } static TpChannelGroupChangeReason wocky_jingle_reason_to_group_change_reason (WockyJingleReason wocky_jingle_reason) { switch (wocky_jingle_reason) { case WOCKY_JINGLE_REASON_BUSY: return TP_CHANNEL_GROUP_CHANGE_REASON_BUSY; case WOCKY_JINGLE_REASON_GONE: return TP_CHANNEL_GROUP_CHANGE_REASON_OFFLINE; case WOCKY_JINGLE_REASON_TIMEOUT: return TP_CHANNEL_GROUP_CHANGE_REASON_NO_ANSWER; case WOCKY_JINGLE_REASON_CONNECTIVITY_ERROR: case WOCKY_JINGLE_REASON_FAILED_APPLICATION: case WOCKY_JINGLE_REASON_FAILED_TRANSPORT: case WOCKY_JINGLE_REASON_GENERAL_ERROR: case WOCKY_JINGLE_REASON_MEDIA_ERROR: case WOCKY_JINGLE_REASON_SECURITY_ERROR: case WOCKY_JINGLE_REASON_INCOMPATIBLE_PARAMETERS: case WOCKY_JINGLE_REASON_UNSUPPORTED_APPLICATIONS: case WOCKY_JINGLE_REASON_UNSUPPORTED_TRANSPORTS: return TP_CHANNEL_GROUP_CHANGE_REASON_ERROR; default: return TP_CHANNEL_GROUP_CHANGE_REASON_NONE; } } static void session_terminated_cb (WockyJingleSession *session, gboolean local_terminator, WockyJingleReason wocky_jingle_reason, const gchar *text, gpointer user_data) { GabbleMediaChannel *channel = (GabbleMediaChannel *) user_data; GabbleMediaChannelPrivate *priv = channel->priv; TpGroupMixin *mixin = TP_GROUP_MIXIN (channel); guint terminator; WockyJingleState state; TpIntset *set; DEBUG ("called"); g_object_get (session, "state", &state, NULL); if (local_terminator) terminator = mixin->self_handle; else terminator = priv->peer; set = tp_intset_new (); /* remove us and the peer from the member list */ tp_intset_add (set, mixin->self_handle); tp_intset_add (set, priv->peer); tp_group_mixin_change_members ((GObject *) channel, text, NULL, set, NULL, NULL, terminator, wocky_jingle_reason_to_group_change_reason (wocky_jingle_reason)); tp_intset_destroy (set); /* Ignore any Google relay session responses we're waiting for. */ g_list_foreach (priv->stream_creation_datas, stream_creation_data_cancel, NULL); /* any contents that we were waiting for have now lost */ g_list_foreach (priv->pending_stream_requests, (GFunc) pending_stream_request_free, NULL); g_list_free (priv->pending_stream_requests); priv->pending_stream_requests = NULL; { GPtrArray *tmp = copy_stream_list (channel); guint i; TpMediaStreamError stream_error = TP_MEDIA_STREAM_ERROR_UNKNOWN; gboolean is_error = extract_media_stream_error_from_jingle_reason ( wocky_jingle_reason, &stream_error); for (i = 0; i < tmp->len; i++) { GabbleMediaStream *stream = tmp->pdata[i]; if (is_error) { guint id; DEBUG ("emitting stream error"); g_object_get (stream, "id", &id, NULL); tp_svc_channel_type_streamed_media_emit_stream_error (channel, id, stream_error, text); } gabble_media_stream_close (stream); } /* All the streams should have closed. */ g_assert (priv->streams->len == 0); g_ptr_array_unref (tmp); } /* remove the session */ tp_clear_object (&priv->session); /* close us if we aren't already closed */ if (!priv->closed) { DEBUG ("calling media channel close from session terminated cb"); gabble_media_channel_close (channel); } } static void session_state_changed_cb (WockyJingleSession *session, GParamSpec *arg1, GabbleMediaChannel *channel) { GObject *as_object = (GObject *) channel; GabbleMediaChannelPrivate *priv = channel->priv; TpGroupMixin *mixin = TP_GROUP_MIXIN (channel); WockyJingleState state; TpIntset *set; DEBUG ("called"); g_object_get (session, "state", &state, NULL); set = tp_intset_new_containing (priv->peer); if (state >= WOCKY_JINGLE_STATE_PENDING_INITIATE_SENT && state < WOCKY_JINGLE_STATE_ACTIVE && !tp_handle_set_is_member (mixin->members, priv->peer)) { /* The first time we send anything to the other user, they materialise * in remote-pending if necessary */ tp_group_mixin_change_members (as_object, "", NULL, NULL, NULL, set, mixin->self_handle, TP_CHANNEL_GROUP_CHANGE_REASON_INVITED); /* Remove CanAdd if it happened to be there to support deprecated * RequestChannel(..., 0) followed by AddMembers([h], ...) semantics. */ tp_group_mixin_change_flags (as_object, 0, TP_CHANNEL_GROUP_FLAG_CAN_ADD); } if (state == WOCKY_JINGLE_STATE_ACTIVE && priv->creator == mixin->self_handle) { DEBUG ("adding peer to the member list and updating flags"); /* add the peer to the member list */ tp_group_mixin_change_members (as_object, "", set, NULL, NULL, NULL, priv->peer, TP_CHANNEL_GROUP_CHANGE_REASON_NONE); } tp_intset_destroy (set); } static void stream_close_cb (GabbleMediaStream *stream, GabbleMediaChannel *chan) { GabbleMediaChannelPrivate *priv = chan->priv; guint id, i; gboolean still_have_audio = FALSE; g_assert (GABBLE_IS_MEDIA_CHANNEL (chan)); g_object_get (stream, "id", &id, NULL); tp_svc_channel_type_streamed_media_emit_stream_removed (chan, id); if (g_ptr_array_remove (priv->streams, stream)) g_object_unref (stream); else g_warning ("stream %p (%s) removed, but it wasn't in priv->streams!", stream, stream->name); gabble_media_channel_hold_stream_closed (chan, stream); for (i = 0; i < priv->streams->len; i++) { GabbleMediaStream *other = g_ptr_array_index (priv->streams, i); if (gabble_media_stream_get_media_type (other) == TP_MEDIA_STREAM_TYPE_AUDIO) { still_have_audio = TRUE; } } if (priv->have_some_audio && !still_have_audio) { /* the last audio stream just closed */ tp_dtmf_player_cancel (priv->dtmf_player); } priv->have_some_audio = still_have_audio; } static void stream_error_cb (GabbleMediaStream *stream, TpMediaStreamError errno, const gchar *message, GabbleMediaChannel *chan) { GabbleMediaChannelPrivate *priv = chan->priv; WockyJingleMediaRtp *c; GList *contents; guint id; /* emit signal */ g_object_get (stream, "id", &id, NULL); tp_svc_channel_type_streamed_media_emit_stream_error (chan, id, errno, message); contents = wocky_jingle_session_get_contents (priv->session); if (wocky_jingle_session_can_modify_contents (priv->session) && g_list_length (contents) > 1) { /* remove stream from session (removal will be signalled * so we can dispose of the stream) */ c = gabble_media_stream_get_content (stream); if (errno == TP_MEDIA_STREAM_ERROR_CODEC_NEGOTIATION_FAILED) wocky_jingle_content_reject ((WockyJingleContent *) c, WOCKY_JINGLE_REASON_FAILED_APPLICATION); else wocky_jingle_session_remove_content (priv->session, (WockyJingleContent *) c); } else { /* We can't remove the content, or it's the only one left; let's * terminate the call. (The alternative is to carry on the call with * only audio/video, which will look or sound bad to the Google * Talk-using peer.) */ DEBUG ("Terminating call in response to stream error"); wocky_jingle_session_terminate (priv->session, media_stream_error_to_jingle_reason (errno), message, NULL); } g_list_free (contents); } static void stream_state_changed_cb (GabbleMediaStream *stream, GParamSpec *pspec, GabbleMediaChannel *chan) { guint id; TpMediaStreamState connection_state; g_object_get (stream, "id", &id, "connection-state", &connection_state, NULL); tp_svc_channel_type_streamed_media_emit_stream_state_changed (chan, id, connection_state); } static void stream_direction_changed_cb (GabbleMediaStream *stream, GParamSpec *pspec, GabbleMediaChannel *chan) { guint id; CombinedStreamDirection combined; TpMediaStreamDirection direction; TpMediaStreamPendingSend pending_send; g_object_get (stream, "id", &id, "combined-direction", &combined, NULL); direction = COMBINED_DIRECTION_GET_DIRECTION (combined); pending_send = COMBINED_DIRECTION_GET_PENDING_SEND (combined); DEBUG ("direction: %u, pending_send: %u", direction, pending_send); tp_svc_channel_type_streamed_media_emit_stream_direction_changed ( chan, id, direction, pending_send); } static void construct_stream (GabbleMediaChannel *chan, WockyJingleContent *c, const gchar *name, const gchar *nat_traversal, const GPtrArray *relays, gboolean initial) { GObject *chan_o = (GObject *) chan; GabbleMediaChannelPrivate *priv = chan->priv; GabbleMediaStream *stream; TpMediaStreamType mtype; guint id; gchar *object_path; gboolean local_hold = (priv->hold_state == TP_LOCAL_HOLD_STATE_HELD || priv->hold_state == TP_LOCAL_HOLD_STATE_PENDING_HOLD); id = priv->next_stream_id++; object_path = g_strdup_printf ("%s/MediaStream%u", priv->object_path, id); stream = gabble_media_stream_new ( tp_base_connection_get_dbus_daemon (TP_BASE_CONNECTION (priv->conn)), object_path, c, name, id, nat_traversal, relays, local_hold); mtype = gabble_media_stream_get_media_type (stream); if (mtype == TP_MEDIA_STREAM_TYPE_AUDIO) { gabble_media_stream_add_dtmf_player (stream, priv->dtmf_player); priv->have_some_audio = TRUE; } DEBUG ("%p: created new MediaStream %p for content '%s'", chan, stream, name); g_ptr_array_add (priv->streams, stream); /* if any RequestStreams call was waiting for a stream to be created for * that content, return from it successfully */ { GList *l = priv->pending_stream_requests; while (l != NULL) { if (pending_stream_request_maybe_satisfy (l->data, chan, c, stream)) { GList *dead = l; pending_stream_request_free (dead->data); l = dead->next; priv->pending_stream_requests = g_list_delete_link ( priv->pending_stream_requests, dead); } else { l = l->next; } } } gabble_signal_connect_weak (stream, "close", (GCallback) stream_close_cb, chan_o); gabble_signal_connect_weak (stream, "error", (GCallback) stream_error_cb, chan_o); gabble_signal_connect_weak (stream, "notify::connection-state", (GCallback) stream_state_changed_cb, chan_o); gabble_signal_connect_weak (stream, "notify::combined-direction", (GCallback) stream_direction_changed_cb, chan_o); if (initial) { /* If we accepted the call, then automagically accept the initial streams * when they pop up */ if (tp_handle_set_is_member (chan->group.members, chan->group.self_handle)) { gabble_media_stream_accept_pending_local_send (stream); } } DEBUG ("emitting StreamAdded with type '%s'", mtype == TP_MEDIA_STREAM_TYPE_AUDIO ? "audio" : "video"); tp_svc_channel_type_streamed_media_emit_stream_added ( chan, id, priv->peer, mtype); /* StreamAdded does not include the stream's direction and pending send * information, so we call the notify::combined-direction handler in order to * emit StreamDirectionChanged for the initial state. */ stream_direction_changed_cb (stream, NULL, chan); gabble_media_channel_hold_new_stream (chan, stream, WOCKY_JINGLE_MEDIA_RTP (c)); if (priv->ready) { /* all of the streams are bidirectional from farsight's point of view, it's * just in the signalling they change */ DEBUG ("emitting MediaSessionHandler:NewStreamHandler signal for stream %d", id); tp_svc_media_session_handler_emit_new_stream_handler (chan, object_path, id, mtype, TP_MEDIA_STREAM_DIRECTION_BIDIRECTIONAL); } g_free (object_path); } static void stream_creation_data_cancel (gpointer p, gpointer unused) { StreamCreationData *d = p; tp_clear_object (&d->content); } static void stream_creation_data_free (gpointer p) { StreamCreationData *d = p; g_free (d->name); if (d->content != NULL) { g_signal_handler_disconnect (d->content, d->removed_id); g_object_unref (d->content); } if (d->self != NULL) { GabbleMediaChannelPrivate *priv = d->self->priv; g_object_remove_weak_pointer (G_OBJECT (d->self), (gpointer *) &d->self); priv->stream_creation_datas = g_list_remove ( priv->stream_creation_datas, d); } g_slice_free (StreamCreationData, d); } static gboolean construct_stream_later_cb (gpointer user_data) { StreamCreationData *d = user_data; if (d->content != NULL && d->self != NULL) construct_stream (d->self, d->content, d->name, d->nat_traversal, NULL, d->initial); return FALSE; } static void google_relay_session_cb (GPtrArray *relays, gpointer user_data) { StreamCreationData *d = user_data; GPtrArray *tp_relays = gabble_build_tp_relay_info (relays); if (d->content != NULL && d->self != NULL) construct_stream (d->self, d->content, d->name, d->nat_traversal, tp_relays, d->initial); g_ptr_array_unref (tp_relays); stream_creation_data_free (d); } static void content_removed_cb (WockyJingleContent *content, StreamCreationData *d) { if (d->content == NULL) return; if (d->self != NULL) { GList *l = d->self->priv->pending_stream_requests; /* if any RequestStreams call was waiting for a stream to be created for * that content, return from it unsuccessfully */ while (l != NULL) { if (pending_stream_request_maybe_fail (l->data, d->self, d->content)) { GList *dead = l; pending_stream_request_free (dead->data); l = dead->next; d->self->priv->pending_stream_requests = g_list_delete_link ( d->self->priv->pending_stream_requests, dead); } else { l = l->next; } } } g_signal_handler_disconnect (d->content, d->removed_id); g_object_unref (d->content); d->content = NULL; } static void create_stream_from_content (GabbleMediaChannel *self, WockyJingleContent *c, gboolean initial) { gchar *name; StreamCreationData *d; g_object_get (c, "name", &name, NULL); if (G_OBJECT_TYPE (c) != WOCKY_TYPE_JINGLE_MEDIA_RTP) { DEBUG ("ignoring non MediaRtp content '%s'", name); g_free (name); return; } d = g_slice_new0 (StreamCreationData); d->self = self; d->name = name; d->content = g_object_ref (c); d->initial = initial; g_object_add_weak_pointer (G_OBJECT (d->self), (gpointer *) &d->self); /* If the content gets removed before we've finished looking up its * relay, we need to cancel the creation of the stream, * and make any PendingStreamRequests fail */ d->removed_id = g_signal_connect (c, "removed", G_CALLBACK (content_removed_cb), d); self->priv->stream_creation_datas = g_list_prepend ( self->priv->stream_creation_datas, d); switch (wocky_jingle_content_get_transport_type (c)) { case JINGLE_TRANSPORT_GOOGLE_P2P: /* See if our server is Google, and if it is, ask them for a relay. * We ask for enough relays for 2 components (RTP and RTCP) since we * don't yet know whether there will be RTCP. */ d->nat_traversal = "gtalk-p2p"; DEBUG ("Attempting to create Google relay session"); wocky_jingle_info_create_google_relay_session ( gabble_jingle_mint_get_info (self->priv->conn->jingle_mint), 2, google_relay_session_cb, d); return; case JINGLE_TRANSPORT_ICE_UDP: d->nat_traversal = "ice-udp"; break; default: d->nat_traversal = "none"; } /* If we got here, just create the stream (do it asynchronously so that the * behaviour is the same in each case) */ g_idle_add_full (G_PRIORITY_DEFAULT, construct_stream_later_cb, d, stream_creation_data_free); } static void session_content_rejected_cb (WockyJingleSession *session, WockyJingleContent *c, WockyJingleReason reason, const gchar *message, gpointer user_data) { GabbleMediaChannel *chan = GABBLE_MEDIA_CHANNEL (user_data); GabbleMediaStream *stream = _find_stream_by_content (chan, c); TpMediaStreamError stream_error = TP_MEDIA_STREAM_ERROR_UNKNOWN; guint id = 0; DEBUG (" "); g_return_if_fail (stream != NULL); g_object_get (stream, "id", &id, NULL); extract_media_stream_error_from_jingle_reason (reason, &stream_error); tp_svc_channel_type_streamed_media_emit_stream_error (chan, id, stream_error, message); } static void session_new_content_cb (WockyJingleSession *session, WockyJingleContent *c, gpointer user_data) { GabbleMediaChannel *chan = GABBLE_MEDIA_CHANNEL (user_data); DEBUG ("called"); create_stream_from_content (chan, c, FALSE); } static void _emit_new_stream (GabbleMediaChannel *chan, GabbleMediaStream *stream) { gchar *object_path; guint id, media_type; g_object_get (stream, "object-path", &object_path, "id", &id, "media-type", &media_type, NULL); /* all of the streams are bidirectional from farsight's point of view, it's * just in the signalling they change */ DEBUG ("emitting MediaSessionHandler:NewStreamHandler signal for %s stream %d ", media_type == TP_MEDIA_STREAM_TYPE_AUDIO ? "audio" : "video", id); tp_svc_media_session_handler_emit_new_stream_handler (chan, object_path, id, media_type, TP_MEDIA_STREAM_DIRECTION_BIDIRECTIONAL); g_free (object_path); } static void gabble_media_channel_ready (TpSvcMediaSessionHandler *iface, DBusGMethodInvocation *context) { GabbleMediaChannel *self = GABBLE_MEDIA_CHANNEL (iface); GabbleMediaChannelPrivate *priv = self->priv; if (priv->session == NULL) { /* This could also be because someone called Ready() before the * SessionHandler was announced. But the fact that the SessionHandler is * actually also the Channel, and thus this method is available before * NewSessionHandler is emitted, is an implementation detail. So the * error message describes the only legitimate situation in which this * could arise. */ GError e = { TP_ERROR, TP_ERROR_NOT_AVAILABLE, "call has already ended" }; DEBUG ("no session, returning an error."); dbus_g_method_return_error (context, &e); return; } if (!priv->ready) { guint i; DEBUG ("emitting NewStreamHandler for each stream"); priv->ready = TRUE; for (i = 0; i < priv->streams->len; i++) _emit_new_stream (self, g_ptr_array_index (priv->streams, i)); } tp_svc_media_session_handler_return_from_ready (context); } static void gabble_media_channel_error (TpSvcMediaSessionHandler *iface, guint errno, const gchar *message, DBusGMethodInvocation *context) { GabbleMediaChannel *self = GABBLE_MEDIA_CHANNEL (iface); GabbleMediaChannelPrivate *priv; GPtrArray *tmp; guint i; WockyJingleState state; g_assert (GABBLE_IS_MEDIA_CHANNEL (self)); priv = self->priv; if (priv->session == NULL) { /* This could also be because someone called Error() before the * SessionHandler was announced. But the fact that the SessionHandler is * actually also the Channel, and thus this method is available before * NewSessionHandler is emitted, is an implementation detail. So the * error message describes the only legitimate situation in which this * could arise. */ GError e = { TP_ERROR, TP_ERROR_NOT_AVAILABLE, "call has already ended" }; DEBUG ("no session, returning an error."); dbus_g_method_return_error (context, &e); return; } DEBUG ("Media.SessionHandler::Error called, error %u (%s) -- " "emitting error on each stream", errno, message); g_object_get (priv->session, "state", &state, NULL); if (state == WOCKY_JINGLE_STATE_ENDED) { tp_svc_media_session_handler_return_from_error (context); return; } else if (state == WOCKY_JINGLE_STATE_PENDING_CREATED) { /* shortcut to prevent sending remove actions if we haven't sent an * initiate yet */ g_object_set (self, "state", WOCKY_JINGLE_STATE_ENDED, NULL); tp_svc_media_session_handler_return_from_error (context); return; } /* Calling gabble_media_stream_error () on all the streams will ultimately * cause them all to emit 'closed'. In response to 'closed', stream_close_cb * unrefs them, and removes them from priv->streams. So, we copy the stream * list to avoid it being modified from underneath us. */ tmp = copy_stream_list (self); for (i = 0; i < tmp->len; i++) { GabbleMediaStream *stream = g_ptr_array_index (tmp, i); gabble_media_stream_error (stream, errno, message, NULL); } g_ptr_array_unref (tmp); tp_svc_media_session_handler_return_from_error (context); } #define TONE_MS 200 #define GAP_MS 100 #define PAUSE_MS 3000 /* arbitrary limit on the length of a tone started with StartTone */ #define MAX_TONE_SECONDS 10 static void gabble_media_channel_start_tone (TpSvcChannelInterfaceDTMF *iface, guint stream_id G_GNUC_UNUSED, guchar event, DBusGMethodInvocation *context) { GabbleMediaChannel *self = GABBLE_MEDIA_CHANNEL (iface); gchar tones[2] = { '\0', '\0' }; GError *error = NULL; if (!self->priv->have_some_audio) { GError e = { TP_ERROR, TP_ERROR_NOT_AVAILABLE, "There are no audio streams" }; dbus_g_method_return_error (context, &e); return; } tones[0] = tp_dtmf_event_to_char (event); if (tp_dtmf_player_play (self->priv->dtmf_player, tones, MAX_TONE_SECONDS * 1000, GAP_MS, PAUSE_MS, &error)) { tp_clear_pointer (&self->priv->deferred_tones, g_free); tp_svc_channel_interface_dtmf_emit_sending_tones (self, tones); tp_svc_channel_interface_dtmf_return_from_start_tone (context); } else { dbus_g_method_return_error (context, error); g_clear_error (&error); } } static void gabble_media_channel_stop_tone (TpSvcChannelInterfaceDTMF *iface, guint stream_id, DBusGMethodInvocation *context) { GabbleMediaChannel *self = GABBLE_MEDIA_CHANNEL (iface); tp_dtmf_player_cancel (self->priv->dtmf_player); tp_svc_channel_interface_dtmf_return_from_stop_tone (context); } static void gabble_media_channel_multiple_tones ( TpSvcChannelInterfaceDTMF *iface, const gchar *dialstring, DBusGMethodInvocation *context) { GabbleMediaChannel *self = GABBLE_MEDIA_CHANNEL (iface); GError *error = NULL; if (!self->priv->have_some_audio) { GError e = { TP_ERROR, TP_ERROR_NOT_AVAILABLE, "There are no audio streams" }; dbus_g_method_return_error (context, &e); return; } if (tp_dtmf_player_play (self->priv->dtmf_player, dialstring, TONE_MS, GAP_MS, PAUSE_MS, &error)) { tp_clear_pointer (&self->priv->deferred_tones, g_free); tp_svc_channel_interface_dtmf_emit_sending_tones (self, dialstring); tp_svc_channel_interface_dtmf_return_from_start_tone (context); } else { dbus_g_method_return_error (context, error); g_clear_error (&error); } } static void channel_iface_init (gpointer g_iface, gpointer iface_data) { TpSvcChannelClass *klass = (TpSvcChannelClass *) g_iface; #define IMPLEMENT(x, suffix) tp_svc_channel_implement_##x (\ klass, gabble_media_channel_##x##suffix) IMPLEMENT(close,_async); IMPLEMENT(get_channel_type,); IMPLEMENT(get_handle,); IMPLEMENT(get_interfaces,); #undef IMPLEMENT } static void dtmf_iface_init (gpointer g_iface, gpointer iface_data) { TpSvcChannelInterfaceDTMFClass *klass = g_iface; #define IMPLEMENT(x) tp_svc_channel_interface_dtmf_implement_##x (\ klass, gabble_media_channel_##x) IMPLEMENT(start_tone); IMPLEMENT(stop_tone); IMPLEMENT(multiple_tones); #undef IMPLEMENT } static void streamed_media_iface_init (gpointer g_iface, gpointer iface_data) { TpSvcChannelTypeStreamedMediaClass *klass = (TpSvcChannelTypeStreamedMediaClass *) g_iface; #define IMPLEMENT(x) tp_svc_channel_type_streamed_media_implement_##x (\ klass, gabble_media_channel_##x) IMPLEMENT(list_streams); IMPLEMENT(remove_streams); IMPLEMENT(request_stream_direction); IMPLEMENT(request_streams); #undef IMPLEMENT } static void media_signalling_iface_init (gpointer g_iface, gpointer iface_data) { TpSvcChannelInterfaceMediaSignallingClass *klass = (TpSvcChannelInterfaceMediaSignallingClass *) g_iface; #define IMPLEMENT(x) tp_svc_channel_interface_media_signalling_implement_##x (\ klass, gabble_media_channel_##x) IMPLEMENT(get_session_handlers); #undef IMPLEMENT } static void session_handler_iface_init (gpointer g_iface, gpointer iface_data) { TpSvcMediaSessionHandlerClass *klass = (TpSvcMediaSessionHandlerClass *) g_iface; #define IMPLEMENT(x) tp_svc_media_session_handler_implement_##x (\ klass, gabble_media_channel_##x) IMPLEMENT(error); IMPLEMENT(ready); #undef IMPLEMENT } telepathy-gabble-0.18.2/src/media-channel-internal.h0000644000175000017500000000524512227000321022247 0ustar00smcvsmcv00000000000000/* * media-channel-internal.h - implementation details shared between * MediaChannel source files * Copyright © 2006–2009 Collabora Ltd. * Copyright © 2006–2009 Nokia Corporation * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 __GABBLE_MEDIA_CHANNEL_INTERNAL_H__ #define __GABBLE_MEDIA_CHANNEL_INTERNAL_H__ #include "media-channel.h" #include #include #include #include "media-stream.h" G_BEGIN_DECLS struct _GabbleMediaChannelPrivate { GabbleConnection *conn; gchar *object_path; TpHandle creator; TpHandle initial_peer; TpHandle peer; gboolean peer_in_rp; WockyJingleSession *session; /* array of referenced GabbleMediaStream*. Always non-NULL. */ GPtrArray *streams; /* list of PendingStreamRequest* in no particular order */ GList *pending_stream_requests; /* list of StreamCreationData* in no particular order */ GList *stream_creation_datas; guint next_stream_id; TpLocalHoldState hold_state; TpLocalHoldStateReason hold_state_reason; TpChannelCallStateFlags call_state; GPtrArray *delayed_request_streams; TpDTMFPlayer *dtmf_player; gchar *deferred_tones; gboolean initial_audio; gboolean initial_video; gboolean immutable_streams; gboolean ready; gboolean closed; gboolean dispose_has_run; gboolean tried_decloaking; gboolean have_some_audio; }; void gabble_media_channel_hold_latch_to_session (GabbleMediaChannel *chan); void gabble_media_channel_hold_new_stream (GabbleMediaChannel *chan, GabbleMediaStream *stream, WockyJingleMediaRtp *content); void gabble_media_channel_hold_stream_closed (GabbleMediaChannel *chan, GabbleMediaStream *stream); void gabble_media_channel_hold_iface_init (gpointer g_iface, gpointer iface_data G_GNUC_UNUSED); void gabble_media_channel_call_state_iface_init (gpointer g_iface, gpointer iface_data G_GNUC_UNUSED); G_END_DECLS #endif /* #ifndef __GABBLE_MEDIA_CHANNEL_INTERNAL_H__ */ telepathy-gabble-0.18.2/src/media-channel.h0000644000175000017500000000527412227000321020437 0ustar00smcvsmcv00000000000000/* * gabble-media-channel.h - Header for GabbleMediaChannel * Copyright (C) 2006 Collabora Ltd. * Copyright (C) 2006 Nokia Corporation * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 __GABBLE_MEDIA_CHANNEL_H__ #define __GABBLE_MEDIA_CHANNEL_H__ #include #include #include #include "presence.h" G_BEGIN_DECLS typedef struct _GabbleMediaChannel GabbleMediaChannel; typedef struct _GabbleMediaChannelPrivate GabbleMediaChannelPrivate; typedef struct _GabbleMediaChannelClass GabbleMediaChannelClass; struct _GabbleMediaChannelClass { GObjectClass parent_class; TpGroupMixinClass group_class; TpPropertiesMixinClass properties_class; TpDBusPropertiesMixinClass dbus_props_class; }; struct _GabbleMediaChannel { GObject parent; TpGroupMixin group; TpPropertiesMixin properties; GabbleMediaChannelPrivate *priv; }; GType gabble_media_channel_get_type (void); /* TYPE MACROS */ #define GABBLE_TYPE_MEDIA_CHANNEL \ (gabble_media_channel_get_type ()) #define GABBLE_MEDIA_CHANNEL(obj) \ (G_TYPE_CHECK_INSTANCE_CAST((obj), GABBLE_TYPE_MEDIA_CHANNEL,\ GabbleMediaChannel)) #define GABBLE_MEDIA_CHANNEL_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST((klass), GABBLE_TYPE_MEDIA_CHANNEL,\ GabbleMediaChannelClass)) #define GABBLE_IS_MEDIA_CHANNEL(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE((obj), GABBLE_TYPE_MEDIA_CHANNEL)) #define GABBLE_IS_MEDIA_CHANNEL_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE((klass), GABBLE_TYPE_MEDIA_CHANNEL)) #define GABBLE_MEDIA_CHANNEL_GET_CLASS(obj) \ (G_TYPE_INSTANCE_GET_CLASS ((obj), GABBLE_TYPE_MEDIA_CHANNEL, \ GabbleMediaChannelClass)) void gabble_media_channel_request_initial_streams (GabbleMediaChannel *chan, GFunc succeeded_cb, GFunc failed_cb, gpointer user_data); void gabble_media_channel_close (GabbleMediaChannel *self); G_END_DECLS #endif /* #ifndef __GABBLE_MEDIA_CHANNEL_H__*/ telepathy-gabble-0.18.2/src/jingle-tp-util.c0000644000175000017500000000507212200204333020605 0ustar00smcvsmcv00000000000000/* * jingle-tp-util.c - Telepathy-flavoured Jingle utility functions * Copyright © 2008–2012 Collabora Ltd. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 "config.h" #include "jingle-tp-util.h" WockyJingleMediaType wocky_jingle_media_type_from_tp (TpMediaStreamType type) { switch (type) { case TP_MEDIA_STREAM_TYPE_AUDIO: return WOCKY_JINGLE_MEDIA_TYPE_AUDIO; case TP_MEDIA_STREAM_TYPE_VIDEO: return WOCKY_JINGLE_MEDIA_TYPE_VIDEO; default: g_return_val_if_reached (WOCKY_JINGLE_MEDIA_TYPE_NONE); } } TpMediaStreamType wocky_jingle_media_type_to_tp (WockyJingleMediaType type) { switch (type) { case WOCKY_JINGLE_MEDIA_TYPE_AUDIO: return TP_MEDIA_STREAM_TYPE_AUDIO; case WOCKY_JINGLE_MEDIA_TYPE_VIDEO: return TP_MEDIA_STREAM_TYPE_VIDEO; default: g_return_val_if_reached (TP_MEDIA_STREAM_TYPE_AUDIO); } } static const gchar * const relay_type_map[] = { /* WOCKY_JINGLE_RELAY_TYPE_UDP */ "udp", /* WOCKY_JINGLE_RELAY_TYPE_TCP */ "tcp", /* WOCKY_JINGLE_RELAY_TYPE_TLS */ "tls", }; GPtrArray * gabble_build_tp_relay_info (GPtrArray *relays) { guint i; GPtrArray *tp_relays = g_ptr_array_sized_new (relays->len); g_ptr_array_set_free_func (tp_relays, (GDestroyNotify) g_hash_table_unref); for (i = 0; i < relays->len; i++) { WockyJingleRelay *relay = g_ptr_array_index (relays, i); g_return_val_if_fail (relay->type < WOCKY_N_JINGLE_RELAY_TYPES, tp_relays); g_ptr_array_add (tp_relays, tp_asv_new ( "type", G_TYPE_STRING, relay_type_map[relay->type], "ip", G_TYPE_STRING, relay->ip, "port", G_TYPE_UINT, relay->port, "username", G_TYPE_STRING, relay->username, "password", G_TYPE_STRING, relay->password, "component", G_TYPE_UINT, relay->component, NULL)); } return tp_relays; } telepathy-gabble-0.18.2/src/jingle-tp-util.h0000644000175000017500000000235112200204333020607 0ustar00smcvsmcv00000000000000/* * jingle-tp-util.h - Header for Telepathy-flavoured Jingle utility functions * Copyright © 2008–2012 Collabora Ltd. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 GABBLE_JINGLE_TP_UTIL_H #define GABBLE_JINGLE_TP_UTIL_H #include #include WockyJingleMediaType wocky_jingle_media_type_from_tp (TpMediaStreamType type); TpMediaStreamType wocky_jingle_media_type_to_tp (WockyJingleMediaType type); GPtrArray *gabble_build_tp_relay_info (GPtrArray *relays); #endif /* GABBLE_JINGLE_TP_UTIL_H */ telepathy-gabble-0.18.2/src/jingle-mint.c0000644000175000017500000002350612200204333020160 0ustar00smcvsmcv00000000000000/* * jingle-mint.c - creates and configures a WockyJingleFactory * Copyright ©2012 Collabora Ltd. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 * * * * "Mint" is intended in the manufacturing sense: a mint is a factory which * produces coins. . It was chosen * in favour of "factory" because this is a "factory factory"; and in favour of * "foundry" to make JingleFactory and this class have different initials. */ #include "config.h" #include "jingle-mint.h" #define DEBUG_FLAG GABBLE_DEBUG_MEDIA #include "debug.h" #include "connection.h" #include "conn-presence.h" #include "jingle-share.h" #include "presence-cache.h" struct _GabbleJingleMintPrivate { GabbleConnection *conn; WockyJingleFactory *factory; }; enum { INCOMING_SESSION = 0, N_SIGNALS }; static guint signals[N_SIGNALS]; enum { PROP_CONNECTION = 1, }; static void connection_status_changed_cb ( GabbleConnection *conn, guint status, guint reason, gpointer user_data); static void connection_porter_available_cb ( GabbleConnection *conn, WockyPorter *porter, gpointer user_data); static void factory_new_session_cb ( WockyJingleFactory *factory, WockyJingleSession *session, gboolean initiated_locally, gpointer user_data); static gboolean factory_query_cap_cb ( WockyJingleFactory *factory, WockyContact *contact, const gchar *cap_or_quirk, gpointer user_data); G_DEFINE_TYPE (GabbleJingleMint, gabble_jingle_mint, G_TYPE_OBJECT) static void gabble_jingle_mint_init (GabbleJingleMint *self) { self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, GABBLE_TYPE_JINGLE_MINT, GabbleJingleMintPrivate); } static void gabble_jingle_mint_get_property ( GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { GabbleJingleMint *self = GABBLE_JINGLE_MINT (object); GabbleJingleMintPrivate *priv = self->priv; switch (property_id) { case PROP_CONNECTION: g_value_set_object (value, priv->conn); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); } } static void gabble_jingle_mint_set_property ( GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { GabbleJingleMint *self = GABBLE_JINGLE_MINT (object); GabbleJingleMintPrivate *priv = self->priv; switch (property_id) { case PROP_CONNECTION: priv->conn = g_value_get_object (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); } } static void gabble_jingle_mint_constructed (GObject *object) { GabbleJingleMint *self = GABBLE_JINGLE_MINT (object); GabbleJingleMintPrivate *priv = self->priv; GObjectClass *parent_class = gabble_jingle_mint_parent_class; if (parent_class->constructed != NULL) parent_class->constructed (object); tp_g_signal_connect_object (priv->conn, "status-changed", (GCallback) connection_status_changed_cb, self, 0); tp_g_signal_connect_object (priv->conn, "porter-available", (GCallback) connection_porter_available_cb, self, 0); } static void gabble_jingle_mint_dispose (GObject *object) { GabbleJingleMint *self = GABBLE_JINGLE_MINT (object); GabbleJingleMintPrivate *priv = self->priv; GObjectClass *parent_class = gabble_jingle_mint_parent_class; g_clear_object (&priv->factory); if (parent_class->dispose != NULL) parent_class->dispose (object); } static void gabble_jingle_mint_class_init (GabbleJingleMintClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); GParamSpec *param_spec; object_class->get_property = gabble_jingle_mint_get_property; object_class->set_property = gabble_jingle_mint_set_property; object_class->constructed = gabble_jingle_mint_constructed; object_class->dispose = gabble_jingle_mint_dispose; g_type_class_add_private (klass, sizeof (GabbleJingleMintPrivate)); param_spec = g_param_spec_object ("connection", "GabbleConnection object", "Gabble connection object that uses this JingleMint object", GABBLE_TYPE_CONNECTION, G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, PROP_CONNECTION, param_spec); signals[INCOMING_SESSION] = g_signal_new ("incoming-session", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, 0, NULL, NULL, g_cclosure_marshal_VOID__OBJECT, G_TYPE_NONE, 1, WOCKY_TYPE_JINGLE_SESSION); } GabbleJingleMint * gabble_jingle_mint_new ( GabbleConnection *connection) { return g_object_new (GABBLE_TYPE_JINGLE_MINT, "connection", connection, NULL); } static void connection_status_changed_cb ( GabbleConnection *conn, guint status, guint reason, gpointer user_data) { GabbleJingleMint *self = GABBLE_JINGLE_MINT (user_data); GabbleJingleMintPrivate *priv = self->priv; switch (status) { case TP_CONNECTION_STATUS_CONNECTING: g_assert (priv->conn != NULL); break; case TP_CONNECTION_STATUS_CONNECTED: { WockyJingleInfo *info = gabble_jingle_mint_get_info (self); gchar *stun_server = NULL; guint stun_port = 0; g_object_get (priv->conn, "stun-server", &stun_server, "stun-port", &stun_port, NULL); if (stun_server != NULL) wocky_jingle_info_take_stun_server (info, stun_server, stun_port, FALSE); g_object_get (priv->conn, "fallback-stun-server", &stun_server, "fallback-stun-port", &stun_port, NULL); if (stun_server != NULL) wocky_jingle_info_take_stun_server (info, stun_server, stun_port, TRUE); wocky_jingle_info_send_request (info, /* FIXME: one day Wocky will know about caps and then we won't * have to pass in a flag here. */ !!(priv->conn->features & GABBLE_CONNECTION_FEATURES_GOOGLE_JINGLE_INFO)); } break; case TP_CONNECTION_STATUS_DISCONNECTED: if (priv->factory != NULL) wocky_jingle_factory_stop (priv->factory); break; } } static void connection_porter_available_cb ( GabbleConnection *conn, WockyPorter *porter, gpointer user_data) { GabbleJingleMint *self = GABBLE_JINGLE_MINT (user_data); GabbleJingleMintPrivate *priv = self->priv; /* If we have a WockyPorter, we should definitely have a WockySession */ g_assert (conn->session != NULL); g_assert (priv->factory == NULL); priv->factory = wocky_jingle_factory_new (conn->session); jingle_share_register (priv->factory); tp_g_signal_connect_object (priv->factory, "new-session", (GCallback) factory_new_session_cb, self, 0); tp_g_signal_connect_object (priv->factory, "query-cap", (GCallback) factory_query_cap_cb, self, 0); } static void session_about_to_initiate_cb ( WockyJingleSession *session, gpointer user_data) { GabbleJingleMint *self = GABBLE_JINGLE_MINT (user_data); GabbleJingleMintPrivate *priv = self->priv; const gchar *peer_jid = wocky_jingle_session_get_peer_jid (session); TpHandleRepoIface *contact_repo = tp_base_connection_get_handles ( (TpBaseConnection *) priv->conn, TP_HANDLE_TYPE_CONTACT); TpHandle peer = tp_handle_ensure (contact_repo, peer_jid, NULL, NULL); /* send directed presence (including our own caps, avatar etc.) to * the peer, if we aren't already visible to them */ if (!conn_presence_visible_to (priv->conn, peer)) conn_presence_signal_own_presence (priv->conn, peer_jid, NULL); } static void factory_new_session_cb ( WockyJingleFactory *factory, WockyJingleSession *session, gboolean initiated_locally, gpointer user_data) { GabbleJingleMint *self = GABBLE_JINGLE_MINT (user_data); if (initiated_locally) tp_g_signal_connect_object (session, "about-to-initiate", (GCallback) session_about_to_initiate_cb, self, 0); /* Proxy the signal outwards if this is a new incoming session. */ if (!initiated_locally) g_signal_emit (self, signals[INCOMING_SESSION], 0, session); } static gboolean factory_query_cap_cb ( WockyJingleFactory *factory, WockyContact *contact, const gchar *cap_or_quirk, gpointer user_data) { GabbleJingleMint *self = GABBLE_JINGLE_MINT (user_data); GabbleJingleMintPrivate *priv = self->priv; GabblePresence *presence = gabble_presence_cache_get_for_contact ( priv->conn->presence_cache, contact); if (presence == NULL) return FALSE; if (WOCKY_IS_RESOURCE_CONTACT (contact)) { const gchar *peer_resource = wocky_resource_contact_get_resource ( WOCKY_RESOURCE_CONTACT (contact)); return gabble_presence_resource_has_caps (presence, peer_resource, gabble_capability_set_predicate_has, cap_or_quirk); } else { return gabble_presence_has_cap (presence, cap_or_quirk); } } WockyJingleFactory * gabble_jingle_mint_get_factory ( GabbleJingleMint *self) { return self->priv->factory; } WockyJingleInfo * gabble_jingle_mint_get_info ( GabbleJingleMint *self) { return wocky_jingle_factory_get_jingle_info (self->priv->factory); } telepathy-gabble-0.18.2/src/jingle-mint.h0000644000175000017500000000445412200204333020166 0ustar00smcvsmcv00000000000000/* * jingle-mint.h - creates and configures a WockyJingleFactory * Copyright ©2012 Collabora Ltd. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 GABBLE_JINGLE_MINT_H #define GABBLE_JINGLE_MINT_H #include #include "types.h" #include typedef struct _GabbleJingleMint GabbleJingleMint; typedef struct _GabbleJingleMintClass GabbleJingleMintClass; typedef struct _GabbleJingleMintPrivate GabbleJingleMintPrivate; struct _GabbleJingleMintClass { GObjectClass parent_class; }; struct _GabbleJingleMint { GObject parent; GabbleJingleMintPrivate *priv; }; GType gabble_jingle_mint_get_type (void); GabbleJingleMint *gabble_jingle_mint_new ( GabbleConnection *connection); WockyJingleFactory *gabble_jingle_mint_get_factory ( GabbleJingleMint *self); WockyJingleInfo *gabble_jingle_mint_get_info ( GabbleJingleMint *self); /* TYPE MACROS */ #define GABBLE_TYPE_JINGLE_MINT \ (gabble_jingle_mint_get_type ()) #define GABBLE_JINGLE_MINT(obj) \ (G_TYPE_CHECK_INSTANCE_CAST((obj), GABBLE_TYPE_JINGLE_MINT, GabbleJingleMint)) #define GABBLE_JINGLE_MINT_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST((klass), GABBLE_TYPE_JINGLE_MINT,\ GabbleJingleMintClass)) #define GABBLE_IS_JINGLE_MINT(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE((obj), GABBLE_TYPE_JINGLE_MINT)) #define GABBLE_IS_JINGLE_MINT_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE((klass), GABBLE_TYPE_JINGLE_MINT)) #define GABBLE_JINGLE_MINT_GET_CLASS(obj) \ (G_TYPE_INSTANCE_GET_CLASS ((obj), GABBLE_TYPE_JINGLE_MINT, \ GabbleJingleMintClass)) #endif /* GABBLE_JINGLE_MINT_H */ telepathy-gabble-0.18.2/src/jingle-share.c0000644000175000017500000003551212200204333020313 0ustar00smcvsmcv00000000000000/* * jingle-share.c - Source for GabbleJingleShare * * Copyright (C) 2010 Collabora Ltd. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 */ /* Share content type deals with file sharing content, ie. file transfers. It * Google's jingle variants (libjingle 0.3/0.4). */ #include "config.h" #include "jingle-share.h" #include #include #include #include #define DEBUG_FLAG GABBLE_DEBUG_SHARE #include "connection.h" #include "debug.h" #include "namespaces.h" #include "util.h" /****************************************************************** * Example description XML: * * * * * foo.txt * * * foo.jpg * * * * stuff * * * * * /temporary/23A53F01/ * /temporary/90266EA1/ * * * * *******************************************************************/ G_DEFINE_TYPE (GabbleJingleShare, gabble_jingle_share, WOCKY_TYPE_JINGLE_CONTENT); /* properties */ enum { PROP_MEDIA_TYPE = 1, PROP_FILENAME, PROP_FILESIZE, LAST_PROPERTY }; struct _GabbleJingleSharePrivate { gboolean dispose_has_run; GabbleJingleShareManifest *manifest; gchar *filename; guint64 filesize; }; static gchar * generate_temp_url (void) { gchar *uuid = gabble_generate_id (); gchar *url = NULL; url = g_strdup_printf ("/temporary/%s/", uuid); g_free (uuid); return url; } static void free_manifest (GabbleJingleShare *self) { GList * i; if (self->priv->manifest) { for (i = self->priv->manifest->entries; i; i = i->next) { GabbleJingleShareManifestEntry *item = i->data; g_free (item->name); g_slice_free (GabbleJingleShareManifestEntry, item); } g_list_free (self->priv->manifest->entries); g_free (self->priv->manifest->source_url); g_free (self->priv->manifest->preview_url); g_slice_free (GabbleJingleShareManifest, self->priv->manifest); self->priv->manifest = NULL; } } static void ensure_manifest (GabbleJingleShare *self) { if (self->priv->manifest == NULL) { GabbleJingleShareManifestEntry *m = NULL; self->priv->manifest = g_slice_new0 (GabbleJingleShareManifest); self->priv->manifest->source_url = generate_temp_url (); self->priv->manifest->preview_url = generate_temp_url (); if (self->priv->filename != NULL) { m = g_slice_new0 (GabbleJingleShareManifestEntry); m->name = g_strdup (self->priv->filename); m->size = self->priv->filesize; self->priv->manifest->entries = g_list_prepend (NULL, m); } } } static void gabble_jingle_share_init (GabbleJingleShare *obj) { GabbleJingleSharePrivate *priv = G_TYPE_INSTANCE_GET_PRIVATE (obj, GABBLE_TYPE_JINGLE_SHARE, GabbleJingleSharePrivate); DEBUG ("jingle share init called"); obj->priv = priv; priv->dispose_has_run = FALSE; } static void gabble_jingle_share_dispose (GObject *object) { GabbleJingleShare *self = GABBLE_JINGLE_SHARE (object); GabbleJingleSharePrivate *priv = self->priv; if (priv->dispose_has_run) return; DEBUG ("dispose called"); priv->dispose_has_run = TRUE; g_free (priv->filename); priv->filename = NULL; free_manifest (self); if (G_OBJECT_CLASS (gabble_jingle_share_parent_class)->dispose) G_OBJECT_CLASS (gabble_jingle_share_parent_class)->dispose (object); } static void parse_description (WockyJingleContent *content, WockyNode *desc_node, GError **error); static void produce_description (WockyJingleContent *obj, WockyNode *content_node); static void gabble_jingle_share_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { GabbleJingleShare *self = GABBLE_JINGLE_SHARE (object); GabbleJingleSharePrivate *priv = self->priv; switch (property_id) { case PROP_MEDIA_TYPE: g_value_set_uint (value, WOCKY_JINGLE_MEDIA_TYPE_NONE); break; case PROP_FILENAME: g_value_set_string (value, priv->filename); break; case PROP_FILESIZE: g_value_set_uint64 (value, priv->filesize); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void gabble_jingle_share_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { GabbleJingleShare *self = GABBLE_JINGLE_SHARE (object); GabbleJingleSharePrivate *priv = self->priv; switch (property_id) { case PROP_MEDIA_TYPE: break; case PROP_FILENAME: g_free (priv->filename); priv->filename = g_value_dup_string (value); free_manifest (self); /* simulate a media_ready when we know our own filename */ _wocky_jingle_content_set_media_ready (WOCKY_JINGLE_CONTENT (self)); break; case PROP_FILESIZE: priv->filesize = g_value_get_uint64 (value); free_manifest (self); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static WockyJingleContentSenders get_default_senders (WockyJingleContent *c) { return WOCKY_JINGLE_CONTENT_SENDERS_INITIATOR; } static void gabble_jingle_share_class_init (GabbleJingleShareClass *cls) { GObjectClass *object_class = G_OBJECT_CLASS (cls); WockyJingleContentClass *content_class = WOCKY_JINGLE_CONTENT_CLASS (cls); g_type_class_add_private (cls, sizeof (GabbleJingleSharePrivate)); object_class->get_property = gabble_jingle_share_get_property; object_class->set_property = gabble_jingle_share_set_property; object_class->dispose = gabble_jingle_share_dispose; content_class->parse_description = parse_description; content_class->produce_description = produce_description; content_class->get_default_senders = get_default_senders; /* This property is here only because jingle-session sets the media-type when constructing the object.. */ g_object_class_install_property (object_class, PROP_MEDIA_TYPE, g_param_spec_uint ("media-type", "media type", "irrelevant media type. Will always be NONE.", WOCKY_JINGLE_MEDIA_TYPE_NONE, WOCKY_JINGLE_MEDIA_TYPE_NONE, WOCKY_JINGLE_MEDIA_TYPE_NONE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (object_class, PROP_FILENAME, g_param_spec_string ("filename", "file name", "The name of the file", NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (object_class, PROP_FILESIZE, g_param_spec_uint64 ("filesize", "file size", "The size of the file", 0, G_MAXUINT64, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); } static void parse_description (WockyJingleContent *content, WockyNode *desc_node, GError **error) { GabbleJingleShare *self = GABBLE_JINGLE_SHARE (content); GabbleJingleSharePrivate *priv = self->priv; WockyNodeIter i; WockyNode *manifest_node = NULL; WockyNode *protocol_node = NULL; WockyNode *http_node = NULL; WockyNode *node; DEBUG ("parse description called"); if (priv->manifest != NULL) { DEBUG ("Not parsing description, we already have a manifest"); return; } manifest_node = wocky_node_get_child (desc_node, "manifest"); if (manifest_node == NULL) { g_set_error (error, WOCKY_XMPP_ERROR, WOCKY_XMPP_ERROR_BAD_REQUEST, "description missing node"); return; } protocol_node = wocky_node_get_child (desc_node, "protocol"); if (protocol_node != NULL) http_node = wocky_node_get_child (protocol_node, "http"); free_manifest (self); priv->manifest = g_slice_new0 (GabbleJingleShareManifest); /* Build the manifest */ wocky_node_iter_init (&i, manifest_node, NULL, NULL); while (wocky_node_iter_next (&i, &node)) { WockyNode *name = NULL; WockyNode *image = NULL; gboolean folder; const gchar *size; GabbleJingleShareManifestEntry *m = NULL; if (!wocky_strdiff (node->name, "folder")) folder = TRUE; else if (!wocky_strdiff (node->name, "file")) folder = FALSE; else continue; name = wocky_node_get_child (node, "name"); if (name == NULL) continue; m = g_slice_new0 (GabbleJingleShareManifestEntry); m->folder = folder; m->name = g_strdup (name->content); size = wocky_node_get_attribute (node, "size"); if (size) m->size = g_ascii_strtoull (size, NULL, 10); image = wocky_node_get_child (node, "image"); if (image) { const gchar *width; const gchar *height; m->image = TRUE; width = wocky_node_get_attribute (image, "width"); if (width) m->image_width = g_ascii_strtoull (width, NULL, 10); height =wocky_node_get_attribute (image, "height"); if (height) m->image_height = g_ascii_strtoull (height, NULL, 10); } priv->manifest->entries = g_list_prepend (priv->manifest->entries, m); } /* Get the source and preview url paths from the protocol/http node */ if (http_node != NULL) { /* clear the previously set values */ wocky_node_iter_init (&i, http_node, "url", NULL); while (wocky_node_iter_next (&i, &node)) { const gchar *name = wocky_node_get_attribute (node, "name"); if (name == NULL) continue; if (!wocky_strdiff (name, "source-path")) { const gchar *url = node->content; priv->manifest->source_url = g_strdup (url); } if (!wocky_strdiff (name, "preview-path")) { const gchar *url = node->content; priv->manifest->preview_url = g_strdup (url); } } } /* Build the filename/filesize property values based on the new manifest */ g_free (priv->filename); priv->filename = NULL; priv->filesize = 0; if (g_list_length (priv->manifest->entries) > 0) { if (g_list_length (priv->manifest->entries) == 1) { GabbleJingleShareManifestEntry *m = priv->manifest->entries->data; if (m->folder) priv->filename = g_strdup_printf ("%s.tar", m->name); else priv->filename = g_strdup (m->name); priv->filesize = m->size; } else { GList *li; gchar *temp; priv->filename = g_strdup (""); for (li = priv->manifest->entries; li; li = li->next) { GabbleJingleShareManifestEntry *m = li->data; temp = priv->filename; priv->filename = g_strdup_printf ("%s%s%s%s", temp, m->name, m->folder? ".tar":"", li->next == NULL? "": "-"); g_free (temp); priv->filesize += m->size; } temp = priv->filename; priv->filename = g_strdup_printf ("%s.tar", temp); g_free (temp); } } _wocky_jingle_content_set_media_ready (content); } static void produce_description (WockyJingleContent *content, WockyNode *content_node) { GabbleJingleShare *self = GABBLE_JINGLE_SHARE (content); GabbleJingleSharePrivate *priv = self->priv; GList *i; WockyNode *desc_node; WockyNode *manifest_node; WockyNode *protocol_node; WockyNode *http_node; WockyNode *url_node; DEBUG ("produce description called"); ensure_manifest (self); desc_node = wocky_node_add_child_ns (content_node, "description", NS_GOOGLE_SESSION_SHARE); manifest_node = wocky_node_add_child (desc_node, "manifest"); for (i = priv->manifest->entries; i; i = i->next) { GabbleJingleShareManifestEntry *m = i->data; WockyNode *file_node; WockyNode *image_node; gchar *size_str, *width_str, *height_str; if (m->folder) file_node = wocky_node_add_child (manifest_node, "folder"); else file_node = wocky_node_add_child (manifest_node, "file"); if (m->size > 0) { size_str = g_strdup_printf ("%" G_GUINT64_FORMAT, m->size); wocky_node_set_attribute (file_node, "size", size_str); g_free (size_str); } wocky_node_add_child_with_content (file_node, "name", m->name); if (m->image && (m->image_width > 0 || m->image_height > 0)) { image_node = wocky_node_add_child (file_node, "image"); if (m->image_width > 0) { width_str = g_strdup_printf ("%d", m->image_width); wocky_node_set_attribute (image_node, "width", width_str); g_free (width_str); } if (m->image_height > 0) { height_str = g_strdup_printf ("%d", m->image_height); wocky_node_set_attribute (image_node, "height", height_str); g_free (height_str); } } } protocol_node = wocky_node_add_child (desc_node, "protocol"); http_node = wocky_node_add_child (protocol_node, "http"); url_node = wocky_node_add_child_with_content (http_node, "url", priv->manifest->source_url); wocky_node_set_attribute (url_node, "name", "source-path"); url_node = wocky_node_add_child_with_content (http_node, "url", priv->manifest->preview_url); wocky_node_set_attribute (url_node, "name", "preview-path"); } GabbleJingleShareManifest * gabble_jingle_share_get_manifest (GabbleJingleShare *self) { ensure_manifest (self); return self->priv->manifest; } void jingle_share_register (WockyJingleFactory *factory) { /* GTalk video call namespace */ wocky_jingle_factory_register_content_type (factory, NS_GOOGLE_SESSION_SHARE, GABBLE_TYPE_JINGLE_SHARE); } telepathy-gabble-0.18.2/src/jingle-share.h0000644000175000017500000000536412200204333020322 0ustar00smcvsmcv00000000000000/* * jingle-share.h - Header for GabbleJingleShare * Copyright (C) 2008 Collabora Ltd. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 __JINGLE_SHARE_H__ #define __JINGLE_SHARE_H__ #include #include G_BEGIN_DECLS typedef struct _GabbleJingleShareClass GabbleJingleShareClass; GType gabble_jingle_share_get_type (void); /* TYPE MACROS */ #define GABBLE_TYPE_JINGLE_SHARE \ (gabble_jingle_share_get_type ()) #define GABBLE_JINGLE_SHARE(obj) \ (G_TYPE_CHECK_INSTANCE_CAST((obj), GABBLE_TYPE_JINGLE_SHARE, \ GabbleJingleShare)) #define GABBLE_JINGLE_SHARE_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST((klass), GABBLE_TYPE_JINGLE_SHARE, \ GabbleJingleShareClass)) #define GABBLE_IS_JINGLE_SHARE(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE((obj), GABBLE_TYPE_JINGLE_SHARE)) #define GABBLE_IS_JINGLE_SHARE_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE((klass), GABBLE_TYPE_JINGLE_SHARE)) #define GABBLE_JINGLE_SHARE_GET_CLASS(obj) \ (G_TYPE_INSTANCE_GET_CLASS ((obj), GABBLE_TYPE_JINGLE_SHARE, \ GabbleJingleShareClass)) struct _GabbleJingleShareClass { WockyJingleContentClass parent_class; }; typedef struct _GabbleJingleSharePrivate GabbleJingleSharePrivate; typedef struct _GabbleJingleShare GabbleJingleShare; struct _GabbleJingleShare { WockyJingleContent parent; GabbleJingleSharePrivate *priv; }; typedef struct { gboolean folder; gboolean image; guint64 size; gchar *name; guint image_width; guint image_height; } GabbleJingleShareManifestEntry; typedef struct { gchar *source_url; gchar *preview_url; /* a list of g_slice_new (GabbleJingleShareManifestEntry) */ GList *entries; } GabbleJingleShareManifest; void jingle_share_register (WockyJingleFactory *factory); gchar *gabble_jingle_share_get_source_url (GabbleJingleShare *content); gchar *gabble_jingle_share_get_preview_url (GabbleJingleShare *content); GabbleJingleShareManifest *gabble_jingle_share_get_manifest ( GabbleJingleShare *content); #endif /* __JINGLE_SHARE_H__ */ telepathy-gabble-0.18.2/src/call-stream.c0000644000175000017500000004366512227000321020157 0ustar00smcvsmcv00000000000000/* * gabble-call-stream.c - Source for GabbleCallStream * Copyright (C) 2009 Collabora Ltd. * @author Sjoerd Simons * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 "config.h" #include #include #include #include #include "call-stream.h" #include "connection.h" #include "jingle-tp-util.h" #include "util.h" #define DEBUG_FLAG GABBLE_DEBUG_MEDIA #include "debug.h" static GPtrArray *gabble_call_stream_add_candidates ( TpBaseMediaCallStream *stream, const GPtrArray *candidates, GError **error); static gboolean gabble_call_stream_set_sending (TpBaseMediaCallStream *stream, gboolean sending, GError **error); static void gabble_call_stream_request_receiving (TpBaseMediaCallStream *stream, TpHandle contact, gboolean receive); G_DEFINE_TYPE(GabbleCallStream, gabble_call_stream, TP_TYPE_BASE_MEDIA_CALL_STREAM) /* properties */ enum { PROP_JINGLE_CONTENT = 1, /* Media interface properties */ PROP_LOCAL_CANDIDATES, PROP_ENDPOINTS, PROP_TRANSPORT, PROP_STUN_SERVERS, PROP_RELAY_INFO, PROP_HAS_SERVER_INFO, PROP_CAN_REQUEST_RECEIVING }; #if 0 /* signal enum */ enum { LAST_SIGNAL }; static guint signals[LAST_SIGNAL] = {0}; #endif /* private structure */ struct _GabbleCallStreamPrivate { gboolean dispose_has_run; WockyJingleContent *content; }; static void gabble_call_stream_init (GabbleCallStream *self) { GabbleCallStreamPrivate *priv = G_TYPE_INSTANCE_GET_PRIVATE (self, GABBLE_TYPE_CALL_STREAM, GabbleCallStreamPrivate); self->priv = priv; } static void gabble_call_stream_dispose (GObject *object); static void gabble_call_stream_finalize (GObject *object); static GPtrArray * get_stun_servers (GabbleCallStream *self) { GPtrArray *arr; WockyJingleFactory *jf; GList *stun_servers; arr = g_ptr_array_new_with_free_func ((GDestroyNotify) g_value_array_free); jf = wocky_jingle_session_get_factory (self->priv->content->session); stun_servers = wocky_jingle_info_get_stun_servers ( wocky_jingle_factory_get_jingle_info (jf)); while (stun_servers != NULL) { WockyStunServer *stun_server = stun_servers->data; GValueArray *va = tp_value_array_build (2, G_TYPE_STRING, stun_server->address, G_TYPE_UINT, (guint) stun_server->port, G_TYPE_INVALID); g_ptr_array_add (arr, va); stun_servers = g_list_delete_link (stun_servers, stun_servers); } return arr; } static void gabble_call_stream_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { GabbleCallStream *stream = GABBLE_CALL_STREAM (object); GabbleCallStreamPrivate *priv = stream->priv; switch (property_id) { case PROP_JINGLE_CONTENT: g_value_set_object (value, priv->content); break; case PROP_CAN_REQUEST_RECEIVING: { WockyJingleDialect dialect = wocky_jingle_session_get_dialect (priv->content->session); g_value_set_boolean (value, !WOCKY_JINGLE_DIALECT_IS_GOOGLE (dialect)); } break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void gabble_call_stream_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { GabbleCallStream *stream = GABBLE_CALL_STREAM (object); GabbleCallStreamPrivate *priv = stream->priv; switch (property_id) { case PROP_JINGLE_CONTENT: priv->content = g_value_dup_object (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void google_relay_session_cb (GPtrArray *relays, gpointer user_data) { TpWeakRef *weak_ref = user_data; TpBaseMediaCallStream *stream = TP_BASE_MEDIA_CALL_STREAM ( tp_weak_ref_dup_object (weak_ref)); if (stream != NULL) { GPtrArray *tp_relays = gabble_build_tp_relay_info (relays); tp_base_media_call_stream_set_relay_info (stream, tp_relays); g_ptr_array_unref (tp_relays); g_object_unref (stream); } tp_weak_ref_destroy (weak_ref); } static void content_state_changed_cb (WockyJingleContent *content, GParamSpec *spec, gpointer user_data) { GabbleCallStream *self = GABBLE_CALL_STREAM (user_data); gabble_call_stream_update_member_states (self); } static void content_remote_members_changed_cb (WockyJingleContent *content, GParamSpec *spec, gpointer user_data) { GabbleCallStream *self = GABBLE_CALL_STREAM (user_data); gabble_call_stream_update_member_states (self); } static void jingle_info_stun_server_changed_cb ( WockyJingleInfo *jingle_info, const gchar *stun_server, guint stun_port, GabbleCallStream *self) { GPtrArray *stun_servers = get_stun_servers (self); tp_base_media_call_stream_set_stun_servers ( TP_BASE_MEDIA_CALL_STREAM (self), stun_servers); g_ptr_array_unref (stun_servers); } static void _new_candidates_cb ( WockyJingleContent *content, GList *candidates, TpCallStreamEndpoint *endpoint) { GPtrArray *tp_candidates; gchar *ufrag, *pwd; if (candidates == NULL) return; if (wocky_jingle_content_get_credentials (content, &ufrag, &pwd)) tp_call_stream_endpoint_set_remote_credentials (endpoint, ufrag, pwd); tp_candidates = gabble_call_candidates_to_array (candidates); tp_call_stream_endpoint_add_new_candidates (endpoint, tp_candidates); g_boxed_free (TP_ARRAY_TYPE_CANDIDATE_LIST, tp_candidates); } static void _endpoint_state_changed_cb ( TpCallStreamEndpoint *endpoint, GParamSpec *spec, WockyJingleContent *content) { TpMediaStreamState state; /* We only care about connecting RTP, RTCP is optional */ state = tp_call_stream_endpoint_get_state (endpoint, 1); wocky_jingle_content_set_transport_state (content, state); } static TpCallStreamEndpoint * _hook_up_endpoint (GabbleCallStream *self, const gchar *path, WockyJingleContent *content) { TpBaseCallStream *base = (TpBaseCallStream *) self; TpBaseConnection *conn = tp_base_call_stream_get_connection (base); TpDBusDaemon *bus = tp_base_connection_get_dbus_daemon (conn); TpCallStreamEndpoint *endpoint; TpStreamTransportType type = 0; GPtrArray *tp_candidates; GList *candidates; gchar *ufrag, *pwd; switch (wocky_jingle_content_get_transport_type (content)) { case JINGLE_TRANSPORT_GOOGLE_P2P: type = TP_STREAM_TRANSPORT_TYPE_GTALK_P2P; break; case JINGLE_TRANSPORT_RAW_UDP: type = TP_STREAM_TRANSPORT_TYPE_RAW_UDP; break; case JINGLE_TRANSPORT_ICE_UDP: type = TP_STREAM_TRANSPORT_TYPE_ICE; break; case JINGLE_TRANSPORT_UNKNOWN: default: g_assert_not_reached (); } /* FIXME: ice??? */ endpoint = tp_call_stream_endpoint_new (bus, path, type, FALSE); if (wocky_jingle_content_get_credentials (content, &ufrag, &pwd)) tp_call_stream_endpoint_set_remote_credentials (endpoint, ufrag, pwd); candidates = wocky_jingle_content_get_remote_candidates (content); tp_candidates = gabble_call_candidates_to_array (candidates); tp_call_stream_endpoint_add_new_candidates (endpoint, tp_candidates); g_boxed_free (TP_ARRAY_TYPE_CANDIDATE_LIST, tp_candidates); tp_g_signal_connect_object (content, "new-candidates", G_CALLBACK (_new_candidates_cb), endpoint, 0); tp_g_signal_connect_object (endpoint, "notify::endpoint-state", G_CALLBACK(_endpoint_state_changed_cb), content, 0); return endpoint; } static void gabble_call_stream_constructed (GObject *obj) { GabbleCallStream *self = GABBLE_CALL_STREAM (obj); TpBaseCallStream *base = (TpBaseCallStream *) self; TpBaseMediaCallStream *media_base = (TpBaseMediaCallStream *) self; GabbleCallStreamPrivate *priv = self->priv; GabbleConnection *conn; TpCallStreamEndpoint *endpoint; gchar *path; WockyJingleTransportType transport; GPtrArray *stun_servers; gboolean locally_created; if (G_OBJECT_CLASS (gabble_call_stream_parent_class)->constructed != NULL) G_OBJECT_CLASS (gabble_call_stream_parent_class)->constructed (obj); conn = GABBLE_CONNECTION (tp_base_call_stream_get_connection (base)); g_object_get (priv->content, "locally-created", &locally_created, NULL); if (locally_created && wocky_jingle_content_sending (priv->content)) tp_base_media_call_stream_set_local_sending ( TP_BASE_MEDIA_CALL_STREAM (self), TRUE); /* Currently we'll only have one endpoint we know right away */ path = g_strdup_printf ("%s/Endpoint", tp_base_call_stream_get_object_path (base)); endpoint = _hook_up_endpoint (self, path, priv->content); tp_base_media_call_stream_add_endpoint (media_base, endpoint); g_object_unref (endpoint); g_free (path); transport = wocky_jingle_content_get_transport_type (priv->content); if (transport == JINGLE_TRANSPORT_GOOGLE_P2P) { DEBUG ("Attempting to create Google relay session"); /* See if our server is Google, and if it is, ask them for a relay. * We ask for enough relays for 2 components (RTP and RTCP) since we * don't yet know whether there will be RTCP. */ wocky_jingle_info_create_google_relay_session ( gabble_jingle_mint_get_info (conn->jingle_mint), 2, google_relay_session_cb, tp_weak_ref_new (self, NULL, NULL)); } else { GPtrArray *relays = g_ptr_array_new (); tp_base_media_call_stream_set_relay_info (media_base, relays); g_ptr_array_unref (relays); } stun_servers = get_stun_servers (self); tp_base_media_call_stream_set_stun_servers ( TP_BASE_MEDIA_CALL_STREAM (self), stun_servers); g_ptr_array_unref (stun_servers); gabble_call_stream_update_member_states (GABBLE_CALL_STREAM (obj)); gabble_signal_connect_weak (priv->content, "notify::state", G_CALLBACK (content_state_changed_cb), obj); gabble_signal_connect_weak (priv->content, "notify::senders", G_CALLBACK (content_remote_members_changed_cb), obj); gabble_signal_connect_weak ( gabble_jingle_mint_get_info (conn->jingle_mint), "stun-server-changed", G_CALLBACK (jingle_info_stun_server_changed_cb), obj); } void gabble_call_stream_update_member_states (GabbleCallStream *self) { TpBaseCallStream *base = TP_BASE_CALL_STREAM (self); TpBaseMediaCallStream *bmcs = TP_BASE_MEDIA_CALL_STREAM (self); GabbleCallStreamPrivate *priv = self->priv; WockyJingleContentState state; TpSendingState local_state; TpSendingState remote_state; TpBaseConnection *conn = tp_base_call_stream_get_connection (base); TpHandleRepoIface *contact_repo = tp_base_connection_get_handles ( conn, TP_HANDLE_TYPE_CONTACT); TpHandle peer; g_object_get (priv->content, "state", &state, NULL); if (state == WOCKY_JINGLE_CONTENT_STATE_REMOVING) return; local_state = tp_base_call_stream_get_local_sending_state (base); remote_state = tp_base_call_stream_get_remote_sending_state (base, 0); if (wocky_jingle_content_sending (priv->content)) { if (tp_base_media_call_stream_get_local_sending (bmcs)) local_state = TP_SENDING_STATE_SENDING; else local_state = TP_SENDING_STATE_PENDING_SEND; } else { if (tp_base_media_call_stream_get_local_sending (bmcs)) local_state = TP_SENDING_STATE_PENDING_STOP_SENDING; else local_state = TP_SENDING_STATE_NONE; } if (wocky_jingle_content_receiving (priv->content)) { remote_state = TP_SENDING_STATE_SENDING; } else { remote_state = TP_SENDING_STATE_NONE; } DEBUG ("State: %d Local: %d Remote: %d", state, local_state, remote_state); tp_base_call_stream_update_local_sending_state (base, local_state, 0, TP_CALL_STATE_CHANGE_REASON_PROGRESS_MADE, "", ""); peer = tp_handle_ensure (contact_repo, wocky_jingle_session_get_peer_jid (priv->content->session), NULL, NULL); tp_base_call_stream_update_remote_sending_state (base, peer, remote_state, 0, TP_CALL_STATE_CHANGE_REASON_PROGRESS_MADE, "", ""); } static void gabble_call_stream_class_init (GabbleCallStreamClass *gabble_call_stream_class) { GObjectClass *object_class = G_OBJECT_CLASS (gabble_call_stream_class); TpBaseMediaCallStreamClass *bmcs_class = TP_BASE_MEDIA_CALL_STREAM_CLASS (gabble_call_stream_class); GParamSpec *param_spec; g_type_class_add_private (gabble_call_stream_class, sizeof (GabbleCallStreamPrivate)); object_class->set_property = gabble_call_stream_set_property; object_class->get_property = gabble_call_stream_get_property; object_class->dispose = gabble_call_stream_dispose; object_class->finalize = gabble_call_stream_finalize; object_class->constructed = gabble_call_stream_constructed; param_spec = g_param_spec_object ("jingle-content", "Jingle Content", "The Jingle Content related to this content object", WOCKY_TYPE_JINGLE_CONTENT, G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, PROP_JINGLE_CONTENT, param_spec); g_object_class_override_property (object_class, PROP_CAN_REQUEST_RECEIVING, "can-request-receiving"); bmcs_class->add_local_candidates = gabble_call_stream_add_candidates; bmcs_class->set_sending = gabble_call_stream_set_sending; bmcs_class->request_receiving = gabble_call_stream_request_receiving; } void gabble_call_stream_dispose (GObject *object) { GabbleCallStream *self = GABBLE_CALL_STREAM (object); GabbleCallStreamPrivate *priv = self->priv; if (priv->dispose_has_run) return; priv->dispose_has_run = TRUE; tp_clear_object (&priv->content); if (G_OBJECT_CLASS (gabble_call_stream_parent_class)->dispose) G_OBJECT_CLASS (gabble_call_stream_parent_class)->dispose (object); } void gabble_call_stream_finalize (GObject *object) { G_OBJECT_CLASS (gabble_call_stream_parent_class)->finalize (object); } static GPtrArray * gabble_call_stream_add_candidates (TpBaseMediaCallStream *stream, const GPtrArray *candidates, GError **error) { GabbleCallStream *self = GABBLE_CALL_STREAM (stream); GabbleCallStreamPrivate *priv = self->priv; GPtrArray *accepted_candidates = g_ptr_array_sized_new (candidates->len); GList *l = NULL; guint i; for (i = 0; i < candidates->len ; i++) { GValueArray *va; WockyJingleCandidate *c; GHashTable *info; guint tptype; WockyJingleCandidateType type; /* borrowed strings, owned by other people. */ const gchar *username; const gchar *password; const gchar *foundation; va = g_ptr_array_index (candidates, i); info = g_value_get_boxed (va->values + 3); tptype = tp_asv_get_uint32 (info, "type", NULL); switch (tptype) { default: /* Anything else is local */ case TP_CALL_STREAM_CANDIDATE_TYPE_HOST: type = WOCKY_JINGLE_CANDIDATE_TYPE_LOCAL; break; case TP_CALL_STREAM_CANDIDATE_TYPE_SERVER_REFLEXIVE: type = WOCKY_JINGLE_CANDIDATE_TYPE_STUN; break; case TP_CALL_STREAM_CANDIDATE_TYPE_RELAY: type = WOCKY_JINGLE_CANDIDATE_TYPE_RELAY; break; } username = tp_asv_get_string (info, "username"); if (username == NULL) username = tp_base_media_call_stream_get_username (stream); password = tp_asv_get_string (info, "password"); if (password == NULL) password = tp_base_media_call_stream_get_password (stream); foundation = tp_asv_get_string (info, "foundation"); if (foundation == NULL) foundation = "1"; c = wocky_jingle_candidate_new ( /* transport protocol */ tp_asv_get_uint32 (info, "protocol", NULL), /* Candidate type */ type, /* id/foundation */ foundation, /* component */ g_value_get_uint (va->values + 0), /* ip */ g_value_get_string (va->values + 1), /* port */ g_value_get_uint (va->values + 2), /* generation */ 0, /* preference */ tp_asv_get_uint32 (info, "priority", NULL), /* username, password */ username, password, /* network */ 0); l = g_list_append (l, c); g_ptr_array_add (accepted_candidates, va); } wocky_jingle_content_add_candidates (priv->content, l); if (accepted_candidates->len == 0 && candidates->len != 0) { g_set_error_literal (error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT, "All candidates had the wrong Type"); tp_clear_pointer (&accepted_candidates, g_ptr_array_unref); } return accepted_candidates; } static gboolean gabble_call_stream_set_sending (TpBaseMediaCallStream *stream, gboolean sending, GError **error) { GabbleCallStream *self = GABBLE_CALL_STREAM (stream); if (sending) tp_base_media_call_stream_set_local_sending (stream, TRUE); wocky_jingle_content_set_sending (self->priv->content, sending); return TRUE; } static void gabble_call_stream_request_receiving (TpBaseMediaCallStream *stream, TpHandle contact, gboolean receive) { GabbleCallStream *self = GABBLE_CALL_STREAM (stream); wocky_jingle_content_request_receiving (self->priv->content, receive); } WockyJingleContent * gabble_call_stream_get_jingle_content (GabbleCallStream *stream) { return stream->priv->content; } telepathy-gabble-0.18.2/src/call-stream.h0000644000175000017500000000451112200204333020147 0ustar00smcvsmcv00000000000000/* * gabble-call-stream.h - Header for GabbleCallStream * Copyright (C) 2009 Collabora Ltd. * @author Sjoerd Simons * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 __GABBLE_CALL_STREAM_H__ #define __GABBLE_CALL_STREAM_H__ #include #include #include G_BEGIN_DECLS typedef struct _GabbleCallStream GabbleCallStream; typedef struct _GabbleCallStreamPrivate GabbleCallStreamPrivate; typedef struct _GabbleCallStreamClass GabbleCallStreamClass; struct _GabbleCallStreamClass { TpBaseMediaCallStreamClass parent_class; }; struct _GabbleCallStream { TpBaseMediaCallStream parent; GabbleCallStreamPrivate *priv; }; GType gabble_call_stream_get_type (void); /* TYPE MACROS */ #define GABBLE_TYPE_CALL_STREAM \ (gabble_call_stream_get_type ()) #define GABBLE_CALL_STREAM(obj) \ (G_TYPE_CHECK_INSTANCE_CAST((obj), GABBLE_TYPE_CALL_STREAM, GabbleCallStream)) #define GABBLE_CALL_STREAM_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST((klass), GABBLE_TYPE_CALL_STREAM, \ GabbleCallStreamClass)) #define GABBLE_IS_CALL_STREAM(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE((obj), GABBLE_TYPE_CALL_STREAM)) #define GABBLE_IS_CALL_STREAM_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE((klass), GABBLE_TYPE_CALL_STREAM)) #define GABBLE_CALL_STREAM_GET_CLASS(obj) \ (G_TYPE_INSTANCE_GET_CLASS ((obj), GABBLE_TYPE_CALL_STREAM, \ GabbleCallStreamClass)) WockyJingleContent *gabble_call_stream_get_jingle_content ( GabbleCallStream *stream); void gabble_call_stream_update_member_states (GabbleCallStream *self); G_END_DECLS #endif /* #ifndef __GABBLE_CALL_STREAM_H__*/ telepathy-gabble-0.18.2/src/call-member-content.c0000644000175000017500000003216512200204333021574 0ustar00smcvsmcv00000000000000/* * call-member-content.c - Source for GabbleCallMemberContent * Copyright (C) 2010 Collabora Ltd. * @author Sjoerd Simons * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 "config.h" #include #include #include "call-member.h" #include "call-member-content.h" #include "util.h" #include "namespaces.h" #define DEBUG_FLAG GABBLE_DEBUG_MEDIA #include "debug.h" G_DEFINE_TYPE(GabbleCallMemberContent, gabble_call_member_content, G_TYPE_OBJECT) /* properties */ enum { PROP_JINGLE_CONTENT = 1, PROP_CONTENT_NAME, PROP_MEDIA_TYPE, PROP_MEMBER }; /* signal enum */ enum { CODECS_CHANGED, GOT_JINGLE_CONTENT, REMOVED, LAST_SIGNAL }; static guint signals[LAST_SIGNAL] = {0}; /* private structure */ struct _GabbleCallMemberContentPrivate { gboolean dispose_has_run; GabbleCallMember *member; WockyJingleContent *jingle_content; gchar *name; WockyJingleMediaType media_type; GList *remote_codecs; gboolean removed; }; #define GABBLE_CALL_MEMBER_CONTENT_GET_PRIVATE(o) \ (G_TYPE_INSTANCE_GET_PRIVATE ((o), GABBLE_TYPE_CALL_MEMBER_CONTENT, \ GabbleCallMemberContentPrivate)) static void gabble_call_member_content_init (GabbleCallMemberContent *self) { GabbleCallMemberContentPrivate *priv = GABBLE_CALL_MEMBER_CONTENT_GET_PRIVATE (self); self->priv = priv; } static void gabble_call_member_content_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { GabbleCallMemberContent *self = GABBLE_CALL_MEMBER_CONTENT (object); GabbleCallMemberContentPrivate *priv = self->priv; switch (property_id) { case PROP_MEMBER: g_value_set_object (value, priv->member); break; case PROP_JINGLE_CONTENT: g_value_set_object (value, priv->jingle_content); break; case PROP_CONTENT_NAME: g_value_set_string (value, priv->name); break; case PROP_MEDIA_TYPE: g_value_set_uint (value, priv->media_type); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void gabble_call_member_content_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { GabbleCallMemberContent *self = GABBLE_CALL_MEMBER_CONTENT (object); GabbleCallMemberContentPrivate *priv = self->priv; switch (property_id) { case PROP_JINGLE_CONTENT: gabble_call_member_content_set_jingle_content (self, g_value_get_object (value)); break; case PROP_MEMBER: priv->member = g_value_get_object (value); break; case PROP_CONTENT_NAME: priv->name = g_value_dup_string (value); g_assert (priv->name != NULL); break; case PROP_MEDIA_TYPE: priv->media_type = g_value_get_uint (value); g_assert (priv->media_type != WOCKY_JINGLE_MEDIA_TYPE_NONE); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void gabble_call_member_content_dispose (GObject *object); static void gabble_call_member_content_finalize (GObject *object); void gabble_call_member_content_add_to_session (GabbleCallMemberContent *self) { GabbleCallMemberContentPrivate *priv = self->priv; const gchar *content_ns; WockyJingleSession *session; WockyJingleContent *content; const gchar *peer_resource; const gchar *transport_ns; if (priv->jingle_content != NULL) return; DEBUG ("Session set for: %s (current jingle %p)", priv->name, priv->jingle_content); session = gabble_call_member_get_session (priv->member); transport_ns = gabble_call_member_get_transport_ns (priv->member); content_ns = NS_JINGLE_RTP; g_assert (session != NULL); peer_resource = wocky_jingle_session_get_peer_resource (session); if (peer_resource != NULL) DEBUG ("existing call, using peer resource %s", peer_resource); else DEBUG ("existing call, using bare JID"); DEBUG ("Creating new jingle content with ns %s : %s", content_ns, transport_ns); content = wocky_jingle_session_add_content (session, priv->media_type, WOCKY_JINGLE_CONTENT_SENDERS_BOTH, priv->name, content_ns, transport_ns); gabble_call_member_content_set_jingle_content (self, content); } static void member_got_session_cb (GabbleCallMember *member, GParamSpec *param, gpointer user_data) { gabble_call_member_content_add_to_session ( GABBLE_CALL_MEMBER_CONTENT (user_data)); } static void gabble_call_member_content_constructed (GObject *obj) { GabbleCallMemberContent *self = GABBLE_CALL_MEMBER_CONTENT (obj); GabbleCallMemberContentPrivate *priv = self->priv; gabble_signal_connect_weak (priv->member, "notify::session", G_CALLBACK (member_got_session_cb), G_OBJECT (self)); if (G_OBJECT_CLASS (gabble_call_member_content_parent_class)->constructed != NULL) G_OBJECT_CLASS ( gabble_call_member_content_parent_class)->constructed (obj); } static void gabble_call_member_content_class_init ( GabbleCallMemberContentClass *gabble_call_member_content_class) { GObjectClass *object_class = G_OBJECT_CLASS (gabble_call_member_content_class); GParamSpec *param_spec; g_type_class_add_private (gabble_call_member_content_class, sizeof (GabbleCallMemberContentPrivate)); object_class->dispose = gabble_call_member_content_dispose; object_class->finalize = gabble_call_member_content_finalize; object_class->get_property = gabble_call_member_content_get_property; object_class->set_property = gabble_call_member_content_set_property; object_class->constructed = gabble_call_member_content_constructed; param_spec = g_param_spec_string ("name", "Name", "The name of this jingle content", NULL, G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, PROP_CONTENT_NAME, param_spec); param_spec = g_param_spec_uint ("media-type", "MediaType", "The media type of this jingle content", WOCKY_JINGLE_MEDIA_TYPE_NONE, WOCKY_JINGLE_MEDIA_TYPE_VIDEO, WOCKY_JINGLE_MEDIA_TYPE_NONE, G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, PROP_MEDIA_TYPE, param_spec); param_spec = g_param_spec_object ("jingle-content", "JingleContent", "The jingle content corresponding to this members content", WOCKY_TYPE_JINGLE_CONTENT, G_PARAM_CONSTRUCT | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, PROP_JINGLE_CONTENT, param_spec); param_spec = g_param_spec_object ("member", "CallMember", "The call member that has this as a content", GABBLE_TYPE_CALL_MEMBER, G_PARAM_CONSTRUCT | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, PROP_MEMBER, param_spec); signals[CODECS_CHANGED] = g_signal_new ("codecs-changed", G_OBJECT_CLASS_TYPE (gabble_call_member_content_class), G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED, 0, NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); signals[GOT_JINGLE_CONTENT] = g_signal_new ("got-jingle-content", G_OBJECT_CLASS_TYPE (gabble_call_member_content_class), G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED, 0, NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); signals[REMOVED] = g_signal_new ("removed", G_OBJECT_CLASS_TYPE (gabble_call_member_content_class), G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED, 0, NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); } void gabble_call_member_content_dispose (GObject *object) { GabbleCallMemberContent *self = GABBLE_CALL_MEMBER_CONTENT (object); GabbleCallMemberContentPrivate *priv = self->priv; if (priv->dispose_has_run) return; priv->dispose_has_run = TRUE; tp_clear_object (&priv->jingle_content); if (G_OBJECT_CLASS (gabble_call_member_content_parent_class)->dispose) G_OBJECT_CLASS (gabble_call_member_content_parent_class)->dispose (object); } void gabble_call_member_content_finalize (GObject *object) { GabbleCallMemberContent *self = GABBLE_CALL_MEMBER_CONTENT (object); GabbleCallMemberContentPrivate *priv = self->priv; g_free (priv->name); G_OBJECT_CLASS (gabble_call_member_content_parent_class)->finalize (object); } WockyJingleMediaType gabble_call_member_content_get_media_type (GabbleCallMemberContent *self) { return self->priv->media_type; } const gchar * gabble_call_member_content_get_name (GabbleCallMemberContent *self) { return self->priv->name; } WockyJingleContent * gabble_call_member_content_get_jingle_content ( GabbleCallMemberContent *self) { return self->priv->jingle_content; } GabbleCallMemberContent * gabble_call_member_content_new (const gchar *name, WockyJingleMediaType type, GabbleCallMember *member) { return GABBLE_CALL_MEMBER_CONTENT (g_object_new ( GABBLE_TYPE_CALL_MEMBER_CONTENT, "name", name, "media-type", type, "member", member, NULL)); } static void call_member_content_jingle_removed_cb (WockyJingleContent *jingle_content, GabbleCallMemberContent *content) { if (!content->priv->removed) { content->priv->removed = TRUE; g_signal_emit (content, signals[REMOVED], 0); } } static void call_member_content_jingle_media_description_cb (WockyJingleMediaRtp *media, WockyJingleMediaDescription *md, gpointer user_data) { GabbleCallMemberContent *self = GABBLE_CALL_MEMBER_CONTENT (user_data); DEBUG ("New codecs from jingle"); g_signal_emit (self, signals[CODECS_CHANGED], 0); } GabbleCallMemberContent * gabble_call_member_content_from_jingle_content ( WockyJingleContent *jingle_content, GabbleCallMember *member) { GabbleCallMemberContent *content; gchar *name; WockyJingleMediaType mtype; g_object_get (jingle_content, "name", &name, "media-type", &mtype, NULL); content = gabble_call_member_content_new (name, mtype, member); gabble_call_member_content_set_jingle_content (content, jingle_content); g_free (name); return content; } gboolean gabble_call_member_content_has_jingle_content ( GabbleCallMemberContent *self) { return self->priv->jingle_content != NULL; } GList * gabble_call_member_content_get_remote_codecs (GabbleCallMemberContent *self) { GList *jcodecs = NULL; if (self->priv->jingle_content != NULL) { WockyJingleMediaDescription *md; md = wocky_jingle_media_rtp_get_remote_media_description ( WOCKY_JINGLE_MEDIA_RTP (self->priv->jingle_content)); if (md != NULL) jcodecs = md->codecs; } return jcodecs != NULL ? jcodecs : self->priv->remote_codecs; } void gabble_call_member_content_set_remote_codecs (GabbleCallMemberContent *self, GList *codecs) { GabbleCallMemberContentPrivate *priv = self->priv; DEBUG ("New codecs set directly on the member"); if (priv->remote_codecs != NULL) { GList *changed = NULL; if (!jingle_media_rtp_compare_codecs (priv->remote_codecs, codecs, &changed, NULL) || changed == NULL) return; g_list_free (changed); } jingle_media_rtp_free_codecs (priv->remote_codecs); priv->remote_codecs = codecs; g_signal_emit (self, signals[CODECS_CHANGED], 0); } GabbleCallMember * gabble_call_member_content_get_member (GabbleCallMemberContent *self) { return self->priv->member; } void gabble_call_member_content_set_jingle_content (GabbleCallMemberContent *self, WockyJingleContent *content) { g_assert (self->priv->jingle_content == NULL); if (content == NULL) return; self->priv->jingle_content = g_object_ref (content); gabble_signal_connect_weak (content, "removed", G_CALLBACK (call_member_content_jingle_removed_cb), G_OBJECT (self)); gabble_signal_connect_weak (content, "remote-media-description", G_CALLBACK (call_member_content_jingle_media_description_cb), G_OBJECT (self)); g_signal_emit (self, signals[GOT_JINGLE_CONTENT], 0); } void gabble_call_member_content_remove (GabbleCallMemberContent *self) { GabbleCallMemberContentPrivate *priv = self->priv; if (priv->removed) return; priv->removed = TRUE; g_object_ref (self); /* Remove ourselves from the sesison */ if (priv->jingle_content != NULL) wocky_jingle_session_remove_content (priv->jingle_content->session, priv->jingle_content); g_signal_emit (self, signals[REMOVED], 0); g_object_unref (self); } telepathy-gabble-0.18.2/src/call-member-content.h0000644000175000017500000000707712200204333021605 0ustar00smcvsmcv00000000000000/* * call-member-content.h - Header for GabbleCallMemberContent * Copyright (C) 2010 Collabora Ltd. * @author Sjoerd Simons * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 __CALL_MEMBER_CONTENT_H__ #define __CALL_MEMBER_CONTENT_H__ #include #include #include "types.h" G_BEGIN_DECLS typedef struct _GabbleCallMemberContentPrivate GabbleCallMemberContentPrivate; typedef struct _GabbleCallMemberContentClass GabbleCallMemberContentClass; struct _GabbleCallMemberContentClass { GObjectClass parent_class; }; struct _GabbleCallMemberContent { GObject parent; GabbleCallMemberContentPrivate *priv; }; GType gabble_call_member_content_get_type (void); /* TYPE MACROS */ #define GABBLE_TYPE_CALL_MEMBER_CONTENT \ (gabble_call_member_content_get_type ()) #define GABBLE_CALL_MEMBER_CONTENT(obj) \ (G_TYPE_CHECK_INSTANCE_CAST((obj), GABBLE_TYPE_CALL_MEMBER_CONTENT, \ GabbleCallMemberContent)) #define GABBLE_CALL_MEMBER_CONTENT_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST((klass), GABBLE_TYPE_CALL_MEMBER_CONTENT, \ GabbleCallMemberContentClass)) #define GABBLE_IS_CALL_MEMBER_CONTENT(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE((obj), GABBLE_TYPE_CALL_MEMBER_CONTENT)) #define GABBLE_IS_CALL_MEMBER_CONTENT_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE((klass), GABBLE_TYPE_CALL_MEMBER_CONTENT)) #define GABBLE_CALL_MEMBER_CONTENT_GET_CLASS(obj) \ (G_TYPE_INSTANCE_GET_CLASS ((obj), GABBLE_TYPE_CALL_MEMBER_CONTENT, \ GabbleCallMemberContentClass)) GabbleCallMemberContent *gabble_call_member_content_new (const gchar *name, WockyJingleMediaType type, GabbleCallMember *member); GabbleCallMemberContent *gabble_call_member_content_from_jingle_content ( WockyJingleContent *jingle_content, GabbleCallMember *member); WockyJingleMediaType gabble_call_member_content_get_media_type ( GabbleCallMemberContent *self); const gchar *gabble_call_member_content_get_name ( GabbleCallMemberContent *self); WockyJingleContent *gabble_call_member_content_get_jingle_content ( GabbleCallMemberContent *self); gboolean gabble_call_member_content_has_jingle_content ( GabbleCallMemberContent *self); GList *gabble_call_member_content_get_remote_codecs ( GabbleCallMemberContent *self); void gabble_call_member_content_set_remote_codecs ( GabbleCallMemberContent *self, GList *codecs); GList * gabble_call_member_content_get_remote_codecs ( GabbleCallMemberContent *self); GabbleCallMember *gabble_call_member_content_get_member ( GabbleCallMemberContent *self); void gabble_call_member_content_set_jingle_content ( GabbleCallMemberContent *self, WockyJingleContent *content); void gabble_call_member_content_add_to_session ( GabbleCallMemberContent *self); void gabble_call_member_content_remove ( GabbleCallMemberContent *self); G_END_DECLS #endif /* #ifndef __CALL_MEMBER_CONTENT_H__*/ telepathy-gabble-0.18.2/src/call-member.c0000644000175000017500000004106712200204333020125 0ustar00smcvsmcv00000000000000/* * call-member.c - Source for CallMember * Copyright (C) 2010 Collabora Ltd. * @author Sjoerd Simons * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 "config.h" #include #include #include "connection.h" #include "call-member.h" #include "base-call-channel.h" #include "util.h" #include "namespaces.h" #define DEBUG_FLAG GABBLE_DEBUG_MEDIA #include "debug.h" G_DEFINE_TYPE(GabbleCallMember, gabble_call_member, G_TYPE_OBJECT) /* signal enum */ enum { FLAGS_CHANGED, CONTENT_ADDED, CONTENT_REMOVED, LAST_SIGNAL }; static guint signals[LAST_SIGNAL] = { 0 }; /* properties */ enum { PROP_CALL = 1, PROP_TARGET, PROP_SESSION }; /* private structure */ struct _GabbleCallMemberPrivate { TpHandle target; GabbleBaseCallChannel *call; TpCallMemberFlags flags; WockyJingleSession *session; GList *contents; gchar *transport_ns; gboolean accepted; gboolean dispose_has_run; }; #define GABBLE_CALL_MEMBER_GET_PRIVATE(o) \ (G_TYPE_INSTANCE_GET_PRIVATE ((o), GABBLE_TYPE_CALL_MEMBER, \ GabbleCallMemberPrivate)) static void gabble_call_member_init (GabbleCallMember *self) { GabbleCallMemberPrivate *priv = GABBLE_CALL_MEMBER_GET_PRIVATE (self); self->priv = priv; priv->accepted = FALSE; } static void gabble_call_member_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { GabbleCallMember *self = GABBLE_CALL_MEMBER (object); GabbleCallMemberPrivate *priv = self->priv; switch (property_id) { case PROP_CALL: g_value_set_object (value, gabble_call_member_get_connection (self)); break; case PROP_SESSION: g_value_set_object (value, priv->session); break; case PROP_TARGET: g_value_set_uint (value, priv->target); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void gabble_call_member_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { GabbleCallMember *self = GABBLE_CALL_MEMBER (object); GabbleCallMemberPrivate *priv = self->priv; switch (property_id) { case PROP_CALL: priv->call = g_value_get_object (value); g_assert (priv->call != NULL); break; case PROP_SESSION: gabble_call_member_set_session (self, WOCKY_JINGLE_SESSION (g_value_get_object (value))); break; case PROP_TARGET: priv->target = g_value_get_uint (value); g_assert (priv->target != 0); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void gabble_call_member_dispose (GObject *object); static void gabble_call_member_finalize (GObject *object); static void gabble_call_member_class_init ( GabbleCallMemberClass *gabble_call_member_class) { GObjectClass *object_class = G_OBJECT_CLASS (gabble_call_member_class); GParamSpec *param_spec; g_type_class_add_private (gabble_call_member_class, sizeof (GabbleCallMemberPrivate)); object_class->dispose = gabble_call_member_dispose; object_class->finalize = gabble_call_member_finalize; object_class->get_property = gabble_call_member_get_property; object_class->set_property = gabble_call_member_set_property; param_spec = g_param_spec_object ("call", "Call", "The base call object that contains this member", GABBLE_TYPE_BASE_CALL_CHANNEL, G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, PROP_CALL, param_spec); param_spec = g_param_spec_object ("session", "Session", "The jingle session below this call", WOCKY_TYPE_JINGLE_SESSION, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, PROP_SESSION, param_spec); param_spec = g_param_spec_uint ("target", "Target", "the target handle of member", 0, G_MAXUINT, 0, G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, PROP_TARGET, param_spec); signals[FLAGS_CHANGED] = g_signal_new ("flags-changed", G_OBJECT_CLASS_TYPE (gabble_call_member_class), G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED, 0, NULL, NULL, g_cclosure_marshal_VOID__UINT, G_TYPE_NONE, 1, G_TYPE_UINT); signals[CONTENT_ADDED] = g_signal_new ("content-added", G_OBJECT_CLASS_TYPE (gabble_call_member_class), G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED, 0, NULL, NULL, g_cclosure_marshal_VOID__OBJECT, G_TYPE_NONE, 1, G_TYPE_OBJECT); signals[CONTENT_REMOVED] = g_signal_new ("content-removed", G_OBJECT_CLASS_TYPE (gabble_call_member_class), G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED, 0, NULL, NULL, g_cclosure_marshal_VOID__OBJECT, G_TYPE_NONE, 1, G_TYPE_OBJECT); } void gabble_call_member_dispose (GObject *object) { GabbleCallMember *self = GABBLE_CALL_MEMBER (object); GabbleCallMemberPrivate *priv = self->priv; GList *l; if (priv->dispose_has_run) return; priv->dispose_has_run = TRUE; tp_clear_object (&priv->session); for (l = priv->contents ; l != NULL; l = g_list_next (l)) g_object_unref (l->data); tp_clear_pointer (&priv->contents, g_list_free); /* release any references held by the object here */ if (G_OBJECT_CLASS (gabble_call_member_parent_class)->dispose) G_OBJECT_CLASS (gabble_call_member_parent_class)->dispose (object); } void gabble_call_member_finalize (GObject *object) { GabbleCallMember *self = GABBLE_CALL_MEMBER (object); GabbleCallMemberPrivate *priv = self->priv; g_free (priv->transport_ns); priv->transport_ns = NULL; G_OBJECT_CLASS (gabble_call_member_parent_class)->finalize (object); } static void remote_state_changed_cb (WockyJingleSession *session, gpointer user_data) { GabbleCallMember *self = GABBLE_CALL_MEMBER (user_data); GabbleCallMemberPrivate *priv = self->priv; TpCallMemberFlags newflags = 0; if (wocky_jingle_session_get_remote_ringing (session)) newflags |= TP_CALL_MEMBER_FLAG_RINGING; if (wocky_jingle_session_get_remote_hold (session)) newflags |= TP_CALL_MEMBER_FLAG_HELD; if (priv->flags == newflags) return; priv->flags = newflags; DEBUG ("Call members flags changed to: %d", priv->flags); g_signal_emit (self, signals[FLAGS_CHANGED], 0, priv->flags); } static void member_content_removed_cb (GabbleCallMemberContent *mcontent, gpointer user_data) { GabbleCallMember *self = GABBLE_CALL_MEMBER (user_data); GabbleCallMemberPrivate *priv = self->priv; priv->contents = g_list_remove (priv->contents, mcontent); g_signal_emit (self, signals[CONTENT_REMOVED], 0, mcontent); g_object_unref (mcontent); } static void gabble_call_member_add_member_content (GabbleCallMember *self, GabbleCallMemberContent *content) { GabbleCallMemberPrivate *priv = self->priv; priv->contents = g_list_prepend (priv->contents, content); gabble_signal_connect_weak (content, "removed", G_CALLBACK (member_content_removed_cb), G_OBJECT (self)); g_signal_emit (self, signals[CONTENT_ADDED], 0, content); } /* This function handles additional contents added by the remote side */ static void new_content_cb (WockyJingleSession *session, WockyJingleContent *c, gpointer user_data) { GabbleCallMember *self = GABBLE_CALL_MEMBER (user_data); GabbleCallMemberContent *content = NULL; if (wocky_jingle_content_is_created_by_us (c)) return; content = gabble_call_member_content_from_jingle_content (c, self); gabble_call_member_add_member_content (self, content); } static gboolean call_member_update_existing_content (GabbleCallMember *self, WockyJingleContent *content) { GList *l; for (l = self->priv->contents; l != NULL ; l = g_list_next (l)) { GabbleCallMemberContent *mcontent = GABBLE_CALL_MEMBER_CONTENT (l->data); if (gabble_call_member_content_has_jingle_content (mcontent)) continue; if (!tp_strdiff (gabble_call_member_content_get_name (mcontent), wocky_jingle_content_get_name (content))) { gabble_call_member_content_set_jingle_content (mcontent, content); return TRUE; } } return FALSE; } void gabble_call_member_set_session (GabbleCallMember *self, WockyJingleSession *session) { GabbleCallMemberPrivate *priv = self->priv; GList *c, *contents; g_assert (priv->session == NULL); g_assert (session != NULL); DEBUG ("Setting session: %p -> %p\n", self, session); priv->session = g_object_ref (session); contents = wocky_jingle_session_get_contents (session); for (c = contents ; c != NULL; c = g_list_next (c)) { WockyJingleContent *content = WOCKY_JINGLE_CONTENT (c->data); if (priv->transport_ns == NULL) { g_object_get (content, "transport-ns", &priv->transport_ns, NULL); } if (!call_member_update_existing_content (self, content)) { GabbleCallMemberContent *mcontent = gabble_call_member_content_from_jingle_content (content, self); gabble_call_member_add_member_content (self, mcontent); } } g_object_notify (G_OBJECT (self), "session"); gabble_signal_connect_weak (priv->session, "remote-state-changed", G_CALLBACK (remote_state_changed_cb), G_OBJECT (self)); gabble_signal_connect_weak (priv->session, "new-content", G_CALLBACK (new_content_cb), G_OBJECT (self)); if (priv->accepted) gabble_call_member_accept (self); g_list_free (contents); } WockyJingleSession * gabble_call_member_get_session (GabbleCallMember *self) { return self->priv->session; } TpCallMemberFlags gabble_call_member_get_flags (GabbleCallMember *self) { return self->priv->flags; } TpHandle gabble_call_member_get_handle ( GabbleCallMember *self) { return self->priv->target; } GList * gabble_call_member_get_contents (GabbleCallMember *self) { GabbleCallMemberPrivate *priv = self->priv; return priv->contents; } GabbleCallMemberContent * gabble_call_member_ensure_content (GabbleCallMember *self, const gchar *name, WockyJingleMediaType mtype) { GabbleCallMemberPrivate *priv = self->priv; GList *l; GabbleCallMemberContent *content = NULL; for (l = priv->contents ; l != NULL; l = g_list_next (l)) { GabbleCallMemberContent *c = GABBLE_CALL_MEMBER_CONTENT (l->data); if (gabble_call_member_content_get_media_type (c) == mtype && !tp_strdiff (gabble_call_member_content_get_name (c), name)) { content = c; break; } } if (content == NULL) { content = gabble_call_member_content_new (name, mtype, self); gabble_call_member_add_member_content (self, content); } return content; } GabbleCallMemberContent * gabble_call_member_create_content (GabbleCallMember *self, const gchar *name, WockyJingleMediaType mtype, WockyJingleContentSenders senders, GError **error) { GabbleCallMemberPrivate *priv = self->priv; const gchar *content_ns; WockyJingleContent *c; GabbleCallMemberContent *content; const gchar *peer_resource; g_assert (priv->session != NULL); peer_resource = wocky_jingle_session_get_peer_resource (priv->session); DEBUG ("Creating new content %s, type %d", name, mtype); if (peer_resource != NULL) DEBUG ("existing call, using peer resource %s", peer_resource); else DEBUG ("existing call, using bare JID"); content_ns = jingle_pick_best_content_type (gabble_call_member_get_connection (self), priv->target, peer_resource, mtype); if (content_ns == NULL) { g_set_error (error, TP_ERROR, TP_ERROR_NOT_AVAILABLE, "Content type %d not available for this resource", mtype); return NULL; } DEBUG ("Creating new jingle content with ns %s : %s", content_ns, priv->transport_ns); c = wocky_jingle_session_add_content (priv->session, mtype, senders, name, content_ns, priv->transport_ns); g_assert (c != NULL); content = gabble_call_member_content_from_jingle_content (c, self); gabble_call_member_add_member_content (self, content); return content; } void gabble_call_member_accept (GabbleCallMember *self) { self->priv->accepted = TRUE; if (self->priv->session != NULL) wocky_jingle_session_accept (self->priv->session); } /** * Start a new session using the existing contents for this member. For now * assumes we're using the latest jingle dialect and ice-udp * FIXME: make dialect and transport selection more dynamic? */ gboolean gabble_call_member_open_session (GabbleCallMember *self, GError **error) { GabbleCallMemberPrivate *priv = self->priv; GabbleConnection *conn = gabble_call_member_get_connection (self); WockyJingleFactory *jf; WockyJingleSession *session; gchar *jid; jid = gabble_peer_to_jid (conn, priv->target, NULL); jf = gabble_jingle_mint_get_factory (conn->jingle_mint); g_return_val_if_fail (jf != NULL, FALSE); session = wocky_jingle_factory_create_session (jf, jid, WOCKY_JINGLE_DIALECT_V032, FALSE); DEBUG ("Created a jingle session: %p", session); priv->transport_ns = g_strdup (NS_JINGLE_TRANSPORT_ICEUDP); gabble_call_member_set_session (self, session); g_free (jid); return TRUE; } gboolean gabble_call_member_start_session (GabbleCallMember *self, const gchar *audio_name, const gchar *video_name, GError **error) { GabbleCallMemberPrivate *priv = self->priv; TpBaseChannel *base_channel = TP_BASE_CHANNEL (priv->call); TpHandle target = tp_base_channel_get_target_handle (base_channel); const gchar *resource; WockyJingleDialect dialect; gchar *jid; const gchar *transport; WockyJingleFactory *jf; WockyJingleSession *session; /* FIXME might need to wait on capabilities, also don't need transport * and dialect already */ if (!jingle_pick_best_resource (gabble_call_member_get_connection (self), target, audio_name != NULL, video_name != NULL, &transport, &dialect, &resource)) { g_set_error (error, TP_ERROR, TP_ERROR_NOT_CAPABLE, "member does not have the desired audio/video capabilities"); return FALSE; } jid = gabble_peer_to_jid (gabble_call_member_get_connection (self), target, resource); jf = gabble_jingle_mint_get_factory ( gabble_call_member_get_connection (self)->jingle_mint); g_return_val_if_fail (jf != NULL, FALSE); session = wocky_jingle_factory_create_session (jf, jid, dialect, FALSE); g_free (jid); gabble_call_member_set_session (self, session); priv->transport_ns = g_strdup (transport); if (audio_name != NULL) gabble_call_member_create_content (self, audio_name, WOCKY_JINGLE_MEDIA_TYPE_AUDIO, WOCKY_JINGLE_CONTENT_SENDERS_BOTH, NULL); if (video_name != NULL) gabble_call_member_create_content (self, video_name, WOCKY_JINGLE_MEDIA_TYPE_VIDEO, WOCKY_JINGLE_CONTENT_SENDERS_BOTH, NULL); return TRUE; } GabbleConnection * gabble_call_member_get_connection (GabbleCallMember *self) { TpBaseChannel *base_chan = TP_BASE_CHANNEL (self->priv->call); return GABBLE_CONNECTION (tp_base_channel_get_connection (base_chan)); } const gchar * gabble_call_member_get_transport_ns (GabbleCallMember *self) { return self->priv->transport_ns; } void gabble_call_member_shutdown (GabbleCallMember *self) { GabbleCallMemberPrivate *priv = self->priv; if (priv->session != NULL) { wocky_jingle_session_terminate (priv->session, WOCKY_JINGLE_REASON_UNKNOWN, NULL, NULL); } /* removing the content will remove it from our list */ while (priv->contents != NULL) gabble_call_member_content_remove ( GABBLE_CALL_MEMBER_CONTENT (priv->contents->data)); } telepathy-gabble-0.18.2/src/call-member.h0000644000175000017500000000655312200204333020133 0ustar00smcvsmcv00000000000000/* * call-member.h - Header for CallMember * Copyright (C) 2010 Collabora Ltd. * @author Sjoerd Simons * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 __GABBLE_CALL_MEMBER_H__ #define __GABBLE_CALL_MEMBER_H__ #include #include #include #include "types.h" #include "call-member-content.h" G_BEGIN_DECLS typedef struct _GabbleCallMemberPrivate GabbleCallMemberPrivate; typedef struct _GabbleCallMemberClass GabbleCallMemberClass; struct _GabbleCallMemberClass { GObjectClass parent_class; }; struct _GabbleCallMember { GObject parent; GabbleCallMemberPrivate *priv; }; GType gabble_call_member_get_type (void); /* TYPE MACROS */ #define GABBLE_TYPE_CALL_MEMBER \ (gabble_call_member_get_type ()) #define GABBLE_CALL_MEMBER(obj) \ (G_TYPE_CHECK_INSTANCE_CAST((obj), GABBLE_TYPE_CALL_MEMBER, \ GabbleCallMember)) #define GABBLE_CALL_MEMBER_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST((klass), GABBLE_TYPE_CALL_MEMBER, \ GabbleCallMemberClass)) #define GABBLE_IS_CALL_MEMBER(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE((obj), GABBLE_TYPE_CALL_MEMBER)) #define GABBLE_IS_CALL_MEMBER_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE((klass), GABBLE_TYPE_CALL_MEMBER)) #define GABBLE_CALL_MEMBER_GET_CLASS(obj) \ (G_TYPE_INSTANCE_GET_CLASS ((obj), GABBLE_TYPE_CALL_MEMBER, \ GabbleCallMemberClass)) void gabble_call_member_set_session (GabbleCallMember *member, WockyJingleSession *session); TpHandle gabble_call_member_get_handle ( GabbleCallMember *self); WockyJingleSession *gabble_call_member_get_session ( GabbleCallMember *self); TpCallMemberFlags gabble_call_member_get_flags ( GabbleCallMember *self); GList *gabble_call_member_get_contents (GabbleCallMember *self); GabbleCallMemberContent * gabble_call_member_ensure_content ( GabbleCallMember *self, const gchar *name, WockyJingleMediaType mtype); GabbleCallMemberContent * gabble_call_member_create_content ( GabbleCallMember *self, const gchar *name, WockyJingleMediaType mtype, WockyJingleContentSenders senders, GError **error); gboolean gabble_call_member_start_session (GabbleCallMember *self, const gchar *audio_name, const gchar *video_name, GError **error); gboolean gabble_call_member_open_session (GabbleCallMember *self, GError **error); void gabble_call_member_accept (GabbleCallMember *self); GabbleConnection * gabble_call_member_get_connection (GabbleCallMember *self); const gchar *gabble_call_member_get_transport_ns (GabbleCallMember *self); void gabble_call_member_shutdown (GabbleCallMember *self); G_END_DECLS #endif /* #ifndef __GABBLE_CALL_MEMBER_H__*/ telepathy-gabble-0.18.2/src/call-muc-channel.c0000644000175000017500000010427412200204333021050 0ustar00smcvsmcv00000000000000/* * call-muc-channel.c - Source for CallMucChannel * Copyright (C) 2010 Collabora Ltd. * @author Sjoerd Simons * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 "config.h" #include #include #include #include #include "call-content.h" #include "muc-channel.h" #include "call-muc-channel.h" #include "util.h" #include "namespaces.h" #include "jingle-tp-util.h" #define DEBUG_FLAG GABBLE_DEBUG_MEDIA #include "debug.h" static void async_initable_iface_init (GAsyncInitableIface *iface); static void call_muc_channel_accept (TpBaseMediaCallChannel *channel); static TpBaseCallContent * call_muc_channel_add_content ( TpBaseCallChannel *base, const gchar *name, TpMediaStreamType type, TpMediaStreamDirection initial_direction, GError **error); static void call_muc_channel_hangup ( TpBaseCallChannel *base, guint reason, const gchar *detailed_reason, const gchar *message); static void call_muc_channel_close (TpBaseChannel *base); G_DEFINE_TYPE_WITH_CODE (GabbleCallMucChannel, gabble_call_muc_channel, GABBLE_TYPE_BASE_CALL_CHANNEL, G_IMPLEMENT_INTERFACE (G_TYPE_ASYNC_INITABLE, async_initable_iface_init); G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CHANNEL_INTERFACE_GROUP, tp_external_group_mixin_iface_init)); typedef enum { STATE_NOT_JOINED = 0, /* Internally preparing before we can send muji information to the muc, only * happens on the initial join */ STATE_PREPARING, /* Sent the stanza with the preparing node */ STATE_PREPARING_SENT, /* We know when our turn is, now waiting for it */ STATE_WAIT_FOR_TURN, /* Our state matches the state we published */ STATE_STABLE, /* we left this muc */ STATE_LEFT, } MucCallState; enum { PROP_MUC = 1, }; /* private structure */ struct _GabbleCallMucChannelPrivate { gboolean dispose_has_run; GabbleMucChannel *muc; WockyMuc *wmuc; gboolean initialized; MucCallState state; /* The list of members who should sent an update before us */ GQueue *before; GQueue *after; /* List of members we should initial a session to after joining */ GQueue *sessions_to_open; gboolean sessions_opened; GQueue *new_contents; /* Our current muji information */ WockyNodeTree *muji; }; typedef struct { GabbleCallMucChannel *self; GSimpleAsyncResult *result; GCancellable *cancellable; gulong cancel_id; gulong ready_id; } ChannelInitialisation; static void channel_init_free (ChannelInitialisation *ci) { g_cancellable_disconnect (ci->cancellable, ci->cancel_id); tp_clear_object (&ci->cancellable); g_signal_handler_disconnect (ci->self->priv->muc, ci->ready_id); g_object_unref (ci->result); g_slice_free (ChannelInitialisation, ci); } static void gabble_call_muc_channel_init (GabbleCallMucChannel *self) { GabbleCallMucChannelPrivate *priv = G_TYPE_INSTANCE_GET_PRIVATE (self, GABBLE_TYPE_CALL_MUC_CHANNEL, GabbleCallMucChannelPrivate); self->priv = priv; priv->before = g_queue_new (); priv->after = g_queue_new (); priv->sessions_to_open = g_queue_new (); priv->new_contents = g_queue_new (); } static void gabble_call_muc_channel_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { GabbleCallMucChannel *self = GABBLE_CALL_MUC_CHANNEL (object); GabbleCallMucChannelPrivate *priv = self->priv; switch (property_id) { case PROP_MUC: g_value_set_object (value, priv->muc); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void gabble_call_muc_channel_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { GabbleCallMucChannel *self = GABBLE_CALL_MUC_CHANNEL (object); GabbleCallMucChannelPrivate *priv = self->priv; switch (property_id) { case PROP_MUC: priv->muc = g_value_get_object (value); g_assert (priv->muc != NULL); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void gabble_call_muc_channel_dispose (GObject *object); static void gabble_call_muc_channel_finalize (GObject *object); static void gabble_call_muc_channel_constructed (GObject *obj) { GabbleCallMucChannel *self = GABBLE_CALL_MUC_CHANNEL (obj); tp_external_group_mixin_init (obj, G_OBJECT (self->priv->muc)); if (G_OBJECT_CLASS (gabble_call_muc_channel_parent_class)->constructed != NULL) G_OBJECT_CLASS (gabble_call_muc_channel_parent_class)->constructed (obj); } static void gabble_call_muc_channel_class_init ( GabbleCallMucChannelClass *gabble_call_muc_channel_class) { GObjectClass *object_class = G_OBJECT_CLASS (gabble_call_muc_channel_class); TpBaseMediaCallChannelClass *base_media_call_class = TP_BASE_MEDIA_CALL_CHANNEL_CLASS (gabble_call_muc_channel_class); TpBaseCallChannelClass *base_call_class = TP_BASE_CALL_CHANNEL_CLASS (gabble_call_muc_channel_class); TpBaseChannelClass *base_channel_class = TP_BASE_CHANNEL_CLASS (gabble_call_muc_channel_class); GParamSpec *param_spec; g_type_class_add_private (gabble_call_muc_channel_class, sizeof (GabbleCallMucChannelPrivate)); object_class->set_property = gabble_call_muc_channel_set_property; object_class->get_property = gabble_call_muc_channel_get_property; object_class->constructed = gabble_call_muc_channel_constructed; object_class->dispose = gabble_call_muc_channel_dispose; object_class->finalize = gabble_call_muc_channel_finalize; base_channel_class->target_handle_type = TP_HANDLE_TYPE_ROOM; base_channel_class->close = call_muc_channel_close; base_call_class->add_content = call_muc_channel_add_content; base_call_class->hangup = call_muc_channel_hangup; base_media_call_class->accept = call_muc_channel_accept; param_spec = g_param_spec_object ("muc", "GabbleMuc object", "The muc to which this call is related", GABBLE_TYPE_MUC_CHANNEL, G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, PROP_MUC, param_spec); tp_external_group_mixin_init_dbus_properties (object_class); } void gabble_call_muc_channel_dispose (GObject *object) { GabbleCallMucChannel *self = GABBLE_CALL_MUC_CHANNEL (object); GabbleCallMucChannelPrivate *priv = self->priv; if (priv->dispose_has_run) return; priv->dispose_has_run = TRUE; tp_clear_object (&priv->wmuc); tp_clear_object (&priv->muji); tp_external_group_mixin_finalize (object); /* release any references held by the object here */ if (G_OBJECT_CLASS (gabble_call_muc_channel_parent_class)->dispose) G_OBJECT_CLASS (gabble_call_muc_channel_parent_class)->dispose (object); } void gabble_call_muc_channel_finalize (GObject *object) { GabbleCallMucChannel *self = GABBLE_CALL_MUC_CHANNEL (object); GabbleCallMucChannelPrivate *priv = self->priv; /* free any data held directly by the object here */ g_queue_free (priv->before); g_queue_free (priv->after); g_queue_free (priv->sessions_to_open); g_queue_free (priv->new_contents); G_OBJECT_CLASS (gabble_call_muc_channel_parent_class)->finalize (object); } static gboolean call_muc_channel_got_codecs (GabbleCallMucChannel *self) { GList *l; for (l = tp_base_call_channel_get_contents ( TP_BASE_CALL_CHANNEL (self)); l != NULL; l = g_list_next (l)) { TpBaseMediaCallContent *content = TP_BASE_MEDIA_CALL_CONTENT (l->data); GHashTable *tp_md; GPtrArray *codecs; /* FIXME: remote_contact==0 ??? */ tp_md = tp_base_media_call_content_get_local_media_description (content, 0); codecs = tp_asv_get_boxed (tp_md, TP_PROP_CALL_CONTENT_MEDIA_DESCRIPTION_CODECS, TP_ARRAY_TYPE_CODEC_LIST); if (codecs == NULL) return FALSE; } return TRUE; } /* Decide on what to do next for an update */ static void call_muc_do_update (GabbleCallMucChannel *self) { GabbleCallMucChannelPrivate *priv = self->priv; MucCallState old = priv->state; switch (priv->state) { case STATE_NOT_JOINED: case STATE_PREPARING_SENT: case STATE_WAIT_FOR_TURN: /* we either didn't want to join yet or are already in the progress of * doing one, no need to take action */ break; case STATE_PREPARING: g_assert (priv->muji == NULL); if (!call_muc_channel_got_codecs (self)) { DEBUG ("Postponing sending prepare, waiting for codecs"); break; } priv->muji = wocky_node_tree_new ("muji", NS_MUJI, NULL); /* fall through */ case STATE_STABLE: /* Start preparation of the next round */ g_assert (priv->muji != NULL); wocky_node_add_child (wocky_node_tree_get_top_node (priv->muji), "preparing"); priv->state = STATE_PREPARING_SENT; gabble_muc_channel_send_presence (priv->muc); break; case STATE_LEFT: /* we left not doing anything */ break; } DEBUG ("Updated muji state %d -> %d", old, priv->state); } static void call_muc_channel_content_local_media_description_updated ( GabbleCallContent *content, TpHandle contact, GHashTable *properties, gpointer user_data) { GabbleCallMucChannel *self = GABBLE_CALL_MUC_CHANNEL (user_data); DEBUG ("Local codecs of a content updated"); call_muc_do_update (self); } static void call_muc_channel_open_new_streams (GabbleCallMucChannel *self) { GabbleCallMucChannelPrivate *priv = self->priv; GabbleCallMember *m; GabbleCallContent *c; if (!priv->sessions_opened) { /* At the point where we opened the sessions we're accepted in the call */ tp_base_call_channel_set_state (TP_BASE_CALL_CHANNEL (self), TP_CALL_STATE_ACCEPTED, 0, TP_CALL_STATE_CHANGE_REASON_PROGRESS_MADE, "", ""); } priv->sessions_opened = TRUE; while ((m = g_queue_pop_head (priv->sessions_to_open)) != NULL) gabble_call_member_open_session (m, NULL); while ((c = g_queue_pop_head (priv->new_contents)) != NULL) { GList *l; l = gabble_call_content_get_member_contents (c); for (; l != NULL; l = g_list_next (l)) { gabble_call_member_content_add_to_session ( GABBLE_CALL_MEMBER_CONTENT (l->data)); } } } static void call_muc_channel_setup_content (GabbleCallMucChannel *self, GabbleCallContent *content) { GabbleCallMucChannelPrivate *priv = self->priv; DEBUG ("Setting up content"); gabble_signal_connect_weak (content, "local-media-description-updated", G_CALLBACK (call_muc_channel_content_local_media_description_updated), G_OBJECT (self)); if (priv->sessions_opened) g_queue_push_tail (priv->new_contents, content); gabble_call_content_new_offer (content, NULL); } static void call_muc_channel_member_content_added_cb (GabbleCallMember *member, GabbleCallMemberContent *content, gpointer user_data) { GabbleCallMucChannel *self = GABBLE_CALL_MUC_CHANNEL (user_data); const gchar *name; WockyJingleMediaType mtype; GList *l; GabbleCallContent *ccontent; /* A new content was added for one of the members, match it up with the call * channels contents */ name = gabble_call_member_content_get_name (content); mtype = gabble_call_member_content_get_media_type (content); DEBUG ("New call member content: %s (type: %d)", name, mtype); for (l = tp_base_call_channel_get_contents ( TP_BASE_CALL_CHANNEL (self)); l != NULL; l = g_list_next (l)) { const char *cname; WockyJingleMediaType cmtype; ccontent = GABBLE_CALL_CONTENT (l->data); cname = tp_base_call_content_get_name ( TP_BASE_CALL_CONTENT (ccontent)); cmtype = gabble_call_content_get_media_type (ccontent); if (!tp_strdiff (cname, name) && mtype == cmtype) goto have_content; } ccontent = gabble_base_call_channel_add_content ( GABBLE_BASE_CALL_CHANNEL (self), name, mtype, self->priv->initialized ? TP_CALL_CONTENT_DISPOSITION_INITIAL : 0); call_muc_channel_setup_content (self, ccontent); have_content: gabble_call_content_add_member_content (ccontent, content); } static GList * call_muc_channel_parse_codecs (GabbleCallMucChannel *self, WockyNode *description) { GList *codecs = NULL; WockyNodeIter iter; WockyNode *payload; wocky_node_iter_init (&iter, description, "payload-type", NS_JINGLE_RTP); while (wocky_node_iter_next (&iter, &payload)) { const gchar *name; const gchar *value; guint id; guint clockrate = 0; guint channels = 0; WockyJingleCodec *codec; WockyNodeIter param_iter; WockyNode *parameter; value = wocky_node_get_attribute (payload, "id"); if (value == NULL) continue; id = atoi (value); name = wocky_node_get_attribute (payload, "name"); if (name == NULL) continue; value = wocky_node_get_attribute (payload, "clockrate"); if (value != NULL) clockrate = atoi (value); value = wocky_node_get_attribute (payload, "channels"); if (value != NULL) channels = atoi (value); codec = jingle_media_rtp_codec_new (id, name, clockrate, channels, NULL); codecs = g_list_append (codecs, codec); wocky_node_iter_init (¶m_iter, payload, "parameter", NS_JINGLE_RTP); while (wocky_node_iter_next (¶m_iter, ¶meter)) { const gchar *key; key = wocky_node_get_attribute (parameter, "name"); value = wocky_node_get_attribute (parameter, "value"); if (key == NULL || value == NULL) continue; g_hash_table_insert (codec->params, g_strdup (key), g_strdup (value)); } } return codecs; } static void call_muc_channel_send_new_state (GabbleCallMucChannel *self) { GabbleCallMucChannelPrivate *priv = self->priv; /* Our turn! */ GQueue *t; WockyNode *m; GList *l; /* switch the before and after queues */ t = priv->before; priv->before = priv->after; priv->after = t; g_object_unref (priv->muji); priv->muji = wocky_node_tree_new ("muji", NS_MUJI, '*', &m, NULL); for (l = tp_base_call_channel_get_contents ( TP_BASE_CALL_CHANNEL (self)); l != NULL; l = g_list_next (l)) { GabbleCallContent *content = GABBLE_CALL_CONTENT (l->data); const gchar *name = tp_base_call_content_get_name ( TP_BASE_CALL_CONTENT (content)); WockyNode *description; GHashTable *tp_md; GPtrArray *codecs; guint i; WockyJingleMediaType mtype = gabble_call_content_get_media_type (content); wocky_node_add_build (m, '(', "content", '@', "name", name, '(', "description", ':', NS_JINGLE_RTP, '*', &description, '@', "media", mtype == WOCKY_JINGLE_MEDIA_TYPE_AUDIO ? "audio" : "video", ')', ')', NULL); /* FIXME: remote_contact==0 ??? */ tp_md = tp_base_media_call_content_get_local_media_description ( TP_BASE_MEDIA_CALL_CONTENT (content), 0); codecs = tp_asv_get_boxed (tp_md, TP_PROP_CALL_CONTENT_MEDIA_DESCRIPTION_CODECS, TP_ARRAY_TYPE_CODEC_LIST); for (i = 0; i < codecs->len; i++) { GValueArray *codec = g_ptr_array_index (codecs, i); WockyNode *pt; GHashTableIter iter; gpointer key, value; gchar *idstr; guint v; idstr = g_strdup_printf ("%d", g_value_get_uint (codec->values)); wocky_node_add_build (description, '(', "payload-type", '*', &pt, '@', "id", idstr, '@', "name", g_value_get_string (codec->values + 1), ')', NULL); g_free (idstr); /* clock-rate */ v = g_value_get_uint (codec->values + 2); if (v > 0) { gchar *rate = g_strdup_printf ("%d", v); wocky_node_set_attribute (pt, "clockrate", rate); g_free (rate); } /* channels */ v = g_value_get_uint (codec->values + 3); if (v > 0) { gchar *channels = g_strdup_printf ("%d", v); wocky_node_set_attribute (pt, "channels", channels); g_free (channels); } g_hash_table_iter_init (&iter, g_value_get_boxed (codec->values + 5)); while (g_hash_table_iter_next (&iter, &key, &value)) wocky_node_add_build (pt, '(', "parameter", '@', "name", (gchar *) key, '@', "value", (gchar *) value, ')', NULL); } } priv->state = STATE_STABLE; gabble_muc_channel_send_presence (priv->muc); } static void call_muc_channel_parse_participant (GabbleCallMucChannel *self, GabbleCallMember *member, WockyNode *muji) { GabbleCallMucChannelPrivate *priv = self->priv; WockyNodeIter iter; WockyNode *content; wocky_node_iter_init (&iter, muji, "content", NS_MUJI); while (wocky_node_iter_next (&iter, &content)) { GabbleCallMemberContent *member_content; WockyNode *description; WockyJingleMediaType mtype; const gchar *name; const gchar *mattr; GList *codecs; name = wocky_node_get_attribute (content, "name"); if (name == NULL) { DEBUG ("Content is missing the name attribute"); continue; } DEBUG ("Parsing content: %s", name); description = wocky_node_get_child (content, "description"); if (description == NULL) { DEBUG ("Content %s is missing a description", name); continue; } mattr = wocky_node_get_attribute (description, "media"); if (mattr == NULL) { DEBUG ("Content %s is missing a media type", name); continue; } if (!tp_strdiff (mattr, "video")) { mtype = WOCKY_JINGLE_MEDIA_TYPE_VIDEO; } else if (!tp_strdiff (mattr, "audio")) { mtype = WOCKY_JINGLE_MEDIA_TYPE_AUDIO; } else { DEBUG ("Content %s has an unknown media type: %s", name, mattr); continue; } member_content = gabble_call_member_ensure_content (member, name, mtype); if (gabble_call_member_content_has_jingle_content (member_content)) continue; codecs = call_muc_channel_parse_codecs (self, description); gabble_call_member_content_set_remote_codecs (member_content, codecs); if (!priv->initialized) { if (mtype == WOCKY_JINGLE_MEDIA_TYPE_AUDIO) g_object_set (self, "initial-audio", TRUE, NULL); else g_object_set (self, "initial-video", TRUE, NULL); } } } static void call_muc_channel_remove_member (GabbleCallMucChannel *self, GabbleCallMember *call_member) { GabbleCallMucChannelPrivate *priv = self->priv; g_queue_remove (priv->before, call_member); g_queue_remove (priv->after, call_member); g_queue_remove (priv->sessions_to_open, call_member); gabble_base_call_channel_remove_member ( GABBLE_BASE_CALL_CHANNEL (self), call_member); } static void call_muc_channel_got_participant_presence (GabbleCallMucChannel *self, WockyMucMember *member, WockyStanza *stanza) { GabbleCallMucChannelPrivate *priv = self->priv; TpHandleRepoIface *contact_repo = tp_base_connection_get_handles ( tp_base_channel_get_connection (TP_BASE_CHANNEL (self)), TP_HANDLE_TYPE_CONTACT); GabbleCallMember *call_member; TpHandle handle; WockyNode *muji; muji = wocky_node_get_child_ns ( wocky_stanza_get_top_node (stanza), "muji", NS_MUJI); DEBUG ("Muji participant: %s", member->from); handle = tp_handle_ensure (contact_repo, member->from, NULL, NULL); call_member = gabble_base_call_channel_get_member_from_handle ( GABBLE_BASE_CALL_CHANNEL (self), handle); if (muji == NULL) { /* Member without muji information remove it if needed otherwise ignore */ if (call_member != NULL) call_muc_channel_remove_member (self, call_member); return; } if (call_member == NULL) { call_member = gabble_base_call_channel_ensure_member_from_handle ( GABBLE_BASE_CALL_CHANNEL (self), handle); gabble_signal_connect_weak (call_member, "content-added", G_CALLBACK (call_muc_channel_member_content_added_cb), G_OBJECT (self)); gabble_call_member_accept (call_member); } if (!priv->sessions_opened && priv->state < STATE_WAIT_FOR_TURN) g_queue_push_tail (priv->sessions_to_open, call_member); call_muc_channel_parse_participant (self, call_member, muji); if (wocky_node_get_child (muji, "preparing")) { /* remote member is preparing something, add to the right queue */ if (!g_queue_find (priv->before, call_member) && !g_queue_find (priv->after, call_member)) { g_queue_push_tail ( priv->state != STATE_WAIT_FOR_TURN ? priv->before : priv->after, call_member); } } else { /* remote member isn't preparing or at least not anymore */ g_queue_remove (priv->before, call_member); g_queue_remove (priv->after, call_member); if (priv->state == STATE_WAIT_FOR_TURN && g_queue_is_empty (priv->before)) { call_muc_channel_send_new_state (self); } } } static void call_muc_channel_presence_cb (WockyMuc *wmuc, WockyStanza *stanza, guint codes, WockyMucMember *who, gpointer user_data) { GabbleCallMucChannel *self = GABBLE_CALL_MUC_CHANNEL (user_data); call_muc_channel_got_participant_presence (self, who, stanza); } static void call_muc_channel_left_cb (GObject *source, WockyStanza *stanza, guint codes, WockyMucMember *member, const gchar *actor_jid, const gchar *why, const gchar *msg, gpointer user_data) { GabbleCallMucChannel *self = GABBLE_CALL_MUC_CHANNEL (user_data); TpHandleRepoIface *contact_repo = tp_base_connection_get_handles ( tp_base_channel_get_connection (TP_BASE_CHANNEL (self)), TP_HANDLE_TYPE_CONTACT); TpHandle handle; GabbleCallMember *call_member; handle = tp_handle_ensure (contact_repo, member->from, NULL, NULL); call_member = gabble_base_call_channel_get_member_from_handle ( GABBLE_BASE_CALL_CHANNEL (self), handle); DEBUG ("%s left the room, %p", member->from, call_member); if (call_member != NULL) call_muc_channel_remove_member (self, call_member); } static void call_muc_channel_update_all_members (GabbleCallMucChannel *self) { GabbleCallMucChannelPrivate *priv = self->priv; GHashTable *members; GHashTableIter iter; gpointer value; members = wocky_muc_members (priv->wmuc); g_hash_table_iter_init (&iter, members); while (g_hash_table_iter_next (&iter, NULL, &value)) { WockyMucMember *member = (WockyMucMember *) value; call_muc_channel_got_participant_presence (self, member, member->presence_stanza); } g_hash_table_unref (members); } static void call_muc_channel_joined_cb (WockyMuc *muc, WockyStanza *stanza, guint codes, gpointer user_data) { GabbleCallMucChannel *self = GABBLE_CALL_MUC_CHANNEL (user_data); call_muc_channel_update_all_members (self); } static void call_muc_channel_pre_presence_cb (WockyMuc *wmuc, WockyStanza *stanza, gpointer user_data) { GabbleCallMucChannel *self = GABBLE_CALL_MUC_CHANNEL (user_data); if (self->priv->muji == NULL) return; wocky_node_add_node_tree (wocky_stanza_get_top_node (stanza), self->priv->muji); } static void call_muc_channel_own_presence_cb (WockyMuc *wmuc, WockyStanza *stanza, guint codes, gpointer user_data) { GabbleCallMucChannel *self = GABBLE_CALL_MUC_CHANNEL (user_data); GabbleCallMucChannelPrivate *priv = self->priv; WockyNode *muji; DEBUG ("Got our own presence"); muji = wocky_node_get_child_ns ( wocky_stanza_get_top_node (stanza), "muji", NS_MUJI); /* If our presence didn't have a muji stanza or had an older version we don't * care about it */ if (muji == NULL || priv->muji == NULL || !wocky_node_equal (muji, wocky_node_tree_get_top_node (priv->muji))) return; switch (priv->state) { case STATE_PREPARING_SENT: DEBUG ("Got our preperation message, now waiting for our turn"); priv->state = STATE_WAIT_FOR_TURN; if (g_queue_is_empty (priv->before)) call_muc_channel_send_new_state (self); break; case STATE_WAIT_FOR_TURN: break; case STATE_STABLE: call_muc_channel_open_new_streams (self); break; default: DEBUG ("Got a muji presence from ourselves before we sent one ?!"); } } static void call_muc_channel_ready (GabbleCallMucChannel *self) { GabbleCallMucChannelPrivate *priv = self->priv; g_object_get (priv->muc, "wocky-muc", &(priv->wmuc), NULL); g_assert (priv->wmuc != NULL); if (wocky_muc_get_state (priv->wmuc) == WOCKY_MUC_JOINED) call_muc_channel_update_all_members (self); /* we care about presences */ gabble_signal_connect_weak (priv->wmuc, "joined", G_CALLBACK (call_muc_channel_joined_cb), G_OBJECT (self)); gabble_signal_connect_weak (priv->wmuc, "presence", G_CALLBACK (call_muc_channel_presence_cb), G_OBJECT (self)); gabble_signal_connect_weak (priv->wmuc, "own-presence", G_CALLBACK (call_muc_channel_own_presence_cb), G_OBJECT (self)); gabble_signal_connect_weak (priv->wmuc, "left", G_CALLBACK (call_muc_channel_left_cb), G_OBJECT (self)); gabble_signal_connect_weak (priv->muc, "pre-presence", G_CALLBACK (call_muc_channel_pre_presence_cb), G_OBJECT (self)); priv->initialized = TRUE; tp_base_channel_register (TP_BASE_CHANNEL (self)); } static void call_muc_channel_cancelled_cb (GCancellable *cancellable, gpointer user_data) { ChannelInitialisation *ci = user_data; DEBUG ("Cancelled"); g_simple_async_result_set_error (ci->result, G_IO_ERROR, G_IO_ERROR_CANCELLED, "Channel request was cancelled"); g_simple_async_result_complete (ci->result); /* called, don't disconnect */ ci->cancel_id = 0; channel_init_free (ci); } static void call_muc_channel_ready_cb (GabbleMucChannel *muc, gpointer user_data) { ChannelInitialisation *ci = user_data; DEBUG ("Happy muc"); call_muc_channel_ready (ci->self); g_simple_async_result_complete (ci->result); channel_init_free (ci); } static void call_muc_channel_init_async (GAsyncInitable *initable, int priority, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { GabbleCallMucChannel *self = GABBLE_CALL_MUC_CHANNEL (initable); GabbleCallMucChannelPrivate *priv = self->priv; TpBaseCallChannel *base = TP_BASE_CALL_CHANNEL (self); GabbleCallContent *content; GSimpleAsyncResult *result; gboolean initial_audio, initial_video; const gchar *initial_audio_name, *initial_video_name; initial_audio = tp_base_call_channel_has_initial_audio (base, &initial_audio_name); initial_video = tp_base_call_channel_has_initial_video (base, &initial_video_name); result = g_simple_async_result_new (G_OBJECT (initable), callback, user_data, NULL); if (initial_audio) { content = gabble_base_call_channel_add_content ( GABBLE_BASE_CALL_CHANNEL (base), initial_audio_name, WOCKY_JINGLE_MEDIA_TYPE_AUDIO, TP_CALL_CONTENT_DISPOSITION_INITIAL); call_muc_channel_setup_content (self, content); } if (initial_video) { content = gabble_base_call_channel_add_content ( GABBLE_BASE_CALL_CHANNEL (base), initial_video_name, WOCKY_JINGLE_MEDIA_TYPE_VIDEO, TP_CALL_CONTENT_DISPOSITION_INITIAL); call_muc_channel_setup_content (self, content); } if (_gabble_muc_channel_is_ready (priv->muc)) { DEBUG ("Muc channel is ready to fly"); call_muc_channel_ready (self); g_simple_async_result_complete_in_idle (result); g_object_unref (result); } else { ChannelInitialisation *ci = g_slice_new0 (ChannelInitialisation); DEBUG ("Muc channel isn't ready yet"); ci->self = self; ci->result = result; ci->ready_id = g_signal_connect (priv->muc, "ready", G_CALLBACK (call_muc_channel_ready_cb), ci); if (cancellable != NULL) { ci->cancellable = g_object_ref (cancellable); ci->cancel_id = g_cancellable_connect (cancellable, G_CALLBACK (call_muc_channel_cancelled_cb), ci, NULL); } } } static void async_initable_iface_init (GAsyncInitableIface *iface) { iface->init_async = call_muc_channel_init_async; } void gabble_call_muc_channel_new_async (GabbleConnection *connection, GCancellable *cancellable, const gchar *path_prefix, GabbleMucChannel *muc, TpHandle target, GHashTable *request, GAsyncReadyCallback callback, gpointer user_data) { gboolean initial_audio = FALSE; gboolean initial_video = FALSE; const gchar *initial_audio_name = NULL; const gchar *initial_video_name = NULL; DEBUG ("Starting initialisation of a Muji call channel"); if (request != NULL) { initial_audio = tp_asv_get_boolean (request, TP_PROP_CHANNEL_TYPE_CALL_INITIAL_AUDIO, NULL); initial_video = tp_asv_get_boolean (request, TP_PROP_CHANNEL_TYPE_CALL_INITIAL_VIDEO, NULL); initial_audio_name = tp_asv_get_string (request, TP_PROP_CHANNEL_TYPE_CALL_INITIAL_AUDIO_NAME); initial_video_name = tp_asv_get_string (request, TP_PROP_CHANNEL_TYPE_CALL_INITIAL_VIDEO_NAME); } g_async_initable_new_async (GABBLE_TYPE_CALL_MUC_CHANNEL, G_PRIORITY_DEFAULT, cancellable, callback, user_data, "muc", muc, "object-path-prefix", path_prefix, "connection", connection, "handle", target, "requested", request != NULL, "mutable-contents", TRUE, "initial-audio", initial_audio, "initial-audio-name", initial_audio_name != NULL ? initial_audio_name : "audio", "initial-video", initial_video, "initial-video-name", initial_video_name != NULL ? initial_video_name : "video", NULL); } GabbleCallMucChannel * gabble_call_muc_channel_new_finish (GObject *source, GAsyncResult *result, GError **error) { GObject *o; o = g_async_initable_new_finish (G_ASYNC_INITABLE (source), result, error); return o != NULL ? GABBLE_CALL_MUC_CHANNEL (o) : NULL; } void gabble_call_muc_channel_incoming_session (GabbleCallMucChannel *self, WockyJingleSession *session) { GabbleCallMember *member; TpHandleRepoIface *contact_repo = tp_base_connection_get_handles ( tp_base_channel_get_connection (TP_BASE_CHANNEL (self)), TP_HANDLE_TYPE_CONTACT); const gchar *jid = wocky_jingle_session_get_peer_jid (session); TpHandle peer = tp_handle_ensure (contact_repo, jid, NULL, NULL); DEBUG ("New incoming session from %s", jid); member = gabble_base_call_channel_get_member_from_handle ( GABBLE_BASE_CALL_CHANNEL (self), peer); if (member == NULL || gabble_call_member_get_session (member) != NULL) { wocky_jingle_session_terminate (session, WOCKY_JINGLE_REASON_UNKNOWN, "Muji jingle session initiated while there already was one", NULL); } else { gabble_call_member_set_session (member, session); } } static void call_muc_channel_accept (TpBaseMediaCallChannel *channel) { GabbleCallMucChannel *self = GABBLE_CALL_MUC_CHANNEL (channel); if (self->priv->state != STATE_NOT_JOINED) return; DEBUG ("Accepted muji channel"); /* Start preparing to join the conference */ self->priv->state = STATE_PREPARING; call_muc_do_update (self); } static TpBaseCallContent * call_muc_channel_add_content (TpBaseCallChannel *base, const gchar *name, TpMediaStreamType type, TpMediaStreamDirection initial_direction, GError **error) { GabbleCallMucChannel *self = GABBLE_CALL_MUC_CHANNEL (base); GabbleCallContent *content; if (initial_direction == TP_MEDIA_STREAM_DIRECTION_NONE) { g_set_error (error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT, "Jingle can not do contents with direction = NONE"); return NULL; } if (initial_direction != TP_MEDIA_STREAM_DIRECTION_BIDIRECTIONAL) { g_set_error (error, TP_ERROR, TP_ERROR_NOT_IMPLEMENTED, "Adding un-directional contents is not supported" " in MUC channels"); return NULL; } content = gabble_base_call_channel_add_content ( GABBLE_BASE_CALL_CHANNEL (base), name, wocky_jingle_media_type_from_tp (type), TP_CALL_CONTENT_DISPOSITION_NONE); call_muc_channel_setup_content (self, content); return TP_BASE_CALL_CONTENT (content); } static void call_muc_channel_leave (GabbleCallMucChannel *self) { GabbleCallMucChannelPrivate *priv = self->priv; if (priv->state == STATE_LEFT) return; tp_clear_object (&priv->muji); priv->state = STATE_LEFT; gabble_muc_channel_send_presence (priv->muc); } static void call_muc_channel_hangup (TpBaseCallChannel *base, guint reason, const gchar *detailed_reason, const gchar *message) { TpBaseCallChannelClass *parent = TP_BASE_CALL_CHANNEL_CLASS ( gabble_call_muc_channel_parent_class); call_muc_channel_leave (GABBLE_CALL_MUC_CHANNEL (base)); if (parent->hangup != NULL) parent->hangup (base, reason, detailed_reason, message); } static void call_muc_channel_close (TpBaseChannel *base) { call_muc_channel_leave (GABBLE_CALL_MUC_CHANNEL (base)); TP_BASE_CHANNEL_CLASS (gabble_call_muc_channel_parent_class)->close (base); } telepathy-gabble-0.18.2/src/call-muc-channel.h0000644000175000017500000000542312200204333021051 0ustar00smcvsmcv00000000000000/* * call-muc-channel.h - Header for GabbleCallMucChannel * Copyright (C) 2010 Collabora Ltd. * @author Sjoerd Simons * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 __GABBLE_CALL_MUC_CHANNEL_H__ #define __GABBLE_CALL_MUC_CHANNEL_H__ #include #include #include "base-call-channel.h" G_BEGIN_DECLS typedef struct _GabbleCallMucChannel GabbleCallMucChannel; typedef struct _GabbleCallMucChannelPrivate GabbleCallMucChannelPrivate; typedef struct _GabbleCallMucChannelClass GabbleCallMucChannelClass; struct _GabbleCallMucChannelClass { GabbleBaseCallChannelClass parent_class; }; struct _GabbleCallMucChannel { GabbleBaseCallChannel parent; GabbleCallMucChannelPrivate *priv; }; GType gabble_call_muc_channel_get_type (void); /* TYPE MACROS */ #define GABBLE_TYPE_CALL_MUC_CHANNEL \ (gabble_call_muc_channel_get_type ()) #define GABBLE_CALL_MUC_CHANNEL(obj) \ (G_TYPE_CHECK_INSTANCE_CAST((obj), GABBLE_TYPE_CALL_MUC_CHANNEL, \ GabbleCallMucChannel)) #define GABBLE_CALL_MUC_CHANNEL_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST((klass), GABBLE_TYPE_CALL_MUC_CHANNEL, \ GabbleCallMucChannelClass)) #define GABBLE_IS_CALL_MUC_CHANNEL(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE((obj), GABBLE_TYPE_CALL_MUC_CHANNEL)) #define GABBLE_IS_CALL_MUC_CHANNEL_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE((klass), GABBLE_TYPE_CALL_MUC_CHANNEL)) #define GABBLE_CALL_MUC_CHANNEL_GET_CLASS(obj) \ (G_TYPE_INSTANCE_GET_CLASS ((obj), GABBLE_TYPE_CALL_MUC_CHANNEL, \ GabbleCallMucChannelClass)) void gabble_call_muc_channel_new_async (GabbleConnection *connection, GCancellable *cancellable, const gchar *path_prefix, GabbleMucChannel *muc, TpHandle target, GHashTable *request, GAsyncReadyCallback callback, gpointer user_data); GabbleCallMucChannel * gabble_call_muc_channel_new_finish (GObject *source, GAsyncResult *result, GError **error); void gabble_call_muc_channel_incoming_session (GabbleCallMucChannel *self, WockyJingleSession *session); G_END_DECLS #endif /* #ifndef __GABBLE_CALL_MUC_CHANNEL_H__*/ telepathy-gabble-0.18.2/src/call-channel.c0000644000175000017500000005514012200204333020263 0ustar00smcvsmcv00000000000000/* * gabble-call-channel.c - Source for GabbleCallChannel * Copyright (C) 2006, 2009 Collabora Ltd. * Copyright (C) 2006 Nokia Corporation * @author Sjoerd Simons * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 "config.h" #include #include #include #include #include #include #include "util.h" #include "call-channel.h" #include "call-content.h" #include "connection.h" #include "jingle-tp-util.h" #include "presence-cache.h" #define DEBUG_FLAG GABBLE_DEBUG_MEDIA #include "debug.h" static void async_initable_iface_init (GAsyncInitableIface *iface); static void call_session_state_changed_cb (WockyJingleSession *session, GParamSpec *param, GabbleCallChannel *self); static void call_member_content_added_cb (GabbleCallMember *member, GabbleCallMemberContent *content, GabbleCallChannel *self); static void call_member_content_removed_cb (GabbleCallMember *member, GabbleCallMemberContent *mcontent, GabbleBaseCallChannel *self); static void call_session_terminated_cb (WockyJingleSession *session, gboolean locally_terminated, WockyJingleReason termination_reason, gchar *reason_text, GabbleCallChannel *self); static void call_channel_accept (TpBaseMediaCallChannel *channel); static TpBaseCallContent * call_channel_add_content ( TpBaseCallChannel *base, const gchar *name, TpMediaStreamType type, TpMediaStreamDirection initial_direction, GError **error); static void call_channel_hold_state_changed (TpBaseMediaCallChannel *self, TpLocalHoldState hold_state, TpLocalHoldStateReason hold_state_reason); G_DEFINE_TYPE_WITH_CODE(GabbleCallChannel, gabble_call_channel, GABBLE_TYPE_BASE_CALL_CHANNEL, G_IMPLEMENT_INTERFACE (G_TYPE_ASYNC_INITABLE, async_initable_iface_init); ); /* properties */ enum { PROP_SESSION = 1, LAST_PROPERTY }; /* private structure */ struct _GabbleCallChannelPrivate { gboolean dispose_has_run; /* Our only call member, owned by the base channel */ GabbleCallMember *member; WockyJingleSession *session; }; static void gabble_call_channel_constructed (GObject *obj) { GabbleCallChannel *self = GABBLE_CALL_CHANNEL (obj); GabbleCallChannelPrivate *priv = self->priv; GabbleBaseCallChannel *g_base = GABBLE_BASE_CALL_CHANNEL (obj); GabbleCallMember *member; member = gabble_base_call_channel_ensure_member_from_handle (g_base, tp_base_channel_get_target_handle (TP_BASE_CHANNEL (obj))); priv->member = member; if (priv->session != NULL) { GList *contents, *l; gabble_call_member_set_session (member, priv->session); gabble_signal_connect_weak (priv->session, "notify::state", G_CALLBACK (call_session_state_changed_cb), obj); gabble_signal_connect_weak (priv->session, "terminated", G_CALLBACK (call_session_terminated_cb), G_OBJECT (self)); gabble_signal_connect_weak (member, "content-added", G_CALLBACK (call_member_content_added_cb), G_OBJECT (self)); gabble_signal_connect_weak (member, "content-removed", G_CALLBACK (call_member_content_removed_cb), G_OBJECT (self)); contents = gabble_call_member_get_contents (member); for (l = contents; l != NULL; l = g_list_next (l)) { GabbleCallMemberContent *content = GABBLE_CALL_MEMBER_CONTENT (l->data); GabbleCallContent *c; c = gabble_base_call_channel_add_content (g_base, gabble_call_member_content_get_name (content), gabble_call_member_content_get_media_type (content), TP_CALL_CONTENT_DISPOSITION_INITIAL); gabble_call_content_add_member_content (c, content); } } if (G_OBJECT_CLASS (gabble_call_channel_parent_class)->constructed != NULL) G_OBJECT_CLASS (gabble_call_channel_parent_class)->constructed (obj); } static void gabble_call_channel_init (GabbleCallChannel *self) { GabbleCallChannelPrivate *priv = G_TYPE_INSTANCE_GET_PRIVATE (self, GABBLE_TYPE_CALL_CHANNEL, GabbleCallChannelPrivate); self->priv = priv; } static void gabble_call_channel_dispose (GObject *object); static void gabble_call_channel_finalize (GObject *object); static void gabble_call_channel_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { GabbleCallChannel *self = GABBLE_CALL_CHANNEL (object); GabbleCallChannelPrivate *priv = self->priv; switch (property_id) { case PROP_SESSION: g_value_set_object (value, priv->session); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void gabble_call_channel_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { GabbleCallChannel *self = GABBLE_CALL_CHANNEL (object); GabbleCallChannelPrivate *priv = self->priv; switch (property_id) { case PROP_SESSION: g_assert (priv->session == NULL); priv->session = g_value_dup_object (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void gabble_call_channel_class_init ( GabbleCallChannelClass *gabble_call_channel_class) { GObjectClass *object_class = G_OBJECT_CLASS (gabble_call_channel_class); TpBaseMediaCallChannelClass *tp_base_media_call_class = TP_BASE_MEDIA_CALL_CHANNEL_CLASS (gabble_call_channel_class); TpBaseCallChannelClass *tp_base_call_class = TP_BASE_CALL_CHANNEL_CLASS (gabble_call_channel_class); TpBaseChannelClass *base_channel_class = TP_BASE_CHANNEL_CLASS (gabble_call_channel_class); GParamSpec *param_spec; g_type_class_add_private (gabble_call_channel_class, sizeof (GabbleCallChannelPrivate)); object_class->constructed = gabble_call_channel_constructed; object_class->get_property = gabble_call_channel_get_property; object_class->set_property = gabble_call_channel_set_property; object_class->dispose = gabble_call_channel_dispose; object_class->finalize = gabble_call_channel_finalize; base_channel_class->target_handle_type = TP_HANDLE_TYPE_CONTACT; tp_base_call_class->add_content = call_channel_add_content; tp_base_media_call_class->accept = call_channel_accept; tp_base_media_call_class->hold_state_changed = call_channel_hold_state_changed; param_spec = g_param_spec_object ("session", "WockyJingleSession object", "Jingle session associated with this media channel object.", WOCKY_TYPE_JINGLE_SESSION, G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, PROP_SESSION, param_spec); } void gabble_call_channel_dispose (GObject *object) { GabbleCallChannel *self = GABBLE_CALL_CHANNEL (object); GabbleCallChannelPrivate *priv = self->priv; if (priv->dispose_has_run) return; self->priv->dispose_has_run = TRUE; tp_clear_object (&priv->session); if (G_OBJECT_CLASS (gabble_call_channel_parent_class)->dispose) G_OBJECT_CLASS (gabble_call_channel_parent_class)->dispose (object); } void gabble_call_channel_finalize (GObject *object) { G_OBJECT_CLASS (gabble_call_channel_parent_class)->finalize (object); } static void call_session_terminated_cb (WockyJingleSession *session, gboolean locally_terminated, WockyJingleReason termination_reason, gchar *reason_text, GabbleCallChannel *self) { TpHandle actor; TpCallStateChangeReason call_reason = TP_CALL_STATE_CHANGE_REASON_UNKNOWN; const gchar *dbus_detail = ""; if (tp_base_call_channel_get_state (TP_BASE_CALL_CHANNEL (self)) == TP_CALL_STATE_ENDED) { DEBUG ("ignoring jingle session terminate, already in ENDED state"); return; } if (reason_text == NULL) reason_text = ""; if (locally_terminated) actor = tp_base_channel_get_self_handle (TP_BASE_CHANNEL (self)); else actor = tp_base_channel_get_target_handle (TP_BASE_CHANNEL (self)); switch (termination_reason) { case WOCKY_JINGLE_REASON_UNKNOWN: call_reason = TP_CALL_STATE_CHANGE_REASON_UNKNOWN; break; case WOCKY_JINGLE_REASON_BUSY: call_reason = TP_CALL_STATE_CHANGE_REASON_BUSY; break; case WOCKY_JINGLE_REASON_CANCEL: if (locally_terminated) call_reason = TP_CALL_STATE_CHANGE_REASON_USER_REQUESTED; else call_reason = TP_CALL_STATE_CHANGE_REASON_REJECTED; break; case WOCKY_JINGLE_REASON_CONNECTIVITY_ERROR: call_reason = TP_CALL_STATE_CHANGE_REASON_CONNECTIVITY_ERROR; break; case WOCKY_JINGLE_REASON_FAILED_APPLICATION: call_reason = TP_CALL_STATE_CHANGE_REASON_MEDIA_ERROR; dbus_detail = TP_ERROR_STR_MEDIA_CODECS_INCOMPATIBLE; break; case WOCKY_JINGLE_REASON_GENERAL_ERROR: call_reason = TP_CALL_STATE_CHANGE_REASON_SERVICE_ERROR; break; case WOCKY_JINGLE_REASON_GONE: /* This one is only in the media channel, we don't have a * Call reason to match */ call_reason = TP_CALL_STATE_CHANGE_REASON_UNKNOWN; break; case WOCKY_JINGLE_REASON_MEDIA_ERROR: call_reason = TP_CALL_STATE_CHANGE_REASON_MEDIA_ERROR; break; case WOCKY_JINGLE_REASON_SUCCESS: call_reason = TP_CALL_STATE_CHANGE_REASON_USER_REQUESTED; break; case WOCKY_JINGLE_REASON_TIMEOUT: call_reason = TP_CALL_STATE_CHANGE_REASON_NO_ANSWER; break; case WOCKY_JINGLE_REASON_DECLINE: call_reason = TP_CALL_STATE_CHANGE_REASON_REJECTED; break; case WOCKY_JINGLE_REASON_ALTERNATIVE_SESSION: break; case WOCKY_JINGLE_REASON_UNSUPPORTED_TRANSPORTS: call_reason = TP_CALL_STATE_CHANGE_REASON_NETWORK_ERROR; break; case WOCKY_JINGLE_REASON_FAILED_TRANSPORT: call_reason = TP_CALL_STATE_CHANGE_REASON_CONNECTIVITY_ERROR; dbus_detail = TP_ERROR_STR_CONNECTION_FAILED; break; case WOCKY_JINGLE_REASON_INCOMPATIBLE_PARAMETERS: call_reason = TP_CALL_STATE_CHANGE_REASON_MEDIA_ERROR; dbus_detail = TP_ERROR_STR_MEDIA_CODECS_INCOMPATIBLE; break; case WOCKY_JINGLE_REASON_SECURITY_ERROR: call_reason = TP_CALL_STATE_CHANGE_REASON_CONNECTIVITY_ERROR; break; case WOCKY_JINGLE_REASON_UNSUPPORTED_APPLICATIONS: call_reason = TP_CALL_STATE_CHANGE_REASON_MEDIA_ERROR; dbus_detail = TP_ERROR_STR_MEDIA_UNSUPPORTED_TYPE; break; case WOCKY_JINGLE_REASON_EXPIRED: /* No matching error in our spec */ call_reason = TP_CALL_STATE_CHANGE_REASON_UNKNOWN; break; default: call_reason = TP_CALL_STATE_CHANGE_REASON_UNKNOWN; break; } DEBUG ("Moving to ENDED state"); tp_base_call_channel_set_state (TP_BASE_CALL_CHANNEL (self), TP_CALL_STATE_ENDED, actor, call_reason, dbus_detail, reason_text); } static void call_session_state_changed_cb (WockyJingleSession *session, GParamSpec *param, GabbleCallChannel *self) { TpBaseCallChannel *cbase = TP_BASE_CALL_CHANNEL (self); WockyJingleState state; g_object_get (session, "state", &state, NULL); if (state == WOCKY_JINGLE_STATE_ACTIVE && !tp_base_call_channel_is_accepted (cbase)) { tp_base_call_channel_remote_accept (cbase); } } /* This function handles additional contents added by the remote side */ static void call_member_content_added_cb (GabbleCallMember *member, GabbleCallMemberContent *content, GabbleCallChannel *self) { GabbleBaseCallChannel *cbase = GABBLE_BASE_CALL_CHANNEL (self); WockyJingleContent *jingle_content; GabbleCallContent *c; jingle_content = gabble_call_member_content_get_jingle_content (content); if (jingle_content == NULL || wocky_jingle_content_is_created_by_us (jingle_content)) return; c = gabble_base_call_channel_add_content (cbase, gabble_call_member_content_get_name (content), gabble_call_member_content_get_media_type (content), TP_CALL_CONTENT_DISPOSITION_NONE); gabble_call_content_add_member_content (c, content); } static gboolean contact_is_media_capable (GabbleCallChannel *self, TpHandle handle, gboolean *wait_ret, GError **error) { TpBaseChannel *base = TP_BASE_CHANNEL (self); TpBaseConnection *base_conn = tp_base_channel_get_connection (base); GabbleConnection *conn = GABBLE_CONNECTION (base_conn); GabblePresence *presence; TpHandleRepoIface *contact_handles = tp_base_connection_get_handles (base_conn, TP_HANDLE_TYPE_CONTACT); gboolean wait = FALSE; presence = gabble_presence_cache_get (conn->presence_cache, handle); if (presence != NULL) { const GabbleCapabilitySet *caps = gabble_presence_peek_caps (presence); if (gabble_capability_set_has_one (caps, gabble_capabilities_get_any_audio_video ())) return TRUE; } /* Okay, they're not capable (yet). Let's figure out whether we should wait, * and return an appropriate error. */ if (gabble_presence_cache_is_unsure (conn->presence_cache, handle)) { DEBUG ("presence cache is still unsure about handle %u", handle); wait = TRUE; } if (wait_ret != NULL) *wait_ret = wait; if (presence == NULL) g_set_error (error, TP_ERROR, TP_ERROR_OFFLINE, "contact %d (%s) has no presence available", handle, tp_handle_inspect (contact_handles, handle)); else g_set_error (error, TP_ERROR, TP_ERROR_NOT_CAPABLE, "contact %d (%s) doesn't have sufficient media caps", handle, tp_handle_inspect (contact_handles, handle)); return FALSE; } static void call_member_content_removed_cb (GabbleCallMember *member, GabbleCallMemberContent *mcontent, GabbleBaseCallChannel *self) { TpBaseCallChannel *cbase = TP_BASE_CALL_CHANNEL (self); GList *l; for (l = tp_base_call_channel_get_contents (cbase); l != NULL; l = g_list_next (l)) { GabbleCallContent *content = GABBLE_CALL_CONTENT (l->data); GList *contents = gabble_call_content_get_member_contents (content); if (contents != NULL && contents->data == mcontent) { tp_base_call_channel_remove_content (cbase, TP_BASE_CALL_CONTENT (content), 0, TP_CALL_STATE_CHANGE_REASON_PROGRESS_MADE, "", ""); break; } } } static void call_channel_continue_init (GabbleCallChannel *self, GSimpleAsyncResult *result) { GabbleCallChannelPrivate *priv = self->priv; TpBaseCallChannel *base = TP_BASE_CALL_CHANNEL (self); TpBaseChannel *tp_base = TP_BASE_CHANNEL (self); GError *error = NULL; gboolean initial_audio, initial_video; const gchar *initial_audio_name = NULL, *initial_video_name = NULL; if (priv->session == NULL) { GabbleCallMember *member; GList *contents, *l; member = gabble_base_call_channel_ensure_member_from_handle ( GABBLE_BASE_CALL_CHANNEL (self), tp_base_channel_get_target_handle (tp_base)); initial_audio = tp_base_call_channel_has_initial_audio (base, &initial_audio_name); initial_video = tp_base_call_channel_has_initial_video (base, &initial_video_name); if (!gabble_call_member_start_session (member, initial_audio ? initial_audio_name : NULL, initial_video ? initial_video_name : NULL, &error)) { g_simple_async_result_set_from_error (result, error); g_error_free (error); goto out; } priv->session = g_object_ref (gabble_call_member_get_session (member)); gabble_signal_connect_weak (priv->session, "notify::state", G_CALLBACK (call_session_state_changed_cb), G_OBJECT (self)); gabble_signal_connect_weak (priv->session, "terminated", G_CALLBACK (call_session_terminated_cb), G_OBJECT (self)); contents = gabble_call_member_get_contents (member); for (l = contents; l != NULL; l = g_list_next (l)) { GabbleCallMemberContent *content = GABBLE_CALL_MEMBER_CONTENT (l->data); GabbleCallContent *c; c = gabble_base_call_channel_add_content ( GABBLE_BASE_CALL_CHANNEL (self), gabble_call_member_content_get_name (content), gabble_call_member_content_get_media_type (content), TP_CALL_CONTENT_DISPOSITION_INITIAL); gabble_call_content_add_member_content (c, content); } gabble_signal_connect_weak (member, "content-added", G_CALLBACK (call_member_content_added_cb), G_OBJECT (self)); gabble_signal_connect_weak (member, "content-removed", G_CALLBACK (call_member_content_removed_cb), G_OBJECT (self)); } tp_base_channel_register (tp_base); out: g_simple_async_result_complete_in_idle (result); g_object_unref (result); } static void call_channel_capabilities_discovered_cb (GabblePresenceCache *cache, TpHandle handle, gpointer user_data) { GSimpleAsyncResult *result = G_SIMPLE_ASYNC_RESULT (user_data); GabbleCallChannel *self = GABBLE_CALL_CHANNEL ( g_async_result_get_source_object (G_ASYNC_RESULT (result))); TpBaseChannel *tp_base = TP_BASE_CHANNEL (self); TpHandle target = tp_base_channel_get_target_handle (tp_base); GError *error_ = NULL; gboolean wait; if (target != handle || tp_base_channel_is_registered (tp_base)) goto out; if (!contact_is_media_capable (self, target, &wait, &error_)) { if (wait) { DEBUG ("contact %u caps still pending", target); g_error_free (error_); } else { DEBUG ("%u: %s", error_->code, error_->message); g_simple_async_result_set_from_error (result, error_); g_error_free (error_); g_simple_async_result_complete_in_idle (result); g_object_unref (result); } } else { call_channel_continue_init (self, result); } out: g_object_unref (self); } static void call_channel_init_async (GAsyncInitable *initable, int priority, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { GabbleCallChannel *self = GABBLE_CALL_CHANNEL (initable); GabbleCallChannelPrivate *priv = self->priv; GabbleBaseCallChannel *base = GABBLE_BASE_CALL_CHANNEL (self); TpBaseChannel *tp_base = TP_BASE_CHANNEL (base); TpHandle target = tp_base_channel_get_target_handle (tp_base); GabbleConnection *conn = GABBLE_CONNECTION ( tp_base_channel_get_connection (tp_base)); GSimpleAsyncResult *result; GError *error_ = NULL; gboolean wait; result = g_simple_async_result_new (G_OBJECT (self), callback, user_data, NULL); if (tp_base_channel_is_registered (tp_base)) { g_simple_async_result_complete_in_idle (result); g_object_unref (result); return; } if (priv->session == NULL && !contact_is_media_capable (self, target, &wait, &error_)) { if (wait) { DEBUG ("contact %u caps still pending, adding anyways", target); g_error_free (error_); /* Fuckers */ gabble_signal_connect_weak (conn->presence_cache, "capabilities-discovered", G_CALLBACK (call_channel_capabilities_discovered_cb), G_OBJECT (result)); } else { DEBUG ("%u: %s", error_->code, error_->message); g_simple_async_result_set_from_error (result, error_); g_error_free (error_); g_simple_async_result_complete_in_idle (result); g_object_unref (result); } } else { call_channel_continue_init (self, result); } } static void async_initable_iface_init (GAsyncInitableIface *iface) { iface->init_async = call_channel_init_async; } static void call_channel_accept (TpBaseMediaCallChannel *channel) { GabbleCallChannel *self = GABBLE_CALL_CHANNEL (channel); wocky_jingle_session_accept (self->priv->session); } static TpBaseCallContent * call_channel_add_content (TpBaseCallChannel *base, const gchar *name, TpMediaStreamType type, TpMediaStreamDirection initial_direction, GError **error) { GabbleCallChannel *self = GABBLE_CALL_CHANNEL (base); GabbleCallContent *content = NULL; GabbleCallMemberContent *mcontent; WockyJingleContentSenders senders; gboolean initiated_by_us; if (initial_direction == TP_MEDIA_STREAM_DIRECTION_NONE) { g_set_error (error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT, "Jingle can not do contents with direction = NONE"); return NULL; } g_object_get (self->priv->session, "local-initiator", &initiated_by_us, NULL); switch (initial_direction) { case TP_MEDIA_STREAM_DIRECTION_SEND: senders = initiated_by_us ? WOCKY_JINGLE_CONTENT_SENDERS_INITIATOR : WOCKY_JINGLE_CONTENT_SENDERS_RESPONDER; break; case TP_MEDIA_STREAM_DIRECTION_RECEIVE: senders = initiated_by_us ? WOCKY_JINGLE_CONTENT_SENDERS_RESPONDER : WOCKY_JINGLE_CONTENT_SENDERS_INITIATOR; break; default: case TP_MEDIA_STREAM_DIRECTION_BIDIRECTIONAL: senders = WOCKY_JINGLE_CONTENT_SENDERS_BOTH; } mcontent = gabble_call_member_create_content (self->priv->member, name, wocky_jingle_media_type_from_tp (type), senders, error); if (mcontent != NULL) { content = gabble_base_call_channel_add_content ( GABBLE_BASE_CALL_CHANNEL (base), name, wocky_jingle_media_type_from_tp (type), TP_CALL_CONTENT_DISPOSITION_NONE); gabble_call_content_add_member_content (content, mcontent); } return TP_BASE_CALL_CONTENT (content); } static void call_channel_hold_state_changed (TpBaseMediaCallChannel *bmcc, TpLocalHoldState hold_state, TpLocalHoldStateReason hold_state_reason) { GabbleCallChannel *self = GABBLE_CALL_CHANNEL (bmcc); switch (hold_state) { case TP_LOCAL_HOLD_STATE_HELD: case TP_LOCAL_HOLD_STATE_PENDING_HOLD: wocky_jingle_session_set_local_hold (self->priv->session, TRUE); break; case TP_LOCAL_HOLD_STATE_UNHELD: wocky_jingle_session_set_local_hold (self->priv->session, FALSE); break; case TP_LOCAL_HOLD_STATE_PENDING_UNHOLD: break; default: g_assert_not_reached (); } } telepathy-gabble-0.18.2/src/call-channel.h0000644000175000017500000000424112200204333020264 0ustar00smcvsmcv00000000000000/* * gabble-call-channel.h - Header for GabbleCallChannel * Copyright (C) 2009 Collabora Ltd. * @author Sjoerd Simons * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 __GABBLE_CALL_CHANNEL_H__ #define __GABBLE_CALL_CHANNEL_H__ #include #include "base-call-channel.h" G_BEGIN_DECLS typedef struct _GabbleCallChannel GabbleCallChannel; typedef struct _GabbleCallChannelPrivate GabbleCallChannelPrivate; typedef struct _GabbleCallChannelClass GabbleCallChannelClass; struct _GabbleCallChannelClass { GabbleBaseCallChannelClass parent_class; }; struct _GabbleCallChannel { GabbleBaseCallChannel parent; GabbleCallChannelPrivate *priv; }; GType gabble_call_channel_get_type (void); /* TYPE MACROS */ #define GABBLE_TYPE_CALL_CHANNEL \ (gabble_call_channel_get_type ()) #define GABBLE_CALL_CHANNEL(obj) \ (G_TYPE_CHECK_INSTANCE_CAST ((obj), \ GABBLE_TYPE_CALL_CHANNEL, GabbleCallChannel)) #define GABBLE_CALL_CHANNEL_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST((klass), \ GABBLE_TYPE_CALL_CHANNEL, GabbleCallChannelClass)) #define GABBLE_IS_CALL_CHANNEL(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE((obj), GABBLE_TYPE_CALL_CHANNEL)) #define GABBLE_IS_CALL_CHANNEL_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE((klass), GABBLE_TYPE_CALL_CHANNEL)) #define GABBLE_CALL_CHANNEL_GET_CLASS(obj) \ (G_TYPE_INSTANCE_GET_CLASS ((obj), \ GABBLE_TYPE_CALL_CHANNEL, GabbleCallChannelClass)) G_END_DECLS #endif /* #ifndef __GABBLE_CALL_CHANNEL_H__*/ telepathy-gabble-0.18.2/src/call-content.c0000644000175000017500000002553012200204333020325 0ustar00smcvsmcv00000000000000/* * gabble-call-content.c - Source for GabbleCallContent * Copyright (C) 2009-2011 Collabora Ltd. * @author Sjoerd Simons * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 "config.h" #include #include #include #include #include "call-member.h" #include "call-content.h" #include "call-stream.h" #include "jingle-tp-util.h" #include "connection.h" #include "util.h" #define DEBUG_FLAG GABBLE_DEBUG_MEDIA #include "debug.h" static void call_content_deinit (TpBaseCallContent *base); static void call_content_local_media_description_updated ( GabbleCallContent *self, TpHandle contact, GHashTable *properties, gpointer data); G_DEFINE_TYPE (GabbleCallContent, gabble_call_content, TP_TYPE_BASE_MEDIA_CALL_CONTENT); /* private structure */ struct _GabbleCallContentPrivate { /* CallMemberContent list */ GList *contents; guint offers; gboolean dispose_has_run; gboolean deinit_has_run; }; static void gabble_call_content_init (GabbleCallContent *self) { GabbleCallContentPrivate *priv = G_TYPE_INSTANCE_GET_PRIVATE (self, GABBLE_TYPE_CALL_CONTENT, GabbleCallContentPrivate); self->priv = priv; g_signal_connect (self, "local-media-description-updated", G_CALLBACK (call_content_local_media_description_updated), NULL); } static void gabble_call_content_dispose (GObject *object); static void gabble_call_content_class_init ( GabbleCallContentClass *gabble_call_content_class) { GObjectClass *object_class = G_OBJECT_CLASS (gabble_call_content_class); TpBaseCallContentClass *bcc_class = TP_BASE_CALL_CONTENT_CLASS (gabble_call_content_class); g_type_class_add_private (gabble_call_content_class, sizeof (GabbleCallContentPrivate)); object_class->dispose = gabble_call_content_dispose; bcc_class->deinit = call_content_deinit; } static void gabble_call_content_dispose (GObject *object) { GabbleCallContent *self = GABBLE_CALL_CONTENT (object); GabbleCallContentPrivate *priv = self->priv; if (priv->dispose_has_run) return; priv->dispose_has_run = TRUE; g_assert (priv->contents == NULL); if (G_OBJECT_CLASS (gabble_call_content_parent_class)->dispose) G_OBJECT_CLASS (gabble_call_content_parent_class)->dispose (object); } static void call_content_deinit (TpBaseCallContent *base) { GabbleCallContent *self = GABBLE_CALL_CONTENT (base); GabbleCallContentPrivate *priv = self->priv; if (priv->deinit_has_run) return; priv->deinit_has_run = TRUE; while (priv->contents != NULL) { GabbleCallMemberContent *c = priv->contents->data; priv->contents = g_list_delete_link (priv->contents, priv->contents); gabble_call_member_content_remove (c); } TP_BASE_CALL_CONTENT_CLASS ( gabble_call_content_parent_class)->deinit (base); } void gabble_call_content_new_offer (GabbleCallContent *self, GabbleCallMemberContent *content) { GabbleCallContentPrivate *priv = self->priv; TpBaseCallContent *base = TP_BASE_CALL_CONTENT (self); TpBaseConnection *conn = tp_base_call_content_get_connection (base); TpCallContentMediaDescription *md; gchar *path; TpHandle handle = 0; path = g_strdup_printf ("%s/Offer%d", tp_base_call_content_get_object_path (base), priv->offers++); if (content != NULL) { handle = gabble_call_member_get_handle ( gabble_call_member_content_get_member (content)); } /* FIXME: no idea... */ md = tp_call_content_media_description_new ( tp_base_connection_get_dbus_daemon (conn), path, handle, (content != NULL), (content == NULL)); if (content != NULL) { GList *codecs, *l; codecs = gabble_call_member_content_get_remote_codecs (content); for (l = codecs; l != NULL; l = g_list_next (l)) { WockyJingleCodec *c = l->data; tp_call_content_media_description_append_codec (md, c->id, c->name, c->clockrate, c->channels, FALSE, /* FIXME: updated?? */ c->params); } } /* FIXME: We have to handle cases where the new codecs are rejected */ tp_base_media_call_content_offer_media_description_async ( TP_BASE_MEDIA_CALL_CONTENT (self), md, NULL, NULL); g_object_unref (md); g_free (path); } WockyJingleMediaType gabble_call_content_get_media_type (GabbleCallContent *self) { TpBaseCallContent *base = TP_BASE_CALL_CONTENT (self); return wocky_jingle_media_type_from_tp ( tp_base_call_content_get_media_type (base)); } static void member_content_codecs_changed (GabbleCallMemberContent *mcontent, gpointer user_data) { GabbleCallContent *self = GABBLE_CALL_CONTENT (user_data); DEBUG ("Preparing new codec offer"); gabble_call_content_new_offer (self, mcontent); } static GList * codec_array_to_list (GPtrArray *codecs) { guint i; GList *l = NULL; if (codecs == NULL) return NULL; for (i = 0; i < codecs->len ; i++) { WockyJingleCodec *c; GValueArray *va; va = g_ptr_array_index (codecs, i); c = jingle_media_rtp_codec_new ( g_value_get_uint (va->values + 0), g_value_get_string (va->values + 1), g_value_get_uint (va->values + 2), g_value_get_uint (va->values + 3), /* g_value_get_boolean (va->values + 4), updated? */ g_value_get_boxed (va->values + 5)); l = g_list_append (l, c); } return l; } static void call_content_local_media_description_updated (GabbleCallContent *self, TpHandle contact, GHashTable *properties, gpointer data) { GList *l; WockyJingleMediaDescription *md = wocky_jingle_media_description_new (); md->codecs = codec_array_to_list (tp_asv_get_boxed (properties, TP_PROP_CALL_CONTENT_MEDIA_DESCRIPTION_CODECS, TP_ARRAY_TYPE_CODEC_LIST)); for (l = self->priv->contents; l != NULL; l = g_list_next (l)) { GabbleCallMemberContent *c = GABBLE_CALL_MEMBER_CONTENT (l->data); WockyJingleContent *j = gabble_call_member_content_get_jingle_content (c); if (j == NULL) continue; /* FIXME react properly on errors ? */ jingle_media_rtp_set_local_media_description (WOCKY_JINGLE_MEDIA_RTP (j), wocky_jingle_media_description_copy (md), TRUE, NULL); } wocky_jingle_media_description_free (md); } static TpStreamTransportType _jingle_to_tp_transport (WockyJingleTransportType jt) { switch (jt) { case JINGLE_TRANSPORT_GOOGLE_P2P: return TP_STREAM_TRANSPORT_TYPE_GTALK_P2P; case JINGLE_TRANSPORT_RAW_UDP: return TP_STREAM_TRANSPORT_TYPE_RAW_UDP; case JINGLE_TRANSPORT_ICE_UDP: return TP_STREAM_TRANSPORT_TYPE_ICE; default: g_return_val_if_reached (G_MAXUINT); } } static void call_content_setup_jingle (GabbleCallContent *self, GabbleCallMemberContent *mcontent) { TpBaseCallContent *base = TP_BASE_CALL_CONTENT (self); WockyJingleContent *jingle; GabbleCallStream *stream; gchar *path; WockyJingleTransportType transport; WockyJingleMediaDescription *md; GHashTable *tp_md; TpHandle contact; jingle = gabble_call_member_content_get_jingle_content (mcontent); if (jingle == NULL) return; transport = wocky_jingle_content_get_transport_type (jingle); path = g_strdup_printf ("%s/Stream%p", tp_base_call_content_get_object_path (base), jingle); stream = g_object_new (GABBLE_TYPE_CALL_STREAM, "object-path", path, "connection", tp_base_call_content_get_connection (base), "jingle-content", jingle, "transport", _jingle_to_tp_transport (transport), NULL); g_free (path); md = wocky_jingle_media_description_new (); /* FIXME: correct??? */ contact = gabble_call_member_get_handle ( gabble_call_member_content_get_member (mcontent)); tp_md = tp_base_media_call_content_get_local_media_description ( TP_BASE_MEDIA_CALL_CONTENT (self), contact); if (tp_md != NULL) { md->codecs = codec_array_to_list (tp_asv_get_boxed (tp_md, TP_PROP_CALL_CONTENT_MEDIA_DESCRIPTION_CODECS, TP_ARRAY_TYPE_CODEC_LIST)); } if (md->codecs != NULL) jingle_media_rtp_set_local_media_description ( WOCKY_JINGLE_MEDIA_RTP (jingle), md, TRUE, NULL); else wocky_jingle_media_description_free (md); tp_base_call_content_add_stream (base, TP_BASE_CALL_STREAM (stream)); gabble_call_stream_update_member_states (stream); g_object_unref (stream); } static void member_content_got_jingle_content_cb (GabbleCallMemberContent *mcontent, gpointer user_data) { GabbleCallContent *self = GABBLE_CALL_CONTENT (user_data); call_content_setup_jingle (self, mcontent); } static void member_content_removed_cb (GabbleCallMemberContent *mcontent, gpointer user_data) { GabbleCallContent *self = GABBLE_CALL_CONTENT (user_data); GabbleCallContentPrivate *priv = self->priv; TpBaseCallContent *base = TP_BASE_CALL_CONTENT (self); WockyJingleContent *content; GList *l; priv->contents = g_list_remove (priv->contents, mcontent); content = gabble_call_member_content_get_jingle_content (mcontent); for (l = tp_base_call_content_get_streams (base); l != NULL; l = l->next) { GabbleCallStream *stream = GABBLE_CALL_STREAM (l->data); if (content == gabble_call_stream_get_jingle_content (stream)) { tp_base_call_content_remove_stream (base, l->data, 0, TP_CALL_STATE_CHANGE_REASON_PROGRESS_MADE, "", ""); break; } } } void gabble_call_content_add_member_content (GabbleCallContent *self, GabbleCallMemberContent *content) { self->priv->contents = g_list_prepend (self->priv->contents, content); call_content_setup_jingle (self, content); gabble_signal_connect_weak (content, "codecs-changed", G_CALLBACK (member_content_codecs_changed), G_OBJECT (self)); gabble_signal_connect_weak (content, "got-jingle-content", G_CALLBACK (member_content_got_jingle_content_cb), G_OBJECT (self)); gabble_signal_connect_weak (content, "removed", G_CALLBACK (member_content_removed_cb), G_OBJECT (self)); gabble_call_content_new_offer (self, content); } GList * gabble_call_content_get_member_contents (GabbleCallContent *self) { return self->priv->contents; } telepathy-gabble-0.18.2/src/call-content.h0000644000175000017500000000514012200204333020325 0ustar00smcvsmcv00000000000000/* * gabble-call-content.h - Header for GabbleCallContent * Copyright (C) 2009 Collabora Ltd. * @author Sjoerd Simons * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 __GABBLE_CALL_CONTENT_H__ #define __GABBLE_CALL_CONTENT_H__ #include #include #include #include "call-member-content.h" G_BEGIN_DECLS typedef struct _GabbleCallContent GabbleCallContent; typedef struct _GabbleCallContentPrivate GabbleCallContentPrivate; typedef struct _GabbleCallContentClass GabbleCallContentClass; struct _GabbleCallContentClass { TpBaseMediaCallContentClass parent_class; }; struct _GabbleCallContent { TpBaseMediaCallContent parent; GabbleCallContentPrivate *priv; }; GType gabble_call_content_get_type (void); /* TYPE MACROS */ #define GABBLE_TYPE_CALL_CONTENT \ (gabble_call_content_get_type ()) #define GABBLE_CALL_CONTENT(obj) \ (G_TYPE_CHECK_INSTANCE_CAST((obj), \ GABBLE_TYPE_CALL_CONTENT, GabbleCallContent)) #define GABBLE_CALL_CONTENT_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST((klass), \ GABBLE_TYPE_CALL_CONTENT, GabbleCallContentClass)) #define GABBLE_IS_CALL_CONTENT(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE((obj), GABBLE_TYPE_CALL_CONTENT)) #define GABBLE_IS_CALL_CONTENT_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE((klass), GABBLE_TYPE_CALL_CONTENT)) #define GABBLE_CALL_CONTENT_GET_CLASS(obj) \ (G_TYPE_INSTANCE_GET_CLASS ((obj), \ GABBLE_TYPE_CALL_CONTENT, GabbleCallContentClass)) WockyJingleMediaType gabble_call_content_get_media_type (GabbleCallContent *self); void gabble_call_content_new_offer (GabbleCallContent *self, GabbleCallMemberContent *content); void gabble_call_content_add_member_content (GabbleCallContent *self, GabbleCallMemberContent *content); GList *gabble_call_content_get_member_contents (GabbleCallContent *self); G_END_DECLS #endif /* #ifndef __GABBLE_CALL_CONTENT_H__*/ telepathy-gabble-0.18.2/src/base-call-channel.c0000644000175000017500000002261012200204333021167 0ustar00smcvsmcv00000000000000/* * base-call-channel.c - Source for GabbleBaseCallChannel * Copyright © 2009–2010 Collabora Ltd. * @author Sjoerd Simons * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 "config.h" #include #include #include #include #include #include "util.h" #include "call-content.h" #include "base-call-channel.h" #include "connection.h" #include "jingle-tp-util.h" #define DEBUG_FLAG GABBLE_DEBUG_MEDIA #include "debug.h" G_DEFINE_TYPE(GabbleBaseCallChannel, gabble_base_call_channel, TP_TYPE_BASE_MEDIA_CALL_CHANNEL); static void gabble_base_call_channel_hangup ( TpBaseCallChannel *base, guint reason, const gchar *detailed_reason, const gchar *message); static void gabble_base_call_channel_close (TpBaseChannel *base); /* properties */ enum { PROP_OBJECT_PATH_PREFIX = 1, LAST_PROPERTY }; /* private structure */ struct _GabbleBaseCallChannelPrivate { gchar *object_path_prefix; gboolean dispose_has_run; /* handle -> CallMember object hash */ GHashTable *members; }; static void gabble_base_call_channel_init (GabbleBaseCallChannel *self) { GabbleBaseCallChannelPrivate *priv = G_TYPE_INSTANCE_GET_PRIVATE (self, GABBLE_TYPE_BASE_CALL_CHANNEL, GabbleBaseCallChannelPrivate); self->priv = priv; priv->members = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, g_object_unref); } static void gabble_base_call_channel_dispose (GObject *object); static void gabble_base_call_channel_finalize (GObject *object); static void gabble_base_call_channel_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { GabbleBaseCallChannel *self = GABBLE_BASE_CALL_CHANNEL (object); GabbleBaseCallChannelPrivate *priv = self->priv; switch (property_id) { case PROP_OBJECT_PATH_PREFIX: g_value_set_string (value, priv->object_path_prefix); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void gabble_base_call_channel_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { GabbleBaseCallChannel *self = GABBLE_BASE_CALL_CHANNEL (object); GabbleBaseCallChannelPrivate *priv = self->priv; switch (property_id) { case PROP_OBJECT_PATH_PREFIX: priv->object_path_prefix = g_value_dup_string (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static gchar * gabble_base_call_channel_get_object_path_suffix (TpBaseChannel *base) { GabbleBaseCallChannel *self = GABBLE_BASE_CALL_CHANNEL (base); GabbleBaseCallChannelPrivate *priv = self->priv; g_assert (priv->object_path_prefix != NULL); return g_strdup_printf ("%s/CallChannel%p", priv->object_path_prefix, self); } static void gabble_base_call_channel_class_init ( GabbleBaseCallChannelClass *gabble_base_call_channel_class) { GObjectClass *object_class = G_OBJECT_CLASS (gabble_base_call_channel_class); TpBaseChannelClass *base_channel_class = TP_BASE_CHANNEL_CLASS (gabble_base_call_channel_class); TpBaseCallChannelClass *tp_base_call_channel_class = TP_BASE_CALL_CHANNEL_CLASS (gabble_base_call_channel_class); GParamSpec *param_spec; g_type_class_add_private (gabble_base_call_channel_class, sizeof (GabbleBaseCallChannelPrivate)); object_class->get_property = gabble_base_call_channel_get_property; object_class->set_property = gabble_base_call_channel_set_property; object_class->dispose = gabble_base_call_channel_dispose; object_class->finalize = gabble_base_call_channel_finalize; base_channel_class->get_object_path_suffix = gabble_base_call_channel_get_object_path_suffix; base_channel_class->close = gabble_base_call_channel_close; tp_base_call_channel_class->hangup = gabble_base_call_channel_hangup; param_spec = g_param_spec_string ("object-path-prefix", "Object path prefix", "prefix of the object path", NULL, G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, PROP_OBJECT_PATH_PREFIX, param_spec); } void gabble_base_call_channel_dispose (GObject *object) { GabbleBaseCallChannel *self = GABBLE_BASE_CALL_CHANNEL (object); GabbleBaseCallChannelPrivate *priv = self->priv; DEBUG ("hello thar"); if (priv->dispose_has_run) return; self->priv->dispose_has_run = TRUE; tp_clear_pointer (&priv->members, g_hash_table_unref); if (G_OBJECT_CLASS (gabble_base_call_channel_parent_class)->dispose) G_OBJECT_CLASS (gabble_base_call_channel_parent_class)->dispose (object); } void gabble_base_call_channel_finalize (GObject *object) { GabbleBaseCallChannel *self = GABBLE_BASE_CALL_CHANNEL (object); GabbleBaseCallChannelPrivate *priv = self->priv; g_free (priv->object_path_prefix); G_OBJECT_CLASS (gabble_base_call_channel_parent_class)->finalize (object); } GabbleCallContent * gabble_base_call_channel_add_content (GabbleBaseCallChannel *self, const gchar *name, WockyJingleMediaType mtype, TpCallContentDisposition disposition) { TpBaseChannel *base = TP_BASE_CHANNEL (self); gchar *object_path; TpBaseCallContent *content; gchar *escaped; /* FIXME could clash when other party in a one-to-one call creates a stream * with the same media type and name */ escaped = tp_escape_as_identifier (name); object_path = g_strdup_printf ("%s/Content_%s_%d", tp_base_channel_get_object_path (base), escaped, mtype); g_free (escaped); content = g_object_new (GABBLE_TYPE_CALL_CONTENT, "connection", tp_base_channel_get_connection (base), "object-path", object_path, "disposition", disposition, "media-type", wocky_jingle_media_type_to_tp (mtype), "name", name, NULL); g_free (object_path); tp_base_call_channel_add_content (TP_BASE_CALL_CHANNEL (self), content); return GABBLE_CALL_CONTENT (content); } static void call_member_flags_changed_cb (GabbleCallMember *member, TpCallMemberFlags flags, gpointer user_data) { TpBaseCallChannel *base = TP_BASE_CALL_CHANNEL (user_data); tp_base_call_channel_update_member_flags (base, gabble_call_member_get_handle (member), flags, 0, TP_CALL_STATE_CHANGE_REASON_PROGRESS_MADE, "", ""); } GabbleCallMember * gabble_base_call_channel_get_member_from_handle ( GabbleBaseCallChannel *self, TpHandle handle) { return g_hash_table_lookup (self->priv->members, GUINT_TO_POINTER (handle)); } GabbleCallMember * gabble_base_call_channel_ensure_member_from_handle ( GabbleBaseCallChannel *self, TpHandle handle) { GabbleBaseCallChannelPrivate *priv = self->priv; GabbleCallMember *m; m = g_hash_table_lookup (priv->members, GUINT_TO_POINTER (handle)); if (m == NULL) { m = GABBLE_CALL_MEMBER (g_object_new (GABBLE_TYPE_CALL_MEMBER, "target", handle, "call", self, NULL)); g_hash_table_insert (priv->members, GUINT_TO_POINTER (handle), m); tp_base_call_channel_update_member_flags (TP_BASE_CALL_CHANNEL (self), gabble_call_member_get_handle (m), gabble_call_member_get_flags (m), 0, TP_CALL_STATE_CHANGE_REASON_PROGRESS_MADE, "", ""); gabble_signal_connect_weak (m, "flags-changed", G_CALLBACK (call_member_flags_changed_cb), G_OBJECT (self)); } return m; } void gabble_base_call_channel_remove_member (GabbleBaseCallChannel *self, GabbleCallMember *member) { TpHandle h = gabble_call_member_get_handle (member); g_assert (g_hash_table_lookup (self->priv->members, GUINT_TO_POINTER (h))== member); gabble_call_member_shutdown (member); tp_base_call_channel_remove_member (TP_BASE_CALL_CHANNEL (self), gabble_call_member_get_handle (member), 0, TP_CALL_STATE_CHANGE_REASON_PROGRESS_MADE, "", ""); g_hash_table_remove (self->priv->members, GUINT_TO_POINTER (h)); } static void gabble_base_call_channel_shutdown_all_members (GabbleBaseCallChannel *self) { GHashTableIter iter; gpointer value; g_hash_table_iter_init (&iter, self->priv->members); while (g_hash_table_iter_next (&iter, NULL, &value)) gabble_call_member_shutdown (GABBLE_CALL_MEMBER (value)); } static void gabble_base_call_channel_hangup (TpBaseCallChannel *base, guint reason, const gchar *detailed_reason, const gchar *message) { gabble_base_call_channel_shutdown_all_members ( GABBLE_BASE_CALL_CHANNEL (base)); } static void gabble_base_call_channel_close (TpBaseChannel *base) { gabble_base_call_channel_shutdown_all_members ( GABBLE_BASE_CALL_CHANNEL (base)); TP_BASE_CHANNEL_CLASS (gabble_base_call_channel_parent_class)->close (base); } telepathy-gabble-0.18.2/src/base-call-channel.h0000644000175000017500000000641412200204333021200 0ustar00smcvsmcv00000000000000/* * base-call-channel.h - Header for GabbleBaseCallChannel * Copyright © 2009–2010 Collabora Ltd. * @author Sjoerd Simons * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 __GABBLE_BASE_CALL_CHANNEL_H__ #define __GABBLE_BASE_CALL_CHANNEL_H__ #include #include #include #include #include "call-member.h" #include "call-content.h" G_BEGIN_DECLS typedef struct _GabbleBaseCallChannel GabbleBaseCallChannel; typedef struct _GabbleBaseCallChannelPrivate GabbleBaseCallChannelPrivate; typedef struct _GabbleBaseCallChannelClass GabbleBaseCallChannelClass; struct _GabbleBaseCallChannelClass { TpBaseMediaCallChannelClass parent_class; }; struct _GabbleBaseCallChannel { TpBaseMediaCallChannel parent; GabbleBaseCallChannelPrivate *priv; }; GType gabble_base_call_channel_get_type (void); /* TYPE MACROS */ #define GABBLE_TYPE_BASE_CALL_CHANNEL \ (gabble_base_call_channel_get_type ()) #define GABBLE_BASE_CALL_CHANNEL(obj) \ (G_TYPE_CHECK_INSTANCE_CAST ((obj), \ GABBLE_TYPE_BASE_CALL_CHANNEL, GabbleBaseCallChannel)) #define GABBLE_BASE_CALL_CHANNEL_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST((klass), \ GABBLE_TYPE_BASE_CALL_CHANNEL, GabbleBaseCallChannelClass)) #define GABBLE_IS_BASE_CALL_CHANNEL(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE((obj), GABBLE_TYPE_BASE_CALL_CHANNEL)) #define GABBLE_IS_BASE_CALL_CHANNEL_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE((klass), GABBLE_TYPE_BASE_CALL_CHANNEL)) #define GABBLE_BASE_CALL_CHANNEL_GET_CLASS(obj) \ (G_TYPE_INSTANCE_GET_CLASS ((obj), \ GABBLE_TYPE_BASE_CALL_CHANNEL, GabbleBaseCallChannelClass)) GabbleCallMember *gabble_base_call_channel_ensure_member ( GabbleBaseCallChannel *self, const gchar *jid); void gabble_base_call_channel_remove_member (GabbleBaseCallChannel *self, GabbleCallMember *member); GabbleCallMember *gabble_base_call_channel_ensure_member_from_handle ( GabbleBaseCallChannel *self, TpHandle handle); GabbleCallMember * gabble_base_call_channel_get_member_from_handle ( GabbleBaseCallChannel *self, TpHandle handle); GabbleCallContent * gabble_base_call_channel_add_content ( GabbleBaseCallChannel *self, const gchar *name, WockyJingleMediaType mtype, TpCallContentDisposition disposition); void gabble_base_call_channel_remove_content (GabbleBaseCallChannel *self, GabbleCallContent *content); GHashTable *gabble_base_call_channel_get_members (GabbleBaseCallChannel *self); G_END_DECLS #endif /* #ifndef __GABBLE_BASE_CALL_CHANNEL_H__*/ telepathy-gabble-0.18.2/src/ft-manager.h0000644000175000017500000000524312200204333017767 0ustar00smcvsmcv00000000000000/* * ft-manager.h - Header for GabbleFtManager * Copyright (C) 2009 Collabora Ltd. * @author: Guillaume Desmottes * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 __GABBLE_FT_MANAGER_H__ #define __GABBLE_FT_MANAGER_H__ #include #include "bytestream-iface.h" #include "types.h" #include G_BEGIN_DECLS typedef struct _GabbleFtManager GabbleFtManager; typedef struct _GabbleFtManagerClass GabbleFtManagerClass; typedef struct _GabbleFtManagerPrivate GabbleFtManagerPrivate; struct _GabbleFtManagerClass { GObjectClass parent_class; }; struct _GabbleFtManager { GObject parent; GabbleFtManagerPrivate *priv; }; GType gabble_ft_manager_get_type (void); /* TYPE MACROS */ #define GABBLE_TYPE_FT_MANAGER \ (gabble_ft_manager_get_type ()) #define GABBLE_FT_MANAGER(obj) \ (G_TYPE_CHECK_INSTANCE_CAST((obj), GABBLE_TYPE_FT_MANAGER, GabbleFtManager)) #define GABBLE_FT_MANAGER_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST((klass), GABBLE_TYPE_FT_MANAGER, GabbleFtManagerClass)) #define GABBLE_IS_FT_MANAGER(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE((obj), GABBLE_TYPE_FT_MANAGER)) #define GABBLE_IS_FT_MANAGER_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE((klass), GABBLE_TYPE_FT_MANAGER)) #define GABBLE_FT_MANAGER_GET_CLASS(obj) \ (G_TYPE_INSTANCE_GET_CLASS ((obj), GABBLE_TYPE_FT_MANAGER, GabbleFtManagerClass)) GabbleFtManager *gabble_ft_manager_new (GabbleConnection *connection); void gabble_ft_manager_handle_si_request (GabbleFtManager *self, GabbleBytestreamIface *bytestream, TpHandle handle, const gchar *stream_id, WockyStanza *msg); #ifdef G_OS_UNIX /* Slight encapsulation violation: this function isn't portable, but we * happen to know that it's only needed if we support Unix sockets, and * we only do *that* on Unix. Otherwise, we can leave it undefined. */ const gchar * gabble_ft_manager_get_tmp_dir (GabbleFtManager *self); #endif G_END_DECLS #endif /* #ifndef __GABBLE_FT_MANAGER_H__*/ telepathy-gabble-0.18.2/src/ft-manager.c0000644000175000017500000007670112227000321017771 0ustar00smcvsmcv00000000000000/* * ft-manager.c - Source for GabbleFtManager * Copyright (C) 2009-2010 Collabora Ltd. * @author: Guillaume Desmottes * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 "config.h" #define _BSD_SOURCE #define _XOPEN_SOURCE /* glibc2 needs this */ #include #include #include #include #include #include #include #ifdef ENABLE_JINGLE_FILE_TRANSFER #include "jingle-share.h" #endif #include "gabble/caps-channel-manager.h" #include "connection.h" #include "ft-manager.h" #include "ft-channel.h" #include "gabble-signals-marshal.h" #include "namespaces.h" #include "presence-cache.h" #include "util.h" #include #include #include #define DEBUG_FLAG GABBLE_DEBUG_FT #include "debug.h" static void channel_manager_iface_init (gpointer, gpointer); static void gabble_ft_manager_channel_created (GabbleFtManager *mgr, GabbleFileTransferChannel *chan, gpointer request_token); static void caps_channel_manager_iface_init (gpointer g_iface, gpointer iface_data); G_DEFINE_TYPE_WITH_CODE (GabbleFtManager, gabble_ft_manager, G_TYPE_OBJECT, G_IMPLEMENT_INTERFACE (TP_TYPE_CHANNEL_MANAGER, channel_manager_iface_init); G_IMPLEMENT_INTERFACE (GABBLE_TYPE_CAPS_CHANNEL_MANAGER, caps_channel_manager_iface_init)); /* properties */ enum { PROP_CONNECTION = 1, LAST_PROPERTY }; /* private structure */ struct _GabbleFtManagerPrivate { gboolean dispose_has_run; GabbleConnection *connection; GList *channels; /* path of the temporary directory used to store UNIX sockets */ gchar *tmp_dir; gulong status_changed_id; }; static void gabble_ft_manager_init (GabbleFtManager *obj) { obj->priv = G_TYPE_INSTANCE_GET_PRIVATE (obj, GABBLE_TYPE_FT_MANAGER, GabbleFtManagerPrivate); obj->priv->connection = NULL; /* allocate any data required by the object here */ obj->priv->channels = NULL; } static void gabble_ft_manager_constructed (GObject *object); static void gabble_ft_manager_dispose (GObject *object); static void gabble_ft_manager_finalize (GObject *object); static void connection_status_changed_cb (GabbleConnection *conn, guint status, guint reason, GabbleFtManager *self); static void gabble_ft_manager_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { GabbleFtManager *self = GABBLE_FT_MANAGER (object); switch (property_id) { case PROP_CONNECTION: g_value_set_object (value, self->priv->connection); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void gabble_ft_manager_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { GabbleFtManager *self = GABBLE_FT_MANAGER (object); switch (property_id) { case PROP_CONNECTION: self->priv->connection = g_value_get_object (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void gabble_ft_manager_class_init (GabbleFtManagerClass *gabble_ft_manager_class) { GObjectClass *object_class = G_OBJECT_CLASS (gabble_ft_manager_class); GParamSpec *param_spec; g_type_class_add_private (gabble_ft_manager_class, sizeof (GabbleFtManagerPrivate)); object_class->constructed = gabble_ft_manager_constructed; object_class->get_property = gabble_ft_manager_get_property; object_class->set_property = gabble_ft_manager_set_property; object_class->dispose = gabble_ft_manager_dispose; object_class->finalize = gabble_ft_manager_finalize; param_spec = g_param_spec_object ("connection", "GabbleConnection object", "Gabble Connection that owns the connection for this FT manager", GABBLE_TYPE_CONNECTION, G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, PROP_CONNECTION, param_spec); } static void gabble_ft_manager_constructed (GObject *object) { void (*chain_up) (GObject *) = G_OBJECT_CLASS (gabble_ft_manager_parent_class)->constructed; GabbleFtManager *self = GABBLE_FT_MANAGER (object); if (chain_up != NULL) chain_up (object); self->priv->status_changed_id = g_signal_connect (self->priv->connection, "status-changed", (GCallback) connection_status_changed_cb, object); } static void ft_manager_close_all (GabbleFtManager *self) { GList *l; while ((l = self->priv->channels) != NULL) { tp_base_channel_close (l->data); /* Channels should have closed and disappeared from the list */ g_assert (l != self->priv->channels); } } void gabble_ft_manager_dispose (GObject *object) { GabbleFtManager *self = GABBLE_FT_MANAGER (object); if (self->priv->dispose_has_run) return; self->priv->dispose_has_run = TRUE; g_assert (self->priv->channels == NULL); if (G_OBJECT_CLASS (gabble_ft_manager_parent_class)->dispose) G_OBJECT_CLASS (gabble_ft_manager_parent_class)->dispose (object); } void gabble_ft_manager_finalize (GObject *object) { GabbleFtManager *self = GABBLE_FT_MANAGER (object); if (self->priv->tmp_dir != NULL) { if (g_rmdir (self->priv->tmp_dir) != 0) { DEBUG ("rmdir failed: %s", g_strerror (errno)); } } g_free (self->priv->tmp_dir); G_OBJECT_CLASS (gabble_ft_manager_parent_class)->finalize (object); } /* Channel Manager interface */ struct foreach_data { TpExportableChannelFunc func; gpointer data; }; static void gabble_ft_manager_iface_foreach_one (gpointer value, gpointer data) { TpExportableChannel *chan; struct foreach_data *f = (struct foreach_data *) data; chan = TP_EXPORTABLE_CHANNEL (value); f->func (chan, f->data); } static void gabble_ft_manager_foreach_channel (TpChannelManager *iface, TpExportableChannelFunc func, gpointer data) { GabbleFtManager *self = GABBLE_FT_MANAGER (iface); struct foreach_data f; f.func = func; f.data = data; g_list_foreach (self->priv->channels, (GFunc) gabble_ft_manager_iface_foreach_one, &f); } static void file_channel_closed_cb (GabbleFileTransferChannel *chan, gpointer user_data) { GabbleFtManager *self = GABBLE_FT_MANAGER (user_data); if (self->priv->channels != NULL) { gchar *path, *id; g_object_get (chan, "target-id", &id, "object-path", &path, NULL); DEBUG ("Removing channel %s with %s", path, id); self->priv->channels = g_list_remove (self->priv->channels, chan); g_object_unref (chan); g_free (id); g_free (path); } } #ifdef ENABLE_JINGLE_FILE_TRANSFER static void gabble_ft_manager_channels_created (GabbleFtManager *self, GList *channels) { GList *i; GHashTable *new_channels = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, NULL); for (i = channels; i ; i = i->next) { GabbleFileTransferChannel *chan = i->data; tp_base_channel_register (TP_BASE_CHANNEL (chan)); gabble_signal_connect_weak (chan, "closed", G_CALLBACK (file_channel_closed_cb), G_OBJECT (self)); self->priv->channels = g_list_append (self->priv->channels, chan); /* The channels can't satisfy a request because this will always be called when we receive an incoming jingle-share session */ g_hash_table_insert (new_channels, chan, NULL); } tp_channel_manager_emit_new_channels (self, new_channels); g_hash_table_unref (new_channels); } #endif static void gabble_ft_manager_channel_created (GabbleFtManager *self, GabbleFileTransferChannel *chan, gpointer request_token) { GSList *requests = NULL; tp_base_channel_register (TP_BASE_CHANNEL (chan)); gabble_signal_connect_weak (chan, "closed", G_CALLBACK (file_channel_closed_cb), G_OBJECT (self)); self->priv->channels = g_list_append (self->priv->channels, chan); if (request_token != NULL) requests = g_slist_prepend (requests, request_token); tp_channel_manager_emit_new_channel (self, TP_EXPORTABLE_CHANNEL (chan), requests); g_slist_free (requests); } #ifdef ENABLE_JINGLE_FILE_TRANSFER static void new_jingle_session_cb (GabbleJingleMint *jm, WockyJingleSession *sess, gpointer data) { GabbleFtManager *self = GABBLE_FT_MANAGER (data); GTalkFileCollection *gtalk_fc = NULL; WockyJingleContent *content = NULL; GabbleJingleShareManifest *manifest = NULL; GList *channels = NULL; GList *cs, *i; if (wocky_jingle_session_get_content_type (sess) == GABBLE_TYPE_JINGLE_SHARE) { cs = wocky_jingle_session_get_contents (sess); if (cs != NULL) { content = WOCKY_JINGLE_CONTENT (cs->data); g_list_free (cs); } if (content == NULL) return; gtalk_fc = gtalk_file_collection_new_from_session ( gabble_jingle_mint_get_factory (jm), sess); if (gtalk_fc) { gchar *token = NULL; g_object_get (gtalk_fc, "token", &token, NULL); manifest = gabble_jingle_share_get_manifest ( GABBLE_JINGLE_SHARE (content)); for (i = manifest->entries; i; i = i->next) { GabbleJingleShareManifestEntry *entry = i->data; GabbleFileTransferChannel *channel = NULL; gchar *filename = NULL; TpHandleRepoIface *contacts = tp_base_connection_get_handles ( TP_BASE_CONNECTION (self->priv->connection), TP_HANDLE_TYPE_CONTACT); TpHandle peer = tp_handle_ensure (contacts, wocky_jingle_session_get_peer_jid (sess), NULL, NULL); filename = g_strdup_printf ("%s%s", entry->name, entry->folder? ".tar":""); channel = gabble_file_transfer_channel_new (self->priv->connection, peer, peer, TP_FILE_TRANSFER_STATE_PENDING, NULL, filename, entry->size, TP_FILE_HASH_TYPE_NONE, NULL, NULL, 0, 0, FALSE, NULL, gtalk_fc, token, NULL, NULL, NULL); g_free (filename); gtalk_file_collection_add_channel (gtalk_fc, channel); channels = g_list_prepend (channels, channel); } if (channels != NULL) gabble_ft_manager_channels_created (self, channels); g_list_free (channels); /* Channels will hold the reference to the gtalk file collection, so we can drop ours already. If no channels were created, then we need to destroy it anyways */ g_object_unref (gtalk_fc); } } } #endif static void connection_status_changed_cb (GabbleConnection *conn, guint status, guint reason, GabbleFtManager *self) { switch (status) { #ifdef ENABLE_JINGLE_FILE_TRANSFER case TP_CONNECTION_STATUS_CONNECTING: g_signal_connect (self->priv->connection->jingle_mint, "incoming-session", G_CALLBACK (new_jingle_session_cb), self); break; #endif case TP_CONNECTION_STATUS_DISCONNECTED: ft_manager_close_all (self); if (self->priv->status_changed_id != 0) { g_signal_handler_disconnect (self->priv->connection, self->priv->status_changed_id); self->priv->status_changed_id = 0; } break; } } static gboolean gabble_ft_manager_handle_request (TpChannelManager *manager, gpointer request_token, GHashTable *request_properties) { GabbleFtManager *self = GABBLE_FT_MANAGER (manager); GabbleFileTransferChannel *chan; TpBaseConnection *base_conn = TP_BASE_CONNECTION (self->priv->connection); TpHandleRepoIface *contact_repo = tp_base_connection_get_handles (base_conn, TP_HANDLE_TYPE_CONTACT); TpHandle handle; const gchar *content_type, *filename, *content_hash, *description; const gchar *file_uri, *service_name; guint64 size, date, initial_offset; TpFileHashType content_hash_type; const GHashTable *metadata; GError *error = NULL; gboolean valid; DEBUG ("File transfer request"); /* We only support file transfer channels */ if (tp_strdiff (tp_asv_get_string (request_properties, TP_IFACE_CHANNEL ".ChannelType"), TP_IFACE_CHANNEL_TYPE_FILE_TRANSFER)) return FALSE; /* And only contact handles */ if (tp_asv_get_uint32 (request_properties, TP_IFACE_CHANNEL ".TargetHandleType", NULL) != TP_HANDLE_TYPE_CONTACT) return FALSE; handle = tp_asv_get_uint32 (request_properties, TP_IFACE_CHANNEL ".TargetHandle", NULL); /* Must be a valid contact handle */ if (!tp_handle_is_valid (contact_repo, handle, &error)) goto error; /* Don't support opening a channel to our self handle */ if (handle == tp_base_connection_get_self_handle (base_conn)) { g_set_error (&error, TP_ERROR, TP_ERROR_NOT_IMPLEMENTED, "Can't open a file transfer channel to yourself"); goto error; } content_type = tp_asv_get_string (request_properties, TP_IFACE_CHANNEL_TYPE_FILE_TRANSFER ".ContentType"); if (content_type == NULL) { g_set_error (&error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT, "ContentType property is mandatory"); goto error; } filename = tp_asv_get_string (request_properties, TP_IFACE_CHANNEL_TYPE_FILE_TRANSFER ".Filename"); if (filename == NULL) { g_set_error (&error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT, "Filename property is mandatory"); goto error; } size = tp_asv_get_uint64 (request_properties, TP_IFACE_CHANNEL_TYPE_FILE_TRANSFER ".Size", NULL); if (size == 0) { g_set_error (&error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT, "Size property is mandatory"); goto error; } content_hash_type = tp_asv_get_uint32 (request_properties, TP_IFACE_CHANNEL_TYPE_FILE_TRANSFER ".ContentHashType", &valid); if (!valid) { /* Assume File_Hash_Type_None */ content_hash_type = TP_FILE_HASH_TYPE_NONE; } else { if (content_hash_type >= NUM_TP_FILE_HASH_TYPES) { g_set_error (&error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT, "%u is not a valid ContentHashType", content_hash_type); goto error; } } if (content_hash_type != TP_FILE_HASH_TYPE_NONE) { content_hash = tp_asv_get_string (request_properties, TP_IFACE_CHANNEL_TYPE_FILE_TRANSFER ".ContentHash"); if (content_hash == NULL) { g_set_error (&error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT, "ContentHash property is mandatory if ContentHashType is " "not None"); goto error; } } else { content_hash = NULL; } description = tp_asv_get_string (request_properties, TP_IFACE_CHANNEL_TYPE_FILE_TRANSFER ".Description"); date = tp_asv_get_uint64 (request_properties, TP_IFACE_CHANNEL_TYPE_FILE_TRANSFER ".Date", NULL); initial_offset = tp_asv_get_uint64 (request_properties, TP_IFACE_CHANNEL_TYPE_FILE_TRANSFER ".InitialOffset", NULL); file_uri = tp_asv_get_string (request_properties, TP_PROP_CHANNEL_TYPE_FILE_TRANSFER_URI); service_name = tp_asv_get_string (request_properties, TP_PROP_CHANNEL_INTERFACE_FILE_TRANSFER_METADATA_SERVICE_NAME); metadata = tp_asv_get_boxed (request_properties, TP_PROP_CHANNEL_INTERFACE_FILE_TRANSFER_METADATA_METADATA, TP_HASH_TYPE_METADATA); if (metadata != NULL && g_hash_table_lookup ((GHashTable *) metadata, "FORM_TYPE")) { g_set_error (&error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT, "Metadata cannot contain an item with key 'FORM_TYPE'"); goto error; } DEBUG ("Requested outgoing channel with contact: %s", tp_handle_inspect (contact_repo, handle)); chan = gabble_file_transfer_channel_new (self->priv->connection, handle, tp_base_connection_get_self_handle (base_conn), TP_FILE_TRANSFER_STATE_PENDING, content_type, filename, size, content_hash_type, content_hash, description, date, initial_offset, TRUE, NULL, NULL, NULL, file_uri, service_name, metadata); if (!gabble_file_transfer_channel_offer_file (chan, &error)) { g_object_unref (chan); goto error; } gabble_ft_manager_channel_created (self, chan, request_token); return TRUE; error: tp_channel_manager_emit_request_failed (self, request_token, error->domain, error->code, error->message); g_error_free (error); return TRUE; } /* Keep in sync with values set in gabble_ft_manager_type_foreach_channel_class */ static const gchar * const file_transfer_channel_fixed_properties[] = { TP_IFACE_CHANNEL ".ChannelType", TP_IFACE_CHANNEL ".TargetHandleType", NULL }; /* ContentHashType has to be first so we can easily skip it when needed */ #define STANDARD_PROPERTIES \ TP_PROP_CHANNEL_TYPE_FILE_TRANSFER_CONTENT_HASH_TYPE, \ TP_PROP_CHANNEL_TARGET_HANDLE, \ TP_PROP_CHANNEL_TARGET_ID, \ TP_PROP_CHANNEL_TYPE_FILE_TRANSFER_CONTENT_TYPE, \ TP_PROP_CHANNEL_TYPE_FILE_TRANSFER_FILENAME, \ TP_PROP_CHANNEL_TYPE_FILE_TRANSFER_SIZE, \ TP_PROP_CHANNEL_TYPE_FILE_TRANSFER_CONTENT_HASH, \ TP_PROP_CHANNEL_TYPE_FILE_TRANSFER_DESCRIPTION, \ TP_PROP_CHANNEL_TYPE_FILE_TRANSFER_DATE, \ TP_PROP_CHANNEL_TYPE_FILE_TRANSFER_URI static const gchar * const file_transfer_channel_allowed_properties[] = { STANDARD_PROPERTIES, NULL }; static const gchar * const file_transfer_channel_allowed_properties_with_metadata_prop[] = { STANDARD_PROPERTIES, TP_PROP_CHANNEL_INTERFACE_FILE_TRANSFER_METADATA_METADATA, NULL }; static const gchar * const file_transfer_channel_allowed_properties_with_both_metadata_props[] = { STANDARD_PROPERTIES, TP_PROP_CHANNEL_INTERFACE_FILE_TRANSFER_METADATA_SERVICE_NAME, TP_PROP_CHANNEL_INTERFACE_FILE_TRANSFER_METADATA_METADATA, NULL }; static void gabble_ft_manager_type_foreach_channel_class (GType type, TpChannelManagerTypeChannelClassFunc func, gpointer user_data) { GHashTable *table; /* general FT class */ table = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, (GDestroyNotify) tp_g_value_slice_free); g_hash_table_insert (table, TP_IFACE_CHANNEL ".ChannelType" , tp_g_value_slice_new_string (TP_IFACE_CHANNEL_TYPE_FILE_TRANSFER)); g_hash_table_insert (table, TP_IFACE_CHANNEL ".TargetHandleType", tp_g_value_slice_new_uint (TP_HANDLE_TYPE_CONTACT)); func (type, table, file_transfer_channel_allowed_properties_with_both_metadata_props, user_data); /* MD5 HashType class */ g_hash_table_insert (table, TP_IFACE_CHANNEL_TYPE_FILE_TRANSFER ".ContentHashType", tp_g_value_slice_new_uint (TP_FILE_HASH_TYPE_MD5)); /* skip ContentHashType in allowed properties */ func (type, table, file_transfer_channel_allowed_properties_with_both_metadata_props + 1, user_data); g_hash_table_unref (table); } static WockyNode * hyvaa_vappua ( WockyNode *si_node, const gchar **filename, const gchar **size_str, GError **error) { #define die_if_null(var, msg) \ if ((var) == NULL) \ { \ g_set_error (error, WOCKY_XMPP_ERROR, WOCKY_XMPP_ERROR_BAD_REQUEST, \ msg); \ return NULL; \ } WockyNode *file_node = wocky_node_get_child_ns (si_node, "file", NS_FILE_TRANSFER); die_if_null (file_node, "Invalid file transfer SI request: no ") die_if_null (*filename = wocky_node_get_attribute (file_node, "name"), "Invalid file transfer SI request: missing file name") die_if_null (*size_str = wocky_node_get_attribute (file_node, "size"), "Invalid file transfer SI request: missing file size") return file_node; #undef die_if_null } static WockyDataForm * find_data_form (WockyNode *file, const gchar *form_type) { WockyNodeIter iter; WockyNode *x; wocky_node_iter_init (&iter, file, "x", NS_X_DATA); while (wocky_node_iter_next (&iter, &x)) { GError *error = NULL; WockyDataForm *form = wocky_data_form_new_from_node (x, &error); WockyDataFormField *field; if (form == NULL) { DEBUG ("Failed to parse data form: %s", error->message); g_clear_error (&error); continue; } field = g_hash_table_lookup (form->fields, "FORM_TYPE"); if (field == NULL) { DEBUG ("Data form doesn't have FORM_TYPE field!"); g_object_unref (form); continue; } /* found it! */ if (!tp_strdiff (field->raw_value_contents[0], form_type)) return form; g_object_unref (form); } return NULL; } static gchar * extract_service_name (WockyNode *file) { WockyDataForm *form = find_data_form (file, NS_TP_FT_METADATA_SERVICE); WockyDataFormField *field; gchar *service_name = NULL; if (form == NULL) return NULL; field = g_hash_table_lookup (form->fields, "ServiceName"); if (field == NULL) { DEBUG ("ServiceName property not present in data form; odd..."); goto out; } if (field->raw_value_contents == NULL || field->raw_value_contents[0] == NULL) { DEBUG ("ServiceName property doesn't have a real value; odd..."); } else { service_name = g_strdup (field->raw_value_contents[0]); } out: g_object_unref (form); return service_name; } static GHashTable * extract_metadata (WockyNode *file) { WockyDataForm *form = find_data_form (file, NS_TP_FT_METADATA); GHashTable *metadata; GHashTableIter iter; gpointer key, value; if (form == NULL) return NULL; metadata = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify) g_strfreev); g_hash_table_iter_init (&iter, form->fields); while (g_hash_table_iter_next (&iter, &key, &value)) { const gchar *var = key; WockyDataFormField *field = value; if (!tp_strdiff (var, "FORM_TYPE")) continue; g_hash_table_insert (metadata, g_strdup (var), g_strdupv (field->raw_value_contents)); } g_object_unref (form); return metadata; } void gabble_ft_manager_handle_si_request (GabbleFtManager *self, GabbleBytestreamIface *bytestream, TpHandle handle, const gchar *stream_id, WockyStanza *msg) { WockyNode *si_node, *file_node, *desc_node; const gchar *filename, *size_str, *content_type, *content_hash, *description; const gchar *date_str; gchar *service_name; GHashTable *metadata; guint64 size; guint64 date = 0; TpFileHashType content_hash_type; GabbleFileTransferChannel *chan; gboolean resume_supported; GError *error = NULL; si_node = wocky_node_get_child_ns ( wocky_stanza_get_top_node (msg), "si", NS_SI); g_assert (si_node != NULL); file_node = hyvaa_vappua (si_node, &filename, &size_str, &error); if (file_node == NULL) { DEBUG ("%s", error->message); gabble_bytestream_iface_close (bytestream, error); g_clear_error (&error); return; } size = g_ascii_strtoull (size_str, NULL, 0); content_type = wocky_node_get_attribute (file_node, "mime-type"); if (content_type == NULL) content_type = "application/octet-stream"; /* The hash is always an MD5-sum, if present. */ content_hash = wocky_node_get_attribute (file_node, "hash"); if (content_hash != NULL) content_hash_type = TP_FILE_HASH_TYPE_MD5; else content_hash_type = TP_FILE_HASH_TYPE_NONE; desc_node = wocky_node_get_child (file_node, "desc"); if (desc_node != NULL) description = desc_node->content; else description = NULL; date_str = wocky_node_get_attribute (file_node, "date"); if (date_str != NULL) { GTimeVal val; /* FIXME: this assume the timezone is always UTC */ if (g_time_val_from_iso8601 (date_str, &val)) date = val.tv_sec; } resume_supported = (wocky_node_get_child (file_node, "range") != NULL); /* metadata */ service_name = extract_service_name (file_node); metadata = extract_metadata (file_node); chan = gabble_file_transfer_channel_new (self->priv->connection, handle, handle, TP_FILE_TRANSFER_STATE_PENDING, content_type, filename, size, content_hash_type, content_hash, description, date, 0, resume_supported, bytestream, NULL, NULL, NULL, service_name, metadata); gabble_ft_manager_channel_created (self, chan, NULL); g_free (service_name); if (metadata != NULL) g_hash_table_unref (metadata); } static void channel_manager_iface_init (gpointer g_iface, gpointer iface_data) { TpChannelManagerIface *iface = g_iface; iface->foreach_channel = gabble_ft_manager_foreach_channel; iface->type_foreach_channel_class = gabble_ft_manager_type_foreach_channel_class; iface->create_channel = gabble_ft_manager_handle_request; iface->ensure_channel = gabble_ft_manager_handle_request; } /* public functions */ GabbleFtManager * gabble_ft_manager_new (GabbleConnection *connection) { g_assert (connection != NULL); return g_object_new (GABBLE_TYPE_FT_MANAGER, "connection", connection, NULL); } #ifdef G_OS_UNIX /* We assume that all Unixes have mkdtemp */ const gchar * gabble_ft_manager_get_tmp_dir (GabbleFtManager *self) { if (self->priv->tmp_dir != NULL) return self->priv->tmp_dir; self->priv->tmp_dir = g_strdup_printf ("%s/gabble-ft-XXXXXX", g_get_tmp_dir ()); self->priv->tmp_dir = mkdtemp (self->priv->tmp_dir); if (self->priv->tmp_dir == NULL) DEBUG ("mkdtemp failed: %s\n", g_strerror (errno)); return self->priv->tmp_dir; } #endif static void add_file_transfer_channel_class (GPtrArray *arr, gboolean include_metadata_properties, const gchar *service_name_str) { GValue monster = {0, }; GHashTable *fixed_properties; GValue *channel_type_value; GValue *target_handle_type_value; GValue *service_name_value; const gchar * const *allowed_properties; g_value_init (&monster, TP_STRUCT_TYPE_REQUESTABLE_CHANNEL_CLASS); g_value_take_boxed (&monster, dbus_g_type_specialized_construct ( TP_STRUCT_TYPE_REQUESTABLE_CHANNEL_CLASS)); fixed_properties = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, (GDestroyNotify) tp_g_value_slice_free); channel_type_value = tp_g_value_slice_new_static_string ( TP_IFACE_CHANNEL_TYPE_FILE_TRANSFER); g_hash_table_insert (fixed_properties, TP_IFACE_CHANNEL ".ChannelType", channel_type_value); target_handle_type_value = tp_g_value_slice_new_uint (TP_HANDLE_TYPE_CONTACT); g_hash_table_insert (fixed_properties, TP_IFACE_CHANNEL ".TargetHandleType", target_handle_type_value); if (service_name_str != NULL) { service_name_value = tp_g_value_slice_new_string (service_name_str); g_hash_table_insert (fixed_properties, TP_PROP_CHANNEL_INTERFACE_FILE_TRANSFER_METADATA_SERVICE_NAME, service_name_value); } if (include_metadata_properties) { if (service_name_str == NULL) allowed_properties = file_transfer_channel_allowed_properties_with_both_metadata_props; else allowed_properties = file_transfer_channel_allowed_properties_with_metadata_prop; } else { allowed_properties = file_transfer_channel_allowed_properties; } dbus_g_type_struct_set (&monster, 0, fixed_properties, 1, allowed_properties, G_MAXUINT); g_hash_table_unref (fixed_properties); g_ptr_array_add (arr, g_value_get_boxed (&monster)); } static void get_contact_caps_foreach (gpointer data, gpointer user_data) { const gchar *ns = data; GPtrArray *arr = user_data; if (!g_str_has_prefix (ns, NS_TP_FT_METADATA "#")) return; add_file_transfer_channel_class (arr, TRUE, ns + strlen (NS_TP_FT_METADATA "#")); } static void gabble_ft_manager_get_contact_caps ( GabbleCapsChannelManager *manager G_GNUC_UNUSED, TpHandle handle G_GNUC_UNUSED, const GabbleCapabilitySet *caps, GPtrArray *arr) { if (gabble_capability_set_has (caps, NS_FILE_TRANSFER) || gabble_capability_set_has (caps, NS_GOOGLE_FEAT_SHARE)) { add_file_transfer_channel_class (arr, gabble_capability_set_has (caps, NS_TP_FT_METADATA), NULL); } gabble_capability_set_foreach (caps, get_contact_caps_foreach, arr); } static void gabble_ft_manager_represent_client ( GabbleCapsChannelManager *manager G_GNUC_UNUSED, const gchar *client_name, const GPtrArray *filters, const gchar * const *cap_tokens G_GNUC_UNUSED, GabbleCapabilitySet *cap_set, GPtrArray *data_forms G_GNUC_UNUSED) { guint i; for (i = 0; i < filters->len; i++) { GHashTable *channel_class = g_ptr_array_index (filters, i); const gchar *service_name; gchar *ns; if (tp_strdiff (tp_asv_get_string (channel_class, TP_IFACE_CHANNEL ".ChannelType"), TP_IFACE_CHANNEL_TYPE_FILE_TRANSFER)) continue; if (tp_asv_get_uint32 (channel_class, TP_IFACE_CHANNEL ".TargetHandleType", NULL) != TP_HANDLE_TYPE_CONTACT) continue; DEBUG ("client %s supports file transfer", client_name); gabble_capability_set_add (cap_set, NS_FILE_TRANSFER); gabble_capability_set_add (cap_set, NS_GOOGLE_FEAT_SHARE); gabble_capability_set_add (cap_set, NS_TP_FT_METADATA); /* now look at service names */ /* capabilities mean being able to RECEIVE said kinds of * FTs. hence, skip Requested=true (locally initiated) channel * classes */ if (tp_asv_get_boolean (channel_class, TP_PROP_CHANNEL_REQUESTED, FALSE)) continue; service_name = tp_asv_get_string (channel_class, TP_PROP_CHANNEL_INTERFACE_FILE_TRANSFER_METADATA_SERVICE_NAME); if (service_name == NULL) continue; ns = g_strconcat (NS_TP_FT_METADATA "#", service_name, NULL); DEBUG ("%s: adding capability %s", client_name, ns); gabble_capability_set_add (cap_set, ns); g_free (ns); } } static void caps_channel_manager_iface_init (gpointer g_iface, gpointer iface_data) { GabbleCapsChannelManagerInterface *iface = g_iface; iface->get_contact_caps = gabble_ft_manager_get_contact_caps; iface->represent_client = gabble_ft_manager_represent_client; } telepathy-gabble-0.18.2/src/ft-channel.h0000644000175000017500000001064612200204333017770 0ustar00smcvsmcv00000000000000/* * ft-channel.h - Header for GabbleFileTransferChannel * Copyright (C) 2009 Collabora Ltd. * @author: Guillaume Desmottes * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 __GABBLE_FILE_TRANSFER_CHANNEL_H__ #define __GABBLE_FILE_TRANSFER_CHANNEL_H__ #include "config.h" #include #include #include typedef struct _GabbleFileTransferChannel GabbleFileTransferChannel; #ifdef ENABLE_JINGLE_FILE_TRANSFER #include "gtalk-file-collection.h" #endif #include "bytestream-factory.h" G_BEGIN_DECLS typedef struct _GabbleFileTransferChannelClass GabbleFileTransferChannelClass; typedef struct _GabbleFileTransferChannelPrivate GabbleFileTransferChannelPrivate; struct _GabbleFileTransferChannelClass { TpBaseChannelClass parent_class; TpDBusPropertiesMixinClass dbus_props_class; }; struct _GabbleFileTransferChannel { TpBaseChannel parent; GabbleFileTransferChannelPrivate *priv; }; GType gabble_file_transfer_channel_get_type (void); /* TYPE MACROS */ #define GABBLE_TYPE_FILE_TRANSFER_CHANNEL \ (gabble_file_transfer_channel_get_type ()) #define GABBLE_FILE_TRANSFER_CHANNEL(obj) \ (G_TYPE_CHECK_INSTANCE_CAST((obj), GABBLE_TYPE_FILE_TRANSFER_CHANNEL, GabbleFileTransferChannel)) #define GABBLE_FILE_TRANSFER_CHANNEL_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST((klass), GABBLE_TYPE_FILE_TRANSFER_CHANNEL, \ GabbleFileTransferChannelClass)) #define GABBLE_IS_FILE_TRANSFER_CHANNEL(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE((obj), GABBLE_TYPE_FILE_TRANSFER_CHANNEL)) #define GABBLE_IS_FILE_TRANSFER_CHANNEL_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE((klass), GABBLE_TYPE_FILE_TRANSFER_CHANNEL)) #define GABBLE_FILE_TRANSFER_CHANNEL_GET_CLASS(obj) \ (G_TYPE_INSTANCE_GET_CLASS ((obj), GABBLE_TYPE_FILE_TRANSFER_CHANNEL, \ GabbleFileTransferChannelClass)) GabbleFileTransferChannel * gabble_file_transfer_channel_new (GabbleConnection *conn, TpHandle handle, TpHandle initiator_handle, TpFileTransferState state, const gchar *content_type, const gchar *filename, guint64 size, TpFileHashType content_hash_type, const gchar *content_hash, const gchar *description, guint64 date, guint64 initial_offset, gboolean resume_supported, GabbleBytestreamIface *bytestream, #ifdef ENABLE_JINGLE_FILE_TRANSFER GTalkFileCollection *gtalk_fc, #else /* It's easier for the calling code if we don't change the number of * arguments based on a #ifdef */ gpointer gtalk_fc_dummy, #endif const gchar *file_collection, const gchar *uri, const gchar *service_name, const GHashTable *metadata); gboolean gabble_file_transfer_channel_offer_file ( GabbleFileTransferChannel *self, GError **error); #ifdef ENABLE_JINGLE_FILE_TRANSFER /* The following methods are a hack, they are 'signal-like' callbacks for the GTalkFileCollection. They have to be made this way because the FileCollection can't send out signals since it needs its signals to be sent to a specific channel only. So instead it calls these callbacks directly on the channel it needs to notify. This is a known layering violation and accepted as the lesser of any other evil [hack]. */ void gabble_file_transfer_channel_gtalk_file_collection_state_changed ( GabbleFileTransferChannel *self, GTalkFileCollectionState gtalk_fc_state, gboolean local_terminator); void gabble_file_transfer_channel_gtalk_file_collection_write_blocked ( GabbleFileTransferChannel *self, gboolean blocked); void gabble_file_transfer_channel_gtalk_file_collection_data_received ( GabbleFileTransferChannel *self, const gchar *data, guint len); #endif G_END_DECLS #endif /* #ifndef __GABBLE_FILE_TRANSFER_CHANNEL_H__*/ telepathy-gabble-0.18.2/src/ft-channel.c0000644000175000017500000021372112227000321017762 0ustar00smcvsmcv00000000000000/* * ft-channel.c - Source for GabbleFileTransferChannel * Copyright (C) 2009-2010 Collabora Ltd. * @author: Guillaume Desmottes * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 "config.h" #include #include #include #include #include #include #include #include #ifdef HAVE_UNISTD_H # include #endif #define DEBUG_FLAG GABBLE_DEBUG_FT #include "debug.h" #include #include #include /* just for the feature-test */ #include "connection.h" #include "ft-channel.h" #include "gabble-signals-marshal.h" #include "namespaces.h" #include "presence-cache.h" #include "util.h" #include #include static void file_transfer_iface_init (gpointer g_iface, gpointer iface_data); static void transferred_chunk (GabbleFileTransferChannel *self, guint64 count); static gboolean set_bytestream (GabbleFileTransferChannel *self, GabbleBytestreamIface *bytestream); #ifdef ENABLE_JINGLE_FILE_TRANSFER static gboolean set_gtalk_file_collection (GabbleFileTransferChannel *self, GTalkFileCollection *gtalk_file_collection); #endif G_DEFINE_TYPE_WITH_CODE (GabbleFileTransferChannel, gabble_file_transfer_channel, TP_TYPE_BASE_CHANNEL, G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CHANNEL_TYPE_FILE_TRANSFER, file_transfer_iface_init); G_IMPLEMENT_INTERFACE (GABBLE_TYPE_SVC_CHANNEL_TYPE_FILETRANSFER_FUTURE, NULL); G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CHANNEL_INTERFACE_FILE_TRANSFER_METADATA, NULL); ); #define GABBLE_UNDEFINED_FILE_SIZE G_MAXUINT64 /* properties */ enum { /* Channel.Type.FileTransfer D-Bus properties */ PROP_STATE = 1, PROP_CONTENT_TYPE, PROP_FILENAME, PROP_SIZE, PROP_CONTENT_HASH_TYPE, PROP_CONTENT_HASH, PROP_DESCRIPTION, PROP_DATE, PROP_AVAILABLE_SOCKET_TYPES, PROP_TRANSFERRED_BYTES, PROP_INITIAL_OFFSET, PROP_RESUME_SUPPORTED, PROP_FILE_COLLECTION, PROP_URI, PROP_CONNECTION, PROP_BYTESTREAM, #ifdef ENABLE_JINGLE_FILE_TRANSFER /* Chan.Type.FileTransfer.FUTURE */ PROP_GTALK_FILE_COLLECTION, #endif /* Chan.Iface.FileTransfer.Metadata */ PROP_SERVICE_NAME, PROP_METADATA, LAST_PROPERTY }; /* private structure */ struct _GabbleFileTransferChannelPrivate { gboolean dispose_has_run; GTimeVal last_transferred_bytes_emitted; guint progress_timer; TpSocketAddressType socket_type; GValue *socket_address; gboolean resume_supported; #ifdef ENABLE_JINGLE_FILE_TRANSFER GTalkFileCollection *gtalk_file_collection; #endif GabbleBytestreamIface *bytestream; GibberListener *listener; GibberTransport *transport; /* properties */ TpFileTransferState state; gchar *content_type; gchar *filename; guint64 size; TpFileHashType content_hash_type; gchar *content_hash; gchar *description; GHashTable *available_socket_types; guint64 transferred_bytes; guint64 initial_offset; guint64 date; gchar *file_collection; gchar *uri; gchar *service_name; GHashTable *metadata; gboolean channel_opened; }; static void gabble_file_transfer_channel_set_state ( TpSvcChannelTypeFileTransfer *iface, TpFileTransferState state, TpFileTransferStateChangeReason reason); static void close_session_and_transport (GabbleFileTransferChannel *self); static void gabble_file_transfer_channel_close (TpBaseChannel *base) { GabbleFileTransferChannel *self = GABBLE_FILE_TRANSFER_CHANNEL (base); if (self->priv->state != TP_FILE_TRANSFER_STATE_COMPLETED && self->priv->state != TP_FILE_TRANSFER_STATE_CANCELLED) { gabble_file_transfer_channel_set_state ( TP_SVC_CHANNEL_TYPE_FILE_TRANSFER (self), TP_FILE_TRANSFER_STATE_CANCELLED, TP_FILE_TRANSFER_STATE_CHANGE_REASON_LOCAL_STOPPED); close_session_and_transport (self); } tp_base_channel_destroyed (base); } static void gabble_file_transfer_channel_init (GabbleFileTransferChannel *obj) { obj->priv = G_TYPE_INSTANCE_GET_PRIVATE (obj, GABBLE_TYPE_FILE_TRANSFER_CHANNEL, GabbleFileTransferChannelPrivate); } static void gabble_file_transfer_channel_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { GabbleFileTransferChannel *self = GABBLE_FILE_TRANSFER_CHANNEL (object); switch (property_id) { case PROP_STATE: g_value_set_uint (value, self->priv->state); break; case PROP_CONTENT_TYPE: g_value_set_string (value, self->priv->content_type); break; case PROP_FILENAME: g_value_set_string (value, self->priv->filename); break; case PROP_SIZE: g_value_set_uint64 (value, self->priv->size); break; case PROP_CONTENT_HASH_TYPE: g_value_set_uint (value, self->priv->content_hash_type); break; case PROP_CONTENT_HASH: g_value_set_string (value, self->priv->content_hash); break; case PROP_DESCRIPTION: g_value_set_string (value, self->priv->description); break; case PROP_AVAILABLE_SOCKET_TYPES: g_value_set_boxed (value, self->priv->available_socket_types); break; case PROP_TRANSFERRED_BYTES: g_value_set_uint64 (value, self->priv->transferred_bytes); break; case PROP_INITIAL_OFFSET: g_value_set_uint64 (value, self->priv->initial_offset); break; case PROP_DATE: g_value_set_uint64 (value, self->priv->date); break; case PROP_FILE_COLLECTION: g_value_set_string (value, self->priv->file_collection); break; case PROP_URI: g_value_set_string (value, self->priv->uri != NULL ? self->priv->uri: ""); break; case PROP_RESUME_SUPPORTED: g_value_set_boolean (value, self->priv->resume_supported); break; case PROP_BYTESTREAM: g_value_set_object (value, self->priv->bytestream); break; #ifdef ENABLE_JINGLE_FILE_TRANSFER case PROP_GTALK_FILE_COLLECTION: g_value_set_object (value, self->priv->gtalk_file_collection); break; #endif case PROP_SERVICE_NAME: g_value_set_string (value, self->priv->service_name); break; case PROP_METADATA: { /* We're fine with priv->metadata being NULL but dbus-glib * doesn't like iterating NULL as if it was a hash table. */ if (self->priv->metadata == NULL) { g_value_take_boxed (value, g_hash_table_new (g_str_hash, g_str_equal)); } else { g_value_set_boxed (value, self->priv->metadata); } } break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void gabble_file_transfer_channel_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { GabbleFileTransferChannel *self = GABBLE_FILE_TRANSFER_CHANNEL (object); switch (property_id) { case PROP_STATE: gabble_file_transfer_channel_set_state ( TP_SVC_CHANNEL_TYPE_FILE_TRANSFER (object), g_value_get_uint (value), TP_FILE_TRANSFER_STATE_CHANGE_REASON_NONE); break; case PROP_CONTENT_TYPE: g_free (self->priv->content_type); self->priv->content_type = g_value_dup_string (value); break; case PROP_FILENAME: g_free (self->priv->filename); self->priv->filename = g_value_dup_string (value); break; case PROP_SIZE: self->priv->size = g_value_get_uint64 (value); break; case PROP_CONTENT_HASH_TYPE: self->priv->content_hash_type = g_value_get_uint (value); break; case PROP_CONTENT_HASH: g_free (self->priv->content_hash); self->priv->content_hash = g_value_dup_string (value); break; case PROP_DESCRIPTION: g_free (self->priv->description); self->priv->description = g_value_dup_string (value); break; case PROP_DATE: self->priv->date = g_value_get_uint64 (value); break; case PROP_INITIAL_OFFSET: self->priv->initial_offset = g_value_get_uint64 (value); break; case PROP_FILE_COLLECTION: g_free (self->priv->file_collection); self->priv->file_collection = g_value_dup_string (value); break; case PROP_URI: g_assert (self->priv->uri == NULL); /* construct only */ self->priv->uri = g_value_dup_string (value); break; case PROP_RESUME_SUPPORTED: self->priv->resume_supported = g_value_get_boolean (value); break; case PROP_BYTESTREAM: set_bytestream (self, GABBLE_BYTESTREAM_IFACE (g_value_get_object (value))); break; #ifdef ENABLE_JINGLE_FILE_TRANSFER case PROP_GTALK_FILE_COLLECTION: set_gtalk_file_collection (self, GTALK_FILE_COLLECTION (g_value_get_object (value))); break; #endif case PROP_SERVICE_NAME: self->priv->service_name = g_value_dup_string (value); break; case PROP_METADATA: self->priv->metadata = g_value_dup_boxed (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void free_array (GArray *array) { g_array_unref (array); } static void connection_presences_updated_cb (GabblePresenceCache *cache, GArray *handles, GabbleFileTransferChannel *self) { TpBaseChannel *base = TP_BASE_CHANNEL (self); TpBaseConnection *base_conn = tp_base_channel_get_connection (base); GabbleConnection *conn = GABBLE_CONNECTION (base_conn); guint i; for (i = 0; i < handles->len ; i++) { TpHandle handle; handle = g_array_index (handles, TpHandle, i); if (handle == tp_base_channel_get_target_handle (base)) { GabblePresence *presence; presence = gabble_presence_cache_get ( conn->presence_cache, handle); if (presence == NULL || presence->status < GABBLE_PRESENCE_XA) { /* Contact is disconnected */ if (self->priv->state != TP_FILE_TRANSFER_STATE_COMPLETED && self->priv->state != TP_FILE_TRANSFER_STATE_CANCELLED) { DEBUG ("peer disconnected. FileTransfer is cancelled"); gabble_file_transfer_channel_set_state ( TP_SVC_CHANNEL_TYPE_FILE_TRANSFER (self), TP_FILE_TRANSFER_STATE_CANCELLED, TP_FILE_TRANSFER_STATE_CHANGE_REASON_REMOTE_STOPPED); } } } } } static void gabble_file_transfer_channel_constructed (GObject *obj) { GabbleFileTransferChannel *self = GABBLE_FILE_TRANSFER_CHANNEL (obj); TpBaseChannel *base = TP_BASE_CHANNEL (self); TpBaseConnection *base_conn = tp_base_channel_get_connection (base); GabbleConnection *conn = GABBLE_CONNECTION (base_conn); TpHandleRepoIface *contact_repo = tp_base_connection_get_handles ( base_conn, TP_HANDLE_TYPE_CONTACT); GArray *socket_access; TpSocketAccessControl access_control; /* Parent constructed chain */ void (*chain_up) (GObject *) = ((GObjectClass *) gabble_file_transfer_channel_parent_class)->constructed; if (chain_up != NULL) chain_up (obj); /* Initialise the available socket types hash table */ self->priv->available_socket_types = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, (GDestroyNotify) free_array); #ifdef GIBBER_TYPE_UNIX_TRANSPORT /* Socket_Address_Type_Unix */ socket_access = g_array_sized_new (FALSE, FALSE, sizeof (TpSocketAccessControl), 1); access_control = TP_SOCKET_ACCESS_CONTROL_LOCALHOST; g_array_append_val (socket_access, access_control); g_hash_table_insert (self->priv->available_socket_types, GUINT_TO_POINTER (TP_SOCKET_ADDRESS_TYPE_UNIX), socket_access); #endif /* Socket_Address_Type_IPv4 */ socket_access = g_array_sized_new (FALSE, FALSE, sizeof (TpSocketAccessControl), 1); access_control = TP_SOCKET_ACCESS_CONTROL_LOCALHOST; g_array_append_val (socket_access, access_control); g_hash_table_insert (self->priv->available_socket_types, GUINT_TO_POINTER (TP_SOCKET_ADDRESS_TYPE_IPV4), socket_access); /* Socket_Address_Type_IPv6 */ socket_access = g_array_sized_new (FALSE, FALSE, sizeof (TpSocketAccessControl), 1); access_control = TP_SOCKET_ACCESS_CONTROL_LOCALHOST; g_array_append_val (socket_access, access_control); g_hash_table_insert (self->priv->available_socket_types, GUINT_TO_POINTER (TP_SOCKET_ADDRESS_TYPE_IPV6), socket_access); gabble_signal_connect_weak (conn->presence_cache, "presences-updated", G_CALLBACK (connection_presences_updated_cb), obj); DEBUG ("New FT channel created: %s (contact: %s, initiator: %s, " "file: \"%s\", size: %" G_GUINT64_FORMAT ")", tp_base_channel_get_object_path (base), tp_handle_inspect (contact_repo, tp_base_channel_get_target_handle (base)), tp_handle_inspect (contact_repo, tp_base_channel_get_initiator (base)), self->priv->filename, self->priv->size); if (!tp_base_channel_is_requested (base)) /* Incoming transfer, URI has to be set by the handler */ g_assert (self->priv->uri == NULL); } static void gabble_file_transfer_channel_dispose (GObject *object); static void gabble_file_transfer_channel_finalize (GObject *object); static gboolean file_transfer_channel_properties_setter (GObject *object, GQuark interface, GQuark name, const GValue *value, gpointer setter_data, GError **error) { GabbleFileTransferChannel *self = (GabbleFileTransferChannel *) object; TpBaseChannel *base = TP_BASE_CHANNEL (self); g_return_val_if_fail (interface == TP_IFACE_QUARK_CHANNEL_TYPE_FILE_TRANSFER, FALSE); /* There is only one property with write access. So TpDBusPropertiesMixin * already checked this. */ g_assert (name == g_quark_from_static_string ("URI")); /* TpDBusPropertiesMixin already checked this */ g_assert (G_VALUE_HOLDS_STRING (value)); if (self->priv->uri != NULL) { g_set_error (error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT, "URI has already be set"); return FALSE; } if (tp_base_channel_is_requested (base)) { g_set_error (error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT, "Channel is not an incoming transfer"); return FALSE; } if (self->priv->state != TP_FILE_TRANSFER_STATE_PENDING) { g_set_error (error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT, "State is not pending; cannot set URI"); return FALSE; } self->priv->uri = g_value_dup_string (value); tp_svc_channel_type_file_transfer_emit_uri_defined (self, self->priv->uri); return TRUE; } static void gabble_file_transfer_channel_fill_immutable_properties (TpBaseChannel *chan, GHashTable *properties) { TpBaseChannelClass *cls = TP_BASE_CHANNEL_CLASS ( gabble_file_transfer_channel_parent_class); cls->fill_immutable_properties (chan, properties); tp_dbus_properties_mixin_fill_properties_hash ( G_OBJECT (chan), properties, TP_IFACE_CHANNEL_TYPE_FILE_TRANSFER, "State", TP_IFACE_CHANNEL_TYPE_FILE_TRANSFER, "ContentType", TP_IFACE_CHANNEL_TYPE_FILE_TRANSFER, "Filename", TP_IFACE_CHANNEL_TYPE_FILE_TRANSFER, "Size", TP_IFACE_CHANNEL_TYPE_FILE_TRANSFER, "ContentHashType", TP_IFACE_CHANNEL_TYPE_FILE_TRANSFER, "ContentHash", TP_IFACE_CHANNEL_TYPE_FILE_TRANSFER, "Description", TP_IFACE_CHANNEL_TYPE_FILE_TRANSFER, "Date", TP_IFACE_CHANNEL_TYPE_FILE_TRANSFER, "AvailableSocketTypes", TP_IFACE_CHANNEL_TYPE_FILE_TRANSFER, "TransferredBytes", TP_IFACE_CHANNEL_TYPE_FILE_TRANSFER, "InitialOffset", GABBLE_IFACE_CHANNEL_TYPE_FILETRANSFER_FUTURE, "FileCollection", TP_IFACE_CHANNEL_INTERFACE_FILE_TRANSFER_METADATA, "ServiceName", TP_IFACE_CHANNEL_INTERFACE_FILE_TRANSFER_METADATA, "Metadata", NULL); /* URI is immutable only for outgoing transfers */ if (tp_base_channel_is_requested (chan)) { tp_dbus_properties_mixin_fill_properties_hash (G_OBJECT (chan), properties, TP_IFACE_CHANNEL_TYPE_FILE_TRANSFER, "URI", NULL); } } static gchar * gabble_file_transfer_channel_get_object_path_suffix (TpBaseChannel *chan) { return g_strdup_printf ("FileTransferChannel/%p", chan); } static GPtrArray * gabble_file_transfer_channel_get_interfaces (TpBaseChannel *base) { GPtrArray *interfaces; interfaces = TP_BASE_CHANNEL_CLASS ( gabble_file_transfer_channel_parent_class)->get_interfaces (base); g_ptr_array_add (interfaces, GABBLE_IFACE_CHANNEL_TYPE_FILETRANSFER_FUTURE); g_ptr_array_add (interfaces, TP_IFACE_CHANNEL_INTERFACE_FILE_TRANSFER_METADATA); return interfaces; } static void gabble_file_transfer_channel_class_init ( GabbleFileTransferChannelClass *gabble_file_transfer_channel_class) { GObjectClass *object_class = G_OBJECT_CLASS ( gabble_file_transfer_channel_class); TpBaseChannelClass *base_class = TP_BASE_CHANNEL_CLASS ( gabble_file_transfer_channel_class); GParamSpec *param_spec; static TpDBusPropertiesMixinPropImpl file_props[] = { { "State", "state", NULL }, { "ContentType", "content-type", NULL }, { "Filename", "filename", NULL }, { "Size", "size", NULL }, { "ContentHashType", "content-hash-type", NULL }, { "ContentHash", "content-hash", NULL }, { "Description", "description", NULL }, { "AvailableSocketTypes", "available-socket-types", NULL }, { "TransferredBytes", "transferred-bytes", NULL }, { "InitialOffset", "initial-offset", NULL }, { "Date", "date", NULL }, { "URI", "uri", NULL }, { NULL } }; static TpDBusPropertiesMixinPropImpl file_future_props[] = { { "FileCollection", "file-collection", NULL }, { NULL } }; static TpDBusPropertiesMixinPropImpl file_metadata_props[] = { { "ServiceName", "service-name", NULL }, { "Metadata", "metadata", NULL }, { NULL } }; static TpDBusPropertiesMixinIfaceImpl prop_interfaces[] = { { TP_IFACE_CHANNEL_TYPE_FILE_TRANSFER, tp_dbus_properties_mixin_getter_gobject_properties, file_transfer_channel_properties_setter, file_props }, { GABBLE_IFACE_CHANNEL_TYPE_FILETRANSFER_FUTURE, tp_dbus_properties_mixin_getter_gobject_properties, NULL, file_future_props }, { TP_IFACE_CHANNEL_INTERFACE_FILE_TRANSFER_METADATA, tp_dbus_properties_mixin_getter_gobject_properties, NULL, file_metadata_props }, { NULL } }; g_type_class_add_private (gabble_file_transfer_channel_class, sizeof (GabbleFileTransferChannelPrivate)); object_class->dispose = gabble_file_transfer_channel_dispose; object_class->finalize = gabble_file_transfer_channel_finalize; object_class->constructed = gabble_file_transfer_channel_constructed; object_class->get_property = gabble_file_transfer_channel_get_property; object_class->set_property = gabble_file_transfer_channel_set_property; base_class->channel_type = TP_IFACE_CHANNEL_TYPE_FILE_TRANSFER; base_class->target_handle_type = TP_HANDLE_TYPE_CONTACT; base_class->get_interfaces = gabble_file_transfer_channel_get_interfaces; base_class->close = gabble_file_transfer_channel_close; base_class->fill_immutable_properties = gabble_file_transfer_channel_fill_immutable_properties; base_class->get_object_path_suffix = gabble_file_transfer_channel_get_object_path_suffix; param_spec = g_param_spec_uint ( "state", "TpFileTransferState state", "State of the file transfer in this channel", 0, NUM_TP_FILE_TRANSFER_STATES, TP_FILE_TRANSFER_STATE_NONE, G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, PROP_STATE, param_spec); param_spec = g_param_spec_string ( "content-type", "gchar *content-type", "ContentType of the file", "application/octet-stream", G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, PROP_CONTENT_TYPE, param_spec); param_spec = g_param_spec_string ( "filename", "gchar *filename", "Name of the file", "", G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, PROP_FILENAME, param_spec); param_spec = g_param_spec_uint64 ( "size", "guint size", "Size of the file in bytes", 0, G_MAXUINT64, GABBLE_UNDEFINED_FILE_SIZE, G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, PROP_SIZE, param_spec); param_spec = g_param_spec_uint ( "content-hash-type", "TpFileHashType content-hash-type", "Hash type", 0, NUM_TP_FILE_HASH_TYPES, TP_FILE_HASH_TYPE_NONE, G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, PROP_CONTENT_HASH_TYPE, param_spec); param_spec = g_param_spec_string ( "content-hash", "gchar *content-hash", "Hash of the file contents", "", G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, PROP_CONTENT_HASH, param_spec); param_spec = g_param_spec_string ( "description", "gchar *description", "Description of the file", "", G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, PROP_DESCRIPTION, param_spec); param_spec = g_param_spec_boxed ( "available-socket-types", "GabbleSupportedSocketMap available-socket-types", "Available socket types", TP_HASH_TYPE_SUPPORTED_SOCKET_MAP, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, PROP_AVAILABLE_SOCKET_TYPES, param_spec); param_spec = g_param_spec_uint64 ( "transferred-bytes", "guint64 transferred-bytes", "Bytes transferred", 0, G_MAXUINT64, 0, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, PROP_TRANSFERRED_BYTES, param_spec); param_spec = g_param_spec_uint64 ( "initial-offset", "guint64 initial_offset", "Offset set at the beginning of the transfer", 0, G_MAXUINT64, 0, G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, PROP_INITIAL_OFFSET, param_spec); param_spec = g_param_spec_uint64 ( "date", "Epoch time", "the last modification time of the file being transferred", 0, G_MAXUINT64, 0, G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, PROP_DATE, param_spec); param_spec = g_param_spec_object ( "bytestream", "Object implementing the GabbleBytestreamIface interface", "Bytestream object used to send the file", G_TYPE_OBJECT, G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, PROP_BYTESTREAM, param_spec); #ifdef ENABLE_JINGLE_FILE_TRANSFER param_spec = g_param_spec_object ( "gtalk-file-collection", "GTalkFileCollection object for gtalk-compatible file transfer", "GTalk compatible file transfer collection", G_TYPE_OBJECT, G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, PROP_GTALK_FILE_COLLECTION, param_spec); #endif param_spec = g_param_spec_boolean ( "resume-supported", "resume is supported", "TRUE if resume is supported on this file transfer channel", FALSE, G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, PROP_RESUME_SUPPORTED, param_spec); param_spec = g_param_spec_string ( "file-collection", "gchar *file_colletion", "Token identifying a collection of files", "", G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, PROP_FILE_COLLECTION, param_spec); param_spec = g_param_spec_string ( "uri", "URI", "URI of the file being transferred", NULL, G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, PROP_URI, param_spec); param_spec = g_param_spec_string ("service-name", "ServiceName", "The Metadata.ServiceName property of this channel", "", G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, PROP_SERVICE_NAME, param_spec); param_spec = g_param_spec_boxed ("metadata", "Metadata", "The Metadata.Metadata property of this channel", TP_HASH_TYPE_METADATA, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, PROP_METADATA, param_spec); gabble_file_transfer_channel_class->dbus_props_class.interfaces = prop_interfaces; tp_dbus_properties_mixin_class_init (object_class, G_STRUCT_OFFSET (GabbleFileTransferChannelClass, dbus_props_class)); } void gabble_file_transfer_channel_dispose (GObject *object) { GabbleFileTransferChannel *self = GABBLE_FILE_TRANSFER_CHANNEL (object); if (self->priv->dispose_has_run) return; DEBUG ("dispose called"); self->priv->dispose_has_run = TRUE; if (self->priv->progress_timer != 0) { g_source_remove (self->priv->progress_timer); self->priv->progress_timer = 0; } close_session_and_transport (self); /* release any references held by the object here */ if (G_OBJECT_CLASS (gabble_file_transfer_channel_parent_class)->dispose) G_OBJECT_CLASS (gabble_file_transfer_channel_parent_class)->dispose (object); } static void erase_socket (GabbleFileTransferChannel *self) { GArray *array; if (self->priv->socket_type != TP_SOCKET_ADDRESS_TYPE_UNIX) /* only UNIX sockets have to be erased */ return; if (self->priv->socket_address == NULL) return; array = g_value_get_boxed (self->priv->socket_address); if (g_unlink (array->data) != 0) { DEBUG ("unlink failed: %s", g_strerror (errno)); } } static void gabble_file_transfer_channel_finalize (GObject *object) { GabbleFileTransferChannel *self = GABBLE_FILE_TRANSFER_CHANNEL (object); /* free any data held directly by the object here */ erase_socket (self); g_free (self->priv->filename); if (self->priv->socket_address != NULL) tp_g_value_slice_free (self->priv->socket_address); g_free (self->priv->content_type); g_free (self->priv->content_hash); g_free (self->priv->description); g_hash_table_unref (self->priv->available_socket_types); g_free (self->priv->file_collection); g_free (self->priv->uri); g_free (self->priv->service_name); if (self->priv->metadata != NULL) g_hash_table_unref (self->priv->metadata); G_OBJECT_CLASS (gabble_file_transfer_channel_parent_class)->finalize (object); } static void close_session_and_transport (GabbleFileTransferChannel *self) { DEBUG ("Closing session and transport"); #ifdef ENABLE_JINGLE_FILE_TRANSFER if (self->priv->gtalk_file_collection != NULL) gtalk_file_collection_terminate (self->priv->gtalk_file_collection, self); tp_clear_object (&self->priv->gtalk_file_collection); #endif if (self->priv->bytestream != NULL) gabble_bytestream_iface_close (self->priv->bytestream, NULL); tp_clear_object (&self->priv->bytestream); tp_clear_object (&self->priv->listener); tp_clear_object (&self->priv->transport); } static gboolean setup_local_socket (GabbleFileTransferChannel *self, TpSocketAddressType address_type, TpSocketAccessControl access_control, const GValue *access_control_param); static void gabble_file_transfer_channel_set_state ( TpSvcChannelTypeFileTransfer *iface, TpFileTransferState state, TpFileTransferStateChangeReason reason) { GabbleFileTransferChannel *self = GABBLE_FILE_TRANSFER_CHANNEL (iface); if (self->priv->state == state) return; self->priv->state = state; tp_svc_channel_type_file_transfer_emit_file_transfer_state_changed (iface, state, reason); } static gboolean check_address_and_access_control (GabbleFileTransferChannel *self, TpSocketAddressType address_type, TpSocketAccessControl access_control, const GValue *access_control_param, GError **error) { GArray *access_arr; guint i; /* Do we support this AddressType? */ access_arr = g_hash_table_lookup (self->priv->available_socket_types, GUINT_TO_POINTER (address_type)); if (access_arr == NULL) { g_set_error (error, TP_ERROR, TP_ERROR_NOT_IMPLEMENTED, "AddressType %u is not implemented", address_type); return FALSE; } /* Do we support this AccessControl? */ for (i = 0; i < access_arr->len; i++) { TpSocketAccessControl control; control = g_array_index (access_arr, TpSocketAccessControl, i); if (control == access_control) return TRUE; } g_set_error (error, TP_ERROR, TP_ERROR_NOT_IMPLEMENTED, "AccesControl %u is not implemented with AddressType %u", access_control, address_type); return FALSE; } static void channel_open (GabbleFileTransferChannel *self) { DEBUG ("Channel open"); /* This is needed in case the ProvideFile wasn't called yet, to know if we should go into OPEN state when ProvideFile gets called. */ self->priv->channel_opened = TRUE; if (self->priv->socket_address != NULL) { /* ProvideFile has already been called. Channel is Open */ tp_svc_channel_type_file_transfer_emit_initial_offset_defined (self, self->priv->initial_offset); gabble_file_transfer_channel_set_state ( TP_SVC_CHANNEL_TYPE_FILE_TRANSFER (self), TP_FILE_TRANSFER_STATE_OPEN, TP_FILE_TRANSFER_STATE_CHANGE_REASON_NONE); if (self->priv->transport != NULL) gibber_transport_block_receiving (self->priv->transport, FALSE); } else { /* Client has to call ProvideFile to open the channel */ gabble_file_transfer_channel_set_state ( TP_SVC_CHANNEL_TYPE_FILE_TRANSFER (self), TP_FILE_TRANSFER_STATE_ACCEPTED, TP_FILE_TRANSFER_STATE_CHANGE_REASON_NONE); } } static void bytestream_closed (GabbleFileTransferChannel *self) { if (self->priv->state != TP_FILE_TRANSFER_STATE_COMPLETED && self->priv->state != TP_FILE_TRANSFER_STATE_CANCELLED) { gboolean receiver = !tp_base_channel_is_requested ( TP_BASE_CHANNEL (self)); /* Something did wrong */ gabble_file_transfer_channel_set_state ( TP_SVC_CHANNEL_TYPE_FILE_TRANSFER (self), TP_FILE_TRANSFER_STATE_CANCELLED, receiver ? TP_FILE_TRANSFER_STATE_CHANGE_REASON_LOCAL_ERROR : TP_FILE_TRANSFER_STATE_CHANGE_REASON_REMOTE_ERROR); } } static void bytestream_state_changed_cb (GabbleBytestreamIface *bytestream, GabbleBytestreamState state, gpointer user_data) { GabbleFileTransferChannel *self = GABBLE_FILE_TRANSFER_CHANNEL (user_data); if (state == GABBLE_BYTESTREAM_STATE_OPEN) { channel_open (self); } else if (state == GABBLE_BYTESTREAM_STATE_CLOSED) { bytestream_closed (self); } } static void bytestream_write_blocked_cb (GabbleBytestreamIface *bytestream, gboolean blocked, GabbleFileTransferChannel *self); static gboolean set_bytestream (GabbleFileTransferChannel *self, GabbleBytestreamIface *bytestream) { if (bytestream == NULL) return FALSE; g_return_val_if_fail (self->priv->bytestream == NULL, FALSE); #ifdef ENABLE_JINGLE_FILE_TRANSFER g_return_val_if_fail (self->priv->gtalk_file_collection == NULL, FALSE); #endif DEBUG ("Setting bytestream to %p", bytestream); self->priv->bytestream = g_object_ref (bytestream); gabble_signal_connect_weak (bytestream, "state-changed", G_CALLBACK (bytestream_state_changed_cb), G_OBJECT (self)); gabble_signal_connect_weak (bytestream, "write-blocked", G_CALLBACK (bytestream_write_blocked_cb), G_OBJECT (self)); return TRUE; } #ifdef ENABLE_JINGLE_FILE_TRANSFER static gboolean set_gtalk_file_collection ( GabbleFileTransferChannel *self, GTalkFileCollection *gtalk_file_collection) { if (gtalk_file_collection == NULL) return FALSE; g_return_val_if_fail (self->priv->bytestream == NULL, FALSE); g_return_val_if_fail (self->priv->gtalk_file_collection == NULL, FALSE); self->priv->gtalk_file_collection = g_object_ref (gtalk_file_collection); /* No need to listen to any signals, the GTalkFileCollection will call our callbacks on his own */ return TRUE; } #endif static void bytestream_negotiate_cb (GabbleBytestreamIface *bytestream, WockyStanza *msg, GObject *object, gpointer user_data) { GabbleFileTransferChannel *self = GABBLE_FILE_TRANSFER_CHANNEL (user_data); WockyNode *si; WockyNode *file = NULL; if (bytestream == NULL) { DEBUG ("receiver refused file offer"); gabble_file_transfer_channel_set_state ( TP_SVC_CHANNEL_TYPE_FILE_TRANSFER (self), TP_FILE_TRANSFER_STATE_CANCELLED, TP_FILE_TRANSFER_STATE_CHANGE_REASON_REMOTE_STOPPED); return; } si = wocky_node_get_child_ns (wocky_stanza_get_top_node (msg), "si", NS_SI); if (si != NULL) file = wocky_node_get_child_ns (si, "file", NULL); if (file != NULL) { WockyNode *range; range = wocky_node_get_child (file, "range"); if (range != NULL) { const gchar *offset_str; offset_str = wocky_node_get_attribute (range, "offset"); if (offset_str != NULL) { self->priv->initial_offset = g_ascii_strtoull (offset_str, NULL, 0); } } } DEBUG ("receiver accepted file offer (offset: %" G_GUINT64_FORMAT ")", self->priv->initial_offset); set_bytestream (self, bytestream); } static void add_metadata_forms (GabbleFileTransferChannel *self, WockyNode *file) { if (!tp_str_empty (self->priv->service_name)) { wocky_node_add_build (file, '(', "x", ':', NS_X_DATA, '@', "type", "result", '(', "field", '@', "var", "FORM_TYPE", '@', "type", "hidden", '(', "value", '$', NS_TP_FT_METADATA_SERVICE, ')', ')', '(', "field", '@', "var", "ServiceName", '(', "value", '$', self->priv->service_name, ')', ')', ')', NULL); } if (self->priv->metadata != NULL && g_hash_table_size (self->priv->metadata) > 0) { WockyNode *x; GHashTableIter iter; gpointer key, val; wocky_node_add_build (file, '(', "x", ':', NS_X_DATA, '*', &x, '@', "type", "result", '(', "field", '@', "var", "FORM_TYPE", '@', "type", "hidden", '(', "value", '$', NS_TP_FT_METADATA, ')', ')', ')', NULL); g_hash_table_iter_init (&iter, self->priv->metadata); while (g_hash_table_iter_next (&iter, &key, &val)) { const gchar * const *values = val; WockyNode *field = wocky_node_add_child (x, "field"); wocky_node_set_attribute (field, "var", key); for (; values != NULL && *values != NULL; values++) { wocky_node_add_child_with_content (field, "value", *values); } } } } static void offer_bytestream (GabbleFileTransferChannel *self, const gchar *jid, const gchar *resource) { GabbleConnection *conn = GABBLE_CONNECTION (tp_base_channel_get_connection ( TP_BASE_CHANNEL (self))); WockyStanza *msg; WockyNode *si_node, *file_node; gchar *stream_id, *size_str, *full_jid; if (resource) full_jid = g_strdup_printf ("%s/%s", jid, resource); else full_jid = g_strdup (jid); DEBUG ("Offering SI Bytestream file transfer to %s", full_jid); /* Outgoing FT , we'll need SOCK5 proxies */ gabble_bytestream_factory_query_socks5_proxies ( conn->bytestream_factory); stream_id = gabble_bytestream_factory_generate_stream_id (); msg = gabble_bytestream_factory_make_stream_init_iq (full_jid, stream_id, NS_FILE_TRANSFER); si_node = wocky_node_get_child_ns ( wocky_stanza_get_top_node (msg), "si", NS_SI); g_assert (si_node != NULL); size_str = g_strdup_printf ("%" G_GUINT64_FORMAT, self->priv->size); file_node = wocky_node_add_child_ns (si_node, "file", NS_FILE_TRANSFER); wocky_node_set_attributes (file_node, "name", self->priv->filename, "size", size_str, "mime-type", self->priv->content_type, NULL); add_metadata_forms (self, file_node); if (self->priv->content_hash != NULL) wocky_node_set_attribute (file_node, "hash", self->priv->content_hash); if (self->priv->date != 0) { time_t t; struct tm *tm; char date_str[21]; t = (time_t) self->priv->date; tm = gmtime (&t); #ifdef G_OS_WIN32 strftime (date_str, sizeof (date_str), "%Y-%m-%dT%H:%M:%SZ", tm); #else strftime (date_str, sizeof (date_str), "%FT%H:%M:%SZ", tm); #endif wocky_node_set_attribute (file_node, "date", date_str); } wocky_node_add_child_with_content (file_node, "desc", self->priv->description); /* we support resume */ wocky_node_add_child (file_node, "range"); gabble_bytestream_factory_negotiate_stream ( conn->bytestream_factory, msg, stream_id, bytestream_negotiate_cb, self, G_OBJECT (self)); g_object_unref (msg); g_free (stream_id); g_free (size_str); g_free (full_jid); } #ifdef ENABLE_JINGLE_FILE_TRANSFER void gabble_file_transfer_channel_gtalk_file_collection_state_changed ( GabbleFileTransferChannel *self, GTalkFileCollectionState state, gboolean local_terminator) { DEBUG ("gtalk ft state changed to %d", state); switch (state) { case GTALK_FILE_COLLECTION_STATE_PENDING: gabble_file_transfer_channel_set_state ( TP_SVC_CHANNEL_TYPE_FILE_TRANSFER (self), TP_FILE_TRANSFER_STATE_PENDING, TP_FILE_TRANSFER_STATE_CHANGE_REASON_NONE); break; case GTALK_FILE_COLLECTION_STATE_ACCEPTED: if (self->priv->state == TP_FILE_TRANSFER_STATE_PENDING) { gabble_file_transfer_channel_set_state ( TP_SVC_CHANNEL_TYPE_FILE_TRANSFER (self), TP_FILE_TRANSFER_STATE_ACCEPTED, TP_FILE_TRANSFER_STATE_CHANGE_REASON_NONE); } break; case GTALK_FILE_COLLECTION_STATE_OPEN: channel_open (self); break; case GTALK_FILE_COLLECTION_STATE_TERMINATED: if (self->priv->state != TP_FILE_TRANSFER_STATE_COMPLETED && self->priv->state != TP_FILE_TRANSFER_STATE_CANCELLED) { gabble_file_transfer_channel_set_state ( TP_SVC_CHANNEL_TYPE_FILE_TRANSFER (self), TP_FILE_TRANSFER_STATE_CANCELLED, local_terminator ? TP_FILE_TRANSFER_STATE_CHANGE_REASON_LOCAL_STOPPED: TP_FILE_TRANSFER_STATE_CHANGE_REASON_REMOTE_STOPPED); } close_session_and_transport (self); break; case GTALK_FILE_COLLECTION_STATE_ERROR: case GTALK_FILE_COLLECTION_STATE_CONNECTION_FAILED: gabble_file_transfer_channel_set_state ( TP_SVC_CHANNEL_TYPE_FILE_TRANSFER (self), TP_FILE_TRANSFER_STATE_CANCELLED, TP_FILE_TRANSFER_STATE_CHANGE_REASON_LOCAL_ERROR); close_session_and_transport (self); break; case GTALK_FILE_COLLECTION_STATE_COMPLETED: gabble_file_transfer_channel_set_state ( TP_SVC_CHANNEL_TYPE_FILE_TRANSFER (self), TP_FILE_TRANSFER_STATE_COMPLETED, TP_FILE_TRANSFER_STATE_CHANGE_REASON_NONE); if (self->priv->transport && gibber_transport_buffer_is_empty (self->priv->transport)) gibber_transport_disconnect (self->priv->transport); break; } } static gboolean offer_gtalk_file_transfer (GabbleFileTransferChannel *self, const gchar *full_jid, GError **error) { TpBaseChannel *base = TP_BASE_CHANNEL (self); GabbleConnection *conn = GABBLE_CONNECTION ( tp_base_channel_get_connection (base)); WockyJingleFactory *jf; GTalkFileCollection *gtalk_file_collection; DEBUG ("Offering Gtalk file transfer to %s", full_jid); jf = gabble_jingle_mint_get_factory (conn->jingle_mint); g_return_val_if_fail (jf != NULL, FALSE); gtalk_file_collection = gtalk_file_collection_new (self, jf, tp_base_channel_get_target_handle (base), full_jid); g_return_val_if_fail (gtalk_file_collection != NULL, FALSE); set_gtalk_file_collection (self, gtalk_file_collection); gtalk_file_collection_initiate (self->priv->gtalk_file_collection, self); /* We would have gotten a set_gtalk_file_collection so we already hold an additional reference to the object, so we can drop the reference we got from the gtalk_file_collection_new. If we didn't get our set_gtalk_file_collection called, then the ft manager doesn't handle us, so it's best to just destroy it anyways */ g_object_unref (gtalk_file_collection); return TRUE; } #endif gboolean gabble_file_transfer_channel_offer_file (GabbleFileTransferChannel *self, GError **error) { TpBaseChannel *base = TP_BASE_CHANNEL (self); TpBaseConnection *base_conn = tp_base_channel_get_connection (base); GabbleConnection *conn = GABBLE_CONNECTION (base_conn); GabblePresence *presence; gboolean result; TpHandleRepoIface *contact_repo, *room_repo; const gchar *jid; gboolean si = FALSE; gboolean use_si = FALSE; const gchar *si_resource = NULL; #ifdef ENABLE_JINGLE_FILE_TRANSFER gboolean jingle_share = FALSE; const gchar *share_resource = NULL; #endif 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); #ifdef ENABLE_JINGLE_FILE_TRANSFER g_return_val_if_fail (self->priv->gtalk_file_collection == NULL, FALSE); #endif presence = gabble_presence_cache_get (conn->presence_cache, tp_base_channel_get_target_handle (base)); if (presence == NULL) { DEBUG ("can't find contact's presence"); g_set_error (error, TP_ERROR, TP_ERROR_OFFLINE, "can't find contact's presence"); return FALSE; } if (self->priv->service_name != NULL || self->priv->metadata != NULL) { if (!gabble_presence_has_cap (presence, NS_TP_FT_METADATA)) { DEBUG ("trying to use Metadata properties on a contact " "who doesn't support it"); g_set_error (error, TP_ERROR, TP_ERROR_NOT_CAPABLE, "The specified contact does not support the " "Metadata extension; you should ensure both ServiceName and " "Metadata properties are not present in the channel " "request"); return FALSE; } } contact_repo = tp_base_connection_get_handles (base_conn, TP_HANDLE_TYPE_CONTACT); room_repo = tp_base_connection_get_handles (base_conn, TP_HANDLE_TYPE_ROOM); jid = tp_handle_inspect (contact_repo, tp_base_channel_get_target_handle (base)); if (gabble_get_room_handle_from_jid (room_repo, jid) == 0) { /* Not a MUC jid, need to get a resource */ /* FIXME: should we check for SI, bytestreams and/or IBB too? * http://bugs.freedesktop.org/show_bug.cgi?id=23777 */ si_resource = gabble_presence_pick_resource_by_caps (presence, 0, gabble_capability_set_predicate_has, NS_FILE_TRANSFER); si = (si_resource != NULL); #ifdef ENABLE_JINGLE_FILE_TRANSFER share_resource = gabble_presence_pick_resource_by_caps (presence, 0, gabble_capability_set_predicate_has, NS_GOOGLE_FEAT_SHARE); jingle_share = (share_resource != NULL); #endif } else { /* MUC jid, we already have the full jid */ si = gabble_presence_has_cap (presence, NS_FILE_TRANSFER); #ifdef ENABLE_JINGLE_FILE_TRANSFER jingle_share = gabble_presence_has_cap (presence, NS_GOOGLE_FEAT_SHARE); #endif } /* Use bytestream if we have SI, but no jingle-share or if we have SI and jingle-share but we have no google relay token */ #ifdef ENABLE_JINGLE_FILE_TRANSFER use_si = si && (!jingle_share || wocky_jingle_info_get_google_relay_token ( gabble_jingle_mint_get_info (conn->jingle_mint)) == NULL); #else use_si = si; #endif if (use_si) { offer_bytestream (self, jid, si_resource); result = TRUE; } #ifdef ENABLE_JINGLE_FILE_TRANSFER else if (jingle_share) { gchar *full_jid = gabble_peer_to_jid (conn, tp_base_channel_get_target_handle (base), share_resource); result = offer_gtalk_file_transfer (self, full_jid, error); g_free (full_jid); } #endif else { DEBUG ("contact doesn't have file transfer capabilities"); g_set_error (error, TP_ERROR, TP_ERROR_NOT_CAPABLE, "contact doesn't have file transfer capabilities"); result = FALSE; } return result; } static void emit_progress_update (GabbleFileTransferChannel *self) { TpSvcChannelTypeFileTransfer *iface = TP_SVC_CHANNEL_TYPE_FILE_TRANSFER (self); g_get_current_time (&self->priv->last_transferred_bytes_emitted); tp_svc_channel_type_file_transfer_emit_transferred_bytes_changed ( iface, self->priv->transferred_bytes); if (self->priv->progress_timer != 0) { g_source_remove (self->priv->progress_timer); self->priv->progress_timer = 0; } } static gboolean emit_progress_update_cb (gpointer user_data) { GabbleFileTransferChannel *self = GABBLE_FILE_TRANSFER_CHANNEL (user_data); emit_progress_update (self); return FALSE; } static void transferred_chunk (GabbleFileTransferChannel *self, guint64 count) { GTimeVal timeval; gint interval; self->priv->transferred_bytes += count; if (self->priv->transferred_bytes + self->priv->initial_offset >= self->priv->size) { /* If the transfer has finished send an update right away */ emit_progress_update (self); return; } if (self->priv->progress_timer != 0) { /* A progress update signal is already scheduled */ return; } /* Only emit the TransferredBytes signal if it has been one second since its * last emission. */ g_get_current_time (&timeval); interval = timeval.tv_sec - self->priv->last_transferred_bytes_emitted.tv_sec; if (interval > 1) { /* At least more then a second apart, emit right away */ emit_progress_update (self); return; } /* Convert interval to milliseconds and calculate it more precisely */ interval *= 1000; interval += (timeval.tv_usec - self->priv->last_transferred_bytes_emitted.tv_usec)/1000; /* Protect against clock skew, if the interval is negative the worst thing * that can happen is that we wait an extra second before emitting the signal */ interval = ABS (interval); if (interval > 1000) emit_progress_update (self); else self->priv->progress_timer = g_timeout_add (1000 - interval, emit_progress_update_cb, self); } static void data_received_cb (GabbleFileTransferChannel *self, const guint8 *data, guint len) { GError *error = NULL; g_assert (self->priv->transport != NULL); if (!gibber_transport_send (self->priv->transport, data, len, &error)) { DEBUG ("sending to transport failed: %s", error->message); g_error_free (error); gabble_file_transfer_channel_set_state ( TP_SVC_CHANNEL_TYPE_FILE_TRANSFER (self), TP_FILE_TRANSFER_STATE_CANCELLED, TP_FILE_TRANSFER_STATE_CHANGE_REASON_LOCAL_ERROR); return; } transferred_chunk (self, (guint64) len); if (self->priv->bytestream != NULL && self->priv->transferred_bytes + self->priv->initial_offset >= self->priv->size) { DEBUG ("Received all the file. Transfer is complete"); gabble_file_transfer_channel_set_state ( TP_SVC_CHANNEL_TYPE_FILE_TRANSFER (self), TP_FILE_TRANSFER_STATE_COMPLETED, TP_FILE_TRANSFER_STATE_CHANGE_REASON_NONE); if (gibber_transport_buffer_is_empty (self->priv->transport)) gibber_transport_disconnect (self->priv->transport); return; } if (!gibber_transport_buffer_is_empty (self->priv->transport)) { /* We don't want to send more data while the buffer isn't empty */ if (self->priv->bytestream != NULL) gabble_bytestream_iface_block_reading (self->priv->bytestream, TRUE); #ifdef ENABLE_JINGLE_FILE_TRANSFER else if (self->priv->gtalk_file_collection != NULL) gtalk_file_collection_block_reading (self->priv->gtalk_file_collection, self, TRUE); #endif } } #ifdef ENABLE_JINGLE_FILE_TRANSFER void gabble_file_transfer_channel_gtalk_file_collection_data_received ( GabbleFileTransferChannel *self, const gchar *data, guint len) { data_received_cb (self, (const guint8 *) data, len); } #endif static void bytestream_data_received_cb (GabbleBytestreamIface *stream, TpHandle sender, GString *data, gpointer user_data) { GabbleFileTransferChannel *self = GABBLE_FILE_TRANSFER_CHANNEL (user_data); data_received_cb (self, (const guint8 *) data->str, data->len); } static void augment_si_reply (WockyNode *si, gpointer user_data) { GabbleFileTransferChannel *self = GABBLE_FILE_TRANSFER_CHANNEL (user_data); WockyNode *file; file = wocky_node_add_child_ns (si, "file", NS_FILE_TRANSFER); if (self->priv->initial_offset != 0) { WockyNode *range; gchar *offset_str; range = wocky_node_add_child (file, "range"); offset_str = g_strdup_printf ("%" G_GUINT64_FORMAT, self->priv->initial_offset); wocky_node_set_attribute (range, "offset", offset_str); /* Don't set "length" attribute as the default is the length of the file * from offset to the end which is what we want when resuming a FT. */ g_free (offset_str); } } /** * gabble_file_transfer_channel_accept_file * * Implements D-Bus method AcceptFile * on interface org.freedesktop.Telepathy.Channel.Type.FileTransfer */ static void gabble_file_transfer_channel_accept_file (TpSvcChannelTypeFileTransfer *iface, guint address_type, guint access_control, const GValue *access_control_param, guint64 offset, DBusGMethodInvocation *context) { GabbleFileTransferChannel *self = GABBLE_FILE_TRANSFER_CHANNEL (iface); TpBaseChannel *base = TP_BASE_CHANNEL (self); GError *error = NULL; if (tp_base_channel_is_requested (base)) { g_set_error (&error, TP_ERROR, TP_ERROR_NOT_AVAILABLE, "Channel is not an incoming transfer"); dbus_g_method_return_error (context, error); g_error_free (error); return; } if (self->priv->state != TP_FILE_TRANSFER_STATE_PENDING) { g_set_error (&error, TP_ERROR, TP_ERROR_NOT_AVAILABLE, "State is not pending; cannot accept file"); dbus_g_method_return_error (context, error); g_error_free (error); return; } if (!check_address_and_access_control (self, address_type, access_control, access_control_param, &error)) { dbus_g_method_return_error (context, error); g_error_free (error); return; } if (!setup_local_socket (self, address_type, access_control, access_control_param)) { DEBUG ("Could not set up local socket"); g_set_error (&error, TP_ERROR, TP_ERROR_NOT_AVAILABLE, "Could not set up local socket"); dbus_g_method_return_error (context, error); g_error_free (error); return; } gabble_file_transfer_channel_set_state (iface, TP_FILE_TRANSFER_STATE_ACCEPTED, TP_FILE_TRANSFER_STATE_CHANGE_REASON_REQUESTED); tp_svc_channel_type_file_transfer_return_from_accept_file (context, self->priv->socket_address); if (self->priv->resume_supported) { self->priv->initial_offset = offset; } else { DEBUG ("Resume is not supported on this file transfer"); self->priv->initial_offset = 0; } if (self->priv->bytestream != NULL) { gabble_signal_connect_weak (self->priv->bytestream, "data-received", G_CALLBACK (bytestream_data_received_cb), G_OBJECT (self)); /* Block the bytestream while the user is not connected to the socket */ gabble_bytestream_iface_block_reading (self->priv->bytestream, TRUE); /* channel state will change to open once the bytestream is open */ gabble_bytestream_iface_accept (self->priv->bytestream, augment_si_reply, self); } #ifdef ENABLE_JINGLE_FILE_TRANSFER else if (self->priv->gtalk_file_collection != NULL) { /* Block the gtalk ft stream while the user is not connected to the socket */ gtalk_file_collection_block_reading (self->priv->gtalk_file_collection, self, TRUE); gtalk_file_collection_accept (self->priv->gtalk_file_collection, self); } #endif else { g_assert_not_reached (); } } /** * gabble_file_transfer_channel_provide_file * * Implements D-Bus method ProvideFile * on interface org.freedesktop.Telepathy.Channel.Type.FileTransfer */ static void gabble_file_transfer_channel_provide_file ( TpSvcChannelTypeFileTransfer *iface, guint address_type, guint access_control, const GValue *access_control_param, DBusGMethodInvocation *context) { GabbleFileTransferChannel *self = GABBLE_FILE_TRANSFER_CHANNEL (iface); GError *error = NULL; if (!tp_base_channel_is_requested (TP_BASE_CHANNEL (self))) { g_set_error (&error, TP_ERROR, TP_ERROR_NOT_AVAILABLE, "Channel is not an outgoing transfer"); dbus_g_method_return_error (context, error); g_error_free (error); return; } if (self->priv->state != TP_FILE_TRANSFER_STATE_PENDING && self->priv->state != TP_FILE_TRANSFER_STATE_ACCEPTED) { g_set_error (&error, TP_ERROR, TP_ERROR_NOT_AVAILABLE, "State is not pending or accepted; cannot provide file"); dbus_g_method_return_error (context, error); g_error_free (error); return; } if (self->priv->socket_address != NULL) { g_set_error (&error, TP_ERROR, TP_ERROR_NOT_AVAILABLE, "ProvideFile has already been called for this channel"); dbus_g_method_return_error (context, error); g_error_free (error); return; } if (!check_address_and_access_control (self, address_type, access_control, access_control_param, &error)) { dbus_g_method_return_error (context, error); g_error_free (error); return; } if (!setup_local_socket (self, address_type, access_control, access_control_param)) { DEBUG ("Could not set up local socket"); g_set_error (&error, TP_ERROR, TP_ERROR_NOT_AVAILABLE, "Could not set up local socket"); dbus_g_method_return_error (context, error); g_error_free (error); return; } if (self->priv->channel_opened) { /* Remote already accepted the file. Channel is Open. * If not channel stay Pending. */ tp_svc_channel_type_file_transfer_emit_initial_offset_defined (self, self->priv->initial_offset); gabble_file_transfer_channel_set_state (iface, TP_FILE_TRANSFER_STATE_OPEN, TP_FILE_TRANSFER_STATE_CHANGE_REASON_REQUESTED); } tp_svc_channel_type_file_transfer_return_from_provide_file (context, self->priv->socket_address); } static void file_transfer_iface_init (gpointer g_iface, gpointer iface_data) { TpSvcChannelTypeFileTransferClass *klass = (TpSvcChannelTypeFileTransferClass *) g_iface; #define IMPLEMENT(x) tp_svc_channel_type_file_transfer_implement_##x (\ klass, gabble_file_transfer_channel_##x) IMPLEMENT (accept_file); IMPLEMENT (provide_file); #undef IMPLEMENT } #ifdef GIBBER_TYPE_UNIX_TRANSPORT static gchar * get_local_unix_socket_path (GabbleFileTransferChannel *self) { TpBaseConnection *base_conn = tp_base_channel_get_connection ( TP_BASE_CHANNEL (self)); GabbleConnection *conn = GABBLE_CONNECTION (base_conn); const gchar *tmp_dir; gchar *path = NULL; gchar *name; struct stat buf; tmp_dir = gabble_ft_manager_get_tmp_dir (conn->ft_manager); if (tmp_dir == NULL) return NULL; name = g_strdup_printf ("ft-channel-%p", self); path = g_build_filename (tmp_dir, name, NULL); g_free (name); if (g_stat (path, &buf) == 0) { /* The file is not supposed to exist */ DEBUG ("file %s already exists", path); g_assert_not_reached (); } return path; } #endif /* * Data is available from the channel so we can send it. */ static void transport_handler (GibberTransport *transport, GibberBuffer *data, gpointer user_data) { GabbleFileTransferChannel *self = GABBLE_FILE_TRANSFER_CHANNEL (user_data); if (self->priv->bytestream != NULL) { if (!gabble_bytestream_iface_send (self->priv->bytestream, data->length, (const gchar *) data->data)) { DEBUG ("Sending failed. Closing the bytestream"); close_session_and_transport (self); return; } } #ifdef ENABLE_JINGLE_FILE_TRANSFER else if (self->priv->gtalk_file_collection != NULL) { if (!gtalk_file_collection_send_data (self->priv->gtalk_file_collection, self, (const gchar *) data->data, data->length)) { DEBUG ("Sending failed. Closing the jingle session"); close_session_and_transport (self); return; } } #endif transferred_chunk (self, (guint64) data->length); if (self->priv->transferred_bytes + self->priv->initial_offset >= self->priv->size) { if (self->priv->bytestream != NULL) { DEBUG ("All the file has been sent. Closing the bytestream"); gabble_file_transfer_channel_set_state ( TP_SVC_CHANNEL_TYPE_FILE_TRANSFER (self), TP_FILE_TRANSFER_STATE_COMPLETED, TP_FILE_TRANSFER_STATE_CHANGE_REASON_NONE); gabble_bytestream_iface_close (self->priv->bytestream, NULL); } #ifdef ENABLE_JINGLE_FILE_TRANSFER else if (self->priv->gtalk_file_collection != NULL) { DEBUG ("All the file has been sent."); gtalk_file_collection_completed (self->priv->gtalk_file_collection, self); } #endif } } static void bytestream_write_blocked_cb (GabbleBytestreamIface *bytestream, gboolean blocked, GabbleFileTransferChannel *self) { if (self->priv->transport != NULL) gibber_transport_block_receiving (self->priv->transport, blocked); } #ifdef ENABLE_JINGLE_FILE_TRANSFER void gabble_file_transfer_channel_gtalk_file_collection_write_blocked ( GabbleFileTransferChannel *self, gboolean blocked) { if (self->priv->transport != NULL) gibber_transport_block_receiving (self->priv->transport, blocked); } #endif static void file_transfer_send (GabbleFileTransferChannel *self) { /* We shouldn't receive data if the bytestream isn't open otherwise it will error out */ if (self->priv->state == TP_FILE_TRANSFER_STATE_OPEN) gibber_transport_block_receiving (self->priv->transport, FALSE); else gibber_transport_block_receiving (self->priv->transport, TRUE); gibber_transport_set_handler (self->priv->transport, transport_handler, self); } static void file_transfer_receive (GabbleFileTransferChannel *self) { /* Client is connected, we can now receive data. Unblock the bytestream */ if (self->priv->bytestream != NULL) gabble_bytestream_iface_block_reading (self->priv->bytestream, FALSE); #ifdef ENABLE_JINGLE_FILE_TRANSFER else if (self->priv->gtalk_file_collection != NULL) gtalk_file_collection_block_reading (self->priv->gtalk_file_collection, self, FALSE); #endif } static void transport_disconnected_cb (GibberTransport *transport, GabbleFileTransferChannel *self) { TpBaseChannel *base = TP_BASE_CHANNEL (self); gboolean requested = tp_base_channel_is_requested (base); DEBUG ("transport to local socket has been disconnected"); /* If we are sending the file, we can expect the transport to be closed as soon as we received all the data. Otherwise, it should only get closed once the channel has gone to state COMPLETED. This allows to make sure we detect an error if the channel is closed while receiving a gtalk-ft folder where the size is an approximation of the real size to be received */ if ((requested && self->priv->transferred_bytes + self->priv->initial_offset < self->priv->size) || (!requested && self->priv->state != TP_FILE_TRANSFER_STATE_COMPLETED)) { gabble_file_transfer_channel_set_state ( TP_SVC_CHANNEL_TYPE_FILE_TRANSFER (self), TP_FILE_TRANSFER_STATE_CANCELLED, TP_FILE_TRANSFER_STATE_CHANGE_REASON_LOCAL_ERROR); close_session_and_transport (self); } } static void transport_buffer_empty_cb (GibberTransport *transport, GabbleFileTransferChannel *self) { /* Buffer is empty so we can unblock the buffer if it was blocked */ if (self->priv->bytestream != NULL) gabble_bytestream_iface_block_reading (self->priv->bytestream, FALSE); #ifdef ENABLE_JINGLE_FILE_TRANSFER if (self->priv->gtalk_file_collection != NULL) gtalk_file_collection_block_reading (self->priv->gtalk_file_collection, self, FALSE); #endif if (self->priv->state > TP_FILE_TRANSFER_STATE_OPEN) gibber_transport_disconnect (transport); } /* * Some client is connecting to the Unix socket. */ static void new_connection_cb (GibberListener *listener, GibberTransport *transport, struct sockaddr_storage *addr, guint size, gpointer user_data) { GabbleFileTransferChannel *self = GABBLE_FILE_TRANSFER_CHANNEL (user_data); TpBaseChannel *base = TP_BASE_CHANNEL (self); DEBUG ("Client connected to local socket"); self->priv->transport = g_object_ref (transport); gabble_signal_connect_weak (transport, "disconnected", G_CALLBACK (transport_disconnected_cb), G_OBJECT (self)); gabble_signal_connect_weak (transport, "buffer-empty", G_CALLBACK (transport_buffer_empty_cb), G_OBJECT (self)); if (!tp_base_channel_is_requested (base)) /* Incoming file transfer */ file_transfer_receive (self); else /* Outgoing file transfer */ file_transfer_send (self); /* stop listening on local socket */ tp_clear_object (&self->priv->listener); } static gboolean setup_local_socket (GabbleFileTransferChannel *self, TpSocketAddressType address_type, TpSocketAccessControl access_control, const GValue *access_control_param) { GError *error = NULL; self->priv->listener = gibber_listener_new (); /* Add this stage the address_type and access_control have been checked and * are supposed to be valid */ #ifdef GIBBER_TYPE_UNIX_TRANSPORT if (address_type == TP_SOCKET_ADDRESS_TYPE_UNIX) { gchar *path; GArray *array; g_assert (access_control == TP_SOCKET_ACCESS_CONTROL_LOCALHOST); path = get_local_unix_socket_path (self); if (path == NULL) return FALSE; if (!gibber_listener_listen_socket (self->priv->listener, (gchar *) path, FALSE, &error)) { DEBUG ("listen_socket failed: %s", error->message); g_error_free (error); tp_clear_object (&self->priv->listener); return FALSE; } array = g_array_sized_new (TRUE, FALSE, sizeof (gchar), strlen (path)); g_array_insert_vals (array, 0, path, strlen (path)); self->priv->socket_address = tp_g_value_slice_new ( DBUS_TYPE_G_UCHAR_ARRAY); g_value_set_boxed (self->priv->socket_address, array); DEBUG ("local socket %s", path); g_free (path); g_array_unref (array); } else #endif if (address_type == TP_SOCKET_ADDRESS_TYPE_IPV4) { int ret; g_assert (access_control == TP_SOCKET_ACCESS_CONTROL_LOCALHOST); ret = gibber_listener_listen_tcp_loopback_af (self->priv->listener, 0, GIBBER_AF_IPV4, &error); if (!ret) { DEBUG ("Error listening on ipv4 socket: %s", error->message); g_error_free (error); return FALSE; } self->priv->socket_address = tp_g_value_slice_new ( TP_STRUCT_TYPE_SOCKET_ADDRESS_IPV4); g_value_take_boxed (self->priv->socket_address, dbus_g_type_specialized_construct ( TP_STRUCT_TYPE_SOCKET_ADDRESS_IPV4)); dbus_g_type_struct_set (self->priv->socket_address, 0, "127.0.0.1", 1, gibber_listener_get_port (self->priv->listener), G_MAXUINT); } else if (address_type == TP_SOCKET_ADDRESS_TYPE_IPV6) { int ret; g_assert (access_control == TP_SOCKET_ACCESS_CONTROL_LOCALHOST); ret = gibber_listener_listen_tcp_loopback_af (self->priv->listener, 0, GIBBER_AF_IPV6, &error); if (!ret) { DEBUG ("Error listening on ipv6 socket: %s", error->message); g_error_free (error); return FALSE; } self->priv->socket_address = tp_g_value_slice_new ( TP_STRUCT_TYPE_SOCKET_ADDRESS_IPV6); g_value_take_boxed (self->priv->socket_address, dbus_g_type_specialized_construct ( TP_STRUCT_TYPE_SOCKET_ADDRESS_IPV6)); dbus_g_type_struct_set (self->priv->socket_address, 0, "::1", 1, gibber_listener_get_port (self->priv->listener), G_MAXUINT); } else { g_assert_not_reached (); } self->priv->socket_type = address_type; gabble_signal_connect_weak (self->priv->listener, "new-connection", G_CALLBACK (new_connection_cb), G_OBJECT (self)); return TRUE; } GabbleFileTransferChannel * gabble_file_transfer_channel_new (GabbleConnection *conn, TpHandle handle, TpHandle initiator_handle, TpFileTransferState state, const gchar *content_type, const gchar *filename, guint64 size, TpFileHashType content_hash_type, const gchar *content_hash, const gchar *description, guint64 date, guint64 initial_offset, gboolean resume_supported, GabbleBytestreamIface *bytestream, #ifdef ENABLE_JINGLE_FILE_TRANSFER GTalkFileCollection *gtalk_file_collection, #else gpointer gtalk_file_collection_dummy, #endif const gchar *file_collection, const gchar *uri, const gchar *service_name, const GHashTable *metadata) { #ifndef ENABLE_JINGLE_FILE_TRANSFER g_assert (gtalk_file_collection_dummy == NULL); #endif return g_object_new (GABBLE_TYPE_FILE_TRANSFER_CHANNEL, "connection", conn, "handle", handle, "initiator-handle", initiator_handle, "requested", (initiator_handle != handle), "state", state, "content-type", content_type, "filename", filename, "size", size, "content-hash-type", content_hash_type, "content-hash", content_hash, "description", description, "date", date, "initial-offset", initial_offset, "resume-supported", resume_supported, "file-collection", file_collection, "bytestream", bytestream, #ifdef ENABLE_JINGLE_FILE_TRANSFER "gtalk-file-collection", gtalk_file_collection, #endif "uri", uri, "service-name", service_name, "metadata", metadata, NULL); } telepathy-gabble-0.18.2/src/vcard-manager.c0000644000175000017500000014715412227000321020460 0ustar00smcvsmcv00000000000000/* * vcard-manager.c - Source for Gabble vCard lookup helper * * Copyright (C) 2007-2010 Collabora Ltd. * Copyright (C) 2006-2010 Nokia Corporation * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 "config.h" #include "vcard-manager.h" #include #include #include #define DEBUG_FLAG GABBLE_DEBUG_VCARD #include "conn-aliasing.h" #include "conn-contact-info.h" #include "connection.h" #include "debug.h" #include "namespaces.h" #include "request-pipeline.h" #include "util.h" static guint default_request_timeout = 180; #define VCARD_CACHE_ENTRY_TTL 60 /* When the server reply with XMPP_ERROR_RESOURCE_CONSTRAINT, wait * request_wait_delay seconds before allowing a vCard request to be sent to * the same recipient */ static guint request_wait_delay = 5 * 60; static const gchar *NO_ALIAS = "none"; struct _GabbleVCardManagerEditInfo { /* name of element to edit */ gchar *element_name; /* If REPLACE, the first element with this name (if any) will be updated; * if APPEND, an element with this name will be added; * if DELETE, all elements with this name will be removed; * if CLEAR, everything except PHOTO and NICKNAME will be deleted, in * preparation for a SetContactInfo operation * if SET_ALIAS and new_alias is NULL, set the best alias we have * as the NICKNAME or FN (as appropriate) if that field doesn't already * have a value * if SET_ALIAS and new_alias is non-NULL, set that * as the NICKNAME or FN (as appropriate), overriding anything already * there */ GabbleVCardEditType edit_type; /* the element to fill in, if edit_type is REPLACE or APPEND. */ WockyNodeTree *element; /* only meaningful if edit_type is SET_ALIAS; see above. */ gchar *new_alias; }; /* signal enum */ enum { NICKNAME_UPDATE, VCARD_UPDATE, GOT_SELF_INITIAL_AVATAR, LAST_SIGNAL }; static guint signals[LAST_SIGNAL] = {0}; /* Properties */ enum { PROP_CONNECTION = 1, PROP_HAVE_SELF_AVATAR, LAST_PROPERTY }; G_DEFINE_TYPE(GabbleVCardManager, gabble_vcard_manager, G_TYPE_OBJECT); typedef struct _GabbleVCardCacheEntry GabbleVCardCacheEntry; struct _GabbleVCardManagerPrivate { gboolean dispose_has_run; GabbleConnection *connection; /* TpHandle borrowed from the entry => owned (GabbleVCardCacheEntry *) */ GHashTable *cache; /* Those (GabbleVCardCacheEntry *) s that have not expired, ordered by * increasing expiry time; borrowed from @cache */ TpHeap *timed_cache; /* Timer which runs out when the first item in the @timed_cache expires */ guint cache_timer; /* Things to do with my own vCard, which is somewhat special - mainly because * we can edit it. There's only one self_handle, so there's no point * bloating every cache entry with these fields. */ gboolean have_self_avatar; /* list of pending edits (GabbleVCardManagerEditInfo structures) */ GList *edits; /* Contains RequestPipelineItem for our SET vCard request, or NULL if we * don't have SET request in the pipeline already. At most one SET request * can be in pipeline at any given time. */ GabbleRequestPipelineItem *edit_pipeline_item; /* List of all pending edit requests that we got. */ GList *edit_requests; /* Patched vCard that we sent to the server to update, but haven't * got confirmation yet. We don't want to store it in cache (visible * to others) before we're sure the server accepts it. */ WockyNodeTree *patched_vcard; }; struct _GabbleVCardManagerRequest { GabbleVCardManager *manager; GabbleVCardCacheEntry *entry; guint timer_id; guint timeout; GabbleVCardManagerCb callback; gpointer user_data; GObject *bound_object; }; struct _GabbleVCardManagerEditRequest { GabbleVCardManager *manager; GabbleVCardManagerEditCb callback; gpointer user_data; GObject *bound_object; /* Set if we have already patched vCard with data from this request, * and sent a SET request to the server to replace the vCard. */ gboolean set_in_pipeline; }; /* An entry in the vCard cache. These exist only as long as: * * 1) the cached message which has not yet expired; and/or * 2) a network request is in the pipeline; and/or * 3) there are requests pending. */ struct _GabbleVCardCacheEntry { /* Parent object */ GabbleVCardManager *manager; /* Referenced handle */ TpHandle handle; /* Pipeline item for our if one is in progress */ GabbleRequestPipelineItem *pipeline_item; /* List of (GabbleVCardManagerRequest *) borrowed from priv->requests */ GSList *pending_requests; /* When requests for this entry receive an error of type "wait", we suspend * further requests and retry again after request_wait_delay seconds. * 0 if not suspended. */ guint suspended_timer_id; /* VCard node for this entry (owned reference), or NULL if there's no node */ WockyNodeTree *vcard_node; /* If @vcard_node is not NULL, the time the message will expire */ time_t expires; }; GQuark gabble_vcard_manager_error_quark (void) { static GQuark quark = 0; if (!quark) quark = g_quark_from_static_string ("gabble-vcard-manager-error"); return quark; } GQuark gabble_vcard_manager_cache_quark (void) { static GQuark quark = 0; if (!quark) quark = g_quark_from_static_string ("gabble-vcard-manager-cache"); return quark; } static void cache_entry_free (void *data); static gint cache_entry_compare (gconstpointer a, gconstpointer b); static void manager_patch_vcard ( GabbleVCardManager *self, WockyNode *vcard_node); static void request_send (GabbleVCardManagerRequest *request, guint timeout); static void gabble_vcard_manager_init (GabbleVCardManager *obj) { GabbleVCardManagerPrivate *priv = G_TYPE_INSTANCE_GET_PRIVATE (obj, GABBLE_TYPE_VCARD_MANAGER, GabbleVCardManagerPrivate); obj->priv = priv; priv->cache = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, cache_entry_free); /* no destructor here - the hash table is responsible for freeing it */ priv->timed_cache = tp_heap_new (cache_entry_compare, NULL); priv->cache_timer = 0; priv->have_self_avatar = FALSE; priv->edits = NULL; } static void gabble_vcard_manager_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec); static void gabble_vcard_manager_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec); static void gabble_vcard_manager_dispose (GObject *object); static void gabble_vcard_manager_finalize (GObject *object); static void gabble_vcard_manager_class_init (GabbleVCardManagerClass *cls) { GObjectClass *object_class = G_OBJECT_CLASS (cls); GParamSpec *param_spec; g_type_class_add_private (cls, sizeof (GabbleVCardManagerPrivate)); object_class->get_property = gabble_vcard_manager_get_property; object_class->set_property = gabble_vcard_manager_set_property; object_class->dispose = gabble_vcard_manager_dispose; object_class->finalize = gabble_vcard_manager_finalize; param_spec = g_param_spec_object ("connection", "GabbleConnection object", "Gabble connection object that owns this vCard lookup helper object.", GABBLE_TYPE_CONNECTION, G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, PROP_CONNECTION, param_spec); param_spec = g_param_spec_boolean ("have-self-avatar", "Have our own avatar", "TRUE after the local user's own vCard has been retrieved in order to " "get their initial avatar.", FALSE, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, PROP_HAVE_SELF_AVATAR, param_spec); /* signal definitions */ signals[NICKNAME_UPDATE] = g_signal_new ("nickname-update", G_TYPE_FROM_CLASS (cls), G_SIGNAL_RUN_LAST, 0, NULL, NULL, g_cclosure_marshal_VOID__UINT, G_TYPE_NONE, 1, G_TYPE_UINT); signals[VCARD_UPDATE] = g_signal_new ("vcard-update", G_TYPE_FROM_CLASS (cls), G_SIGNAL_RUN_LAST, 0, NULL, NULL, g_cclosure_marshal_VOID__UINT, G_TYPE_NONE, 1, G_TYPE_UINT); signals[GOT_SELF_INITIAL_AVATAR] = g_signal_new ("got-self-initial-avatar", G_TYPE_FROM_CLASS (cls), G_SIGNAL_RUN_LAST, 0, NULL, NULL, g_cclosure_marshal_VOID__STRING, G_TYPE_NONE, 1, G_TYPE_STRING); } static void gabble_vcard_manager_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { GabbleVCardManager *self = GABBLE_VCARD_MANAGER (object); GabbleVCardManagerPrivate *priv = self->priv; switch (property_id) { case PROP_CONNECTION: g_value_set_object (value, priv->connection); break; case PROP_HAVE_SELF_AVATAR: g_value_set_boolean (value, priv->have_self_avatar); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void gabble_vcard_manager_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { GabbleVCardManager *self = GABBLE_VCARD_MANAGER (object); GabbleVCardManagerPrivate *priv = self->priv; switch (property_id) { case PROP_CONNECTION: priv->connection = g_value_get_object (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void delete_request (GabbleVCardManagerRequest *request); static void cancel_request (GabbleVCardManagerRequest *request); static void cancel_all_edit_requests (GabbleVCardManager *manager); static gint cache_entry_compare (gconstpointer a, gconstpointer b) { const GabbleVCardCacheEntry *foo = a; const GabbleVCardCacheEntry *bar = b; return foo->expires - bar->expires; } static void cache_entry_free (gpointer data) { GabbleVCardCacheEntry *entry = data; g_assert (entry != NULL); while (entry->pending_requests) { cancel_request (entry->pending_requests->data); } if (entry->pipeline_item) { gabble_request_pipeline_item_cancel (entry->pipeline_item); } g_clear_object (&entry->vcard_node); g_slice_free (GabbleVCardCacheEntry, entry); } static GabbleVCardCacheEntry * cache_entry_get (GabbleVCardManager *manager, TpHandle handle) { GabbleVCardManagerPrivate *priv = manager->priv; GabbleVCardCacheEntry *entry; entry = g_hash_table_lookup (priv->cache, GUINT_TO_POINTER (handle)); if (entry) return entry; entry = g_slice_new0 (GabbleVCardCacheEntry); entry->manager = manager; entry->handle = handle; g_hash_table_insert (priv->cache, GUINT_TO_POINTER (handle), entry); return entry; } static gboolean cache_entry_timeout (gpointer data) { GabbleVCardManager *manager = data; GabbleVCardManagerPrivate *priv = manager->priv; GabbleVCardCacheEntry *entry; time_t now = time (NULL); while (NULL != (entry = tp_heap_peek_first (priv->timed_cache))) { if (entry->expires > now) break; /* shouldn't have in-flight request nor any pending requests */ g_assert (entry->pipeline_item == NULL); gabble_vcard_manager_invalidate_cache (manager, entry->handle); } priv->cache_timer = 0; if (entry) { priv->cache_timer = g_timeout_add_seconds ( entry->expires - time (NULL), cache_entry_timeout, manager); } return FALSE; } static void cache_entry_attempt_to_free (GabbleVCardCacheEntry *entry) { GabbleVCardManagerPrivate *priv = entry->manager->priv; TpBaseConnection *base = (TpBaseConnection *) priv->connection; if (entry->vcard_node != NULL) { DEBUG ("Not freeing vCard cache entry %p: it has a cached vCard %p", entry, entry->vcard_node); return; } if (entry->pipeline_item != NULL) { DEBUG ("Not freeing vCard cache entry %p: it has a pipeline_item %p", entry, entry->pipeline_item); return; } if (entry->pending_requests != NULL) { DEBUG ("Not freeing vCard cache entry %p: it has pending requests", entry); return; } /* If there is a suspended request, it must be in entry-> pending_requests */ g_assert (entry->suspended_timer_id == 0); if (entry->handle == tp_base_connection_get_self_handle (base)) { /* if we do have some pending edits, we should also have * some pipeline items or pending requests */ g_assert (priv->edit_pipeline_item || priv->edits == NULL); } tp_heap_remove (priv->timed_cache, entry); g_hash_table_remove (priv->cache, GUINT_TO_POINTER (entry->handle)); } void gabble_vcard_manager_invalidate_cache (GabbleVCardManager *manager, TpHandle handle) { GabbleVCardManagerPrivate *priv = manager->priv; GabbleVCardCacheEntry *entry = g_hash_table_lookup (priv->cache, GUINT_TO_POINTER (handle)); TpHandleRepoIface *contact_repo = tp_base_connection_get_handles ( (TpBaseConnection *) priv->connection, TP_HANDLE_TYPE_CONTACT); g_return_if_fail (tp_handle_is_valid (contact_repo, handle, NULL)); if (!entry) return; tp_heap_remove (priv->timed_cache, entry); g_clear_object (&entry->vcard_node); cache_entry_attempt_to_free (entry); } static void complete_one_request (GabbleVCardManagerRequest *request, WockyNode *vcard_node, GError *error); static void cache_entry_complete_requests (GabbleVCardCacheEntry *entry, GError *error) { GSList *cur, *tmp; WockyNode *vcard_node = NULL; tmp = g_slist_copy (entry->pending_requests); if (entry->vcard_node != NULL) vcard_node = wocky_node_tree_get_top_node (entry->vcard_node); for (cur = tmp; cur != NULL; cur = cur->next) { GabbleVCardManagerRequest *request = cur->data; complete_one_request (request, error ? NULL : vcard_node, error); } g_slist_free (tmp); } static void complete_one_request (GabbleVCardManagerRequest *request, WockyNode *vcard_node, GError *error) { if (request->callback) { (request->callback) (request->manager, request, request->entry->handle, vcard_node, error, request->user_data); } delete_request (request); } static void disconnect_entry_foreach (gpointer handle, gpointer value, gpointer unused) { GError err = { TP_ERROR, TP_ERROR_DISCONNECTED, "Connection closed" }; GabbleVCardCacheEntry *entry = value; if (entry->suspended_timer_id) { g_source_remove (entry->suspended_timer_id); entry->suspended_timer_id = 0; } cache_entry_complete_requests (entry, &err); if (entry->pipeline_item) { gabble_request_pipeline_item_cancel (entry->pipeline_item); entry->pipeline_item = NULL; } } static void gabble_vcard_manager_dispose (GObject *object) { GabbleVCardManager *self = GABBLE_VCARD_MANAGER (object); GabbleVCardManagerPrivate *priv = self->priv; if (priv->dispose_has_run) return; priv->dispose_has_run = TRUE; DEBUG ("%p", object); if (priv->edits != NULL) { g_list_foreach (priv->edits, (GFunc) gabble_vcard_manager_edit_info_free, NULL); g_list_free (priv->edits); } priv->edits = NULL; if (priv->cache_timer) g_source_remove (priv->cache_timer); g_hash_table_foreach (priv->cache, disconnect_entry_foreach, NULL); tp_heap_destroy (priv->timed_cache); g_hash_table_unref (priv->cache); if (priv->edit_pipeline_item) gabble_request_pipeline_item_cancel (priv->edit_pipeline_item); cancel_all_edit_requests (self); if (G_OBJECT_CLASS (gabble_vcard_manager_parent_class)->dispose) G_OBJECT_CLASS (gabble_vcard_manager_parent_class)->dispose (object); } static void gabble_vcard_manager_finalize (GObject *object) { DEBUG ("%p", object); G_OBJECT_CLASS (gabble_vcard_manager_parent_class)->finalize (object); } gchar * vcard_get_avatar_sha1 (WockyNode *vcard) { gchar *sha1; const gchar *binval_value; guchar *avatar; gsize outlen; WockyNode *node; WockyNode *binval; node = wocky_node_get_child (vcard, "PHOTO"); if (!node) return g_strdup (""); DEBUG ("Our vCard has a PHOTO %p", node); binval = wocky_node_get_child (node, "BINVAL"); if (!binval) return g_strdup (""); binval_value = binval->content; if (!binval_value) return g_strdup (""); avatar = g_base64_decode (binval_value, &outlen); if (avatar) { sha1 = sha1_hex ((gchar *) avatar, outlen); g_free (avatar); DEBUG ("Successfully decoded PHOTO.BINVAL, SHA-1 %s", sha1); } else { DEBUG ("Avatar is in garbled Base64, ignoring it!"); sha1 = g_strdup (""); } return sha1; } /* Called during connection. */ static void initial_request_cb (GabbleVCardManager *self, GabbleVCardManagerRequest *request, TpHandle handle, WockyNode *vcard, GError *error, gpointer user_data) { GabbleVCardManagerPrivate *priv = self->priv; gchar *alias = user_data; gchar *sha1; if (vcard) { /* We now have our own avatar (or lack thereof) so can answer * GetAvatarTokens([self_handle]) */ priv->have_self_avatar = TRUE; /* Do we have an avatar already? If so, the presence cache ought to be * told (anyone else's avatar SHA-1 we'd get from their presence, * but unless we have another XEP-0153 resource connected, we never * see our own presence) */ sha1 = vcard_get_avatar_sha1 (vcard); g_signal_emit (self, signals[GOT_SELF_INITIAL_AVATAR], 0, sha1); g_free (sha1); } g_free (alias); } static void status_changed_cb (GObject *object, guint status, guint reason, gpointer user_data) { GabbleVCardManager *self = GABBLE_VCARD_MANAGER (user_data); GabbleVCardManagerPrivate *priv = self->priv; GabbleConnection *conn = GABBLE_CONNECTION (object); TpBaseConnection *base = (TpBaseConnection *) conn; if (status == TP_CONNECTION_STATUS_CONNECTED) { gchar *alias; GabbleConnectionAliasSource alias_src; /* if we have a better alias, patch it into our vCard on the server */ alias_src = _gabble_connection_get_cached_alias (conn, tp_base_connection_get_self_handle (base), &alias); if (alias_src >= GABBLE_CONNECTION_ALIAS_FROM_VCARD) { priv->edits = g_list_append (priv->edits, gabble_vcard_manager_edit_info_new (NULL, alias, GABBLE_VCARD_EDIT_SET_ALIAS, NULL)); } g_free (alias); /* FIXME: we happen to know that synchronous errors can't happen */ gabble_vcard_manager_request (self, tp_base_connection_get_self_handle (base), 0, initial_request_cb, NULL, (GObject *) self); } } /** * gabble_vcard_manager_new: * @conn: The #GabbleConnection to use for vCard lookup * * Creates an object to use for Jabber vCard lookup (JEP 0054). * There should be one of these per connection */ GabbleVCardManager * gabble_vcard_manager_new (GabbleConnection *conn) { GabbleVCardManager *self; g_return_val_if_fail (GABBLE_IS_CONNECTION (conn), NULL); self = GABBLE_VCARD_MANAGER (g_object_new (GABBLE_TYPE_VCARD_MANAGER, "connection", conn, NULL)); g_signal_connect (conn, "status-changed", G_CALLBACK (status_changed_cb), self); return self; } static void notify_delete_request (gpointer data, GObject *obj); static void notify_delete_edit_request (gpointer data, GObject *obj); static void delete_request (GabbleVCardManagerRequest *request) { GabbleVCardManager *manager = request->manager; DEBUG ("Discarding request %p", request); g_assert (NULL != request); g_assert (NULL != manager); g_assert (NULL != request->entry); g_assert (GABBLE_IS_VCARD_MANAGER (manager)); /* poison the request, so assertions about it will fail if there's a * dangling reference */ request->manager = NULL; request->entry->pending_requests = g_slist_remove (request->entry->pending_requests, request); cache_entry_attempt_to_free (request->entry); if (NULL != request->bound_object) { g_object_weak_unref (request->bound_object, notify_delete_request, request); } if (0 != request->timer_id) { g_source_remove (request->timer_id); } g_slice_free (GabbleVCardManagerRequest, request); } static gboolean timeout_request (gpointer data) { GabbleVCardManagerRequest *request = (GabbleVCardManagerRequest *) data; g_return_val_if_fail (data != NULL, FALSE); DEBUG ("Request %p timed out, notifying callback %p", request, request->callback); request->timer_id = 0; /* The pipeline machinery will call our callback with the error "canceled" */ gabble_request_pipeline_item_cancel (request->entry->pipeline_item); return FALSE; } static void cancel_request (GabbleVCardManagerRequest *request) { GError err = { GABBLE_VCARD_MANAGER_ERROR, GABBLE_VCARD_MANAGER_ERROR_CANCELLED, "Request cancelled" }; g_assert (request != NULL); DEBUG ("Request %p cancelled, notifying callback %p", request, request->callback); complete_one_request (request, NULL, &err); } static gchar * extract_nickname (WockyNode *vcard_node) { WockyNode *node; const gchar *nick; node = wocky_node_get_child (vcard_node, "NICKNAME"); if (node == NULL) return NULL; nick = node->content; return g_strdup (nick); } static void observe_vcard (GabbleConnection *conn, GabbleVCardManager *manager, TpHandle handle, WockyNode *vcard_node) { TpHandleRepoIface *contact_repo = tp_base_connection_get_handles ( (TpBaseConnection *) conn, TP_HANDLE_TYPE_CONTACT); const gchar *field = ""; gchar *alias; const gchar *old_alias; alias = extract_nickname (vcard_node); if (alias == NULL) { WockyNode *fn_node = wocky_node_get_child (vcard_node, "FN"); if (fn_node != NULL) { const gchar *fn = fn_node->content; if (!tp_str_empty (fn)) { field = ""; alias = g_strdup (fn); } } } g_signal_emit (G_OBJECT (manager), signals[VCARD_UPDATE], 0, handle); old_alias = gabble_vcard_manager_get_cached_alias (manager, handle); if (old_alias != NULL && !tp_strdiff (old_alias, alias)) { DEBUG ("no change to vCard alias \"%s\" for handle %u", alias, handle); g_free (alias); return; } if (alias != NULL) { DEBUG ("got vCard alias \"%s\" for handle %u from %s", alias, handle, field); /* takes ownership of alias */ tp_handle_set_qdata (contact_repo, handle, gabble_vcard_manager_cache_quark (), alias, g_free); } else { DEBUG ("got no vCard alias for handle %u", handle); tp_handle_set_qdata (contact_repo, handle, gabble_vcard_manager_cache_quark (), (gchar *) NO_ALIAS, NULL); } if ((old_alias != NULL) || (alias != NULL)) g_signal_emit (G_OBJECT (manager), signals[NICKNAME_UPDATE], 0, handle); } /* Called when a pre-set get request failed, or when a set request succeeded * or failed. */ static void replace_reply_cb (GabbleConnection *conn, WockyStanza *reply_msg, gpointer user_data, GError *error) { GabbleVCardManager *self = GABBLE_VCARD_MANAGER (user_data); GabbleVCardManagerPrivate *priv = self->priv; TpBaseConnection *base = (TpBaseConnection *) conn; GList *li; WockyNode *node = NULL; /* If we sent a SET request, it's dead now. */ priv->edit_pipeline_item = NULL; DEBUG ("called: %s error", (error) ? "some" : "no"); if (error) { /* We won't need our patched vcard after all */ g_clear_object (&priv->patched_vcard); } else { GabbleVCardCacheEntry *entry = cache_entry_get (self, tp_base_connection_get_self_handle (base)); /* We must have patched vcard by now */ g_assert (priv->patched_vcard != NULL); /* Finally we may put the new vcard in the cache. */ g_clear_object (&entry->vcard_node); entry->vcard_node = priv->patched_vcard; priv->patched_vcard = NULL; node = wocky_node_tree_get_top_node (entry->vcard_node); /* observe it so we pick up alias updates */ observe_vcard (conn, self, tp_base_connection_get_self_handle (base), node); } /* Scan all edit requests, call and remove ones whose data made it * into SET request that just returned. */ li = priv->edit_requests; while (li) { GabbleVCardManagerEditRequest *req = li->data; li = g_list_next (li); if (req->set_in_pipeline || error) { if (req->callback) { (req->callback) (req->manager, req, node, error, req->user_data); } gabble_vcard_manager_remove_edit_request (req); } } if (error != NULL) { if (priv->edits != NULL) { /* All the requests for these edits have just been cancelled. */ g_list_foreach (priv->edits, (GFunc) gabble_vcard_manager_edit_info_free, NULL); g_list_free (priv->edits); priv->edits = NULL; } } else { /* If we've received more edit requests in the meantime, send them off. */ manager_patch_vcard (self, node); } } /* This function must return TRUE for any significant change, but may also * return TRUE for insignificant changes, as long as they aren't commonly done * (NICKNAME, PHOTO and in future FN are the problematic ones). */ static gboolean gabble_vcard_manager_replace_is_significant (GabbleVCardManagerEditInfo *info, WockyNode *old_vcard) { gboolean seen = FALSE; WockyNodeIter i; WockyNode *node, *replacement_node; g_return_val_if_fail (info->element != NULL, FALSE); replacement_node = wocky_node_tree_get_top_node (info->element); /* Find the first node matching the one we want to edit */ wocky_node_iter_init (&i, old_vcard, info->element_name, NULL); while (wocky_node_iter_next (&i, &node)) { /* if there are >= 2 copies of this field, we're going to reduce that * to 1 */ if (seen) return TRUE; seen = TRUE; /* This depends on PHOTO's children being TYPE, BINVAL in the correct * order—which is required by the vcard-temp schema, soooo... */ if (!wocky_node_equal (node, replacement_node)) return TRUE; } /* if there are no copies of this field, we're going to add one; otherwise, * seen == TRUE implies we've seen exactly one copy, and it matched what * we want */ return !seen; } static gboolean remove_all_children_named ( WockyNode *node, const gchar *name) { WockyNodeIter iter; gboolean changed = FALSE; wocky_node_iter_init (&iter, node, name, NULL); while (wocky_node_iter_next (&iter, NULL)) { wocky_node_iter_remove (&iter); changed = TRUE; } return changed; } static gboolean gabble_vcard_manager_edit_info_apply_replace ( GabbleVCardManagerEditInfo *info, WockyNode *vcard_node, GabbleVCardManager *vcard_manager) { g_return_val_if_fail (info->edit_type == GABBLE_VCARD_EDIT_REPLACE, FALSE); if (!gabble_vcard_manager_can_use_vcard_field (vcard_manager, info->element_name)) { DEBUG ("ignoring vcard node %s because this server doesn't " "support it", info->element_name); return FALSE; } /* A special case for replacing one field with another: we detect no-op * changes more actively, because we make changes of this type quite * frequently (on every login), and as well as wasting bandwidth, setting * the vCard too often can cause a memory leak in OpenFire (see fd.o#25341). */ if (! gabble_vcard_manager_replace_is_significant (info, vcard_node)) { DEBUG ("ignoring no-op vCard %s replacement", info->element_name); return FALSE; } remove_all_children_named (vcard_node, info->element_name); wocky_node_add_node_tree (vcard_node, info->element); return TRUE; } static gboolean gabble_vcard_manager_edit_info_apply_append ( GabbleVCardManagerEditInfo *info, WockyNode *vcard_node, GabbleVCardManager *vcard_manager) { if (!gabble_vcard_manager_can_use_vcard_field (vcard_manager, info->element_name)) { DEBUG ("ignoring vcard node %s because this server doesn't " "support it", info->element_name); return FALSE; } wocky_node_add_node_tree (vcard_node, info->element); return TRUE; } static gboolean gabble_vcard_manager_edit_info_apply_delete ( GabbleVCardManagerEditInfo *info, WockyNode *vcard_node, GabbleVCardManager *vcard_manager) { return remove_all_children_named (vcard_node, info->element_name); } static gboolean gabble_vcard_manager_edit_info_apply_clear ( GabbleVCardManagerEditInfo *info, WockyNode *vcard_node, GabbleVCardManager *vcard_manager) { /* Blow almost everything away! As a special case, the photo gets left in * place from the old vCard, because SetContactInfo doesn't touch * photos, and CLEAR is only used by SetContactInfo */ WockyNodeIter iter; WockyNode *node; gboolean changed = FALSE; wocky_node_iter_init (&iter, vcard_node, NULL, NULL); while (wocky_node_iter_next (&iter, &node)) { if (tp_strdiff (node->name, "PHOTO")) { wocky_node_iter_remove (&iter); changed = TRUE; } } return changed; } /* SET_ALIAS is shorthand for a REPLACE operation or nothing */ static gboolean gabble_vcard_manager_edit_info_apply_set_alias ( GabbleVCardManagerEditInfo *info, WockyNode *old_vcard, GabbleVCardManager *vcard_manager) { GabbleConnection *conn = vcard_manager->priv->connection; TpBaseConnection *base = (TpBaseConnection *) conn; g_assert (info->element_name == NULL); if (gabble_vcard_manager_can_use_vcard_field (vcard_manager, "NICKNAME")) { info->element_name = g_strdup ("NICKNAME"); } else { /* Google Talk servers won't let us set a NICKNAME; recover by * setting the FN */ info->element_name = g_strdup ("FN"); } if (info->new_alias == NULL) { /* We're just trying to fix a possibly-incomplete SetContactInfo() */ WockyNode *node = wocky_node_get_child (old_vcard, info->element_name); gchar *alias; /* If the user has set this field explicitly via SetContactInfo(), * that takes precedence */ if (node != NULL) return FALSE; if (_gabble_connection_get_cached_alias (conn, tp_base_connection_get_self_handle (base), &alias) < GABBLE_CONNECTION_ALIAS_FROM_VCARD) { /* not good enough to want to put it in the vCard */ g_free (alias); return FALSE; } info->new_alias = alias; } info->element = wocky_node_tree_new (info->element_name, NS_VCARD_TEMP, '$', info->new_alias, NULL); info->edit_type = GABBLE_VCARD_EDIT_REPLACE; return gabble_vcard_manager_edit_info_apply_replace (info, old_vcard, vcard_manager); } typedef gboolean (*EditFunction) ( GabbleVCardManagerEditInfo *info, WockyNode *vcard_node, GabbleVCardManager *vcard_manager); static const EditFunction edit_functions[] = { gabble_vcard_manager_edit_info_apply_replace, gabble_vcard_manager_edit_info_apply_append, gabble_vcard_manager_edit_info_apply_delete, gabble_vcard_manager_edit_info_apply_clear, gabble_vcard_manager_edit_info_apply_set_alias, }; static gboolean gabble_vcard_manager_edit_info_apply (GabbleVCardManagerEditInfo *info, WockyNode *vcard_node, GabbleVCardManager *vcard_manager) { return edit_functions[info->edit_type] (info, vcard_node, vcard_manager); } static void manager_patch_vcard (GabbleVCardManager *self, WockyNode *old_vcard_node) { GabbleVCardManagerPrivate *priv = self->priv; WockyNodeTree *vcard_node_tree; WockyNode *vcard_node; WockyStanza *msg; GList *li; gboolean changed = FALSE; /* Bail out if we don't have outstanding edits to make, or if we already * have a set request in progress. */ if (priv->edits == NULL || priv->edit_pipeline_item != NULL) return; vcard_node_tree = wocky_node_tree_new_from_node (old_vcard_node); vcard_node = wocky_node_tree_get_top_node (vcard_node_tree); /* Apply any unsent edits to the patched vCard */ for (li = priv->edits; li != NULL; li = li->next) { if (gabble_vcard_manager_edit_info_apply (li->data, vcard_node, self)) changed = TRUE; } if (!changed) { DEBUG ("nothing really changed, not updating vCard"); g_clear_object (&vcard_node_tree); goto out; } DEBUG("patching vcard"); /* We'll save the patched vcard, and if the server says * we're ok, put it into the cache. But we want to leave the * original vcard in the cache until that happens. */ priv->patched_vcard = vcard_node_tree; msg = wocky_stanza_build (WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_SET, NULL, NULL, NULL); wocky_node_add_node_tree (wocky_stanza_get_top_node (msg), vcard_node_tree); priv->edit_pipeline_item = gabble_request_pipeline_enqueue ( priv->connection->req_pipeline, msg, default_request_timeout, replace_reply_cb, self); g_object_unref (msg); out: /* We've applied those, forget about them */ g_list_foreach (priv->edits, (GFunc) gabble_vcard_manager_edit_info_free, NULL); g_list_free (priv->edits); priv->edits = NULL; /* Current edit requests are in the pipeline, remember it so we * know which ones we may complete when the SET returns */ for (li = priv->edit_requests; li; li = g_list_next (li)) { GabbleVCardManagerEditRequest *edit = (GabbleVCardManagerEditRequest *) li->data; edit->set_in_pipeline = TRUE; } } static gboolean suspended_request_timeout_cb (gpointer data) { GabbleVCardManagerRequest *request = data; /* Send the request again */ request->entry->suspended_timer_id = 0; request_send (request, request->timeout); return FALSE; } static gboolean is_item_not_found (const GError *error) { return (error->domain == WOCKY_XMPP_ERROR && error->code == WOCKY_XMPP_ERROR_ITEM_NOT_FOUND); } /* Called when a GET request in the pipeline has either succeeded or failed. */ static void pipeline_reply_cb (GabbleConnection *conn, WockyStanza *reply_msg, gpointer user_data, GError *error) { GabbleVCardManagerRequest *request = user_data; GabbleVCardCacheEntry *entry = request->entry; GabbleVCardManager *self = GABBLE_VCARD_MANAGER (entry->manager); GabbleVCardManagerPrivate *priv = self->priv; TpBaseConnection *base = (TpBaseConnection *) conn; TpHandleRepoIface *contact_repo = tp_base_connection_get_handles (base, TP_HANDLE_TYPE_CONTACT); WockyNode *vcard_node = NULL; DEBUG("called for entry %p", entry); g_assert (tp_handle_is_valid (contact_repo, entry->handle, NULL)); g_assert (entry->pipeline_item != NULL); g_assert (entry->suspended_timer_id == 0); entry->pipeline_item = NULL; /* XEP-0054 says that the server MUST return if you have no * vCard set, so we should treat that case identically to the server * returning success but with no node. */ if (error != NULL && !is_item_not_found (error)) { /* First, handle the error "wait": suspend the request and replay it * later */ WockyXmppErrorType error_type = WOCKY_XMPP_ERROR_TYPE_CANCEL; GError *stanza_error = NULL; if (reply_msg != NULL && wocky_stanza_extract_errors (reply_msg, &error_type, &stanza_error, NULL, NULL)) { if (error_type == WOCKY_XMPP_ERROR_TYPE_WAIT) { DEBUG ("%s", g_quark_to_string (stanza_error->domain)); DEBUG ("Retrieving %u's vCard returned a temporary <%s/> error; " "trying againg in %u seconds", entry->handle, wocky_xmpp_stanza_error_to_string (stanza_error), request_wait_delay); g_source_remove (request->timer_id); request->timer_id = 0; entry->suspended_timer_id = g_timeout_add_seconds ( request_wait_delay, suspended_request_timeout_cb, request); g_error_free (stanza_error); return; } g_error_free (stanza_error); } /* If request for our own vCard failed, and we do have * pending edits to make, cancel those and return error * to the user */ if (entry->handle == tp_base_connection_get_self_handle (base) && priv->edits != NULL) { /* We won't have a chance to apply those, might as well forget them */ g_list_foreach (priv->edits, (GFunc) gabble_vcard_manager_edit_info_free, NULL); g_list_free (priv->edits); priv->edits = NULL; replace_reply_cb (conn, reply_msg, self, error); } /* Complete pending GET requests */ cache_entry_complete_requests (entry, error); return; } g_assert (reply_msg != NULL); vcard_node = wocky_node_get_child ( wocky_stanza_get_top_node (reply_msg), "vCard"); if (NULL == vcard_node) { /* We need a vCard node for the current API */ DEBUG ("successful lookup response contained no node, " "creating an empty one"); vcard_node = wocky_node_add_child_ns ( wocky_stanza_get_top_node (reply_msg), "vCard", NS_VCARD_TEMP); } /* Put the message in the cache */ entry->vcard_node = wocky_node_tree_new_from_node (vcard_node); entry->expires = time (NULL) + VCARD_CACHE_ENTRY_TTL; tp_heap_add (priv->timed_cache, entry); if (priv->cache_timer == 0) { GabbleVCardCacheEntry *first = tp_heap_peek_first (priv->timed_cache); priv->cache_timer = g_timeout_add_seconds ( first->expires - time (NULL), cache_entry_timeout, self); } /* We have freshly updated cache for our vCard, edit it if * there are any pending edits and no outstanding set request. */ if (entry->handle == tp_base_connection_get_self_handle (base)) { manager_patch_vcard (self, vcard_node); } /* Observe the vCard as it goes past */ observe_vcard (priv->connection, self, entry->handle, vcard_node); /* Complete all pending requests successfully */ cache_entry_complete_requests (entry, NULL); } static void notify_delete_request (gpointer data, GObject *obj) { GabbleVCardManagerRequest *request = data; request->bound_object = NULL; delete_request (request); } static void request_send (GabbleVCardManagerRequest *request, guint timeout) { GabbleVCardCacheEntry *entry = request->entry; GabbleConnection *conn = entry->manager->priv->connection; TpBaseConnection *base = (TpBaseConnection *) conn; TpHandleRepoIface *contact_repo = tp_base_connection_get_handles (base, TP_HANDLE_TYPE_CONTACT); g_assert (request->timer_id == 0); if (entry->pipeline_item) { DEBUG ("adding to cache entry %p with already pending", entry); } else if (entry->suspended_timer_id != 0) { DEBUG ("adding to cache entry %p with suspended", entry); } else { const char *jid; WockyStanza *msg; request->timer_id = g_timeout_add_seconds (request->timeout, timeout_request, request); if (entry->handle == tp_base_connection_get_self_handle (base)) { DEBUG ("Cache entry %p is my own, not setting @to", entry); jid = NULL; } else { jid = tp_handle_inspect (contact_repo, entry->handle); DEBUG ("Cache entry %p is not mine, @to = %s", entry, jid); } msg = wocky_stanza_build (WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_GET, NULL, jid, '(', "vCard", ':', NS_VCARD_TEMP, ')', NULL); entry->pipeline_item = gabble_request_pipeline_enqueue ( conn->req_pipeline, msg, timeout, pipeline_reply_cb, request); g_object_unref (msg); DEBUG ("adding request to cache entry %p and queueing the ", entry); } } /* Request the vCard for the given handle. When it arrives, call the given * callback. * * The callback may be NULL if you just want the side-effect of this * operation, which is to update the cached alias. * * Note: this method assumes that vCard for the given handle is not available * already. Before using it either check that the vCard is not available * using gabble_vcard_manager_get_cached(), or explicitly invalidate the * cache using gabble_vcard_manager_invalidate_cache() to request cache * refresh. * * FIXME: the timeout is not always obeyed when there is already a request * on the same handle. It should perhaps be removed. * * The connection must be connected. */ GabbleVCardManagerRequest * gabble_vcard_manager_request (GabbleVCardManager *self, TpHandle handle, guint timeout, GabbleVCardManagerCb callback, gpointer user_data, GObject *object) { GabbleVCardManagerPrivate *priv = self->priv; TpBaseConnection *base = (TpBaseConnection *) priv->connection; TpHandleRepoIface *contact_repo = tp_base_connection_get_handles (base, TP_HANDLE_TYPE_CONTACT); GabbleVCardManagerRequest *request; GabbleVCardCacheEntry *entry = cache_entry_get (self, handle); g_return_val_if_fail (tp_base_connection_get_status (base) == TP_CONNECTION_STATUS_CONNECTED, NULL); g_return_val_if_fail (tp_handle_is_valid (contact_repo, handle, NULL), NULL); g_assert (entry->vcard_node == NULL); if (timeout == 0) timeout = default_request_timeout; request = g_slice_new0 (GabbleVCardManagerRequest); DEBUG ("Created request %p to retrieve <%u>'s vCard", request, handle); request->timeout = timeout; request->manager = self; request->entry = entry; request->callback = callback; request->user_data = user_data; request->bound_object = object; if (NULL != object) g_object_weak_ref (object, notify_delete_request, request); request->entry->pending_requests = g_slist_prepend (request->entry->pending_requests, request); request_send (request, timeout); return request; } /* Add a pending request to edit the vCard. When it finishes, call the given * callback. The callback may be NULL. * * The method takes over the ownership of the callers reference to \a edits and * its contents. * * The connection must be connected to call this method. */ GabbleVCardManagerEditRequest * gabble_vcard_manager_edit (GabbleVCardManager *self, guint timeout, GabbleVCardManagerEditCb callback, gpointer user_data, GObject *object, GList *edits) { GabbleVCardManagerPrivate *priv = self->priv; TpBaseConnection *base = (TpBaseConnection *) priv->connection; GabbleVCardManagerEditRequest *req; GabbleVCardCacheEntry *entry; g_return_val_if_fail (tp_base_connection_get_status (base) == TP_CONNECTION_STATUS_CONNECTED, NULL); /* Invalidate our current vCard and ensure that we're going to get * it in the near future */ DEBUG ("called; invalidating cache"); gabble_vcard_manager_invalidate_cache (self, tp_base_connection_get_self_handle (base)); DEBUG ("checking if we have pending requests already"); entry = cache_entry_get (self, tp_base_connection_get_self_handle (base)); if (!priv->edit_pipeline_item && !entry->pending_requests) { DEBUG ("we don't, create one"); /* create dummy GET request if neccessary */ gabble_vcard_manager_request (self, tp_base_connection_get_self_handle (base), 0, NULL, NULL, NULL); } priv->edits = g_list_concat (priv->edits, edits); req = g_slice_new (GabbleVCardManagerEditRequest); req->manager = self; req->callback = callback; req->user_data = user_data; req->set_in_pipeline = FALSE; req->bound_object = object; if (NULL != object) g_object_weak_ref (object, notify_delete_edit_request, req); priv->edit_requests = g_list_append (priv->edit_requests, req); return req; } void gabble_vcard_manager_remove_edit_request (GabbleVCardManagerEditRequest *request) { GabbleVCardManagerPrivate *priv = request->manager->priv; DEBUG("request == %p", request); g_return_if_fail (request != NULL); g_assert (NULL != g_list_find (priv->edit_requests, request)); if (request->bound_object) g_object_weak_unref (request->bound_object, notify_delete_edit_request, request); g_slice_free (GabbleVCardManagerEditRequest, request); priv->edit_requests = g_list_remove (priv->edit_requests, request); } static void notify_delete_edit_request (gpointer data, GObject *obj) { GabbleVCardManagerEditRequest *request = data; DEBUG("request == %p", request); request->bound_object = NULL; gabble_vcard_manager_remove_edit_request (request); } static void cancel_all_edit_requests (GabbleVCardManager *self) { GabbleVCardManagerPrivate *priv = self->priv; GError cancelled = { GABBLE_VCARD_MANAGER_ERROR, GABBLE_VCARD_MANAGER_ERROR_CANCELLED, "Request cancelled" }; while (priv->edit_requests) { GabbleVCardManagerEditRequest *req = priv->edit_requests->data; if (req->callback) { (req->callback) (req->manager, req, NULL, &cancelled, req->user_data); } gabble_vcard_manager_remove_edit_request (req); } } void gabble_vcard_manager_cancel_request (GabbleVCardManager *self, GabbleVCardManagerRequest *request) { g_return_if_fail (GABBLE_IS_VCARD_MANAGER (self)); g_return_if_fail (NULL != request); g_return_if_fail (self == request->manager); cancel_request (request); } /** * Return cached message for the handle's vCard if it's available. */ gboolean gabble_vcard_manager_get_cached (GabbleVCardManager *self, TpHandle handle, WockyNode **node) { GabbleVCardManagerPrivate *priv = self->priv; GabbleVCardCacheEntry *entry = g_hash_table_lookup (priv->cache, GUINT_TO_POINTER (handle)); TpHandleRepoIface *contact_repo = tp_base_connection_get_handles ( (TpBaseConnection *) priv->connection, TP_HANDLE_TYPE_CONTACT); g_return_val_if_fail (tp_handle_is_valid (contact_repo, handle, NULL), FALSE); if ((entry == NULL) || (entry->vcard_node == NULL)) return FALSE; if (node != NULL) *node = wocky_node_tree_get_top_node (entry->vcard_node); return TRUE; } /** * Return the cached alias derived from the vCard for the given handle, * if any. If there is no cached alias, return NULL. */ const gchar * gabble_vcard_manager_get_cached_alias (GabbleVCardManager *self, TpHandle handle) { GabbleVCardManagerPrivate *priv; TpHandleRepoIface *contact_repo; const gchar *s; g_return_val_if_fail (GABBLE_IS_VCARD_MANAGER (self), NULL); priv = self->priv; contact_repo = tp_base_connection_get_handles ( (TpBaseConnection *) priv->connection, TP_HANDLE_TYPE_CONTACT); g_return_val_if_fail (tp_handle_is_valid (contact_repo, handle, NULL), NULL); s = tp_handle_get_qdata (contact_repo, handle, gabble_vcard_manager_cache_quark ()); if (s == NO_ALIAS) s = NULL; return s; } /** * Return TRUE if we've tried looking up an alias for this handle before. */ gboolean gabble_vcard_manager_has_cached_alias (GabbleVCardManager *self, TpHandle handle) { GabbleVCardManagerPrivate *priv; TpHandleRepoIface *contact_repo; gpointer p; g_return_val_if_fail (GABBLE_IS_VCARD_MANAGER (self), FALSE); priv = self->priv; contact_repo = tp_base_connection_get_handles ( (TpBaseConnection *) priv->connection, TP_HANDLE_TYPE_CONTACT); g_return_val_if_fail (tp_handle_is_valid (contact_repo, handle, NULL), FALSE); p = tp_handle_get_qdata (contact_repo, handle, gabble_vcard_manager_cache_quark ()); return p != NULL; } /* For unit tests only */ void gabble_vcard_manager_set_suspend_reply_timeout (guint timeout) { request_wait_delay = timeout; } void gabble_vcard_manager_set_default_request_timeout (guint timeout) { default_request_timeout = timeout; } GabbleVCardManagerEditInfo * gabble_vcard_manager_edit_info_new (const gchar *element_name, const gchar *element_value, GabbleVCardEditType edit_type, ...) { GabbleVCardManagerEditInfo *info; va_list ap; info = g_slice_new0 (GabbleVCardManagerEditInfo); info->element_name = g_strdup (element_name); info->edit_type = edit_type; va_start (ap, edit_type); switch (edit_type) { case GABBLE_VCARD_EDIT_REPLACE: case GABBLE_VCARD_EDIT_APPEND: g_return_val_if_fail (element_name != NULL, NULL); info->element = wocky_node_tree_new_va (element_name, NS_VCARD_TEMP, ap); va_end (ap); if (element_value != NULL) wocky_node_set_content (wocky_node_tree_get_top_node (info->element), element_value); break; case GABBLE_VCARD_EDIT_SET_ALIAS: g_return_val_if_fail (element_name == NULL, NULL); info->new_alias = g_strdup (element_value); element_value = NULL; /* deliberate fall-through to check the varargs... */ case GABBLE_VCARD_EDIT_DELETE: case GABBLE_VCARD_EDIT_CLEAR: { const gchar *first_edit = NULL; g_return_val_if_fail (element_value == NULL, NULL); first_edit = va_arg (ap, const gchar *); va_end (ap); g_return_val_if_fail (first_edit == NULL, NULL); break; } } return info; } void gabble_vcard_manager_edit_info_add_child ( GabbleVCardManagerEditInfo *edit_info, const gchar *key, const gchar *value) { g_return_if_fail (edit_info->element != NULL); wocky_node_add_child_with_content ( wocky_node_tree_get_top_node (edit_info->element), key, value); } void gabble_vcard_manager_edit_info_free (GabbleVCardManagerEditInfo *info) { g_free (info->element_name); g_free (info->new_alias); g_clear_object (&info->element); g_slice_free (GabbleVCardManagerEditInfo, info); } gboolean gabble_vcard_manager_has_limited_vcard_fields (GabbleVCardManager *self) { if (self->priv->connection->features & GABBLE_CONNECTION_FEATURES_GOOGLE_ROSTER) return TRUE; return FALSE; } gboolean gabble_vcard_manager_can_use_vcard_field (GabbleVCardManager *self, const gchar *field_name) { if (self->priv->connection->features & GABBLE_CONNECTION_FEATURES_GOOGLE_ROSTER) { /* Google's server only allows N, FN, PHOTO and URL */ if (tp_strdiff (field_name, "N") && tp_strdiff (field_name, "FN") && tp_strdiff (field_name, "PHOTO") && tp_strdiff (field_name, "URL")) { return FALSE; } } return TRUE; } telepathy-gabble-0.18.2/src/vcard-manager.h0000644000175000017500000001442612200204333020460 0ustar00smcvsmcv00000000000000/* * vcard-manager.h - vCard lookup helper for Gabble connections * * Copyright (C) 2006-2010 Collabora Ltd. * Copyright (C) 2006-2010 Nokia Corporation * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 __GABBLE_VCARD_MANAGER_H__ #define __GABBLE_VCARD_MANAGER_H__ #include #include #include "types.h" G_BEGIN_DECLS typedef struct _GabbleVCardManagerPrivate GabbleVCardManagerPrivate; typedef struct _GabbleVCardManagerClass GabbleVCardManagerClass; typedef struct _GabbleVCardManagerRequest GabbleVCardManagerRequest; typedef struct _GabbleVCardManagerEditRequest GabbleVCardManagerEditRequest; typedef struct _GabbleVCardManagerEditInfo GabbleVCardManagerEditInfo; /** * GabbleVCardManagerError: * @GABBLE_VCARD_MANAGER_ERROR_CANCELLED: The vCard request was cancelled * @GABBLE_VCARD_MANAGER_ERROR_TIMEOUT: The vCard request timed out * @GABBLE_VCARD_MANAGER_ERROR_UNKNOWN: An unknown error occured */ typedef enum { GABBLE_VCARD_MANAGER_ERROR_CANCELLED, GABBLE_VCARD_MANAGER_ERROR_TIMEOUT, GABBLE_VCARD_MANAGER_ERROR_UNKNOWN } GabbleVCardManagerError; GQuark gabble_vcard_manager_error_quark (void); #define GABBLE_VCARD_MANAGER_ERROR gabble_vcard_manager_error_quark () GType gabble_vcard_manager_get_type (void); /* TYPE MACROS */ #define GABBLE_TYPE_VCARD_MANAGER \ (gabble_vcard_manager_get_type ()) #define GABBLE_VCARD_MANAGER(obj) \ (G_TYPE_CHECK_INSTANCE_CAST((obj), GABBLE_TYPE_VCARD_MANAGER, \ GabbleVCardManager)) #define GABBLE_VCARD_MANAGER_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST((klass), GABBLE_TYPE_VCARD_MANAGER, \ GabbleVCardManagerClass)) #define GABBLE_IS_VCARD_MANAGER(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE((obj), GABBLE_TYPE_VCARD_MANAGER)) #define GABBLE_IS_VCARD_MANAGER_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE((klass), GABBLE_TYPE_VCARD_MANAGER)) #define GABBLE_VCARD_MANAGER_GET_CLASS(obj) \ (G_TYPE_INSTANCE_GET_CLASS ((obj), GABBLE_TYPE_VCARD_MANAGER, \ GabbleVCardManagerClass)) struct _GabbleVCardManagerClass { GObjectClass parent_class; }; struct _GabbleVCardManager { GObject parent; GabbleVCardManagerPrivate *priv; }; typedef enum { GABBLE_VCARD_EDIT_REPLACE, GABBLE_VCARD_EDIT_APPEND, GABBLE_VCARD_EDIT_DELETE, GABBLE_VCARD_EDIT_CLEAR, GABBLE_VCARD_EDIT_SET_ALIAS } GabbleVCardEditType; typedef void (*GabbleVCardManagerCb)(GabbleVCardManager *self, GabbleVCardManagerRequest *request, TpHandle handle, WockyNode *vcard, GError *error, gpointer user_data); GabbleVCardManager *gabble_vcard_manager_new (GabbleConnection *); GQuark gabble_vcard_manager_cache_quark (void); GabbleVCardManagerRequest *gabble_vcard_manager_request (GabbleVCardManager *, TpHandle, guint timeout, GabbleVCardManagerCb, gpointer user_data, GObject *object); void gabble_vcard_manager_cancel_request (GabbleVCardManager *manager, GabbleVCardManagerRequest *request); const gchar *gabble_vcard_manager_get_cached_alias (GabbleVCardManager *, TpHandle); gboolean gabble_vcard_manager_has_cached_alias (GabbleVCardManager *manager, TpHandle handle); gboolean gabble_vcard_manager_get_cached (GabbleVCardManager *, TpHandle, WockyNode **); void gabble_vcard_manager_invalidate_cache (GabbleVCardManager *, TpHandle); typedef void (*GabbleVCardManagerEditCb)(GabbleVCardManager *self, GabbleVCardManagerEditRequest *request, WockyNode *vcard, GError *error, gpointer user_data); GabbleVCardManagerEditRequest *gabble_vcard_manager_edit (GabbleVCardManager *, guint timeout, GabbleVCardManagerEditCb, gpointer user_data, GObject *object, GList *edits); void gabble_vcard_manager_remove_edit_request (GabbleVCardManagerEditRequest *); gchar *vcard_get_avatar_sha1 (WockyNode *vcard); GabbleVCardManagerEditInfo *gabble_vcard_manager_edit_info_new ( const gchar *element_name, const gchar *element_value, GabbleVCardEditType edit_type, ...) G_GNUC_NULL_TERMINATED; void gabble_vcard_manager_edit_info_add_child ( GabbleVCardManagerEditInfo *edit_info, const gchar *key, const gchar *value); void gabble_vcard_manager_edit_info_free (GabbleVCardManagerEditInfo *info); gboolean gabble_vcard_manager_has_limited_vcard_fields ( GabbleVCardManager *self); gboolean gabble_vcard_manager_can_use_vcard_field (GabbleVCardManager *self, const gchar *field_name); /* For unit tests only */ void gabble_vcard_manager_set_suspend_reply_timeout (guint timeout); void gabble_vcard_manager_set_default_request_timeout (guint timeout); G_END_DECLS #endif telepathy-gabble-0.18.2/src/util.c0000644000175000017500000010240112200204333016710 0ustar00smcvsmcv00000000000000/* * util.c - Source for Gabble utility functions * Copyright (C) 2006-2007 Collabora Ltd. * Copyright (C) 2006-2007 Nokia Corporation * @author Robert McQueen * @author Simon McVittie * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 "config.h" #include "util.h" #include #include #include #include #include #include #include #define DEBUG_FLAG GABBLE_DEBUG_JID #include "conn-aliasing.h" #include "connection.h" #include "debug.h" #include "namespaces.h" #include "presence-cache.h" gchar * sha1_hex (const gchar *bytes, guint len) { gchar *hex = g_compute_checksum_for_string (G_CHECKSUM_SHA1, bytes, len); guint i; for (i = 0; i < SHA1_HASH_SIZE * 2; i++) { g_assert (hex[i] != '\0'); hex[i] = g_ascii_tolower (hex[i]); } g_assert (hex[SHA1_HASH_SIZE * 2] == '\0'); return hex; } void sha1_bin (const gchar *bytes, guint len, guchar out[SHA1_HASH_SIZE]) { GChecksum *checksum = g_checksum_new (G_CHECKSUM_SHA1); gsize out_len = SHA1_HASH_SIZE; g_assert (g_checksum_type_get_length (G_CHECKSUM_SHA1) == SHA1_HASH_SIZE); g_checksum_update (checksum, (const guchar *) bytes, len); g_checksum_get_digest (checksum, out, &out_len); g_assert (out_len == SHA1_HASH_SIZE); g_checksum_free (checksum); } /** gabble_generate_id: * * RFC4122 version 4 compliant random UUIDs generator. * * Returns: A string with RFC41122 version 4 random UUID, must be freed with * g_free(). */ gchar * gabble_generate_id (void) { GRand *grand; gchar *str; struct { guint32 time_low; guint16 time_mid; guint16 time_hi_and_version; guint8 clock_seq_hi_and_rsv; guint8 clock_seq_low; guint16 node_hi; guint32 node_low; } uuid; /* Fill with random. Every new GRand are seede with 128 bit read from * /dev/urandom (or the current time on non-unix systems). This makes the * random source good enough for our usage, but may not be suitable for all * situation outside Gabble. */ grand = g_rand_new (); uuid.time_low = g_rand_int (grand); uuid.time_mid = (guint16) g_rand_int_range (grand, 0, G_MAXUINT16); uuid.time_hi_and_version = (guint16) g_rand_int_range (grand, 0, G_MAXUINT16); uuid.clock_seq_hi_and_rsv = (guint8) g_rand_int_range (grand, 0, G_MAXUINT8); uuid.clock_seq_low = (guint8) g_rand_int_range (grand, 0, G_MAXUINT8); uuid.node_hi = (guint16) g_rand_int_range (grand, 0, G_MAXUINT16); uuid.node_low = g_rand_int (grand); g_rand_free (grand); /* Set the two most significant bits (bits 6 and 7) of the * clock_seq_hi_and_rsv to zero and one, respectively. */ uuid.clock_seq_hi_and_rsv = (uuid.clock_seq_hi_and_rsv & 0x3F) | 0x80; /* Set the four most significant bits (bits 12 through 15) of the * time_hi_and_version field to 4 */ uuid.time_hi_and_version = (uuid.time_hi_and_version & 0x0fff) | 0x4000; str = g_strdup_printf ("%08x-%04x-%04x-%02x%02x-%04x%08x", uuid.time_low, uuid.time_mid, uuid.time_hi_and_version, uuid.clock_seq_hi_and_rsv, uuid.clock_seq_low, uuid.node_hi, uuid.node_low); return str; } void lm_message_node_add_own_nick (WockyNode *node, GabbleConnection *connection) { gchar *nick; GabbleConnectionAliasSource source; TpBaseConnection *base = (TpBaseConnection *) connection; source = _gabble_connection_get_cached_alias (connection, tp_base_connection_get_self_handle (base), &nick); if (source > GABBLE_CONNECTION_ALIAS_FROM_JID) wocky_node_add_child_with_content_ns_q (node, "nick", nick, g_quark_from_static_string (NS_NICK)); g_free (nick); } /** * gabble_get_room_handle_from_jid: * @room_repo: The %TP_HANDLE_TYPE_ROOM handle repository * @jid: A JID * * Given a JID seen in the from="" attribute on a stanza, work out whether * it's something to do with a MUC, and if so, return its handle. * * Returns: The handle of the MUC, if the JID refers to either a MUC * we're in, or a contact's channel-specific JID inside a MUC. * Returns 0 if the JID is either invalid, or nothing to do with a * known MUC (typically this will mean it's the global JID of a contact). */ TpHandle gabble_get_room_handle_from_jid (TpHandleRepoIface *room_repo, const gchar *jid) { TpHandle handle; gchar *room; room = gabble_remove_resource (jid); if (room == NULL) return 0; handle = tp_handle_lookup (room_repo, room, NULL, NULL); g_free (room); return handle; } #define INVALID_HANDLE(e, f, ...) \ G_STMT_START { \ DEBUG (f, ##__VA_ARGS__); \ g_set_error (e, TP_ERROR, TP_ERROR_INVALID_HANDLE, f, ##__VA_ARGS__);\ } G_STMT_END gchar * gabble_normalize_room (TpHandleRepoIface *repo, const gchar *jid, gpointer context, GError **error) { GabbleConnection *conn; gchar *qualified_name, *resource; /* Only look up the canonical room name if we got a GabbleConnection. * This should only happen in the test-handles test. */ if (context != NULL) { conn = GABBLE_CONNECTION (context); qualified_name = gabble_connection_get_canonical_room_name (conn, jid); if (qualified_name == NULL) { INVALID_HANDLE (error, "requested room handle %s does not specify a server, but we " "have not discovered any local conference servers and no " "fallback was provided", jid); return NULL; } } else { qualified_name = g_strdup (jid); } if (!wocky_decode_jid (qualified_name, NULL, NULL, &resource)) { INVALID_HANDLE (error, "room JID %s is invalid", qualified_name); return NULL; } if (resource != NULL) { INVALID_HANDLE (error, "invalid room JID %s: contains nickname part after '/' too", qualified_name); g_free (qualified_name); g_free (resource); return NULL; } return qualified_name; } gchar * gabble_remove_resource (const gchar *jid) { char *slash = strchr (jid, '/'); gchar *buf; if (slash == NULL) return g_strdup (jid); /* The user and domain parts can't contain '/', assuming it's valid */ buf = g_malloc (slash - jid + 1); strncpy (buf, jid, slash - jid); buf[slash - jid] = '\0'; return buf; } gchar * gabble_encode_jid ( const gchar *node, const gchar *domain, const gchar *resource) { gchar *tmp, *ret; g_return_val_if_fail (domain != NULL, NULL); if (node != NULL && resource != NULL) tmp = g_strdup_printf ("%s@%s/%s", node, domain, resource); else if (node != NULL) tmp = g_strdup_printf ("%s@%s", node, domain); else if (resource != NULL) tmp = g_strdup_printf ("%s/%s", domain, resource); else tmp = g_strdup (domain); ret = g_utf8_normalize (tmp, -1, G_NORMALIZE_NFKC); g_free (tmp); return ret; } /* * gabble_normalize_contact * @repo: The %TP_HANDLE_TYPE_ROOM handle repository or NULL * @jid: A JID * @context: One of %GabbleNormalizeContactJIDMode casted into gpointer * @error: pointer in which to return a GError in case of failure. * * Normalize contact JID. If @repo is provided and the context is not * clear (we don't know for sure whether it's global or room JID), it's * used to try and detect room JIDs. * * Returns: Normalized JID. */ gchar * gabble_normalize_contact (TpHandleRepoIface *repo, const gchar *jid, gpointer context, GError **error) { guint mode = GPOINTER_TO_UINT (context); gchar *username = NULL, *server = NULL, *resource = NULL; gchar *ret = NULL; if (!wocky_decode_jid (jid, &username, &server, &resource) || !username) { INVALID_HANDLE (error, "JID %s is invalid or has no node part", jid); goto OUT; } if (mode == GABBLE_JID_ROOM_MEMBER && resource == NULL) { INVALID_HANDLE (error, "JID %s can't be a room member - it has no resource", jid); goto OUT; } if (mode != GABBLE_JID_GLOBAL && resource != NULL) { ret = gabble_encode_jid (username, server, resource); if (mode == GABBLE_JID_ROOM_MEMBER || (repo != NULL && tp_dynamic_handle_repo_lookup_exact (repo, ret))) { /* either we know from context that it's a room member, or we * already saw that contact in a room. Use ret as our answer */ goto OUT; } else { g_free (ret); } } /* if we get here, we suspect it's a global JID, either because the context * says it is, or because the context isn't sure and we haven't seen it in * use as a room member */ ret = gabble_encode_jid (username, server, NULL); OUT: g_free (username); g_free (server); g_free (resource); return ret; } /** * lm_message_node_extract_properties * * Map a XML node to a properties hash table * (used to parse a subset of the OLPC and tubes protocol) * * Example: * * * prop1_value * 7 * * * lm_message_node_extract_properties (node, "prop"); * * --> { "prop1" : "prop1_value", "prop2" : 7 } * * Returns a hash table mapping names to GValue of the specified type. * Valid types are: str, int, uint, bytes. * */ GHashTable * lm_message_node_extract_properties (WockyNode *node, const gchar *prop) { GHashTable *properties; WockyNodeIter i; WockyNode *child; properties = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify) tp_g_value_slice_free); if (node == NULL) return properties; wocky_node_iter_init (&i, node, prop, NULL); while (wocky_node_iter_next (&i, &child)) { const gchar *name = wocky_node_get_attribute (child, "name"); const gchar *type = wocky_node_get_attribute (child, "type"); const gchar *value = child->content; GValue *gvalue; if (name == NULL || type == NULL || value == NULL) continue; if (0 == strcmp (type, "bytes")) { GArray *arr; guchar *st; gsize outlen; st = g_base64_decode (value, &outlen); if (!st) continue; arr = g_array_new (FALSE, FALSE, sizeof (guchar)); g_array_append_vals (arr, st, outlen); gvalue = g_slice_new0 (GValue); g_value_init (gvalue, DBUS_TYPE_G_UCHAR_ARRAY); g_value_take_boxed (gvalue, arr); g_hash_table_insert (properties, g_strdup (name), gvalue); g_free (st); } else if (0 == strcmp (type, "str")) { gvalue = g_slice_new0 (GValue); g_value_init (gvalue, G_TYPE_STRING); g_value_set_string (gvalue, value); g_hash_table_insert (properties, g_strdup (name), gvalue); } else if (0 == strcmp (type, "int")) { gvalue = g_slice_new0 (GValue); g_value_init (gvalue, G_TYPE_INT); g_value_set_int (gvalue, strtol (value, NULL, 10)); g_hash_table_insert (properties, g_strdup (name), gvalue); } else if (0 == strcmp (type, "uint")) { gvalue = g_slice_new0 (GValue); g_value_init (gvalue, G_TYPE_UINT); g_value_set_uint (gvalue, strtoul (value, NULL, 10)); g_hash_table_insert (properties, g_strdup (name), gvalue); } else if (0 == strcmp (type, "bool")) { gboolean val; if (!tp_strdiff (value, "0") || !tp_strdiff (value, "false")) { val = FALSE; } else if (!tp_strdiff (value, "1") || !tp_strdiff (value, "true")) { val = TRUE; } else { DEBUG ("invalid boolean value: %s", value); continue; } gvalue = g_slice_new0 (GValue); g_value_init (gvalue, G_TYPE_BOOLEAN); g_value_set_boolean (gvalue, val); g_hash_table_insert (properties, g_strdup (name), gvalue); } } return properties; } struct _set_child_from_property_data { WockyNode *node; const gchar *prop; }; static void set_child_from_property (gpointer key, gpointer value, gpointer user_data) { GValue *gvalue = value; struct _set_child_from_property_data *data = (struct _set_child_from_property_data *) user_data; WockyNode *child; const char *type = NULL; if (G_VALUE_TYPE (gvalue) == G_TYPE_STRING) { type = "str"; } else if (G_VALUE_TYPE (gvalue) == DBUS_TYPE_G_UCHAR_ARRAY) { type = "bytes"; } else if (G_VALUE_TYPE (gvalue) == G_TYPE_INT) { type = "int"; } else if (G_VALUE_TYPE (gvalue) == G_TYPE_UINT) { type = "uint"; } else if (G_VALUE_TYPE (gvalue) == G_TYPE_BOOLEAN) { type = "bool"; } else { /* a type we don't know how to handle: ignore it */ DEBUG ("property with unknown type \"%s\"", g_type_name (G_VALUE_TYPE (gvalue))); return; } child = wocky_node_add_child (data->node, data->prop); if (G_VALUE_TYPE (gvalue) == G_TYPE_STRING) { wocky_node_set_content (child, g_value_get_string (gvalue)); } else if (G_VALUE_TYPE (gvalue) == DBUS_TYPE_G_UCHAR_ARRAY) { GArray *arr; gchar *str; type = "bytes"; arr = g_value_get_boxed (gvalue); str = g_base64_encode ((guchar *) arr->data, arr->len); wocky_node_set_content (child, str); g_free (str); } else if (G_VALUE_TYPE (gvalue) == G_TYPE_INT) { gchar *str; str = g_strdup_printf ("%d", g_value_get_int (gvalue)); wocky_node_set_content (child, str); g_free (str); } else if (G_VALUE_TYPE (gvalue) == G_TYPE_UINT) { gchar *str; str = g_strdup_printf ("%u", g_value_get_uint (gvalue)); wocky_node_set_content (child, str); g_free (str); } else if (G_VALUE_TYPE (gvalue) == G_TYPE_BOOLEAN) { /* we output as "0" or "1" despite the canonical representation for * xs:boolean being "false" or "true", for compatibility with older * Gabble versions (OLPC Trial-3) */ wocky_node_set_content (child, g_value_get_boolean (gvalue) ? "1" : "0"); } else { g_assert_not_reached (); } wocky_node_set_attribute (child, "name", key); wocky_node_set_attribute (child, "type", type); } /** * * lm_message_node_set_children_from_properties * * Map a properties hash table to a XML node. * * Example: * * properties = { "prop1" : "prop1_value", "prop2" : 7 } * * lm_message_node_add_children_from_properties (node, properties, "prop"); * * --> * prop1_value * 7 * * */ void lm_message_node_add_children_from_properties (WockyNode *node, GHashTable *properties, const gchar *prop) { struct _set_child_from_property_data data; data.node = node; data.prop = prop; g_hash_table_foreach (properties, set_child_from_property, &data); } typedef struct { GObject *instance; GObject *user_data; gulong handler_id; } WeakHandlerCtx; static WeakHandlerCtx * whc_new (GObject *instance, GObject *user_data) { WeakHandlerCtx *ctx = g_slice_new0 (WeakHandlerCtx); ctx->instance = instance; ctx->user_data = user_data; return ctx; } static void whc_free (WeakHandlerCtx *ctx) { g_slice_free (WeakHandlerCtx, ctx); } static void user_data_destroyed_cb (gpointer, GObject *); static void instance_destroyed_cb (gpointer ctx_, GObject *where_the_instance_was) { WeakHandlerCtx *ctx = ctx_; DEBUG ("instance for %p destroyed; cleaning up", ctx); /* No need to disconnect the signal here, the instance has gone away. */ g_object_weak_unref (ctx->user_data, user_data_destroyed_cb, ctx); whc_free (ctx); } static void user_data_destroyed_cb (gpointer ctx_, GObject *where_the_user_data_was) { WeakHandlerCtx *ctx = ctx_; DEBUG ("user_data for %p destroyed; disconnecting", ctx); g_signal_handler_disconnect (ctx->instance, ctx->handler_id); g_object_weak_unref (ctx->instance, instance_destroyed_cb, ctx); whc_free (ctx); } /** * gabble_signal_connect_weak: * @instance: the instance to connect to. * @detailed_signal: a string of the form "signal-name::detail". * @c_handler: the GCallback to connect. * @user_data: an object to pass as data to c_handler calls. * * Connects a #GCallback function to a signal for a particular object, as if * with g_signal_connect(). Additionally, arranges for the signal handler to be * disconnected if @user_data is destroyed. * * This is intended to be a convenient way for objects to use themselves as * user_data for callbacks without having to explicitly disconnect all the * handlers in their finalizers. */ void gabble_signal_connect_weak (gpointer instance, const gchar *detailed_signal, GCallback c_handler, GObject *user_data) { GObject *instance_obj = G_OBJECT (instance); WeakHandlerCtx *ctx = whc_new (instance_obj, user_data); DEBUG ("connecting to %p:%s with context %p", instance, detailed_signal, ctx); ctx->handler_id = g_signal_connect (instance, detailed_signal, c_handler, user_data); g_object_weak_ref (instance_obj, instance_destroyed_cb, ctx); g_object_weak_ref (user_data, user_data_destroyed_cb, ctx); } typedef struct { GSourceFunc function; GObject *object; guint source_id; } WeakIdleCtx; static void idle_weak_ref_notify (gpointer data, GObject *dead_object) { g_source_remove (GPOINTER_TO_UINT (data)); } static void idle_removed (gpointer data) { WeakIdleCtx *ctx = (WeakIdleCtx *) data; g_slice_free (WeakIdleCtx, ctx); } static gboolean idle_callback (gpointer data) { WeakIdleCtx *ctx = (WeakIdleCtx *) data; if (ctx->function ((gpointer) ctx->object)) { return TRUE; } else { g_object_weak_unref ( ctx->object, idle_weak_ref_notify, GUINT_TO_POINTER (ctx->source_id)); return FALSE; } } /* Like g_idle_add(), but cancel the callback if the provided object is * finalized. */ guint gabble_idle_add_weak (GSourceFunc function, GObject *object) { WeakIdleCtx *ctx; ctx = g_slice_new0 (WeakIdleCtx); ctx->function = function; ctx->object = object; ctx->source_id = g_idle_add_full ( G_PRIORITY_DEFAULT_IDLE, idle_callback, ctx, idle_removed); g_object_weak_ref ( object, idle_weak_ref_notify, GUINT_TO_POINTER (ctx->source_id)); return ctx->source_id; } GPtrArray * gabble_g_ptr_array_copy (GPtrArray *source) { GPtrArray *ret = g_ptr_array_sized_new (source->len); guint i; for (i = 0; i < source->len; i++) g_ptr_array_add (ret, g_ptr_array_index (source, i)); return ret; } WockyBareContact * ensure_bare_contact_from_jid (GabbleConnection *conn, const gchar *jid) { WockyContactFactory *contact_factory; contact_factory = wocky_session_get_contact_factory (conn->session); return wocky_contact_factory_ensure_bare_contact (contact_factory, jid); } TpHandle ensure_handle_from_contact ( GabbleConnection *conn, WockyContact *contact) { TpHandleRepoIface *contact_repo = tp_base_connection_get_handles ( (TpBaseConnection *) conn, TP_HANDLE_TYPE_CONTACT); gchar *jid = wocky_contact_dup_jid (contact); GError *error = NULL; TpHandle handle = tp_handle_ensure (contact_repo, jid, NULL, &error); if (handle == 0) { g_critical ("Contact %p has JID '%s' which is not valid: %s", contact, jid, error->message); g_clear_error (&error); } g_free (jid); return handle; } #ifdef ENABLE_VOIP #define TWICE(x) x, x static gboolean jingle_pick_resource_or_bare_jid (GabblePresence *presence, GabbleCapabilitySet *caps, const gchar **resource) { const gchar *ret; if (gabble_presence_has_resources (presence)) { ret = gabble_presence_pick_resource_by_caps (presence, GABBLE_CLIENT_TYPE_PHONE, gabble_capability_set_predicate_at_least, caps); if (resource != NULL) *resource = ret; return (ret != NULL); } else if (gabble_capability_set_at_least ( gabble_presence_peek_caps (presence), caps)) { if (resource != NULL) *resource = NULL; return TRUE; } else { return FALSE; } } gboolean jingle_pick_best_resource (GabbleConnection *conn, TpHandle peer, gboolean want_audio, gboolean want_video, const char **transport_ns, WockyJingleDialect *dialect, const gchar **resource_out) { /* We prefer gtalk-p2p to ice, because it can use tcp and https relays (if * available). */ static const GabbleFeatureFallback transports[] = { { TRUE, TWICE (NS_GOOGLE_TRANSPORT_P2P) }, { TRUE, TWICE (NS_JINGLE_TRANSPORT_ICEUDP) }, { TRUE, TWICE (NS_JINGLE_TRANSPORT_RAWUDP) }, { FALSE, NULL, NULL } }; GabblePresence *presence; GabbleCapabilitySet *caps; const gchar *resource = NULL; gboolean success = FALSE; presence = gabble_presence_cache_get (conn->presence_cache, peer); if (presence == NULL) { DEBUG ("contact %d has no presence available", peer); return FALSE; } *dialect = WOCKY_JINGLE_DIALECT_ERROR; *transport_ns = NULL; g_return_val_if_fail (want_audio || want_video, FALSE); /* from here on, goto FINALLY to free this, instead of returning early */ caps = gabble_capability_set_new (); /* Try newest Jingle standard */ gabble_capability_set_add (caps, NS_JINGLE_RTP); if (want_audio) gabble_capability_set_add (caps, NS_JINGLE_RTP_AUDIO); if (want_video) gabble_capability_set_add (caps, NS_JINGLE_RTP_VIDEO); if (jingle_pick_resource_or_bare_jid (presence, caps, &resource)) { *dialect = WOCKY_JINGLE_DIALECT_V032; goto CHOOSE_TRANSPORT; } /* Else try older Jingle draft */ gabble_capability_set_clear (caps); if (want_audio) gabble_capability_set_add (caps, NS_JINGLE_DESCRIPTION_AUDIO); if (want_video) gabble_capability_set_add (caps, NS_JINGLE_DESCRIPTION_VIDEO); if (jingle_pick_resource_or_bare_jid (presence, caps, &resource)) { *dialect = WOCKY_JINGLE_DIALECT_V015; goto CHOOSE_TRANSPORT; } /* The Google dialects can't do video alone. */ if (!want_audio) { DEBUG ("No resource which supports video alone available"); goto FINALLY; } /* Okay, let's try GTalk 0.3, possibly with video. */ gabble_capability_set_clear (caps); gabble_capability_set_add (caps, NS_GOOGLE_FEAT_VOICE); if (want_video) gabble_capability_set_add (caps, NS_GOOGLE_FEAT_VIDEO); if (jingle_pick_resource_or_bare_jid (presence, caps, &resource)) { *dialect = WOCKY_JINGLE_DIALECT_GTALK3; goto CHOOSE_TRANSPORT; } if (want_video) { DEBUG ("No resource which supports audio+video available"); goto FINALLY; } /* Maybe GTalk 0.4 will save us all... ? */ gabble_capability_set_clear (caps); gabble_capability_set_add (caps, NS_GOOGLE_FEAT_VOICE); gabble_capability_set_add (caps, NS_GOOGLE_TRANSPORT_P2P); if (jingle_pick_resource_or_bare_jid (presence, caps, &resource)) { *dialect = WOCKY_JINGLE_DIALECT_GTALK4; goto CHOOSE_TRANSPORT; } /* Nope, nothing we can do. */ goto FINALLY; CHOOSE_TRANSPORT: if (resource_out != NULL) *resource_out = resource; success = TRUE; if (*dialect == WOCKY_JINGLE_DIALECT_GTALK4 || *dialect == WOCKY_JINGLE_DIALECT_GTALK3) { /* the GTalk dialects only support google p2p as transport protocol. */ *transport_ns = NS_GOOGLE_TRANSPORT_P2P; } else if (resource == NULL) { *transport_ns = gabble_presence_pick_best_feature (presence, transports, gabble_capability_set_predicate_has); } else { *transport_ns = gabble_presence_resource_pick_best_feature (presence, resource, transports, gabble_capability_set_predicate_has); } if (*transport_ns == NULL) success = FALSE; FINALLY: gabble_capability_set_free (caps); return success; } const gchar * jingle_pick_best_content_type (GabbleConnection *conn, TpHandle peer, const gchar *resource, WockyJingleMediaType type) { GabblePresence *presence; const GabbleFeatureFallback content_types[] = { /* if $thing is supported, then use it */ { TRUE, TWICE (NS_JINGLE_RTP) }, { type == WOCKY_JINGLE_MEDIA_TYPE_VIDEO, TWICE (NS_JINGLE_DESCRIPTION_VIDEO) }, { type == WOCKY_JINGLE_MEDIA_TYPE_AUDIO, TWICE (NS_JINGLE_DESCRIPTION_AUDIO) }, /* odd Google ones: if $thing is supported, use $other_thing */ { type == WOCKY_JINGLE_MEDIA_TYPE_AUDIO, NS_GOOGLE_FEAT_VOICE, NS_GOOGLE_SESSION_PHONE }, { type == WOCKY_JINGLE_MEDIA_TYPE_VIDEO, NS_GOOGLE_FEAT_VIDEO, NS_GOOGLE_SESSION_VIDEO }, { FALSE, NULL, NULL } }; presence = gabble_presence_cache_get (conn->presence_cache, peer); if (presence == NULL) { DEBUG ("contact %d has no presence available", peer); return NULL; } if (resource == NULL) { return gabble_presence_pick_best_feature (presence, content_types, gabble_capability_set_predicate_has); } else { return gabble_presence_resource_pick_best_feature (presence, resource, content_types, gabble_capability_set_predicate_has); } } static TpCallStreamCandidateType tp_candidate_type_from_jingle (WockyJingleCandidateType type) { switch (type) { default: /* Consider UNKNOWN as LOCAL/HOST */ case WOCKY_JINGLE_CANDIDATE_TYPE_LOCAL: return TP_CALL_STREAM_CANDIDATE_TYPE_HOST; case WOCKY_JINGLE_CANDIDATE_TYPE_STUN: return TP_CALL_STREAM_CANDIDATE_TYPE_SERVER_REFLEXIVE; case WOCKY_JINGLE_CANDIDATE_TYPE_RELAY: return TP_CALL_STREAM_CANDIDATE_TYPE_RELAY; } } /** * @candidates: (element-type WockyJingleCandidate): candidates * * Returns: (transfer full): a GABBLE_ARRAY_TYPE_CANDIDATE_LIST, i.e. * a(usqa{sv}) */ GPtrArray * gabble_call_candidates_to_array (GList *candidates) { GPtrArray *arr; GList *c; arr = g_ptr_array_sized_new (g_list_length (candidates)); for (c = candidates; c != NULL; c = g_list_next (c)) { WockyJingleCandidate *cand = (WockyJingleCandidate *) c->data; GValueArray *a; GHashTable *info; info = tp_asv_new ( "protocol", G_TYPE_UINT, cand->protocol, "type", G_TYPE_UINT, tp_candidate_type_from_jingle (cand->type), "foundation", G_TYPE_STRING, cand->id, "priority", G_TYPE_UINT, cand->preference, "username", G_TYPE_STRING, cand->username, "password", G_TYPE_STRING, cand->password, NULL); a = tp_value_array_build (4, G_TYPE_UINT, cand->component, G_TYPE_STRING, cand->address, G_TYPE_UINT, cand->port, TP_HASH_TYPE_CANDIDATE_INFO, info, G_TYPE_INVALID); g_ptr_array_add (arr, a); } return arr; } #endif gchar * gabble_peer_to_jid (GabbleConnection *conn, TpHandle peer, const gchar *resource) { TpHandleRepoIface *repo = tp_base_connection_get_handles ( TP_BASE_CONNECTION (conn), TP_HANDLE_TYPE_CONTACT); const gchar *target = tp_handle_inspect (repo, peer); if (resource == NULL) return g_strdup (target); return g_strdup_printf ("%s/%s", target, resource); } /* Like wocky_enum_from_nick, but for GFlagsValues instead. */ gboolean gabble_flag_from_nick (GType flag_type, const gchar *nick, guint *value) { GFlagsClass *klass = g_type_class_ref (flag_type); GFlagsValue *flag_value; g_return_val_if_fail (klass != NULL, FALSE); g_return_val_if_fail (value != NULL, FALSE); flag_value = g_flags_get_value_by_nick (klass, nick); g_type_class_unref (klass); if (flag_value != NULL) { *value = flag_value->value; return TRUE; } else { return FALSE; } } /** * gabble_simple_async_succeed_or_fail_in_idle: * @self: the source object for an asynchronous function * @callback: a callback to call when @todo things have been done * @user_data: user data for the callback * @source_tag: the source tag for a #GSimpleAsyncResult * @error: (allow-none): %NULL to indicate success, or an error on failure * * Create a new #GSimpleAsyncResult and schedule it to call its callback * in an idle. If @error is %NULL, report success with * tp_simple_async_report_success_in_idle(); if @error is non-%NULL, * use g_simple_async_report_gerror_in_idle(). */ void gabble_simple_async_succeed_or_fail_in_idle (gpointer self, GAsyncReadyCallback callback, gpointer user_data, gpointer source_tag, const GError *error) { if (error == NULL) { tp_simple_async_report_success_in_idle (self, callback, user_data, source_tag); } else { /* not const-correct yet: GNOME #622004 */ g_simple_async_report_gerror_in_idle (self, callback, user_data, (GError *) error); } } /** * gabble_simple_async_countdown_new: * @self: the source object for an asynchronous function * @callback: a callback to call when @todo things have been done * @user_data: user data for the callback * @source_tag: the source tag for a #GSimpleAsyncResult * @todo: number of things to do before calling @callback (at least 1) * * Create a new #GSimpleAsyncResult that will call its callback when a number * of asynchronous operations have happened. * * An internal counter is initialized to @todo, incremented with * gabble_simple_async_countdown_inc() or decremented with * gabble_simple_async_countdown_dec(). * * When that counter reaches zero, if an error has been set with * g_simple_async_result_set_from_error() or similar, the operation fails; * otherwise, it succeeds. * * The caller must not use the operation result functions, such as * g_simple_async_result_get_op_res_gssize() - this async result is only * suitable for "void" async methods which return either success or a #GError, * i.e. the same signature as g_async_initable_init_async(). * * Returns: (transfer full): a counter */ GSimpleAsyncResult * gabble_simple_async_countdown_new (gpointer self, GAsyncReadyCallback callback, gpointer user_data, gpointer source_tag, gssize todo) { GSimpleAsyncResult *simple; g_return_val_if_fail (todo >= 1, NULL); simple = g_simple_async_result_new (self, callback, user_data, source_tag); /* We (ab)use the op_res member as a count of things to do. When * it reaches zero, the operation completes with any error that has been * set, or with success. */ g_simple_async_result_set_op_res_gssize (simple, todo); /* we keep one extra reference as long as the counter is nonzero */ g_object_ref (simple); return simple; } /** * gabble_simple_async_countdown_inc: * @simple: a result created by gabble_simple_async_countdown_new() * * Increment the counter in @simple, indicating that an additional async * operation has been started. An additional call to * gabble_simple_async_countdown_dec() will be needed to make @simple * call its callback. */ void gabble_simple_async_countdown_inc (GSimpleAsyncResult *simple) { gssize todo = g_simple_async_result_get_op_res_gssize (simple); g_return_if_fail (todo >= 1); g_simple_async_result_set_op_res_gssize (simple, todo + 1); } /** * gabble_simple_async_countdown_dec: * @simple: a result created by gabble_simple_async_countdown_new() * * Decrement the counter in @simple. If the number of things to do has * reached zero, schedule @simple to call its callback in an idle, then * unref it. * * When one of the asynchronous operations needed for @simple succeeds, * this should be signalled by a call to this function. * * When one of the asynchronous operations needed for @simple fails, * this should be signalled by a call to g_simple_async_result_set_from_error() * (or one of the similar functions), followed by a call to this function. * If more than one async operation fails in this way, the #GError from the * last failure will be used. */ void gabble_simple_async_countdown_dec (GSimpleAsyncResult *simple) { gssize todo = g_simple_async_result_get_op_res_gssize (simple); g_simple_async_result_set_op_res_gssize (simple, --todo); if (todo <= 0) { g_simple_async_result_complete_in_idle (simple); g_object_unref (simple); } } telepathy-gabble-0.18.2/src/util.h0000644000175000017500000001120012200204333016711 0ustar00smcvsmcv00000000000000/* * util.h - Headers for Gabble utility functions * Copyright (C) 2006 Collabora Ltd. * Copyright (C) 2006 Nokia Corporation * @author Robert McQueen * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 __GABBLE_UTIL_H__ #define __GABBLE_UTIL_H__ #include #include #include #include #include "types.h" /* Guarantees that the resulting hash is in lower-case */ gchar *sha1_hex (const gchar *bytes, guint len); /* A SHA1 digest is 20 bytes long */ #define SHA1_HASH_SIZE 20 void sha1_bin (const gchar *bytes, guint len, guchar out[SHA1_HASH_SIZE]); gchar *gabble_generate_id (void); void lm_message_node_add_own_nick (WockyNode *node, GabbleConnection *conn); G_GNUC_WARN_UNUSED_RESULT gchar *gabble_encode_jid (const gchar *node, const gchar *domain, const gchar *resource); gchar *gabble_remove_resource (const gchar *jid); gchar *gabble_normalize_contact (TpHandleRepoIface *repo, const gchar *jid, gpointer userdata, GError **error); gchar *gabble_normalize_room (TpHandleRepoIface *repo, const gchar *jid, gpointer context, GError **error); TpHandle gabble_get_room_handle_from_jid (TpHandleRepoIface *room_repo, const gchar *jid); GHashTable *lm_message_node_extract_properties (WockyNode *node, const gchar *prop); void lm_message_node_add_children_from_properties (WockyNode *node, GHashTable *properties, const gchar *prop); void gabble_signal_connect_weak (gpointer instance, const gchar *detailed_signal, GCallback c_handler, GObject *user_data); guint gabble_idle_add_weak (GSourceFunc function, GObject *object); GPtrArray *gabble_g_ptr_array_copy (GPtrArray *source); WockyBareContact * ensure_bare_contact_from_jid (GabbleConnection *conn, const gchar *jid); TpHandle ensure_handle_from_contact ( GabbleConnection *conn, WockyContact *contact); #ifdef ENABLE_VOIP gboolean jingle_pick_best_resource (GabbleConnection *conn, TpHandle peer, gboolean want_audio, gboolean want_video, const char **transport_ns, WockyJingleDialect *dialect, const gchar **resource_out); const gchar *jingle_pick_best_content_type (GabbleConnection *conn, TpHandle peer, const gchar *resource, WockyJingleMediaType type); GPtrArray *gabble_call_candidates_to_array (GList *candidates); #endif gchar * gabble_peer_to_jid (GabbleConnection *conn, TpHandle peer, const gchar *resource); gboolean gabble_flag_from_nick (GType flag_type, const gchar *nick, guint *value); void gabble_simple_async_succeed_or_fail_in_idle (gpointer self, GAsyncReadyCallback callback, gpointer user_data, gpointer source_tag, const GError *error); GSimpleAsyncResult *gabble_simple_async_countdown_new (gpointer self, GAsyncReadyCallback callback, gpointer user_data, gpointer source_tag, gssize todo); void gabble_simple_async_countdown_inc (GSimpleAsyncResult *simple); void gabble_simple_async_countdown_dec (GSimpleAsyncResult *simple); /* Boilerplate for telling servers which implement XEP-0079 not to store these * messages for delivery later. Include it in your call to wocky_stanza_build() * like so: * * wocky_stanza_build (WOCKY_STANZA_TYPE_MESSAGE, WOCKY_STANZA_SUB_TYPE_NONE, * NULL, jid, * '(', "close", * ':', NS_TUBES, * '@', "tube", id_str, * ')', * GABBLE_AMP_DO_NOT_STORE_SPEC, * NULL); * * Every 1000th user will win a Marshall amplifier! */ #define GABBLE_AMP_DO_NOT_STORE_SPEC \ '(', "amp", \ ':', NS_AMP, \ '(', "rule", \ '@', "condition", "deliver-at", \ '@', "value", "stored", \ '@', "action", "error", \ ')', \ '(', "rule", \ '@', "condition", "match-resource", \ '@', "value", "exact", \ '@', "action", "error", \ ')', \ ')' #endif /* __GABBLE_UTIL_H__ */ telepathy-gabble-0.18.2/src/types.h0000644000175000017500000000354312200204333017113 0ustar00smcvsmcv00000000000000/* * gabble-types.h - Header for Gabble type definitions * * Copyright (C) 2006 Collabora Ltd. * Copyright (C) 2006 Nokia Corporation * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 __GABBLE_TYPES_H__ #define __GABBLE_TYPES_H__ #include "config.h" #include #include "gabble/types.h" G_BEGIN_DECLS typedef struct _GabbleDisco GabbleDisco; typedef struct _GabbleMucChannel GabbleMucChannel; typedef struct _GabblePresence GabblePresence; typedef struct _GabblePresenceCache GabblePresenceCache; typedef struct _GabbleRoster GabbleRoster; typedef struct _GabbleRosterChannel GabbleRosterChannel; typedef struct _GabbleVCardManager GabbleVCardManager; typedef struct _GabbleBytestreamFactory GabbleBytestreamFactory; typedef struct _GabblePrivateTubesFactory GabblePrivateTubesFactory; typedef struct _GabbleRequestPipeline GabbleRequestPipeline; typedef struct _GabbleTubesChannel GabbleTubesChannel; typedef struct _GabbleCallMember GabbleCallMember; typedef struct _GabbleCallMemberContent GabbleCallMemberContent; struct _GabbleDiscoIdentity { gchar *category; gchar *type; gchar *lang; gchar *name; }; G_END_DECLS #endif telepathy-gabble-0.18.2/src/tube-stream.c0000644000175000017500000021177012227000321020175 0ustar00smcvsmcv00000000000000/* * tube-stream.c - Source for GabbleTubeStream * Copyright (C) 2007-2008 Collabora Ltd. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 "config.h" #include "tube-stream.h" #include #include #include #include #ifdef HAVE_UNISTD_H # include #endif #include #include #include #include "extensions/extensions.h" #define DEBUG_FLAG GABBLE_DEBUG_TUBES #include #include #include #include #include #include #include "bytestream-factory.h" #include "bytestream-iface.h" #include "connection.h" #include "debug.h" #include "disco.h" #include "gabble-signals-marshal.h" #include "muc-channel.h" #include "muc-tube-stream.h" #include "namespaces.h" #include "presence-cache.h" #include "presence.h" #include "tube-iface.h" #include "util.h" static void tube_iface_init (gpointer g_iface, gpointer iface_data); static void streamtube_iface_init (gpointer g_iface, gpointer iface_data); G_DEFINE_TYPE_WITH_CODE (GabbleTubeStream, gabble_tube_stream, TP_TYPE_BASE_CHANNEL, G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_DBUS_PROPERTIES, tp_dbus_properties_mixin_iface_init); G_IMPLEMENT_INTERFACE (GABBLE_TYPE_TUBE_IFACE, tube_iface_init); G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CHANNEL_TYPE_STREAM_TUBE, streamtube_iface_init); G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CHANNEL_INTERFACE_TUBE, NULL); G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CHANNEL_INTERFACE_GROUP, tp_external_group_mixin_iface_init); ); static const gchar * const gabble_tube_stream_channel_allowed_properties[] = { TP_IFACE_CHANNEL ".TargetHandle", TP_IFACE_CHANNEL ".TargetID", TP_IFACE_CHANNEL_TYPE_STREAM_TUBE ".Service", NULL }; /* Linux glibc bits/socket.h suggests that struct sockaddr_storage is * not guaranteed to be big enough for AF_UNIX addresses */ typedef union { #ifdef GIBBER_TYPE_UNIX_TRANSPORT /* we'd call this unix, but gcc predefines that. Thanks, gcc */ struct sockaddr_un un; #endif struct sockaddr_in ipv4; struct sockaddr_in6 ipv6; } SockAddr; /* signals */ enum { OPENED, NEW_CONNECTION, CLOSED, OFFERED, LAST_SIGNAL }; static guint signals[LAST_SIGNAL] = {0}; /* properties */ enum { PROP_SELF_HANDLE = 1, PROP_ID, PROP_TYPE, PROP_SERVICE, PROP_PARAMETERS, PROP_STATE, PROP_ADDRESS_TYPE, PROP_ADDRESS, PROP_ACCESS_CONTROL, PROP_ACCESS_CONTROL_PARAM, PROP_SUPPORTED_SOCKET_TYPES, PROP_MUC, LAST_PROPERTY }; struct _GabbleTubeStreamPrivate { TpHandle self_handle; guint64 id; /* Bytestreams for tubes. One tube can have several bytestreams. The * mapping between the tube bytestream and the transport to the local * application is stored in the transport_to_bytestream and * bytestream_to_transport fields. This is used both on initiator-side and * on recipient-side. */ /* (GabbleBytestreamIface *) -> (GibberTransport *) * * The (b->t) is inserted as soon as they are created. On initiator side, * we receive an incoming bytestream, create a transport and insert (b->t). * On recipient side, we receive an incoming transport, create a bytestream * and insert (b->t). */ GHashTable *bytestream_to_transport; /* (GibberTransport *) -> (GabbleBytestreamIface *) * * The (t->b) is also inserted as soon as they are created. */ GHashTable *transport_to_bytestream; /* (GibberTransport *) -> guint */ GHashTable *transport_to_id; guint last_connection_id; gchar *service; GHashTable *parameters; TpTubeChannelState state; TpSocketAddressType address_type; GValue *address; TpSocketAccessControl access_control; GValue *access_control_param; /* listen for connections from local applications */ GibberListener *local_listener; GabbleMucChannel *muc; gboolean dispose_has_run; }; static GPtrArray * gabble_tube_stream_get_interfaces (TpBaseChannel *base) { GPtrArray *interfaces; interfaces = TP_BASE_CHANNEL_CLASS ( gabble_tube_stream_parent_class)->get_interfaces (base); g_ptr_array_add (interfaces, TP_IFACE_CHANNEL_INTERFACE_TUBE); return interfaces; } typedef struct { GabbleTubeStream *self; TpHandle contact; } transport_connected_data; static void data_received_cb (GabbleBytestreamIface *ibb, TpHandle sender, GString *data, gpointer user_data); static void transport_connected_cb (GibberTransport *transport, transport_connected_data *data); #ifdef GIBBER_TYPE_UNIX_TRANSPORT static void generate_ascii_string (guint len, gchar *buf) { const gchar *chars = "0123456789" "abcdefghijklmnopqrstuvwxyz" "ABCDEFGHIJKLMNOPQRSTUVWXYZ" "_-"; guint i; for (i = 0; i < len; i++) buf[i] = chars[g_random_int_range (0, 64)]; } #endif static void transport_handler (GibberTransport *transport, GibberBuffer *data, gpointer user_data) { GabbleTubeStream *self = GABBLE_TUBE_STREAM (user_data); GabbleTubeStreamPrivate *priv = self->priv; GabbleBytestreamIface *bytestream; bytestream = g_hash_table_lookup (priv->transport_to_bytestream, transport); if (bytestream == NULL) { DEBUG ("no open bytestream associated with this transport"); return; } gabble_bytestream_iface_send (bytestream, data->length, (const gchar *) data->data); } static void fire_connection_closed (GabbleTubeStream *self, GibberTransport *transport, const gchar *error, const gchar *debug_msg) { GabbleTubeStreamPrivate *priv = self->priv; guint connection_id; connection_id = GPOINTER_TO_UINT (g_hash_table_lookup (priv->transport_to_id, transport)); if (connection_id == 0) { DEBUG ("ConnectionClosed has already been fired for this connection"); return; } /* remove the ID so we are sure we won't fire ConnectionClosed twice for the * same connection. */ g_hash_table_remove (priv->transport_to_id, transport); tp_svc_channel_type_stream_tube_emit_connection_closed (self, connection_id, error, debug_msg); } static void transport_disconnected_cb (GibberTransport *transport, GabbleTubeStream *self) { GabbleTubeStreamPrivate *priv = self->priv; GabbleBytestreamIface *bytestream; fire_connection_closed (self, transport, TP_ERROR_STR_CANCELLED, "local socket has been disconnected"); bytestream = g_hash_table_lookup (priv->transport_to_bytestream, transport); if (bytestream == NULL) return; DEBUG ("transport disconnected. close the extra bytestream"); gabble_bytestream_iface_close (bytestream, NULL); } static void remove_transport (GabbleTubeStream *self, GabbleBytestreamIface *bytestream, GibberTransport *transport) { GabbleTubeStreamPrivate *priv = self->priv; DEBUG ("disconnect and remove transport"); g_signal_handlers_disconnect_matched (transport, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, self); /* The callback on the "connected" signal doesn't match the above * disconnection as it receives a transport_connected_data as user_data * and not the self pointer. */ g_signal_handlers_disconnect_matched (transport, G_SIGNAL_MATCH_FUNC, 0, 0, NULL, G_CALLBACK (transport_connected_cb), NULL); gibber_transport_disconnect (transport); fire_connection_closed (self, transport, TP_ERROR_STR_CONNECTION_LOST, "bytestream has been broken"); g_hash_table_remove (priv->transport_to_bytestream, transport); g_hash_table_remove (priv->bytestream_to_transport, bytestream); g_hash_table_remove (priv->transport_to_id, transport); } static void transport_buffer_empty_cb (GibberTransport *transport, GabbleTubeStream *self) { GabbleTubeStreamPrivate *priv = self->priv; GabbleBytestreamIface *bytestream; GabbleBytestreamState state; bytestream = g_hash_table_lookup (priv->transport_to_bytestream, transport); g_assert (bytestream != NULL); g_object_get (bytestream, "state", &state, NULL); if (state == GABBLE_BYTESTREAM_STATE_CLOSED) { DEBUG ("buffer is now empty. Transport can be removed"); remove_transport (self, bytestream, transport); return; } /* Buffer is empty so we can unblock the buffer if it was blocked */ gabble_bytestream_iface_block_reading (bytestream, FALSE); } static void add_transport (GabbleTubeStream *self, GibberTransport *transport, GabbleBytestreamIface *bytestream) { gibber_transport_set_handler (transport, transport_handler, self); g_signal_connect (transport, "disconnected", G_CALLBACK (transport_disconnected_cb), self); g_signal_connect (transport, "buffer-empty", G_CALLBACK (transport_buffer_empty_cb), self); /* We can transfer transport's data; unblock it. */ gibber_transport_block_receiving (transport, FALSE); } static void bytestream_write_blocked_cb (GabbleBytestreamIface *bytestream, gboolean blocked, GabbleTubeStream *self) { GabbleTubeStreamPrivate *priv = self->priv; GibberTransport *transport; transport = g_hash_table_lookup (priv->bytestream_to_transport, bytestream); g_assert (transport != NULL); gibber_transport_block_receiving (transport, blocked); } static void extra_bytestream_state_changed_cb (GabbleBytestreamIface *bytestream, GabbleBytestreamState state, gpointer user_data) { GabbleTubeStream *self = GABBLE_TUBE_STREAM (user_data); GabbleTubeStreamPrivate *priv = self->priv; DEBUG ("Called."); if (state == GABBLE_BYTESTREAM_STATE_OPEN) { GibberTransport *transport; DEBUG ("extra bytestream open"); g_signal_connect (bytestream, "data-received", G_CALLBACK (data_received_cb), self); g_signal_connect (bytestream, "write-blocked", G_CALLBACK (bytestream_write_blocked_cb), self); transport = g_hash_table_lookup (priv->bytestream_to_transport, bytestream); g_assert (transport != NULL); add_transport (self, transport, bytestream); } else if (state == GABBLE_BYTESTREAM_STATE_CLOSED) { GibberTransport *transport; DEBUG ("extra bytestream closed"); transport = g_hash_table_lookup (priv->bytestream_to_transport, bytestream); if (transport != NULL) { if (gibber_transport_buffer_is_empty (transport)) { DEBUG ("Buffer is empty, we can remove the transport"); remove_transport (self, bytestream, transport); } else { DEBUG ("Wait buffer is empty before disconnect the transport"); } } } } static void extra_bytestream_negotiate_cb (GabbleBytestreamIface *bytestream, WockyStanza *msg, GObject *object, gpointer user_data) { GabbleTubeStream *self = GABBLE_TUBE_STREAM (object); GabbleTubeStreamPrivate *priv = self->priv; GibberTransport *transport = GIBBER_TRANSPORT (user_data); if (bytestream == NULL) { DEBUG ("initiator refused new bytestream"); fire_connection_closed (self, transport, TP_ERROR_STR_CONNECTION_REFUSED, "connection has been refused"); g_object_unref (transport); return; } DEBUG ("extra bytestream accepted"); /* transport has been refed in start_stream_initiation () */ g_assert (gibber_transport_get_state (transport) == GIBBER_TRANSPORT_CONNECTED); g_hash_table_insert (priv->bytestream_to_transport, g_object_ref (bytestream), transport); g_hash_table_insert (priv->transport_to_bytestream, g_object_ref (transport), g_object_ref (bytestream)); g_signal_connect (bytestream, "state-changed", G_CALLBACK (extra_bytestream_state_changed_cb), self); } static gboolean start_stream_initiation (GabbleTubeStream *self, GibberTransport *transport, GError **error) { GabbleTubeStreamPrivate *priv = self->priv; TpBaseChannel *base = TP_BASE_CHANNEL (self); TpBaseChannelClass *cls = TP_BASE_CHANNEL_GET_CLASS (base); TpBaseConnection *base_conn = tp_base_channel_get_connection (base); GabbleConnection *conn = GABBLE_CONNECTION (base_conn); TpHandle initiator = tp_base_channel_get_initiator (base); WockyNode *node, *si_node; WockyStanza *msg; TpHandleRepoIface *contact_repo; const gchar *jid; gchar *full_jid, *stream_id, *id_str; contact_repo = tp_base_connection_get_handles ( base_conn, TP_HANDLE_TYPE_CONTACT); jid = tp_handle_inspect (contact_repo, initiator); if (cls->target_handle_type == TP_HANDLE_TYPE_CONTACT) { /* Private tube */ GabblePresence *presence; const gchar *resource; presence = gabble_presence_cache_get (conn->presence_cache, initiator); if (presence == NULL) { DEBUG ("can't find initiator's presence"); g_set_error (error, TP_ERROR, TP_ERROR_NOT_AVAILABLE, "can't find initiator's presence"); return FALSE; } resource = gabble_presence_pick_resource_by_caps (presence, 0, gabble_capability_set_predicate_has, NS_TUBES); if (resource == NULL) { DEBUG ("initiator doesn't have tubes capabilities"); g_set_error (error, TP_ERROR, TP_ERROR_NOT_AVAILABLE, "initiator doesn't have tubes capabilities"); return FALSE; } full_jid = g_strdup_printf ("%s/%s", jid, resource); } else { /* Muc tube */ full_jid = g_strdup (jid); } stream_id = gabble_bytestream_factory_generate_stream_id (); msg = gabble_bytestream_factory_make_stream_init_iq (full_jid, stream_id, NS_TUBES); si_node = wocky_node_get_child_ns ( wocky_stanza_get_top_node (msg), "si", NS_SI); g_assert (si_node != NULL); id_str = g_strdup_printf ("%" G_GUINT64_FORMAT, priv->id); if (cls->target_handle_type == TP_HANDLE_TYPE_CONTACT) { node = wocky_node_add_child_ns (si_node, "stream", NS_TUBES); } else { node = wocky_node_add_child_ns (si_node, "muc-stream", NS_TUBES); } wocky_node_set_attribute (node, "tube", id_str); gabble_bytestream_factory_negotiate_stream ( conn->bytestream_factory, msg, stream_id, extra_bytestream_negotiate_cb, g_object_ref (transport), G_OBJECT (self)); /* FIXME: data and one ref on data->transport are leaked if the tube is * closed before we got the SI reply. */ g_object_unref (msg); g_free (stream_id); g_free (full_jid); g_free (id_str); return TRUE; } static guint generate_connection_id (GabbleTubeStream *self, GibberTransport *transport) { GabbleTubeStreamPrivate *priv = self->priv; priv->last_connection_id++; g_hash_table_insert (priv->transport_to_id, transport, GUINT_TO_POINTER (priv->last_connection_id)); return priv->last_connection_id; } static void fire_new_local_connection (GabbleTubeStream *self, GibberTransport *transport) { guint connection_id; connection_id = generate_connection_id (self, transport); tp_svc_channel_type_stream_tube_emit_new_local_connection (self, connection_id); } #ifdef GIBBER_TYPE_UNIX_TRANSPORT static void credentials_received_cb (GibberUnixTransport *transport, GibberBuffer *buffer, GibberCredentials *credentials, GError *error, gpointer user_data) { GabbleTubeStream *self = GABBLE_TUBE_STREAM (user_data); /* Credentials received; reblock the transport */ gibber_transport_block_receiving (GIBBER_TRANSPORT (transport), TRUE); if (error != NULL) { DEBUG ("Didn't receive credentials (%s). Closing transport", error->message); goto credentials_received_cb_out; } g_assert (credentials != NULL); if (buffer->length != 1) { DEBUG ("Got more than one byte (%" G_GSIZE_FORMAT "). Rejecting", buffer->length); goto credentials_received_cb_out; } if (credentials->uid != getuid ()) { DEBUG ("Wrong uid (%u). Rejecting", credentials->uid); goto credentials_received_cb_out; } DEBUG ("Connection properly authentificated"); if (!start_stream_initiation (self, GIBBER_TRANSPORT (transport), NULL)) { DEBUG ("SI failed. Closing connection"); } else { fire_new_local_connection (self, GIBBER_TRANSPORT (transport)); } credentials_received_cb_out: /* start_stream_initiation reffed the transport if everything went fine */ g_object_unref (transport); } #endif static gboolean check_incoming_connection (GabbleTubeStream *self, GibberTransport *transport) { GabbleTubeStreamPrivate *priv = self->priv; if (priv->access_control == TP_SOCKET_ACCESS_CONTROL_LOCALHOST) { return TRUE; } #ifdef GIBBER_TYPE_UNIX_TRANSPORT else if (priv->access_control == TP_SOCKET_ACCESS_CONTROL_CREDENTIALS) { if (!gibber_unix_transport_recv_credentials ( GIBBER_UNIX_TRANSPORT (transport), credentials_received_cb, self)) { DEBUG ("Can't receive credentials. Closing transport"); return FALSE; } /* Temporarly unblock the transport to be able to receive credentials */ gibber_transport_block_receiving (transport, FALSE); /* We ref the transport so it won't be destroyed by GibberListener */ g_object_ref (transport); /* Returns FALSE as we are waiting for credentials so SI can't be * started yet. */ return FALSE; } #endif else if (priv->access_control == TP_SOCKET_ACCESS_CONTROL_PORT) { struct sockaddr_storage addr; socklen_t len = sizeof (addr); int ret; char peer_host[NI_MAXHOST]; char peer_port[NI_MAXSERV]; guint port; gchar *host; gchar *tmp; if (!gibber_transport_get_peeraddr (transport, &addr, &len)) { DEBUG ("gibber_transport_get_peeraddr failed"); return FALSE; } gibber_normalize_address (&addr); g_assert (addr.ss_family == AF_INET || addr.ss_family == AF_INET6); ret = getnameinfo ((struct sockaddr *) &addr, len, peer_host, NI_MAXHOST, peer_port, NI_MAXSERV, NI_NUMERICHOST | NI_NUMERICSERV); if (ret != 0) { DEBUG ("getnameinfo failed: %s", gai_strerror(ret)); return FALSE; } dbus_g_type_struct_get (priv->access_control_param, 0, &host, 1, &port, G_MAXUINT); if (tp_strdiff (host, peer_host)) { DEBUG ("Wrong ip: %s (%s was expected)", peer_host, host); g_free (host); return FALSE; } g_free (host); tmp = g_strdup_printf ("%u", port); if (tp_strdiff (tmp, peer_port)) { DEBUG ("Wrong port: %s (%u was expected)", peer_port, port); g_free (tmp); return FALSE; } g_free (tmp); return TRUE; } else { /* access_control has already been checked when accepting the tube */ g_assert_not_reached (); } return FALSE; } /* callback for listening connections from the local application */ static void local_new_connection_cb (GibberListener *listener, GibberTransport *transport, struct sockaddr_storage *addr, guint size, gpointer user_data) { GabbleTubeStream *self = GABBLE_TUBE_STREAM (user_data); /* Block the transport while there is no open bytestream to transfer * its data. */ gibber_transport_block_receiving (transport, TRUE); if (!check_incoming_connection (self, transport)) { /* We didn't ref the connection so it will be destroyed by the * GibberListener if needed. */ return; } /* Streams in stream tubes are established with stream initiation (XEP-0095). * We use SalutSiBytestreamManager. */ if (!start_stream_initiation (self, transport, NULL)) { DEBUG ("closing new client connection"); } else { fire_new_local_connection (self, transport); } } #ifdef GIBBER_TYPE_UNIX_TRANSPORT static gboolean set_credentials_access_control_param (GValue *access_control_param, GibberTransport *transport) { guint8 credentials; credentials = g_random_int_range (0, G_MAXUINT8); /* The Credentials access control would have be rejected earlier if the * socket type wasn't UNIX. */ if (!gibber_unix_transport_send_credentials ( GIBBER_UNIX_TRANSPORT (transport), &credentials, sizeof (guint8))) { DEBUG ("send_credentials failed"); return FALSE; } g_value_init (access_control_param, G_TYPE_UCHAR); g_value_set_uchar (access_control_param, credentials); return TRUE; } #endif static gboolean set_port_access_control_param (GValue *access_control_param, GibberTransport *transport) { struct sockaddr_storage addr; socklen_t addrlen = sizeof (struct sockaddr_storage); char host[NI_MAXHOST]; char port_str[NI_MAXSERV]; int ret; guint16 port; unsigned long tmp; gchar *endptr; if (!gibber_transport_get_sockaddr (transport, &addr, &addrlen)) { DEBUG ("Failed to get connection address"); return FALSE; } ret = getnameinfo ((struct sockaddr *) &addr, addrlen, host, NI_MAXHOST, port_str, NI_MAXSERV, NI_NUMERICHOST | NI_NUMERICSERV); if (ret != 0) { DEBUG ("getnameinfo failed: %s", g_strerror (ret)); return FALSE; } tmp = strtoul (port_str, &endptr, 10); if (!endptr || *endptr || tmp > G_MAXUINT16) { DEBUG ("invalid port: %s", port_str); return FALSE; } port = (guint16) tmp; g_value_init (access_control_param, TP_STRUCT_TYPE_SOCKET_ADDRESS_IPV4); g_value_take_boxed (access_control_param, dbus_g_type_specialized_construct ( TP_STRUCT_TYPE_SOCKET_ADDRESS_IPV4)); dbus_g_type_struct_set (access_control_param, 0, host, 1, port, G_MAXUINT); return TRUE; } static transport_connected_data * transport_connected_data_new (GabbleTubeStream *self, TpHandle contact) { transport_connected_data *data = g_slice_new (transport_connected_data); data->self = self; data->contact = contact; return data; } static void transport_connected_data_free (transport_connected_data *data) { g_slice_free (transport_connected_data, data); } static void fire_new_remote_connection (GabbleTubeStream *self, GibberTransport *transport, TpHandle contact) { GabbleTubeStreamPrivate *priv = self->priv; GValue access_control_param = {0,}; guint connection_id; #ifdef GIBBER_TYPE_UNIX_TRANSPORT if (priv->access_control == TP_SOCKET_ACCESS_CONTROL_CREDENTIALS) { if (!set_credentials_access_control_param (&access_control_param, transport)) { gibber_transport_disconnect (transport); return; } } else #endif if (priv->access_control == TP_SOCKET_ACCESS_CONTROL_PORT) { if (!set_port_access_control_param (&access_control_param, transport)) { gibber_transport_disconnect (transport); return; } } else { /* set a dummy value */ g_value_init (&access_control_param, G_TYPE_INT); g_value_set_int (&access_control_param, 0); } /* fire NewConnection D-Bus signal */ connection_id = GPOINTER_TO_UINT (g_hash_table_lookup (priv->transport_to_id, transport)); g_assert (connection_id != 0); tp_svc_channel_type_stream_tube_emit_new_remote_connection (self, contact, &access_control_param, connection_id); g_value_unset (&access_control_param); } static void transport_connected_cb (GibberTransport *transport, transport_connected_data *data) { GabbleTubeStreamPrivate *priv = data->self->priv; GabbleBytestreamIface *bytestream; fire_new_remote_connection (data->self, transport, data->contact); bytestream = g_hash_table_lookup (priv->transport_to_bytestream, transport); if (bytestream == NULL) return; gabble_bytestream_iface_block_reading (bytestream, FALSE); } static GibberTransport * new_connection_to_socket (GabbleTubeStream *self, GabbleBytestreamIface *bytestream, TpHandle contact) { GabbleTubeStreamPrivate *priv = self->priv; TpBaseChannel *base = TP_BASE_CHANNEL (self); GibberTransport *transport; DEBUG ("Called."); g_assert (tp_base_channel_is_requested (base)); #ifdef GIBBER_TYPE_UNIX_TRANSPORT if (priv->address_type == TP_SOCKET_ADDRESS_TYPE_UNIX) { GArray *array; array = g_value_get_boxed (priv->address); DEBUG ("Will try to connect to socket: %s", (const gchar *) array->data); transport = GIBBER_TRANSPORT (gibber_unix_transport_new ()); gibber_unix_transport_connect (GIBBER_UNIX_TRANSPORT (transport), array->data, NULL); } else #endif if (priv->address_type == TP_SOCKET_ADDRESS_TYPE_IPV4 || priv->address_type == TP_SOCKET_ADDRESS_TYPE_IPV6) { gchar *ip; guint port; dbus_g_type_struct_get (priv->address, 0, &ip, 1, &port, G_MAXUINT); transport = GIBBER_TRANSPORT (gibber_tcp_transport_new ()); gibber_tcp_transport_connect (GIBBER_TCP_TRANSPORT (transport), ip, port); g_free (ip); } else { g_assert_not_reached (); } /* Block the transport while there is no open bytestream to transfer * its data. */ gibber_transport_block_receiving (transport, TRUE); generate_connection_id (self, transport); gabble_bytestream_iface_block_reading (bytestream, TRUE); g_hash_table_insert (priv->bytestream_to_transport, g_object_ref (bytestream), g_object_ref (transport)); g_hash_table_insert (priv->transport_to_bytestream, g_object_ref (transport), g_object_ref (bytestream)); g_signal_connect (bytestream, "state-changed", G_CALLBACK (extra_bytestream_state_changed_cb), self); g_object_unref (transport); return transport; } static gboolean tube_stream_open (GabbleTubeStream *self, GError **error) { GabbleTubeStreamPrivate *priv = self->priv; TpBaseChannel *base = TP_BASE_CHANNEL (self); DEBUG ("called"); if (tp_base_channel_is_requested (base)) /* Nothing to do if we are the initiator of this tube. * We'll connect to the socket each time request a new bytestream. */ return TRUE; /* We didn't create this tube so it doesn't have * a socket associated with it. Let's create one */ g_assert (priv->address == NULL); g_assert (priv->local_listener == NULL); priv->local_listener = gibber_listener_new (); g_signal_connect (priv->local_listener, "new-connection", G_CALLBACK (local_new_connection_cb), self); #ifdef GIBBER_TYPE_UNIX_TRANSPORT if (priv->address_type == TP_SOCKET_ADDRESS_TYPE_UNIX) { GArray *array; gchar suffix[8]; gchar *path; int ret; generate_ascii_string (8, suffix); path = g_strdup_printf ("/tmp/stream-gabble-%.8s", suffix); DEBUG ("create socket: %s", path); array = g_array_sized_new (TRUE, FALSE, sizeof (gchar), strlen (path)); g_array_insert_vals (array, 0, path, strlen (path)); priv->address = tp_g_value_slice_new (DBUS_TYPE_G_UCHAR_ARRAY); g_value_set_boxed (priv->address, array); g_array_unref (array); ret = gibber_listener_listen_socket (priv->local_listener, path, FALSE, error); if (ret != TRUE) { g_assert (error != NULL && *error != NULL); DEBUG ("Error listening on socket %s: %s", path, (*error)->message); g_free (path); return FALSE; } if (priv->access_control == TP_SOCKET_ACCESS_CONTROL_LOCALHOST) { /* Everyone can use the socket */ chmod (path, 0777); } g_free (path); } else #endif if (priv->address_type == TP_SOCKET_ADDRESS_TYPE_IPV4) { int ret; ret = gibber_listener_listen_tcp_loopback_af (priv->local_listener, 0, GIBBER_AF_IPV4, error); if (!ret) { g_assert (error != NULL && *error != NULL); DEBUG ("Error listening on socket: %s", (*error)->message); return FALSE; } priv->address = tp_g_value_slice_new ( TP_STRUCT_TYPE_SOCKET_ADDRESS_IPV4); g_value_take_boxed (priv->address, dbus_g_type_specialized_construct ( TP_STRUCT_TYPE_SOCKET_ADDRESS_IPV4)); dbus_g_type_struct_set (priv->address, 0, "127.0.0.1", 1, gibber_listener_get_port (priv->local_listener), G_MAXUINT); } else if (priv->address_type == TP_SOCKET_ADDRESS_TYPE_IPV6) { int ret; ret = gibber_listener_listen_tcp_loopback_af (priv->local_listener, 0, GIBBER_AF_IPV6, error); if (!ret) { g_assert (error != NULL && *error != NULL); DEBUG ("Error listening on socket: %s", (*error)->message); return FALSE; } priv->address = tp_g_value_slice_new ( TP_STRUCT_TYPE_SOCKET_ADDRESS_IPV6); g_value_take_boxed (priv->address, dbus_g_type_specialized_construct ( TP_STRUCT_TYPE_SOCKET_ADDRESS_IPV6)); dbus_g_type_struct_set (priv->address, 0, "::1", 1, gibber_listener_get_port (priv->local_listener), G_MAXUINT); } else { g_assert_not_reached (); } return TRUE; } static void gabble_tube_stream_init (GabbleTubeStream *self) { GabbleTubeStreamPrivate *priv = G_TYPE_INSTANCE_GET_PRIVATE (self, GABBLE_TYPE_TUBE_STREAM, GabbleTubeStreamPrivate); self->priv = priv; priv->transport_to_bytestream = g_hash_table_new_full (g_direct_hash, g_direct_equal, (GDestroyNotify) g_object_unref, (GDestroyNotify) g_object_unref); priv->bytestream_to_transport = g_hash_table_new_full (g_direct_hash, g_direct_equal, (GDestroyNotify) g_object_unref, (GDestroyNotify) g_object_unref); priv->transport_to_id = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, NULL); priv->last_connection_id = 0; priv->address_type = TP_SOCKET_ADDRESS_TYPE_UNIX; priv->address = NULL; priv->access_control = TP_SOCKET_ACCESS_CONTROL_LOCALHOST; priv->access_control_param = NULL; priv->dispose_has_run = FALSE; } static gboolean close_each_extra_bytestream (gpointer key, gpointer value, gpointer user_data) { GabbleTubeStream *self = GABBLE_TUBE_STREAM (user_data); GabbleTubeStreamPrivate *priv = self->priv; GibberTransport *transport = (GibberTransport *) value; GabbleBytestreamIface *bytestream = (GabbleBytestreamIface *) key; /* We are iterating over priv->fd_to_bytestreams so we can't modify it. * Disconnect signals so extra_bytestream_state_changed_cb won't be * called */ g_signal_handlers_disconnect_matched (bytestream, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, self); g_signal_handlers_disconnect_matched (transport, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, self); /* The callback on the "connected" signal doesn't match the above * disconnection as it receives a transport_connected_data as user_data * and not the self pointer. */ g_signal_handlers_disconnect_matched (transport, G_SIGNAL_MATCH_FUNC, 0, 0, NULL, G_CALLBACK (transport_connected_cb), NULL); gabble_bytestream_iface_close (bytestream, NULL); gibber_transport_disconnect (transport); fire_connection_closed (self, transport, TP_ERROR_STR_CANCELLED, "tube is closing"); g_hash_table_remove (priv->transport_to_bytestream, transport); return TRUE; } static void gabble_tube_stream_dispose (GObject *object) { GabbleTubeStream *self = GABBLE_TUBE_STREAM (object); GabbleTubeStreamPrivate *priv = self->priv; TpBaseChannel *base = (TpBaseChannel *) self; if (priv->dispose_has_run) return; gabble_tube_iface_close (GABBLE_TUBE_IFACE (self), TRUE); if (tp_base_channel_is_requested (base) && priv->address_type == TP_SOCKET_ADDRESS_TYPE_UNIX && priv->address != NULL) { /* We created a new UNIX socket. Let's delete it */ GArray *array; GString *path; array = g_value_get_boxed (priv->address); path = g_string_new_len (array->data, array->len); if (g_unlink (path->str) != 0) { DEBUG ("unlink of %s failed: %s", path->str, g_strerror (errno)); } g_string_free (path, TRUE); } tp_clear_pointer (&priv->transport_to_bytestream, g_hash_table_unref); tp_clear_pointer (&priv->bytestream_to_transport, g_hash_table_unref); tp_clear_pointer (&priv->transport_to_id, g_hash_table_unref); tp_clear_object (&priv->local_listener); if (priv->muc != NULL) { tp_external_group_mixin_finalize (object); } priv->dispose_has_run = TRUE; if (G_OBJECT_CLASS (gabble_tube_stream_parent_class)->dispose) G_OBJECT_CLASS (gabble_tube_stream_parent_class)->dispose (object); } static void gabble_tube_stream_finalize (GObject *object) { GabbleTubeStream *self = GABBLE_TUBE_STREAM (object); GabbleTubeStreamPrivate *priv = self->priv; g_free (priv->service); g_hash_table_unref (priv->parameters); if (priv->address != NULL) { tp_g_value_slice_free (priv->address); priv->address = NULL; } if (priv->access_control_param != NULL) { tp_g_value_slice_free (priv->access_control_param); priv->access_control_param = NULL; } G_OBJECT_CLASS (gabble_tube_stream_parent_class)->finalize (object); } static void gabble_tube_stream_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { GabbleTubeStream *self = GABBLE_TUBE_STREAM (object); GabbleTubeStreamPrivate *priv = self->priv; switch (property_id) { case PROP_SELF_HANDLE: g_value_set_uint (value, priv->self_handle); break; case PROP_ID: g_value_set_uint64 (value, priv->id); break; case PROP_TYPE: g_value_set_uint (value, TP_TUBE_TYPE_STREAM); break; case PROP_SERVICE: g_value_set_string (value, priv->service); break; case PROP_PARAMETERS: g_value_set_boxed (value, priv->parameters); break; case PROP_STATE: g_value_set_uint (value, priv->state); break; case PROP_ADDRESS_TYPE: g_value_set_uint (value, priv->address_type); break; case PROP_ADDRESS: g_value_set_pointer (value, priv->address); break; case PROP_ACCESS_CONTROL: g_value_set_uint (value, priv->access_control); break; case PROP_ACCESS_CONTROL_PARAM: g_value_set_pointer (value, priv->access_control_param); break; case PROP_SUPPORTED_SOCKET_TYPES: g_value_take_boxed (value, gabble_tube_stream_get_supported_socket_types ()); break; case PROP_MUC: g_value_set_object (value, priv->muc); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void gabble_tube_stream_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { GabbleTubeStream *self = GABBLE_TUBE_STREAM (object); GabbleTubeStreamPrivate *priv = self->priv; switch (property_id) { case PROP_SELF_HANDLE: priv->self_handle = g_value_get_uint (value); break; case PROP_ID: priv->id = g_value_get_uint64 (value); break; case PROP_SERVICE: g_free (priv->service); priv->service = g_value_dup_string (value); break; case PROP_PARAMETERS: if (priv->parameters != NULL) g_hash_table_unref (priv->parameters); priv->parameters = g_value_dup_boxed (value); break; case PROP_ADDRESS_TYPE: g_assert (g_value_get_uint (value) == TP_SOCKET_ADDRESS_TYPE_UNIX || g_value_get_uint (value) == TP_SOCKET_ADDRESS_TYPE_IPV4 || g_value_get_uint (value) == TP_SOCKET_ADDRESS_TYPE_IPV6); priv->address_type = g_value_get_uint (value); break; case PROP_ADDRESS: if (priv->address == NULL) { priv->address = tp_g_value_slice_dup (g_value_get_pointer (value)); } break; case PROP_ACCESS_CONTROL: priv->access_control = g_value_get_uint (value); break; case PROP_ACCESS_CONTROL_PARAM: if (priv->access_control_param == NULL) { priv->access_control_param = tp_g_value_slice_dup ( g_value_get_pointer (value)); } break; case PROP_MUC: priv->muc = g_value_get_object (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void gabble_tube_stream_constructed (GObject *obj) { GabbleTubeStream *self = GABBLE_TUBE_STREAM (obj); GabbleTubeStreamPrivate *priv = self->priv; TpBaseChannel *base = TP_BASE_CHANNEL (self); TpBaseChannelClass *cls = TP_BASE_CHANNEL_GET_CLASS (base); TpBaseConnection *base_conn = tp_base_channel_get_connection (base); GabbleConnection *conn = GABBLE_CONNECTION (base_conn); void (*chain_up) (GObject *) = ((GObjectClass *) gabble_tube_stream_parent_class)->constructed; if (chain_up != NULL) chain_up (obj); if (tp_base_channel_is_requested (base)) { /* We initiated this tube */ priv->state = TP_TUBE_CHANNEL_STATE_NOT_OFFERED; } else { priv->state = TP_TUBE_CHANNEL_STATE_LOCAL_PENDING; /* We'll need SOCKS5 proxies if the tube is accepted */ gabble_bytestream_factory_query_socks5_proxies ( conn->bytestream_factory); } if (cls->target_handle_type == TP_HANDLE_TYPE_CONTACT) { g_assert (priv->muc == NULL); } else { g_assert (priv->muc != NULL); tp_external_group_mixin_init (obj, (GObject *) priv->muc); } } static void gabble_tube_stream_fill_immutable_properties (TpBaseChannel *chan, GHashTable *properties) { TpBaseChannelClass *cls = TP_BASE_CHANNEL_CLASS ( gabble_tube_stream_parent_class); cls->fill_immutable_properties (chan, properties); tp_dbus_properties_mixin_fill_properties_hash ( G_OBJECT (chan), properties, TP_IFACE_CHANNEL_TYPE_STREAM_TUBE, "Service", TP_IFACE_CHANNEL_TYPE_STREAM_TUBE, "SupportedSocketTypes", NULL); if (!tp_base_channel_is_requested (chan)) { tp_dbus_properties_mixin_fill_properties_hash ( G_OBJECT (chan), properties, TP_IFACE_CHANNEL_INTERFACE_TUBE, "Parameters", NULL); } } static gchar * gabble_tube_stream_get_object_path_suffix (TpBaseChannel *base) { GabbleTubeStream *self = GABBLE_TUBE_STREAM (base); return g_strdup_printf ("StreamTubeChannel/%u/%" G_GUINT64_FORMAT, tp_base_channel_get_target_handle (base), self->priv->id); } static void gabble_tube_stream_close (TpBaseChannel *base) { gabble_tube_iface_close (GABBLE_TUBE_IFACE (base), FALSE); } static void gabble_tube_stream_class_init (GabbleTubeStreamClass *gabble_tube_stream_class) { static TpDBusPropertiesMixinPropImpl stream_tube_props[] = { { "Service", "service", NULL }, { "SupportedSocketTypes", "supported-socket-types", NULL }, { NULL } }; static TpDBusPropertiesMixinPropImpl tube_iface_props[] = { { "Parameters", "parameters", NULL }, { "State", "state", NULL }, { NULL } }; static TpDBusPropertiesMixinIfaceImpl prop_interfaces[] = { { TP_IFACE_CHANNEL_TYPE_STREAM_TUBE, tp_dbus_properties_mixin_getter_gobject_properties, NULL, stream_tube_props, }, { TP_IFACE_CHANNEL_INTERFACE_TUBE, tp_dbus_properties_mixin_getter_gobject_properties, NULL, tube_iface_props, }, { NULL } }; GObjectClass *object_class = G_OBJECT_CLASS (gabble_tube_stream_class); TpBaseChannelClass *base_class = TP_BASE_CHANNEL_CLASS (gabble_tube_stream_class); GParamSpec *param_spec; object_class->get_property = gabble_tube_stream_get_property; object_class->set_property = gabble_tube_stream_set_property; object_class->constructed = gabble_tube_stream_constructed; object_class->dispose = gabble_tube_stream_dispose; object_class->finalize = gabble_tube_stream_finalize; base_class->channel_type = TP_IFACE_CHANNEL_TYPE_STREAM_TUBE; base_class->get_interfaces = gabble_tube_stream_get_interfaces; base_class->target_handle_type = TP_HANDLE_TYPE_CONTACT; base_class->close = gabble_tube_stream_close; base_class->fill_immutable_properties = gabble_tube_stream_fill_immutable_properties; base_class->get_object_path_suffix = gabble_tube_stream_get_object_path_suffix; g_type_class_add_private (gabble_tube_stream_class, sizeof (GabbleTubeStreamPrivate)); g_object_class_override_property (object_class, PROP_SELF_HANDLE, "self-handle"); g_object_class_override_property (object_class, PROP_ID, "id"); g_object_class_override_property (object_class, PROP_TYPE, "type"); g_object_class_override_property (object_class, PROP_SERVICE, "service"); g_object_class_override_property (object_class, PROP_PARAMETERS, "parameters"); g_object_class_override_property (object_class, PROP_STATE, "state"); param_spec = g_param_spec_boxed ( "supported-socket-types", "Supported socket types", "GHashTable containing supported socket types.", TP_HASH_TYPE_SUPPORTED_SOCKET_MAP, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, PROP_SUPPORTED_SOCKET_TYPES, param_spec); param_spec = g_param_spec_uint ( "address-type", "address type", "a TpSocketAddressType representing the type of the listening" "address of the local service", 0, NUM_TP_SOCKET_ADDRESS_TYPES - 1, TP_SOCKET_ADDRESS_TYPE_UNIX, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, PROP_ADDRESS_TYPE, param_spec); param_spec = g_param_spec_pointer ( "address", "address", "The listening address of the local service, as indicated by the " "address-type", G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, PROP_ADDRESS, param_spec); param_spec = g_param_spec_uint ( "access-control", "access control", "a TpSocketAccessControl representing the access control " "the local service applies to the local socket", 0, NUM_TP_SOCKET_ACCESS_CONTROLS - 1, TP_SOCKET_ACCESS_CONTROL_LOCALHOST, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, PROP_ACCESS_CONTROL, param_spec); param_spec = g_param_spec_pointer ( "access-control-param", "access control param", "A parameter for the access control type, to be interpreted as specified" "in the documentation for the Socket_Access_Control enum.", G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, PROP_ACCESS_CONTROL_PARAM, param_spec); param_spec = g_param_spec_object ( "muc", "GabbleMucChannel object", "Gabble text MUC channel corresponding to this Tube channel object, " "if the handle type is ROOM.", GABBLE_TYPE_MUC_CHANNEL, G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, PROP_MUC, param_spec); signals[OPENED] = g_signal_new ("tube-opened", G_OBJECT_CLASS_TYPE (gabble_tube_stream_class), G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED, 0, NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); signals[NEW_CONNECTION] = g_signal_new ("tube-new-connection", G_OBJECT_CLASS_TYPE (gabble_tube_stream_class), G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED, 0, NULL, NULL, g_cclosure_marshal_VOID__UINT, G_TYPE_NONE, 1, G_TYPE_UINT); signals[CLOSED] = g_signal_new ("tube-closed", G_OBJECT_CLASS_TYPE (gabble_tube_stream_class), G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED, 0, NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); signals[OFFERED] = g_signal_new ("tube-offered", G_OBJECT_CLASS_TYPE (gabble_tube_stream_class), G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED, 0, NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); gabble_tube_stream_class->dbus_props_class.interfaces = prop_interfaces; tp_dbus_properties_mixin_class_init (object_class, G_STRUCT_OFFSET (GabbleTubeStreamClass, dbus_props_class)); tp_external_group_mixin_init_dbus_properties (object_class); } static void data_received_cb (GabbleBytestreamIface *bytestream, TpHandle sender, GString *data, gpointer user_data) { GabbleTubeStream *tube = GABBLE_TUBE_STREAM (user_data); GabbleTubeStreamPrivate *priv = tube->priv; GibberTransport *transport; GError *error = NULL; transport = g_hash_table_lookup (priv->bytestream_to_transport, bytestream); g_assert (transport != NULL); /* If something goes wrong when trying to write the data on the transport, * it could be disconnected, causing its removal from the hash tables. * When removed, the transport would be destroyed as the hash tables keep a * ref on it and so we'll call _buffer_is_empty on a destroyed transport. * We avoid that by reffing the transport between the 2 calls so we keep it * artificially alive if needed. */ g_object_ref (transport); if (!gibber_transport_send (transport, (const guint8 *) data->str, data->len, &error)) { DEBUG ("sending failed: %s", error->message); g_error_free (error); g_object_unref (transport); return; } if (!gibber_transport_buffer_is_empty (transport)) { /* We don't want to send more data while the buffer isn't empty */ DEBUG ("tube buffer isn't empty. Block the bytestream"); gabble_bytestream_iface_block_reading (bytestream, TRUE); } g_object_unref (transport); } GabbleTubeStream * gabble_tube_stream_new (GabbleConnection *conn, TpHandle handle, TpHandleType handle_type, TpHandle self_handle, TpHandle initiator, const gchar *service, GHashTable *parameters, guint64 id, GabbleMucChannel *muc, gboolean requested) { GabbleTubeStream *obj; GType gtype = GABBLE_TYPE_TUBE_STREAM; if (handle_type == TP_HANDLE_TYPE_ROOM) gtype = GABBLE_TYPE_MUC_TUBE_STREAM; obj = g_object_new (gtype, "connection", conn, "handle", handle, "self-handle", self_handle, "initiator-handle", initiator, "service", service, "parameters", parameters, "id", id, "muc", muc, "requested", requested, NULL); return obj; } /** * gabble_tube_stream_accept * * Implements gabble_tube_iface_accept on GabbleTubeIface */ static gboolean gabble_tube_stream_accept (GabbleTubeIface *tube, GError **error) { GabbleTubeStream *self = GABBLE_TUBE_STREAM (tube); GabbleTubeStreamPrivate *priv = self->priv; if (!gabble_tube_stream_check_params (priv->address_type, NULL, priv->access_control, priv->access_control_param, error)) { goto fail; } if (priv->state != TP_TUBE_CHANNEL_STATE_LOCAL_PENDING) { g_set_error (error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT, "Tube is not in the local pending state"); goto fail; } if (!tube_stream_open (self, error)) { gabble_tube_iface_close (GABBLE_TUBE_IFACE (self), TRUE); goto fail; } priv->state = TP_TUBE_CHANNEL_STATE_OPEN; tp_svc_channel_interface_tube_emit_tube_channel_state_changed (self, TP_TUBE_CHANNEL_STATE_OPEN); g_signal_emit (G_OBJECT (self), signals[OPENED], 0); return TRUE; fail: priv->address_type = 0; priv->access_control = 0; tp_g_value_slice_free (priv->access_control_param); priv->access_control_param = NULL; return FALSE; } /** * gabble_tube_iface_stream_close * * Implements gabble_tube_iface_close on GabbleTubeIface */ static void gabble_tube_iface_stream_close (GabbleTubeIface *tube, gboolean closed_remotely) { GabbleTubeStream *self = GABBLE_TUBE_STREAM (tube); GabbleTubeStreamPrivate *priv = self->priv; TpBaseChannel *base = TP_BASE_CHANNEL (self); TpBaseChannelClass *cls = TP_BASE_CHANNEL_GET_CLASS (base); TpBaseConnection *base_conn = tp_base_channel_get_connection (base); GabbleConnection *conn = GABBLE_CONNECTION (base_conn); if (tp_base_channel_is_destroyed (base)) return; g_hash_table_foreach_remove (priv->bytestream_to_transport, close_each_extra_bytestream, self); if (!closed_remotely && cls->target_handle_type == TP_HANDLE_TYPE_CONTACT) { WockyStanza *msg; const gchar *jid; TpHandleRepoIface *contact_repo = tp_base_connection_get_handles ( base_conn, TP_HANDLE_TYPE_CONTACT); gchar *id_str; jid = tp_handle_inspect (contact_repo, tp_base_channel_get_target_handle (base)); id_str = g_strdup_printf ("%" G_GUINT64_FORMAT, priv->id); /* Send the close message */ msg = wocky_stanza_build (WOCKY_STANZA_TYPE_MESSAGE, WOCKY_STANZA_SUB_TYPE_NONE, NULL, jid, '(', "close", ':', NS_TUBES, '@', "tube", id_str, ')', GABBLE_AMP_DO_NOT_STORE_SPEC, NULL); g_free (id_str); _gabble_connection_send (conn, msg, NULL); g_object_unref (msg); } /* Take a ref to ourselves as when we emit tube-closed * GabbleTubesChannel will drop our last ref but we still need to * declare ourselves as destroyed. This is rubbish, but will * disappear when we finally remove the Tubes channel type.. */ g_object_ref (self); if (cls->target_handle_type == TP_HANDLE_TYPE_ROOM) gabble_muc_channel_send_presence (priv->muc); g_signal_emit (G_OBJECT (self), signals[CLOSED], 0); tp_base_channel_destroyed (base); g_object_unref (self); } static void augment_si_accept_iq (WockyNode *si, gpointer user_data) { wocky_node_add_child_ns (si, "tube", NS_TUBES); } /** * gabble_tube_stream_add_bytestream * * Implements gabble_tube_iface_add_bytestream on GabbleTubeIface */ static void gabble_tube_stream_add_bytestream (GabbleTubeIface *tube, GabbleBytestreamIface *bytestream) { GabbleTubeStream *self = GABBLE_TUBE_STREAM (tube); GabbleTubeStreamPrivate *priv = self->priv; TpBaseChannel *base = TP_BASE_CHANNEL (self); TpHandle contact; GibberTransport *transport; if (!tp_base_channel_is_requested (base)) { DEBUG ("I'm not the initiator of this tube, can't accept " "an extra bytestream"); gabble_bytestream_iface_close (bytestream, NULL); return; } g_object_get (bytestream, "peer-handle", &contact, NULL); /* New bytestream, let's connect to the socket */ transport = new_connection_to_socket (self, bytestream, contact); if (transport != NULL) { if (priv->state == TP_TUBE_CHANNEL_STATE_REMOTE_PENDING) { DEBUG ("Received first connection. Tube is now open"); priv->state = TP_TUBE_CHANNEL_STATE_OPEN; tp_svc_channel_interface_tube_emit_tube_channel_state_changed ( self, TP_TUBE_CHANNEL_STATE_OPEN); g_signal_emit (G_OBJECT (self), signals[OPENED], 0); } DEBUG ("accept the extra bytestream"); gabble_bytestream_iface_accept (bytestream, augment_si_accept_iq, self); g_signal_emit (G_OBJECT (self), signals[NEW_CONNECTION], 0, contact); if (gibber_transport_get_state (transport) == GIBBER_TRANSPORT_CONNECTED) { gabble_bytestream_iface_block_reading (bytestream, FALSE); fire_new_remote_connection (self, transport, contact); } else { /* NewConnection will be fired once the transport is connected. * We can't get access_control_param (as the source port for example) * until it's connected. */ transport_connected_data *data; data = transport_connected_data_new (self, contact); g_signal_connect_data (transport, "connected", G_CALLBACK (transport_connected_cb), data, (GClosureNotify) transport_connected_data_free, 0); } } else { gabble_bytestream_iface_close (bytestream, NULL); } } #ifdef GIBBER_TYPE_UNIX_TRANSPORT static gboolean check_unix_params (TpSocketAddressType address_type, const GValue *address, TpSocketAccessControl access_control, const GValue *access_control_param, GError **error) { GArray *array; GString *socket_address; struct stat stat_buff; guint i; struct sockaddr_un dummy; g_assert (address_type == TP_SOCKET_ADDRESS_TYPE_UNIX); /* Check address type */ if (address != NULL) { if (G_VALUE_TYPE (address) != DBUS_TYPE_G_UCHAR_ARRAY) { g_set_error (error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT, "Unix socket address is supposed to be ay"); return FALSE; } array = g_value_get_boxed (address); if (array->len > sizeof (dummy.sun_path) - 1) { g_set_error (error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT, "Unix socket path is too long (max length allowed: %" G_GSIZE_FORMAT ")", sizeof (dummy.sun_path) - 1); return FALSE; } for (i = 0; i < array->len; i++) { if (g_array_index (array, gchar , i) == '\0') { g_set_error (error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT, "Unix socket path can't contain zero bytes"); return FALSE; } } socket_address = g_string_new_len (array->data, array->len); if (g_stat (socket_address->str, &stat_buff) == -1) { DEBUG ("Error calling stat on socket: %s", g_strerror (errno)); g_set_error (error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT, "%s: %s", socket_address->str, g_strerror (errno)); g_string_free (socket_address, TRUE); return FALSE; } if (!S_ISSOCK (stat_buff.st_mode)) { DEBUG ("%s is not a socket", socket_address->str); g_set_error (error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT, "%s is not a socket", socket_address->str); g_string_free (socket_address, TRUE); return FALSE; } g_string_free (socket_address, TRUE); } if (access_control == TP_SOCKET_ACCESS_CONTROL_LOCALHOST || access_control == TP_SOCKET_ACCESS_CONTROL_CREDENTIALS) { /* no variant associated */ return TRUE; } g_set_error (error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT, "%u socket access control is not supported", access_control); return FALSE; } #endif static gboolean check_ip_params (TpSocketAddressType address_type, const GValue *address, TpSocketAccessControl access_control, const GValue *access_control_param, GError **error) { /* Check address type */ if (address != NULL) { gchar *ip; guint port; struct addrinfo req, *result = NULL; int ret; if (address_type == TP_SOCKET_ADDRESS_TYPE_IPV4) { if (G_VALUE_TYPE (address) != TP_STRUCT_TYPE_SOCKET_ADDRESS_IPV4) { g_set_error (error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT, "IPv4 socket address is supposed to be sq"); return FALSE; } } else if (address_type == TP_SOCKET_ADDRESS_TYPE_IPV6) { if (G_VALUE_TYPE (address) != TP_STRUCT_TYPE_SOCKET_ADDRESS_IPV6) { g_set_error (error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT, "IPv6 socket address is supposed to be sq"); return FALSE; } } else { g_return_val_if_reached (FALSE); } dbus_g_type_struct_get (address, 0, &ip, 1, &port, G_MAXUINT); memset (&req, 0, sizeof (req)); req.ai_flags = AI_NUMERICHOST; req.ai_socktype = SOCK_STREAM; req.ai_protocol = IPPROTO_TCP; if (address_type == TP_SOCKET_ADDRESS_TYPE_IPV4) req.ai_family = AF_INET; else req.ai_family = AF_INET6; ret = getaddrinfo (ip, NULL, &req, &result); if (ret != 0) { g_set_error (error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT, "Invalid address: %s", gai_strerror (ret)); g_free (ip); return FALSE; } g_free (ip); freeaddrinfo (result); } if (access_control == TP_SOCKET_ACCESS_CONTROL_LOCALHOST) { return TRUE; } else if (access_control == TP_SOCKET_ACCESS_CONTROL_PORT) { if (access_control_param != NULL) { if (G_VALUE_TYPE (access_control_param) != TP_STRUCT_TYPE_SOCKET_ADDRESS_IPV4) { g_set_error (error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT, "Port access param is supposed to be sq"); return FALSE; } } return TRUE; } g_set_error (error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT, "%u socket access control is not supported", access_control); return FALSE; } /* used to check access control parameters both for OfferStreamTube and * AcceptStreamTube. In case of AcceptStreamTube, address is NULL because we * listen on the socket after the parameters have been accepted */ gboolean gabble_tube_stream_check_params (TpSocketAddressType address_type, const GValue *address, TpSocketAccessControl access_control, const GValue *access_control_param, GError **error) { switch (address_type) { #ifdef GIBBER_TYPE_UNIX_TRANSPORT case TP_SOCKET_ADDRESS_TYPE_UNIX: return check_unix_params (address_type, address, access_control, access_control_param, error); #endif case TP_SOCKET_ADDRESS_TYPE_IPV4: case TP_SOCKET_ADDRESS_TYPE_IPV6: return check_ip_params (address_type, address, access_control, access_control_param, error); default: g_set_error (error, TP_ERROR, TP_ERROR_NOT_IMPLEMENTED, "Address type %d not implemented", address_type); return FALSE; } } static gboolean send_tube_offer (GabbleTubeStream *self, GError **error) { GabbleTubeStreamPrivate *priv = self->priv; TpBaseChannel *base = TP_BASE_CHANNEL (self); TpBaseConnection *base_conn = tp_base_channel_get_connection (base); GabbleConnection *conn = GABBLE_CONNECTION (base_conn); TpBaseChannelClass *cls = TP_BASE_CHANNEL_GET_CLASS (base); WockyNode *tube_node = NULL; WockyStanza *msg; TpHandleRepoIface *contact_repo; const gchar *jid; gboolean result; GabblePresence *presence; const gchar *resource; gchar *full_jid; g_assert (cls->target_handle_type == TP_HANDLE_TYPE_CONTACT); contact_repo = tp_base_connection_get_handles (base_conn, TP_HANDLE_TYPE_CONTACT); jid = tp_handle_inspect (contact_repo, tp_base_channel_get_target_handle (base)); presence = gabble_presence_cache_get (conn->presence_cache, tp_base_channel_get_target_handle (base)); if (presence == NULL) { DEBUG ("can't find tube recipient's presence"); g_set_error (error, TP_ERROR, TP_ERROR_NOT_AVAILABLE, "can't find tube recipient's presence"); return FALSE; } resource = gabble_presence_pick_resource_by_caps (presence, 0, gabble_capability_set_predicate_has, NS_TUBES); if (resource == NULL) { DEBUG ("tube recipient doesn't have tubes capabilities"); g_set_error (error, TP_ERROR, TP_ERROR_NOT_AVAILABLE, "tube recipient doesn't have tubes capabilities"); return FALSE; } full_jid = g_strdup_printf ("%s/%s", jid, resource); msg = wocky_stanza_build (WOCKY_STANZA_TYPE_MESSAGE, WOCKY_STANZA_SUB_TYPE_NONE, NULL, full_jid, '(', "tube", '*', &tube_node, ':', NS_TUBES, ')', GABBLE_AMP_DO_NOT_STORE_SPEC, NULL); g_free (full_jid); g_assert (tube_node != NULL); gabble_tube_iface_publish_in_node (GABBLE_TUBE_IFACE (self), base_conn, tube_node); result = _gabble_connection_send (conn, msg, error); if (result) { priv->state = TP_TUBE_CHANNEL_STATE_REMOTE_PENDING; } g_object_unref (msg); return TRUE; } /* can be called both from the old tube API and the new tube API */ gboolean gabble_tube_stream_offer (GabbleTubeStream *self, GError **error) { GabbleTubeStreamPrivate *priv = self->priv; TpBaseChannel *base = TP_BASE_CHANNEL (self); TpBaseChannelClass *cls = TP_BASE_CHANNEL_GET_CLASS (base); g_assert (priv->state == TP_TUBE_CHANNEL_STATE_NOT_OFFERED); if (cls->target_handle_type == TP_HANDLE_TYPE_CONTACT) { /* 1-1 tube. Send tube offer message */ if (!send_tube_offer (self, error)) return FALSE; } else { /* muc tube is open as soon it's offered */ priv->state = TP_TUBE_CHANNEL_STATE_OPEN; g_signal_emit (G_OBJECT (self), signals[OPENED], 0); gabble_muc_channel_send_presence (priv->muc); } g_signal_emit (G_OBJECT (self), signals[OFFERED], 0); return TRUE; } static void destroy_socket_control_list (gpointer data) { GArray *tab = data; g_array_unref (tab); } /** * gabble_tube_stream_get_supported_socket_types * * Used to implement D-Bus property * org.freedesktop.Telepathy.Channel.Type.StreamTube.SupportedSocketTypes * and D-Bus method GetAvailableStreamTubeTypes * on org.freedesktop.Telepathy.Channel.Type.Tubes */ GHashTable * gabble_tube_stream_get_supported_socket_types (void) { GHashTable *ret; GArray *ipv4_tab, *ipv6_tab; TpSocketAccessControl access_control; #ifdef GIBBER_TYPE_UNIX_TRANSPORT GArray *unix_tab; #endif ret = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, destroy_socket_control_list); #ifdef GIBBER_TYPE_UNIX_TRANSPORT /* Socket_Address_Type_Unix */ unix_tab = g_array_sized_new (FALSE, FALSE, sizeof (TpSocketAccessControl), 1); access_control = TP_SOCKET_ACCESS_CONTROL_LOCALHOST; g_array_append_val (unix_tab, access_control); /* Credentials-passing is non-portable, so only advertise it on platforms * where we have an implementation (like Linux) */ if (gibber_unix_transport_supports_credentials ()) { access_control = TP_SOCKET_ACCESS_CONTROL_CREDENTIALS; g_array_append_val (unix_tab, access_control); } g_hash_table_insert (ret, GUINT_TO_POINTER (TP_SOCKET_ADDRESS_TYPE_UNIX), unix_tab); #endif /* Socket_Address_Type_IPv4 */ ipv4_tab = g_array_sized_new (FALSE, FALSE, sizeof (TpSocketAccessControl), 1); access_control = TP_SOCKET_ACCESS_CONTROL_LOCALHOST; g_array_append_val (ipv4_tab, access_control); access_control = TP_SOCKET_ACCESS_CONTROL_PORT; g_array_append_val (ipv4_tab, access_control); g_hash_table_insert (ret, GUINT_TO_POINTER (TP_SOCKET_ADDRESS_TYPE_IPV4), ipv4_tab); /* Socket_Address_Type_IPv6 */ ipv6_tab = g_array_sized_new (FALSE, FALSE, sizeof (TpSocketAccessControl), 1); access_control = TP_SOCKET_ACCESS_CONTROL_LOCALHOST; g_array_append_val (ipv6_tab, access_control); access_control = TP_SOCKET_ACCESS_CONTROL_PORT; g_array_append_val (ipv6_tab, access_control); g_hash_table_insert (ret, GUINT_TO_POINTER (TP_SOCKET_ADDRESS_TYPE_IPV6), ipv6_tab); return ret; } /** * gabble_tube_stream_offer_async * * Implements D-Bus method Offer * on org.freedesktop.Telepathy.Channel.Type.StreamTube */ static void gabble_tube_stream_offer_async (TpSvcChannelTypeStreamTube *iface, guint address_type, const GValue *address, guint access_control, GHashTable *parameters, DBusGMethodInvocation *context) { GabbleTubeStream *self = GABBLE_TUBE_STREAM (iface); GabbleTubeStreamPrivate *priv = self->priv; GError *error = NULL; TpBaseChannel *base = TP_BASE_CHANNEL (self); TpBaseChannelClass *cls = TP_BASE_CHANNEL_GET_CLASS (base); if (priv->state != TP_TUBE_CHANNEL_STATE_NOT_OFFERED) { g_set_error (&error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT, "Tube is not in the not offered state"); dbus_g_method_return_error (context, error); g_error_free (error); return; } if (!gabble_tube_stream_check_params (address_type, address, access_control, NULL, &error)) { dbus_g_method_return_error (context, error); g_error_free (error); return; } g_assert (address_type == TP_SOCKET_ADDRESS_TYPE_UNIX || address_type == TP_SOCKET_ADDRESS_TYPE_IPV4 || address_type == TP_SOCKET_ADDRESS_TYPE_IPV6); g_assert (priv->address == NULL); priv->address_type = address_type; priv->address = tp_g_value_slice_dup (address); g_assert (priv->access_control == TP_SOCKET_ACCESS_CONTROL_LOCALHOST); priv->access_control = access_control; g_object_set (self, "parameters", parameters, NULL); if (!gabble_tube_stream_offer (self, &error)) { gabble_tube_iface_stream_close (GABBLE_TUBE_IFACE (self), TRUE); dbus_g_method_return_error (context, error); g_error_free (error); return; } if (cls->target_handle_type == TP_HANDLE_TYPE_CONTACT) { tp_svc_channel_interface_tube_emit_tube_channel_state_changed ( self, TP_TUBE_CHANNEL_STATE_REMOTE_PENDING); } else { tp_svc_channel_interface_tube_emit_tube_channel_state_changed ( self, TP_TUBE_CHANNEL_STATE_OPEN); } tp_svc_channel_type_stream_tube_return_from_offer (context); } /** * gabble_tube_stream_accept_async * * Implements D-Bus method Accept * on org.freedesktop.Telepathy.Channel.Type.StreamTube */ static void gabble_tube_stream_accept_async (TpSvcChannelTypeStreamTube *iface, guint address_type, guint access_control, const GValue *access_control_param, DBusGMethodInvocation *context) { GabbleTubeStream *self = GABBLE_TUBE_STREAM (iface); GabbleTubeStreamPrivate *priv = self->priv; GError *error = NULL; /* parameters sanity checks are done in gabble_tube_stream_accept */ priv->address_type = address_type; priv->access_control = access_control; if (priv->access_control_param != NULL) tp_g_value_slice_free (priv->access_control_param); priv->access_control_param = tp_g_value_slice_dup (access_control_param); if (!gabble_tube_stream_accept (GABBLE_TUBE_IFACE (self), &error)) { dbus_g_method_return_error (context, error); g_error_free (error); return; } #if 0 /* TODO: add a property "muc" and set it at initialization */ if (priv->handle_type == TP_HANDLE_TYPE_ROOM) gabble_muc_channel_send_presence (self->muc, NULL); #endif tp_svc_channel_type_stream_tube_return_from_accept (context, priv->address); } const gchar * const * gabble_tube_stream_channel_get_allowed_properties (void) { return gabble_tube_stream_channel_allowed_properties; } static void tube_iface_init (gpointer g_iface, gpointer iface_data) { GabbleTubeIfaceClass *klass = (GabbleTubeIfaceClass *) g_iface; klass->accept = gabble_tube_stream_accept; klass->close = gabble_tube_iface_stream_close; klass->add_bytestream = gabble_tube_stream_add_bytestream; } static void streamtube_iface_init (gpointer g_iface, gpointer iface_data) { TpSvcChannelTypeStreamTubeClass *klass = (TpSvcChannelTypeStreamTubeClass *) g_iface; #define IMPLEMENT(x, suffix) tp_svc_channel_type_stream_tube_implement_##x (\ klass, gabble_tube_stream_##x##suffix) IMPLEMENT(offer,_async); IMPLEMENT(accept,_async); #undef IMPLEMENT } telepathy-gabble-0.18.2/src/tube-stream.h0000644000175000017500000000571512200204333020202 0ustar00smcvsmcv00000000000000/* * tube-stream.h - Header for GabbleTubeStream * Copyright (C) 2007 Collabora Ltd. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 __GABBLE_TUBE_STREAM_H__ #define __GABBLE_TUBE_STREAM_H__ #include #include #include #include "connection.h" #include "extensions/extensions.h" #include "muc-channel.h" G_BEGIN_DECLS typedef struct _GabbleTubeStream GabbleTubeStream; typedef struct _GabbleTubeStreamPrivate GabbleTubeStreamPrivate; typedef struct _GabbleTubeStreamClass GabbleTubeStreamClass; struct _GabbleTubeStreamClass { TpBaseChannelClass parent_class; TpDBusPropertiesMixinClass dbus_props_class; }; struct _GabbleTubeStream { TpBaseChannel parent; GabbleTubeStreamPrivate *priv; }; GType gabble_tube_stream_get_type (void); /* TYPE MACROS */ #define GABBLE_TYPE_TUBE_STREAM \ (gabble_tube_stream_get_type ()) #define GABBLE_TUBE_STREAM(obj) \ (G_TYPE_CHECK_INSTANCE_CAST((obj), GABBLE_TYPE_TUBE_STREAM, GabbleTubeStream)) #define GABBLE_TUBE_STREAM_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST((klass), GABBLE_TYPE_TUBE_STREAM,\ GabbleTubeStreamClass)) #define GABBLE_IS_TUBE_STREAM(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE((obj), GABBLE_TYPE_TUBE_STREAM)) #define GABBLE_IS_TUBE_STREAM_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE((klass), GABBLE_TYPE_TUBE_STREAM)) #define GABBLE_TUBE_STREAM_GET_CLASS(obj) \ (G_TYPE_INSTANCE_GET_CLASS ((obj), GABBLE_TYPE_TUBE_STREAM,\ GabbleTubeStreamClass)) GabbleTubeStream *gabble_tube_stream_new (GabbleConnection *conn, TpHandle handle, TpHandleType handle_type, TpHandle self_handle, TpHandle initiator, const gchar *service, GHashTable *parameters, guint64 id, GabbleMucChannel *muc, gboolean requested); gboolean gabble_tube_stream_check_params (TpSocketAddressType address_type, const GValue *address, TpSocketAccessControl access_control, const GValue *access_control_param, GError **error); gboolean gabble_tube_stream_offer (GabbleTubeStream *self, GError **error); GHashTable *gabble_tube_stream_get_supported_socket_types (void); const gchar * const * gabble_tube_stream_channel_get_allowed_properties (void); G_END_DECLS #endif /* #ifndef __GABBLE_TUBE_STREAM_H__ */ telepathy-gabble-0.18.2/src/tube-dbus.c0000644000175000017500000014750112227000321017637 0ustar00smcvsmcv00000000000000/* * tube-dbus.c - Source for GabbleTubeDBus * Copyright (C) 2007-2008 Collabora Ltd. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 "config.h" #include "tube-dbus.h" #include #include #include #include #include #include #include #include #include "extensions/extensions.h" #define DEBUG_FLAG GABBLE_DEBUG_TUBES #include "bytestream-factory.h" #include "bytestream-ibb.h" #include "bytestream-iface.h" #include "connection.h" #include "debug.h" #include "disco.h" #include "gabble-signals-marshal.h" #include "muc-tube-dbus.h" #include "namespaces.h" #include "presence-cache.h" #include "tube-iface.h" #include "util.h" /* When we receive D-Bus messages to be delivered to the application and the * application is not yet connected to the D-Bus tube, theses D-Bus messages * are queued and delivered when the application connects to the D-Bus tube. * * If the application never connects, there is a risk that the contact sends * too many messages and eat all the memory. To avoid this, there is an * arbitrary limit on the queue size set to 4MB. */ #define MAX_QUEUE_SIZE (4096*1024) static void tube_iface_init (gpointer g_iface, gpointer iface_data); static void dbustube_iface_init (gpointer g_iface, gpointer iface_data); G_DEFINE_TYPE_WITH_CODE (GabbleTubeDBus, gabble_tube_dbus, TP_TYPE_BASE_CHANNEL, G_IMPLEMENT_INTERFACE (GABBLE_TYPE_TUBE_IFACE, tube_iface_init); G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CHANNEL_TYPE_DBUS_TUBE, dbustube_iface_init); G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CHANNEL_INTERFACE_TUBE, NULL); G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CHANNEL_INTERFACE_GROUP, tp_external_group_mixin_iface_init); ); static const gchar * const gabble_tube_dbus_channel_allowed_properties[] = { TP_IFACE_CHANNEL ".TargetHandle", TP_IFACE_CHANNEL ".TargetID", TP_IFACE_CHANNEL_TYPE_DBUS_TUBE ".ServiceName", NULL }; /* signals */ enum { OPENED, CLOSED, OFFERED, LAST_SIGNAL }; static guint signals[LAST_SIGNAL] = {0}; /* properties */ enum { PROP_SELF_HANDLE = 1, PROP_ID, PROP_BYTESTREAM, PROP_STREAM_ID, PROP_TYPE, PROP_SERVICE, PROP_PARAMETERS, PROP_STATE, PROP_DBUS_ADDRESS, PROP_DBUS_NAME, PROP_DBUS_NAMES, PROP_MUC, PROP_SUPPORTED_ACCESS_CONTROLS, LAST_PROPERTY }; struct _GabbleTubeDBusPrivate { TpHandle self_handle; guint64 id; GabbleBytestreamIface *bytestream; gchar *stream_id; gchar *service; GHashTable *parameters; GabbleMucChannel *muc; TpSocketAccessControl access_control; /* GArray of guint */ GArray *supported_access_controls; /* For outgoing tubes, TRUE if the offer has been sent over the network. For * incoming tubes, always TRUE. */ gboolean offered; /* our unique D-Bus name on the virtual tube bus (NULL for 1-1 D-Bus tubes)*/ gchar *dbus_local_name; /* the address that we are listening for D-Bus connections on */ gchar *dbus_srv_addr; /* the path of the UNIX socket used by the D-Bus server */ gchar *socket_path; /* the server that's listening on dbus_srv_addr */ DBusServer *dbus_srv; /* the connection to dbus_srv from a local client, or NULL */ DBusConnection *dbus_conn; /* the queue of D-Bus messages to be delivered to a local client when it * will connect */ GSList *dbus_msg_queue; /* current size of the queue in bytes. The maximum is MAX_QUEUE_SIZE */ unsigned long dbus_msg_queue_size; /* mapping of contact handle -> D-Bus name (empty for 1-1 D-Bus tubes) */ GHashTable *dbus_names; /* mapping of D-Bus name -> contact handle */ GHashTable *dbus_name_to_handle; /* Message reassembly buffer (CONTACT tubes only) */ GString *reassembly_buffer; /* Number of bytes that will be in the next message, 0 if unknown */ guint32 reassembly_bytes_needed; gboolean dispose_has_run; }; #define GABBLE_TUBE_DBUS_GET_PRIVATE(obj) ((obj)->priv) static GPtrArray * gabble_tube_dbus_get_interfaces (TpBaseChannel *base) { GPtrArray *interfaces; interfaces = TP_BASE_CHANNEL_CLASS ( gabble_tube_dbus_parent_class)->get_interfaces (base); g_ptr_array_add (interfaces, TP_IFACE_CHANNEL_INTERFACE_TUBE); return interfaces; } static void data_received_cb (GabbleBytestreamIface *stream, TpHandle sender, GString *data, gpointer user_data); /* * Characters used are permissible both in filenames and in D-Bus names. (See * D-Bus specification for restrictions.) */ static void generate_ascii_string (guint len, gchar *buf) { const gchar *chars = "0123456789" "abcdefghijklmnopqrstuvwxyz" "ABCDEFGHIJKLMNOPQRSTUVWXYZ" "_-"; guint i; for (i = 0; i < len; i++) buf[i] = chars[g_random_int_range (0, 64)]; } static DBusHandlerResult filter_cb (DBusConnection *conn, DBusMessage *msg, void *user_data) { GabbleTubeDBus *tube = GABBLE_TUBE_DBUS (user_data); GabbleTubeDBusPrivate *priv = GABBLE_TUBE_DBUS_GET_PRIVATE (tube); gchar *marshalled = NULL; gint len; if (dbus_message_get_type (msg) == DBUS_MESSAGE_TYPE_SIGNAL && !tp_strdiff (dbus_message_get_interface (msg), "org.freedesktop.DBus.Local") && !tp_strdiff (dbus_message_get_member (msg), "Disconnected")) { /* connection was disconnected */ DEBUG ("connection was disconnected"); dbus_connection_close (priv->dbus_conn); tp_clear_pointer (&priv->dbus_conn, dbus_connection_unref); goto out; } if (priv->dbus_local_name != NULL) { if (!dbus_message_set_sender (msg, priv->dbus_local_name)) DEBUG ("dbus_message_set_sender failed"); } if (!dbus_message_marshal (msg, &marshalled, &len)) goto out; if (GABBLE_IS_BYTESTREAM_MUC (priv->bytestream)) { /* This bytestream support direct send */ const gchar *dest; dest = dbus_message_get_destination (msg); if (dest != NULL) { TpHandle handle; handle = GPOINTER_TO_UINT (g_hash_table_lookup ( priv->dbus_name_to_handle, dest)); if (handle == 0) { DEBUG ("Unknown D-Bus name: %s", dest); goto out; } gabble_bytestream_muc_send_to ( GABBLE_BYTESTREAM_MUC (priv->bytestream), handle, len, marshalled); goto out; } } gabble_bytestream_iface_send (priv->bytestream, len, marshalled); out: if (marshalled != NULL) g_free (marshalled); return DBUS_HANDLER_RESULT_HANDLED; } static dbus_bool_t allow_all_connections (DBusConnection *conn, unsigned long uid, void *data) { return TRUE; } static void new_connection_cb (DBusServer *server, DBusConnection *conn, void *data) { GabbleTubeDBus *tube = GABBLE_TUBE_DBUS (data); GabbleTubeDBusPrivate *priv = GABBLE_TUBE_DBUS_GET_PRIVATE (tube); guint32 serial; GSList *i; if (priv->dbus_conn != NULL) /* we already have a connection; drop this new one */ /* return without reffing conn means it will be dropped */ return; DEBUG ("got connection"); dbus_connection_ref (conn); dbus_connection_setup_with_g_main (conn, NULL); dbus_connection_add_filter (conn, filter_cb, tube, NULL); priv->dbus_conn = conn; if (priv->access_control == TP_SOCKET_ACCESS_CONTROL_LOCALHOST) { /* By default libdbus use Credentials access control. If user wants * to use the Localhost access control, we need to bypass this check. */ dbus_connection_set_unix_user_function (conn, allow_all_connections, NULL, NULL); } /* We may have received messages to deliver before the local connection is * established. Theses messages are kept in the dbus_msg_queue list and are * delivered as soon as we get the connection. */ DEBUG ("%u messages in the queue (%lu bytes)", g_slist_length (priv->dbus_msg_queue), priv->dbus_msg_queue_size); priv->dbus_msg_queue = g_slist_reverse (priv->dbus_msg_queue); for (i = priv->dbus_msg_queue; i != NULL; i = g_slist_delete_link (i, i)) { DBusMessage *msg = i->data; DEBUG ("delivering queued message from '%s' to '%s' on the " "new connection", dbus_message_get_sender (msg), dbus_message_get_destination (msg)); dbus_connection_send (priv->dbus_conn, msg, &serial); dbus_message_unref (msg); } priv->dbus_msg_queue = NULL; priv->dbus_msg_queue_size = 0; } static void gabble_tube_dbus_close (TpBaseChannel *base) { GabbleTubeDBus *self = GABBLE_TUBE_DBUS (base); GabbleTubeDBusPrivate *priv = GABBLE_TUBE_DBUS_GET_PRIVATE (self); /* Take a ref to ourselves as when we emit tube-closed * GabbleTubesChannel will drop our last ref but we still need to * declare ourselves as destroyed. This is rubbish, but will * disappear when we finally remove the Tubes channel type.. */ g_object_ref (base); if (priv->bytestream != NULL) gabble_bytestream_iface_close (priv->bytestream, NULL); else g_signal_emit (G_OBJECT (self), signals[CLOSED],0); tp_base_channel_destroyed (base); g_object_unref (base); } /* There is two step to enable receiving a D-Bus connection from the local * application: * - listen on the socket * - add the socket in the mainloop * * We need to know the socket path to return from the AcceptDBusTube D-Bus * call but the socket in the mainloop must be added only when we are ready * to receive connections, that is when the bytestream is fully open with the * remote contact. * * See also Bug 13891: * https://bugs.freedesktop.org/show_bug.cgi?id=13891 * */ static gboolean create_dbus_server (GabbleTubeDBus *self, GError **err) { #define SERVER_LISTEN_MAX_TRIES 5 GabbleTubeDBusPrivate *priv = GABBLE_TUBE_DBUS_GET_PRIVATE (self); guint i; if (priv->dbus_srv != NULL) return TRUE; for (i = 0; i < SERVER_LISTEN_MAX_TRIES; i++) { gchar suffix[8]; DBusError error; g_free (priv->dbus_srv_addr); g_free (priv->socket_path); generate_ascii_string (8, suffix); priv->socket_path = g_strdup_printf ("%s/dbus-gabble-%.8s", g_get_tmp_dir (), suffix); priv->dbus_srv_addr = g_strdup_printf ("unix:path=%s", priv->socket_path); dbus_error_init (&error); priv->dbus_srv = dbus_server_listen (priv->dbus_srv_addr, &error); if (priv->dbus_srv != NULL) break; DEBUG ("dbus_server_listen failed (try %u): %s: %s", i, error.name, error.message); dbus_error_free (&error); } if (priv->dbus_srv == NULL) { DEBUG ("all attempts failed. Close the tube"); g_free (priv->dbus_srv_addr); priv->dbus_srv_addr = NULL; g_free (priv->socket_path); priv->socket_path = NULL; g_set_error (err, TP_ERROR, TP_ERROR_NOT_AVAILABLE, "Can't create D-Bus server"); return FALSE; } DEBUG ("listening on %s", priv->dbus_srv_addr); dbus_server_set_new_connection_function (priv->dbus_srv, new_connection_cb, self, NULL); return TRUE; } static void tube_dbus_open (GabbleTubeDBus *self) { GabbleTubeDBusPrivate *priv = GABBLE_TUBE_DBUS_GET_PRIVATE (self); TpBaseChannel *base = TP_BASE_CHANNEL (self); TpBaseChannelClass *cls = TP_BASE_CHANNEL_GET_CLASS (base); g_signal_connect (priv->bytestream, "data-received", G_CALLBACK (data_received_cb), self); /* TODO: we should remove this call once muc D-Bus tube new API are * implemented as the server should already exist. */ if (!create_dbus_server (self, NULL)) tp_base_channel_close (TP_BASE_CHANNEL (self)); if (priv->dbus_srv != NULL) { dbus_server_setup_with_g_main (priv->dbus_srv, NULL); } if (cls->target_handle_type == TP_HANDLE_TYPE_ROOM) { /* add yourself in dbus names */ gabble_tube_dbus_add_name (self, priv->self_handle, priv->dbus_local_name); gabble_muc_channel_send_presence (priv->muc); } } static void gabble_tube_dbus_init (GabbleTubeDBus *self) { GabbleTubeDBusPrivate *priv = G_TYPE_INSTANCE_GET_PRIVATE (self, GABBLE_TYPE_TUBE_DBUS, GabbleTubeDBusPrivate); self->priv = priv; } static TpTubeChannelState get_tube_state (GabbleTubeDBus *self) { GabbleTubeDBusPrivate *priv = GABBLE_TUBE_DBUS_GET_PRIVATE (self); GabbleBytestreamState bytestream_state; if (!priv->offered) return TP_TUBE_CHANNEL_STATE_NOT_OFFERED; if (priv->bytestream == NULL) /* bytestream not yet created as we're waiting for the SI reply */ return TP_TUBE_CHANNEL_STATE_REMOTE_PENDING; g_object_get (priv->bytestream, "state", &bytestream_state, NULL); switch (bytestream_state) { case GABBLE_BYTESTREAM_STATE_OPEN: return TP_TUBE_CHANNEL_STATE_OPEN; break; case GABBLE_BYTESTREAM_STATE_LOCAL_PENDING: case GABBLE_BYTESTREAM_STATE_ACCEPTED: return TP_TUBE_CHANNEL_STATE_LOCAL_PENDING; break; case GABBLE_BYTESTREAM_STATE_INITIATING: return TP_TUBE_CHANNEL_STATE_REMOTE_PENDING; break; default: g_return_val_if_reached (0); } } static void bytestream_state_changed_cb (GabbleBytestreamIface *bytestream, GabbleBytestreamState state, gpointer user_data) { GabbleTubeDBus *self = GABBLE_TUBE_DBUS (user_data); GabbleTubeDBusPrivate *priv = GABBLE_TUBE_DBUS_GET_PRIVATE (self); TpBaseChannel *base = TP_BASE_CHANNEL (self); TpBaseChannelClass *cls = TP_BASE_CHANNEL_GET_CLASS (base); if (state == GABBLE_BYTESTREAM_STATE_CLOSED) { tp_clear_object (&priv->bytestream); g_signal_emit (G_OBJECT (self), signals[CLOSED], 0); if (cls->target_handle_type == TP_HANDLE_TYPE_ROOM) gabble_muc_channel_send_presence (priv->muc); } else if (state == GABBLE_BYTESTREAM_STATE_OPEN) { tube_dbus_open (self); tp_svc_channel_interface_tube_emit_tube_channel_state_changed (self, TP_TUBE_CHANNEL_STATE_OPEN); g_signal_emit (G_OBJECT (self), signals[OPENED], 0); } } static void gabble_tube_dbus_dispose (GObject *object) { GabbleTubeDBus *self = GABBLE_TUBE_DBUS (object); GabbleTubeDBusPrivate *priv = GABBLE_TUBE_DBUS_GET_PRIVATE (self); DEBUG ("called"); if (priv->dispose_has_run) return; priv->dispose_has_run = TRUE; if (priv->bytestream != NULL) gabble_bytestream_iface_close (priv->bytestream, NULL); if (priv->dbus_conn != NULL) dbus_connection_close (priv->dbus_conn); tp_clear_pointer (&priv->dbus_conn, dbus_connection_unref); if (priv->dbus_srv != NULL) dbus_server_disconnect (priv->dbus_srv); tp_clear_pointer (&priv->dbus_srv, dbus_server_unref); if (priv->socket_path != NULL) { if (g_unlink (priv->socket_path) != 0) { DEBUG ("unlink of %s failed: %s", priv->socket_path, g_strerror (errno)); } } if (priv->dbus_msg_queue != NULL) { GSList *i; for (i = priv->dbus_msg_queue; i != NULL; i = g_slist_delete_link (i, i)) { DBusMessage *msg = i->data; dbus_message_unref (msg); } priv->dbus_msg_queue = NULL; priv->dbus_msg_queue_size = 0; } tp_clear_pointer (&priv->dbus_srv_addr, g_free); tp_clear_pointer (&priv->socket_path, g_free); tp_clear_pointer (&priv->dbus_local_name, g_free); tp_clear_pointer (&priv->dbus_names, g_hash_table_unref); tp_clear_pointer (&priv->dbus_name_to_handle, g_hash_table_unref); if (priv->reassembly_buffer) g_string_free (priv->reassembly_buffer, TRUE); if (G_OBJECT_CLASS (gabble_tube_dbus_parent_class)->dispose) G_OBJECT_CLASS (gabble_tube_dbus_parent_class)->dispose (object); } static void gabble_tube_dbus_finalize (GObject *object) { GabbleTubeDBus *self = GABBLE_TUBE_DBUS (object); GabbleTubeDBusPrivate *priv = GABBLE_TUBE_DBUS_GET_PRIVATE (self); g_free (priv->stream_id); g_free (priv->service); g_hash_table_unref (priv->parameters); g_array_unref (priv->supported_access_controls); if (priv->muc != NULL) tp_external_group_mixin_finalize (object); G_OBJECT_CLASS (gabble_tube_dbus_parent_class)->finalize (object); } static void gabble_tube_dbus_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { GabbleTubeDBus *self = GABBLE_TUBE_DBUS (object); GabbleTubeDBusPrivate *priv = GABBLE_TUBE_DBUS_GET_PRIVATE (self); switch (property_id) { case PROP_SELF_HANDLE: g_value_set_uint (value, priv->self_handle); break; case PROP_ID: g_value_set_uint64 (value, priv->id); break; case PROP_BYTESTREAM: g_value_set_object (value, priv->bytestream); break; case PROP_STREAM_ID: g_value_set_string (value, priv->stream_id); break; case PROP_TYPE: g_value_set_uint (value, TP_TUBE_TYPE_DBUS); break; case PROP_SERVICE: g_value_set_string (value, priv->service); break; case PROP_PARAMETERS: g_value_set_boxed (value, priv->parameters); break; case PROP_STATE: g_value_set_uint (value, get_tube_state (self)); break; case PROP_DBUS_ADDRESS: g_value_set_string (value, priv->dbus_srv_addr); break; case PROP_DBUS_NAME: g_value_set_string (value, priv->dbus_local_name); break; case PROP_DBUS_NAMES: g_value_set_boxed (value, priv->dbus_names); break; case PROP_MUC: g_value_set_object (value, priv->muc); break; case PROP_SUPPORTED_ACCESS_CONTROLS: g_value_set_boxed (value, priv->supported_access_controls); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void gabble_tube_dbus_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { GabbleTubeDBus *self = GABBLE_TUBE_DBUS (object); GabbleTubeDBusPrivate *priv = GABBLE_TUBE_DBUS_GET_PRIVATE (self); switch (property_id) { case PROP_SELF_HANDLE: priv->self_handle = g_value_get_uint (value); break; case PROP_ID: priv->id = g_value_get_uint64 (value); break; case PROP_BYTESTREAM: if (priv->bytestream == NULL) { GabbleBytestreamState state; priv->bytestream = g_value_get_object (value); g_object_ref (priv->bytestream); g_object_get (priv->bytestream, "state", &state, NULL); if (state == GABBLE_BYTESTREAM_STATE_OPEN) { tube_dbus_open (self); } g_signal_connect (priv->bytestream, "state-changed", G_CALLBACK (bytestream_state_changed_cb), self); } break; case PROP_STREAM_ID: g_free (priv->stream_id); priv->stream_id = g_value_dup_string (value); break; case PROP_SERVICE: g_free (priv->service); priv->service = g_value_dup_string (value); break; case PROP_PARAMETERS: if (priv->parameters != NULL) g_hash_table_unref (priv->parameters); priv->parameters = g_value_dup_boxed (value); break; case PROP_MUC: priv->muc = g_value_get_object (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void gabble_tube_dbus_constructed (GObject *obj) { GabbleTubeDBus *self = GABBLE_TUBE_DBUS (obj); GabbleTubeDBusPrivate *priv = self->priv; TpBaseChannel *base = TP_BASE_CHANNEL (obj); TpBaseChannelClass *cls = TP_BASE_CHANNEL_GET_CLASS (base); TpBaseConnection *base_conn = tp_base_channel_get_connection (base); GabbleConnection *conn = GABBLE_CONNECTION (base_conn); TpHandleRepoIface *contact_repo = tp_base_connection_get_handles ( base_conn, TP_HANDLE_TYPE_CONTACT); guint access_control; void (*chain_up) (GObject *) = ((GObjectClass *) gabble_tube_dbus_parent_class)->constructed; if (chain_up != NULL) chain_up (obj); priv->dbus_names = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, g_free); g_assert (priv->self_handle != 0); if (cls->target_handle_type == TP_HANDLE_TYPE_ROOM) { /* We have to create a pseudo-IBB bytestream that will be * used by this MUC tube to communicate. */ GabbleBytestreamMuc *bytestream; gchar *nick; g_assert (priv->stream_id != NULL); priv->dbus_name_to_handle = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, NULL); g_assert (wocky_decode_jid ( tp_handle_inspect (contact_repo, priv->self_handle), NULL, NULL, &nick)); g_assert (nick != NULL); priv->dbus_local_name = _gabble_generate_dbus_unique_name (nick); DEBUG ("local name: %s", priv->dbus_local_name); bytestream = gabble_bytestream_factory_create_muc ( conn->bytestream_factory, tp_base_channel_get_target_handle (base), priv->stream_id, GABBLE_BYTESTREAM_STATE_LOCAL_PENDING); g_object_set (self, "bytestream", bytestream, NULL); g_free (nick); g_assert (priv->muc != NULL); tp_external_group_mixin_init (obj, (GObject *) priv->muc); } else { /* The D-Bus names mapping is used in muc tubes only */ priv->dbus_local_name = NULL; priv->dbus_name_to_handle = NULL; /* For contact (IBB) tubes we need to be able to reassemble messages. */ priv->reassembly_buffer = g_string_new (""); priv->reassembly_bytes_needed = 0; g_assert (priv->muc == NULL); if (tp_base_channel_is_requested (base)) { /* We created this outgoing 1-1 D-Bus tube and so will need SOCKS5 * proxies when we'll offer it. */ gabble_bytestream_factory_query_socks5_proxies ( conn->bytestream_factory); } } /* Tube needs to be offered if we initiated AND requested it. Being * the initiator is not enough as we could re-join a muc containing and old * tube we created when we were in this room some time ago. In that case, we * have to accept it if we want to re-join the tube. */ if (tp_base_channel_get_initiator (base) == priv->self_handle && tp_base_channel_is_requested (base)) { priv->offered = FALSE; } else { /* Incoming tubes have already been offered, as it were. */ priv->offered = TRUE; } /* default access control is Credentials as that's the one used by the old * tube API */ priv->access_control = TP_SOCKET_ACCESS_CONTROL_CREDENTIALS; /* Set SupportedAccessesControl */ priv->supported_access_controls = g_array_sized_new (FALSE, FALSE, sizeof (guint), 1); access_control = TP_SOCKET_ACCESS_CONTROL_CREDENTIALS; g_array_append_val (priv->supported_access_controls, access_control); access_control = TP_SOCKET_ACCESS_CONTROL_LOCALHOST; g_array_append_val (priv->supported_access_controls, access_control); } static void gabble_tube_dbus_fill_immutable_properties (TpBaseChannel *chan, GHashTable *properties) { TpBaseChannelClass *cls = TP_BASE_CHANNEL_CLASS ( gabble_tube_dbus_parent_class); cls->fill_immutable_properties (chan, properties); tp_dbus_properties_mixin_fill_properties_hash ( G_OBJECT (chan), properties, TP_IFACE_CHANNEL_TYPE_DBUS_TUBE, "ServiceName", TP_IFACE_CHANNEL_TYPE_DBUS_TUBE, "SupportedAccessControls", NULL); if (!tp_base_channel_is_requested (chan)) { tp_dbus_properties_mixin_fill_properties_hash ( G_OBJECT (chan), properties, TP_IFACE_CHANNEL_INTERFACE_TUBE, "Parameters", NULL); } } static gchar * gabble_tube_dbus_get_object_path_suffix (TpBaseChannel *base) { GabbleTubeDBus *self = GABBLE_TUBE_DBUS (base); return g_strdup_printf ("DBusTubeChannel/%u/%" G_GUINT64_FORMAT, tp_base_channel_get_target_handle (base), self->priv->id); } static void gabble_tube_dbus_class_init (GabbleTubeDBusClass *gabble_tube_dbus_class) { static TpDBusPropertiesMixinPropImpl dbus_tube_props[] = { { "ServiceName", "service", NULL }, { "DBusNames", "dbus-names", NULL }, { "SupportedAccessControls", "supported-access-controls", NULL }, { NULL } }; static TpDBusPropertiesMixinPropImpl tube_iface_props[] = { { "Parameters", "parameters", NULL }, { "State", "state", NULL }, { NULL } }; static TpDBusPropertiesMixinIfaceImpl prop_interfaces[] = { { TP_IFACE_CHANNEL_TYPE_DBUS_TUBE, tp_dbus_properties_mixin_getter_gobject_properties, NULL, dbus_tube_props, }, { TP_IFACE_CHANNEL_INTERFACE_TUBE, tp_dbus_properties_mixin_getter_gobject_properties, NULL, tube_iface_props, }, { NULL } }; GObjectClass *object_class = G_OBJECT_CLASS (gabble_tube_dbus_class); TpBaseChannelClass *base_class = TP_BASE_CHANNEL_CLASS (gabble_tube_dbus_class); GParamSpec *param_spec; object_class->get_property = gabble_tube_dbus_get_property; object_class->set_property = gabble_tube_dbus_set_property; object_class->constructed = gabble_tube_dbus_constructed; object_class->dispose = gabble_tube_dbus_dispose; object_class->finalize = gabble_tube_dbus_finalize; base_class->channel_type = TP_IFACE_CHANNEL_TYPE_DBUS_TUBE; base_class->get_interfaces = gabble_tube_dbus_get_interfaces; base_class->target_handle_type = TP_HANDLE_TYPE_CONTACT; base_class->close = gabble_tube_dbus_close; base_class->fill_immutable_properties = gabble_tube_dbus_fill_immutable_properties; base_class->get_object_path_suffix = gabble_tube_dbus_get_object_path_suffix; g_type_class_add_private (gabble_tube_dbus_class, sizeof (GabbleTubeDBusPrivate)); g_object_class_override_property (object_class, PROP_SELF_HANDLE, "self-handle"); g_object_class_override_property (object_class, PROP_ID, "id"); g_object_class_override_property (object_class, PROP_TYPE, "type"); g_object_class_override_property (object_class, PROP_SERVICE, "service"); g_object_class_override_property (object_class, PROP_PARAMETERS, "parameters"); g_object_class_override_property (object_class, PROP_STATE, "state"); param_spec = g_param_spec_object ( "bytestream", "Object implementing the GabbleBytestreamIface interface", "Bytestream object used for streaming data for this" "tube object.", G_TYPE_OBJECT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, PROP_BYTESTREAM, param_spec); param_spec = g_param_spec_string ( "stream-id", "stream id", "The identifier of this tube's bytestream", "", G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, PROP_STREAM_ID, param_spec); param_spec = g_param_spec_string ( "dbus-address", "D-Bus address", "The D-Bus address on which this tube will listen for connections", "", G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, PROP_DBUS_ADDRESS, param_spec); param_spec = g_param_spec_string ( "dbus-name", "D-Bus name", "The local D-Bus name on the virtual bus (used for muc tubes only).", "", G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, PROP_DBUS_NAME, param_spec); param_spec = g_param_spec_boxed ( "dbus-names", "D-Bus names", "Mapping of contact handles to D-Bus names (used for muc tubes only).", TP_HASH_TYPE_DBUS_TUBE_PARTICIPANTS, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, PROP_DBUS_NAMES, param_spec); param_spec = g_param_spec_object ("muc", "GabbleMucChannel object", "Gabble text MUC channel corresponding to this Tube channel object, " "if the handle type is ROOM.", GABBLE_TYPE_MUC_CHANNEL, G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, PROP_MUC, param_spec); param_spec = g_param_spec_boxed ( "supported-access-controls", "Supported access-controls", "GArray containing supported access controls.", DBUS_TYPE_G_UINT_ARRAY, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, PROP_SUPPORTED_ACCESS_CONTROLS, param_spec); signals[OPENED] = g_signal_new ("tube-opened", G_OBJECT_CLASS_TYPE (gabble_tube_dbus_class), G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED, 0, NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); signals[CLOSED] = g_signal_new ("tube-closed", G_OBJECT_CLASS_TYPE (gabble_tube_dbus_class), G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED, 0, NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); signals[OFFERED] = g_signal_new ("tube-offered", G_OBJECT_CLASS_TYPE (gabble_tube_dbus_class), G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED, 0, NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); gabble_tube_dbus_class->dbus_props_class.interfaces = prop_interfaces; tp_dbus_properties_mixin_class_init (object_class, G_STRUCT_OFFSET (GabbleTubeDBusClass, dbus_props_class)); tp_external_group_mixin_init_dbus_properties (object_class); } static void bytestream_negotiate_cb (GabbleBytestreamIface *bytestream, WockyStanza *msg, GObject *object, gpointer user_data) { GabbleTubeIface *tube = user_data; if (bytestream == NULL) { /* Tube was declined by remote user. Close it */ gabble_tube_iface_close (tube, TRUE); return; } /* Tube was accepted by remote user */ g_object_set (tube, "bytestream", bytestream, NULL); gabble_tube_iface_accept (tube, NULL); } gboolean gabble_tube_dbus_offer (GabbleTubeDBus *tube, GError **error) { GabbleTubeDBusPrivate *priv = tube->priv; TpBaseChannel *base = TP_BASE_CHANNEL (tube); TpBaseChannelClass *cls = TP_BASE_CHANNEL_GET_CLASS (base); TpBaseConnection *base_conn = tp_base_channel_get_connection (base); GabbleConnection *conn = GABBLE_CONNECTION (base_conn); if (priv->offered) { g_set_error (error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT, "Tube has already been offered"); return FALSE; } if (cls->target_handle_type == TP_HANDLE_TYPE_CONTACT) { TpHandleRepoIface *contact_repo = tp_base_connection_get_handles ( base_conn, TP_HANDLE_TYPE_CONTACT); const gchar *jid, *resource; gchar *full_jid; GabblePresence *presence; WockyNode *tube_node, *si_node; WockyStanza *msg; jid = tp_handle_inspect (contact_repo, tp_base_channel_get_target_handle (base)); presence = gabble_presence_cache_get (conn->presence_cache, tp_base_channel_get_target_handle (base)); if (presence == NULL) { DEBUG ("can't find contact %s's presence", jid); g_set_error (error, TP_ERROR, TP_ERROR_NOT_AVAILABLE, "can't find contact %s's presence", jid); return FALSE; } resource = gabble_presence_pick_resource_by_caps (presence, 0, gabble_capability_set_predicate_has, NS_TUBES); if (resource == NULL) { DEBUG ("contact %s doesn't have tubes capabilities", jid); g_set_error (error, TP_ERROR, TP_ERROR_NOT_AVAILABLE, "contact %s doesn't have tubes capabilities", jid); return FALSE; } full_jid = g_strdup_printf ("%s/%s", jid, resource); msg = gabble_bytestream_factory_make_stream_init_iq (full_jid, priv->stream_id, NS_TUBES); si_node = wocky_node_get_child_ns ( wocky_stanza_get_top_node (msg), "si", NS_SI); g_assert (si_node != NULL); tube_node = wocky_node_add_child_ns (si_node, "tube", NS_TUBES); gabble_tube_iface_publish_in_node (GABBLE_TUBE_IFACE (tube), base_conn, tube_node); tube->priv->offered = TRUE; gabble_bytestream_factory_negotiate_stream ( conn->bytestream_factory, msg, priv->stream_id, bytestream_negotiate_cb, tube, G_OBJECT (tube)); /* We don't create the bytestream of private D-Bus tube yet. * It will be when we'll receive the answer of the SI request */ g_object_unref (msg); g_free (full_jid); tp_svc_channel_interface_tube_emit_tube_channel_state_changed (tube, TP_TUBE_CHANNEL_STATE_REMOTE_PENDING); } else { tube->priv->offered = TRUE; g_object_set (priv->bytestream, "state", GABBLE_BYTESTREAM_STATE_OPEN, NULL); gabble_muc_channel_send_presence (priv->muc); } if (!create_dbus_server (tube, error)) return FALSE; g_signal_emit (G_OBJECT (tube), signals[OFFERED], 0); return TRUE; } static void message_received (GabbleTubeDBus *tube, TpHandle sender, const char *data, size_t len) { GabbleTubeDBusPrivate *priv = GABBLE_TUBE_DBUS_GET_PRIVATE (tube); TpBaseChannel *base = TP_BASE_CHANNEL (tube); TpBaseChannelClass *cls = TP_BASE_CHANNEL_GET_CLASS (base); DBusMessage *msg; DBusError error = {0,}; const gchar *sender_name; const gchar *destination; guint32 serial; msg = dbus_message_demarshal (data, len, &error); if (msg == NULL) { /* message was corrupted */ DEBUG ("received corrupted message from %d: %s: %s", sender, error.name, error.message); dbus_error_free (&error); return; } if (cls->target_handle_type == TP_HANDLE_TYPE_ROOM) { destination = dbus_message_get_destination (msg); /* If destination is NULL this msg is broadcasted (signals) so we don't * have to check it */ if (destination != NULL && tp_strdiff (priv->dbus_local_name, destination)) { /* This message is not intended for this participant. * Discard it. */ DEBUG ("message not intended for this participant (destination = " "%s)", destination); goto unref; } sender_name = g_hash_table_lookup (priv->dbus_names, GUINT_TO_POINTER (sender)); if (tp_strdiff (sender_name, dbus_message_get_sender (msg))) { DEBUG ("invalid sender %s (expected %s for sender handle %d)", dbus_message_get_sender (msg), sender_name, sender); goto unref; } } if (!priv->dbus_conn) { DEBUG ("no D-Bus connection: queuing the message"); /* If the application never connects to the private dbus connection, we * don't want to eat all the memory. Only queue MAX_QUEUE_SIZE bytes. If * there are more messages, drop them. */ if (priv->dbus_msg_queue_size + len > MAX_QUEUE_SIZE) { DEBUG ("D-Bus message queue size limit reached (%u bytes). " "Ignore this message.", MAX_QUEUE_SIZE); goto unref; } priv->dbus_msg_queue = g_slist_prepend (priv->dbus_msg_queue, msg); priv->dbus_msg_queue_size += len; /* returns without unref the message */ return; } DEBUG ("delivering message from '%s' to '%s'", dbus_message_get_sender (msg), dbus_message_get_destination (msg)); /* XXX: what do do if this returns FALSE? */ dbus_connection_send (priv->dbus_conn, msg, &serial); unref: dbus_message_unref (msg); } static guint32 collect_le32 (char *str) { unsigned char *bytes = (unsigned char *) str; return bytes[0] | (bytes[1] << 8) | (bytes[2] << 16) | (bytes[3] << 24); } static guint32 collect_be32 (char *str) { unsigned char *bytes = (unsigned char *) str; return (bytes[0] << 24) | (bytes[1] << 16) | (bytes[2] << 24) | bytes[3]; } static void data_received_cb (GabbleBytestreamIface *stream, TpHandle sender, GString *data, gpointer user_data) { GabbleTubeDBus *tube = GABBLE_TUBE_DBUS (user_data); GabbleTubeDBusPrivate *priv = GABBLE_TUBE_DBUS_GET_PRIVATE (tube); TpBaseChannel *base = TP_BASE_CHANNEL (tube); TpBaseChannelClass *cls = TP_BASE_CHANNEL_GET_CLASS (base); if (cls->target_handle_type == TP_HANDLE_TYPE_CONTACT) { GString *buf = priv->reassembly_buffer; g_assert (buf != NULL); g_string_append_len (buf, data->str, data->len); DEBUG ("Received %" G_GSIZE_FORMAT " bytes, so we now have %" G_GSIZE_FORMAT " bytes in reassembly buffer", data->len, buf->len); /* Each D-Bus message has a 16-byte fixed header, in which * * * byte 0 is 'l' (ell) or 'B' for endianness * * bytes 4-7 are body length "n" in bytes in that endianness * * bytes 12-15 are length "m" of param array in bytes in that * endianness * * followed by m + n + ((8 - (m % 8)) % 8) bytes of other content. */ while (buf->len >= 16) { guint32 body_length, params_length, m; /* see if we have a whole message and have already calculated * how many bytes it needs */ if (priv->reassembly_bytes_needed != 0) { if (buf->len >= priv->reassembly_bytes_needed) { DEBUG ("Received complete D-Bus message of size %" G_GINT32_FORMAT, priv->reassembly_bytes_needed); message_received (tube, sender, buf->str, priv->reassembly_bytes_needed); g_string_erase (buf, 0, priv->reassembly_bytes_needed); priv->reassembly_bytes_needed = 0; } else { /* we'll have to wait for more data */ break; } } if (buf->len < 16) break; /* work out how big the next message is going to be */ if (buf->str[0] == DBUS_BIG_ENDIAN) { body_length = collect_be32 (buf->str + 4); m = collect_be32 (buf->str + 12); } else if (buf->str[0] == DBUS_LITTLE_ENDIAN) { body_length = collect_le32 (buf->str + 4); m = collect_le32 (buf->str + 12); } else { DEBUG ("D-Bus message has unknown endianness byte 0x%x, " "closing tube", (unsigned int) buf->str[0]); gabble_tube_iface_close ((GabbleTubeIface *) tube, TRUE); return; } /* pad to 8-byte boundary */ params_length = m + ((8 - (m % 8)) % 8); g_assert (params_length % 8 == 0); g_assert (params_length >= m); g_assert (params_length < m + 8); priv->reassembly_bytes_needed = params_length + body_length + 16; /* n.b.: this looks as if it could be simplified to just the third * test, but that would be wrong if the addition had overflowed, so * don't do that. The first and second tests are sufficient to * ensure no overflow on 32-bit platforms */ if (body_length > DBUS_MAXIMUM_MESSAGE_LENGTH || params_length > DBUS_MAXIMUM_ARRAY_LENGTH || priv->reassembly_bytes_needed > DBUS_MAXIMUM_MESSAGE_LENGTH) { DEBUG ("D-Bus message is too large to be valid, closing tube"); gabble_tube_iface_close ((GabbleTubeIface *) tube, TRUE); return; } g_assert (priv->reassembly_bytes_needed != 0); DEBUG ("We need %" G_GINT32_FORMAT " bytes for the next full " "message", priv->reassembly_bytes_needed); } } else { /* MUC bytestreams are message-boundary preserving, which is necessary, * because we can't assume we started at the beginning */ g_assert (GABBLE_IS_BYTESTREAM_MUC (priv->bytestream)); message_received (tube, sender, data->str, data->len); } } GabbleTubeDBus * gabble_tube_dbus_new (GabbleConnection *conn, TpHandle handle, TpHandleType handle_type, TpHandle self_handle, TpHandle initiator, const gchar *service, GHashTable *parameters, const gchar *stream_id, guint64 id, GabbleBytestreamIface *bytestream, GabbleMucChannel *muc, gboolean requested) { GabbleTubeDBus *tube; GType gtype = GABBLE_TYPE_TUBE_DBUS; if (handle_type == TP_HANDLE_TYPE_ROOM) gtype = GABBLE_TYPE_MUC_TUBE_DBUS; tube = g_object_new (gtype, "connection", conn, "handle", handle, "self-handle", self_handle, "initiator-handle", initiator, "service", service, "parameters", parameters, "stream-id", stream_id, "id", id, "muc", muc, "requested", requested, NULL); if (bytestream != NULL) g_object_set (tube, "bytestream", bytestream, NULL); return tube; } static void augment_si_accept_iq (WockyNode *si, gpointer user_data) { wocky_node_add_child_ns (si, "tube", NS_TUBES); } /* * gabble_tube_dbus_accept * * Implements gabble_tube_iface_accept on GabbleTubeIface */ static gboolean gabble_tube_dbus_accept (GabbleTubeIface *tube, GError **error) { GabbleTubeDBus *self = GABBLE_TUBE_DBUS (tube); GabbleTubeDBusPrivate *priv = GABBLE_TUBE_DBUS_GET_PRIVATE (self); TpBaseChannel *base = TP_BASE_CHANNEL (tube); TpBaseChannelClass *cls = TP_BASE_CHANNEL_GET_CLASS (base); GabbleBytestreamState state; g_assert (priv->bytestream != NULL); g_object_get (priv->bytestream, "state", &state, NULL); if (state != GABBLE_BYTESTREAM_STATE_LOCAL_PENDING) return TRUE; if (cls->target_handle_type == TP_HANDLE_TYPE_CONTACT) { /* Bytestream was created using a SI request so * we have to accept it */ DEBUG ("accept the SI request"); gabble_bytestream_iface_accept (priv->bytestream, augment_si_accept_iq, self); } else { /* No SI so the bytestream is open */ DEBUG ("no SI, bytestream open"); g_object_set (priv->bytestream, "state", GABBLE_BYTESTREAM_STATE_OPEN, NULL); } if (!create_dbus_server (self, error)) return FALSE; return TRUE; } /* * gabble_tube_iface_dbus_close * * Implements gabble_tube_iface_close on GabbleTubeIface */ static void gabble_tube_iface_dbus_close (GabbleTubeIface *tube, gboolean closed_remotely) { tp_base_channel_close (TP_BASE_CHANNEL (tube)); } /** * gabble_tube_dbus_add_bytestream * * Implements gabble_tube_iface_add_bytestream on GabbleTubeIface */ static void gabble_tube_dbus_add_bytestream (GabbleTubeIface *tube, GabbleBytestreamIface *bytestream) { /* FIXME: should we support this, if we don't have a bytestream yet? */ DEBUG ("D-Bus doesn't support extra bytestream"); gabble_bytestream_iface_close (bytestream, NULL); } gboolean gabble_tube_dbus_add_name (GabbleTubeDBus *self, TpHandle handle, const gchar *name) { GabbleTubeDBusPrivate *priv = GABBLE_TUBE_DBUS_GET_PRIVATE (self); TpBaseChannel *base = TP_BASE_CHANNEL (self); TpBaseChannelClass *cls = TP_BASE_CHANNEL_GET_CLASS (base); TpBaseConnection *base_conn = tp_base_channel_get_connection (base); TpHandleRepoIface *contact_repo = tp_base_connection_get_handles ( base_conn, TP_HANDLE_TYPE_CONTACT); gchar *name_copy; GHashTable *added; GArray *removed; g_assert (cls->target_handle_type == TP_HANDLE_TYPE_ROOM); g_assert (g_hash_table_size (priv->dbus_names) == g_hash_table_size (priv->dbus_name_to_handle)); if (g_hash_table_lookup (priv->dbus_names, GUINT_TO_POINTER (handle)) != NULL) { DEBUG ("contact %d has already announced his D-Bus name", handle); return FALSE; } if (g_hash_table_lookup (priv->dbus_name_to_handle, name) != NULL) { DEBUG ("D-Bus name %s already used", name); return FALSE; } if (g_str_has_prefix (name, ":2.")) { gchar *nick, *supposed_name; const gchar *jid; jid = tp_handle_inspect (contact_repo, handle); g_assert (wocky_decode_jid (jid, NULL, NULL, &nick)); supposed_name = _gabble_generate_dbus_unique_name (nick); g_free (nick); if (tp_strdiff (name, supposed_name)) { DEBUG ("contact %s announces %s as D-Bus name but it should be %s", jid, name, supposed_name); g_free (supposed_name); return FALSE; } g_free (supposed_name); } name_copy = g_strdup (name); g_hash_table_insert (priv->dbus_names, GUINT_TO_POINTER (handle), name_copy); g_hash_table_insert (priv->dbus_name_to_handle, name_copy, GUINT_TO_POINTER (handle)); /* Fire DBusNamesChanged (new API) */ added = g_hash_table_new (g_direct_hash, g_direct_equal); removed = g_array_new (FALSE, FALSE, sizeof (TpHandle)); g_hash_table_insert (added, GUINT_TO_POINTER (handle), (gchar *) name); tp_svc_channel_type_dbus_tube_emit_dbus_names_changed (self, added, removed); g_hash_table_unref (added); g_array_unref (removed); return TRUE; } gboolean gabble_tube_dbus_remove_name (GabbleTubeDBus *self, TpHandle handle) { GabbleTubeDBusPrivate *priv = GABBLE_TUBE_DBUS_GET_PRIVATE (self); TpBaseChannel *base = TP_BASE_CHANNEL (self); TpBaseChannelClass *cls = TP_BASE_CHANNEL_GET_CLASS (base); const gchar *name; GHashTable *added; GArray *removed; g_assert (cls->target_handle_type == TP_HANDLE_TYPE_ROOM); name = g_hash_table_lookup (priv->dbus_names, GUINT_TO_POINTER (handle)); if (name == NULL) return FALSE; g_hash_table_remove (priv->dbus_name_to_handle, name); g_hash_table_remove (priv->dbus_names, GUINT_TO_POINTER (handle)); g_assert (g_hash_table_size (priv->dbus_names) == g_hash_table_size (priv->dbus_name_to_handle)); /* Fire DBusNamesChanged (new API) */ added = g_hash_table_new (g_direct_hash, g_direct_equal); removed = g_array_new (FALSE, FALSE, sizeof (TpHandle)); g_array_append_val (removed, handle); tp_svc_channel_type_dbus_tube_emit_dbus_names_changed (self, added, removed); g_hash_table_unref (added); g_array_unref (removed); return TRUE; } gboolean gabble_tube_dbus_handle_in_names (GabbleTubeDBus *self, TpHandle handle) { GabbleTubeDBusPrivate *priv = GABBLE_TUBE_DBUS_GET_PRIVATE (self); TpBaseChannelClass *cls = TP_BASE_CHANNEL_GET_CLASS (self); g_assert (cls->target_handle_type == TP_HANDLE_TYPE_ROOM); return (g_hash_table_lookup (priv->dbus_names, GUINT_TO_POINTER (handle)) != NULL); } gchar * _gabble_generate_dbus_unique_name (const gchar *nick) { gchar *encoded, *result; size_t len; guint i; len = strlen (nick); if (len <= 186) { encoded = g_base64_encode ((const guchar *) nick, len); } else { guchar sha1[20]; GString *tmp; sha1_bin (nick, len, sha1); tmp = g_string_sized_new (169 + 20); g_string_append_len (tmp, nick, 169); g_string_append_len (tmp, (const gchar *) sha1, 20); encoded = g_base64_encode ((const guchar *) tmp->str, tmp->len); g_string_free (tmp, TRUE); } for (i = 0; encoded[i] != '\0'; i++) { switch (encoded[i]) { case '+': encoded[i] = '_'; break; case '/': encoded[i] = '-'; break; case '=': encoded[i] = 'A'; break; } } result = g_strdup_printf (":2.%s", encoded); g_free (encoded); return result; } const gchar * const * gabble_tube_dbus_channel_get_allowed_properties (void) { return gabble_tube_dbus_channel_allowed_properties; } static gboolean gabble_tube_dbus_check_access_control (GabbleTubeDBus *self, guint access_control, GError **error) { switch (access_control) { case TP_SOCKET_ACCESS_CONTROL_CREDENTIALS: case TP_SOCKET_ACCESS_CONTROL_LOCALHOST: break; default: g_set_error (error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT, "%u socket access control is not supported", access_control); return FALSE; } return TRUE; } /** * gabble_tube_dbus_offer_async * * Implemnets D-Bus method Offer on interface * org.freedesktop.Telepathy.Channel.Type.DBusTube */ static void gabble_tube_dbus_offer_async (TpSvcChannelTypeDBusTube *self, GHashTable *parameters, guint access_control, DBusGMethodInvocation *context) { GabbleTubeDBus *tube = GABBLE_TUBE_DBUS (self); GabbleTubeDBusPrivate *priv = GABBLE_TUBE_DBUS_GET_PRIVATE (tube); GError *error = NULL; if (!gabble_tube_dbus_check_access_control (tube, access_control, &error)) { dbus_g_method_return_error (context, error); g_error_free (error); return; } priv->access_control = access_control; g_object_set (self, "parameters", parameters, NULL); if (gabble_tube_dbus_offer (tube, &error)) { tp_svc_channel_type_dbus_tube_return_from_offer (context, tube->priv->dbus_srv_addr); } else { g_assert (error != NULL); dbus_g_method_return_error (context, error); g_error_free (error); } } /** * gabble_tube_dbus_accept_async * * Implements D-Bus method Accept on interface * org.freedesktop.Telepathy.Channel.Type.DBusTube */ static void gabble_tube_dbus_accept_async (TpSvcChannelTypeDBusTube *self, guint access_control, DBusGMethodInvocation *context) { GabbleTubeDBus *tube = GABBLE_TUBE_DBUS (self); GError *error = NULL; if (!gabble_tube_dbus_check_access_control (tube, access_control, &error)) { dbus_g_method_return_error (context, error); g_error_free (error); return; } if (gabble_tube_dbus_accept (GABBLE_TUBE_IFACE (tube), &error)) { tp_svc_channel_type_dbus_tube_return_from_accept (context, tube->priv->dbus_srv_addr); ; } else { g_assert (error != NULL); dbus_g_method_return_error (context, error); g_error_free (error); } } static void tube_iface_init (gpointer g_iface, gpointer iface_data) { GabbleTubeIfaceClass *klass = (GabbleTubeIfaceClass *) g_iface; klass->accept = gabble_tube_dbus_accept; klass->close = gabble_tube_iface_dbus_close; klass->add_bytestream = gabble_tube_dbus_add_bytestream; } static void dbustube_iface_init (gpointer g_iface, gpointer iface_data) { TpSvcChannelTypeDBusTubeClass *klass = (TpSvcChannelTypeDBusTubeClass *) g_iface; #define IMPLEMENT(x, suffix) tp_svc_channel_type_dbus_tube_implement_##x (\ klass, gabble_tube_dbus_##x##suffix) IMPLEMENT(offer,_async); IMPLEMENT(accept,_async); #undef IMPLEMENT } telepathy-gabble-0.18.2/src/tube-dbus.h0000644000175000017500000000624112200204333017637 0ustar00smcvsmcv00000000000000/* * tube-dbus.h - Header for GabbleTubeDBus * Copyright (C) 2007 Collabora Ltd. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 __GABBLE_TUBE_DBUS_H__ #define __GABBLE_TUBE_DBUS_H__ #include #include #include #include "connection.h" #include "bytestream-iface.h" #include "extensions/extensions.h" #include "muc-channel.h" G_BEGIN_DECLS typedef struct _GabbleTubeDBus GabbleTubeDBus; typedef struct _GabbleTubeDBusClass GabbleTubeDBusClass; typedef struct _GabbleTubeDBusPrivate GabbleTubeDBusPrivate; struct _GabbleTubeDBusClass { TpBaseChannelClass parent_class; TpDBusPropertiesMixinClass dbus_props_class; TpGroupMixinClass group_class; }; struct _GabbleTubeDBus { TpBaseChannel parent; TpGroupMixin group; GabbleTubeDBusPrivate *priv; }; GType gabble_tube_dbus_get_type (void); /* TYPE MACROS */ #define GABBLE_TYPE_TUBE_DBUS \ (gabble_tube_dbus_get_type ()) #define GABBLE_TUBE_DBUS(obj) \ (G_TYPE_CHECK_INSTANCE_CAST((obj), GABBLE_TYPE_TUBE_DBUS, GabbleTubeDBus)) #define GABBLE_TUBE_DBUS_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST((klass), GABBLE_TYPE_TUBE_DBUS,\ GabbleTubeDBusClass)) #define GABBLE_IS_TUBE_DBUS(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE((obj), GABBLE_TYPE_TUBE_DBUS)) #define GABBLE_IS_TUBE_DBUS_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE((klass), GABBLE_TYPE_TUBE_DBUS)) #define GABBLE_TUBE_DBUS_GET_CLASS(obj) \ (G_TYPE_INSTANCE_GET_CLASS ((obj), GABBLE_TYPE_TUBE_DBUS,\ GabbleTubeDBusClass)) GabbleTubeDBus *gabble_tube_dbus_new (GabbleConnection *conn, TpHandle handle, TpHandleType handle_type, TpHandle self_handle, TpHandle initiator, const gchar *service, GHashTable *parameters, const gchar *stream_id, guint64 id, GabbleBytestreamIface *bytestream, GabbleMucChannel *muc, gboolean requested); gboolean gabble_tube_dbus_add_name (GabbleTubeDBus *tube, TpHandle handle, const gchar *name); gboolean gabble_tube_dbus_remove_name (GabbleTubeDBus *tube, TpHandle handle); gboolean gabble_tube_dbus_handle_in_names (GabbleTubeDBus *tube, TpHandle handle); gboolean gabble_tube_dbus_offer (GabbleTubeDBus *tube, GError **error); const gchar * const * gabble_tube_dbus_channel_get_allowed_properties (void); /* Only extern for the benefit of tests/test-dtube-unique-names.c */ gchar *_gabble_generate_dbus_unique_name (const gchar *nick); G_END_DECLS #endif /* #ifndef __GABBLE_TUBE_DBUS_H__ */ telepathy-gabble-0.18.2/src/tube-iface.c0000644000175000017500000001445012227000321017745 0ustar00smcvsmcv00000000000000/* * tube-iface.c - Source for GabbleTube interface * Copyright (C) 2007-2008 Collabora Ltd. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 "config.h" #include "tube-iface.h" #include #include #include "connection.h" #include "util.h" gboolean gabble_tube_iface_accept (GabbleTubeIface *self, GError **error) { gboolean (*virtual_method)(GabbleTubeIface *, GError **) = GABBLE_TUBE_IFACE_GET_CLASS (self)->accept; g_assert (virtual_method != NULL); return virtual_method (self, error); } void gabble_tube_iface_close (GabbleTubeIface *self, gboolean closed_remotely) { void (*virtual_method)(GabbleTubeIface *, gboolean) = GABBLE_TUBE_IFACE_GET_CLASS (self)->close; g_assert (virtual_method != NULL); virtual_method (self, closed_remotely); } void gabble_tube_iface_add_bytestream (GabbleTubeIface *self, GabbleBytestreamIface *bytestream) { void (*virtual_method)(GabbleTubeIface *, GabbleBytestreamIface *) = GABBLE_TUBE_IFACE_GET_CLASS (self)->add_bytestream; g_assert (virtual_method != NULL); virtual_method (self, bytestream); } static void gabble_tube_iface_base_init (gpointer klass) { static gboolean initialized = FALSE; if (!initialized) { GParamSpec *param_spec; param_spec = g_param_spec_uint ( "self-handle", "Self handle", "The handle to use for ourself. This can be different from the " "connection's self handle if our handle is a room handle.", 0, G_MAXUINT, 0, G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); g_object_interface_install_property (klass, param_spec); param_spec = g_param_spec_uint64 ( "id", "id", "The unique identifier of this tube", 0, G_MAXUINT32, 0, G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); g_object_interface_install_property (klass, param_spec); param_spec = g_param_spec_uint ( "type", "Tube type", "The TpTubeType this tube object.", 0, G_MAXUINT32, 0, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); g_object_interface_install_property (klass, param_spec); param_spec = g_param_spec_string ( "service", "service name", "the service associated with this tube object.", "", G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); g_object_interface_install_property (klass, param_spec); param_spec = g_param_spec_boxed ( "parameters", "parameters GHashTable", "GHashTable containing parameters of this tube object.", TP_HASH_TYPE_STRING_VARIANT_MAP, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); g_object_interface_install_property (klass, param_spec); param_spec = g_param_spec_uint ( "state", "Tube state", "The TpTubeChannelState of this tube object", 0, G_MAXUINT32, TP_TUBE_CHANNEL_STATE_REMOTE_PENDING, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); g_object_interface_install_property (klass, param_spec); initialized = TRUE; } } GType gabble_tube_iface_get_type (void) { static GType type = 0; if (type == 0) { static const GTypeInfo info = { sizeof (GabbleTubeIfaceClass), gabble_tube_iface_base_init, /* base_init */ NULL, /* base_finalize */ NULL, /* class_init */ NULL, /* class_finalize */ NULL, /* class_data */ 0, 0, /* n_preallocs */ NULL /* instance_init */ }; type = g_type_register_static (G_TYPE_INTERFACE, "GabbleTubeIface", &info, 0); } return type; } void gabble_tube_iface_publish_in_node (GabbleTubeIface *tube, TpBaseConnection *conn, WockyNode *node) { WockyNode *parameters_node; GHashTable *parameters; TpTubeType type; gchar *service, *id_str; guint64 tube_id; TpHandleRepoIface *contact_repo = tp_base_connection_get_handles ( conn, TP_HANDLE_TYPE_CONTACT); TpHandle initiator_handle; g_object_get (G_OBJECT (tube), "type", &type, "initiator-handle", &initiator_handle, "service", &service, "parameters", ¶meters, "id", &tube_id, NULL); id_str = g_strdup_printf ("%" G_GUINT64_FORMAT, tube_id); wocky_node_set_attributes (node, "service", service, "id", id_str, NULL); g_free (id_str); switch (type) { case TP_TUBE_TYPE_DBUS: { gchar *name, *stream_id; g_object_get (G_OBJECT (tube), "stream-id", &stream_id, "dbus-name", &name, NULL); wocky_node_set_attributes (node, "type", "dbus", "stream-id", stream_id, "initiator", tp_handle_inspect (contact_repo, initiator_handle), NULL); if (name != NULL) wocky_node_set_attribute (node, "dbus-name", name); g_free (name); g_free (stream_id); } break; case TP_TUBE_TYPE_STREAM: { wocky_node_set_attribute (node, "type", "stream"); } break; default: { g_return_if_reached (); } } parameters_node = wocky_node_add_child_with_content (node, "parameters", NULL); lm_message_node_add_children_from_properties (parameters_node, parameters, "parameter"); g_free (service); g_hash_table_unref (parameters); } telepathy-gabble-0.18.2/src/tube-iface.h0000644000175000017500000000444212227000321017752 0ustar00smcvsmcv00000000000000/* * tube-iface.h - Header for GabbleTube interface * Copyright (C) 2007-2008 Collabora Ltd. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 __GABBLE_TUBE_IFACE_H__ #define __GABBLE_TUBE_IFACE_H__ #include #include #include "bytestream-iface.h" G_BEGIN_DECLS typedef struct _GabbleTubeIface GabbleTubeIface; typedef struct _GabbleTubeIfaceClass GabbleTubeIfaceClass; struct _GabbleTubeIfaceClass { GTypeInterface parent; gboolean (*accept) (GabbleTubeIface *tube, GError **error); void (*close) (GabbleTubeIface *tube, gboolean closed_remotely); void (*add_bytestream) (GabbleTubeIface *tube, GabbleBytestreamIface *bytestream); }; GType gabble_tube_iface_get_type (void); /* TYPE MACROS */ #define GABBLE_TYPE_TUBE_IFACE \ (gabble_tube_iface_get_type ()) #define GABBLE_TUBE_IFACE(obj) \ (G_TYPE_CHECK_INSTANCE_CAST((obj), GABBLE_TYPE_TUBE_IFACE, GabbleTubeIface)) #define GABBLE_IS_TUBE_IFACE(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE((obj), GABBLE_TYPE_TUBE_IFACE)) #define GABBLE_TUBE_IFACE_GET_CLASS(obj) \ (G_TYPE_INSTANCE_GET_INTERFACE ((obj), GABBLE_TYPE_TUBE_IFACE,\ GabbleTubeIfaceClass)) gboolean gabble_tube_iface_accept (GabbleTubeIface *tube, GError **error); void gabble_tube_iface_close (GabbleTubeIface *tube, gboolean closed_remotely); void gabble_tube_iface_add_bytestream (GabbleTubeIface *tube, GabbleBytestreamIface *bytestream); void gabble_tube_iface_publish_in_node (GabbleTubeIface *tube, TpBaseConnection *conn, WockyNode *node); G_END_DECLS #endif /* #ifndef __GABBLE_TUBE_IFACE_H__ */ telepathy-gabble-0.18.2/src/tls-certificate.c0000644000175000017500000002434112227000321021023 0ustar00smcvsmcv00000000000000/* * tls-certificate.c - Source for GabbleTLSCertificate * Copyright (C) 2010 Collabora Ltd. * @author Cosimo Cecchi * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 "config.h" #include "tls-certificate.h" #include #include #define DEBUG_FLAG GABBLE_DEBUG_TLS #include "debug.h" static void tls_certificate_iface_init (gpointer g_iface, gpointer iface_data); G_DEFINE_TYPE_WITH_CODE (GabbleTLSCertificate, gabble_tls_certificate, G_TYPE_OBJECT, G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_AUTHENTICATION_TLS_CERTIFICATE, tls_certificate_iface_init); G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_DBUS_PROPERTIES, tp_dbus_properties_mixin_iface_init);) struct _GabbleTLSCertificatePrivate { gchar *object_path; gchar *cert_type; TpTLSCertificateState cert_state; GPtrArray *rejections; GPtrArray *cert_data; TpDBusDaemon *daemon; gboolean dispose_has_run; }; enum { PROP_OBJECT_PATH = 1, PROP_STATE, PROP_REJECTIONS, PROP_CERTIFICATE_TYPE, PROP_CERTIFICATE_CHAIN_DATA, /* not exported */ PROP_DBUS_DAEMON, NUM_PROPERTIES }; static void gabble_tls_certificate_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { GabbleTLSCertificate *self = GABBLE_TLS_CERTIFICATE (object); switch (property_id) { case PROP_OBJECT_PATH: g_value_set_string (value, self->priv->object_path); break; case PROP_STATE: g_value_set_uint (value, self->priv->cert_state); break; case PROP_REJECTIONS: g_value_set_boxed (value, self->priv->rejections); break; case PROP_CERTIFICATE_TYPE: g_value_set_string (value, self->priv->cert_type); break; case PROP_CERTIFICATE_CHAIN_DATA: g_value_set_boxed (value, self->priv->cert_data); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void gabble_tls_certificate_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { GabbleTLSCertificate *self = GABBLE_TLS_CERTIFICATE (object); switch (property_id) { case PROP_OBJECT_PATH: self->priv->object_path = g_value_dup_string (value); break; case PROP_CERTIFICATE_TYPE: self->priv->cert_type = g_value_dup_string (value); break; case PROP_CERTIFICATE_CHAIN_DATA: self->priv->cert_data = g_value_dup_boxed (value); break; case PROP_DBUS_DAEMON: self->priv->daemon = g_value_dup_object (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, value); break; } } static void gabble_tls_certificate_finalize (GObject *object) { GabbleTLSCertificate *self = GABBLE_TLS_CERTIFICATE (object); tp_clear_boxed (TP_ARRAY_TYPE_TLS_CERTIFICATE_REJECTION_LIST, &self->priv->rejections); g_free (self->priv->object_path); g_free (self->priv->cert_type); g_ptr_array_unref (self->priv->cert_data); G_OBJECT_CLASS (gabble_tls_certificate_parent_class)->finalize (object); } static void gabble_tls_certificate_dispose (GObject *object) { GabbleTLSCertificate *self = GABBLE_TLS_CERTIFICATE (object); if (self->priv->dispose_has_run) return; self->priv->dispose_has_run = TRUE; tp_clear_object (&self->priv->daemon); G_OBJECT_CLASS (gabble_tls_certificate_parent_class)->dispose (object); } static void gabble_tls_certificate_constructed (GObject *object) { GabbleTLSCertificate *self = GABBLE_TLS_CERTIFICATE (object); void (*chain_up) (GObject *) = G_OBJECT_CLASS (gabble_tls_certificate_parent_class)->constructed; if (chain_up != NULL) chain_up (object); /* register the certificate on the bus */ tp_dbus_daemon_register_object (self->priv->daemon, self->priv->object_path, self); } static void gabble_tls_certificate_init (GabbleTLSCertificate *self) { self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, GABBLE_TYPE_TLS_CERTIFICATE, GabbleTLSCertificatePrivate); self->priv->rejections = g_ptr_array_new (); } static void gabble_tls_certificate_class_init (GabbleTLSCertificateClass *klass) { static TpDBusPropertiesMixinPropImpl object_props[] = { { "State", "state", NULL }, { "Rejections", "rejections", NULL }, { "CertificateType", "certificate-type", NULL }, { "CertificateChainData", "certificate-chain-data", NULL }, { NULL } }; static TpDBusPropertiesMixinIfaceImpl prop_interfaces[] = { { TP_IFACE_AUTHENTICATION_TLS_CERTIFICATE, tp_dbus_properties_mixin_getter_gobject_properties, NULL, object_props, }, { NULL } }; GObjectClass *oclass = G_OBJECT_CLASS (klass); GParamSpec *pspec; g_type_class_add_private (klass, sizeof (GabbleTLSCertificatePrivate)); oclass->finalize = gabble_tls_certificate_finalize; oclass->dispose = gabble_tls_certificate_dispose; oclass->set_property = gabble_tls_certificate_set_property; oclass->get_property = gabble_tls_certificate_get_property; oclass->constructed = gabble_tls_certificate_constructed; pspec = g_param_spec_string ("object-path", "D-Bus object path", "The D-Bus object path used for this object on the bus.", NULL, G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); g_object_class_install_property (oclass, PROP_OBJECT_PATH, pspec); pspec = g_param_spec_uint ("state", "State of this certificate", "The state of this TLS certificate.", 0, NUM_TP_TLS_CERTIFICATE_STATES - 1, TP_TLS_CERTIFICATE_STATE_PENDING, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); g_object_class_install_property (oclass, PROP_STATE, pspec); pspec = g_param_spec_boxed ("rejections", "The reject reasons", "The reasons why this TLS certificate has been rejected", TP_ARRAY_TYPE_TLS_CERTIFICATE_REJECTION_LIST, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); g_object_class_install_property (oclass, PROP_REJECTIONS, pspec); pspec = g_param_spec_string ("certificate-type", "The certificate type", "The type of this certificate.", NULL, G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); g_object_class_install_property (oclass, PROP_CERTIFICATE_TYPE, pspec); pspec = g_param_spec_boxed ("certificate-chain-data", "The certificate chain data", "The raw PEM-encoded trust chain of this certificate.", TP_ARRAY_TYPE_UCHAR_ARRAY_LIST, G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); g_object_class_install_property (oclass, PROP_CERTIFICATE_CHAIN_DATA, pspec); pspec = g_param_spec_object ("dbus-daemon", "The DBus daemon connection", "The connection to the DBus daemon owning the CM", TP_TYPE_DBUS_DAEMON, G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); g_object_class_install_property (oclass, PROP_DBUS_DAEMON, pspec); klass->dbus_props_class.interfaces = prop_interfaces; tp_dbus_properties_mixin_class_init (oclass, G_STRUCT_OFFSET (GabbleTLSCertificateClass, dbus_props_class)); } static void gabble_tls_certificate_accept (TpSvcAuthenticationTLSCertificate *cert, DBusGMethodInvocation *context) { GabbleTLSCertificate *self = GABBLE_TLS_CERTIFICATE (cert); DEBUG ("Accept() called on the TLS certificate; current state %u", self->priv->cert_state); if (self->priv->cert_state != TP_TLS_CERTIFICATE_STATE_PENDING) { GError error = { TP_ERROR, TP_ERROR_INVALID_ARGUMENT, "Calling Accept() on a certificate with state != PENDING " "doesn't make sense." }; dbus_g_method_return_error (context, &error); return; } self->priv->cert_state = TP_TLS_CERTIFICATE_STATE_ACCEPTED; tp_svc_authentication_tls_certificate_emit_accepted (self); tp_svc_authentication_tls_certificate_return_from_accept (context); } static void gabble_tls_certificate_reject (TpSvcAuthenticationTLSCertificate *cert, const GPtrArray *rejections, DBusGMethodInvocation *context) { GabbleTLSCertificate *self = GABBLE_TLS_CERTIFICATE (cert); DEBUG ("Reject() called on the TLS certificate with rejections %p, " "length %u; current state %u", rejections, rejections->len, self->priv->cert_state); if (rejections->len < 1) { GError error = { TP_ERROR, TP_ERROR_INVALID_ARGUMENT, "Calling Reject() with a zero-length rejection list." }; dbus_g_method_return_error (context, &error); return; } if (self->priv->cert_state != TP_TLS_CERTIFICATE_STATE_PENDING) { GError error = { TP_ERROR, TP_ERROR_INVALID_ARGUMENT, "Calling Reject() on a certificate with state != PENDING " "doesn't make sense." }; dbus_g_method_return_error (context, &error); return; } tp_clear_boxed (TP_ARRAY_TYPE_TLS_CERTIFICATE_REJECTION_LIST, &self->priv->rejections); self->priv->rejections = g_boxed_copy (TP_ARRAY_TYPE_TLS_CERTIFICATE_REJECTION_LIST, rejections); self->priv->cert_state = TP_TLS_CERTIFICATE_STATE_REJECTED; tp_svc_authentication_tls_certificate_emit_rejected ( self, self->priv->rejections); tp_svc_authentication_tls_certificate_return_from_reject (context); } static void tls_certificate_iface_init (gpointer g_iface, gpointer iface_data) { TpSvcAuthenticationTLSCertificateClass *klass = g_iface; #define IMPLEMENT(x) \ tp_svc_authentication_tls_certificate_implement_##x ( \ klass, gabble_tls_certificate_##x) IMPLEMENT (accept); IMPLEMENT (reject); #undef IMPLEMENT } telepathy-gabble-0.18.2/src/tls-certificate.h0000644000175000017500000000440512200204333021027 0ustar00smcvsmcv00000000000000/* * tls-certificate.h - Header for GabbleTLSCertificate * Copyright (C) 2010 Collabora Ltd. * @author Cosimo Cecchi * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 __GABBLE_TLS_CERTIFICATE_H__ #define __GABBLE_TLS_CERTIFICATE_H__ #include #include G_BEGIN_DECLS typedef struct _GabbleTLSCertificate GabbleTLSCertificate; typedef struct _GabbleTLSCertificateClass GabbleTLSCertificateClass; typedef struct _GabbleTLSCertificatePrivate GabbleTLSCertificatePrivate; struct _GabbleTLSCertificateClass { GObjectClass parent_class; TpDBusPropertiesMixinClass dbus_props_class; }; struct _GabbleTLSCertificate { GObject parent; GabbleTLSCertificatePrivate *priv; }; GType gabble_tls_certificate_get_type (void); #define GABBLE_TYPE_TLS_CERTIFICATE \ (gabble_tls_certificate_get_type ()) #define GABBLE_TLS_CERTIFICATE(obj) \ (G_TYPE_CHECK_INSTANCE_CAST((obj), GABBLE_TYPE_TLS_CERTIFICATE, \ GabbleTLSCertificate)) #define GABBLE_TLS_CERTIFICATE_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST((klass), GABBLE_TYPE_TLS_CERTIFICATE, \ GabbleTLSCertificateClass)) #define GABBLE_IS_TLS_CERTIFICATE(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE((obj), GABBLE_TYPE_TLS_CERTIFICATE)) #define GABBLE_IS_TLS_CERTIFICATE_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE((klass), GABBLE_TYPE_TLS_CERTIFICATE)) #define GABBLE_TLS_CERTIFICATE_GET_CLASS(obj) \ (G_TYPE_INSTANCE_GET_CLASS ((obj), GABBLE_TYPE_TLS_CERTIFICATE, \ GabbleTLSCertificateClass)) G_END_DECLS #endif /* #ifndef __GABBLE_TLS_CERTIFICATE_H__*/ telepathy-gabble-0.18.2/src/sidecar.c0000644000175000017500000000441512200204333017353 0ustar00smcvsmcv00000000000000/* * sidecar.c — interface for connection sidecars * Copyright © 2009 Collabora Ltd. * Copyright © 2009 Nokia Corporation * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 "config.h" #include "gabble/sidecar.h" GType gabble_sidecar_get_type (void) { static GType type = 0; if (type == 0) { static const GTypeInfo info = { sizeof (GabbleSidecarInterface), NULL, /* base_init */ NULL, /* base_finalize */ NULL, /* class_init */ NULL, /* class_finalize */ NULL, /* class_data */ 0, 0, /* n_preallocs */ NULL /* instance_init */ }; type = g_type_register_static (G_TYPE_INTERFACE, "GabbleSidecar", &info, 0); } return type; } const gchar * gabble_sidecar_get_interface (GabbleSidecar *sidecar) { GabbleSidecarInterface *iface = GABBLE_SIDECAR_GET_INTERFACE (sidecar); return iface->interface; } /** * gabble_sidecar_get_immutable_properties: * @sidecar: a sidecar object * * Retrieves the immutable properties for a sidecar, as a map from * fully-qualified property names to GValues containing the properties' values * (such as that created with tp_asv_new ()). * * Returns: a non-%NULL hash of @sidecar's immutable properties, which must be * unreffed but not modified by the caller. */ GHashTable * gabble_sidecar_get_immutable_properties (GabbleSidecar *sidecar) { GabbleSidecarInterface *iface = GABBLE_SIDECAR_GET_INTERFACE (sidecar); if (iface->get_immutable_properties) return iface->get_immutable_properties (sidecar); else return g_hash_table_new (NULL, NULL); } telepathy-gabble-0.18.2/src/server-tls-manager.c0000644000175000017500000004054512200204333021463 0ustar00smcvsmcv00000000000000/* * server-tls-manager.c - Source for GabbleServerTLSManager * Copyright (C) 2010 Collabora Ltd. * @author Cosimo Cecchi * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 "config.h" #include "server-tls-manager.h" #include #include #define DEBUG_FLAG GABBLE_DEBUG_TLS #include "debug.h" #include "gabble/caps-channel-manager.h" #include "connection.h" #include "server-tls-channel.h" #include "util.h" #include "extensions/extensions.h" #include static void channel_manager_iface_init (gpointer, gpointer); G_DEFINE_TYPE_WITH_CODE (GabbleServerTLSManager, gabble_server_tls_manager, WOCKY_TYPE_TLS_HANDLER, G_IMPLEMENT_INTERFACE (TP_TYPE_CHANNEL_MANAGER, channel_manager_iface_init); G_IMPLEMENT_INTERFACE (GABBLE_TYPE_CAPS_CHANNEL_MANAGER, NULL)); enum { PROP_CONNECTION = 1, PROP_INTERACTIVE_TLS, NUM_PROPERTIES }; struct _GabbleServerTLSManagerPrivate { /* Properties */ GabbleConnection *connection; gboolean interactive_tls; /* Current operation data */ gchar *peername; GStrv reference_identities; WockyTLSSession *tls_session; GabbleServerTLSChannel *channel; GSimpleAsyncResult *async_result; /* List of owned TpBaseChannel not yet closed by the client */ GList *completed_channels; gboolean dispose_has_run; }; #define chainup ((WockyTLSHandlerClass *) \ gabble_server_tls_manager_parent_class) static void gabble_server_tls_manager_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { GabbleServerTLSManager *self = GABBLE_SERVER_TLS_MANAGER (object); switch (property_id) { case PROP_CONNECTION: g_value_set_object (value, self->priv->connection); break; case PROP_INTERACTIVE_TLS: g_value_set_boolean (value, self->priv->interactive_tls); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void gabble_server_tls_manager_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { GabbleServerTLSManager *self = GABBLE_SERVER_TLS_MANAGER (object); switch (property_id) { case PROP_CONNECTION: self->priv->connection = g_value_dup_object (value); break; case PROP_INTERACTIVE_TLS: self->priv->interactive_tls = g_value_get_boolean (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void close_all (GabbleServerTLSManager *self) { GList *l; if (self->priv->channel != NULL) tp_base_channel_close (TP_BASE_CHANNEL (self->priv->channel)); l = self->priv->completed_channels; while (l != NULL) { /* use a temporary variable as the ::closed callback will delete * the link from the list. */ GList *next = l->next; tp_base_channel_close (l->data); l = next; } } static void connection_status_changed_cb (GabbleConnection *conn, guint status, guint reason, gpointer user_data) { GabbleServerTLSManager *self = user_data; DEBUG ("Connection status changed, now %d", status); if (status == TP_CONNECTION_STATUS_DISCONNECTED) { close_all (self); tp_clear_object (&self->priv->connection); } } static void complete_verify (GabbleServerTLSManager *self) { /* Move channel to a list until a client Close() it */ if (self->priv->channel != NULL) { self->priv->completed_channels = g_list_prepend ( self->priv->completed_channels, g_object_ref (self->priv->channel)); } g_simple_async_result_complete (self->priv->async_result); /* Reset to initial state */ tp_clear_pointer (&self->priv->peername, g_free); tp_clear_pointer (&self->priv->reference_identities, g_strfreev); g_clear_object (&self->priv->tls_session); g_clear_object (&self->priv->channel); g_clear_object (&self->priv->async_result); } static void verify_fallback_cb (GObject *source, GAsyncResult *result, gpointer user_data) { GabbleServerTLSManager *self = (GabbleServerTLSManager *) source; GError *error = NULL; if (!chainup->verify_finish_func (WOCKY_TLS_HANDLER (self), result, &error)) g_simple_async_result_take_error (self->priv->async_result, error); complete_verify (self); } static void server_tls_channel_closed_cb (GabbleServerTLSChannel *channel, gpointer user_data) { GabbleServerTLSManager *self = user_data; DEBUG ("Server TLS channel closed."); if (channel == self->priv->channel) { /* fallback to the old-style non interactive TLS verification */ DEBUG ("Channel closed, but unhandled, falling back..."); chainup->verify_async_func (WOCKY_TLS_HANDLER (self), self->priv->tls_session, self->priv->peername, self->priv->reference_identities, verify_fallback_cb, NULL); self->priv->channel = NULL; } else { GList *l; l = g_list_find (self->priv->completed_channels, channel); g_assert (l != NULL); self->priv->completed_channels = g_list_delete_link ( self->priv->completed_channels, l); } tp_channel_manager_emit_channel_closed_for_object (self, TP_EXPORTABLE_CHANNEL (channel)); g_object_unref (channel); } GQuark gabble_server_tls_error_quark (void) { static GQuark quark = 0; if (!quark) quark = g_quark_from_static_string ("server-tls-error"); return quark; } static void tls_certificate_accepted_cb (GabbleTLSCertificate *certificate, gpointer user_data) { GabbleServerTLSManager *self = user_data; DEBUG ("TLS certificate accepted"); complete_verify (self); } static void tls_certificate_rejected_cb (GabbleTLSCertificate *certificate, GPtrArray *rejections, gpointer user_data) { GabbleServerTLSManager *self = user_data; DEBUG ("TLS certificate rejected with rejections %p, length %u.", rejections, rejections->len); g_simple_async_result_set_error (self->priv->async_result, GABBLE_SERVER_TLS_ERROR, 0, "TLS certificate rejected"); complete_verify (self); } static void extend_string_ptr_array (GPtrArray *array, GStrv new_elements) { gint i; if (new_elements != NULL) { for (i = 0; new_elements[i] != NULL; i++) { if (!tp_str_empty (new_elements[i])) g_ptr_array_add (array, g_strdup (new_elements[i])); } } } static void fill_reference_identities (GabbleServerTLSManager *self, const gchar *peername, GStrv original_extra_identities) { GPtrArray *identities; gchar *connect_server = NULL; gchar *explicit_server = NULL; GStrv extra_certificate_identities = NULL; g_return_if_fail (self->priv->reference_identities == NULL); g_object_get (self->priv->connection, "connect-server", &connect_server, "explicit-server", &explicit_server, "extra-certificate-identities", &extra_certificate_identities, NULL); identities = g_ptr_array_new (); /* The peer name, i.e, the domain part of the JID */ g_ptr_array_add (identities, g_strdup (peername)); /* The extra identities that the caller of verify_async() passed */ extend_string_ptr_array (identities, original_extra_identities); /* The explicitly overridden server (if in use) */ if (!tp_str_empty (explicit_server) && !tp_strdiff (connect_server, explicit_server)) { g_ptr_array_add (identities, g_strdup (explicit_server)); } /* Extra identities added to the account as a result of user choices */ extend_string_ptr_array (identities, extra_certificate_identities); /* Null terminate, since this is a gchar** */ g_ptr_array_add (identities, NULL); self->priv->reference_identities = (GStrv) g_ptr_array_free (identities, FALSE); g_strfreev (extra_certificate_identities); g_free (explicit_server); g_free (connect_server); } static void gabble_server_tls_manager_verify_async (WockyTLSHandler *handler, WockyTLSSession *tls_session, const gchar *peername, GStrv extra_identities, GAsyncReadyCallback callback, gpointer user_data) { GabbleServerTLSManager *self = GABBLE_SERVER_TLS_MANAGER (handler); GabbleTLSCertificate *certificate; GSimpleAsyncResult *result; g_return_if_fail (self->priv->async_result == NULL); DEBUG ("verify_async() called on the GabbleServerTLSManager."); result = g_simple_async_result_new (G_OBJECT (self), callback, user_data, gabble_server_tls_manager_verify_async); if (self->priv->connection == NULL) { DEBUG ("connection already went away; failing immediately"); g_simple_async_result_set_error (result, TP_ERROR, TP_ERROR_CANCELLED, "The Telepathy connection has already been disconnected"); g_simple_async_result_complete_in_idle (result); g_object_unref (result); return; } self->priv->async_result = result; fill_reference_identities (self, peername, extra_identities); if (!self->priv->interactive_tls) { DEBUG ("ignore-ssl-errors is set, fallback to non-interactive " "verification."); chainup->verify_async_func (WOCKY_TLS_HANDLER (self), tls_session, peername, self->priv->reference_identities, verify_fallback_cb, NULL); return; } self->priv->tls_session = g_object_ref (tls_session); self->priv->peername = g_strdup (peername); self->priv->channel = g_object_new (GABBLE_TYPE_SERVER_TLS_CHANNEL, "connection", self->priv->connection, "tls-session", tls_session, "hostname", peername, "reference-identities", self->priv->reference_identities, NULL); g_signal_connect (self->priv->channel, "closed", G_CALLBACK (server_tls_channel_closed_cb), self); certificate = gabble_server_tls_channel_get_certificate (self->priv->channel); g_signal_connect (certificate, "accepted", G_CALLBACK (tls_certificate_accepted_cb), self); g_signal_connect (certificate, "rejected", G_CALLBACK (tls_certificate_rejected_cb), self); /* emit NewChannel on the ChannelManager iface */ tp_channel_manager_emit_new_channel (self, (TpExportableChannel *) self->priv->channel, NULL); } static gboolean gabble_server_tls_manager_verify_finish (WockyTLSHandler *self, GAsyncResult *result, GError **error) { wocky_implement_finish_void (self, gabble_server_tls_manager_verify_async); } static void gabble_server_tls_manager_init (GabbleServerTLSManager *self) { self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, GABBLE_TYPE_SERVER_TLS_MANAGER, GabbleServerTLSManagerPrivate); } static void gabble_server_tls_manager_dispose (GObject *object) { GabbleServerTLSManager *self = GABBLE_SERVER_TLS_MANAGER (object); DEBUG ("%p", self); if (self->priv->dispose_has_run) return; self->priv->dispose_has_run = TRUE; tp_clear_object (&self->priv->tls_session); tp_clear_object (&self->priv->connection); G_OBJECT_CLASS (gabble_server_tls_manager_parent_class)->dispose (object); } static void gabble_server_tls_manager_finalize (GObject *object) { GabbleServerTLSManager *self = GABBLE_SERVER_TLS_MANAGER (object); DEBUG ("%p", self); close_all (self); g_free (self->priv->peername); g_strfreev (self->priv->reference_identities); G_OBJECT_CLASS (gabble_server_tls_manager_parent_class)->finalize (object); } static void gabble_server_tls_manager_constructed (GObject *object) { GabbleServerTLSManager *self = GABBLE_SERVER_TLS_MANAGER (object); void (*chain_up) (GObject *) = G_OBJECT_CLASS (gabble_server_tls_manager_parent_class)->constructed; if (chain_up != NULL) chain_up (object); DEBUG ("Server TLS Manager constructed"); gabble_signal_connect_weak (self->priv->connection, "status-changed", G_CALLBACK (connection_status_changed_cb), object); } static void gabble_server_tls_manager_class_init (GabbleServerTLSManagerClass *klass) { GObjectClass *oclass = G_OBJECT_CLASS (klass); WockyTLSHandlerClass *hclass = WOCKY_TLS_HANDLER_CLASS (klass); GParamSpec *pspec; g_type_class_add_private (klass, sizeof (GabbleServerTLSManagerPrivate)); oclass->dispose = gabble_server_tls_manager_dispose; oclass->finalize = gabble_server_tls_manager_finalize; oclass->constructed = gabble_server_tls_manager_constructed; oclass->set_property = gabble_server_tls_manager_set_property; oclass->get_property = gabble_server_tls_manager_get_property; hclass->verify_async_func = gabble_server_tls_manager_verify_async; hclass->verify_finish_func = gabble_server_tls_manager_verify_finish; pspec = g_param_spec_object ("connection", "GabbleConnection object", "Gabble connection object that owns this manager.", GABBLE_TYPE_CONNECTION, G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); g_object_class_install_property (oclass, PROP_CONNECTION, pspec); pspec = g_param_spec_boolean ("interactive-tls", "Interactive TLS setting", "Whether interactive TLS certificate verification is enabled.", FALSE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); g_object_class_install_property (oclass, PROP_INTERACTIVE_TLS, pspec); } static void gabble_server_tls_manager_foreach_channel (TpChannelManager *manager, TpExportableChannelFunc func, gpointer user_data) { GabbleServerTLSManager *self = GABBLE_SERVER_TLS_MANAGER (manager); GList *l; if (self->priv->channel != NULL) func (TP_EXPORTABLE_CHANNEL (self->priv->channel), user_data); for (l = self->priv->completed_channels; l != NULL; l = l->next) { func (l->data, user_data); } } static void channel_manager_iface_init (gpointer g_iface, gpointer iface_data) { TpChannelManagerIface *iface = g_iface; iface->foreach_channel = gabble_server_tls_manager_foreach_channel; /* these channels are not requestable. */ iface->ensure_channel = NULL; iface->create_channel = NULL; iface->request_channel = NULL; iface->foreach_channel_class = NULL; } static TpConnectionStatusReason cert_reject_reason_to_conn_reason (TpTLSCertificateRejectReason tls_reason) { #define EASY_CASE(x) \ case TP_TLS_CERTIFICATE_REJECT_REASON_ ## x: \ return TP_CONNECTION_STATUS_REASON_CERT_ ## x; switch (tls_reason) { EASY_CASE (UNTRUSTED); EASY_CASE (EXPIRED); EASY_CASE (NOT_ACTIVATED); EASY_CASE (FINGERPRINT_MISMATCH); EASY_CASE (HOSTNAME_MISMATCH); EASY_CASE (SELF_SIGNED); EASY_CASE (REVOKED); EASY_CASE (INSECURE); EASY_CASE (LIMIT_EXCEEDED); case TP_TLS_CERTIFICATE_REJECT_REASON_UNKNOWN: default: return TP_CONNECTION_STATUS_REASON_CERT_OTHER_ERROR; } #undef EASY_CASE } void gabble_server_tls_manager_get_rejection_details (GabbleServerTLSManager *self, gchar **dbus_error, GHashTable **details, TpConnectionStatusReason *reason) { GabbleTLSCertificate *certificate; GPtrArray *rejections; GValueArray *rejection; TpTLSCertificateRejectReason tls_reason; /* We probably want the rejection details of last completed operation */ g_return_if_fail (self->priv->completed_channels != NULL); certificate = gabble_server_tls_channel_get_certificate ( self->priv->completed_channels->data); g_object_get (certificate, "rejections", &rejections, NULL); /* we return 'Invalid_Argument' if Reject() is called with zero * reasons, so if this fails something bad happened. */ g_assert (rejections->len >= 1); rejection = g_ptr_array_index (rejections, 0); tls_reason = g_value_get_uint (g_value_array_get_nth (rejection, 0)); *dbus_error = g_value_dup_string (g_value_array_get_nth (rejection, 1)); *details = g_value_dup_boxed (g_value_array_get_nth (rejection, 2)); *reason = cert_reject_reason_to_conn_reason (tls_reason); tp_clear_boxed (TP_ARRAY_TYPE_TLS_CERTIFICATE_REJECTION_LIST, &rejections); } telepathy-gabble-0.18.2/src/server-tls-manager.h0000644000175000017500000000522012200204333021457 0ustar00smcvsmcv00000000000000/* * server-tls-manager.h - Header for GabbleServerTLSManager * Copyright (C) 2010 Collabora Ltd. * @author Cosimo Cecchi * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 __GABBLE_SERVER_TLS_MANAGER_H__ #define __GABBLE_SERVER_TLS_MANAGER_H__ #include #include #include #include "extensions/extensions.h" G_BEGIN_DECLS typedef struct _GabbleServerTLSManager GabbleServerTLSManager; typedef struct _GabbleServerTLSManagerClass GabbleServerTLSManagerClass; typedef struct _GabbleServerTLSManagerPrivate GabbleServerTLSManagerPrivate; struct _GabbleServerTLSManagerClass { WockyTLSHandlerClass parent_class; }; struct _GabbleServerTLSManager { WockyTLSHandler parent; GabbleServerTLSManagerPrivate *priv; }; GType gabble_server_tls_manager_get_type (void); #define GABBLE_TYPE_SERVER_TLS_MANAGER \ (gabble_server_tls_manager_get_type ()) #define GABBLE_SERVER_TLS_MANAGER(obj) \ (G_TYPE_CHECK_INSTANCE_CAST((obj), GABBLE_TYPE_SERVER_TLS_MANAGER, \ GabbleServerTLSManager)) #define GABBLE_SERVER_TLS_MANAGER_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST((klass), GABBLE_TYPE_SERVER_TLS_MANAGER, \ GabbleServerTLSManagerClass)) #define GABBLE_IS_SERVER_TLS_MANAGER(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE((obj), GABBLE_TYPE_SERVER_TLS_MANAGER)) #define GABBLE_IS_SERVER_TLS_MANAGER_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE((klass), GABBLE_TYPE_SERVER_TLS_MANAGER)) #define GABBLE_SERVER_TLS_MANAGER_GET_CLASS(obj) \ (G_TYPE_INSTANCE_GET_CLASS ((obj), GABBLE_TYPE_SERVER_TLS_MANAGER, \ GabbleServerTLSManagerClass)) #define GABBLE_SERVER_TLS_ERROR gabble_server_tls_error_quark () GQuark gabble_server_tls_error_quark (void); void gabble_server_tls_manager_get_rejection_details ( GabbleServerTLSManager *self, gchar **dbus_error, GHashTable **details, TpConnectionStatusReason *reason); G_END_DECLS #endif /* #ifndef __GABBLE_SERVER_TLS_MANAGER_H__ */ telepathy-gabble-0.18.2/src/server-tls-channel.c0000644000175000017500000002247312200204333021461 0ustar00smcvsmcv00000000000000/* * server-tls-channel.c - Source for GabbleServerTLSChannel * Copyright (C) 2010 Collabora Ltd. * @author Cosimo Cecchi * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 #include "server-tls-channel.h" #include #include #include #define DEBUG_FLAG GABBLE_DEBUG_TLS #include "debug.h" #include "connection.h" #include "tls-certificate.h" G_DEFINE_TYPE_WITH_CODE (GabbleServerTLSChannel, gabble_server_tls_channel, TP_TYPE_BASE_CHANNEL, G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CHANNEL_TYPE_SERVER_TLS_CONNECTION, NULL)); static void gabble_server_tls_channel_close (TpBaseChannel *base); enum { /* server TLS channel iface */ PROP_SERVER_CERTIFICATE = 1, PROP_HOSTNAME, PROP_REFERENCE_IDENTITIES, /* not exported */ PROP_TLS_SESSION, NUM_PROPERTIES }; struct _GabbleServerTLSChannelPrivate { WockyTLSSession *tls_session; GabbleTLSCertificate *server_cert; gchar *server_cert_path; gchar *hostname; GStrv reference_identities; gboolean dispose_has_run; }; static void gabble_server_tls_channel_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { GabbleServerTLSChannel *self = GABBLE_SERVER_TLS_CHANNEL (object); switch (property_id) { case PROP_SERVER_CERTIFICATE: g_value_set_boxed (value, self->priv->server_cert_path); break; case PROP_HOSTNAME: g_value_set_string (value, self->priv->hostname); break; case PROP_REFERENCE_IDENTITIES: g_value_set_boxed (value, self->priv->reference_identities); break; case PROP_TLS_SESSION: g_value_set_object (value, self->priv->tls_session); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void gabble_server_tls_channel_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { GabbleServerTLSChannel *self = GABBLE_SERVER_TLS_CHANNEL (object); switch (property_id) { case PROP_TLS_SESSION: self->priv->tls_session = g_value_dup_object (value); break; case PROP_HOSTNAME: self->priv->hostname = g_value_dup_string (value); break; case PROP_REFERENCE_IDENTITIES: self->priv->reference_identities = g_value_dup_boxed (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void gabble_server_tls_channel_finalize (GObject *object) { GabbleServerTLSChannel *self = GABBLE_SERVER_TLS_CHANNEL (object); DEBUG ("Finalize TLS channel"); g_free (self->priv->server_cert_path); g_free (self->priv->hostname); g_strfreev (self->priv->reference_identities); G_OBJECT_CLASS (gabble_server_tls_channel_parent_class)->finalize (object); } static void gabble_server_tls_channel_dispose (GObject *object) { GabbleServerTLSChannel *self = GABBLE_SERVER_TLS_CHANNEL (object); if (self->priv->dispose_has_run) return; DEBUG ("Dispose TLS channel"); self->priv->dispose_has_run = TRUE; tp_clear_object (&self->priv->server_cert); tp_clear_object (&self->priv->tls_session); G_OBJECT_CLASS (gabble_server_tls_channel_parent_class)->dispose (object); } static const gchar * cert_type_to_str (WockyTLSCertType type) { const gchar *retval = NULL; switch (type) { case WOCKY_TLS_CERT_TYPE_X509: retval = "x509"; break; case WOCKY_TLS_CERT_TYPE_OPENPGP: retval = "pgp"; break; default: break; } return retval; } static void gabble_server_tls_channel_constructed (GObject *object) { GabbleServerTLSChannel *self = GABBLE_SERVER_TLS_CHANNEL (object); TpBaseChannel *base = TP_BASE_CHANNEL (self); TpBaseConnection *base_conn = tp_base_channel_get_connection (base); void (*chain_up) (GObject *) = G_OBJECT_CLASS (gabble_server_tls_channel_parent_class)->constructed; WockyTLSCertType cert_type; const gchar *path; gchar *cert_object_path; GPtrArray *certificates; if (chain_up != NULL) chain_up (object); tp_base_channel_register (base); /* create the TLS certificate object */ path = tp_base_channel_get_object_path (base); cert_object_path = g_strdup_printf ("%s/TLSCertificateObject", path); certificates = wocky_tls_session_get_peers_certificate ( self->priv->tls_session, &cert_type); self->priv->server_cert = g_object_new (GABBLE_TYPE_TLS_CERTIFICATE, "object-path", cert_object_path, "certificate-chain-data", certificates, "certificate-type", cert_type_to_str (cert_type), "dbus-daemon", GABBLE_CONNECTION (base_conn)->daemon, NULL); self->priv->server_cert_path = cert_object_path; DEBUG ("Server TLS channel constructed at %s", path); } static void gabble_server_tls_channel_fill_immutable_properties ( TpBaseChannel *chan, GHashTable *properties) { TP_BASE_CHANNEL_CLASS (gabble_server_tls_channel_parent_class) ->fill_immutable_properties (chan, properties); tp_dbus_properties_mixin_fill_properties_hash ( G_OBJECT (chan), properties, TP_IFACE_CHANNEL_TYPE_SERVER_TLS_CONNECTION, "ServerCertificate", TP_IFACE_CHANNEL_TYPE_SERVER_TLS_CONNECTION, "Hostname", TP_IFACE_CHANNEL_TYPE_SERVER_TLS_CONNECTION, "ReferenceIdentities", NULL); } static gchar * gabble_server_tls_channel_get_object_path_suffix (TpBaseChannel *base) { static guint count = 0; return g_strdup_printf ("ServerTLSChannel%u", ++count); } static void gabble_server_tls_channel_init (GabbleServerTLSChannel *self) { self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, GABBLE_TYPE_SERVER_TLS_CHANNEL, GabbleServerTLSChannelPrivate); } static void gabble_server_tls_channel_class_init (GabbleServerTLSChannelClass *klass) { static TpDBusPropertiesMixinPropImpl server_tls_props[] = { { "ServerCertificate", "server-certificate", NULL }, { "Hostname", "hostname", NULL }, { "ReferenceIdentities", "reference-identities", NULL }, { NULL } }; GObjectClass *oclass = G_OBJECT_CLASS (klass); TpBaseChannelClass *base_class = TP_BASE_CHANNEL_CLASS (klass); GParamSpec *pspec; g_type_class_add_private (klass, sizeof (GabbleServerTLSChannelPrivate)); oclass->get_property = gabble_server_tls_channel_get_property; oclass->set_property = gabble_server_tls_channel_set_property; oclass->dispose = gabble_server_tls_channel_dispose; oclass->finalize = gabble_server_tls_channel_finalize; oclass->constructed = gabble_server_tls_channel_constructed; base_class->channel_type = TP_IFACE_CHANNEL_TYPE_SERVER_TLS_CONNECTION; base_class->target_handle_type = TP_HANDLE_TYPE_NONE; base_class->fill_immutable_properties = gabble_server_tls_channel_fill_immutable_properties; base_class->get_object_path_suffix = gabble_server_tls_channel_get_object_path_suffix; base_class->close = gabble_server_tls_channel_close; pspec = g_param_spec_boxed ("server-certificate", "Server certificate path", "The object path of the server certificate.", DBUS_TYPE_G_OBJECT_PATH, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); g_object_class_install_property (oclass, PROP_SERVER_CERTIFICATE, pspec); pspec = g_param_spec_string ("hostname", "The hostname to be verified", "The hostname which should be certified by the server certificate.", NULL, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); g_object_class_install_property (oclass, PROP_HOSTNAME, pspec); pspec = g_param_spec_boxed ("reference-identities", "The various identities to check the certificate against", "The server certificate identity should match one of these identities.", G_TYPE_STRV, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); g_object_class_install_property (oclass, PROP_REFERENCE_IDENTITIES, pspec); pspec = g_param_spec_object ("tls-session", "The WockyTLSSession", "The WockyTLSSession object containing the TLS information", WOCKY_TYPE_TLS_SESSION, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); g_object_class_install_property (oclass, PROP_TLS_SESSION, pspec); tp_dbus_properties_mixin_implement_interface (oclass, TP_IFACE_QUARK_CHANNEL_TYPE_SERVER_TLS_CONNECTION, tp_dbus_properties_mixin_getter_gobject_properties, NULL, server_tls_props); } static void gabble_server_tls_channel_close (TpBaseChannel *base) { DEBUG ("Close() called on the TLS channel %p", base); tp_base_channel_destroyed (base); } GabbleTLSCertificate * gabble_server_tls_channel_get_certificate (GabbleServerTLSChannel *self) { return self->priv->server_cert; } telepathy-gabble-0.18.2/src/server-tls-channel.h0000644000175000017500000000472412200204333021465 0ustar00smcvsmcv00000000000000/* * server-tls-channel.h - Header for GabbleServerTLSChannel * Copyright (C) 2010 Collabora Ltd. * @author Cosimo Cecchi * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 __GABBLE_SERVER_TLS_CHANNEL_H__ #define __GABBLE_SERVER_TLS_CHANNEL_H__ #include #include #include #include "tls-certificate.h" G_BEGIN_DECLS typedef struct _GabbleServerTLSChannelPrivate GabbleServerTLSChannelPrivate; typedef struct _GabbleServerTLSChannelClass GabbleServerTLSChannelClass; typedef struct _GabbleServerTLSChannel GabbleServerTLSChannel; struct _GabbleServerTLSChannelClass { TpBaseChannelClass base_class; }; struct _GabbleServerTLSChannel { TpBaseChannel parent; GabbleServerTLSChannelPrivate *priv; }; GType gabble_server_tls_channel_get_type (void); #define GABBLE_TYPE_SERVER_TLS_CHANNEL \ (gabble_server_tls_channel_get_type ()) #define GABBLE_SERVER_TLS_CHANNEL(obj) \ (G_TYPE_CHECK_INSTANCE_CAST((obj), GABBLE_TYPE_SERVER_TLS_CHANNEL, \ GabbleServerTLSChannel)) #define GABBLE_SERVER_TLS_CHANNEL_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST((klass), GABBLE_TYPE_SERVER_TLS_CHANNEL, \ GabbleServerTLSChannelClass)) #define GABBLE_IS_SERVER_TLS_CHANNEL(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE((obj), GABBLE_TYPE_SERVER_TLS_CHANNEL)) #define GABBLE_IS_SERVER_TLS_CHANNEL_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE((klass), GABBLE_TYPE_SERVER_TLS_CHANNEL)) #define GABBLE_SERVER_TLS_CHANNEL_GET_CLASS(obj) \ (G_TYPE_INSTANCE_GET_CLASS ((obj), GABBLE_TYPE_SERVER_TLS_CHANNEL,\ GabbleServerTLSChannelClass)) GabbleTLSCertificate * gabble_server_tls_channel_get_certificate ( GabbleServerTLSChannel *self); G_END_DECLS #endif /* #ifndef __GABBLE_SERVER_TLS_CHANNEL_H__*/ telepathy-gabble-0.18.2/src/server-sasl-channel.c0000644000175000017500000010307512227000321021617 0ustar00smcvsmcv00000000000000/* * server-sasl-channel.c - Source for GabbleServerSaslChannel * Copyright (C) 2010 Collabora Ltd. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 "config.h" #include "server-sasl-channel.h" #include "gabble-signals-marshal.h" #include #include #include #include #include #ifdef HAVE_UNISTD_H # include #endif #include #include #include #include #include #include #define DEBUG_FLAG GABBLE_DEBUG_AUTH #include #include "connection.h" #include "debug.h" #include "namespaces.h" #include "util.h" static void sasl_auth_iface_init (gpointer, gpointer); G_DEFINE_TYPE_WITH_CODE (GabbleServerSaslChannel, gabble_server_sasl_channel, TP_TYPE_BASE_CHANNEL, G_IMPLEMENT_INTERFACE ( TP_TYPE_SVC_CHANNEL_TYPE_SERVER_AUTHENTICATION, NULL); G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CHANNEL_INTERFACE_SECURABLE, NULL); G_IMPLEMENT_INTERFACE ( TP_TYPE_SVC_CHANNEL_INTERFACE_SASL_AUTHENTICATION, sasl_auth_iface_init)); enum { /* server authentication channel */ PROP_AUTH_METHOD = 1, /* sasl authentication channel */ PROP_AVAILABLE_MECHANISMS, PROP_HAS_INITIAL_DATA, PROP_CAN_TRY_AGAIN, PROP_SECURE, PROP_SASL_STATUS, PROP_SASL_ERROR, PROP_SASL_ERROR_DETAILS, PROP_AUTHORIZATION_IDENTITY, PROP_DEFAULT_USERNAME, PROP_DEFAULT_REALM, LAST_PROPERTY, }; /* private structure */ struct _GabbleServerSaslChannelPrivate { /* Immutable SASL properties */ GStrv available_mechanisms; gboolean secure; /* Mutable SASL properties */ TpSASLStatus sasl_status; gchar *sasl_error; GHashTable *sasl_error_details; /* Given to the Connection on request */ TpConnectionStatusReason disconnect_reason; GError *wocky_auth_error /* = NULL */; GSimpleAsyncResult *result; }; static GPtrArray * gabble_server_sasl_channel_get_interfaces (TpBaseChannel *base) { GPtrArray *interfaces; interfaces = TP_BASE_CHANNEL_CLASS ( gabble_server_sasl_channel_parent_class)->get_interfaces (base); g_ptr_array_add (interfaces, TP_IFACE_CHANNEL_INTERFACE_SASL_AUTHENTICATION); g_ptr_array_add (interfaces, TP_IFACE_CHANNEL_INTERFACE_SECURABLE); return interfaces; } static void gabble_server_sasl_channel_init (GabbleServerSaslChannel *self) { GabbleServerSaslChannelPrivate *priv = G_TYPE_INSTANCE_GET_PRIVATE (self, GABBLE_TYPE_SERVER_SASL_CHANNEL, GabbleServerSaslChannelPrivate); self->priv = priv; priv->sasl_status = TP_SASL_STATUS_NOT_STARTED; priv->sasl_error = NULL; priv->sasl_error_details = tp_asv_new (NULL, NULL); /* a safe assumption if we don't set anything else */ priv->disconnect_reason = TP_CONNECTION_STATUS_REASON_AUTHENTICATION_FAILED; } static void gabble_server_sasl_channel_fill_immutable_properties (TpBaseChannel *channel, GHashTable *properties) { TP_BASE_CHANNEL_CLASS (gabble_server_sasl_channel_parent_class) ->fill_immutable_properties (channel, properties); tp_dbus_properties_mixin_fill_properties_hash (G_OBJECT (channel), properties, TP_IFACE_CHANNEL_TYPE_SERVER_AUTHENTICATION, "AuthenticationMethod", TP_IFACE_CHANNEL_INTERFACE_SASL_AUTHENTICATION, "AvailableMechanisms", TP_IFACE_CHANNEL_INTERFACE_SASL_AUTHENTICATION, "HasInitialData", TP_IFACE_CHANNEL_INTERFACE_SASL_AUTHENTICATION, "CanTryAgain", TP_IFACE_CHANNEL_INTERFACE_SASL_AUTHENTICATION, "AuthorizationIdentity", TP_IFACE_CHANNEL_INTERFACE_SASL_AUTHENTICATION, "DefaultRealm", TP_IFACE_CHANNEL_INTERFACE_SASL_AUTHENTICATION, "DefaultUsername", TP_IFACE_CHANNEL_INTERFACE_SECURABLE, "Encrypted", TP_IFACE_CHANNEL_INTERFACE_SECURABLE, "Verified", NULL); } static gchar * gabble_server_sasl_channel_get_object_path_suffix (TpBaseChannel *channel) { return g_strdup ("ServerSASLChannel"); } static void gabble_server_sasl_channel_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { TpBaseChannel *channel = TP_BASE_CHANNEL (object); GabbleServerSaslChannel *self = GABBLE_SERVER_SASL_CHANNEL (object); GabbleServerSaslChannelPrivate *priv = self->priv; switch (property_id) { case PROP_SASL_STATUS: g_value_set_uint (value, priv->sasl_status); break; case PROP_SASL_ERROR: g_value_set_string (value, priv->sasl_error); break; case PROP_SASL_ERROR_DETAILS: g_value_set_boxed (value, priv->sasl_error_details); break; case PROP_AUTH_METHOD: g_value_set_static_string (value, TP_IFACE_CHANNEL_INTERFACE_SASL_AUTHENTICATION); break; case PROP_AVAILABLE_MECHANISMS: g_value_set_boxed (value, priv->available_mechanisms); break; case PROP_SECURE: g_value_set_boolean (value, priv->secure); break; case PROP_CAN_TRY_AGAIN: /* Wocky can't retry SASL authentication (although XMPP can) */ g_value_set_boolean (value, FALSE); break; case PROP_HAS_INITIAL_DATA: /* Yes, XMPP has "initial data" in its SASL */ g_value_set_boolean (value, TRUE); break; case PROP_AUTHORIZATION_IDENTITY: /* As per RFC 3920, the authorization identity for c2s connections * is the desired JID. We can't use conn_util_get_bare_self_jid at * this stage of the connection process, because it hasn't been * initialized yet. */ { gchar *jid, *username, *stream_server; g_object_get (tp_base_channel_get_connection (channel), "username", &username, "stream-server", &stream_server, NULL); jid = g_strconcat (username, "@", stream_server, NULL); g_free (username); g_free (stream_server); g_value_take_string (value, jid); } break; case PROP_DEFAULT_REALM: /* Like WockySaslDigestMd5, we use the stream server as the default * realm, for interoperability with servers that fail to supply a * realm but expect us to have this default. */ g_object_get_property ( G_OBJECT (tp_base_channel_get_connection (channel)), "stream-server", value); break; case PROP_DEFAULT_USERNAME: /* In practice, XMPP servers normally want us to authenticate as the * local-part of the JID. */ g_object_get_property ( G_OBJECT (tp_base_channel_get_connection (channel)), "username", value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void gabble_server_sasl_channel_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { GabbleServerSaslChannel *chan = GABBLE_SERVER_SASL_CHANNEL (object); GabbleServerSaslChannelPrivate *priv = chan->priv; switch (property_id) { case PROP_SECURE: priv->secure = g_value_get_boolean (value); break; case PROP_AVAILABLE_MECHANISMS: priv->available_mechanisms = g_value_dup_boxed (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void gabble_server_sasl_channel_finalize (GObject *object) { GabbleServerSaslChannel *self = GABBLE_SERVER_SASL_CHANNEL (object); GabbleServerSaslChannelPrivate *priv = self->priv; /* a ref is held for the channel's lifetime */ g_assert (tp_base_channel_is_destroyed ((TpBaseChannel *) self)); g_assert (priv->result == NULL); g_strfreev (priv->available_mechanisms); g_free (priv->sasl_error); g_hash_table_unref (priv->sasl_error_details); g_clear_error (&priv->wocky_auth_error); if (G_OBJECT_CLASS (gabble_server_sasl_channel_parent_class)->finalize) G_OBJECT_CLASS (gabble_server_sasl_channel_parent_class)->finalize (object); } static void gabble_server_sasl_channel_close (TpBaseChannel *channel); static void gabble_server_sasl_channel_class_init (GabbleServerSaslChannelClass *klass) { static TpDBusPropertiesMixinPropImpl server_auth_props[] = { { "AuthenticationMethod", "auth-method", NULL }, { NULL } }; static TpDBusPropertiesMixinPropImpl sasl_auth_props[] = { { "AvailableMechanisms", "available-mechanisms", NULL }, { "HasInitialData", "has-initial-data", NULL }, { "CanTryAgain", "can-try-again", NULL }, { "SASLStatus", "sasl-status", NULL }, { "SASLError", "sasl-error", NULL }, { "SASLErrorDetails", "sasl-error-details", NULL }, { "AuthorizationIdentity", "authorization-identity", NULL }, { "DefaultRealm", "default-realm", NULL }, { "DefaultUsername", "default-username", NULL }, { NULL } }; static TpDBusPropertiesMixinPropImpl securable_props[] = { /* For the moment we only have a unified "secure" property, which * implies we're both encrypted and verified */ { "Encrypted", "secure", NULL }, { "Verified", "secure", NULL }, { NULL } }; static TpDBusPropertiesMixinIfaceImpl prop_interfaces[] = { { TP_IFACE_CHANNEL_TYPE_SERVER_AUTHENTICATION, tp_dbus_properties_mixin_getter_gobject_properties, NULL, server_auth_props, }, { TP_IFACE_CHANNEL_INTERFACE_SASL_AUTHENTICATION, tp_dbus_properties_mixin_getter_gobject_properties, NULL, sasl_auth_props, }, { TP_IFACE_CHANNEL_INTERFACE_SECURABLE, tp_dbus_properties_mixin_getter_gobject_properties, NULL, securable_props, }, { NULL } }; GObjectClass *object_class = G_OBJECT_CLASS (klass); TpBaseChannelClass *channel_class = TP_BASE_CHANNEL_CLASS (klass); GParamSpec *param_spec; g_type_class_add_private (klass, sizeof (GabbleServerSaslChannelPrivate)); object_class->get_property = gabble_server_sasl_channel_get_property; object_class->set_property = gabble_server_sasl_channel_set_property; object_class->finalize = gabble_server_sasl_channel_finalize; channel_class->channel_type = TP_IFACE_CHANNEL_TYPE_SERVER_AUTHENTICATION; channel_class->get_interfaces = gabble_server_sasl_channel_get_interfaces; channel_class->target_handle_type = TP_HANDLE_TYPE_NONE; channel_class->fill_immutable_properties = gabble_server_sasl_channel_fill_immutable_properties; channel_class->get_object_path_suffix = gabble_server_sasl_channel_get_object_path_suffix; channel_class->close = gabble_server_sasl_channel_close; param_spec = g_param_spec_string ("auth-method", "Authentication method", "Method of authentication (D-Bus interface)", TP_IFACE_CHANNEL_INTERFACE_SASL_AUTHENTICATION, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, PROP_AUTH_METHOD, param_spec); param_spec = g_param_spec_string ("authorization-identity", "AuthorizationIdentity", "Identity for which we wish to be authorized", NULL, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, PROP_AUTHORIZATION_IDENTITY, param_spec); param_spec = g_param_spec_string ("default-realm", "DefaultRealm", "Default realm if the server does not supply one", NULL, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, PROP_DEFAULT_REALM, param_spec); param_spec = g_param_spec_string ("default-username", "DefaultUsername", "Default simple username if the user does not supply one", NULL, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, PROP_DEFAULT_USERNAME, param_spec); param_spec = g_param_spec_uint ("sasl-status", "SASLStatus", "Status of this channel", 0, NUM_TP_SASL_STATUSES, 0, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, PROP_SASL_STATUS, param_spec); param_spec = g_param_spec_string ("sasl-error", "SASLError", "D-Bus error name", "", G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, PROP_SASL_ERROR, param_spec); param_spec = g_param_spec_boxed ("sasl-error-details", "SASLErrorDetails", "Extra details of a SASL error", TP_HASH_TYPE_STRING_VARIANT_MAP, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, PROP_SASL_ERROR_DETAILS, param_spec); param_spec = g_param_spec_boxed ("available-mechanisms", "Available authentication mechanisms", "The set of mechanisms the server advertised.", G_TYPE_STRV, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, PROP_AVAILABLE_MECHANISMS, param_spec); param_spec = g_param_spec_boolean ("can-try-again", "CanTryAgain", "True if failed SASL can be retried without reconnecting", FALSE, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, PROP_CAN_TRY_AGAIN, param_spec); param_spec = g_param_spec_boolean ("has-initial-data", "HasInitialData", "True if SASL has initial data", TRUE, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, PROP_HAS_INITIAL_DATA, param_spec); param_spec = g_param_spec_boolean ("secure", "Is secure", "Is this channel secure (encrypted and verified)?", FALSE, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, PROP_SECURE, param_spec); klass->dbus_props_class.interfaces = prop_interfaces; tp_dbus_properties_mixin_class_init (object_class, G_STRUCT_OFFSET (GabbleServerSaslChannelClass, dbus_props_class)); } static void set_errors ( GabbleServerSaslChannel *self, const gchar *dbus_error, const gchar *debug_message, const GError *error) { g_free (self->priv->sasl_error); self->priv->sasl_error = g_strdup (dbus_error); g_hash_table_remove_all (self->priv->sasl_error_details); if (debug_message != NULL) tp_asv_set_string (self->priv->sasl_error_details, "debug-message", debug_message); g_clear_error (&self->priv->wocky_auth_error); self->priv->wocky_auth_error = g_error_copy (error); } static void change_current_state (GabbleServerSaslChannel *self, TpSASLStatus status) { self->priv->sasl_status = status; tp_svc_channel_interface_sasl_authentication_emit_sasl_status_changed ( self, self->priv->sasl_status, self->priv->sasl_error, self->priv->sasl_error_details); } static void complete_operation ( GabbleServerSaslChannel *self, gboolean in_idle) { GabbleServerSaslChannelPrivate *priv = self->priv; GSimpleAsyncResult *r = priv->result; g_return_if_fail (priv->result != NULL); priv->result = NULL; if (in_idle) g_simple_async_result_complete_in_idle (r); else g_simple_async_result_complete (r); g_object_unref (r); } /** * SASL Authentication Channel Interface */ static void gabble_server_sasl_channel_raise ( DBusGMethodInvocation *context, TpError code, const gchar *message, ...) G_GNUC_PRINTF (3, 4); static void gabble_server_sasl_channel_raise (DBusGMethodInvocation *context, TpError code, const gchar *message, ...) { va_list ap; GError *error = NULL; va_start (ap, message); error = g_error_new_valist (TP_ERROR, code, message, ap); va_end (ap); dbus_g_method_return_error (context, error); g_error_free (error); } /* When called from start_mechanism, initial_data can be NULL. When called * from D-Bus as StartMechanismWithData, it can't. */ static void gabble_server_sasl_channel_start_mechanism_with_data ( TpSvcChannelInterfaceSASLAuthentication *iface, const gchar *in_Mechanism, const GArray *in_InitialData, DBusGMethodInvocation *context) { GabbleServerSaslChannel *self = GABBLE_SERVER_SASL_CHANNEL (iface); GabbleServerSaslChannelPrivate *priv = self->priv; WockyAuthRegistryStartData *start_data; GString *initial_data = NULL; if (self->priv->sasl_status != TP_SASL_STATUS_NOT_STARTED) { gabble_server_sasl_channel_raise (context, TP_ERROR_NOT_AVAILABLE, "Mechanisms can only be started in state Not_Started, not %u", self->priv->sasl_status); DEBUG ("cannot start: state %u != Not_Started", self->priv->sasl_status); return; } /* NotStarted state is entered by creating the channel: the caller must * call start_auth_async immediately */ g_assert (priv->result != NULL); g_assert (g_simple_async_result_is_valid (G_ASYNC_RESULT (priv->result), G_OBJECT (self), gabble_server_sasl_channel_start_auth_async)); if (tp_strv_contains ((const gchar * const *) priv->available_mechanisms, in_Mechanism)) { if (in_InitialData != NULL) { /* The initial data might be secret (for PLAIN etc.), and also might * not be UTF-8 or even text, so we just output the length */ DEBUG ("Starting %s authentication with %u bytes of initial data", in_Mechanism, in_InitialData->len); initial_data = g_string_new_len (in_InitialData->data, in_InitialData->len); } else { DEBUG ("Starting %s authentication without initial data", in_Mechanism); } change_current_state (self, TP_SASL_STATUS_IN_PROGRESS); dbus_g_method_return (context); start_data = wocky_auth_registry_start_data_new (in_Mechanism, initial_data); g_simple_async_result_set_op_res_gpointer (priv->result, start_data, (GDestroyNotify) wocky_auth_registry_start_data_free); complete_operation (self, TRUE); if (initial_data != NULL) g_string_free (initial_data, TRUE); } else { DEBUG ("cannot start: %s is not a supported mechanism", in_Mechanism); gabble_server_sasl_channel_raise (context, TP_ERROR_NOT_IMPLEMENTED, "Selected mechanism is not available."); } } static void gabble_server_sasl_channel_start_mechanism ( TpSvcChannelInterfaceSASLAuthentication *iface, const gchar *mech, DBusGMethodInvocation *context) { gabble_server_sasl_channel_start_mechanism_with_data (iface, mech, NULL, context); } static void gabble_server_sasl_channel_respond ( TpSvcChannelInterfaceSASLAuthentication *channel, const GArray *in_Response_Data, DBusGMethodInvocation *context) { GabbleServerSaslChannel *self = GABBLE_SERVER_SASL_CHANNEL (channel); GabbleServerSaslChannelPrivate *priv = self->priv; GString *response_data; if (self->priv->sasl_status != TP_SASL_STATUS_IN_PROGRESS) { gabble_server_sasl_channel_raise (context, TP_ERROR_NOT_AVAILABLE, "You can only respond to challenges in state In_Progress, not %u", self->priv->sasl_status); DEBUG ("cannot respond: state %u != In_Progress", self->priv->sasl_status); return; } if (priv->result == NULL) { gabble_server_sasl_channel_raise (context, TP_ERROR_NOT_AVAILABLE, "You already responded to the most recent challenge"); DEBUG ("cannot respond: already responded"); return; } g_assert (g_simple_async_result_is_valid (G_ASYNC_RESULT (priv->result), G_OBJECT (self), gabble_server_sasl_channel_challenge_async)); /* The response might be secret (for PLAIN etc.), and also might * not be UTF-8 or even text, so we just output the length */ DEBUG ("responding with %u bytes", in_Response_Data->len); if (in_Response_Data->len > 0) response_data = g_string_new_len (in_Response_Data->data, in_Response_Data->len); else response_data = NULL; g_simple_async_result_set_op_res_gpointer (priv->result, response_data, (GDestroyNotify) wocky_g_string_free); complete_operation (self, TRUE); tp_svc_channel_interface_sasl_authentication_return_from_respond ( context); } static void gabble_server_sasl_channel_accept_sasl ( TpSvcChannelInterfaceSASLAuthentication *channel, DBusGMethodInvocation *context) { GabbleServerSaslChannel *self = GABBLE_SERVER_SASL_CHANNEL (channel); GabbleServerSaslChannelPrivate *priv = self->priv; const gchar *message = NULL; switch (self->priv->sasl_status) { case TP_SASL_STATUS_NOT_STARTED: message = "Authentication has not yet begun (Not_Started)"; break; case TP_SASL_STATUS_IN_PROGRESS: /* In this state, the only valid time to call this method is in response * to a challenge, to indicate that, actually, that challenge was * additional data for a successful authentication. */ if (priv->result == NULL) { message = "In_Progress, but you already responded to the last " "challenge"; } else { DEBUG ("client says the last challenge was actually final data " "and has accepted it"); g_assert (g_simple_async_result_is_valid (G_ASYNC_RESULT (priv->result), G_OBJECT (self), gabble_server_sasl_channel_challenge_async)); change_current_state (self, TP_SASL_STATUS_CLIENT_ACCEPTED); } break; case TP_SASL_STATUS_SERVER_SUCCEEDED: /* The server has already said yes, and the caller is waiting for * success_async(), i.e. waiting for the UI to check whether it's * happy too. AcceptSASL means that it is. */ DEBUG ("client has accepted server's success"); g_assert (g_simple_async_result_is_valid (G_ASYNC_RESULT (priv->result), G_OBJECT (self), gabble_server_sasl_channel_success_async)); change_current_state (self, TP_SASL_STATUS_SUCCEEDED); break; case TP_SASL_STATUS_CLIENT_ACCEPTED: message = "Client already accepted authentication (Client_Accepted)"; break; case TP_SASL_STATUS_SUCCEEDED: message = "Authentication already succeeded (Succeeded)"; break; case TP_SASL_STATUS_SERVER_FAILED: message = "Authentication has already failed (Server_Failed)"; break; case TP_SASL_STATUS_CLIENT_FAILED: message = "Authentication has already been aborted (Client_Failed)"; break; default: g_assert_not_reached (); } if (message != NULL) { DEBUG ("cannot accept SASL: %s", message); gabble_server_sasl_channel_raise (context, TP_ERROR_NOT_AVAILABLE, "%s", message); return; } if (priv->result != NULL) { /* This is a bit weird - this code is run for two different async * results. In the In_Progress case, this code results in * success with the GSimpleAsyncResult's op_res left as NULL, which * is what Wocky wants for an empty response. In the Server_Succeeded * response, the async result is just success or error - we succeed. */ /* We want want to complete not in an idle because if we do we * will hit fd.o#32278. This is safe because we're being called * from dbus-glib in the main loop. */ complete_operation (self, FALSE); } tp_svc_channel_interface_sasl_authentication_return_from_accept_sasl ( context); } static void gabble_server_sasl_channel_abort_sasl ( TpSvcChannelInterfaceSASLAuthentication *channel, guint in_Reason, const gchar *in_Debug_Message, DBusGMethodInvocation *context) { GabbleServerSaslChannel *self = GABBLE_SERVER_SASL_CHANNEL (channel); GabbleServerSaslChannelPrivate *priv = self->priv; const gchar *dbus_error; switch (self->priv->sasl_status) { case TP_SASL_STATUS_SERVER_FAILED: case TP_SASL_STATUS_CLIENT_FAILED: DEBUG ("ignoring attempt to abort: we already failed"); break; case TP_SASL_STATUS_SUCCEEDED: case TP_SASL_STATUS_CLIENT_ACCEPTED: DEBUG ("cannot abort: client already called AcceptSASL"); gabble_server_sasl_channel_raise (context, TP_ERROR_NOT_AVAILABLE, "Authentication has already succeeded - too late to abort"); return; case TP_SASL_STATUS_NOT_STARTED: case TP_SASL_STATUS_IN_PROGRESS: case TP_SASL_STATUS_SERVER_SUCCEEDED: { GError *error = NULL; switch (in_Reason) { case TP_SASL_ABORT_REASON_INVALID_CHALLENGE: g_set_error (&error, WOCKY_AUTH_ERROR, WOCKY_AUTH_ERROR_INVALID_REPLY, "invalid challenge (%s)", in_Debug_Message); dbus_error = TP_ERROR_STR_SERVICE_CONFUSED; break; case TP_SASL_ABORT_REASON_USER_ABORT: g_set_error (&error, WOCKY_AUTH_ERROR, WOCKY_AUTH_ERROR_FAILURE, "user aborted auth (%s)", in_Debug_Message); dbus_error = TP_ERROR_STR_CANCELLED; break; default: g_set_error (&error, WOCKY_AUTH_ERROR, WOCKY_AUTH_ERROR_FAILURE, "unknown reason code %u, treating as User_Abort (%s)", in_Reason, in_Debug_Message); dbus_error = TP_ERROR_STR_CANCELLED; break; } DEBUG ("%s", error->message); set_errors (self, dbus_error, in_Debug_Message, error); change_current_state (self, TP_SASL_STATUS_CLIENT_FAILED); if (priv->result != NULL) { /* If Not_Started, we're returning failure from start_auth_async. * If In_Progress, we might be returning failure from * challenge_async, if one is outstanding. * If Server_Succeeded, we're returning failure from success_async. */ g_simple_async_result_set_from_error (priv->result, error); complete_operation (self, TRUE); } g_error_free (error); break; } default: g_assert_not_reached (); } tp_svc_channel_interface_sasl_authentication_return_from_abort_sasl ( context); } static void sasl_auth_iface_init (gpointer klass, gpointer unused G_GNUC_UNUSED) { #define IMPLEMENT(x) \ tp_svc_channel_interface_sasl_authentication_implement_##x ( \ klass, gabble_server_sasl_channel_##x) IMPLEMENT (start_mechanism); IMPLEMENT (start_mechanism_with_data); IMPLEMENT (respond); IMPLEMENT (accept_sasl); IMPLEMENT (abort_sasl); #undef IMPLEMENT } void gabble_server_sasl_channel_start_auth_async (GabbleServerSaslChannel *self, GAsyncReadyCallback callback, gpointer user_data) { GabbleServerSaslChannelPrivate *priv = self->priv; g_assert (priv->result == NULL); g_assert (priv->sasl_status == TP_SASL_STATUS_NOT_STARTED); DEBUG ("Starting authentication"); priv->result = g_simple_async_result_new (G_OBJECT (self), callback, user_data, gabble_server_sasl_channel_start_auth_async); tp_base_channel_register (TP_BASE_CHANNEL (self)); } gboolean gabble_server_sasl_channel_start_auth_finish (GabbleServerSaslChannel *self, GAsyncResult *result, WockyAuthRegistryStartData **start_data, GError **error) { wocky_implement_finish_copy_pointer (self, gabble_server_sasl_channel_start_auth_async, wocky_auth_registry_start_data_dup, start_data); } void gabble_server_sasl_channel_challenge_async (GabbleServerSaslChannel *self, const GString *challenge_data, GAsyncReadyCallback callback, gpointer user_data) { GabbleServerSaslChannelPrivate *priv = self->priv; GArray *challenge_ay; g_assert (!tp_base_channel_is_destroyed ((TpBaseChannel *) self)); g_assert (priv->result == NULL); /* it might be sensitive, and also might not be UTF-8 text, so just print * the length */ DEBUG ("New challenge, %" G_GSIZE_FORMAT " bytes", challenge_data->len); priv->result = g_simple_async_result_new (G_OBJECT (self), callback, user_data, gabble_server_sasl_channel_challenge_async); switch (priv->sasl_status) { case TP_SASL_STATUS_IN_PROGRESS: challenge_ay = g_array_sized_new (FALSE, FALSE, sizeof (gchar), challenge_data->len); g_array_append_vals (challenge_ay, challenge_data->str, challenge_data->len); tp_svc_channel_interface_sasl_authentication_emit_new_challenge ( self, challenge_ay); break; case TP_SASL_STATUS_CLIENT_FAILED: g_return_if_fail (priv->wocky_auth_error != NULL); g_simple_async_result_set_from_error (priv->result, priv->wocky_auth_error); complete_operation (self, TRUE); return; default: g_assert_not_reached (); } } gboolean gabble_server_sasl_channel_challenge_finish (GabbleServerSaslChannel *self, GAsyncResult *result, GString **response, GError **error) { wocky_implement_finish_copy_pointer (self, gabble_server_sasl_channel_challenge_async, wocky_g_string_dup, response); } void gabble_server_sasl_channel_success_async (GabbleServerSaslChannel *self, GAsyncReadyCallback callback, gpointer user_data) { GabbleServerSaslChannelPrivate *priv = self->priv; g_assert (!tp_base_channel_is_destroyed ((TpBaseChannel *) self)); g_assert (priv->result == NULL); priv->result = g_simple_async_result_new (G_OBJECT (self), callback, user_data, gabble_server_sasl_channel_success_async); DEBUG (""); if (self->priv->sasl_status != TP_SASL_STATUS_CLIENT_ACCEPTED) { change_current_state (self, TP_SASL_STATUS_SERVER_SUCCEEDED); } else { change_current_state (self, TP_SASL_STATUS_SUCCEEDED); complete_operation (self, TRUE); } } gboolean gabble_server_sasl_channel_success_finish (GabbleServerSaslChannel *self, GAsyncResult *result, GError **error) { wocky_implement_finish_void (self, gabble_server_sasl_channel_success_async); } void gabble_server_sasl_channel_fail (GabbleServerSaslChannel *self, const GError *error) { GError *tp_error = NULL; TpConnectionStatusReason conn_reason; if (self->priv->sasl_error != NULL) { DEBUG ("already failed, ignoring further error: %s", error->message); return; } gabble_set_tp_conn_error_from_wocky (error, TP_CONNECTION_STATUS_CONNECTING, &conn_reason, &tp_error); g_assert (tp_error->domain == TP_ERROR); DEBUG ("auth failed: %s", tp_error->message); set_errors (self, tp_error_get_dbus_name (tp_error->code), tp_error->message, error); change_current_state (self, TP_SASL_STATUS_SERVER_FAILED); self->priv->disconnect_reason = conn_reason; } /* * Public */ GabbleServerSaslChannel * gabble_server_sasl_channel_new (GabbleConnection *conn, GStrv available_mechanisms, gboolean secure, const gchar *session_id) { GabbleServerSaslChannel *obj; g_return_val_if_fail (GABBLE_IS_CONNECTION (conn), NULL); obj = GABBLE_SERVER_SASL_CHANNEL ( g_object_new (GABBLE_TYPE_SERVER_SASL_CHANNEL, "connection", conn, "available-mechanisms", available_mechanisms, "secure", secure, NULL)); return obj; } static void gabble_server_sasl_channel_close (TpBaseChannel *channel) { GabbleServerSaslChannel *self = GABBLE_SERVER_SASL_CHANNEL (channel); GabbleServerSaslChannelPrivate *priv = self->priv; GError error = { WOCKY_AUTH_ERROR, WOCKY_AUTH_ERROR_FAILURE, "Client aborted authentication." }; DEBUG ("called on %p", self); switch (priv->sasl_status) { case TP_SASL_STATUS_NOT_STARTED: case TP_SASL_STATUS_IN_PROGRESS: case TP_SASL_STATUS_SERVER_SUCCEEDED: set_errors (self, TP_ERROR_STR_AUTHENTICATION_FAILED, "Close() called", &error); break; case TP_SASL_STATUS_CLIENT_ACCEPTED: case TP_SASL_STATUS_SUCCEEDED: /* Hooray! */ break; case TP_SASL_STATUS_SERVER_FAILED: case TP_SASL_STATUS_CLIENT_FAILED: g_warn_if_fail (priv->sasl_error != NULL); break; } if (priv->result != NULL) { DEBUG ("closed channel"); g_simple_async_result_set_from_error (priv->result, &error); complete_operation (self, TRUE); } tp_base_channel_destroyed (channel); } /** * @dbus_error: (out) (transfer full): the D-Bus error name * @details: (out) (transfer full) (element-type utf8 GObject.Value): the * error details * @reason: (out): the reason with which to disconnect * @error: (out): an error in a domain Wocky understands describing what went * wrong * * Returns: %TRUE if an error was copied; %FALSE leaving the 'out' parameters * untouched if there is no error */ gboolean gabble_server_sasl_channel_get_failure_details (GabbleServerSaslChannel *self, gchar **dbus_error, GHashTable **details, TpConnectionStatusReason *reason, GError **error) { if (self->priv->sasl_error != NULL) { if (dbus_error != NULL) *dbus_error = g_strdup (self->priv->sasl_error); if (details != NULL) *details = g_hash_table_ref (self->priv->sasl_error_details); if (reason != NULL) *reason = self->priv->disconnect_reason; if (error != NULL) *error = g_error_copy (self->priv->wocky_auth_error); return TRUE; } else { return FALSE; } } telepathy-gabble-0.18.2/src/server-sasl-channel.h0000644000175000017500000000751412200204333021625 0ustar00smcvsmcv00000000000000/* * server-sasl-channel.h - Header for GabbleServerSaslChannel * Copyright (C) 2010 Collabora Ltd. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 __GABBLE_SERVER_SASL_CHANNEL_H__ #define __GABBLE_SERVER_SASL_CHANNEL_H__ #include #include #include #include "types.h" G_BEGIN_DECLS #define X_TELEPATHY_PASSWORD "X-TELEPATHY-PASSWORD" typedef struct _GabbleServerSaslChannelPrivate GabbleServerSaslChannelPrivate; typedef struct _GabbleServerSaslChannelClass GabbleServerSaslChannelClass; typedef struct _GabbleServerSaslChannel GabbleServerSaslChannel; struct _GabbleServerSaslChannelClass { TpBaseChannelClass parent_class; TpDBusPropertiesMixinClass dbus_props_class; }; struct _GabbleServerSaslChannel { TpBaseChannel parent; GabbleServerSaslChannelPrivate *priv; }; GType gabble_server_sasl_channel_get_type (void); /* TYPE MACROS */ #define GABBLE_TYPE_SERVER_SASL_CHANNEL \ (gabble_server_sasl_channel_get_type ()) #define GABBLE_SERVER_SASL_CHANNEL(obj) \ (G_TYPE_CHECK_INSTANCE_CAST((obj), GABBLE_TYPE_SERVER_SASL_CHANNEL,\ GabbleServerSaslChannel)) #define GABBLE_SERVER_SASL_CHANNEL_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST((klass), GABBLE_TYPE_SERVER_SASL_CHANNEL,\ GabbleServerSaslChannelClass)) #define GABBLE_IS_SERVER_SASL_CHANNEL(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE((obj), GABBLE_TYPE_SERVER_SASL_CHANNEL)) #define GABBLE_IS_SERVER_SASL_CHANNEL_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE((klass), GABBLE_TYPE_SERVER_SASL_CHANNEL)) #define GABBLE_SERVER_SASL_CHANNEL_GET_CLASS(obj) \ (G_TYPE_INSTANCE_GET_CLASS ((obj), GABBLE_TYPE_SERVER_SASL_CHANNEL,\ GabbleServerSaslChannelClass)) GabbleServerSaslChannel *gabble_server_sasl_channel_new ( GabbleConnection *conn, GStrv available_mechanisms, gboolean secure, const gchar *session_id); void gabble_server_sasl_channel_start_auth_async ( GabbleServerSaslChannel *self, GAsyncReadyCallback callback, gpointer user_data); gboolean gabble_server_sasl_channel_start_auth_finish ( GabbleServerSaslChannel *self, GAsyncResult *result, WockyAuthRegistryStartData **start_data, GError **error); void gabble_server_sasl_channel_challenge_async (GabbleServerSaslChannel *self, const GString *challenge_data, GAsyncReadyCallback callback, gpointer user_data); gboolean gabble_server_sasl_channel_challenge_finish ( GabbleServerSaslChannel *self, GAsyncResult *result, GString **response, GError **error); void gabble_server_sasl_channel_success_async (GabbleServerSaslChannel *self, GAsyncReadyCallback callback, gpointer user_data); gboolean gabble_server_sasl_channel_success_finish ( GabbleServerSaslChannel *self, GAsyncResult *result, GError **error); void gabble_server_sasl_channel_fail (GabbleServerSaslChannel *self, const GError *error); gboolean gabble_server_sasl_channel_get_failure_details ( GabbleServerSaslChannel *self, gchar **dbus_error, GHashTable **details, TpConnectionStatusReason *reason, GError **error); G_END_DECLS #endif /* #ifndef __GABBLE_SERVER_SASL_CHANNEL_H__*/ telepathy-gabble-0.18.2/src/search-manager.c0000644000175000017500000004024012200204333020612 0ustar00smcvsmcv00000000000000/* * search-manager.c - TpChannelManager implementation for ContactSearch channels * Copyright (C) 2009 Collabora Ltd. * Copyright (C) 2009 Nokia Corporation * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 "config.h" #include "search-manager.h" #include #include #include #define DEBUG_FLAG GABBLE_DEBUG_SEARCH #include "gabble/caps-channel-manager.h" #include "connection.h" #include "debug.h" #include "disco.h" #include "search-channel.h" #include "util.h" static void channel_manager_iface_init (gpointer, gpointer); static void caps_channel_manager_iface_init (gpointer, gpointer); G_DEFINE_TYPE_WITH_CODE (GabbleSearchManager, gabble_search_manager, G_TYPE_OBJECT, G_IMPLEMENT_INTERFACE (TP_TYPE_CHANNEL_MANAGER, channel_manager_iface_init); G_IMPLEMENT_INTERFACE (GABBLE_TYPE_CAPS_CHANNEL_MANAGER, caps_channel_manager_iface_init)); static void new_search_channel (GabbleSearchManager *self, const gchar *server, gpointer request_token); /* properties */ enum { PROP_CONNECTION = 1, LAST_PROPERTY }; struct _GabbleSearchManagerPrivate { GabbleConnection *conn; /* Used to represent a set of channels. * Keys are GabbleSearchChannel *, values are an arbitrary non-NULL pointer. */ GHashTable *channels; gchar *default_jud; gboolean disco_done; /* List of request tokens (gpointer) waiting that the disco process is * completed. */ GSList *requests_waiting_disco; gboolean dispose_has_run; }; static void gabble_search_manager_init (GabbleSearchManager *self) { self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, GABBLE_TYPE_SEARCH_MANAGER, GabbleSearchManagerPrivate); self->priv->channels = g_hash_table_new_full (g_direct_hash, g_direct_equal, g_object_unref, NULL); self->priv->conn = NULL; self->priv->dispose_has_run = FALSE; self->priv->disco_done = FALSE; self->priv->requests_waiting_disco = NULL; } static void gabble_search_manager_close_all (GabbleSearchManager *self) { GList *chans, *l; DEBUG ("closing channels"); /* We can't use a GHashTableIter as closing the channel while remove it from * the hash table and we can't modify a hash table while iterating on it. */ chans = g_hash_table_get_keys (self->priv->channels); for (l = chans; l != NULL; l = g_list_next (l)) tp_base_channel_close (TP_BASE_CHANNEL (l->data)); g_list_free (chans); /* base-connection cancels all the pending requests when disconnecting so we * don't have to do anything. */ g_slist_free (self->priv->requests_waiting_disco); self->priv->requests_waiting_disco = NULL; } static void disco_item_found_cb (GabbleDisco *disco, GabbleDiscoItem *item, GabbleSearchManager *self) { if (tp_strdiff (item->category, "directory") || tp_strdiff (item->type, "user")) return; DEBUG ("Found contact directory: %s\n", item->jid); g_free (self->priv->default_jud); self->priv->default_jud = g_strdup (item->jid); } static void disco_done_cb (GabbleDisco *disco, GabbleSearchManager *self) { GSList *l; DEBUG ("Disco is done; complete pending requests"); self->priv->disco_done = TRUE; for (l = self->priv->requests_waiting_disco; l != NULL; l = g_slist_next (l)) { gpointer request_token = l->data; if (self->priv->default_jud != NULL) { new_search_channel (self, self->priv->default_jud, request_token); } else { tp_channel_manager_emit_request_failed (self, request_token, TP_ERROR, TP_ERROR_INVALID_ARGUMENT, "No Server has been specified and no server has been " "discovered on the connection"); } } g_slist_free (self->priv->requests_waiting_disco); self->priv->requests_waiting_disco = NULL; } static void connection_status_changed_cb (GabbleConnection *conn, guint status, guint reason, GabbleSearchManager *self) { switch (status) { case TP_CONNECTION_STATUS_CONNECTING: /* Track Search server available on the connection. * * The GabbleDisco object is created after the channel manager so we * can connect this signal in our constructor. */ gabble_signal_connect_weak (self->priv->conn->disco, "item-found", G_CALLBACK (disco_item_found_cb), G_OBJECT (self)); gabble_signal_connect_weak (self->priv->conn->disco, "done", G_CALLBACK (disco_done_cb), G_OBJECT (self)); break; case TP_CONNECTION_STATUS_DISCONNECTED: gabble_search_manager_close_all (self); break; default: return; } } static GObject * gabble_search_manager_constructor (GType type, guint n_props, GObjectConstructParam *props) { GObject *obj = G_OBJECT_CLASS (gabble_search_manager_parent_class)-> constructor (type, n_props, props); GabbleSearchManager *self = GABBLE_SEARCH_MANAGER (obj); gabble_signal_connect_weak (self->priv->conn, "status-changed", G_CALLBACK (connection_status_changed_cb), G_OBJECT (obj)); return obj; } static void gabble_search_manager_dispose (GObject *object) { GabbleSearchManager *fac = GABBLE_SEARCH_MANAGER (object); GabbleSearchManagerPrivate *priv = fac->priv; if (priv->dispose_has_run) return; DEBUG ("dispose called"); priv->dispose_has_run = TRUE; gabble_search_manager_close_all (fac); if (G_OBJECT_CLASS (gabble_search_manager_parent_class)->dispose) G_OBJECT_CLASS (gabble_search_manager_parent_class)->dispose (object); } static void gabble_search_manager_finalize (GObject *object) { GabbleSearchManager *fac = GABBLE_SEARCH_MANAGER (object); GabbleSearchManagerPrivate *priv = fac->priv; g_free (priv->default_jud); /* close_all removed all the channels from the hash table */ g_assert_cmpuint (g_hash_table_size (priv->channels), ==, 0); g_hash_table_unref (priv->channels); if (G_OBJECT_CLASS (gabble_search_manager_parent_class)->finalize) G_OBJECT_CLASS (gabble_search_manager_parent_class)->finalize (object); } static void gabble_search_manager_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { GabbleSearchManager *self = GABBLE_SEARCH_MANAGER (object); switch (property_id) { case PROP_CONNECTION: g_value_set_object (value, self->priv->conn); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void gabble_search_manager_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { GabbleSearchManager *self = GABBLE_SEARCH_MANAGER (object); switch (property_id) { case PROP_CONNECTION: self->priv->conn = g_value_get_object (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void gabble_search_manager_class_init (GabbleSearchManagerClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); GParamSpec *param_spec; g_type_class_add_private (klass, sizeof (GabbleSearchManagerPrivate)); object_class->constructor = gabble_search_manager_constructor; object_class->dispose = gabble_search_manager_dispose; object_class->finalize = gabble_search_manager_finalize; object_class->get_property = gabble_search_manager_get_property; object_class->set_property = gabble_search_manager_set_property; param_spec = g_param_spec_object ("connection", "GabbleConnection object", "Gabble connection object that owns this ContactSearch manager.", GABBLE_TYPE_CONNECTION, G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, PROP_CONNECTION, param_spec); } static void gabble_search_manager_foreach_channel (TpChannelManager *manager, TpExportableChannelFunc func, gpointer user_data) { GabbleSearchManager *self = GABBLE_SEARCH_MANAGER (manager); GHashTableIter iter; gpointer chan; g_hash_table_iter_init (&iter, self->priv->channels); while (g_hash_table_iter_next (&iter, &chan, NULL)) { /* Don't list channels which are not ready as they have not been * announced in NewChannels yet.*/ if (gabble_search_channel_is_ready (GABBLE_SEARCH_CHANNEL (chan))) func (chan, user_data); } } static const gchar * const search_channel_fixed_properties[] = { TP_IFACE_CHANNEL ".ChannelType", NULL }; static const gchar * const search_channel_allowed_properties[] = { TP_IFACE_CHANNEL_TYPE_CONTACT_SEARCH ".Server", NULL }; static void gabble_search_manager_type_foreach_channel_class (GType type, TpChannelManagerTypeChannelClassFunc func, gpointer user_data) { GHashTable *table = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, (GDestroyNotify) tp_g_value_slice_free); GValue *value; value = tp_g_value_slice_new_string ( TP_IFACE_CHANNEL_TYPE_CONTACT_SEARCH); g_hash_table_insert (table, (gchar *) search_channel_fixed_properties[0], value); func (type, table, search_channel_allowed_properties, user_data); g_hash_table_unref (table); } static void remove_search_channel (GabbleSearchManager *self, GabbleSearchChannel *chan) { g_hash_table_remove (self->priv->channels, chan); } static void search_channel_closed_cb (GabbleSearchChannel *chan, GabbleSearchManager *self) { tp_channel_manager_emit_channel_closed_for_object (self, (TpExportableChannel *) chan); remove_search_channel (self, chan); } typedef struct { GabbleSearchManager *self; gpointer request_token; gchar *server; } RequestContext; static RequestContext * request_context_new (GabbleSearchManager *self, gpointer request_token, const gchar *server) { RequestContext *ctx = g_slice_new (RequestContext); ctx->self = g_object_ref (self); ctx->request_token = request_token; ctx->server = g_strdup (server); return ctx; } static void request_context_free (RequestContext *ctx) { g_object_unref (ctx->self); g_free (ctx->server); g_slice_free (RequestContext, ctx); } static void search_channel_ready_or_not_cb (GabbleSearchChannel *chan, GQuark domain, gint code, const gchar *message, RequestContext *ctx) { if (domain == 0) { GSList *request_tokens = g_slist_prepend (NULL, ctx->request_token); tp_channel_manager_emit_new_channel (ctx->self, (TpExportableChannel *) chan, request_tokens); g_slist_free (request_tokens); } else { if (domain == WOCKY_XMPP_ERROR) { domain = TP_ERROR; switch (code) { case WOCKY_XMPP_ERROR_FORBIDDEN: code = TP_ERROR_PERMISSION_DENIED; break; case WOCKY_XMPP_ERROR_JID_MALFORMED: code = TP_ERROR_INVALID_ARGUMENT; break; default: code = TP_ERROR_NOT_AVAILABLE; } } else { g_assert (domain == TP_ERROR); } tp_channel_manager_emit_request_failed (ctx->self, ctx->request_token, domain, code, message); remove_search_channel (ctx->self, chan); } request_context_free (ctx); } static void new_search_channel (GabbleSearchManager *self, const gchar *server, gpointer request_token) { GabbleSearchManagerPrivate *priv = self->priv; GabbleSearchChannel *chan; TpBaseConnection *base_conn = TP_BASE_CONNECTION (priv->conn); g_assert (server != NULL); chan = g_object_new (GABBLE_TYPE_SEARCH_CHANNEL, "connection", priv->conn, "server", server, "initiator-handle", tp_base_connection_get_self_handle (base_conn), NULL); g_hash_table_insert (priv->channels, chan, priv->channels); g_signal_connect (chan, "closed", (GCallback) search_channel_closed_cb, self); g_signal_connect (chan, "ready-or-not", (GCallback) search_channel_ready_or_not_cb, request_context_new (self, request_token, server)); } static gboolean gabble_search_manager_create_channel (TpChannelManager *manager, gpointer request_token, GHashTable *request_properties) { GabbleSearchManager *self = GABBLE_SEARCH_MANAGER (manager); GError *error = NULL; const gchar *channel_type; const gchar *server; channel_type = tp_asv_get_string (request_properties, TP_IFACE_CHANNEL ".ChannelType"); if (tp_strdiff (channel_type, TP_IFACE_CHANNEL_TYPE_CONTACT_SEARCH)) return FALSE; if (tp_channel_manager_asv_has_unknown_properties (request_properties, search_channel_fixed_properties, search_channel_allowed_properties, &error)) goto error; server = tp_asv_get_string (request_properties, TP_IFACE_CHANNEL_TYPE_CONTACT_SEARCH ".Server"); if (tp_str_empty (server)) { /* Treat an empty server as equivalent to omitting the server entirely. */ server = NULL; } else if (!wocky_decode_jid (server, NULL, NULL, NULL)) { /* On the other hand, if the JID's invalid, blow up. */ g_set_error (&error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT, "Specified server '%s' is not a valid JID", server); goto error; } if (server == NULL) { if (self->priv->default_jud == NULL) { if (self->priv->disco_done) { error = g_error_new (TP_ERROR, TP_ERROR_INVALID_ARGUMENT, "No Server has been specified and no server has been " "discovered on the connection"); goto error; } /* Wait until disco is finished as we still have a chance to * discover a search server. */ DEBUG ("No Server has been specified; wait for the end of " "the disco process"); self->priv->requests_waiting_disco = g_slist_append ( self->priv->requests_waiting_disco, request_token); return TRUE; } DEBUG ("No Server specified; use %s as default", self->priv->default_jud); server = self->priv->default_jud; } new_search_channel (self, server, request_token); return TRUE; error: tp_channel_manager_emit_request_failed (self, request_token, error->domain, error->code, error->message); g_error_free (error); return TRUE; } static void channel_manager_iface_init (gpointer g_iface, gpointer iface_data) { TpChannelManagerIface *iface = g_iface; iface->foreach_channel = gabble_search_manager_foreach_channel; iface->type_foreach_channel_class = gabble_search_manager_type_foreach_channel_class; iface->create_channel = gabble_search_manager_create_channel; iface->request_channel = gabble_search_manager_create_channel; /* Ensuring these channels doesn't really make much sense. */ iface->ensure_channel = NULL; } static void caps_channel_manager_iface_init (gpointer g_iface, gpointer iface_data) { /* Leave everything unimplemented. */ } telepathy-gabble-0.18.2/src/search-manager.h0000644000175000017500000000423312200204333020621 0ustar00smcvsmcv00000000000000/* * search-manager.h - Header for GabbleSearchManager * Copyright (C) 2009 Collabora Ltd. * Copyright (C) 2009 Nokia Corporation * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 __SEARCH_MANAGER_H__ #define __SEARCH_MANAGER_H__ #include G_BEGIN_DECLS typedef struct _GabbleSearchManager GabbleSearchManager; typedef struct _GabbleSearchManagerClass GabbleSearchManagerClass; typedef struct _GabbleSearchManagerPrivate GabbleSearchManagerPrivate; struct _GabbleSearchManagerClass { GObjectClass parent_class; }; struct _GabbleSearchManager { GObject parent; GabbleSearchManagerPrivate *priv; }; GType gabble_search_manager_get_type (void); /* TYPE MACROS */ #define GABBLE_TYPE_SEARCH_MANAGER \ (gabble_search_manager_get_type ()) #define GABBLE_SEARCH_MANAGER(obj) \ (G_TYPE_CHECK_INSTANCE_CAST((obj), GABBLE_TYPE_SEARCH_MANAGER, GabbleSearchManager)) #define GABBLE_SEARCH_MANAGER_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST((klass), GABBLE_TYPE_SEARCH_MANAGER,\ GabbleSearchManagerClass)) #define GABBLE_IS_SEARCH_MANAGER(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE((obj), GABBLE_TYPE_SEARCH_MANAGER)) #define GABBLE_IS_SEARCH_MANAGER_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE((klass), GABBLE_TYPE_SEARCH_MANAGER)) #define GABBLE_SEARCH_MANAGER_GET_CLASS(obj) \ (G_TYPE_INSTANCE_GET_CLASS ((obj), GABBLE_TYPE_SEARCH_MANAGER,\ GabbleSearchManagerClass)) G_END_DECLS #endif /* #ifndef __SEARCH_MANAGER_H__ */ telepathy-gabble-0.18.2/src/search-channel.c0000644000175000017500000010607312200204333020617 0ustar00smcvsmcv00000000000000/* * search-channel.c - implementation of ContactSearch channels * Copyright (C) 2009 Collabora Ltd. * Copyright (C) 2009 Nokia Corporation * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 "config.h" #include "search-channel.h" #include #include #include #include #define DEBUG_FLAG GABBLE_DEBUG_SEARCH #include #include "connection.h" #include "debug.h" #include "gabble-signals-marshal.h" #include "namespaces.h" #include "util.h" /* properties */ enum { PROP_SEARCH_STATE = 1, PROP_AVAILABLE_SEARCH_KEYS, PROP_SERVER, PROP_LIMIT, LAST_PROPERTY }; /* signal enum */ enum { READY_OR_NOT, LAST_SIGNAL }; static guint signals[LAST_SIGNAL] = {0}; /* private structure */ struct _GabbleSearchChannelPrivate { TpChannelContactSearchState state; gchar **available_search_keys; gchar *server; gboolean xforms; /* owned tp_name (gchar *) => owned xmpp_name (gchar *) * This mapping contains the fields that are supported by the server so * if a tp_name can be mapped to different xmpp_name, the hash table will * map to the one supported. */ GHashTable *tp_to_xmpp; /* Array of owned (gchar *) containing all the boolean search terms * supported by this server. */ GPtrArray *boolean_keys; GHashTable *results; /* TRUE if the channel is ready to be used (we received the keys supported * by the server). */ gboolean ready; TpHandleSet *result_handles; }; /* Human-readable values of TpChannelContactSearchState. */ static const gchar *states[] = { "not started", "in progress", "more available", "completed", "failed", }; static void contact_search_iface_init (gpointer, gpointer); G_DEFINE_TYPE_WITH_CODE (GabbleSearchChannel, gabble_search_channel, TP_TYPE_BASE_CHANNEL, G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CHANNEL_TYPE_CONTACT_SEARCH, contact_search_iface_init); ) /* Mapping between XEP 0055/misc data forms fields seen in the wild and * vCard/Telepathy names. */ typedef struct { gchar *xmpp_name; gchar *tp_name; } FieldNameMapping; /* XMPP is not clear about the semantic of "first" and "last". We asked for * clarifications on the standards mailing list and got confirmation * that "first" is actually "Given Name" and "last" is "Family Name". * See http://mail.jabber.org/pipermail/standards/2009-August/022530.html */ static const FieldNameMapping field_mappings[] = { /* Fields specified for non-Data Forms searches */ { "first", "x-n-given" }, { "last", "x-n-family" }, { "nick", "nickname" }, { "email", "email" }, /* Fields observed in implementations of Data Forms searches */ /* ejabberd */ { "user", "x-telepathy-identifier" }, { "fn", "fn" }, { "middle", "x-n-additional" }, { "bday", "bday" }, { "ctry", "x-adr-country" }, { "locality", "x-adr-locality" }, { "x-gender", "x-gender" }, { "orgname", "x-org-name" }, { "orgunit", "x-org-unit" }, { "given", "x-n-given" }, { "family", "x-n-family" }, { "nickname", "nickname" }, /* openfire */ { "search", "" }, /* one big search box */ { "Name", "fn" }, { "Email", "email" }, /* openfire also includes "Username" which is the user part of the jid */ { NULL, NULL }, }; #define NUM_UNEXTENDED_FIELDS 4 static GHashTable *xmpp_to_tp = NULL; static GHashTable *unextended_xmpp_to_tp = NULL; static void build_mapping_tables (void) { guint i; g_return_if_fail (xmpp_to_tp == NULL); xmpp_to_tp = g_hash_table_new (g_str_hash, g_str_equal); unextended_xmpp_to_tp = g_hash_table_new (g_str_hash, g_str_equal); for (i = 0; i < NUM_UNEXTENDED_FIELDS; i++) { g_hash_table_insert (xmpp_to_tp, field_mappings[i].xmpp_name, field_mappings[i].tp_name); } tp_g_hash_table_update (unextended_xmpp_to_tp, xmpp_to_tp, NULL, NULL); for (; field_mappings[i].xmpp_name != NULL; i++) { g_hash_table_insert (xmpp_to_tp, field_mappings[i].xmpp_name, field_mappings[i].tp_name); } } /* Supported field */ static void supported_fields_discovered (GabbleSearchChannel *chan) { DEBUG ("called"); tp_base_channel_register ((TpBaseChannel *) chan); chan->priv->ready = TRUE; g_signal_emit (chan, signals[READY_OR_NOT], 0, 0, 0, NULL); } static void supported_field_discovery_failed (GabbleSearchChannel *chan, const GError *error) { DEBUG ("called: %s, %u, %s", g_quark_to_string (error->domain), error->code, error->message); g_signal_emit (chan, signals[READY_OR_NOT], 0, error->domain, error->code, error->message); } static GPtrArray * parse_unextended_field_response ( GabbleSearchChannel *self, WockyNode *query_node, GError **error) { GPtrArray *search_keys = g_ptr_array_new (); WockyNodeIter i; WockyNode *field; wocky_node_iter_init (&i, query_node, NULL, NULL); while (wocky_node_iter_next (&i, &field)) { gchar *tp_name; if (!strcmp (field->name, "instructions")) { DEBUG ("server gave us some instructions: %s", field->content); continue; } tp_name = g_hash_table_lookup (unextended_xmpp_to_tp, field->name); if (tp_name != NULL) { g_ptr_array_add (search_keys, tp_name); g_hash_table_insert (self->priv->tp_to_xmpp, g_strdup (tp_name), g_strdup (field->name)); } else { g_set_error (error, TP_ERROR, TP_ERROR_NOT_AVAILABLE, "server is broken: %s is not a field defined in XEP 0055", field->name); g_ptr_array_unref (search_keys); return NULL; } } return search_keys; } static gboolean name_in_array (GPtrArray *array, const gchar *name) { guint i; for (i = 0; i < array->len; i++) { if (!tp_strdiff (g_ptr_array_index (array, i), name)) return TRUE; } return FALSE; } static GPtrArray * parse_data_form ( GabbleSearchChannel *self, WockyNode *x_node, GError **error) { GPtrArray *search_keys = g_ptr_array_new (); WockyNodeIter i; WockyNode *n; if (tp_strdiff (wocky_node_get_attribute (x_node, "type"), "form")) { g_set_error (error, TP_ERROR, TP_ERROR_NOT_AVAILABLE, "server is broken: not type='form'"); goto fail; } wocky_node_iter_init (&i, x_node, NULL, NULL); while (wocky_node_iter_next (&i, &n)) { const gchar *type = wocky_node_get_attribute (n, "type"); const gchar *var = wocky_node_get_attribute (n, "var"); gchar *tp_name; if (!strcmp (n->name, "title") || !strcmp (n->name, "instructions")) { DEBUG ("ignoring <%s>: %s", n->name, n->content); continue; } if (strcmp (n->name, "field")) { /* and don't make sense here, and nothing else is * legal. */ DEBUG ("<%s> is not , <instructions> or <field>", n->name); continue; } if (!strcmp (var, "FORM_TYPE")) { const gchar *form_type = wocky_node_get_content_from_child (n, "value"); if (tp_strdiff (form_type, NS_SEARCH)) { DEBUG ("<x> form does not have FORM_TYPE %s", NS_SEARCH); g_set_error (error, TP_ERROR, TP_ERROR_NOT_AVAILABLE, "server is broken: form lacking FORM_TYPE %s", NS_SEARCH); goto fail; } continue; } /* Openfire's search plugin has one search box, called "search", and * tickyboxes controlling which fields it searches. * * So: if the only non-tickybox is a field called "search", expose that * field as "", and remember the tickyboxes. When submitting the form, * tick them all (XXX: or maybe have a whitelist?) */ if (!tp_strdiff (type, "boolean")) { g_ptr_array_add (self->priv->boolean_keys, g_strdup (var)); continue; } tp_name = g_hash_table_lookup (xmpp_to_tp, var); if (tp_name != NULL) { if (name_in_array (search_keys, tp_name)) { DEBUG ("%s has already been added as a search key; Skipping", tp_name); } else { g_ptr_array_add (search_keys, tp_name); g_hash_table_insert (self->priv->tp_to_xmpp, g_strdup (tp_name), g_strdup (var)); } } else { DEBUG ("Unknown data form field: %s\n", var); } } return search_keys; fail: g_ptr_array_unref (search_keys); return NULL; } static void parse_search_field_response (GabbleSearchChannel *chan, WockyNode *query_node) { WockyNode *x_node; GPtrArray *search_keys = NULL; GError *e = NULL; g_return_if_fail (query_node != NULL); x_node = wocky_node_get_child_ns (query_node, "x", NS_X_DATA); if (x_node == NULL) { chan->priv->xforms = FALSE; search_keys = parse_unextended_field_response (chan, query_node, &e); } else { chan->priv->xforms = TRUE; search_keys = parse_data_form (chan, x_node, &e); } if (search_keys == NULL) { supported_field_discovery_failed (chan, e); g_error_free (e); return; } DEBUG ("extracted available fields"); g_ptr_array_add (search_keys, NULL); chan->priv->available_search_keys = (gchar **) g_ptr_array_free (search_keys, FALSE); supported_fields_discovered (chan); } static void query_reply_cb (GabbleConnection *conn, WockyStanza *sent_msg, WockyStanza *reply_msg, GObject *object, gpointer user_data) { GabbleSearchChannel *chan = GABBLE_SEARCH_CHANNEL (object); WockyNode *query_node; GError *err = NULL; query_node = wocky_node_get_child_ns ( wocky_stanza_get_top_node (reply_msg), "query", NS_SEARCH); if (wocky_stanza_extract_errors (reply_msg, NULL, &err, NULL, NULL)) { /* pass */ } else if (NULL == query_node) { err = g_error_new (TP_ERROR, TP_ERROR_NOT_AVAILABLE, "%s is broken: it replied to our <query> with an empty IQ", chan->priv->server); } else { parse_search_field_response (chan, query_node); } if (err != NULL) { supported_field_discovery_failed (chan, err); g_error_free (err); } } static void request_search_fields (GabbleSearchChannel *chan) { TpBaseChannel *base = TP_BASE_CHANNEL (chan); TpBaseConnection *base_conn = tp_base_channel_get_connection (base); WockyStanza *msg; GError *error = NULL; msg = wocky_stanza_build (WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_GET, NULL, chan->priv->server, '(', "query", ':', NS_SEARCH, ')', NULL); if (! _gabble_connection_send_with_reply (GABBLE_CONNECTION (base_conn), msg, query_reply_cb, (GObject *) chan, NULL, &error)) { supported_field_discovery_failed (chan, error); g_error_free (error); } g_object_unref (msg); } /* Search implementation */ /** * change_search_state: * @chan: a search channel * @state: the new state for the channel * @reason: an error in the TP_ERROR domain if the search has failed; NULL * otherwise. */ static void change_search_state (GabbleSearchChannel *chan, TpChannelContactSearchState state, const GError *reason) { GabbleSearchChannelPrivate *priv = chan->priv; GHashTable *details = g_hash_table_new (g_str_hash, g_str_equal); const gchar *error_name = NULL; GValue v = { 0, }; switch (state) { case TP_CHANNEL_CONTACT_SEARCH_STATE_NOT_STARTED: /* Gabble shouldn't ever get into state More_Available */ case TP_CHANNEL_CONTACT_SEARCH_STATE_MORE_AVAILABLE: g_assert_not_reached (); return; case TP_CHANNEL_CONTACT_SEARCH_STATE_IN_PROGRESS: g_assert (priv->state == TP_CHANNEL_CONTACT_SEARCH_STATE_NOT_STARTED); break; case TP_CHANNEL_CONTACT_SEARCH_STATE_COMPLETED: case TP_CHANNEL_CONTACT_SEARCH_STATE_FAILED: g_assert (priv->state == TP_CHANNEL_CONTACT_SEARCH_STATE_IN_PROGRESS); break; } if (state == TP_CHANNEL_CONTACT_SEARCH_STATE_FAILED) { g_assert (reason != NULL); g_assert (reason->domain == TP_ERROR); error_name = tp_error_get_dbus_name (reason->code); g_value_init (&v, G_TYPE_STRING); g_value_set_static_string (&v, reason->message); g_hash_table_insert (details, "debug-message", &v); } else { g_assert (reason == NULL); } DEBUG ("moving from %s to %s for reason '%s'", states[priv->state], states[state], error_name == NULL ? "" : error_name); priv->state = state; tp_svc_channel_type_contact_search_emit_search_state_changed ( chan, state, (error_name == NULL ? "" : error_name), details); g_hash_table_unref (details); } /** * make_field: * @field_name: name of a vCard field; must be a static string. * @values: strv of values for the field. * * Returns: the Contact_Info_Field (field_name, [], values). */ static GValueArray * make_field (const gchar *field_name, gchar **values) { GValueArray *field = g_value_array_new (3); GValue *value; static const gchar **empty = { NULL }; g_value_array_append (field, NULL); value = g_value_array_get_nth (field, 0); g_value_init (value, G_TYPE_STRING); g_value_set_static_string (value, field_name); g_value_array_append (field, NULL); value = g_value_array_get_nth (field, 1); g_value_init (value, G_TYPE_STRV); g_value_set_static_boxed (value, empty); g_value_array_append (field, NULL); value = g_value_array_get_nth (field, 2); g_value_init (value, G_TYPE_STRV); g_value_set_boxed (value, values); return field; } static gchar * ht_lookup_and_remove (GHashTable *info_map, gchar *field_name) { gchar *ret = g_hash_table_lookup (info_map, field_name); g_hash_table_remove (info_map, field_name); return ret; } static void add_search_result (GabbleSearchChannel *chan, GHashTable *info_map) { GPtrArray *info = g_ptr_array_new (); gchar *jid, *first = NULL, *last = NULL; gpointer key, value; GHashTableIter iter; jid = ht_lookup_and_remove (info_map, "jid"); if (jid == NULL) { DEBUG ("no jid; giving up"); return; } if (!wocky_decode_jid (jid, NULL, NULL, NULL)) { DEBUG ("'%s' is not a valid jid; ignoring this result", jid); return; } g_hash_table_iter_init (&iter, info_map); while (g_hash_table_iter_next (&iter, &key, &value)) { const gchar *tp_name; gchar *components[] = { value, NULL }; tp_name = g_hash_table_lookup (xmpp_to_tp, key); if (tp_name == NULL) { DEBUG ("<item> contained field we don't understand (%s); ignoring it:" , (const gchar *) key); continue; } if (value == NULL) { DEBUG ("field %s (%s) doesn't have a value; ignoring it", (const gchar *) key, tp_name); continue; } DEBUG ("found field %s (%s): %s", (const gchar *) key, tp_name, (const gchar *) value); g_ptr_array_add (info, make_field (tp_name, components)); if (!tp_strdiff (key, "last") || !tp_strdiff (key, "family")) last = value; else if (!tp_strdiff (key, "first") || !tp_strdiff (key, "given")) first = value; } /* Build 'n' field: Family Name, Given Name, Additional Names, Honorific * Prefixes, and Honorific Suffixes. */ if (last != NULL || first != NULL) { gchar *components[] = { (last == NULL ? "" : last), (first == NULL ? "" : first), "", "", "", NULL }; g_ptr_array_add (info, make_field ("n", components)); } g_hash_table_insert (chan->priv->results, g_strdup (jid), info); } static void parse_result_item (GabbleSearchChannel *chan, WockyNode *item) { const gchar *jid = wocky_node_get_attribute (item, "jid"); GHashTable *info; WockyNodeIter i; WockyNode *n; if (jid == NULL) { DEBUG ("<item> didn't have a jid attribute; skipping"); return; } info = g_hash_table_new (g_str_hash, g_str_equal); g_hash_table_insert (info, "jid", (gchar *) jid); wocky_node_iter_init (&i, item, NULL, NULL); while (wocky_node_iter_next (&i, &n)) { gchar *value = (gchar *) n->content; g_hash_table_insert (info, n->name, value); } add_search_result (chan, info); g_hash_table_unref (info); } static void parse_extended_result_item (GabbleSearchChannel *chan, WockyNode *item) { GHashTable *info = g_hash_table_new (g_str_hash, g_str_equal); WockyNodeIter i; WockyNode *field; wocky_node_iter_init (&i, item, "field", NULL); while (wocky_node_iter_next (&i, &field)) { WockyNode *value_node; const gchar *var, *value; var = wocky_node_get_attribute (field, "var"); if (var == NULL) { DEBUG ("Ignore <field/> without 'var' attribut"); continue; } value_node = wocky_node_get_child (field, "value"); if (value_node == NULL) { DEBUG ("Ignore <field/> without <value/> child"); continue; } value = value_node->content; g_hash_table_insert (info, (gchar *) var, (gchar *) value); } if (g_hash_table_lookup (info, "jid") == NULL) { DEBUG ("<item> didn't have a jid attribute; skipping"); } else { add_search_result (chan, info); } g_hash_table_unref (info); } static gboolean parse_unextended_search_results (GabbleSearchChannel *chan, WockyNode *query_node, GError **error) { WockyNodeIter i; WockyNode *item; wocky_node_iter_init (&i, query_node, "item", NULL); while (wocky_node_iter_next (&i, &item)) { parse_result_item (chan, item); } return TRUE; } static gboolean parse_extended_search_results (GabbleSearchChannel *chan, WockyNode *query_node, GError **error) { WockyNode *x, *item; WockyNodeIter i; x = wocky_node_get_child_ns (query_node, "x", NS_X_DATA); if (x == NULL) { g_set_error (error, TP_ERROR, TP_ERROR_NOT_AVAILABLE, "reply doens't contain a <x> node"); return FALSE; } wocky_node_iter_init (&i, x, NULL, NULL); while (wocky_node_iter_next (&i, &item)) { if (!tp_strdiff (item->name, "item")) parse_extended_result_item (chan, item); else if (!tp_strdiff (item->name, "reported")) /* Ignore <reported> node */ continue; else if (!tp_strdiff (item->name, "title")) DEBUG ("title: %s", item->content); else DEBUG ("found <%s/> in <x/> rather than <item/>, <title/> and " "<reported/>, skipping", item->name); } return TRUE; } static gboolean parse_search_results (GabbleSearchChannel *chan, WockyNode *query_node, GError **error) { if (chan->priv->xforms) return parse_extended_search_results (chan, query_node, error); else return parse_unextended_search_results (chan, query_node, error); } static void search_reply_cb (GabbleConnection *conn, WockyStanza *sent_msg, WockyStanza *reply_msg, GObject *object, gpointer user_data) { GabbleSearchChannel *chan = GABBLE_SEARCH_CHANNEL (object); WockyNode *query_node; GError *stanza_error = NULL; GError *err = NULL; DEBUG ("called"); if (chan->priv->state != TP_CHANNEL_CONTACT_SEARCH_STATE_IN_PROGRESS) { DEBUG ("state is %s, not in progress; ignoring results", states[chan->priv->state]); return; } query_node = wocky_node_get_child_ns ( wocky_stanza_get_top_node (reply_msg), "query", NS_SEARCH); if (wocky_stanza_extract_errors (reply_msg, NULL, &stanza_error, NULL, NULL)) { gabble_set_tp_error_from_wocky (stanza_error, &err); g_clear_error (&stanza_error); } else if (NULL == query_node) { err = g_error_new (TP_ERROR, TP_ERROR_NOT_AVAILABLE, "%s is broken: its iq reply didn't contain a <query/>", chan->priv->server); } else { parse_search_results (chan, query_node, &err); } if (err == NULL) { /* fire SearchStateChanged */ tp_svc_channel_type_contact_search_emit_search_result_received (chan, chan->priv->results); change_search_state (chan, TP_CHANNEL_CONTACT_SEARCH_STATE_COMPLETED, NULL); } else { DEBUG ("Searching failed: %s", err->message); change_search_state (chan, TP_CHANNEL_CONTACT_SEARCH_STATE_FAILED, err); g_error_free (err); } } static gboolean validate_terms (GabbleSearchChannel *chan, GHashTable *terms, GError **error) { const gchar * const *asks = (const gchar * const *) chan->priv->available_search_keys; GHashTableIter iter; gpointer key; g_hash_table_iter_init (&iter, terms); while (g_hash_table_iter_next (&iter, &key, NULL)) { gchar *field = key; if (!tp_strv_contains (asks, field)) { DEBUG ("%s is not in AvailableSearchKeys", field); g_set_error (error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT, "%s is not in AvailableSearchKeys", field); return FALSE; } } return TRUE; } static void build_unextended_query ( GabbleSearchChannel *self, WockyNode *query, GHashTable *terms) { GHashTableIter iter; gpointer key, value; g_hash_table_iter_init (&iter, terms); while (g_hash_table_iter_next (&iter, &key, &value)) { gchar *xmpp_field = g_hash_table_lookup (self->priv->tp_to_xmpp, key); g_assert (xmpp_field != NULL); wocky_node_add_child_with_content (query, xmpp_field, value); } } static void build_extended_query (GabbleSearchChannel *self, WockyNode *query, GHashTable *terms) { WockyNode *x, *field; GHashTableIter iter; gpointer key, value; x = wocky_node_add_child_ns_q (query, "x", g_quark_from_static_string (NS_X_DATA)); wocky_node_set_attribute (x, "type", "submit"); /* add FORM_TYPE */ field = wocky_node_add_child (x, "field"); wocky_node_set_attributes (field, "type", "hidden", "var", "FORM_TYPE", NULL); wocky_node_add_child_with_content (field, "value", NS_SEARCH); /* Add search terms */ g_hash_table_iter_init (&iter, terms); while (g_hash_table_iter_next (&iter, &key, &value)) { const gchar *tp_name = key; gchar *xmpp_field = g_hash_table_lookup (self->priv->tp_to_xmpp, tp_name); g_assert (xmpp_field != NULL); field = wocky_node_add_child (x, "field"); wocky_node_set_attribute (field, "var", xmpp_field); wocky_node_add_child_with_content (field, "value", value); if (!tp_strdiff (tp_name, "")) { /* Open fire search. Tick all the boolean fields */ guint i; for (i = 0; i < self->priv->boolean_keys->len; i++) { xmpp_field = g_ptr_array_index (self->priv->boolean_keys, i); field = wocky_node_add_child (x, "field"); wocky_node_set_attributes (field, "var", xmpp_field, "type", "boolean", NULL); wocky_node_add_child_with_content (field, "value", "1"); } } } } static gboolean do_search (GabbleSearchChannel *chan, GHashTable *terms, GError **error) { TpBaseChannel *base = TP_BASE_CHANNEL (chan); TpBaseConnection *base_conn = tp_base_channel_get_connection (base); WockyStanza *msg; WockyNode *query; gboolean ret; DEBUG ("called"); if (!validate_terms (chan, terms, error)) return FALSE; msg = wocky_stanza_build (WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_SET, NULL, chan->priv->server, '(', "query", ':', NS_SEARCH, '*', &query, ')', NULL); if (chan->priv->xforms) { build_extended_query (chan, query, terms); } else { build_unextended_query (chan, query, terms); } DEBUG ("Sending search"); if (_gabble_connection_send_with_reply (GABBLE_CONNECTION (base_conn), msg, search_reply_cb, (GObject *) chan, NULL, error)) { ret = TRUE; change_search_state (chan, TP_CHANNEL_CONTACT_SEARCH_STATE_IN_PROGRESS, NULL); } else { ret = FALSE; } g_object_unref (msg); return ret; } /* GObject implementation */ static void gabble_search_channel_init (GabbleSearchChannel *self) { GabbleSearchChannelPrivate *priv = G_TYPE_INSTANCE_GET_PRIVATE (self, GABBLE_TYPE_SEARCH_CHANNEL, GabbleSearchChannelPrivate); self->priv = priv; } static void free_info (GPtrArray *info) { g_boxed_free (TP_ARRAY_TYPE_CONTACT_INFO_FIELD_LIST, info); } static GObject * gabble_search_channel_constructor (GType type, guint n_props, GObjectConstructParam *props) { GObject *obj; GabbleSearchChannel *chan; TpBaseChannel *base; TpBaseConnection *conn; obj = G_OBJECT_CLASS (gabble_search_channel_parent_class)->constructor ( type, n_props, props); chan = GABBLE_SEARCH_CHANNEL (obj); base = TP_BASE_CHANNEL (obj); conn = tp_base_channel_get_connection (base); chan->priv->result_handles = tp_handle_set_new ( tp_base_connection_get_handles (conn, TP_HANDLE_TYPE_CONTACT)); chan->priv->tp_to_xmpp = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); chan->priv->boolean_keys = g_ptr_array_new (); chan->priv->results = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify) free_info); request_search_fields (chan); return obj; } static void gabble_search_channel_finalize (GObject *obj) { GabbleSearchChannel *chan = GABBLE_SEARCH_CHANNEL (obj); GabbleSearchChannelPrivate *priv = chan->priv; guint i; DEBUG ("bye bye %p", obj); g_free (priv->server); tp_handle_set_destroy (priv->result_handles); g_hash_table_unref (chan->priv->tp_to_xmpp); g_free (chan->priv->available_search_keys); for (i = 0; i < priv->boolean_keys->len; i++) { g_free (g_ptr_array_index (priv->boolean_keys, i)); } g_ptr_array_unref (priv->boolean_keys); g_hash_table_unref (chan->priv->results); if (G_OBJECT_CLASS (gabble_search_channel_parent_class)->finalize) G_OBJECT_CLASS (gabble_search_channel_parent_class)->finalize (obj); } static void gabble_search_channel_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { GabbleSearchChannel *chan = GABBLE_SEARCH_CHANNEL (object); switch (property_id) { case PROP_SEARCH_STATE: g_value_set_uint (value, chan->priv->state); break; case PROP_AVAILABLE_SEARCH_KEYS: g_value_set_boxed (value, chan->priv->available_search_keys); break; case PROP_SERVER: g_value_set_string (value, chan->priv->server); break; case PROP_LIMIT: g_value_set_uint (value, 0); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void gabble_search_channel_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { GabbleSearchChannel *chan = GABBLE_SEARCH_CHANNEL (object); switch (property_id) { case PROP_SERVER: chan->priv->server = g_value_dup_string (value); g_assert (chan->priv->server != NULL); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void gabble_search_channel_fill_immutable_properties ( TpBaseChannel *chan, GHashTable *properties) { TP_BASE_CHANNEL_CLASS (gabble_search_channel_parent_class)->fill_immutable_properties ( chan, properties); tp_dbus_properties_mixin_fill_properties_hash ( G_OBJECT (chan), properties, TP_IFACE_CHANNEL_TYPE_CONTACT_SEARCH, "AvailableSearchKeys", TP_IFACE_CHANNEL_TYPE_CONTACT_SEARCH, "Server", TP_IFACE_CHANNEL_TYPE_CONTACT_SEARCH, "Limit", NULL); } static gchar * gabble_search_channel_get_object_path_suffix (TpBaseChannel *base) { GabbleSearchChannel *self = GABBLE_SEARCH_CHANNEL (base); gchar *escaped, *ret; escaped = tp_escape_as_identifier (self->priv->server); ret = g_strdup_printf ("SearchChannel_%s_%p", escaped, self); g_free (escaped); return ret; } static void gabble_search_channel_class_init (GabbleSearchChannelClass *klass) { static TpDBusPropertiesMixinPropImpl search_channel_props[] = { { "SearchState", "search-state", NULL }, { "AvailableSearchKeys", "available-search-keys", NULL }, { "Server", "server", NULL }, { "Limit", "limit", NULL }, { NULL } }; GObjectClass *object_class = G_OBJECT_CLASS (klass); TpBaseChannelClass *base_class = TP_BASE_CHANNEL_CLASS (klass); GParamSpec *param_spec; g_type_class_add_private (klass, sizeof (GabbleSearchChannelPrivate)); object_class->constructor = gabble_search_channel_constructor; object_class->finalize = gabble_search_channel_finalize; object_class->get_property = gabble_search_channel_get_property; object_class->set_property = gabble_search_channel_set_property; base_class->channel_type = TP_IFACE_CHANNEL_TYPE_CONTACT_SEARCH; base_class->target_handle_type = TP_HANDLE_TYPE_NONE; base_class->fill_immutable_properties = gabble_search_channel_fill_immutable_properties; base_class->get_object_path_suffix = gabble_search_channel_get_object_path_suffix; /* We don't have to do any special clean-up when told to close, so we can * just roll over and die immediately. */ base_class->close = tp_base_channel_destroyed; param_spec = g_param_spec_uint ("search-state", "Search state", "The current state of the search represented by this channel", TP_CHANNEL_CONTACT_SEARCH_STATE_NOT_STARTED, TP_CHANNEL_CONTACT_SEARCH_STATE_FAILED, TP_CHANNEL_CONTACT_SEARCH_STATE_NOT_STARTED, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, PROP_SEARCH_STATE, param_spec); param_spec = g_param_spec_boxed ("available-search-keys", "Available search keys", "The set of search keys supported by this channel", G_TYPE_STRV, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, PROP_AVAILABLE_SEARCH_KEYS, param_spec); param_spec = g_param_spec_string ("server", "Search server", "The user directory server used by this search", NULL, G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, PROP_SERVER, param_spec); param_spec = g_param_spec_uint ("limit", "Result limit", "Always 0 for unlimited in Gabble", 0, 0, 0, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, PROP_LIMIT, param_spec); /* Emitted when we get a reply from the server about which search keys it * supports. Its three arguments are the components of a GError. If the * server gave us a set of search keys, and they were sane, all components * will be 0 or %NULL, indicating that this channel can be announced and * used; if the server doesn't actually speak XEP 0055 or is full of bees, * they'll be an error in either the GABBLE_XMPP_ERROR or the TP_ERROR * domain. */ signals[READY_OR_NOT] = g_signal_new ("ready-or-not", G_OBJECT_CLASS_TYPE (klass), G_SIGNAL_RUN_LAST, 0, NULL, NULL, gabble_marshal_VOID__UINT_INT_STRING, G_TYPE_NONE, 3, G_TYPE_UINT, G_TYPE_INT, G_TYPE_STRING); tp_dbus_properties_mixin_implement_interface (object_class, TP_IFACE_QUARK_CHANNEL_TYPE_CONTACT_SEARCH, tp_dbus_properties_mixin_getter_gobject_properties, NULL, search_channel_props); build_mapping_tables (); } static void gabble_search_channel_search (TpSvcChannelTypeContactSearch *self, GHashTable *terms, DBusGMethodInvocation *context) { GabbleSearchChannel *chan = GABBLE_SEARCH_CHANNEL (self); GabbleSearchChannelPrivate *priv = chan->priv; GError *error = NULL; if (priv->state != TP_CHANNEL_CONTACT_SEARCH_STATE_NOT_STARTED) { error = g_error_new (TP_ERROR, TP_ERROR_NOT_AVAILABLE, "SearchState is %s", states[priv->state]); goto err; } if (do_search (chan, terms, &error)) { tp_svc_channel_type_contact_search_return_from_search (context); return; } err: dbus_g_method_return_error (context, error); g_error_free (error); } static void gabble_search_channel_stop (TpSvcChannelTypeContactSearch *self, DBusGMethodInvocation *context) { GabbleSearchChannel *chan = GABBLE_SEARCH_CHANNEL (self); GabbleSearchChannelPrivate *priv = chan->priv; switch (priv->state) { case TP_CHANNEL_CONTACT_SEARCH_STATE_IN_PROGRESS: { GError e = { TP_ERROR, TP_ERROR_CANCELLED, "Stop() called" }; change_search_state (chan, TP_CHANNEL_CONTACT_SEARCH_STATE_FAILED, &e); /* Deliberately falling through to return from the method: */ } case TP_CHANNEL_CONTACT_SEARCH_STATE_COMPLETED: case TP_CHANNEL_CONTACT_SEARCH_STATE_FAILED: tp_svc_channel_type_contact_search_return_from_stop (context); break; case TP_CHANNEL_CONTACT_SEARCH_STATE_NOT_STARTED: { GError e = { TP_ERROR, TP_ERROR_NOT_AVAILABLE, "Search() hasn't been called yet" }; dbus_g_method_return_error (context, &e); break; } case TP_CHANNEL_CONTACT_SEARCH_STATE_MORE_AVAILABLE: g_assert_not_reached (); } } gboolean gabble_search_channel_is_ready (GabbleSearchChannel *self) { return self->priv->ready; } static void contact_search_iface_init (gpointer g_iface, gpointer iface_data) { TpSvcChannelTypeContactSearchClass *klass = g_iface; #define IMPLEMENT(x) tp_svc_channel_type_contact_search_implement_##x (\ klass, gabble_search_channel_##x) IMPLEMENT(search); IMPLEMENT(stop); #undef IMPLEMENT } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������telepathy-gabble-0.18.2/src/search-channel.h��������������������������������������������������������0000644�0001750�0001750�00000004457�12200204333�020627� 0����������������������������������������������������������������������������������������������������ustar�00smcv����������������������������smcv����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * search-channel.h - Header for GabbleSearchChannel * Copyright (C) 2009 Collabora Ltd. * Copyright (C) 2009 Nokia Corporation * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 __GABBLE_SEARCH_CHANNEL_H__ #define __GABBLE_SEARCH_CHANNEL_H__ #include <glib-object.h> #include <telepathy-glib/telepathy-glib.h> G_BEGIN_DECLS typedef struct _GabbleSearchChannel GabbleSearchChannel; typedef struct _GabbleSearchChannelClass GabbleSearchChannelClass; typedef struct _GabbleSearchChannelPrivate GabbleSearchChannelPrivate; struct _GabbleSearchChannelClass { TpBaseChannelClass base_class; }; struct _GabbleSearchChannel { TpBaseChannel base; GabbleSearchChannelPrivate *priv; }; GType gabble_search_channel_get_type (void); /* TYPE MACROS */ #define GABBLE_TYPE_SEARCH_CHANNEL \ (gabble_search_channel_get_type ()) #define GABBLE_SEARCH_CHANNEL(obj) \ (G_TYPE_CHECK_INSTANCE_CAST((obj), GABBLE_TYPE_SEARCH_CHANNEL, GabbleSearchChannel)) #define GABBLE_SEARCH_CHANNEL_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST((klass), GABBLE_TYPE_SEARCH_CHANNEL,\ GabbleSearchChannelClass)) #define GABBLE_IS_SEARCH_CHANNEL(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE((obj), GABBLE_TYPE_SEARCH_CHANNEL)) #define GABBLE_IS_SEARCH_CHANNEL_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE((klass), GABBLE_TYPE_SEARCH_CHANNEL)) #define GABBLE_SEARCH_CHANNEL_GET_CLASS(obj) \ (G_TYPE_INSTANCE_GET_CLASS ((obj), GABBLE_TYPE_SEARCH_CHANNEL, \ GabbleSearchChannelClass)) gboolean gabble_search_channel_is_ready (GabbleSearchChannel *chan); G_END_DECLS #endif /* #ifndef __GABBLE_SEARCH_CHANNEL_H__*/ �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������telepathy-gabble-0.18.2/src/roomlist-manager.c������������������������������������������������������0000644�0001750�0001750�00000030645�12200204333�021225� 0����������������������������������������������������������������������������������������������������ustar�00smcv����������������������������smcv����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * roomlist-manager - ChannelManager for room lists * Copyright (C) 2006-2008 Collabora Ltd. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 "config.h" #include "roomlist-manager.h" #include <stdlib.h> #include <string.h> #include <dbus/dbus-glib.h> #include <dbus/dbus-glib-lowlevel.h> #include <telepathy-glib/telepathy-glib.h> #include <telepathy-glib/telepathy-glib-dbus.h> #define DEBUG_FLAG GABBLE_DEBUG_MUC #include "gabble/caps-channel-manager.h" #include "connection.h" #include "debug.h" #include "namespaces.h" #include "roomlist-channel.h" #include "util.h" static void channel_manager_iface_init (gpointer, gpointer); G_DEFINE_TYPE_WITH_CODE (GabbleRoomlistManager, gabble_roomlist_manager, G_TYPE_OBJECT, G_IMPLEMENT_INTERFACE (TP_TYPE_CHANNEL_MANAGER, channel_manager_iface_init); G_IMPLEMENT_INTERFACE (GABBLE_TYPE_CAPS_CHANNEL_MANAGER, NULL)); /* properties */ enum { PROP_CONNECTION = 1, LAST_PROPERTY }; struct _GabbleRoomlistManagerPrivate { GabbleConnection *conn; gulong status_changed_id; GPtrArray *channels; gboolean dispose_has_run; }; static void gabble_roomlist_manager_init (GabbleRoomlistManager *self) { self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, GABBLE_TYPE_ROOMLIST_MANAGER, GabbleRoomlistManagerPrivate); /* In practice we probably won't have more than one room list at a time */ self->priv->channels = g_ptr_array_sized_new (2); } static void gabble_roomlist_manager_close_all (GabbleRoomlistManager *self) { DEBUG ("%p", self); if (self->priv->status_changed_id != 0) { g_signal_handler_disconnect (self->priv->conn, self->priv->status_changed_id); self->priv->status_changed_id = 0; } /* Use a temporary variable because we don't want * roomlist_channel_closed_cb to remove the channel from the ptr array a * second time */ if (self->priv->channels != NULL) { GPtrArray *tmp = self->priv->channels; guint i; self->priv->channels = NULL; for (i = 0; i < tmp->len; i++) { GabbleRoomlistChannel *channel = g_ptr_array_index (tmp, i); DEBUG ("Channel's refcount is %u before unref", G_OBJECT (channel)->ref_count); g_object_unref (channel); } g_ptr_array_unref (tmp); } } static void gabble_roomlist_manager_dispose (GObject *object) { GabbleRoomlistManager *self = GABBLE_ROOMLIST_MANAGER (object); if (self->priv->dispose_has_run) return; DEBUG ("running"); self->priv->dispose_has_run = TRUE; gabble_roomlist_manager_close_all (self); g_assert (self->priv->channels == NULL); if (G_OBJECT_CLASS (gabble_roomlist_manager_parent_class)->dispose) G_OBJECT_CLASS (gabble_roomlist_manager_parent_class)->dispose (object); } static void gabble_roomlist_manager_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { GabbleRoomlistManager *self = GABBLE_ROOMLIST_MANAGER (object); switch (property_id) { case PROP_CONNECTION: g_value_set_object (value, self->priv->conn); break; } } static void gabble_roomlist_manager_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { GabbleRoomlistManager *self = GABBLE_ROOMLIST_MANAGER (object); switch (property_id) { case PROP_CONNECTION: g_assert (self->priv->conn == NULL); self->priv->conn = g_value_get_object (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void connection_status_changed_cb (GabbleConnection *conn, guint status, guint reason, GabbleRoomlistManager *self) { switch (status) { case TP_CONNECTION_STATUS_CONNECTING: break; case TP_CONNECTION_STATUS_CONNECTED: break; case TP_CONNECTION_STATUS_DISCONNECTED: DEBUG ("disconnected, closing room lists"); gabble_roomlist_manager_close_all (self); break; default: g_assert_not_reached (); } } static void gabble_roomlist_manager_constructed (GObject *object) { GabbleRoomlistManager *self = GABBLE_ROOMLIST_MANAGER (object); void (*chain_up) (GObject *) = G_OBJECT_CLASS (gabble_roomlist_manager_parent_class)->constructed; DEBUG ("%p", self); if (chain_up != NULL) chain_up (object); self->priv->status_changed_id = g_signal_connect (self->priv->conn, "status-changed", (GCallback) connection_status_changed_cb, object); } static void gabble_roomlist_manager_class_init (GabbleRoomlistManagerClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); GParamSpec *param_spec; g_type_class_add_private (klass, sizeof (GabbleRoomlistManagerPrivate)); object_class->constructed = gabble_roomlist_manager_constructed; object_class->dispose = gabble_roomlist_manager_dispose; object_class->get_property = gabble_roomlist_manager_get_property; object_class->set_property = gabble_roomlist_manager_set_property; param_spec = g_param_spec_object ("connection", "GabbleConnection object", "Gabble connection object that owns this room-list manager.", GABBLE_TYPE_CONNECTION, G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, PROP_CONNECTION, param_spec); } static void gabble_roomlist_manager_foreach_channel (TpChannelManager *manager, TpExportableChannelFunc foreach, gpointer user_data) { GabbleRoomlistManager *self = GABBLE_ROOMLIST_MANAGER (manager); guint i; for (i = 0; i < self->priv->channels->len; i++) { TpExportableChannel *channel = TP_EXPORTABLE_CHANNEL ( g_ptr_array_index (self->priv->channels, i)); foreach (channel, user_data); } } static const gchar * const roomlist_channel_fixed_properties[] = { TP_IFACE_CHANNEL ".ChannelType", TP_IFACE_CHANNEL ".TargetHandleType", NULL }; static const gchar * const roomlist_channel_allowed_properties[] = { TP_IFACE_CHANNEL_TYPE_ROOM_LIST ".Server", NULL }; static void gabble_roomlist_manager_type_foreach_channel_class (GType type, TpChannelManagerTypeChannelClassFunc func, gpointer user_data) { GHashTable *table = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, (GDestroyNotify) tp_g_value_slice_free); GValue *value; value = tp_g_value_slice_new (G_TYPE_STRING); g_value_set_static_string (value, TP_IFACE_CHANNEL_TYPE_ROOM_LIST); g_hash_table_insert (table, TP_IFACE_CHANNEL ".ChannelType", value); value = tp_g_value_slice_new (G_TYPE_UINT); g_value_set_uint (value, TP_HANDLE_TYPE_NONE); g_hash_table_insert (table, TP_IFACE_CHANNEL ".TargetHandleType", value); func (type, table, roomlist_channel_allowed_properties, user_data); g_hash_table_unref (table); } static void roomlist_channel_closed_cb (GabbleRoomlistChannel *channel, gpointer user_data) { GabbleRoomlistManager *self = GABBLE_ROOMLIST_MANAGER (user_data); tp_channel_manager_emit_channel_closed_for_object (self, TP_EXPORTABLE_CHANNEL (channel)); if (self->priv->channels != NULL) { g_ptr_array_remove (self->priv->channels, channel); g_object_unref (channel); } } static gboolean gabble_roomlist_manager_handle_request (TpChannelManager *manager, gpointer request_token, GHashTable *request_properties, gboolean require_new) { GabbleRoomlistManager *self = GABBLE_ROOMLIST_MANAGER (manager); GabbleRoomlistChannel *channel = NULL; GError *error = NULL; GSList *request_tokens; const gchar *server; if (tp_strdiff (tp_asv_get_string (request_properties, TP_IFACE_CHANNEL ".ChannelType"), TP_IFACE_CHANNEL_TYPE_ROOM_LIST)) return FALSE; if (tp_asv_get_uint32 (request_properties, TP_IFACE_CHANNEL ".TargetHandleType", NULL) != 0) { g_set_error (&error, TP_ERROR, TP_ERROR_NOT_IMPLEMENTED, "RoomList channels can't have a target handle"); goto error; } if (tp_channel_manager_asv_has_unknown_properties (request_properties, roomlist_channel_fixed_properties, roomlist_channel_allowed_properties, &error)) goto error; server = tp_asv_get_string (request_properties, TP_IFACE_CHANNEL_TYPE_ROOM_LIST ".Server"); if (server == NULL || server[0] == '\0') { server = _gabble_connection_find_conference_server (self->priv->conn); if (server == NULL) { g_set_error (&error, TP_ERROR, TP_ERROR_NOT_AVAILABLE, "Unable to choose a default conference server"); goto error; } } if (!require_new) { guint i; for (i = 0; i < self->priv->channels->len; i++) { gchar *its_server; gboolean good; channel = g_ptr_array_index (self->priv->channels, i); g_object_get (channel, "conference-server", &its_server, NULL); good = !tp_strdiff (its_server, server); g_free (its_server); if (good) { tp_channel_manager_emit_request_already_satisfied (self, request_token, TP_EXPORTABLE_CHANNEL (channel)); return TRUE; } } } /* no existing channel is suitable - make a new one */ channel = _gabble_roomlist_channel_new (self->priv->conn, server); g_signal_connect (channel, "closed", (GCallback) roomlist_channel_closed_cb, self); g_ptr_array_add (self->priv->channels, channel); request_tokens = g_slist_prepend (NULL, request_token); tp_channel_manager_emit_new_channel (self, TP_EXPORTABLE_CHANNEL (channel), request_tokens); g_slist_free (request_tokens); return TRUE; error: tp_channel_manager_emit_request_failed (self, request_token, error->domain, error->code, error->message); g_error_free (error); return TRUE; } static gboolean gabble_roomlist_manager_create_channel (TpChannelManager *manager, gpointer request_token, GHashTable *request_properties) { return gabble_roomlist_manager_handle_request (manager, request_token, request_properties, TRUE); } static gboolean gabble_roomlist_manager_request_channel (TpChannelManager *manager, gpointer request_token, GHashTable *request_properties) { return gabble_roomlist_manager_handle_request (manager, request_token, request_properties, FALSE); } static gboolean gabble_roomlist_manager_ensure_channel (TpChannelManager *manager, gpointer request_token, GHashTable *request_properties) { return gabble_roomlist_manager_handle_request (manager, request_token, request_properties, FALSE); } static void channel_manager_iface_init (gpointer g_iface, gpointer iface_data) { TpChannelManagerIface *iface = g_iface; iface->foreach_channel = gabble_roomlist_manager_foreach_channel; iface->type_foreach_channel_class = gabble_roomlist_manager_type_foreach_channel_class; iface->request_channel = gabble_roomlist_manager_request_channel; iface->create_channel = gabble_roomlist_manager_create_channel; iface->ensure_channel = gabble_roomlist_manager_ensure_channel; } �������������������������������������������������������������������������������������������telepathy-gabble-0.18.2/src/roomlist-manager.h������������������������������������������������������0000644�0001750�0001750�00000004257�12200204333�021232� 0����������������������������������������������������������������������������������������������������ustar�00smcv����������������������������smcv����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * roomlist-manager - ChannelManager for room lists * Copyright (C) 2006-2008 Collabora Ltd. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 GABBLE_ROOMLIST_MANAGER_H #define GABBLE_ROOMLIST_MANAGER_H #include <glib-object.h> G_BEGIN_DECLS typedef struct _GabbleRoomlistManager GabbleRoomlistManager; typedef struct _GabbleRoomlistManagerClass GabbleRoomlistManagerClass; typedef struct _GabbleRoomlistManagerPrivate GabbleRoomlistManagerPrivate; struct _GabbleRoomlistManagerClass { GObjectClass parent_class; }; struct _GabbleRoomlistManager { GObject parent; GabbleRoomlistManagerPrivate *priv; }; GType gabble_roomlist_manager_get_type (void); /* TYPE MACROS */ #define GABBLE_TYPE_ROOMLIST_MANAGER \ (gabble_roomlist_manager_get_type ()) #define GABBLE_ROOMLIST_MANAGER(obj) \ (G_TYPE_CHECK_INSTANCE_CAST((obj), GABBLE_TYPE_ROOMLIST_MANAGER,\ GabbleRoomlistManager)) #define GABBLE_ROOMLIST_MANAGER_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST((klass), GABBLE_TYPE_ROOMLIST_MANAGER,\ GabbleRoomlistManagerClass)) #define GABBLE_IS_ROOMLIST_MANAGER(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE((obj), GABBLE_TYPE_ROOMLIST_MANAGER)) #define GABBLE_IS_ROOMLIST_MANAGER_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE((klass), GABBLE_TYPE_ROOMLIST_MANAGER)) #define GABBLE_ROOMLIST_MANAGER_GET_CLASS(obj) \ (G_TYPE_INSTANCE_GET_CLASS ((obj), GABBLE_TYPE_ROOMLIST_MANAGER,\ GabbleRoomlistManagerClass)) G_END_DECLS #endif �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������telepathy-gabble-0.18.2/src/roomlist-channel.c������������������������������������������������������0000644�0001750�0001750�00000042115�12200204333�021216� 0����������������������������������������������������������������������������������������������������ustar�00smcv����������������������������smcv����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * gabble-roomlist-channel.c - Source for GabbleRoomlistChannel * Copyright (C) 2005 Collabora Ltd. * Copyright (C) 2005 Nokia Corporation * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 "config.h" #include "roomlist-channel.h" #include <string.h> #include <dbus/dbus-glib.h> #include <telepathy-glib/telepathy-glib.h> #include <telepathy-glib/telepathy-glib-dbus.h> #define DEBUG_FLAG GABBLE_DEBUG_ROOMLIST #include "connection.h" #include "debug.h" #include "disco.h" #include "namespaces.h" #include "util.h" static void roomlist_iface_init (gpointer, gpointer); G_DEFINE_TYPE_WITH_CODE (GabbleRoomlistChannel, gabble_roomlist_channel, TP_TYPE_BASE_CHANNEL, G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CHANNEL_TYPE_ROOM_LIST, roomlist_iface_init); ); /* properties */ enum { PROP_CONFERENCE_SERVER = 1, LAST_PROPERTY }; /* private structure */ struct _GabbleRoomlistChannelPrivate { gchar *conference_server; gboolean listing; gpointer disco_pipeline; TpHandleSet *signalled_rooms; GPtrArray *pending_room_signals; guint timer_source_id; gboolean dispose_has_run; }; #define ROOM_SIGNAL_INTERVAL 300 static gboolean emit_room_signal (gpointer data); static void gabble_roomlist_channel_close (TpBaseChannel *base); static void gabble_roomlist_channel_init (GabbleRoomlistChannel *self) { GabbleRoomlistChannelPrivate *priv = G_TYPE_INSTANCE_GET_PRIVATE (self, GABBLE_TYPE_ROOMLIST_CHANNEL, GabbleRoomlistChannelPrivate); self->priv = priv; priv->pending_room_signals = g_ptr_array_new (); } static void gabble_roomlist_channel_constructed (GObject *obj) { GObjectClass *parent_class = gabble_roomlist_channel_parent_class; GabbleRoomlistChannel *self = GABBLE_ROOMLIST_CHANNEL (obj); TpBaseChannel *base_chan = (TpBaseChannel *) self; TpBaseConnection *conn = tp_base_channel_get_connection (base_chan); TpHandleRepoIface *room_handles; if (parent_class->constructed != NULL) parent_class->constructed (obj); room_handles = tp_base_connection_get_handles (conn, TP_HANDLE_TYPE_ROOM); self->priv->signalled_rooms = tp_handle_set_new (room_handles); tp_base_channel_register (TP_BASE_CHANNEL (obj)); } static void gabble_roomlist_channel_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { GabbleRoomlistChannel *chan = GABBLE_ROOMLIST_CHANNEL (object); GabbleRoomlistChannelPrivate *priv = chan->priv; switch (property_id) { case PROP_CONFERENCE_SERVER: g_value_set_string (value, priv->conference_server); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void gabble_roomlist_channel_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { GabbleRoomlistChannel *chan = GABBLE_ROOMLIST_CHANNEL (object); GabbleRoomlistChannelPrivate *priv = chan->priv; switch (property_id) { case PROP_CONFERENCE_SERVER: g_free (priv->conference_server); priv->conference_server = g_value_dup_string (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void gabble_roomlist_channel_dispose (GObject *object); static void gabble_roomlist_channel_finalize (GObject *object); static void gabble_roomlist_channel_fill_immutable_properties ( TpBaseChannel *chan, GHashTable *properties) { TP_BASE_CHANNEL_CLASS (gabble_roomlist_channel_parent_class)->fill_immutable_properties ( chan, properties); tp_dbus_properties_mixin_fill_properties_hash ( G_OBJECT (chan), properties, TP_IFACE_CHANNEL_TYPE_ROOM_LIST, "Server", NULL); } static gchar * gabble_roomlist_channel_get_object_path_suffix (TpBaseChannel *base) { return g_strdup_printf ("RoomlistChannel%p", base); } static void gabble_roomlist_channel_class_init (GabbleRoomlistChannelClass *klass) { static TpDBusPropertiesMixinPropImpl roomlist_props[] = { { "Server", "conference-server", NULL }, { NULL } }; TpBaseChannelClass *base_class = TP_BASE_CHANNEL_CLASS (klass); GObjectClass *object_class = G_OBJECT_CLASS (klass); GParamSpec *param_spec; g_type_class_add_private (klass, sizeof (GabbleRoomlistChannelPrivate)); object_class->constructed = gabble_roomlist_channel_constructed; object_class->get_property = gabble_roomlist_channel_get_property; object_class->set_property = gabble_roomlist_channel_set_property; object_class->dispose = gabble_roomlist_channel_dispose; object_class->finalize = gabble_roomlist_channel_finalize; base_class->channel_type = TP_IFACE_CHANNEL_TYPE_ROOM_LIST; base_class->target_handle_type = TP_HANDLE_TYPE_NONE; base_class->get_object_path_suffix = gabble_roomlist_channel_get_object_path_suffix; base_class->fill_immutable_properties = gabble_roomlist_channel_fill_immutable_properties; base_class->close = gabble_roomlist_channel_close; param_spec = g_param_spec_string ("conference-server", "Name of conference server to use", "Name of the XMPP conference server on which to list rooms", "", G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, PROP_CONFERENCE_SERVER, param_spec); tp_dbus_properties_mixin_implement_interface (object_class, TP_IFACE_QUARK_CHANNEL_TYPE_ROOM_LIST, tp_dbus_properties_mixin_getter_gobject_properties, NULL, roomlist_props); } static void stop_listing (GabbleRoomlistChannel *self); static void gabble_roomlist_channel_dispose (GObject *object) { GabbleRoomlistChannel *self = GABBLE_ROOMLIST_CHANNEL (object); GabbleRoomlistChannelPrivate *priv = self->priv; if (priv->dispose_has_run) return; priv->dispose_has_run = TRUE; stop_listing (self); g_assert (priv->pending_room_signals != NULL); g_assert (priv->pending_room_signals->len == 0); g_ptr_array_unref (priv->pending_room_signals); priv->pending_room_signals = NULL; if (G_OBJECT_CLASS (gabble_roomlist_channel_parent_class)->dispose) G_OBJECT_CLASS (gabble_roomlist_channel_parent_class)->dispose (object); } static void gabble_roomlist_channel_finalize (GObject *object) { GabbleRoomlistChannel *self = GABBLE_ROOMLIST_CHANNEL (object); GabbleRoomlistChannelPrivate *priv = self->priv; /* free any data held directly by the object here */ g_free (priv->conference_server); if (priv->signalled_rooms != NULL) tp_handle_set_destroy (priv->signalled_rooms); G_OBJECT_CLASS (gabble_roomlist_channel_parent_class)->finalize (object); } GabbleRoomlistChannel * _gabble_roomlist_channel_new (GabbleConnection *conn, const gchar *conference_server) { TpHandle initiator; g_return_val_if_fail (GABBLE_IS_CONNECTION (conn), NULL); g_return_val_if_fail (conference_server != NULL, NULL); /* We are always the initiator. */ initiator = tp_base_connection_get_self_handle ((TpBaseConnection *) conn); return GABBLE_ROOMLIST_CHANNEL ( g_object_new (GABBLE_TYPE_ROOMLIST_CHANNEL, "connection", conn, "initiator-handle", initiator, "requested", TRUE, "conference-server", conference_server, NULL)); } static gboolean emit_room_signal (gpointer data) { GabbleRoomlistChannel *chan = data; GabbleRoomlistChannelPrivate *priv = chan->priv; GType room_info_type = TP_STRUCT_TYPE_ROOM_INFO; if (!priv->listing) return FALSE; if (priv->pending_room_signals->len == 0) return TRUE; tp_svc_channel_type_room_list_emit_got_rooms ( (TpSvcChannelTypeRoomList *) chan, priv->pending_room_signals); while (priv->pending_room_signals->len != 0) { gpointer boxed = g_ptr_array_index (priv->pending_room_signals, 0); g_boxed_free (room_info_type, boxed); g_ptr_array_remove_index_fast (priv->pending_room_signals, 0); } return TRUE; } static void room_info_cb (gpointer pipeline, GabbleDiscoItem *item, gpointer user_data) { GabbleRoomlistChannel *chan = user_data; TpBaseChannel *base; GabbleRoomlistChannelPrivate *priv; TpHandleRepoIface *room_handles; const char *jid, *category, *type, *var, *name; TpHandle handle; GHashTable *keys; GValue room = {0,}; GValue *tmp; gpointer k, v; GType room_info_type = TP_STRUCT_TYPE_ROOM_INFO; #define INSERT_KEY(hash, name, type, type2, value) \ do {\ tmp = g_slice_new0 (GValue); \ g_value_init (tmp, (type)); \ g_value_set_##type2 (tmp, (value)); \ g_hash_table_insert (hash, (name), tmp); \ } while (0) g_assert (GABBLE_IS_ROOMLIST_CHANNEL (chan)); base = TP_BASE_CHANNEL (chan); priv = chan->priv; room_handles = tp_base_connection_get_handles ( tp_base_channel_get_connection (base), TP_HANDLE_TYPE_ROOM); jid = item->jid; name = item->name; category = item->category; type = item->type; if (0 != strcmp (category, "conference") || 0 != strcmp (type, "text")) return; if (!g_hash_table_lookup_extended (item->features, "http://jabber.org/protocol/muc", &k, &v)) { /* not muc */ return; } handle = tp_handle_ensure (room_handles, jid, NULL, NULL); if (handle == 0) { DEBUG ("ignoring listed room with invalid JID '%s'", jid); return; } DEBUG ("got room identity, name=%s, category=%s, type=%s", name, category, type); keys = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, (GDestroyNotify) tp_g_value_slice_free); INSERT_KEY (keys, "handle-name", G_TYPE_STRING, string, tp_handle_inspect (room_handles, handle)); INSERT_KEY (keys, "name", G_TYPE_STRING, string, name); if (g_hash_table_lookup_extended (item->features, "muc_membersonly", &k, &v)) INSERT_KEY (keys, "invite-only", G_TYPE_BOOLEAN, boolean, TRUE); if (g_hash_table_lookup_extended (item->features, "muc_open", &k, &v)) INSERT_KEY (keys, "invite-only", G_TYPE_BOOLEAN, boolean, FALSE); if (g_hash_table_lookup_extended (item->features, "muc_passwordprotected", &k, &v)) INSERT_KEY (keys, "password", G_TYPE_BOOLEAN, boolean, TRUE); if (g_hash_table_lookup_extended (item->features, "muc_unsecure", &k, &v)) INSERT_KEY (keys, "password", G_TYPE_BOOLEAN, boolean, FALSE); if (g_hash_table_lookup_extended (item->features, "muc_unsecured", &k, &v)) INSERT_KEY (keys, "password", G_TYPE_BOOLEAN, boolean, FALSE); if (g_hash_table_lookup_extended (item->features, "muc_hidden", &k, &v)) INSERT_KEY (keys, "hidden", G_TYPE_BOOLEAN, boolean, TRUE); if (g_hash_table_lookup_extended (item->features, "muc_public", &k, &v)) INSERT_KEY (keys, "hidden", G_TYPE_BOOLEAN, boolean, FALSE); if (g_hash_table_lookup_extended (item->features, "muc_membersonly", &k, &v)) INSERT_KEY (keys, "members-only", G_TYPE_BOOLEAN, boolean, TRUE); if (g_hash_table_lookup_extended (item->features, "muc_open", &k, &v)) INSERT_KEY (keys, "members-only", G_TYPE_BOOLEAN, boolean, FALSE); if (g_hash_table_lookup_extended (item->features, "muc_moderated", &k, &v)) INSERT_KEY (keys, "moderated", G_TYPE_BOOLEAN, boolean, TRUE); if (g_hash_table_lookup_extended (item->features, "muc_unmoderated", &k, &v)) INSERT_KEY (keys, "moderated", G_TYPE_BOOLEAN, boolean, FALSE); if (g_hash_table_lookup_extended (item->features, "muc_nonanonymous", &k, &v)) INSERT_KEY (keys, "anonymous", G_TYPE_BOOLEAN, boolean, FALSE); if (g_hash_table_lookup_extended (item->features, "muc_anonymous", &k, &v)) INSERT_KEY (keys, "anonymous", G_TYPE_BOOLEAN, boolean, TRUE); if (g_hash_table_lookup_extended (item->features, "muc_semianonymous", &k, &v)) INSERT_KEY (keys, "anonymous", G_TYPE_BOOLEAN, boolean, FALSE); if (g_hash_table_lookup_extended (item->features, "muc_persistent", &k, &v)) INSERT_KEY (keys, "persistent", G_TYPE_BOOLEAN, boolean, TRUE); if (g_hash_table_lookup_extended (item->features, "muc_temporary", &k, &v)) INSERT_KEY (keys, "persistent", G_TYPE_BOOLEAN, boolean, FALSE); var = g_hash_table_lookup (item->features, "muc#roominfo_description"); if (var != NULL) INSERT_KEY (keys, "description", G_TYPE_STRING, string, var); var = g_hash_table_lookup (item->features, "muc#roominfo_occupants"); if (var != NULL) INSERT_KEY (keys, "members", G_TYPE_UINT, uint, (guint) g_ascii_strtoull (var, NULL, 10)); var = g_hash_table_lookup (item->features, "muc#roominfo_lang"); if (var != NULL) INSERT_KEY (keys, "language", G_TYPE_STRING, string, var); /* transfer the room handle ref to signalled_rooms */ tp_handle_set_add (priv->signalled_rooms, handle); g_value_init (&room, room_info_type); g_value_take_boxed (&room, dbus_g_type_specialized_construct (room_info_type)); dbus_g_type_struct_set (&room, 0, handle, 1, "org.freedesktop.Telepathy.Channel.Type.Text", 2, keys, G_MAXUINT); DEBUG ("adding new room signal data to pending: %s", jid); g_ptr_array_add (priv->pending_room_signals, g_value_get_boxed (&room)); g_hash_table_unref (keys); } static void rooms_end_cb (gpointer data, gpointer user_data) { GabbleRoomlistChannel *chan = user_data; GabbleRoomlistChannelPrivate *priv = chan->priv; emit_room_signal (chan); priv->listing = FALSE; tp_svc_channel_type_room_list_emit_listing_rooms ( (TpSvcChannelTypeRoomList *) chan, FALSE); g_source_remove (priv->timer_source_id); priv->timer_source_id = 0; } static void stop_listing (GabbleRoomlistChannel *self) { GabbleRoomlistChannelPrivate *priv = self->priv; if (priv->listing) { emit_room_signal (self); priv->listing = FALSE; tp_svc_channel_type_room_list_emit_listing_rooms ( (TpSvcChannelTypeRoomList *) self, FALSE); } if (priv->disco_pipeline != NULL) { gabble_disco_pipeline_destroy (priv->disco_pipeline); priv->disco_pipeline = NULL; } if (priv->timer_source_id) { g_source_remove (priv->timer_source_id); priv->timer_source_id = 0; } g_assert (priv->pending_room_signals->len == 0); } static void gabble_roomlist_channel_close (TpBaseChannel *base) { GabbleRoomlistChannel *self = GABBLE_ROOMLIST_CHANNEL (base); DEBUG ("called on %p", self); stop_listing (self); tp_base_channel_destroyed (base); } static void gabble_roomlist_channel_get_listing_rooms (TpSvcChannelTypeRoomList *iface, DBusGMethodInvocation *context) { GabbleRoomlistChannel *self = GABBLE_ROOMLIST_CHANNEL (iface); GabbleRoomlistChannelPrivate *priv; g_assert (GABBLE_IS_ROOMLIST_CHANNEL (self)); priv = self->priv; tp_svc_channel_type_room_list_return_from_get_listing_rooms ( context, priv->listing); } static void gabble_roomlist_channel_list_rooms (TpSvcChannelTypeRoomList *iface, DBusGMethodInvocation *context) { GabbleRoomlistChannel *self = GABBLE_ROOMLIST_CHANNEL (iface); GabbleRoomlistChannelPrivate *priv = self->priv; TpBaseChannel *base = TP_BASE_CHANNEL (self); GabbleConnection *conn = GABBLE_CONNECTION (tp_base_channel_get_connection (base)); priv->listing = TRUE; tp_svc_channel_type_room_list_emit_listing_rooms (iface, TRUE); if (priv->disco_pipeline == NULL) priv->disco_pipeline = gabble_disco_pipeline_init (conn->disco, room_info_cb, rooms_end_cb, self); gabble_disco_pipeline_run (priv->disco_pipeline, priv->conference_server); priv->timer_source_id = g_timeout_add (ROOM_SIGNAL_INTERVAL, emit_room_signal, self); tp_svc_channel_type_room_list_return_from_list_rooms (context); } static void gabble_roomlist_channel_stop_listing (TpSvcChannelTypeRoomList *iface, DBusGMethodInvocation *context) { GabbleRoomlistChannel *self = GABBLE_ROOMLIST_CHANNEL (iface); g_assert (GABBLE_IS_ROOMLIST_CHANNEL (self)); stop_listing (self); tp_svc_channel_type_room_list_return_from_stop_listing (context); } static void roomlist_iface_init (gpointer g_iface, gpointer iface_data) { TpSvcChannelTypeRoomListClass *klass = (TpSvcChannelTypeRoomListClass *) g_iface; #define IMPLEMENT(x) tp_svc_channel_type_room_list_implement_##x (\ klass, gabble_roomlist_channel_##x) IMPLEMENT(get_listing_rooms); IMPLEMENT(list_rooms); IMPLEMENT(stop_listing); #undef IMPLEMENT } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������telepathy-gabble-0.18.2/src/roomlist-channel.h������������������������������������������������������0000644�0001750�0001750�00000004735�12200204333�021231� 0����������������������������������������������������������������������������������������������������ustar�00smcv����������������������������smcv����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * gabble-roomlist-channel.h - Header for GabbleRoomlistChannel * Copyright (C) 2005 Collabora Ltd. * Copyright (C) 2005 Nokia Corporation * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 __GABBLE_ROOMLIST_CHANNEL_H__ #define __GABBLE_ROOMLIST_CHANNEL_H__ #include <glib-object.h> #include <telepathy-glib/telepathy-glib.h> #include "connection.h" G_BEGIN_DECLS typedef struct _GabbleRoomlistChannel GabbleRoomlistChannel; typedef struct _GabbleRoomlistChannelPrivate GabbleRoomlistChannelPrivate; typedef struct _GabbleRoomlistChannelClass GabbleRoomlistChannelClass; struct _GabbleRoomlistChannelClass { TpBaseChannelClass parent_class; }; struct _GabbleRoomlistChannel { TpBaseChannel parent; GabbleRoomlistChannelPrivate *priv; }; GType gabble_roomlist_channel_get_type (void); /* TYPE MACROS */ #define GABBLE_TYPE_ROOMLIST_CHANNEL \ (gabble_roomlist_channel_get_type ()) #define GABBLE_ROOMLIST_CHANNEL(obj) \ (G_TYPE_CHECK_INSTANCE_CAST((obj), GABBLE_TYPE_ROOMLIST_CHANNEL,\ GabbleRoomlistChannel)) #define GABBLE_ROOMLIST_CHANNEL_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST((klass), GABBLE_TYPE_ROOMLIST_CHANNEL,\ GabbleRoomlistChannelClass)) #define GABBLE_IS_ROOMLIST_CHANNEL(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE((obj), GABBLE_TYPE_ROOMLIST_CHANNEL)) #define GABBLE_IS_ROOMLIST_CHANNEL_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE((klass), GABBLE_TYPE_ROOMLIST_CHANNEL)) #define GABBLE_ROOMLIST_CHANNEL_GET_CLASS(obj) \ (G_TYPE_INSTANCE_GET_CLASS ((obj), GABBLE_TYPE_ROOMLIST_CHANNEL,\ GabbleRoomlistChannelClass)) GabbleRoomlistChannel *_gabble_roomlist_channel_new (GabbleConnection *conn, const gchar *conference_server); G_END_DECLS #endif /* #ifndef __GABBLE_ROOMLIST_CHANNEL_H__*/ �����������������������������������telepathy-gabble-0.18.2/src/room-config.c�����������������������������������������������������������0000644�0001750�0001750�00000006120�12200204333�020153� 0����������������������������������������������������������������������������������������������������ustar�00smcv����������������������������smcv����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * room-config.c - Channel.Interface.RoomConfig1 implementation * Copyright ©2011 Collabora Ltd. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 "config.h" #include "room-config.h" #include "muc-channel.h" #define DEBUG_FLAG GABBLE_DEBUG_MUC #include "debug.h" static void gabble_room_config_update_configuration_async ( TpBaseRoomConfig *base_config, GHashTable *validated_properties, GAsyncReadyCallback callback, gpointer user_data); struct _GabbleRoomConfigPrivate { gpointer hi_dere; }; G_DEFINE_TYPE (GabbleRoomConfig, gabble_room_config, TP_TYPE_BASE_ROOM_CONFIG) static void gabble_room_config_init (GabbleRoomConfig *self) { self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, GABBLE_TYPE_ROOM_CONFIG, GabbleRoomConfigPrivate); } static void gabble_room_config_class_init (GabbleRoomConfigClass *klass) { TpBaseRoomConfigClass *parent_class = TP_BASE_ROOM_CONFIG_CLASS (klass); parent_class->update_async = gabble_room_config_update_configuration_async; g_type_class_add_private (klass, sizeof (GabbleRoomConfigPrivate)); } GabbleRoomConfig * gabble_room_config_new ( TpBaseChannel *channel) { g_return_val_if_fail (TP_IS_BASE_CHANNEL (channel), NULL); return g_object_new (GABBLE_TYPE_ROOM_CONFIG, "channel", channel, NULL); } static void updated_configuration_cb ( GObject *source, GAsyncResult *result, gpointer user_data) { GabbleMucChannel *channel = GABBLE_MUC_CHANNEL (source); GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (user_data); GError *error = NULL; if (!gabble_muc_channel_update_configuration_finish (channel, result, &error)) { g_simple_async_result_set_from_error (simple, error); g_clear_error (&error); } g_simple_async_result_complete (simple); g_object_unref (simple); } static void gabble_room_config_update_configuration_async ( TpBaseRoomConfig *base_config, GHashTable *validated_properties, GAsyncReadyCallback callback, gpointer user_data) { TpBaseChannel *base_channel = tp_base_room_config_dup_channel (base_config); GSimpleAsyncResult *simple = g_simple_async_result_new ( G_OBJECT (base_config), callback, user_data, gabble_room_config_update_configuration_async); gabble_muc_channel_update_configuration_async ( GABBLE_MUC_CHANNEL (base_channel), validated_properties, updated_configuration_cb, simple); g_object_unref (base_channel); } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������telepathy-gabble-0.18.2/src/room-config.h�����������������������������������������������������������0000644�0001750�0001750�00000004233�12200204333�020163� 0����������������������������������������������������������������������������������������������������ustar�00smcv����������������������������smcv����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * room-config.h - header for Channel.I.RoomConfig1 implementation * Copyright ©2011 Collabora Ltd. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 GABBLE_ROOM_CONFIG_H #define GABBLE_ROOM_CONFIG_H #include <glib-object.h> #include <telepathy-glib/telepathy-glib.h> typedef struct _GabbleRoomConfig GabbleRoomConfig; typedef struct _GabbleRoomConfigClass GabbleRoomConfigClass; typedef struct _GabbleRoomConfigPrivate GabbleRoomConfigPrivate; struct _GabbleRoomConfigClass { TpBaseRoomConfigClass parent_class; }; struct _GabbleRoomConfig { TpBaseRoomConfig parent; GabbleRoomConfigPrivate *priv; }; GabbleRoomConfig *gabble_room_config_new ( TpBaseChannel *channel); /* TYPE MACROS */ GType gabble_room_config_get_type (void); #define GABBLE_TYPE_ROOM_CONFIG \ (gabble_room_config_get_type ()) #define GABBLE_ROOM_CONFIG(obj) \ (G_TYPE_CHECK_INSTANCE_CAST((obj), GABBLE_TYPE_ROOM_CONFIG, GabbleRoomConfig)) #define GABBLE_ROOM_CONFIG_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST((klass), GABBLE_TYPE_ROOM_CONFIG,\ GabbleRoomConfigClass)) #define GABBLE_IS_ROOM_CONFIG(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE((obj), GABBLE_TYPE_ROOM_CONFIG)) #define GABBLE_IS_ROOM_CONFIG_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE((klass), GABBLE_TYPE_ROOM_CONFIG)) #define GABBLE_ROOM_CONFIG_GET_CLASS(obj) \ (G_TYPE_INSTANCE_GET_CLASS ((obj), GABBLE_TYPE_ROOM_CONFIG, \ GabbleRoomConfigClass)) #endif /* GABBLE_ROOM_CONFIG_H */ ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������telepathy-gabble-0.18.2/src/roster.c����������������������������������������������������������������0000644�0001750�0001750�00000335266�12227000321�017272� 0����������������������������������������������������������������������������������������������������ustar�00smcv����������������������������smcv����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * roster.c - Source for Gabble roster helper * * Copyright © 2006–2010 Collabora Ltd. * Copyright © 2006–2010 Nokia Corporation * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 "config.h" #include "roster.h" #include <string.h> #include <dbus/dbus-glib.h> #include <telepathy-glib/telepathy-glib.h> #include <telepathy-glib/telepathy-glib-dbus.h> #include <wocky/wocky.h> #define DEBUG_FLAG GABBLE_DEBUG_ROSTER #include "gabble/caps-channel-manager.h" #include "conn-aliasing.h" #include "conn-presence.h" #include "conn-util.h" #include "connection.h" #include "debug.h" #include "namespaces.h" #include "presence-cache.h" #include "util.h" #define GOOGLE_ROSTER_VERSION "2" /* signal enum */ enum { NICKNAMES_UPDATE, LAST_SIGNAL }; static guint signals[LAST_SIGNAL] = { 0 }; struct _GabbleRosterPrivate { GabbleConnection *conn; gulong porter_available_id; gulong status_changed_id; GCancellable *cancel_on_disconnect; guint iq_cb; guint presence_cb; GHashTable *items; TpHandleSet *groups; /* set of contacts whose subscription requests will automatically be * accepted during this session */ TpHandleSet *pre_authorized; gboolean received; gboolean dispose_has_run; }; typedef enum { GOOGLE_ITEM_TYPE_INVALID = -1, GOOGLE_ITEM_TYPE_NORMAL = 0, GOOGLE_ITEM_TYPE_BLOCKED, GOOGLE_ITEM_TYPE_HIDDEN, GOOGLE_ITEM_TYPE_PINNED, } GoogleItemType; typedef enum { GABBLE_ROSTER_SUBSCRIPTION_NONE = 0, GABBLE_ROSTER_SUBSCRIPTION_FROM, GABBLE_ROSTER_SUBSCRIPTION_TO, GABBLE_ROSTER_SUBSCRIPTION_BOTH, GABBLE_ROSTER_SUBSCRIPTION_REMOVE, GABBLE_ROSTER_SUBSCRIPTION_INVALID, } GabbleRosterSubscription; typedef struct _GabbleRosterItemEdit GabbleRosterItemEdit; struct _GabbleRosterItemEdit { TpHandleRepoIface *contact_repo; TpHandle handle; /* if TRUE, we must create this roster item, so send the IQ even if we * don't appear to be changing anything */ gboolean create; /* list of reffed GSimpleAsyncResult */ GSList *results; /* if these are ..._INVALID, that means don't edit */ GabbleRosterSubscription new_subscription; GoogleItemType new_google_type; /* owned by the GabbleRosterItemEdit. If NULL, that means don't edit... */ gchar *new_name; /* if TRUE, disregard new_name and remove name='' from the roster item */ gboolean remove_name; TpHandleSet *add_to_groups; TpHandleSet *remove_from_groups; gboolean remove_from_all_other_groups; }; typedef struct _GabbleRosterItem GabbleRosterItem; struct _GabbleRosterItem { GabbleRosterSubscription subscription; gboolean ask_subscribe; GoogleItemType google_type; gchar *name; gchar *alias_for; TpHandleSet *groups; /* if TRUE, an edit attempt is already "in-flight" so we can't send off * edits immediately - instead, store them in unsent_edits */ gboolean edits_in_flight; GabbleRosterItemEdit *unsent_edits; /* Might not match @subscription and @ask_subscribe exactly, in cases where * we're working around server breakage */ TpSubscriptionState subscribe; TpSubscriptionState publish; gchar *publish_request; gboolean stored; gboolean blocked; /* If non-zero, the GSource id for a call to flicker_prevention_timeout. */ guint flicker_prevention_id; }; typedef struct _FlickerPreventionCtx FlickerPreventionCtx; struct _FlickerPreventionCtx { GabbleRoster *roster; TpHandle handle; GabbleRosterItem *item; }; static void roster_item_cancel_flicker_timeout (GabbleRosterItem *item); static void _gabble_roster_item_free (GabbleRosterItem *item); static void item_edit_free (GabbleRosterItemEdit *edits); static void gabble_roster_close_all (GabbleRoster *roster); static void mutable_iface_init (TpMutableContactListInterface *iface); static void blockable_iface_init (TpBlockableContactListInterface *iface); static void contact_groups_iface_init (TpContactGroupListInterface *iface); static void mutable_contact_groups_iface_init ( TpMutableContactGroupListInterface *iface); G_DEFINE_TYPE_WITH_CODE (GabbleRoster, gabble_roster, TP_TYPE_BASE_CONTACT_LIST, G_IMPLEMENT_INTERFACE (TP_TYPE_MUTABLE_CONTACT_LIST, mutable_iface_init); G_IMPLEMENT_INTERFACE (TP_TYPE_CONTACT_GROUP_LIST, contact_groups_iface_init); G_IMPLEMENT_INTERFACE (TP_TYPE_MUTABLE_CONTACT_GROUP_LIST, mutable_contact_groups_iface_init); G_IMPLEMENT_INTERFACE (TP_TYPE_BLOCKABLE_CONTACT_LIST, blockable_iface_init); G_IMPLEMENT_INTERFACE (GABBLE_TYPE_CAPS_CHANNEL_MANAGER, NULL)) static void gabble_roster_init (GabbleRoster *obj) { GabbleRosterPrivate *priv = G_TYPE_INSTANCE_GET_PRIVATE (obj, GABBLE_TYPE_ROSTER, GabbleRosterPrivate); obj->priv = priv; priv->items = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, (GDestroyNotify) _gabble_roster_item_free); } static void gabble_roster_dispose (GObject *object) { GabbleRoster *self = GABBLE_ROSTER (object); GabbleRosterPrivate *priv = self->priv; if (priv->dispose_has_run) return; DEBUG ("dispose called"); priv->dispose_has_run = TRUE; g_assert (priv->iq_cb == 0); g_assert (priv->presence_cb == 0); gabble_roster_close_all (self); g_assert (priv->groups == NULL); g_assert (priv->pre_authorized == NULL); if (G_OBJECT_CLASS (gabble_roster_parent_class)->dispose) G_OBJECT_CLASS (gabble_roster_parent_class)->dispose (object); } static void gabble_roster_finalize (GObject *object) { GabbleRoster *self = GABBLE_ROSTER (object); GabbleRosterPrivate *priv = self->priv; DEBUG ("called with %p", object); g_hash_table_unref (priv->items); G_OBJECT_CLASS (gabble_roster_parent_class)->finalize (object); } static void _gabble_roster_item_free (GabbleRosterItem *item) { g_assert (item != NULL); tp_handle_set_destroy (item->groups); item_edit_free (item->unsent_edits); g_free (item->name); g_free (item->alias_for); g_free (item->publish_request); roster_item_cancel_flicker_timeout (item); g_slice_free (GabbleRosterItem, item); } static const gchar * _subscription_to_string (GabbleRosterSubscription subscription) { switch (subscription) { case GABBLE_ROSTER_SUBSCRIPTION_NONE: return "none"; case GABBLE_ROSTER_SUBSCRIPTION_FROM: return "from"; case GABBLE_ROSTER_SUBSCRIPTION_TO: return "to"; case GABBLE_ROSTER_SUBSCRIPTION_BOTH: return "both"; case GABBLE_ROSTER_SUBSCRIPTION_REMOVE: return "remove"; default: g_assert_not_reached (); return NULL; } } static GabbleRosterSubscription _parse_item_subscription (WockyNode *item_node) { const gchar *subscription; g_assert (item_node != NULL); subscription = wocky_node_get_attribute (item_node, "subscription"); if (NULL == subscription || 0 == strcmp (subscription, "none")) return GABBLE_ROSTER_SUBSCRIPTION_NONE; else if (0 == strcmp (subscription, "from")) return GABBLE_ROSTER_SUBSCRIPTION_FROM; else if (0 == strcmp (subscription, "to")) return GABBLE_ROSTER_SUBSCRIPTION_TO; else if (0 == strcmp (subscription, "both")) return GABBLE_ROSTER_SUBSCRIPTION_BOTH; else if (0 == strcmp (subscription, "remove")) return GABBLE_ROSTER_SUBSCRIPTION_REMOVE; else { NODE_DEBUG (item_node, "got unexpected subscription value"); return GABBLE_ROSTER_SUBSCRIPTION_NONE; } } static TpHandleSet * _parse_item_groups (WockyNode *item_node, TpBaseConnection *conn) { TpHandleRepoIface *group_repo = tp_base_connection_get_handles ( conn, TP_HANDLE_TYPE_GROUP); TpHandleSet *groups = tp_handle_set_new (group_repo); TpHandle handle; WockyNodeIter i; WockyNode *group_node; wocky_node_iter_init (&i, item_node, "group", NULL); while (wocky_node_iter_next (&i, &group_node)) { const gchar *value = group_node->content; if (NULL == value) continue; handle = tp_handle_ensure (group_repo, value, NULL, NULL); if (!handle) continue; tp_handle_set_add (groups, handle); } return groups; } static const gchar * _google_item_type_to_string (GoogleItemType google_type) { switch (google_type) { case GOOGLE_ITEM_TYPE_NORMAL: return NULL; case GOOGLE_ITEM_TYPE_BLOCKED: return "B"; case GOOGLE_ITEM_TYPE_HIDDEN: return "H"; case GOOGLE_ITEM_TYPE_PINNED: return "P"; case GOOGLE_ITEM_TYPE_INVALID: g_assert_not_reached (); return NULL; } g_assert_not_reached (); return NULL; } static GoogleItemType _parse_google_item_type (WockyNode *item_node) { const gchar *google_type; g_assert (item_node != NULL); google_type = wocky_node_get_attribute_ns (item_node, "t", NS_GOOGLE_ROSTER); if (NULL == google_type) return GOOGLE_ITEM_TYPE_NORMAL; else if (!tp_strdiff (google_type, "B")) return GOOGLE_ITEM_TYPE_BLOCKED; else if (!tp_strdiff (google_type, "H")) return GOOGLE_ITEM_TYPE_HIDDEN; else if (!tp_strdiff (google_type, "P")) return GOOGLE_ITEM_TYPE_PINNED; NODE_DEBUG (item_node, "got unexpected google contact type value"); return GOOGLE_ITEM_TYPE_NORMAL; } static gchar * _extract_google_alias_for (WockyNode *item_node) { return g_strdup (wocky_node_get_attribute_ns (item_node, "alias-for", NS_GOOGLE_ROSTER)); } static gboolean _google_roster_item_should_keep (const gchar *jid, GabbleRosterItem *item) { /* hide hidden items */ if (item->google_type == GOOGLE_ITEM_TYPE_HIDDEN) { DEBUG ("hiding %s: gr:t='H'", jid); return FALSE; } /* allow items that we've requested a subscription from */ if (item->ask_subscribe) return TRUE; if (item->subscription != GABBLE_ROSTER_SUBSCRIPTION_NONE && item->subscription != GABBLE_ROSTER_SUBSCRIPTION_REMOVE) return TRUE; /* discard anything else */ DEBUG ("hiding %s: no subscription", jid); return FALSE; } static GabbleRosterItem * _gabble_roster_item_lookup (GabbleRoster *roster, TpHandle handle) { GabbleRosterPrivate *priv = roster->priv; g_assert (roster != NULL); g_assert (GABBLE_IS_ROSTER (roster)); return g_hash_table_lookup (priv->items, GUINT_TO_POINTER (handle)); } static GabbleRosterItem * _gabble_roster_item_ensure (GabbleRoster *roster, TpHandle handle) { GabbleRosterPrivate *priv = roster->priv; TpHandleRepoIface *group_repo = tp_base_connection_get_handles ( (TpBaseConnection *) priv->conn, TP_HANDLE_TYPE_GROUP); TpHandleRepoIface *contact_repo = tp_base_connection_get_handles ( (TpBaseConnection *) priv->conn, TP_HANDLE_TYPE_CONTACT); GabbleRosterItem *item; item = _gabble_roster_item_lookup (roster, handle); g_assert (tp_handle_is_valid (contact_repo, handle, NULL)); if (NULL == item) { GabbleConnectionAliasSource source; gchar *alias = NULL; source = _gabble_connection_get_cached_alias (priv->conn, handle, &alias); g_assert (source < GABBLE_CONNECTION_ALIAS_FROM_ROSTER); if (source <= GABBLE_CONNECTION_ALIAS_FROM_JID) { g_free (alias); alias = NULL; } item = g_slice_new0 (GabbleRosterItem); /* We may want it to be on the roster, but it's not there yet, so the * most accurate description of how to reach this state is "remove". * We'll keep this transient roster item for as long as it has edits * pending, or some other reason to stay around (a pending publish * request, for instance). */ item->subscription = GABBLE_ROSTER_SUBSCRIPTION_REMOVE; item->subscribe = TP_SUBSCRIPTION_STATE_NO; item->publish = TP_SUBSCRIPTION_STATE_NO; item->name = alias; item->groups = tp_handle_set_new (group_repo); g_hash_table_insert (priv->items, GUINT_TO_POINTER (handle), item); } return item; } static gboolean _gabble_roster_item_maybe_remove (GabbleRoster *roster, TpHandle handle) { GabbleRosterPrivate *priv = roster->priv; TpHandleRepoIface *contact_repo = tp_base_connection_get_handles ( (TpBaseConnection *) priv->conn, TP_HANDLE_TYPE_CONTACT); GabbleRosterItem *item; g_assert (roster != NULL); g_assert (GABBLE_IS_ROSTER (roster)); g_assert (tp_handle_is_valid (contact_repo, handle, NULL)); item = _gabble_roster_item_lookup (roster, handle); g_return_val_if_fail (item != NULL, FALSE); /* don't remove items that are really on our server-side roster */ if (item->subscription != GABBLE_ROSTER_SUBSCRIPTION_REMOVE) { DEBUG ("contact#%u is still on the roster", handle); return FALSE; } /* don't remove items that have edits in flight */ if (item->edits_in_flight) { DEBUG ("contact#%u has edits in flight", handle); return FALSE; } /* don't remove transient items that represent publish/subscribe state */ if (item->publish != TP_SUBSCRIPTION_STATE_NO) { DEBUG ("contact#%u has publish=%u", handle, item->publish); return FALSE; } if (item->subscribe != TP_SUBSCRIPTION_STATE_NO) { DEBUG ("contact#%u has subscribe=%u", handle, item->subscribe); return FALSE; } DEBUG ("removing contact#%u", handle); item = NULL; g_hash_table_remove (priv->items, GUINT_TO_POINTER (handle)); return TRUE; } static GabbleRosterItem * _gabble_roster_item_update (GabbleRoster *roster, TpHandle contact_handle, WockyNode *node, gboolean google_roster_mode, gboolean *nickname_updated) { GabbleRosterPrivate *priv = roster->priv; GabbleRosterItem *item; const gchar *ask, *name; TpIntset *new_groups, *added_to, *removed_from, *removed_from2; TpHandleSet *new_groups_handle_set, *old_groups; TpBaseContactList *base = (TpBaseContactList *) roster; TpHandleRepoIface *contact_repo = tp_base_connection_get_handles ( (TpBaseConnection *) priv->conn, TP_HANDLE_TYPE_CONTACT); TpHandleRepoIface *group_repo = tp_base_connection_get_handles ( (TpBaseConnection *) priv->conn, TP_HANDLE_TYPE_GROUP); g_assert (roster != NULL); g_assert (GABBLE_IS_ROSTER (roster)); g_assert (tp_handle_is_valid (contact_repo, contact_handle, NULL)); g_assert (node != NULL); item = _gabble_roster_item_ensure (roster, contact_handle); item->subscription = _parse_item_subscription (node); ask = wocky_node_get_attribute (node, "ask"); if (NULL != ask && 0 == strcmp (ask, "subscribe")) item->ask_subscribe = TRUE; else item->ask_subscribe = FALSE; if (google_roster_mode) { item->google_type = _parse_google_item_type (node); g_free (item->alias_for); item->alias_for = _extract_google_alias_for (node); } if (item->subscription == GABBLE_ROSTER_SUBSCRIPTION_REMOVE) name = NULL; else name = wocky_node_get_attribute (node, "name"); if (tp_strdiff (item->name, name)) { g_free (item->name); item->name = g_strdup (name); DEBUG ("name for contact#%u changed to %s", contact_handle, name); *nickname_updated = TRUE; } else { *nickname_updated = FALSE; } new_groups_handle_set = _parse_item_groups (node, (TpBaseConnection *) priv->conn); new_groups = tp_handle_set_peek (new_groups_handle_set); old_groups = tp_handle_set_copy (item->groups); removed_from = tp_intset_difference (tp_handle_set_peek (item->groups), new_groups); added_to = tp_handle_set_update (item->groups, new_groups); removed_from2 = tp_handle_set_difference_update (item->groups, removed_from); if (roster->priv->groups != NULL) { TpIntset *created_groups = tp_handle_set_update (roster->priv->groups, new_groups); /* we don't need to do this work if TpBaseContactList will just be * ignoring it, as it will before we've received the roster */ if (tp_base_contact_list_get_state ((TpBaseContactList *) roster, NULL) == TP_CONTACT_LIST_STATE_SUCCESS && !tp_intset_is_empty (created_groups)) { GPtrArray *strv = g_ptr_array_sized_new (tp_intset_size ( created_groups)); TpIntsetFastIter iter; TpHandle group; tp_intset_fast_iter_init (&iter, created_groups); while (tp_intset_fast_iter_next (&iter, &group)) { const gchar *group_name = tp_handle_inspect (group_repo, group); DEBUG ("Group was just created: #%u '%s'", group, group_name); g_ptr_array_add (strv, (gchar *) group_name); } tp_base_contact_list_groups_created ((TpBaseContactList *) roster, (const gchar * const *) strv->pdata, strv->len); g_ptr_array_unref (strv); } tp_clear_pointer (&created_groups, tp_intset_destroy); } /* We emit one GroupsChanged signal per contact, because that's most natural * for XMPP, where we usually get a roster push for a single contact. * * The exception is when we're receiving our initial roster, but until we do * that we don't need to emit GroupsChanged anyway; TpBaseContactList will * recover state. */ if (tp_base_contact_list_get_state (base, NULL) == TP_CONTACT_LIST_STATE_SUCCESS && (!tp_intset_is_empty (added_to) || !tp_intset_is_empty (removed_from))) { GPtrArray *added_names = g_ptr_array_sized_new (tp_intset_size (added_to)); GPtrArray *removed_names = g_ptr_array_sized_new ( tp_intset_size (removed_from)); TpHandleSet *the_contact = tp_handle_set_new (contact_repo); TpIntsetFastIter iter; TpHandle group; tp_handle_set_add (the_contact, contact_handle); tp_intset_fast_iter_init (&iter, added_to); while (tp_intset_fast_iter_next (&iter, &group)) { const gchar *group_name = tp_handle_inspect (group_repo, group); DEBUG ("Contact #%u added to group #%u '%s'", contact_handle, group, group_name); g_ptr_array_add (added_names, (gchar *) group_name); } tp_intset_fast_iter_init (&iter, removed_from); while (tp_intset_fast_iter_next (&iter, &group)) { const gchar *group_name = tp_handle_inspect (group_repo, group); DEBUG ("Contact #%u removed from group #%u '%s'", contact_handle, group, group_name); g_ptr_array_add (removed_names, (gchar *) group_name); } tp_base_contact_list_groups_changed ((TpBaseContactList *) roster, the_contact, (const gchar * const *) added_names->pdata, added_names->len, (const gchar * const *) removed_names->pdata, removed_names->len); } tp_intset_destroy (added_to); tp_intset_destroy (removed_from); tp_intset_destroy (removed_from2); new_groups = NULL; tp_handle_set_destroy (new_groups_handle_set); tp_handle_set_destroy (old_groups); return item; } #ifdef ENABLE_DEBUG static void _gabble_roster_item_dump_group (guint handle, gpointer user_data) { g_string_append_printf ((GString *) user_data, "group#%u ", handle); } static gchar * _gabble_roster_item_dump (GabbleRosterItem *item) { GString *str; g_assert (item != NULL); str = g_string_new ("subscription: "); g_string_append (str, _subscription_to_string (item->subscription)); if (item->ask_subscribe) g_string_append (str, ", ask: subscribe"); if (item->google_type != GOOGLE_ITEM_TYPE_NORMAL) g_string_append_printf (str, ", google_type: %s", _google_item_type_to_string (item->google_type)); if (item->name) g_string_append_printf (str, ", name: %s", item->name); if (item->groups) { tp_intset_foreach (tp_handle_set_peek (item->groups), _gabble_roster_item_dump_group, str); } return g_string_free (str, FALSE); } #endif /* ENABLE_DEBUG */ static WockyStanza * _gabble_roster_message_new (GabbleRoster *roster, WockyStanzaSubType sub_type, WockyNode **query_return) { GabbleRosterPrivate *priv = roster->priv; WockyStanza *message; WockyNode *query_node; g_assert (roster != NULL); g_assert (GABBLE_IS_ROSTER (roster)); message = wocky_stanza_build (WOCKY_STANZA_TYPE_IQ, sub_type, NULL, NULL, '(', "query", ':', WOCKY_XMPP_NS_ROSTER, '*', &query_node, ')', NULL); if (query_return != NULL) *query_return = query_node; if (priv->conn->features & GABBLE_CONNECTION_FEATURES_GOOGLE_ROSTER) { GQuark gr = g_quark_from_static_string (NS_GOOGLE_ROSTER); wocky_node_attribute_ns_set_prefix (gr, "gr"); wocky_node_set_attribute_ns (query_node, "ext", GOOGLE_ROSTER_VERSION, NS_GOOGLE_ROSTER); wocky_node_set_attribute_ns (query_node, "include", "all", NS_GOOGLE_ROSTER); } return message; } struct _ItemToMessageContext { TpBaseConnection *conn; WockyNode *item_node; }; static void _gabble_roster_item_put_group_in_message (guint handle, gpointer user_data) { struct _ItemToMessageContext *ctx = (struct _ItemToMessageContext *)user_data; TpHandleRepoIface *group_repo = tp_base_connection_get_handles ( ctx->conn, TP_HANDLE_TYPE_GROUP); const char *name = tp_handle_inspect (group_repo, handle); wocky_node_add_child_with_content (ctx->item_node, "group", name); } /* * _gabble_roster_item_to_message: * @roster: the roster * @item: the state we would like the contact's roster item to have (*not* * the state it currently has!) * @handle: a contact * * Returns: the necessary IQ to change @handle's state to match that of @item */ static WockyStanza * _gabble_roster_item_to_message (GabbleRoster *roster, TpHandle handle, GabbleRosterItem *item) { GabbleRosterPrivate *priv = roster->priv; TpHandleRepoIface *contact_repo = tp_base_connection_get_handles ( (TpBaseConnection *) priv->conn, TP_HANDLE_TYPE_CONTACT); WockyStanza *message; WockyNode *query_node, *item_node; const gchar *jid; struct _ItemToMessageContext ctx = { (TpBaseConnection *) priv->conn, }; g_assert (roster != NULL); g_assert (GABBLE_IS_ROSTER (roster)); g_assert (tp_handle_is_valid (contact_repo, handle, NULL)); g_assert (item != NULL); message = _gabble_roster_message_new (roster, WOCKY_STANZA_SUB_TYPE_SET, &query_node); item_node = wocky_node_add_child (query_node, "item"); ctx.item_node = item_node; jid = tp_handle_inspect (contact_repo, handle); wocky_node_set_attribute (item_node, "jid", jid); if (item->subscription != GABBLE_ROSTER_SUBSCRIPTION_NONE) { const gchar *subscription = _subscription_to_string (item->subscription); wocky_node_set_attribute (item_node, "subscription", subscription); } if (item->subscription == GABBLE_ROSTER_SUBSCRIPTION_REMOVE) goto DONE; if ((priv->conn->features & GABBLE_CONNECTION_FEATURES_GOOGLE_ROSTER) && item->google_type != GOOGLE_ITEM_TYPE_NORMAL) { GQuark gr = g_quark_from_static_string (NS_GOOGLE_ROSTER); wocky_node_attribute_ns_set_prefix (gr, "gr"); wocky_node_set_attribute_ns (item_node, "t", _google_item_type_to_string (item->google_type), NS_GOOGLE_ROSTER); } if (item->ask_subscribe) wocky_node_set_attribute (item_node, "ask", "subscribe"); if (item->name) wocky_node_set_attribute (item_node, "name", item->name); if (item->groups) { tp_intset_foreach (tp_handle_set_peek (item->groups), _gabble_roster_item_put_group_in_message, (void *)&ctx); } DONE: return message; } static FlickerPreventionCtx * flicker_prevention_ctx_new (GabbleRoster *roster, TpHandle handle, GabbleRosterItem *item) { FlickerPreventionCtx *ret = g_slice_new (FlickerPreventionCtx); /* Not taking a ref to the roster. The context is owned by the Item (well, by * its timeout) which is owned by the roster. */ ret->roster = roster; /* Not taking a ref to the handle; we borrow the roster's ref, which is * released after the GabbleRosterItem is freed, at which point this context * will be destroyed. */ ret->handle = handle; ret->item = item; return ret; } static void flicker_prevention_ctx_free (gpointer ctx_) { FlickerPreventionCtx *ctx = ctx_; ctx->roster = NULL; ctx->handle = 0; ctx->item = NULL; g_slice_free (FlickerPreventionCtx, ctx); } /* As described in roster/test-google-roster.py, we work around a Google Talk * server bug to avoid contacts flickering off and onto * subscribe:remote-pending when you try to subscribe to someone's presence. * * When we see a roster item with subscription=none/from and ask=subscribe: * * if no call to this function is scheduled, we schedule a call * * if one is already scheduled, we cancel it. * * When we see a roster item with subscription=none/from and no ask=subscribe: * * if a call to this timeout is scheduled, do nothing, in case the contact * flickers back to ask=subscribe before this fires; * * if a call to this timeout is not scheduled, remove the contact from the * subscribe list. * * This way, our subscription being cancelled or our subscription requests * being rescinded will show up on the subscribe list, albeit with a slight lag * in certain situations in case we're just seeing the Google talk server bug. */ static gboolean flicker_prevention_timeout (gpointer ctx_) { FlickerPreventionCtx *ctx = ctx_; GabbleRosterItem *item = ctx->item; DEBUG ("called for %u", ctx->handle); if ((item->subscription == GABBLE_ROSTER_SUBSCRIPTION_REMOVE || item->subscription == GABBLE_ROSTER_SUBSCRIPTION_NONE) && !item->ask_subscribe) { TpHandleRepoIface *contact_repo = tp_base_connection_get_handles ( (TpBaseConnection *) ctx->roster->priv->conn, TP_HANDLE_TYPE_CONTACT); TpHandleSet *rem = tp_handle_set_new (contact_repo); DEBUG ("removing %u from subscribe", ctx->handle); item->subscribe = TP_SUBSCRIPTION_STATE_NO; tp_handle_set_add (rem, ctx->handle); tp_base_contact_list_contacts_changed ((TpBaseContactList *) ctx->roster, rem, NULL); tp_handle_set_destroy (rem); } else { DEBUG ("subscription=%s and ask_subscribe=%s, nothing to do", _subscription_to_string (item->subscription), item->ask_subscribe ? "true" : "false"); } ctx->item->flicker_prevention_id = 0; return FALSE; } static void roster_item_ensure_flicker_timeout (GabbleRoster *roster, TpHandle handle, GabbleRosterItem *item) { if (item->flicker_prevention_id == 0) { FlickerPreventionCtx *ctx = flicker_prevention_ctx_new (roster, handle, item); item->flicker_prevention_id = g_timeout_add_seconds_full ( G_PRIORITY_DEFAULT, 1, flicker_prevention_timeout, ctx, flicker_prevention_ctx_free); } } static void roster_item_cancel_flicker_timeout (GabbleRosterItem *item) { if (item->flicker_prevention_id != 0) { g_source_remove (item->flicker_prevention_id); item->flicker_prevention_id = 0; } } static gboolean roster_item_set_publish (GabbleRosterItem *item, TpSubscriptionState publish, const gchar *request) { gboolean changed = FALSE; g_assert (publish == TP_SUBSCRIPTION_STATE_ASK || request == NULL); if (item->publish != publish) changed = TRUE; item->publish = publish; if (tp_strdiff (item->publish_request, request)) { changed = TRUE; g_free (item->publish_request); item->publish_request = g_strdup (request); } return changed; } static gboolean roster_item_set_subscribe (GabbleRosterItem *item, TpSubscriptionState subscribe) { if (item->subscribe != subscribe) { item->subscribe = subscribe; return TRUE; } return FALSE; } static gboolean is_google_roster_push ( GabbleRoster *roster, WockyNode *query_node) { if (roster->priv->conn->features & GABBLE_CONNECTION_FEATURES_GOOGLE_ROSTER) { const char *gr_ext = wocky_node_get_attribute_ns ( query_node, "ext", NS_GOOGLE_ROSTER); if (!tp_strdiff (gr_ext, GOOGLE_ROSTER_VERSION)) return TRUE; } return FALSE; } /** * validate_roster_item: * @contact_repo: the handle repository for contacts * @item_node: an <item> child of a <query xmlns='jabber:iq:roster'> * @jid_out: location at which to store the roster item's jid, borrowed from * @item_node, if the item is valid. * * Returns: a reference to a handle for the roster item, or 0 if the item seems * to be malformed. */ static TpHandle validate_roster_item ( TpHandleRepoIface *contact_repo, WockyNode *item_node, const gchar **jid_out) { const gchar *jid; TpHandle handle; jid = wocky_node_get_attribute (item_node, "jid"); if (!jid) { NODE_DEBUG (item_node, "item node has no jid, skipping"); return 0; } if (strchr (jid, '/') != NULL) { /* Avoid fd.o #12791 */ NODE_DEBUG (item_node, "item node has resource in jid, skipping"); return 0; } handle = tp_handle_ensure (contact_repo, jid, NULL, NULL); if (handle == 0) { NODE_DEBUG (item_node, "item jid is malformed, skipping"); return 0; } *jid_out = jid; return handle; } /* * process_roster: * @roster: a roster object * @query_node: a <query xmlns='jabber:iq:roster'/> node * * Processes an incoming roster push. */ static void process_roster ( GabbleRoster *roster, WockyNode *query_node) { GabbleRosterPrivate *priv = roster->priv; TpBaseConnection *conn = (TpBaseConnection *) priv->conn; TpHandleRepoIface *contact_repo = tp_base_connection_get_handles (conn, TP_HANDLE_TYPE_CONTACT); GArray *updated_nicknames = g_array_new (FALSE, FALSE, sizeof (TpHandle)); /* asymmetry is because we don't get locally pending subscription * requests via <roster>, we get it via <presence> */ TpHandleSet *changed = tp_handle_set_new (contact_repo); TpHandleSet *removed = tp_handle_set_new (contact_repo); /* We may not have a deny list */ TpHandleSet *blocking_changed; TpHandleSet *referenced_handles = tp_handle_set_new (contact_repo); gboolean google_roster = is_google_roster_push (roster, query_node); WockyNodeIter j; WockyNode *item_node; if (google_roster) blocking_changed = tp_handle_set_new (contact_repo); else blocking_changed = NULL; /* iterate every <item> sub-node */ wocky_node_iter_init (&j, query_node, "item", NULL); while (wocky_node_iter_next (&j, &item_node)) { const char *jid; TpHandle handle; GabbleRosterItem *item; gboolean nickname_updated; handle = validate_roster_item (contact_repo, item_node, &jid); if (handle == 0) continue; /* transfer ownership of the reference to referenced_handles */ tp_handle_set_add (referenced_handles, handle); item = _gabble_roster_item_update (roster, handle, item_node, google_roster, &nickname_updated); #ifdef ENABLE_DEBUG if (DEBUGGING) { gchar *dump = _gabble_roster_item_dump (item); DEBUG ("jid: %s, %s", jid, dump); g_free (dump); } #endif if (nickname_updated) g_array_append_val (updated_nicknames, handle); /* handle publish list changes */ switch (item->subscription) { case GABBLE_ROSTER_SUBSCRIPTION_FROM: case GABBLE_ROSTER_SUBSCRIPTION_BOTH: if (google_roster && !_google_roster_item_should_keep (jid, item)) { if (roster_item_set_publish (item, TP_SUBSCRIPTION_STATE_NO, NULL)) tp_handle_set_add (changed, handle); } else { if (roster_item_set_publish (item, TP_SUBSCRIPTION_STATE_YES, NULL)) tp_handle_set_add (changed, handle); } break; case GABBLE_ROSTER_SUBSCRIPTION_NONE: case GABBLE_ROSTER_SUBSCRIPTION_TO: case GABBLE_ROSTER_SUBSCRIPTION_REMOVE: /* publish channel is a bit odd, the roster item doesn't tell us * if someone is awaiting our approval - we get this via presence * type=subscribe, so we have to not remove them if they're * already local_pending in our publish channel. NO -> NO is a * no-op, so YES -> NO is the only case left. */ if (item->publish == TP_SUBSCRIPTION_STATE_YES) { tp_handle_set_add (changed, handle); roster_item_set_publish (item, TP_SUBSCRIPTION_STATE_NO, NULL); } break; default: g_assert_not_reached (); } /* handle subscribe list changes */ switch (item->subscription) { case GABBLE_ROSTER_SUBSCRIPTION_TO: case GABBLE_ROSTER_SUBSCRIPTION_BOTH: if (google_roster && !_google_roster_item_should_keep (jid, item)) { if (roster_item_set_subscribe (item, TP_SUBSCRIPTION_STATE_NO)) tp_handle_set_add (changed, handle); } else { if (roster_item_set_subscribe (item, TP_SUBSCRIPTION_STATE_YES)) tp_handle_set_add (changed, handle); } roster_item_cancel_flicker_timeout (item); break; case GABBLE_ROSTER_SUBSCRIPTION_NONE: case GABBLE_ROSTER_SUBSCRIPTION_FROM: if (item->ask_subscribe) { if (item->subscribe == TP_SUBSCRIPTION_STATE_YES) { DEBUG ("not letting gtalk demote member %u to pending", handle); } else { if (item->flicker_prevention_id == 0) roster_item_ensure_flicker_timeout (roster, handle, item); else roster_item_cancel_flicker_timeout (item); if (roster_item_set_subscribe (item, TP_SUBSCRIPTION_STATE_ASK)) tp_handle_set_add (changed, handle); } } else if (item->flicker_prevention_id == 0) { /* We're not expecting this contact's ask=subscribe to * flicker off and on again, so let's remove them immediately. */ if (roster_item_set_subscribe (item, TP_SUBSCRIPTION_STATE_NO)) tp_handle_set_add (changed, handle); } else { DEBUG ("delaying removal of %s from pending", jid); } break; case GABBLE_ROSTER_SUBSCRIPTION_REMOVE: if (roster_item_set_subscribe (item, TP_SUBSCRIPTION_STATE_NO)) tp_handle_set_add (changed, handle); break; default: g_assert_not_reached (); } /* handle stored list changes */ switch (item->subscription) { case GABBLE_ROSTER_SUBSCRIPTION_NONE: case GABBLE_ROSTER_SUBSCRIPTION_TO: case GABBLE_ROSTER_SUBSCRIPTION_FROM: case GABBLE_ROSTER_SUBSCRIPTION_BOTH: if (google_roster && /* Don't hide contacts from stored if they're pending. * This works around two Google Talk issues: * - When you try to subscribe to someone, you get a flickering * ask="subscribe"; * - When somebody tries to subscribe to you, you get a presence * with type="subscribe" followed by a roster update with * subscribe="none". * See test-google-roster.py for more details. */ item->subscribe != TP_SUBSCRIPTION_STATE_ASK && item->publish != TP_SUBSCRIPTION_STATE_ASK && !_google_roster_item_should_keep (jid, item)) { tp_handle_set_remove (changed, handle); tp_handle_set_add (removed, handle); item->stored = FALSE; } else { if (!item->stored) tp_handle_set_add (changed, handle); item->stored = TRUE; } break; case GABBLE_ROSTER_SUBSCRIPTION_REMOVE: tp_handle_set_remove (changed, handle); if (item->stored) tp_handle_set_add (removed, handle); item->stored = FALSE; break; default: g_assert_not_reached (); } /* handle deny list changes */ if (google_roster) { switch (item->subscription) { case GABBLE_ROSTER_SUBSCRIPTION_NONE: case GABBLE_ROSTER_SUBSCRIPTION_TO: case GABBLE_ROSTER_SUBSCRIPTION_FROM: case GABBLE_ROSTER_SUBSCRIPTION_BOTH: if (item->google_type == GOOGLE_ITEM_TYPE_BLOCKED) { if (!item->blocked) tp_handle_set_add (blocking_changed, handle); item->blocked = TRUE; } else { if (item->blocked) tp_handle_set_add (blocking_changed, handle); item->blocked = FALSE; } break; case GABBLE_ROSTER_SUBSCRIPTION_REMOVE: if (item->blocked) tp_handle_set_add (blocking_changed, handle); item->blocked = FALSE; break; default: g_assert_not_reached (); } } _gabble_roster_item_maybe_remove (roster, handle); } if (updated_nicknames->len > 0) g_signal_emit (roster, signals[NICKNAMES_UPDATE], 0, updated_nicknames); tp_base_contact_list_contacts_changed ((TpBaseContactList *) roster, changed, removed); if (google_roster) { tp_base_contact_list_contact_blocking_changed ( (TpBaseContactList *) roster, blocking_changed); tp_handle_set_destroy (blocking_changed); } g_array_unref (updated_nicknames); tp_handle_set_destroy (changed); tp_handle_set_destroy (removed); tp_handle_set_destroy (referenced_handles); } static void roster_item_apply_edits (GabbleRoster *roster, TpHandle contact, GabbleRosterItem *item); /** * got_roster_iq: * * Called by loudmouth when we get an incoming <iq>. This handler * is concerned only with roster queries, and allows other handlers * if queries other than rosters are received. * * Returns: %TRUE if handled, %FALSE to allow more handlers */ static gboolean got_roster_iq (GabbleRoster *roster, WockyStanza *message) { GabbleRosterPrivate *priv = roster->priv; WockyNode *iq_node, *query_node; WockyStanzaSubType sub_type; if (priv->conn == NULL) return FALSE; iq_node = wocky_stanza_get_top_node (message); query_node = wocky_node_get_child_ns (iq_node, "query", WOCKY_XMPP_NS_ROSTER); if (query_node == NULL) return FALSE; wocky_stanza_get_type_info (message, NULL, &sub_type); /* if this is a result, it's from our initial query. if it's a set, * it's a roster push. otherwise, it's not for us. */ if (sub_type != WOCKY_STANZA_SUB_TYPE_RESULT && sub_type != WOCKY_STANZA_SUB_TYPE_SET) { NODE_DEBUG (iq_node, "unhandled roster IQ"); return FALSE; } if (sub_type == WOCKY_STANZA_SUB_TYPE_RESULT && priv->received) { /* <https://bugs.freedesktop.org/show_bug.cgi?id=42186>: some super-buggy * XMPP server running on vk.com sends its reply to our roster query twice. */ DEBUG ("The server sent replied to our roster query more than once! " "Ignoring this reply"); return FALSE; } process_roster (roster, query_node); if (sub_type == WOCKY_STANZA_SUB_TYPE_RESULT) { /* We are handling the response to our initial roster request. */ GHashTableIter iter; gpointer k, v; GArray *members = g_array_sized_new (FALSE, FALSE, sizeof (guint), g_hash_table_size (roster->priv->items)); GSList *edited_items = NULL; /* If we're subscribed to somebody (subscription=to or =both), * and we haven't received presence from them, * we know they're offline. Let clients know that. */ g_hash_table_iter_init (&iter, roster->priv->items); while (g_hash_table_iter_next (&iter, &k, &v)) { GabbleRosterItem *item = v; TpHandle contact = GPOINTER_TO_UINT (k); GabblePresence *presence = gabble_presence_cache_get ( priv->conn->presence_cache, contact); if (item->subscribe == TP_SUBSCRIPTION_STATE_YES && (presence == NULL || presence->status == GABBLE_PRESENCE_UNKNOWN)) { /* The contact might be in the presence cache with UNKNOWN * presence if we've received a message from them before the * roster arrived: an item is forcibly added to stash the * nickname which might have been included in the <message/> in * the presence cache. (This seems like a rather illogical place * to stash such nicknames—if anything, they should live in * GabbleImFactory—but there we go.) * * So if this is the case, we flip their status to OFFLINE. We * don't use gabble_presence_update() because we want to signal * all the unknown→offline transitions together. */ if (presence != NULL) presence->status = GABBLE_PRESENCE_OFFLINE; g_array_append_val (members, contact); } if (item->unsent_edits != NULL) edited_items = g_slist_prepend (edited_items, item); } conn_presence_emit_presence_update (priv->conn, members); g_array_unref (members); /* The roster is now complete and we can emit signals... */ tp_base_contact_list_set_list_received ((TpBaseContactList *) roster); priv->received = TRUE; /* ... and carry out any pending edits */ for (; edited_items != NULL; edited_items = g_slist_delete_link (edited_items, edited_items)) { GabbleRosterItem *item = edited_items->data; roster_item_apply_edits (roster, item->unsent_edits->handle, item); } } else /* WOCKY_STANZA_SUB_TYPE_SET */ { /* acknowledge roster */ _gabble_connection_acknowledge_set_iq (priv->conn, message); } return TRUE; } static gboolean gabble_roster_iq_cb (WockyPorter *porter, WockyStanza *message, gpointer user_data) { GabbleRoster *roster = GABBLE_ROSTER (user_data); return got_roster_iq (roster, message); } static void _gabble_roster_send_presence_ack (GabbleRoster *roster, const gchar *from, WockyStanzaSubType sub_type, gboolean changed) { GabbleRosterPrivate *priv = roster->priv; WockyStanza *reply; if (!changed) { DEBUG ("not sending ack to avoid loop with buggy server"); return; } switch (sub_type) { case WOCKY_STANZA_SUB_TYPE_UNSUBSCRIBE: sub_type = WOCKY_STANZA_SUB_TYPE_UNSUBSCRIBED; break; case WOCKY_STANZA_SUB_TYPE_SUBSCRIBED: sub_type = WOCKY_STANZA_SUB_TYPE_SUBSCRIBE; break; case WOCKY_STANZA_SUB_TYPE_UNSUBSCRIBED: sub_type = WOCKY_STANZA_SUB_TYPE_UNSUBSCRIBE; break; default: g_assert_not_reached (); return; } reply = wocky_stanza_build (WOCKY_STANZA_TYPE_PRESENCE, sub_type, NULL, from, NULL); /* no content */ _gabble_connection_send (priv->conn, reply, NULL); g_object_unref (reply); } static gboolean gabble_roster_handle_subscribed (GabbleRoster *roster, TpHandle handle, const gchar *message, GError **error); static gboolean gabble_roster_presence_cb (WockyPorter *porter, WockyStanza *message, gpointer user_data) { GabbleRoster *roster = GABBLE_ROSTER (user_data); GabbleRosterPrivate *priv = roster->priv; TpBaseConnection *conn = (TpBaseConnection *) priv->conn; TpHandleRepoIface *contact_repo = tp_base_connection_get_handles (conn, TP_HANDLE_TYPE_CONTACT); WockyNode *pres_node, *child_node; const char *from; WockyStanzaSubType sub_type; TpHandleSet *tmp; TpHandle handle; const gchar *status_message = NULL; gboolean ret; GabbleRosterItem *item; if (priv->conn == NULL) return FALSE; from = wocky_stanza_get_from (message); pres_node = wocky_stanza_get_top_node (message); if (from == NULL) { NODE_DEBUG (pres_node, "presence stanza without from attribute, " "ignoring"); return FALSE; } wocky_stanza_get_type_info (message, NULL, &sub_type); handle = tp_handle_ensure (contact_repo, from, NULL, NULL); if (handle == 0) { NODE_DEBUG (pres_node, "ignoring presence from malformed jid"); return FALSE; } if (handle == tp_base_connection_get_self_handle (conn)) { NODE_DEBUG (pres_node, "ignoring presence from ourselves on another " "resource"); return FALSE; } g_assert (handle != 0); child_node = wocky_node_get_child (pres_node, "status"); if (child_node != NULL) status_message = child_node->content; item = _gabble_roster_item_ensure (roster, handle); switch (sub_type) { case WOCKY_STANZA_SUB_TYPE_SUBSCRIBE: DEBUG ("making %s (handle %u) local pending on the publish channel", from, handle); /* we re-emit ContactsChanged here, even if their state was already ASK * with the same message, because the fact that they've nagged us again * is significant */ roster_item_set_publish (item, TP_SUBSCRIPTION_STATE_ASK, status_message); tmp = tp_handle_set_new (contact_repo); tp_handle_set_add (tmp, handle); tp_base_contact_list_contacts_changed ((TpBaseContactList *) roster, tmp, NULL); if (tp_handle_set_is_member (roster->priv->pre_authorized, handle)) { GError *error = NULL; DEBUG ("%s (handle %u) was pre-authorized, will accept their " "request", from, handle); if (!gabble_roster_handle_subscribed (roster, handle, "", &error)) { DEBUG ("Authorizing pre-authorized request failed: %s", error->message); g_clear_error (&error); } } tp_handle_set_destroy (tmp); ret = TRUE; break; case WOCKY_STANZA_SUB_TYPE_UNSUBSCRIBE: DEBUG ("removing %s (handle %u) from the publish channel", from, handle); if (item->publish == TP_SUBSCRIPTION_STATE_YES || item->publish == TP_SUBSCRIPTION_STATE_ASK) { roster_item_set_publish (item, TP_SUBSCRIPTION_STATE_REMOVED_REMOTELY, NULL); tmp = tp_handle_set_new (contact_repo); tp_handle_set_add (tmp, handle); tp_base_contact_list_contacts_changed ((TpBaseContactList *) roster, tmp, NULL); tp_handle_set_destroy (tmp); _gabble_roster_send_presence_ack (roster, from, sub_type, TRUE); } else { _gabble_roster_send_presence_ack (roster, from, sub_type, FALSE); } ret = TRUE; break; case WOCKY_STANZA_SUB_TYPE_SUBSCRIBED: DEBUG ("adding %s (handle %u) to the subscribe channel", from, handle); if (item->subscribe != TP_SUBSCRIPTION_STATE_YES) { item->subscribe = TP_SUBSCRIPTION_STATE_YES; tmp = tp_handle_set_new (contact_repo); tp_handle_set_add (tmp, handle); tp_base_contact_list_contacts_changed ((TpBaseContactList *) roster, tmp, NULL); tp_handle_set_destroy (tmp); _gabble_roster_send_presence_ack (roster, from, sub_type, TRUE); } else { _gabble_roster_send_presence_ack (roster, from, sub_type, FALSE); } ret = TRUE; break; case WOCKY_STANZA_SUB_TYPE_UNSUBSCRIBED: DEBUG ("removing %s (handle %u) from the subscribe channel", from, handle); if (item->subscribe == TP_SUBSCRIPTION_STATE_YES || item->subscribe == TP_SUBSCRIPTION_STATE_ASK) { item->subscribe = TP_SUBSCRIPTION_STATE_REMOVED_REMOTELY; tmp = tp_handle_set_new (contact_repo); tp_handle_set_add (tmp, handle); tp_base_contact_list_contacts_changed ((TpBaseContactList *) roster, tmp, NULL); tp_handle_set_destroy (tmp); _gabble_roster_send_presence_ack (roster, from, sub_type, TRUE); } else { _gabble_roster_send_presence_ack (roster, from, sub_type, FALSE); } ret = TRUE; break; default: ret = FALSE; } return ret; } static void gabble_roster_close_all (GabbleRoster *self) { GabbleRosterPrivate *priv = self->priv; DEBUG ("closing channels"); if (self->priv->status_changed_id != 0) { g_signal_handler_disconnect (self->priv->conn, self->priv->status_changed_id); self->priv->status_changed_id = 0; } if (self->priv->porter_available_id != 0) { g_signal_handler_disconnect (self->priv->conn, self->priv->porter_available_id); self->priv->porter_available_id = 0; } tp_clear_pointer (&priv->groups, tp_handle_set_destroy); tp_clear_pointer (&priv->pre_authorized, tp_handle_set_destroy); if (self->priv->cancel_on_disconnect != NULL) g_cancellable_cancel (self->priv->cancel_on_disconnect); tp_clear_object (&self->priv->cancel_on_disconnect); if (self->priv->iq_cb != 0) { WockyPorter *porter = gabble_connection_dup_porter (self->priv->conn); DEBUG ("removing callbacks"); g_assert (self->priv->presence_cb != 0); wocky_porter_unregister_handler (porter, self->priv->iq_cb); self->priv->iq_cb = 0; wocky_porter_unregister_handler (porter, self->priv->presence_cb); self->priv->presence_cb = 0; g_object_unref (porter); } } static void roster_received_cb (GObject *source_object, GAsyncResult *result, gpointer weak_ref) { GabbleRoster *self = tp_weak_ref_dup_object (weak_ref); if (self != NULL) { WockyStanza *response; GError *error = NULL; if (conn_util_send_iq_finish ((GabbleConnection *) source_object, result, &response, &error)) { got_roster_iq (self, response); g_object_unref (response); } else { DEBUG ("%s", error->message); g_clear_error (&error); } } tp_clear_object (&self); tp_weak_ref_destroy (weak_ref); } static void gabble_roster_porter_available_cb (GabbleConnection *conn, WockyPorter *porter, GabbleRoster *self) { DEBUG ("adding callbacks"); g_assert (self->priv->iq_cb == 0); g_assert (self->priv->presence_cb == 0); self->priv->iq_cb = wocky_c2s_porter_register_handler_from_server ( WOCKY_C2S_PORTER (porter), WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_NONE, WOCKY_PORTER_HANDLER_PRIORITY_NORMAL, gabble_roster_iq_cb, self, '(', "query", ':', WOCKY_XMPP_NS_ROSTER, ')', NULL); self->priv->presence_cb = wocky_porter_register_handler_from_anyone (porter, WOCKY_STANZA_TYPE_PRESENCE, WOCKY_STANZA_SUB_TYPE_NONE, WOCKY_PORTER_HANDLER_PRIORITY_MIN, gabble_roster_presence_cb, self, NULL); } static void connection_status_changed_cb (GabbleConnection *conn, guint status, guint reason, GabbleRoster *self) { switch (status) { case TP_CONNECTION_STATUS_CONNECTED: { WockyStanza *stanza; TpBaseContactList *base = TP_BASE_CONTACT_LIST (self); self->priv->cancel_on_disconnect = g_cancellable_new (); if (tp_base_contact_list_get_download_at_connection (base)) { DEBUG ("requesting roster"); stanza = _gabble_roster_message_new (self, WOCKY_STANZA_SUB_TYPE_GET, NULL); conn_util_send_iq_async (conn, stanza, self->priv->cancel_on_disconnect, roster_received_cb, tp_weak_ref_new (self, NULL, NULL)); g_object_unref (stanza); } else { DEBUG ("don't request the roster because the property" " ContactList.DownloadAtConnection is FALSE"); } } break; case TP_CONNECTION_STATUS_DISCONNECTED: gabble_roster_close_all (self); break; } } static void gabble_roster_constructed (GObject *obj) { GabbleRoster *self = GABBLE_ROSTER (obj); TpBaseContactList *base = TP_BASE_CONTACT_LIST (obj); void (*chain_up)(GObject *) = ((GObjectClass *) gabble_roster_parent_class)->constructed; TpHandleRepoIface *group_repo; TpHandleRepoIface *contact_repo; if (chain_up != NULL) chain_up (obj); /* FIXME: This is not a strong reference because that would create a cycle. * I'd like to have a cyclic reference and break it at disconnect time, * like the contact list example in telepathy-glib does, but we can't do * that because the rest of Gabble assumes that the roster remains useful * until the bitter end (for instance, gabble_im_channel_dispose looks * at the contact's subscription). */ self->priv->conn = GABBLE_CONNECTION (tp_base_contact_list_get_connection ( base, NULL)); g_assert (GABBLE_IS_CONNECTION (self->priv->conn)); group_repo = tp_base_connection_get_handles ( (TpBaseConnection *) self->priv->conn, TP_HANDLE_TYPE_GROUP); contact_repo = tp_base_connection_get_handles ( (TpBaseConnection *) self->priv->conn, TP_HANDLE_TYPE_CONTACT); self->priv->status_changed_id = g_signal_connect (self->priv->conn, "status-changed", (GCallback) connection_status_changed_cb, obj); self->priv->porter_available_id = g_signal_connect (self->priv->conn, "porter-available", G_CALLBACK (gabble_roster_porter_available_cb), obj); self->priv->groups = tp_handle_set_new (group_repo); self->priv->pre_authorized = tp_handle_set_new (contact_repo); } GabbleRoster * gabble_roster_new (GabbleConnection *conn) { g_return_val_if_fail (conn != NULL, NULL); return g_object_new (GABBLE_TYPE_ROSTER, "connection", conn, NULL); } static GabbleRosterItemEdit * item_edit_new (TpHandleRepoIface *contact_repo, TpHandle handle) { GabbleRosterItemEdit *self = g_slice_new0 (GabbleRosterItemEdit); self->contact_repo = g_object_ref (contact_repo); self->handle = handle; self->new_subscription = GABBLE_ROSTER_SUBSCRIPTION_INVALID; self->new_google_type = GOOGLE_ITEM_TYPE_INVALID; return self; } static void item_edit_free (GabbleRosterItemEdit *edits) { GSList *slist; if (!edits) return; edits->results = g_slist_reverse (edits->results); for (slist = edits->results; slist != NULL; slist = slist->next) { gabble_simple_async_countdown_dec (slist->data); g_object_unref (slist->data); } g_slist_free (edits->results); g_object_unref (edits->contact_repo); tp_clear_pointer (&edits->add_to_groups, tp_handle_set_destroy); tp_clear_pointer (&edits->remove_from_groups, tp_handle_set_destroy); g_free (edits->new_name); g_slice_free (GabbleRosterItemEdit, edits); } static void roster_edited_cb (GObject *, GAsyncResult *, gpointer); static gboolean gabble_roster_handle_subscribed (GabbleRoster *roster, TpHandle handle, const gchar *message, GError **error); /* * Cancel any subscriptions on @item by sending unsubscribe and/or * unsubscribed, as appropriate. */ static gboolean roster_item_cancel_subscriptions ( GabbleRoster *roster, TpHandle contact, GabbleRosterItem *item, GError **error) { TpHandleRepoIface *contact_repo = tp_base_connection_get_handles ( (TpBaseConnection *) roster->priv->conn, TP_HANDLE_TYPE_CONTACT); const gchar *contact_id = tp_handle_inspect (contact_repo, contact); gboolean ret = TRUE; if (item->subscription == GABBLE_ROSTER_SUBSCRIPTION_FROM || item->subscription == GABBLE_ROSTER_SUBSCRIPTION_BOTH) { DEBUG ("sending unsubscribed"); ret = gabble_connection_send_presence (roster->priv->conn, WOCKY_STANZA_SUB_TYPE_UNSUBSCRIBED, contact_id, NULL, error); } if (ret && (item->subscription == GABBLE_ROSTER_SUBSCRIPTION_TO || item->subscription == GABBLE_ROSTER_SUBSCRIPTION_BOTH)) { DEBUG ("sending unsubscribe"); ret = gabble_connection_send_presence (roster->priv->conn, WOCKY_STANZA_SUB_TYPE_UNSUBSCRIBE, contact_id, NULL, error); } return ret; } /* Apply the unsent edits to the given roster item. * * \param roster The roster * \param contact The contact handle * \param item contact's roster item on roster */ static void roster_item_apply_edits (GabbleRoster *roster, TpHandle contact, GabbleRosterItem *item) { gboolean altered = FALSE; GabbleRosterItem edited_item; TpIntset *intset; GabbleRosterPrivate *priv = roster->priv; TpHandleRepoIface *group_repo = tp_base_connection_get_handles ( (TpBaseConnection *) priv->conn, TP_HANDLE_TYPE_GROUP); GabbleRosterItemEdit *edits = item->unsent_edits; WockyStanza *message; if (!priv->received) { DEBUG ("Initial roster has not arrived yet, not editing it"); return; } if (item->edits_in_flight) { DEBUG ("Edits still in flight for contact#%u, not applying more", contact); return; } if (edits == NULL) { DEBUG ("Nothing to do for contact#%u", contact); return; } DEBUG ("Applying edits to contact#%u", contact); memcpy (&edited_item, item, sizeof (GabbleRosterItem)); #ifdef ENABLE_DEBUG if (DEBUGGING) { gchar *dump = _gabble_roster_item_dump (&edited_item); DEBUG ("Before, contact#%u: %s", contact, dump); g_free (dump); } #endif if (edits->create) { DEBUG ("Creating new item"); altered = TRUE; } if (edits->new_google_type != GOOGLE_ITEM_TYPE_INVALID && edits->new_google_type != item->google_type) { DEBUG ("Changing Google type from %d to %d", item->google_type, edits->new_google_type); altered = TRUE; edited_item.google_type = edits->new_google_type; } if (edits->new_subscription != GABBLE_ROSTER_SUBSCRIPTION_INVALID && edits->new_subscription != item->subscription) { /* Here we check the google_type of the *edited* item (as patched in the * block above) to deal correctly with a batch of edits containing both * (un)block and remove. */ if (edits->new_subscription == GABBLE_ROSTER_SUBSCRIPTION_REMOVE && edited_item.google_type == GOOGLE_ITEM_TYPE_BLOCKED) { GError *error = NULL; /* If they're blocked, we can't just remove them from the roster, * because that would unblock them! So instead, we cancel both * subscription directions. */ DEBUG ("contact is blocked; not removing"); if (!roster_item_cancel_subscriptions (roster, contact, item, &error)) { GSList *slist; /* in practice this error will probably be overwritten by one * from the IQ-set later, but if that succeeds for some reason, * we do want to signal error */ for (slist = edits->results; slist != NULL; slist = slist->next) g_simple_async_result_set_from_error (slist->data, error); g_clear_error (&error); } /* deliberately not setting altered: we haven't altered the roster, * as such. */ } else { DEBUG ("Changing subscription from %d to %d", item->subscription, edits->new_subscription); altered = TRUE; edited_item.subscription = edits->new_subscription; } } if (edits->remove_name) { DEBUG ("Removing name='' (was '%s')", item->name); altered = TRUE; edited_item.name = NULL; } else if (edits->new_name != NULL && tp_strdiff (item->name, edits->new_name)) { DEBUG ("Changing name from %s to %s", item->name, edits->new_name); altered = TRUE; edited_item.name = edits->new_name; } if (edits->add_to_groups != NULL || edits->remove_from_groups != NULL || edits->remove_from_all_other_groups) { #ifdef ENABLE_DEBUG if (DEBUGGING) { if (edits->add_to_groups != NULL) { GString *str = g_string_new ("Adding to groups: "); tp_intset_foreach (tp_handle_set_peek (edits->add_to_groups), _gabble_roster_item_dump_group, str); DEBUG("%s", g_string_free (str, FALSE)); } else { DEBUG ("Not adding to any groups"); } if (edits->remove_from_all_other_groups) { DEBUG ("Removing from all other groups"); } if (edits->remove_from_groups != NULL) { GString *str = g_string_new ("Removing from groups: "); tp_intset_foreach (tp_handle_set_peek (edits->remove_from_groups), _gabble_roster_item_dump_group, str); DEBUG("%s", g_string_free (str, FALSE)); } else { DEBUG ("Not removing from any groups"); } } #endif edited_item.groups = tp_handle_set_new (group_repo); if (!edits->remove_from_all_other_groups) { intset = tp_handle_set_update (edited_item.groups, tp_handle_set_peek (item->groups)); tp_intset_destroy (intset); } if (edits->add_to_groups) { intset = tp_handle_set_update (edited_item.groups, tp_handle_set_peek (edits->add_to_groups)); tp_intset_destroy (intset); } if (edits->remove_from_groups) { intset = tp_handle_set_difference_update (edited_item.groups, tp_handle_set_peek (edits->remove_from_groups)); tp_intset_destroy (intset); } if (!tp_intset_is_equal (tp_handle_set_peek (edited_item.groups), tp_handle_set_peek (item->groups))) altered = TRUE; } /* If we changed something about a transient GabbleRosterItem that * wasn't actually on our server-side roster yet, and we weren't actually * trying to delete it, then we need to create it as a side-effect. */ if (altered && edits->new_subscription != GABBLE_ROSTER_SUBSCRIPTION_REMOVE && edited_item.subscription == GABBLE_ROSTER_SUBSCRIPTION_REMOVE) { edits->new_subscription = GABBLE_ROSTER_SUBSCRIPTION_NONE; edited_item.subscription = GABBLE_ROSTER_SUBSCRIPTION_NONE; } #ifdef ENABLE_DEBUG if (DEBUGGING) { gchar *dump = _gabble_roster_item_dump (&edited_item); DEBUG ("After, contact#%u: %s", contact, dump); g_free (dump); } #endif if (!altered) { DEBUG ("Contact#%u not actually changed - nothing to do", contact); item_edit_free (item->unsent_edits); item->unsent_edits = NULL; return; } DEBUG ("Contact#%u did change, sending message", contact); message = _gabble_roster_item_to_message (roster, contact, &edited_item); /* we're sending the unsent edits - on success, roster_edited_cb will own * them */ item->unsent_edits = NULL; item->edits_in_flight = TRUE; conn_util_send_iq_async (priv->conn, message, priv->cancel_on_disconnect, roster_edited_cb, edits); if (edits->new_google_type == GOOGLE_ITEM_TYPE_BLOCKED) gabble_presence_cache_really_remove (priv->conn->presence_cache, contact); if (edited_item.groups != item->groups) { tp_handle_set_destroy (edited_item.groups); } } /* Called when an edit to the roster item has either succeeded or failed. */ static void roster_edited_cb (GObject *source_object, GAsyncResult *result, gpointer user_data) { GabbleConnection *conn = GABBLE_CONNECTION (source_object); GabbleRoster *roster = conn->roster; GabbleRosterItemEdit *edit = user_data; GabbleRosterItem *item = NULL; if (edit->results != NULL) { GError *tp_error = NULL; if (!conn_util_send_iq_finish (conn, result, NULL, &tp_error)) { GSList *slist; for (slist = edit->results; slist != NULL; slist = slist->next) g_simple_async_result_set_from_error (slist->data, tp_error); g_clear_error (&tp_error); } } if (roster != NULL) item = _gabble_roster_item_lookup (roster, edit->handle); if (item != NULL) { item->edits_in_flight = FALSE; /* if more edits have been queued since we sent this batch, do them */ roster_item_apply_edits (roster, edit->handle, item); if (item->subscription == GABBLE_ROSTER_SUBSCRIPTION_REMOVE && edit->new_subscription != GABBLE_ROSTER_SUBSCRIPTION_REMOVE) { /* The server claims to have created the item, so we should believe * that the item exists, even though we haven't yet had the roster * push that should confirm it. This will result in * _gabble_roster_item_maybe_remove not removing it. */ item->subscription = edit->new_subscription; } _gabble_roster_item_maybe_remove (roster, edit->handle); } item_edit_free (edit); } static void gabble_roster_handle_set_blocked (GabbleRoster *roster, TpHandle handle, gboolean blocked, GSimpleAsyncResult *result) { GabbleRosterPrivate *priv = roster->priv; TpHandleRepoIface *contact_repo = tp_base_connection_get_handles ( (TpBaseConnection *) priv->conn, TP_HANDLE_TYPE_CONTACT); GabbleRosterItem *item; g_return_if_fail (roster != NULL); g_return_if_fail (GABBLE_IS_ROSTER (roster)); g_return_if_fail (tp_handle_is_valid (contact_repo, handle, NULL)); g_return_if_fail (priv->conn->features & GABBLE_CONNECTION_FEATURES_GOOGLE_ROSTER); item = _gabble_roster_item_ensure (roster, handle); if (item->unsent_edits == NULL) item->unsent_edits = item_edit_new (contact_repo, handle); DEBUG ("queue edit to contact#%u - change subscription to blocked=%d", handle, blocked); if (blocked) item->unsent_edits->new_google_type = GOOGLE_ITEM_TYPE_BLOCKED; else item->unsent_edits->new_google_type = GOOGLE_ITEM_TYPE_NORMAL; gabble_simple_async_countdown_inc (result); item->unsent_edits->results = g_slist_prepend ( item->unsent_edits->results, g_object_ref (result)); /* maybe we can apply the edit immediately? */ roster_item_apply_edits (roster, handle, item); } gboolean gabble_roster_handle_has_entry (GabbleRoster *roster, TpHandle handle) { GabbleRosterPrivate *priv = roster->priv; TpHandleRepoIface *contact_repo = tp_base_connection_get_handles ( (TpBaseConnection *) priv->conn, TP_HANDLE_TYPE_CONTACT); GabbleRosterItem *item; g_return_val_if_fail (roster != NULL, FALSE); g_return_val_if_fail (GABBLE_IS_ROSTER (roster), FALSE); g_return_val_if_fail (tp_handle_is_valid (contact_repo, handle, NULL), FALSE); item = _gabble_roster_item_lookup (roster, handle); return (NULL != item); } const gchar * gabble_roster_handle_get_name (GabbleRoster *roster, TpHandle handle) { GabbleRosterPrivate *priv = roster->priv; TpHandleRepoIface *contact_repo = tp_base_connection_get_handles ( (TpBaseConnection *) priv->conn, TP_HANDLE_TYPE_CONTACT); GabbleRosterItem *item; g_return_val_if_fail (roster != NULL, NULL); g_return_val_if_fail (GABBLE_IS_ROSTER (roster), NULL); g_return_val_if_fail (tp_handle_is_valid (contact_repo, handle, NULL), NULL); item = _gabble_roster_item_lookup (roster, handle); if (NULL == item) return NULL; return item->name; } gboolean gabble_roster_handle_set_name (GabbleRoster *roster, TpHandle handle, const gchar *name, GError **error) { GabbleRosterPrivate *priv = roster->priv; TpHandleRepoIface *contact_repo = tp_base_connection_get_handles ( (TpBaseConnection *) priv->conn, TP_HANDLE_TYPE_CONTACT); GabbleRosterItem *item; g_return_val_if_fail (roster != NULL, FALSE); g_return_val_if_fail (GABBLE_IS_ROSTER (roster), FALSE); g_return_val_if_fail (tp_handle_is_valid (contact_repo, handle, NULL), FALSE); item = _gabble_roster_item_ensure (roster, handle); g_return_val_if_fail (item != NULL, FALSE); if (item->unsent_edits == NULL) item->unsent_edits = item_edit_new (contact_repo, handle); tp_clear_pointer (&item->unsent_edits->new_name, g_free); if (name == NULL) { DEBUG ("queue edit to contact#%u - remove name", handle); item->unsent_edits->remove_name = TRUE; } else { DEBUG ("queue edit to contact#%u - set name='%s'", handle, name); item->unsent_edits->remove_name = FALSE; item->unsent_edits->new_name = g_strdup (name); } /* maybe we can apply the edit immediately? */ roster_item_apply_edits (roster, handle, item); /* FIXME: this method should be async so we don't need to assume * success */ return TRUE; } static void gabble_roster_handle_remove (GabbleRoster *roster, TpHandle handle, GSimpleAsyncResult *result) { GabbleRosterPrivate *priv = roster->priv; TpBaseContactList *base = (TpBaseContactList *) roster; TpHandleRepoIface *contact_repo = tp_base_connection_get_handles ( (TpBaseConnection *) priv->conn, TP_HANDLE_TYPE_CONTACT); GabbleRosterItem *item; g_return_if_fail (roster != NULL); g_return_if_fail (GABBLE_IS_ROSTER (roster)); g_return_if_fail (tp_handle_is_valid (contact_repo, handle, NULL)); item = _gabble_roster_item_lookup (roster, handle); if (item == NULL) return; /* If the contact is really stored on the server, deleting their roster item * is sufficient. If they're not, we might have some state resulting from * a publish request or remote removal or something. */ if (item->subscription == GABBLE_ROSTER_SUBSCRIPTION_REMOVE) { /* These will clear a status of REMOVED_REMOTELY or ASK */ roster_item_set_publish (item, TP_SUBSCRIPTION_STATE_NO, NULL); roster_item_set_subscribe (item, TP_SUBSCRIPTION_STATE_NO); /* If there are no edits in-flight, we may just be able to delete the * contact list entry and return early. If there are edits in flight, * we should not return early: the in-flight edit might be * creating the roster item, so we need to queue up a second edit * that will delete it again. */ if (_gabble_roster_item_maybe_remove (roster, handle)) { TpHandleSet *removed = tp_handle_set_new (contact_repo); tp_handle_set_add (removed, handle); tp_base_contact_list_contacts_changed (base, NULL, removed); tp_handle_set_destroy (removed); return; } } if (item->unsent_edits == NULL) item->unsent_edits = item_edit_new (contact_repo, handle); DEBUG ("queue edit to contact#%u - change subscription to REMOVE", handle); item->unsent_edits->new_subscription = GABBLE_ROSTER_SUBSCRIPTION_REMOVE; gabble_simple_async_countdown_inc (result); item->unsent_edits->results = g_slist_prepend ( item->unsent_edits->results, g_object_ref (result)); /* maybe we can apply the edit immediately? */ roster_item_apply_edits (roster, handle, item); } static void gabble_roster_handle_add (GabbleRoster *roster, TpHandle handle, GSimpleAsyncResult *result) { GabbleRosterPrivate *priv = roster->priv; TpHandleRepoIface *contact_repo = tp_base_connection_get_handles ( (TpBaseConnection *) priv->conn, TP_HANDLE_TYPE_CONTACT); GabbleRosterItem *item; gboolean do_add = FALSE; g_return_if_fail (roster != NULL); g_return_if_fail (GABBLE_IS_ROSTER (roster)); g_return_if_fail (tp_handle_is_valid (contact_repo, handle, NULL)); if (!gabble_roster_handle_has_entry (roster, handle)) do_add = TRUE; item = _gabble_roster_item_ensure (roster, handle); if (item->google_type == GOOGLE_ITEM_TYPE_HIDDEN || item->subscription == GABBLE_ROSTER_SUBSCRIPTION_REMOVE) do_add = TRUE; if (!do_add) return; if (item->unsent_edits == NULL) item->unsent_edits = item_edit_new (contact_repo, handle); DEBUG ("queue edit to contact#%u - change google type to NORMAL", handle); item->unsent_edits->create = TRUE; item->unsent_edits->new_google_type = GOOGLE_ITEM_TYPE_NORMAL; if (result != NULL) { gabble_simple_async_countdown_inc (result); item->unsent_edits->results = g_slist_prepend ( item->unsent_edits->results, g_object_ref (result)); } /* maybe we can apply the edit immediately? */ roster_item_apply_edits (roster, handle, item); } static void gabble_roster_handle_add_to_group (GabbleRoster *roster, TpHandle handle, TpHandle group, GSimpleAsyncResult *result) { GabbleRosterPrivate *priv = roster->priv; TpHandleRepoIface *contact_repo = tp_base_connection_get_handles ( (TpBaseConnection *) priv->conn, TP_HANDLE_TYPE_CONTACT); TpHandleRepoIface *group_repo = tp_base_connection_get_handles ( (TpBaseConnection *) priv->conn, TP_HANDLE_TYPE_GROUP); GabbleRosterItem *item; g_return_if_fail (roster != NULL); g_return_if_fail (GABBLE_IS_ROSTER (roster)); g_return_if_fail (tp_handle_is_valid (contact_repo, handle, NULL)); g_return_if_fail (tp_handle_is_valid (group_repo, group, NULL)); item = _gabble_roster_item_ensure (roster, handle); if (item->unsent_edits == NULL) item->unsent_edits = item_edit_new (contact_repo, handle); DEBUG ("queue edit to contact#%u - add to group#%u", handle, group); gabble_simple_async_countdown_inc (result); item->unsent_edits->results = g_slist_prepend ( item->unsent_edits->results, g_object_ref (result)); if (!item->unsent_edits->add_to_groups) { item->unsent_edits->add_to_groups = tp_handle_set_new (group_repo); } tp_handle_set_add (item->unsent_edits->add_to_groups, group); if (item->unsent_edits->remove_from_groups) { tp_handle_set_remove (item->unsent_edits->remove_from_groups, group); } /* maybe we can apply the edit immediately? */ roster_item_apply_edits (roster, handle, item); } static void gabble_roster_handle_remove_from_group (GabbleRoster *roster, TpHandle handle, TpHandle group, GSimpleAsyncResult *result) { GabbleRosterPrivate *priv = roster->priv; TpHandleRepoIface *contact_repo = tp_base_connection_get_handles ( (TpBaseConnection *) priv->conn, TP_HANDLE_TYPE_CONTACT); TpHandleRepoIface *group_repo = tp_base_connection_get_handles ( (TpBaseConnection *) priv->conn, TP_HANDLE_TYPE_GROUP); GabbleRosterItem *item; g_return_if_fail (roster != NULL); g_return_if_fail (GABBLE_IS_ROSTER (roster)); g_return_if_fail (tp_handle_is_valid (contact_repo, handle, NULL)); g_return_if_fail (tp_handle_is_valid (group_repo, group, NULL)); item = _gabble_roster_item_ensure (roster, handle); if (item->unsent_edits == NULL) item->unsent_edits = item_edit_new (contact_repo, handle); DEBUG ("queue edit to contact#%u - remove from group#%u", handle, group); gabble_simple_async_countdown_inc (result); item->unsent_edits->results = g_slist_prepend ( item->unsent_edits->results, g_object_ref (result)); if (!item->unsent_edits->remove_from_groups) { item->unsent_edits->remove_from_groups = tp_handle_set_new ( group_repo); } tp_handle_set_add (item->unsent_edits->remove_from_groups, group); if (item->unsent_edits->add_to_groups) { tp_handle_set_remove (item->unsent_edits->add_to_groups, group); } /* maybe we can apply the edit immediately? */ roster_item_apply_edits (roster, handle, item); } static gboolean gabble_roster_handle_subscribed ( GabbleRoster *roster, TpHandle handle, const gchar *message, GError **error) { TpHandleRepoIface *contact_repo = tp_base_connection_get_handles ( (TpBaseConnection *) roster->priv->conn, TP_HANDLE_TYPE_CONTACT); const gchar *contact_id = tp_handle_inspect (contact_repo, handle); /* send <presence type="subscribed"> */ return gabble_connection_send_presence (roster->priv->conn, WOCKY_STANZA_SUB_TYPE_SUBSCRIBED, contact_id, message, error); } static TpHandleSet * gabble_roster_dup_contacts (TpBaseContactList *base) { GabbleRoster *self = GABBLE_ROSTER (base); TpHandleSet *set; TpHandleRepoIface *contact_repo = tp_base_connection_get_handles ( (TpBaseConnection *) self->priv->conn, TP_HANDLE_TYPE_CONTACT); GHashTableIter iter; gpointer k, v; set = tp_handle_set_new (contact_repo); g_hash_table_iter_init (&iter, self->priv->items); while (g_hash_table_iter_next (&iter, &k, &v)) { GabbleRosterItem *item = v; /* add all the interesting items */ if (item->stored || item->subscribe != TP_SUBSCRIPTION_STATE_NO || item->publish != TP_SUBSCRIPTION_STATE_NO) tp_handle_set_add (set, GPOINTER_TO_UINT (k)); } return set; } static void gabble_roster_dup_states (TpBaseContactList *base, TpHandle contact, TpSubscriptionState *subscribe, TpSubscriptionState *publish, gchar **publish_request) { GabbleRoster *self = GABBLE_ROSTER (base); GabbleRosterItem *item = _gabble_roster_item_lookup (self, contact); if (item == NULL) { if (subscribe != NULL) *subscribe = TP_SUBSCRIPTION_STATE_NO; if (publish != NULL) *publish = TP_SUBSCRIPTION_STATE_NO; if (publish_request != NULL) *publish_request = NULL; } else { if (subscribe != NULL) *subscribe = item->subscribe; if (publish != NULL) *publish = item->publish; if (publish_request != NULL) *publish_request = g_strdup (item->publish_request); } } typedef struct { GAsyncReadyCallback callback; gpointer user_data; TpHandleSet *contacts; gchar *message; } SubscribeContext; static void gabble_roster_request_subscription_added_cb (GObject *source, GAsyncResult *result, gpointer user_data) { GabbleRoster *self = GABBLE_ROSTER (source); TpHandleRepoIface *contact_repo = tp_base_connection_get_handles ( (TpBaseConnection *) self->priv->conn, TP_HANDLE_TYPE_CONTACT); SubscribeContext *context = user_data; GError *error = NULL; TpIntsetFastIter iter; TpHandle contact; /* Now that we've added all the contacts, send off all the subscription * requests; stop if we hit an error. */ tp_intset_fast_iter_init (&iter, tp_handle_set_peek (context->contacts)); while (tp_intset_fast_iter_next (&iter, &contact)) { GabbleRosterItem *item = _gabble_roster_item_lookup (self, contact); const gchar *contact_id = tp_handle_inspect (contact_repo, contact); /* Note that we *do* send redundant requests if the contact is in * ask=subscribe state, since those have semantic value - nagging the * contact again. There's no point in requesting subscription if the * contact has already said yes, though. */ if (item != NULL && (item->subscription == GABBLE_ROSTER_SUBSCRIPTION_TO || item->subscription == GABBLE_ROSTER_SUBSCRIPTION_BOTH)) { DEBUG ("Already subscribed to contact#%u '%s', not re-requesting", contact, contact_id); continue; } /* stop trying at the first NetworkError, on the assumption that it'll * be fatal */ if (!gabble_connection_send_presence (self->priv->conn, WOCKY_STANZA_SUB_TYPE_SUBSCRIBE, contact_id, context->message, &error)) break; } gabble_simple_async_succeed_or_fail_in_idle (self, context->callback, context->user_data, NULL, error); g_clear_error (&error); tp_clear_pointer (&context->contacts, tp_handle_set_destroy); g_free (context->message); g_slice_free (SubscribeContext, context); } static void gabble_roster_request_subscription_async (TpBaseContactList *base, TpHandleSet *contacts, const gchar *message, GAsyncReadyCallback callback, gpointer user_data) { GabbleRoster *self = GABBLE_ROSTER (base); SubscribeContext *context = g_slice_new0 (SubscribeContext); GSimpleAsyncResult *result = gabble_simple_async_countdown_new (self, gabble_roster_request_subscription_added_cb, context, gabble_roster_request_subscription_async, 1); TpIntsetFastIter iter; TpHandle contact; /* Before subscribing, add items to the roster * (GTalk depends on this clearing the H flag) */ context->contacts = tp_handle_set_copy (contacts); context->callback = callback; context->user_data = user_data; context->message = g_strdup (message); tp_intset_fast_iter_init (&iter, tp_handle_set_peek (contacts)); while (tp_intset_fast_iter_next (&iter, &contact)) gabble_roster_handle_add (self, contact, result); /* When all of those edits have been applied, the callback will send the * <presence type='subscribe'> requests. */ gabble_simple_async_countdown_dec (result); g_object_unref (result); } static void gabble_roster_authorize_publication_async (TpBaseContactList *base, TpHandleSet *contacts, GAsyncReadyCallback callback, gpointer user_data) { GabbleRoster *self = GABBLE_ROSTER (base); TpIntsetFastIter iter; TpHandle contact; GError *error = NULL; TpHandleRepoIface *contact_repo = tp_base_connection_get_handles ( (TpBaseConnection *) self->priv->conn, TP_HANDLE_TYPE_CONTACT); tp_intset_fast_iter_init (&iter, tp_handle_set_peek (contacts)); while (tp_intset_fast_iter_next (&iter, &contact)) { GabbleRosterItem *item = _gabble_roster_item_lookup (self, contact); const gchar *contact_id = tp_handle_inspect (contact_repo, contact); if (item == NULL || item->publish == TP_SUBSCRIPTION_STATE_NO || item->publish == TP_SUBSCRIPTION_STATE_REMOVED_REMOTELY) { /* The contact didn't ask for our presence, so we can't usefully * send out <presence type='subscribed'/> (as per RFC3921 §9.2, * our server shouldn't forward it anyway). However, we can * remember this "pre-authorization" and use it later in the * session to auto-approve a subscription request. */ DEBUG ("Noting that contact #%u '%s' is pre-authorized", contact, contact_id); tp_handle_set_add (self->priv->pre_authorized, contact); } else if (item->publish == TP_SUBSCRIPTION_STATE_ASK) { /* stop trying at the first NetworkError, on the assumption that * it'll be fatal */ DEBUG ("Sending <presence type='subscribed'/> to contact#%u '%s'", contact, contact_id); if (!gabble_roster_handle_subscribed (self, contact, "", &error)) break; } else { DEBUG ("contact #%u '%s' already has publish=Y, nothing to do", contact, contact_id); } } gabble_simple_async_succeed_or_fail_in_idle (self, callback, user_data, gabble_roster_authorize_publication_async, error); g_clear_error (&error); } static void gabble_roster_store_contacts_async (TpBaseContactList *base, TpHandleSet *contacts, GAsyncReadyCallback callback, gpointer user_data) { GabbleRoster *self = GABBLE_ROSTER (base); TpIntsetFastIter iter; TpHandle contact; GSimpleAsyncResult *result = gabble_simple_async_countdown_new (self, callback, user_data, gabble_roster_store_contacts_async, 1); tp_intset_fast_iter_init (&iter, tp_handle_set_peek (contacts)); while (tp_intset_fast_iter_next (&iter, &contact)) gabble_roster_handle_add (self, contact, result); gabble_simple_async_countdown_dec (result); g_object_unref (result); } static void gabble_roster_remove_contacts_async (TpBaseContactList *base, TpHandleSet *contacts, GAsyncReadyCallback callback, gpointer user_data) { GabbleRoster *self = GABBLE_ROSTER (base); TpIntsetFastIter iter; TpHandle contact; GSimpleAsyncResult *result = gabble_simple_async_countdown_new (self, callback, user_data, gabble_roster_request_subscription_async, 1); tp_intset_fast_iter_init (&iter, tp_handle_set_peek (contacts)); while (tp_intset_fast_iter_next (&iter, &contact)) gabble_roster_handle_remove (self, contact, result); gabble_simple_async_countdown_dec (result); g_object_unref (result); } static void gabble_roster_unsubscribe_async (TpBaseContactList *base, TpHandleSet *contacts, GAsyncReadyCallback callback, gpointer user_data) { GabbleRoster *self = GABBLE_ROSTER (base); TpHandleRepoIface *contact_repo = tp_base_connection_get_handles ( (TpBaseConnection *) self->priv->conn, TP_HANDLE_TYPE_CONTACT); TpHandleSet *changed = tp_handle_set_new (contact_repo); TpHandleSet *removed = tp_handle_set_new (contact_repo); TpIntsetFastIter iter; TpHandle contact; GError *error = NULL; tp_intset_fast_iter_init (&iter, tp_handle_set_peek (contacts)); while (tp_intset_fast_iter_next (&iter, &contact)) { const gchar *contact_id = tp_handle_inspect (contact_repo, contact); GabbleRosterItem *item = _gabble_roster_item_lookup (self, contact); if (item == NULL || item->subscribe == TP_SUBSCRIPTION_STATE_NO) { DEBUG ("contact #%u '%s' absent or has subscribe=N, nothing to do", contact, contact_id); } else if (item->subscribe == TP_SUBSCRIPTION_STATE_REMOVED_REMOTELY) { /* just acknowledge remote removal */ DEBUG ("contact #%u '%s' had subscribe=R, moving to publish=N", contact, contact_id); roster_item_set_subscribe (item, TP_SUBSCRIPTION_STATE_NO); if (_gabble_roster_item_maybe_remove (self, contact)) tp_handle_set_add (removed, contact); else tp_handle_set_add (changed, contact); } else { /* Deny a request (if ASK) or revoke previously-granted permission * (if YES). Stop trying at the first NetworkError, on the * assumption that it'll be fatal. Any changes will be signalled when * confirmed by a roster push. */ DEBUG ("Sending <presence type='unsubscribe'/> to contact#%u '%s'", contact, contact_id); if (!gabble_connection_send_presence (self->priv->conn, WOCKY_STANZA_SUB_TYPE_UNSUBSCRIBE, contact_id, "", &error)) break; } } tp_base_contact_list_contacts_changed (base, changed, removed); gabble_simple_async_succeed_or_fail_in_idle (self, callback, user_data, gabble_roster_unsubscribe_async, error); g_clear_error (&error); tp_handle_set_destroy (changed); tp_handle_set_destroy (removed); } static void gabble_roster_unpublish_async (TpBaseContactList *base, TpHandleSet *contacts, GAsyncReadyCallback callback, gpointer user_data) { GabbleRoster *self = GABBLE_ROSTER (base); TpHandleRepoIface *contact_repo = tp_base_connection_get_handles ( (TpBaseConnection *) self->priv->conn, TP_HANDLE_TYPE_CONTACT); TpHandleSet *changed = tp_handle_set_new (contact_repo); TpHandleSet *removed = tp_handle_set_new (contact_repo); TpIntsetFastIter iter; TpHandle contact; GError *error = NULL; tp_intset_fast_iter_init (&iter, tp_handle_set_peek (contacts)); while (tp_intset_fast_iter_next (&iter, &contact)) { const gchar *contact_id = tp_handle_inspect (contact_repo, contact); GabbleRosterItem *item = _gabble_roster_item_lookup (self, contact); /* If moving from YES to NO, the roster callback will make the change * visible to D-Bus when it actually takes effect. * * If moving from ASK to NO, remove it from publish:local_pending here, * because the roster callback doesn't know if it can * (subscription='none' is used both during request and when it's * rejected). * * If moving from REMOVED_REMOTELY to NO, there's no real change at the * XMPP level, so this is our only chance to make the change visible. */ if (item != NULL && (item->publish == TP_SUBSCRIPTION_STATE_ASK || item->publish == TP_SUBSCRIPTION_STATE_REMOVED_REMOTELY)) { if (item->publish == TP_SUBSCRIPTION_STATE_ASK) DEBUG ("contact #%u '%s' had publish=A, moving to publish=N", contact, contact_id); else DEBUG ("contact #%u '%s' had publish=R, moving to publish=N", contact, contact_id); roster_item_set_publish (item, TP_SUBSCRIPTION_STATE_NO, NULL); if (_gabble_roster_item_maybe_remove (self, contact)) tp_handle_set_add (removed, contact); else tp_handle_set_add (changed, contact); } if (item == NULL || item->publish == TP_SUBSCRIPTION_STATE_NO) { DEBUG ("contact #%u '%s' already has publish=N, nothing to do", contact, contact_id); } else { /* stop trying at the first NetworkError, on the assumption that * it'll be fatal */ DEBUG ("Sending <presence type='unsubscribed'/> to contact#%u '%s'", contact, contact_id); if (!gabble_connection_send_presence (self->priv->conn, WOCKY_STANZA_SUB_TYPE_UNSUBSCRIBED, contact_id, "", &error)) break; } } tp_base_contact_list_contacts_changed (base, changed, removed); gabble_simple_async_succeed_or_fail_in_idle (self, callback, user_data, gabble_roster_unpublish_async, error); g_clear_error (&error); tp_handle_set_destroy (changed); tp_handle_set_destroy (removed); } static void gabble_roster_download_async (TpBaseContactList *base, GAsyncReadyCallback callback, gpointer user_data) { GabbleRoster *self = GABBLE_ROSTER (base); GError *error = NULL; if (!tp_base_contact_list_get_download_at_connection (base)) { WockyStanza *stanza; DEBUG ("Downloading roster requested"); stanza = _gabble_roster_message_new (self, WOCKY_STANZA_SUB_TYPE_GET, NULL); conn_util_send_iq_async (self->priv->conn, stanza, self->priv->cancel_on_disconnect, roster_received_cb, tp_weak_ref_new (self, NULL, NULL)); g_object_unref (stanza); } else { DEBUG ("Downloading roster requested but it is already requested at " "connection. Just do nothing and return."); } gabble_simple_async_succeed_or_fail_in_idle (self, callback, user_data, gabble_roster_download_async, error); g_clear_error (&error); } static TpHandleSet * gabble_roster_dup_blocked_contacts (TpBaseContactList *base) { GabbleRoster *self = GABBLE_ROSTER (base); TpHandleSet *set; TpHandleRepoIface *contact_repo = tp_base_connection_get_handles ( (TpBaseConnection *) self->priv->conn, TP_HANDLE_TYPE_CONTACT); GHashTableIter iter; gpointer k, v; set = tp_handle_set_new (contact_repo); g_hash_table_iter_init (&iter, self->priv->items); while (g_hash_table_iter_next (&iter, &k, &v)) { GabbleRosterItem *item = v; if (item->blocked) tp_handle_set_add (set, GPOINTER_TO_UINT (k)); } return set; } static gboolean gabble_roster_can_block (TpBaseContactList *base) { GabbleRoster *self = GABBLE_ROSTER (base); return (self->priv->conn->features & GABBLE_CONNECTION_FEATURES_GOOGLE_ROSTER) != 0; } static void gabble_roster_block_contacts_async (TpBaseContactList *base, TpHandleSet *contacts, GAsyncReadyCallback callback, gpointer user_data) { GabbleRoster *self = GABBLE_ROSTER (base); TpIntsetFastIter iter; TpHandle contact; GSimpleAsyncResult *result = gabble_simple_async_countdown_new (self, callback, user_data, gabble_roster_request_subscription_async, 1); tp_intset_fast_iter_init (&iter, tp_handle_set_peek (contacts)); while (tp_intset_fast_iter_next (&iter, &contact)) gabble_roster_handle_set_blocked (self, contact, TRUE, result); gabble_simple_async_countdown_dec (result); g_object_unref (result); } static void gabble_roster_unblock_contacts_async (TpBaseContactList *base, TpHandleSet *contacts, GAsyncReadyCallback callback, gpointer user_data) { GabbleRoster *self = GABBLE_ROSTER (base); TpIntsetFastIter iter; TpHandle contact; GSimpleAsyncResult *result = gabble_simple_async_countdown_new (self, callback, user_data, gabble_roster_request_subscription_async, 1); tp_intset_fast_iter_init (&iter, tp_handle_set_peek (contacts)); while (tp_intset_fast_iter_next (&iter, &contact)) gabble_roster_handle_set_blocked (self, contact, FALSE, result); gabble_simple_async_countdown_dec (result); g_object_unref (result); } static GStrv gabble_roster_dup_groups (TpBaseContactList *base) { GabbleRoster *self = GABBLE_ROSTER (base); TpHandleRepoIface *group_repo = tp_base_connection_get_handles ( (TpBaseConnection *) self->priv->conn, TP_HANDLE_TYPE_GROUP); GPtrArray *ret; if (self->priv->groups != NULL) { TpIntsetFastIter iter; TpHandle group; ret = g_ptr_array_sized_new ( tp_handle_set_size (self->priv->groups) + 1); tp_intset_fast_iter_init (&iter, tp_handle_set_peek (self->priv->groups)); while (tp_intset_fast_iter_next (&iter, &group)) { g_ptr_array_add (ret, g_strdup (tp_handle_inspect (group_repo, group))); } } else { ret = g_ptr_array_sized_new (1); } g_ptr_array_add (ret, NULL); return (GStrv) g_ptr_array_free (ret, FALSE); } static GStrv gabble_roster_dup_contact_groups (TpBaseContactList *base, TpHandle contact) { GabbleRoster *self = GABBLE_ROSTER (base); GPtrArray *ret; GabbleRosterItem *item = _gabble_roster_item_lookup (self, contact); if (item != NULL && item->groups != NULL) { TpHandleRepoIface *group_repo = tp_base_connection_get_handles ( (TpBaseConnection *) self->priv->conn, TP_HANDLE_TYPE_GROUP); TpIntsetFastIter iter; TpHandle group; ret = g_ptr_array_sized_new (tp_handle_set_size (item->groups) + 1); tp_intset_fast_iter_init (&iter, tp_handle_set_peek (item->groups)); while (tp_intset_fast_iter_next (&iter, &group)) { g_ptr_array_add (ret, g_strdup (tp_handle_inspect (group_repo, group))); } } else { ret = g_ptr_array_sized_new (1); } g_ptr_array_add (ret, NULL); return (GStrv) g_ptr_array_free (ret, FALSE); } static TpHandleSet * gabble_roster_dup_group_members (TpBaseContactList *base, const gchar *group) { GabbleRoster *self = GABBLE_ROSTER (base); TpHandleSet *set; TpHandleRepoIface *contact_repo = tp_base_connection_get_handles ( (TpBaseConnection *) self->priv->conn, TP_HANDLE_TYPE_CONTACT); TpHandleRepoIface *group_repo = tp_base_connection_get_handles ( (TpBaseConnection *) self->priv->conn, TP_HANDLE_TYPE_GROUP); TpHandle group_handle; GHashTableIter iter; gpointer k, v; set = tp_handle_set_new (contact_repo); g_hash_table_iter_init (&iter, self->priv->items); group_handle = tp_handle_lookup (group_repo, group, NULL, NULL); if (G_UNLIKELY (group_handle == 0)) { /* clearly it doesn't have members */ return set; } while (g_hash_table_iter_next (&iter, &k, &v)) { GabbleRosterItem *item = v; if (item->groups != NULL && tp_handle_set_is_member (item->groups, group_handle)) tp_handle_set_add (set, GPOINTER_TO_UINT (k)); } return set; } static void gabble_roster_set_contact_groups_async (TpBaseContactList *base, TpHandle contact, const gchar * const *groups, gsize n, GAsyncReadyCallback callback, gpointer user_data) { GabbleRoster *self = GABBLE_ROSTER (base); GabbleRosterItem *item = _gabble_roster_item_ensure (self, contact); TpHandleRepoIface *contact_repo = tp_base_connection_get_handles ( (TpBaseConnection *) self->priv->conn, TP_HANDLE_TYPE_CONTACT); TpHandleRepoIface *group_repo = tp_base_connection_get_handles ( (TpBaseConnection *) self->priv->conn, TP_HANDLE_TYPE_GROUP); TpHandleSet *groups_set = tp_handle_set_new (group_repo); GPtrArray *groups_created = g_ptr_array_new (); guint i; GSimpleAsyncResult *result = gabble_simple_async_countdown_new (self, callback, user_data, gabble_roster_request_subscription_async, 1); for (i = 0; i < n; i++) { TpHandle group_handle = tp_handle_ensure (group_repo, groups[i], NULL, NULL); if (G_UNLIKELY (group_handle == 0)) continue; tp_handle_set_add (groups_set, group_handle); if (!tp_handle_set_is_member (self->priv->groups, group_handle)) { tp_handle_set_add (self->priv->groups, group_handle); g_ptr_array_add (groups_created, (gchar *) groups[i]); } } if (groups_created->len > 0) { tp_base_contact_list_groups_created (base, (const gchar * const *) groups_created->pdata, groups_created->len); } g_ptr_array_unref (groups_created); if (item->unsent_edits == NULL) item->unsent_edits = item_edit_new (contact_repo, contact); DEBUG ("queue edit to contact#%u - set %" G_GSIZE_FORMAT "contact groups", contact, n); tp_clear_pointer (&item->unsent_edits->add_to_groups, tp_handle_set_destroy); item->unsent_edits->add_to_groups = groups_set; item->unsent_edits->remove_from_all_other_groups = TRUE; tp_clear_pointer (&item->unsent_edits->remove_from_groups, tp_handle_set_destroy); gabble_simple_async_countdown_inc (result); item->unsent_edits->results = g_slist_prepend ( item->unsent_edits->results, g_object_ref (result)); /* maybe we can apply the edit immediately? */ roster_item_apply_edits (self, contact, item); gabble_simple_async_countdown_dec (result); g_object_unref (result); } static void gabble_roster_set_group_members_async (TpBaseContactList *base, const gchar *group, TpHandleSet *contacts, GAsyncReadyCallback callback, gpointer user_data) { GabbleRoster *self = GABBLE_ROSTER (base); TpHandleRepoIface *group_repo = tp_base_connection_get_handles ( (TpBaseConnection *) self->priv->conn, TP_HANDLE_TYPE_GROUP); TpHandle group_handle = tp_handle_ensure (group_repo, group, NULL, NULL); GSimpleAsyncResult *result = gabble_simple_async_countdown_new (self, callback, user_data, gabble_roster_set_group_members_async, 1); GHashTableIter iter; gpointer k; /* You can't add people to an invalid group. */ if (G_UNLIKELY (group_handle == 0)) { g_simple_async_result_set_error (result, TP_ERROR, TP_ERROR_INVALID_ARGUMENT, "Invalid group name: %s", group); goto finally; } /* we create the group even if @contacts is empty, as the base class * requires */ if (!tp_handle_set_is_member (self->priv->groups, group_handle)) { tp_handle_set_add (self->priv->groups, group_handle); tp_base_contact_list_groups_created (base, &group, 1); } g_hash_table_iter_init (&iter, self->priv->items); while (g_hash_table_iter_next (&iter, &k, NULL)) { TpHandle contact = GPOINTER_TO_UINT (k); if (tp_handle_set_is_member (contacts, contact)) gabble_roster_handle_add_to_group (self, contact, group_handle, result); else gabble_roster_handle_remove_from_group (self, contact, group_handle, result); } finally: gabble_simple_async_countdown_dec (result); g_object_unref (result); } static void gabble_roster_add_to_group_async (TpBaseContactList *base, const gchar *group, TpHandleSet *contacts, GAsyncReadyCallback callback, gpointer user_data) { GabbleRoster *self = GABBLE_ROSTER (base); TpIntsetFastIter iter; TpHandle contact; TpHandleRepoIface *group_repo = tp_base_connection_get_handles ( (TpBaseConnection *) self->priv->conn, TP_HANDLE_TYPE_GROUP); TpHandle group_handle = tp_handle_ensure (group_repo, group, NULL, NULL); GSimpleAsyncResult *result = gabble_simple_async_countdown_new (self, callback, user_data, gabble_roster_add_to_group_async, 1); /* You can't add people to an invalid group. */ if (G_UNLIKELY (group_handle == 0)) { g_simple_async_result_set_error (result, TP_ERROR, TP_ERROR_INVALID_ARGUMENT, "Invalid group name: %s", group); goto finally; } /* we create the group even if @contacts is empty, as the base class * requires */ if (!tp_handle_set_is_member (self->priv->groups, group_handle)) { tp_handle_set_add (self->priv->groups, group_handle); tp_base_contact_list_groups_created (base, &group, 1); } tp_intset_fast_iter_init (&iter, tp_handle_set_peek (contacts)); while (tp_intset_fast_iter_next (&iter, &contact)) { /* we ignore any NetworkError */ gabble_roster_handle_add_to_group (self, contact, group_handle, result); } finally: gabble_simple_async_countdown_dec (result); g_object_unref (result); } static void gabble_roster_remove_from_group_async (TpBaseContactList *base, const gchar *group, TpHandleSet *contacts, GAsyncReadyCallback callback, gpointer user_data) { GabbleRoster *self = GABBLE_ROSTER (base); TpIntsetFastIter iter; TpHandle contact; TpHandleRepoIface *group_repo = tp_base_connection_get_handles ( (TpBaseConnection *) self->priv->conn, TP_HANDLE_TYPE_GROUP); TpHandle group_handle = tp_handle_lookup (group_repo, group, NULL, NULL); GSimpleAsyncResult *result = gabble_simple_async_countdown_new (self, callback, user_data, gabble_roster_remove_from_group_async, 1); /* if the group didn't exist then we have nothing to do */ if (group_handle == 0) goto finally; tp_intset_fast_iter_init (&iter, tp_handle_set_peek (contacts)); while (tp_intset_fast_iter_next (&iter, &contact)) { gabble_roster_handle_remove_from_group (self, contact, group_handle, result); } finally: gabble_simple_async_countdown_dec (result); g_object_unref (result); } typedef struct { TpHandle group_handle; GAsyncReadyCallback callback; gpointer user_data; TpHandleSet *contacts; } RemoveGroupContext; static void gabble_roster_remove_group_removed_cb (GObject *source, GAsyncResult *result, gpointer user_data) { GabbleRoster *self = GABBLE_ROSTER (source); RemoveGroupContext *context = user_data; if (context->group_handle != 0) { TpHandleRepoIface *group_repo = tp_base_connection_get_handles ( (TpBaseConnection *) self->priv->conn, TP_HANDLE_TYPE_GROUP); const gchar *group = tp_handle_inspect (group_repo, context->group_handle); GHashTableIter iter; gpointer k, v; TpHandle remaining_member = 0; /* Now that we've signalled the group being removed, to be internally * consistent we should believe that the contacts are no longer there; * if a subsequent roster push says they *are* there, we'll just put * them back. * * However, if the group has members that we didn't remove (because * members were added since we sent off the removal requests), we can't * really remove the group. * * We defer the contact removal until after we've signalled group * removal, so that TpBaseContactList can see who used to be in the * group. */ g_hash_table_iter_init (&iter, self->priv->items); while (g_hash_table_iter_next (&iter, &k, &v)) { TpHandle contact = GPOINTER_TO_UINT (k); GabbleRosterItem *item = v; if (item->groups != NULL && tp_handle_set_is_member (item->groups, context->group_handle)) { if (!tp_handle_set_is_member (context->contacts, contact)) remaining_member = contact; } } if (remaining_member == 0) { tp_handle_set_remove (self->priv->groups, context->group_handle); tp_base_contact_list_groups_removed ((TpBaseContactList *) self, &group, 1); g_hash_table_iter_init (&iter, self->priv->items); while (g_hash_table_iter_next (&iter, NULL, &v)) { GabbleRosterItem *item = v; if (item->groups != NULL && tp_handle_set_is_member (item->groups, context->group_handle)) tp_handle_set_remove (item->groups, context->group_handle); } } else { DEBUG ("contact #%u is still a member of group '%s', not removing", remaining_member, group); } } context->callback (source, result, context->user_data); tp_clear_pointer (&context->contacts, tp_handle_set_destroy); g_slice_free (RemoveGroupContext, context); } static void gabble_roster_remove_group_async (TpBaseContactList *base, const gchar *group, GAsyncReadyCallback callback, gpointer user_data) { GabbleRoster *self = GABBLE_ROSTER (base); GHashTableIter iter; gpointer k, v; TpHandleRepoIface *contact_repo = tp_base_connection_get_handles ( (TpBaseConnection *) self->priv->conn, TP_HANDLE_TYPE_CONTACT); TpHandleRepoIface *group_repo = tp_base_connection_get_handles ( (TpBaseConnection *) self->priv->conn, TP_HANDLE_TYPE_GROUP); GSimpleAsyncResult *result; RemoveGroupContext *context; context = g_slice_new0 (RemoveGroupContext); context->group_handle = tp_handle_lookup (group_repo, group, NULL, NULL); context->callback = callback; context->user_data = user_data; context->contacts = tp_handle_set_new (contact_repo); result = gabble_simple_async_countdown_new (self, gabble_roster_remove_group_removed_cb, context, gabble_roster_remove_group_async, 1); /* if the group didn't exist then we have nothing to do */ if (context->group_handle == 0 || !tp_handle_set_is_member (self->priv->groups, context->group_handle)) goto finally; g_hash_table_iter_init (&iter, self->priv->items); while (g_hash_table_iter_next (&iter, &k, &v)) { TpHandle contact = GPOINTER_TO_UINT (k); GabbleRosterItem *item = v; if (item->groups != NULL && tp_handle_set_is_member (item->groups, context->group_handle)) { tp_handle_set_add (context->contacts, contact); gabble_roster_handle_remove_from_group (self, contact, context->group_handle, result); } } finally: gabble_simple_async_countdown_dec (result); g_object_unref (result); } static void mutable_iface_init (TpMutableContactListInterface *iface) { iface->request_subscription_async = gabble_roster_request_subscription_async; iface->authorize_publication_async = gabble_roster_authorize_publication_async; iface->store_contacts_async = gabble_roster_store_contacts_async; iface->remove_contacts_async = gabble_roster_remove_contacts_async; iface->unsubscribe_async = gabble_roster_unsubscribe_async; iface->unpublish_async = gabble_roster_unpublish_async; /* we use the default _finish functions, which assume a GSimpleAsyncResult */ } static void blockable_iface_init (TpBlockableContactListInterface *iface) { iface->can_block = gabble_roster_can_block; iface->dup_blocked_contacts = gabble_roster_dup_blocked_contacts; iface->block_contacts_async = gabble_roster_block_contacts_async; iface->unblock_contacts_async = gabble_roster_unblock_contacts_async; /* we use the default _finish functions, which assume a GSimpleAsyncResult */ } static void contact_groups_iface_init (TpContactGroupListInterface *iface) { iface->dup_groups = gabble_roster_dup_groups; iface->dup_contact_groups = gabble_roster_dup_contact_groups; iface->dup_group_members = gabble_roster_dup_group_members; } static void mutable_contact_groups_iface_init (TpMutableContactGroupListInterface *iface) { iface->set_contact_groups_async = gabble_roster_set_contact_groups_async; iface->set_group_members_async = gabble_roster_set_group_members_async; iface->add_to_group_async = gabble_roster_add_to_group_async; iface->remove_from_group_async = gabble_roster_remove_from_group_async; iface->remove_group_async = gabble_roster_remove_group_async; /* we use the default _finish functions, which assume a GSimpleAsyncResult */ } static void gabble_roster_class_init (GabbleRosterClass *cls) { GObjectClass *object_class = G_OBJECT_CLASS (cls); TpBaseContactListClass *base_class = TP_BASE_CONTACT_LIST_CLASS (cls); g_type_class_add_private (cls, sizeof (GabbleRosterPrivate)); object_class->constructed = gabble_roster_constructed; object_class->dispose = gabble_roster_dispose; object_class->finalize = gabble_roster_finalize; base_class->dup_states = gabble_roster_dup_states; base_class->dup_contacts = gabble_roster_dup_contacts; base_class->download_async = gabble_roster_download_async; signals[NICKNAMES_UPDATE] = g_signal_new ( "nicknames-update", G_TYPE_FROM_CLASS (cls), G_SIGNAL_RUN_LAST, 0, NULL, NULL, g_cclosure_marshal_VOID__BOXED, G_TYPE_NONE, 1, DBUS_TYPE_G_UINT_ARRAY); } gboolean gabble_roster_handle_sends_presence_to_us (GabbleRoster *self, TpHandle handle) { TpHandleRepoIface *contact_repo = tp_base_connection_get_handles ( (TpBaseConnection *) self->priv->conn, TP_HANDLE_TYPE_CONTACT); GabbleRosterItem *item; g_return_val_if_fail (GABBLE_IS_ROSTER (self), FALSE); g_return_val_if_fail (tp_handle_is_valid (contact_repo, handle, NULL), FALSE); item = _gabble_roster_item_lookup (self, handle); if (item == NULL) return FALSE; return (item->subscription == GABBLE_ROSTER_SUBSCRIPTION_TO || item->subscription == GABBLE_ROSTER_SUBSCRIPTION_BOTH); } gboolean gabble_roster_handle_gets_presence_from_us (GabbleRoster *self, TpHandle handle) { TpHandleRepoIface *contact_repo = tp_base_connection_get_handles ( (TpBaseConnection *) self->priv->conn, TP_HANDLE_TYPE_CONTACT); GabbleRosterItem *item; g_return_val_if_fail (GABBLE_IS_ROSTER (self), FALSE); g_return_val_if_fail (tp_handle_is_valid (contact_repo, handle, NULL), FALSE); item = _gabble_roster_item_lookup (self, handle); if (item == NULL) return FALSE; if (item->blocked) return FALSE; return (item->subscription == GABBLE_ROSTER_SUBSCRIPTION_FROM || item->subscription == GABBLE_ROSTER_SUBSCRIPTION_BOTH); } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������telepathy-gabble-0.18.2/src/roster.h����������������������������������������������������������������0000644�0001750�0001750�00000004607�12200204333�017267� 0����������������������������������������������������������������������������������������������������ustar�00smcv����������������������������smcv����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * roster.h - Headers for Gabble roster helper * * Copyright © 2006–2010 Collabora Ltd. * Copyright © 2006–2010 Nokia Corporation * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 __ROSTER_H__ #define __ROSTER_H__ #include <glib-object.h> #include <telepathy-glib/telepathy-glib.h> #include "types.h" G_BEGIN_DECLS typedef struct _GabbleRosterPrivate GabbleRosterPrivate; typedef struct _GabbleRosterClass GabbleRosterClass; GType gabble_roster_get_type (void); /* TYPE MACROS */ #define GABBLE_TYPE_ROSTER \ (gabble_roster_get_type ()) #define GABBLE_ROSTER(obj) \ (G_TYPE_CHECK_INSTANCE_CAST((obj), GABBLE_TYPE_ROSTER, GabbleRoster)) #define GABBLE_ROSTER_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST((klass), GABBLE_TYPE_ROSTER, GabbleRosterClass)) #define GABBLE_IS_ROSTER(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE((obj), GABBLE_TYPE_ROSTER)) #define GABBLE_IS_ROSTER_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE((klass), GABBLE_TYPE_ROSTER)) #define GABBLE_ROSTER_GET_CLASS(obj) \ (G_TYPE_INSTANCE_GET_CLASS ((obj), GABBLE_TYPE_ROSTER, GabbleRosterClass)) struct _GabbleRosterClass { TpBaseContactListClass parent_class; }; struct _GabbleRoster { TpBaseContactList parent; GabbleRosterPrivate *priv; }; GabbleRoster *gabble_roster_new (GabbleConnection *); gboolean gabble_roster_handle_sends_presence_to_us (GabbleRoster *, TpHandle); gboolean gabble_roster_handle_gets_presence_from_us (GabbleRoster *, TpHandle); const gchar *gabble_roster_handle_get_name (GabbleRoster *, TpHandle); gboolean gabble_roster_handle_set_name (GabbleRoster *, TpHandle, const gchar *, GError **); gboolean gabble_roster_handle_has_entry (GabbleRoster *, TpHandle); G_END_DECLS #endif /* __ROSTER_H__ */ �������������������������������������������������������������������������������������������������������������������������telepathy-gabble-0.18.2/src/request-pipeline.c������������������������������������������������������0000644�0001750�0001750�00000030230�12200204333�021226� 0����������������������������������������������������������������������������������������������������ustar�00smcv����������������������������smcv����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * request-pipeline.c - Pipeline logic for XMPP requests * * Copyright (C) 2007 Collabora Ltd. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 "config.h" #include "request-pipeline.h" #include <telepathy-glib/telepathy-glib.h> #define DEBUG_FLAG GABBLE_DEBUG_PIPELINE #include "connection.h" #include "debug.h" #include "util.h" #define DEFAULT_REQUEST_TIMEOUT 180 #define REQUEST_PIPELINE_SIZE 10 /* Properties */ enum { PROP_CONNECTION = 1, LAST_PROPERTY }; G_DEFINE_TYPE (GabbleRequestPipeline, gabble_request_pipeline, G_TYPE_OBJECT); struct _GabbleRequestPipelineItem { GabbleRequestPipeline *pipeline; WockyStanza *message; guint timer_id; guint timeout; gboolean in_flight; gboolean zombie; GabbleRequestPipelineCb callback; gpointer user_data; }; struct _GabbleRequestPipelinePrivate { GabbleConnection *connection; GSList *pending_items; GSList *items_in_flight; /* Zombie storage (items which were cancelled while the IQ was in flight) */ GSList *crypt_items; gboolean dispose_has_run; }; GQuark gabble_request_pipeline_error_quark (void) { static GQuark quark = 0; if (!quark) quark = g_quark_from_static_string ("gabble-request-pipeline-error"); return quark; } #define GABBLE_REQUEST_PIPELINE_GET_PRIVATE(o) ((o)->priv) static void gabble_request_pipeline_init (GabbleRequestPipeline *obj) { GabbleRequestPipelinePrivate *priv = G_TYPE_INSTANCE_GET_PRIVATE (obj, GABBLE_TYPE_REQUEST_PIPELINE, GabbleRequestPipelinePrivate); obj->priv = priv; } static void gabble_request_pipeline_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec); static void gabble_request_pipeline_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec); static void gabble_request_pipeline_dispose (GObject *object); static void gabble_request_pipeline_finalize (GObject *object); static void gabble_request_pipeline_go (GabbleRequestPipeline *pipeline); static void gabble_request_pipeline_class_init (GabbleRequestPipelineClass *cls) { GObjectClass *object_class = G_OBJECT_CLASS (cls); GParamSpec *param_spec; g_type_class_add_private (cls, sizeof (GabbleRequestPipelinePrivate)); object_class->get_property = gabble_request_pipeline_get_property; object_class->set_property = gabble_request_pipeline_set_property; object_class->dispose = gabble_request_pipeline_dispose; object_class->finalize = gabble_request_pipeline_finalize; param_spec = g_param_spec_object ("connection", "GabbleConnection object", "Gabble connection object that owns this request pipeline helper " "object.", GABBLE_TYPE_CONNECTION, G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, PROP_CONNECTION, param_spec); } static void gabble_request_pipeline_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { GabbleRequestPipeline *chan = GABBLE_REQUEST_PIPELINE (object); GabbleRequestPipelinePrivate *priv = GABBLE_REQUEST_PIPELINE_GET_PRIVATE (chan); switch (property_id) { case PROP_CONNECTION: g_value_set_object (value, priv->connection); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void gabble_request_pipeline_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { GabbleRequestPipeline *chan = GABBLE_REQUEST_PIPELINE (object); GabbleRequestPipelinePrivate *priv = GABBLE_REQUEST_PIPELINE_GET_PRIVATE (chan); switch (property_id) { case PROP_CONNECTION: priv->connection = g_value_get_object (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } GabbleRequestPipeline * gabble_request_pipeline_new (GabbleConnection *conn) { GabbleRequestPipeline *self; g_return_val_if_fail (GABBLE_IS_CONNECTION (conn), NULL); self = GABBLE_REQUEST_PIPELINE (g_object_new (GABBLE_TYPE_REQUEST_PIPELINE, "connection", conn, NULL)); return self; } static void delete_item (GabbleRequestPipelineItem *item) { GabbleRequestPipelinePrivate *priv; g_assert (GABBLE_IS_REQUEST_PIPELINE (item->pipeline)); priv = GABBLE_REQUEST_PIPELINE_GET_PRIVATE (item->pipeline); DEBUG ("deleting item %p", item); if (item->zombie) { priv->crypt_items = g_slist_remove (priv->crypt_items, item); } else if (item->in_flight) { priv->items_in_flight = g_slist_remove (priv->items_in_flight, item); } else { priv->pending_items = g_slist_remove (priv->pending_items, item); } if (item->timer_id) g_source_remove (item->timer_id); tp_clear_pointer (&item->message, g_object_unref); g_slice_free (GabbleRequestPipelineItem, item); } static void gabble_request_pipeline_create_zombie (GabbleRequestPipeline *pipeline, GabbleRequestPipelineItem *item, GError *error) { GabbleRequestPipelinePrivate *priv = pipeline->priv; g_assert (!item->zombie); if (item->timer_id != 0) { g_source_remove (item->timer_id); item->timer_id = 0; } (item->callback) (priv->connection, NULL, item->user_data, error); if (item->in_flight) { item->zombie = TRUE; priv->items_in_flight = g_slist_remove (priv->items_in_flight, item); priv->crypt_items = g_slist_prepend (priv->crypt_items, item); gabble_request_pipeline_go (pipeline); } else { delete_item (item); } } void gabble_request_pipeline_item_cancel (GabbleRequestPipelineItem *item) { GError cancelled = { GABBLE_REQUEST_PIPELINE_ERROR, GABBLE_REQUEST_PIPELINE_ERROR_CANCELLED, "Request cancelled" }; gabble_request_pipeline_create_zombie (item->pipeline, item, &cancelled); } static void gabble_request_pipeline_flush (GabbleRequestPipeline *self, GSList **list) { GabbleRequestPipelineItem *item; GError disconnected = { TP_ERROR, TP_ERROR_DISCONNECTED, "Request failed because connection became disconnected" }; while (*list != NULL) { item = (*list)->data; if (!item->zombie) (item->callback) (self->priv->connection, NULL, item->user_data, &disconnected); delete_item (item); } } static void gabble_request_pipeline_dispose (GObject *object) { GabbleRequestPipeline *self = GABBLE_REQUEST_PIPELINE (object); GabbleRequestPipelinePrivate *priv = GABBLE_REQUEST_PIPELINE_GET_PRIVATE (self); if (priv->dispose_has_run) return; priv->dispose_has_run = TRUE; DEBUG ("disposing request-pipeline"); gabble_request_pipeline_flush (self, &priv->items_in_flight); gabble_request_pipeline_flush (self, &priv->pending_items); gabble_request_pipeline_flush (self, &priv->crypt_items); g_idle_remove_by_data (self); if (G_OBJECT_CLASS (gabble_request_pipeline_parent_class)->dispose) G_OBJECT_CLASS (gabble_request_pipeline_parent_class)->dispose (object); } static void gabble_request_pipeline_finalize (GObject *object) { G_OBJECT_CLASS (gabble_request_pipeline_parent_class)->finalize (object); } static void response_cb (GabbleConnection *conn, WockyStanza *sent, WockyStanza *reply, GObject *object, gpointer user_data) { GabbleRequestPipelineItem *item = (GabbleRequestPipelineItem *) user_data; GabbleRequestPipeline *pipeline = item->pipeline; GabbleRequestPipelinePrivate *priv; g_assert (GABBLE_IS_REQUEST_PIPELINE (pipeline)); priv = GABBLE_REQUEST_PIPELINE_GET_PRIVATE (pipeline); DEBUG ("got reply for request %p", item); if (NULL == g_slist_find (priv->items_in_flight, item)) return; g_assert (item->in_flight); priv->items_in_flight = g_slist_remove (priv->items_in_flight, item); if (!item->zombie) { GError *error = NULL; wocky_stanza_extract_errors (reply, NULL, &error, NULL, NULL); item->callback (priv->connection, reply, item->user_data, error); g_clear_error (&error); } else { DEBUG ("ignoring zombie connection reply"); } delete_item (item); gabble_request_pipeline_go (pipeline); } static gboolean timeout_cb (gpointer data) { GabbleRequestPipelineItem *item = (GabbleRequestPipelineItem *) data; GError timed_out = { GABBLE_REQUEST_PIPELINE_ERROR, GABBLE_REQUEST_PIPELINE_ERROR_TIMEOUT, "Request timed out" }; gabble_request_pipeline_create_zombie (item->pipeline, item, &timed_out); return FALSE; } static void send_next_request (GabbleRequestPipeline *pipeline) { GabbleRequestPipelinePrivate *priv = GABBLE_REQUEST_PIPELINE_GET_PRIVATE (pipeline); GabbleRequestPipelineItem *item; GError *error = NULL; if (priv->pending_items == NULL) return; item = priv->pending_items->data; DEBUG ("processing request %p", item); g_assert (item->in_flight == FALSE); priv->pending_items = g_slist_remove (priv->pending_items, item); if (!_gabble_connection_send_with_reply (priv->connection, item->message, response_cb, G_OBJECT (pipeline), item, &error)) { item->callback (priv->connection, NULL, item->user_data, error); delete_item (item); send_next_request (pipeline); } else { priv->items_in_flight = g_slist_prepend (priv->items_in_flight, item); item->in_flight = TRUE; item->timer_id = g_timeout_add_seconds (item->timeout, timeout_cb, item); } } static void gabble_request_pipeline_go (GabbleRequestPipeline *pipeline) { GabbleRequestPipelinePrivate *priv = GABBLE_REQUEST_PIPELINE_GET_PRIVATE (pipeline); DEBUG ("called; %d pending items, %d items in flight", g_slist_length (priv->pending_items), g_slist_length (priv->items_in_flight)); while (priv->pending_items && (g_slist_length (priv->items_in_flight) < REQUEST_PIPELINE_SIZE)) { send_next_request (pipeline); } } static gboolean delayed_run_pipeline (gpointer user_data) { GabbleRequestPipeline *pipeline = (GabbleRequestPipeline *) user_data; gabble_request_pipeline_go (pipeline); return FALSE; } GabbleRequestPipelineItem * gabble_request_pipeline_enqueue (GabbleRequestPipeline *pipeline, WockyStanza *msg, guint timeout, GabbleRequestPipelineCb callback, gpointer user_data) { GabbleRequestPipelinePrivate *priv = GABBLE_REQUEST_PIPELINE_GET_PRIVATE (pipeline); GabbleRequestPipelineItem *item = g_slice_new0 (GabbleRequestPipelineItem); g_return_val_if_fail (callback != NULL, NULL); item->pipeline = pipeline; item->message = msg; if (timeout == 0) timeout = DEFAULT_REQUEST_TIMEOUT; item->timeout = timeout; item->in_flight = FALSE; item->callback = callback; item->user_data = user_data; g_object_ref (msg); priv->pending_items = g_slist_append (priv->pending_items, item); DEBUG ("enqueued new request as item %p", item); DEBUG ("number of items in flight: %d", g_slist_length (priv->items_in_flight)); /* If the pipeline isn't full, schedule a run. Run it delayed so that if * there's an error, the callback will be called after this function returns. */ if (g_slist_length (priv->items_in_flight) < REQUEST_PIPELINE_SIZE) gabble_idle_add_weak (delayed_run_pipeline, G_OBJECT (pipeline)); return item; } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������telepathy-gabble-0.18.2/src/request-pipeline.h������������������������������������������������������0000644�0001750�0001750�00000006177�12200204333�021250� 0����������������������������������������������������������������������������������������������������ustar�00smcv����������������������������smcv����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * request-pipeline.h - Pipeline logic for XMPP requests * * Copyright (C) 2007 Collabora Ltd. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 __GABBLE_REQUEST_PIPELINE_H__ #define __GABBLE_REQUEST_PIPELINE_H__ #include <glib-object.h> #include <wocky/wocky.h> #include "types.h" G_BEGIN_DECLS typedef struct _GabbleRequestPipelinePrivate GabbleRequestPipelinePrivate; typedef struct _GabbleRequestPipelineClass GabbleRequestPipelineClass; typedef struct _GabbleRequestPipelineItem GabbleRequestPipelineItem; typedef void (*GabbleRequestPipelineCb) (GabbleConnection *conn, WockyStanza *msg, gpointer user_data, GError *error); /** * GabbleRequestPipelineError: * @GABBLE_REQUEST_PIPELINE_ERROR_CANCELLED: The request was cancelled * @GABBLE_REQUEST_PIPELINE_ERROR_TIMEOUT: The request timed out */ typedef enum { GABBLE_REQUEST_PIPELINE_ERROR_CANCELLED, GABBLE_REQUEST_PIPELINE_ERROR_TIMEOUT } GabbleRequestPipelineError; GQuark gabble_request_pipeline_error_quark (void); #define GABBLE_REQUEST_PIPELINE_ERROR gabble_request_pipeline_error_quark () GType gabble_request_pipeline_get_type (void); /* TYPE MACROS */ #define GABBLE_TYPE_REQUEST_PIPELINE \ (gabble_request_pipeline_get_type ()) #define GABBLE_REQUEST_PIPELINE(obj) \ (G_TYPE_CHECK_INSTANCE_CAST ((obj), GABBLE_TYPE_REQUEST_PIPELINE, \ GabbleRequestPipeline)) #define GABBLE_REQUEST_PIPELINE_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST ((klass), GABBLE_TYPE_REQUEST_PIPELINE, \ GabbleRequestPipelineClass)) #define GABBLE_IS_REQUEST_PIPELINE(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GABBLE_TYPE_REQUEST_PIPELINE)) #define GABBLE_IS_REQUEST_PIPELINE_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE ((klass), GABBLE_TYPE_REQUEST_PIPELINE)) #define GABBLE_REQUEST_PIPELINE_GET_CLASS(obj) \ (G_TYPE_INSTANCE_GET_CLASS ((obj), GABBLE_TYPE_REQUEST_PIPELINE, \ GabbleRequestPipelineClass)) struct _GabbleRequestPipelineClass { GObjectClass parent_class; }; struct _GabbleRequestPipeline { GObject parent; GabbleRequestPipelinePrivate *priv; }; GabbleRequestPipeline *gabble_request_pipeline_new (GabbleConnection *conn); GabbleRequestPipelineItem *gabble_request_pipeline_enqueue (GabbleRequestPipeline *pipeline, WockyStanza *msg, guint timeout, GabbleRequestPipelineCb callback, gpointer user_data); void gabble_request_pipeline_item_cancel (GabbleRequestPipelineItem *req); G_END_DECLS #endif �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������telepathy-gabble-0.18.2/src/private-tubes-factory.c�������������������������������������������������0000644�0001750�0001750�00000126002�12227000321�022175� 0����������������������������������������������������������������������������������������������������ustar�00smcv����������������������������smcv����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * private-tubes-factory.c - Source for GabblePrivateTubesFactory * Copyright (C) 2006 Collabora Ltd. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 "config.h" #include "private-tubes-factory.h" #include <string.h> #include <glib-object.h> #include <dbus/dbus-glib.h> #include <dbus/dbus-glib-lowlevel.h> #include <telepathy-glib/telepathy-glib.h> #include <telepathy-glib/telepathy-glib-dbus.h> #include "extensions/extensions.h" #define DEBUG_FLAG GABBLE_DEBUG_TUBES #include "bytestream-factory.h" #include "gabble/caps-channel-manager.h" #include "connection.h" #include "debug.h" #include "muc-channel.h" #include "muc-factory.h" #include "namespaces.h" #include "presence-cache.h" #include "tube-dbus.h" #include "tube-stream.h" #include "util.h" static GabbleTubeIface * new_channel_from_stanza (GabblePrivateTubesFactory *self, WockyStanza *stanza, WockyNode *tube_node, guint64 tube_id, GabbleBytestreamIface *bytestream); static gboolean private_tubes_factory_tube_close_cb ( WockyPorter *porter, WockyStanza *msg, gpointer user_data); static gboolean private_tubes_factory_msg_tube_cb ( WockyPorter *porter, WockyStanza *msg, gpointer user_data); static void channel_manager_iface_init (gpointer, gpointer); static void caps_channel_manager_iface_init (gpointer, gpointer); G_DEFINE_TYPE_WITH_CODE (GabblePrivateTubesFactory, gabble_private_tubes_factory, G_TYPE_OBJECT, G_IMPLEMENT_INTERFACE (TP_TYPE_CHANNEL_MANAGER, channel_manager_iface_init); G_IMPLEMENT_INTERFACE (GABBLE_TYPE_CAPS_CHANNEL_MANAGER, caps_channel_manager_iface_init)); /* properties */ enum { PROP_CONNECTION = 1, LAST_PROPERTY }; struct _GabblePrivateTubesFactoryPrivate { GabbleConnection *conn; gulong status_changed_id; guint msg_tube_cb; guint msg_close_cb; /* guint tube ID => (owned) (GabbleTubeIface *) */ GHashTable *tubes; gboolean dispose_has_run; }; #define GABBLE_PRIVATE_TUBES_FACTORY_GET_PRIVATE(obj) ((obj)->priv) static const gchar * const tubes_channel_fixed_properties[] = { TP_PROP_CHANNEL_CHANNEL_TYPE, TP_PROP_CHANNEL_TARGET_HANDLE_TYPE, NULL }; static const gchar * const old_tubes_channel_allowed_properties[] = { TP_PROP_CHANNEL_TARGET_HANDLE, TP_PROP_CHANNEL_TARGET_ID, NULL }; gboolean gabble_private_tubes_factory_extract_tube_information ( TpHandleRepoIface *contact_repo, WockyNode *tube_node, TpTubeType *type, TpHandle *initiator_handle, const gchar **service, GHashTable **parameters, guint64 *tube_id) { if (type != NULL) { const gchar *_type; _type = wocky_node_get_attribute (tube_node, "type"); if (!tp_strdiff (_type, "stream")) { *type = TP_TUBE_TYPE_STREAM; } else if (!tp_strdiff (_type, "dbus")) { *type = TP_TUBE_TYPE_DBUS; } else { DEBUG ("Unknown tube type: %s", _type); return FALSE; } } if (initiator_handle != NULL) { const gchar *initiator; initiator = wocky_node_get_attribute (tube_node, "initiator"); if (initiator != NULL) { *initiator_handle = tp_handle_ensure (contact_repo, initiator, GUINT_TO_POINTER (GABBLE_JID_ROOM_MEMBER), NULL); if (*initiator_handle == 0) { DEBUG ("invalid initiator JID %s", initiator); return FALSE; } } else { *initiator_handle = 0; } } if (service != NULL) { *service = wocky_node_get_attribute (tube_node, "service"); } if (parameters != NULL) { WockyNode *node; node = wocky_node_get_child (tube_node, "parameters"); *parameters = lm_message_node_extract_properties (node, "parameter"); } if (tube_id != NULL) { const gchar *str; guint64 tmp; str = wocky_node_get_attribute (tube_node, "id"); if (str == NULL) { DEBUG ("no tube id in SI request"); return FALSE; } tmp = g_ascii_strtoull (str, NULL, 10); if (tmp == 0 || tmp > G_MAXUINT32) { DEBUG ("tube id is non-numeric or out of range: %s", str); return FALSE; } *tube_id = tmp; } return TRUE; } static void gabble_private_tubes_factory_init (GabblePrivateTubesFactory *self) { GabblePrivateTubesFactoryPrivate *priv = G_TYPE_INSTANCE_GET_PRIVATE (self, GABBLE_TYPE_PRIVATE_TUBES_FACTORY, GabblePrivateTubesFactoryPrivate); self->priv = priv; priv->tubes = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, (GDestroyNotify) g_object_unref); priv->conn = NULL; priv->dispose_has_run = FALSE; } static void gabble_private_tubes_factory_close_all ( GabblePrivateTubesFactory *fac); static void porter_available_cb ( GabbleConnection *connection, WockyPorter *porter, gpointer user_data) { GabblePrivateTubesFactory *self = GABBLE_PRIVATE_TUBES_FACTORY (user_data); GabblePrivateTubesFactoryPrivate *priv = self->priv; priv->msg_tube_cb = wocky_porter_register_handler_from_anyone (porter, WOCKY_STANZA_TYPE_MESSAGE, WOCKY_STANZA_SUB_TYPE_NONE, WOCKY_PORTER_HANDLER_PRIORITY_MAX, private_tubes_factory_msg_tube_cb, self, '(', "tube", ':', NS_TUBES, ')', NULL); priv->msg_close_cb = wocky_porter_register_handler_from_anyone (porter, WOCKY_STANZA_TYPE_MESSAGE, WOCKY_STANZA_SUB_TYPE_NONE, WOCKY_PORTER_HANDLER_PRIORITY_MAX, private_tubes_factory_tube_close_cb, self, '(', "close", ':', NS_TUBES, ')', NULL); } static void connection_status_changed_cb (GabbleConnection *conn, guint status, guint reason, GabblePrivateTubesFactory *self) { switch (status) { case TP_CONNECTION_STATUS_DISCONNECTED: gabble_private_tubes_factory_close_all (self); break; } } static GObject * gabble_private_tubes_factory_constructor (GType type, guint n_props, GObjectConstructParam *props) { GObject *obj; GabblePrivateTubesFactory *self; GabblePrivateTubesFactoryPrivate *priv; obj = G_OBJECT_CLASS (gabble_private_tubes_factory_parent_class)-> constructor (type, n_props, props); self = GABBLE_PRIVATE_TUBES_FACTORY (obj); priv = GABBLE_PRIVATE_TUBES_FACTORY_GET_PRIVATE (self); self->priv->status_changed_id = g_signal_connect (self->priv->conn, "status-changed", (GCallback) connection_status_changed_cb, obj); tp_g_signal_connect_object (priv->conn, "porter-available", (GCallback) porter_available_cb, obj, 0); return obj; } static void gabble_private_tubes_factory_dispose (GObject *object) { GabblePrivateTubesFactory *fac = GABBLE_PRIVATE_TUBES_FACTORY (object); GabblePrivateTubesFactoryPrivate *priv = GABBLE_PRIVATE_TUBES_FACTORY_GET_PRIVATE (fac); if (priv->dispose_has_run) return; DEBUG ("dispose called"); priv->dispose_has_run = TRUE; gabble_private_tubes_factory_close_all (fac); g_assert (priv->tubes == NULL); if (G_OBJECT_CLASS (gabble_private_tubes_factory_parent_class)->dispose) G_OBJECT_CLASS (gabble_private_tubes_factory_parent_class)->dispose ( object); } static void gabble_private_tubes_factory_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { GabblePrivateTubesFactory *fac = GABBLE_PRIVATE_TUBES_FACTORY (object); GabblePrivateTubesFactoryPrivate *priv = GABBLE_PRIVATE_TUBES_FACTORY_GET_PRIVATE (fac); switch (property_id) { case PROP_CONNECTION: g_value_set_object (value, priv->conn); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void gabble_private_tubes_factory_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { GabblePrivateTubesFactory *fac = GABBLE_PRIVATE_TUBES_FACTORY (object); GabblePrivateTubesFactoryPrivate *priv = GABBLE_PRIVATE_TUBES_FACTORY_GET_PRIVATE (fac); switch (property_id) { case PROP_CONNECTION: priv->conn = g_value_get_object (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void gabble_private_tubes_factory_class_init ( GabblePrivateTubesFactoryClass *gabble_private_tubes_factory_class) { GObjectClass *object_class = G_OBJECT_CLASS ( gabble_private_tubes_factory_class); GParamSpec *param_spec; g_type_class_add_private (gabble_private_tubes_factory_class, sizeof (GabblePrivateTubesFactoryPrivate)); object_class->constructor = gabble_private_tubes_factory_constructor; object_class->dispose = gabble_private_tubes_factory_dispose; object_class->get_property = gabble_private_tubes_factory_get_property; object_class->set_property = gabble_private_tubes_factory_set_property; param_spec = g_param_spec_object ( "connection", "GabbleConnection object", "Gabble connection object that owns this Tubes channel manager object.", GABBLE_TYPE_CONNECTION, G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, PROP_CONNECTION, param_spec); } static void gabble_private_tubes_factory_close_all (GabblePrivateTubesFactory *fac) { GabblePrivateTubesFactoryPrivate *priv = GABBLE_PRIVATE_TUBES_FACTORY_GET_PRIVATE (fac); DEBUG ("closing 1-1 tubes channels"); if (priv->status_changed_id != 0) { g_signal_handler_disconnect (priv->conn, priv->status_changed_id); priv->status_changed_id = 0; } if (priv->msg_tube_cb != 0) { WockyPorter *porter = wocky_session_get_porter (priv->conn->session); wocky_porter_unregister_handler (porter, priv->msg_tube_cb); priv->msg_tube_cb = 0; wocky_porter_unregister_handler (porter, priv->msg_close_cb); priv->msg_close_cb = 0; } tp_clear_pointer (&priv->tubes, g_hash_table_unref); } static void add_service_to_array (const gchar *service, GPtrArray *arr, TpTubeType type, TpHandle handle) { GValue monster = {0, }; GHashTable *fixed_properties; GValue *channel_type_value; GValue *target_handle_type_value; gchar *tube_allowed_properties[] = { TP_PROP_CHANNEL_TARGET_HANDLE, TP_PROP_CHANNEL_TARGET_ID, NULL }; g_assert (type == TP_TUBE_TYPE_STREAM || type == TP_TUBE_TYPE_DBUS); g_value_init (&monster, TP_STRUCT_TYPE_REQUESTABLE_CHANNEL_CLASS); g_value_take_boxed (&monster, dbus_g_type_specialized_construct ( TP_STRUCT_TYPE_REQUESTABLE_CHANNEL_CLASS)); fixed_properties = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, (GDestroyNotify) tp_g_value_slice_free); channel_type_value = tp_g_value_slice_new (G_TYPE_STRING); if (type == TP_TUBE_TYPE_STREAM) g_value_set_static_string (channel_type_value, TP_IFACE_CHANNEL_TYPE_STREAM_TUBE); else g_value_set_static_string (channel_type_value, TP_IFACE_CHANNEL_TYPE_DBUS_TUBE); g_hash_table_insert (fixed_properties, TP_PROP_CHANNEL_CHANNEL_TYPE, channel_type_value); target_handle_type_value = tp_g_value_slice_new (G_TYPE_UINT); g_value_set_uint (target_handle_type_value, TP_HANDLE_TYPE_CONTACT); g_hash_table_insert (fixed_properties, TP_PROP_CHANNEL_TARGET_HANDLE_TYPE, target_handle_type_value); target_handle_type_value = tp_g_value_slice_new (G_TYPE_STRING); g_value_set_string (target_handle_type_value, service); if (type == TP_TUBE_TYPE_STREAM) g_hash_table_insert (fixed_properties, TP_PROP_CHANNEL_TYPE_STREAM_TUBE_SERVICE, target_handle_type_value); else g_hash_table_insert (fixed_properties, TP_PROP_CHANNEL_TYPE_DBUS_TUBE_SERVICE_NAME, target_handle_type_value); dbus_g_type_struct_set (&monster, 0, fixed_properties, 1, tube_allowed_properties, G_MAXUINT); g_hash_table_unref (fixed_properties); g_ptr_array_add (arr, g_value_get_boxed (&monster)); } static void add_generic_tube_caps (GPtrArray *arr) { GValue monster1 = {0,}, monster2 = {0,}; GHashTable *fixed_properties; GValue *channel_type_value; GValue *target_handle_type_value; /* StreamTube */ g_value_init (&monster1, TP_STRUCT_TYPE_REQUESTABLE_CHANNEL_CLASS); g_value_take_boxed (&monster1, dbus_g_type_specialized_construct ( TP_STRUCT_TYPE_REQUESTABLE_CHANNEL_CLASS)); fixed_properties = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, (GDestroyNotify) tp_g_value_slice_free); channel_type_value = tp_g_value_slice_new (G_TYPE_STRING); g_value_set_static_string (channel_type_value, TP_IFACE_CHANNEL_TYPE_STREAM_TUBE); g_hash_table_insert (fixed_properties, TP_PROP_CHANNEL_CHANNEL_TYPE, channel_type_value); target_handle_type_value = tp_g_value_slice_new (G_TYPE_UINT); g_value_set_uint (target_handle_type_value, TP_HANDLE_TYPE_CONTACT); g_hash_table_insert (fixed_properties, TP_PROP_CHANNEL_TARGET_HANDLE_TYPE, target_handle_type_value); dbus_g_type_struct_set (&monster1, 0, fixed_properties, 1, gabble_tube_stream_channel_get_allowed_properties (), G_MAXUINT); g_hash_table_unref (fixed_properties); g_ptr_array_add (arr, g_value_get_boxed (&monster1)); /* DBusTube */ g_value_init (&monster2, TP_STRUCT_TYPE_REQUESTABLE_CHANNEL_CLASS); g_value_take_boxed (&monster2, dbus_g_type_specialized_construct ( TP_STRUCT_TYPE_REQUESTABLE_CHANNEL_CLASS)); fixed_properties = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, (GDestroyNotify) tp_g_value_slice_free); channel_type_value = tp_g_value_slice_new (G_TYPE_STRING); g_value_set_static_string (channel_type_value, TP_IFACE_CHANNEL_TYPE_DBUS_TUBE); g_hash_table_insert (fixed_properties, TP_PROP_CHANNEL_CHANNEL_TYPE, channel_type_value); target_handle_type_value = tp_g_value_slice_new (G_TYPE_UINT); g_value_set_uint (target_handle_type_value, TP_HANDLE_TYPE_CONTACT); g_hash_table_insert (fixed_properties, TP_PROP_CHANNEL_TARGET_HANDLE_TYPE, target_handle_type_value); dbus_g_type_struct_set (&monster2, 0, fixed_properties, 1, gabble_tube_dbus_channel_get_allowed_properties (), G_MAXUINT); g_hash_table_unref (fixed_properties); g_ptr_array_add (arr, g_value_get_boxed (&monster2)); } #define STREAM_CAP_PREFIX (NS_TUBES "/stream#") #define DBUS_CAP_PREFIX (NS_TUBES "/dbus#") typedef struct { gboolean supports_tubes; GPtrArray *arr; TpHandle handle; } GetContactCapsClosure; static void get_contact_caps_foreach (gpointer data, gpointer user_data) { const gchar *ns = data; GetContactCapsClosure *closure = user_data; if (!g_str_has_prefix (ns, NS_TUBES)) return; closure->supports_tubes = TRUE; if (g_str_has_prefix (ns, STREAM_CAP_PREFIX)) add_service_to_array (ns + strlen (STREAM_CAP_PREFIX), closure->arr, TP_TUBE_TYPE_STREAM, closure->handle); else if (g_str_has_prefix (ns, DBUS_CAP_PREFIX)) add_service_to_array (ns + strlen (DBUS_CAP_PREFIX), closure->arr, TP_TUBE_TYPE_DBUS, closure->handle); } static void gabble_private_tubes_factory_get_contact_caps ( GabbleCapsChannelManager *manager, TpHandle handle, const GabbleCapabilitySet *caps, GPtrArray *arr) { GabblePrivateTubesFactory *self = GABBLE_PRIVATE_TUBES_FACTORY (manager); GetContactCapsClosure closure = { FALSE, arr, handle }; TpBaseConnection *base_conn = TP_BASE_CONNECTION (self->priv->conn); /* Always claim that we support tubes. */ closure.supports_tubes = (handle == tp_base_connection_get_self_handle (base_conn)); gabble_capability_set_foreach (caps, get_contact_caps_foreach, &closure); if (closure.supports_tubes) add_generic_tube_caps (arr); } static void gabble_private_tubes_factory_add_cap (GabbleCapsChannelManager *manager, const gchar *client_name, GHashTable *cap, GabbleCapabilitySet *cap_set) { const gchar *channel_type, *service; gchar *ns = NULL; /* capabilities mean being able to RECEIVE said kinds of tubes. hence, * skip Requested=true (locally initiated) channel classes */ if (tp_asv_get_boolean (cap, TP_PROP_CHANNEL_REQUESTED, FALSE)) return; channel_type = tp_asv_get_string (cap, TP_PROP_CHANNEL_CHANNEL_TYPE); /* this channel is not for this factory */ if (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)) return; if (tp_asv_get_uint32 (cap, TP_PROP_CHANNEL_TARGET_HANDLE_TYPE, NULL) != TP_HANDLE_TYPE_CONTACT) return; if (!tp_strdiff (channel_type, TP_IFACE_CHANNEL_TYPE_STREAM_TUBE)) { service = tp_asv_get_string (cap, TP_PROP_CHANNEL_TYPE_STREAM_TUBE_SERVICE); if (service != NULL) ns = g_strconcat (STREAM_CAP_PREFIX, service, NULL); } else if (!tp_strdiff (channel_type, TP_IFACE_CHANNEL_TYPE_DBUS_TUBE)) { service = tp_asv_get_string (cap, TP_PROP_CHANNEL_TYPE_DBUS_TUBE_SERVICE_NAME); if (service != NULL) ns = g_strconcat (DBUS_CAP_PREFIX, service, NULL); } if (ns != NULL) { DEBUG ("%s: adding capability %s", client_name, ns); gabble_capability_set_add (cap_set, ns); g_free (ns); } } static void gabble_private_tubes_factory_represent_client ( GabbleCapsChannelManager *manager, const gchar *client_name, const GPtrArray *filters, const gchar * const *cap_tokens, GabbleCapabilitySet *cap_set, GPtrArray *data_forms) { guint i; for (i = 0; i < filters->len; i++) { gabble_private_tubes_factory_add_cap (manager, client_name, g_ptr_array_index (filters, i), cap_set); } } struct _ForeachData { TpExportableChannelFunc foreach; gpointer user_data; }; static void _foreach_slave (gpointer key, gpointer value, gpointer user_data) { struct _ForeachData *data = user_data; TpExportableChannel *chan = TP_EXPORTABLE_CHANNEL (value); data->foreach (chan, data->user_data); } static void gabble_private_tubes_factory_foreach_channel (TpChannelManager *manager, TpExportableChannelFunc foreach, gpointer user_data) { GabblePrivateTubesFactory *fac = GABBLE_PRIVATE_TUBES_FACTORY (manager); GabblePrivateTubesFactoryPrivate *priv = GABBLE_PRIVATE_TUBES_FACTORY_GET_PRIVATE (fac); struct _ForeachData data; data.user_data = user_data; data.foreach = foreach; g_hash_table_foreach (priv->tubes, _foreach_slave, &data); } void gabble_private_tubes_factory_handle_si_tube_request ( GabblePrivateTubesFactory *self, GabbleBytestreamIface *bytestream, TpHandle handle, const gchar *stream_id, WockyStanza *msg) { GabblePrivateTubesFactoryPrivate *priv = self->priv; TpHandleRepoIface *contact_repo = tp_base_connection_get_handles ( (TpBaseConnection *) priv->conn, TP_HANDLE_TYPE_CONTACT); WockyNode *si_node, *tube_node; WockyStanzaType stanza_type; WockyStanzaSubType sub_type; guint64 tube_id; GabbleTubeIface *tube; DEBUG ("contact#%u stream %s", handle, stream_id); g_return_if_fail (tp_handle_is_valid (contact_repo, handle, NULL)); wocky_stanza_get_type_info (msg, &stanza_type, &sub_type); g_return_if_fail (stanza_type == WOCKY_STANZA_TYPE_IQ); g_return_if_fail (sub_type == WOCKY_STANZA_SUB_TYPE_SET); si_node = wocky_node_get_child_ns ( wocky_stanza_get_top_node (msg), "si", NS_SI); g_return_if_fail (si_node != NULL); tube_node = wocky_node_get_child_ns (si_node, "tube", NS_TUBES); g_return_if_fail (tube_node != NULL); if (!gabble_private_tubes_factory_extract_tube_information ( contact_repo, tube_node, NULL, NULL, NULL, NULL, &tube_id)) { GError e = { WOCKY_XMPP_ERROR, WOCKY_XMPP_ERROR_BAD_REQUEST, "<tube> has no id attribute" }; NODE_DEBUG (tube_node, e.message); gabble_bytestream_iface_close (bytestream, &e); return; } tube = g_hash_table_lookup (priv->tubes, GUINT_TO_POINTER (tube_id)); if (tube != NULL) { GError e = { WOCKY_XMPP_ERROR, WOCKY_XMPP_ERROR_BAD_REQUEST, "tube ID already in use" }; NODE_DEBUG (tube_node, e.message); gabble_bytestream_iface_close (bytestream, &e); return; } /* New tube */ tube = new_channel_from_stanza (self, msg, tube_node, tube_id, bytestream); } void gabble_private_tubes_factory_handle_si_stream_request ( GabblePrivateTubesFactory *self, GabbleBytestreamIface *bytestream, TpHandle handle, const gchar *stream_id, WockyStanza *msg) { GabblePrivateTubesFactoryPrivate *priv = GABBLE_PRIVATE_TUBES_FACTORY_GET_PRIVATE (self); TpHandleRepoIface *contact_repo = tp_base_connection_get_handles ( (TpBaseConnection *) priv->conn, TP_HANDLE_TYPE_CONTACT); const gchar *tmp; guint64 tube_id; WockyNode *si_node, *stream_node; GabbleTubeIface *tube; WockyStanzaType stanza_type; WockyStanzaSubType sub_type; DEBUG ("contact#%u stream %s", handle, stream_id); g_return_if_fail (tp_handle_is_valid (contact_repo, handle, NULL)); wocky_stanza_get_type_info (msg, &stanza_type, &sub_type); g_return_if_fail (stanza_type == WOCKY_STANZA_TYPE_IQ); g_return_if_fail (sub_type == WOCKY_STANZA_SUB_TYPE_SET); si_node = wocky_node_get_child_ns ( wocky_stanza_get_top_node (msg), "si", NS_SI); g_return_if_fail (si_node != NULL); stream_node = wocky_node_get_child_ns (si_node, "stream", NS_TUBES); g_return_if_fail (stream_node != NULL); tmp = wocky_node_get_attribute (stream_node, "tube"); if (tmp == NULL) { GError e = { WOCKY_XMPP_ERROR, WOCKY_XMPP_ERROR_BAD_REQUEST, "<stream> has no tube attribute" }; NODE_DEBUG (stream_node, e.message); gabble_bytestream_iface_close (bytestream, &e); return; } tube_id = g_ascii_strtoull (tmp, NULL, 10); if (tube_id == 0 || tube_id > G_MAXUINT32) { GError e = { WOCKY_XMPP_ERROR, WOCKY_XMPP_ERROR_BAD_REQUEST, "<stream> tube ID attribute non-numeric or out of range" }; DEBUG ("tube id is non-numeric or out of range: %s", tmp); gabble_bytestream_iface_close (bytestream, &e); return; } tube = g_hash_table_lookup (priv->tubes, GUINT_TO_POINTER (tube_id)); if (tube == NULL) { GError e = { WOCKY_XMPP_ERROR, WOCKY_XMPP_ERROR_BAD_REQUEST, "<stream> tube attribute points to a nonexistent " "tube" }; DEBUG ("tube %" G_GUINT64_FORMAT " doesn't exist", tube_id); gabble_bytestream_iface_close (bytestream, &e); return; } DEBUG ("received new bytestream request for existing tube: %" G_GUINT64_FORMAT, tube_id); gabble_tube_iface_add_bytestream (tube, bytestream); } static gboolean tube_msg_checks (GabblePrivateTubesFactory *self, WockyStanza *msg, WockyNode *node, TpHandle *out_handle, guint64 *out_tube_id) { GabblePrivateTubesFactoryPrivate *priv = GABBLE_PRIVATE_TUBES_FACTORY_GET_PRIVATE (self); TpHandleRepoIface *contact_repo = tp_base_connection_get_handles ( (TpBaseConnection *) priv->conn, TP_HANDLE_TYPE_CONTACT); const gchar *from, *tmp; TpHandle handle; guint64 tube_id; from = wocky_node_get_attribute ( wocky_stanza_get_top_node (msg), "from"); if (from == NULL) { STANZA_DEBUG (msg, "got a message without a from field"); return FALSE; } handle = tp_handle_lookup (contact_repo, from, NULL, NULL); if (handle == 0) { DEBUG ("Invalid from field"); return FALSE; } tmp = wocky_node_get_attribute (node, "id"); if (tmp == NULL) { DEBUG ("failed to get the tube ID"); return FALSE; } tube_id = g_ascii_strtoull (tmp, NULL, 10); if (tube_id == 0 || tube_id > G_MAXUINT32) { DEBUG ("tube ID is non-numeric or out of range: %s", tmp); return FALSE; } if (out_tube_id != NULL) *out_tube_id = tube_id; if (out_handle != NULL) *out_handle = handle; return TRUE; } static gboolean private_tubes_factory_msg_tube_cb ( WockyPorter *porter, WockyStanza *msg, gpointer user_data) { GabblePrivateTubesFactory *self = GABBLE_PRIVATE_TUBES_FACTORY (user_data); GabblePrivateTubesFactoryPrivate *priv = GABBLE_PRIVATE_TUBES_FACTORY_GET_PRIVATE (self); WockyNode *node; guint64 tube_id; GabbleTubeIface *channel; TpHandle handle; node = wocky_node_get_child_ns ( wocky_stanza_get_top_node (msg), "tube", NS_TUBES); g_return_val_if_fail (node != NULL, FALSE); if (!tube_msg_checks (self, msg, node, &handle, &tube_id)) return FALSE; channel = g_hash_table_lookup (priv->tubes, GUINT_TO_POINTER (tube_id)); if (channel != NULL) { TpHandle tube_handle = 0; g_object_get (channel, "handle", &tube_handle, NULL); DEBUG ("tube ID already in use; do not open the offered tube and close " "the existing tube if it's to the same contact"); /* only close the existing channel if it's the same contact * otherwise contacts could force close unrelated tubes. */ if (handle == tube_handle) gabble_tube_iface_close (channel, FALSE); return TRUE; } channel = new_channel_from_stanza (self, msg, node, tube_id, NULL); return TRUE; } static gboolean private_tubes_factory_tube_close_cb ( WockyPorter *porter, WockyStanza *msg, gpointer user_data) { GabblePrivateTubesFactory *self = GABBLE_PRIVATE_TUBES_FACTORY (user_data); GabblePrivateTubesFactoryPrivate *priv = GABBLE_PRIVATE_TUBES_FACTORY_GET_PRIVATE (self); WockyNode *node; guint64 tube_id; GabbleTubeIface *channel; TpTubeType type; node = wocky_node_get_child_ns ( wocky_stanza_get_top_node (msg), "close", NS_TUBES); g_return_val_if_fail (node != NULL, FALSE); if (!tube_msg_checks (self, msg, node, NULL, &tube_id)) return FALSE; channel = g_hash_table_lookup (priv->tubes, GUINT_TO_POINTER (tube_id)); if (channel == NULL) { DEBUG ("<close> tube attribute points to a nonexistent tube"); return TRUE; } g_object_get (channel, "type", &type, NULL); if (type != TP_TUBE_TYPE_STREAM) { DEBUG ("Only stream tubes can be closed using a close message"); return TRUE; } DEBUG ("tube %" G_GUINT64_FORMAT " was closed by remote peer", tube_id); gabble_tube_iface_close (channel, TRUE); return TRUE; } static GabbleTubeIface * gabble_private_tubes_factory_lookup (GabblePrivateTubesFactory *self, const gchar *type, TpHandle handle, const gchar *service) { GabblePrivateTubesFactoryPrivate *priv = GABBLE_PRIVATE_TUBES_FACTORY_GET_PRIVATE (self); GHashTableIter iter; gpointer value; g_hash_table_iter_init (&iter, priv->tubes); while (g_hash_table_iter_next (&iter, NULL, &value)) { GabbleTubeIface *tube = value; gboolean match = FALSE; gchar *channel_type, *channel_service; TpHandle channel_handle; g_object_get (tube, "channel-type", &channel_type, "handle", &channel_handle, "service", &channel_service, NULL); if (!tp_strdiff (type, channel_type) && handle == channel_handle && !tp_strdiff (service, channel_service)) match = TRUE; g_free (channel_type); g_free (channel_service); if (match) return tube; } return NULL; } static void channel_closed_cb (GabbleTubeIface *tube, GabblePrivateTubesFactory *self) { GabblePrivateTubesFactoryPrivate *priv = GABBLE_PRIVATE_TUBES_FACTORY_GET_PRIVATE (self); guint64 id; g_object_get (tube, "id", &id, NULL); tp_channel_manager_emit_channel_closed_for_object (self, TP_EXPORTABLE_CHANNEL (tube)); if (priv->tubes != NULL) g_hash_table_remove (priv->tubes, GUINT_TO_POINTER (id)); } static guint64 generate_tube_id (GabblePrivateTubesFactory *self) { GabblePrivateTubesFactoryPrivate *priv = GABBLE_PRIVATE_TUBES_FACTORY_GET_PRIVATE (self); guint out; /* probably totally overkill */ do { out = g_random_int_range (1, G_MAXINT32); } while (g_hash_table_lookup (priv->tubes, GUINT_TO_POINTER (out)) != NULL); return out; } /* Returns: (transfer none): new tube channel. the channel manager holds * the ref to this channel, so don't unref it! */ static GabbleTubeIface * new_channel_from_request (GabblePrivateTubesFactory *self, GHashTable *request) { GabblePrivateTubesFactoryPrivate *priv = GABBLE_PRIVATE_TUBES_FACTORY_GET_PRIVATE (self); GabbleTubeIface *tube; TpBaseConnection *base_conn = TP_BASE_CONNECTION (priv->conn); gchar *stream_id; TpHandle handle; const gchar *ctype, *service; TpHandleType handle_type; GHashTable *parameters; guint64 tube_id; ctype = tp_asv_get_string (request, TP_PROP_CHANNEL_CHANNEL_TYPE); handle = tp_asv_get_uint32 (request, TP_PROP_CHANNEL_TARGET_HANDLE, NULL); handle_type = tp_asv_get_uint32 (request, TP_PROP_CHANNEL_TARGET_HANDLE_TYPE, NULL); tube_id = generate_tube_id (self); /* requested tubes have an empty parameters dict */ parameters = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify) tp_g_value_slice_free); if (!tp_strdiff (ctype, TP_IFACE_CHANNEL_TYPE_STREAM_TUBE)) { service = tp_asv_get_string (request, TP_PROP_CHANNEL_TYPE_STREAM_TUBE_SERVICE); tube = GABBLE_TUBE_IFACE (gabble_tube_stream_new (priv->conn, handle, handle_type, tp_base_connection_get_self_handle (base_conn), tp_base_connection_get_self_handle (base_conn), service, parameters, tube_id, NULL, TRUE)); } else if (!tp_strdiff (ctype, TP_IFACE_CHANNEL_TYPE_DBUS_TUBE)) { service = tp_asv_get_string (request, TP_PROP_CHANNEL_TYPE_DBUS_TUBE_SERVICE_NAME); stream_id = gabble_bytestream_factory_generate_stream_id (); tube = GABBLE_TUBE_IFACE (gabble_tube_dbus_new (priv->conn, handle, handle_type, tp_base_connection_get_self_handle (base_conn), tp_base_connection_get_self_handle (base_conn), service, parameters, stream_id, tube_id, NULL, NULL, TRUE)); g_free (stream_id); } else { g_return_val_if_reached (NULL); } tp_base_channel_register ((TpBaseChannel *) tube); g_signal_connect (tube, "closed", G_CALLBACK (channel_closed_cb), self); g_hash_table_insert (priv->tubes, GUINT_TO_POINTER (tube_id), tube); g_hash_table_unref (parameters); return tube; } static void send_tube_close_msg (GabblePrivateTubesFactory *self, const gchar *jid, guint64 tube_id) { GabblePrivateTubesFactoryPrivate *priv = GABBLE_PRIVATE_TUBES_FACTORY_GET_PRIVATE (self); WockyPorter *porter; WockyStanza *msg; gchar *id_str; id_str = g_strdup_printf ("%" G_GUINT64_FORMAT, tube_id); porter = gabble_connection_dup_porter (priv->conn); /* Send the close message */ msg = wocky_stanza_build (WOCKY_STANZA_TYPE_MESSAGE, WOCKY_STANZA_SUB_TYPE_NONE, NULL, jid, '(', "close", ':', NS_TUBES, '@', "tube", id_str, ')', GABBLE_AMP_DO_NOT_STORE_SPEC, NULL); g_free (id_str); wocky_porter_send (porter, msg); g_object_unref (porter); g_object_unref (msg); } /* Returns: (transfer none): new tube channel. the channel manager holds * the ref to this channel, so don't unref it! */ static GabbleTubeIface * new_channel_from_stanza (GabblePrivateTubesFactory *self, WockyStanza *stanza, WockyNode *tube_node, guint64 tube_id, GabbleBytestreamIface *bytestream) { GabblePrivateTubesFactoryPrivate *priv = GABBLE_PRIVATE_TUBES_FACTORY_GET_PRIVATE (self); GabbleTubeIface *tube; TpBaseConnection *base_conn = TP_BASE_CONNECTION (priv->conn); TpHandleRepoIface *contact_repo = tp_base_connection_get_handles ( (TpBaseConnection *) priv->conn, TP_HANDLE_TYPE_CONTACT); TpTubeType type; TpHandle handle; const gchar *service; GHashTable *parameters; /* the validity of this has already been checked by wocky */ handle = tp_handle_ensure (contact_repo, wocky_stanza_get_from (stanza), NULL, NULL); g_return_val_if_fail (handle != 0, NULL); if (!gabble_private_tubes_factory_extract_tube_information ( contact_repo, tube_node, &type, NULL, &service, ¶meters, NULL)) { DEBUG ("can't extract <tube> information from message"); send_tube_close_msg (self, wocky_stanza_get_from (stanza), tube_id); return NULL; } if (bytestream == NULL && type != TP_TUBE_TYPE_STREAM) { DEBUG ("Only stream tubes are allowed to be created using messages"); send_tube_close_msg (self, wocky_stanza_get_from (stanza), tube_id); return NULL; } else if (bytestream != NULL && type != TP_TUBE_TYPE_DBUS) { GError e = { WOCKY_XMPP_ERROR, WOCKY_XMPP_ERROR_FORBIDDEN, "Only D-Bus tubes are allowed to be created using SI" }; DEBUG ("%s", e.message); gabble_bytestream_iface_close (bytestream, &e); return NULL; } if (type == TP_TUBE_TYPE_STREAM) { tube = GABBLE_TUBE_IFACE (gabble_tube_stream_new (priv->conn, handle, TP_HANDLE_TYPE_CONTACT, tp_base_connection_get_self_handle (base_conn), handle, service, parameters, tube_id, NULL, FALSE)); } else { WockyNode *si_node; const gchar *stream_id; si_node = wocky_node_get_child_ns ( wocky_stanza_get_top_node (stanza), "si", NS_SI); g_return_val_if_fail (si_node != NULL, NULL); stream_id = wocky_node_get_attribute (si_node, "id"); g_return_val_if_fail (stream_id != NULL, NULL); tube = GABBLE_TUBE_IFACE (gabble_tube_dbus_new (priv->conn, handle, TP_HANDLE_TYPE_CONTACT, tp_base_connection_get_self_handle (base_conn), handle, service, parameters, stream_id, tube_id, bytestream, NULL, FALSE)); } tp_base_channel_register ((TpBaseChannel *) tube); g_signal_connect (tube, "closed", G_CALLBACK (channel_closed_cb), self); g_hash_table_insert (priv->tubes, GUINT_TO_POINTER (tube_id), tube); g_hash_table_unref (parameters); tp_channel_manager_emit_new_channel (self, TP_EXPORTABLE_CHANNEL (tube), NULL); return tube; } GabblePrivateTubesFactory * gabble_private_tubes_factory_new (GabbleConnection *conn) { g_return_val_if_fail (GABBLE_IS_CONNECTION (conn), NULL); return g_object_new ( GABBLE_TYPE_PRIVATE_TUBES_FACTORY, "connection", conn, NULL); } static void gabble_private_tubes_factory_type_foreach_channel_class (GType type, TpChannelManagerTypeChannelClassFunc func, gpointer user_data) { GHashTable *table; GValue *value; /* 1-1 Channel.Type.StreamTube */ table = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, (GDestroyNotify) tp_g_value_slice_free); value = tp_g_value_slice_new (G_TYPE_STRING); g_value_set_static_string (value, TP_IFACE_CHANNEL_TYPE_STREAM_TUBE); g_hash_table_insert (table, TP_PROP_CHANNEL_CHANNEL_TYPE, value); value = tp_g_value_slice_new (G_TYPE_UINT); g_value_set_uint (value, TP_HANDLE_TYPE_CONTACT); g_hash_table_insert (table, TP_PROP_CHANNEL_TARGET_HANDLE_TYPE, value); func (type, table, gabble_tube_stream_channel_get_allowed_properties (), user_data); g_hash_table_unref (table); /* 1-1 Channel.Type.DBusTube */ table = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, (GDestroyNotify) tp_g_value_slice_free); value = tp_g_value_slice_new (G_TYPE_STRING); g_value_set_static_string (value, TP_IFACE_CHANNEL_TYPE_DBUS_TUBE); g_hash_table_insert (table, TP_PROP_CHANNEL_CHANNEL_TYPE, value); value = tp_g_value_slice_new (G_TYPE_UINT); g_value_set_uint (value, TP_HANDLE_TYPE_CONTACT); g_hash_table_insert (table, TP_PROP_CHANNEL_TARGET_HANDLE_TYPE, value); func (type, table, gabble_tube_dbus_channel_get_allowed_properties (), user_data); g_hash_table_unref (table); } static gboolean gabble_private_tubes_factory_requestotron (GabblePrivateTubesFactory *self, gpointer request_token, GHashTable *request_properties, gboolean require_new) { TpBaseConnection *base_conn = (TpBaseConnection *) self->priv->conn; TpHandle handle; GError *error = NULL; const gchar *channel_type; GabbleTubeIface *channel; const gchar *service = NULL; if (tp_asv_get_uint32 (request_properties, TP_PROP_CHANNEL_TARGET_HANDLE_TYPE, NULL) != TP_HANDLE_TYPE_CONTACT) return FALSE; channel_type = tp_asv_get_string (request_properties, TP_PROP_CHANNEL_CHANNEL_TYPE); if (tp_strdiff (channel_type, TP_IFACE_CHANNEL_TYPE_STREAM_TUBE) && tp_strdiff (channel_type, TP_IFACE_CHANNEL_TYPE_DBUS_TUBE)) return FALSE; if (! tp_strdiff (channel_type, TP_IFACE_CHANNEL_TYPE_STREAM_TUBE)) { if (tp_channel_manager_asv_has_unknown_properties (request_properties, tubes_channel_fixed_properties, gabble_tube_stream_channel_get_allowed_properties (), &error)) goto error; /* "Service" is a mandatory, not-fixed property */ service = tp_asv_get_string (request_properties, TP_PROP_CHANNEL_TYPE_STREAM_TUBE_SERVICE); if (service == NULL) { g_set_error (&error, TP_ERROR, TP_ERROR_NOT_IMPLEMENTED, "Request does not contain the mandatory property '%s'", TP_PROP_CHANNEL_TYPE_STREAM_TUBE_SERVICE); goto error; } } else if (! tp_strdiff (channel_type, TP_IFACE_CHANNEL_TYPE_DBUS_TUBE)) { GError *err = NULL; if (tp_channel_manager_asv_has_unknown_properties (request_properties, tubes_channel_fixed_properties, gabble_tube_dbus_channel_get_allowed_properties (), &error)) goto error; /* "ServiceName" is a mandatory, not-fixed property */ service = tp_asv_get_string (request_properties, TP_PROP_CHANNEL_TYPE_DBUS_TUBE_SERVICE_NAME); if (service == NULL) { g_set_error (&error, TP_ERROR, TP_ERROR_NOT_IMPLEMENTED, "Request does not contain the mandatory property '%s'", TP_PROP_CHANNEL_TYPE_DBUS_TUBE_SERVICE_NAME); goto error; } if (!tp_dbus_check_valid_bus_name (service, TP_DBUS_NAME_TYPE_WELL_KNOWN, &err)) { g_set_error (&error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT, "Invalid ServiceName: %s", err->message); g_error_free (err); goto error; } } /* validity already checked by TpBaseConnection */ handle = tp_asv_get_uint32 (request_properties, TP_PROP_CHANNEL_TARGET_HANDLE, NULL); g_assert (handle != 0); /* Don't support opening a channel to our self handle */ if (handle == tp_base_connection_get_self_handle (base_conn)) { g_set_error (&error, TP_ERROR, TP_ERROR_NOT_AVAILABLE, "Can't open a channel to your self handle"); goto error; } channel = gabble_private_tubes_factory_lookup (self, channel_type, handle, service); if (channel == NULL) { GSList *request_tokens = NULL; channel = new_channel_from_request (self, request_properties); if (request_token != NULL) request_tokens = g_slist_prepend (NULL, request_token); tp_channel_manager_emit_new_channel (self, TP_EXPORTABLE_CHANNEL (channel), request_tokens); g_slist_free (request_tokens); } else { if (require_new) { g_set_error (&error, TP_ERROR, TP_ERROR_NOT_AVAILABLE, "A channel to #%u (service: %s) is already open", handle, service); goto error; } tp_channel_manager_emit_request_already_satisfied (self, request_token, TP_EXPORTABLE_CHANNEL (channel)); } return TRUE; error: tp_channel_manager_emit_request_failed (self, request_token, error->domain, error->code, error->message); g_error_free (error); return TRUE; } static gboolean gabble_private_tubes_factory_create_channel (TpChannelManager *manager, gpointer request_token, GHashTable *request_properties) { GabblePrivateTubesFactory *self = GABBLE_PRIVATE_TUBES_FACTORY (manager); return gabble_private_tubes_factory_requestotron (self, request_token, request_properties, TRUE); } static gboolean gabble_private_tubes_factory_request_channel (TpChannelManager *manager, gpointer request_token, GHashTable *request_properties) { GabblePrivateTubesFactory *self = GABBLE_PRIVATE_TUBES_FACTORY (manager); return gabble_private_tubes_factory_requestotron (self, request_token, request_properties, FALSE); } static gboolean gabble_private_tubes_factory_ensure_channel (TpChannelManager *manager, gpointer request_token, GHashTable *request_properties) { GabblePrivateTubesFactory *self = GABBLE_PRIVATE_TUBES_FACTORY (manager); return gabble_private_tubes_factory_requestotron (self, request_token, request_properties, FALSE); } static void channel_manager_iface_init (gpointer g_iface, gpointer iface_data) { TpChannelManagerIface *iface = g_iface; iface->foreach_channel = gabble_private_tubes_factory_foreach_channel; iface->type_foreach_channel_class = gabble_private_tubes_factory_type_foreach_channel_class; iface->create_channel = gabble_private_tubes_factory_create_channel; iface->request_channel = gabble_private_tubes_factory_request_channel; iface->ensure_channel = gabble_private_tubes_factory_ensure_channel; } static void caps_channel_manager_iface_init (gpointer g_iface, gpointer iface_data) { GabbleCapsChannelManagerInterface *iface = g_iface; iface->get_contact_caps = gabble_private_tubes_factory_get_contact_caps; iface->represent_client = gabble_private_tubes_factory_represent_client; } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������telepathy-gabble-0.18.2/src/private-tubes-factory.h�������������������������������������������������0000644�0001750�0001750�00000006317�12227000321�022210� 0����������������������������������������������������������������������������������������������������ustar�00smcv����������������������������smcv����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * private-tubes-factory.h - Header for GabblePrivateTubesFactory * Copyright (C) 2007 Collabora Ltd. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 __PRIVATE_TUBES_FACTORY_H__ #define __PRIVATE_TUBES_FACTORY_H__ #include <glib-object.h> #include <telepathy-glib/telepathy-glib.h> #include "connection.h" #include "bytestream-iface.h" G_BEGIN_DECLS typedef struct _GabblePrivateTubesFactoryClass GabblePrivateTubesFactoryClass; typedef struct _GabblePrivateTubesFactoryPrivate GabblePrivateTubesFactoryPrivate; struct _GabblePrivateTubesFactoryClass { GObjectClass parent_class; }; struct _GabblePrivateTubesFactory { GObject parent; GabblePrivateTubesFactoryPrivate *priv; }; GType gabble_private_tubes_factory_get_type (void); /* TYPE MACROS */ #define GABBLE_TYPE_PRIVATE_TUBES_FACTORY \ (gabble_private_tubes_factory_get_type ()) #define GABBLE_PRIVATE_TUBES_FACTORY(obj) \ (G_TYPE_CHECK_INSTANCE_CAST((obj), GABBLE_TYPE_PRIVATE_TUBES_FACTORY,\ GabblePrivateTubesFactory)) #define GABBLE_PRIVATE_TUBES_FACTORY_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST((klass), GABBLE_TYPE_PRIVATE_TUBES_FACTORY,\ GabblePrivateTubesFactoryClass)) #define GABBLE_IS_PRIVATE_TUBES_FACTORY(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE((obj), GABBLE_TYPE_PRIVATE_TUBES_FACTORY)) #define GABBLE_IS_PRIVATE_TUBES_FACTORY_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE((klass), GABBLE_TYPE_PRIVATE_TUBES_FACTORY)) #define GABBLE_PRIVATE_TUBES_FACTORY_GET_CLASS(obj) \ (G_TYPE_INSTANCE_GET_CLASS ((obj), GABBLE_TYPE_PRIVATE_TUBES_FACTORY,\ GabblePrivateTubesFactoryClass)) GabblePrivateTubesFactory * gabble_private_tubes_factory_new ( GabbleConnection *conn); /* New 1-1 D-Bus tube requested by the remote contact */ void gabble_private_tubes_factory_handle_si_tube_request ( GabblePrivateTubesFactory *fac, GabbleBytestreamIface *bytestream, TpHandle handle, const gchar *stream_id, WockyStanza *msg); /* New connection requested in a existing 1-1 stream tube */ void gabble_private_tubes_factory_handle_si_stream_request ( GabblePrivateTubesFactory *fac, GabbleBytestreamIface *bytestream, TpHandle handle, const gchar *stream_id, WockyStanza *msg); gboolean gabble_private_tubes_factory_extract_tube_information ( TpHandleRepoIface *contact_repo, WockyNode *tube_node, TpTubeType *type, TpHandle *initiator_handle, const gchar **service, GHashTable **parameters, guint64 *tube_id); G_END_DECLS #endif /* #ifndef __PRIVATE_TUBES_FACTORY_H__ */ �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������telepathy-gabble-0.18.2/src/protocol.c��������������������������������������������������������������0000644�0001750�0001750�00000034362�12227000321�017606� 0����������������������������������������������������������������������������������������������������ustar�00smcv����������������������������smcv����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * protocol.c - source for GabbleJabberProtocol * Copyright (C) 2007-2010 Collabora Ltd. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 "config.h" #include "protocol.h" #include <string.h> #include <telepathy-glib/telepathy-glib.h> #include <telepathy-glib/telepathy-glib-dbus.h> #include <dbus/dbus-protocol.h> #include <dbus/dbus-glib.h> #include "extensions/extensions.h" #include "conn-presence.h" #include "connection.h" #include "connection-manager.h" #include "im-factory.h" #ifdef ENABLE_VOIP #include "media-factory.h" #endif #include "private-tubes-factory.h" #include "roomlist-manager.h" #include "search-manager.h" #include "util.h" #include "addressing-util.h" #define PROTOCOL_NAME "jabber" #define ICON_NAME "im-" PROTOCOL_NAME #define VCARD_FIELD_NAME "x-" PROTOCOL_NAME #define ENGLISH_NAME "Jabber" static void addressing_iface_init (TpProtocolAddressingInterface *iface); G_DEFINE_TYPE_WITH_CODE (GabbleJabberProtocol, gabble_jabber_protocol, TP_TYPE_BASE_PROTOCOL, G_IMPLEMENT_INTERFACE (TP_TYPE_PROTOCOL_ADDRESSING, addressing_iface_init); ) static TpCMParamSpec jabber_params[] = { { "account", DBUS_TYPE_STRING_AS_STRING, G_TYPE_STRING, TP_CONN_MGR_PARAM_FLAG_REQUIRED | TP_CONN_MGR_PARAM_FLAG_REGISTER, NULL, 0 /* unused */, /* FIXME: validate the JID according to the RFC */ tp_cm_param_filter_string_nonempty, NULL }, { "password", DBUS_TYPE_STRING_AS_STRING, G_TYPE_STRING, TP_CONN_MGR_PARAM_FLAG_REGISTER | TP_CONN_MGR_PARAM_FLAG_SECRET, NULL, 0 /* unused */, NULL, NULL }, { "server", DBUS_TYPE_STRING_AS_STRING, G_TYPE_STRING, 0, NULL, 0 /* unused */, /* FIXME: validate the server properly */ tp_cm_param_filter_string_nonempty, NULL }, { "resource", DBUS_TYPE_STRING_AS_STRING, G_TYPE_STRING, 0, NULL, 0 /* unused */, /* FIXME: validate the resource according to the RFC */ tp_cm_param_filter_string_nonempty, NULL }, { "priority", DBUS_TYPE_INT16_AS_STRING, G_TYPE_INT, TP_CONN_MGR_PARAM_FLAG_HAS_DEFAULT, GINT_TO_POINTER(0), 0 /* unused */, NULL, NULL }, { "port", DBUS_TYPE_UINT16_AS_STRING, G_TYPE_UINT, TP_CONN_MGR_PARAM_FLAG_HAS_DEFAULT, GUINT_TO_POINTER(5222), 0 /* unused */, tp_cm_param_filter_uint_nonzero, NULL }, { "old-ssl", DBUS_TYPE_BOOLEAN_AS_STRING, G_TYPE_BOOLEAN, TP_CONN_MGR_PARAM_FLAG_HAS_DEFAULT, GINT_TO_POINTER(FALSE), 0 /* unused */, NULL, NULL }, { "require-encryption", DBUS_TYPE_BOOLEAN_AS_STRING, G_TYPE_BOOLEAN, TP_CONN_MGR_PARAM_FLAG_HAS_DEFAULT, GINT_TO_POINTER(TRUE), 0 /* unused */, NULL, NULL }, { "register", DBUS_TYPE_BOOLEAN_AS_STRING, G_TYPE_BOOLEAN, TP_CONN_MGR_PARAM_FLAG_HAS_DEFAULT, GINT_TO_POINTER(FALSE), 0 /* unused */, NULL, NULL }, { "low-bandwidth", DBUS_TYPE_BOOLEAN_AS_STRING, G_TYPE_BOOLEAN, TP_CONN_MGR_PARAM_FLAG_HAS_DEFAULT, GINT_TO_POINTER(FALSE), 0 /* unused */, NULL, NULL }, { "https-proxy-server", DBUS_TYPE_STRING_AS_STRING, G_TYPE_STRING, 0, NULL, 0 /* unused */, /* FIXME: validate properly */ tp_cm_param_filter_string_nonempty, NULL }, { "https-proxy-port", DBUS_TYPE_UINT16_AS_STRING, G_TYPE_UINT, TP_CONN_MGR_PARAM_FLAG_HAS_DEFAULT, GUINT_TO_POINTER(GABBLE_PARAMS_DEFAULT_HTTPS_PROXY_PORT), 0 /* unused */, tp_cm_param_filter_uint_nonzero, NULL }, { "fallback-conference-server", DBUS_TYPE_STRING_AS_STRING, G_TYPE_STRING, TP_CONN_MGR_PARAM_FLAG_HAS_DEFAULT, "conference.telepathy.im", 0 /* offset, not used */, /* FIXME: validate properly */ tp_cm_param_filter_string_nonempty, NULL }, { "stun-server", DBUS_TYPE_STRING_AS_STRING, G_TYPE_STRING, 0, NULL, 0 /* unused */, /* FIXME: validate properly */ tp_cm_param_filter_string_nonempty, NULL }, { "stun-port", DBUS_TYPE_UINT16_AS_STRING, G_TYPE_UINT, TP_CONN_MGR_PARAM_FLAG_HAS_DEFAULT, GUINT_TO_POINTER(GABBLE_PARAMS_DEFAULT_STUN_PORT), 0 /* unused */, tp_cm_param_filter_uint_nonzero, NULL }, { "fallback-stun-server", DBUS_TYPE_STRING_AS_STRING, G_TYPE_STRING, TP_CONN_MGR_PARAM_FLAG_HAS_DEFAULT, GABBLE_PARAMS_DEFAULT_FALLBACK_STUN_SERVER, 0 /* unused */, /* FIXME: validate properly */ tp_cm_param_filter_string_nonempty, NULL }, { "fallback-stun-port", DBUS_TYPE_UINT16_AS_STRING, G_TYPE_UINT, TP_CONN_MGR_PARAM_FLAG_HAS_DEFAULT, GUINT_TO_POINTER(GABBLE_PARAMS_DEFAULT_STUN_PORT), 0 /* unused */, tp_cm_param_filter_uint_nonzero, NULL }, { "ignore-ssl-errors", DBUS_TYPE_BOOLEAN_AS_STRING, G_TYPE_BOOLEAN, TP_CONN_MGR_PARAM_FLAG_HAS_DEFAULT, GINT_TO_POINTER(FALSE), 0 /* unused */, NULL, NULL }, { "alias", DBUS_TYPE_STRING_AS_STRING, G_TYPE_STRING, 0, NULL, 0 /* unused */, /* setting a 0-length alias makes no sense */ tp_cm_param_filter_string_nonempty, NULL }, { "fallback-socks5-proxies", "as", 0, TP_CONN_MGR_PARAM_FLAG_HAS_DEFAULT, NULL, 0 /* unused */, NULL, NULL }, { "keepalive-interval", "u", G_TYPE_UINT, TP_CONN_MGR_PARAM_FLAG_HAS_DEFAULT, GUINT_TO_POINTER (30), 0 /* unused */, NULL, NULL }, { TP_PROP_CONNECTION_INTERFACE_CONTACT_LIST_DOWNLOAD_AT_CONNECTION, DBUS_TYPE_BOOLEAN_AS_STRING, G_TYPE_BOOLEAN, TP_CONN_MGR_PARAM_FLAG_HAS_DEFAULT | TP_CONN_MGR_PARAM_FLAG_DBUS_PROPERTY, GUINT_TO_POINTER (TRUE), 0 /* unused */, NULL, NULL }, { GABBLE_PROP_CONNECTION_INTERFACE_GABBLE_DECLOAK_DECLOAK_AUTOMATICALLY, DBUS_TYPE_BOOLEAN_AS_STRING, G_TYPE_BOOLEAN, TP_CONN_MGR_PARAM_FLAG_HAS_DEFAULT | TP_CONN_MGR_PARAM_FLAG_DBUS_PROPERTY, GINT_TO_POINTER (TRUE), 0 /* unused */, NULL, NULL }, { "fallback-servers", "as", 0, 0, NULL, 0 /* unused */, NULL, NULL }, { "extra-certificate-identities", "as", 0, 0, NULL, 0 /* unused */, NULL, NULL }, { NULL, NULL, 0, 0, NULL, 0 } }; static const gchar *default_socks5_proxies[] = GABBLE_PARAMS_DEFAULT_SOCKS5_PROXIES; static void gabble_jabber_protocol_init (GabbleJabberProtocol *self) { } static const TpCMParamSpec * get_parameters (TpBaseProtocol *self G_GNUC_UNUSED) { static gsize init_value = 0; if (g_once_init_enter (&init_value)) { guint i; for (i = 0; jabber_params[i].name != NULL; i++) { if (!g_strcmp0 (jabber_params[i].name, "fallback-socks5-proxies")) { jabber_params[i].gtype = G_TYPE_STRV; jabber_params[i].def = default_socks5_proxies; } else if (!g_strcmp0 (jabber_params[i].name, "fallback-servers")) { jabber_params[i].gtype = G_TYPE_STRV; } else if (!g_strcmp0 (jabber_params[i].name, "extra-certificate-identities")) { jabber_params[i].gtype = G_TYPE_STRV; } } g_once_init_leave (&init_value, 1); } return jabber_params; } #define MAP(x,y) { x, y } #define SAME(x) { x, x } /* This should be in sync with jabber_params from connection-manager.c, * and should contain all settable params/props for the connection, * except account and password, which are set manually. */ struct ParamMapping { const gchar *tp_param; const gchar *conn_prop; } params2props[] = { MAP ("server", "explicit-server"), SAME ("resource"), SAME ("priority"), SAME ("port"), SAME ("old-ssl"), SAME ("require-encryption"), SAME ("register"), SAME ("low-bandwidth"), SAME ("https-proxy-server"), SAME ("https-proxy-port"), SAME ("fallback-conference-server"), SAME ("stun-server"), SAME ("stun-port"), SAME ("fallback-stun-server"), SAME ("fallback-stun-port"), SAME ("ignore-ssl-errors"), SAME ("alias"), SAME ("fallback-socks5-proxies"), SAME ("keepalive-interval"), MAP (TP_PROP_CONNECTION_INTERFACE_CONTACT_LIST_DOWNLOAD_AT_CONNECTION, "download-roster-at-connection"), MAP (GABBLE_PROP_CONNECTION_INTERFACE_GABBLE_DECLOAK_DECLOAK_AUTOMATICALLY, "decloak-automatically"), SAME ("fallback-servers"), SAME ("extra-certificate-identities"), SAME (NULL) }; #undef SAME #undef MAP static TpBaseConnection * new_connection (TpBaseProtocol *protocol, GHashTable *params, GError **error) { GabbleConnection *conn; guint i; conn = g_object_new (GABBLE_TYPE_CONNECTION, "protocol", PROTOCOL_NAME, "password", tp_asv_get_string (params, "password"), NULL); /* split up account into username, stream-server and resource */ if (!_gabble_connection_set_properties_from_account (conn, tp_asv_get_string (params, "account"), error)) { g_object_unref (G_OBJECT (conn)); return NULL; } /* fill in the rest of the properties */ for (i = 0; params2props[i].tp_param != NULL; i++) { GValue *val = g_hash_table_lookup (params, params2props[i].tp_param); if (val != NULL) { g_object_set_property (G_OBJECT (conn), params2props[i].conn_prop, val); } } return TP_BASE_CONNECTION (conn); } static gchar * normalize_contact (TpBaseProtocol *self G_GNUC_UNUSED, const gchar *contact, GError **error) { return gabble_normalize_contact (NULL, contact, GUINT_TO_POINTER (GABBLE_JID_GLOBAL), error); } static gchar * identify_account (TpBaseProtocol *self G_GNUC_UNUSED, GHashTable *asv, GError **error) { const gchar *account = tp_asv_get_string (asv, "account"); g_assert (account != NULL); return g_strdup (account); } static GPtrArray * get_interfaces_array (TpBaseProtocol *self) { GPtrArray *interfaces; interfaces = TP_BASE_PROTOCOL_CLASS ( gabble_jabber_protocol_parent_class)->get_interfaces_array (self); g_ptr_array_add (interfaces, TP_IFACE_PROTOCOL_INTERFACE_PRESENCE); g_ptr_array_add (interfaces, TP_IFACE_PROTOCOL_INTERFACE_ADDRESSING); return interfaces; } static const TpPresenceStatusSpec * get_presence_statuses (TpBaseProtocol *self) { return conn_presence_statuses (); } static void get_connection_details (TpBaseProtocol *self, GStrv *connection_interfaces, GType **channel_managers, gchar **icon_name, gchar **english_name, gchar **vcard_field) { if (connection_interfaces != NULL) { *connection_interfaces = g_strdupv ( (GStrv) gabble_connection_get_implemented_interfaces ()); } if (channel_managers != NULL) { GType types[] = { #ifdef ENABLE_FILE_TRANSFER GABBLE_TYPE_FT_MANAGER, #endif GABBLE_TYPE_IM_FACTORY, #ifdef ENABLE_VOIP GABBLE_TYPE_MEDIA_FACTORY, #endif GABBLE_TYPE_MUC_FACTORY, GABBLE_TYPE_ROOMLIST_MANAGER, GABBLE_TYPE_SEARCH_MANAGER, GABBLE_TYPE_PRIVATE_TUBES_FACTORY, G_TYPE_INVALID }; *channel_managers = g_memdup (types, sizeof(types)); } if (icon_name != NULL) { *icon_name = g_strdup (ICON_NAME); } if (vcard_field != NULL) { *vcard_field = g_strdup (VCARD_FIELD_NAME); } if (english_name != NULL) { *english_name = g_strdup (ENGLISH_NAME); } } static GStrv dup_authentication_types (TpBaseProtocol *self) { const gchar * const types[] = { TP_IFACE_CHANNEL_TYPE_SERVER_TLS_CONNECTION, TP_IFACE_CHANNEL_INTERFACE_SASL_AUTHENTICATION, NULL }; return g_strdupv ((GStrv) types); } static GStrv dup_supported_uri_schemes (TpBaseProtocol *self) { return g_strdupv ((gchar **) gabble_get_addressable_uri_schemes ()); } static GStrv dup_supported_vcard_fields (TpBaseProtocol *self) { return g_strdupv ((gchar **) gabble_get_addressable_vcard_fields ()); } static gchar * addressing_normalize_vcard_address (TpBaseProtocol *self, const gchar *vcard_field, const gchar *vcard_address, GError **error) { gchar *normalized_address = gabble_normalize_vcard_address (vcard_field, vcard_address, error); if (normalized_address == NULL) { /* InvalidHandle makes no sense in Protocol */ if (error != NULL && g_error_matches (*error, TP_ERROR, TP_ERROR_INVALID_HANDLE)) { (*error)->code = TP_ERROR_INVALID_ARGUMENT; } } return normalized_address; } static gchar * addressing_normalize_contact_uri (TpBaseProtocol *self, const gchar *uri, GError **error) { gchar *normalized_address = NULL; normalized_address = gabble_normalize_contact_uri (uri, error); if (normalized_address == NULL) { /* InvalidHandle makes no sense in Protocol */ if (error != NULL && g_error_matches (*error, TP_ERROR, TP_ERROR_INVALID_HANDLE)) { (*error)->code = TP_ERROR_INVALID_ARGUMENT; } } return normalized_address; } static void gabble_jabber_protocol_class_init (GabbleJabberProtocolClass *klass) { TpBaseProtocolClass *base_class = (TpBaseProtocolClass *) klass; 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->get_statuses = get_presence_statuses; base_class->dup_authentication_types = dup_authentication_types; } TpBaseProtocol * gabble_jabber_protocol_new (void) { return g_object_new (GABBLE_TYPE_JABBER_PROTOCOL, "name", PROTOCOL_NAME, NULL); } static void addressing_iface_init (TpProtocolAddressingInterface *iface) { iface->dup_supported_vcard_fields = dup_supported_vcard_fields; iface->dup_supported_uri_schemes = dup_supported_uri_schemes; iface->normalize_vcard_address = addressing_normalize_vcard_address; iface->normalize_contact_uri = addressing_normalize_contact_uri; } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������telepathy-gabble-0.18.2/src/protocol.h��������������������������������������������������������������0000644�0001750�0001750�00000004532�12200204333�017607� 0����������������������������������������������������������������������������������������������������ustar�00smcv����������������������������smcv����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * protocol.h - header for GabbleJabberProtocol * Copyright (C) 2007-2010 Collabora Ltd. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 JABBER_PROTOCOL_H #define JABBER_PROTOCOL_H #include <glib-object.h> #include <telepathy-glib/telepathy-glib.h> G_BEGIN_DECLS typedef struct _GabbleJabberProtocol GabbleJabberProtocol; typedef struct _GabbleJabberProtocolPrivate GabbleJabberProtocolPrivate; typedef struct _GabbleJabberProtocolClass GabbleJabberProtocolClass; typedef struct _GabbleJabberProtocolClassPrivate GabbleJabberProtocolClassPrivate; struct _GabbleJabberProtocolClass { TpBaseProtocolClass parent_class; GabbleJabberProtocolClassPrivate *priv; }; struct _GabbleJabberProtocol { TpBaseProtocol parent; GabbleJabberProtocolPrivate *priv; }; GType gabble_jabber_protocol_get_type (void); #define GABBLE_TYPE_JABBER_PROTOCOL \ (gabble_jabber_protocol_get_type ()) #define GABBLE_JABBER_PROTOCOL(obj) \ (G_TYPE_CHECK_INSTANCE_CAST ((obj), \ GABBLE_TYPE_JABBER_PROTOCOL, \ GabbleJabberProtocol)) #define GABBLE_JABBER_PROTOCOL_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST ((klass), \ GABBLE_TYPE_JABBER_PROTOCOL, \ GabbleJabberProtocolClass)) #define GABBLE_IS_JABBER_PROTOCOL_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE ((klass), \ GABBLE_TYPE_JABBER_PROTOCOL)) #define GABBLE_JABBER_PROTOCOL_GET_CLASS(klass) \ (G_TYPE_INSTANCE_GET_CLASS ((obj), \ GABBLE_TYPE_JABBER_PROTOCOL, \ GabbleJabberProtocolClass)) gchar *gabble_jabber_protocol_normalize_contact (const gchar *id, GError **error); TpBaseProtocol *gabble_jabber_protocol_new (void); G_END_DECLS #endif ����������������������������������������������������������������������������������������������������������������������������������������������������������������������telepathy-gabble-0.18.2/src/presence-cache.c��������������������������������������������������������0000644�0001750�0001750�00000226774�12200204333�020624� 0����������������������������������������������������������������������������������������������������ustar�00smcv����������������������������smcv����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * gabble-presence-cache.c - Gabble's contact presence cache * Copyright (C) 2005 Collabora Ltd. * Copyright (C) 2005 Nokia Corporation * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 "config.h" #include "presence-cache.h" #include "vcard-manager.h" #include "gabble-enumtypes.h" #include <stdlib.h> #include <string.h> #include <glib.h> /* When five DIFFERENT guys report the same caps for a given bundle, it'll * be enough. But if only ONE guy use the verification string (XEP-0115 v1.5), * it'll be enough too. */ #define CAPABILITY_BUNDLE_ENOUGH_TRUST 5 #define DEBUG_FLAG GABBLE_DEBUG_PRESENCE #include <dbus/dbus-glib.h> #include <telepathy-glib/telepathy-glib.h> #include <wocky/wocky.h> #define DEBUG_FLAG GABBLE_DEBUG_PRESENCE #include "gabble/capabilities.h" #include "gabble/caps-channel-manager.h" #include "conn-presence.h" #include "debug.h" #include "disco.h" #include "gabble-signals-marshal.h" #include "namespaces.h" #include "util.h" #include "roster.h" #include "types.h" /* Time period from the cache creation in which we're unsure whether we * got initial presence from all the contacts. */ #define UNSURE_PERIOD 5 /* Time period from a de-cloak request in which we're unsure whether the * contact will disclose their presence later, or not at all. */ #define DECLOAK_PERIOD 5 G_DEFINE_TYPE (GabblePresenceCache, gabble_presence_cache, G_TYPE_OBJECT); /* properties */ enum { PROP_CONNECTION = 1, LAST_PROPERTY }; /* signal enum */ enum { PRESENCES_UPDATED, NICKNAME_UPDATE, CAPABILITIES_UPDATE, AVATAR_UPDATE, CAPABILITIES_DISCOVERED, LOCATION_UPDATED, UNSURE_PERIOD_ENDED, CLIENT_TYPES_UPDATED, LAST_SIGNAL }; static guint signals[LAST_SIGNAL] = { 0 }; struct _GabblePresenceCachePrivate { GabbleConnection *conn; gulong status_changed_cb; guint message_cb; guint presence_cb; GHashTable *presence; TpHandleSet *presence_handles; GHashTable *capabilities; GHashTable *disco_pending; guint caps_serial; guint unsure_id; /* handle => DecloakContext */ GHashTable *decloak_requests; TpHandleSet *decloak_handles; /* The cached contacts' location. * The key is the contact's TpHandle. * The value is a GHashTable of the user's location: * - the key is a gchar* as per XEP-0080 * - the value is a slice allocation GValue, the exact * type depends on the key. */ GHashTable *location; /* Are we resetting the image hash as per XEP-0153 section 4.4 */ gboolean avatar_reset_pending; gboolean dispose_has_run; }; typedef struct _DiscoWaiter DiscoWaiter; struct _DiscoWaiter { TpHandleRepoIface *repo; TpHandle handle; gchar *resource; guint serial; gboolean disco_requested; gchar *hash; gchar *ver; }; /** * disco_waiter_new () */ static DiscoWaiter * disco_waiter_new (TpHandleRepoIface *repo, TpHandle handle, const gchar *resource, const gchar *hash, const gchar *ver, guint serial) { DiscoWaiter *waiter; g_assert (repo); waiter = g_slice_new0 (DiscoWaiter); waiter->repo = repo; waiter->handle = handle; waiter->resource = g_strdup (resource); waiter->hash = g_strdup (hash); waiter->ver = g_strdup (ver); waiter->serial = serial; DEBUG ("created waiter %p for handle %u with serial %u", waiter, handle, serial); return waiter; } static void disco_waiter_free (DiscoWaiter *waiter) { g_assert (NULL != waiter); DEBUG ("freeing waiter %p for handle %u with serial %u", waiter, waiter->handle, waiter->serial); g_free (waiter->resource); g_free (waiter->hash); g_free (waiter->ver); g_slice_free (DiscoWaiter, waiter); } static void disco_waiter_list_free (GSList *list) { GSList *i; DEBUG ("list %p", list); for (i = list; NULL != i; i = i->next) disco_waiter_free ((DiscoWaiter *) i->data); g_slist_free (list); } static guint disco_waiter_list_get_request_count (GSList *list) { guint c = 0; GSList *i; for (i = list; i; i = i->next) { DiscoWaiter *waiter = (DiscoWaiter *) i->data; if (waiter->disco_requested) { if (!tp_strdiff (waiter->hash, "sha-1")) /* One waiter is enough if * 1. the request has a verification string * 2. the hash algorithm is supported */ c += CAPABILITY_BUNDLE_ENOUGH_TRUST; else c++; } } return c; } static GabbleCapabilityInfo * capability_info_get (GabblePresenceCache *cache, const gchar *node) { GabblePresenceCachePrivate *priv = cache->priv; GabbleCapabilityInfo *info = g_hash_table_lookup (priv->capabilities, node); if (NULL == info) { info = g_slice_new0 (GabbleCapabilityInfo); info->cap_set = NULL; info->client_types = 0; info->guys = tp_intset_new (); g_hash_table_insert (priv->capabilities, g_strdup (node), info); } return info; } static void capability_info_free (GabbleCapabilityInfo *info) { if (info->cap_set != NULL) { gabble_capability_set_free (info->cap_set); info->cap_set = NULL; } wocky_disco_identity_array_free (info->identities); info->identities = NULL; if (info->data_forms != NULL) g_ptr_array_unref (info->data_forms); info->data_forms = NULL; tp_intset_destroy (info->guys); g_slice_free (GabbleCapabilityInfo, info); } static void replace_data_forms (GabbleCapabilityInfo *info, GPtrArray *data_forms) { if (data_forms == info->data_forms) return; tp_clear_pointer (&info->data_forms, g_ptr_array_unref); if (data_forms != NULL) info->data_forms = g_ptr_array_ref (data_forms); } static guint capability_info_recvd (GabblePresenceCache *cache, const gchar *node, TpHandle handle, GabbleCapabilitySet *cap_set, guint trust_inc, guint client_types, GPtrArray *data_forms) { GabbleCapabilityInfo *info = capability_info_get (cache, node); if (info->cap_set == NULL || !gabble_capability_set_equals (cap_set, info->cap_set)) { /* The caps are not valid, either because we detected inconsistency * between several contacts using the same node (when the hash is not * used), or because this is the first caps report and the caps were * never set. */ tp_intset_clear (info->guys); if (info->cap_set == NULL) info->cap_set = gabble_capability_set_new (); else gabble_capability_set_clear (info->cap_set); gabble_capability_set_update (info->cap_set, cap_set); info->trust = 0; } if (!tp_intset_is_member (info->guys, handle)) { tp_intset_add (info->guys, handle); info->trust += trust_inc; } info->client_types = client_types; replace_data_forms (info, data_forms); return info->trust; } typedef struct { GabblePresenceCache *cache; TpHandle handle; guint timeout_id; const gchar *reason; } DecloakContext; static DecloakContext * decloak_context_new (GabblePresenceCache *cache, TpHandle handle, const gchar *reason) { DecloakContext *dc = g_slice_new0 (DecloakContext); dc->cache = cache; dc->handle = handle; dc->reason = reason; dc->timeout_id = 0; return dc; } static void decloak_context_free (gpointer data) { DecloakContext *dc = data; tp_handle_set_remove (dc->cache->priv->decloak_handles, dc->handle); if (dc->timeout_id != 0) g_source_remove (dc->timeout_id); g_slice_free (DecloakContext, dc); } static void gabble_presence_cache_init (GabblePresenceCache *presence_cache); static GObject * gabble_presence_cache_constructor (GType type, guint n_props, GObjectConstructParam *props); static void gabble_presence_cache_dispose (GObject *object); static void gabble_presence_cache_finalize (GObject *object); static void gabble_presence_cache_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec); static void gabble_presence_cache_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec); static GabblePresence *_cache_insert (GabblePresenceCache *cache, TpHandle handle); static void gabble_presence_cache_porter_available_cb ( GabbleConnection *conn, WockyPorter *porter, gpointer user_data); static void gabble_presence_cache_status_changed_cb (GabbleConnection *, TpConnectionStatus, TpConnectionStatusReason, gpointer); static gboolean _parse_message_message ( WockyPorter *porter, WockyStanza *message, gpointer user_data); static gboolean gabble_presence_cache_presence_cb ( WockyPorter *porter, WockyStanza *message, gpointer user_data); static void gabble_presence_cache_class_init (GabblePresenceCacheClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); GParamSpec *param_spec; g_type_class_add_private (object_class, sizeof (GabblePresenceCachePrivate)); object_class->constructor = gabble_presence_cache_constructor; object_class->dispose = gabble_presence_cache_dispose; object_class->finalize = gabble_presence_cache_finalize; object_class->get_property = gabble_presence_cache_get_property; object_class->set_property = gabble_presence_cache_set_property; param_spec = g_param_spec_object ("connection", "GabbleConnection object", "Gabble connection object that owns this presence cache.", GABBLE_TYPE_CONNECTION, G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, PROP_CONNECTION, param_spec); signals[PRESENCES_UPDATED] = g_signal_new ( "presences-updated", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, 0, NULL, NULL, g_cclosure_marshal_VOID__BOXED, G_TYPE_NONE, 1, DBUS_TYPE_G_UINT_ARRAY); signals[NICKNAME_UPDATE] = g_signal_new ( "nickname-update", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, 0, NULL, NULL, g_cclosure_marshal_VOID__UINT, G_TYPE_NONE, 1, G_TYPE_UINT); signals[CAPABILITIES_UPDATE] = g_signal_new ( "capabilities-update", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, 0, NULL, NULL, gabble_marshal_VOID__UINT_POINTER_POINTER, G_TYPE_NONE, 3, G_TYPE_UINT, G_TYPE_POINTER, G_TYPE_POINTER); signals[AVATAR_UPDATE] = g_signal_new ( "avatar-update", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, 0, NULL, NULL, g_cclosure_marshal_VOID__UINT_POINTER, G_TYPE_NONE, 2, G_TYPE_UINT, G_TYPE_POINTER); signals[CAPABILITIES_DISCOVERED] = g_signal_new ( "capabilities-discovered", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, 0, NULL, NULL, g_cclosure_marshal_VOID__UINT, G_TYPE_NONE, 1, G_TYPE_UINT); signals[LOCATION_UPDATED] = g_signal_new ( "location-update", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, 0, NULL, NULL, g_cclosure_marshal_VOID__UINT, G_TYPE_NONE, 1, G_TYPE_UINT); signals[UNSURE_PERIOD_ENDED] = g_signal_new ( "unsure-period-ended", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, 0, NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); signals[CLIENT_TYPES_UPDATED] = g_signal_new ( "client-types-updated", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, 0, NULL, NULL, g_cclosure_marshal_VOID__UINT, G_TYPE_NONE, 1, TP_TYPE_HANDLE); } static gboolean gabble_presence_cache_end_unsure_period (gpointer data) { GabblePresenceCache *self = GABBLE_PRESENCE_CACHE (data); DEBUG ("%p", data); self->priv->unsure_id = 0; g_signal_emit (self, signals[UNSURE_PERIOD_ENDED], 0); return FALSE; } static void gabble_presence_cache_init (GabblePresenceCache *cache) { GabblePresenceCachePrivate *priv = G_TYPE_INSTANCE_GET_PRIVATE (cache, GABBLE_TYPE_PRESENCE_CACHE, GabblePresenceCachePrivate); cache->priv = priv; priv->presence = g_hash_table_new_full (NULL, NULL, NULL, g_object_unref); priv->capabilities = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify) capability_info_free); priv->disco_pending = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify) disco_waiter_list_free); priv->caps_serial = 1; priv->decloak_requests = g_hash_table_new_full (NULL, NULL, NULL, decloak_context_free); priv->location = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, (GDestroyNotify) g_hash_table_unref); } static void gabble_presence_cache_add_bundle_caps (GabblePresenceCache *cache, const gchar *node, const gchar *ns); static void gabble_presence_cache_add_bundles (GabblePresenceCache *cache) { /* We cache the ext='' bundles Gabble advertises: older Gabbles * advertise these and don't support hashed caps, and we shouldn't * need to disco them. */ gabble_presence_cache_add_bundle_caps (cache, NS_GABBLE_CAPS "#" BUNDLE_VOICE_V1, NS_GOOGLE_FEAT_VOICE); gabble_presence_cache_add_bundle_caps (cache, NS_GABBLE_CAPS "#" BUNDLE_VIDEO_V1, NS_GOOGLE_FEAT_VIDEO); gabble_presence_cache_add_bundle_caps (cache, NS_GABBLE_CAPS "#" BUNDLE_SHARE_V1, NS_GOOGLE_FEAT_SHARE); gabble_presence_cache_add_bundle_caps (cache, NS_GABBLE_CAPS "#" BUNDLE_CAMERA_V1, NS_GOOGLE_FEAT_CAMERA); } static GObject * gabble_presence_cache_constructor (GType type, guint n_props, GObjectConstructParam *props) { GObject *obj; GabblePresenceCachePrivate *priv; obj = G_OBJECT_CLASS (gabble_presence_cache_parent_class)-> constructor (type, n_props, props); priv = GABBLE_PRESENCE_CACHE (obj)->priv; g_assert (priv->conn != NULL); g_assert (priv->presence_handles != NULL); g_assert (priv->decloak_handles != NULL); gabble_presence_cache_add_bundles ((GabblePresenceCache *) obj); priv->status_changed_cb = g_signal_connect (priv->conn, "status-changed", G_CALLBACK (gabble_presence_cache_status_changed_cb), obj); tp_g_signal_connect_object (priv->conn, "porter-available", G_CALLBACK (gabble_presence_cache_porter_available_cb), obj, 0); return obj; } static void gabble_presence_cache_dispose (GObject *object) { GabblePresenceCache *self = GABBLE_PRESENCE_CACHE (object); GabblePresenceCachePrivate *priv = self->priv; if (priv->dispose_has_run) return; DEBUG ("dispose called"); priv->dispose_has_run = TRUE; if (priv->unsure_id != 0) { g_source_remove (priv->unsure_id); priv->unsure_id = 0; } tp_clear_pointer (&priv->decloak_requests, g_hash_table_unref); tp_clear_pointer (&priv->decloak_handles, tp_handle_set_destroy); g_assert (priv->message_cb == 0); g_assert (priv->presence_cb == 0); g_signal_handler_disconnect (priv->conn, priv->status_changed_cb); tp_clear_pointer (&priv->presence, g_hash_table_unref); tp_clear_pointer (&priv->capabilities, g_hash_table_unref); tp_clear_pointer (&priv->disco_pending, g_hash_table_unref); tp_clear_pointer (&priv->presence_handles, tp_handle_set_destroy); tp_clear_pointer (&priv->location, g_hash_table_unref); if (G_OBJECT_CLASS (gabble_presence_cache_parent_class)->dispose) G_OBJECT_CLASS (gabble_presence_cache_parent_class)->dispose (object); } static void gabble_presence_cache_finalize (GObject *object) { DEBUG ("called with %p", object); G_OBJECT_CLASS (gabble_presence_cache_parent_class)->finalize (object); } static void gabble_presence_cache_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { GabblePresenceCache *cache = GABBLE_PRESENCE_CACHE (object); GabblePresenceCachePrivate *priv = cache->priv; switch (property_id) { case PROP_CONNECTION: g_value_set_object (value, priv->conn); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void gabble_presence_cache_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { GabblePresenceCache *cache = GABBLE_PRESENCE_CACHE (object); GabblePresenceCachePrivate *priv = cache->priv; TpHandleRepoIface *contact_repo; switch (property_id) { case PROP_CONNECTION: g_assert (priv->conn == NULL); /* construct-only */ g_assert (priv->presence_handles == NULL); /* construct-only */ g_assert (priv->decloak_handles == NULL); /* construct-only */ priv->conn = g_value_get_object (value); contact_repo = tp_base_connection_get_handles ( (TpBaseConnection *) priv->conn, TP_HANDLE_TYPE_CONTACT); priv->presence_handles = tp_handle_set_new (contact_repo); priv->decloak_handles = tp_handle_set_new (contact_repo); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void gabble_presence_cache_porter_available_cb ( GabbleConnection *conn, WockyPorter *porter, gpointer user_data) { GabblePresenceCache *cache = GABBLE_PRESENCE_CACHE (user_data); GabblePresenceCachePrivate *priv = cache->priv; priv->message_cb = wocky_porter_register_handler_from_anyone (porter, WOCKY_STANZA_TYPE_MESSAGE, WOCKY_STANZA_SUB_TYPE_NONE, WOCKY_PORTER_HANDLER_PRIORITY_MAX, _parse_message_message, cache, NULL); priv->presence_cb = wocky_porter_register_handler_from_anyone (porter, WOCKY_STANZA_TYPE_PRESENCE, WOCKY_STANZA_SUB_TYPE_NONE, WOCKY_PORTER_HANDLER_PRIORITY_MIN, gabble_presence_cache_presence_cb, cache, NULL); } static void gabble_presence_cache_status_changed_cb (GabbleConnection *conn, TpConnectionStatus status, TpConnectionStatusReason reason, gpointer data) { GabblePresenceCache *cache = GABBLE_PRESENCE_CACHE (data); GabblePresenceCachePrivate *priv = cache->priv; g_assert (conn == priv->conn); switch (status) { case TP_CONNECTION_STATUS_CONNECTING: break; case TP_CONNECTION_STATUS_CONNECTED: /* After waiting UNSURE_PERIOD seconds for initial presences to trickle * in, the "unsure period" ends. */ priv->unsure_id = g_timeout_add_seconds (UNSURE_PERIOD, gabble_presence_cache_end_unsure_period, cache); break; case TP_CONNECTION_STATUS_DISCONNECTED: if (conn->session != NULL) { WockyPorter *porter = wocky_session_get_porter (conn->session); if (priv->message_cb != 0) wocky_porter_unregister_handler (porter, priv->message_cb); if (priv->presence_cb != 0) wocky_porter_unregister_handler (porter, priv->presence_cb); priv->message_cb = 0; priv->presence_cb = 0; } break; default: g_assert_not_reached (); } } static GabblePresenceId _presence_node_get_status (WockyNode *pres_node) { const gchar *presence_show = wocky_node_get_content_from_child (pres_node, "show"); if (!presence_show) { /* NODE_DEBUG (pres_node, "empty <show> tag received from server, " "setting presence to available"); */ return GABBLE_PRESENCE_AVAILABLE; } if (0 == strcmp (presence_show, JABBER_PRESENCE_SHOW_AWAY)) return GABBLE_PRESENCE_AWAY; else if (0 == strcmp (presence_show, JABBER_PRESENCE_SHOW_CHAT)) return GABBLE_PRESENCE_CHAT; else if (0 == strcmp (presence_show, JABBER_PRESENCE_SHOW_DND)) return GABBLE_PRESENCE_DND; else if (0 == strcmp (presence_show, JABBER_PRESENCE_SHOW_XA)) return GABBLE_PRESENCE_XA; else { NODE_DEBUG (pres_node, "unrecognised <show/> value received from server, " "setting presence to available"); return GABBLE_PRESENCE_AVAILABLE; } } static void _grab_nickname (GabblePresenceCache *cache, TpHandle handle, const gchar *from, WockyNode *node) { const gchar *nickname; GabblePresence *presence; node = wocky_node_get_child_ns (node, "nick", NS_NICK); if (NULL == node) return; presence = gabble_presence_cache_get (cache, handle); if (NULL == presence) return; nickname = node->content; DEBUG ("got nickname \"%s\" for %s", nickname, from); if (tp_strdiff (presence->nickname, nickname)) { g_free (presence->nickname); presence->nickname = g_strdup (nickname); g_signal_emit (cache, signals[NICKNAME_UPDATE], 0, handle); } } static void self_vcard_request_cb (GabbleVCardManager *self, GabbleVCardManagerRequest *request, TpHandle handle, WockyNode *vcard, GError *error, gpointer user_data) { GabblePresenceCache *cache = user_data; GabblePresenceCachePrivate *priv = cache->priv; gchar *sha1 = NULL; priv->avatar_reset_pending = FALSE; if (vcard != NULL) { sha1 = vcard_get_avatar_sha1 (vcard); /* FIXME: presence->avatar_sha1 is resetted in * self_avatar_resolve_conflict() and the following signal set it in * conn-avatars.c. Doing that in 2 different files is confusing. */ g_signal_emit (cache, signals[AVATAR_UPDATE], 0, handle, sha1); g_free (sha1); } DEBUG ("End of avatar conflict resolution"); } static void self_avatar_resolve_conflict (GabblePresenceCache *cache) { GabblePresenceCachePrivate *priv = cache->priv; TpBaseConnection *base_conn = (TpBaseConnection *) priv->conn; GabblePresence *presence = priv->conn->self_presence; GError *error = NULL; if (tp_base_connection_get_status (base_conn) != TP_CONNECTION_STATUS_CONNECTED) { DEBUG ("no longer connected"); return; } /* We don't want recursive image resetting * * FIXME: There is a race here: if the other resource sends us first the * hash 'hash1', and then 'hash2' while the vCard request generated for * 'hash1' is still pending, the current code doesn't send a new vCard * request, although it should because Gabble cannot know whether the reply * will be for hash1 or hash2. The good solution would be to store the * received hash and each time the hash is different, cancel the previous * vCard request and send a new one. However, this is tricky, so we don't * implement it. * * This race is not so bad: the only bad consequence is if the other Jabber * client changes the avatar twice quickly, we may get only the first one. * The real contacts should still get the last avatar. */ if (priv->avatar_reset_pending) { DEBUG ("There is already an avatar conflict resolution pending."); return; } /* according to XEP-0153 section 4.3-2. 3rd bullet: * if we receive a photo from another resource, then we MUST * immediately send a presence update with an empty update child * element (no photo node), then re-download our own vCard; * when that arrives, we may start setting the photo node in our * presence again. */ DEBUG ("Reset our avatar, signal our presence without an avatar and request" " our own vCard."); priv->avatar_reset_pending = TRUE; g_free (presence->avatar_sha1); presence->avatar_sha1 = NULL; if (!conn_presence_signal_own_presence (priv->conn, NULL, &error)) { DEBUG ("failed to send own presence: %s", error->message); g_error_free (error); } gabble_vcard_manager_invalidate_cache (priv->conn->vcard_manager, tp_base_connection_get_self_handle (base_conn)); gabble_vcard_manager_request (priv->conn->vcard_manager, tp_base_connection_get_self_handle (base_conn), 0, self_vcard_request_cb, cache, NULL); } static void _grab_avatar_sha1 (GabblePresenceCache *cache, TpHandle handle, const gchar *from, WockyNode *node) { GabblePresenceCachePrivate *priv = cache->priv; TpBaseConnection *base_conn = (TpBaseConnection *) priv->conn; const gchar *sha1; WockyNode *x_node, *photo_node; GabblePresence *presence; if (handle == tp_base_connection_get_self_handle (base_conn)) presence = priv->conn->self_presence; else presence = gabble_presence_cache_get (cache, handle); if (NULL == presence) return; x_node = wocky_node_get_child_ns (node, "x", NS_VCARD_TEMP_UPDATE); if (NULL == x_node) { /* If (handle == base_conn->self_handle), then this means * that one of our other resources does not support XEP-0153. According * to that XEP, we MUST now stop advertising the image hash, at least * until all instances of non-conforming resources have gone offline, by * setting presence->avatar_sha1 to NULL. * * However, this would mean that logging in (e.g.) with an old version * of Gabble would disable avatars in this newer version, which is * quite a silly failure mode. As a result, we ignore this * requirement and hope that non-conforming clients won't alter the * <PHOTO>, which should in practice be true. * * If handle != self_handle, then in any case we want to ignore this * message for vCard purposes. */ return; } photo_node = wocky_node_get_child (x_node, "photo"); /* If there is no photo node, the resource supports XEP-0153, but has * nothing in particular to say about the avatar. */ if (NULL == photo_node) return; sha1 = photo_node->content; /* "" means we know there is no avatar. NULL means we don't know what is the * avatar. In this case, there is a <photo> node. */ if (sha1 == NULL) sha1 = ""; if (tp_strdiff (presence->avatar_sha1, sha1)) { if (handle == tp_base_connection_get_self_handle (base_conn)) { DEBUG ("Avatar conflict! Received hash '%s' and our cache is '%s'", sha1, presence->avatar_sha1 == NULL ? "<NULL>" : presence->avatar_sha1); self_avatar_resolve_conflict (cache); } else if (tp_base_connection_get_status (base_conn) == TP_CONNECTION_STATUS_CONNECTED) { g_free (presence->avatar_sha1); presence->avatar_sha1 = g_strdup (sha1); gabble_vcard_manager_invalidate_cache (priv->conn->vcard_manager, handle); g_signal_emit (cache, signals[AVATAR_UPDATE], 0, handle, sha1); } } } static GSList * _parse_cap_bundles ( WockyNode *lm_node, const gchar **hash, const gchar **ver, const gchar **node) { const gchar *ext; GSList *uris = NULL; WockyNode *cap_node; *hash = NULL; *ver = NULL; cap_node = wocky_node_get_child_ns (lm_node, "c", NS_CAPS); if (NULL == cap_node) return NULL; *hash = wocky_node_get_attribute (cap_node, "hash"); *node = wocky_node_get_attribute (cap_node, "node"); if (NULL == *node) return NULL; *ver = wocky_node_get_attribute (cap_node, "ver"); if (NULL != *ver) uris = g_slist_prepend (uris, g_strdup (*ver)); /* If there is a hash, the remote contact uses XEP-0115 v1.5 and the 'ext' * attribute MUST be ignored. */ if (NULL != *hash) return uris; ext = wocky_node_get_attribute (cap_node, "ext"); if (NULL != ext) { gchar **exts, **i; exts = g_strsplit (ext, " ", 0); for (i = exts; NULL != *i; i++) uris = g_slist_prepend (uris, g_strdup (*i)); g_strfreev (exts); } return uris; } static void _parse_node (GabblePresence *presence, WockyNode *lm_node, const gchar *resource, guint serial) { WockyNode *cap_node; const gchar *node; cap_node = wocky_node_get_child_ns (lm_node, "c", NS_CAPS); if (NULL == cap_node) return; node = wocky_node_get_attribute (cap_node, "node"); if (!tp_strdiff (node, "http://mail.google.com/xmpp/client/caps")) { GabbleCapabilitySet *cap_set = gabble_capability_set_new (); DEBUG ("Client is Google Web Client"); gabble_capability_set_add (cap_set, QUIRK_GOOGLE_WEBMAIL_CLIENT); gabble_capability_set_add (cap_set, QUIRK_OMITS_CONTENT_CREATORS); gabble_presence_set_capabilities (presence, resource, cap_set, NULL, serial); gabble_capability_set_free (cap_set); } if (!tp_strdiff (node, "http://www.android.com/gtalk/client/caps") || !tp_strdiff (node, "http://www.android.com/gtalk/client/caps2")) { GabbleCapabilitySet *cap_set = gabble_capability_set_new (); DEBUG ("Client is Android GTalk Client"); gabble_capability_set_add (cap_set, QUIRK_ANDROID_GTALK_CLIENT); gabble_presence_set_capabilities (presence, resource, cap_set, NULL, serial); gabble_capability_set_free (cap_set); } } static void _caps_disco_cb (GabbleDisco *disco, GabbleDiscoRequest *request, const gchar *jid, const gchar *node, WockyNode *query_result, GError *error, gpointer user_data); static void redisco (GabblePresenceCache *cache, GabbleDisco *disco, DiscoWaiter *waiter, const gchar *node) { const gchar *waiter_jid; gchar *full_jid; waiter_jid = tp_handle_inspect (waiter->repo, waiter->handle); if (waiter->resource != NULL) full_jid = g_strdup_printf ("%s/%s", waiter_jid, waiter->resource); else full_jid = g_strdup (waiter_jid); gabble_disco_request (disco, GABBLE_DISCO_TYPE_INFO, full_jid, node, _caps_disco_cb, cache, G_OBJECT (cache), NULL); waiter->disco_requested = TRUE; g_free (full_jid); } static void disco_failed (GabblePresenceCache *cache, GabbleDisco *disco, const gchar *node, GSList *waiters) { GabblePresenceCachePrivate *priv = cache->priv; GSList *i; DiscoWaiter *waiter = NULL; gchar *full_jid = NULL; for (i = waiters; NULL != i; i = i->next) { waiter = (DiscoWaiter *) i->data; if (!waiter->disco_requested) { redisco (cache, disco, waiter, node); break; } } if (NULL != i) { DEBUG ("sent a retry disco request to %s for URI %s", full_jid, node); } else { /* The contact sends us an error and we don't have any other * contacts to send the discovery request on the same node. We * cannot get the caps for this node. */ DEBUG ("failed to find a suitable candidate to retry disco " "request for URI %s", node); g_hash_table_remove (priv->disco_pending, node); } g_free (full_jid); } static DiscoWaiter * find_matching_waiter (GSList *waiters, TpHandle godot, const gchar *resource) { GSList *i; for (i = waiters; NULL != i; i = i->next) { DiscoWaiter *waiter = i->data; if (waiter->handle == godot && !tp_strdiff (waiter->resource, resource)) return waiter; } return NULL; } static void emit_capabilities_update (GabblePresenceCache *cache, TpHandle handle, const GabbleCapabilitySet *old_cap_set, const GabbleCapabilitySet *new_cap_set) { if (gabble_capability_set_equals (old_cap_set, new_cap_set)) { DEBUG ("no change in caps for handle %u", handle); } else { if (DEBUGGING) { gchar *diff = gabble_capability_set_dump_diff (old_cap_set, new_cap_set, " "); DEBUG ("Emitting caps update for handle %u\n%s", handle, diff); g_free (diff); } g_signal_emit (cache, signals[CAPABILITIES_UPDATE], 0, handle, old_cap_set, new_cap_set); } } /** * set_caps_for: * * Sets caps for @waiter to (@caps, @cap_set), having received * a trusted reply from @responder_{handle,jid}. */ static void set_caps_for (DiscoWaiter *waiter, GabblePresenceCache *cache, GabbleCapabilitySet *cap_set, guint client_types, GPtrArray *data_forms, TpHandle responder_handle, const gchar *responder_jid) { GabblePresence *presence = gabble_presence_cache_get (cache, waiter->handle); GabbleCapabilitySet *old_cap_set; const GabbleCapabilitySet *new_cap_set; if (presence == NULL) return; old_cap_set = gabble_presence_dup_caps (presence); DEBUG ("setting caps for %d (thanks to %d %s)", waiter->handle, responder_handle, responder_jid); gabble_presence_set_capabilities (presence, waiter->resource, cap_set, data_forms, waiter->serial); new_cap_set = gabble_presence_peek_caps (presence); emit_capabilities_update (cache, waiter->handle, old_cap_set, new_cap_set); gabble_capability_set_free (old_cap_set); if (gabble_presence_update_client_types (presence, waiter->resource, client_types)) g_signal_emit (cache, signals[CLIENT_TYPES_UPDATED], 0, waiter->handle); } static void emit_capabilities_discovered (GabblePresenceCache *cache, TpHandle handle) { g_signal_emit (cache, signals[CAPABILITIES_DISCOVERED], 0, handle); } static guint client_types_from_message (TpHandle handle, WockyNode *lm_node, const gchar *resource) { WockyNode *identity, *query_result = (WockyNode *) lm_node; WockyNodeIter iter; guint client_types = 0; /* Find all identity nodes in the result. */ wocky_node_iter_init (&iter, query_result, "identity", NS_DISCO_INFO); while (wocky_node_iter_next (&iter, &identity)) { const gchar *category = wocky_node_get_attribute (identity, "category"); const gchar *type = wocky_node_get_attribute (identity, "type"); guint value; if (category == NULL || type == NULL) continue; /* So, turns out if you disco a specific resource of a gtalk contact, the Google servers will reply with the identity node as if you disco'd the bare jid, so will get something like: <identity category='account' type='registered' name='Google Talk User Account'/> which is just great. So, let's special case android phones as their resources will start with "android" and let's just say they're phones. */ if (!tp_strdiff (category, "account") && (resource != NULL && g_str_has_prefix (resource, "android")) && !tp_strdiff (type, "registered")) { client_types |= GABBLE_CLIENT_TYPE_PHONE; } else if (!tp_strdiff (category, "client") && gabble_flag_from_nick (GABBLE_TYPE_CLIENT_TYPE, type, &value)) { DEBUG ("Got type for %u: %s (%u)", handle, type, value); client_types |= value; } } return client_types; } static GPtrArray * data_forms_from_message (WockyNode *node) { GPtrArray *out = g_ptr_array_new_with_free_func (g_object_unref); WockyNodeIter iter; WockyNode *x_node = NULL; wocky_node_iter_init (&iter, node, "x", WOCKY_XMPP_NS_DATA); while (wocky_node_iter_next (&iter, &x_node)) { WockyDataForm *form = wocky_data_form_new_from_node (x_node, NULL); /* we've already parsed the reply to check the hash matches, so * we can already guarantee these data forms will be parsed * fine */ if (G_LIKELY (form != NULL)) g_ptr_array_add (out, form); } return out; } static void _signal_presences_updated (GabblePresenceCache *cache, TpHandle handle) { GArray *handles; handles = g_array_sized_new (FALSE, FALSE, sizeof (TpHandle), 1); g_array_append_val (handles, handle); g_signal_emit (cache, signals[PRESENCES_UPDATED], 0, handles); g_array_unref (handles); } static void _caps_disco_cb (GabbleDisco *disco, GabbleDiscoRequest *request, const gchar *jid, const gchar *node, WockyNode *query_result, GError *error, gpointer user_data) { GSList *waiters, *i; DiscoWaiter *waiter_self; GabblePresenceCache *cache; GabblePresenceCachePrivate *priv; TpHandleRepoIface *contact_repo; GabbleCapabilitySet *cap_set; guint trust; TpHandle handle = 0; gboolean bad_hash = FALSE; TpBaseConnection *base_conn; gchar *resource; gboolean jid_is_valid; gpointer key; guint client_types = 0; GPtrArray *data_forms = NULL; cache = GABBLE_PRESENCE_CACHE (user_data); priv = cache->priv; base_conn = TP_BASE_CONNECTION (priv->conn); contact_repo = tp_base_connection_get_handles (base_conn, TP_HANDLE_TYPE_CONTACT); if (NULL == node) { DEBUG ("got disco response with NULL node, ignoring"); return; } waiters = g_hash_table_lookup (priv->disco_pending, node); if (NULL != error) { DEBUG ("disco query failed: %s", error->message); disco_failed (cache, disco, node, waiters); return; } handle = tp_handle_ensure (contact_repo, jid, NULL, NULL); if (handle == 0) { DEBUG ("Ignoring presence from invalid JID %s", jid); return; } /* If tp_handle_ensure () was happy with the jid, it's valid. */ jid_is_valid = wocky_decode_jid (jid, NULL, NULL, &resource); g_assert (jid_is_valid); waiter_self = find_matching_waiter (waiters, handle, resource); g_free (resource); if (NULL == waiter_self) { DEBUG ("Ignoring non requested disco reply from %s", jid); return; } /* Now onto caps */ cap_set = gabble_capability_set_new_from_stanza (query_result); client_types = client_types_from_message (handle, query_result, waiter_self->resource); data_forms = data_forms_from_message (query_result); /* Only 'sha-1' is mandatory to implement by XEP-0115. If the remote contact * uses another hash algorithm, don't check the hash and fallback to the old * method. The hash method is not included in the discovery request nor * response but we saved it in disco_pending when we received the presence * stanza. */ if (!tp_strdiff (waiter_self->hash, "sha-1")) { gchar *computed_hash; computed_hash = wocky_caps_hash_compute_from_node (query_result); if (computed_hash == NULL) { DEBUG ("Unable to compute caps hash for '%s'.", jid); trust = 0; bad_hash = TRUE; } else if (g_str_equal (waiter_self->ver, computed_hash)) { trust = capability_info_recvd (cache, node, handle, cap_set, CAPABILITY_BUNDLE_ENOUGH_TRUST, client_types, data_forms); } else { DEBUG ("The verification string '%s' announced by '%s' does not " "match our hash of their disco reply '%s'.", waiter_self->ver, jid, computed_hash); trust = 0; bad_hash = TRUE; } g_free (computed_hash); } else { trust = capability_info_recvd (cache, node, handle, cap_set, 1, client_types, data_forms); } /* Remove the node from the hash table without freeing the key or list of * waiters. * * In the 'enough trust' case, this needs to be done before emitting the * signal, so that when recipients of the capabilities-discovered signal ask * whether we're unsure about the handle, there is no pending disco request * that would make us unsure. * * In the 'not enough trust' branch, we re-use 'key' when updating the table. */ if (!g_hash_table_lookup_extended (priv->disco_pending, node, &key, NULL)) g_assert_not_reached (); g_hash_table_steal (priv->disco_pending, node); if (trust >= CAPABILITY_BUNDLE_ENOUGH_TRUST) { WockyNodeTree *query_node = wocky_node_tree_new_from_node (query_result); WockyCapsCache *caps_cache = wocky_caps_cache_dup_shared (); if (DEBUGGING) { gchar *tmp = gabble_capability_set_dump (cap_set, " "); DEBUG ("trusting %s to mean:\n%s", node, tmp); g_free (tmp); } /* Update external cache. */ wocky_caps_cache_insert (caps_cache, node, query_node); g_object_unref (caps_cache); g_object_unref (query_node); /* We trust this caps node. Serve all its waiters. */ for (i = waiters; NULL != i; i = i->next) { DiscoWaiter *waiter = (DiscoWaiter *) i->data; set_caps_for (waiter, cache, cap_set, client_types, data_forms, handle, jid); emit_capabilities_discovered (cache, waiter->handle); } g_free (key); disco_waiter_list_free (waiters); } else { /* We don't trust this yet (either the hash was bad, or we haven't had * enough responses, as appropriate). */ /* Set caps for the contact that replied (if the hash was correct) and * remove them from the list of waiters. * FIXME I think we should respect the caps, even if the hash is wrong, * for the jid that answered the query. */ if (!bad_hash) { if (DEBUGGING) { gchar *tmp = gabble_capability_set_dump (cap_set, " "); DEBUG ("%s not yet fully trusted to mean:\n%s", node, tmp); g_free (tmp); } set_caps_for (waiter_self, cache, cap_set, client_types, data_forms, handle, jid); } waiters = g_slist_remove (waiters, waiter_self); g_hash_table_insert (priv->disco_pending, key, waiters); emit_capabilities_discovered (cache, waiter_self->handle); disco_waiter_free (waiter_self); /* Ensure that we have enough pending requests to get enough trust for * this node. */ for (i = waiters; i != NULL; i = i->next) { DiscoWaiter *waiter = (DiscoWaiter *) i->data; if (trust + disco_waiter_list_get_request_count (waiters) >= CAPABILITY_BUNDLE_ENOUGH_TRUST) break; if (!waiter->disco_requested) redisco (cache, disco, waiter, node); } } gabble_capability_set_free (cap_set); g_ptr_array_unref (data_forms); } static gboolean get_google_cap (const gchar *fragment, const gchar **ns) { if (!tp_strdiff (fragment, "voice-v1")) { *ns = NS_GOOGLE_FEAT_VOICE; return TRUE; } else if (!tp_strdiff (fragment, "video-v1")) { *ns = NS_GOOGLE_FEAT_VIDEO; return TRUE; } else if (!tp_strdiff (fragment, "share-v1")) { *ns = NS_GOOGLE_FEAT_SHARE; return TRUE; } else if (!tp_strdiff (fragment, "sms-v1")) { *ns = NULL; return TRUE; } else if (!tp_strdiff (fragment, "pmuc-v1")) { *ns = NULL; return TRUE; } else if (!tp_strdiff (fragment, "camera-v1")) { *ns = NULL; return TRUE; } return FALSE; } static void _process_caps_uri (GabblePresenceCache *cache, const gchar *from, const gchar *node, const gchar *fragment, const gchar *hash, const gchar *ver, TpHandle handle, const gchar *resource, guint serial) { GabbleCapabilityInfo *info; WockyNodeTree *cached_query_reply; GabbleCapabilitySet *cached_caps = NULL; GabblePresenceCachePrivate *priv; TpHandleRepoIface *contact_repo; WockyCapsCache *caps_cache; gchar *uri = g_strdup_printf ("%s#%s", node, fragment); const gchar *ns = NULL; priv = cache->priv; contact_repo = tp_base_connection_get_handles ( (TpBaseConnection *) priv->conn, TP_HANDLE_TYPE_CONTACT); info = capability_info_get (cache, uri); caps_cache = wocky_caps_cache_dup_shared (); cached_query_reply = wocky_caps_cache_lookup (caps_cache, uri); if (cached_query_reply != NULL) { WockyNode *query = wocky_node_tree_get_top_node (cached_query_reply); cached_caps = gabble_capability_set_new_from_stanza (query); if (cached_caps == NULL) { gchar *query_str = wocky_node_to_string (query); g_warning ("couldn't re-parse cached query node, which was: %s", query_str); g_free (query_str); } } g_object_unref (caps_cache); if (cached_caps != NULL || info->trust >= CAPABILITY_BUNDLE_ENOUGH_TRUST || tp_intset_is_member (info->guys, handle)) { GabblePresence *presence = gabble_presence_cache_get (cache, handle); GabbleCapabilitySet *cap_set = cached_caps ? cached_caps : info->cap_set; /* we already have enough trust for this node; apply the cached value to * the (handle, resource) */ DEBUG ("enough trust for URI %s, setting caps for %u (%s)", uri, handle, from); if (presence) { guint types; gabble_presence_set_capabilities ( presence, resource, cap_set, info->data_forms, serial); /* We can only get this information from actual disco replies, * so we depend on having this information from the caps cache. */ if (cached_query_reply != NULL) { WockyNode *query = wocky_node_tree_get_top_node (cached_query_reply); types = client_types_from_message (handle, query, resource); } else { types = info->client_types; } if (gabble_presence_update_client_types (presence, resource, types)) g_signal_emit (cache, signals[CLIENT_TYPES_UPDATED], 0, handle); } else DEBUG ("presence not found"); if (cached_caps != NULL) gabble_capability_set_free (cached_caps); } else if (hash == NULL && get_google_cap (fragment, &ns)) { /* if the hash is NULL then are looking at the ext='...' values, * so this is starting to smell like Google */ GabblePresence *presence = gabble_presence_cache_get (cache, handle); /* we already know about this fragment; apply the known value to * the (handle, resource) */ DEBUG ("we know about fragment %s, setting caps for %u (%s)", fragment, handle, from); if (presence != NULL) { GabbleCapabilitySet *cap_set = gabble_capability_set_new (); if (ns != NULL) gabble_capability_set_add (cap_set, ns); gabble_presence_set_capabilities ( presence, resource, cap_set, NULL, serial); gabble_capability_set_free (cap_set); } else { DEBUG ("presence not found"); } } else { GSList *waiters; DiscoWaiter *waiter; guint possible_trust; gboolean found; gpointer key; gpointer value = NULL; DEBUG ("not enough trust for URI %s", uri); /* Are we already waiting for responses for this URI? */ found = g_hash_table_lookup_extended (priv->disco_pending, uri, &key, &value); waiters = (GSList *) value; waiter = find_matching_waiter (waiters, handle, resource); if (waiter != NULL) { /* We've already asked this jid about this node; just update the * serial. */ DEBUG ("updating serial for waiter (%s, %s) from %u to %u", from, uri, waiter->serial, serial); waiter->serial = serial; goto out; } waiter = disco_waiter_new (contact_repo, handle, resource, hash, ver, serial); waiters = g_slist_prepend (waiters, waiter); /* If the URI was already in the hash table, steal it and re-use the same * URI for the following insertion. Otherwise, make a copy of the URI for * use as a key. */ if (found) g_hash_table_steal (priv->disco_pending, key); else key = g_strdup (uri); g_hash_table_insert (priv->disco_pending, key, waiters); /* When all the responses we're waiting for return, will we have enough * trust? */ possible_trust = disco_waiter_list_get_request_count (waiters); if (info->trust + possible_trust < CAPABILITY_BUNDLE_ENOUGH_TRUST) { /* DISCO */ DEBUG ("only %u trust out of %u possible thus far, sending " "disco for URI %s", info->trust + possible_trust, CAPABILITY_BUNDLE_ENOUGH_TRUST, uri); gabble_disco_request (priv->conn->disco, GABBLE_DISCO_TYPE_INFO, from, uri, _caps_disco_cb, cache, G_OBJECT (cache), NULL); /* enough DISCO for you, buddy */ waiter->disco_requested = TRUE; } } out: if (cached_query_reply != NULL) g_object_unref (cached_query_reply); g_free (uri); } static void _process_caps (GabblePresenceCache *cache, GabblePresence *presence, TpHandle handle, const gchar *from, WockyNode *lm_node) { const gchar *resource; GSList *uris, *i; GabblePresenceCachePrivate *priv; GabbleCapabilitySet *old_cap_set = NULL; guint serial; const gchar *hash, *ver, *node; priv = cache->priv; serial = priv->caps_serial++; resource = strchr (from, '/'); if (resource != NULL) resource++; uris = _parse_cap_bundles (lm_node, &hash, &ver, &node); if (presence) { old_cap_set = gabble_presence_dup_caps (presence); _parse_node (presence, lm_node, resource, serial); } /* XEP-0115 §8.4 allows a server to strip out <c/> from presences it relays * to a client if it knows that the <c/> hasn't changed since the last time * it relayed one for this resource to the client. Thus, the client MUST NOT * expect to get <c/> on every <presence/>, and shouldn't erase previous caps * in that case. * * If the <presence/> stanza didn't contain a <c/> node at all, then there * will be no iterations of this loop, and hence no calls to * gabble_presence_set_capabilities(), and hence the caps will be preserved. * Not pretty, but it seems to work. */ for (i = uris; NULL != i; i = i->next) { _process_caps_uri (cache, from, node, (gchar *) i->data, hash, ver, handle, resource, serial); g_free (i->data); } if (presence) { const GabbleCapabilitySet *new_cap_set = gabble_presence_peek_caps (presence); emit_capabilities_update (cache, handle, old_cap_set, new_cap_set); } else { DEBUG ("No presence for handle %u, not updating caps", handle); } if (old_cap_set != NULL) gabble_capability_set_free (old_cap_set); g_slist_free (uris); } static void presence_cache_check_for_decloak_request ( GabblePresenceCache *cache, WockyStanza *stanza, TpHandle handle, const gchar *from) { GabblePresenceCachePrivate *priv = cache->priv; WockyNode *presence_node = wocky_stanza_get_top_node (stanza); WockyNode *child_node; /* If we receive (directed or broadcast) presence of any sort from someone, * it counts as a reply to any pending de-cloak request we might have been * tracking */ g_hash_table_remove (priv->decloak_requests, GUINT_TO_POINTER (handle)); child_node = wocky_node_get_child_ns (presence_node, "temppres", NS_TEMPPRES); if (child_node != NULL) { gboolean decloak; const gchar *reason; /* this is a request to de-cloak, i.e. leak a minimal version of our * presence to the peer */ g_object_get (priv->conn, "decloak-automatically", &decloak, NULL); reason = wocky_node_get_attribute (child_node, "reason"); if (reason == NULL) reason = ""; DEBUG ("Considering whether to decloak, reason='%s', conclusion=%d", reason, decloak); conn_decloak_emit_requested (priv->conn, handle, reason, decloak); if (decloak) gabble_connection_send_capabilities (priv->conn, from, NULL); } } /* FIXME: in a cruel twist of fate, this is called by GabbleMucChannel! * Presumably this is because the handler priority here is MIN, so WockyMuc * steals the presence stanza before we can scrape our information out of it? */ gboolean gabble_presence_parse_presence_message ( GabblePresenceCache *cache, TpHandle handle, const gchar *from, WockyStanza *message) { GabblePresenceCachePrivate *priv = cache->priv; const gchar *prio; gint8 priority = 0; const gchar *resource, *status_message = NULL; gchar *my_full_jid; WockyNode *presence_node; WockyStanzaSubType sub_type; GabblePresenceId presence_id; GabblePresence *presence; /* The server should not send back the presence stanza about ourself (same * resource). If it does, we just ignore the received stanza. We want to * avoid any infinite ping-pong with the server due to XEP-0153 4.2-2-3. */ my_full_jid = gabble_connection_get_full_jid (priv->conn); if (!tp_strdiff (from, my_full_jid)) { g_free (my_full_jid); return TRUE; } g_free (my_full_jid); presence_node = wocky_stanza_get_top_node (message); g_assert (0 == strcmp (presence_node->name, "presence")); resource = strchr (from, '/'); if (resource != NULL) resource++; presence = gabble_presence_cache_get (cache, handle); if (NULL != presence) /* Once we've received presence from somebody, we don't need to keep the * presence around when it's unavailable. */ presence->keep_unavailable = FALSE; status_message = wocky_node_get_content_from_child (presence_node, "status"); prio = wocky_node_get_content_from_child (presence_node, "priority"); if (prio != NULL) priority = CLAMP (atoi (prio), G_MININT8, G_MAXINT8); presence_cache_check_for_decloak_request (cache, message, handle, from); wocky_stanza_get_type_info (message, NULL, &sub_type); switch (sub_type) { case WOCKY_STANZA_SUB_TYPE_NONE: case WOCKY_STANZA_SUB_TYPE_AVAILABLE: presence_id = _presence_node_get_status (presence_node); gabble_presence_cache_update (cache, handle, resource, presence_id, status_message, priority); if (!presence) presence = gabble_presence_cache_get (cache, handle); _grab_nickname (cache, handle, from, presence_node); _grab_avatar_sha1 (cache, handle, from, presence_node); _process_caps (cache, presence, handle, from, presence_node); return TRUE; case WOCKY_STANZA_SUB_TYPE_ERROR: { GError *error = NULL; gboolean ret; NODE_DEBUG (presence_node, "Received error presence"); ret = wocky_stanza_extract_errors (message, NULL, &error, NULL, NULL); g_assert (ret); /* If there's a <status/> in this presence, it's our own echoed back at * us. So we don't want to use that. Instead, we use the <error><text> if * there is any, or the name of the error condition if not. */ if (tp_str_empty (error->message)) status_message = wocky_enum_to_nick (WOCKY_TYPE_XMPP_ERROR, error->code); else status_message = error->message; gabble_presence_cache_update (cache, handle, resource, GABBLE_PRESENCE_ERROR, status_message, priority); return TRUE; } case WOCKY_STANZA_SUB_TYPE_UNAVAILABLE: if (gabble_roster_handle_sends_presence_to_us (priv->conn->roster, handle)) presence_id = GABBLE_PRESENCE_OFFLINE; else presence_id = GABBLE_PRESENCE_UNKNOWN; gabble_presence_cache_update (cache, handle, resource, presence_id, status_message, priority); return TRUE; default: return FALSE; } } /* FIXME: this scrapes nicknames out of <messages>, and relies on im-channel.c * setting keep_unavailable back to FALSE to make nicknames random peers send * us disappear once we close the accompanying messages. As a side effect, it * makes specifying <nick> in MUC messages work, which is questionable * behaviour. See vcard/test-alias-message.py. * * It would be cleaner to make the IM channel stash the nickname if we want it * to go away when the channel closes, rather than relying on this * spooky-action-at-a-distance. */ static gboolean _parse_message_message ( WockyPorter *porter, WockyStanza *message, gpointer user_data) { GabblePresenceCache *cache = GABBLE_PRESENCE_CACHE (user_data); GabblePresenceCachePrivate *priv = cache->priv; TpHandleRepoIface *contact_repo = tp_base_connection_get_handles ( (TpBaseConnection *) priv->conn, TP_HANDLE_TYPE_CONTACT); const gchar *from = wocky_stanza_get_from (message); TpHandle handle; WockyStanzaSubType sub_type; WockyNode *node; GabblePresence *presence; if (NULL == from) { STANZA_DEBUG (message, "message without from attribute, ignoring"); return FALSE; } handle = tp_handle_ensure (contact_repo, from, NULL, NULL); if (0 == handle) { STANZA_DEBUG (message, "ignoring message from malformed jid"); return FALSE; } wocky_stanza_get_type_info (message, NULL, &sub_type); switch (sub_type) { case WOCKY_STANZA_SUB_TYPE_NONE: case WOCKY_STANZA_SUB_TYPE_NORMAL: case WOCKY_STANZA_SUB_TYPE_CHAT: case WOCKY_STANZA_SUB_TYPE_GROUPCHAT: break; default: return FALSE; } presence = gabble_presence_cache_get (cache, handle); if (NULL == presence) { presence = _cache_insert (cache, handle); presence->keep_unavailable = TRUE; } node = wocky_stanza_get_top_node (message); _grab_nickname (cache, handle, from, node); return FALSE; } /* * gabble_presence_cache_presence_cb: * * Called by Wocky when we get an incoming <presence>. */ static gboolean gabble_presence_cache_presence_cb ( WockyPorter *porter, WockyStanza *message, gpointer user_data) { GabblePresenceCache *cache = GABBLE_PRESENCE_CACHE (user_data); GabblePresenceCachePrivate *priv = cache->priv; TpHandleRepoIface *contact_repo = tp_base_connection_get_handles ( (TpBaseConnection *) priv->conn, TP_HANDLE_TYPE_CONTACT); const char *from = wocky_stanza_get_from (message); TpHandle handle; if (NULL == from) { STANZA_DEBUG (message, "message without from attribute, ignoring"); return FALSE; } handle = tp_handle_ensure (contact_repo, from, NULL, NULL); if (0 == handle) { STANZA_DEBUG (message, "ignoring message from malformed jid"); return FALSE; } return gabble_presence_parse_presence_message (cache, handle, from, message); } GabblePresenceCache * gabble_presence_cache_new (GabbleConnection *conn) { return g_object_new (GABBLE_TYPE_PRESENCE_CACHE, "connection", conn, NULL); } GabblePresence * gabble_presence_cache_get (GabblePresenceCache *cache, TpHandle handle) { GabblePresenceCachePrivate *priv = cache->priv; TpHandleRepoIface *contact_repo = tp_base_connection_get_handles ( (TpBaseConnection *) priv->conn, TP_HANDLE_TYPE_CONTACT); g_assert (tp_handle_is_valid (contact_repo, handle, NULL)); return g_hash_table_lookup (priv->presence, GUINT_TO_POINTER (handle)); } GabblePresence * gabble_presence_cache_get_for_contact ( GabblePresenceCache *cache, WockyContact *contact) { GabblePresenceCachePrivate *priv = cache->priv; TpHandle handle = ensure_handle_from_contact (priv->conn, contact); return g_hash_table_lookup (priv->presence, GUINT_TO_POINTER (handle)); } void gabble_presence_cache_maybe_remove ( GabblePresenceCache *cache, TpHandle handle) { GabblePresenceCachePrivate *priv = cache->priv; TpHandleRepoIface *contact_repo = tp_base_connection_get_handles ( (TpBaseConnection *) priv->conn, TP_HANDLE_TYPE_CONTACT); GabblePresence *presence; presence = gabble_presence_cache_get (cache, handle); if (NULL == presence) return; if ((presence->status == GABBLE_PRESENCE_OFFLINE || presence->status == GABBLE_PRESENCE_UNKNOWN) && presence->status_message == NULL && !presence->keep_unavailable) { const gchar *jid; jid = tp_handle_inspect (contact_repo, handle); DEBUG ("discarding cached presence for unavailable jid %s", jid); g_hash_table_remove (priv->presence, GUINT_TO_POINTER (handle)); tp_handle_set_remove (priv->presence_handles, handle); } } static GabblePresence * _cache_insert ( GabblePresenceCache *cache, TpHandle handle) { GabblePresenceCachePrivate *priv = cache->priv; GabblePresence *presence; presence = gabble_presence_new (); g_hash_table_insert (priv->presence, GUINT_TO_POINTER (handle), presence); tp_handle_set_add (priv->presence_handles, handle); return presence; } static gboolean gabble_presence_cache_do_update ( GabblePresenceCache *cache, TpHandle handle, const gchar *resource, GabblePresenceId presence_id, const gchar *status_message, gint8 priority, gboolean *update_client_types) { GabblePresenceCachePrivate *priv = cache->priv; GabblePresence *presence; GabbleCapabilitySet *old_cap_set; const GabbleCapabilitySet *new_cap_set; gboolean ret = FALSE; if (DEBUGGING) { TpHandleRepoIface *contact_repo = tp_base_connection_get_handles ( (TpBaseConnection *) priv->conn, TP_HANDLE_TYPE_CONTACT); const gchar *jid = tp_handle_inspect (contact_repo, handle); const gchar *presence_name = wocky_enum_to_nick ( GABBLE_TYPE_PRESENCE_ID, presence_id); if (presence_name == NULL) presence_name = "plugin-specific, not an element of GabblePresenceId"; DEBUG ("%s (%d) resource %s prio %d presence %d (%s) message \"%s\"", jid, handle, resource == NULL ? "<null>" : resource, priority, presence_id, presence_name, status_message == NULL ? "<null>" : status_message); } presence = gabble_presence_cache_get (cache, handle); if (presence == NULL) presence = _cache_insert (cache, handle); old_cap_set = gabble_presence_dup_caps (presence); ret = gabble_presence_update (presence, resource, presence_id, status_message, priority, update_client_types, time (NULL)); new_cap_set = gabble_presence_peek_caps (presence); emit_capabilities_update (cache, handle, old_cap_set, new_cap_set); gabble_capability_set_free (old_cap_set); return ret; } void gabble_presence_cache_update ( GabblePresenceCache *cache, TpHandle handle, const gchar *resource, GabblePresenceId presence_id, const gchar *status_message, gint8 priority) { gboolean update_client_types = FALSE; if (gabble_presence_cache_do_update (cache, handle, resource, presence_id, status_message, priority, &update_client_types)) { _signal_presences_updated (cache, handle); } if (update_client_types) g_signal_emit (cache, signals[CLIENT_TYPES_UPDATED], 0, handle); gabble_presence_cache_maybe_remove (cache, handle); } void gabble_presence_cache_update_many ( GabblePresenceCache *cache, const GArray *contact_handles, const gchar *resource, GabblePresenceId presence_id, const gchar *status_message, gint8 priority) { GArray *updated; guint i; updated = g_array_sized_new (FALSE, FALSE, sizeof (TpHandle), contact_handles->len); for (i = 0 ; i < contact_handles->len ; i++) { TpHandle handle; handle = g_array_index (contact_handles, TpHandle, i); if (gabble_presence_cache_do_update (cache, handle, resource, presence_id, status_message, priority, NULL)) { g_array_append_val (updated, handle); } } if (updated->len > 0) g_signal_emit (cache, signals[PRESENCES_UPDATED], 0, updated); g_array_unref (updated); for (i = 0 ; i < contact_handles->len ; i++) { TpHandle handle; handle = g_array_index (contact_handles, TpHandle, i); gabble_presence_cache_maybe_remove (cache, handle); } } static void gabble_presence_cache_add_bundle_caps (GabblePresenceCache *cache, const gchar *node, const gchar *namespace) { GabbleCapabilityInfo *info; info = capability_info_get (cache, node); /* The caps are immediately valid, because we already know this bundle */ if (info->cap_set == NULL) info->cap_set = gabble_capability_set_new (); info->trust = CAPABILITY_BUNDLE_ENOUGH_TRUST; if (namespace != NULL) gabble_capability_set_add (info->cap_set, namespace); } void gabble_presence_cache_add_own_caps ( GabblePresenceCache *cache, const gchar *ver, const GabbleCapabilitySet *cap_set, const GPtrArray *identities, GPtrArray *data_forms) { gchar *uri = g_strdup_printf ("%s#%s", NS_GABBLE_CAPS, ver); GabbleCapabilityInfo *info = capability_info_get (cache, uri); TpBaseConnection *base_conn = TP_BASE_CONNECTION (cache->priv->conn); if (info->complete) goto out; DEBUG ("caching our own caps (%s)", uri); /* If this node was already in the cache but not labelled as complete, either * the entry's correct, or someone's poisoning us with a SHA-1 collision. * Let's update the entry just in case. */ if (info->cap_set == NULL) { info->cap_set = gabble_capability_set_copy (cap_set); } else { gabble_capability_set_clear (info->cap_set); gabble_capability_set_update (info->cap_set, cap_set); } wocky_disco_identity_array_free (info->identities); info->identities = NULL; if (identities != NULL) info->identities = wocky_disco_identity_array_copy (identities); info->complete = TRUE; info->trust = CAPABILITY_BUNDLE_ENOUGH_TRUST; tp_intset_add (info->guys, tp_base_connection_get_self_handle (base_conn)); replace_data_forms (info, data_forms); /* FIXME: we should satisfy any waiters for this node now. fd.o bug #24619. */ out: g_free (uri); } /** * gabble_presence_cache_peek_own_caps: * @cache: a presence cache * @ver: a verification string or bundle name * * If the capabilities corresponding to @ver have been added to the cache with * gabble_presence_cache_add_own_caps(), returns a set of those capabilities; * otherwise, returns %NULL. * * Since the cache only records features Gabble understands (omitting unknown * features, identities, and data forms), we can only serve up disco replies * from the cache if we know we once advertised exactly this verification * string ourselves. * * Returns: a set of capabilities, if we know exactly what @ver means. */ const GabbleCapabilityInfo * gabble_presence_cache_peek_own_caps ( GabblePresenceCache *cache, const gchar *ver) { gchar *uri = g_strdup_printf ("%s#%s", NS_GABBLE_CAPS, ver); GabbleCapabilityInfo *info = capability_info_get (cache, uri); g_free (uri); if (info->complete) { g_assert (info->cap_set != NULL); return info; } else { return NULL; } } void gabble_presence_cache_really_remove ( GabblePresenceCache *cache, TpHandle handle) { GabblePresenceCachePrivate *priv = cache->priv; TpHandleRepoIface *contact_repo = tp_base_connection_get_handles ( (TpBaseConnection *) priv->conn, TP_HANDLE_TYPE_CONTACT); const gchar *jid; jid = tp_handle_inspect (contact_repo, handle); DEBUG ("forced to discard cached presence for jid %s", jid); g_hash_table_remove (priv->presence, GUINT_TO_POINTER (handle)); tp_handle_set_remove (priv->presence_handles, handle); } void gabble_presence_cache_contacts_added_to_olpc_view (GabblePresenceCache *self, TpHandleSet *handles) { GArray *tmp, *changed; guint i; tmp = tp_handle_set_to_array (handles); changed = g_array_new (FALSE, FALSE, sizeof (TpHandle)); for (i = 0; i < tmp->len; i++) { TpHandle handle; GabblePresence *presence; handle = g_array_index (tmp, TpHandle, i); presence = gabble_presence_cache_get (self, handle); if (presence == NULL) { presence = _cache_insert (self, handle); } if (gabble_presence_added_to_view (presence)) { g_array_append_val (changed, handle); } } if (changed->len > 0) { g_signal_emit (self, signals[PRESENCES_UPDATED], 0, changed); } g_array_unref (tmp); g_array_unref (changed); } void gabble_presence_cache_contacts_removed_from_olpc_view ( GabblePresenceCache *self, TpHandleSet *handles) { GArray *tmp, *changed; guint i; tmp = tp_handle_set_to_array (handles); changed = g_array_new (FALSE, FALSE, sizeof (TpHandle)); for (i = 0; i < tmp->len; i++) { TpHandle handle; GabblePresence *presence; handle = g_array_index (tmp, TpHandle, i); presence = gabble_presence_cache_get (self, handle); if (presence == NULL) { presence = _cache_insert (self, handle); } if (gabble_presence_removed_from_view (presence)) { g_array_append_val (changed, handle); gabble_presence_cache_maybe_remove (self, handle); } } if (changed->len > 0) { g_signal_emit (self, signals[PRESENCES_UPDATED], 0, changed); } g_array_unref (tmp); g_array_unref (changed); } static gboolean gabble_presence_cache_caps_pending (GabblePresenceCache *cache, TpHandle handle) { GabblePresenceCachePrivate *priv = cache->priv; GList *uris, *li; uris = g_hash_table_get_values (priv->disco_pending); for (li = uris; li != NULL; li = li->next) { GSList *waiters; for (waiters = li->data; waiters != NULL; waiters = waiters->next) { DiscoWaiter *w = waiters->data; if (w->handle == handle) { g_list_free (uris); return TRUE; } } } g_list_free (uris); return FALSE; } /* Return whether we're "unsure" about the capabilities of @handle. * Currently, this means either of: * * - we've connected within the last UNSURE_PERIOD seconds and haven't * received presence for @handle yet * - we know what @handle's caps hash/bundles are, but we're still * performing service discovery to find out what they mean */ gboolean gabble_presence_cache_is_unsure (GabblePresenceCache *cache, TpHandle handle) { GabblePresenceCachePrivate *priv = cache->priv; TpBaseConnection *base_conn = TP_BASE_CONNECTION (priv->conn); /* we might not have had any presence at all - if we're not connected yet, or * are still in the "unsure period", assume we might get initial presence * soon. * * Presences with keep_unavailable are the result of caching someone's * nick from <message> stanzas, so they don't count as real presence - if * someone sends us a <message>, their presence might still follow. */ if (tp_base_connection_get_status (base_conn) != TP_CONNECTION_STATUS_CONNECTED || priv->unsure_id != 0) { GabblePresence *presence = gabble_presence_cache_get (cache, handle); if (presence == NULL || presence->keep_unavailable) { DEBUG ("No presence for %u yet, still waiting for possible initial " "presence burst", handle); return TRUE; } } /* FIXME: if we've had the roster, we can be sure that people who're * not in it won't be sending us an initial presence, so ideally the * above should be roster-aware? */ /* if we don't know what the caps mean, we're unsure */ if (gabble_presence_cache_caps_pending (cache, handle)) { DEBUG ("Still working out what %u's caps hash means", handle); return TRUE; } /* if we're waiting for a de-cloak response, we're unsure */ if (tp_handle_set_is_member (priv->decloak_handles, handle)) { DEBUG ("Waiting to see if %u will decloak", handle); return TRUE; } DEBUG ("No, I'm sure about %u by now", handle); return FALSE; } static gboolean gabble_presence_cache_decloak_timeout_cb (gpointer data) { DecloakContext *dc = data; GabblePresenceCache *self = dc->cache; TpHandle handle = dc->handle; DEBUG ("De-cloak request for %u timed out", handle); /* This frees @dc, do not dereference it afterwards. This needs to be done * before emitting the signal, so that when recipients of the channel ask * whether we're unsure about the handle, there is no pending decloak * request that would make us unsure. */ g_hash_table_remove (self->priv->decloak_requests, GUINT_TO_POINTER (handle)); /* As a side-effect of freeing @dc, this should have happened. */ g_assert (!tp_handle_set_is_member (self->priv->decloak_handles, handle)); /* FIXME: this is an abuse of this signal, but it serves the same * purpose: poking any pending media channels to tell them that @handle * might have left the "unsure" state */ emit_capabilities_discovered (self, handle); return FALSE; } /* @reason must be a statically-allocated string. */ gboolean gabble_presence_cache_request_decloaking (GabblePresenceCache *self, TpHandle handle, const gchar *reason) { DecloakContext *dc; GabblePresence *presence; TpHandleRepoIface *contact_repo = tp_base_connection_get_handles ( (TpBaseConnection *) self->priv->conn, TP_HANDLE_TYPE_CONTACT); presence = gabble_presence_cache_get (self, handle); if (presence != NULL && presence->status != GABBLE_PRESENCE_OFFLINE && presence->status != GABBLE_PRESENCE_UNKNOWN) { DEBUG ("We know that this contact is online, no point asking for " "decloak"); return FALSE; } /* if we've already asked them to de-cloak for the same reason, do nothing */ if (tp_handle_set_is_member (self->priv->decloak_handles, handle)) { dc = g_hash_table_lookup (self->priv->decloak_requests, GUINT_TO_POINTER (handle)); if (dc != NULL && !tp_strdiff (reason, dc->reason)) { DEBUG ("Already asked %u to decloak for reason '%s'", handle, reason); return TRUE; } } DEBUG ("Asking %u to decloak", handle); dc = decloak_context_new (self, handle, reason); dc->timeout_id = g_timeout_add_seconds (DECLOAK_PERIOD, gabble_presence_cache_decloak_timeout_cb, dc); g_hash_table_insert (self->priv->decloak_requests, GUINT_TO_POINTER (handle), dc); tp_handle_set_add (self->priv->decloak_handles, handle); gabble_connection_request_decloak (self->priv->conn, tp_handle_inspect (contact_repo, handle), reason, NULL); return TRUE; } void gabble_presence_cache_update_location (GabblePresenceCache *cache, TpHandle handle, GHashTable *new_location) { GabblePresenceCachePrivate *priv = cache->priv; g_hash_table_insert (priv->location, GUINT_TO_POINTER (handle), new_location); g_signal_emit (cache, signals[LOCATION_UPDATED], 0, handle); } /* The return value should be g_hash_table_unref'ed. */ GHashTable * gabble_presence_cache_get_location (GabblePresenceCache *cache, TpHandle handle) { GabblePresenceCachePrivate *priv = cache->priv; GHashTable *location = NULL; location = g_hash_table_lookup (priv->location, GUINT_TO_POINTER (handle)); if (location != NULL) { g_hash_table_ref (location); return location; } return NULL; } gboolean gabble_presence_cache_disco_in_progress (GabblePresenceCache *cache, TpHandle handle, const gchar *resource) { GabblePresenceCachePrivate *priv = cache->priv; GList *l, *waiter_list; gboolean in_progress = FALSE; waiter_list = g_hash_table_get_values (priv->disco_pending); for (l = waiter_list; !in_progress && l != NULL; l = l->next) { GList *j; for (j = l->data; !in_progress && j != NULL; j = j->next) { DiscoWaiter *w = j->data; if (w != NULL && w->handle == handle && !tp_strdiff (w->resource, resource)) in_progress = TRUE; } } g_list_free (waiter_list); return in_progress; } TpHandle gabble_presence_cache_get_handle (GabblePresenceCache *cache, GabblePresence *presence) { GHashTableIter iter; gpointer key, val; g_hash_table_iter_init (&iter, cache->priv->presence); while (g_hash_table_iter_next (&iter, &key, &val)) { if (presence == val) return GPOINTER_TO_UINT (key); } return 0; } ����telepathy-gabble-0.18.2/src/presence-cache.h��������������������������������������������������������0000644�0001750�0001750�00000012121�12200204333�020604� 0����������������������������������������������������������������������������������������������������ustar�00smcv����������������������������smcv����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * gabble-presence-cache.h - Headers for Gabble's contact presence cache * Copyright (C) 2005 Collabora Ltd. * Copyright (C) 2005 Nokia Corporation * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 __GABBLE_PRESENCE_CACHE_H__ #define __GABBLE_PRESENCE_CACHE_H__ #include <glib-object.h> #include "presence.h" G_BEGIN_DECLS #define GABBLE_TYPE_PRESENCE_CACHE gabble_presence_cache_get_type () #define GABBLE_PRESENCE_CACHE(obj) \ (G_TYPE_CHECK_INSTANCE_CAST ((obj), \ GABBLE_TYPE_PRESENCE_CACHE, GabblePresenceCache)) #define GABBLE_PRESENCE_CACHE_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST ((klass), \ GABBLE_TYPE_PRESENCE_CACHE, GabblePresenceCacheClass)) #define GABBLE_IS_PRESENCE_CACHE(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \ GABBLE_TYPE_PRESENCE_CACHE)) #define GABBLE_IS_PRESENCE_CACHE_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE ((klass), \ GABBLE_TYPE_PRESENCE_CACHE)) #define GABBLE_PRESENCE_CACHE_GET_CLASS(obj) \ (G_TYPE_INSTANCE_GET_CLASS ((obj), \ GABBLE_TYPE_PRESENCE_CACHE, GabblePresenceCacheClass)) #define JABBER_PRESENCE_SHOW_AWAY "away" #define JABBER_PRESENCE_SHOW_CHAT "chat" #define JABBER_PRESENCE_SHOW_DND "dnd" #define JABBER_PRESENCE_SHOW_XA "xa" typedef struct _GabbleCapabilityInfo GabbleCapabilityInfo; struct _GabbleCapabilityInfo { /* struct _GabbleCapabilityInfo can be allocated before receiving the contact's * caps. In this case, cap_set is NULL. */ GabbleCapabilitySet *cap_set; /* array of GabbleDiscoIdentity or NULL */ GPtrArray *identities; /* array of WockyDataForm or NULL */ GPtrArray *data_forms; TpIntset *guys; guint trust; /* bitfield of GabbleClientType flags */ guint client_types; /* TRUE if this cache entry is one of our own, so between caps and * per_channel_manager_caps it holds the complete set of features for the * node. */ gboolean complete; }; typedef struct _GabblePresenceCachePrivate GabblePresenceCachePrivate; struct _GabblePresenceCache { GObject parent; GabblePresenceCachePrivate *priv; }; typedef struct _GabblePresenceCacheClass GabblePresenceCacheClass; struct _GabblePresenceCacheClass { GObjectClass parent_class; }; GType gabble_presence_cache_get_type (void); GabblePresenceCache *gabble_presence_cache_new (GabbleConnection *conn); GabblePresence *gabble_presence_cache_get (GabblePresenceCache *cache, TpHandle handle); GabblePresence *gabble_presence_cache_get_for_contact ( GabblePresenceCache *cache, WockyContact *contact); void gabble_presence_cache_update (GabblePresenceCache *cache, TpHandle handle, const gchar *resource, GabblePresenceId presence_id, const gchar *status_message, gint8 priority); void gabble_presence_cache_update_many (GabblePresenceCache *cache, const GArray *contact_handles, const gchar *resource, GabblePresenceId presence_id, const gchar *status_message, gint8 priority); void gabble_presence_cache_maybe_remove (GabblePresenceCache *cache, TpHandle handle); void gabble_presence_cache_add_own_caps (GabblePresenceCache *cache, const gchar *ver, const GabbleCapabilitySet *cap_set, const GPtrArray *identities, GPtrArray *data_forms); const GabbleCapabilityInfo *gabble_presence_cache_peek_own_caps ( GabblePresenceCache *cache, const gchar *ver); void gabble_presence_cache_really_remove (GabblePresenceCache *cache, TpHandle handle); gboolean gabble_presence_parse_presence_message ( GabblePresenceCache *cache, TpHandle handle, const gchar *from, WockyStanza *message); void gabble_presence_cache_contacts_added_to_olpc_view ( GabblePresenceCache *cache, TpHandleSet *handles); void gabble_presence_cache_contacts_removed_from_olpc_view ( GabblePresenceCache *cache, TpHandleSet *handles); gboolean gabble_presence_cache_is_unsure (GabblePresenceCache *cache, TpHandle handle); gboolean gabble_presence_cache_request_decloaking (GabblePresenceCache *self, TpHandle handle, const gchar *reason); void gabble_presence_cache_update_location (GabblePresenceCache *cache, TpHandle handle, GHashTable *location); GHashTable* gabble_presence_cache_get_location (GabblePresenceCache *cache, TpHandle handle); gboolean gabble_presence_cache_disco_in_progress (GabblePresenceCache *cache, TpHandle handle, const gchar *resource); TpHandle gabble_presence_cache_get_handle (GabblePresenceCache *cache, GabblePresence *presence); G_END_DECLS #endif /* __GABBLE_PRESENCE_CACHE_H__ */ �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������telepathy-gabble-0.18.2/src/presence.c��������������������������������������������������������������0000644�0001750�0001750�00000061147�12200204333�017552� 0����������������������������������������������������������������������������������������������������ustar�00smcv����������������������������smcv����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * gabble-presence.c - Gabble's per-contact presence structure * Copyright (C) 2005 Collabora Ltd. * Copyright (C) 2005 Nokia Corporation * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 "config.h" #include "presence.h" #include <string.h> #include <telepathy-glib/telepathy-glib.h> #include <wocky/wocky.h> #include "gabble/capabilities.h" #include "conn-presence.h" #include "presence-cache.h" #include "namespaces.h" #include "util.h" #include "gabble-enumtypes.h" #define DEBUG_FLAG GABBLE_DEBUG_PRESENCE #include "debug.h" static void xep_0115_capabilities_iface_init (gpointer, gpointer); G_DEFINE_TYPE_WITH_CODE (GabblePresence, gabble_presence, G_TYPE_OBJECT, G_IMPLEMENT_INTERFACE (WOCKY_TYPE_XEP_0115_CAPABILITIES, xep_0115_capabilities_iface_init); ) typedef struct _Resource Resource; struct _Resource { gchar *name; guint client_type; GabbleCapabilitySet *cap_set; GPtrArray *data_forms; guint caps_serial; GabblePresenceId status; gchar *status_message; gint8 priority; /* The last time we saw an available (or chatty! \o\ /o/) presence for * this resource. */ time_t last_available; }; struct _GabblePresencePrivate { /* The aggregated caps of all the contacts' resources. */ GabbleCapabilitySet *cap_set; /* The aggregated data forms of all the contacts' resources */ GPtrArray *data_forms; gchar *no_resource_status_message; GSList *resources; guint olpc_views; gchar *active_resource; }; static Resource * _resource_new (gchar *name) { Resource *new = g_slice_new0 (Resource); new->name = name; new->client_type = 0; new->cap_set = gabble_capability_set_new (); new->data_forms = g_ptr_array_new_with_free_func ( (GDestroyNotify) g_object_unref); new->status = GABBLE_PRESENCE_OFFLINE; new->status_message = NULL; new->priority = 0; new->caps_serial = 0; new->last_available = 0; return new; } static void _resource_free (Resource *resource) { g_free (resource->name); g_free (resource->status_message); gabble_capability_set_free (resource->cap_set); g_ptr_array_unref (resource->data_forms); g_slice_free (Resource, resource); } static void gabble_presence_finalize (GObject *object) { GSList *i; GabblePresence *presence = GABBLE_PRESENCE (object); GabblePresencePrivate *priv = presence->priv; for (i = priv->resources; NULL != i; i = i->next) _resource_free (i->data); g_slist_free (priv->resources); gabble_capability_set_free (priv->cap_set); g_ptr_array_unref (priv->data_forms); g_free (presence->nickname); g_free (presence->avatar_sha1); g_free (priv->no_resource_status_message); g_free (priv->active_resource); } static void gabble_presence_class_init (GabblePresenceClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); g_type_class_add_private (object_class, sizeof (GabblePresencePrivate)); object_class->finalize = gabble_presence_finalize; } static void gabble_presence_init (GabblePresence *self) { GabblePresencePrivate *priv; self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, GABBLE_TYPE_PRESENCE, GabblePresencePrivate); priv = self->priv; priv->cap_set = gabble_capability_set_new (); priv->data_forms = g_ptr_array_new_with_free_func ( (GDestroyNotify) g_object_unref); priv->resources = NULL; self->status = GABBLE_PRESENCE_UNKNOWN; } GabblePresence * gabble_presence_new (void) { return g_object_new (GABBLE_TYPE_PRESENCE, NULL); } static gboolean resource_better_than ( Resource *a, Resource *b, GabbleClientType preferred_client_type) { if (a->priority < 0) return FALSE; if (NULL == b) return TRUE; if (preferred_client_type != 0) { gboolean a_p = a->client_type & preferred_client_type; gboolean b_p = b->client_type & preferred_client_type; if (a_p && !b_p) return TRUE; if (!a_p && b_p) return FALSE; } if (a->status < b->status) return FALSE; else if (a->status > b->status) return TRUE; if (a->last_available < b->last_available) return FALSE; else if (a->last_available > b->last_available) return TRUE; return (a->priority > b->priority); } gboolean gabble_presence_has_cap (GabblePresence *presence, const gchar *ns) { g_return_val_if_fail (presence != NULL, FALSE); return gabble_capability_set_has (presence->priv->cap_set, ns); } GabbleCapabilitySet * gabble_presence_dup_caps (GabblePresence *presence) { g_return_val_if_fail (presence != NULL, NULL); return gabble_capability_set_copy (presence->priv->cap_set); } const GabbleCapabilitySet * gabble_presence_peek_caps (GabblePresence *presence) { g_return_val_if_fail (presence != NULL, NULL); return presence->priv->cap_set; } GPtrArray * gabble_presence_peek_data_forms (GabblePresence *presence) { g_return_val_if_fail (presence != NULL, NULL); return presence->priv->data_forms; } gboolean gabble_presence_has_resources (GabblePresence *self) { return (self->priv->resources != NULL); } /* * gabble_presence_pick_resource_by_caps: * @presence: a presence, which may not be NULL * @preferred_client_type: a single client type flag, such as * GABBLE_CLIENT_TYPE_PHONE, to specify that resources with that type should * be preferred to those without it; or 0 to express no preference * @predicate: a condition which must be satisfied by a resource's * capabilities, or NULL to disregard capabilities * @user_data: the second argument for @predicate (ignored if @predicate is * NULL) * */ const gchar * gabble_presence_pick_resource_by_caps ( GabblePresence *presence, GabbleClientType preferred_client_type, GabbleCapabilitySetPredicate predicate, gconstpointer user_data) { GabblePresencePrivate *priv = presence->priv; GSList *i; Resource *chosen = NULL; g_return_val_if_fail (presence != NULL, NULL); for (i = priv->resources; NULL != i; i = i->next) { Resource *res = (Resource *) i->data; if (predicate != NULL && !predicate (res->cap_set, user_data)) continue; if (resource_better_than (res, chosen, preferred_client_type)) chosen = res; } if (chosen) return chosen->name; else return NULL; } gboolean gabble_presence_resource_has_caps (GabblePresence *presence, const gchar *resource, GabbleCapabilitySetPredicate predicate, gconstpointer user_data) { GabblePresencePrivate *priv = presence->priv; GSList *i; for (i = priv->resources; NULL != i; i = i->next) { Resource *res = (Resource *) i->data; if (!tp_strdiff (res->name, resource)) return predicate (res->cap_set, user_data); } return FALSE; } static void extend_and_dup (GPtrArray *target, GPtrArray *source) { if (source == NULL) return; g_ptr_array_foreach (source, (GFunc) g_object_ref, NULL); tp_g_ptr_array_extend (target, source); } void gabble_presence_set_capabilities (GabblePresence *presence, const gchar *resource, const GabbleCapabilitySet *cap_set, const GPtrArray *data_forms, guint serial) { GabblePresencePrivate *priv = presence->priv; GSList *i; if (resource == NULL && priv->resources != NULL) { /* This is consistent with the handling of presence: if we get presence * from a bare JID, we throw away all the resources, and if we get * presence from a resource, any presence we stored from a bare JID is * overridden by the aggregated presence. */ DEBUG ("Ignoring caps for NULL resource since we have presence for " "some resources"); return; } gabble_capability_set_clear (priv->cap_set); g_ptr_array_set_size (priv->data_forms, 0); if (resource == NULL) { DEBUG ("Setting capabilities for bare JID"); gabble_capability_set_update (priv->cap_set, cap_set); extend_and_dup (priv->data_forms, (GPtrArray *) data_forms); return; } DEBUG ("about to add caps to resource %s with serial %u", resource, serial); for (i = priv->resources; NULL != i; i = i->next) { Resource *tmp = (Resource *) i->data; /* This does not use _find_resource() because it also refreshes * priv->cap_set as we go. */ if (0 == strcmp (tmp->name, resource)) { DEBUG ("found resource %s", resource); if (serial > tmp->caps_serial) { DEBUG ("new serial %u, old %u, clearing caps", serial, tmp->caps_serial); tmp->caps_serial = serial; gabble_capability_set_clear (tmp->cap_set); g_ptr_array_set_size (tmp->data_forms, 0); } if (serial >= tmp->caps_serial) { DEBUG ("updating caps for resource %s", resource); gabble_capability_set_update (tmp->cap_set, cap_set); /* TODO: deal with duplicates */ extend_and_dup (tmp->data_forms, (GPtrArray *) data_forms); } } gabble_capability_set_update (priv->cap_set, tmp->cap_set); /* TODO: deal with duplicates */ extend_and_dup (priv->data_forms, tmp->data_forms); } g_signal_emit_by_name (presence, "capabilities-changed"); } static Resource * _find_resource (GabblePresence *presence, const gchar *resource) { GSList *i; /* you've been warned! */ g_return_val_if_fail (presence != NULL, NULL); g_return_val_if_fail (resource != NULL, NULL); for (i = presence->priv->resources; NULL != i; i = i->next) { Resource *res = i->data; if (!tp_strdiff (res->name, resource)) return res; } return NULL; } static gboolean aggregate_resources (GabblePresence *presence) { GabblePresencePrivate *priv = presence->priv; GSList *i; Resource *best = NULL; guint old_client_types = presence->client_types; /* select the most preferable Resource and update presence->* based on our * choice */ gabble_capability_set_clear (priv->cap_set); presence->status = GABBLE_PRESENCE_OFFLINE; for (i = priv->resources; NULL != i; i = i->next) { Resource *r = (Resource *) i->data; gabble_capability_set_update (priv->cap_set, r->cap_set); /* This doesn't use resource_better_than() because phone preferences take * priority above all others whereas this is only using the PC thing as a * last-ditch tiebreak. wjt looked into changing this but gave up because * it's messy and the phone preference stuff will go away when we do * Jingle call forking anyway: * <https://bugs.freedesktop.org/show_bug.cgi?id=26673> */ /* trump existing status & message if it's more present * or has the same presence and was more recently available * or has the same presence and a higher priority */ if (best == NULL || r->status > best->status || (r->status == best->status && (r->last_available > best->last_available || r->priority > best->priority)) || (r->client_type & GABBLE_CLIENT_TYPE_PC && !(best->client_type & GABBLE_CLIENT_TYPE_PC))) best = r; } if (best != NULL) { presence->status = best->status; presence->status_message = best->status_message; presence->client_types = best->client_type; g_free (priv->active_resource); priv->active_resource = g_strdup (best->name); } if (presence->status <= GABBLE_PRESENCE_HIDDEN && priv->olpc_views > 0) { /* Contact is in at least one view and we didn't receive a better * presence from him so announce it as available */ presence->status = GABBLE_PRESENCE_AVAILABLE; g_free (presence->status_message); presence->status_message = NULL; } return old_client_types != presence->client_types; } gboolean gabble_presence_update (GabblePresence *presence, const gchar *resource, GabblePresenceId status, const gchar *status_message, gint8 priority, gboolean *update_client_types, time_t now) { GabblePresencePrivate *priv = presence->priv; Resource *res; GabblePresenceId old_status; gchar *old_status_message; GSList *i; gboolean ret = FALSE; /* save our current state */ old_status = presence->status; old_status_message = g_strdup (presence->status_message); if (NULL == resource) { /* presence from a JID with no resource: free all resources and set * presence directly */ for (i = priv->resources; i; i = i->next) _resource_free (i->data); g_slist_free (priv->resources); priv->resources = NULL; if (tp_strdiff (priv->no_resource_status_message, status_message)) { g_free (priv->no_resource_status_message); priv->no_resource_status_message = g_strdup (status_message); } presence->status = status; presence->status_message = priv->no_resource_status_message; goto OUT; } res = _find_resource (presence, resource); /* remove, create or update a Resource as appropriate */ if (status <= GABBLE_PRESENCE_LAST_UNAVAILABLE) { if (NULL != res) { priv->resources = g_slist_remove (priv->resources, res); _resource_free (res); res = NULL; /* recalculate aggregate capability mask */ gabble_capability_set_clear (priv->cap_set); for (i = priv->resources; i; i = i->next) { Resource *r = (Resource *) i->data; gabble_capability_set_update (priv->cap_set, r->cap_set); } } } else { if (NULL == res) { res = _resource_new (g_strdup (resource)); priv->resources = g_slist_append (priv->resources, res); } res->status = status; if (tp_strdiff (res->status_message, status_message)) { g_free (res->status_message); res->status_message = g_strdup (status_message); } res->priority = priority; if (res->status >= GABBLE_PRESENCE_AVAILABLE) res->last_available = now; } /* select the most preferable Resource and update presence->* based on our * choice */ gabble_capability_set_clear (priv->cap_set); presence->status = GABBLE_PRESENCE_OFFLINE; /* use the status message from any offline Resource we're * keeping around just because it has a message on it */ presence->status_message = res ? res->status_message : NULL; if (update_client_types != NULL) *update_client_types = aggregate_resources (presence); else aggregate_resources (presence); OUT: /* detect changes */ if (presence->status != old_status || tp_strdiff (presence->status_message, old_status_message)) ret = TRUE; g_free (old_status_message); return ret; } void gabble_presence_add_status_and_vcard (GabblePresence *presence, WockyStanza *stanza) { WockyNode *node = wocky_stanza_get_top_node (stanza); WockyNode *vcard_node; switch (presence->status) { case GABBLE_PRESENCE_AVAILABLE: case GABBLE_PRESENCE_OFFLINE: case GABBLE_PRESENCE_HIDDEN: break; case GABBLE_PRESENCE_AWAY: wocky_node_add_child_with_content (node, "show", JABBER_PRESENCE_SHOW_AWAY); break; case GABBLE_PRESENCE_CHAT: wocky_node_add_child_with_content (node, "show", JABBER_PRESENCE_SHOW_CHAT); break; case GABBLE_PRESENCE_DND: wocky_node_add_child_with_content (node, "show", JABBER_PRESENCE_SHOW_DND); break; case GABBLE_PRESENCE_XA: wocky_node_add_child_with_content (node, "show", JABBER_PRESENCE_SHOW_XA); break; default: { /* FIXME: we're almost duplicate the add_child code here, * and we're calling into conn-presence which is not nice. */ TpConnectionPresenceType presence_type = conn_presence_get_type (presence); switch (presence_type) { case TP_CONNECTION_PRESENCE_TYPE_AVAILABLE: case TP_CONNECTION_PRESENCE_TYPE_OFFLINE: case TP_CONNECTION_PRESENCE_TYPE_HIDDEN: break; case TP_CONNECTION_PRESENCE_TYPE_AWAY: wocky_node_add_child_with_content (node, "show", JABBER_PRESENCE_SHOW_AWAY); break; case TP_CONNECTION_PRESENCE_TYPE_BUSY: wocky_node_add_child_with_content (node, "show", JABBER_PRESENCE_SHOW_DND); break; case TP_CONNECTION_PRESENCE_TYPE_EXTENDED_AWAY: wocky_node_add_child_with_content (node, "show", JABBER_PRESENCE_SHOW_XA); break; default: g_critical ("%s: Unexpected Telepathy presence type: %d", G_STRFUNC, presence_type); break; } } } if (presence->status_message) wocky_node_add_child_with_content (node, "status", presence->status_message); vcard_node = wocky_node_add_child_ns (node, "x", NS_VCARD_TEMP_UPDATE); if (presence->avatar_sha1 != NULL) { wocky_node_add_child_with_content (vcard_node, "photo", presence->avatar_sha1); } } WockyStanza * gabble_presence_as_message (GabblePresence *presence, const gchar *to) { GabblePresencePrivate *priv = presence->priv; WockyStanza *message; WockyStanzaSubType subtype; Resource *res = priv->resources->data; /* pick first resource */ g_assert (NULL != res); if (presence->status == GABBLE_PRESENCE_OFFLINE) subtype = WOCKY_STANZA_SUB_TYPE_UNAVAILABLE; else subtype = WOCKY_STANZA_SUB_TYPE_AVAILABLE; message = wocky_stanza_build (WOCKY_STANZA_TYPE_PRESENCE, subtype, NULL, to, NULL); gabble_presence_add_status_and_vcard (presence, message); if (res->priority) { gchar *priority = g_strdup_printf ("%d", res->priority); WockyNode *node; node = wocky_stanza_get_top_node (message); wocky_node_add_child_with_content (node, "priority", priority); g_free (priority); } return message; } gchar * gabble_presence_dump (GabblePresence *presence) { GSList *i; GString *ret = g_string_new (""); gchar *tmp; GabblePresencePrivate *priv = presence->priv; const gchar *presence_name = wocky_enum_to_nick (GABBLE_TYPE_PRESENCE_ID, presence->status); if (presence_name == NULL) presence_name = "plugin-specific, not an element of GabblePresenceId"; g_string_append_printf (ret, "nickname: %s\n" "accumulated status: %d (%s)\n" "accumulated status msg: %s\n" "kept while unavailable: %d\n", presence->nickname, presence->status, presence_name, presence->status_message, presence->keep_unavailable); if (priv->cap_set != NULL) { tmp = gabble_capability_set_dump (priv->cap_set, " "); g_string_append (ret, "capabilities:\n"); g_string_append (ret, tmp); g_free (tmp); } g_string_append_printf (ret, "resources:\n"); for (i = priv->resources; i; i = i->next) { Resource *res = (Resource *) i->data; g_string_append_printf (ret, " %s\n" " status: %d\n" " status msg: %s\n" " priority: %d\n", res->name, res->status, res->status_message, res->priority); if (res->cap_set != NULL) { tmp = gabble_capability_set_dump (res->cap_set, " "); g_string_append (ret, " capabilities:\n"); g_string_append (ret, tmp); g_free (tmp); } } if (priv->resources == NULL) g_string_append_printf (ret, " (none)\n"); return g_string_free (ret, FALSE); } gboolean gabble_presence_added_to_view (GabblePresence *self) { GabblePresencePrivate *priv = self->priv; GabblePresenceId old_status; gchar *old_status_message; gboolean ret = FALSE; /* save our current state */ old_status = self->status; old_status_message = g_strdup (self->status_message); priv->olpc_views++; aggregate_resources (self); /* detect changes */ if (self->status != old_status || tp_strdiff (self->status_message, old_status_message)) ret = TRUE; g_free (old_status_message); return ret; } gboolean gabble_presence_removed_from_view (GabblePresence *self) { GabblePresencePrivate *priv = self->priv; GabblePresenceId old_status; gchar *old_status_message; gboolean ret = FALSE; /* save our current state */ old_status = self->status; old_status_message = g_strdup (self->status_message); priv->olpc_views--; aggregate_resources (self); /* detect changes */ if (self->status != old_status || tp_strdiff (self->status_message, old_status_message)) ret = TRUE; g_free (old_status_message); return ret; } gconstpointer gabble_presence_resource_pick_best_feature (GabblePresence *presence, const gchar *resource, const GabbleFeatureFallback *table, GabbleCapabilitySetPredicate predicate) { Resource *res; const GabbleFeatureFallback *row; g_return_val_if_fail (presence != NULL, NULL); g_return_val_if_fail (resource != NULL, NULL); g_return_val_if_fail (predicate != NULL, NULL); g_return_val_if_fail (table != NULL, NULL); res = _find_resource (presence, resource); if (res == NULL) return NULL; for (row = table; row->result != NULL; row++) { if (row->considered && predicate (res->cap_set, row->check_data)) { return row->result; } } return NULL; } gconstpointer gabble_presence_pick_best_feature (GabblePresence *presence, const GabbleFeatureFallback *table, GabbleCapabilitySetPredicate predicate) { const GabbleFeatureFallback *row; g_return_val_if_fail (presence != NULL, NULL); g_return_val_if_fail (predicate != NULL, NULL); g_return_val_if_fail (table != NULL, NULL); for (row = table; row->result != NULL; row++) { if (row->considered && predicate (presence->priv->cap_set, row->check_data)) { return row->result; } } return NULL; } /* FIXME: this function should be combined with * gabble_presence_set_capabilities(). */ gboolean gabble_presence_update_client_types (GabblePresence *presence, const gchar *resource, guint client_types) { Resource *res; if (resource == NULL && presence->priv->resources != NULL) { DEBUG ("Ignoring client types for NULL resource since we have " "presence for some resources"); return FALSE; } if (resource == NULL) { guint old_client_types = presence->client_types; presence->client_types = client_types; return (old_client_types != client_types); } res = _find_resource (presence, resource); if (res == NULL) return FALSE; res->client_type = client_types; return aggregate_resources (presence); } gchar ** gabble_presence_get_client_types_array (GabblePresence *presence, const char **resource_name) { GPtrArray *array = g_ptr_array_new (); GFlagsClass *klass = g_type_class_ref (GABBLE_TYPE_CLIENT_TYPE); if (klass != NULL) { guint i; for (i = 0; i < klass->n_values; i++) { GFlagsValue *value = &klass->values[i]; if (presence->client_types & value->value) g_ptr_array_add (array, g_strdup (value->value_nick)); } g_type_class_unref (klass); } g_ptr_array_add (array, NULL); if (resource_name != NULL) *resource_name = presence->priv->active_resource; return (gchar **) g_ptr_array_free (array, FALSE); } static const GPtrArray * gabble_presence_get_data_forms (WockyXep0115Capabilities *caps) { GabblePresence *presence = GABBLE_PRESENCE (caps); return presence->priv->data_forms; } static void xep_0115_capabilities_iface_init (gpointer g_iface, gpointer iface_data) { WockyXep0115CapabilitiesInterface *iface = g_iface; iface->get_data_forms = gabble_presence_get_data_forms; } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������telepathy-gabble-0.18.2/src/presence.h��������������������������������������������������������������0000644�0001750�0001750�00000011701�12200204333�017546� 0����������������������������������������������������������������������������������������������������ustar�00smcv����������������������������smcv����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * gabble-presence.h - Headers for Gabble's per-contact presence structure * Copyright (C) 2005 Collabora Ltd. * Copyright (C) 2005 Nokia Corporation * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 __GABBLE_PRESENCE_H__ #define __GABBLE_PRESENCE_H__ #include <glib-object.h> #include "gabble/capabilities.h" #include "connection.h" #include "types.h" G_BEGIN_DECLS #define GABBLE_TYPE_PRESENCE gabble_presence_get_type () #define GABBLE_PRESENCE(obj) \ (G_TYPE_CHECK_INSTANCE_CAST ((obj), \ GABBLE_TYPE_PRESENCE, GabblePresence)) #define GABBLE_PRESENCE_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST ((klass), \ GABBLE_TYPE_PRESENCE, GabblePresenceClass)) #define GABBLE_IS_PRESENCE(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \ GABBLE_TYPE_PRESENCE)) #define GABBLE_IS_PRESENCE_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE ((klass), \ GABBLE_TYPE_PRESENCE)) #define GABBLE_PRESENCE_GET_CLASS(obj) \ (G_TYPE_INSTANCE_GET_CLASS ((obj), \ GABBLE_TYPE_PRESENCE, GabblePresenceClass)) typedef struct _GabblePresencePrivate GabblePresencePrivate; struct _GabblePresence { GObject parent; GabblePresenceId status; gchar *status_message; gchar *nickname; gchar *avatar_sha1; guint client_types; gboolean keep_unavailable; GabblePresencePrivate *priv; }; typedef enum { GABBLE_CLIENT_TYPE_BOT = 1 << 0, GABBLE_CLIENT_TYPE_CONSOLE = 1 << 1, GABBLE_CLIENT_TYPE_GAME = 1 << 2, GABBLE_CLIENT_TYPE_HANDHELD = 1 << 3, GABBLE_CLIENT_TYPE_PC = 1 << 4, GABBLE_CLIENT_TYPE_PHONE = 1 << 5, GABBLE_CLIENT_TYPE_WEB = 1 << 6, GABBLE_CLIENT_TYPE_SMS = 1 << 7, } GabbleClientType; typedef struct _GabblePresenceClass GabblePresenceClass; struct _GabblePresenceClass { GObjectClass parent_class; }; GType gabble_presence_get_type (void); GabblePresence* gabble_presence_new (void); gboolean gabble_presence_update (GabblePresence *presence, const gchar *resource, GabblePresenceId status, const gchar *status_message, gint8 priority, gboolean *update_client_types, time_t now); void gabble_presence_set_capabilities (GabblePresence *presence, const gchar *resource, const GabbleCapabilitySet *cap_set, const GPtrArray *data_forms, guint serial); gboolean gabble_presence_has_cap (GabblePresence *presence, const gchar *ns); GabbleCapabilitySet *gabble_presence_dup_caps (GabblePresence *presence); const GabbleCapabilitySet *gabble_presence_peek_caps (GabblePresence *presence); GPtrArray *gabble_presence_peek_data_forms (GabblePresence *presence); gboolean gabble_presence_has_resources (GabblePresence *self); const gchar *gabble_presence_pick_resource_by_caps (GabblePresence *presence, GabbleClientType preferred_client_type, GabbleCapabilitySetPredicate predicate, gconstpointer user_data); gboolean gabble_presence_resource_has_caps (GabblePresence *presence, const gchar *resource, GabbleCapabilitySetPredicate predicate, gconstpointer user_data); WockyStanza *gabble_presence_as_message (GabblePresence *presence, const gchar *to); void gabble_presence_add_status_and_vcard (GabblePresence *presence, WockyStanza *stanza); gchar *gabble_presence_dump (GabblePresence *presence); gboolean gabble_presence_added_to_view (GabblePresence *presence); gboolean gabble_presence_removed_from_view (GabblePresence *presence); /* Data-driven feature fallback */ typedef struct { gboolean considered; gconstpointer check_data; gconstpointer result; } GabbleFeatureFallback; gconstpointer gabble_presence_resource_pick_best_feature ( GabblePresence *presence, const gchar *resource, const GabbleFeatureFallback *table, GabbleCapabilitySetPredicate predicate); gconstpointer gabble_presence_pick_best_feature (GabblePresence *presence, const GabbleFeatureFallback *table, GabbleCapabilitySetPredicate predicate); gboolean gabble_presence_update_client_types (GabblePresence *presence, const gchar *resource, guint client_types); gchar **gabble_presence_get_client_types_array (GabblePresence *presence, const gchar **resource_name); G_END_DECLS #endif /* __GABBLE_PRESENCE_H__ */ ���������������������������������������������������������������telepathy-gabble-0.18.2/src/plugin-loader.c���������������������������������������������������������0000644�0001750�0001750�00000024230�12312320036�020503� 0����������������������������������������������������������������������������������������������������ustar�00smcv����������������������������smcv����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * plugin-loader.c — plugin support for telepathy-gabble * Copyright © 2009 Collabora Ltd. * Copyright © 2009 Nokia Corporation * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 "config.h" #include "plugin-loader.h" #include <glib.h> #ifdef ENABLE_PLUGINS # include <gmodule.h> #endif #include <telepathy-glib/telepathy-glib.h> #define DEBUG_FLAG GABBLE_DEBUG_PLUGINS #include "debug.h" #include "gabble/plugin.h" G_DEFINE_TYPE(GabblePluginLoader, gabble_plugin_loader, G_TYPE_OBJECT) struct _GabblePluginLoaderPrivate { GPtrArray *plugins; }; #ifdef ENABLE_PLUGINS static void plugin_loader_try_to_load ( GabblePluginLoader *self, const gchar *path) { GModule *m = g_module_open (path, G_MODULE_BIND_LOCAL); gpointer func; GabblePluginCreateImpl create; GabblePlugin *plugin; if (m == NULL) { const gchar *e = g_module_error (); /* the errors often seem to be prefixed by the filename */ if (g_str_has_prefix (e, path)) DEBUG ("%s", e); else DEBUG ("%s: %s", path, e); return; } if (!g_module_symbol (m, "gabble_plugin_create", &func)) { DEBUG ("%s", g_module_error ()); g_module_close (m); return; } /* We're about to try to instantiate an object. This installs the * class with the type system, so we should ensure that this * plug-in is never accidentally unloaded. */ g_module_make_resident (m); /* Here goes nothing... */ create = func; plugin = create (); if (plugin == NULL) { g_warning ("gabble_plugin_create () failed for %s", path); } else { gchar *sidecars = g_strjoinv (", ", (gchar **) gabble_plugin_get_sidecar_interfaces (plugin)); const gchar *version = gabble_plugin_get_version (plugin); if (version == NULL) version = "(unspecified)"; DEBUG ("loaded '%s' version %s (%s), implementing these sidecars: %s", gabble_plugin_get_name (plugin), version, path, sidecars); g_free (sidecars); g_ptr_array_add (self->priv->plugins, plugin); } } static void gabble_plugin_loader_probe (GabblePluginLoader *self) { const gchar *directory_names = g_getenv ("GABBLE_PLUGIN_DIR"); gchar **dir_array; gchar **ptr; GDir *d; const gchar *file; if (!g_module_supported ()) { DEBUG ("modules aren't supported on this platform."); return; } if (directory_names == NULL) directory_names = PLUGIN_DIR; #ifdef G_OS_WIN32 dir_array = g_strsplit (directory_names, ";", 0); #else dir_array = g_strsplit (directory_names, ":", 0); #endif for (ptr = dir_array ; *ptr != NULL ; ptr++) { GError *error = NULL; DEBUG ("probing %s", *ptr); d = g_dir_open (*ptr, 0, &error); if (d == NULL) { DEBUG ("%s", error->message); g_clear_error (&error); continue; } while ((file = g_dir_read_name (d)) != NULL) { gchar *path; if (!g_str_has_suffix (file, G_MODULE_SUFFIX)) continue; path = g_build_filename (*ptr, file, NULL); plugin_loader_try_to_load (self, path); g_free (path); } g_dir_close (d); } g_strfreev (dir_array); } #endif static void gabble_plugin_loader_init (GabblePluginLoader *self) { GabblePluginLoaderPrivate *priv = G_TYPE_INSTANCE_GET_PRIVATE (self, GABBLE_TYPE_PLUGIN_LOADER, GabblePluginLoaderPrivate); self->priv = priv; priv->plugins = g_ptr_array_new_with_free_func (g_object_unref); } static GObject * gabble_plugin_loader_constructor ( GType type, guint n_props, GObjectConstructParam *props) { static gpointer singleton = NULL; if (singleton == NULL) { singleton = G_OBJECT_CLASS (gabble_plugin_loader_parent_class)-> constructor (type, n_props, props); g_object_add_weak_pointer (G_OBJECT (singleton), &singleton); return singleton; } else { return g_object_ref (singleton); } } static void gabble_plugin_loader_constructed (GObject *object) { GabblePluginLoader *self = GABBLE_PLUGIN_LOADER (object); void (*chain_up) (GObject *) = G_OBJECT_CLASS (gabble_plugin_loader_parent_class)->constructed; if (chain_up != NULL) chain_up (object); #ifdef ENABLE_PLUGINS gabble_plugin_loader_probe (self); #else DEBUG ("built without plugin support, not actually loading anything"); (void) self; /* silence unused variable warning. */ #endif } static void gabble_plugin_loader_finalize (GObject *object) { GabblePluginLoader *self = GABBLE_PLUGIN_LOADER (object); void (*chain_up) (GObject *) = G_OBJECT_CLASS (gabble_plugin_loader_parent_class)->finalize; tp_clear_pointer (&self->priv->plugins, g_ptr_array_unref); if (chain_up != NULL) chain_up (object); } static void gabble_plugin_loader_class_init (GabblePluginLoaderClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); g_type_class_add_private (klass, sizeof (GabblePluginLoaderPrivate)); object_class->constructor = gabble_plugin_loader_constructor; object_class->constructed = gabble_plugin_loader_constructed; object_class->finalize = gabble_plugin_loader_finalize; } GabblePluginLoader * gabble_plugin_loader_dup () { return g_object_new (GABBLE_TYPE_PLUGIN_LOADER, NULL); } static void create_sidecar_cb ( GObject *plugin_obj, GAsyncResult *nested_result, gpointer user_data) { GSimpleAsyncResult *result = user_data; GabbleSidecar *sidecar; GError *error = NULL; sidecar = gabble_plugin_create_sidecar_finish (GABBLE_PLUGIN (plugin_obj), nested_result, &error); if (sidecar == NULL) { g_simple_async_result_set_from_error (result, error); g_clear_error (&error); } else { g_simple_async_result_set_op_res_gpointer (result, sidecar, g_object_unref); } g_simple_async_result_complete (result); g_object_unref (result); } void gabble_plugin_loader_create_sidecar ( GabblePluginLoader *self, const gchar *sidecar_interface, GabbleConnection *connection, WockySession *session, GAsyncReadyCallback callback, gpointer user_data) { GabblePluginLoaderPrivate *priv = self->priv; guint i; for (i = 0; i < priv->plugins->len; i++) { GabblePlugin *p = g_ptr_array_index (priv->plugins, i); if (gabble_plugin_implements_sidecar (p, sidecar_interface)) { GSimpleAsyncResult *res = g_simple_async_result_new (G_OBJECT (self), callback, user_data, gabble_plugin_loader_create_sidecar); GabblePluginConnection *gabble_conn = GABBLE_PLUGIN_CONNECTION (connection); gabble_plugin_create_sidecar_async (p, sidecar_interface, gabble_conn, session, create_sidecar_cb, res); return; } } g_simple_async_report_error_in_idle (G_OBJECT (self), callback, user_data, TP_ERROR, TP_ERROR_NOT_IMPLEMENTED, "No plugin implements sidecar '%s'", sidecar_interface); } GabbleSidecar * gabble_plugin_loader_create_sidecar_finish ( GabblePluginLoader *self, GAsyncResult *result, GError **error) { GabbleSidecar *sidecar; if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), error)) return NULL; g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (self), gabble_plugin_loader_create_sidecar), NULL); sidecar = GABBLE_SIDECAR (g_simple_async_result_get_op_res_gpointer ( G_SIMPLE_ASYNC_RESULT (result))); return g_object_ref (sidecar); } TpPresenceStatusSpec * gabble_plugin_loader_append_statuses ( GabblePluginLoader *self, const TpPresenceStatusSpec *base_statuses) { GabblePluginLoaderPrivate *priv = self->priv; GArray *result = g_array_new (TRUE, TRUE, sizeof (TpPresenceStatusSpec)); guint i; for (i = 0; base_statuses[i].name != NULL; i++) g_array_append_val (result, base_statuses[i]); for (i = 0; i < priv->plugins->len; i++) { GabblePlugin *p = g_ptr_array_index (priv->plugins, i); const TpPresenceStatusSpec *statuses = gabble_plugin_get_custom_presence_statuses (p); if (statuses != NULL) { guint j; for (j = 0; statuses[j].name != NULL; j++) g_array_append_val (result, statuses[j]); } } return (TpPresenceStatusSpec *) g_array_free (result, FALSE); } const gchar * gabble_plugin_loader_presence_status_for_privacy_list ( GabblePluginLoader *loader, const gchar *list_name) { GabblePluginLoaderPrivate *priv = loader->priv; guint i; for (i = 0; i < priv->plugins->len; i++) { GabblePlugin *p = g_ptr_array_index (priv->plugins, i); const gchar *status = gabble_plugin_presence_status_for_privacy_list (p, list_name); if (status != NULL) return status; } return NULL; } static void copy_to_other_array (gpointer data, gpointer user_data) { g_ptr_array_add (user_data, data); } GPtrArray * gabble_plugin_loader_create_channel_managers ( GabblePluginLoader *self, GabblePluginConnection *plugin_connection) { GPtrArray *out = g_ptr_array_new (); guint i; for (i = 0; i < self->priv->plugins->len; i++) { GabblePlugin *plugin = g_ptr_array_index (self->priv->plugins, i); GPtrArray *managers; managers = gabble_plugin_create_channel_managers (plugin, plugin_connection); if (managers == NULL) continue; g_ptr_array_foreach (managers, copy_to_other_array, out); g_ptr_array_unref (managers); } return out; } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������telepathy-gabble-0.18.2/src/plugin-loader.h���������������������������������������������������������0000644�0001750�0001750�00000006152�12200204333�020510� 0����������������������������������������������������������������������������������������������������ustar�00smcv����������������������������smcv����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * plugin-loader.h — plugin support for telepathy-gabble * Copyright © 2009 Collabora Ltd. * Copyright © 2009 Nokia Corporation * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 __PLUGIN_LOADER_H__ #define __PLUGIN_LOADER_H__ #include <glib-object.h> #include <gio/gio.h> #include <telepathy-glib/telepathy-glib.h> #include <wocky/wocky.h> #include "gabble/sidecar.h" #include "gabble/plugin-connection.h" typedef struct _GabblePluginLoader GabblePluginLoader; typedef struct _GabblePluginLoaderClass GabblePluginLoaderClass; typedef struct _GabblePluginLoaderPrivate GabblePluginLoaderPrivate; struct _GabblePluginLoaderClass { GObjectClass parent_class; }; struct _GabblePluginLoader { GObject parent; GabblePluginLoaderPrivate *priv; }; GType gabble_plugin_loader_get_type (void); /* TYPE MACROS */ #define GABBLE_TYPE_PLUGIN_LOADER \ (gabble_plugin_loader_get_type ()) #define GABBLE_PLUGIN_LOADER(obj) \ (G_TYPE_CHECK_INSTANCE_CAST((obj), GABBLE_TYPE_PLUGIN_LOADER, \ GabblePluginLoader)) #define GABBLE_PLUGIN_LOADER_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST((klass), GABBLE_TYPE_PLUGIN_LOADER, \ GabblePluginLoaderClass)) #define GABBLE_IS_PLUGIN_LOADER(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE((obj), GABBLE_TYPE_PLUGIN_LOADER)) #define GABBLE_IS_PLUGIN_LOADER_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE((klass), GABBLE_TYPE_PLUGIN_LOADER)) #define GABBLE_PLUGIN_LOADER_GET_CLASS(obj) \ (G_TYPE_INSTANCE_GET_CLASS ((obj), GABBLE_TYPE_PLUGIN_LOADER, \ GabblePluginLoaderClass)) GabblePluginLoader *gabble_plugin_loader_dup (void); void gabble_plugin_loader_create_sidecar ( GabblePluginLoader *self, const gchar *sidecar_interface, GabbleConnection *connection, WockySession *session, GAsyncReadyCallback callback, gpointer user_data); GabbleSidecar *gabble_plugin_loader_create_sidecar_finish ( GabblePluginLoader *self, GAsyncResult *result, GError **error); TpPresenceStatusSpec *gabble_plugin_loader_append_statuses ( GabblePluginLoader *self, const TpPresenceStatusSpec *base_statuses); const gchar *gabble_plugin_loader_presence_status_for_privacy_list ( GabblePluginLoader *loader, const gchar *list_name); GPtrArray * gabble_plugin_loader_create_channel_managers ( GabblePluginLoader *self, GabblePluginConnection *plugin_connection); #endif /* #ifndef __PLUGIN_LOADER_H__ */ ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������telepathy-gabble-0.18.2/src/olpc-activity.c���������������������������������������������������������0000644�0001750�0001750�00000015717�12200204333�020537� 0����������������������������������������������������������������������������������������������������ustar�00smcv����������������������������smcv����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * olpc-activity.c - Source for GabbleOlpcActivity * Copyright (C) 2008 Collabora Ltd. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 "config.h" #include "olpc-activity.h" #include <stdlib.h> #include <string.h> #include <glib.h> #define DEBUG_FLAG GABBLE_DEBUG_OLPC #include "debug.h" #include "extensions/extensions.h" #include "gabble-signals-marshal.h" #include "namespaces.h" #include "util.h" /* properties */ enum { PROP_CONNECTION = 1, PROP_ROOM, PROP_ID, PROP_PROPERTIES, LAST_PROPERTY }; struct _GabbleOlpcActivityPrivate { GabbleConnection *conn; gboolean dispose_has_run; }; G_DEFINE_TYPE (GabbleOlpcActivity, gabble_olpc_activity, G_TYPE_OBJECT); static void gabble_olpc_activity_init (GabbleOlpcActivity *self) { GabbleOlpcActivityPrivate *priv = G_TYPE_INSTANCE_GET_PRIVATE (self, GABBLE_TYPE_OLPC_ACTIVITY, GabbleOlpcActivityPrivate); self->priv = priv; priv->dispose_has_run = FALSE; } static void gabble_olpc_activity_finalize (GObject *object) { GabbleOlpcActivity *self = GABBLE_OLPC_ACTIVITY (object); if (self->id != NULL) { g_free (self->id); self->id = NULL; } if (self->properties != NULL) { g_hash_table_unref (self->properties); self->properties = NULL; } G_OBJECT_CLASS (gabble_olpc_activity_parent_class)->finalize (object); } static void gabble_olpc_activity_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { GabbleOlpcActivity *self = GABBLE_OLPC_ACTIVITY (object); GabbleOlpcActivityPrivate *priv = self->priv; switch (property_id) { case PROP_CONNECTION: g_value_set_object (value, priv->conn); break; case PROP_ROOM: g_value_set_uint (value, self->room); break; case PROP_ID: g_value_set_string (value, self->id); break; case PROP_PROPERTIES: g_value_set_boxed (value, self->properties); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void gabble_olpc_activity_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { GabbleOlpcActivity *self = GABBLE_OLPC_ACTIVITY (object); GabbleOlpcActivityPrivate *priv = self->priv; switch (property_id) { case PROP_CONNECTION: priv->conn = g_value_get_object (value); break; case PROP_ROOM: self->room = g_value_get_uint (value); break; case PROP_ID: g_free (self->id); self->id = g_value_dup_string (value); break; case PROP_PROPERTIES: if (self->properties != NULL) g_hash_table_unref (self->properties); self->properties = g_value_get_boxed (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static GObject * gabble_olpc_activity_constructor (GType type, guint n_props, GObjectConstructParam *props) { GObject *obj; GabbleOlpcActivity *self; obj = G_OBJECT_CLASS (gabble_olpc_activity_parent_class)-> constructor (type, n_props, props); self = GABBLE_OLPC_ACTIVITY (obj); g_assert (self->room != 0); DEBUG ("new activity %s (%d)", gabble_olpc_activity_get_room (self), self->room); return obj; } static void gabble_olpc_activity_class_init ( GabbleOlpcActivityClass *gabble_olpc_activity_class) { GObjectClass *object_class = G_OBJECT_CLASS (gabble_olpc_activity_class); GParamSpec *param_spec; object_class->get_property = gabble_olpc_activity_get_property; object_class->set_property = gabble_olpc_activity_set_property; object_class->constructor = gabble_olpc_activity_constructor; g_type_class_add_private (gabble_olpc_activity_class, sizeof (GabbleOlpcActivityPrivate)); object_class->finalize = gabble_olpc_activity_finalize; param_spec = g_param_spec_object ( "connection", "GabbleConnection object", "Gabble connection object that owns this activity object.", GABBLE_TYPE_CONNECTION, G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, PROP_CONNECTION, param_spec); param_spec = g_param_spec_uint ( "room", "activity room", "a TpHandle representing the activity room", 0, G_MAXUINT, 0, G_PARAM_CONSTRUCT | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, PROP_ROOM, param_spec); param_spec = g_param_spec_string ( "id", "activity id", "the activity ID", NULL, G_PARAM_CONSTRUCT | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, PROP_ID, param_spec); param_spec = g_param_spec_boxed ( "properties", "activity properties", "a GHashTable containing activity's properties", G_TYPE_HASH_TABLE, G_PARAM_CONSTRUCT | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, PROP_PROPERTIES, param_spec); } GabbleOlpcActivity * gabble_olpc_activity_new (GabbleConnection *conn, TpHandle room) { return g_object_new (GABBLE_TYPE_OLPC_ACTIVITY, "connection", conn, "room", room, NULL); } const gchar * gabble_olpc_activity_get_room (GabbleOlpcActivity *self) { TpHandleRepoIface *room_repo = tp_base_connection_get_handles ( (TpBaseConnection *) self->priv->conn, TP_HANDLE_TYPE_ROOM); return tp_handle_inspect (room_repo, self->room); } gboolean gabble_olpc_activity_is_visible (GabbleOlpcActivity *self) { GValue *gv; /* false if incomplete */ if (self->id == NULL || self->properties == NULL) return FALSE; gv = g_hash_table_lookup (self->properties, "private"); if (gv == NULL) { return FALSE; } /* if they put something non-boolean in it, err on the side of privacy */ if (!G_VALUE_HOLDS_BOOLEAN (gv)) return FALSE; /* if they specified a privacy level, go with it */ return !g_value_get_boolean (gv); } �������������������������������������������������telepathy-gabble-0.18.2/src/olpc-activity.h���������������������������������������������������������0000644�0001750�0001750�00000004772�12200204333�020543� 0����������������������������������������������������������������������������������������������������ustar�00smcv����������������������������smcv����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * olpc-activity.h - Header for GabbleOlpcActivity * Copyright (C) 2008 Collabora Ltd. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 __GABBLE_OLPC_ACTIVITY_H__ #define __GABBLE_OLPC_ACTIVITY_H__ #include <glib-object.h> #include <telepathy-glib/telepathy-glib.h> #include "connection.h" G_BEGIN_DECLS typedef struct _GabbleOlpcActivity GabbleOlpcActivity; typedef struct _GabbleOlpcActivityClass GabbleOlpcActivityClass; typedef struct _GabbleOlpcActivityPrivate GabbleOlpcActivityPrivate; struct _GabbleOlpcActivityClass { GObjectClass parent_class; }; struct _GabbleOlpcActivity { GObject parent; TpHandle room; gchar *id; GHashTable *properties; GabbleOlpcActivityPrivate *priv; }; GType gabble_olpc_activity_get_type (void); /* TYPE MACROS */ #define GABBLE_TYPE_OLPC_ACTIVITY \ (gabble_olpc_activity_get_type ()) #define GABBLE_OLPC_ACTIVITY(obj) \ (G_TYPE_CHECK_INSTANCE_CAST((obj), GABBLE_TYPE_OLPC_ACTIVITY,\ GabbleOlpcActivity)) #define GABBLE_OLPC_ACTIVITY_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST((klass), GABBLE_TYPE_OLPC_ACTIVITY,\ GabbleOlpcActivityClass)) #define GABBLE_IS_OLPC_ACTIVITY(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE((obj), GABBLE_TYPE_OLPC_ACTIVITY)) #define GABBLE_IS_OLPC_ACTIVITY_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE((klass), GABBLE_TYPE_OLPC_ACTIVITY)) #define GABBLE_OLPC_ACTIVITY_GET_CLASS(obj) \ (G_TYPE_INSTANCE_GET_CLASS ((obj), GABBLE_TYPE_OLPC_ACTIVITY,\ GabbleOlpcActivityClass)) GabbleOlpcActivity * gabble_olpc_activity_new (GabbleConnection *conn, TpHandle room); const gchar * gabble_olpc_activity_get_room (GabbleOlpcActivity *activity); gboolean gabble_olpc_activity_is_visible (GabbleOlpcActivity *activity); G_END_DECLS #endif /* #ifndef __GABBLE_OLPC_ACTIVITY_H__ */ ������telepathy-gabble-0.18.2/src/namespaces.h������������������������������������������������������������0000644�0001750�0001750�00000016133�12200204333�020065� 0����������������������������������������������������������������������������������������������������ustar�00smcv����������������������������smcv����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * namespaces.h - XMPP namespace constants * Copyright (C) 2005 Collabora Ltd. * Copyright (C) 2005 Nokia Corporation * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 __GABBLE_NAMESPACES__H__ #define __GABBLE_NAMESPACES__H__ #include "gabble/namespaces.h" #define NS_AMP "http://jabber.org/protocol/amp" #define NS_BYTESTREAMS "http://jabber.org/protocol/bytestreams" #define NS_CHAT_STATES "http://jabber.org/protocol/chatstates" #define NS_DISCO_INFO "http://jabber.org/protocol/disco#info" #define NS_DISCO_ITEMS "http://jabber.org/protocol/disco#items" #define NS_FEATURENEG "http://jabber.org/protocol/feature-neg" #define NS_FILE_TRANSFER "http://jabber.org/protocol/si/profile/file-transfer" #define NS_GOOGLE_CAPS "http://www.google.com/xmpp/client/caps" #define NS_GOOGLE_FEAT_SESSION "http://www.google.com/xmpp/protocol/session" #define NS_GOOGLE_FEAT_SHARE "http://google.com/xmpp/protocol/share/v1" #define NS_GOOGLE_FEAT_VOICE "http://www.google.com/xmpp/protocol/voice/v1" #define NS_GOOGLE_FEAT_VIDEO "http://www.google.com/xmpp/protocol/video/v1" #define NS_GOOGLE_FEAT_CAMERA "http://www.google.com/xmpp/protocol/camera/v1" #define NS_GOOGLE_JINGLE_INFO "google:jingleinfo" #define NS_GOOGLE_ROSTER "google:roster" #define NS_GOOGLE_QUEUE "google:queue" #define NS_IBB "http://jabber.org/protocol/ibb" /* Namespaces for XEP-0166 draft v0.15, the most capable Jingle dialect * supported by telepathy-gabble < 0.7.16, including the versions shipped with * Maemo Chinook and Diablo. */ #define NS_JINGLE015 "http://jabber.org/protocol/jingle" /* RTP audio capability in Jingle v0.15 (obsoleted by NS_JINGLE_RTP) */ #define NS_JINGLE_DESCRIPTION_AUDIO \ "http://jabber.org/protocol/jingle/description/audio" /* RTP video capability in Jingle v0.15 (obsoleted by NS_JINGLE_RTP) */ #define NS_JINGLE_DESCRIPTION_VIDEO \ "http://jabber.org/protocol/jingle/description/video" /* XEP-0166 draft */ #define NS_JINGLE032 "urn:xmpp:jingle:1" #define NS_JINGLE_ERRORS "urn:xmpp:jingle:errors:1" /* XEP-0167 (Jingle RTP) */ #define NS_JINGLE_RTP "urn:xmpp:jingle:apps:rtp:1" #define NS_JINGLE_RTP_ERRORS "urn:xmpp:jingle:apps:rtp:errors:1" #define NS_JINGLE_RTP_INFO "urn:xmpp:jingle:apps:rtp:info:1" #define NS_JINGLE_RTP_AUDIO "urn:xmpp:jingle:apps:rtp:audio" #define NS_JINGLE_RTP_VIDEO "urn:xmpp:jingle:apps:rtp:video" /* ProtoXEPs for rtcp-fb and rtp-hdrext */ #define NS_JINGLE_RTCP_FB "urn:xmpp:jingle:apps:rtp:rtcp-fb:0" #define NS_JINGLE_RTP_HDREXT "urn:xmpp:jingle:apps:rtp:rtp-hdrext:0" /* Google's Jingle dialect */ #define NS_GOOGLE_SESSION "http://www.google.com/session" /* Audio capability in Google Jingle dialect */ #define NS_GOOGLE_SESSION_PHONE "http://www.google.com/session/phone" /* Video capability in Google's Jingle dialect */ #define NS_GOOGLE_SESSION_VIDEO "http://www.google.com/session/video" /* File transfer capability in Google's Jingle dialect */ #define NS_GOOGLE_SESSION_SHARE "http://www.google.com/session/share" /* google-p2p transport */ #define NS_GOOGLE_TRANSPORT_P2P "http://www.google.com/transport/p2p" /* Jingle RAW-UDP transport */ #define NS_JINGLE_TRANSPORT_RAWUDP "urn:xmpp:jingle:transports:raw-udp:1" /* Jingle ICE-UDP transport */ #define NS_JINGLE_TRANSPORT_ICEUDP "urn:xmpp:jingle:transports:ice-udp:1" #define NS_LAST "jabber:iq:last" #define NS_MUC "http://jabber.org/protocol/muc" #define NS_MUC_BYTESTREAM "http://telepathy.freedesktop.org/xmpp/protocol/muc-bytestream" #define NS_MUC_USER "http://jabber.org/protocol/muc#user" #define NS_MUC_ADMIN "http://jabber.org/protocol/muc#admin" #define NS_MUC_OWNER "http://jabber.org/protocol/muc#owner" #define NS_NICK "http://jabber.org/protocol/nick" #define NS_OOB "jabber:iq:oob" #define NS_OLPC_BUDDY_PROPS "http://laptop.org/xmpp/buddy-properties" #define NS_OLPC_ACTIVITIES "http://laptop.org/xmpp/activities" #define NS_OLPC_CURRENT_ACTIVITY "http://laptop.org/xmpp/current-activity" #define NS_OLPC_ACTIVITY_PROPS "http://laptop.org/xmpp/activity-properties" #define NS_OLPC_BUDDY "http://laptop.org/xmpp/buddy" #define NS_OLPC_ACTIVITY "http://laptop.org/xmpp/activity" #define NS_PUBSUB "http://jabber.org/protocol/pubsub" #define NS_PRESENCE_INVISIBLE "presence-invisible" #define NS_PRIVACY "jabber:iq:privacy" #define NS_INVISIBLE "urn:xmpp:invisible:0" #define NS_RECEIPTS "urn:xmpp:receipts" #define NS_REGISTER "jabber:iq:register" #define NS_ROSTER "jabber:iq:roster" #define NS_SEARCH "jabber:iq:search" #define NS_SI "http://jabber.org/protocol/si" #define NS_SI_MULTIPLE "http://telepathy.freedesktop.org/xmpp/si-multiple" #define NS_TUBES "http://telepathy.freedesktop.org/xmpp/tubes" #define NS_MUJI "http://telepathy.freedesktop.org/xmpp/muji" #define NS_VCARD_TEMP "vcard-temp" #define NS_VCARD_TEMP_UPDATE "vcard-temp:x:update" #define NS_X_DATA "jabber:x:data" #define NS_X_DELAY "jabber:x:delay" #define NS_X_CONFERENCE "jabber:x:conference" #define NS_XMPP_STANZAS "urn:ietf:params:xml:ns:xmpp-stanzas" #define NS_VERSION "jabber:iq:version" #define NS_GEOLOC "http://jabber.org/protocol/geoloc" #define NS_GOOGLE_MAIL_NOTIFY "google:mail:notify" #define NS_GOOGLE_SETTING "google:setting" #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" #define NS_TP_FT_METADATA_SERVICE "http://telepathy.freedesktop.org/xmpp/file-transfer-service" #define NS_TP_FT_METADATA "http://telepathy.freedesktop.org/xmpp/file-transfer-metadata" /* This is used by WLM to convert Windows Live ID to XMPP jid. * See http://msdn.microsoft.com/en-us/library/live/hh550849.aspx */ #define NS_WLM_JID_LOOKUP "http://messenger.live.com/xmpp/jidlookup" #endif /* __GABBLE_NAMESPACES__H__ */ �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������telepathy-gabble-0.18.2/src/muc-tube-stream.c�������������������������������������������������������0000644�0001750�0001750�00000003235�12200204333�020752� 0����������������������������������������������������������������������������������������������������ustar�00smcv����������������������������smcv����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * muc-tube-stream.c - Source for GabbleMucTubeStream * Copyright (C) 2012 Collabora Ltd. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 "config.h" #include "muc-tube-stream.h" G_DEFINE_TYPE (GabbleMucTubeStream, gabble_muc_tube_stream, GABBLE_TYPE_TUBE_STREAM) static GPtrArray * gabble_muc_tube_stream_get_interfaces (TpBaseChannel *base) { GPtrArray *interfaces; interfaces = TP_BASE_CHANNEL_CLASS ( gabble_muc_tube_stream_parent_class)->get_interfaces (base); g_ptr_array_add (interfaces, TP_IFACE_CHANNEL_INTERFACE_GROUP); return interfaces; } static void gabble_muc_tube_stream_init (GabbleMucTubeStream *self) { } static void gabble_muc_tube_stream_class_init ( GabbleMucTubeStreamClass *gabble_muc_tube_stream_class) { TpBaseChannelClass *base_class = TP_BASE_CHANNEL_CLASS ( gabble_muc_tube_stream_class); base_class->get_interfaces = gabble_muc_tube_stream_get_interfaces; base_class->target_handle_type = TP_HANDLE_TYPE_ROOM; } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������telepathy-gabble-0.18.2/src/muc-tube-stream.h�������������������������������������������������������0000644�0001750�0001750�00000004131�12200204333�020753� 0����������������������������������������������������������������������������������������������������ustar�00smcv����������������������������smcv����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * muc-tube-stream.h - Header for GabbleMucTubeStream * Copyright (C) 2012 Collabora Ltd. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 __GABBLE_MUC_TUBE_STREAM_H__ #define __GABBLE_MUC_TUBE_STREAM_H__ #include <glib-object.h> #include "tube-stream.h" G_BEGIN_DECLS typedef struct _GabbleMucTubeStream GabbleMucTubeStream; typedef struct _GabbleMucTubeStreamClass GabbleMucTubeStreamClass; struct _GabbleMucTubeStreamClass { GabbleTubeStreamClass parent_class; }; struct _GabbleMucTubeStream { GabbleTubeStream parent; }; GType gabble_muc_tube_stream_get_type (void); /* TYPE MACROS */ #define GABBLE_TYPE_MUC_TUBE_STREAM \ (gabble_muc_tube_stream_get_type ()) #define GABBLE_MUC_TUBE_STREAM(obj) \ (G_TYPE_CHECK_INSTANCE_CAST((obj), GABBLE_TYPE_MUC_TUBE_STREAM, GabbleMucTubeStream)) #define GABBLE_MUC_TUBE_STREAM_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST((klass), GABBLE_TYPE_MUC_TUBE_STREAM,\ GabbleMucTubeStreamClass)) #define GABBLE_IS_MUC_TUBE_STREAM(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE((obj), GABBLE_TYPE_MUC_TUBE_STREAM)) #define GABBLE_IS_MUC_TUBE_STREAM_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE((klass), GABBLE_TYPE_MUC_TUBE_STREAM)) #define GABBLE_MUC_TUBE_STREAM_GET_CLASS(obj) \ (G_TYPE_INSTANCE_GET_CLASS ((obj), GABBLE_TYPE_MUC_TUBE_STREAM,\ GabbleMucTubeStreamClass)) G_END_DECLS #endif /* #ifndef __GABBLE_MUC_TUBE_STREAM_H__ */ ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������telepathy-gabble-0.18.2/src/muc-tube-dbus.c���������������������������������������������������������0000644�0001750�0001750�00000003177�12200204333�020421� 0����������������������������������������������������������������������������������������������������ustar�00smcv����������������������������smcv����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * muc-tube-dbus.c - Source for GabbleMucTubeDBus * Copyright (C) 2012 Collabora Ltd. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 "config.h" #include "muc-tube-dbus.h" G_DEFINE_TYPE (GabbleMucTubeDBus, gabble_muc_tube_dbus, GABBLE_TYPE_TUBE_DBUS) static GPtrArray * gabble_muc_tube_dbus_get_interfaces (TpBaseChannel *base) { GPtrArray *interfaces; interfaces = TP_BASE_CHANNEL_CLASS ( gabble_muc_tube_dbus_parent_class)->get_interfaces (base); g_ptr_array_add (interfaces, TP_IFACE_CHANNEL_INTERFACE_GROUP); return interfaces; } static void gabble_muc_tube_dbus_init (GabbleMucTubeDBus *self) { } static void gabble_muc_tube_dbus_class_init ( GabbleMucTubeDBusClass *gabble_muc_tube_dbus_class) { TpBaseChannelClass *base_class = TP_BASE_CHANNEL_CLASS ( gabble_muc_tube_dbus_class); base_class->get_interfaces = gabble_muc_tube_dbus_get_interfaces; base_class->target_handle_type = TP_HANDLE_TYPE_ROOM; } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������telepathy-gabble-0.18.2/src/muc-tube-dbus.h���������������������������������������������������������0000644�0001750�0001750�00000004035�12200204333�020420� 0����������������������������������������������������������������������������������������������������ustar�00smcv����������������������������smcv����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * muc-tube-dbus.h - Header for GabbleMucTubeDBus * Copyright (C) 2012 Collabora Ltd. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 __GABBLE_MUC_TUBE_DBUS_H__ #define __GABBLE_MUC_TUBE_DBUS_H__ #include <glib-object.h> #include "tube-dbus.h" G_BEGIN_DECLS typedef struct _GabbleMucTubeDBus GabbleMucTubeDBus; typedef struct _GabbleMucTubeDBusClass GabbleMucTubeDBusClass; struct _GabbleMucTubeDBusClass { GabbleTubeDBusClass parent_class; }; struct _GabbleMucTubeDBus { GabbleTubeDBus parent; }; GType gabble_muc_tube_dbus_get_type (void); /* TYPE MACROS */ #define GABBLE_TYPE_MUC_TUBE_DBUS \ (gabble_muc_tube_dbus_get_type ()) #define GABBLE_MUC_TUBE_DBUS(obj) \ (G_TYPE_CHECK_INSTANCE_CAST((obj), GABBLE_TYPE_MUC_TUBE_DBUS, GabbleMucTubeDBus)) #define GABBLE_MUC_TUBE_DBUS_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST((klass), GABBLE_TYPE_MUC_TUBE_DBUS,\ GabbleMucTubeDBusClass)) #define GABBLE_IS_MUC_TUBE_DBUS(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE((obj), GABBLE_TYPE_MUC_TUBE_DBUS)) #define GABBLE_IS_MUC_TUBE_DBUS_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE((klass), GABBLE_TYPE_MUC_TUBE_DBUS)) #define GABBLE_MUC_TUBE_DBUS_GET_CLASS(obj) \ (G_TYPE_INSTANCE_GET_CLASS ((obj), GABBLE_TYPE_MUC_TUBE_DBUS,\ GabbleMucTubeDBusClass)) G_END_DECLS #endif /* #ifndef __GABBLE_MUC_TUBE_DBUS_H__ */ ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������telepathy-gabble-0.18.2/src/muc-factory.c�����������������������������������������������������������0000644�0001750�0001750�00000157407�12227000321�020204� 0����������������������������������������������������������������������������������������������������ustar�00smcv����������������������������smcv����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * muc-factory.c - Source for GabbleMucFactory * Copyright (C) 2006 Collabora Ltd. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 "config.h" #include "muc-factory.h" #include <string.h> #include <dbus/dbus-glib.h> #include <dbus/dbus-glib-lowlevel.h> #include <wocky/wocky.h> #include <telepathy-glib/telepathy-glib.h> #include <telepathy-glib/telepathy-glib-dbus.h> #define DEBUG_FLAG GABBLE_DEBUG_MUC #include "gabble/caps-channel-manager.h" #include "connection.h" #include "conn-olpc.h" #include "debug.h" #include "disco.h" #include "im-channel.h" #ifdef ENABLE_VOIP #include "media-factory.h" #endif #include "message-util.h" #include "muc-channel.h" #include "namespaces.h" #include "presence-cache.h" #include "tube-dbus.h" #include "tube-stream.h" #include "util.h" #ifdef ENABLE_VOIP #include "call-muc-channel.h" #endif static void channel_manager_iface_init (gpointer, gpointer); G_DEFINE_TYPE_WITH_CODE (GabbleMucFactory, gabble_muc_factory, G_TYPE_OBJECT, G_IMPLEMENT_INTERFACE (TP_TYPE_CHANNEL_MANAGER, channel_manager_iface_init); G_IMPLEMENT_INTERFACE (GABBLE_TYPE_CAPS_CHANNEL_MANAGER, NULL)); /* properties */ enum { PROP_CONNECTION = 1, LAST_PROPERTY }; typedef struct { GabbleMucFactory *self; gpointer token; } Request; struct _GabbleMucFactoryPrivate { GabbleConnection *conn; gulong status_changed_id; guint message_cb_id; /* GUINT_TO_POINTER(room_handle) => (GabbleMucChannel *) */ GHashTable *text_channels; /* Tube channels which will be considered ready when the corresponding * text channel is created. * Borrowed GabbleMucChannel => owned GQueue of borrowed GabbleTubeIface */ GHashTable *text_needed_for_tube; /* GabbleDiscoRequest * => NULL (used as a set) */ GHashTable *disco_requests; /* Map from channels to the request-tokens of requests that they will satisfy * when they're ready. * Borrowed TpExportableChannel => GSList of gpointer */ GHashTable *queued_requests; gboolean dispose_has_run; }; static GObject *gabble_muc_factory_constructor (GType type, guint n_props, GObjectConstructParam *props); static void gabble_muc_factory_associate_tube (GabbleMucFactory *self, GabbleMucChannel *gmuc, GabbleTubeIface *tube); static void gabble_muc_factory_init (GabbleMucFactory *fac) { GabbleMucFactoryPrivate *priv = G_TYPE_INSTANCE_GET_PRIVATE (fac, GABBLE_TYPE_MUC_FACTORY, GabbleMucFactoryPrivate); fac->priv = priv; priv->text_channels = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, g_object_unref); priv->text_needed_for_tube = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, (GDestroyNotify) g_queue_free); priv->disco_requests = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, NULL); priv->queued_requests = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, NULL); priv->conn = NULL; priv->dispose_has_run = FALSE; } static void cancel_disco_request (gpointer key, gpointer value, gpointer user_data) { GabbleDisco *disco = GABBLE_DISCO (user_data); GabbleDiscoRequest *request = (GabbleDiscoRequest *) key; gabble_disco_cancel_request (disco, request); } static void gabble_muc_factory_close_all (GabbleMucFactory *fac); static void gabble_muc_factory_dispose (GObject *object) { GabbleMucFactory *fac = GABBLE_MUC_FACTORY (object); GabbleMucFactoryPrivate *priv = fac->priv; if (priv->dispose_has_run) return; DEBUG ("dispose called"); priv->dispose_has_run = TRUE; gabble_muc_factory_close_all (fac); g_assert (priv->text_channels == NULL); g_assert (priv->text_needed_for_tube == NULL); g_assert (priv->queued_requests == NULL); g_hash_table_foreach (priv->disco_requests, cancel_disco_request, priv->conn->disco); g_hash_table_unref (priv->disco_requests); if (G_OBJECT_CLASS (gabble_muc_factory_parent_class)->dispose) G_OBJECT_CLASS (gabble_muc_factory_parent_class)->dispose (object); } static void gabble_muc_factory_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { GabbleMucFactory *fac = GABBLE_MUC_FACTORY (object); GabbleMucFactoryPrivate *priv = fac->priv; switch (property_id) { case PROP_CONNECTION: g_value_set_object (value, priv->conn); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void gabble_muc_factory_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { GabbleMucFactory *fac = GABBLE_MUC_FACTORY (object); GabbleMucFactoryPrivate *priv = fac->priv; switch (property_id) { case PROP_CONNECTION: priv->conn = g_value_get_object (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void gabble_muc_factory_class_init (GabbleMucFactoryClass *gabble_muc_factory_class) { GObjectClass *object_class = G_OBJECT_CLASS (gabble_muc_factory_class); GParamSpec *param_spec; g_type_class_add_private (gabble_muc_factory_class, sizeof (GabbleMucFactoryPrivate)); object_class->constructor = gabble_muc_factory_constructor; object_class->dispose = gabble_muc_factory_dispose; object_class->get_property = gabble_muc_factory_get_property; object_class->set_property = gabble_muc_factory_set_property; param_spec = g_param_spec_object ("connection", "GabbleConnection object", "Gabble connection object that owns this MUC factory object.", GABBLE_TYPE_CONNECTION, G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, PROP_CONNECTION, param_spec); } /** * muc_channel_closed_cb: * * Signal callback for when a MUC channel is closed. Removes the references * that MucFactory holds to them. */ static void muc_channel_closed_cb (GabbleMucChannel *chan, gpointer user_data) { GabbleMucFactory *fac = GABBLE_MUC_FACTORY (user_data); GabbleMucFactoryPrivate *priv = fac->priv; TpBaseChannel *base = TP_BASE_CHANNEL (chan); TpHandle room_handle; /* channel is actually reappearing, announce it */ if (tp_base_channel_is_respawning (base)) { tp_channel_manager_emit_new_channel (fac, TP_EXPORTABLE_CHANNEL (chan), NULL); return; } if (tp_base_channel_is_registered (base)) { tp_channel_manager_emit_channel_closed_for_object (fac, TP_EXPORTABLE_CHANNEL (chan)); } if (tp_base_channel_is_destroyed (base) && priv->text_channels != NULL) { g_object_get (chan, "handle", &room_handle, NULL); DEBUG ("removing MUC channel with handle %d", room_handle); g_hash_table_remove (priv->text_channels, GUINT_TO_POINTER (room_handle)); } } static void muc_ready_cb (GabbleMucChannel *text_chan, gpointer data) { GabbleMucFactory *fac = GABBLE_MUC_FACTORY (data); GabbleMucFactoryPrivate *priv = fac->priv; GHashTable *channels; TpBaseChannel *base = TP_BASE_CHANNEL (text_chan); GSList *requests_satisfied_text = NULL; GQueue *tube_channels; DEBUG ("text chan=%p", text_chan); channels = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, (GDestroyNotify) g_slist_free); requests_satisfied_text = g_hash_table_lookup ( priv->queued_requests, text_chan); g_hash_table_steal (priv->queued_requests, text_chan); requests_satisfied_text = g_slist_reverse (requests_satisfied_text); /* Announce tube channels now */ tube_channels = g_hash_table_lookup (priv->text_needed_for_tube, text_chan); if (tube_channels != NULL) { GList *l; for (l = tube_channels->head; l != NULL; l = l->next) { GabbleTubeIface *tube_chan = GABBLE_TUBE_IFACE (l->data); GSList *requests_satisfied_tube; requests_satisfied_tube = g_hash_table_lookup ( priv->queued_requests, tube_chan); g_hash_table_steal (priv->queued_requests, tube_chan); requests_satisfied_tube = g_slist_reverse (requests_satisfied_tube); g_hash_table_insert (channels, tube_chan, requests_satisfied_tube); } g_hash_table_remove (priv->text_needed_for_tube, text_chan); } /* only announce channels which are on the bus (requested or * requested with an invite, not channels only around because they * have to be) */ if (tp_base_channel_is_registered (base)) { tp_channel_manager_emit_new_channel (fac, TP_EXPORTABLE_CHANNEL (text_chan), requests_satisfied_text); } tp_channel_manager_emit_new_channels (fac, channels); g_hash_table_unref (channels); } static void muc_join_error_cb (GabbleMucChannel *chan, GError *error, gpointer data) { GabbleMucFactory *fac = GABBLE_MUC_FACTORY (data); GabbleMucFactoryPrivate *priv = fac->priv; GQueue *tube_channels; GSList *requests_satisfied; GSList *iter; DEBUG ("error->code=%u, error->message=\"%s\"", error->code, error->message); requests_satisfied = g_slist_reverse (g_hash_table_lookup ( priv->queued_requests, chan)); g_hash_table_steal (priv->queued_requests, chan); for (iter = requests_satisfied; iter != NULL; iter = iter->next) { tp_channel_manager_emit_request_failed (fac, iter->data, error->domain, error->code, error->message); } g_slist_free (requests_satisfied); /* tube channels */ tube_channels = g_hash_table_lookup (priv->text_needed_for_tube, chan); if (tube_channels != NULL) { GList *l; for (l = tube_channels->head; l != NULL; l = l->next) { GabbleTubeIface *tube_chan = GABBLE_TUBE_IFACE (l->data); requests_satisfied = g_hash_table_lookup ( priv->queued_requests, tube_chan); g_hash_table_steal (priv->queued_requests, tube_chan); requests_satisfied = g_slist_reverse (requests_satisfied); for (iter = requests_satisfied; iter != NULL; iter = iter->next) { tp_channel_manager_emit_request_failed (fac, iter->data, error->domain, error->code, error->message); } g_slist_free (requests_satisfied); } g_hash_table_remove (priv->text_needed_for_tube, chan); } } static void muc_sub_channel_closed_cb (TpSvcChannel *chan, gpointer user_data) { GabbleMucFactory *fac = GABBLE_MUC_FACTORY (user_data); GabbleMucChannel *muc; tp_channel_manager_emit_channel_closed_for_object (fac, TP_EXPORTABLE_CHANNEL (chan)); /* GabbleTubeDBus, GabbleTubeStream, and GabbleMucCallChannel all * have "muc" properties. */ g_object_get (chan, "muc", &muc, NULL); if (muc == NULL) return; if (gabble_muc_channel_can_be_closed (muc) && gabble_muc_channel_get_autoclose (muc)) tp_base_channel_close (TP_BASE_CHANNEL (muc)); } #ifdef ENABLE_VOIP static void muc_channel_new_call (GabbleMucChannel *muc, GabbleCallMucChannel *call, GSList *requests, gpointer user_data) { GabbleMucFactory *fac = GABBLE_MUC_FACTORY (user_data); DEBUG ("Emitting new Call channel"); tp_channel_manager_emit_new_channel (fac, TP_EXPORTABLE_CHANNEL (call), requests); g_signal_connect (call, "closed", G_CALLBACK (muc_sub_channel_closed_cb), fac); } #endif static void muc_channel_new_tube (GabbleMucChannel *channel, GabbleTubeIface *tube, gpointer user_data) { GabbleMucFactory *fac = GABBLE_MUC_FACTORY (user_data); /* If the muc channel is ready announce the tube channel right away * otherwise wait for the text channel to be ready */ if (_gabble_muc_channel_is_ready (channel)) tp_channel_manager_emit_new_channel (fac, TP_EXPORTABLE_CHANNEL (tube), NULL); else gabble_muc_factory_associate_tube (fac, channel, tube); g_signal_connect (tube, "closed", G_CALLBACK (muc_sub_channel_closed_cb), fac); } /** * new_muc_channel */ static GabbleMucChannel * new_muc_channel (GabbleMucFactory *fac, TpHandle handle, gboolean invited, TpHandle inviter, const gchar *message, gboolean requested, gboolean initially_register, GHashTable *initial_channels, GArray *initial_handles, char **initial_ids, const char *room_name) { GabbleMucFactoryPrivate *priv = fac->priv; TpBaseConnection *conn = (TpBaseConnection *) priv->conn; GabbleMucChannel *chan; char *object_path; GPtrArray *initial_channels_array = NULL; g_assert (gabble_muc_factory_find_text_channel (fac, handle) == NULL); object_path = g_strdup_printf ("%s/MucChannel%u", tp_base_connection_get_object_path (conn), handle); initial_channels_array = g_ptr_array_new (); if (initial_channels != NULL) { GHashTableIter iter; gpointer key; g_hash_table_iter_init (&iter, initial_channels); while (g_hash_table_iter_next (&iter, &key, NULL)) { g_ptr_array_add (initial_channels_array, key); } } if (initial_handles != NULL) g_array_ref (initial_handles); else initial_handles = g_array_new (FALSE, TRUE, sizeof (TpHandle)); DEBUG ("creating new chan, object path %s", object_path); chan = g_object_new (GABBLE_TYPE_MUC_CHANNEL, "connection", priv->conn, "object-path", object_path, "handle", handle, "invited", invited, "initiator-handle", invited ? inviter : tp_base_connection_get_self_handle (conn), "invitation-message", message, "requested", requested, "initial-channels", initial_channels_array, "initial-invitee-handles", initial_handles, "initial-invitee-ids", initial_ids, "room-name", room_name, "initially-register", initially_register, NULL); g_signal_connect (chan, "closed", (GCallback) muc_channel_closed_cb, fac); g_signal_connect (chan, "new-tube", (GCallback) muc_channel_new_tube, fac); #ifdef ENABLE_VOIP g_signal_connect (chan, "new-call", (GCallback) muc_channel_new_call, fac); #endif g_hash_table_insert (priv->text_channels, GUINT_TO_POINTER (handle), chan); g_free (object_path); g_ptr_array_unref (initial_channels_array); g_array_unref (initial_handles); if (_gabble_muc_channel_is_ready (chan)) muc_ready_cb (chan, fac); else g_signal_connect (chan, "ready", G_CALLBACK (muc_ready_cb), fac); g_signal_connect (chan, "join-error", G_CALLBACK (muc_join_error_cb), fac); return chan; } static void do_invite (GabbleMucFactory *fac, const gchar *room, TpHandle inviter_handle, const gchar *reason) { GabbleMucFactoryPrivate *priv = fac->priv; TpHandleRepoIface *room_repo = tp_base_connection_get_handles ( (TpBaseConnection *) priv->conn, TP_HANDLE_TYPE_ROOM); TpHandle room_handle; room_handle = tp_handle_ensure (room_repo, room, NULL, NULL); if (room_handle == 0) { DEBUG ("got a MUC invitation message with invalid room JID \"%s\"; " "ignoring", room); return; } if (gabble_muc_factory_find_text_channel (fac, room_handle) == NULL) { new_muc_channel (fac, room_handle, TRUE, inviter_handle, reason, FALSE, TRUE, NULL, NULL, NULL, NULL); } else { DEBUG ("ignoring invite to room \"%s\"; we're already there", room); } } struct DiscoInviteData { GabbleMucFactory *factory; gchar *reason; TpHandle inviter; }; /** * obsolete_invite_disco_cb: * * Callback for disco request we fired upon encountering obsolete disco. * If the object is in fact MUC room, create a channel for it. */ static void obsolete_invite_disco_cb (GabbleDisco *self, GabbleDiscoRequest *request, const gchar *jid, const gchar *node, WockyNode *query_result, GError* error, gpointer user_data) { struct DiscoInviteData *data = (struct DiscoInviteData *) user_data; GabbleMucFactory *fac = GABBLE_MUC_FACTORY (data->factory); GabbleMucFactoryPrivate *priv = fac->priv; WockyNode *identity; const char *category = NULL, *type = NULL; g_hash_table_remove (priv->disco_requests, request); if (error != NULL) { DEBUG ("ignoring obsolete invite to room '%s'; got disco error: %s", jid, error->message); goto out; } identity = wocky_node_get_child (query_result, "identity"); if (identity != NULL) { category = wocky_node_get_attribute (identity, "category"); type = wocky_node_get_attribute (identity, "type"); } if (tp_strdiff (category, "conference") || tp_strdiff (type, "text")) { DEBUG ("obsolete invite request specified inappropriate jid '%s' " "(not a text conference); ignoring request", jid); goto out; } /* OK, it's MUC after all, create a new channel */ do_invite (fac, jid, data->inviter, data->reason); out: g_free (data->reason); g_slice_free (struct DiscoInviteData, data); } static gboolean process_muc_invite (GabbleMucFactory *fac, WockyStanza *message, const gchar *from, TpChannelTextSendError send_error) { GabbleMucFactoryPrivate *priv = fac->priv; TpBaseConnection *conn = (TpBaseConnection *) priv->conn; TpHandleRepoIface *contact_repo = tp_base_connection_get_handles (conn, TP_HANDLE_TYPE_CONTACT); WockyNode *x_node, *invite_node, *reason_node; const gchar *invite_from, *reason = NULL; TpHandle inviter_handle; gchar *room; /* does it have a muc subnode? */ x_node = wocky_node_get_child_ns ( wocky_stanza_get_top_node (message), "x", NS_MUC_USER); if (x_node == NULL) return FALSE; /* and an invitation? */ invite_node = wocky_node_get_child (x_node, "invite"); if (invite_node == NULL) return FALSE; /* FIXME: do something with these? */ if (send_error != GABBLE_TEXT_CHANNEL_SEND_NO_ERROR) { STANZA_DEBUG (message, "got a MUC invitation message with a send " "error; ignoring"); return TRUE; } invite_from = wocky_node_get_attribute (invite_node, "from"); if (invite_from == NULL) { STANZA_DEBUG (message, "got a MUC invitation message with no JID; " "ignoring"); return TRUE; } inviter_handle = tp_handle_ensure (contact_repo, invite_from, NULL, NULL); if (inviter_handle == 0) { STANZA_DEBUG (message, "got a MUC invitation message with invalid " "inviter JID; ignoring"); return TRUE; } reason_node = wocky_node_get_child (invite_node, "reason"); if (reason_node != NULL) reason = reason_node->content; /* create the channel */ room = gabble_remove_resource (from); do_invite (fac, room, inviter_handle, reason); g_free (room); return TRUE; } static gboolean process_obsolete_invite (GabbleMucFactory *fac, WockyStanza *message, const gchar *from, const gchar *body, TpChannelTextSendError send_error) { GabbleMucFactoryPrivate *priv = fac->priv; TpBaseConnection *conn = (TpBaseConnection *) priv->conn; TpHandleRepoIface *contact_repo = tp_base_connection_get_handles (conn, TP_HANDLE_TYPE_CONTACT); WockyNode *x_node; const gchar *room; TpHandle inviter_handle; GabbleDiscoRequest *request; struct DiscoInviteData *disco_udata; /* check for obsolete invite method */ x_node = wocky_node_get_child_ns ( wocky_stanza_get_top_node (message), "x", NS_X_CONFERENCE); if (x_node == NULL) return FALSE; /* this can only happen if the user sent an obsolete invite with another * client or something */ if (send_error != GABBLE_TEXT_CHANNEL_SEND_NO_ERROR) { STANZA_DEBUG (message, "got an obsolete MUC invitation message with " "a send error; ignoring"); return TRUE; } /* the room JID is in x */ room = wocky_node_get_attribute (x_node, "jid"); if (room == NULL) { STANZA_DEBUG (message, "got a obsolete MUC invitation with no room JID; ignoring"); return TRUE; } /* the inviter JID is in "from" */ inviter_handle = tp_handle_ensure (contact_repo, from, NULL, NULL); if (inviter_handle == 0) { STANZA_DEBUG (message, "got an obsolete MUC invitation message from " "an invalid JID; ignoring"); return TRUE; } disco_udata = g_slice_new0 (struct DiscoInviteData); disco_udata->factory = fac; disco_udata->reason = g_strdup (body); disco_udata->inviter = inviter_handle; DEBUG ("received obsolete MUC invite from handle %u (%s), discoing room %s", inviter_handle, from, room); request = gabble_disco_request (priv->conn->disco, GABBLE_DISCO_TYPE_INFO, room, NULL, obsolete_invite_disco_cb, disco_udata, G_OBJECT (fac), NULL); if (request != NULL) { g_hash_table_insert (priv->disco_requests, request, NULL); } else { DEBUG ("obsolete MUC invite disco failed, freeing info"); g_free (disco_udata->reason); g_slice_free (struct DiscoInviteData, disco_udata); } return TRUE; } /** * muc_factory_message_cb: * * Called by Wocky when we get an incoming <message>. * We filter only groupchat and MUC messages, ignoring the rest. */ static gboolean muc_factory_message_cb ( WockyPorter *porter, WockyStanza *message, gpointer user_data) { GabbleMucFactory *fac = GABBLE_MUC_FACTORY (user_data); GabbleMucFactoryPrivate *priv = fac->priv; const gchar *from, *body, *id; time_t stamp; TpChannelTextMessageType msgtype; gint state; TpChannelTextSendError send_error; TpDeliveryStatus delivery_status; if (!gabble_message_util_parse_incoming_message (message, &from, &stamp, &msgtype, &id, &body, &state, &send_error, &delivery_status)) return TRUE; if (conn_olpc_process_activity_properties_message (priv->conn, message, from)) return TRUE; if (conn_olpc_process_activity_uninvite_message (priv->conn, message, from)) return TRUE; if (process_muc_invite (fac, message, from, send_error)) return TRUE; if (process_obsolete_invite (fac, message, from, body, send_error)) return TRUE; /* we used to check if a room with the jid exists, instead at this * * point we stop caring: actual MUC messages are handled internally * * by the wocky muc implementation */ return FALSE; } void gabble_muc_factory_broadcast_presence (GabbleMucFactory *self) { GabbleMucFactoryPrivate *priv = self->priv; GHashTableIter iter; gpointer channel = NULL; if (priv->text_channels == NULL) return; g_hash_table_iter_init (&iter, priv->text_channels); while (g_hash_table_iter_next (&iter, NULL, &channel)) { g_assert (GABBLE_IS_MUC_CHANNEL (channel)); gabble_muc_channel_send_presence (GABBLE_MUC_CHANNEL (channel)); } } static void gabble_muc_factory_associate_tube (GabbleMucFactory *self, GabbleMucChannel *gmuc, GabbleTubeIface *tube) { GabbleMucFactoryPrivate *priv = self->priv; GQueue *queue; queue = g_hash_table_lookup (priv->text_needed_for_tube, gmuc); if (queue == NULL) { queue = g_queue_new (); g_hash_table_insert (priv->text_needed_for_tube, gmuc, queue); } g_queue_push_tail (queue, tube); } static void gabble_muc_factory_associate_request (GabbleMucFactory *self, gpointer channel, gpointer request) { GabbleMucFactoryPrivate *priv = self->priv; GSList *list = g_hash_table_lookup (priv->queued_requests, channel); g_assert (TP_IS_EXPORTABLE_CHANNEL (channel)); g_hash_table_steal (priv->queued_requests, channel); list = g_slist_prepend (list, request); g_hash_table_insert (priv->queued_requests, channel, list); } static void cancel_queued_requests ( GabbleMucFactory *self, GSList *requests_satisfied) { GSList *iter; requests_satisfied = g_slist_reverse (requests_satisfied); for (iter = requests_satisfied; iter != NULL; iter = iter->next) { tp_channel_manager_emit_request_failed (self, iter->data, TP_ERROR, TP_ERROR_DISCONNECTED, "Unable to complete this channel request, we're disconnecting!"); } g_slist_free (requests_satisfied); } static void gabble_muc_factory_close_all (GabbleMucFactory *self) { GabbleMucFactoryPrivate *priv = self->priv; DEBUG ("closing channels"); if (priv->status_changed_id != 0) { g_signal_handler_disconnect (priv->conn, priv->status_changed_id); priv->status_changed_id = 0; } if (priv->queued_requests != NULL) { GHashTableIter iter; gpointer value; g_hash_table_iter_init (&iter, priv->queued_requests); while (g_hash_table_iter_next (&iter, NULL, &value)) { cancel_queued_requests (self, value); g_hash_table_iter_steal (&iter); } } tp_clear_pointer (&priv->queued_requests, g_hash_table_unref); tp_clear_pointer (&priv->text_needed_for_tube, g_hash_table_unref); /* Use a temporary variable because we don't want * muc_channel_closed_cb remove the channel from the hash table a * second time */ if (priv->text_channels != NULL) { GHashTable *tmp = priv->text_channels; GHashTableIter iter; gpointer chan; priv->text_channels = NULL; g_hash_table_iter_init (&iter, tmp); while (g_hash_table_iter_next (&iter, NULL, &chan)) gabble_muc_channel_teardown (GABBLE_MUC_CHANNEL (chan)); g_hash_table_unref (tmp); } if (priv->message_cb_id != 0) { WockyPorter *porter = gabble_connection_dup_porter (priv->conn); wocky_porter_unregister_handler (porter, priv->message_cb_id); priv->message_cb_id = 0; g_object_unref (porter); } } static void porter_available_cb ( GabbleConnection *conn, WockyPorter *porter, gpointer user_data) { GabbleMucFactory *self = GABBLE_MUC_FACTORY (user_data); self->priv->message_cb_id = wocky_porter_register_handler_from_anyone (porter, WOCKY_STANZA_TYPE_MESSAGE, WOCKY_STANZA_SUB_TYPE_NONE, WOCKY_PORTER_HANDLER_PRIORITY_NORMAL, muc_factory_message_cb, self, NULL); } static void connection_status_changed_cb (GabbleConnection *conn, guint status, guint reason, GabbleMucFactory *self) { switch (status) { case TP_CONNECTION_STATUS_DISCONNECTED: gabble_muc_factory_close_all (self); break; } } static GObject * gabble_muc_factory_constructor (GType type, guint n_props, GObjectConstructParam *props) { GObject *obj = G_OBJECT_CLASS (gabble_muc_factory_parent_class)-> constructor (type, n_props, props); GabbleMucFactory *self = GABBLE_MUC_FACTORY (obj); GabbleMucFactoryPrivate *priv = self->priv; priv->status_changed_id = g_signal_connect (priv->conn, "status-changed", (GCallback) connection_status_changed_cb, obj); tp_g_signal_connect_object (priv->conn, "porter-available", (GCallback) porter_available_cb, obj, 0); return obj; } static void gabble_muc_factory_foreach_channel (TpChannelManager *manager, TpExportableChannelFunc foreach, gpointer user_data) { GabbleMucFactory *fac = GABBLE_MUC_FACTORY (manager); GabbleMucFactoryPrivate *priv = fac->priv; GHashTableIter iter; gpointer value; g_hash_table_iter_init (&iter, priv->text_channels); while (g_hash_table_iter_next (&iter, NULL, &value)) { GabbleMucChannel *gmuc = GABBLE_MUC_CHANNEL (value); foreach (TP_EXPORTABLE_CHANNEL (gmuc), user_data); gabble_muc_channel_foreach_tubes (gmuc, foreach, user_data); #ifdef ENABLE_VOIP g_list_foreach (gabble_muc_channel_get_call_channels (gmuc), (GFunc) foreach, user_data); #endif } } /** * ensure_muc_channel: * * Create a MUC channel in response to RequestChannel. * * Return TRUE if it already existed, or return FALSE * if it needed to be created (so isn't ready yet). */ static gboolean ensure_muc_channel (GabbleMucFactory *fac, GabbleMucFactoryPrivate *priv, TpHandle handle, GabbleMucChannel **ret, gboolean requested, gboolean export_text, GHashTable *initial_channels, GArray *initial_handles, char **initial_ids, const char *room_name) { TpBaseConnection *base_conn = (TpBaseConnection *) priv->conn; *ret = gabble_muc_factory_find_text_channel (fac, handle); if (*ret == NULL) { *ret = new_muc_channel (fac, handle, FALSE, tp_base_connection_get_self_handle (base_conn), NULL, requested, export_text, initial_channels, initial_handles, initial_ids, room_name); gabble_muc_channel_set_autoclose (*ret, !export_text); } else { if (export_text) gabble_muc_channel_set_autoclose (*ret, FALSE); } if (_gabble_muc_channel_is_ready (*ret)) return TRUE; else return FALSE; } void gabble_muc_factory_handle_si_stream_request (GabbleMucFactory *self, GabbleBytestreamIface *bytestream, TpHandle room_handle, const gchar *stream_id, WockyStanza *msg) { GabbleMucFactoryPrivate *priv = self->priv; TpHandleRepoIface *room_repo = tp_base_connection_get_handles ( (TpBaseConnection *) priv->conn, TP_HANDLE_TYPE_ROOM); GabbleMucChannel *gmuc = NULL; WockyStanzaType stanza_type; WockyStanzaSubType sub_type; g_return_if_fail (tp_handle_is_valid (room_repo, room_handle, NULL)); wocky_stanza_get_type_info (msg, &stanza_type, &sub_type); g_return_if_fail (stanza_type == WOCKY_STANZA_TYPE_IQ); g_return_if_fail (sub_type == WOCKY_STANZA_SUB_TYPE_SET); gmuc = gabble_muc_factory_find_text_channel (self, room_handle); if (gmuc == NULL) { GError e = { WOCKY_XMPP_ERROR, WOCKY_XMPP_ERROR_BAD_REQUEST, "No MUC channel available" }; DEBUG ("MUC channel doesn't exist handle %d", room_handle); gabble_bytestream_iface_close (bytestream, &e); return; } gabble_muc_channel_handle_si_stream_request ( gmuc, bytestream, stream_id, msg); } GabbleMucChannel * gabble_muc_factory_find_text_channel (GabbleMucFactory *self, TpHandle handle) { 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_PROP_CHANNEL_CHANNEL_TYPE, TP_PROP_CHANNEL_TARGET_HANDLE_TYPE, NULL }; static const gchar * const * muc_tubes_channel_fixed_properties = muc_channel_fixed_properties; static const gchar * const muc_channel_allowed_properties[] = { 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 void gabble_muc_factory_type_foreach_channel_class (GType type, TpChannelManagerTypeChannelClassFunc func, gpointer user_data) { GHashTable *table = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, (GDestroyNotify) tp_g_value_slice_free); GValue *channel_type_value, *handle_type_value; 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_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_PROP_CHANNEL_TARGET_HANDLE_TYPE, handle_type_value); /* Channel.Type.Text */ g_value_set_static_string (channel_type_value, TP_IFACE_CHANNEL_TYPE_TEXT); func (type, table, muc_channel_allowed_properties, user_data); /* Muc Channel.Type.StreamTube */ g_value_set_static_string (channel_type_value, TP_IFACE_CHANNEL_TYPE_STREAM_TUBE); func (type, table, gabble_tube_stream_channel_get_allowed_properties (), user_data); /* Muc Channel.Type.DBusTube */ g_value_set_static_string (channel_type_value, TP_IFACE_CHANNEL_TYPE_DBUS_TUBE); func (type, table, gabble_tube_dbus_channel_get_allowed_properties (), user_data); #ifdef ENABLE_VOIP /* Muc Channel.Type.Call */ g_value_set_static_string (channel_type_value, TP_IFACE_CHANNEL_TYPE_CALL); func (type, table, gabble_media_factory_call_channel_allowed_properties (), user_data); #endif g_hash_table_unref (table); } static gboolean handle_text_channel_request (GabbleMucFactory *self, gpointer request_token, GHashTable *request_properties, gboolean require_new, TpHandle room, GError **error) { GabbleMucFactoryPrivate *priv = self->priv; TpBaseConnection *conn = TP_BASE_CONNECTION (priv->conn); GabbleMucChannel *text_chan; TpHandleSet *handles; TpIntset *continue_handles; guint i; gboolean ret = TRUE; TpHandleRepoIface *contact_handles = tp_base_connection_get_handles (conn, TP_HANDLE_TYPE_CONTACT); TpHandleRepoIface *room_handles = tp_base_connection_get_handles (conn, TP_HANDLE_TYPE_ROOM); GPtrArray *initial_channels; GHashTable *final_channels; /* used as a set: (char *) -> NULL */ GArray *initial_handles, *final_handles; char **initial_ids, **final_ids; const char *invite_msg; const gchar *room_name, *server_prop; if (tp_channel_manager_asv_has_unknown_properties (request_properties, muc_channel_fixed_properties, muc_channel_allowed_properties, error)) return FALSE; initial_channels = tp_asv_get_boxed (request_properties, TP_PROP_CHANNEL_INTERFACE_CONFERENCE_INITIAL_CHANNELS, TP_ARRAY_TYPE_OBJECT_PATH_LIST); initial_handles = tp_asv_get_boxed (request_properties, TP_PROP_CHANNEL_INTERFACE_CONFERENCE_INITIAL_INVITEE_HANDLES, DBUS_TYPE_G_UINT_ARRAY); initial_ids = tp_asv_get_boxed (request_properties, TP_PROP_CHANNEL_INTERFACE_CONFERENCE_INITIAL_INVITEE_IDS, G_TYPE_STRV); invite_msg = tp_asv_get_string (request_properties, TP_PROP_CHANNEL_INTERFACE_CONFERENCE_INVITATION_MESSAGE); room_name = tp_asv_get_string (request_properties, TP_PROP_CHANNEL_INTERFACE_ROOM_ROOM_NAME); server_prop = tp_asv_get_string (request_properties, TP_PROP_CHANNEL_INTERFACE_ROOM_SERVER); handles = tp_handle_set_new (contact_handles); continue_handles = tp_intset_new (); final_channels = g_hash_table_new (g_str_hash, g_str_equal); /* look at the list of initial channels, build a set of handles to invite */ if (initial_channels != NULL) { TpDBusDaemon *dbus_daemon = tp_base_connection_get_dbus_daemon (conn); DBusGConnection *bus = tp_proxy_get_dbus_connection (dbus_daemon); for (i = 0; i < initial_channels->len; i++) { const char *object_path = g_ptr_array_index (initial_channels, i); GObject *object; TpHandle handle; TpBaseConnection *connection; object = dbus_g_connection_lookup_g_object (bus, object_path); if (!GABBLE_IS_IM_CHANNEL (object)) { DEBUG ("Channel %s is not an ImChannel, ignoring", object_path); continue; } connection = tp_base_channel_get_connection ( TP_BASE_CHANNEL (object)); if ((GabbleConnection *) connection != priv->conn) { DEBUG ("Channel %s is from a different Connection, ignoring", object_path); continue; } handle = tp_base_channel_get_target_handle ( TP_BASE_CHANNEL (object)); tp_handle_set_add (handles, handle); tp_intset_add (continue_handles, handle); g_hash_table_insert (final_channels, (char *) object_path, NULL); } } /* look at the list of initial handles, add these to the handles set */ if (initial_handles != NULL) { for (i = 0; i < initial_handles->len; i++) { TpHandle handle = g_array_index (initial_handles, TpHandle, i); if (tp_handle_inspect (contact_handles, handle) == NULL) { DEBUG ("Bad Handle %u, ignoring", handle); continue; } tp_handle_set_add (handles, handle); } } /* look at the list of initial ids, add these to the handles set */ if (initial_ids != NULL) { char **ptr; for (ptr = initial_ids; *ptr != NULL; ptr++) { char *id = *ptr; TpHandle handle = tp_handle_ensure (contact_handles, id, NULL, NULL); if (handle == 0) { DEBUG ("Bad ID '%s', ignoring", id); continue; } tp_handle_set_add (handles, handle); } } /* build new InitialInviteeHandles and InitialInviteeIDs */ /* FIXME: include Self Handle to comply with spec ? */ final_handles = tp_handle_set_to_array (handles); final_ids = g_new0 (char *, final_handles->len + 1); for (i = 0; i < final_handles->len; i++) { TpHandle handle = g_array_index (final_handles, TpHandle, i); const char *id = tp_handle_inspect (contact_handles, handle); final_ids[i] = (char *) id; } /* TargetHandleType=None and TargetHandle=0 */ if (room == 0) { char *uuid, *id, *server = ""; gchar *tmp = NULL; /* There's no super obvious way to tell.. you can't invite GMail users to * a non-Google MUC (it just doesn't work), and if your own account is on * a Google server, you may as well use a Google PMUC. If one of your * initial contacts is using GMail, you should also use a Google PMUC */ for (i = 0; i < final_handles->len; i++) { TpHandle handle = g_array_index (final_handles, TpHandle, i); GabblePresence *presence; presence = gabble_presence_cache_get (priv->conn->presence_cache, handle); if (presence != NULL && gabble_presence_has_cap (presence, QUIRK_GOOGLE_WEBMAIL_CLIENT)) { DEBUG ("Initial invitee includes Google Webmail client"); server = "@groupchat.google.com"; break; } } if (server_prop != NULL) { tmp = g_strdup_printf ("@%s", server_prop); server = tmp; } if (room_name != NULL && room_name[0] != '\0') { id = g_strdup_printf ("%s%s", room_name, server); } else { uuid = gabble_generate_id (); id = g_strdup_printf ("private-chat-%s%s", uuid, server); g_free (uuid); DEBUG ("Creating PMUC '%s'", id); } room = tp_handle_ensure (room_handles, id, NULL, error); g_free (id); g_free (tmp); if (room == 0) { ret = FALSE; goto out; } } /* Make sure TargetID and RoomName don't conflict. */ if (room_name != NULL && room_name[0] != '\0') { const gchar *target_id = tp_handle_inspect (room_handles, room); gchar *target_room = NULL; gboolean ok; /* JIDs that are handles must already be valid. */ ok = wocky_decode_jid (target_id, &target_room, NULL, NULL); g_assert (ok); ok = !tp_strdiff (target_room, room_name); if (!ok) { g_set_error (error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT, "TargetID's node part (%s) doesn't match RoomName (%s)", target_room, room_name); ret = FALSE; } g_free (target_room); if (!ok) goto out; } /* Make sure TargetID and Server don't conflict. */ if (server_prop != NULL) { const gchar *target_id = tp_handle_inspect (room_handles, room); gchar *target_server = NULL; gboolean ok = TRUE; /* JIDs that are handles must already be valid. */ ok = wocky_decode_jid (target_id, NULL, &target_server, NULL); g_assert (ok); if (target_server != NULL) ok = !tp_strdiff (target_server, server_prop); else ok = TRUE; if (!ok) { g_set_error (error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT, "TargetID's domain part (%s) doesn't match Server (%s)", target_server, server_prop); ret = FALSE; } g_free (target_server); if (!ok) goto out; } if (ensure_muc_channel (self, priv, room, &text_chan, TRUE, TRUE, final_channels, final_handles, final_ids, room_name)) { /* channel exists */ if (require_new && tp_base_channel_is_registered (TP_BASE_CHANNEL (text_chan))) { g_set_error (error, TP_ERROR, TP_ERROR_NOT_AVAILABLE, "That channel has already been created (or requested)"); ret = FALSE; } else { if (initial_channels != NULL || initial_handles != NULL || initial_ids != NULL) { g_set_error (error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT, "Cannot set InitialChannels, InitialInviteeHandles or " "InitialInviteIDs for existing channel"); ret = FALSE; } else { if (tp_base_channel_is_registered (TP_BASE_CHANNEL (text_chan))) { tp_channel_manager_emit_request_already_satisfied (self, request_token, TP_EXPORTABLE_CHANNEL (text_chan)); } else { GSList *tokens; tp_base_channel_register (TP_BASE_CHANNEL (text_chan)); tokens = g_slist_append (NULL, request_token); tp_channel_manager_emit_new_channel (self, TP_EXPORTABLE_CHANNEL (text_chan), tokens); g_slist_free (tokens); } ret = TRUE; } } goto out; } else { gabble_muc_factory_associate_request (self, text_chan, request_token); } /* invite all of the invitees to this new MUC */ /* members included in an InitialChannel will want the <continue/> node set */ for (i = 0; i < final_handles->len; i++) { TpHandle handle = g_array_index (final_handles, TpHandle, i); char *id = final_ids[i]; GError *error2 = NULL; gboolean continue_; continue_ = tp_intset_is_member (continue_handles, handle); /* N.B. contrary to what Google's own spec implies, an invite message * will not be handled correctly by the GMail client. We're going to * have to strip it out of invites to GMail clients */ gabble_muc_channel_send_invite (text_chan, id, invite_msg, continue_, &error2); if (error2 != NULL) { DEBUG ("%s", error2->message); g_error_free (error2); continue; } } out: g_hash_table_unref (final_channels); g_array_unref (final_handles); g_free (final_ids); tp_handle_set_destroy (handles); tp_intset_destroy (continue_handles); return ret; } static gboolean handle_tube_channel_request (GabbleMucFactory *self, gpointer request_token, GHashTable *request_properties, gboolean require_new, TpHandle handle, GError **error) { GabbleMucFactoryPrivate *priv = self->priv; gboolean can_announce_now; GabbleMucChannel * gmuc; GabbleTubeIface *new_channel; gmuc = gabble_muc_factory_find_text_channel (self, handle); if (gmuc == NULL) ensure_muc_channel (self, priv, handle, &gmuc, FALSE, FALSE, NULL, NULL, NULL, NULL); can_announce_now = _gabble_muc_channel_is_ready (gmuc); new_channel = gabble_muc_channel_tube_request (gmuc, request_token, request_properties, TRUE); g_signal_connect (new_channel, "closed", G_CALLBACK (muc_sub_channel_closed_cb), self); if (can_announce_now) { GSList *request_tokens; request_tokens = g_slist_prepend (NULL, request_token); tp_channel_manager_emit_new_channel (self, TP_EXPORTABLE_CHANNEL (new_channel), request_tokens); g_slist_free (request_tokens); } else { gabble_muc_factory_associate_tube (self, gmuc, new_channel); /* And now finally associate the new stream or dbus tube channel with * the request token so that when the muc channel is ready, the request * will be satisfied. */ gabble_muc_factory_associate_request (self, new_channel, request_token); } return TRUE; } static gboolean handle_stream_tube_channel_request (GabbleMucFactory *self, gpointer request_token, GHashTable *request_properties, gboolean require_new, TpHandle handle, GError **error) { const gchar *service; if (tp_channel_manager_asv_has_unknown_properties (request_properties, muc_tubes_channel_fixed_properties, gabble_tube_stream_channel_get_allowed_properties (), error)) return FALSE; /* "Service" is a mandatory, not-fixed property */ service = tp_asv_get_string (request_properties, TP_PROP_CHANNEL_TYPE_STREAM_TUBE_SERVICE); if (service == NULL) { g_set_error (error, TP_ERROR, TP_ERROR_NOT_IMPLEMENTED, "Request does not contain the mandatory property '%s'", TP_PROP_CHANNEL_TYPE_STREAM_TUBE_SERVICE); return FALSE; } return handle_tube_channel_request (self, request_token, request_properties, require_new, handle, error); } static gboolean handle_dbus_tube_channel_request (GabbleMucFactory *self, gpointer request_token, GHashTable *request_properties, gboolean require_new, TpHandle handle, GError **error) { const gchar *service; if (tp_channel_manager_asv_has_unknown_properties (request_properties, muc_tubes_channel_fixed_properties, gabble_tube_dbus_channel_get_allowed_properties (), error)) return FALSE; /* "ServiceName" is a mandatory, not-fixed property */ service = tp_asv_get_string (request_properties, TP_PROP_CHANNEL_TYPE_DBUS_TUBE_SERVICE_NAME); if (service == NULL) { g_set_error (error, TP_ERROR, TP_ERROR_NOT_IMPLEMENTED, "Request does not contain the mandatory property '%s'", TP_PROP_CHANNEL_TYPE_DBUS_TUBE_SERVICE_NAME); return FALSE; } return handle_tube_channel_request (self, request_token, request_properties, require_new, handle, error); } #ifdef ENABLE_VOIP static void call_muc_channel_request_cb (GObject *source, GAsyncResult *result, gpointer user_data) { Request *r = user_data; GabbleMucFactory *self = GABBLE_MUC_FACTORY (r->self); GabbleMucChannel *channel = GABBLE_MUC_CHANNEL (source); gpointer request_token = r->token; GError *error = NULL; if (!gabble_muc_channel_request_call_finish (channel, result, &error)) { tp_channel_manager_emit_request_failed (self, request_token, error->domain, error->code, error->message); g_error_free (error); } /* No need to handle a successful request, this is handled when the muc * signals a new call channel automagically */ g_object_unref (r->self); g_slice_free (Request, r); } static gboolean handle_call_channel_request (GabbleMucFactory *self, gpointer request_token, GHashTable *request_properties, gboolean require_new, TpHandle handle, GError **error) { GabbleMucFactoryPrivate *priv = self->priv; gboolean initial_audio, initial_video; GabbleMucChannel *muc; GabbleCallMucChannel *call; Request *r; if (tp_channel_manager_asv_has_unknown_properties (request_properties, muc_channel_fixed_properties, gabble_media_factory_call_channel_allowed_properties (), error)) return FALSE; initial_audio = tp_asv_get_boolean (request_properties, TP_PROP_CHANNEL_TYPE_CALL_INITIAL_AUDIO, NULL); initial_video = tp_asv_get_boolean (request_properties, TP_PROP_CHANNEL_TYPE_CALL_INITIAL_VIDEO, NULL); if (!initial_audio && !initial_video) { g_set_error (error, TP_ERROR, TP_ERROR_NOT_IMPLEMENTED, "Request didn't set either InitialAudio or InitialVideo"); return FALSE; } ensure_muc_channel (self, priv, handle, &muc, FALSE, FALSE, NULL, NULL, NULL, NULL); call = gabble_muc_channel_get_call (muc); if (call != NULL) { if (require_new) { g_set_error (error, TP_ERROR, TP_ERROR_NOT_AVAILABLE, "There is already a call in this muc"); goto error; } else { tp_channel_manager_emit_request_already_satisfied (self, request_token, TP_EXPORTABLE_CHANNEL (call)); goto out; } } /* FIXME not coping properly with deinitialisation */ r = g_slice_new (Request); r->self = g_object_ref (self); r->token = request_token; gabble_muc_channel_request_call (muc, request_properties, require_new, request_token, call_muc_channel_request_cb, r); out: return TRUE; error: return FALSE; } #endif 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_STREAM_TUBE, handle_stream_tube_channel_request }, { TP_IFACE_CHANNEL_TYPE_DBUS_TUBE, handle_dbus_tube_channel_request }, #ifdef ENABLE_VOIP { TP_IFACE_CHANNEL_TYPE_CALL, handle_call_channel_request }, #endif { NULL } }; static gboolean gabble_muc_factory_request (GabbleMucFactory *self, gpointer request_token, GHashTable *request_properties, gboolean require_new) { GError *error = NULL; TpHandleType handle_type; TpHandle handle; gboolean conference, room; const gchar *channel_type; ChannelTypeHandler *h; handle_type = tp_asv_get_uint32 (request_properties, TP_PROP_CHANNEL_TARGET_HANDLE_TYPE, NULL); channel_type = tp_asv_get_string (request_properties, 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_PROP_CHANNEL_INTERFACE_CONFERENCE_INITIAL_CHANNELS) || g_hash_table_lookup (request_properties, TP_PROP_CHANNEL_INTERFACE_CONFERENCE_INITIAL_INVITEE_HANDLES) || g_hash_table_lookup (request_properties, TP_PROP_CHANNEL_INTERFACE_CONFERENCE_INITIAL_INVITEE_IDS))); room = (handle_type == TP_HANDLE_TYPE_NONE && !tp_strdiff (channel_type, TP_IFACE_CHANNEL_TYPE_TEXT) && g_hash_table_lookup (request_properties, TP_PROP_CHANNEL_INTERFACE_ROOM_ROOM_NAME)); /* the channel must either be a room, or a new conference */ if (handle_type != TP_HANDLE_TYPE_ROOM && !conference && !room) return FALSE; /* validity already checked by TpBaseConnection */ handle = tp_asv_get_uint32 (request_properties, TP_PROP_CHANNEL_TARGET_HANDLE, NULL); g_assert (conference || room || handle != 0); for (h = channel_type_handlers; h->channel_type != NULL; h++) { 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; } return FALSE; } static gboolean gabble_muc_factory_create_channel (TpChannelManager *manager, gpointer request_token, GHashTable *request_properties) { GabbleMucFactory *self = GABBLE_MUC_FACTORY (manager); return gabble_muc_factory_request (self, request_token, request_properties, TRUE); } static gboolean gabble_muc_factory_request_channel (TpChannelManager *manager, gpointer request_token, GHashTable *request_properties) { GabbleMucFactory *self = GABBLE_MUC_FACTORY (manager); return gabble_muc_factory_request (self, request_token, request_properties, FALSE); } static gboolean gabble_muc_factory_ensure_channel (TpChannelManager *manager, gpointer request_token, GHashTable *request_properties) { GabbleMucFactory *self = GABBLE_MUC_FACTORY (manager); return gabble_muc_factory_request (self, request_token, request_properties, FALSE); } #ifdef ENABLE_VOIP gboolean gabble_muc_factory_handle_jingle_session (GabbleMucFactory *self, WockyJingleSession *session) { GabbleMucFactoryPrivate *priv = self->priv; TpHandleRepoIface *room_repo = tp_base_connection_get_handles ( (TpBaseConnection *) priv->conn, TP_HANDLE_TYPE_ROOM); TpHandle room; room = gabble_get_room_handle_from_jid (room_repo, wocky_jingle_session_get_peer_jid (session)); if (room != 0) { GabbleMucChannel *channel; channel = gabble_muc_factory_find_text_channel (self, room); g_assert (GABBLE_IS_MUC_CHANNEL (channel)); if (channel != NULL) return gabble_muc_channel_handle_jingle_session (channel, session); } return FALSE; } #endif static void channel_manager_iface_init (gpointer g_iface, gpointer iface_data) { TpChannelManagerIface *iface = g_iface; iface->foreach_channel = gabble_muc_factory_foreach_channel; iface->type_foreach_channel_class = gabble_muc_factory_type_foreach_channel_class; iface->request_channel = gabble_muc_factory_request_channel; iface->create_channel = gabble_muc_factory_create_channel; iface->ensure_channel = gabble_muc_factory_ensure_channel; } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������telepathy-gabble-0.18.2/src/muc-factory.h�����������������������������������������������������������0000644�0001750�0001750�00000005204�12200204333�020174� 0����������������������������������������������������������������������������������������������������ustar�00smcv����������������������������smcv����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * muc-factory.h - Header for GabbleMucFactory * Copyright (C) 2006 Collabora Ltd. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 __MUC_FACTORY_H__ #define __MUC_FACTORY_H__ #include "config.h" #include <glib-object.h> #include <wocky/wocky.h> #include "bytestream-iface.h" #include "types.h" G_BEGIN_DECLS typedef struct _GabbleMucFactory GabbleMucFactory; typedef struct _GabbleMucFactoryClass GabbleMucFactoryClass; typedef struct _GabbleMucFactoryPrivate GabbleMucFactoryPrivate; struct _GabbleMucFactoryClass { GObjectClass parent_class; }; struct _GabbleMucFactory { GObject parent; GabbleMucFactoryPrivate *priv; }; GType gabble_muc_factory_get_type (void); /* TYPE MACROS */ #define GABBLE_TYPE_MUC_FACTORY \ (gabble_muc_factory_get_type ()) #define GABBLE_MUC_FACTORY(obj) \ (G_TYPE_CHECK_INSTANCE_CAST((obj), GABBLE_TYPE_MUC_FACTORY, \ GabbleMucFactory)) #define GABBLE_MUC_FACTORY_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST((klass), GABBLE_TYPE_MUC_FACTORY, \ GabbleMucFactoryClass)) #define GABBLE_IS_MUC_FACTORY(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE((obj), GABBLE_TYPE_MUC_FACTORY)) #define GABBLE_IS_MUC_FACTORY_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE((klass), GABBLE_TYPE_MUC_FACTORY)) #define GABBLE_MUC_FACTORY_GET_CLASS(obj) \ (G_TYPE_INSTANCE_GET_CLASS ((obj), GABBLE_TYPE_MUC_FACTORY, \ GabbleMucFactoryClass)) GabbleMucChannel *gabble_muc_factory_find_text_channel (GabbleMucFactory *self, TpHandle handle); void gabble_muc_factory_handle_si_stream_request (GabbleMucFactory *self, GabbleBytestreamIface *bytestream, TpHandle room_handle, const gchar *stream_id, WockyStanza *msg); void gabble_muc_factory_broadcast_presence (GabbleMucFactory *self); #ifdef ENABLE_VOIP gboolean gabble_muc_factory_handle_jingle_session (GabbleMucFactory *self, WockyJingleSession *session); #endif G_END_DECLS #endif /* #ifndef __MUC_FACTORY_H__ */ ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������telepathy-gabble-0.18.2/src/muc-channel.c�����������������������������������������������������������0000644�0001750�0001750�00000374735�12227000321�020152� 0����������������������������������������������������������������������������������������������������ustar�00smcv����������������������������smcv����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * gabble-muc-channel.c - Source for GabbleMucChannel * Copyright (C) 2006 Collabora Ltd. * Copyright (C) 2006 Nokia Corporation * @author Ole Andre Vadla Ravnaas <ole.andre.ravnaas@collabora.co.uk> * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 "config.h" #include "muc-channel.h" #include <stdio.h> #include <string.h> #include <wocky/wocky.h> #include <dbus/dbus-glib.h> #include <telepathy-glib/telepathy-glib.h> #include <telepathy-glib/telepathy-glib-dbus.h> #define DEBUG_FLAG GABBLE_DEBUG_MUC #include "connection.h" #include "conn-aliasing.h" #include "conn-util.h" #include "debug.h" #include "disco.h" #include "error.h" #include "message-util.h" #include "room-config.h" #include "namespaces.h" #include "presence.h" #include "util.h" #include "presence-cache.h" #include "gabble-signals-marshal.h" #include "gabble-enumtypes.h" #include "tube-dbus.h" #include "tube-stream.h" #include "private-tubes-factory.h" #include "bytestream-factory.h" #define DEFAULT_JOIN_TIMEOUT 180 #define DEFAULT_LEAVE_TIMEOUT 180 #define MAX_NICK_RETRIES 3 #define PROPS_POLL_INTERVAL_LOW 60 * 5 #define PROPS_POLL_INTERVAL_HIGH 60 static void password_iface_init (gpointer, gpointer); static void subject_iface_init (gpointer, gpointer); #ifdef ENABLE_VOIP static void gabble_muc_channel_start_call_creation (GabbleMucChannel *gmuc, GHashTable *request); static void muc_call_channel_finish_requests (GabbleMucChannel *self, GabbleCallMucChannel *call, GError *error); #endif G_DEFINE_TYPE_WITH_CODE (GabbleMucChannel, gabble_muc_channel, TP_TYPE_BASE_CHANNEL, G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CHANNEL_INTERFACE_GROUP, tp_group_mixin_iface_init); G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CHANNEL_INTERFACE_PASSWORD, password_iface_init); G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CHANNEL_TYPE_TEXT, tp_message_mixin_text_iface_init); G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CHANNEL_INTERFACE_MESSAGES, tp_message_mixin_messages_iface_init); G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CHANNEL_INTERFACE_CHAT_STATE, tp_message_mixin_chat_state_iface_init) G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CHANNEL_INTERFACE_CONFERENCE, NULL); G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CHANNEL_INTERFACE_ROOM, NULL); G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CHANNEL_INTERFACE_ROOM_CONFIG, tp_base_room_config_iface_init); G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CHANNEL_INTERFACE_SUBJECT, subject_iface_init); ) static void gabble_muc_channel_send (GObject *obj, TpMessage *message, TpMessageSendingFlags flags); static gboolean gabble_muc_channel_send_chat_state (GObject *object, TpChannelChatState state, GError **error); static void gabble_muc_channel_close (TpBaseChannel *base); /* signal enum */ enum { READY, JOIN_ERROR, PRE_INVITE, CONTACT_JOIN, PRE_PRESENCE, NEW_TUBE, #ifdef ENABLE_VOIP NEW_CALL, #endif LAST_SIGNAL }; static guint signals[LAST_SIGNAL] = {0}; /* properties */ enum { PROP_STATE = 1, PROP_INITIALLY_REGISTER, PROP_INVITED, PROP_INVITATION_MESSAGE, PROP_SELF_JID, PROP_WOCKY_MUC, PROP_INITIAL_CHANNELS, PROP_INITIAL_INVITEE_HANDLES, PROP_INITIAL_INVITEE_IDS, PROP_ORIGINAL_CHANNELS, PROP_ROOM_NAME, PROP_SERVER, PROP_SUBJECT, PROP_SUBJECT_ACTOR, PROP_SUBJECT_TIMESTAMP, PROP_CAN_SET_SUBJECT, LAST_PROPERTY }; static const gchar *muc_states[] = { "MUC_STATE_CREATED", "MUC_STATE_INITIATED", "MUC_STATE_AUTH", "MUC_STATE_JOINED", "MUC_STATE_ENDED", }; /* private structures */ struct _GabbleMucChannelPrivate { GabbleMucState state; gboolean closing; gboolean autoclose; gboolean initially_register; guint join_timer_id; guint poll_timer_id; guint leave_timer_id; gboolean must_provide_password; DBusGMethodInvocation *password_ctx; const gchar *jid; gboolean requested; guint nick_retry_count; GString *self_jid; WockyMucRole self_role; WockyMucAffiliation self_affil; guint recv_id; TpBaseRoomConfig *room_config; GHashTable *properties_being_updated; /* Room interface */ gchar *room_name; gchar *server; /* Subject interface */ gchar *subject; gchar *subject_actor; gint64 subject_timestamp; gboolean can_set_subject; DBusGMethodInvocation *set_subject_context; gchar *set_subject_stanza_id; gboolean ready; gboolean dispose_has_run; gboolean invited; gchar *invitation_message; WockyMuc *wmuc; /* tube ID => owned GabbleTubeIface */ GHashTable *tubes; #ifdef ENABLE_VOIP /* Current active call */ GabbleCallMucChannel *call; /* All calls, active one + potential ended ones */ GList *calls; /* List of GSimpleAsyncResults for the various request for a call */ GList *call_requests; gboolean call_initiating; #endif GCancellable *requests_cancellable; GPtrArray *initial_channels; GArray *initial_handles; char **initial_ids; gboolean have_received_error_type_wait; }; typedef struct { GabbleMucChannel *channel; TpMessage *message; gchar *token; } _GabbleMUCSendMessageCtx; static GPtrArray * gabble_muc_channel_get_interfaces (TpBaseChannel *base) { GPtrArray *interfaces; interfaces = TP_BASE_CHANNEL_CLASS ( gabble_muc_channel_parent_class)->get_interfaces (base); g_ptr_array_add (interfaces, TP_IFACE_CHANNEL_INTERFACE_GROUP); g_ptr_array_add (interfaces, TP_IFACE_CHANNEL_INTERFACE_PASSWORD); g_ptr_array_add (interfaces, TP_IFACE_CHANNEL_INTERFACE_CHAT_STATE); g_ptr_array_add (interfaces, TP_IFACE_CHANNEL_INTERFACE_MESSAGES); g_ptr_array_add (interfaces, TP_IFACE_CHANNEL_INTERFACE_CONFERENCE); g_ptr_array_add (interfaces, TP_IFACE_CHANNEL_INTERFACE_ROOM); g_ptr_array_add (interfaces, TP_IFACE_CHANNEL_INTERFACE_ROOM_CONFIG); g_ptr_array_add (interfaces, TP_IFACE_CHANNEL_INTERFACE_SUBJECT); return interfaces; } static void gabble_muc_channel_init (GabbleMucChannel *self) { GabbleMucChannelPrivate *priv = G_TYPE_INSTANCE_GET_PRIVATE (self, GABBLE_TYPE_MUC_CHANNEL, GabbleMucChannelPrivate); self->priv = priv; priv->requests_cancellable = g_cancellable_new (); priv->tubes = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, (GDestroyNotify) g_object_unref); } static TpHandle create_room_identity (GabbleMucChannel *) G_GNUC_WARN_UNUSED_RESULT; /* signatures for presence handlers */ static void handle_fill_presence (WockyMuc *muc, WockyStanza *stanza, gpointer user_data); static void handle_renamed (GObject *source, WockyStanza *stanza, guint codes, gpointer data); static void handle_error (GObject *source, WockyStanza *stanza, WockyXmppErrorType errtype, const GError *error, gpointer data); static void handle_join (WockyMuc *muc, WockyStanza *stanza, guint codes, gpointer data); static void handle_parted (GObject *source, WockyStanza *stanza, guint codes, const gchar *actor_jid, const gchar *why, const gchar *msg, gpointer data); static void handle_left (GObject *source, WockyStanza *stanza, guint codes, WockyMucMember *who, const gchar *actor_jid, const gchar *why, const gchar *msg, gpointer data); static void handle_presence (GObject *source, WockyStanza *stanza, guint codes, WockyMucMember *who, gpointer data); static void handle_perms (GObject *source, WockyStanza *stanza, GHashTable *code, const gchar *actor, const gchar *why, gpointer data); /* signatures for message handlers */ static void handle_message (GObject *source, WockyStanza *stanza, WockyMucMsgType type, const gchar *xmpp_id, GDateTime *datetime, WockyMucMember *who, const gchar *text, const gchar *subject, WockyMucMsgState state, gpointer data); static void handle_errmsg (GObject *source, WockyStanza *stanza, WockyMucMsgType type, const gchar *xmpp_id, GDateTime *datetime, WockyMucMember *who, const gchar *text, WockyXmppErrorType etype, const GError *error, gpointer data); /* Signatures for some other stuff. */ static void _gabble_muc_channel_handle_subject (GabbleMucChannel *chan, TpHandleType handle_type, TpHandle sender, GDateTime *datetime, const gchar *subject, WockyStanza *msg, const GError *error); static void _gabble_muc_channel_receive (GabbleMucChannel *chan, TpChannelTextMessageType msg_type, TpHandleType handle_type, TpHandle sender, GDateTime *datetime, const gchar *id, const gchar *text, WockyStanza *msg, const GError *send_error, TpDeliveryStatus delivery_status); static void gabble_muc_channel_constructed (GObject *obj) { GabbleMucChannel *self = GABBLE_MUC_CHANNEL (obj); GabbleMucChannelPrivate *priv = self->priv; TpBaseChannel *base = TP_BASE_CHANNEL (self); TpBaseConnection *base_conn = tp_base_channel_get_connection (base); TpHandleRepoIface *room_handles, *contact_handles; TpHandle target, initiator, self_handle; gchar *tmp; TpChannelTextMessageType types[] = { TP_CHANNEL_TEXT_MESSAGE_TYPE_NORMAL, TP_CHANNEL_TEXT_MESSAGE_TYPE_ACTION, TP_CHANNEL_TEXT_MESSAGE_TYPE_NOTICE, }; const gchar * supported_content_types[] = { "text/plain", NULL }; void (*chain_up) (GObject *) = ((GObjectClass *) gabble_muc_channel_parent_class)->constructed; gboolean ok; if (chain_up != NULL) chain_up (obj); room_handles = tp_base_connection_get_handles (base_conn, TP_HANDLE_TYPE_ROOM); contact_handles = tp_base_connection_get_handles (base_conn, TP_HANDLE_TYPE_CONTACT); /* get, and sanity-check, the room's jid */ target = tp_base_channel_get_target_handle (base); priv->jid = tp_handle_inspect (room_handles, target); g_assert (priv->jid != NULL && strchr (priv->jid, '/') == NULL); /* The factory should have given us an initiator */ initiator = tp_base_channel_get_initiator (base); g_assert (initiator != 0); /* create our own identity in the room */ self_handle = create_room_identity (self); /* this causes us to have one ref to the self handle which is unreffed * at the end of this function */ /* initialize our own role and affiliation */ priv->self_role = WOCKY_MUC_ROLE_NONE; priv->self_affil = WOCKY_MUC_AFFILIATION_NONE; /* initialise the wocky muc object */ { GabbleConnection *conn = GABBLE_CONNECTION (base_conn); WockyPorter *porter = gabble_connection_dup_porter (conn); const gchar *room_jid = tp_handle_inspect (contact_handles, self_handle); gchar *user_jid = gabble_connection_get_full_jid (conn); WockyMuc *wmuc = g_object_new (WOCKY_TYPE_MUC, "porter", porter, "jid", room_jid, /* room@service.name/nick */ "user", user_jid, /* user@doma.in/resource */ NULL); /* various presence handlers */ g_signal_connect (wmuc, "nick-change", (GCallback) handle_renamed, self); g_signal_connect (wmuc, "presence", (GCallback) handle_presence, self); g_signal_connect (wmuc, "joined", (GCallback) handle_join, self); g_signal_connect (wmuc, "permissions", (GCallback) handle_perms, self); g_signal_connect (wmuc, "parted", (GCallback) handle_parted, self); g_signal_connect (wmuc, "left", (GCallback) handle_left, self); g_signal_connect (wmuc, "error", (GCallback) handle_error, self); g_signal_connect (wmuc, "fill-presence", G_CALLBACK (handle_fill_presence), self); /* message handler(s) (just one needed so far) */ g_signal_connect (wmuc, "message", (GCallback) handle_message, self); g_signal_connect (wmuc, "message-error",(GCallback) handle_errmsg, self); priv->wmuc = wmuc; g_free (user_jid); g_object_unref (porter); } /* register object on the bus */ if (priv->initially_register) tp_base_channel_register (base); /* initialize group mixin */ tp_group_mixin_init (obj, G_STRUCT_OFFSET (GabbleMucChannel, group), contact_handles, self_handle); /* set initial group flags */ tp_group_mixin_change_flags (obj, TP_CHANNEL_GROUP_FLAG_PROPERTIES | TP_CHANNEL_GROUP_FLAG_CHANNEL_SPECIFIC_HANDLES | TP_CHANNEL_GROUP_FLAG_HANDLE_OWNERS_NOT_AVAILABLE | TP_CHANNEL_GROUP_FLAG_CAN_ADD, 0); /* initialize message mixin */ tp_message_mixin_init (obj, G_STRUCT_OFFSET (GabbleMucChannel, message_mixin), base_conn); tp_message_mixin_implement_sending (obj, gabble_muc_channel_send, G_N_ELEMENTS (types), types, 0, TP_DELIVERY_REPORTING_SUPPORT_FLAG_RECEIVE_FAILURES | TP_DELIVERY_REPORTING_SUPPORT_FLAG_RECEIVE_SUCCESSES, supported_content_types); tp_message_mixin_implement_send_chat_state (obj, gabble_muc_channel_send_chat_state); tp_group_mixin_add_handle_owner (obj, self_handle, tp_base_connection_get_self_handle (base_conn)); /* Room interface */ g_object_get (self, "target-id", &tmp, NULL); if (priv->room_name != NULL) ok = wocky_decode_jid (tmp, NULL, &(priv->server), NULL); else ok = wocky_decode_jid (tmp, &(priv->room_name), &(priv->server), NULL); g_free (tmp); /* Asserting here is fine because the target ID has already been * checked so we know it's valid. */ g_assert (ok); priv->subject = NULL; priv->subject_actor = NULL; priv->subject_timestamp = G_MAXINT64; /* fd.o#13157: The subject is currently assumed to be modifiable by everyone * in the room (role >= VISITOR). When that bug is fixed, it will be: */ /* Modifiable via special <message/>s, if the user's role is high enough; * "high enough" is defined by the muc#roominfo_changesubject and * muc#roomconfig_changesubject settings. */ priv->can_set_subject = TRUE; { TpBaseRoomConfigProperty mutable_properties[] = { TP_BASE_ROOM_CONFIG_ANONYMOUS, TP_BASE_ROOM_CONFIG_INVITE_ONLY, TP_BASE_ROOM_CONFIG_MODERATED, TP_BASE_ROOM_CONFIG_TITLE, TP_BASE_ROOM_CONFIG_PERSISTENT, TP_BASE_ROOM_CONFIG_PRIVATE, TP_BASE_ROOM_CONFIG_PASSWORD_PROTECTED, TP_BASE_ROOM_CONFIG_PASSWORD, }; guint i; priv->room_config = (TpBaseRoomConfig *) gabble_room_config_new ((TpBaseChannel *) self); for (i = 0; i < G_N_ELEMENTS (mutable_properties); i++) tp_base_room_config_set_property_mutable (priv->room_config, mutable_properties[i], TRUE); /* Just to get those mutable properties out there. */ tp_base_room_config_emit_properties_changed (priv->room_config); } if (priv->invited) { /* invited: add ourself to local pending and the inviter to members */ TpIntset *members = tp_intset_new_containing (initiator); TpIntset *pending = tp_intset_new_containing (self_handle); tp_group_mixin_change_members (obj, priv->invitation_message, members, NULL, pending, NULL, initiator, TP_CHANNEL_GROUP_CHANGE_REASON_INVITED); tp_intset_destroy (members); tp_intset_destroy (pending); /* we've dealt with it (and copied it elsewhere), so there's no point * in keeping it */ g_free (priv->invitation_message); priv->invitation_message = NULL; /* mark channel ready so NewChannel is emitted immediately */ priv->ready = TRUE; } else { /* not invited: add ourselves to members (and hence join immediately) */ GError *error = NULL; GArray *members = g_array_sized_new (FALSE, FALSE, sizeof (TpHandle), 1); g_assert (initiator == tp_base_connection_get_self_handle (base_conn)); g_assert (priv->invitation_message == NULL); g_array_append_val (members, self_handle); tp_group_mixin_add_members (obj, members, "", &error); g_assert (error == NULL); g_array_unref (members); } } typedef struct { const gchar *var; const gchar *config_property_name; gboolean value; } FeatureMapping; static FeatureMapping * lookup_feature (const gchar *var) { static FeatureMapping features[] = { { "muc_nonanonymous", "anonymous", FALSE }, { "muc_semianonymous", "anonymous", TRUE }, { "muc_anonymous", "anonymous", TRUE }, { "muc_open", "invite-only", FALSE }, { "muc_membersonly", "invite-only", TRUE }, { "muc_unmoderated", "moderated", FALSE }, { "muc_moderated", "moderated", TRUE }, { "muc_unsecure", "password-protected", FALSE }, { "muc_unsecured", "password-protected", FALSE }, { "muc_passwordprotected", "password-protected", TRUE }, { "muc_temporary", "persistent", FALSE }, { "muc_persistent", "persistent", TRUE }, { "muc_public", "private", FALSE }, { "muc_hidden", "private", TRUE }, /* The MUC namespace is included as a feature in disco results. We ignore * it here. */ { NS_MUC, NULL, FALSE }, { NULL } }; FeatureMapping *f; for (f = features; f->var != NULL; f++) if (strcmp (var, f->var) == 0) return f; return NULL; } static const gchar * map_feature ( WockyNode *feature, GValue *value) { const gchar *var = wocky_node_get_attribute (feature, "var"); FeatureMapping *f; if (var == NULL) return NULL; f = lookup_feature (var); if (f == NULL) { DEBUG ("unhandled feature '%s'", var); return NULL; } if (f->config_property_name != NULL) { g_value_init (value, G_TYPE_BOOLEAN); g_value_set_boolean (value, f->value); } return f->config_property_name; } static const gchar * handle_form ( WockyNode *x, GValue *value) { WockyNodeIter j; WockyNode *field; wocky_node_iter_init (&j, x, "field", NULL); while (wocky_node_iter_next (&j, &field)) { const gchar *var = wocky_node_get_attribute (field, "var"); const gchar *description; if (tp_strdiff (var, "muc#roominfo_description")) continue; description = wocky_node_get_content_from_child (field, "value"); g_value_init (value, G_TYPE_STRING); g_value_set_string (value, description != NULL ? description : ""); return "description"; } return NULL; } static void properties_disco_cb (GabbleDisco *disco, GabbleDiscoRequest *request, const gchar *jid, const gchar *node, WockyNode *query_result, GError *error, gpointer user_data) { GabbleMucChannel *chan = user_data; GabbleMucChannelPrivate *priv = chan->priv; WockyNode *lm_node; WockyNodeIter i; WockyNode *child; g_assert (GABBLE_IS_MUC_CHANNEL (chan)); if (error) { DEBUG ("got error %s", error->message); return; } /* * Update room definition. */ /* ROOM_PROP_NAME */ lm_node = wocky_node_get_child (query_result, "identity"); if (lm_node) { const gchar *category, *type, *name; category = wocky_node_get_attribute (lm_node, "category"); type = wocky_node_get_attribute (lm_node, "type"); name = wocky_node_get_attribute (lm_node, "name"); if (!tp_strdiff (category, "conference") && !tp_strdiff (type, "text") && name != NULL) { g_object_set (priv->room_config, "title", name, NULL); } } wocky_node_iter_init (&i, query_result, NULL, NULL); while (wocky_node_iter_next (&i, &child)) { const gchar *config_property_name = NULL; GValue val = { 0, }; if (strcmp (child->name, "feature") == 0) { config_property_name = map_feature (child, &val); } else if (strcmp (child->name, "x") == 0 && wocky_node_has_ns (child, NS_X_DATA)) { config_property_name = handle_form (child, &val); } if (config_property_name != NULL) { g_object_set_property ((GObject *) priv->room_config, config_property_name, &val); g_value_unset (&val); } } /* This could be the first time we've fetched the room properties, or it * could be a later time; either way, this method does the right thing. */ tp_base_room_config_set_retrieved (priv->room_config); } static void room_properties_update (GabbleMucChannel *chan) { GabbleMucChannelPrivate *priv; TpBaseChannel *base; GabbleConnection *conn; GError *error = NULL; g_assert (GABBLE_IS_MUC_CHANNEL (chan)); priv = chan->priv; base = TP_BASE_CHANNEL (chan); conn = GABBLE_CONNECTION (tp_base_channel_get_connection (base)); if (gabble_disco_request (conn->disco, GABBLE_DISCO_TYPE_INFO, priv->jid, NULL, properties_disco_cb, chan, G_OBJECT (chan), &error) == NULL) { DEBUG ("disco query failed: '%s'", error->message); g_error_free (error); } } static TpHandle create_room_identity (GabbleMucChannel *chan) { TpBaseChannel *base = TP_BASE_CHANNEL (chan); GabbleMucChannelPrivate *priv = chan->priv; TpBaseConnection *conn = tp_base_channel_get_connection (base); TpHandleRepoIface *contact_repo; gchar *alias = NULL; GabbleConnectionAliasSource source; contact_repo = tp_base_connection_get_handles (conn, TP_HANDLE_TYPE_CONTACT); g_assert (priv->self_jid == NULL); source = _gabble_connection_get_cached_alias (GABBLE_CONNECTION (conn), tp_base_connection_get_self_handle (conn), &alias); g_assert (alias != NULL); if (source == GABBLE_CONNECTION_ALIAS_FROM_JID) { /* If our 'alias' is, in fact, our JID, we'll just use the local part as * our MUC resource. */ gchar *local_part; g_assert (wocky_decode_jid (alias, &local_part, NULL, NULL)); g_assert (local_part != NULL); g_free (alias); alias = local_part; } priv->self_jid = g_string_new (priv->jid); g_string_append_c (priv->self_jid, '/'); g_string_append (priv->self_jid, alias); g_free (alias); return tp_handle_ensure (contact_repo, priv->self_jid->str, GUINT_TO_POINTER (GABBLE_JID_ROOM_MEMBER), NULL); } static void send_join_request (GabbleMucChannel *gmuc) { GabbleMucChannelPrivate *priv = gmuc->priv; wocky_muc_join (priv->wmuc, NULL); } static void tube_pre_presence (GabbleMucChannel *gmuc, WockyStanza *stanza) { GabbleMucChannelPrivate *priv = gmuc->priv; TpBaseConnection *conn = tp_base_channel_get_connection ( TP_BASE_CHANNEL (gmuc)); WockyNode *tubes_node; GHashTableIter iter; gpointer value; tubes_node = wocky_node_add_child_with_content_ns ( wocky_stanza_get_top_node (stanza), "tubes", NULL, NS_TUBES); g_hash_table_iter_init (&iter, priv->tubes); while (g_hash_table_iter_next (&iter, NULL, &value)) { GabbleTubeIface *tube = value; TpTubeChannelState state; WockyNode *tube_node; TpTubeType type; TpHandle initiator; g_object_get (tube, "state", &state, "type", &type, "initiator-handle", &initiator, NULL); if (state != TP_TUBE_CHANNEL_STATE_OPEN) continue; if (type == TP_TUBE_TYPE_STREAM && initiator != TP_GROUP_MIXIN (gmuc)->self_handle) /* We only announce stream tubes we initiated */ continue; tube_node = wocky_node_add_child_with_content (tubes_node, "tube", NULL); gabble_tube_iface_publish_in_node (tube, conn, tube_node); } } static gboolean timeout_leave (gpointer data) { GabbleMucChannel *chan = data; DEBUG ("leave timed out (we never got our unavailable presence echoed " "back to us by the conf server), closing channel now"); tp_base_channel_destroyed (TP_BASE_CHANNEL (chan)); return FALSE; } static void send_leave_message (GabbleMucChannel *gmuc, const gchar *reason) { GabbleMucChannelPrivate *priv = gmuc->priv; TpBaseChannel *base = TP_BASE_CHANNEL (gmuc); WockyStanza *stanza = wocky_muc_create_presence (priv->wmuc, WOCKY_STANZA_SUB_TYPE_UNAVAILABLE, reason); tube_pre_presence (gmuc, stanza); g_signal_emit (gmuc, signals[PRE_PRESENCE], 0, stanza); _gabble_connection_send ( GABBLE_CONNECTION (tp_base_channel_get_connection (base)), stanza, NULL); g_object_unref (stanza); priv->leave_timer_id = g_timeout_add_seconds (DEFAULT_LEAVE_TIMEOUT, timeout_leave, gmuc); } static void gabble_muc_channel_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { GabbleMucChannel *chan = GABBLE_MUC_CHANNEL (object); GabbleMucChannelPrivate *priv = chan->priv; switch (property_id) { case PROP_STATE: g_value_set_uint (value, priv->state); break; case PROP_INITIALLY_REGISTER: g_value_set_boolean (value, priv->initially_register); break; case PROP_SELF_JID: g_value_set_string (value, priv->self_jid->str); break; case PROP_WOCKY_MUC: g_value_set_object (value, priv->wmuc); break; case PROP_INVITATION_MESSAGE: g_value_set_string (value, ""); break; case PROP_INITIAL_CHANNELS: g_value_set_boxed (value, priv->initial_channels); break; case PROP_INITIAL_INVITEE_HANDLES: g_value_set_boxed (value, priv->initial_handles); break; case PROP_INITIAL_INVITEE_IDS: g_value_set_boxed (value, priv->initial_ids); break; case PROP_ORIGINAL_CHANNELS: /* We don't have a useful value for this - we don't necessarily know * which chatroom member is which global handle, and the main purpose * of OriginalChannels is to be able to split off merged channels, * which we can't do anyway in XMPP. */ g_value_take_boxed (value, g_hash_table_new (NULL, NULL)); break; case PROP_ROOM_NAME: g_value_set_string (value, priv->room_name); break; case PROP_SERVER: g_value_set_string (value, priv->server); break; case PROP_SUBJECT: g_value_set_string (value, priv->subject); break; case PROP_SUBJECT_ACTOR: g_value_set_string (value, priv->subject_actor); break; case PROP_SUBJECT_TIMESTAMP: g_value_set_int64 (value, priv->subject_timestamp); break; case PROP_CAN_SET_SUBJECT: g_value_set_boolean (value, priv->can_set_subject); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void channel_state_changed (GabbleMucChannel *chan, GabbleMucState prev_state, GabbleMucState new_state); static void gabble_muc_channel_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { GabbleMucChannel *chan = GABBLE_MUC_CHANNEL (object); GabbleMucChannelPrivate *priv = chan->priv; GabbleMucState prev_state; switch (property_id) { case PROP_STATE: prev_state = priv->state; priv->state = g_value_get_uint (value); if (priv->state != prev_state) channel_state_changed (chan, prev_state, priv->state); break; case PROP_INITIALLY_REGISTER: priv->initially_register = g_value_get_boolean (value); break; case PROP_INVITED: priv->invited = g_value_get_boolean (value); break; case PROP_INVITATION_MESSAGE: g_assert (priv->invitation_message == NULL); priv->invitation_message = g_value_dup_string (value); break; case PROP_INITIAL_CHANNELS: priv->initial_channels = g_value_dup_boxed (value); g_assert (priv->initial_channels != NULL); break; case PROP_INITIAL_INVITEE_HANDLES: priv->initial_handles = g_value_dup_boxed (value); g_assert (priv->initial_handles != NULL); break; case PROP_INITIAL_INVITEE_IDS: priv->initial_ids = g_value_dup_boxed (value); break; case PROP_ROOM_NAME: priv->room_name = g_value_dup_string (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void gabble_muc_channel_dispose (GObject *object); static void gabble_muc_channel_finalize (GObject *object); static gboolean gabble_muc_channel_add_member (GObject *obj, TpHandle handle, const gchar *message, GError **error); static gboolean gabble_muc_channel_remove_member (GObject *obj, TpHandle handle, const gchar *message, GError **error); static void gabble_muc_channel_fill_immutable_properties ( TpBaseChannel *chan, GHashTable *properties) { TP_BASE_CHANNEL_CLASS (gabble_muc_channel_parent_class)->fill_immutable_properties ( chan, properties); tp_dbus_properties_mixin_fill_properties_hash ( G_OBJECT (chan), properties, 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_IFACE_CHANNEL_INTERFACE_MESSAGES, "MessagePartSupportFlags", TP_IFACE_CHANNEL_INTERFACE_MESSAGES, "DeliveryReportingSupport", TP_IFACE_CHANNEL_INTERFACE_MESSAGES, "SupportedContentTypes", TP_IFACE_CHANNEL_INTERFACE_MESSAGES, "MessageTypes", TP_IFACE_CHANNEL_INTERFACE_ROOM, "RoomName", TP_IFACE_CHANNEL_INTERFACE_ROOM, "Server", NULL); } static void gabble_muc_channel_class_init (GabbleMucChannelClass *gabble_muc_channel_class) { static TpDBusPropertiesMixinPropImpl conference_props[] = { { "Channels", "initial-channels", NULL, }, { "InitialChannels", "initial-channels", NULL }, { "InitialInviteeHandles", "initial-invitee-handles", NULL }, { "InitialInviteeIDs", "initial-invitee-ids", NULL }, { "InvitationMessage", "invitation-message", NULL }, { "OriginalChannels", "original-channels", NULL }, { NULL } }; static TpDBusPropertiesMixinPropImpl room_props[] = { { "RoomName", "room-name", NULL, }, { "Server", "server", NULL }, { NULL } }; static TpDBusPropertiesMixinPropImpl subject_props[] = { { "Subject", "subject", NULL }, { "Actor", "subject-actor", NULL }, { "Timestamp", "subject-timestamp", NULL }, { "CanSet", "can-set-subject", NULL }, { NULL } }; static TpDBusPropertiesMixinIfaceImpl prop_interfaces[] = { { TP_IFACE_CHANNEL_INTERFACE_CONFERENCE, tp_dbus_properties_mixin_getter_gobject_properties, NULL, conference_props, }, { TP_IFACE_CHANNEL_INTERFACE_ROOM, tp_dbus_properties_mixin_getter_gobject_properties, NULL, room_props, }, { TP_IFACE_CHANNEL_INTERFACE_SUBJECT, tp_dbus_properties_mixin_getter_gobject_properties, NULL, subject_props, }, { NULL } }; GObjectClass *object_class = G_OBJECT_CLASS (gabble_muc_channel_class); TpBaseChannelClass *base_class = TP_BASE_CHANNEL_CLASS (object_class); GParamSpec *param_spec; g_type_class_add_private (gabble_muc_channel_class, sizeof (GabbleMucChannelPrivate)); object_class->constructed = gabble_muc_channel_constructed; object_class->get_property = gabble_muc_channel_get_property; object_class->set_property = gabble_muc_channel_set_property; object_class->dispose = gabble_muc_channel_dispose; object_class->finalize = gabble_muc_channel_finalize; base_class->channel_type = TP_IFACE_CHANNEL_TYPE_TEXT; base_class->target_handle_type = TP_HANDLE_TYPE_ROOM; base_class->get_interfaces = gabble_muc_channel_get_interfaces; base_class->fill_immutable_properties = gabble_muc_channel_fill_immutable_properties; base_class->close = gabble_muc_channel_close; param_spec = g_param_spec_uint ("state", "Channel state", "The current state that the channel is in.", 0, G_MAXUINT32, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, PROP_STATE, param_spec); param_spec = g_param_spec_boolean ("initially-register", "Initially register", "whether to register the channel on the bus on creation", TRUE, G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, PROP_INITIALLY_REGISTER, param_spec); param_spec = g_param_spec_boolean ("invited", "Invited?", "Whether the user has been invited to the channel.", FALSE, G_PARAM_CONSTRUCT_ONLY | G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, PROP_INVITED, param_spec); param_spec = g_param_spec_string ("invitation-message", "Invitation message", "The message we were sent when invited; NULL if not invited or if " "already processed", NULL, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, PROP_INVITATION_MESSAGE, param_spec); param_spec = g_param_spec_string ("self-jid", "Our self JID", "Our self muc jid in this room", NULL, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, PROP_SELF_JID, param_spec); param_spec = g_param_spec_object ("wocky-muc", "Wocky MUC Object", "The backend (Wocky) MUC instance", WOCKY_TYPE_MUC, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, PROP_WOCKY_MUC, param_spec); param_spec = g_param_spec_boxed ("initial-channels", "Initial Channels", "The initial channels offered with this Conference", TP_ARRAY_TYPE_OBJECT_PATH_LIST, G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, PROP_INITIAL_CHANNELS, param_spec); param_spec = g_param_spec_boxed ("initial-invitee-handles", "Initial Invitee Handles", "The handles of the Conference's initial invitees", DBUS_TYPE_G_UINT_ARRAY, G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, PROP_INITIAL_INVITEE_HANDLES, param_spec); param_spec = g_param_spec_boxed ("initial-invitee-ids", "Initial Invitee IDs", "The identifiers of the Conference's initial invitees", G_TYPE_STRV, G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, PROP_INITIAL_INVITEE_IDS, param_spec); param_spec = g_param_spec_boxed ("original-channels", "OriginalChannels", "Map from channel-specific handles to originally-offered channels", TP_HASH_TYPE_CHANNEL_ORIGINATOR_MAP, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, PROP_ORIGINAL_CHANNELS, param_spec); param_spec = g_param_spec_string ("room-name", "RoomName", "The human-readable identifier of a chat room.", "", G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, PROP_ROOM_NAME, param_spec); param_spec = g_param_spec_string ("server", "Server", "the DNS name of the server hosting this channel", "", G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, PROP_SERVER, param_spec); param_spec = g_param_spec_string ("subject", "Subject.Subject", "The subject of the room", NULL, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, PROP_SUBJECT, param_spec); param_spec = g_param_spec_string ("subject-actor", "Subject.Actor", "The JID of the contact who last changed the subject", NULL, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, PROP_SUBJECT_ACTOR, param_spec); param_spec = g_param_spec_int64 ("subject-timestamp", "Subject.Timestamp", "The UNIX timestamp at which the subject was last changed", G_MININT64, G_MAXINT64, 0, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, PROP_SUBJECT_TIMESTAMP, param_spec); param_spec = g_param_spec_boolean ("can-set-subject", "Subject.CanSet", "Whether we believe we can set the subject", TRUE, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, PROP_CAN_SET_SUBJECT, param_spec); signals[READY] = g_signal_new ("ready", G_OBJECT_CLASS_TYPE (gabble_muc_channel_class), G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED, 0, NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); signals[JOIN_ERROR] = g_signal_new ("join-error", G_OBJECT_CLASS_TYPE (gabble_muc_channel_class), G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED, 0, NULL, NULL, g_cclosure_marshal_VOID__POINTER, G_TYPE_NONE, 1, G_TYPE_POINTER); signals[PRE_INVITE] = g_signal_new ("pre-invite", G_OBJECT_CLASS_TYPE (gabble_muc_channel_class), G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED, 0, NULL, NULL, g_cclosure_marshal_VOID__STRING, G_TYPE_NONE, 1, G_TYPE_STRING); signals[CONTACT_JOIN] = g_signal_new ("contact-join", G_OBJECT_CLASS_TYPE (gabble_muc_channel_class), G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED, 0, NULL, NULL, g_cclosure_marshal_VOID__UINT, G_TYPE_NONE, 1, G_TYPE_UINT); signals[PRE_PRESENCE] = g_signal_new ("pre-presence", G_OBJECT_CLASS_TYPE (gabble_muc_channel_class), G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED, 0, NULL, NULL, g_cclosure_marshal_VOID__POINTER, G_TYPE_NONE, 1, G_TYPE_POINTER); signals[NEW_TUBE] = g_signal_new ("new-tube", G_OBJECT_CLASS_TYPE (gabble_muc_channel_class), G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED, 0, NULL, NULL, g_cclosure_marshal_VOID__OBJECT, /* this should be GABBLE_TYPE_TUBE_IFACE but GObject * wants a value type, not an interface. */ G_TYPE_NONE, 1, TP_TYPE_BASE_CHANNEL); #ifdef ENABLE_VOIP signals[NEW_CALL] = g_signal_new ("new-call", G_OBJECT_CLASS_TYPE (gabble_muc_channel_class), G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED, 0, NULL, NULL, gabble_marshal_VOID__OBJECT_POINTER, G_TYPE_NONE, 2, GABBLE_TYPE_CALL_MUC_CHANNEL, G_TYPE_POINTER); #endif gabble_muc_channel_class->dbus_props_class.interfaces = prop_interfaces; tp_dbus_properties_mixin_class_init (object_class, G_STRUCT_OFFSET (GabbleMucChannelClass, dbus_props_class)); tp_message_mixin_init_dbus_properties (object_class); tp_base_room_config_register_class (base_class); tp_group_mixin_class_init (object_class, G_STRUCT_OFFSET (GabbleMucChannelClass, group_class), gabble_muc_channel_add_member, gabble_muc_channel_remove_member); tp_group_mixin_init_dbus_properties (object_class); tp_group_mixin_class_allow_self_removal (object_class); } static void clear_join_timer (GabbleMucChannel *chan); static void clear_poll_timer (GabbleMucChannel *chan); static void clear_leave_timer (GabbleMucChannel *chan); void gabble_muc_channel_dispose (GObject *object) { GabbleMucChannel *self = GABBLE_MUC_CHANNEL (object); GabbleMucChannelPrivate *priv = self->priv; if (priv->dispose_has_run) return; DEBUG ("called"); priv->dispose_has_run = TRUE; clear_join_timer (self); clear_poll_timer (self); clear_leave_timer (self); tp_clear_object (&priv->wmuc); tp_clear_object (&priv->requests_cancellable); tp_clear_object (&priv->room_config); tp_clear_pointer (&priv->tubes, g_hash_table_unref); if (G_OBJECT_CLASS (gabble_muc_channel_parent_class)->dispose) G_OBJECT_CLASS (gabble_muc_channel_parent_class)->dispose (object); } void gabble_muc_channel_finalize (GObject *object) { GabbleMucChannel *self = GABBLE_MUC_CHANNEL (object); GabbleMucChannelPrivate *priv = self->priv; DEBUG ("called"); /* free any data held directly by the object here */ if (priv->self_jid) { g_string_free (priv->self_jid, TRUE); } if (priv->initial_channels != NULL) { g_boxed_free (TP_ARRAY_TYPE_OBJECT_PATH_LIST, priv->initial_channels); priv->initial_channels = NULL; } if (priv->initial_handles != NULL) { g_boxed_free (DBUS_TYPE_G_UINT_ARRAY, priv->initial_handles); priv->initial_handles = NULL; } if (priv->initial_ids != NULL) { g_boxed_free (G_TYPE_STRV, priv->initial_ids); priv->initial_ids = NULL; } g_free (priv->room_name); g_free (priv->server); g_free (priv->subject); g_free (priv->subject_actor); tp_group_mixin_finalize (object); tp_message_mixin_finalize (object); G_OBJECT_CLASS (gabble_muc_channel_parent_class)->finalize (object); } static void clear_join_timer (GabbleMucChannel *chan) { GabbleMucChannelPrivate *priv = chan->priv; if (priv->join_timer_id != 0) { g_source_remove (priv->join_timer_id); priv->join_timer_id = 0; } } static void clear_poll_timer (GabbleMucChannel *chan) { GabbleMucChannelPrivate *priv = chan->priv; if (priv->poll_timer_id != 0) { g_source_remove (priv->poll_timer_id); priv->poll_timer_id = 0; } } static void clear_leave_timer (GabbleMucChannel *chan) { GabbleMucChannelPrivate *priv = chan->priv; if (priv->leave_timer_id != 0) { g_source_remove (priv->leave_timer_id); priv->leave_timer_id = 0; } } static void change_must_provide_password ( GabbleMucChannel *chan, gboolean must_provide_password) { GabbleMucChannelPrivate *priv; TpChannelPasswordFlags added, removed; g_assert (GABBLE_IS_MUC_CHANNEL (chan)); priv = chan->priv; if (priv->must_provide_password == !!must_provide_password) return; priv->must_provide_password = !!must_provide_password; if (must_provide_password) { added = TP_CHANNEL_PASSWORD_FLAG_PROVIDE; removed = 0; } else { added = 0; removed = TP_CHANNEL_PASSWORD_FLAG_PROVIDE; } DEBUG ("emitting password flags changed, added 0x%X, removed 0x%X", added, removed); tp_svc_channel_interface_password_emit_password_flags_changed ( chan, added, removed); } static void provide_password_return_if_pending (GabbleMucChannel *chan, gboolean success) { GabbleMucChannelPrivate *priv = chan->priv; if (priv->password_ctx) { dbus_g_method_return (priv->password_ctx, success); priv->password_ctx = NULL; } if (success) { change_must_provide_password (chan, FALSE); } } static void close_channel (GabbleMucChannel *chan, const gchar *reason, gboolean inform_muc, TpHandle actor, guint reason_code); static gboolean timeout_join (gpointer data) { GabbleMucChannel *chan = data; DEBUG ("join timed out, closing channel"); provide_password_return_if_pending (chan, FALSE); close_channel (chan, NULL, FALSE, 0, 0); return FALSE; } static gboolean timeout_poll (gpointer data) { GabbleMucChannel *chan = data; DEBUG ("polling for room properties"); room_properties_update (chan); return TRUE; } static void channel_state_changed (GabbleMucChannel *chan, GabbleMucState prev_state, GabbleMucState new_state) { GabbleMucChannelPrivate *priv = chan->priv; TpBaseChannel *base = TP_BASE_CHANNEL (chan); DEBUG ("state changed from %s to %s", muc_states[prev_state], muc_states[new_state]); if (new_state == MUC_STATE_INITIATED) { priv->join_timer_id = g_timeout_add_seconds (DEFAULT_JOIN_TIMEOUT, timeout_join, chan); } else if (new_state == MUC_STATE_JOINED) { gboolean low_bandwidth; gint interval; provide_password_return_if_pending (chan, TRUE); clear_join_timer (chan); g_object_get (GABBLE_CONNECTION (tp_base_channel_get_connection (base)), "low-bandwidth", &low_bandwidth, NULL); if (low_bandwidth) interval = PROPS_POLL_INTERVAL_LOW; else interval = PROPS_POLL_INTERVAL_HIGH; priv->poll_timer_id = g_timeout_add_seconds (interval, timeout_poll, chan); } else if (new_state == MUC_STATE_ENDED) { clear_poll_timer (chan); } if (new_state == MUC_STATE_JOINED || new_state == MUC_STATE_AUTH) { if (!priv->ready) { g_signal_emit (chan, signals[READY], 0); priv->ready = TRUE; } } } static void return_from_set_subject ( GabbleMucChannel *self, const GError *error) { GabbleMucChannelPrivate *priv = self->priv; if (error == NULL) tp_svc_channel_interface_subject_return_from_set_subject ( priv->set_subject_context); else dbus_g_method_return_error (priv->set_subject_context, error); priv->set_subject_context = NULL; tp_clear_pointer (&priv->set_subject_stanza_id, g_free); } static void close_channel (GabbleMucChannel *chan, const gchar *reason, gboolean inform_muc, TpHandle actor, guint reason_code) { TpBaseChannel *base = TP_BASE_CHANNEL (chan); GabbleMucChannelPrivate *priv = chan->priv; GabbleConnection *conn = GABBLE_CONNECTION ( tp_base_channel_get_connection (base)); TpIntset *set; GArray *handles; GError error = { TP_ERROR, TP_ERROR_CANCELLED, "Muc channel closed below us" }; if (tp_base_channel_is_destroyed (base)) return; /* if priv->closing is TRUE, we're waiting for the MUC to echo our * presence. however, if we're being asked to close again, but this * time without letting the muc know, let's actually close. if we * don't then the channel won't disappear from the bus properly. */ if (priv->closing && !inform_muc) { clear_leave_timer (chan); tp_base_channel_destroyed (base); return; } /* If inform_muc is TRUE it means that we're closing the channel * gracefully and we don't mind if the channel doesn't actually * close behind the scenes if a tube/call is still open. Every call * to this function has inform_muc=FALSE, except for Channel.Close() * and RemoveMembers(self_handle) */ if (inform_muc && !gabble_muc_channel_can_be_closed (chan)) { priv->autoclose = TRUE; tp_base_channel_disappear (base); return; } DEBUG ("Closing"); /* Ensure we stay alive even while telling everyone else to abandon us. */ g_object_ref (chan); g_hash_table_remove_all (priv->tubes); #ifdef ENABLE_VOIP muc_call_channel_finish_requests (chan, NULL, &error); #endif g_cancellable_cancel (priv->requests_cancellable); #ifdef ENABLE_VOIP while (priv->calls != NULL) tp_base_channel_close (TP_BASE_CHANNEL (priv->calls->data)); #endif set = tp_intset_new_containing (TP_GROUP_MIXIN (chan)->self_handle); tp_group_mixin_change_members ((GObject *) chan, reason, NULL, set, NULL, NULL, actor, reason_code); tp_intset_destroy (set); /* If we're currently in the MUC, tell it we're leaving and wait for a reply; * handle_parted() will call tp_base_channel_destroyed() and all the Closed * signals will be emitted. (Since there's no waiting-for-password state on * the protocol level, MUC_STATE_AUTH doesn't count as ‘in the MUC’.) See * fd.o#19930 for more details. */ if (inform_muc && priv->state >= MUC_STATE_INITIATED && priv->state != MUC_STATE_AUTH) { send_leave_message (chan, reason); priv->closing = TRUE; } else { tp_base_channel_destroyed (base); } handles = tp_handle_set_to_array (chan->group.members); gabble_presence_cache_update_many (conn->presence_cache, handles, NULL, GABBLE_PRESENCE_UNKNOWN, NULL, 0); g_array_unref (handles); if (priv->set_subject_context != NULL) return_from_set_subject (chan, &error); g_object_set (chan, "state", MUC_STATE_ENDED, NULL); g_object_unref (chan); } gboolean _gabble_muc_channel_is_ready (GabbleMucChannel *chan) { GabbleMucChannelPrivate *priv; g_assert (GABBLE_IS_MUC_CHANNEL (chan)); priv = chan->priv; return priv->ready; } /* returns TRUE if there are no tube or Call channels open in this MUC */ gboolean gabble_muc_channel_can_be_closed (GabbleMucChannel *chan) { GabbleMucChannelPrivate *priv = chan->priv; if (g_hash_table_size (priv->tubes) > 0) return FALSE; if (priv->calls != NULL || priv->call_requests != NULL || priv->call_initiating) return FALSE; return TRUE; } gboolean gabble_muc_channel_get_autoclose (GabbleMucChannel *chan) { return chan->priv->autoclose; } void gabble_muc_channel_set_autoclose (GabbleMucChannel *chan, gboolean autoclose) { chan->priv->autoclose = autoclose; } static gboolean handle_nick_conflict (GabbleMucChannel *chan, WockyStanza *stanza, GError **tp_error) { GabbleMucChannelPrivate *priv = chan->priv; TpBaseChannel *base = TP_BASE_CHANNEL (chan); TpGroupMixin *mixin = TP_GROUP_MIXIN (chan); TpHandleRepoIface *contact_repo = tp_base_connection_get_handles ( tp_base_channel_get_connection (base), TP_HANDLE_TYPE_CONTACT); TpHandle self_handle; TpIntset *add_rp, *remove_rp; const gchar *from = wocky_stanza_get_from (stanza); /* If this is a nick conflict message with a resource in the JID, and the * resource doesn't match the one we're currently trying to join as, then * ignore it. This works around a bug in Google Talk's MUC server, which * sends the conflict message twice. It's valid for there to be no resource * in the from='' field. If Google didn't include the resource, we couldn't * work around the bug; but they happen to do so, so yay. * <https://bugs.freedesktop.org/show_bug.cgi?id=35619> * * FIXME: WockyMuc should provide a _join_async() method and do all this for * us. */ g_assert (from != NULL); if (strchr (from, '/') != NULL && tp_strdiff (from, priv->self_jid->str)) { DEBUG ("ignoring spurious conflict message for %s", from); return TRUE; } if (priv->nick_retry_count >= MAX_NICK_RETRIES) { g_set_error (tp_error, TP_ERROR, TP_ERROR_NOT_AVAILABLE, "nickname already in use and retry count exceeded"); return FALSE; } /* Add a _ to our jid, and update the group mixin's self handle * and remote pending members appropriately. */ g_string_append_c (priv->self_jid, '_'); g_object_set (priv->wmuc, "jid", priv->self_jid->str, NULL); self_handle = tp_handle_ensure (contact_repo, priv->self_jid->str, GUINT_TO_POINTER (GABBLE_JID_ROOM_MEMBER), NULL); add_rp = tp_intset_new (); remove_rp = tp_intset_new (); tp_intset_add (add_rp, self_handle); tp_intset_add (remove_rp, mixin->self_handle); tp_group_mixin_change_self_handle ((GObject *) chan, self_handle); tp_group_mixin_change_members ((GObject *) chan, NULL, NULL, remove_rp, NULL, add_rp, 0, TP_CHANNEL_GROUP_CHANGE_REASON_RENAMED); tp_intset_destroy (add_rp); tp_intset_destroy (remove_rp); priv->nick_retry_count++; send_join_request (chan); return TRUE; } static void room_created_submit_reply_cb ( GObject *source, GAsyncResult *result, gpointer user_data) { if (conn_util_send_iq_finish (GABBLE_CONNECTION (source), result, NULL, NULL)) DEBUG ("failed to submit room config"); } static WockyNode * config_form_get_form_node (WockyStanza *stanza) { WockyNode *query, *x; WockyNodeIter i; /* find the query node */ query = wocky_node_get_child (wocky_stanza_get_top_node (stanza), "query"); if (query == NULL) return NULL; /* then the form node */ wocky_node_iter_init (&i, query, "x", NS_X_DATA); while (wocky_node_iter_next (&i, &x)) { if (!tp_strdiff (wocky_node_get_attribute (x, "type"), "form")) return x; } return NULL; } static void perms_config_form_reply_cb ( GObject *source, GAsyncResult *result, gpointer user_data) { GabbleMucChannel *self = GABBLE_MUC_CHANNEL (user_data); GabbleMucChannelPrivate *priv = self->priv; WockyStanza *reply = NULL; WockyNode *form_node, *field; WockyNodeIter i; if (!conn_util_send_iq_finish (GABBLE_CONNECTION (source), result, &reply, NULL)) { DEBUG ("request for config form failed, property permissions " "will be inaccurate"); goto OUT; } /* just in case our affiliation has changed in the meantime */ if (priv->self_affil != WOCKY_MUC_AFFILIATION_OWNER) goto OUT; form_node = config_form_get_form_node (reply); if (form_node == NULL) { DEBUG ("form node not found, property permissions will be inaccurate"); goto OUT; } wocky_node_iter_init (&i, form_node, "field", NULL); while (wocky_node_iter_next (&i, &field)) { const gchar *var = wocky_node_get_attribute (field, "var"); if (!tp_strdiff (var, "muc#roomconfig_roomdesc") || !tp_strdiff (var, "muc#owner_roomdesc")) { tp_base_room_config_set_property_mutable (priv->room_config, TP_BASE_ROOM_CONFIG_DESCRIPTION, TRUE); tp_base_room_config_emit_properties_changed (priv->room_config); break; } } OUT: tp_clear_object (&reply); g_object_unref (self); } static void emit_subject_changed (GabbleMucChannel *chan) { const gchar *changed[] = { "Subject", "Actor", "Timestamp", NULL }; tp_dbus_properties_mixin_emit_properties_changed (G_OBJECT (chan), TP_IFACE_CHANNEL_INTERFACE_SUBJECT, changed); } static void update_permissions (GabbleMucChannel *chan) { GabbleMucChannelPrivate *priv = chan->priv; TpBaseChannel *base = TP_BASE_CHANNEL (chan); TpChannelGroupFlags grp_flags_add, grp_flags_rem; /* * Update group flags. */ grp_flags_add = TP_CHANNEL_GROUP_FLAG_CAN_ADD | TP_CHANNEL_GROUP_FLAG_MESSAGE_ADD; grp_flags_rem = 0; if (priv->self_role == WOCKY_MUC_ROLE_MODERATOR) { grp_flags_add |= TP_CHANNEL_GROUP_FLAG_CAN_REMOVE | TP_CHANNEL_GROUP_FLAG_MESSAGE_REMOVE; } else { grp_flags_rem |= TP_CHANNEL_GROUP_FLAG_CAN_REMOVE | TP_CHANNEL_GROUP_FLAG_MESSAGE_REMOVE; } tp_group_mixin_change_flags ((GObject *) chan, grp_flags_add, grp_flags_rem); /* Update RoomConfig.CanUpdateConfiguration */ /* The room configuration is part of the "room definition", so is defined by * the XEP to be editable only by owners. */ if (priv->self_affil == WOCKY_MUC_AFFILIATION_OWNER) { tp_base_room_config_set_can_update_configuration (priv->room_config, TRUE); } else { tp_base_room_config_set_can_update_configuration (priv->room_config, FALSE); } tp_base_room_config_emit_properties_changed (priv->room_config); if (priv->self_affil == WOCKY_MUC_AFFILIATION_OWNER) { /* request the configuration form purely to see if the description * is writable by us in this room. sigh. GO MUC!!! */ GabbleConnection *conn = GABBLE_CONNECTION ( tp_base_channel_get_connection (base)); WockyStanza *stanza = wocky_stanza_build ( WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_GET, NULL, priv->jid, '(', "query", ':', WOCKY_NS_MUC_OWNER, ')', NULL); conn_util_send_iq_async (conn, stanza, NULL, perms_config_form_reply_cb, g_object_ref (chan)); g_object_unref (stanza); } } /* connect to wocky-muc:SIG_PRESENCE_ERROR */ static void handle_error (GObject *source, WockyStanza *stanza, WockyXmppErrorType errtype, const GError *error, gpointer data) { GabbleMucChannel *gmuc = GABBLE_MUC_CHANNEL (data); GabbleMucChannelPrivate *priv = gmuc->priv; TpChannelGroupChangeReason reason = TP_CHANNEL_GROUP_CHANGE_REASON_NONE; g_assert (GABBLE_IS_MUC_CHANNEL (gmuc)); if (priv->state >= MUC_STATE_JOINED) { DEBUG ("presence error while already member of the channel -- NYI"); return; } if (error->code == WOCKY_XMPP_ERROR_NOT_AUTHORIZED) { /* channel can sit requiring a password indefinitely */ clear_join_timer (gmuc); /* Password already provided and incorrect? */ if (priv->state == MUC_STATE_AUTH) { provide_password_return_if_pending (gmuc, FALSE); return; } DEBUG ("password required to join; signalling"); change_must_provide_password (gmuc, TRUE); g_object_set (gmuc, "state", MUC_STATE_AUTH, NULL); } else { GError *tp_error = NULL; switch (error->code) { case WOCKY_XMPP_ERROR_FORBIDDEN: tp_error = g_error_new (TP_ERROR, TP_ERROR_CHANNEL_BANNED, "banned from room"); reason = TP_CHANNEL_GROUP_CHANGE_REASON_BANNED; break; case WOCKY_XMPP_ERROR_SERVICE_UNAVAILABLE: tp_error = g_error_new (TP_ERROR, TP_ERROR_CHANNEL_FULL, "room is full"); reason = TP_CHANNEL_GROUP_CHANGE_REASON_BUSY; break; case WOCKY_XMPP_ERROR_REGISTRATION_REQUIRED: tp_error = g_error_new (TP_ERROR, TP_ERROR_CHANNEL_INVITE_ONLY, "room is invite only"); break; case WOCKY_XMPP_ERROR_CONFLICT: if (handle_nick_conflict (gmuc, stanza, &tp_error)) return; break; default: tp_error = g_error_new (TP_ERROR, TP_ERROR_NOT_AVAILABLE, "%s", wocky_xmpp_error_description (error->code)); break; } g_signal_emit (gmuc, signals[JOIN_ERROR], 0, tp_error); close_channel (gmuc, tp_error->message, FALSE, 0, reason); g_error_free (tp_error); } } static void tube_closed_cb (GabbleTubeIface *tube, GabbleMucChannel *gmuc) { GabbleMucChannelPrivate *priv = gmuc->priv; guint64 tube_id; g_object_get (tube, "id", &tube_id, NULL); g_hash_table_remove (priv->tubes, GUINT_TO_POINTER (tube_id)); } static GabbleTubeIface * create_new_tube (GabbleMucChannel *gmuc, TpTubeType type, TpHandle initiator, const gchar *service, GHashTable *parameters, const gchar *stream_id, guint64 tube_id, GabbleBytestreamIface *bytestream, gboolean requested) { GabbleMucChannelPrivate *priv = gmuc->priv; TpBaseChannel *base = TP_BASE_CHANNEL (gmuc); GabbleConnection *conn = GABBLE_CONNECTION ( tp_base_channel_get_connection (base)); TpHandle self_handle = TP_GROUP_MIXIN (gmuc)->self_handle; TpHandle handle = tp_base_channel_get_target_handle (base); GabbleTubeIface *tube; switch (type) { case TP_TUBE_TYPE_DBUS: tube = GABBLE_TUBE_IFACE (gabble_tube_dbus_new (conn, handle, TP_HANDLE_TYPE_ROOM, self_handle, initiator, service, parameters, stream_id, tube_id, bytestream, gmuc, requested)); break; case TP_TUBE_TYPE_STREAM: tube = GABBLE_TUBE_IFACE (gabble_tube_stream_new (conn, handle, TP_HANDLE_TYPE_ROOM, self_handle, initiator, service, parameters, tube_id, gmuc, requested)); break; default: g_return_val_if_reached (NULL); } tp_base_channel_register ((TpBaseChannel *) tube); DEBUG ("create tube %" G_GUINT64_FORMAT, tube_id); g_hash_table_insert (priv->tubes, GUINT_TO_POINTER (tube_id), tube); g_signal_connect (tube, "closed", G_CALLBACK (tube_closed_cb), gmuc); return tube; } static guint64 generate_tube_id (GabbleMucChannel *self) { GabbleMucChannelPrivate *priv = self->priv; guint64 out; /* probably totally overkill */ do { out = g_random_int_range (1, G_MAXINT32); } while (g_hash_table_lookup (priv->tubes, GUINT_TO_POINTER (out)) != NULL); return out; } GabbleTubeIface * gabble_muc_channel_tube_request (GabbleMucChannel *self, gpointer request_token, GHashTable *request_properties, gboolean require_new) { GabbleTubeIface *tube; const gchar *channel_type; const gchar *service; GHashTable *parameters = NULL; guint64 tube_id; gchar *stream_id; TpTubeType type; tube_id = generate_tube_id (self); channel_type = tp_asv_get_string (request_properties, TP_PROP_CHANNEL_CHANNEL_TYPE); if (!tp_strdiff (channel_type, TP_IFACE_CHANNEL_TYPE_STREAM_TUBE)) { type = TP_TUBE_TYPE_STREAM; service = tp_asv_get_string (request_properties, TP_PROP_CHANNEL_TYPE_STREAM_TUBE_SERVICE); } else if (! tp_strdiff (channel_type, TP_IFACE_CHANNEL_TYPE_DBUS_TUBE)) { type = TP_TUBE_TYPE_DBUS; service = tp_asv_get_string (request_properties, TP_PROP_CHANNEL_TYPE_DBUS_TUBE_SERVICE_NAME); } else /* This assertion is safe: this function's caller only calls it in one of * the above cases. * FIXME: but it would be better to pass an enum member or something maybe. */ g_assert_not_reached (); /* requested tubes have an empty parameters dict */ parameters = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify) tp_g_value_slice_free); /* if the service property is missing, the requestotron rejects the request */ g_assert (service != NULL); DEBUG ("Request a tube channel with type='%s' and service='%s'", channel_type, service); stream_id = gabble_bytestream_factory_generate_stream_id (); tube = create_new_tube (self, type, TP_GROUP_MIXIN (self)->self_handle, service, parameters, stream_id, tube_id, NULL, TRUE); g_free (stream_id); g_hash_table_unref (parameters); return tube; } void gabble_muc_channel_foreach_tubes (GabbleMucChannel *gmuc, TpExportableChannelFunc foreach, gpointer user_data) { GabbleMucChannelPrivate *priv = gmuc->priv; GHashTableIter iter; gpointer value; g_hash_table_iter_init (&iter, priv->tubes); while (g_hash_table_iter_next (&iter, NULL, &value)) { foreach (TP_EXPORTABLE_CHANNEL (value), user_data); } } void gabble_muc_channel_handle_si_stream_request (GabbleMucChannel *self, GabbleBytestreamIface *bytestream, const gchar *stream_id, WockyStanza *msg) { GabbleMucChannelPrivate *priv = self->priv; WockyNode *si_node, *stream_node; const gchar *tmp; guint64 tube_id; GabbleTubeIface *tube; si_node = wocky_node_get_child_ns ( wocky_stanza_get_top_node (msg), "si", NS_SI); g_return_if_fail (si_node != NULL); stream_node = wocky_node_get_child_ns (si_node, "muc-stream", NS_TUBES); g_return_if_fail (stream_node != NULL); tmp = wocky_node_get_attribute (stream_node, "tube"); if (tmp == NULL) { GError e = { WOCKY_XMPP_ERROR, WOCKY_XMPP_ERROR_BAD_REQUEST, "<muc-stream> has no tube attribute" }; NODE_DEBUG (stream_node, e.message); gabble_bytestream_iface_close (bytestream, &e); return; } tube_id = g_ascii_strtoull (tmp, NULL, 10); if (tube_id == 0 || tube_id > G_MAXUINT32) { GError e = { WOCKY_XMPP_ERROR, WOCKY_XMPP_ERROR_BAD_REQUEST, "<muc-stream> tube ID attribute non-numeric or out of range" }; DEBUG ("tube id is non-numeric or out of range: %s", tmp); gabble_bytestream_iface_close (bytestream, &e); return; } tube = g_hash_table_lookup (priv->tubes, GUINT_TO_POINTER (tube_id)); if (tube == NULL) { GError e = { WOCKY_XMPP_ERROR, WOCKY_XMPP_ERROR_BAD_REQUEST, "<muc-stream> tube attribute points to a nonexistent " "tube" }; DEBUG ("tube %" G_GUINT64_FORMAT " doesn't exist", tube_id); gabble_bytestream_iface_close (bytestream, &e); return; } DEBUG ("received new bytestream request for existing tube: %" G_GUINT64_FORMAT, tube_id); gabble_tube_iface_add_bytestream (tube, bytestream); } static void tubes_presence_update (GabbleMucChannel *gmuc, TpHandle contact, WockyNode *pnode) { GabbleMucChannelPrivate *priv = gmuc->priv; TpHandleRepoIface *contact_repo = tp_base_connection_get_handles ( tp_base_channel_get_connection (TP_BASE_CHANNEL (gmuc)), TP_HANDLE_TYPE_CONTACT); const gchar *presence_type; WockyNode *tubes_node; GHashTable *old_dbus_tubes; GHashTableIter iter; gpointer key, value; WockyNodeIter i; WockyNode *tube_node; if (contact == TP_GROUP_MIXIN (gmuc)->self_handle) /* We don't need to inspect our own presence */ return; presence_type = wocky_node_get_attribute (pnode, "type"); if (!tp_strdiff (presence_type, "unavailable")) { g_hash_table_iter_init (&iter, priv->tubes); while (g_hash_table_iter_next (&iter, NULL, &value)) { GabbleTubeDBus *tube = value; if (!GABBLE_IS_TUBE_DBUS (value)) continue; gabble_tube_dbus_remove_name (tube, contact); } } tubes_node = wocky_node_get_child_ns (pnode, "tubes", NS_TUBES); if (tubes_node == NULL) return; /* Fill old_dbus_tubes with D-BUS tubes previously announced by * the contact */ old_dbus_tubes = g_hash_table_new (g_direct_hash, g_direct_equal); g_hash_table_iter_init (&iter, priv->tubes); while (g_hash_table_iter_next (&iter, &key, &value)) { if (!GABBLE_IS_TUBE_DBUS (value)) continue; if (gabble_tube_dbus_handle_in_names (GABBLE_TUBE_DBUS (value), contact)) { g_hash_table_insert (old_dbus_tubes, key, value); } } wocky_node_iter_init (&i, tubes_node, NULL, NULL); while (wocky_node_iter_next (&i, &tube_node)) { const gchar *stream_id; GabbleTubeIface *tube; guint64 tube_id; TpTubeType type; stream_id = wocky_node_get_attribute (tube_node, "stream-id"); if (!gabble_private_tubes_factory_extract_tube_information ( contact_repo, tube_node, NULL, NULL, NULL, NULL, &tube_id)) { DEBUG ("Bad tube ID, skipping to next child of <tubes>"); continue; } tube = g_hash_table_lookup (priv->tubes, GUINT_TO_POINTER (tube_id)); if (tube == NULL) { /* We don't know yet this tube */ const gchar *service; TpHandle initiator_handle; GHashTable *parameters; if (gabble_private_tubes_factory_extract_tube_information ( contact_repo, tube_node, &type, &initiator_handle, &service, ¶meters, NULL)) { if (type == TP_TUBE_TYPE_DBUS && initiator_handle == 0) { DEBUG ("D-Bus tube initiator missing"); /* skip to the next child of <tubes> */ continue; } else if (type == TP_TUBE_TYPE_STREAM) { initiator_handle = contact; } tube = create_new_tube (gmuc, type, initiator_handle, service, parameters, stream_id, tube_id, NULL, FALSE); g_signal_emit (gmuc, signals[NEW_TUBE], 0, tube); g_hash_table_unref (parameters); } } else { /* The contact is in the tube. * Remove it from old_dbus_tubes if needed */ g_hash_table_remove (old_dbus_tubes, GUINT_TO_POINTER (tube_id)); } if (tube == NULL) /* skip to the next child of <tubes> */ continue; g_object_get (tube, "type", &type, NULL); if (type == TP_TUBE_TYPE_DBUS) { /* Update mapping of handle -> D-Bus name. */ if (!gabble_tube_dbus_handle_in_names (GABBLE_TUBE_DBUS (tube), contact)) { /* Contact just joined the tube */ const gchar *new_name; new_name = wocky_node_get_attribute (tube_node, "dbus-name"); if (!new_name) { DEBUG ("Contact %u isn't announcing their D-Bus name", contact); /* skip to the next child of <tubes> */ continue; } gabble_tube_dbus_add_name (GABBLE_TUBE_DBUS (tube), contact, new_name); } } } /* Tubes remaining in old_dbus_tubes was left by the contact */ g_hash_table_iter_init (&iter, old_dbus_tubes); while (g_hash_table_iter_next (&iter, NULL, &value)) { gabble_tube_dbus_remove_name (GABBLE_TUBE_DBUS (value), contact); } g_hash_table_unref (old_dbus_tubes); } /* ************************************************************************* */ /* presence related signal handlers */ /* not actually a signal handler, but used by them. */ static void handle_tube_presence (GabbleMucChannel *gmuc, TpHandle from, WockyStanza *stanza) { WockyNode *node = wocky_stanza_get_top_node (stanza); if (from == 0) return; tubes_presence_update (gmuc, from, node); } static TpChannelGroupChangeReason muc_status_codes_to_change_reason (guint codes) { if ((codes & WOCKY_MUC_CODE_BANNED) != 0) return TP_CHANNEL_GROUP_CHANGE_REASON_BANNED; else if ((codes & ( WOCKY_MUC_CODE_KICKED | WOCKY_MUC_CODE_KICKED_AFFILIATION | WOCKY_MUC_CODE_KICKED_ROOM_PRIVATISED | WOCKY_MUC_CODE_KICKED_SHUTDOWN )) != 0) return TP_CHANNEL_GROUP_CHANGE_REASON_KICKED; else return TP_CHANNEL_GROUP_CHANGE_REASON_NONE; } /* connect to wocky-muc:SIG_PARTED, which we will receive when the MUC tells * * us that we have left the channel */ static void handle_parted (GObject *source, WockyStanza *stanza, guint codes, const gchar *actor_jid, const gchar *why, const gchar *msg, gpointer data) { WockyMuc *wmuc = WOCKY_MUC (source); GabbleMucChannel *gmuc = GABBLE_MUC_CHANNEL (data); TpBaseChannel *base = TP_BASE_CHANNEL (gmuc); GabbleMucChannelPrivate *priv = gmuc->priv; TpChannelGroupChangeReason reason = TP_CHANNEL_GROUP_CHANGE_REASON_NONE; TpHandleRepoIface *contact_repo = tp_base_connection_get_handles (tp_base_channel_get_connection (base), TP_HANDLE_TYPE_CONTACT); TpIntset *handles = NULL; TpHandle member = 0; TpHandle actor = 0; const char *jid = wocky_muc_jid (wmuc); DEBUG ("called with jid='%s'", jid); member = tp_handle_ensure (contact_repo, jid, NULL, NULL); if (priv->closing) { /* This was a timeout to ensure that leaving a room with a * non-responsive conference server still meant the channel * closed (eventually). */ clear_leave_timer (gmuc); /* Close has been called, and we informed the MUC of our leaving * by sending a presence stanza of type='unavailable'. Now this * has been returned to us we know we've successfully left the * MUC, so we can finally close the channel here. */ tp_base_channel_destroyed (TP_BASE_CHANNEL (gmuc)); return; } if (member == 0) { DEBUG ("bizarre: ignoring our own malformed MUC JID '%s'", jid); return; } handles = tp_intset_new (); tp_intset_add (handles, member); if (actor_jid != NULL) { actor = tp_handle_ensure (contact_repo, actor_jid, NULL, NULL); if (actor == 0) DEBUG ("ignoring invalid actor JID %s", actor_jid); } reason = muc_status_codes_to_change_reason (codes); /* handle_tube_presence creates tubes if need be, so bypass it here: */ tubes_presence_update (gmuc, member, wocky_stanza_get_top_node (stanza)); close_channel (gmuc, why, FALSE, actor, reason); tp_intset_destroy (handles); } /* connect to wocky-muc:SIG_LEFT, which we will receive when the MUC informs * * us someone [else] has left the channel */ static void handle_left (GObject *source, WockyStanza *stanza, guint codes, WockyMucMember *who, const gchar *actor_jid, const gchar *why, const gchar *msg, gpointer data) { GabbleMucChannel *gmuc = GABBLE_MUC_CHANNEL (data); TpBaseChannel *base = TP_BASE_CHANNEL (gmuc); TpChannelGroupChangeReason reason = TP_CHANNEL_GROUP_CHANGE_REASON_NONE; TpHandleRepoIface *contact_repo = tp_base_connection_get_handles (tp_base_channel_get_connection (base), TP_HANDLE_TYPE_CONTACT); TpIntset *handles = NULL; TpHandle member = 0; TpHandle actor = 0; member = tp_handle_ensure (contact_repo, who->from, NULL, NULL); if (member == 0) { DEBUG ("ignoring malformed MUC JID '%s'", who->from); return; } handles = tp_intset_new (); tp_intset_add (handles, member); if (actor_jid != NULL) { actor = tp_handle_ensure (contact_repo, actor_jid, NULL, NULL); if (actor == 0) DEBUG ("ignoring invalid actor JID %s", actor_jid); } reason = muc_status_codes_to_change_reason (codes); /* handle_tube_presence creates tubes if need be, so bypass it here: */ tubes_presence_update (gmuc, member, wocky_stanza_get_top_node (stanza)); tp_group_mixin_change_members (data, why, NULL, handles, NULL, NULL, actor, reason); tp_message_mixin_change_chat_state (data, member, TP_CHANNEL_CHAT_STATE_GONE); tp_intset_destroy (handles); } /* connect to wocky-muc:SIG_PERM_CHANGE, which we will receive when the * * MUC informs us our role/affiliation has been altered */ static void handle_perms (GObject *source, WockyStanza *stanza, GHashTable *code, const gchar *actor, const gchar *why, gpointer data) { WockyMuc *wmuc = WOCKY_MUC (source); GabbleMucChannel *gmuc = GABBLE_MUC_CHANNEL (data); GabbleMucChannelPrivate *priv = gmuc->priv; TpHandle myself = TP_GROUP_MIXIN (gmuc)->self_handle; priv->self_role = wocky_muc_role (wmuc); priv->self_affil = wocky_muc_affiliation (wmuc); room_properties_update (gmuc); update_permissions (gmuc); handle_tube_presence (gmuc, myself, stanza); } static void handle_fill_presence (WockyMuc *muc, WockyStanza *stanza, gpointer user_data) { GabbleMucChannel *self = GABBLE_MUC_CHANNEL (user_data); GabbleMucChannelPrivate *priv = self->priv; TpBaseChannel *base = TP_BASE_CHANNEL (self); TpBaseConnection *base_conn = tp_base_channel_get_connection (base); GabbleConnection *conn = GABBLE_CONNECTION (base_conn); TpHandleRepoIface *contact_repo = tp_base_connection_get_handles (base_conn, TP_HANDLE_TYPE_CONTACT); TpHandle self_handle; self_handle = tp_handle_ensure (contact_repo, priv->self_jid->str, GUINT_TO_POINTER (GABBLE_JID_ROOM_MEMBER), NULL); gabble_presence_add_status_and_vcard (conn->self_presence, stanza); /* If we are invisible, show us as dnd in muc, since we can't be invisible */ if (conn->self_presence->status == GABBLE_PRESENCE_HIDDEN) wocky_node_add_child_with_content (wocky_stanza_get_top_node (stanza), "show", JABBER_PRESENCE_SHOW_DND); /* Sync the presence we send over the wire with what is in our presence cache */ gabble_presence_cache_update (conn->presence_cache, self_handle, NULL, conn->self_presence->status, conn->self_presence->status_message, 0); tube_pre_presence (self, stanza); g_signal_emit (self, signals[PRE_PRESENCE], 0, (WockyStanza *) stanza); } /* connect to wocky-muc:SIG_NICK_CHANGE, which we will receive when the * * MUC informs us our nick has been changed for some reason */ static void handle_renamed (GObject *source, WockyStanza *stanza, guint codes, gpointer data) { WockyMuc *wmuc = WOCKY_MUC (source); GabbleMucChannel *gmuc = GABBLE_MUC_CHANNEL (data); TpBaseChannel *base = TP_BASE_CHANNEL (gmuc); TpHandleRepoIface *contact_repo = tp_base_connection_get_handles (tp_base_channel_get_connection (base), TP_HANDLE_TYPE_CONTACT); TpIntset *old_self = tp_intset_new (); const gchar *me = wocky_muc_jid (wmuc); const gchar *me2 = wocky_muc_user (wmuc); TpHandle myself = tp_handle_ensure (contact_repo, me, GUINT_TO_POINTER (GABBLE_JID_ROOM_MEMBER), NULL); TpHandle userid = tp_handle_ensure (contact_repo, me2, GUINT_TO_POINTER (GABBLE_JID_ROOM_MEMBER), NULL); tp_intset_add (old_self, TP_GROUP_MIXIN (gmuc)->self_handle); tp_group_mixin_change_self_handle (data, myself); tp_group_mixin_add_handle_owner (data, myself, userid); tp_group_mixin_change_members (data, "", NULL, old_self, NULL, NULL, 0, 0); handle_tube_presence (gmuc, myself, stanza); tp_intset_destroy (old_self); } static void update_roster_presence (GabbleMucChannel *gmuc, WockyMucMember *member, TpHandleRepoIface *contact_repo, TpHandleSet *members, TpHandleSet *owners, GHashTable *omap) { TpBaseChannel *base = TP_BASE_CHANNEL (gmuc); GabbleConnection *conn = GABBLE_CONNECTION (tp_base_channel_get_connection (base)); TpHandle owner = 0; TpHandle handle = tp_handle_ensure (contact_repo, member->from, GUINT_TO_POINTER (GABBLE_JID_ROOM_MEMBER), NULL); if (member->jid != NULL) { owner = tp_handle_ensure (contact_repo, member->jid, GUINT_TO_POINTER (GABBLE_JID_GLOBAL), NULL); if (owner == 0) DEBUG ("Invalid owner handle '%s', treating as no owner", member->jid); else tp_handle_set_add (owners, owner); } gabble_presence_parse_presence_message (conn->presence_cache, handle, member->from, (WockyStanza *) member->presence_stanza); tp_handle_set_add (members, handle); g_hash_table_insert (omap, GUINT_TO_POINTER (handle), GUINT_TO_POINTER (owner)); /* make a note of the fact that owner JIDs are visible to us */ /* notify whomever that an identifiable contact joined the MUC */ if (owner != 0) { tp_group_mixin_change_flags (G_OBJECT (gmuc), 0, TP_CHANNEL_GROUP_FLAG_HANDLE_OWNERS_NOT_AVAILABLE); g_signal_emit (gmuc, signals[CONTACT_JOIN], 0, owner); } handle_tube_presence (gmuc, handle, member->presence_stanza); } /* connect to wocky_muc SIG_JOINED which we should receive when we receive * * the final (ie our own) presence in the roster: (note that if our nick was * * changed by the MUC we will already have received a SIG_NICK_CHANGE: */ static void handle_join (WockyMuc *muc, WockyStanza *stanza, guint codes, gpointer data) { GabbleMucChannel *gmuc = GABBLE_MUC_CHANNEL (data); TpBaseChannel *base = TP_BASE_CHANNEL (gmuc); TpBaseConnection *base_conn = tp_base_channel_get_connection (base); TpHandleRepoIface *contact_repo = tp_base_connection_get_handles (base_conn, TP_HANDLE_TYPE_CONTACT); TpHandleSet *members = tp_handle_set_new (contact_repo); TpHandleSet *owners = tp_handle_set_new (contact_repo); GHashTable *omap = g_hash_table_new (g_direct_hash, g_direct_equal); GHashTable *member_jids = wocky_muc_members (muc); const gchar *me = wocky_muc_jid (muc); TpHandle myself = tp_handle_ensure (contact_repo, me, GUINT_TO_POINTER (GABBLE_JID_ROOM_MEMBER), NULL); GHashTableIter iter; WockyMucMember *member; g_hash_table_iter_init (&iter, member_jids); while (g_hash_table_iter_next (&iter, NULL, (gpointer *)&member)) update_roster_presence (gmuc, member, contact_repo, members, owners, omap); g_hash_table_insert (omap, GUINT_TO_POINTER (myself), GUINT_TO_POINTER (tp_base_connection_get_self_handle (base_conn))); tp_handle_set_add (members, myself); tp_group_mixin_add_handle_owners (G_OBJECT (gmuc), omap); tp_group_mixin_change_members (G_OBJECT (gmuc), "", tp_handle_set_peek (members), NULL, NULL, NULL, 0, 0); /* accept the config of the room if it was created for us: */ if (codes & WOCKY_MUC_CODE_NEW_ROOM) { WockyStanza *accept = wocky_stanza_build ( WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_SET, NULL, gmuc->priv->jid, '(', "query", ':', WOCKY_NS_MUC_OWNER, '(', "x", ':', WOCKY_XMPP_NS_DATA, '@', "type", "submit", ')', ')', NULL); conn_util_send_iq_async (GABBLE_CONNECTION (base_conn), accept, NULL, room_created_submit_reply_cb, NULL); g_object_unref (accept); } g_object_set (gmuc, "state", MUC_STATE_JOINED, NULL); tp_handle_set_destroy (members); tp_handle_set_destroy (owners); g_hash_table_unref (omap); g_hash_table_unref (member_jids); } /* connect to wocky-muc:SIG_PRESENCE, which is fired for presences that are * * NOT our own after the initial roster has been received: */ static void handle_presence (GObject *source, WockyStanza *stanza, guint codes, WockyMucMember *who, gpointer data) { GabbleMucChannel *gmuc = GABBLE_MUC_CHANNEL (data); #ifdef ENABLE_VOIP GabbleMucChannelPrivate *priv = gmuc->priv; #endif TpBaseChannel *base = TP_BASE_CHANNEL (gmuc); TpBaseConnection *base_conn = tp_base_channel_get_connection (base); GabbleConnection *conn = GABBLE_CONNECTION (base_conn); TpHandleRepoIface *contact_repo = tp_base_connection_get_handles (base_conn, TP_HANDLE_TYPE_CONTACT); TpHandle owner = 0; TpHandle handle = tp_handle_ensure (contact_repo, who->from, GUINT_TO_POINTER (GABBLE_JID_ROOM_MEMBER), NULL); TpHandleSet *handles = tp_handle_set_new (contact_repo); /* is the 'real' jid field of the presence set? If so, use it: */ if (who->jid != NULL) { owner = tp_handle_ensure (contact_repo, who->jid, GUINT_TO_POINTER (GABBLE_JID_GLOBAL), NULL); if (owner == 0) { DEBUG ("Invalid owner handle '%s' ignored", who->jid); } else /* note that JIDs are known to us in this MUC */ { tp_group_mixin_change_flags (G_OBJECT (data), 0, TP_CHANNEL_GROUP_FLAG_HANDLE_OWNERS_NOT_AVAILABLE); } } gabble_presence_parse_presence_message (conn->presence_cache, handle, who->from, (WockyStanza *) who->presence_stanza); /* add the member in quesion */ tp_handle_set_add (handles, handle); tp_group_mixin_change_members (data, "", tp_handle_set_peek (handles), NULL, NULL, NULL, 0, 0); /* record the owner (0 for no owner) */ tp_group_mixin_add_handle_owner (data, handle, owner); handle_tube_presence (gmuc, handle, stanza); #ifdef ENABLE_VOIP if (!priv->call_initiating && priv->call == NULL) { WockyNode *m; /* Check for muji nodes */ m = wocky_node_get_child_ns ( wocky_stanza_get_top_node (stanza), "muji", NS_MUJI); if (m != NULL && wocky_node_get_child_ns (m, "content", NS_MUJI) != NULL) { DEBUG ("Detected a muji call in progress, starting a call channel!"); gabble_muc_channel_start_call_creation (gmuc, NULL); } } #endif tp_handle_set_destroy (handles); } /* ************************************************************************ */ /* message signal handlers */ static void handle_message (GObject *source, WockyStanza *stanza, WockyMucMsgType type, const gchar *xmpp_id, GDateTime *datetime, WockyMucMember *who, const gchar *text, const gchar *subject, WockyMucMsgState state, gpointer data) { GabbleMucChannel *gmuc = GABBLE_MUC_CHANNEL (data); TpBaseChannel *base = TP_BASE_CHANNEL (gmuc); TpBaseConnection *conn = tp_base_channel_get_connection (base); gboolean from_member = (who != NULL); TpChannelTextMessageType msg_type; TpHandleRepoIface *repo; TpHandleType handle_type; TpHandle from; if (from_member) { handle_type = TP_HANDLE_TYPE_CONTACT; repo = tp_base_connection_get_handles (conn, handle_type); from = tp_handle_ensure (repo, who->from, GUINT_TO_POINTER (GABBLE_JID_ROOM_MEMBER), NULL); if (from == 0) { DEBUG ("Message from MUC member with no handle, discarding."); return; } } else /* directly from MUC itself */ { handle_type = TP_HANDLE_TYPE_ROOM; repo = tp_base_connection_get_handles (conn, handle_type); from = tp_base_channel_get_target_handle (base); } switch (type) { case WOCKY_MUC_MSG_NORMAL: msg_type = TP_CHANNEL_TEXT_MESSAGE_TYPE_NORMAL; break; case WOCKY_MUC_MSG_ACTION: msg_type = TP_CHANNEL_TEXT_MESSAGE_TYPE_ACTION; break; default: msg_type = TP_CHANNEL_TEXT_MESSAGE_TYPE_NOTICE; } if (text != NULL) _gabble_muc_channel_receive (gmuc, msg_type, handle_type, from, datetime, xmpp_id, text, stanza, NULL, TP_DELIVERY_STATUS_DELIVERED); if (from_member && state != WOCKY_MUC_MSG_STATE_NONE) { TpChannelChatState tp_msg_state; switch (state) { case WOCKY_MUC_MSG_STATE_ACTIVE: tp_msg_state = TP_CHANNEL_CHAT_STATE_ACTIVE; break; case WOCKY_MUC_MSG_STATE_COMPOSING: tp_msg_state = TP_CHANNEL_CHAT_STATE_COMPOSING; break; case WOCKY_MUC_MSG_STATE_INACTIVE: tp_msg_state = TP_CHANNEL_CHAT_STATE_INACTIVE; break; case WOCKY_MUC_MSG_STATE_PAUSED: tp_msg_state = TP_CHANNEL_CHAT_STATE_PAUSED; break; default: tp_msg_state = TP_CHANNEL_CHAT_STATE_ACTIVE; } tp_message_mixin_change_chat_state ((GObject *) gmuc, from, tp_msg_state); } if (subject != NULL) _gabble_muc_channel_handle_subject (gmuc, handle_type, from, datetime, subject, stanza, NULL); } static void handle_errmsg (GObject *source, WockyStanza *stanza, WockyMucMsgType type, const gchar *xmpp_id, GDateTime *datetime, WockyMucMember *who, const gchar *text, WockyXmppErrorType etype, const GError *error, gpointer data) { GabbleMucChannel *gmuc = GABBLE_MUC_CHANNEL (data); GabbleMucChannelPrivate *priv = gmuc->priv; TpBaseChannel *base = TP_BASE_CHANNEL (gmuc); TpBaseConnection *conn = tp_base_channel_get_connection (base); gboolean from_member = (who != NULL); TpDeliveryStatus ds = TP_DELIVERY_STATUS_DELIVERED; TpHandleRepoIface *repo = NULL; TpHandleType handle_type; TpHandle from = 0; const gchar *subject; if (from_member) { handle_type = TP_HANDLE_TYPE_CONTACT; repo = tp_base_connection_get_handles (conn, handle_type); from = tp_handle_ensure (repo, who->from, GUINT_TO_POINTER (GABBLE_JID_ROOM_MEMBER), NULL); if (from == 0) { DEBUG ("Message from MUC member with no handle, discarding."); return; } } else /* directly from MUC itself */ { handle_type = TP_HANDLE_TYPE_ROOM; repo = tp_base_connection_get_handles (conn, handle_type); from = tp_base_channel_get_target_handle (base); } if (etype == WOCKY_XMPP_ERROR_TYPE_WAIT) { ds = TP_DELIVERY_STATUS_TEMPORARILY_FAILED; /* Some MUCs have very strict rate limiting like "at most one stanza per * second". Since chat state notifications count towards this, if the * user types a message very quickly then the typing notification is * accepted but then the stanza containing the actual message is * rejected. * * So: if we ever get rate-limited, let's just stop sending chat states. * * https://bugs.freedesktop.org/show_bug.cgi?id=43166 */ DEBUG ("got <error type='wait'>, disabling chat state notifications"); priv->have_received_error_type_wait = TRUE; } else { ds = TP_DELIVERY_STATUS_PERMANENTLY_FAILED; } if (text != NULL) _gabble_muc_channel_receive (gmuc, TP_CHANNEL_TEXT_MESSAGE_TYPE_NOTICE, handle_type, from, datetime, xmpp_id, text, stanza, error, ds); /* FIXME: this is stupid. WockyMuc gives us the subject for non-errors, but * doesn't bother for errors. */ subject = wocky_node_get_content_from_child ( wocky_stanza_get_top_node (stanza), "subject"); /* The server is under no obligation to echo the <subject> element back if it * sends us an error. Fortunately, it should preserve the id='' element so we * can check for that instead. */ if (subject != NULL || (priv->set_subject_stanza_id != NULL && !tp_strdiff (xmpp_id, priv->set_subject_stanza_id))) _gabble_muc_channel_handle_subject (gmuc, handle_type, from, datetime, subject, stanza, error); } /* ************************************************************************* */ /** * _gabble_muc_channel_handle_subject: handle room subject updates */ void _gabble_muc_channel_handle_subject (GabbleMucChannel *chan, TpHandleType handle_type, TpHandle sender, GDateTime *datetime, const gchar *subject, WockyStanza *msg, const GError *error) { GabbleMucChannelPrivate *priv; const gchar *actor; gint64 timestamp = datetime != NULL ? g_date_time_to_unix (datetime) : G_MAXINT64; g_assert (GABBLE_IS_MUC_CHANNEL (chan)); priv = chan->priv; if (error != NULL) { if (priv->set_subject_context != NULL) { GError *tp_error = NULL; gabble_set_tp_error_from_wocky (error, &tp_error); if (tp_str_empty (tp_error->message)) g_prefix_error (&tp_error, "failed to change subject"); return_from_set_subject (chan, tp_error); g_clear_error (&tp_error); /* Get the properties into a consistent state. */ room_properties_update (chan); } return; } /* Channel.Interface.Subject properties */ if (handle_type == TP_HANDLE_TYPE_CONTACT) { TpHandleRepoIface *contact_handles = tp_base_connection_get_handles ( tp_base_channel_get_connection (TP_BASE_CHANNEL (chan)), handle_type); actor = tp_handle_inspect (contact_handles, sender); } else { actor = ""; } g_free (priv->subject); g_free (priv->subject_actor); priv->subject = g_strdup (subject); priv->subject_actor = g_strdup (actor); priv->subject_timestamp = timestamp; DEBUG ("Subject changed to '%s' by '%s' at %" G_GINT64_FORMAT "", subject, actor, timestamp); /* Emit signals */ emit_subject_changed (chan); if (priv->set_subject_context != NULL) return_from_set_subject (chan, NULL); } /** * _gabble_muc_channel_receive: receive MUC messages */ static void _gabble_muc_channel_receive (GabbleMucChannel *chan, TpChannelTextMessageType msg_type, TpHandleType sender_handle_type, TpHandle sender, GDateTime *datetime, const gchar *id, const gchar *text, WockyStanza *msg, const GError *send_error, TpDeliveryStatus error_status) { TpBaseChannel *base; TpBaseConnection *base_conn; TpMessage *message; TpHandle muc_self_handle; gboolean is_echo; gboolean is_error; gchar *tmp; gint64 timestamp = datetime != NULL ? g_date_time_to_unix (datetime): 0; g_assert (GABBLE_IS_MUC_CHANNEL (chan)); base = TP_BASE_CHANNEL (chan); base_conn = tp_base_channel_get_connection (base); muc_self_handle = chan->group.self_handle; /* Is this an error report? */ is_error = (send_error != NULL); if (is_error && sender == muc_self_handle) { /* So this is a <message from="ourself" type="error">. I can only think * that this would happen if we send an error stanza and the MUC reflects * it back at us, so let's just ignore it. */ STANZA_DEBUG (msg, "ignoring error stanza from ourself"); return; } /* Is this an echo from the MUC of a message we just sent? */ is_echo = ((sender == muc_self_handle) && (timestamp == 0)); /* Having excluded the "error from ourself" case, is_error and is_echo are * mutually exclusive. */ /* Ignore messages from the channel. The only such messages I have seen in * practice have been on devel@conference.pidgin.im, which sends useful * messages like "foo has set the subject to: ..." and "This room is not * anonymous". */ if (!is_echo && !is_error && sender_handle_type == TP_HANDLE_TYPE_ROOM) { STANZA_DEBUG (msg, "ignoring message from muc"); return; } /* are we actually hidden? */ if (!tp_base_channel_is_registered (base)) { DEBUG ("making MUC channel reappear!"); tp_base_channel_reopened_with_requested (base, FALSE, sender); } /* let's not autoclose now */ chan->priv->autoclose = FALSE; message = tp_cm_message_new (base_conn, 2); /* Header common to normal message and delivery-echo */ if (msg_type != TP_CHANNEL_TEXT_MESSAGE_TYPE_NORMAL) tp_message_set_uint32 (message, 0, "message-type", msg_type); if (timestamp != 0) tp_message_set_int64 (message, 0, "message-sent", timestamp); /* Body */ tp_message_set_string (message, 1, "content-type", "text/plain"); tp_message_set_string (message, 1, "content", text); if (is_error || is_echo) { /* Error reports and echos of our own messages are represented as * delivery reports. */ TpMessage *delivery_report = tp_cm_message_new (base_conn, 1); TpDeliveryStatus status = is_error ? error_status : TP_DELIVERY_STATUS_DELIVERED; tmp = gabble_generate_id (); tp_message_set_string (delivery_report, 0, "message-token", tmp); g_free (tmp); tp_message_set_uint32 (delivery_report, 0, "message-type", TP_CHANNEL_TEXT_MESSAGE_TYPE_DELIVERY_REPORT); tp_message_set_uint32 (delivery_report, 0, "delivery-status", status); if (id != NULL) tp_message_set_string (delivery_report, 0, "delivery-token", id); if (send_error != NULL) { tp_message_set_uint32 (delivery_report, 0, "delivery-error", gabble_tp_send_error_from_wocky_xmpp_error (send_error->code)); if (!tp_str_empty (send_error->message)) { guint body_part_number = tp_message_append_part (delivery_report); tp_message_set_string (delivery_report, body_part_number, "content-type", "text/plain"); tp_message_set_string (delivery_report, body_part_number, "content", send_error->message); } } /* We do not set a message-sender on the report: the intended recipient * of the original message was the MUC, so the spec says we should omit * it. * * The sender of the echo, however, is ourself. (Unless we get errors * for messages that we didn't send, which would be odd.) */ tp_cm_message_set_sender (message, muc_self_handle); /* If we sent the message whose delivery has succeeded or failed, we * trust the id='' attribute. */ if (id != NULL) tp_message_set_string (message, 0, "message-token", id); tp_cm_message_take_message (delivery_report, 0, "delivery-echo", message); tp_message_mixin_take_received (G_OBJECT (chan), delivery_report); } else { /* Messages from the MUC itself should have no sender. */ if (sender_handle_type == TP_HANDLE_TYPE_CONTACT) tp_cm_message_set_sender (message, sender); if (timestamp != 0) tp_message_set_boolean (message, 0, "scrollback", TRUE); if (id != NULL) tp_message_set_string (message, 0, "message-token", id); tp_message_mixin_take_received (G_OBJECT (chan), message); } } static void gabble_muc_channel_close (TpBaseChannel *base) { GabbleMucChannel *self = GABBLE_MUC_CHANNEL (base); close_channel (self, NULL, TRUE, 0, 0); } /** * gabble_muc_channel_get_password_flags * * Implements D-Bus method GetPasswordFlags * on interface org.freedesktop.Telepathy.Channel.Interface.Password */ static void gabble_muc_channel_get_password_flags (TpSvcChannelInterfacePassword *iface, DBusGMethodInvocation *context) { GabbleMucChannel *self = GABBLE_MUC_CHANNEL (iface); GabbleMucChannelPrivate *priv; g_assert (GABBLE_IS_MUC_CHANNEL (self)); priv = self->priv; tp_svc_channel_interface_password_return_from_get_password_flags (context, priv->must_provide_password ? TP_CHANNEL_PASSWORD_FLAG_PROVIDE : 0); } /** * gabble_muc_channel_provide_password * * Implements D-Bus method ProvidePassword * on interface org.freedesktop.Telepathy.Channel.Interface.Password * * @context: The D-Bus invocation context to use to return values * or throw an error. */ static void gabble_muc_channel_provide_password (TpSvcChannelInterfacePassword *iface, const gchar *password, DBusGMethodInvocation *context) { GabbleMucChannel *self = GABBLE_MUC_CHANNEL (iface); GabbleMucChannelPrivate *priv; g_assert (GABBLE_IS_MUC_CHANNEL (self)); priv = self->priv; if (!priv->must_provide_password || priv->password_ctx != NULL) { GError error = { TP_ERROR, TP_ERROR_NOT_AVAILABLE, "password cannot be provided in the current state" }; dbus_g_method_return_error (context, &error); } else { g_object_set (priv->wmuc, "password", password, NULL); send_join_request (self); priv->password_ctx = context; } } static void _gabble_muc_channel_message_sent_cb (GObject *source, GAsyncResult *res, gpointer user_data) { WockyPorter *porter = WOCKY_PORTER (source); _GabbleMUCSendMessageCtx *context = user_data; GabbleMucChannel *chan = context->channel; TpMessage *message = context->message; GError *error = NULL; if (wocky_porter_send_finish (porter, res, &error)) { tp_message_mixin_sent ((GObject *) chan, message, TP_MESSAGE_SENDING_FLAG_REPORT_DELIVERY, context->token, NULL); } else { tp_message_mixin_sent ((GObject *) chan, context->message, TP_MESSAGE_SENDING_FLAG_REPORT_DELIVERY, NULL, error); g_free (error); } g_object_unref (context->channel); g_object_unref (context->message); g_free (context->token); g_slice_free (_GabbleMUCSendMessageCtx, context); } /** * gabble_muc_channel_send * * Indirectly implements (via TpMessageMixin) D-Bus method Send on interface * org.freedesktop.Telepathy.Channel.Type.Text and D-Bus method SendMessage on * Channel.Interface.Messages */ static void gabble_muc_channel_send (GObject *obj, TpMessage *message, TpMessageSendingFlags flags) { GabbleMucChannel *self = GABBLE_MUC_CHANNEL (obj); TpBaseChannel *base = TP_BASE_CHANNEL (self); GabbleMucChannelPrivate *priv = self->priv; TpBaseConnection *base_conn; GabbleConnection *gabble_conn; _GabbleMUCSendMessageCtx *context = NULL; WockyStanza *stanza = NULL; WockyPorter *porter = NULL; GError *error = NULL; gchar *id = NULL; base_conn = tp_base_channel_get_connection (base); gabble_conn = GABBLE_CONNECTION (base_conn); tp_message_mixin_change_chat_state (obj, tp_base_channel_get_self_handle (base), TP_CHANNEL_CHAT_STATE_ACTIVE); stanza = gabble_message_util_build_stanza (message, gabble_conn, WOCKY_STANZA_SUB_TYPE_GROUPCHAT, TP_CHANNEL_CHAT_STATE_ACTIVE, priv->jid, FALSE, &id, &error); if (stanza != NULL) { context = g_slice_new0 (_GabbleMUCSendMessageCtx); context->channel = g_object_ref (obj); context->message = g_object_ref (message); context->token = id; porter = gabble_connection_dup_porter (gabble_conn); wocky_porter_send_async (porter, stanza, NULL, _gabble_muc_channel_message_sent_cb, context); g_object_unref (stanza); g_object_unref (porter); } else { tp_message_mixin_sent (obj, message, flags, NULL, error); g_error_free (error); } } gboolean gabble_muc_channel_send_invite (GabbleMucChannel *self, const gchar *jid, const gchar *message, gboolean continue_, GError **error) { TpBaseChannel *base = TP_BASE_CHANNEL (self); GabbleMucChannelPrivate *priv = self->priv; WockyStanza *msg; WockyNode *invite_node; gboolean result; g_signal_emit (self, signals[PRE_INVITE], 0, jid); msg = wocky_stanza_build ( WOCKY_STANZA_TYPE_MESSAGE, WOCKY_STANZA_SUB_TYPE_NONE, NULL, priv->jid, '(', "x", ':', NS_MUC_USER, '(', "invite", '@', "to", jid, '*', &invite_node, ')', ')', NULL); if (message != NULL && *message != '\0') { wocky_node_add_child_with_content (invite_node, "reason", message); } if (continue_) { wocky_node_add_child (invite_node, "continue"); } DEBUG ("sending MUC invitation for room %s to contact %s with reason " "\"%s\"", priv->jid, jid, message); result = _gabble_connection_send ( GABBLE_CONNECTION (tp_base_channel_get_connection (base)), msg, error); g_object_unref (msg); return result; } static gboolean gabble_muc_channel_add_member (GObject *obj, TpHandle handle, const gchar *message, GError **error) { GabbleMucChannel *self = GABBLE_MUC_CHANNEL (obj); TpBaseChannel *base = TP_BASE_CHANNEL (self); GabbleMucChannelPrivate *priv = self->priv; TpGroupMixin *mixin; const gchar *jid; mixin = TP_GROUP_MIXIN (obj); if (handle == mixin->self_handle) { TpBaseConnection *conn = tp_base_channel_get_connection (base); TpIntset *set_remove_members, *set_remote_pending; GArray *arr_members; /* are we already a member or in remote pending? */ if (tp_handle_set_is_member (mixin->members, handle) || tp_handle_set_is_member (mixin->remote_pending, handle)) { g_set_error (error, TP_ERROR, TP_ERROR_NOT_AVAILABLE, "already a member or in remote pending"); return FALSE; } /* add ourself to remote pending and remove the inviter's * main jid from the member list */ set_remove_members = tp_intset_new (); set_remote_pending = tp_intset_new (); arr_members = tp_handle_set_to_array (mixin->members); if (arr_members->len > 0) { tp_intset_add (set_remove_members, g_array_index (arr_members, guint, 0)); } g_array_unref (arr_members); tp_intset_add (set_remote_pending, handle); tp_group_mixin_add_handle_owner (obj, mixin->self_handle, tp_base_connection_get_self_handle (conn)); tp_group_mixin_change_members (obj, "", NULL, set_remove_members, NULL, set_remote_pending, 0, priv->invited ? TP_CHANNEL_GROUP_CHANGE_REASON_INVITED : TP_CHANNEL_GROUP_CHANGE_REASON_NONE); tp_intset_destroy (set_remove_members); tp_intset_destroy (set_remote_pending); /* seek to enter the room */ send_join_request (self); g_object_set (obj, "state", MUC_STATE_INITIATED, NULL); /* deny adding */ tp_group_mixin_change_flags (obj, 0, TP_CHANNEL_GROUP_FLAG_CAN_ADD); return TRUE; } /* check that we're indeed a member when attempting to invite others */ if (priv->state < MUC_STATE_JOINED) { g_set_error (error, TP_ERROR, TP_ERROR_NOT_AVAILABLE, "channel membership is required for inviting others"); return FALSE; } jid = tp_handle_inspect (TP_GROUP_MIXIN (self)->handle_repo, handle); return gabble_muc_channel_send_invite (self, jid, message, FALSE, error); } static void kick_request_reply_cb (GabbleConnection *conn, WockyStanza *sent_msg, WockyStanza *reply_msg, GObject *object, gpointer user_data) { if (wocky_stanza_extract_errors (reply_msg, NULL, NULL, NULL, NULL)) { DEBUG ("Failed to kick user %s from room", (const char *) user_data); } } static gboolean gabble_muc_channel_remove_member (GObject *obj, TpHandle handle, const gchar *message, GError **error) { GabbleMucChannel *chan = GABBLE_MUC_CHANNEL (obj); TpBaseChannel *base = TP_BASE_CHANNEL (chan); GabbleMucChannelPrivate *priv = chan->priv; TpGroupMixin *group = TP_GROUP_MIXIN (chan); WockyStanza *msg; WockyNode *item_node; const gchar *jid, *nick; gboolean result; if (handle == group->self_handle) { /* User wants to leave the MUC */ close_channel (chan, message, TRUE, group->self_handle, TP_CHANNEL_GROUP_CHANGE_REASON_NONE); return TRUE; } /* Otherwise, the user wants to kick someone. */ msg = wocky_stanza_build (WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_SET, NULL, priv->jid, '(', "query", ':', NS_MUC_ADMIN, '(', "item", '*', &item_node, ')', ')', NULL); jid = tp_handle_inspect (TP_GROUP_MIXIN (obj)->handle_repo, handle); nick = strchr (jid, '/'); if (nick != NULL) nick++; wocky_node_set_attributes (item_node, "nick", nick, "role", "none", NULL); if (*message != '\0') { wocky_node_add_child_with_content (item_node, "reason", message); } DEBUG ("sending MUC kick request for contact %u (%s) to room %s with reason " "\"%s\"", handle, jid, priv->jid, message); result = _gabble_connection_send_with_reply ( GABBLE_CONNECTION (tp_base_channel_get_connection (base)), msg, kick_request_reply_cb, obj, (gpointer) jid, error); g_object_unref (msg); return result; } static void request_config_form_reply_cb ( GObject *source, GAsyncResult *result, gpointer user_data); void gabble_muc_channel_update_configuration_async ( GabbleMucChannel *self, GHashTable *validated_properties, GAsyncReadyCallback callback, gpointer user_data) { GabbleMucChannelPrivate *priv = self->priv; TpBaseChannel *base = (TpBaseChannel *) self; GabbleConnection *conn = GABBLE_CONNECTION (tp_base_channel_get_connection (base)); WockyStanza *stanza; GSimpleAsyncResult *result = g_simple_async_result_new ((GObject *) self, callback, user_data, gabble_muc_channel_update_configuration_async); g_assert (priv->properties_being_updated == NULL); stanza = wocky_stanza_build ( WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_GET, NULL, priv->jid, '(', "query", ':', WOCKY_NS_MUC_OWNER, ')', NULL); conn_util_send_iq_async (conn, stanza, NULL, request_config_form_reply_cb, result); g_object_unref (stanza); priv->properties_being_updated = g_hash_table_ref (validated_properties); } gboolean gabble_muc_channel_update_configuration_finish ( GabbleMucChannel *self, GAsyncResult *result, GError **error) { wocky_implement_finish_void (self, gabble_muc_channel_update_configuration_async); } typedef const gchar * (*MapFieldFunc) (const GValue *value); typedef struct { const gchar *var; TpBaseRoomConfigProperty prop_id; MapFieldFunc map; } ConfigFormMapping; static const gchar * map_bool (const GValue *value) { return g_value_get_boolean (value) ? "1" : "0"; } static const gchar * map_bool_inverted (const GValue *value) { return g_value_get_boolean (value) ? "0" : "1"; } static const gchar * map_roomconfig_whois (const GValue *value) { return g_value_get_boolean (value) ? "moderators" : "anyone"; } static const gchar * map_owner_whois (const GValue *value) { return g_value_get_boolean (value) ? "admins" : "anyone"; } static ConfigFormMapping form_mappings[] = { { "anonymous", TP_BASE_ROOM_CONFIG_ANONYMOUS, map_bool }, { "muc#roomconfig_whois", TP_BASE_ROOM_CONFIG_ANONYMOUS, map_roomconfig_whois }, { "muc#owner_whois", TP_BASE_ROOM_CONFIG_ANONYMOUS, map_owner_whois }, { "members_only", TP_BASE_ROOM_CONFIG_INVITE_ONLY, map_bool }, { "muc#roomconfig_membersonly", TP_BASE_ROOM_CONFIG_INVITE_ONLY, map_bool }, { "muc#owner_inviteonly", TP_BASE_ROOM_CONFIG_INVITE_ONLY, map_bool }, { "moderated", TP_BASE_ROOM_CONFIG_MODERATED, map_bool }, { "muc#roomconfig_moderatedroom", TP_BASE_ROOM_CONFIG_MODERATED, map_bool }, { "muc#owner_moderatedroom", TP_BASE_ROOM_CONFIG_MODERATED, map_bool }, { "title", TP_BASE_ROOM_CONFIG_TITLE, g_value_get_string }, { "muc#roomconfig_roomname", TP_BASE_ROOM_CONFIG_TITLE, g_value_get_string }, { "muc#owner_roomname", TP_BASE_ROOM_CONFIG_TITLE, g_value_get_string }, { "muc#roomconfig_roomdesc", TP_BASE_ROOM_CONFIG_DESCRIPTION, g_value_get_string }, { "muc#owner_roomdesc", TP_BASE_ROOM_CONFIG_DESCRIPTION, g_value_get_string }, { "password", TP_BASE_ROOM_CONFIG_PASSWORD, g_value_get_string }, { "muc#roomconfig_roomsecret", TP_BASE_ROOM_CONFIG_PASSWORD, g_value_get_string }, { "muc#owner_roomsecret", TP_BASE_ROOM_CONFIG_PASSWORD, g_value_get_string }, { "password_protected", TP_BASE_ROOM_CONFIG_PASSWORD_PROTECTED, map_bool }, { "muc#roomconfig_passwordprotectedroom", TP_BASE_ROOM_CONFIG_PASSWORD_PROTECTED, map_bool }, { "muc#owner_passwordprotectedroom", TP_BASE_ROOM_CONFIG_PASSWORD_PROTECTED, map_bool }, { "persistent", TP_BASE_ROOM_CONFIG_PERSISTENT, map_bool }, { "muc#roomconfig_persistentroom", TP_BASE_ROOM_CONFIG_PERSISTENT, map_bool }, { "muc#owner_persistentroom", TP_BASE_ROOM_CONFIG_PERSISTENT, map_bool }, { "public", TP_BASE_ROOM_CONFIG_PRIVATE, map_bool_inverted }, { "muc#roomconfig_publicroom", TP_BASE_ROOM_CONFIG_PRIVATE, map_bool_inverted }, { "muc#owner_publicroom", TP_BASE_ROOM_CONFIG_PRIVATE, map_bool_inverted }, { NULL } }; static ConfigFormMapping * lookup_config_form_field (const gchar *var) { ConfigFormMapping *f; for (f = form_mappings; f->var != NULL; f++) if (strcmp (var, f->var) == 0) return f; DEBUG ("unknown field %s", var); return NULL; } static void request_config_form_submit_reply_cb ( GObject *source, GAsyncResult *result, gpointer user_data); static void request_config_form_reply_cb ( GObject *source, GAsyncResult *result, gpointer user_data) { GabbleConnection *conn = GABBLE_CONNECTION (source); GSimpleAsyncResult *update_result = G_SIMPLE_ASYNC_RESULT (user_data); GabbleMucChannel *chan = GABBLE_MUC_CHANNEL ( g_async_result_get_source_object ((GAsyncResult *) update_result)); GabbleMucChannelPrivate *priv = chan->priv; GHashTable *properties = priv->properties_being_updated; WockyStanza *reply = NULL; WockyStanza *submit_iq = NULL; WockyNode *form_node, *submit_node, *child; GError *error = NULL; guint i, props_left; WockyNodeIter j; if (!conn_util_send_iq_finish (conn, result, &reply, &error)) { g_prefix_error (&error, "failed to request configuration form: "); goto OUT; } form_node = config_form_get_form_node (reply); if (form_node == NULL) { g_set_error (&error, TP_ERROR, TP_ERROR_SERVICE_CONFUSED, "MUC configuration form didn't actually contain a form"); goto OUT; } /* initialize */ submit_iq = wocky_stanza_build ( WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_SET, NULL, priv->jid, '(', "query", ':', WOCKY_NS_MUC_OWNER, '(', "x", ':', WOCKY_XMPP_NS_DATA, '@', "type", "submit", '*', &submit_node, ')', ')', NULL); /* we assume that the number of props will fit in a guint on all supported * platforms, so fail at compile time if this is no longer the case */ #if TP_NUM_BASE_ROOM_CONFIG_PROPERTIES > 32 #error GabbleMUCChannel request_config_form_reply_cb needs porting to TpIntset #endif props_left = 0; for (i = 0; i < TP_NUM_BASE_ROOM_CONFIG_PROPERTIES; i++) { if (g_hash_table_lookup (properties, GUINT_TO_POINTER (i)) != NULL) props_left |= 1 << i; } wocky_node_iter_init (&j, form_node, "field", NULL); while (wocky_node_iter_next (&j, &child)) { const gchar *var, *type_str; WockyNode *field_node; ConfigFormMapping *f; GValue *value = NULL; var = wocky_node_get_attribute (child, "var"); if (var == NULL) { DEBUG ("skipping node '%s' because of lacking var attribute", child->name); continue; } f = lookup_config_form_field (var); /* add the corresponding field node to the reply message */ field_node = wocky_node_add_child (submit_node, "field"); wocky_node_set_attribute (field_node, "var", var); type_str = wocky_node_get_attribute (child, "type"); if (type_str != NULL) { wocky_node_set_attribute (field_node, "type", type_str); } if (f != NULL) value = g_hash_table_lookup (properties, GUINT_TO_POINTER (f->prop_id)); if (value != NULL) { const gchar *val_str; /* Known property and we have a value to set */ DEBUG ("transforming %s...", wocky_enum_to_nick (TP_TYPE_BASE_ROOM_CONFIG_PROPERTY, f->prop_id)); g_assert (f->map != NULL); val_str = f->map (value); /* add the corresponding value node(s) to the reply message */ DEBUG ("Setting value %s for %s", val_str, var); wocky_node_add_child_with_content (field_node, "value", val_str); props_left &= ~(1 << f->prop_id); } else { /* Copy all the <value> nodes */ WockyNodeIter k; WockyNode *value_node; wocky_node_iter_init (&k, child, "value", NULL); while (wocky_node_iter_next (&k, &value_node)) wocky_node_add_child_with_content (field_node, "value", value_node->content); } } if (props_left != 0) { GString *unsubstituted = g_string_new (""); printf ("\n%s: the following properties were not substituted:\n", G_STRFUNC); for (i = 0; i < TP_NUM_BASE_ROOM_CONFIG_PROPERTIES; i++) { if ((props_left & (1 << i)) != 0) { const gchar *name = wocky_enum_to_nick ( TP_TYPE_BASE_ROOM_CONFIG_PROPERTY, i); printf (" %s\n", name); if (unsubstituted->len > 0) g_string_append (unsubstituted, ", "); g_string_append (unsubstituted, name); } } printf ("\nthis is a MUC server compatibility bug in gabble, please " "report it with a full debug log attached (running gabble " "with WOCKY_DEBUG=xmpp)\n\n"); fflush (stdout); error = g_error_new (TP_ERROR, TP_ERROR_SERVICE_CONFUSED, "Couldn't find fields corresponding to %s in the muc#owner form. " "This is a MUC server compatibility bug in Gabble.", unsubstituted->str); g_string_free (unsubstituted, TRUE); goto OUT; } conn_util_send_iq_async (conn, submit_iq, NULL, request_config_form_submit_reply_cb, update_result); OUT: if (error != NULL) { g_simple_async_result_set_from_error (update_result, error); g_simple_async_result_complete (update_result); g_object_unref (update_result); tp_clear_pointer (&priv->properties_being_updated, g_hash_table_unref); g_clear_error (&error); } tp_clear_object (&reply); tp_clear_object (&submit_iq); g_object_unref (chan); } static void request_config_form_submit_reply_cb ( GObject *source, GAsyncResult *result, gpointer user_data) { GSimpleAsyncResult *update_result = G_SIMPLE_ASYNC_RESULT (user_data); GabbleMucChannel *chan = GABBLE_MUC_CHANNEL ( g_async_result_get_source_object ((GAsyncResult *) update_result)); GabbleMucChannelPrivate *priv = chan->priv; GError *error = NULL; if (!conn_util_send_iq_finish (GABBLE_CONNECTION (source), result, NULL, &error)) { g_prefix_error (&error, "submitted configuration form was rejected: "); g_simple_async_result_set_from_error (update_result, error); g_clear_error (&error); } g_simple_async_result_complete (update_result); tp_clear_pointer (&priv->properties_being_updated, g_hash_table_unref); /* Get the properties into a consistent state. */ room_properties_update (chan); g_object_unref (chan); g_object_unref (update_result); } static gboolean gabble_muc_channel_send_chat_state (GObject *object, TpChannelChatState state, GError **error) { GabbleMucChannel *self = GABBLE_MUC_CHANNEL (object); GabbleMucChannelPrivate *priv = self->priv; TpBaseChannel *base = TP_BASE_CHANNEL (self); if (priv->have_received_error_type_wait) return TRUE; return gabble_message_util_send_chat_state (G_OBJECT (self), GABBLE_CONNECTION (tp_base_channel_get_connection (base)), WOCKY_STANZA_SUB_TYPE_GROUPCHAT, state, priv->jid, error); } void gabble_muc_channel_send_presence (GabbleMucChannel *self) { GabbleMucChannelPrivate *priv = self->priv; TpBaseChannel *base = TP_BASE_CHANNEL (self); WockyStanza *stanza; /* do nothing if we havn't actually joined yet */ if (priv->state < MUC_STATE_INITIATED) return; stanza = wocky_muc_create_presence (priv->wmuc, WOCKY_STANZA_SUB_TYPE_NONE, NULL); _gabble_connection_send ( GABBLE_CONNECTION (tp_base_channel_get_connection (base)), stanza, NULL); g_object_unref (stanza); } #ifdef ENABLE_VOIP GabbleCallMucChannel * gabble_muc_channel_get_call (GabbleMucChannel *gmuc) { return gmuc->priv->call; } GList * gabble_muc_channel_get_call_channels (GabbleMucChannel *self) { return self->priv->calls; } static void muc_channel_call_state_changed_cb (GabbleCallMucChannel *muc, TpCallState state, TpCallFlags flags, GValueArray *reason, GHashTable *details, GabbleMucChannel *gmuc) { GabbleMucChannelPrivate *priv = gmuc->priv; if (state != TP_CALL_STATE_ENDED) return; if (priv->call == muc) priv->call = NULL; } static void muc_channel_call_closed_cb (GabbleCallMucChannel *muc, gpointer user_data) { GabbleMucChannel *gmuc = GABBLE_MUC_CHANNEL (user_data); GabbleMucChannelPrivate *priv = gmuc->priv; g_assert (g_list_find (priv->calls, muc) != NULL); /* closed the active muc */ if (priv->call == muc) priv->call = NULL; priv->calls = g_list_remove (priv->calls, muc); g_object_unref (muc); } static void muc_call_channel_finish_requests (GabbleMucChannel *self, GabbleCallMucChannel *call, GError *error) { GList *l; if (call != NULL) { GSList *requests = NULL; DEBUG ("Call channel created"); for (l = self->priv->call_requests ; l != NULL; l = g_list_next (l)) requests = g_slist_append (requests, g_simple_async_result_get_op_res_gpointer ( G_SIMPLE_ASYNC_RESULT(l->data))); g_signal_emit (self, signals[NEW_CALL], 0, call, requests); g_slist_free (requests); } else { DEBUG ("Failed to create call channel: %s", error->message); } for (l = self->priv->call_requests ; l != NULL; l = g_list_next (l)) { GSimpleAsyncResult *r = G_SIMPLE_ASYNC_RESULT (l->data); if (error != NULL) g_simple_async_result_set_from_error (r, error); g_simple_async_result_complete (r); g_object_unref (r); } g_list_free (self->priv->call_requests); self->priv->call_requests = NULL; } static void muc_channel_call_channel_done_cb (GObject *source, GAsyncResult *result, gpointer user_data) { GabbleMucChannel *gmuc = GABBLE_MUC_CHANNEL (user_data); GabbleMucChannelPrivate *priv = gmuc->priv; GError *error = NULL; g_assert (priv->call == NULL); if (tp_base_channel_is_destroyed (TP_BASE_CHANNEL (gmuc))) goto out; priv->call_initiating = FALSE; priv->call = gabble_call_muc_channel_new_finish (source, result, &error); if (priv->call == NULL) goto error; priv->calls = g_list_prepend (priv->calls, priv->call); g_signal_connect (priv->call, "closed", G_CALLBACK (muc_channel_call_closed_cb), gmuc); g_signal_connect (priv->call, "call-state-changed", G_CALLBACK (muc_channel_call_state_changed_cb), gmuc); error: muc_call_channel_finish_requests (gmuc, priv->call, error); g_clear_error (&error); out: /* we kept ourselves reffed while the call channel was being created */ g_object_unref (gmuc); } static void gabble_muc_channel_start_call_creation (GabbleMucChannel *gmuc, GHashTable *request) { GabbleMucChannelPrivate *priv = gmuc->priv; TpBaseChannel *base = TP_BASE_CHANNEL (gmuc); TpBaseConnection *base_conn = tp_base_channel_get_connection (base); const gchar *prefix; g_assert (!priv->call_initiating); g_assert (priv->call == NULL); priv->call_initiating = TRUE; /* We want to put the call channel "under" ourself, but let it decide its * exact path. TpBaseChannel makes this a little awkward — the vfunc for * picking your own path is supposed to return the suffix, and the base class * pastes on the connection's path before that. * * So... we pass the bit of our own path that's after the connection's path * to the call channel; it builds its suffix based on that and its own * address; and finally TpBaseChannel pastes the connection path back on. :) */ prefix = tp_base_channel_get_object_path (base) + strlen (tp_base_connection_get_object_path (base_conn)) + 1 /* for the slash */; /* Keep ourselves reffed while call channels are created */ g_object_ref (gmuc); gabble_call_muc_channel_new_async ( GABBLE_CONNECTION (base_conn), priv->requests_cancellable, prefix, gmuc, tp_base_channel_get_target_handle (base), request, muc_channel_call_channel_done_cb, gmuc); } void gabble_muc_channel_request_call (GabbleMucChannel *gmuc, GHashTable *request, gboolean require_new, gpointer token, GAsyncReadyCallback callback, gpointer user_data) { GabbleMucChannelPrivate *priv = gmuc->priv; GSimpleAsyncResult *res; /* FIXME: Ponder whether this function should even be used when the call * already exists and to have the return indicate that it was already * satisfied (instead of a newly created channel) */ g_assert (priv->call == NULL); if (require_new && priv->call_initiating) { g_simple_async_report_error_in_idle (G_OBJECT (gmuc), callback, user_data, TP_ERROR, TP_ERROR_NOT_AVAILABLE, "A request for a call is already in progress"); return; } if (!priv->call_initiating) gabble_muc_channel_start_call_creation (gmuc, request); res = g_simple_async_result_new (G_OBJECT (gmuc), callback, user_data, gabble_muc_channel_request_call_finish); g_simple_async_result_set_op_res_gpointer (res, token, NULL); priv->call_requests = g_list_append (priv->call_requests, res); } gboolean gabble_muc_channel_request_call_finish (GabbleMucChannel *gmuc, GAsyncResult *result, GError **error) { if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), error)) return FALSE; g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (gmuc), gabble_muc_channel_request_call_finish), FALSE); return TRUE; } gboolean gabble_muc_channel_handle_jingle_session (GabbleMucChannel *self, WockyJingleSession *session) { GabbleMucChannelPrivate *priv = self->priv; /* No Muji no need to handle call sessions */ if (priv->call == NULL) return FALSE; gabble_call_muc_channel_incoming_session (priv->call, session); return TRUE; } #endif void gabble_muc_channel_teardown (GabbleMucChannel *gmuc) { close_channel (gmuc, NULL, FALSE, 0, 0); } static void sent_subject_cb ( GObject *source, GAsyncResult *result, gpointer user_data) { GabbleMucChannel *self = GABBLE_MUC_CHANNEL (user_data); GabbleMucChannelPrivate *priv = self->priv; GError *error = NULL; if (!wocky_porter_send_finish (WOCKY_PORTER (source), result, &error)) { DEBUG ("buh, failed to send a <message> to change the subject: %s", error->message); if (priv->set_subject_context != NULL) { GError *tp_error = NULL; gabble_set_tp_error_from_wocky (error, &tp_error); return_from_set_subject (self, tp_error); g_clear_error (&tp_error); } g_clear_error (&error); } /* otherwise, we wait for a reply! */ g_object_unref (self); } static void gabble_muc_channel_set_subject (TpSvcChannelInterfaceSubject *iface, const gchar *subject, DBusGMethodInvocation *context) { GabbleMucChannel *self = GABBLE_MUC_CHANNEL (iface); GabbleMucChannelPrivate *priv = self->priv; GabbleConnection *conn = GABBLE_CONNECTION (tp_base_channel_get_connection ( TP_BASE_CHANNEL (self))); WockyPorter *porter = wocky_session_get_porter (conn->session); if (priv->state < MUC_STATE_JOINED) { GError error = { TP_ERROR, TP_ERROR_NOT_AVAILABLE, "Steady on. You're not in the room yet" }; dbus_g_method_return_error (context, &error); } else if (priv->state > MUC_STATE_JOINED || priv->closing) { GError error = { TP_ERROR, TP_ERROR_NOT_AVAILABLE, "Already left/leaving the room" }; dbus_g_method_return_error (context, &error); } else if (priv->set_subject_context != NULL) { GError error = { TP_ERROR, TP_ERROR_NOT_AVAILABLE, "Hey! Stop changing the subject! (Your last request is still in " "flight.)" }; dbus_g_method_return_error (context, &error); } else { WockyXmppConnection *xmpp_conn; WockyStanza *stanza; g_assert (priv->set_subject_stanza_id == NULL); g_object_get (porter, "connection", &xmpp_conn, NULL); priv->set_subject_stanza_id = wocky_xmpp_connection_new_id (xmpp_conn); g_object_unref (xmpp_conn); stanza = wocky_stanza_build ( WOCKY_STANZA_TYPE_MESSAGE, WOCKY_STANZA_SUB_TYPE_GROUPCHAT, NULL, priv->jid, '@', "id", priv->set_subject_stanza_id, '(', "subject", '$', subject, ')', NULL); priv->set_subject_context = context; wocky_porter_send_async (porter, stanza, NULL, sent_subject_cb, g_object_ref (self)); g_object_unref (stanza); } } static void password_iface_init (gpointer g_iface, gpointer iface_data) { TpSvcChannelInterfacePasswordClass *klass = (TpSvcChannelInterfacePasswordClass *) g_iface; #define IMPLEMENT(x) tp_svc_channel_interface_password_implement_##x (\ klass, gabble_muc_channel_##x) IMPLEMENT(get_password_flags); IMPLEMENT(provide_password); #undef IMPLEMENT } static void subject_iface_init (gpointer g_iface, gpointer iface_data) { TpSvcChannelInterfaceSubjectClass *klass = (TpSvcChannelInterfaceSubjectClass *) g_iface; #define IMPLEMENT(x) tp_svc_channel_interface_subject_implement_##x (\ klass, gabble_muc_channel_##x) IMPLEMENT(set_subject); #undef IMPLEMENT } �����������������������������������telepathy-gabble-0.18.2/src/muc-channel.h�����������������������������������������������������������0000644�0001750�0001750�00000011014�12200204333�020131� 0����������������������������������������������������������������������������������������������������ustar�00smcv����������������������������smcv����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * gabble-muc-channel.h - Header for GabbleMucChannel * Copyright (C) 2006 Collabora Ltd. * Copyright (C) 2006 Nokia Corporation * @author Ole Andre Vadla Ravnaas <ole.andre.ravnaas@collabora.co.uk> * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 __GABBLE_MUC_CHANNEL_H__ #define __GABBLE_MUC_CHANNEL_H__ #include "config.h" #include <glib-object.h> #include <gio/gio.h> #include <telepathy-glib/telepathy-glib.h> #include "types.h" #ifdef ENABLE_VOIP #include "call-muc-channel.h" #endif #include "tube-iface.h" G_BEGIN_DECLS typedef enum { MUC_STATE_CREATED = 0, MUC_STATE_INITIATED, MUC_STATE_AUTH, MUC_STATE_JOINED, MUC_STATE_ENDED, } GabbleMucState; typedef struct _GabbleMucChannelClass GabbleMucChannelClass; typedef struct _GabbleMucChannelPrivate GabbleMucChannelPrivate; struct _GabbleMucChannelClass { TpBaseChannelClass parent_class; TpGroupMixinClass group_class; TpDBusPropertiesMixinClass dbus_props_class; }; struct _GabbleMucChannel { TpBaseChannel parent; TpGroupMixin group; TpMessageMixin message_mixin; GabbleMucChannelPrivate *priv; }; GType gabble_muc_channel_get_type (void); /* TYPE MACROS */ #define GABBLE_TYPE_MUC_CHANNEL \ (gabble_muc_channel_get_type ()) #define GABBLE_MUC_CHANNEL(obj) \ (G_TYPE_CHECK_INSTANCE_CAST((obj), GABBLE_TYPE_MUC_CHANNEL, \ GabbleMucChannel)) #define GABBLE_MUC_CHANNEL_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST((klass), GABBLE_TYPE_MUC_CHANNEL,\ GabbleMucChannelClass)) #define GABBLE_IS_MUC_CHANNEL(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE((obj), GABBLE_TYPE_MUC_CHANNEL)) #define GABBLE_IS_MUC_CHANNEL_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE((klass), GABBLE_TYPE_MUC_CHANNEL)) #define GABBLE_MUC_CHANNEL_GET_CLASS(obj) \ (G_TYPE_INSTANCE_GET_CLASS ((obj), GABBLE_TYPE_MUC_CHANNEL,\ GabbleMucChannelClass)) gboolean _gabble_muc_channel_is_ready (GabbleMucChannel *chan); void gabble_muc_channel_set_autoclose (GabbleMucChannel *chan, gboolean autoclose); gboolean gabble_muc_channel_get_autoclose (GabbleMucChannel *chan); gboolean gabble_muc_channel_can_be_closed (GabbleMucChannel *chan); void gabble_muc_channel_send_presence (GabbleMucChannel *chan); gboolean gabble_muc_channel_send_invite (GabbleMucChannel *self, const gchar *jid, const gchar *message, gboolean continue_, GError **error); GabbleTubeIface * gabble_muc_channel_tube_request (GabbleMucChannel *self, gpointer request_token, GHashTable *request_properties, gboolean require_new); void gabble_muc_channel_foreach_tubes (GabbleMucChannel *gmuc, TpExportableChannelFunc foreach, gpointer user_data); void gabble_muc_channel_handle_si_stream_request (GabbleMucChannel *self, GabbleBytestreamIface *bytestream, const gchar *stream_id, WockyStanza *msg); #ifdef ENABLE_VOIP GabbleCallMucChannel * gabble_muc_channel_get_call (GabbleMucChannel *gmuc); GList * gabble_muc_channel_get_call_channels (GabbleMucChannel *self); void gabble_muc_channel_request_call (GabbleMucChannel *gmuc, GHashTable *request, gboolean require_new, gpointer token, GAsyncReadyCallback callback, gpointer user_data); gboolean gabble_muc_channel_request_call_finish (GabbleMucChannel *gmuc, GAsyncResult *result, GError **error); gboolean gabble_muc_channel_handle_jingle_session (GabbleMucChannel *channel, WockyJingleSession *session); #endif void gabble_muc_channel_update_configuration_async ( GabbleMucChannel *self, GHashTable *validated_properties, GAsyncReadyCallback callback, gpointer user_data); gboolean gabble_muc_channel_update_configuration_finish ( GabbleMucChannel *self, GAsyncResult *result, GError **error); void gabble_muc_channel_teardown (GabbleMucChannel *gmuc); G_END_DECLS #endif /* #ifndef __GABBLE_MUC_CHANNEL_H__*/ ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������telepathy-gabble-0.18.2/src/message-util.c����������������������������������������������������������0000644�0001750�0001750�00000037054�12227000321�020345� 0����������������������������������������������������������������������������������������������������ustar�00smcv����������������������������smcv����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * message-util.c - Messages interface utility functions * Copyright (C) 2006-2008 Collabora Ltd. * Copyright (C) 2006-2008 Nokia Corporation * @author Ole Andre Vadla Ravnaas <ole.andre.ravnaas@collabora.co.uk> * @author Robert McQueen <robert.mcqueen@collabora.co.uk> * @author Senko Rasic <senko@senko.net> * @author Will Thompson <will.thompson@collabora.co.uk> * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 "config.h" #include "message-util.h" #include <string.h> #include <time.h> #include <telepathy-glib/telepathy-glib.h> #include <wocky/wocky.h> #define DEBUG_FLAG GABBLE_DEBUG_IM #include "debug.h" #include "namespaces.h" #include "util.h" void gabble_message_util_add_chat_state (WockyStanza *stanza, TpChannelChatState state) { WockyNode *n = wocky_stanza_get_top_node (stanza); const gchar *node_name = NULL; switch (state) { case TP_CHANNEL_CHAT_STATE_GONE: node_name = "gone"; break; case TP_CHANNEL_CHAT_STATE_INACTIVE: node_name = "inactive"; break; case TP_CHANNEL_CHAT_STATE_ACTIVE: node_name = "active"; break; case TP_CHANNEL_CHAT_STATE_PAUSED: node_name = "paused"; break; case TP_CHANNEL_CHAT_STATE_COMPOSING: node_name = "composing"; break; } if (node_name != NULL) wocky_node_add_child_ns_q (n, node_name, g_quark_from_static_string (NS_CHAT_STATES)); } /** * gabble_message_util_build_stanza * @message: the message to be sent * @conn: the connection owning this channel * @subtype: the Loudmouth message subtype * @state: the Telepathy chat state, or -1 if unknown or not applicable * @recipient: the recipient's JID * @send_nick: whether to include our own nick in the message * @token: return the message id * @error: return the error if operation failed * * Returns: The wocky stanza for the message */ WockyStanza * gabble_message_util_build_stanza (TpMessage *message, GabbleConnection *conn, WockyStanzaSubType subtype, TpChannelChatState state, const char *recipient, gboolean send_nick, gchar **token, GError **error) { const GHashTable *part; WockyStanza *stanza = NULL; WockyNode *node; guint type = TP_CHANNEL_TEXT_MESSAGE_TYPE_NORMAL; gboolean result = TRUE; const gchar *content_type, *text; gchar *id = NULL; guint n_parts; #define RETURN_INVALID_ARGUMENT(msg, ...) \ G_STMT_START { \ DEBUG (msg , ## __VA_ARGS__); \ g_set_error (error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT, \ msg , ## __VA_ARGS__); \ return NULL; \ } G_STMT_END part = tp_message_peek (message, 0); if (tp_asv_lookup (part, "message-type") != NULL) type = tp_asv_get_uint32 (part, "message-type", &result); if (!result) RETURN_INVALID_ARGUMENT ("message-type must be a 32-bit unsigned integer"); if (type >= NUM_TP_CHANNEL_TEXT_MESSAGE_TYPES) RETURN_INVALID_ARGUMENT ("invalid message type: %u", type); n_parts = tp_message_count_parts (message); if (n_parts != 2) RETURN_INVALID_ARGUMENT ("message must contain exactly 1 part, not %u", (n_parts - 1)); part = tp_message_peek (message, 1); content_type = tp_asv_get_string (part, "content-type"); text = tp_asv_get_string (part, "content"); if (content_type == NULL || tp_strdiff (content_type, "text/plain")) RETURN_INVALID_ARGUMENT ("message must be text/plain"); if (text == NULL) RETURN_INVALID_ARGUMENT ("content must be a UTF-8 string"); if (!subtype) { switch (type) { case TP_CHANNEL_TEXT_MESSAGE_TYPE_NORMAL: case TP_CHANNEL_TEXT_MESSAGE_TYPE_ACTION: subtype = WOCKY_STANZA_SUB_TYPE_CHAT; break; case TP_CHANNEL_TEXT_MESSAGE_TYPE_NOTICE: subtype = WOCKY_STANZA_SUB_TYPE_NORMAL; break; } } /* Generate a UUID for the message */ id = gabble_generate_id (); tp_message_set_string (message, 0, "message-token", id); stanza = wocky_stanza_build (WOCKY_STANZA_TYPE_MESSAGE, subtype, NULL, recipient, '@', "id", id, '*', &node, NULL); if (send_nick) lm_message_node_add_own_nick (node, conn); if (type == TP_CHANNEL_TEXT_MESSAGE_TYPE_ACTION) { gchar *tmp; tmp = g_strconcat ("/me ", text, NULL); wocky_node_add_child_with_content (node, "body", tmp); g_free (tmp); } else { wocky_node_add_child_with_content (node, "body", text); } gabble_message_util_add_chat_state (stanza, state); if (token != NULL) *token = id; else g_free (id); gabble_connection_update_last_use (conn); return stanza; } /** * gabble_message_util_send_chat_state: * @obj: a channel implementation featuring TpMessageMixin * @conn: the connection owning this channel * @subtype: the Loudmouth message subtype * @state: the Telepathy chat state * @recipient: the recipient's JID * @error: pointer in which to return a GError in case of failure. * * Returns: %TRUE if the message was sent successfully; %FALSE otherwise. */ gboolean gabble_message_util_send_chat_state (GObject *obj, GabbleConnection *conn, WockyStanzaSubType subtype, TpChannelChatState state, const char *recipient, GError **error) { WockyStanza *msg = wocky_stanza_build (WOCKY_STANZA_TYPE_MESSAGE, subtype, NULL, recipient, NULL); gboolean result; gabble_message_util_add_chat_state (msg, state); result = _gabble_connection_send (conn, msg, error); g_object_unref (msg); return result; } TpChannelTextSendError gabble_tp_send_error_from_wocky_xmpp_error (WockyXmppError err) { switch (err) { /* Note: Google replies with <service-unavailable/> if you send a * message to someone you're not subscribed to. But * http://xmpp.org/rfcs/rfc3921.html#rules explicitly says that means * the user is offline and doesn't have offline storage. I think Google * should be returning <forbidden/> or <not-authorized/>. --wjt */ case WOCKY_XMPP_ERROR_SERVICE_UNAVAILABLE: case WOCKY_XMPP_ERROR_RECIPIENT_UNAVAILABLE: return TP_CHANNEL_TEXT_SEND_ERROR_OFFLINE; case WOCKY_XMPP_ERROR_ITEM_NOT_FOUND: case WOCKY_XMPP_ERROR_JID_MALFORMED: case WOCKY_XMPP_ERROR_REMOTE_SERVER_TIMEOUT: return TP_CHANNEL_TEXT_SEND_ERROR_INVALID_CONTACT; case WOCKY_XMPP_ERROR_FORBIDDEN: case WOCKY_XMPP_ERROR_NOT_AUTHORIZED: case WOCKY_XMPP_ERROR_POLICY_VIOLATION: return TP_CHANNEL_TEXT_SEND_ERROR_PERMISSION_DENIED; case WOCKY_XMPP_ERROR_RESOURCE_CONSTRAINT: return TP_CHANNEL_TEXT_SEND_ERROR_TOO_LONG; case WOCKY_XMPP_ERROR_FEATURE_NOT_IMPLEMENTED: return TP_CHANNEL_TEXT_SEND_ERROR_NOT_IMPLEMENTED; default: return TP_CHANNEL_TEXT_SEND_ERROR_UNKNOWN; } } static TpChannelTextSendError _tp_send_error_from_xmpp_error ( WockyXmppErrorType error_type, GError *error, TpDeliveryStatus *delivery_status) { /* The thing calling us should have got this back from * wocky_stanza_extract_errors(). */ g_assert (error->domain == WOCKY_XMPP_ERROR); DEBUG ("got xmpp error: %s (type=%u): '%s'", wocky_xmpp_stanza_error_to_string (error), error_type, error->message); if (error_type == WOCKY_XMPP_ERROR_TYPE_WAIT) *delivery_status = TP_DELIVERY_STATUS_TEMPORARILY_FAILED; else *delivery_status = TP_DELIVERY_STATUS_PERMANENTLY_FAILED; /* these are based on descriptions of errors, and some testing */ switch (error->code) { /* Note: Google replies with <service-unavailable/> if you send a * message to someone you're not subscribed to. But * http://xmpp.org/rfcs/rfc3921.html#rules explicitly says that means * the user is offline and doesn't have offline storage. I think Google * should be returning <forbidden/> or <not-authorized/>. --wjt */ case WOCKY_XMPP_ERROR_SERVICE_UNAVAILABLE: case WOCKY_XMPP_ERROR_RECIPIENT_UNAVAILABLE: return TP_CHANNEL_TEXT_SEND_ERROR_OFFLINE; case WOCKY_XMPP_ERROR_ITEM_NOT_FOUND: case WOCKY_XMPP_ERROR_JID_MALFORMED: case WOCKY_XMPP_ERROR_REMOTE_SERVER_TIMEOUT: return TP_CHANNEL_TEXT_SEND_ERROR_INVALID_CONTACT; case WOCKY_XMPP_ERROR_FORBIDDEN: case WOCKY_XMPP_ERROR_NOT_AUTHORIZED: return TP_CHANNEL_TEXT_SEND_ERROR_PERMISSION_DENIED; case WOCKY_XMPP_ERROR_RESOURCE_CONSTRAINT: return TP_CHANNEL_TEXT_SEND_ERROR_TOO_LONG; case WOCKY_XMPP_ERROR_FEATURE_NOT_IMPLEMENTED: return TP_CHANNEL_TEXT_SEND_ERROR_NOT_IMPLEMENTED; default: return TP_CHANNEL_TEXT_SEND_ERROR_UNKNOWN; } } static gint _tp_chat_state_from_message (WockyStanza *message) { WockyNode *node; #define MAP_TO(str, state) \ node = wocky_node_get_child_ns ( \ wocky_stanza_get_top_node (message), str, \ NS_CHAT_STATES); \ if (node != NULL) \ return state; MAP_TO ("active", TP_CHANNEL_CHAT_STATE_ACTIVE); MAP_TO ("composing", TP_CHANNEL_CHAT_STATE_COMPOSING); MAP_TO ("inactive", TP_CHANNEL_CHAT_STATE_INACTIVE); MAP_TO ("paused", TP_CHANNEL_CHAT_STATE_PAUSED); MAP_TO ("gone", TP_CHANNEL_CHAT_STATE_GONE); #undef MAP_TO return -1; } /** * gabble_message_util_parse_incoming_message: * @message: an incoming XMPP message * @from: will be set to the message sender's jid. * @stamp: will be set to the message's timestamp if it's a delayed message, or * to 0 otherwise. * @msgtype: will be set to the message's type. * @body_ret: will be set to the contents of the message's body, or %NULL if it * had no body. * @state: will be set to the %TpChannelChatState of the message, or -1 if * there was no chat state in the message. * @send_error: set to the relevant send error if the message contained an * error node, or to %GABBLE_TEXT_CHANNEL_SEND_NO_ERROR otherwise. * @delivery_status: set to TemporarilyFailed if an <error type="wait"/> is * encountered, to PermanentlyFailed if any other <error/> is * encountered, and to Unknown otherwise. * * Parses an incoming <message> stanza, producing various bits of the message * as various out parameters. * * Returns: %TRUE if the <message> was successfully parsed, even if it * contained no body, chat state or send error; %FALSE otherwise. */ gboolean gabble_message_util_parse_incoming_message (WockyStanza *message, const gchar **from, time_t *stamp, TpChannelTextMessageType *msgtype, const gchar **id, const gchar **body_ret, gint *state, TpChannelTextSendError *send_error, TpDeliveryStatus *delivery_status) { const gchar *type, *body; WockyNode *node; WockyXmppErrorType error_type; GError *error = NULL; *send_error = GABBLE_TEXT_CHANNEL_SEND_NO_ERROR; *delivery_status = TP_DELIVERY_STATUS_UNKNOWN; if (wocky_stanza_extract_errors (message, &error_type, &error, NULL, NULL)) { *send_error = _tp_send_error_from_xmpp_error (error_type, error, delivery_status); g_clear_error (&error); } *id = wocky_node_get_attribute (wocky_stanza_get_top_node (message), "id"); *from = wocky_node_get_attribute (wocky_stanza_get_top_node (message), "from"); if (*from == NULL) { STANZA_DEBUG (message, "got a message without a from field"); return FALSE; } type = wocky_node_get_attribute (wocky_stanza_get_top_node (message), "type"); /* * Parse timestamp of delayed messages. For non-delayed, it's * 0 and the channel code should set the current timestamp. */ *stamp = 0; node = wocky_node_get_child_ns ( wocky_stanza_get_top_node (message), "x", NS_X_DELAY); if (node != NULL) { const gchar *stamp_str; /* These timestamps do not contain a timezone, but are understood to be * in GMT. They're in the format yyyymmddThhmmss, so if we append 'Z' * we'll get (one of the many valid syntaxes for) an ISO-8601 timestamp. */ stamp_str = wocky_node_get_attribute (node, "stamp"); if (stamp_str != NULL) { GTimeVal timeval = { 0, 0 }; gchar *stamp_dup = g_strdup_printf ("%sZ", stamp_str); if (g_time_val_from_iso8601 (stamp_dup, &timeval)) { *stamp = timeval.tv_sec; } else { DEBUG ("%s: malformed date string '%s' for jabber:x:delay", G_STRFUNC, stamp_str); } g_free (stamp_dup); } } /* * Parse body if it exists. */ node = wocky_node_get_child (wocky_stanza_get_top_node (message), "body"); if (node) { body = node->content; } else { body = NULL; } /* Messages starting with /me are ACTION messages, and the /me should be * removed. type="chat" messages are NORMAL. everything else is * something that doesn't necessarily expect a reply or ongoing * conversation ("normal") or has been auto-sent, so we make it NOTICE in * all other cases. */ *msgtype = TP_CHANNEL_TEXT_MESSAGE_TYPE_NOTICE; *body_ret = body; if (body != NULL) { if (wocky_node_get_child_ns ( wocky_stanza_get_top_node (message), "google-rbc-announcement", "google:metadata") != NULL) { /* Fixes: https://bugs.freedesktop.org/show_bug.cgi?id=36647 */ return FALSE; } if (type == NULL && wocky_node_get_child_ns ( wocky_stanza_get_top_node (message), "time", "google:timestamp") != NULL && wocky_node_get_child_ns ( wocky_stanza_get_top_node (message), "x", "jabber:x:delay") != NULL) { /* Google servers send offline messages without a type. Work around * this. */ *msgtype = TP_CHANNEL_TEXT_MESSAGE_TYPE_NORMAL; } else if (0 == strncmp (body, "/me ", 4)) { *msgtype = TP_CHANNEL_TEXT_MESSAGE_TYPE_ACTION; *body_ret = body + 4; } else if (type != NULL && (0 == strcmp (type, "chat") || 0 == strcmp (type, "groupchat"))) { *msgtype = TP_CHANNEL_TEXT_MESSAGE_TYPE_NORMAL; *body_ret = body; } } /* Parse chat state if it exists. */ *state = _tp_chat_state_from_message (message); return TRUE; } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������telepathy-gabble-0.18.2/src/message-util.h����������������������������������������������������������0000644�0001750�0001750�00000004107�12200204333�020343� 0����������������������������������������������������������������������������������������������������ustar�00smcv����������������������������smcv����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * message-util.h - Header for Messages interface utility functions * Copyright (C) 2008 Collabora Ltd. * Copyright (C) 2008 Nokia Corporation * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 __GABBLE_MESSAGE_UTIL_H__ #define __GABBLE_MESSAGE_UTIL_H__ #include <telepathy-glib/telepathy-glib.h> #include <wocky/wocky.h> #include <wocky/wocky.h> #include "connection.h" G_BEGIN_DECLS void gabble_message_util_add_chat_state (WockyStanza *stanza, TpChannelChatState state); WockyStanza * gabble_message_util_build_stanza (TpMessage *message, GabbleConnection *conn, WockyStanzaSubType subtype, TpChannelChatState state, const char *recipient, gboolean send_nick, gchar **token, GError **error); gboolean gabble_message_util_send_chat_state (GObject *obj, GabbleConnection *conn, WockyStanzaSubType subtype, TpChannelChatState state, const char *recipient, GError **error); #define GABBLE_TEXT_CHANNEL_SEND_NO_ERROR ((TpChannelTextSendError)-1) gboolean gabble_message_util_parse_incoming_message (WockyStanza *message, const gchar **from, time_t *stamp, TpChannelTextMessageType *msgtype, const gchar **id, const gchar **body_ret, gint *state, TpChannelTextSendError *send_error, TpDeliveryStatus *delivery_status); TpChannelTextSendError gabble_tp_send_error_from_wocky_xmpp_error (WockyXmppError err); G_END_DECLS #endif /* #ifndef __GABBLE_MESSAGE_UTIL_H__ */ ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������telepathy-gabble-0.18.2/src/legacy-caps.c�����������������������������������������������������������0000644�0001750�0001750�00000002516�12227000321�020131� 0����������������������������������������������������������������������������������������������������ustar�00smcv����������������������������smcv����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * legacy-caps.c - Connection.Interface.Capabilities constants and utilities * Copyright (C) 2005 Collabora Ltd. * Copyright (C) 2005 Nokia Corporation * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 "config.h" #include "legacy-caps.h" #include <telepathy-glib/telepathy-glib-dbus.h> #define DEBUG_FLAG GABBLE_DEBUG_PRESENCE #include "debug.h" #ifdef ENABLE_VOIP #include "media-factory.h" #endif const CapabilityConversionData capabilities_conversions[] = { #ifdef ENABLE_VOIP { TP_IFACE_CHANNEL_TYPE_STREAMED_MEDIA, _gabble_media_factory_typeflags_to_caps, _gabble_media_factory_caps_to_typeflags }, #endif { NULL, NULL, NULL} }; ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������telepathy-gabble-0.18.2/src/legacy-caps.h�����������������������������������������������������������0000644�0001750�0001750�00000002653�12227000321�020140� 0����������������������������������������������������������������������������������������������������ustar�00smcv����������������������������smcv����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * legacy-caps.h - Connection.Interface.Capabilities constants and utilities * Copyright (C) 2005 Collabora Ltd. * Copyright (C) 2005 Nokia Corporation * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 __GABBLE_LEGACY_CAPS__H__ #define __GABBLE_LEGACY_CAPS__H__ #include <glib-object.h> #include "gabble/capabilities.h" typedef void (*TypeFlagsToCapsFunc) (guint typeflags, GabbleCapabilitySet *caps); typedef guint (*CapsToTypeFlagsFunc) (const GabbleCapabilitySet *caps); typedef struct _CapabilityConversionData CapabilityConversionData; struct _CapabilityConversionData { const gchar *iface; TypeFlagsToCapsFunc tf2c_fn; CapsToTypeFlagsFunc c2tf_fn; }; extern const CapabilityConversionData capabilities_conversions[]; #endif �������������������������������������������������������������������������������������telepathy-gabble-0.18.2/src/im-factory.c������������������������������������������������������������0000644�0001750�0001750�00000060747�12200204333�020025� 0����������������������������������������������������������������������������������������������������ustar�00smcv����������������������������smcv����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * im-factory.c - Source for GabbleImFactory * Copyright (C) 2006 Collabora Ltd. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 "config.h" #include "im-factory.h" #include <string.h> #include <dbus/dbus-glib.h> #include <dbus/dbus-glib-lowlevel.h> #include <telepathy-glib/telepathy-glib.h> #include <telepathy-glib/telepathy-glib-dbus.h> #include <wocky/wocky.h> #define DEBUG_FLAG GABBLE_DEBUG_IM #include "extensions/extensions.h" #include "gabble/caps-channel-manager.h" #include "connection.h" #include "debug.h" #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); G_DEFINE_TYPE_WITH_CODE (GabbleImFactory, gabble_im_factory, G_TYPE_OBJECT, G_IMPLEMENT_INTERFACE (TP_TYPE_CHANNEL_MANAGER, channel_manager_iface_init); G_IMPLEMENT_INTERFACE (GABBLE_TYPE_CAPS_CHANNEL_MANAGER, caps_channel_manager_iface_init)); /* properties */ enum { PROP_CONNECTION = 1, LAST_PROPERTY }; struct _GabbleImFactoryPrivate { GabbleConnection *conn; guint message_cb_id; guint delivery_report_cb_id; GHashTable *channels; gulong status_changed_id; gboolean dispose_has_run; }; static void gabble_im_factory_init (GabbleImFactory *self) { self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, GABBLE_TYPE_IM_FACTORY, GabbleImFactoryPrivate); self->priv->channels = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, g_object_unref); self->priv->conn = NULL; self->priv->dispose_has_run = FALSE; } 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 void gabble_im_factory_constructed (GObject *obj) { 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); tp_g_signal_connect_object (self->priv->conn, "porter-available", (GCallback) porter_available_cb, obj, 0); } static void gabble_im_factory_close_all (GabbleImFactory *); static void gabble_im_factory_dispose (GObject *object) { GabbleImFactory *fac = GABBLE_IM_FACTORY (object); GabbleImFactoryPrivate *priv = fac->priv; if (priv->dispose_has_run) return; DEBUG ("dispose called"); priv->dispose_has_run = TRUE; gabble_im_factory_close_all (fac); g_assert (priv->channels == NULL); if (G_OBJECT_CLASS (gabble_im_factory_parent_class)->dispose) G_OBJECT_CLASS (gabble_im_factory_parent_class)->dispose (object); } static void gabble_im_factory_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { GabbleImFactory *fac = GABBLE_IM_FACTORY (object); GabbleImFactoryPrivate *priv = fac->priv; switch (property_id) { case PROP_CONNECTION: g_value_set_object (value, priv->conn); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void gabble_im_factory_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { GabbleImFactory *fac = GABBLE_IM_FACTORY (object); GabbleImFactoryPrivate *priv = fac->priv; switch (property_id) { case PROP_CONNECTION: priv->conn = g_value_get_object (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void gabble_im_factory_class_init (GabbleImFactoryClass *gabble_im_factory_class) { GObjectClass *object_class = G_OBJECT_CLASS (gabble_im_factory_class); GParamSpec *param_spec; g_type_class_add_private (gabble_im_factory_class, sizeof (GabbleImFactoryPrivate)); object_class->constructed = gabble_im_factory_constructed; object_class->dispose = gabble_im_factory_dispose; object_class->get_property = gabble_im_factory_get_property; object_class->set_property = gabble_im_factory_set_property; param_spec = g_param_spec_object ("connection", "GabbleConnection object", "Gabble connection object that owns this IM channel manager object.", GABBLE_TYPE_CONNECTION, G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, PROP_CONNECTION, param_spec); } static GabbleIMChannel *get_channel_for_incoming_message ( GabbleImFactory *self, const gchar *jid, gboolean create_if_missing); /** * im_factory_message_cb: * * Called by Wocky when we get an incoming <message>. */ static gboolean im_factory_message_cb ( WockyPorter *porter, WockyStanza *message, gpointer user_data) { GabbleImFactory *fac = GABBLE_IM_FACTORY (user_data); const gchar *from, *body, *id; time_t stamp; TpChannelTextMessageType msgtype; 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)) return TRUE; if (body == NULL && state == -1) { return FALSE; } /* 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) { 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 TRUE; } if (send_error != GABBLE_TEXT_CHANNEL_SEND_NO_ERROR) { if (body == NULL) { DEBUG ("ignoring error sending chat state to %s", from); return TRUE; } DEBUG ("got error sending to %s, msgtype %u, body:\n%s", from, msgtype, body); _gabble_im_channel_report_delivery (chan, msgtype, stamp, id, body, send_error, delivery_status); } else if (body != NULL) { _gabble_im_channel_receive (chan, message, msgtype, from, stamp, id, body, state); } else if (state != -1) { _gabble_im_channel_state_receive (chan, (TpChannelChatState) state); } return TRUE; } /* Signals incoming delivery receipts. http://xmpp.org/extensions/xep-0184.html */ static gboolean im_factory_receipt_cb ( WockyPorter *porter, WockyStanza *message, gpointer user_data) { GabbleImFactory *self = GABBLE_IM_FACTORY (user_data); WockyNode *received; const gchar *from, *received_id; GabbleIMChannel *channel; received = wocky_node_get_child_ns (wocky_stanza_get_top_node (message), "received", NS_RECEIPTS); g_return_val_if_fail (received != NULL, FALSE); received_id = wocky_node_get_attribute (received, "id"); if (received_id == NULL) { STANZA_DEBUG (message, "but *what* did you receive?!"); return TRUE; } from = wocky_stanza_get_from (message); channel = get_channel_for_incoming_message (self, from, FALSE); if (channel == NULL) { DEBUG ("no existing channel with '%s'; ignoring receipt", from); return TRUE; } gabble_im_channel_receive_receipt (channel, received_id); return TRUE; } /** * im_channel_closed_cb: * * Signal callback for when an IM channel is closed. Removes the references * that #GabbleConnection holds to them - unless the channel has pending * messages, in which case it is re-announced (so from the perspective of the * D-Bus API, it was replaced by an identical channel). */ static void im_channel_closed_cb (GabbleIMChannel *chan, gpointer user_data) { GabbleImFactory *self = GABBLE_IM_FACTORY (user_data); GabbleImFactoryPrivate *priv = self->priv; TpBaseChannel *base = TP_BASE_CHANNEL (chan); TpHandle contact_handle = tp_base_channel_get_target_handle (base); DEBUG ("%p, channel %p", self, chan); if (tp_base_channel_is_registered (base)) { tp_channel_manager_emit_channel_closed_for_object (self, (TpExportableChannel *) chan); } if (priv->channels != NULL) { if (tp_base_channel_is_destroyed (base)) { DEBUG ("removing channel with handle %u", contact_handle); g_hash_table_remove (priv->channels, GUINT_TO_POINTER (contact_handle)); } else if (tp_base_channel_is_respawning (base)) { DEBUG ("reopening channel with handle %u due to pending messages", contact_handle); tp_channel_manager_emit_new_channel (self, (TpExportableChannel *) chan, NULL); } else { /* this basically means tp_base_channel_disappear() must * have been called; this doesn't have any meaning in this * channel manager. */ g_assert_not_reached (); } } } /* * 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, gpointer request_token) { GabbleImFactoryPrivate *priv = fac->priv; TpBaseConnection *conn = (TpBaseConnection *) priv->conn; GabbleIMChannel *chan; GSList *request_tokens; TpHandle initiator; g_return_val_if_fail (handle != 0, NULL); if (request_token != NULL) initiator = tp_base_connection_get_self_handle (conn); else initiator = handle; chan = g_object_new (GABBLE_TYPE_IM_CHANNEL, "connection", priv->conn, "handle", handle, "initiator-handle", initiator, "requested", (handle != initiator), NULL); tp_base_channel_register ((TpBaseChannel *) chan); g_signal_connect (chan, "closed", (GCallback) im_channel_closed_cb, fac); g_hash_table_insert (priv->channels, GUINT_TO_POINTER (handle), chan); if (request_token != NULL) request_tokens = g_slist_prepend (NULL, request_token); else request_tokens = NULL; tp_channel_manager_emit_new_channel (fac, (TpExportableChannel *) chan, request_tokens); g_slist_free (request_tokens); 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) { /* Use a temporary variable (the macro does this) because we don't want * im_channel_closed_cb to remove the channel from the hash table a * second time */ tp_clear_pointer (&self->priv->channels, g_hash_table_unref); if (self->priv->status_changed_id != 0) { g_signal_handler_disconnect (self->priv->conn, self->priv->status_changed_id); self->priv->status_changed_id = 0; } if (self->priv->message_cb_id != 0) { WockyPorter *porter = gabble_connection_dup_porter (self->priv->conn); wocky_porter_unregister_handler (porter, self->priv->message_cb_id); self->priv->message_cb_id = 0; wocky_porter_unregister_handler (porter, self->priv->delivery_report_cb_id); self->priv->delivery_report_cb_id = 0; g_object_unref (porter); } } static void connection_status_changed_cb (GabbleConnection *conn, guint status, guint reason, GabbleImFactory *self) { switch (status) { case TP_CONNECTION_STATUS_DISCONNECTED: gabble_im_factory_close_all (self); break; } } 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; DEBUG ("adding callbacks"); self->priv->message_cb_id = wocky_porter_register_handler_from_anyone (porter, WOCKY_STANZA_TYPE_MESSAGE, WOCKY_STANZA_SUB_TYPE_NONE, WOCKY_PORTER_HANDLER_PRIORITY_MIN, im_factory_message_cb, self, NULL); self->priv->delivery_report_cb_id = wocky_porter_register_handler_from_anyone (porter, WOCKY_STANZA_TYPE_MESSAGE, WOCKY_STANZA_SUB_TYPE_NONE, WOCKY_PORTER_HANDLER_PRIORITY_MIN, im_factory_receipt_cb, self, '(', "received", ':', NS_RECEIPTS, ')', NULL); 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, const GabbleCapabilitySet *caps, GPtrArray *arr) { /* We don't need to check this contact's capabilities, we assume every * contact support text channels. */ /* NOTE: if any more contact caps are added here which are not * assumed then be sure to change the implementation of * gabble_connection_get_handle_contact_capabilities, as that uses * this function to fill the contact caps hash for all contacts * (even offline). */ GValue monster = {0, }; GHashTable *fixed_properties; GValue *channel_type_value; GValue *target_handle_type_value; gchar *text_allowed_properties[] = { TP_IFACE_CHANNEL ".TargetHandle", NULL }; g_assert (handle != 0); g_value_init (&monster, TP_STRUCT_TYPE_REQUESTABLE_CHANNEL_CLASS); g_value_take_boxed (&monster, dbus_g_type_specialized_construct ( TP_STRUCT_TYPE_REQUESTABLE_CHANNEL_CLASS)); fixed_properties = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, (GDestroyNotify) tp_g_value_slice_free); channel_type_value = tp_g_value_slice_new (G_TYPE_STRING); g_value_set_static_string (channel_type_value, TP_IFACE_CHANNEL_TYPE_TEXT); g_hash_table_insert (fixed_properties, TP_IFACE_CHANNEL ".ChannelType", channel_type_value); target_handle_type_value = tp_g_value_slice_new (G_TYPE_UINT); g_value_set_uint (target_handle_type_value, TP_HANDLE_TYPE_CONTACT); g_hash_table_insert (fixed_properties, TP_IFACE_CHANNEL ".TargetHandleType", target_handle_type_value); dbus_g_type_struct_set (&monster, 0, fixed_properties, 1, text_allowed_properties, G_MAXUINT); g_hash_table_unref (fixed_properties); g_ptr_array_add (arr, g_value_get_boxed (&monster)); } struct _ForeachData { TpExportableChannelFunc func; gpointer user_data; }; static void _foreach_slave (gpointer key, gpointer value, gpointer user_data) { struct _ForeachData *data = user_data; TpExportableChannel *chan = TP_EXPORTABLE_CHANNEL (value); data->func (chan, data->user_data); } static void gabble_im_factory_foreach_channel (TpChannelManager *manager, TpExportableChannelFunc func, gpointer user_data) { GabbleImFactory *self = GABBLE_IM_FACTORY (manager); struct _ForeachData data; data.user_data = user_data; data.func = func; g_hash_table_foreach (self->priv->channels, _foreach_slave, &data); } static const gchar * const im_channel_fixed_properties[] = { TP_IFACE_CHANNEL ".ChannelType", TP_IFACE_CHANNEL ".TargetHandleType", NULL }; static const gchar * const im_channel_allowed_properties[] = { TP_IFACE_CHANNEL ".TargetHandle", TP_IFACE_CHANNEL ".TargetID", NULL }; static void gabble_im_factory_type_foreach_channel_class (GType type, TpChannelManagerTypeChannelClassFunc func, gpointer user_data) { GHashTable *table = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, (GDestroyNotify) tp_g_value_slice_free); GValue *value; value = tp_g_value_slice_new (G_TYPE_STRING); g_value_set_static_string (value, TP_IFACE_CHANNEL_TYPE_TEXT); g_hash_table_insert (table, (gchar *) im_channel_fixed_properties[0], value); value = tp_g_value_slice_new (G_TYPE_UINT); g_value_set_uint (value, TP_HANDLE_TYPE_CONTACT); g_hash_table_insert (table, (gchar *) im_channel_fixed_properties[1], value); func (type, table, im_channel_allowed_properties, user_data); g_hash_table_unref (table); } static gboolean gabble_im_factory_requestotron (GabbleImFactory *self, gpointer request_token, GHashTable *request_properties, gboolean require_new) { TpHandle handle; GError *error = NULL; TpExportableChannel *channel; if (tp_strdiff (tp_asv_get_string (request_properties, TP_IFACE_CHANNEL ".ChannelType"), TP_IFACE_CHANNEL_TYPE_TEXT)) return FALSE; if (tp_asv_get_uint32 (request_properties, TP_IFACE_CHANNEL ".TargetHandleType", NULL) != TP_HANDLE_TYPE_CONTACT) return FALSE; /* validity already checked by TpBaseConnection */ handle = tp_asv_get_uint32 (request_properties, TP_IFACE_CHANNEL ".TargetHandle", NULL); g_assert (handle != 0); if (tp_channel_manager_asv_has_unknown_properties (request_properties, im_channel_fixed_properties, im_channel_allowed_properties, &error)) goto error; channel = g_hash_table_lookup (self->priv->channels, GUINT_TO_POINTER (handle)); if (channel == NULL) { new_im_channel (self, handle, request_token); return TRUE; } if (require_new) { g_set_error (&error, TP_ERROR, TP_ERROR_NOT_AVAILABLE, "Already chatting with contact #%u in another channel", handle); goto error; } tp_channel_manager_emit_request_already_satisfied (self, request_token, channel); return TRUE; error: tp_channel_manager_emit_request_failed (self, request_token, error->domain, error->code, error->message); g_error_free (error); return TRUE; } static gboolean gabble_im_factory_create_channel (TpChannelManager *manager, gpointer request_token, GHashTable *request_properties) { GabbleImFactory *self = GABBLE_IM_FACTORY (manager); return gabble_im_factory_requestotron (self, request_token, request_properties, TRUE); } static gboolean gabble_im_factory_request_channel (TpChannelManager *manager, gpointer request_token, GHashTable *request_properties) { GabbleImFactory *self = GABBLE_IM_FACTORY (manager); return gabble_im_factory_requestotron (self, request_token, request_properties, FALSE); } static gboolean gabble_im_factory_ensure_channel (TpChannelManager *manager, gpointer request_token, GHashTable *request_properties) { GabbleImFactory *self = GABBLE_IM_FACTORY (manager); return gabble_im_factory_requestotron (self, request_token, request_properties, FALSE); } static void channel_manager_iface_init (gpointer g_iface, gpointer iface_data) { TpChannelManagerIface *iface = g_iface; iface->foreach_channel = gabble_im_factory_foreach_channel; iface->type_foreach_channel_class = gabble_im_factory_type_foreach_channel_class; iface->create_channel = gabble_im_factory_create_channel; iface->request_channel = gabble_im_factory_request_channel; iface->ensure_channel = gabble_im_factory_ensure_channel; } static void caps_channel_manager_iface_init (gpointer g_iface, gpointer iface_data) { GabbleCapsChannelManagerInterface *iface = g_iface; iface->get_contact_caps = gabble_im_factory_get_contact_caps; } �������������������������telepathy-gabble-0.18.2/src/im-factory.h������������������������������������������������������������0000644�0001750�0001750�00000004025�12200204333�020015� 0����������������������������������������������������������������������������������������������������ustar�00smcv����������������������������smcv����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * im-factory.h - Header for GabbleImFactory * Copyright (C) 2006 Collabora Ltd. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 __IM_FACTORY_H__ #define __IM_FACTORY_H__ #include <glib-object.h> #include "im-channel.h" G_BEGIN_DECLS typedef struct _GabbleImFactory GabbleImFactory; typedef struct _GabbleImFactoryClass GabbleImFactoryClass; typedef struct _GabbleImFactoryPrivate GabbleImFactoryPrivate; struct _GabbleImFactoryClass { GObjectClass parent_class; }; struct _GabbleImFactory { GObject parent; GabbleImFactoryPrivate *priv; }; GType gabble_im_factory_get_type (void); /* TYPE MACROS */ #define GABBLE_TYPE_IM_FACTORY \ (gabble_im_factory_get_type ()) #define GABBLE_IM_FACTORY(obj) \ (G_TYPE_CHECK_INSTANCE_CAST((obj), GABBLE_TYPE_IM_FACTORY, GabbleImFactory)) #define GABBLE_IM_FACTORY_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST((klass), GABBLE_TYPE_IM_FACTORY,\ GabbleImFactoryClass)) #define GABBLE_IS_IM_FACTORY(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE((obj), GABBLE_TYPE_IM_FACTORY)) #define GABBLE_IS_IM_FACTORY_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE((klass), GABBLE_TYPE_IM_FACTORY)) #define GABBLE_IM_FACTORY_GET_CLASS(obj) \ (G_TYPE_INSTANCE_GET_CLASS ((obj), GABBLE_TYPE_IM_FACTORY,\ GabbleImFactoryClass)) G_END_DECLS #endif /* #ifndef __IM_FACTORY_H__ */ �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������telepathy-gabble-0.18.2/src/im-channel.c������������������������������������������������������������0000644�0001750�0001750�00000052402�12200204333�017753� 0����������������������������������������������������������������������������������������������������ustar�00smcv����������������������������smcv����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * gabble-im-channel.c - Source for GabbleIMChannel * Copyright (C) 2005 Collabora Ltd. * Copyright (C) 2005 Nokia Corporation * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 "config.h" #include "im-channel.h" #include <string.h> #include <dbus/dbus-glib.h> #include <telepathy-glib/telepathy-glib.h> #include <telepathy-glib/telepathy-glib-dbus.h> #define DEBUG_FLAG GABBLE_DEBUG_IM #include "connection.h" #include "debug.h" #include "disco.h" #include "message-util.h" #include "namespaces.h" #include "presence.h" #include "presence-cache.h" #include "roster.h" #include "util.h" static void destroyable_iface_init (gpointer, gpointer); G_DEFINE_TYPE_WITH_CODE (GabbleIMChannel, gabble_im_channel, TP_TYPE_BASE_CHANNEL, G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CHANNEL_TYPE_TEXT, tp_message_mixin_text_iface_init); G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CHANNEL_INTERFACE_MESSAGES, tp_message_mixin_messages_iface_init); G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CHANNEL_INTERFACE_CHAT_STATE, tp_message_mixin_chat_state_iface_init) G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CHANNEL_INTERFACE_DESTROYABLE, destroyable_iface_init)); static void _gabble_im_channel_send_message (GObject *object, TpMessage *message, TpMessageSendingFlags flags); static void gabble_im_channel_close (TpBaseChannel *base_chan); static gboolean _gabble_im_channel_send_chat_state (GObject *object, TpChannelChatState state, GError **error); /* private structure */ typedef enum { CHAT_STATES_UNKNOWN, CHAT_STATES_SUPPORTED, CHAT_STATES_NOT_SUPPORTED } ChatStateSupport; struct _GabbleIMChannelPrivate { gchar *peer_jid; gboolean send_nick; ChatStateSupport chat_states_supported; gboolean dispose_has_run; }; typedef struct { GabbleIMChannel *channel; TpMessage *message; gchar *token; TpMessageSendingFlags flags; } _GabbleIMSendMessageCtx; static GPtrArray * gabble_im_channel_get_interfaces (TpBaseChannel *base) { GPtrArray *interfaces; interfaces = TP_BASE_CHANNEL_CLASS ( gabble_im_channel_parent_class)->get_interfaces (base); g_ptr_array_add (interfaces, TP_IFACE_CHANNEL_INTERFACE_CHAT_STATE); g_ptr_array_add (interfaces, TP_IFACE_CHANNEL_INTERFACE_MESSAGES); g_ptr_array_add (interfaces, TP_IFACE_CHANNEL_INTERFACE_DESTROYABLE); return interfaces; } static void gabble_im_channel_init (GabbleIMChannel *self) { GabbleIMChannelPrivate *priv = G_TYPE_INSTANCE_GET_PRIVATE (self, GABBLE_TYPE_IM_CHANNEL, GabbleIMChannelPrivate); self->priv = priv; } static void gabble_im_channel_constructed (GObject *obj) { GabbleIMChannel *self = GABBLE_IM_CHANNEL (obj); TpBaseChannel *base = TP_BASE_CHANNEL (self); GabbleIMChannelPrivate *priv = self->priv; TpBaseConnection *base_conn = tp_base_channel_get_connection (base); GabbleConnection *conn = GABBLE_CONNECTION (base_conn); TpHandleRepoIface *contact_handles = tp_base_connection_get_handles (base_conn, TP_HANDLE_TYPE_CONTACT); TpHandle target = tp_base_channel_get_target_handle (base); TpChannelTextMessageType types[] = { TP_CHANNEL_TEXT_MESSAGE_TYPE_NORMAL, TP_CHANNEL_TEXT_MESSAGE_TYPE_ACTION, TP_CHANNEL_TEXT_MESSAGE_TYPE_NOTICE, }; const gchar * supported_content_types[] = { "text/plain", NULL }; void (*chain_up) (GObject *) = ((GObjectClass *) gabble_im_channel_parent_class)->constructed; if (chain_up != NULL) chain_up (obj); priv->peer_jid = g_strdup (tp_handle_inspect (contact_handles, target)); if (gabble_roster_handle_gets_presence_from_us (conn->roster, target)) priv->send_nick = FALSE; else priv->send_nick = TRUE; tp_message_mixin_init (obj, G_STRUCT_OFFSET (GabbleIMChannel, message_mixin), base_conn); /* We deliberately do not include * TP_DELIVERY_REPORTING_SUPPORT_FLAG_RECEIVE_SUCCESSES here, even though we * support requesting receipts, because XEP-0184 provides no guarantees. */ tp_message_mixin_implement_sending (obj, _gabble_im_channel_send_message, G_N_ELEMENTS (types), types, 0, TP_DELIVERY_REPORTING_SUPPORT_FLAG_RECEIVE_FAILURES, supported_content_types); priv->chat_states_supported = CHAT_STATES_UNKNOWN; tp_message_mixin_implement_send_chat_state (obj, _gabble_im_channel_send_chat_state); } static void gabble_im_channel_dispose (GObject *object); static void gabble_im_channel_finalize (GObject *object); static void gabble_im_channel_fill_immutable_properties (TpBaseChannel *chan, GHashTable *properties) { TpBaseChannelClass *cls = TP_BASE_CHANNEL_CLASS ( gabble_im_channel_parent_class); cls->fill_immutable_properties (chan, properties); tp_dbus_properties_mixin_fill_properties_hash ( G_OBJECT (chan), properties, TP_IFACE_CHANNEL_INTERFACE_MESSAGES, "MessagePartSupportFlags", TP_IFACE_CHANNEL_INTERFACE_MESSAGES, "DeliveryReportingSupport", TP_IFACE_CHANNEL_INTERFACE_MESSAGES, "SupportedContentTypes", TP_IFACE_CHANNEL_INTERFACE_MESSAGES, "MessageTypes", 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) { GObjectClass *object_class = G_OBJECT_CLASS (gabble_im_channel_class); TpBaseChannelClass *base_class = TP_BASE_CHANNEL_CLASS (gabble_im_channel_class); g_type_class_add_private (gabble_im_channel_class, sizeof (GabbleIMChannelPrivate)); object_class->constructed = gabble_im_channel_constructed; object_class->dispose = gabble_im_channel_dispose; object_class->finalize = gabble_im_channel_finalize; base_class->channel_type = TP_IFACE_CHANNEL_TYPE_TEXT; base_class->get_interfaces = gabble_im_channel_get_interfaces; base_class->target_handle_type = TP_HANDLE_TYPE_CONTACT; 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); } static gboolean chat_states_supported (GabbleIMChannel *self, gboolean include_unknown) { GabbleIMChannelPrivate *priv = self->priv; TpBaseChannel *base = (TpBaseChannel *) self; GabbleConnection *conn = GABBLE_CONNECTION (tp_base_channel_get_connection (base)); GabblePresence *presence; presence = gabble_presence_cache_get (conn->presence_cache, tp_base_channel_get_target_handle (base)); if (presence != NULL && gabble_presence_has_cap (presence, NS_CHAT_STATES)) return TRUE; switch (priv->chat_states_supported) { case CHAT_STATES_UNKNOWN: return include_unknown; case CHAT_STATES_SUPPORTED: return TRUE; case CHAT_STATES_NOT_SUPPORTED: return FALSE; default: g_assert_not_reached (); return FALSE; } } static gboolean receipts_conceivably_supported ( GabbleIMChannel *self) { TpBaseChannel *base = (TpBaseChannel *) self; GabbleConnection *conn = GABBLE_CONNECTION (tp_base_channel_get_connection (base)); GabblePresence *presence; presence = gabble_presence_cache_get (conn->presence_cache, tp_base_channel_get_target_handle (base)); /* ...except it's never null because _parse_message_message() in * presence-cache.c. I hate this exactly as much as I did when I wrote the * FIXME on that function. */ if (presence != NULL) return gabble_presence_has_cap (presence, NS_RECEIPTS); /* Otherwise ... who knows. Why not ask for one? */ return TRUE; } static void gabble_im_channel_dispose (GObject *object) { GabbleIMChannel *self = GABBLE_IM_CHANNEL (object); TpBaseChannel *base = (TpBaseChannel *) self; GabbleIMChannelPrivate *priv = self->priv; GabbleConnection *conn = GABBLE_CONNECTION (tp_base_channel_get_connection (base)); TpHandle target = tp_base_channel_get_target_handle (base); if (priv->dispose_has_run) return; priv->dispose_has_run = TRUE; if (!gabble_roster_handle_sends_presence_to_us (conn->roster, target)) { GabblePresence *presence = gabble_presence_cache_get ( conn->presence_cache, target); if (NULL != presence) { presence->keep_unavailable = FALSE; gabble_presence_cache_maybe_remove (conn->presence_cache, target); } } tp_message_mixin_maybe_send_gone (object); if (G_OBJECT_CLASS (gabble_im_channel_parent_class)->dispose) G_OBJECT_CLASS (gabble_im_channel_parent_class)->dispose (object); } static void gabble_im_channel_finalize (GObject *object) { GabbleIMChannel *self = GABBLE_IM_CHANNEL (object); GabbleIMChannelPrivate *priv = self->priv; /* free any data held directly by the object here */ DEBUG ("%p", object); g_free (priv->peer_jid); tp_message_mixin_finalize (object); G_OBJECT_CLASS (gabble_im_channel_parent_class)->finalize (object); } static void _gabble_im_channel_message_sent_cb (GObject *source, GAsyncResult *res, gpointer user_data) { WockyPorter *porter = WOCKY_PORTER (source); GError *error = NULL; _GabbleIMSendMessageCtx *context = user_data; GabbleIMChannel *chan = context->channel; TpMessage *message = context->message; if (wocky_porter_send_finish (porter, res, &error)) { tp_message_mixin_sent ((GObject *) chan, message, context->flags, context->token, NULL); } else { tp_message_mixin_sent ((GObject *) chan, context->message, 0, NULL, error); } g_object_unref (context->channel); g_object_unref (context->message); g_free (context->token); g_slice_free (_GabbleIMSendMessageCtx, context); } static void _gabble_im_channel_send_message (GObject *object, TpMessage *message, TpMessageSendingFlags flags) { GabbleIMChannel *self = GABBLE_IM_CHANNEL (object); TpBaseChannel *base = (TpBaseChannel *) self; TpBaseConnection *base_conn; GabbleConnection *gabble_conn; GabbleIMChannelPrivate *priv; TpChannelChatState state = -1; WockyStanza *stanza = NULL; gchar *id = NULL; GError *error = NULL; WockyPorter *porter; _GabbleIMSendMessageCtx *context; g_assert (GABBLE_IS_IM_CHANNEL (self)); priv = self->priv; base_conn = tp_base_channel_get_connection (base); gabble_conn = GABBLE_CONNECTION (base_conn); if (chat_states_supported (self, TRUE)) { state = TP_CHANNEL_CHAT_STATE_ACTIVE; tp_message_mixin_change_chat_state (object, tp_base_connection_get_self_handle (base_conn), state); } stanza = gabble_message_util_build_stanza (message, gabble_conn, 0, state, priv->peer_jid, priv->send_nick, &id, &error); if (stanza != NULL) { if ((flags & TP_MESSAGE_SENDING_FLAG_REPORT_DELIVERY) && receipts_conceivably_supported (self)) { wocky_node_add_child_ns (wocky_stanza_get_top_node (stanza), "request", NS_RECEIPTS); flags = TP_MESSAGE_SENDING_FLAG_REPORT_DELIVERY; } else { flags = 0; } porter = gabble_connection_dup_porter (gabble_conn); context = g_slice_new0 (_GabbleIMSendMessageCtx); context->channel = g_object_ref (base); context->message = g_object_ref (message); context->token = id; context->flags = flags; wocky_porter_send_async (porter, stanza, NULL, _gabble_im_channel_message_sent_cb, context); g_object_unref (porter); g_object_unref (stanza); } else { tp_message_mixin_sent (object, message, 0, NULL, error); g_error_free (error); } if (priv->send_nick) priv->send_nick = FALSE; } 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; } static void maybe_send_delivery_report ( GabbleIMChannel *self, WockyStanza *message, const gchar *jid, const gchar *id) { TpBaseChannel *base = TP_BASE_CHANNEL (self); TpHandle target = tp_base_channel_get_target_handle (base); TpBaseConnection *base_conn = tp_base_channel_get_connection (base); GabbleConnection *conn = GABBLE_CONNECTION (base_conn); WockyStanza *report; if (id == NULL) return; if (wocky_node_get_child_ns (wocky_stanza_get_top_node (message), "request", NS_RECEIPTS) == NULL) return; if (conn->self_presence->status == GABBLE_PRESENCE_HIDDEN || !gabble_roster_handle_gets_presence_from_us (conn->roster, target)) return; report = wocky_stanza_build ( WOCKY_STANZA_TYPE_MESSAGE, WOCKY_STANZA_SUB_TYPE_NONE, NULL, jid, '(', "received", ':', NS_RECEIPTS, '@', "id", id, ')', NULL); _gabble_connection_send (conn, report, NULL); g_object_unref (report); } /* * _gabble_im_channel_receive: * @chan: a channel * @message: the <message> stanza, from which all the following arguments were * extracted. * @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 * @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. */ void _gabble_im_channel_receive (GabbleIMChannel *chan, WockyStanza *message, TpChannelTextMessageType type, const char *from, time_t timestamp, const gchar *id, const char *text, gint state) { GabbleIMChannelPrivate *priv; TpBaseChannel *base_chan; TpHandle peer; TpMessage *msg; g_assert (GABBLE_IS_IM_CHANNEL (chan)); priv = chan->priv; base_chan = (TpBaseChannel *) chan; peer = tp_base_channel_get_target_handle (base_chan); /* 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); msg = build_message (chan, type, timestamp, text); tp_cm_message_set_sender (msg, peer); tp_message_set_int64 (msg, 0, "message-received", time (NULL)); if (id != NULL) tp_message_set_string (msg, 0, "message-token", id); tp_message_mixin_take_received (G_OBJECT (chan), msg); maybe_send_delivery_report (chan, message, from, id); } 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 *delivery_report; gchar *tmp; 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) { /* 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'; priv->chat_states_supported = CHAT_STATES_UNKNOWN; } 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)); tmp = gabble_generate_id (); tp_message_set_string (delivery_report, 0, "message-token", tmp); g_free (tmp); tp_message_set_uint32 (delivery_report, 0, "delivery-status", delivery_status); tp_message_set_uint32 (delivery_report, 0, "delivery-error", send_error); if (id != NULL) tp_message_set_string (delivery_report, 0, "delivery-token", id); if (text != NULL) { TpMessage *msg = build_message (self, type, timestamp, text); /* This is a delivery report, so the original sender of the echoed message * must be us! */ tp_cm_message_set_sender (msg, tp_base_connection_get_self_handle (base_conn)); /* 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_cm_message_take_message (delivery_report, 0, "delivery-echo", msg); } tp_message_mixin_take_received (G_OBJECT (self), delivery_report); } /** * _gabble_im_channel_state_receive * * Send the D-BUS signal ChatStateChanged * on org.freedesktop.Telepathy.Channel.Interface.ChatState */ void _gabble_im_channel_state_receive (GabbleIMChannel *chan, TpChannelChatState state) { GabbleIMChannelPrivate *priv; TpBaseChannel *base_chan; g_assert (GABBLE_IS_IM_CHANNEL (chan)); base_chan = (TpBaseChannel *) chan; priv = chan->priv; priv->chat_states_supported = CHAT_STATES_SUPPORTED; tp_message_mixin_change_chat_state ((GObject *) chan, tp_base_channel_get_target_handle (base_chan), state); } void gabble_im_channel_receive_receipt ( GabbleIMChannel *self, const gchar *receipt_id) { _gabble_im_channel_report_delivery (self, TP_CHANNEL_TEXT_MESSAGE_TYPE_NORMAL, 0, receipt_id, NULL, GABBLE_TEXT_CHANNEL_SEND_NO_ERROR, TP_DELIVERY_STATUS_DELIVERED); } static void gabble_im_channel_close (TpBaseChannel *base_chan) { GabbleIMChannel *self = GABBLE_IM_CHANNEL (base_chan); tp_message_mixin_maybe_send_gone ((GObject *) self); /* The IM factory will resurrect the channel if we have pending * messages. When we're resurrected, we want the initiator * to be the contact who sent us those messages, if it isn't already */ if (tp_message_mixin_has_pending_messages ((GObject *) self, NULL)) { DEBUG ("Not really closing, I still have pending messages"); tp_message_mixin_set_rescued ((GObject *) self); tp_base_channel_reopened (base_chan, tp_base_channel_get_target_handle (base_chan)); } else { DEBUG ("Actually closing, I have no pending messages"); tp_base_channel_destroyed (base_chan); } } /** * gabble_im_channel_destroy * * Implements D-Bus method Destroy * on interface org.freedesktop.Telepathy.Channel.Interface.Destroyable */ static void gabble_im_channel_destroy (TpSvcChannelInterfaceDestroyable *iface, DBusGMethodInvocation *context) { g_assert (GABBLE_IS_IM_CHANNEL (iface)); DEBUG ("called on %p, clearing pending messages", iface); tp_message_mixin_clear ((GObject *) iface); gabble_im_channel_close (TP_BASE_CHANNEL (iface)); tp_svc_channel_interface_destroyable_return_from_destroy (context); } static gboolean _gabble_im_channel_send_chat_state (GObject *object, TpChannelChatState state, GError **error) { GabbleIMChannel *self = GABBLE_IM_CHANNEL (object); GabbleIMChannelPrivate *priv = self->priv; TpBaseChannel *base = (TpBaseChannel *) self; TpBaseConnection *base_conn = tp_base_channel_get_connection (base); /* Only send anything to the peer if we actually know they support chat * states. */ if (!chat_states_supported (self, FALSE)) return TRUE; return gabble_message_util_send_chat_state (G_OBJECT (self), GABBLE_CONNECTION (base_conn), WOCKY_STANZA_SUB_TYPE_CHAT, state, priv->peer_jid, error); } static void destroyable_iface_init (gpointer g_iface, gpointer iface_data) { TpSvcChannelInterfaceDestroyableClass *klass = g_iface; #define IMPLEMENT(x) tp_svc_channel_interface_destroyable_implement_##x (\ klass, gabble_im_channel_##x) IMPLEMENT(destroy); #undef IMPLEMENT } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������telepathy-gabble-0.18.2/src/im-channel.h������������������������������������������������������������0000644�0001750�0001750�00000005537�12200204333�017767� 0����������������������������������������������������������������������������������������������������ustar�00smcv����������������������������smcv����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * gabble-im-channel.h - Header for GabbleIMChannel * Copyright (C) 2005 Collabora Ltd. * Copyright (C) 2005 Nokia Corporation * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 __GABBLE_IM_CHANNEL_H__ #define __GABBLE_IM_CHANNEL_H__ #include <glib-object.h> #include <time.h> #include <telepathy-glib/telepathy-glib.h> #include <wocky/wocky.h> G_BEGIN_DECLS typedef struct _GabbleIMChannel GabbleIMChannel; typedef struct _GabbleIMChannelClass GabbleIMChannelClass; typedef struct _GabbleIMChannelPrivate GabbleIMChannelPrivate; struct _GabbleIMChannelClass { TpBaseChannelClass parent_class; }; struct _GabbleIMChannel { TpBaseChannel parent; TpMessageMixin message_mixin; GabbleIMChannelPrivate *priv; }; GType gabble_im_channel_get_type (void); /* TYPE MACROS */ #define GABBLE_TYPE_IM_CHANNEL \ (gabble_im_channel_get_type ()) #define GABBLE_IM_CHANNEL(obj) \ (G_TYPE_CHECK_INSTANCE_CAST((obj), GABBLE_TYPE_IM_CHANNEL, GabbleIMChannel)) #define GABBLE_IM_CHANNEL_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST((klass), GABBLE_TYPE_IM_CHANNEL,\ GabbleIMChannelClass)) #define GABBLE_IS_IM_CHANNEL(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE((obj), GABBLE_TYPE_IM_CHANNEL)) #define GABBLE_IS_IM_CHANNEL_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE((klass), GABBLE_TYPE_IM_CHANNEL)) #define GABBLE_IM_CHANNEL_GET_CLASS(obj) \ (G_TYPE_INSTANCE_GET_CLASS ((obj), GABBLE_TYPE_IM_CHANNEL, \ GabbleIMChannelClass)) void _gabble_im_channel_receive (GabbleIMChannel *chan, WockyStanza *message, TpChannelTextMessageType type, const char *from, time_t timestamp, const char *id, const char *text, gint state); void _gabble_im_channel_state_receive (GabbleIMChannel *chan, TpChannelChatState state); void gabble_im_channel_receive_receipt ( GabbleIMChannel *self, const gchar *receipt_id); 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__*/ �����������������������������������������������������������������������������������������������������������������������������������������������������������������telepathy-gabble-0.18.2/src/gabble.h����������������������������������������������������������������0000644�0001750�0001750�00000002147�12200204333�017162� 0����������������������������������������������������������������������������������������������������ustar�00smcv����������������������������smcv����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * gabble.h - entry point and utility functions for telepathy-gabble * Copyright (C) 2005 Collabora Ltd. * Copyright (C) 2005 Nokia Corporation * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 __GABBLE_H__ #define __GABBLE_H__ #include <dbus/dbus-glib.h> G_BEGIN_DECLS int gabble_main (int argc, char **argv); /* To be used only by tests */ void gabble_init (void); G_END_DECLS #endif /* #ifndef __GABBLE_H__*/ �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������telepathy-gabble-0.18.2/src/gabble.c����������������������������������������������������������������0000644�0001750�0001750�00000011102�12200204333�017144� 0����������������������������������������������������������������������������������������������������ustar�00smcv����������������������������smcv����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * gabble.h - entry point and utility functions for telepathy-gabble * Copyright (C) 2005 Collabora Ltd. * Copyright (C) 2005 Nokia Corporation * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 "config.h" #include "gabble.h" #ifdef HAVE_UNISTD_H # include <unistd.h> #endif #include <dbus/dbus.h> #include <glib/gstdio.h> #include <telepathy-glib/telepathy-glib.h> #include <wocky/wocky.h> #include "debug.h" #include "connection-manager.h" #include "plugin-loader.h" static TpBaseConnectionManager * construct_cm (void) { return (TpBaseConnectionManager *) g_object_new ( GABBLE_TYPE_CONNECTION_MANAGER, NULL); } #ifdef ENABLE_DEBUG static TpDebugSender *debug_sender = NULL; static void log_to_debug_sender (const gchar *log_domain, GLogLevelFlags log_level, const gchar *string) { GTimeVal now; g_return_if_fail (TP_IS_DEBUG_SENDER (debug_sender)); g_get_current_time (&now); tp_debug_sender_add_message (debug_sender, &now, log_domain, log_level, string); } /* Whether we redirect all wocky log message purely to the debug sender */ static gboolean redirect_wocky = FALSE; /* Whether to add a timestamp to the output messages */ static gboolean stamp_logs = FALSE; static void log_handler (const gchar *log_domain, GLogLevelFlags log_level, const gchar *message, gpointer user_data) { if (!redirect_wocky || tp_strdiff (log_domain, "wocky")) { if (stamp_logs) { GTimeVal now; gchar *now_str = NULL; gchar *tmp; g_get_current_time (&now); now_str = g_time_val_to_iso8601 (&now); tmp = g_strdup_printf ("%s.%06ld: %s", now_str, now.tv_usec, message); g_log_default_handler (log_domain, log_level, tmp, NULL); g_free (now_str); g_free (tmp); } else { g_log_default_handler (log_domain, log_level, message, NULL); } } /* Gabble messages are already sent to the debug sender in gabble_debug. */ if (log_level != G_LOG_LEVEL_DEBUG || tp_strdiff (log_domain, G_LOG_DOMAIN)) log_to_debug_sender (log_domain, log_level, message); } #endif void gabble_init (void) { if (!dbus_threads_init_default ()) g_error ("Unable to initialize libdbus thread-safety (out of memory?)"); g_type_init (); wocky_init (); } static void try_to_delete_old_caps_cache (void) { gchar *cache = g_build_path (G_DIR_SEPARATOR_S, g_get_user_cache_dir (), "telepathy", "gabble", "caps-cache.db", NULL); if (g_file_test (cache, G_FILE_TEST_IS_REGULAR)) { /* fire and forget */ g_unlink (cache); } g_free (cache); } int gabble_main (int argc, char **argv) { GabblePluginLoader *loader; int out; GLogLevelFlags fatal_mask; tp_debug_divert_messages (g_getenv ("GABBLE_LOGFILE")); #ifdef ENABLE_FATAL_CRITICALS /* make critical warnings fatal */ fatal_mask = g_log_set_always_fatal (G_LOG_FATAL_MASK); fatal_mask |= G_LOG_LEVEL_CRITICAL; g_log_set_always_fatal (fatal_mask); #endif #ifdef ENABLE_DEBUG gabble_debug_set_flags_from_env (); stamp_logs = (g_getenv ("GABBLE_TIMING") != NULL); if (g_getenv ("WOCKY_DEBUG") == NULL) { redirect_wocky = TRUE; wocky_debug_set_flags ( WOCKY_DEBUG_XMPP | WOCKY_DEBUG_AUTH | WOCKY_DEBUG_PORTER ); } debug_sender = tp_debug_sender_dup (); g_log_set_default_handler (log_handler, NULL); if (g_getenv ("GABBLE_PERSIST") != NULL) tp_debug_set_persistent (TRUE); #endif loader = gabble_plugin_loader_dup (); try_to_delete_old_caps_cache (); out = tp_run_connection_manager ("telepathy-gabble", VERSION, construct_cm, argc, argv); g_object_unref (loader); #ifdef ENABLE_DEBUG g_log_set_default_handler (g_log_default_handler, NULL); g_object_unref (debug_sender); #endif wocky_deinit (); return out; } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������telepathy-gabble-0.18.2/src/error.h�����������������������������������������������������������������0000644�0001750�0001750�00000001754�12200204333�017102� 0����������������������������������������������������������������������������������������������������ustar�00smcv����������������������������smcv����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * gabble-error.h - Header for Gabble's error handling API * Copyright (C) 2006-2007 Collabora Ltd. * Copyright (C) 2006 Nokia Corporation * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 __GABBLE_ERROR_H__ #define __GABBLE_ERROR_H__ #include <gabble/gabble.h> #endif /* __GABBLE_ERROR_H__ */ ��������������������telepathy-gabble-0.18.2/src/error.c�����������������������������������������������������������������0000644�0001750�0001750�00000034216�12200204333�017074� 0����������������������������������������������������������������������������������������������������ustar�00smcv����������������������������smcv����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * gabble-error.c - Source for Gabble's error handling API * Copyright (C) 2006-2007 Collabora Ltd. * Copyright (C) 2006 Nokia Corporation * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 "config.h" #include "error.h" #include <stdio.h> #include <stdlib.h> #include "namespaces.h" #include "util.h" #include <wocky/wocky.h> static inline TpError set_conn_reason (TpConnectionStatusReason *p, TpConnectionStatusReason r, TpError e) { if (p != NULL) *p = r; return e; } #define set_easy_conn_reason(p, suffix) \ set_conn_reason (p, TP_CONNECTION_STATUS_REASON_ ## suffix, \ TP_ERROR_ ## suffix) static TpError map_wocky_xmpp_error (const GError *error, TpConnectionStatusReason *conn_reason) { g_return_val_if_fail (error->domain == WOCKY_XMPP_ERROR, TP_ERROR_NOT_AVAILABLE); switch (error->code) { case WOCKY_XMPP_ERROR_REDIRECT: case WOCKY_XMPP_ERROR_GONE: /* FIXME: wild guess at the right error */ return set_conn_reason (conn_reason, TP_CONNECTION_STATUS_REASON_NONE_SPECIFIED, TP_ERROR_DOES_NOT_EXIST); case WOCKY_XMPP_ERROR_BAD_REQUEST: case WOCKY_XMPP_ERROR_UNEXPECTED_REQUEST: /* probably an internal error in Gabble/Wocky */ return set_conn_reason (conn_reason, TP_CONNECTION_STATUS_REASON_NETWORK_ERROR, TP_ERROR_CONFUSED); case WOCKY_XMPP_ERROR_JID_MALFORMED: return set_conn_reason (conn_reason, TP_CONNECTION_STATUS_REASON_NETWORK_ERROR, TP_ERROR_INVALID_HANDLE); case WOCKY_XMPP_ERROR_NOT_AUTHORIZED: case WOCKY_XMPP_ERROR_PAYMENT_REQUIRED: case WOCKY_XMPP_ERROR_FORBIDDEN: /* FIXME: the closest we've got for these, I think? */ return set_conn_reason (conn_reason, TP_CONNECTION_STATUS_REASON_AUTHENTICATION_FAILED, TP_ERROR_PERMISSION_DENIED); case WOCKY_XMPP_ERROR_ITEM_NOT_FOUND: return set_conn_reason (conn_reason, TP_CONNECTION_STATUS_REASON_NONE_SPECIFIED, TP_ERROR_DOES_NOT_EXIST); case WOCKY_XMPP_ERROR_RECIPIENT_UNAVAILABLE: return set_conn_reason (conn_reason, TP_CONNECTION_STATUS_REASON_NONE_SPECIFIED, TP_ERROR_OFFLINE); case WOCKY_XMPP_ERROR_REMOTE_SERVER_NOT_FOUND: /* FIXME: or NetworkError? */ return set_conn_reason (conn_reason, TP_CONNECTION_STATUS_REASON_NONE_SPECIFIED, TP_ERROR_DOES_NOT_EXIST); case WOCKY_XMPP_ERROR_NOT_ALLOWED: case WOCKY_XMPP_ERROR_NOT_ACCEPTABLE: case WOCKY_XMPP_ERROR_REGISTRATION_REQUIRED: case WOCKY_XMPP_ERROR_SUBSCRIPTION_REQUIRED: /* FIXME: the closest we've got for all these, I think? */ return set_conn_reason (conn_reason, TP_CONNECTION_STATUS_REASON_AUTHENTICATION_FAILED, TP_ERROR_PERMISSION_DENIED); case WOCKY_XMPP_ERROR_REMOTE_SERVER_TIMEOUT: return set_easy_conn_reason (conn_reason, NETWORK_ERROR); case WOCKY_XMPP_ERROR_CONFLICT: /* this is the best we can do in general - callers should * special-case <conflict/> according to their domain knowledge, * to turn it into RegistrationExists, ConnectionReplaced, etc. */ return set_conn_reason (conn_reason, TP_CONNECTION_STATUS_REASON_NAME_IN_USE, TP_ERROR_NOT_AVAILABLE); case WOCKY_XMPP_ERROR_INTERNAL_SERVER_ERROR: return set_conn_reason (conn_reason, TP_CONNECTION_STATUS_REASON_NONE_SPECIFIED, TP_ERROR_SERVICE_CONFUSED); case WOCKY_XMPP_ERROR_RESOURCE_CONSTRAINT: /* FIXME: Telepathy's ServiceBusy means the server, but the remote * client can also raise <resource-constraint/> */ return set_conn_reason (conn_reason, TP_CONNECTION_STATUS_REASON_NONE_SPECIFIED, TP_ERROR_SERVICE_BUSY); case WOCKY_XMPP_ERROR_FEATURE_NOT_IMPLEMENTED: case WOCKY_XMPP_ERROR_SERVICE_UNAVAILABLE: return set_conn_reason (conn_reason, TP_CONNECTION_STATUS_REASON_NONE_SPECIFIED, TP_ERROR_NOT_AVAILABLE); case WOCKY_XMPP_ERROR_UNDEFINED_CONDITION: default: return set_conn_reason (conn_reason, TP_CONNECTION_STATUS_REASON_NONE_SPECIFIED, TP_ERROR_NOT_AVAILABLE); } } static TpError map_wocky_auth_error (const GError *error, TpConnectionStatusReason *conn_reason) { g_return_val_if_fail (error->domain == WOCKY_AUTH_ERROR, TP_ERROR_NOT_AVAILABLE); switch (error->code) { case WOCKY_AUTH_ERROR_CONNRESET: return set_conn_reason (conn_reason, TP_CONNECTION_STATUS_REASON_NETWORK_ERROR, TP_ERROR_CONNECTION_LOST); case WOCKY_AUTH_ERROR_NETWORK: case WOCKY_AUTH_ERROR_STREAM: return set_easy_conn_reason (conn_reason, NETWORK_ERROR); case WOCKY_AUTH_ERROR_RESOURCE_CONFLICT: return set_conn_reason (conn_reason, TP_CONNECTION_STATUS_REASON_NAME_IN_USE, TP_ERROR_ALREADY_CONNECTED); case WOCKY_AUTH_ERROR_NOT_SUPPORTED: case WOCKY_AUTH_ERROR_NO_SUPPORTED_MECHANISMS: return set_conn_reason (conn_reason, TP_CONNECTION_STATUS_REASON_AUTHENTICATION_FAILED, TP_ERROR_NOT_IMPLEMENTED); case WOCKY_AUTH_ERROR_INVALID_REPLY: return set_conn_reason (conn_reason, TP_CONNECTION_STATUS_REASON_NONE_SPECIFIED, TP_ERROR_SERVICE_CONFUSED); case WOCKY_AUTH_ERROR_INIT_FAILED: case WOCKY_AUTH_ERROR_NO_CREDENTIALS: case WOCKY_AUTH_ERROR_NOT_AUTHORIZED: case WOCKY_AUTH_ERROR_FAILURE: default: return set_easy_conn_reason (conn_reason, AUTHENTICATION_FAILED); } } static TpError map_wocky_connector_error (const GError *error, TpConnectionStatusReason *conn_reason) { g_return_val_if_fail (error->domain == WOCKY_CONNECTOR_ERROR, TP_ERROR_NOT_AVAILABLE); switch (error->code) { case WOCKY_CONNECTOR_ERROR_SESSION_DENIED: return set_easy_conn_reason (conn_reason, AUTHENTICATION_FAILED); case WOCKY_CONNECTOR_ERROR_BIND_CONFLICT: return set_conn_reason (conn_reason, TP_CONNECTION_STATUS_REASON_NAME_IN_USE, TP_ERROR_ALREADY_CONNECTED); case WOCKY_CONNECTOR_ERROR_REGISTRATION_CONFLICT: return set_conn_reason (conn_reason, TP_CONNECTION_STATUS_REASON_NAME_IN_USE, TP_ERROR_REGISTRATION_EXISTS); case WOCKY_CONNECTOR_ERROR_REGISTRATION_REJECTED: /* AuthenticationFailed is the closest ConnectionStatusReason to * "I tried but couldn't register you an account." */ return set_conn_reason (conn_reason, TP_CONNECTION_STATUS_REASON_AUTHENTICATION_FAILED, TP_ERROR_PERMISSION_DENIED); case WOCKY_CONNECTOR_ERROR_REGISTRATION_UNSUPPORTED: return set_conn_reason (conn_reason, TP_CONNECTION_STATUS_REASON_AUTHENTICATION_FAILED, TP_ERROR_NOT_AVAILABLE); default: return set_easy_conn_reason (conn_reason, NETWORK_ERROR); } } static TpError map_wocky_stream_error (const GError *error, TpConnectionStatus previous_status, TpConnectionStatusReason *conn_reason) { g_return_val_if_fail (error->domain == WOCKY_XMPP_STREAM_ERROR, TP_ERROR_NOT_AVAILABLE); switch (error->code) { case WOCKY_XMPP_STREAM_ERROR_HOST_UNKNOWN: /* If we get this while we're logging in, it's because we're trying * to connect to foo@bar.com but the server doesn't know about * bar.com, probably because the user entered a non-GTalk JID into * a GTalk profile that forces the server. */ return set_easy_conn_reason (conn_reason, AUTHENTICATION_FAILED); case WOCKY_XMPP_STREAM_ERROR_CONFLICT: if (previous_status == TP_CONNECTION_STATUS_CONNECTED) { return set_conn_reason (conn_reason, TP_CONNECTION_STATUS_REASON_NAME_IN_USE, TP_ERROR_CONNECTION_REPLACED); } else { return set_conn_reason (conn_reason, TP_CONNECTION_STATUS_REASON_NAME_IN_USE, TP_ERROR_ALREADY_CONNECTED); } default: return set_easy_conn_reason (conn_reason, NETWORK_ERROR); } } static TpError map_wocky_tls_cert_error (const GError *error, TpConnectionStatusReason *conn_reason) { g_return_val_if_fail (error->domain == WOCKY_TLS_CERT_ERROR, TP_ERROR_NOT_AVAILABLE); switch (error->code) { case WOCKY_TLS_CERT_NO_CERTIFICATE: return set_easy_conn_reason (conn_reason, CERT_NOT_PROVIDED); case WOCKY_TLS_CERT_INSECURE: case WOCKY_TLS_CERT_SIGNER_UNKNOWN: case WOCKY_TLS_CERT_SIGNER_UNAUTHORISED: case WOCKY_TLS_CERT_REVOKED: case WOCKY_TLS_CERT_MAYBE_DOS: return set_easy_conn_reason (conn_reason, CERT_UNTRUSTED); case WOCKY_TLS_CERT_EXPIRED: return set_easy_conn_reason (conn_reason, CERT_EXPIRED); case WOCKY_TLS_CERT_NOT_ACTIVE: return set_easy_conn_reason (conn_reason, CERT_NOT_ACTIVATED); case WOCKY_TLS_CERT_NAME_MISMATCH: return set_easy_conn_reason (conn_reason, CERT_HOSTNAME_MISMATCH); case WOCKY_TLS_CERT_INTERNAL_ERROR: case WOCKY_TLS_CERT_UNKNOWN_ERROR: default: return set_conn_reason (conn_reason, TP_CONNECTION_STATUS_REASON_CERT_OTHER_ERROR, TP_ERROR_ENCRYPTION_ERROR); } } static TpError map_connection_error (const GError *error) { switch (error->code) { case WOCKY_XMPP_CONNECTION_ERROR_EOS: case WOCKY_XMPP_CONNECTION_ERROR_CLOSED: return TP_ERROR_CANCELLED; case WOCKY_XMPP_CONNECTION_ERROR_NOT_OPEN: case WOCKY_XMPP_CONNECTION_ERROR_IS_CLOSED: case WOCKY_XMPP_CONNECTION_ERROR_IS_OPEN: default: return TP_ERROR_DISCONNECTED; } } static const gchar * get_error_prefix (GEnumClass *klass, gint code, const gchar *fallback) { GEnumValue *value; if (klass == NULL) return fallback; value = g_enum_get_value (klass, code); if (value == NULL || value->value_name == NULL) return fallback; return value->value_name; } void gabble_set_tp_conn_error_from_wocky (const GError *wocky_error, TpConnectionStatus previous_status, TpConnectionStatusReason *conn_reason, GError **error) { GEnumClass *klass; const gchar *name; if (conn_reason != NULL) *conn_reason = TP_CONNECTION_STATUS_REASON_NONE_SPECIFIED; g_return_if_fail (wocky_error != NULL); if (wocky_error->domain == WOCKY_XMPP_ERROR) { klass = g_type_class_ref (WOCKY_TYPE_XMPP_ERROR); name = get_error_prefix (klass, wocky_error->code, "unknown WockyXmppError code"); g_set_error (error, TP_ERROR, map_wocky_xmpp_error (wocky_error, conn_reason), "%s (#%d): %s", name, wocky_error->code, wocky_error->message); g_type_class_unref (klass); } else if (wocky_error->domain == G_IO_ERROR) { klass = g_type_class_ref (G_TYPE_IO_ERROR_ENUM); name = get_error_prefix (klass, wocky_error->code, "unknown GIOError code"); /* FIXME: is it safe to assume that every GIOError we encounter from * Wocky is a NetworkError? */ g_set_error (error, TP_ERROR, TP_ERROR_NETWORK_ERROR, "%s (#%d): %s", name, wocky_error->code, wocky_error->message); g_type_class_unref (klass); if (conn_reason != NULL) *conn_reason = TP_CONNECTION_STATUS_REASON_NETWORK_ERROR; } else if (wocky_error->domain == WOCKY_AUTH_ERROR) { klass = g_type_class_ref (WOCKY_TYPE_AUTH_ERROR); name = get_error_prefix (klass, wocky_error->code, "unknown WockyAuthError code"); g_set_error (error, TP_ERROR, map_wocky_auth_error (wocky_error, conn_reason), "%s (#%d): %s", name, wocky_error->code, wocky_error->message); g_type_class_unref (klass); } else if (wocky_error->domain == WOCKY_CONNECTOR_ERROR) { klass = g_type_class_ref (WOCKY_TYPE_CONNECTOR_ERROR); name = get_error_prefix (klass, wocky_error->code, "unknown WockyConnectorError code"); g_set_error (error, TP_ERROR, map_wocky_connector_error (wocky_error, conn_reason), "%s (#%d): %s", name, wocky_error->code, wocky_error->message); g_type_class_unref (klass); } else if (wocky_error->domain == WOCKY_XMPP_STREAM_ERROR) { klass = g_type_class_ref (WOCKY_TYPE_XMPP_STREAM_ERROR); name = get_error_prefix (klass, wocky_error->code, "unknown WockyXmppStreamError code"); g_set_error (error, TP_ERROR, map_wocky_stream_error (wocky_error, previous_status, conn_reason), "%s (#%d): %s", name, wocky_error->code, wocky_error->message); g_type_class_unref (klass); } else if (wocky_error->domain == WOCKY_TLS_CERT_ERROR) { klass = g_type_class_ref (WOCKY_TYPE_TLS_CERT_STATUS); name = get_error_prefix (klass, wocky_error->code, "unknown WockyTLSCertStatus code"); g_set_error (error, TP_ERROR, map_wocky_tls_cert_error (wocky_error, conn_reason), "%s (#%d): %s", name, wocky_error->code, wocky_error->message); g_type_class_unref (klass); } else if (wocky_error->domain == WOCKY_XMPP_CONNECTION_ERROR) { /* FIXME: there's no GEnum for WockyXmppConnectionError. */ g_set_error_literal (error, TP_ERROR, map_connection_error (wocky_error), wocky_error->message); } else { /* best we can do... */ g_set_error (error, TP_ERROR, TP_ERROR_NOT_AVAILABLE, "%s (#%d): %s", g_quark_to_string (wocky_error->domain), wocky_error->code, wocky_error->message); } } void gabble_set_tp_error_from_wocky (const GError *wocky_error, GError **error) { gabble_set_tp_conn_error_from_wocky (wocky_error, TP_CONNECTION_STATUS_CONNECTED, NULL, error); } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������telepathy-gabble-0.18.2/src/disco.c�����������������������������������������������������������������0000644�0001750�0001750�00000063223�12200204333�017044� 0����������������������������������������������������������������������������������������������������ustar�00smcv����������������������������smcv����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * disco.c - Source for Gabble service discovery * * Copyright (C) 2006, 2008 Collabora Ltd. * Copyright (C) 2006, 2008 Nokia Corporation * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 * * -- LET'S DISCO!!! \o/ \o_ _o/ /\o/\ _/o/- -\o\_ -- */ #include "config.h" #include "disco.h" #include <string.h> #include <dbus/dbus-glib.h> #include <dbus/dbus-glib-lowlevel.h> #include <telepathy-glib/telepathy-glib.h> #define DEBUG_FLAG GABBLE_DEBUG_DISCO #include "connection.h" #include "debug.h" #include "error.h" #include "namespaces.h" #include "util.h" #include "gabble-signals-marshal.h" #define DEFAULT_REQUEST_TIMEOUT 20 #define DISCO_PIPELINE_SIZE 10 /* signals */ enum { ITEM_FOUND, DONE, LAST_SIGNAL }; static guint signals[LAST_SIGNAL] = {0}; /* Properties */ enum { PROP_CONNECTION = 1, LAST_PROPERTY }; G_DEFINE_TYPE(GabbleDisco, gabble_disco, G_TYPE_OBJECT); struct _GabbleDiscoPrivate { GabbleConnection *connection; GSList *service_cache; GList *requests; gboolean dispose_has_run; }; struct _GabbleDiscoRequest { GabbleDisco *disco; guint timer_id; GabbleDiscoType type; gchar *jid; gchar *node; GabbleDiscoCb callback; gpointer user_data; GObject *bound_object; }; GQuark gabble_disco_error_quark (void) { static GQuark quark = 0; if (!quark) quark = g_quark_from_static_string ("gabble-disco-error"); return quark; } static void gabble_disco_init (GabbleDisco *obj) { GabbleDiscoPrivate *priv = G_TYPE_INSTANCE_GET_PRIVATE (obj, GABBLE_TYPE_DISCO, GabbleDiscoPrivate); obj->priv = priv; } static GObject *gabble_disco_constructor (GType type, guint n_props, GObjectConstructParam *props); static void gabble_disco_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec); static void gabble_disco_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec); static void gabble_disco_dispose (GObject *object); static void gabble_disco_finalize (GObject *object); static void gabble_disco_class_init (GabbleDiscoClass *gabble_disco_class) { GObjectClass *object_class = G_OBJECT_CLASS (gabble_disco_class); GParamSpec *param_spec; g_type_class_add_private (gabble_disco_class, sizeof (GabbleDiscoPrivate)); object_class->constructor = gabble_disco_constructor; object_class->get_property = gabble_disco_get_property; object_class->set_property = gabble_disco_set_property; object_class->dispose = gabble_disco_dispose; object_class->finalize = gabble_disco_finalize; param_spec = g_param_spec_object ("connection", "GabbleConnection object", "Gabble connection object that owns this XMPP Discovery object.", GABBLE_TYPE_CONNECTION, G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, PROP_CONNECTION, param_spec); signals[ITEM_FOUND] = g_signal_new ("item-found", G_OBJECT_CLASS_TYPE (gabble_disco_class), G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED, 0, NULL, NULL, g_cclosure_marshal_VOID__POINTER, G_TYPE_NONE, 1, G_TYPE_POINTER); signals[DONE] = g_signal_new ("done", G_OBJECT_CLASS_TYPE (gabble_disco_class), G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED, 0, NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); } static void gabble_disco_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { GabbleDisco *chan = GABBLE_DISCO (object); GabbleDiscoPrivate *priv = chan->priv; switch (property_id) { case PROP_CONNECTION: g_value_set_object (value, priv->connection); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void gabble_disco_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { GabbleDisco *chan = GABBLE_DISCO (object); GabbleDiscoPrivate *priv = chan->priv; switch (property_id) { case PROP_CONNECTION: priv->connection = g_value_get_object (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void gabble_disco_conn_status_changed_cb (GabbleConnection *conn, TpConnectionStatus status, TpConnectionStatusReason reason, gpointer data); static GObject * gabble_disco_constructor (GType type, guint n_props, GObjectConstructParam *props) { GObject *obj; GabbleDisco *disco; GabbleDiscoPrivate *priv; obj = G_OBJECT_CLASS (gabble_disco_parent_class)-> constructor (type, n_props, props); disco = GABBLE_DISCO (obj); priv = disco->priv; g_signal_connect (priv->connection, "status-changed", G_CALLBACK (gabble_disco_conn_status_changed_cb), disco); return obj; } static void cancel_request (GabbleDiscoRequest *request); static void gabble_disco_dispose (GObject *object) { GabbleDisco *self = GABBLE_DISCO (object); GabbleDiscoPrivate *priv = self->priv; GSList *l; if (priv->dispose_has_run) return; priv->dispose_has_run = TRUE; DEBUG ("dispose called"); /* cancel request removes the element from the list after cancelling */ while (priv->requests) cancel_request (priv->requests->data); for (l = priv->service_cache; l; l = g_slist_next (l)) { GabbleDiscoItem *item = (GabbleDiscoItem *) l->data; g_free ((char *) item->jid); g_free ((char *) item->name); g_free ((char *) item->category); g_free ((char *) item->type); g_hash_table_unref (item->features); g_free (item); } g_slist_free (priv->service_cache); priv->service_cache = NULL; if (G_OBJECT_CLASS (gabble_disco_parent_class)->dispose) G_OBJECT_CLASS (gabble_disco_parent_class)->dispose (object); } static void gabble_disco_finalize (GObject *object) { DEBUG ("called with %p", object); G_OBJECT_CLASS (gabble_disco_parent_class)->finalize (object); } /** * gabble_disco_new: * @conn: The #GabbleConnection to use for service discovery * * Creates an object to use for Jabber service discovery (DISCO) * There should be one of these per connection */ GabbleDisco * gabble_disco_new (GabbleConnection *conn) { GabbleDisco *disco; g_return_val_if_fail (GABBLE_IS_CONNECTION (conn), NULL); disco = GABBLE_DISCO (g_object_new (GABBLE_TYPE_DISCO, "connection", conn, NULL)); return disco; } static void notify_delete_request (gpointer data, GObject *obj); static void delete_request (GabbleDiscoRequest *request) { GabbleDisco *disco = request->disco; GabbleDiscoPrivate *priv; g_assert (NULL != request); g_assert (GABBLE_IS_DISCO (disco)); priv = disco->priv; g_assert (NULL != g_list_find (priv->requests, request)); priv->requests = g_list_remove (priv->requests, request); if (NULL != request->bound_object) { g_object_weak_unref (request->bound_object, notify_delete_request, request); } if (0 != request->timer_id) { g_source_remove (request->timer_id); } g_free (request->jid); g_free (request->node); g_slice_free (GabbleDiscoRequest, request); } static gboolean timeout_request (gpointer data) { GabbleDiscoRequest *request = (GabbleDiscoRequest *) data; GabbleDisco *disco; GError *err = NULL; g_return_val_if_fail (data != NULL, FALSE); err = g_error_new (GABBLE_DISCO_ERROR, GABBLE_DISCO_ERROR_TIMEOUT, "Request for %s on %s timed out", (request->type == GABBLE_DISCO_TYPE_INFO)?"info":"items", request->jid); /* Temporarily ref the disco object to avoid crashing if the callback * destroys us (as seen in test-disco-no-reply.py) */ disco = g_object_ref (request->disco); /* also, we're about to run the callback, so it's too late to cancel it - * avoid crashing if running the callback destroys the bound object */ if (NULL != request->bound_object) { g_object_weak_unref (request->bound_object, notify_delete_request, request); request->bound_object = NULL; } (request->callback)(request->disco, request, request->jid, request->node, NULL, err, request->user_data); g_error_free (err); request->timer_id = 0; delete_request (request); g_object_unref (disco); return FALSE; } static void cancel_request (GabbleDiscoRequest *request) { GError *err = NULL; g_assert (request != NULL); err = g_error_new (GABBLE_DISCO_ERROR, GABBLE_DISCO_ERROR_CANCELLED, "Request for %s on %s cancelled", (request->type == GABBLE_DISCO_TYPE_INFO)?"info":"items", request->jid); (request->callback)(request->disco, request, request->jid, request->node, NULL, err, request->user_data); g_error_free (err); delete_request (request); } static const char * disco_type_to_xmlns (GabbleDiscoType type) { switch (type) { case GABBLE_DISCO_TYPE_INFO: return NS_DISCO_INFO; case GABBLE_DISCO_TYPE_ITEMS: return NS_DISCO_ITEMS; default: g_assert_not_reached (); } return NULL; } static void request_reply_cb (GabbleConnection *conn, WockyStanza *sent_msg, WockyStanza *reply_msg, GObject *object, gpointer user_data) { GabbleDiscoRequest *request = (GabbleDiscoRequest *) user_data; GabbleDisco *disco = GABBLE_DISCO (object); GabbleDiscoPrivate *priv = disco->priv; WockyNode *query_node; GError *err = NULL; g_assert (request); if (!g_list_find (priv->requests, request)) return; query_node = wocky_node_get_child_ns ( wocky_stanza_get_top_node (reply_msg), "query", disco_type_to_xmlns (request->type)); if (wocky_stanza_extract_errors (reply_msg, NULL, &err, NULL, NULL)) { /* pass */ } else if (NULL == query_node) { err = g_error_new (GABBLE_DISCO_ERROR, GABBLE_DISCO_ERROR_UNKNOWN, "disco response contained no <query> node"); } request->callback (request->disco, request, request->jid, request->node, query_node, err, request->user_data); delete_request (request); if (err) g_error_free (err); } static void notify_delete_request (gpointer data, GObject *obj) { GabbleDiscoRequest *request = (GabbleDiscoRequest *) data; request->bound_object = NULL; delete_request (request); } /** * gabble_disco_request: * @self: #GabbleDisco object to use for request * @type: type of request * @jid: Jabber ID to request on * @node: node to request on @jid, or NULL * @callback: #GabbleDiscoCb to call on request fullfilment * @object: GObject to bind request to. the callback will not be * called if this object has been unrefed. NULL if not needed * @error: #GError to return a telepathy error in if unable to make * request, NULL if unneeded. * * Make a disco request on the given jid with the default timeout. */ GabbleDiscoRequest * gabble_disco_request (GabbleDisco *self, GabbleDiscoType type, const gchar *jid, const char *node, GabbleDiscoCb callback, gpointer user_data, GObject *object, GError **error) { return gabble_disco_request_with_timeout (self, type, jid, node, DEFAULT_REQUEST_TIMEOUT, callback, user_data, object, error); } /** * gabble_disco_request_with_timeout: * @self: #GabbleDisco object to use for request * @type: type of request * @jid: Jabber ID to request on * @node: node to request on @jid, or NULL * @timeout: the time until the request fails, in seconds * @callback: #GabbleDiscoCb to call on request fullfilment * @object: GObject to bind request to. the callback will not be * called if this object has been unrefed. NULL if not needed * @error: #GError to return a telepathy error in if unable to make * request, NULL if unneeded. * * Make a disco request on the given jid, which will fail unless a reply * is received within the given timeout interval. */ GabbleDiscoRequest * gabble_disco_request_with_timeout (GabbleDisco *self, GabbleDiscoType type, const gchar *jid, const char *node, guint timeout, GabbleDiscoCb callback, gpointer user_data, GObject *object, GError **error) { GabbleDiscoPrivate *priv = self->priv; GabbleDiscoRequest *request; WockyStanza *msg; WockyNode *lm_node; request = g_slice_new0 (GabbleDiscoRequest); request->disco = self; request->type = type; request->jid = g_strdup (jid); if (node) request->node = g_strdup (node); request->callback = callback; request->user_data = user_data; request->bound_object = object; if (NULL != object) g_object_weak_ref (object, notify_delete_request, request); DEBUG ("Creating disco request %p for %s", request, request->jid); priv->requests = g_list_prepend (priv->requests, request); msg = wocky_stanza_build (WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_GET, NULL, jid, '(', "query", ':', disco_type_to_xmlns (type), '*', &lm_node, ')', NULL); if (node) { wocky_node_set_attribute (lm_node, "node", node); } if (! _gabble_connection_send_with_reply (priv->connection, msg, request_reply_cb, G_OBJECT(self), request, error)) { delete_request (request); g_object_unref (msg); return NULL; } else { request->timer_id = g_timeout_add_seconds (timeout, timeout_request, request); g_object_unref (msg); return request; } } void gabble_disco_cancel_request (GabbleDisco *disco, GabbleDiscoRequest *request) { GabbleDiscoPrivate *priv; g_return_if_fail (GABBLE_IS_DISCO (disco)); g_return_if_fail (NULL != request); priv = disco->priv; g_return_if_fail (NULL != g_list_find (priv->requests, request)); cancel_request (request); } /* Disco pipeline */ typedef struct _GabbleDiscoPipeline GabbleDiscoPipeline; struct _GabbleDiscoPipeline { GabbleDisco *disco; gpointer user_data; GabbleDiscoPipelineCb callback; GabbleDiscoEndCb end_callback; GPtrArray *disco_pipeline; GHashTable *remaining_items; GabbleDiscoRequest *list_request; gboolean running; }; static void gabble_disco_fill_pipeline (GabbleDisco *disco, GabbleDiscoPipeline *pipeline); static void item_info_cb (GabbleDisco *disco, GabbleDiscoRequest *request, const gchar *jid, const gchar *node, WockyNode *result, GError *error, gpointer user_data) { WockyNode *identity, *value_node, *feature; const char *category, *type, *var, *name, *value; GHashTable *keys; GabbleDiscoItem item; WockyNodeIter i; GabbleDiscoPipeline *pipeline = (GabbleDiscoPipeline *) user_data; g_ptr_array_remove_fast (pipeline->disco_pipeline, request); if (error) { DEBUG ("got error %s", error->message); goto done; } identity = wocky_node_get_child (result, "identity"); if (NULL == identity) goto done; name = wocky_node_get_attribute (identity, "name"); if (NULL == name) goto done; category = wocky_node_get_attribute (identity, "category"); if (NULL == category) goto done; type = wocky_node_get_attribute (identity, "type"); if (NULL == type) goto done; DEBUG ("got item identity, jid=%s, name=%s, category=%s, type=%s", jid, name, category, type); keys = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); wocky_node_iter_init (&i, result, NULL, NULL); while (wocky_node_iter_next (&i, &feature)) { if (0 == strcmp (feature->name, "feature")) { var = wocky_node_get_attribute (feature, "var"); if (var) g_hash_table_insert (keys, g_strdup (var), NULL); } else if (0 == strcmp (feature->name, "x")) { if (wocky_node_has_ns (feature, NS_X_DATA)) { WockyNodeIter j; WockyNode *field; wocky_node_iter_init (&j, feature, "field", NULL); while (wocky_node_iter_next (&j, &field)) { var = wocky_node_get_attribute (field, "var"); if (NULL == var) continue; value_node = wocky_node_get_child (field, "value"); if (NULL == value_node) continue; value = value_node->content; if (NULL == value) continue; g_hash_table_insert (keys, g_strdup (var), g_strdup (value)); } } } } item.jid = jid; item.name = name; item.category = category; item.type = type; item.features = keys; pipeline->callback (pipeline, &item, pipeline->user_data); g_hash_table_unref (keys); done: gabble_disco_fill_pipeline (disco, pipeline); return; } static gboolean return_true (gpointer key, gpointer value, gpointer data) { return TRUE; } static void gabble_disco_fill_pipeline (GabbleDisco *disco, GabbleDiscoPipeline *pipeline) { if (!pipeline->running) { DEBUG ("pipeline not running, not refilling"); } else { /* send disco requests for the JIDs in the remaining_items hash table * until there are DISCO_PIPELINE_SIZE requests in progress */ while (pipeline->disco_pipeline->len < DISCO_PIPELINE_SIZE) { gchar *jid; GabbleDiscoRequest *request; jid = (gchar *) g_hash_table_find (pipeline->remaining_items, return_true, NULL); if (NULL == jid) break; request = gabble_disco_request (disco, GABBLE_DISCO_TYPE_INFO, jid, NULL, item_info_cb, pipeline, G_OBJECT(disco), NULL); g_ptr_array_add (pipeline->disco_pipeline, request); /* frees jid */ g_hash_table_remove (pipeline->remaining_items, jid); } if (0 == pipeline->disco_pipeline->len) { /* signal that the pipeline has finished */ pipeline->running = FALSE; pipeline->end_callback (pipeline, pipeline->user_data); } } } static void disco_items_cb (GabbleDisco *disco, GabbleDiscoRequest *request, const gchar *jid, const gchar *node, WockyNode *result, GError *error, gpointer user_data) { const char *item_jid; gpointer key, value; GabbleDiscoPipeline *pipeline = (GabbleDiscoPipeline *) user_data; WockyNodeIter i; WockyNode *item; pipeline->list_request = NULL; if (error) { DEBUG ("Got error on items request: %s", error->message); goto out; } wocky_node_iter_init (&i, result, "item", NULL); while (wocky_node_iter_next (&i, &item)) { item_jid = wocky_node_get_attribute (item, "jid"); if (NULL != item_jid && !g_hash_table_lookup_extended (pipeline->remaining_items, item_jid, &key, &value)) { gchar *tmp = g_strdup (item_jid); DEBUG ("discovered service item: %s", tmp); g_hash_table_insert (pipeline->remaining_items, tmp, tmp); } } out: gabble_disco_fill_pipeline (disco, pipeline); } /** * gabble_disco_pipeline_init: * @disco: disco object to use in the pipeline * @callback: GFunc to call on request fullfilment * @user_data: the usual * * Prepares the pipeline for making the ITEM request on the server and * subsequent INFO elements on returned items. * * GabbleDiscoPipeline is opaque structure for the user. */ gpointer gabble_disco_pipeline_init (GabbleDisco *disco, GabbleDiscoPipelineCb callback, GabbleDiscoEndCb end_callback, gpointer user_data) { GabbleDiscoPipeline *pipeline = g_new (GabbleDiscoPipeline, 1); pipeline->user_data = user_data; pipeline->callback = callback; pipeline->end_callback = end_callback; pipeline->disco_pipeline = g_ptr_array_sized_new (DISCO_PIPELINE_SIZE); pipeline->remaining_items = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); pipeline->running = TRUE; pipeline->disco = disco; return pipeline; } /** * gabble_disco_pipeline_run: * @self: reference to the pipeline structure * @server: server to query * * Makes ITEMS request on the server, and afterwards queries for INFO * on each item. INFO queries are pipelined. The item properties are stored * in hash table parameter to the callback function. The user is responsible * for destroying the hash table after it's done with. * * Upon returning all the results, the end_callback is called with * reference to the pipeline. */ void gabble_disco_pipeline_run (gpointer self, const char *server) { GabbleDiscoPipeline *pipeline = (GabbleDiscoPipeline *) self; pipeline->running = TRUE; pipeline->list_request = gabble_disco_request (pipeline->disco, GABBLE_DISCO_TYPE_ITEMS, server, NULL, disco_items_cb, pipeline, G_OBJECT (pipeline->disco), NULL); } /** * gabble_disco_pipeline_cancel: * @pipeline: pipeline to cancel * * Flushes the pipeline (cancels all pending disco requests) and * destroys it. */ void gabble_disco_pipeline_destroy (gpointer self) { GabbleDiscoPipeline *pipeline = (GabbleDiscoPipeline *) self; pipeline->running = FALSE; if (pipeline->list_request != NULL) { gabble_disco_cancel_request (pipeline->disco, pipeline->list_request); pipeline->list_request = NULL; } /* iterate using a while loop otherwise we're modifying * the array as we iterate it, and miss things! */ while (pipeline->disco_pipeline->len > 0) { GabbleDiscoRequest *request = g_ptr_array_index (pipeline->disco_pipeline, 0); gabble_disco_cancel_request (pipeline->disco, request); } g_hash_table_unref (pipeline->remaining_items); g_ptr_array_unref (pipeline->disco_pipeline); g_free (pipeline); } static void service_feature_copy_one (gpointer k, gpointer v, gpointer user_data) { char *key = (char *) k; char *value = (char *) v; GHashTable *target = (GHashTable *) user_data; g_hash_table_insert (target, g_strdup (key), g_strdup (value)); } /* Service discovery */ static void services_cb (gpointer pipeline, GabbleDiscoItem *item, gpointer user_data) { GabbleDisco *disco = GABBLE_DISCO (user_data); GabbleDiscoPrivate *priv = disco->priv; GabbleDiscoItem *my_item = g_new0 (GabbleDiscoItem, 1); my_item->jid = g_strdup (item->jid); my_item->name = g_strdup (item->name); my_item->category = g_strdup (item->category); my_item->type = g_strdup (item->type); my_item->features = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); g_hash_table_foreach (item->features, service_feature_copy_one, my_item->features); priv->service_cache = g_slist_prepend (priv->service_cache, my_item); g_signal_emit (G_OBJECT (disco), signals[ITEM_FOUND], 0, my_item); } static void end_cb (gpointer pipeline, gpointer user_data) { GabbleDisco *disco = GABBLE_DISCO (user_data); GabbleDiscoPrivate *priv = disco->priv; gabble_disco_pipeline_destroy (pipeline); priv->service_cache = g_slist_reverse (priv->service_cache); g_signal_emit (G_OBJECT (disco), signals[DONE], 0); } static void gabble_disco_conn_status_changed_cb (GabbleConnection *conn, TpConnectionStatus status, TpConnectionStatusReason reason, gpointer data) { GabbleDisco *disco = GABBLE_DISCO (data); GabbleDiscoPrivate *priv = disco->priv; if (status == TP_CONNECTION_STATUS_CONNECTED) { char *server; gpointer pipeline; g_object_get (priv->connection, "stream-server", &server, NULL); g_assert (server != NULL); DEBUG ("connected, initiating service discovery on %s", server); pipeline = gabble_disco_pipeline_init (disco, services_cb, end_cb, disco); gabble_disco_pipeline_run (pipeline, server); g_free (server); } } const GabbleDiscoItem * gabble_disco_service_find (GabbleDisco *disco, const char *category, const char *type, const char *feature) { GabbleDiscoPrivate *priv; GSList *l; g_assert (GABBLE_IS_DISCO (disco)); priv = disco->priv; for (l = priv->service_cache; l; l = g_slist_next (l)) { GabbleDiscoItem *item = (GabbleDiscoItem *) l->data; if (category != NULL && tp_strdiff (category, item->category)) continue; if (type != NULL && tp_strdiff (type, item->type)) continue; if (feature != NULL && !g_hash_table_lookup_extended (item->features, feature, NULL, NULL)) continue; return item; } return NULL; } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������telepathy-gabble-0.18.2/src/disco.h�����������������������������������������������������������������0000644�0001750�0001750�00000010470�12200204333�017045� 0����������������������������������������������������������������������������������������������������ustar�00smcv����������������������������smcv����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * disco.h - Headers for Gabble service discovery * * Copyright (C) 2006 Collabora Ltd. * Copyright (C) 2006 Nokia Corporation * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 * * -- LET'S DISCO!!! \o/ \o_ _o/ /\o/\ _/o/- -\o\_ -- */ #ifndef __GABBLE_DISCO_H__ #define __GABBLE_DISCO_H__ #include <glib-object.h> #include <wocky/wocky.h> #include "types.h" G_BEGIN_DECLS typedef enum { GABBLE_DISCO_TYPE_INFO, GABBLE_DISCO_TYPE_ITEMS } GabbleDiscoType; typedef struct _GabbleDiscoClass GabbleDiscoClass; typedef struct _GabbleDiscoPrivate GabbleDiscoPrivate; typedef struct _GabbleDiscoRequest GabbleDiscoRequest; /** * GabbleDiscoError: * @GABBLE_DISCO_ERROR_CANCELLED: The DISCO request was cancelled * @GABBLE_DISCO_ERROR_TIMEOUT: The DISCO request timed out * @GABBLE_DISCO_ERROR_UNKNOWN: An unknown error occured */ typedef enum { GABBLE_DISCO_ERROR_CANCELLED, GABBLE_DISCO_ERROR_TIMEOUT, GABBLE_DISCO_ERROR_UNKNOWN } GabbleDiscoError; GQuark gabble_disco_error_quark (void); #define GABBLE_DISCO_ERROR gabble_disco_error_quark () GType gabble_disco_get_type (void); /* TYPE MACROS */ #define GABBLE_TYPE_DISCO \ (gabble_disco_get_type ()) #define GABBLE_DISCO(obj) \ (G_TYPE_CHECK_INSTANCE_CAST((obj), GABBLE_TYPE_DISCO, GabbleDisco)) #define GABBLE_DISCO_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST((klass), GABBLE_TYPE_DISCO, GabbleDiscoClass)) #define GABBLE_IS_DISCO(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE((obj), GABBLE_TYPE_DISCO)) #define GABBLE_IS_DISCO_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE((klass), GABBLE_TYPE_DISCO)) #define GABBLE_DISCO_GET_CLASS(obj) \ (G_TYPE_INSTANCE_GET_CLASS ((obj), GABBLE_TYPE_DISCO, GabbleDiscoClass)) struct _GabbleDiscoClass { GObjectClass parent_class; }; struct _GabbleDisco { GObject parent; GabbleDiscoPrivate *priv; }; typedef void (*GabbleDiscoCb)(GabbleDisco *self, GabbleDiscoRequest *request, const gchar *jid, const gchar *node, WockyNode *query_result, GError* error, gpointer user_data); GabbleDisco *gabble_disco_new (GabbleConnection *); GabbleDiscoRequest *gabble_disco_request (GabbleDisco *self, GabbleDiscoType type, const gchar *jid, const char *node, GabbleDiscoCb callback, gpointer user_data, GObject *object, GError **error); GabbleDiscoRequest *gabble_disco_request_with_timeout (GabbleDisco *self, GabbleDiscoType type, const gchar *jid, const char *node, guint timeout, GabbleDiscoCb callback, gpointer user_data, GObject *object, GError **error); void gabble_disco_cancel_request (GabbleDisco *, GabbleDiscoRequest *); /* Pipelines */ typedef struct _GabbleDiscoItem GabbleDiscoItem; struct _GabbleDiscoItem { const gchar *jid; const char *name; const char *category; const char *type; GHashTable *features; }; typedef void (*GabbleDiscoPipelineCb)(gpointer pipeline, GabbleDiscoItem *item, gpointer user_data); typedef void (*GabbleDiscoEndCb)(gpointer pipeline, gpointer user_data); gpointer gabble_disco_pipeline_init (GabbleDisco *disco, GabbleDiscoPipelineCb callback, GabbleDiscoEndCb end_callback, gpointer user_data); void gabble_disco_pipeline_run (gpointer self, const char *server); void gabble_disco_pipeline_destroy (gpointer self); /* Service discovery */ const GabbleDiscoItem * gabble_disco_service_find (GabbleDisco *disco, const char *category, const char *type, const char *feature); G_END_DECLS #endif ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������telepathy-gabble-0.18.2/src/debug.c�����������������������������������������������������������������0000644�0001750�0001750�00000007200�12200204333�017022� 0����������������������������������������������������������������������������������������������������ustar�00smcv����������������������������smcv����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ #include "config.h" #include "debug.h" #include <stdarg.h> #ifdef HAVE_UNISTD_H # include <unistd.h> #endif #include <sys/types.h> #include <sys/stat.h> #ifdef HAVE_FCNTL_H # include <fcntl.h> #endif #include <errno.h> #include <glib/gstdio.h> #include <telepathy-glib/telepathy-glib.h> static GabbleDebugFlags flags = 0; /* Remember to keep this array up to date with the GabbleDebugFlags enum in debug.h */ static GDebugKey keys[] = { { "presence", GABBLE_DEBUG_PRESENCE }, { "groups", GABBLE_DEBUG_GROUPS }, { "roster", GABBLE_DEBUG_ROSTER }, { "disco", GABBLE_DEBUG_DISCO }, { "properties", GABBLE_DEBUG_PROPERTIES }, { "roomlist", GABBLE_DEBUG_ROOMLIST }, { "media-channel", GABBLE_DEBUG_MEDIA }, { "im", GABBLE_DEBUG_IM }, { "muc", GABBLE_DEBUG_MUC }, { "connection", GABBLE_DEBUG_CONNECTION }, { "vcard", GABBLE_DEBUG_VCARD }, { "pipeline", GABBLE_DEBUG_PIPELINE }, { "jid", GABBLE_DEBUG_JID }, { "olpc", GABBLE_DEBUG_OLPC }, { "bytestream", GABBLE_DEBUG_BYTESTREAM }, { "tubes", GABBLE_DEBUG_TUBES }, { "location", GABBLE_DEBUG_LOCATION }, { "file-transfer", GABBLE_DEBUG_FT }, { "search", GABBLE_DEBUG_SEARCH }, { "base-channel", GABBLE_DEBUG_BASE_CHANNEL }, { "plugins", GABBLE_DEBUG_PLUGINS }, { "mail", GABBLE_DEBUG_MAIL_NOTIF }, { "authentication", GABBLE_DEBUG_AUTH }, { "share", GABBLE_DEBUG_SHARE }, { "tls", GABBLE_DEBUG_TLS }, { "client-types", GABBLE_DEBUG_CLIENT_TYPES }, { 0, }, }; void gabble_debug_set_flags_from_env () { guint nkeys; const gchar *flags_string; for (nkeys = 0; keys[nkeys].value; nkeys++); flags_string = g_getenv ("GABBLE_DEBUG"); tp_debug_set_flags (flags_string); tp_debug_set_flags (flags_string); if (flags_string != NULL) { gabble_debug_set_flags (g_parse_debug_string (flags_string, keys, nkeys)); } } void gabble_debug_set_flags (GabbleDebugFlags new_flags) { flags |= new_flags; } gboolean gabble_debug_flag_is_set (GabbleDebugFlags flag) { return flag & flags; } GHashTable *flag_to_domains = NULL; static const gchar * debug_flag_to_domain (GabbleDebugFlags flag) { if (G_UNLIKELY (flag_to_domains == NULL)) { guint i; flag_to_domains = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, g_free); for (i = 0; keys[i].value; i++) { GDebugKey key = keys[i]; gchar *val; val = g_strdup_printf ("%s/%s", G_LOG_DOMAIN, key.key); g_hash_table_insert (flag_to_domains, GUINT_TO_POINTER (key.value), val); } } return g_hash_table_lookup (flag_to_domains, GUINT_TO_POINTER (flag)); } void gabble_debug_free (void) { if (flag_to_domains == NULL) return; g_hash_table_unref (flag_to_domains); flag_to_domains = NULL; } static void log_to_debug_sender (GLogLevelFlags level, GabbleDebugFlags flag, const gchar *message) { TpDebugSender *dbg; GTimeVal now; dbg = tp_debug_sender_dup (); g_get_current_time (&now); tp_debug_sender_add_message (dbg, &now, debug_flag_to_domain (flag), level, message); g_object_unref (dbg); } void gabble_log (GLogLevelFlags level, GabbleDebugFlags flag, const gchar *format, ...) { gchar *message; va_list args; va_start (args, format); message = g_strdup_vprintf (format, args); va_end (args); log_to_debug_sender (level, flag, message); if (flag & flags || level > G_LOG_LEVEL_DEBUG) g_log (G_LOG_DOMAIN, level, "%s", message); g_free (message); } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������telepathy-gabble-0.18.2/src/debug.h�����������������������������������������������������������������0000644�0001750�0001750�00000006501�12200204333�017032� 0����������������������������������������������������������������������������������������������������ustar�00smcv����������������������������smcv����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#ifndef __DEBUG_H__ #define __DEBUG_H__ #include "config.h" #include <glib.h> #include <wocky/wocky.h> G_BEGIN_DECLS /* Remember to keep this enum up to date with the keys array in debug.c */ typedef enum { GABBLE_DEBUG_PRESENCE = 1 << 0, GABBLE_DEBUG_GROUPS = 1 << 1, GABBLE_DEBUG_ROSTER = 1 << 2, GABBLE_DEBUG_DISCO = 1 << 3, GABBLE_DEBUG_PROPERTIES = 1 << 4, GABBLE_DEBUG_ROOMLIST = 1 << 5, GABBLE_DEBUG_MEDIA = 1 << 6, GABBLE_DEBUG_MUC = 1 << 7, GABBLE_DEBUG_CONNECTION = 1 << 8, GABBLE_DEBUG_IM = 1 << 9, GABBLE_DEBUG_TUBES = 1 << 10, GABBLE_DEBUG_VCARD = 1 << 11, GABBLE_DEBUG_PIPELINE = 1 << 12, GABBLE_DEBUG_JID = 1 << 13, GABBLE_DEBUG_OLPC = 1 << 14, GABBLE_DEBUG_BYTESTREAM = 1 << 16, GABBLE_DEBUG_LOCATION = 1 << 17, GABBLE_DEBUG_FT = 1 << 18, GABBLE_DEBUG_SEARCH = 1 << 19, GABBLE_DEBUG_BASE_CHANNEL = 1 << 20, GABBLE_DEBUG_PLUGINS = 1 << 21, GABBLE_DEBUG_MAIL_NOTIF = 1 << 22, GABBLE_DEBUG_AUTH = 1 << 23, GABBLE_DEBUG_SLACKER = 1 << 24, GABBLE_DEBUG_SHARE = 1 << 25, GABBLE_DEBUG_TLS = 1 << 26, GABBLE_DEBUG_CLIENT_TYPES = 1 << 27, } GabbleDebugFlags; void gabble_debug_set_flags_from_env (void); void gabble_debug_set_flags (GabbleDebugFlags flags); gboolean gabble_debug_flag_is_set (GabbleDebugFlags flag); void gabble_debug_free (void); void gabble_log (GLogLevelFlags level, GabbleDebugFlags flag, const gchar *format, ...) G_GNUC_PRINTF (3, 4); G_END_DECLS #ifdef DEBUG_FLAG #define ERROR(format, ...) \ G_STMT_START \ { \ gabble_log (G_LOG_LEVEL_ERROR, DEBUG_FLAG, "%s (%s): " format, \ G_STRFUNC, G_STRLOC, ##__VA_ARGS__); \ g_assert_not_reached (); \ } \ G_STMT_END #define CRITICAL(format, ...) \ gabble_log (G_LOG_LEVEL_CRITICAL, DEBUG_FLAG, "%s (%s): " format, \ G_STRFUNC, G_STRLOC, ##__VA_ARGS__) #define WARNING(format, ...) \ gabble_log (G_LOG_LEVEL_WARNING, DEBUG_FLAG, "%s (%s): " format, \ G_STRFUNC, G_STRLOC, ##__VA_ARGS__) #define MESSAGE(format, ...) \ gabble_log (G_LOG_LEVEL_MESSAGE, DEBUG_FLAG, "%s (%s): " format, \ G_STRFUNC, G_STRLOC, ##__VA_ARGS__) #define INFO(format, ...) \ gabble_log (G_LOG_LEVEL_INFO, DEBUG_FLAG, "%s (%s): " format, \ G_STRFUNC, G_STRLOC, ##__VA_ARGS__) #ifdef ENABLE_DEBUG # define DEBUG(format, ...) \ gabble_log (G_LOG_LEVEL_DEBUG, DEBUG_FLAG, "%s (%s): " format, \ G_STRFUNC, G_STRLOC, ##__VA_ARGS__) # define DEBUGGING gabble_debug_flag_is_set (DEBUG_FLAG) # define STANZA_DEBUG(st, s) \ NODE_DEBUG (wocky_stanza_get_top_node (st), s) # define NODE_DEBUG(n, s) \ G_STMT_START { \ gchar *debug_tmp = wocky_node_to_string (n); \ gabble_log (G_LOG_LEVEL_DEBUG, DEBUG_FLAG, "%s: %s:\n%s", G_STRFUNC, s, debug_tmp); \ g_free (debug_tmp); \ } G_STMT_END #else /* !defined (ENABLE_DEBUG) */ static inline void DEBUG ( const gchar *format, ...) { } # define DEBUGGING 0 static inline void STANZA_DEBUG ( WockyStanza *stanza, const gchar *format, ...) { } static inline void NODE_DEBUG ( WockyNode *node, const gchar *format, ...) { } #endif /* !defined (ENABLE_DEBUG) */ #endif /* DEBUG_FLAG */ #endif /* __DEBUG_H__ */ �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������telepathy-gabble-0.18.2/src/connection-manager.c����������������������������������������������������0000644�0001750�0001750�00000005111�12200204333�021502� 0����������������������������������������������������������������������������������������������������ustar�00smcv����������������������������smcv����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * gabble-connection-manager.c - Source for GabbleConnectionManager * Copyright (C) 2005-2007 Collabora Ltd. * Copyright (C) 2005-2007 Nokia Corporation * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 "config.h" #include "connection-manager.h" #include <string.h> #include <dbus/dbus-protocol.h> #include <dbus/dbus-glib.h> #include <telepathy-glib/telepathy-glib.h> #include <telepathy-glib/telepathy-glib-dbus.h> #include <wocky/wocky.h> #include "connection.h" #include "debug.h" #include "extensions/extensions.h" #include "protocol.h" G_DEFINE_TYPE(GabbleConnectionManager, gabble_connection_manager, TP_TYPE_BASE_CONNECTION_MANAGER) /* type definition stuff */ static void gabble_connection_manager_init (GabbleConnectionManager *self) { } static void gabble_connection_manager_constructed (GObject *object) { GabbleConnectionManager *self = GABBLE_CONNECTION_MANAGER (object); TpBaseConnectionManager *base = (TpBaseConnectionManager *) self; void (*constructed) (GObject *) = ((GObjectClass *) gabble_connection_manager_parent_class)->constructed; TpBaseProtocol *protocol; if (constructed != NULL) constructed (object); protocol = gabble_jabber_protocol_new (); tp_base_connection_manager_add_protocol (base, protocol); g_object_unref (protocol); } static void gabble_connection_manager_finalize (GObject *object) { wocky_caps_cache_free_shared (); gabble_debug_free (); G_OBJECT_CLASS (gabble_connection_manager_parent_class)->finalize (object); } static void gabble_connection_manager_class_init (GabbleConnectionManagerClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); TpBaseConnectionManagerClass *base_class = (TpBaseConnectionManagerClass *) klass; base_class->cm_dbus_name = "gabble"; object_class->constructed = gabble_connection_manager_constructed; object_class->finalize = gabble_connection_manager_finalize; } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������telepathy-gabble-0.18.2/src/connection-manager.h����������������������������������������������������0000644�0001750�0001750�00000004566�12200204333�021524� 0����������������������������������������������������������������������������������������������������ustar�00smcv����������������������������smcv����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * gabble-connection-manager.h - Header for GabbleConnectionManager * Copyright (C) 2005 Collabora Ltd. * Copyright (C) 2005 Nokia Corporation * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 __GABBLE_CONNECTION_MANAGER_H__ #define __GABBLE_CONNECTION_MANAGER_H__ #include <glib-object.h> #include <telepathy-glib/telepathy-glib.h> G_BEGIN_DECLS typedef struct _GabbleConnectionManager GabbleConnectionManager; typedef struct _GabbleConnectionManagerClass GabbleConnectionManagerClass; typedef struct _GabbleConnectionManagerPrivate GabbleConnectionManagerPrivate; struct _GabbleConnectionManagerClass { TpBaseConnectionManagerClass parent_class; }; struct _GabbleConnectionManager { TpBaseConnectionManager parent; }; GType gabble_connection_manager_get_type (void); /* TYPE MACROS */ #define GABBLE_TYPE_CONNECTION_MANAGER \ (gabble_connection_manager_get_type ()) #define GABBLE_CONNECTION_MANAGER(obj) \ (G_TYPE_CHECK_INSTANCE_CAST((obj), GABBLE_TYPE_CONNECTION_MANAGER, \ GabbleConnectionManager)) #define GABBLE_CONNECTION_MANAGER_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST((klass), GABBLE_TYPE_CONNECTION_MANAGER, \ GabbleConnectionManagerClass)) #define GABBLE_IS_CONNECTION_MANAGER(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE((obj), GABBLE_TYPE_CONNECTION_MANAGER)) #define GABBLE_IS_CONNECTION_MANAGER_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE((klass), GABBLE_TYPE_CONNECTION_MANAGER)) #define GABBLE_CONNECTION_MANAGER_GET_CLASS(obj) \ (G_TYPE_INSTANCE_GET_CLASS ((obj), GABBLE_TYPE_CONNECTION_MANAGER, \ GabbleConnectionManagerClass)) G_END_DECLS #endif /* #ifndef __GABBLE_CONNECTION_MANAGER_H__*/ ������������������������������������������������������������������������������������������������������������������������������������������telepathy-gabble-0.18.2/src/connection.c������������������������������������������������������������0000644�0001750�0001750�00000371116�12227000321�020105� 0����������������������������������������������������������������������������������������������������ustar�00smcv����������������������������smcv����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * gabble-connection.c - Source for GabbleConnection * Copyright (C) 2005-2010 Collabora Ltd. * Copyright (C) 2005-2010 Nokia Corporation * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 "config.h" #include "connection.h" #include "gabble.h" #include <stdio.h> #include <string.h> #include <dbus/dbus-glib.h> #include <dbus/dbus-glib-lowlevel.h> #include <glib-object.h> #include <wocky/wocky.h> #include <telepathy-glib/telepathy-glib.h> #include <telepathy-glib/telepathy-glib-dbus.h> #include "extensions/extensions.h" #define DEBUG_FLAG GABBLE_DEBUG_CONNECTION #include <gabble/error.h> #include "bytestream-factory.h" #include "gabble/capabilities.h" #include "gabble/caps-channel-manager.h" #include "gabble/plugin-connection.h" #include "caps-hash.h" #include "auth-manager.h" #include "conn-aliasing.h" #include "conn-avatars.h" #include "conn-client-types.h" #include "conn-contact-info.h" #include "conn-location.h" #include "conn-presence.h" #include "conn-sidecars.h" #include "conn-mail-notif.h" #include "conn-olpc.h" #include "conn-power-saving.h" #include "debug.h" #include "disco.h" #include "im-factory.h" #include "legacy-caps.h" #include "muc-factory.h" #include "namespaces.h" #include "presence-cache.h" #include "presence.h" #include "request-pipeline.h" #include "roomlist-manager.h" #include "roster.h" #include "search-manager.h" #include "server-tls-channel.h" #include "server-tls-manager.h" #include "plugin-loader.h" #include "private-tubes-factory.h" #include "util.h" #include "vcard-manager.h" #include "conn-util.h" #include "conn-addressing.h" #ifdef ENABLE_VOIP #include "media-channel.h" #include "media-factory.h" #endif static guint disco_reply_timeout = 5; #define DISCONNECT_TIMEOUT 5 static void capabilities_service_iface_init (gpointer, gpointer); static void gabble_conn_contact_caps_iface_init (gpointer, gpointer); static void conn_capabilities_fill_contact_attributes (GObject *obj, const GArray *contacts, GHashTable *attributes_hash); static void conn_contact_capabilities_fill_contact_attributes (GObject *obj, const GArray *contacts, GHashTable *attributes_hash); static void gabble_plugin_connection_iface_init ( GabblePluginConnectionInterface *iface, gpointer conn); static gchar *_gabble_plugin_connection_get_full_jid ( GabblePluginConnection *conn); static TpBaseContactList *_gabble_plugin_connection_get_contact_list ( GabblePluginConnection *conn); G_DEFINE_TYPE_WITH_CODE(GabbleConnection, gabble_connection, TP_TYPE_BASE_CONNECTION, G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CONNECTION_INTERFACE_ALIASING, conn_aliasing_iface_init); G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CONNECTION_INTERFACE_AVATARS, conn_avatars_iface_init); G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CONNECTION_INTERFACE_CONTACT_INFO, conn_contact_info_iface_init); G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CONNECTION_INTERFACE_CAPABILITIES, capabilities_service_iface_init); G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_DBUS_PROPERTIES, tp_dbus_properties_mixin_iface_init); G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CONNECTION_INTERFACE_CONTACTS, tp_contacts_mixin_iface_init); G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CONNECTION_INTERFACE_CONTACT_LIST, tp_base_contact_list_mixin_list_iface_init); G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CONNECTION_INTERFACE_CONTACT_GROUPS, tp_base_contact_list_mixin_groups_iface_init); G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CONNECTION_INTERFACE_CONTACT_BLOCKING, tp_base_contact_list_mixin_blocking_iface_init); G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CONNECTION_INTERFACE_SIMPLE_PRESENCE, tp_presence_mixin_simple_presence_iface_init); G_IMPLEMENT_INTERFACE (GABBLE_TYPE_SVC_CONNECTION_INTERFACE_GABBLE_DECLOAK, conn_decloak_iface_init); G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CONNECTION_INTERFACE_LOCATION, location_iface_init); G_IMPLEMENT_INTERFACE (GABBLE_TYPE_SVC_OLPC_BUDDY_INFO, olpc_buddy_info_iface_init); G_IMPLEMENT_INTERFACE (GABBLE_TYPE_SVC_OLPC_ACTIVITY_PROPERTIES, olpc_activity_properties_iface_init); G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CONNECTION_INTERFACE_CONTACT_CAPABILITIES, gabble_conn_contact_caps_iface_init); G_IMPLEMENT_INTERFACE (GABBLE_TYPE_SVC_CONNECTION_FUTURE, conn_future_iface_init); G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CONNECTION_INTERFACE_MAIL_NOTIFICATION, conn_mail_notif_iface_init); G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CONNECTION_INTERFACE_CLIENT_TYPES, conn_client_types_iface_init); G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CONNECTION_INTERFACE_POWER_SAVING, conn_power_saving_iface_init); G_IMPLEMENT_INTERFACE (GABBLE_TYPE_SVC_CONNECTION_INTERFACE_ADDRESSING, conn_addressing_iface_init); G_IMPLEMENT_INTERFACE (GABBLE_TYPE_PLUGIN_CONNECTION, gabble_plugin_connection_iface_init); ) /* properties */ enum { PROP_CONNECT_SERVER = 1, PROP_EXPLICIT_SERVER, PROP_PORT, PROP_OLD_SSL, PROP_REQUIRE_ENCRYPTION, PROP_REGISTER, PROP_LOW_BANDWIDTH, PROP_STREAM_SERVER, PROP_USERNAME, PROP_PASSWORD, PROP_RESOURCE, PROP_PRIORITY, PROP_HTTPS_PROXY_SERVER, PROP_HTTPS_PROXY_PORT, PROP_FALLBACK_CONFERENCE_SERVER, PROP_STUN_SERVER, PROP_STUN_PORT, PROP_FALLBACK_STUN_SERVER, PROP_FALLBACK_STUN_PORT, PROP_IGNORE_SSL_ERRORS, PROP_ALIAS, PROP_FALLBACK_SOCKS5_PROXIES, PROP_KEEPALIVE_INTERVAL, PROP_DECLOAK_AUTOMATICALLY, PROP_FALLBACK_SERVERS, PROP_EXTRA_CERTIFICATE_IDENTITIES, PROP_POWER_SAVING, PROP_DOWNLOAD_AT_CONNECTION, LAST_PROPERTY }; /* private structure */ struct _GabbleConnectionPrivate { WockyConnector *connector; WockyPorter *porter; WockyPing *pinger; GCancellable *cancellable; /* connection properties */ gchar *connect_server; gchar *explicit_server; guint port; gboolean old_ssl; gboolean require_encryption; gboolean ignore_ssl_errors; TpConnectionStatusReason ssl_error; gboolean do_register; gboolean low_bandwidth; guint keepalive_interval; gchar *https_proxy_server; guint16 https_proxy_port; gchar *stun_server; guint16 stun_port; gchar *fallback_stun_server; guint16 fallback_stun_port; gchar *fallback_conference_server; GStrv fallback_socks5_proxies; gboolean decloak_automatically; GStrv fallback_servers; guint fallback_server_index; GStrv extra_certificate_identities; gboolean power_saving; /* authentication properties */ gchar *stream_server; gchar *username; gchar *password; gchar *resource; gint8 priority; gchar *alias; /* reference to conference server name */ const gchar *conference_server; /* serial number of current advertised caps */ guint caps_serial; /* Last activity time for XEP-0012 purposes, where "activity" is defined to * mean "sending a message". */ time_t last_activity_time; /* capabilities from various sources: */ /* subscriptions on behalf of the Connection, like PEP "+notify" * namespaces (this one is add-only) */ GabbleCapabilitySet *notify_caps; /* caps provided by Capabilities.AdvertiseCapabilities (tp-spec 0.16) */ GabbleCapabilitySet *legacy_caps; /* additional caps that we advertise until the first call to * AdvertiseCapabilities or UpdateCapabilities, for vague historical * reasons */ GabbleCapabilitySet *bonus_caps; /* sidecar caps set by gabble_connection_update_sidecar_capabilities */ GabbleCapabilitySet *sidecar_caps; /* caps provided via ContactCapabilities.UpdateCapabilities () * gchar * (client name) => GabbleCapabilitySet * */ GHashTable *client_caps; /* the union of the above */ GabbleCapabilitySet *all_caps; /* data forms provided via UpdateCapabilities() * gchar * (client name) => GPtrArray<owned WockyDataForm> */ GHashTable *client_data_forms; /* auth manager */ GabbleAuthManager *auth_manager; /* server TLS manager */ GabbleServerTLSManager *server_tls_manager; /* IM factory; we need this to add text caps for everyone (even * offline contacts) which is done through the IM factory's * GabbleCapsChannelManagerInterface->get_contact_caps function. */ GabbleImFactory *im_factory; /* stream id returned by the connector */ gchar *stream_id; /* timer used when trying to properly disconnect */ guint disconnect_timer; /* Number of things we are waiting for before changing the connection status * to connected */ guint waiting_connected; /* Used to cancel pending calls to _gabble_connection_send_with_reply(). It * should not be necessary because by the time we get to cancelling this (in * our dispose()) the porter should be long dead and have called back for all * outstanding requests. However... call this paranoia, and a desire to * delete the Loudmouth compatibility layer without spending any more hours * untangling even more old code. */ GCancellable *iq_reply_cancellable; gboolean closing; /* gobject housekeeping */ gboolean dispose_has_run; }; static void connection_capabilities_update_cb (GabblePresenceCache *cache, TpHandle handle, const GabbleCapabilitySet *old_cap_set, const GabbleCapabilitySet *new_cap_set, gpointer user_data); static gboolean gabble_connection_refresh_capabilities (GabbleConnection *self, GabbleCapabilitySet **old_out); static void add_to_array (gpointer data, gpointer user_data) { g_ptr_array_add (user_data, data); } static GPtrArray * _gabble_connection_create_channel_managers (TpBaseConnection *conn) { GabbleConnection *self = GABBLE_CONNECTION (conn); GabblePluginConnection *plugin_connection = GABBLE_PLUGIN_CONNECTION (self); GPtrArray *channel_managers = g_ptr_array_sized_new (10); GabblePluginLoader *loader; GPtrArray *tmp; self->roster = gabble_roster_new (self); g_signal_connect (self->roster, "nicknames-update", G_CALLBACK (gabble_conn_aliasing_nicknames_updated), self); g_ptr_array_add (channel_managers, self->roster); self->priv->im_factory = g_object_new (GABBLE_TYPE_IM_FACTORY, "connection", self, NULL); g_ptr_array_add (channel_managers, self->priv->im_factory); g_ptr_array_add (channel_managers, g_object_new (GABBLE_TYPE_ROOMLIST_MANAGER, "connection", self, NULL)); g_ptr_array_add (channel_managers, g_object_new (GABBLE_TYPE_SEARCH_MANAGER, "connection", self, NULL)); self->priv->auth_manager = g_object_new (GABBLE_TYPE_AUTH_MANAGER, "connection", self, NULL); g_ptr_array_add (channel_managers, self->priv->auth_manager); self->priv->server_tls_manager = g_object_new (GABBLE_TYPE_SERVER_TLS_MANAGER, "connection", self, NULL); g_ptr_array_add (channel_managers, self->priv->server_tls_manager); self->muc_factory = g_object_new (GABBLE_TYPE_MUC_FACTORY, "connection", self, NULL); g_ptr_array_add (channel_managers, self->muc_factory); self->private_tubes_factory = gabble_private_tubes_factory_new (self); g_ptr_array_add (channel_managers, self->private_tubes_factory); #ifdef ENABLE_VOIP self->jingle_mint = gabble_jingle_mint_new (self); g_ptr_array_add (channel_managers, g_object_new (GABBLE_TYPE_MEDIA_FACTORY, "connection", self, NULL)); #endif #ifdef ENABLE_FILE_TRANSFER self->ft_manager = gabble_ft_manager_new (self); g_ptr_array_add (channel_managers, self->ft_manager); #endif /* plugin channel managers */ loader = gabble_plugin_loader_dup (); tmp = gabble_plugin_loader_create_channel_managers (loader, plugin_connection); g_object_unref (loader); g_ptr_array_foreach (tmp, add_to_array, channel_managers); g_ptr_array_unref (tmp); return channel_managers; } static void gabble_plugin_connection_iface_init ( GabblePluginConnectionInterface *iface, gpointer conn) { iface->add_sidecar_own_caps = gabble_connection_add_sidecar_own_caps; iface->add_sidecar_own_caps_full= gabble_connection_add_sidecar_own_caps_full; iface->get_session = gabble_connection_get_session; iface->get_full_jid = _gabble_plugin_connection_get_full_jid; iface->get_jid_for_caps = gabble_connection_get_jid_for_caps; iface->pick_best_resource_for_caps = gabble_connection_pick_best_resource_for_caps; iface->get_contact_list = _gabble_plugin_connection_get_contact_list; iface->get_caps = gabble_connection_get_caps; } static GObject * gabble_connection_constructor (GType type, guint n_construct_properties, GObjectConstructParam *construct_params) { GabbleConnection *self = GABBLE_CONNECTION ( G_OBJECT_CLASS (gabble_connection_parent_class)->constructor ( type, n_construct_properties, construct_params)); GabbleConnectionPrivate *priv = G_TYPE_INSTANCE_GET_PRIVATE (self, GABBLE_TYPE_CONNECTION, GabbleConnectionPrivate); TpBaseConnection *base = TP_BASE_CONNECTION (self); DEBUG("Post-construction: (GabbleConnection *)%p", self); tp_base_connection_add_possible_client_interest (base, TP_IFACE_QUARK_CONNECTION_INTERFACE_MAIL_NOTIFICATION); self->req_pipeline = gabble_request_pipeline_new (self); self->disco = gabble_disco_new (self); self->vcard_manager = gabble_vcard_manager_new (self); g_signal_connect (self->vcard_manager, "nickname-update", G_CALLBACK (gabble_conn_aliasing_nickname_updated), self); self->presence_cache = gabble_presence_cache_new (self); g_signal_connect (self->presence_cache, "nickname-update", G_CALLBACK (gabble_conn_aliasing_nickname_updated), self); g_signal_connect (self->presence_cache, "capabilities-update", G_CALLBACK (connection_capabilities_update_cb), self); tp_contacts_mixin_init (G_OBJECT (self), G_STRUCT_OFFSET (GabbleConnection, contacts)); tp_base_connection_register_with_contacts_mixin (base); tp_base_contact_list_mixin_register_with_contacts_mixin (base); conn_aliasing_init (self); conn_avatars_init (self); conn_contact_info_init (self); conn_presence_init (self); conn_olpc_activity_properties_init (self); conn_location_init (self); conn_sidecars_init (self); conn_mail_notif_init (self); conn_client_types_init (self); conn_addressing_init (self); tp_contacts_mixin_add_contact_attributes_iface (G_OBJECT (self), TP_IFACE_CONNECTION_INTERFACE_CAPABILITIES, conn_capabilities_fill_contact_attributes); tp_contacts_mixin_add_contact_attributes_iface (G_OBJECT (self), TP_IFACE_CONNECTION_INTERFACE_CONTACT_CAPABILITIES, conn_contact_capabilities_fill_contact_attributes); self->bytestream_factory = gabble_bytestream_factory_new (self); self->avatar_requests = g_hash_table_new (NULL, NULL); self->vcard_requests = g_hash_table_new (NULL, NULL); if (priv->fallback_socks5_proxies == NULL) { /* No proxies have been defined, set the default ones */ gchar *default_socks5_proxies[] = GABBLE_PARAMS_DEFAULT_SOCKS5_PROXIES; g_object_set (self, "fallback-socks5-proxies", default_socks5_proxies, NULL); } priv->all_caps = gabble_capability_set_new (); priv->notify_caps = gabble_capability_set_new (); priv->legacy_caps = gabble_capability_set_new (); priv->sidecar_caps = gabble_capability_set_new (); priv->client_caps = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify) gabble_capability_set_free); priv->client_data_forms = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify) g_ptr_array_unref); /* Historically, the optional Jingle transports were in our initial * presence, but could be removed by AdvertiseCapabilities(). Emulate * that here for now. */ priv->bonus_caps = gabble_capability_set_new (); #ifdef ENABLE_VOIP gabble_capability_set_add (priv->bonus_caps, NS_GOOGLE_TRANSPORT_P2P); gabble_capability_set_add (priv->bonus_caps, NS_JINGLE_TRANSPORT_ICEUDP); #endif return (GObject *) self; } static gchar * dup_default_resource (void) { /* This is a once-per-process leak. */ static gchar *default_resource = NULL; if (G_UNLIKELY (default_resource == NULL)) { char *local_machine_id = dbus_get_local_machine_id (); if (local_machine_id == NULL) g_error ("Out of memory getting local machine ID"); default_resource = sha1_hex (local_machine_id, strlen (local_machine_id)); /* Let's keep the resource a maneagable length. */ default_resource[8] = '\0'; dbus_free (local_machine_id); } return g_strdup (default_resource); } static void gabble_connection_constructed (GObject *object) { GabbleConnection *self = GABBLE_CONNECTION (object); GabbleConnectionPrivate *priv = self->priv; void (*chain_up)(GObject *) = G_OBJECT_CLASS (gabble_connection_parent_class)->constructed; if (chain_up != NULL) chain_up (object); if (priv->resource == NULL) { priv->resource = dup_default_resource (); DEBUG ("defaulted resource to %s", priv->resource); } /* set initial presence */ self->self_presence = gabble_presence_new (); g_assert (priv->resource); gabble_presence_update (self->self_presence, priv->resource, GABBLE_PRESENCE_AVAILABLE, NULL, priv->priority, NULL, time (NULL)); } static void gabble_connection_init (GabbleConnection *self) { GError *error = NULL; GabbleConnectionPrivate *priv = G_TYPE_INSTANCE_GET_PRIVATE (self, GABBLE_TYPE_CONNECTION, GabbleConnectionPrivate); DEBUG("Initializing (GabbleConnection *)%p", self); self->daemon = tp_dbus_daemon_dup (&error); if (self->daemon == NULL) { g_error ("Failed to connect to dbus daemon: %s", error->message); } self->priv = priv; priv->iq_reply_cancellable = g_cancellable_new (); priv->caps_serial = 1; priv->last_activity_time = time (NULL); priv->port = 5222; gabble_capabilities_init (self); } static void gabble_connection_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { GabbleConnection *self = (GabbleConnection *) object; GabbleConnectionPrivate *priv = self->priv; switch (property_id) { case PROP_CONNECT_SERVER: g_value_set_string (value, priv->connect_server); break; case PROP_EXPLICIT_SERVER: g_value_set_string (value, priv->explicit_server); break; case PROP_STREAM_SERVER: g_value_set_string (value, priv->stream_server); break; case PROP_PORT: g_value_set_uint (value, priv->port); break; case PROP_OLD_SSL: g_value_set_boolean (value, priv->old_ssl); break; case PROP_REQUIRE_ENCRYPTION: g_value_set_boolean (value, priv->require_encryption); break; case PROP_REGISTER: g_value_set_boolean (value, priv->do_register); break; case PROP_LOW_BANDWIDTH: g_value_set_boolean (value, priv->low_bandwidth); break; case PROP_USERNAME: g_value_set_string (value, priv->username); break; case PROP_PASSWORD: g_value_set_string (value, priv->password); break; case PROP_RESOURCE: g_value_set_string (value, priv->resource); break; case PROP_PRIORITY: g_value_set_schar (value, priv->priority); break; case PROP_HTTPS_PROXY_SERVER: g_value_set_string (value, priv->https_proxy_server); break; case PROP_HTTPS_PROXY_PORT: g_value_set_uint (value, priv->https_proxy_port); break; case PROP_FALLBACK_CONFERENCE_SERVER: g_value_set_string (value, priv->fallback_conference_server); break; case PROP_IGNORE_SSL_ERRORS: g_value_set_boolean (value, priv->ignore_ssl_errors); break; case PROP_ALIAS: g_value_set_string (value, priv->alias); break; case PROP_STUN_SERVER: g_value_set_string (value, priv->stun_server); break; case PROP_STUN_PORT: g_value_set_uint (value, priv->stun_port); break; case PROP_FALLBACK_STUN_SERVER: g_value_set_string (value, priv->fallback_stun_server); break; case PROP_FALLBACK_STUN_PORT: g_value_set_uint (value, priv->fallback_stun_port); break; case PROP_FALLBACK_SOCKS5_PROXIES: g_value_set_boxed (value, priv->fallback_socks5_proxies); break; case PROP_KEEPALIVE_INTERVAL: g_value_set_uint (value, priv->keepalive_interval); break; case PROP_DECLOAK_AUTOMATICALLY: g_value_set_boolean (value, priv->decloak_automatically); break; case PROP_FALLBACK_SERVERS: g_value_set_boxed (value, priv->fallback_servers); break; case PROP_EXTRA_CERTIFICATE_IDENTITIES: g_value_set_boxed (value, priv->extra_certificate_identities); break; case PROP_POWER_SAVING: g_value_set_boolean (value, priv->power_saving); break; case PROP_DOWNLOAD_AT_CONNECTION: { gboolean download_at_connection = TRUE; if (self->roster != NULL) { g_object_get (self->roster, "download-at-connection", &download_at_connection, NULL); } else { g_warn_if_reached (); } g_value_set_boolean (value, download_at_connection); break; } default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void gabble_connection_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { GabbleConnection *self = (GabbleConnection *) object; GabbleConnectionPrivate *priv = self->priv; switch (property_id) { case PROP_EXPLICIT_SERVER: g_free (priv->explicit_server); priv->explicit_server = g_value_dup_string (value); if (priv->connect_server == NULL) priv->connect_server = g_value_dup_string (value); break; case PROP_PORT: priv->port = g_value_get_uint (value); break; case PROP_OLD_SSL: priv->old_ssl = g_value_get_boolean (value); break; case PROP_REQUIRE_ENCRYPTION: priv->require_encryption = g_value_get_boolean (value); break; case PROP_REGISTER: priv->do_register = g_value_get_boolean (value); break; case PROP_LOW_BANDWIDTH: priv->low_bandwidth = g_value_get_boolean (value); break; case PROP_STREAM_SERVER: g_free (priv->stream_server); priv->stream_server = g_value_dup_string (value); break; case PROP_USERNAME: g_free (priv->username); priv->username = g_value_dup_string (value); break; case PROP_PASSWORD: g_free (priv->password); priv->password = g_value_dup_string (value); break; case PROP_RESOURCE: if (tp_strdiff (priv->resource, g_value_get_string (value))) { gchar *old_resource = priv->resource; gchar *new_resource = g_value_dup_string (value); time_t now = time (NULL); priv->resource = new_resource; /* Add self presence for new resource... */ gabble_presence_update (self->self_presence, new_resource, self->self_presence->status, self->self_presence->status_message, priv->priority, NULL, now); /* ...and remove it for the old one. */ gabble_presence_update (self->self_presence, old_resource, GABBLE_PRESENCE_OFFLINE, NULL, 0, NULL, now); g_free (old_resource); } break; case PROP_PRIORITY: priv->priority = g_value_get_schar (value); break; case PROP_HTTPS_PROXY_SERVER: g_free (priv->https_proxy_server); priv->https_proxy_server = g_value_dup_string (value); break; case PROP_HTTPS_PROXY_PORT: priv->https_proxy_port = g_value_get_uint (value); break; case PROP_FALLBACK_CONFERENCE_SERVER: g_free (priv->fallback_conference_server); priv->fallback_conference_server = g_value_dup_string (value); break; case PROP_IGNORE_SSL_ERRORS: priv->ignore_ssl_errors = g_value_get_boolean (value); break; case PROP_ALIAS: g_free (priv->alias); priv->alias = g_value_dup_string (value); break; case PROP_STUN_SERVER: g_free (priv->stun_server); priv->stun_server = g_value_dup_string (value); break; case PROP_STUN_PORT: priv->stun_port = g_value_get_uint (value); break; case PROP_FALLBACK_STUN_SERVER: g_free (priv->fallback_stun_server); priv->fallback_stun_server = g_value_dup_string (value); break; case PROP_FALLBACK_STUN_PORT: priv->fallback_stun_port = g_value_get_uint (value); break; case PROP_FALLBACK_SOCKS5_PROXIES: if (priv->fallback_socks5_proxies != NULL) g_strfreev (priv->fallback_socks5_proxies); priv->fallback_socks5_proxies = g_value_dup_boxed (value); break; case PROP_KEEPALIVE_INTERVAL: priv->keepalive_interval = g_value_get_uint (value); if (priv->pinger != NULL) g_object_set (priv->pinger, "ping-interval", priv->keepalive_interval, NULL); break; case PROP_DECLOAK_AUTOMATICALLY: priv->decloak_automatically = g_value_get_boolean (value); break; case PROP_FALLBACK_SERVERS: if (priv->fallback_servers != NULL) g_strfreev (priv->fallback_servers); priv->fallback_servers = g_value_dup_boxed (value); priv->fallback_server_index = 0; break; case PROP_EXTRA_CERTIFICATE_IDENTITIES: if (priv->extra_certificate_identities != NULL) g_strfreev (priv->extra_certificate_identities); priv->extra_certificate_identities = g_value_dup_boxed (value); break; case PROP_POWER_SAVING: priv->power_saving = g_value_get_boolean (value); break; case PROP_DOWNLOAD_AT_CONNECTION: /* Connection parameters are set by TpBaseProtocolClass->new_connection * after the channel managers are created. So at this step self->roster * is not NULL and we pass the property. */ if (self->roster != NULL) g_object_set (self->roster, "download-at-connection", g_value_get_boolean (value), NULL); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void gabble_connection_dispose (GObject *object); static void gabble_connection_finalize (GObject *object); static void connection_shut_down (TpBaseConnection *base); static gboolean _gabble_connection_connect (TpBaseConnection *base, GError **error); static gchar * gabble_connection_get_unique_name (TpBaseConnection *self) { GabbleConnectionPrivate *priv = GABBLE_CONNECTION (self)->priv; gchar *unique_name = gabble_encode_jid (priv->username, priv->stream_server, priv->resource); if (priv->username == NULL) { gchar *tmp = unique_name; unique_name = g_strdup_printf ("%s_%p", tmp, self); g_free (tmp); } return unique_name; } /* For the benefit of the unit tests, this will allow the connection to * be NULL */ void _gabble_connection_create_handle_repos (TpBaseConnection *conn, TpHandleRepoIface *repos[NUM_TP_HANDLE_TYPES]) { repos[TP_HANDLE_TYPE_CONTACT] = tp_dynamic_handle_repo_new (TP_HANDLE_TYPE_CONTACT, gabble_normalize_contact, GUINT_TO_POINTER (GABBLE_JID_ANY)); repos[TP_HANDLE_TYPE_ROOM] = tp_dynamic_handle_repo_new (TP_HANDLE_TYPE_ROOM, gabble_normalize_room, conn); } #define TWICE(x) (x), (x) static const gchar *implemented_interfaces[] = { /* conditionally present interfaces */ TP_IFACE_CONNECTION_INTERFACE_MAIL_NOTIFICATION, GABBLE_IFACE_OLPC_ACTIVITY_PROPERTIES, GABBLE_IFACE_OLPC_BUDDY_INFO, /* always present interfaces */ TP_IFACE_CONNECTION_INTERFACE_POWER_SAVING, TP_IFACE_CONNECTION_INTERFACE_ALIASING, TP_IFACE_CONNECTION_INTERFACE_CAPABILITIES, TP_IFACE_CONNECTION_INTERFACE_SIMPLE_PRESENCE, TP_IFACE_CONNECTION_INTERFACE_PRESENCE, TP_IFACE_CONNECTION_INTERFACE_AVATARS, TP_IFACE_CONNECTION_INTERFACE_CONTACT_INFO, TP_IFACE_CONNECTION_INTERFACE_CONTACTS, TP_IFACE_CONNECTION_INTERFACE_CONTACT_LIST, TP_IFACE_CONNECTION_INTERFACE_CONTACT_GROUPS, TP_IFACE_CONNECTION_INTERFACE_REQUESTS, TP_IFACE_CONNECTION_INTERFACE_CONTACT_CAPABILITIES, TP_IFACE_CONNECTION_INTERFACE_LOCATION, GABBLE_IFACE_CONNECTION_INTERFACE_GABBLE_DECLOAK, GABBLE_IFACE_CONNECTION_FUTURE, TP_IFACE_CONNECTION_INTERFACE_CLIENT_TYPES, GABBLE_IFACE_CONNECTION_INTERFACE_ADDRESSING, NULL }; static const gchar **interfaces_always_present = implemented_interfaces + 3; const gchar ** gabble_connection_get_implemented_interfaces (void) { return implemented_interfaces; } const gchar ** gabble_connection_get_guaranteed_interfaces (void) { return interfaces_always_present; } static GPtrArray * _gabble_connection_get_interfaces_always_present (TpBaseConnection *base) { GPtrArray *interfaces; const gchar **iter; interfaces = TP_BASE_CONNECTION_CLASS ( gabble_connection_parent_class)->get_interfaces_always_present (base); for (iter = interfaces_always_present; *iter != NULL; iter++) g_ptr_array_add (interfaces, (gchar *) *iter); return interfaces; } static void gabble_connection_class_init (GabbleConnectionClass *gabble_connection_class) { GObjectClass *object_class = G_OBJECT_CLASS (gabble_connection_class); TpBaseConnectionClass *parent_class = TP_BASE_CONNECTION_CLASS ( gabble_connection_class); static TpDBusPropertiesMixinPropImpl location_props[] = { { "LocationAccessControlTypes", NULL, NULL }, { "LocationAccessControl", NULL, NULL }, { "SupportedLocationFeatures", NULL, NULL }, { NULL } }; static TpDBusPropertiesMixinPropImpl decloak_props[] = { { "DecloakAutomatically", TWICE ("decloak-automatically") }, { NULL } }; static TpDBusPropertiesMixinPropImpl mail_notif_props[] = { { "MailNotificationFlags", NULL, NULL }, { "UnreadMailCount", NULL, NULL }, { "UnreadMails", NULL, NULL }, { "MailAddress", NULL, NULL }, { NULL } }; static TpDBusPropertiesMixinPropImpl power_saving_props[] = { { "PowerSavingActive", "power-saving", NULL }, { NULL } }; static TpDBusPropertiesMixinIfaceImpl prop_interfaces[] = { /* 0 */ { TP_IFACE_CONNECTION_INTERFACE_LOCATION, conn_location_properties_getter, conn_location_properties_setter, location_props, }, /* 1 */ { TP_IFACE_CONNECTION_INTERFACE_AVATARS, conn_avatars_properties_getter, NULL, NULL, }, /* 2 */ { TP_IFACE_CONNECTION_INTERFACE_CONTACT_INFO, conn_contact_info_properties_getter, NULL, NULL, }, /* 3 */ { GABBLE_IFACE_CONNECTION_INTERFACE_GABBLE_DECLOAK, tp_dbus_properties_mixin_getter_gobject_properties, tp_dbus_properties_mixin_setter_gobject_properties, decloak_props, }, { TP_IFACE_CONNECTION_INTERFACE_MAIL_NOTIFICATION, conn_mail_notif_properties_getter, NULL, mail_notif_props, }, { TP_IFACE_CONNECTION_INTERFACE_POWER_SAVING, tp_dbus_properties_mixin_getter_gobject_properties, NULL, power_saving_props, }, { NULL } }; prop_interfaces[1].props = conn_avatars_properties; prop_interfaces[2].props = conn_contact_info_properties; DEBUG("Initializing (GabbleConnectionClass *)%p", gabble_connection_class); object_class->get_property = gabble_connection_get_property; object_class->set_property = gabble_connection_set_property; object_class->constructor = gabble_connection_constructor; object_class->constructed = gabble_connection_constructed; parent_class->create_handle_repos = _gabble_connection_create_handle_repos; parent_class->get_unique_connection_name = gabble_connection_get_unique_name; parent_class->create_channel_factories = NULL; parent_class->create_channel_managers = _gabble_connection_create_channel_managers; parent_class->shut_down = connection_shut_down; parent_class->start_connecting = _gabble_connection_connect; parent_class->get_interfaces_always_present = _gabble_connection_get_interfaces_always_present; g_type_class_add_private (gabble_connection_class, sizeof (GabbleConnectionPrivate)); object_class->dispose = gabble_connection_dispose; object_class->finalize = gabble_connection_finalize; g_object_class_install_property (object_class, PROP_CONNECT_SERVER, g_param_spec_string ( "connect-server", "Hostname or IP of Jabber server", "The server used when establishing a connection.", NULL, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); /* * The explicit-server property can be used for verification of a * server certificate. It's important that it comes from the user * and is not the result of any other outside lookup, unlike the * connect-server property. */ g_object_class_install_property (object_class, PROP_EXPLICIT_SERVER, g_param_spec_string ( "explicit-server", "Explicit Hostname or IP of Jabber server", "Server explicitly specified by the user to connect to.", NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (object_class, PROP_PORT, g_param_spec_uint ( "port", "Jabber server port", "The port used when establishing a connection.", 1, G_MAXUINT16, 5222, G_PARAM_CONSTRUCT | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (object_class, PROP_OLD_SSL, g_param_spec_boolean ( "old-ssl", "Old-style SSL tunneled connection", "Establish the entire connection to the server within an " "SSL-encrypted tunnel. Note that this is not the same as connecting " "with TLS, which is not yet supported.", FALSE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property ( object_class, PROP_REQUIRE_ENCRYPTION, g_param_spec_boolean ( "require-encryption", "Require encryption", "Require the connection to be encrypted, either via old-style SSL, " "or StartTLS mechanisms.", FALSE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (object_class, PROP_REGISTER, g_param_spec_boolean ( "register", "Register account on server", "Register a new account on server.", FALSE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (object_class, PROP_LOW_BANDWIDTH, g_param_spec_boolean ( "low-bandwidth", "Low bandwidth mode", "Determines whether we are in low bandwidth mode. This influences " "polling behaviour.", FALSE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (object_class, PROP_STREAM_SERVER, g_param_spec_string ( "stream-server", "The server name used to initialise the stream.", "The server name used when initialising the stream, which is " "usually the part after the @ in the user's JID.", NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (object_class, PROP_USERNAME, g_param_spec_string ( "username", "Jabber username", "The username used when authenticating.", NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (object_class, PROP_PASSWORD, g_param_spec_string ( "password", "Jabber password", "The password used when authenticating.", NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (object_class, PROP_RESOURCE, g_param_spec_string ( "resource", "Jabber resource", "The Jabber resource used when authenticating.", "Telepathy", G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (object_class, PROP_PRIORITY, g_param_spec_char ( "priority", "Jabber presence priority", "The default priority used when reporting our presence.", G_MININT8, G_MAXINT8, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (object_class, PROP_HTTPS_PROXY_SERVER, g_param_spec_string ( "https-proxy-server", "The server name used as an HTTPS proxy server", "The server name used as an HTTPS proxy server.", NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (object_class, PROP_HTTPS_PROXY_PORT, g_param_spec_uint ( "https-proxy-port", "The HTTP proxy server port", "The HTTP proxy server port.", 0, G_MAXUINT16, GABBLE_PARAMS_DEFAULT_HTTPS_PROXY_PORT, G_PARAM_CONSTRUCT | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (object_class, PROP_FALLBACK_CONFERENCE_SERVER, g_param_spec_string ( "fallback-conference-server", "The conference server used as fallback", "The conference server used as fallback when everything else fails.", NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (object_class, PROP_STUN_SERVER, g_param_spec_string ( "stun-server", "STUN server", "STUN server.", NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (object_class, PROP_STUN_PORT, g_param_spec_uint ( "stun-port", "STUN port", "STUN port.", 0, G_MAXUINT16, GABBLE_PARAMS_DEFAULT_STUN_PORT, G_PARAM_CONSTRUCT | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (object_class, PROP_FALLBACK_STUN_SERVER, g_param_spec_string ( "fallback-stun-server", "fallback STUN server", "Fallback STUN server.", GABBLE_PARAMS_DEFAULT_FALLBACK_STUN_SERVER, G_PARAM_CONSTRUCT | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (object_class, PROP_FALLBACK_STUN_PORT, g_param_spec_uint ( "fallback-stun-port", "fallback STUN port", "Fallback STUN port.", 0, G_MAXUINT16, GABBLE_PARAMS_DEFAULT_STUN_PORT, G_PARAM_CONSTRUCT | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (object_class, PROP_IGNORE_SSL_ERRORS, g_param_spec_boolean ( "ignore-ssl-errors", "Ignore SSL errors", "Continue connecting even if the server's " "SSL certificate is invalid or missing.", FALSE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (object_class, PROP_ALIAS, g_param_spec_string ( "alias", "Alias/nick for local user", "Alias/nick for local user", NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (object_class, PROP_FALLBACK_SOCKS5_PROXIES, g_param_spec_boxed ( "fallback-socks5-proxies", "fallback SOCKS5 proxies", "Fallback SOCKS5 proxies.", G_TYPE_STRV, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (object_class, PROP_KEEPALIVE_INTERVAL, g_param_spec_uint ( "keepalive-interval", "keepalive interval", "Seconds between keepalive packets, or 0 to disable", 0, G_MAXUINT, 30, G_PARAM_CONSTRUCT | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property ( object_class, PROP_DECLOAK_AUTOMATICALLY, g_param_spec_boolean ( "decloak-automatically", "Decloak automatically?", "Leak presence and capabilities when requested", FALSE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property ( object_class, PROP_DOWNLOAD_AT_CONNECTION, g_param_spec_boolean ( "download-roster-at-connection", "Download the contact list at connection?", "Download the contact list at connection?", TRUE, G_PARAM_CONSTRUCT | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (object_class, PROP_FALLBACK_SERVERS, g_param_spec_boxed ( "fallback-servers", "Fallback servers", "List of servers to fallback to (syntax server[:port][,oldssl]", G_TYPE_STRV, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (object_class, PROP_EXTRA_CERTIFICATE_IDENTITIES, g_param_spec_boxed ( "extra-certificate-identities", "Extra Certificate Reference Identities", "Extra identities to check certificate against. These are present as a " "result of a user choice or configuration.", G_TYPE_STRV, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property ( object_class, PROP_POWER_SAVING, g_param_spec_boolean ( "power-saving", "Power saving active?", "Queue remote presence updates server-side for less network chatter", FALSE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); gabble_connection_class->properties_class.interfaces = prop_interfaces; tp_dbus_properties_mixin_class_init (object_class, G_STRUCT_OFFSET (GabbleConnectionClass, properties_class)); tp_contacts_mixin_class_init (object_class, G_STRUCT_OFFSET (GabbleConnectionClass, contacts_class)); conn_presence_class_init (gabble_connection_class); conn_contact_info_class_init (gabble_connection_class); tp_base_contact_list_mixin_class_init (parent_class); } static void gabble_connection_dispose (GObject *object) { GabbleConnection *self = GABBLE_CONNECTION (object); TpBaseConnection *base = (TpBaseConnection *) self; GabbleConnectionPrivate *priv = self->priv; if (priv->dispose_has_run) return; priv->dispose_has_run = TRUE; DEBUG ("called"); g_assert ((tp_base_connection_get_status (base) == TP_CONNECTION_STATUS_DISCONNECTED) || (tp_base_connection_get_status (base) == TP_INTERNAL_CONNECTION_STATUS_NEW)); tp_clear_object (&self->bytestream_factory); tp_clear_object (&self->disco); tp_clear_object (&self->req_pipeline); tp_clear_object (&self->vcard_manager); #ifdef ENABLE_VOIP tp_clear_object (&self->jingle_mint); #endif /* remove borrowed references before TpBaseConnection unrefs the channel * factories */ self->roster = NULL; self->muc_factory = NULL; self->private_tubes_factory = NULL; priv->auth_manager = NULL; tp_clear_object (&self->self_presence); tp_clear_object (&self->presence_cache); conn_olpc_activity_properties_dispose (self); g_hash_table_unref (self->avatar_requests); g_hash_table_unref (self->vcard_requests); conn_presence_dispose (self); conn_mail_notif_dispose (self); tp_clear_object (&priv->connector); tp_clear_object (&self->session); /* The porter was borrowed from the session. */ priv->porter = NULL; /* Cancel any outstanding _gabble_connection_send_with_reply() requests. */ g_cancellable_cancel (priv->iq_reply_cancellable); tp_clear_object (&priv->iq_reply_cancellable); g_hash_table_unref (priv->client_caps); gabble_capability_set_free (priv->all_caps); gabble_capability_set_free (priv->notify_caps); gabble_capability_set_free (priv->legacy_caps); gabble_capability_set_free (priv->sidecar_caps); gabble_capability_set_free (priv->bonus_caps); g_hash_table_unref (priv->client_data_forms); if (priv->disconnect_timer != 0) { g_source_remove (priv->disconnect_timer); priv->disconnect_timer = 0; } tp_clear_object (&self->pep_location); tp_clear_object (&self->pep_nick); tp_clear_object (&self->pep_olpc_buddy_props); tp_clear_object (&self->pep_olpc_activities); tp_clear_object (&self->pep_olpc_current_act); tp_clear_object (&self->pep_olpc_act_props); conn_sidecars_dispose (self); tp_clear_object (&self->daemon); if (G_OBJECT_CLASS (gabble_connection_parent_class)->dispose) G_OBJECT_CLASS (gabble_connection_parent_class)->dispose (object); } static void gabble_connection_finalize (GObject *object) { GabbleConnection *self = GABBLE_CONNECTION (object); GabbleConnectionPrivate *priv = self->priv; DEBUG ("called with %p", object); g_free (priv->connect_server); g_free (priv->explicit_server); g_free (priv->stream_server); g_free (priv->username); g_free (priv->password); g_free (priv->resource); g_free (priv->https_proxy_server); g_free (priv->stun_server); g_free (priv->fallback_stun_server); g_free (priv->fallback_conference_server); g_strfreev (priv->fallback_socks5_proxies); g_strfreev (priv->fallback_servers); g_strfreev (priv->extra_certificate_identities); g_free (priv->alias); g_free (priv->stream_id); tp_contacts_mixin_finalize (G_OBJECT(self)); conn_presence_finalize (self); conn_contact_info_finalize (self); gabble_capabilities_finalize (self); G_OBJECT_CLASS (gabble_connection_parent_class)->finalize (object); } /** * _gabble_connection_set_properties_from_account * * Parses an account string which may be one of the following forms: * username@server * username@server/resource * and sets the properties for username, stream server and resource * appropriately. Also sets the connect server to the stream server if one has * not yet been specified. */ gboolean _gabble_connection_set_properties_from_account (GabbleConnection *conn, const gchar *account, GError **error) { char *username, *server, *resource; gboolean result; g_assert (GABBLE_IS_CONNECTION (conn)); g_assert (account != NULL); username = server = resource = NULL; result = TRUE; if (!wocky_decode_jid (account, &username, &server, &resource)) { g_set_error (error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT, "unable to extract JID from account name"); result = FALSE; goto OUT; } g_object_set (G_OBJECT (conn), "username", username, "stream-server", server, NULL); /* only override the default resource if we actually got one */ if (resource) g_object_set (G_OBJECT (conn), "resource", resource, NULL); OUT: g_free (username); g_free (server); g_free (resource); return result; } /** * gabble_connection_get_full_jid: * * Returns: the full jid (including resource) of this connection, which must be * freed by the caller. */ gchar * gabble_connection_get_full_jid (GabbleConnection *conn) { const gchar *bare_jid = conn_util_get_bare_self_jid (conn); return g_strconcat (bare_jid, "/", conn->priv->resource, NULL); } static gchar * _gabble_plugin_connection_get_full_jid (GabblePluginConnection *plugin_conn) { GabbleConnection *conn = GABBLE_CONNECTION (plugin_conn); return gabble_connection_get_full_jid (conn); } /** * gabble_connection_dup_porter: * * Returns: (transfer full): the #WockyPorter instance driving this connection. */ WockyPorter *gabble_connection_dup_porter (GabbleConnection *conn) { GabbleConnectionPrivate *priv; g_assert (GABBLE_IS_CONNECTION (conn)); priv = conn->priv; if (priv->porter != NULL) return g_object_ref (priv->porter); return NULL; } WockySession * gabble_connection_get_session (GabblePluginConnection *plugin_connection) { GabbleConnection *connection = GABBLE_CONNECTION (plugin_connection); g_return_val_if_fail (GABBLE_IS_CONNECTION (connection), NULL); return connection->session; } /** * _gabble_connection_send * * Send an WockyStanza and trap network errors appropriately. */ gboolean _gabble_connection_send (GabbleConnection *conn, WockyStanza *msg, GError **error) { g_assert (GABBLE_IS_CONNECTION (conn)); if (conn->priv->porter == NULL) { g_set_error_literal (error, TP_ERROR, TP_ERROR_NETWORK_ERROR, "connection is disconnected"); return FALSE; } wocky_porter_send (conn->priv->porter, msg); return TRUE; } void gabble_connection_update_last_use (GabbleConnection *conn) { conn->priv->last_activity_time = time (NULL); } static gdouble gabble_connection_get_last_use (GabbleConnection *conn) { return difftime (time (NULL), conn->priv->last_activity_time); } typedef struct { GabbleConnectionMsgReplyFunc reply_func; GabbleConnection *conn; WockyStanza *sent_msg; gpointer user_data; GObject *object; gboolean object_alive; } GabbleMsgHandlerData; static void message_send_object_destroy_notify_cb (gpointer data, GObject *where_the_object_was) { GabbleMsgHandlerData *handler_data = data; handler_data->object = NULL; handler_data->object_alive = FALSE; } static void msg_handler_data_free (GabbleMsgHandlerData *handler_data) { g_object_unref (handler_data->sent_msg); if (handler_data->object != NULL) { g_object_weak_unref (handler_data->object, message_send_object_destroy_notify_cb, handler_data); } g_slice_free (GabbleMsgHandlerData, handler_data); } static void message_send_reply_cb ( GObject *source, GAsyncResult *result, gpointer user_data) { GabbleMsgHandlerData *handler_data = user_data; if (handler_data->object_alive && handler_data->reply_func != NULL) { WockyPorter *porter = WOCKY_PORTER (source); GError *error = NULL; WockyStanza *stanza = wocky_porter_send_iq_finish (porter, result, &error); if (stanza != NULL) { handler_data->reply_func (handler_data->conn, handler_data->sent_msg, stanza, handler_data->object, handler_data->user_data); g_object_unref (stanza); } else { DEBUG ("send_iq_async failed: %s", error->message); g_error_free (error); } } msg_handler_data_free (handler_data); } /** * _gabble_connection_send_with_reply * * Send a tracked WockyStanza and trap network errors appropriately. * * If object is non-NULL the handler will follow the lifetime of that object, * which means that if the object is destroyed the callback will not be invoked. * * if reply_func is NULL the reply will be ignored. */ gboolean _gabble_connection_send_with_reply (GabbleConnection *conn, WockyStanza *msg, GabbleConnectionMsgReplyFunc reply_func, GObject *object, gpointer user_data, GError **error) { GabbleConnectionPrivate *priv; GabbleMsgHandlerData *handler_data; g_assert (GABBLE_IS_CONNECTION (conn)); priv = conn->priv; if (priv->porter == NULL) { g_set_error_literal (error, TP_ERROR, TP_ERROR_NETWORK_ERROR, "connection is disconnected"); return FALSE; } g_object_ref (msg); handler_data = g_slice_new (GabbleMsgHandlerData); handler_data->reply_func = reply_func; handler_data->conn = conn; handler_data->sent_msg = msg; handler_data->user_data = user_data; handler_data->object = object; handler_data->object_alive = TRUE; if (object != NULL) { g_object_weak_ref (object, message_send_object_destroy_notify_cb, handler_data); } wocky_porter_send_iq_async (priv->porter, msg, priv->iq_reply_cancellable, message_send_reply_cb, handler_data); return TRUE; } static void connect_iq_callbacks (GabbleConnection *conn); static gboolean iq_disco_cb (WockyPorter *, WockyStanza *, gpointer); static gboolean iq_version_cb (WockyPorter *, WockyStanza *, gpointer); static void connection_disco_cb (GabbleDisco *, GabbleDiscoRequest *, const gchar *, const gchar *, WockyNode *, GError *, gpointer); static void decrement_waiting_connected (GabbleConnection *connection); static void connection_initial_presence_cb (GObject *, GAsyncResult *, gpointer); static void gabble_connection_disconnect_with_tp_error (GabbleConnection *self, const GError *tp_error, TpConnectionStatusReason reason) { TpBaseConnection *base = (TpBaseConnection *) self; GHashTable *details = tp_asv_new ( "debug-message", G_TYPE_STRING, tp_error->message, NULL); g_assert (tp_error->domain == TP_ERROR); tp_base_connection_disconnect_with_dbus_error (base, tp_error_get_dbus_name (tp_error->code), details, reason); g_hash_table_unref (details); } static void remote_closed_cb (WockyPorter *porter, GabbleConnection *self) { TpBaseConnection *base = TP_BASE_CONNECTION (self); GError e = { TP_ERROR, TP_ERROR_CONNECTION_LOST, "server closed its XMPP stream" }; if (tp_base_connection_get_status (base) == TP_CONNECTION_STATUS_DISCONNECTED) /* Ignore if we are already disconnecting/disconnected */ return; DEBUG ("server closed its XMPP stream; close ours"); /* Changing the state to Disconnect will call connection_shut_down which * will properly close the porter. */ gabble_connection_disconnect_with_tp_error (self, &e, TP_CONNECTION_STATUS_REASON_NETWORK_ERROR); } static void force_close_cb (GObject *source, GAsyncResult *res, gpointer user_data) { GabbleConnection *self = GABBLE_CONNECTION (user_data); TpBaseConnection *base = TP_BASE_CONNECTION (self); GError *error = NULL; if (!wocky_porter_force_close_finish (WOCKY_PORTER (source), res, &error)) { DEBUG ("force close failed: %s", error->message); g_error_free (error); } else { DEBUG ("connection properly closed (forced)"); } tp_base_connection_finish_shutdown (base); } static void remote_error_cb (WockyPorter *porter, GQuark domain, gint code, gchar *msg, GabbleConnection *self) { TpConnectionStatusReason reason = TP_CONNECTION_STATUS_REASON_NONE_SPECIFIED; TpBaseConnection *base = (TpBaseConnection *) self; GabbleConnectionPrivate *priv = self->priv; GError e = { domain, code, msg }; GError *error = NULL; if (tp_base_connection_get_status (base) == TP_CONNECTION_STATUS_DISCONNECTED) /* Ignore if we are already disconnecting/disconnected */ return; gabble_set_tp_conn_error_from_wocky (&e, tp_base_connection_get_status (base), &reason, &error); g_assert (error->domain == TP_ERROR); DEBUG ("Force closing of the connection %p", self); priv->closing = TRUE; wocky_porter_force_close_async (priv->porter, NULL, force_close_cb, self); gabble_connection_disconnect_with_tp_error (self, error, reason); g_error_free (error); } static void connector_error_disconnect (GabbleConnection *self, GError *error) { GError *tp_error = NULL; TpBaseConnection *base = (TpBaseConnection *) self; TpConnectionStatusReason reason = TP_CONNECTION_STATUS_REASON_NETWORK_ERROR; gchar *dbus_error = NULL; GHashTable *details = NULL; if (error->domain == GABBLE_SERVER_TLS_ERROR) { gabble_server_tls_manager_get_rejection_details ( self->priv->server_tls_manager, &dbus_error, &details, &reason); /* new-style interactive TLS verification error */ DEBUG ("New-style TLS verification error, reason %u, dbus error %s", reason, dbus_error); tp_base_connection_disconnect_with_dbus_error (base, dbus_error, details, reason); tp_clear_pointer (&details, g_hash_table_unref); g_free (dbus_error); return; } if (error->domain == WOCKY_AUTH_ERROR && gabble_auth_manager_get_failure_details (self->priv->auth_manager, &dbus_error, &details, &reason)) { DEBUG ("Interactive authentication error, reason %u, dbus error %s", reason, dbus_error); tp_base_connection_disconnect_with_dbus_error (base, dbus_error, details, reason); tp_clear_pointer (&details, g_hash_table_unref); g_free (dbus_error); return; } gabble_set_tp_conn_error_from_wocky (error, tp_base_connection_get_status (base), &reason, &tp_error); DEBUG ("connection failed: %s", tp_error->message); g_assert (tp_error->domain == TP_ERROR); gabble_connection_disconnect_with_tp_error (self, tp_error, reason); g_error_free (tp_error); } static void bare_jid_disco_cb (GabbleDisco *disco, GabbleDiscoRequest *request, const gchar *jid, const gchar *node, WockyNode *result, GError *disco_error, gpointer user_data) { GabbleConnection *conn = user_data; if (disco_error != NULL) { DEBUG ("Got disco error on bare jid: %s", disco_error->message); } else { WockyNodeIter i; WockyNode *child; wocky_node_iter_init (&i, result, "identity", NULL); while (wocky_node_iter_next (&i, &child)) { const gchar *category = wocky_node_get_attribute (child, "category"); const gchar *type = wocky_node_get_attribute (child, "type"); if (!tp_strdiff (category, "pubsub") && !tp_strdiff (type, "pep")) { DEBUG ("Server advertises PEP support in our jid features"); conn->features |= GABBLE_CONNECTION_FEATURES_PEP; } } } decrement_waiting_connected (conn); } /** * next_fallback_server * * Launch connection using next fallback server if any, return * FALSE if they all have been tried or the error is not * network related (e.g. login failed). */ static gboolean next_fallback_server (GabbleConnection *self, const GError *error) { GabbleConnectionPrivate *priv = self->priv; const gchar *server; gchar **split; guint port = 5222; gboolean old_ssl = FALSE; GNetworkAddress *addr; GError *tmp_error = NULL; /* Only retry on connection failures */ if (error->domain != G_IO_ERROR) return FALSE; if (priv->fallback_servers == NULL || priv->fallback_servers[priv->fallback_server_index] == NULL) return FALSE; server = priv->fallback_servers[priv->fallback_server_index++]; split = g_strsplit (server, ",", 2); if (split[0] == NULL) { g_strfreev (split); return FALSE; } else if (split[1] != NULL && !tp_strdiff (split[1], "oldssl")) { old_ssl = TRUE; port = 5223; } addr = G_NETWORK_ADDRESS ( g_network_address_parse (split[0], port, &tmp_error)); if (!addr) { g_warning ("%s", tmp_error->message); g_error_free (tmp_error); return FALSE; } g_free (priv->connect_server); priv->connect_server = g_strdup (g_network_address_get_hostname (addr)); priv->port = g_network_address_get_port (addr); priv->old_ssl = old_ssl; g_object_notify (G_OBJECT (self), "connect-server"); g_object_notify (G_OBJECT (self), "port"); g_object_notify (G_OBJECT (self), "old-ssl"); g_object_unref (addr); _gabble_connection_connect (TP_BASE_CONNECTION (self), NULL); g_strfreev (split); return TRUE; } /** * connector_connected * * Stage 2 of connecting, this function is called once the connect operation * has finished. It checks if the connection succeeded, creates and starts * the WockyPorter, then sends two discovery requests to find the * server's features (one to the server and one to our bare jid). */ static void connector_connected (GabbleConnection *self, WockyXmppConnection *conn, const gchar *jid, GError *error) { GabbleConnectionPrivate *priv = self->priv; TpBaseConnection *base = (TpBaseConnection *) self; TpHandleRepoIface *contact_handles = tp_base_connection_get_handles (base, TP_HANDLE_TYPE_CONTACT); /* cleanup the cancellable */ tp_clear_object (&priv->cancellable); /* We went to closing while we were connecting... drop the connection and * finish the shutdown */ if (priv->closing) { if (conn != NULL) g_object_unref (conn); else g_error_free (error); tp_base_connection_finish_shutdown (base); return; } /* We don't need the connector any more */ tp_clear_object (&priv->connector); if (conn == NULL) { if (!next_fallback_server (self, error)) connector_error_disconnect (self, error); g_error_free (error); return; } DEBUG ("connected (jid: %s)", jid); self->session = wocky_session_new_with_connection (conn, jid); priv->porter = wocky_session_get_porter (self->session); g_assert (WOCKY_IS_C2S_PORTER (priv->porter)); priv->pinger = wocky_ping_new (WOCKY_C2S_PORTER (priv->porter), priv->keepalive_interval); g_signal_connect (priv->porter, "remote-closed", G_CALLBACK (remote_closed_cb), self); g_signal_connect (priv->porter, "remote-error", G_CALLBACK (remote_error_cb), self); g_signal_emit_by_name (self, "porter-available", priv->porter); connect_iq_callbacks (self); wocky_pep_service_start (self->pep_location, self->session); wocky_pep_service_start (self->pep_nick, self->session); wocky_pep_service_start (self->pep_olpc_buddy_props, self->session); wocky_pep_service_start (self->pep_olpc_activities, self->session); wocky_pep_service_start (self->pep_olpc_current_act, self->session); wocky_pep_service_start (self->pep_olpc_act_props, self->session); /* Don't use wocky_session_start as we don't want to start all the * components (Roster, presence-manager, etc) for now */ wocky_porter_start (priv->porter); tp_base_connection_set_self_handle (base, tp_handle_ensure (contact_handles, jid, NULL, &error)); if (tp_base_connection_get_self_handle (base) == 0) { DEBUG ("couldn't get our self handle: %s", error->message); if (error->domain != TP_ERROR) { error->domain = TP_ERROR; error->code = TP_ERROR_INVALID_HANDLE; } gabble_connection_disconnect_with_tp_error (self, error, TP_CONNECTION_STATUS_REASON_NETWORK_ERROR); g_error_free (error); return; } /* update priv->resource and priv->stream_server from the server's JID */ if (!_gabble_connection_set_properties_from_account (self, jid, &error)) { DEBUG ("couldn't parse our own JID: %s", error->message); if (error->domain != TP_ERROR) { error->domain = TP_ERROR; error->code = TP_ERROR_INVALID_ARGUMENT; } gabble_connection_disconnect_with_tp_error (self, error, TP_CONNECTION_STATUS_REASON_NETWORK_ERROR); g_error_free (error); return; } DEBUG ("Created self handle %d, our JID is %s", tp_base_connection_get_self_handle (base), jid); /* set initial capabilities */ gabble_connection_refresh_capabilities (self, NULL); /* Disco server features */ if (!gabble_disco_request_with_timeout (self->disco, GABBLE_DISCO_TYPE_INFO, priv->stream_server, NULL, disco_reply_timeout, connection_disco_cb, self, G_OBJECT (self), &error)) { DEBUG ("sending disco request failed: %s", error->message); if (error->domain != TP_ERROR) { error->domain = TP_ERROR; error->code = TP_ERROR_NETWORK_ERROR; } gabble_connection_disconnect_with_tp_error (self, error, TP_CONNECTION_STATUS_REASON_NETWORK_ERROR); g_error_free (error); } /* Disco our own bare jid to check if PEP is supported */ if (!gabble_disco_request_with_timeout (self->disco, GABBLE_DISCO_TYPE_INFO, conn_util_get_bare_self_jid (self), NULL, disco_reply_timeout, bare_jid_disco_cb, self, G_OBJECT (self), &error)) { DEBUG ("Sending disco request to our own bare jid failed: %s", error->message); if (error->domain != TP_ERROR) { error->domain = TP_ERROR; error->code = TP_ERROR_NETWORK_ERROR; } gabble_connection_disconnect_with_tp_error (self, error, TP_CONNECTION_STATUS_REASON_NETWORK_ERROR); g_error_free (error); } self->priv->waiting_connected = 2; } static void connector_connect_cb (GObject *source, GAsyncResult *res, gpointer user_data) { GabbleConnection *self = GABBLE_CONNECTION (user_data); GabbleConnectionPrivate *priv = self->priv; WockyXmppConnection *conn; GError *error = NULL; gchar *jid = NULL; conn = wocky_connector_connect_finish (WOCKY_CONNECTOR (source), res, &jid, &(priv->stream_id), &error); connector_connected (self, conn, jid, error); g_free (jid); } static void connector_register_cb (GObject *source, GAsyncResult *res, gpointer user_data) { GabbleConnection *self = GABBLE_CONNECTION (user_data); GabbleConnectionPrivate *priv = self->priv; WockyXmppConnection *conn; GError *error = NULL; gchar *jid = NULL; conn = wocky_connector_register_finish (WOCKY_CONNECTOR (source), res, &jid, &(priv->stream_id), &error); connector_connected (self, conn, jid, error); g_free (jid); } static gboolean connection_iq_last_cb ( WockyPorter *porter, WockyStanza *stanza, gpointer user_data) { GabbleConnection *self = GABBLE_CONNECTION (user_data); const gchar *from = wocky_stanza_get_from (stanza); /* Aside from 21 being an appropriate number, 2 ^ 64 is 20 digits long. */ char seconds[21]; /* Check if the peer, if any, is authorized to receive our presence. */ if (from != NULL) { TpHandleRepoIface *contact_repo = tp_base_connection_get_handles ( (TpBaseConnection *) self, TP_HANDLE_TYPE_CONTACT); TpHandle handle = tp_handle_lookup (contact_repo, from, NULL, NULL); /* If there's no handle for them, they're certainly not on the roster. */ if (handle == 0 || !gabble_roster_handle_gets_presence_from_us (self->roster, handle)) { wocky_porter_send_iq_error (porter, stanza, WOCKY_XMPP_ERROR_FORBIDDEN, NULL); return TRUE; } } sprintf (seconds, "%.0f", gabble_connection_get_last_use (self)); wocky_porter_acknowledge_iq (porter, stanza, '(', "query", ':', NS_LAST, '@', "seconds", seconds, ')', NULL); return TRUE; } static void connect_iq_callbacks (GabbleConnection *conn) { GabbleConnectionPrivate *priv = conn->priv; wocky_porter_register_handler_from_anyone (priv->porter, WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_GET, WOCKY_PORTER_HANDLER_PRIORITY_NORMAL, iq_disco_cb, conn, '(', "query", ':', NS_DISCO_INFO, ')', NULL); wocky_porter_register_handler_from_anyone (priv->porter, WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_GET, WOCKY_PORTER_HANDLER_PRIORITY_NORMAL, iq_version_cb, conn, '(', "query", ':', NS_VERSION, ')', NULL); wocky_porter_register_handler_from_anyone (priv->porter, WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_GET, WOCKY_PORTER_HANDLER_PRIORITY_NORMAL, connection_iq_last_cb, conn, '(', "query", ':', NS_LAST, ')', NULL); } /** * _gabble_connection_connect * * Use the stored server & authentication details to commence * the stages for connecting to the server and authenticating. * Will create a WockyConnector. * * Stage 1 is _gabble_connection_connect calling wocky_connector_connect_async * Stage 2 is connector_connected initiating service discovery * Stage 3 is connection_disco_cb processing the server's features, and setting * initial presence * Stage 4 is set_status_to_connected setting the CONNECTED state. */ static gboolean _gabble_connection_connect (TpBaseConnection *base, GError **error) { GabbleConnection *conn = GABBLE_CONNECTION (base); GabbleConnectionPrivate *priv = conn->priv; WockyTLSHandler *tls_handler; char *jid; gboolean interactive_tls; gchar *user_certs_dir; g_assert (priv->connector == NULL); g_assert (priv->port <= G_MAXUINT16); g_assert (priv->stream_server != NULL); g_assert (priv->resource != NULL); jid = gabble_encode_jid (priv->username, priv->stream_server, NULL); tls_handler = WOCKY_TLS_HANDLER (priv->server_tls_manager); priv->connector = wocky_connector_new (jid, priv->password, priv->resource, WOCKY_AUTH_REGISTRY (priv->auth_manager), tls_handler); g_free (jid); #ifdef GTLS_SYSTEM_CA_CERTIFICATES /* system certs */ wocky_tls_handler_add_ca (tls_handler, GTLS_SYSTEM_CA_CERTIFICATES); #endif /* user certs */ user_certs_dir = g_build_filename (g_get_user_config_dir (), "telepathy", "certs", NULL); wocky_tls_handler_add_ca (tls_handler, user_certs_dir); g_free (user_certs_dir); /* If the UI explicitly specified a port or a server, pass them to Loudmouth * rather than letting it do an SRV lookup. * * If the port is 5222 (the default) then unless the UI also specified a * server or old-style SSL, we ignore it and do an SRV lookup anyway. This * means that UIs that blindly pass the default back to Gabble work * correctly. If the user really did mean 5222, then when the SRV lookup * fails we fall back to that anyway. */ if (priv->port != 5222 || priv->connect_server != NULL || priv->old_ssl) { gchar *server; if (priv->connect_server != NULL) server = priv->connect_server; else server = priv->stream_server; DEBUG ("disabling SRV because \"server\" or \"old-ssl\" was specified " "or port was not 5222, will connect to %s", server); g_object_set (priv->connector, "xmpp-server", server, "xmpp-port", priv->port, NULL); } else { DEBUG ("letting SRV lookup decide server and port"); } /* We want to enable interactive TLS verification also in * case encryption is not required, and we don't ignore SSL errors. */ interactive_tls = !conn->priv->ignore_ssl_errors; if (!conn->priv->require_encryption && !conn->priv->ignore_ssl_errors) { DEBUG ("require-encryption is False; flipping ignore_ssl_errors to True"); conn->priv->ignore_ssl_errors = TRUE; } g_object_set (priv->connector, "old-ssl", priv->old_ssl, /* We always wants to support old servers */ "legacy", TRUE, NULL); g_object_set (tls_handler, "interactive-tls", interactive_tls, "ignore-ssl-errors", priv->ignore_ssl_errors, NULL); if (priv->old_ssl) { g_object_set (priv->connector, "tls-required", FALSE, NULL); } else { g_object_set (priv->connector, "tls-required", priv->require_encryption, "plaintext-auth-allowed", !priv->require_encryption, NULL); } priv->cancellable = g_cancellable_new (); if (priv->do_register) { DEBUG ("Start registering"); wocky_connector_register_async (priv->connector, priv->cancellable, connector_register_cb, conn); } else { DEBUG ("Start connecting"); wocky_connector_connect_async (priv->connector, priv->cancellable, connector_connect_cb, conn); } return TRUE; } static void closed_cb (GObject *source, GAsyncResult *res, gpointer user_data) { GabbleConnection *self = GABBLE_CONNECTION (user_data); GabbleConnectionPrivate *priv = self->priv; TpBaseConnection *base = TP_BASE_CONNECTION (self); GError *error = NULL; if (priv->disconnect_timer != 0) { /* stop the timer */ g_source_remove (priv->disconnect_timer); priv->disconnect_timer = 0; } if (!wocky_porter_close_finish (WOCKY_PORTER (source), res, &error)) { DEBUG ("close failed: %s", error->message); if (g_error_matches (error, WOCKY_PORTER_ERROR, WOCKY_PORTER_ERROR_FORCIBLY_CLOSED)) { /* Close operation has been aborted because a force_close operation * has been started. tp_base_connection_finish_shutdown will be * called once this force_close operation is completed so we don't * do it here. */ g_error_free (error); return; } g_error_free (error); } else { DEBUG ("connection properly closed"); } tp_base_connection_finish_shutdown (base); } static gboolean disconnect_timeout_cb (gpointer data) { GabbleConnection *self = GABBLE_CONNECTION (data); GabbleConnectionPrivate *priv = self->priv; DEBUG ("Close operation timed out. Force closing"); priv->disconnect_timer = 0; wocky_porter_force_close_async (priv->porter, NULL, force_close_cb, self); return FALSE; } static void connection_shut_down (TpBaseConnection *base) { GabbleConnection *self = GABBLE_CONNECTION (base); GabbleConnectionPrivate *priv = self->priv; tp_clear_object (&priv->pinger); if (priv->closing) return; priv->closing = TRUE; if (priv->porter != NULL) { DEBUG ("connection may still be open; closing it: %p", base); g_assert (priv->disconnect_timer == 0); priv->disconnect_timer = g_timeout_add_seconds (DISCONNECT_TIMEOUT, disconnect_timeout_cb, self); wocky_porter_close_async (priv->porter, NULL, closed_cb, self); return; } else if (priv->connector != NULL) { DEBUG ("Cancelling the porter connection"); g_cancellable_cancel (priv->cancellable); return; } DEBUG ("neither porter nor connector is alive: clean up the base connection"); tp_base_connection_finish_shutdown (base); } void gabble_connection_fill_in_caps (GabbleConnection *self, WockyStanza *presence_message) { GabblePresence *presence = self->self_presence; WockyNode *node = wocky_stanza_get_top_node (presence_message); gchar *caps_hash; gboolean share_v1, voice_v1, video_v1; GString *ext = g_string_new (""); /* XEP-0115 version 1.5 uses a verification string in the 'ver' attribute */ caps_hash = caps_hash_compute_from_self_presence (self); node = wocky_node_add_child_ns (node, "c", NS_CAPS); wocky_node_set_attributes ( node, "hash", "sha-1", "node", NS_GABBLE_CAPS, "ver", caps_hash, NULL); /* Ensure this set of capabilities is in the cache. */ gabble_presence_cache_add_own_caps (self->presence_cache, caps_hash, gabble_presence_peek_caps (presence), NULL, gabble_presence_peek_data_forms (presence)); /* XEP-0115 deprecates 'ext' feature bundles. But we still need * BUNDLE_VOICE_V1 it for backward-compatibility with Gabble 0.2 */ g_string_append (ext, BUNDLE_PMUC_V1); share_v1 = gabble_presence_has_cap (presence, NS_GOOGLE_FEAT_SHARE); voice_v1 = gabble_presence_has_cap (presence, NS_GOOGLE_FEAT_VOICE); video_v1 = gabble_presence_has_cap (presence, NS_GOOGLE_FEAT_VIDEO); if (share_v1) g_string_append (ext, " " BUNDLE_SHARE_V1); if (voice_v1) g_string_append (ext, " " BUNDLE_VOICE_V1); if (video_v1) g_string_append (ext, " " BUNDLE_VIDEO_V1); if (gabble_presence_has_cap (presence, NS_GOOGLE_FEAT_CAMERA)) g_string_append (ext, " " BUNDLE_CAMERA_V1); wocky_node_set_attribute (node, "ext", ext->str); g_string_free (ext, TRUE); g_free (caps_hash); } gboolean gabble_connection_send_capabilities (GabbleConnection *self, const gchar *recipient, GError **error) { TpHandleRepoIface *contact_repo = tp_base_connection_get_handles ( (TpBaseConnection *) self, TP_HANDLE_TYPE_CONTACT); WockyStanza *message; gboolean ret; TpHandle handle; /* if we don't have a handle allocated for the recipient, they clearly aren't * getting our presence... */ handle = tp_handle_lookup (contact_repo, recipient, NULL, NULL); if (handle != 0 && conn_presence_visible_to (self, handle)) { /* nothing to do, they should already have had our presence */ return TRUE; } /* We deliberately don't include anything except the caps here */ message = wocky_stanza_build ( WOCKY_STANZA_TYPE_PRESENCE, WOCKY_STANZA_SUB_TYPE_AVAILABLE, NULL, recipient, NULL); gabble_connection_fill_in_caps (self, message); ret = _gabble_connection_send (self, message, error); g_object_unref (message); return ret; } gboolean gabble_connection_request_decloak (GabbleConnection *self, const gchar *to, const gchar *reason, GError **error) { GabblePresence *presence = self->self_presence; WockyStanza *message = gabble_presence_as_message (presence, to); WockyNode *decloak; gboolean ret; gabble_connection_fill_in_caps (self, message); decloak = wocky_node_add_child_ns (wocky_stanza_get_top_node (message), "temppres", NS_TEMPPRES); if (reason != NULL && *reason != '\0') { wocky_node_set_attribute (decloak, "reason", reason); } ret = _gabble_connection_send (self, message, error); g_object_unref (message); return ret; } static gboolean gabble_connection_refresh_capabilities (GabbleConnection *self, GabbleCapabilitySet **old_out) { TpBaseConnection *base = (TpBaseConnection *) self; GError *error = NULL; GHashTableIter iter; gpointer k, v; GabbleCapabilitySet *save_set; GPtrArray *data_forms; save_set = self->priv->all_caps; self->priv->all_caps = gabble_capability_set_new (); data_forms = g_ptr_array_new (); gabble_capability_set_update (self->priv->all_caps, gabble_capabilities_get_fixed_caps ()); gabble_capability_set_update (self->priv->all_caps, self->priv->notify_caps); gabble_capability_set_update (self->priv->all_caps, self->priv->legacy_caps); gabble_capability_set_update (self->priv->all_caps, self->priv->sidecar_caps); gabble_capability_set_update (self->priv->all_caps, self->priv->bonus_caps); /* first, normal caps */ g_hash_table_iter_init (&iter, self->priv->client_caps); while (g_hash_table_iter_next (&iter, &k, &v)) { if (DEBUGGING) { gchar *s = gabble_capability_set_dump (v, " "); DEBUG ("incorporating caps for %s:\n%s", (const gchar *) k, s); g_free (s); } gabble_capability_set_update (self->priv->all_caps, v); } /* now data forms */ g_hash_table_iter_init (&iter, self->priv->client_data_forms); /* just borrow the ref, data_forms doesn't have a free func */ while (g_hash_table_iter_next (&iter, &k, &v)) tp_g_ptr_array_extend (data_forms, v); if (self->self_presence != NULL) gabble_presence_set_capabilities (self->self_presence, self->priv->resource, self->priv->all_caps, data_forms, self->priv->caps_serial++); if (gabble_capability_set_equals (self->priv->all_caps, save_set)) { gabble_capability_set_free (save_set); g_ptr_array_unref (data_forms); DEBUG ("nothing to do"); return FALSE; } /* don't signal presence unless we're already CONNECTED */ if (tp_base_connection_get_status (base) != TP_CONNECTION_STATUS_CONNECTED) { gabble_capability_set_free (save_set); g_ptr_array_unref (data_forms); DEBUG ("not emitting self-presence stanza: not connected yet"); return FALSE; } if (!conn_presence_signal_own_presence (self, NULL, &error)) { gabble_capability_set_free (save_set); g_ptr_array_unref (data_forms); DEBUG ("error sending presence: %s", error->message); g_error_free (error); return FALSE; } if (old_out == NULL) gabble_capability_set_free (save_set); else *old_out = save_set; g_ptr_array_unref (data_forms); return TRUE; } /** * _gabble_connection_send_iq_result * * Function used to acknowledge an IQ stanza. */ void _gabble_connection_acknowledge_set_iq (GabbleConnection *conn, WockyStanza *iq) { wocky_porter_acknowledge_iq (wocky_session_get_porter (conn->session), iq, NULL); } static void add_feature_node (gpointer namespace, gpointer result_query) { WockyNode *feature_node; feature_node = wocky_node_add_child_with_content (result_query, "feature", NULL); wocky_node_set_attribute (feature_node, "var", namespace); } static void add_identity_node (const GabbleDiscoIdentity *identity, gpointer result_query) { WockyNode *identity_node; identity_node = wocky_node_add_child_with_content (result_query, "identity", NULL); wocky_node_set_attribute (identity_node, "category", identity->category); wocky_node_set_attribute (identity_node, "type", identity->type); if (identity->lang) wocky_node_set_attribute (identity_node, "lang", identity->lang); if (identity->name) wocky_node_set_attribute (identity_node, "name", identity->name); } /** * iq_disco_cb * * Called by Wocky when we get an incoming <iq> with a <query xmlns="disco#info"> * node. This handler handles disco-related IQs. */ static gboolean iq_disco_cb (WockyPorter *porter, WockyStanza *stanza, gpointer user_data) { GabbleConnection *self = GABBLE_CONNECTION (user_data); WockyStanza *result; WockyNode *query, *result_query; const gchar *node, *suffix; const GabbleCapabilityInfo *info = NULL; const GabbleCapabilitySet *features = NULL; const GPtrArray *identities = NULL; const GPtrArray *data_forms = NULL; /* query's existence is checked by WockyPorter before this function is called */ query = wocky_node_get_child (wocky_stanza_get_top_node (stanza), "query"); node = wocky_node_get_attribute (query, "node"); if (node && ( 0 != strncmp (node, NS_GABBLE_CAPS "#", strlen (NS_GABBLE_CAPS) + 1) || strlen (node) < strlen (NS_GABBLE_CAPS) + 2)) { STANZA_DEBUG (stanza, "got iq disco query with unexpected node attribute"); return FALSE; } if (node == NULL) suffix = NULL; else suffix = node + strlen (NS_GABBLE_CAPS) + 1; result = wocky_stanza_build_iq_result (stanza, '(', "query", ':', NS_DISCO_INFO, '*', &result_query, ')', NULL); /* If we get an IQ without an id='', there's not much we can do. */ if (result == NULL) return FALSE; if (node) wocky_node_set_attribute (result_query, "node", node); if (node == NULL) { features = gabble_presence_peek_caps (self->self_presence); data_forms = gabble_presence_peek_data_forms (self->self_presence); } else { /* If node is not NULL, it can be either a caps bundle as defined in the * legacy XEP-0115 version 1.3 or an hash as defined in XEP-0115 version * 1.5. Let's see if it's a verification string we've told the cache about. */ info = gabble_presence_cache_peek_own_caps (self->presence_cache, suffix); } if (info) { features = info->cap_set; identities = info->identities; data_forms = info->data_forms; } if (identities && identities->len != 0) { g_ptr_array_foreach ((GPtrArray *) identities, (GFunc) add_identity_node, result_query); } else { /* Every entity MUST have at least one identity (XEP-0030). Gabble publishes * one identity. If you change the identity here, you also need to change * caps_hash_compute_from_self_presence(). */ wocky_node_add_build (result_query, '(', "identity", '@', "category", "client", '@', "name", PACKAGE_STRING, '@', "type", CLIENT_TYPE, ')', NULL); } if (features == NULL) { /* Otherwise, is it one of the caps bundles we advertise? These are not * just shoved into the cache with gabble_presence_cache_add_own_caps() * because capabilities_get_features() always includes a few bonus * features... */ if (!tp_strdiff (suffix, BUNDLE_SHARE_V1)) features = gabble_capabilities_get_bundle_share_v1 (); if (!tp_strdiff (suffix, BUNDLE_VOICE_V1)) features = gabble_capabilities_get_bundle_voice_v1 (); if (!tp_strdiff (suffix, BUNDLE_VIDEO_V1)) features = gabble_capabilities_get_bundle_video_v1 (); if (!tp_strdiff (suffix, BUNDLE_CAMERA_V1)) features = gabble_capabilities_get_bundle_camera_v1 (); } if (data_forms != NULL) { guint i; for (i = 0; i < data_forms->len; i++) { WockyDataForm *form = g_ptr_array_index (data_forms, i); wocky_data_form_add_to_node (form, result_query); } } if (features == NULL && tp_strdiff (suffix, BUNDLE_PMUC_V1)) { wocky_porter_send_iq_error (porter, stanza, WOCKY_XMPP_ERROR_ITEM_NOT_FOUND, NULL); } else { /* Send an empty reply for a pmuc-v1 disco, matching Google's behaviour. */ if (features != NULL) { gabble_capability_set_foreach (features, add_feature_node, result_query); } wocky_porter_send (self->priv->porter, result); } g_object_unref (result); return TRUE; } static gboolean iq_version_cb (WockyPorter *porter, WockyStanza *stanza, gpointer user_data) { WockyStanza *result; result = wocky_stanza_build_iq_result (stanza, '(', "query", ':', NS_VERSION, '(', "name", '$', PACKAGE_NAME, ')', '(', "version", '$', PACKAGE_VERSION, ')', ')', NULL); if (result == NULL) return FALSE; wocky_porter_send (porter, result); g_object_unref ((GObject *) result); return TRUE; } /** * set_status_to_connected * * Stage 4 of connecting, this function is called once all the events we were * waiting for happened. * */ static void set_status_to_connected (GabbleConnection *conn) { TpBaseConnection *base = (TpBaseConnection *) conn; if (tp_base_connection_get_status (base) == TP_CONNECTION_STATUS_DISCONNECTED) { /* We already failed to connect, but at the time an async thing was * still pending, and now it has finished. Do nothing special. */ return; } if (conn->features & GABBLE_CONNECTION_FEATURES_PEP) { const gchar *ifaces[] = { GABBLE_IFACE_OLPC_BUDDY_INFO, GABBLE_IFACE_OLPC_ACTIVITY_PROPERTIES, NULL }; tp_base_connection_add_interfaces ((TpBaseConnection *) conn, ifaces); } if (conn->features & GABBLE_CONNECTION_FEATURES_GOOGLE_MAIL_NOTIFY) { const gchar *ifaces[] = { TP_IFACE_CONNECTION_INTERFACE_MAIL_NOTIFICATION, NULL }; tp_base_connection_add_interfaces ((TpBaseConnection *) conn, ifaces); } if (tp_base_contact_list_can_block (gabble_connection_get_contact_list (conn))) { const gchar *ifaces[] = { TP_IFACE_CONNECTION_INTERFACE_CONTACT_BLOCKING, NULL }; tp_base_connection_add_interfaces ((TpBaseConnection *) conn, ifaces); } /* go go gadget on-line */ tp_base_connection_change_status (base, TP_CONNECTION_STATUS_CONNECTED, TP_CONNECTION_STATUS_REASON_REQUESTED); } static void decrement_waiting_connected (GabbleConnection *conn) { conn->priv->waiting_connected--; if (conn->priv->waiting_connected == 0) set_status_to_connected (conn); } static void conn_wlm_jid_lookup_cb (GObject *source, GAsyncResult *result, gpointer user_data) { GabbleConnection *conn = (GabbleConnection *) source; GSimpleAsyncResult *my_result = user_data; WockyStanza *iq = NULL; WockyNode *node; const gchar *jid = NULL; GError *error = NULL; if (!conn_util_send_iq_finish (conn, result, &iq, &error)) { g_simple_async_result_take_error (my_result, error); goto out; } node = wocky_node_get_child_ns (wocky_stanza_get_top_node (iq), "getjid", NS_WLM_JID_LOOKUP); if (node != NULL) { jid = wocky_node_get_content_from_child (node, "jid"); } if (!tp_str_empty (jid)) { g_simple_async_result_set_op_res_gpointer (my_result, g_strdup (jid), g_free); } else { g_simple_async_result_set_error (my_result, TP_ERROR, TP_ERROR_INVALID_HANDLE, "jid not found in getjid reply"); } out: g_simple_async_result_complete (my_result); g_object_unref (my_result); } static void conn_wlm_jid_lookup_async (TpHandleRepoIface *repo, TpBaseConnection *base, const gchar *id, gpointer context, GAsyncReadyCallback callback, gpointer user_data) { GabbleConnection *self = GABBLE_CONNECTION (base); WockyStanza *iq; GSimpleAsyncResult *result; gchar *normal_id; GError *error = NULL; result = g_simple_async_result_new ((GObject *) repo, callback, user_data, conn_wlm_jid_lookup_async); /* First, do local normalization */ normal_id = gabble_normalize_contact (repo, id, context, &error); if (normal_id == NULL) { g_simple_async_result_take_error (result, error); g_simple_async_result_complete_in_idle (result); g_object_unref (result); return; } /* If it is already a WLM jid, return it */ if (g_str_has_suffix (normal_id, "@messenger.live.com")) { g_simple_async_result_set_op_res_gpointer (result, normal_id, g_free); g_simple_async_result_complete_in_idle (result); g_object_unref (result); return; } /* Ask the server to give a WLM jid */ iq = wocky_stanza_build (WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_GET, conn_util_get_bare_self_jid (self), normal_id, '(', "getjid", ':', NS_WLM_JID_LOOKUP, ')', NULL); conn_util_send_iq_async (self, iq, NULL, conn_wlm_jid_lookup_cb, result); g_object_unref (iq); g_free (normal_id); } static gchar * conn_wlm_jid_lookup_finish (TpHandleRepoIface *repo, GAsyncResult *result, GError **error) { GSimpleAsyncResult *simple = (GSimpleAsyncResult *) result; g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (repo), conn_wlm_jid_lookup_async), NULL); if (g_simple_async_result_propagate_error (simple, error)) return NULL; return g_strdup (g_simple_async_result_get_op_res_gpointer (simple)); } static void connection_disco_cb (GabbleDisco *disco, GabbleDiscoRequest *request, const gchar *jid, const gchar *node, WockyNode *result, GError *disco_error, gpointer user_data) { GabbleConnection *conn = GABBLE_CONNECTION (user_data); TpBaseConnection *base = (TpBaseConnection *) conn; if (tp_base_connection_get_status (base) != TP_CONNECTION_STATUS_CONNECTING) { g_assert (tp_base_connection_get_status (base) == TP_CONNECTION_STATUS_DISCONNECTED); return; } if (disco_error) { DEBUG ("got disco error, setting no features: %s", disco_error->message); if (disco_error->code == GABBLE_DISCO_ERROR_TIMEOUT) { DEBUG ("didn't receive a response to our disco request: disconnect"); goto ERROR; } } else { WockyNodeIter i; WockyNode *child; NODE_DEBUG (result, "got"); wocky_node_iter_init (&i, result, NULL, NULL); while (wocky_node_iter_next (&i, &child)) { if (0 == strcmp (child->name, "identity")) { const gchar *category = wocky_node_get_attribute (child, "category"); const gchar *type = wocky_node_get_attribute (child, "type"); if (!tp_strdiff (category, "pubsub") && !tp_strdiff (type, "pep")) { DEBUG ("Server advertises PEP support in its features"); conn->features |= GABBLE_CONNECTION_FEATURES_PEP; } } else if (0 == strcmp (child->name, "feature")) { const gchar *var = wocky_node_get_attribute (child, "var"); if (var == NULL) continue; if (0 == strcmp (var, NS_GOOGLE_ROSTER)) conn->features |= GABBLE_CONNECTION_FEATURES_GOOGLE_ROSTER; #ifdef ENABLE_VOIP else if (0 == strcmp (var, NS_GOOGLE_JINGLE_INFO)) conn->features |= GABBLE_CONNECTION_FEATURES_GOOGLE_JINGLE_INFO; #endif else if (0 == strcmp (var, NS_PRESENCE_INVISIBLE)) conn->features |= GABBLE_CONNECTION_FEATURES_PRESENCE_INVISIBLE; else if (0 == strcmp (var, NS_PRIVACY)) conn->features |= GABBLE_CONNECTION_FEATURES_PRIVACY; else if (0 == strcmp (var, NS_INVISIBLE)) conn->features |= GABBLE_CONNECTION_FEATURES_INVISIBLE; else if (0 == strcmp (var, NS_GOOGLE_MAIL_NOTIFY)) conn->features |= GABBLE_CONNECTION_FEATURES_GOOGLE_MAIL_NOTIFY; else if (0 == strcmp (var, NS_GOOGLE_SHARED_STATUS)) conn->features |= GABBLE_CONNECTION_FEATURES_GOOGLE_SHARED_STATUS; else if (0 == strcmp (var, NS_GOOGLE_QUEUE)) conn->features |= GABBLE_CONNECTION_FEATURES_GOOGLE_QUEUE; else if (0 == strcmp (var, NS_GOOGLE_SETTING)) conn->features |= GABBLE_CONNECTION_FEATURES_GOOGLE_SETTING; else if (0 == strcmp (var, NS_WLM_JID_LOOKUP)) conn->features |= GABBLE_CONNECTION_FEATURES_WLM_JID_LOOKUP; } } DEBUG ("set features flags to %d", conn->features); } if ((conn->features & GABBLE_CONNECTION_FEATURES_WLM_JID_LOOKUP) != 0) { TpHandleRepoIface *contact_repo = tp_base_connection_get_handles (base, TP_HANDLE_TYPE_CONTACT); tp_dynamic_handle_repo_set_normalize_async ( (TpDynamicHandleRepo *) contact_repo, conn_wlm_jid_lookup_async, conn_wlm_jid_lookup_finish); } conn_presence_set_initial_presence_async (conn, connection_initial_presence_cb, NULL); return; ERROR: tp_base_connection_change_status (base, TP_CONNECTION_STATUS_DISCONNECTED, TP_CONNECTION_STATUS_REASON_NETWORK_ERROR); return; } /** * connection_initial_presence_cb * * Stage 4 of connecting, this function is called after our initial presence * has been set (or has failed unrecoverably). It marks the connection as * online (or failed, as appropriate); the former triggers the roster being * retrieved. */ static void connection_initial_presence_cb (GObject *source_object, GAsyncResult *res, gpointer user_data) { GabbleConnection *self = (GabbleConnection *) source_object; TpBaseConnection *base = (TpBaseConnection *) self; GError *error = NULL; if (!conn_presence_set_initial_presence_finish (self, res, &error)) { DEBUG ("error setting up initial presence: %s", error->message); if (tp_base_connection_get_status (base) != TP_CONNECTION_STATUS_DISCONNECTED) tp_base_connection_change_status (base, TP_CONNECTION_STATUS_DISCONNECTED, TP_CONNECTION_STATUS_REASON_NETWORK_ERROR); g_error_free (error); } else { decrement_waiting_connected (self); } } /**************************************************************************** * D-BUS EXPORTED METHODS * ****************************************************************************/ static void gabble_free_rcc_list (GPtrArray *rccs) { g_boxed_free (TP_ARRAY_TYPE_REQUESTABLE_CHANNEL_CLASS_LIST, rccs); } /** * gabble_connection_build_contact_caps: * @handle: a contact * @caps: @handle's XMPP capabilities * * Returns: an array containing the channel classes corresponding to @caps. */ static GPtrArray * gabble_connection_build_contact_caps ( GabbleConnection *self, TpHandle handle, const GabbleCapabilitySet *caps) { TpBaseConnection *base_conn = TP_BASE_CONNECTION (self); TpChannelManagerIter iter; TpChannelManager *manager; GPtrArray *ret = g_ptr_array_new (); tp_base_connection_channel_manager_iter_init (&iter, base_conn); while (tp_base_connection_channel_manager_iter_next (&iter, &manager)) { if (GABBLE_IS_CAPS_CHANNEL_MANAGER (manager)) { gabble_caps_channel_manager_get_contact_capabilities ( GABBLE_CAPS_CHANNEL_MANAGER (manager), handle, caps, ret); } } return ret; } static void _emit_capabilities_changed (GabbleConnection *conn, TpHandle handle, const GabbleCapabilitySet *old_set, const GabbleCapabilitySet *new_set) { GPtrArray *caps_arr; const CapabilityConversionData *ccd; GHashTable *hash; guint i; if (gabble_capability_set_equals (old_set, new_set)) return; /* o.f.T.C.Capabilities */ caps_arr = g_ptr_array_new (); for (ccd = capabilities_conversions; NULL != ccd->iface; ccd++) { guint old_specific = ccd->c2tf_fn (old_set); guint new_specific = ccd->c2tf_fn (new_set); if (old_specific != 0 || new_specific != 0) { GValue caps_monster_struct = {0, }; guint old_generic = old_specific ? TP_CONNECTION_CAPABILITY_FLAG_CREATE | TP_CONNECTION_CAPABILITY_FLAG_INVITE : 0; guint new_generic = new_specific ? TP_CONNECTION_CAPABILITY_FLAG_CREATE | TP_CONNECTION_CAPABILITY_FLAG_INVITE : 0; if (0 == (old_specific ^ new_specific)) continue; g_value_init (&caps_monster_struct, TP_STRUCT_TYPE_CAPABILITY_CHANGE); g_value_take_boxed (&caps_monster_struct, dbus_g_type_specialized_construct (TP_STRUCT_TYPE_CAPABILITY_CHANGE)); dbus_g_type_struct_set (&caps_monster_struct, 0, handle, 1, ccd->iface, 2, old_generic, 3, new_generic, 4, old_specific, 5, new_specific, G_MAXUINT); g_ptr_array_add (caps_arr, g_value_get_boxed (&caps_monster_struct)); } } if (caps_arr->len) tp_svc_connection_interface_capabilities_emit_capabilities_changed ( conn, caps_arr); for (i = 0; i < caps_arr->len; i++) { g_boxed_free (TP_STRUCT_TYPE_CAPABILITY_CHANGE, g_ptr_array_index (caps_arr, i)); } g_ptr_array_unref (caps_arr); /* o.f.T.C.ContactCapabilities */ caps_arr = gabble_connection_build_contact_caps (conn, handle, new_set); hash = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify) gabble_free_rcc_list); g_hash_table_insert (hash, GUINT_TO_POINTER (handle), caps_arr); tp_svc_connection_interface_contact_capabilities_emit_contact_capabilities_changed ( conn, hash); g_hash_table_unref (hash); } static const GabbleCapabilitySet * empty_caps_set (void) { static GabbleCapabilitySet *empty = NULL; if (G_UNLIKELY (empty == NULL)) empty = gabble_capability_set_new (); return empty; } /* * gabble_connection_get_handle_contact_capabilities: * * Returns: an array of channel classes representing @handle's capabilities */ static GPtrArray * gabble_connection_get_handle_contact_capabilities ( GabbleConnection *self, TpHandle handle) { TpBaseConnection *base = TP_BASE_CONNECTION (self); GabblePresence *p; const GabbleCapabilitySet *caps; if (handle == tp_base_connection_get_self_handle (base)) p = self->self_presence; else p = gabble_presence_cache_get (self->presence_cache, handle); if (p == NULL) caps = empty_caps_set (); else caps = gabble_presence_peek_caps (p); return gabble_connection_build_contact_caps (self, handle, caps); } static void connection_capabilities_update_cb (GabblePresenceCache *cache, TpHandle handle, const GabbleCapabilitySet *old_cap_set, const GabbleCapabilitySet *new_cap_set, gpointer user_data) { GabbleConnection *conn = GABBLE_CONNECTION (user_data); _emit_capabilities_changed (conn, handle, old_cap_set, new_cap_set); } /** * gabble_connection_advertise_capabilities * * Implements D-Bus method AdvertiseCapabilities * on interface org.freedesktop.Telepathy.Connection.Interface.Capabilities * * @error: Used to return a pointer to a GError detailing any error * that occurred, D-Bus will throw the error only if this * function returns FALSE. * * Returns: TRUE if successful, FALSE if an error was thrown. */ static void gabble_connection_advertise_capabilities (TpSvcConnectionInterfaceCapabilities *iface, const GPtrArray *add, const gchar **del, DBusGMethodInvocation *context) { GabbleConnection *self = GABBLE_CONNECTION (iface); TpBaseConnection *base = (TpBaseConnection *) self; guint i; GabbleConnectionPrivate *priv = self->priv; const CapabilityConversionData *ccd; GPtrArray *ret; GabbleCapabilitySet *save_set; GabbleCapabilitySet *add_set, *remove_set; TP_BASE_CONNECTION_ERROR_IF_NOT_CONNECTED (base, context); /* Now that someone has told us our *actual* capabilities, we can stop * advertising spurious caps in initial presence */ gabble_capability_set_clear (self->priv->bonus_caps); add_set = gabble_capability_set_new (); remove_set = gabble_capability_set_new (); for (i = 0; i < add->len; i++) { GValue iface_flags_pair = {0, }; gchar *channel_type; guint flags; g_value_init (&iface_flags_pair, TP_STRUCT_TYPE_CAPABILITY_PAIR); g_value_set_static_boxed (&iface_flags_pair, g_ptr_array_index (add, i)); dbus_g_type_struct_get (&iface_flags_pair, 0, &channel_type, 1, &flags, G_MAXUINT); for (ccd = capabilities_conversions; NULL != ccd->iface; ccd++) if (g_str_equal (channel_type, ccd->iface)) ccd->tf2c_fn (flags, add_set); g_free (channel_type); } for (i = 0; NULL != del[i]; i++) { for (ccd = capabilities_conversions; NULL != ccd->iface; ccd++) if (g_str_equal (del[i], ccd->iface)) ccd->tf2c_fn (~0, remove_set); } gabble_capability_set_update (priv->legacy_caps, add_set); gabble_capability_set_exclude (priv->legacy_caps, remove_set); if (DEBUGGING) { gchar *add_str = gabble_capability_set_dump (add_set, " "); gchar *remove_str = gabble_capability_set_dump (remove_set, " "); DEBUG ("caps to add:\n%s", add_str); DEBUG ("caps to remove:\n%s", remove_str); g_free (add_str); g_free (remove_str); } gabble_capability_set_free (add_set); gabble_capability_set_free (remove_set); if (gabble_connection_refresh_capabilities (self, &save_set)) { _emit_capabilities_changed (self, tp_base_connection_get_self_handle (base), save_set, priv->all_caps); gabble_capability_set_free (save_set); } ret = g_ptr_array_new (); for (ccd = capabilities_conversions; NULL != ccd->iface; ccd++) { guint tp_caps = ccd->c2tf_fn (self->priv->all_caps); if (tp_caps != 0) { GValue iface_flags_pair = {0, }; g_value_init (&iface_flags_pair, TP_STRUCT_TYPE_CAPABILITY_PAIR); g_value_take_boxed (&iface_flags_pair, dbus_g_type_specialized_construct ( TP_STRUCT_TYPE_CAPABILITY_PAIR)); dbus_g_type_struct_set (&iface_flags_pair, 0, ccd->iface, 1, tp_caps, G_MAXUINT); g_ptr_array_add (ret, g_value_get_boxed (&iface_flags_pair)); } } tp_svc_connection_interface_capabilities_return_from_advertise_capabilities ( context, ret); g_ptr_array_foreach (ret, (GFunc) g_value_array_free, NULL); g_ptr_array_unref (ret); } static const gchar * get_form_type (WockyDataForm *form) { WockyDataFormField *field; field = g_hash_table_lookup (form->fields, "FORM_TYPE"); g_assert (field != NULL); return field->raw_value_contents[0]; } static const gchar * check_form_is_unique (GabbleConnection *self, const gchar *form_type) { GHashTableIter iter; gpointer key, value; g_hash_table_iter_init (&iter, self->priv->client_data_forms); while (g_hash_table_iter_next (&iter, &key, &value)) { const gchar *manager = key; GPtrArray *data_forms = value; guint i; if (data_forms == NULL || data_forms->len == 0) continue; for (i = 0; i < data_forms->len; i++) { WockyDataForm *form = g_ptr_array_index (data_forms, i); if (!tp_strdiff (get_form_type (form), form_type)) return manager; } } return NULL; } static gboolean check_data_form_in_list (GPtrArray *array, const gchar *form_type) { guint i; for (i = 0; i < array->len; i++) { WockyDataForm *form = g_ptr_array_index (array, i); if (!tp_strdiff (get_form_type (form), form_type)) return TRUE; } return FALSE; } static gboolean check_data_form_is_valid (GabbleConnection *self, WockyDataForm *form, GPtrArray *existing_forms) { WockyDataFormField *field; const gchar *form_type, *other_client; /* We want rid of forms with no FORM_TYPE quickly. */ field = g_hash_table_lookup (form->fields, "FORM_TYPE"); if (field == NULL || tp_str_empty (field->raw_value_contents[0])) { WARNING ("data form with no FORM_TYPE field; ignoring"); return FALSE; } form_type = field->raw_value_contents[0]; /* We'll get warnings (potentially bad) if two clients cause a * channel manager to create two data forms with the same FORM_TYPE, * or if multiple channel managers create two data forms with the * same FORM_TYPE, for the same client. This is probably not a * problem in practice given hardly anyone uses data forms in entity * capabilities anyway. */ /* We don't want the same data form from another caps channel * manager for this client either */ if (check_data_form_in_list (existing_forms, form_type)) { WARNING ("duplicate data form '%s' from another channel " "manager; ignoring", form_type); return FALSE; } /* And lastly we don't want a form we're already advertising. */ other_client = check_form_is_unique (self, form_type); if (other_client != NULL) { WARNING ("Data form '%s' already provided by client " "%s; ignoring", form_type, other_client); return FALSE; } return TRUE; } /** * gabble_connection_update_capabilities * * Implements D-Bus method UpdateCapabilities * on interface * org.freedesktop.Telepathy.Connection.Interface.ContactCapabilities */ static void gabble_connection_update_capabilities ( TpSvcConnectionInterfaceContactCapabilities *iface, const GPtrArray *clients, DBusGMethodInvocation *context) { GabbleConnection *self = GABBLE_CONNECTION (iface); TpBaseConnection *base = (TpBaseConnection *) self; GabbleCapabilitySet *old_caps = NULL; guint i; /* Now that someone has told us our *actual* capabilities, we can stop * advertising spurious caps in initial presence */ gabble_capability_set_clear (self->priv->bonus_caps); DEBUG ("enter"); for (i = 0; i < clients->len; i++) { GValueArray *va = g_ptr_array_index (clients, i); const gchar *client_name = g_value_get_string (va->values + 0); const GPtrArray *filters = g_value_get_boxed (va->values + 1); const gchar * const * cap_tokens = g_value_get_boxed (va->values + 2); GabbleCapabilitySet *cap_set; GPtrArray *data_forms; TpChannelManagerIter iter; TpChannelManager *manager; g_hash_table_remove (self->priv->client_caps, client_name); g_hash_table_remove (self->priv->client_data_forms, client_name); if ((cap_tokens == NULL || cap_tokens[0] == NULL) && filters->len == 0) { /* no capabilities */ DEBUG ("client %s can't do anything", client_name); continue; } cap_set = gabble_capability_set_new (); data_forms = g_ptr_array_new_with_free_func ( (GDestroyNotify) g_object_unref); tp_base_connection_channel_manager_iter_init (&iter, base); while (tp_base_connection_channel_manager_iter_next (&iter, &manager)) { if (GABBLE_IS_CAPS_CHANNEL_MANAGER (manager)) { GPtrArray *forms = g_ptr_array_new_with_free_func ( (GDestroyNotify) g_object_unref); guint j; /* First, represent the client... */ gabble_caps_channel_manager_represent_client ( GABBLE_CAPS_CHANNEL_MANAGER (manager), client_name, filters, cap_tokens, cap_set, forms); /* Now check the forms... */ for (j = 0; j < forms->len; j++) { WockyDataForm *form = g_ptr_array_index (forms, j); if (check_data_form_is_valid (self, form, data_forms)) g_ptr_array_add (data_forms, g_object_ref (form)); } g_ptr_array_unref (forms); } } /* first deal with normal caps */ if (gabble_capability_set_size (cap_set) > 0) { if (DEBUGGING) { gchar *s = gabble_capability_set_dump (cap_set, " "); DEBUG ("client %s contributes:\n%s", client_name, s); g_free (s); } g_hash_table_insert (self->priv->client_caps, g_strdup (client_name), cap_set); } else { DEBUG ("client %s has no interesting capabilities", client_name); gabble_capability_set_free (cap_set); } /* now data forms */ if (data_forms->len > 0) { guint j; /* now print out what forms we have here */ DEBUG ("client %s contributes %u data form%s:", client_name, data_forms->len, data_forms->len > 1 ? "s" : ""); for (j = 0; j < data_forms->len; j++) DEBUG (" - %s", get_form_type (g_ptr_array_index (data_forms, j))); g_hash_table_insert (self->priv->client_data_forms, g_strdup (client_name), data_forms); } else { DEBUG ("client %s has no interesting data forms", client_name); g_ptr_array_unref (data_forms); } } if (gabble_connection_refresh_capabilities (self, &old_caps)) { _emit_capabilities_changed (self, tp_base_connection_get_self_handle (base), old_caps, self->priv->all_caps); gabble_capability_set_free (old_caps); } tp_svc_connection_interface_contact_capabilities_return_from_update_capabilities ( context); } static const gchar *assumed_caps[] = { TP_IFACE_CHANNEL_TYPE_TEXT, NULL }; /** * gabble_connection_get_handle_capabilities * * Add capabilities of handle to the given GPtrArray */ static void gabble_connection_get_handle_capabilities (GabbleConnection *self, TpHandle handle, GPtrArray *arr) { TpBaseConnection *base = (TpBaseConnection *) self; GabblePresence *pres; const CapabilityConversionData *ccd; guint typeflags; const gchar **assumed; if (0 == handle) { /* obsolete request for the connection's capabilities, do nothing */ return; } if (handle == tp_base_connection_get_self_handle (base)) pres = self->self_presence; else pres = gabble_presence_cache_get (self->presence_cache, handle); if (NULL != pres) { const GabbleCapabilitySet *cap_set = gabble_presence_peek_caps (pres); for (ccd = capabilities_conversions; NULL != ccd->iface; ccd++) { typeflags = ccd->c2tf_fn (cap_set); if (typeflags) { GValue monster = {0, }; g_value_init (&monster, TP_STRUCT_TYPE_CONTACT_CAPABILITY); g_value_take_boxed (&monster, dbus_g_type_specialized_construct ( TP_STRUCT_TYPE_CONTACT_CAPABILITY)); dbus_g_type_struct_set (&monster, 0, handle, 1, ccd->iface, 2, TP_CONNECTION_CAPABILITY_FLAG_CREATE | TP_CONNECTION_CAPABILITY_FLAG_INVITE, 3, typeflags, G_MAXUINT); g_ptr_array_add (arr, g_value_get_boxed (&monster)); } } } for (assumed = assumed_caps; NULL != *assumed; assumed++) { GValue monster = {0, }; g_value_init (&monster, TP_STRUCT_TYPE_CONTACT_CAPABILITY); g_value_take_boxed (&monster, dbus_g_type_specialized_construct ( TP_STRUCT_TYPE_CONTACT_CAPABILITY)); dbus_g_type_struct_set (&monster, 0, handle, 1, *assumed, 2, TP_CONNECTION_CAPABILITY_FLAG_CREATE | TP_CONNECTION_CAPABILITY_FLAG_INVITE, 3, 0, G_MAXUINT); g_ptr_array_add (arr, g_value_get_boxed (&monster)); } } static void conn_capabilities_fill_contact_attributes (GObject *obj, const GArray *contacts, GHashTable *attributes_hash) { GabbleConnection *self = GABBLE_CONNECTION (obj); guint i; GPtrArray *array = NULL; for (i = 0; i < contacts->len; i++) { TpHandle handle = g_array_index (contacts, TpHandle, i); if (array == NULL) array = g_ptr_array_new (); gabble_connection_get_handle_capabilities (self, handle, array); if (array->len > 0) { GValue *val = tp_g_value_slice_new ( TP_ARRAY_TYPE_CONTACT_CAPABILITY_LIST); g_value_take_boxed (val, array); tp_contacts_mixin_set_contact_attribute (attributes_hash, handle, TP_IFACE_CONNECTION_INTERFACE_CAPABILITIES"/caps", val); array = NULL; } } if (array != NULL) g_ptr_array_unref (array); } static void conn_contact_capabilities_fill_contact_attributes (GObject *obj, const GArray *contacts, GHashTable *attributes_hash) { GabbleConnection *self = GABBLE_CONNECTION (obj); guint i; for (i = 0; i < contacts->len; i++) { TpHandle handle = g_array_index (contacts, TpHandle, i); GValue *val = tp_g_value_slice_new_take_boxed ( TP_ARRAY_TYPE_REQUESTABLE_CHANNEL_CLASS_LIST, gabble_connection_get_handle_contact_capabilities (self, handle)); tp_contacts_mixin_set_contact_attribute (attributes_hash, handle, TP_IFACE_CONNECTION_INTERFACE_CONTACT_CAPABILITIES"/capabilities", val); } } /** * gabble_connection_get_capabilities * * Implements D-Bus method GetCapabilities * on interface org.freedesktop.Telepathy.Connection.Interface.Capabilities */ static void gabble_connection_get_capabilities (TpSvcConnectionInterfaceCapabilities *iface, const GArray *handles, DBusGMethodInvocation *context) { GabbleConnection *self = GABBLE_CONNECTION (iface); TpBaseConnection *base = (TpBaseConnection *) self; TpHandleRepoIface *contact_handles = tp_base_connection_get_handles (base, TP_HANDLE_TYPE_CONTACT); guint i; GPtrArray *ret; GError *error = NULL; TP_BASE_CONNECTION_ERROR_IF_NOT_CONNECTED (base, context); if (!tp_handles_are_valid (contact_handles, handles, TRUE, &error)) { dbus_g_method_return_error (context, error); g_error_free (error); return; } ret = g_ptr_array_new (); for (i = 0; i < handles->len; i++) { TpHandle handle = g_array_index (handles, TpHandle, i); gabble_connection_get_handle_capabilities (self, handle, ret); } tp_svc_connection_interface_capabilities_return_from_get_capabilities ( context, ret); for (i = 0; i < ret->len; i++) { g_value_array_free (g_ptr_array_index (ret, i)); } g_ptr_array_unref (ret); } /** * gabble_connection_get_contact_capabilities * * Implements D-Bus method GetContactCapabilities * on interface * org.freedesktop.Telepathy.Connection.Interface.ContactCapabilities */ static void gabble_connection_get_contact_capabilities ( TpSvcConnectionInterfaceContactCapabilities *iface, const GArray *handles, DBusGMethodInvocation *context) { GabbleConnection *self = GABBLE_CONNECTION (iface); TpBaseConnection *base = (TpBaseConnection *) self; TpHandleRepoIface *contact_handles = tp_base_connection_get_handles (base, TP_HANDLE_TYPE_CONTACT); guint i; GHashTable *ret; GError *error = NULL; TP_BASE_CONNECTION_ERROR_IF_NOT_CONNECTED (base, context); if (!tp_handles_are_valid (contact_handles, handles, FALSE, &error)) { dbus_g_method_return_error (context, error); g_error_free (error); return; } ret = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify) gabble_free_rcc_list); for (i = 0; i < handles->len; i++) { TpHandle handle = g_array_index (handles, TpHandle, i); GPtrArray *arr; arr = gabble_connection_get_handle_contact_capabilities (self, handle); g_hash_table_insert (ret, GUINT_TO_POINTER (handle), arr); } tp_svc_connection_interface_contact_capabilities_return_from_get_contact_capabilities (context, ret); g_hash_table_unref (ret); } const char * _gabble_connection_find_conference_server (GabbleConnection *conn) { GabbleConnectionPrivate *priv; g_assert (GABBLE_IS_CONNECTION (conn)); priv = conn->priv; if (priv->conference_server == NULL) { /* Find first server that has NS_MUC feature */ const GabbleDiscoItem *item = gabble_disco_service_find (conn->disco, "conference", "text", NS_MUC); if (item != NULL) priv->conference_server = item->jid; } if (priv->conference_server == NULL) priv->conference_server = priv->fallback_conference_server; return priv->conference_server; } gchar * gabble_connection_get_canonical_room_name (GabbleConnection *conn, const gchar *name) { const gchar *server; g_assert (GABBLE_IS_CONNECTION (conn)); if (strchr (name, '@')) return g_strdup (name); server = _gabble_connection_find_conference_server (conn); if (server == NULL) return NULL; return gabble_encode_jid (name, server, NULL); } void gabble_connection_ensure_capabilities (GabbleConnection *self, const GabbleCapabilitySet *ensured) { gabble_capability_set_update (self->priv->notify_caps, ensured); gabble_connection_refresh_capabilities (self, NULL); } gboolean gabble_connection_send_presence (GabbleConnection *conn, WockyStanzaSubType sub_type, const gchar *contact, const gchar *status, GError **error) { WockyStanza *message; gboolean result; message = wocky_stanza_build (WOCKY_STANZA_TYPE_PRESENCE, sub_type, NULL, contact, NULL); if (WOCKY_STANZA_SUB_TYPE_SUBSCRIBE == sub_type) lm_message_node_add_own_nick ( wocky_stanza_get_top_node (message), conn); if (!tp_str_empty (status)) wocky_node_add_child_with_content ( wocky_stanza_get_top_node (message), "status", status); result = _gabble_connection_send (conn, message, error); g_object_unref (message); return result; } static void capabilities_service_iface_init (gpointer g_iface, gpointer iface_data) { TpSvcConnectionInterfaceCapabilitiesClass *klass = (TpSvcConnectionInterfaceCapabilitiesClass *) g_iface; #define IMPLEMENT(x) tp_svc_connection_interface_capabilities_implement_##x (\ klass, gabble_connection_##x) IMPLEMENT(advertise_capabilities); IMPLEMENT(get_capabilities); #undef IMPLEMENT } static void gabble_conn_contact_caps_iface_init (gpointer g_iface, gpointer iface_data) { TpSvcConnectionInterfaceContactCapabilitiesClass *klass = g_iface; #define IMPLEMENT(x) \ tp_svc_connection_interface_contact_capabilities_implement_##x (\ klass, gabble_connection_##x) IMPLEMENT(get_contact_capabilities); IMPLEMENT(update_capabilities); #undef IMPLEMENT } /* For unit tests only */ void gabble_connection_set_disco_reply_timeout (guint timeout) { disco_reply_timeout = timeout; } void gabble_connection_update_sidecar_capabilities (GabbleConnection *self, const GabbleCapabilitySet *add_set, const GabbleCapabilitySet *remove_set) { TpBaseConnection *base = TP_BASE_CONNECTION (self); GabbleConnectionPrivate *priv = self->priv; GabbleCapabilitySet *save_set; if (add_set == NULL && remove_set == NULL) return; if (add_set != NULL) gabble_capability_set_update (priv->sidecar_caps, add_set); if (remove_set != NULL) gabble_capability_set_exclude (priv->sidecar_caps, remove_set); if (DEBUGGING) { if (add_set != NULL) { gchar *add_str = gabble_capability_set_dump (add_set, " "); DEBUG ("sidecar caps to add:\n%s", add_str); g_free (add_str); } if (remove_set != NULL) { gchar *remove_str = gabble_capability_set_dump (remove_set, " "); DEBUG ("sidecar caps to remove:\n%s", remove_str); g_free (remove_str); } } if (gabble_connection_refresh_capabilities (self, &save_set)) { _emit_capabilities_changed (self, tp_base_connection_get_self_handle (base), save_set, priv->all_caps); gabble_capability_set_free (save_set); } } /* identities is actually a WockyDiscoIdentityArray */ gchar * gabble_connection_add_sidecar_own_caps_full (GabblePluginConnection *self, const GabbleCapabilitySet *cap_set, const GPtrArray *identities, GPtrArray *data_forms) { GabbleConnection *conn = GABBLE_CONNECTION (self); GPtrArray *identities_copy = ((identities == NULL) ? wocky_disco_identity_array_new () : wocky_disco_identity_array_copy (identities)); gchar *ver; /* XEP-0030 requires at least 1 identity. We don't need more. */ if (identities_copy->len == 0) g_ptr_array_add (identities_copy, wocky_disco_identity_new ("client", CLIENT_TYPE, NULL, PACKAGE_STRING)); ver = gabble_caps_hash_compute_full (cap_set, identities_copy, data_forms); gabble_presence_cache_add_own_caps (conn->presence_cache, ver, cap_set, identities_copy, data_forms); wocky_disco_identity_array_free (identities_copy); return ver; } gchar * gabble_connection_add_sidecar_own_caps (GabblePluginConnection *self, const GabbleCapabilitySet *cap_set, const GPtrArray *identities) { return gabble_connection_add_sidecar_own_caps_full (self, cap_set, identities, NULL); } const gchar * gabble_connection_get_jid_for_caps (GabblePluginConnection *plugin_conn, WockyXep0115Capabilities *caps) { TpHandle handle; TpBaseConnection *base; TpHandleRepoIface *contact_handles; GabbleConnection *conn = GABBLE_CONNECTION (plugin_conn); g_return_val_if_fail (GABBLE_IS_CONNECTION (conn), NULL); g_return_val_if_fail (GABBLE_IS_PRESENCE (caps), NULL); base = (TpBaseConnection *) conn; if ((GabblePresence *) caps == conn->self_presence) { handle = tp_base_connection_get_self_handle (base); } else { handle = gabble_presence_cache_get_handle (conn->presence_cache, (GabblePresence *) caps); } contact_handles = tp_base_connection_get_handles (base, TP_HANDLE_TYPE_CONTACT); return tp_handle_inspect (contact_handles, handle); } const gchar * gabble_connection_pick_best_resource_for_caps ( GabblePluginConnection *plugin_connection, const gchar *jid, GabbleCapabilitySetPredicate predicate, gconstpointer user_data) { TpBaseConnection *base; TpHandleRepoIface *contact_handles; TpHandle handle; GabbleConnection *connection = GABBLE_CONNECTION (plugin_connection); GabblePresence *presence; g_return_val_if_fail (GABBLE_IS_CONNECTION (connection), NULL); g_return_val_if_fail (!tp_str_empty (jid), NULL); base = (TpBaseConnection *) connection; contact_handles = tp_base_connection_get_handles (base, TP_HANDLE_TYPE_CONTACT); handle = tp_handle_ensure (contact_handles, jid, NULL, NULL); if (handle == 0) return NULL; presence = gabble_presence_cache_get (connection->presence_cache, handle); if (presence == NULL) return NULL; return gabble_presence_pick_resource_by_caps (presence, 0, predicate, user_data); } TpBaseContactList * gabble_connection_get_contact_list (GabbleConnection *connection) { g_return_val_if_fail (GABBLE_IS_CONNECTION (connection), NULL); return (TpBaseContactList *) connection->roster; } static TpBaseContactList * _gabble_plugin_connection_get_contact_list ( GabblePluginConnection *plugin_connection) { GabbleConnection *connection = GABBLE_CONNECTION (plugin_connection); return gabble_connection_get_contact_list (connection); } WockyXep0115Capabilities * gabble_connection_get_caps (GabblePluginConnection *plugin_connection, TpHandle handle) { GabblePresence *presence; GabbleConnection *connection = GABBLE_CONNECTION (plugin_connection); g_return_val_if_fail (GABBLE_IS_CONNECTION (connection), NULL); g_return_val_if_fail (handle > 0, NULL); presence = gabble_presence_cache_get (connection->presence_cache, handle); return (WockyXep0115Capabilities *) presence; } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������telepathy-gabble-0.18.2/src/connection.h������������������������������������������������������������0000644�0001750�0001750�00000024435�12227000321�020111� 0����������������������������������������������������������������������������������������������������ustar�00smcv����������������������������smcv����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * gabble-connection.h - Header for GabbleConnection * Copyright © 2005-2012 Collabora Ltd. * Copyright © 2005-2010 Nokia Corporation * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 __GABBLE_CONNECTION_H__ #define __GABBLE_CONNECTION_H__ #include "config.h" #include <dbus/dbus-glib.h> #include <glib-object.h> #include <telepathy-glib/telepathy-glib.h> #include <wocky/wocky.h> #include "gabble/capabilities.h" #ifdef ENABLE_FILE_TRANSFER #include "ft-manager.h" #endif #ifdef ENABLE_VOIP #include "jingle-mint.h" #endif #include "muc-factory.h" #include "types.h" #include <gabble/capabilities-set.h> #include <gabble/types.h> #include <gabble/plugin-connection.h> G_BEGIN_DECLS #define GABBLE_TYPE_CONNECTION (gabble_connection_get_type ()) #define GABBLE_CONNECTION(obj) \ (G_TYPE_CHECK_INSTANCE_CAST((obj), GABBLE_TYPE_CONNECTION, GabbleConnection)) #define GABBLE_CONNECTION_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST((klass), GABBLE_TYPE_CONNECTION, \ GabbleConnectionClass)) #define GABBLE_IS_CONNECTION(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE((obj), GABBLE_TYPE_CONNECTION)) #define GABBLE_IS_CONNECTION_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE((klass), GABBLE_TYPE_CONNECTION)) #define GABBLE_CONNECTION_GET_CLASS(obj) \ (G_TYPE_INSTANCE_GET_CLASS ((obj), GABBLE_TYPE_CONNECTION, \ GabbleConnectionClass)) typedef struct _GabbleConnectionClass GabbleConnectionClass; GType gabble_connection_get_type (void); void gabble_connection_update_sidecar_capabilities ( GabbleConnection *connection, const GabbleCapabilitySet *add_set, const GabbleCapabilitySet *remove_set); gchar *gabble_connection_add_sidecar_own_caps ( GabblePluginConnection *connection, const GabbleCapabilitySet *cap_set, const GPtrArray *identities) G_GNUC_WARN_UNUSED_RESULT; gchar *gabble_connection_add_sidecar_own_caps_full ( GabblePluginConnection *connection, const GabbleCapabilitySet *cap_set, const GPtrArray *identities, GPtrArray *data_forms) G_GNUC_WARN_UNUSED_RESULT; WockySession *gabble_connection_get_session ( GabblePluginConnection *connection); gchar *gabble_connection_get_full_jid (GabbleConnection *conn); const gchar * gabble_connection_get_jid_for_caps (GabblePluginConnection *conn, WockyXep0115Capabilities *caps); const gchar * gabble_connection_pick_best_resource_for_caps ( GabblePluginConnection *connection, const gchar *jid, GabbleCapabilitySetPredicate predicate, gconstpointer user_data); TpBaseContactList * gabble_connection_get_contact_list ( GabbleConnection *connection); WockyXep0115Capabilities * gabble_connection_get_caps ( GabblePluginConnection *connection, TpHandle handle); /* Default parameters for optional parameters */ #define GABBLE_PARAMS_DEFAULT_HTTPS_PROXY_PORT 443 #define GABBLE_PARAMS_DEFAULT_STUN_PORT 3478 #define GABBLE_PARAMS_DEFAULT_FALLBACK_STUN_SERVER "stun.telepathy.im" #define GABBLE_PARAMS_DEFAULT_SOCKS5_PROXIES { NULL } /* order must match array of statuses in conn-presence.c */ /* in increasing order of presence */ /*< prefix=GABBLE_PRESENCE >*/ typedef enum { GABBLE_PRESENCE_OFFLINE = 0, GABBLE_PRESENCE_UNKNOWN, GABBLE_PRESENCE_ERROR, GABBLE_PRESENCE_LAST_UNAVAILABLE = GABBLE_PRESENCE_ERROR, /*< skip >*/ GABBLE_PRESENCE_HIDDEN, GABBLE_PRESENCE_XA, GABBLE_PRESENCE_AWAY, GABBLE_PRESENCE_DND, GABBLE_PRESENCE_AVAILABLE, GABBLE_PRESENCE_CHAT, NUM_GABBLE_PRESENCES /*< skip >*/ } GabblePresenceId; /*< flags >*/ typedef enum { GABBLE_CONNECTION_FEATURES_NONE = 0, GABBLE_CONNECTION_FEATURES_GOOGLE_JINGLE_INFO = 1 << 0, GABBLE_CONNECTION_FEATURES_GOOGLE_ROSTER = 1 << 1, GABBLE_CONNECTION_FEATURES_PRESENCE_INVISIBLE = 1 << 2, GABBLE_CONNECTION_FEATURES_PRIVACY = 1 << 3, GABBLE_CONNECTION_FEATURES_PEP = 1 << 4, GABBLE_CONNECTION_FEATURES_GOOGLE_MAIL_NOTIFY = 1 << 5, GABBLE_CONNECTION_FEATURES_INVISIBLE = 1 << 6, GABBLE_CONNECTION_FEATURES_GOOGLE_SHARED_STATUS = 1 << 7, GABBLE_CONNECTION_FEATURES_GOOGLE_QUEUE = 1 << 8, GABBLE_CONNECTION_FEATURES_GOOGLE_SETTING = 1 << 9, GABBLE_CONNECTION_FEATURES_WLM_JID_LOOKUP = 1 << 10, } GabbleConnectionFeatures; typedef struct _GabbleConnectionPrivate GabbleConnectionPrivate; typedef struct _GabbleConnectionMailNotificationPrivate GabbleConnectionMailNotificationPrivate; typedef struct _GabbleConnectionPresencePrivate GabbleConnectionPresencePrivate; typedef void (*GabbleConnectionMsgReplyFunc) ( GabbleConnection *conn, WockyStanza *sent_msg, WockyStanza *reply_msg, GObject *object, gpointer user_data); typedef enum { /* The JID could be a "global" JID, or a MUC room member. We'll assume * that it's a global JID (and remove the resource) unless we've seen * that JID in a MUC before. */ GABBLE_JID_ANY = 0, /* The JID is definitely global. Remove the resource. */ GABBLE_JID_GLOBAL, /* The JID is definitely a room member. Assert that there is a "resource" * (nickname) and don't remove it. */ GABBLE_JID_ROOM_MEMBER } GabbleNormalizeContactJIDMode; struct _GabbleConnectionClass { TpBaseConnectionClass parent_class; TpDBusPropertiesMixinClass properties_class; TpPresenceMixinClass presence_class; TpContactsMixinClass contacts_class; }; struct _GabbleConnection { TpBaseConnection parent; TpPresenceMixin presence; TpContactsMixin contacts; /* DBus daemon instance */ TpDBusDaemon *daemon; WockySession *session; /* channel factories borrowed from TpBaseConnection's list */ GabbleRoster *roster; GabbleMucFactory *muc_factory; GabblePrivateTubesFactory *private_tubes_factory; /* DISCO! */ GabbleDisco *disco; /* connection feature flags */ GabbleConnectionFeatures features; /* presence */ GabblePresenceCache *presence_cache; GabblePresence *self_presence; GabbleConnectionPresencePrivate *presence_priv; /* IQ request pipeline helper, so simultaneous requests don't make * servers hate us */ GabbleRequestPipeline *req_pipeline; /* vCard lookup helper */ GabbleVCardManager *vcard_manager; /* OLPC hash tables */ GHashTable *olpc_activities_info; GHashTable *olpc_pep_activities; GHashTable *olpc_invited_activities; GHashTable *olpc_current_act; /* bytestream factory */ GabbleBytestreamFactory *bytestream_factory; /* outstanding avatar requests */ GHashTable *avatar_requests; /* outstanding vcard requests */ GHashTable *vcard_requests; #ifdef ENABLE_VOIP GabbleJingleMint *jingle_mint; #endif #ifdef ENABLE_FILE_TRANSFER /* file transfer manager */ GabbleFtManager *ft_manager; #endif /* PEP */ WockyPepService *pep_nick; WockyPepService *pep_location; WockyPepService *pep_olpc_buddy_props; WockyPepService *pep_olpc_activities; WockyPepService *pep_olpc_current_act; WockyPepService *pep_olpc_act_props; /* Sidecars */ /* gchar *interface → GabbleSidecar */ GHashTable *sidecars; /* gchar *interface → GList<DBusGMethodInvocation> */ GHashTable *pending_sidecars; /* Mail Notification */ GabbleConnectionMailNotificationPrivate *mail_priv; /* ContactInfo.SupportedFields, or NULL to use the generic one */ GPtrArray *contact_info_fields; GabbleConnectionPrivate *priv; }; typedef enum { GABBLE_CONNECTION_ALIAS_NONE = 0, GABBLE_CONNECTION_ALIAS_FROM_JID, GABBLE_CONNECTION_ALIAS_FROM_VCARD, GABBLE_CONNECTION_ALIAS_FROM_MUC_RESOURCE, GABBLE_CONNECTION_ALIAS_FROM_CONNMGR, GABBLE_CONNECTION_ALIAS_FROM_PRESENCE, GABBLE_CONNECTION_ALIAS_FROM_ROSTER } GabbleConnectionAliasSource; WockyPorter *gabble_connection_dup_porter (GabbleConnection *conn); gboolean _gabble_connection_set_properties_from_account ( GabbleConnection *conn, const gchar *account, GError **error); gboolean _gabble_connection_send (GabbleConnection *conn, WockyStanza *msg, GError **error); gboolean _gabble_connection_send_with_reply (GabbleConnection *conn, WockyStanza *msg, GabbleConnectionMsgReplyFunc reply_func, GObject *object, gpointer user_data, GError **error); void _gabble_connection_acknowledge_set_iq (GabbleConnection *conn, WockyStanza *iq); void gabble_connection_update_last_use (GabbleConnection *conn); const char *_gabble_connection_find_conference_server (GabbleConnection *); gchar *gabble_connection_get_canonical_room_name (GabbleConnection *conn, const gchar *jid); void gabble_connection_ensure_capabilities (GabbleConnection *self, const GabbleCapabilitySet *ensured); gboolean gabble_connection_send_presence (GabbleConnection *conn, WockyStanzaSubType sub_type, const gchar *contact, const gchar *status, GError **error); gboolean gabble_connection_send_capabilities (GabbleConnection *self, const gchar *recipient, GError **error); gboolean gabble_connection_request_decloak (GabbleConnection *self, const gchar *to, const gchar *reason, GError **error); void gabble_connection_fill_in_caps (GabbleConnection *self, WockyStanza *presence_message); gboolean _gabble_connection_invisible_privacy_list_set_active ( GabbleConnection *self, gboolean active, GError **error); const gchar **gabble_connection_get_implemented_interfaces (void); const gchar **gabble_connection_get_guaranteed_interfaces (void); /* extern only for the benefit of the unit tests */ void _gabble_connection_create_handle_repos (TpBaseConnection *conn, TpHandleRepoIface *repos[NUM_TP_HANDLE_TYPES]); /* For unit tests only */ void gabble_connection_set_disco_reply_timeout (guint timeout); G_END_DECLS #endif /* #ifndef __GABBLE_CONNECTION_H__*/ �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������telepathy-gabble-0.18.2/src/conn-mail-notif.c�������������������������������������������������������0000644�0001750�0001750�00000056377�12200204333�020751� 0����������������������������������������������������������������������������������������������������ustar�00smcv����������������������������smcv����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * conn-mail-notif - Gabble mail notification interface * Copyright (C) 2009-2010 Collabora Ltd. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 */ /* This is implementation of Gmail Notification protocol as documented at * http://code.google.com/apis/talk/jep_extensions/gmail.html * This is the only protocol supported at the moment. To add new protocol, * one would have to split the google specific parts wich are the * update_unread_mails() function and the new-mail signal on google xml * namespace. The data structure and suscription mechanism shall remain across * protocols. */ #include "config.h" #include "conn-mail-notif.h" #include <string.h> #include <dbus/dbus-glib-lowlevel.h> #include <telepathy-glib/telepathy-glib.h> #include <telepathy-glib/telepathy-glib-dbus.h> #include <wocky/wocky.h> #define DEBUG_FLAG GABBLE_DEBUG_MAIL_NOTIF #include "connection.h" #include "debug.h" #include "namespaces.h" #include "util.h" /* The Google server stops pushing <new-mail> updates for the periode of * POLL_DURATION seconds. To ensure that MailNotification remains accurate, * we manually update every POLL_DELAY second the mail information. */ #define POLL_DELAY 5 #define POLL_DURATION 60 enum { PROP_MAIL_NOTIFICATION_FLAGS, PROP_UNREAD_MAIL_COUNT, PROP_UNREAD_MAILS, PROP_MAIL_ADDRESS, NUM_OF_PROP, }; struct _GabbleConnectionMailNotificationPrivate { gboolean interested; gchar *inbox_url; GHashTable *unread_mails; guint unread_count; guint new_mail_handler_id; guint poll_timeout_id; guint poll_count; GList *inbox_url_requests; /* list of DBusGMethodInvocation */ gboolean should_set_google_settings; }; static void update_unread_mails (GabbleConnection *conn); static void return_from_request_inbox_url (GabbleConnection *conn) { GValueArray *result = NULL; GPtrArray *empty_array = NULL; GError *error = NULL; GList *it; GabbleConnectionMailNotificationPrivate *priv = conn->mail_priv; if (priv->inbox_url != NULL && priv->inbox_url[0] == '\0') { error = g_error_new (TP_ERROR, TP_ERROR_NETWORK_ERROR, "Server did not provide base URL."); } else if (priv->inbox_url == NULL) { error = g_error_new (TP_ERROR, TP_ERROR_DISCONNECTED, "Connection was disconnected during request."); } else { empty_array = g_ptr_array_new (); result = tp_value_array_build (3, G_TYPE_STRING, priv->inbox_url, G_TYPE_UINT, TP_HTTP_METHOD_GET, TP_ARRAY_TYPE_HTTP_POST_DATA_LIST, empty_array, G_TYPE_INVALID); } it = priv->inbox_url_requests; while (it != NULL) { DBusGMethodInvocation *context = it->data; if (error != NULL) dbus_g_method_return_error (context, error); else tp_svc_connection_interface_mail_notification_return_from_request_inbox_url ( context, result); it = g_list_next (it); } if (error == NULL) { g_value_array_free (result); g_ptr_array_unref (empty_array); } else { g_error_free (error); } g_list_free (priv->inbox_url_requests); priv->inbox_url_requests = NULL; } static inline gboolean check_supported_or_dbus_return (GabbleConnection *conn, DBusGMethodInvocation *context) { TpBaseConnection *base = TP_BASE_CONNECTION (conn); if (tp_base_connection_get_status (base) != TP_CONNECTION_STATUS_CONNECTED) { GError e = { TP_ERROR, TP_ERROR_DISCONNECTED, "Not connected" }; dbus_g_method_return_error (context, &e); return TRUE; } if (!(conn->features & GABBLE_CONNECTION_FEATURES_GOOGLE_MAIL_NOTIFY)) { tp_dbus_g_method_return_not_implemented (context); return TRUE; } return FALSE; } static void gabble_mail_notification_request_inbox_url ( TpSvcConnectionInterfaceMailNotification *iface, DBusGMethodInvocation *context) { GabbleConnection *conn = GABBLE_CONNECTION (iface); GabbleConnectionMailNotificationPrivate *priv = conn->mail_priv; if (check_supported_or_dbus_return (conn, context)) return; /* TODO Make sure we don't have to authenticate again */ priv->inbox_url_requests = g_list_append (priv->inbox_url_requests, context); if (priv->inbox_url != NULL) return_from_request_inbox_url (conn); } static void gabble_mail_notification_request_mail_url ( TpSvcConnectionInterfaceMailNotification *iface, const gchar *in_id, const GValue *in_url_data, DBusGMethodInvocation *context) { GabbleConnection *conn = GABBLE_CONNECTION (iface); GabbleConnectionMailNotificationPrivate *priv = conn->mail_priv; if (check_supported_or_dbus_return (conn, context)) return; /* TODO Make sure we don't have to authenticate again */ if (priv->inbox_url != NULL && priv->inbox_url[0] != 0) { GValueArray *result; gchar *url = NULL; GPtrArray *empty_array; /* IDs are decimal on the XMPP side and hexadecimal on the wemail side. */ guint64 tid = g_ascii_strtoull (in_id, NULL, 0); url = g_strdup_printf ("%s/#inbox/%" G_GINT64_MODIFIER "x", priv->inbox_url, tid); empty_array = g_ptr_array_new (); result = tp_value_array_build (3, G_TYPE_STRING, url ? url : "", G_TYPE_UINT, TP_HTTP_METHOD_GET, TP_ARRAY_TYPE_HTTP_POST_DATA_LIST, empty_array, G_TYPE_INVALID); tp_svc_connection_interface_mail_notification_return_from_request_mail_url ( context, result); g_value_array_free (result); g_ptr_array_unref (empty_array); g_free (url); } else { GError error = { TP_ERROR, TP_ERROR_NETWORK_ERROR, "Failed to retrieve URL from server."}; dbus_g_method_return_error (context, &error); } } static gboolean sender_each (WockyNode *node, gpointer user_data) { GPtrArray *senders = user_data; if (!tp_strdiff ("1", wocky_node_get_attribute (node, "unread"))) { GValueArray *sender; const gchar *name; const gchar *address; name = wocky_node_get_attribute (node, "name"); address = wocky_node_get_attribute (node, "address"); sender = tp_value_array_build (2, G_TYPE_STRING, name ? name : "", G_TYPE_STRING, address ? address : "", G_TYPE_INVALID); g_ptr_array_add (senders, sender); } return TRUE; } static gboolean handle_senders (WockyNode *parent_node, GHashTable *mail) { gboolean dirty = FALSE; WockyNode *node; node = wocky_node_get_child (parent_node, "senders"); if (node != NULL) { GType addr_list_type = TP_ARRAY_TYPE_MAIL_ADDRESS_LIST; GPtrArray *senders, *old_senders; senders = g_ptr_array_new (); wocky_node_each_child (node, sender_each, senders); old_senders = tp_asv_get_boxed (mail, "senders", addr_list_type); if (old_senders == NULL || senders->len != old_senders->len) dirty = TRUE; tp_asv_take_boxed (mail, "senders", addr_list_type, senders); } return dirty; } static gboolean handle_subject (WockyNode *parent_node, GHashTable *mail) { gboolean dirty = FALSE; WockyNode *node; node = wocky_node_get_child (parent_node, "subject"); if (node != NULL) { if (tp_strdiff (node->content, tp_asv_get_string (mail, "subject"))) { dirty = TRUE; tp_asv_set_string (mail, "subject", node->content); } } return dirty; } static gboolean handle_snippet (WockyNode *parent_node, GHashTable *mail) { gboolean dirty = FALSE; WockyNode *node; node = wocky_node_get_child (parent_node, "snippet"); if (node != NULL) { if (tp_strdiff (node->content, tp_asv_get_string (mail, "content"))) { dirty = TRUE; tp_asv_set_boolean (mail, "truncated", TRUE); tp_asv_set_string (mail, "content", node->content); } } return dirty; } /* Structure used has user_data mail_thread_info_each callback */ typedef struct { GabbleConnection *conn; /* stolen from conn -> unread_mails, the left items in this is * represent the removed emails */ GHashTable *old_mails; GPtrArray *mails_added; } MailThreadCollector; static gboolean mail_thread_info_each (WockyNode *node, gpointer user_data) { MailThreadCollector *collector = user_data; if (!tp_strdiff (node->name, "mail-thread-info")) { GHashTable *mail = NULL; const gchar *val_str; gchar *tid; gboolean dirty = FALSE; val_str = wocky_node_get_attribute (node, "tid"); /* We absolutly need an ID */ if (val_str == NULL) return TRUE; tid = g_strdup (val_str); if (collector->old_mails != NULL) { mail = g_hash_table_lookup (collector->old_mails, tid); g_hash_table_steal (collector->old_mails, tid); } if (mail == NULL) { mail = tp_asv_new ("id", G_TYPE_STRING, tid, "url-data", G_TYPE_STRING, "", NULL); dirty = TRUE; } val_str = wocky_node_get_attribute (node, "date"); if (val_str != NULL) { gint64 date; date = (g_ascii_strtoll (val_str, NULL, 0) / 1000l); if (date != tp_asv_get_int64 (mail, "received-timestamp", NULL)) dirty = TRUE; tp_asv_set_int64 (mail, "received-timestamp", date); } if (handle_senders (node, mail)) dirty = TRUE; if (handle_subject (node, mail)) dirty = TRUE; if (handle_snippet (node, mail)) dirty = TRUE; /* gives tid ownership to unread_mails hash table */ g_hash_table_insert (collector->conn->mail_priv->unread_mails, tid, mail); if (dirty) g_ptr_array_add (collector->mails_added, mail); } return TRUE; } static void store_unread_mails (GabbleConnection *conn, WockyNode *mailbox) { GabbleConnectionMailNotificationPrivate *priv = conn->mail_priv; GHashTableIter iter; GPtrArray *mails_removed; MailThreadCollector collector; const gchar *url, *unread_count; collector.conn = conn; collector.old_mails = priv->unread_mails; priv->unread_mails = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify) g_hash_table_unref); collector.mails_added = g_ptr_array_new (); url = wocky_node_get_attribute (mailbox, "url"); g_free (priv->inbox_url); /* Use empty string to differentiate pending request from server failing to provide an URL.*/ if (url != NULL) priv->inbox_url = g_strdup (url); else priv->inbox_url = g_strdup (""); /* Store new mails */ wocky_node_each_child (mailbox, mail_thread_info_each, &collector); /* Generate the list of removed thread IDs */ mails_removed = g_ptr_array_new_with_free_func (g_free); if (collector.old_mails != NULL) { gpointer key; g_hash_table_iter_init (&iter, collector.old_mails); while (g_hash_table_iter_next (&iter, &key, NULL)) { gchar *tid = key; g_ptr_array_add (mails_removed, g_strdup (tid)); } g_hash_table_unref (collector.old_mails); } g_ptr_array_add (mails_removed, NULL); unread_count = wocky_node_get_attribute (mailbox, "total-matched"); if (unread_count != NULL) priv->unread_count = (guint)g_ascii_strtoll (unread_count, NULL, 0); else priv->unread_count = g_hash_table_size (priv->unread_mails); tp_svc_connection_interface_mail_notification_emit_unread_mails_changed ( conn, priv->unread_count, collector.mails_added, (const char **)mails_removed->pdata); g_ptr_array_unref (collector.mails_added); g_ptr_array_unref (mails_removed); } static void set_settings_cb (GObject *source_object, GAsyncResult *res, gpointer user_data) { GError *error = NULL; WockyPorter *porter = WOCKY_PORTER (source_object); WockyStanza *reply = wocky_porter_send_iq_finish (porter, res, &error); if (reply == NULL || wocky_stanza_extract_errors (reply, NULL, &error, NULL, NULL)) { DEBUG ("Failed to set google user settings: %s", error->message); g_error_free (error); } tp_clear_object (&reply); } static void query_unread_mails_cb (GObject *source_object, GAsyncResult *res, gpointer user_data) { GError *error = NULL; WockyPorter *porter = WOCKY_PORTER (source_object); WockyStanza *reply = wocky_porter_send_iq_finish (porter, res, &error); GabbleConnection *conn = GABBLE_CONNECTION (user_data); if (reply == NULL || wocky_stanza_extract_errors (reply, NULL, &error, NULL, NULL)) { DEBUG ("Failed retreive unread emails information: %s", error->message); g_error_free (error); } else if (conn->mail_priv->interested) { WockyNode *node = wocky_node_get_child ( wocky_stanza_get_top_node (reply), "mailbox"); DEBUG ("Got unread mail details"); if (node != NULL) store_unread_mails (conn, node); } /* else we no longer care about unread mail, so ignore it */ tp_clear_object (&reply); return_from_request_inbox_url (conn); } static void update_unread_mails (GabbleConnection *conn) { TpBaseConnection *base = TP_BASE_CONNECTION (conn); WockyStanza *query; WockyPorter *porter = wocky_session_get_porter (conn->session); if (tp_base_connection_get_status (base) != TP_CONNECTION_STATUS_CONNECTED) return; if (!(conn->features & GABBLE_CONNECTION_FEATURES_GOOGLE_MAIL_NOTIFY)) return; DEBUG ("Updating unread mails information"); query = wocky_stanza_build (WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_GET, NULL, NULL, '(', "query", ':', NS_GOOGLE_MAIL_NOTIFY, ')', NULL); wocky_porter_send_iq_async (porter, query, NULL, query_unread_mails_cb, conn); g_object_unref (query); } static gboolean poll_unread_mails_cb (gpointer user_data) { GabbleConnection *conn = GABBLE_CONNECTION (user_data); GabbleConnectionMailNotificationPrivate *priv = conn->mail_priv; if (priv->poll_count * POLL_DELAY >= POLL_DURATION) { DEBUG ("%i seconds since <new-mail>, stopping polling", priv->poll_count * POLL_DELAY); priv->poll_timeout_id = 0; priv->poll_count = 0; return FALSE; } priv->poll_count++; /* When no subscriber, keep counting time, but don't actually update the * data since nobody would care about it */ if (priv->interested) { update_unread_mails (conn); DEBUG ("%i seconds since <new-mail>, still polling", priv->poll_count * POLL_DELAY); } return TRUE; } static gboolean new_mail_handler (WockyPorter *porter, WockyStanza *stanza, gpointer user_data) { GabbleConnection *conn = GABBLE_CONNECTION (user_data); if (conn->mail_priv->interested) { DEBUG ("Got Google <new-mail> notification"); update_unread_mails (conn); conn->mail_priv->poll_count = 0; if (conn->mail_priv->poll_timeout_id == 0) { DEBUG ("Starting to poll mail for next %i seconds", POLL_DURATION); conn->mail_priv->poll_timeout_id = g_timeout_add_seconds ( POLL_DELAY, (GSourceFunc) poll_unread_mails_cb, conn); } } return TRUE; } /* Make sure google knows we want mail notifications. According to * Google clients should set 'mailnotifications' to true when needed * but never to false, for compatibility reasons: * https://code.google.com/apis/talk/jep_extensions/usersettings.html#3 */ static void ensure_google_settings (GabbleConnection *self) { TpBaseConnection *base = TP_BASE_CONNECTION (self); WockyStanza *query; WockyPorter *porter; if (!self->mail_priv->should_set_google_settings) return; if (tp_base_connection_get_status (base) != TP_CONNECTION_STATUS_CONNECTED) return; porter = wocky_session_get_porter (self->session); query = wocky_stanza_build (WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_SET, NULL, NULL, '@', "id", "user-setting-3", '(', "usersetting", ':', NS_GOOGLE_SETTING, '(', "mailnotifications", '@', "value", "true", ')', ')', NULL); wocky_porter_send_iq_async (porter, query, NULL, set_settings_cb, self); self->mail_priv->should_set_google_settings = FALSE; g_object_unref (query); } static void connection_status_changed (GabbleConnection *conn, TpConnectionStatus status, TpConnectionStatusReason reason, gpointer user_data) { if (status == TP_CONNECTION_STATUS_CONNECTED && (conn->features & GABBLE_CONNECTION_FEATURES_GOOGLE_MAIL_NOTIFY)) { DEBUG ("Connected, registering Google 'new-mail' notification"); conn->mail_priv->new_mail_handler_id = wocky_c2s_porter_register_handler_from_server ( WOCKY_C2S_PORTER (wocky_session_get_porter (conn->session)), WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_SET, WOCKY_PORTER_HANDLER_PRIORITY_NORMAL, new_mail_handler, conn, '(', "new-mail", ':', NS_GOOGLE_MAIL_NOTIFY, ')', NULL); if (conn->features & GABBLE_CONNECTION_FEATURES_GOOGLE_SETTING) conn->mail_priv->should_set_google_settings = TRUE; if (conn->mail_priv->interested) { DEBUG ("Someone is already interested in MailNotification"); update_unread_mails (conn); ensure_google_settings (conn); } } } /* called on transition from 0 to 1 interested clients */ static void mail_clients_interested_cb (GabbleConnection *self, const gchar *token G_GNUC_UNUSED, gpointer nil G_GNUC_UNUSED) { DEBUG ("The first client is interested"); self->mail_priv->interested = TRUE; update_unread_mails (self); ensure_google_settings (self); } /* called on transition from 1 to 0 interested clients */ static void mail_clients_uninterested_cb (GabbleConnection *self, const gchar *token G_GNUC_UNUSED, gpointer nil G_GNUC_UNUSED) { DEBUG ("All clients lost interest, cleaning up"); self->mail_priv->interested = FALSE; self->mail_priv->unread_count = 0; tp_clear_pointer (&self->mail_priv->inbox_url, g_free); return_from_request_inbox_url (self); tp_clear_pointer (&self->mail_priv->unread_mails, g_hash_table_unref); } void conn_mail_notif_init (GabbleConnection *conn) { conn->mail_priv = g_slice_new0 (GabbleConnectionMailNotificationPrivate); g_signal_connect (conn, "status-changed", G_CALLBACK (connection_status_changed), conn); g_signal_connect (conn, "clients-interested::" TP_IFACE_CONNECTION_INTERFACE_MAIL_NOTIFICATION, G_CALLBACK (mail_clients_interested_cb), NULL); g_signal_connect (conn, "clients-uninterested::" TP_IFACE_CONNECTION_INTERFACE_MAIL_NOTIFICATION, G_CALLBACK (mail_clients_uninterested_cb), NULL); } void conn_mail_notif_dispose (GabbleConnection *conn) { GabbleConnectionMailNotificationPrivate *priv = conn->mail_priv; if (priv == NULL) return; tp_clear_pointer (&priv->inbox_url, g_free); return_from_request_inbox_url (conn); tp_clear_pointer (&priv->unread_mails, g_hash_table_unref); priv->unread_count = 0; if (priv->new_mail_handler_id != 0) { WockyPorter *porter = wocky_session_get_porter (conn->session); wocky_porter_unregister_handler (porter, priv->new_mail_handler_id); priv->new_mail_handler_id = 0; } if (priv->poll_timeout_id != 0) g_source_remove (priv->poll_timeout_id); g_slice_free (GabbleConnectionMailNotificationPrivate, priv); conn->mail_priv = NULL; } void conn_mail_notif_iface_init (gpointer g_iface, gpointer iface_data) { TpSvcConnectionInterfaceMailNotificationClass *klass = g_iface; #define IMPLEMENT(x) tp_svc_connection_interface_mail_notification_implement_##x (\ klass, gabble_mail_notification_##x) IMPLEMENT (request_inbox_url); IMPLEMENT (request_mail_url); #undef IMPLEMENT } static GPtrArray * get_unread_mails (GabbleConnection *conn) { GabbleConnectionMailNotificationPrivate *priv = conn->mail_priv; GPtrArray *mails = g_ptr_array_new (); GHashTableIter iter; gpointer value; if (priv->unread_mails != NULL) { g_hash_table_iter_init (&iter, priv->unread_mails); while (g_hash_table_iter_next (&iter, NULL, &value)) { GHashTable *mail = value; g_ptr_array_add (mails, mail); } } return mails; } void conn_mail_notif_properties_getter (GObject *object, GQuark interface, GQuark name, GValue *value, gpointer getter_data) { static GQuark prop_quarks[NUM_OF_PROP] = {0}; GabbleConnection *conn = GABBLE_CONNECTION (object); GabbleConnectionMailNotificationPrivate *priv = conn->mail_priv; if (G_UNLIKELY (prop_quarks[0] == 0)) { prop_quarks[PROP_MAIL_NOTIFICATION_FLAGS] = g_quark_from_static_string ("MailNotificationFlags"); prop_quarks[PROP_UNREAD_MAIL_COUNT] = g_quark_from_static_string ("UnreadMailCount"); prop_quarks[PROP_UNREAD_MAILS] = g_quark_from_static_string ("UnreadMails"); prop_quarks[PROP_MAIL_ADDRESS] = g_quark_from_static_string ("MailAddress"); } DEBUG ("MailNotification get property %s", g_quark_to_string (name)); if (name == prop_quarks[PROP_MAIL_NOTIFICATION_FLAGS]) { if (conn->features & GABBLE_CONNECTION_FEATURES_GOOGLE_MAIL_NOTIFY) g_value_set_uint (value, TP_MAIL_NOTIFICATION_FLAG_SUPPORTS_UNREAD_MAIL_COUNT | TP_MAIL_NOTIFICATION_FLAG_SUPPORTS_UNREAD_MAILS | TP_MAIL_NOTIFICATION_FLAG_SUPPORTS_REQUEST_INBOX_URL | TP_MAIL_NOTIFICATION_FLAG_SUPPORTS_REQUEST_MAIL_URL | TP_MAIL_NOTIFICATION_FLAG_THREAD_BASED ); else g_value_set_uint (value, 0); } else if (name == prop_quarks[PROP_UNREAD_MAIL_COUNT]) { g_value_set_uint (value, priv->unread_count); } else if (name == prop_quarks[PROP_UNREAD_MAILS]) { GPtrArray *mails = get_unread_mails (conn); g_value_set_boxed (value, mails); g_ptr_array_unref (mails); } else if (name == prop_quarks[PROP_MAIL_ADDRESS]) { TpBaseConnection *base = TP_BASE_CONNECTION (object); TpHandleRepoIface *contact_handles = tp_base_connection_get_handles (base, TP_HANDLE_TYPE_CONTACT); TpHandle self = tp_base_connection_get_self_handle (base); const gchar *bare_jid = tp_handle_inspect (contact_handles, self); /* After some testing I found that the bare jid (username@stream_server) * always represent the e-mail address on Google account. */ g_value_set_string (value, bare_jid); } else { g_assert_not_reached (); } } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������telepathy-gabble-0.18.2/src/conn-mail-notif.h�������������������������������������������������������0000644�0001750�0001750�00000002513�12200204333�020735� 0����������������������������������������������������������������������������������������������������ustar�00smcv����������������������������smcv����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * conn-mail-notif.h - Header for Gabble connection mail notification interface * Copyright (C) 2009 Collabora Ltd. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 __CONN_EMAIL_NOTIF_H__ #define __CONN_EMAIL_NOTIF_H__ #include <glib-object.h> #include "connection.h" G_BEGIN_DECLS void conn_mail_notif_init (GabbleConnection *conn); void conn_mail_notif_dispose (GabbleConnection *conn); void conn_mail_notif_iface_init (gpointer g_iface, gpointer iface_data); void conn_mail_notif_properties_getter (GObject *object, GQuark interface, GQuark name, GValue *value, gpointer getter_data); G_END_DECLS #endif /* __CONN_EMAIL_NOTIF_H__ */ �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������telepathy-gabble-0.18.2/src/conn-util.c�������������������������������������������������������������0000644�0001750�0001750�00000006307�12200204333�017653� 0����������������������������������������������������������������������������������������������������ustar�00smcv����������������������������smcv����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * conn-util.c - Header for Gabble connection kitchen-sink code. * Copyright (C) 2010 Collabora Ltd. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 "config.h" #include "conn-util.h" #include <gabble/gabble.h> #define DEBUG_FLAG GABBLE_DEBUG_CONNECTION #include "debug.h" #include "namespaces.h" #include "util.h" #include <wocky/wocky.h> static void conn_util_send_iq_cb (GObject *source_object, GAsyncResult *res, gpointer user_data) { WockyPorter *porter = WOCKY_PORTER (source_object); WockyStanza *reply; GSimpleAsyncResult *result = G_SIMPLE_ASYNC_RESULT (user_data); GError *error = NULL; reply = wocky_porter_send_iq_finish (porter, res, &error); if (reply != NULL) { g_simple_async_result_set_op_res_gpointer (result, reply, (GDestroyNotify) g_object_unref); } else { g_simple_async_result_set_from_error (result, error); g_clear_error (&error); } g_simple_async_result_complete (result); g_object_unref (result); } void conn_util_send_iq_async (GabbleConnection *self, WockyStanza *stanza, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { WockyPorter *porter = wocky_session_get_porter (self->session); GSimpleAsyncResult *result = g_simple_async_result_new (G_OBJECT (self), callback, user_data, conn_util_send_iq_async); wocky_porter_send_iq_async (porter, stanza, cancellable, conn_util_send_iq_cb, result); } gboolean conn_util_send_iq_finish (GabbleConnection *self, GAsyncResult *result, WockyStanza **response, GError **error) { GSimpleAsyncResult *res; WockyStanza *resp; GError *err = NULL; g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (self), conn_util_send_iq_async), FALSE); res = (GSimpleAsyncResult *) result; resp = g_simple_async_result_get_op_res_gpointer (res); if (g_simple_async_result_propagate_error (res, &err) || wocky_stanza_extract_errors (resp, NULL, &err, NULL, NULL)) { gabble_set_tp_error_from_wocky (err, error); g_error_free (err); return FALSE; } if (response != NULL) *response = g_object_ref (resp); return TRUE; } const gchar * conn_util_get_bare_self_jid (GabbleConnection *conn) { TpBaseConnection *base = TP_BASE_CONNECTION (conn); TpHandleRepoIface *contact_handles = tp_base_connection_get_handles (base, TP_HANDLE_TYPE_CONTACT); TpHandle self = tp_base_connection_get_self_handle (base); return tp_handle_inspect (contact_handles, self); } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������telepathy-gabble-0.18.2/src/conn-util.h�������������������������������������������������������������0000644�0001750�0001750�00000002546�12200204333�017661� 0����������������������������������������������������������������������������������������������������ustar�00smcv����������������������������smcv����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * conn-util.h - Header for Gabble connection kitchen-sink code. * Copyright (C) 2010 Collabora Ltd. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 GABBLE_CONN_UTIL_H #define GABBLE_CONN_UTIL_H #include <glib.h> #include "connection.h" G_BEGIN_DECLS void conn_util_send_iq_async (GabbleConnection *self, WockyStanza *stanza, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); gboolean conn_util_send_iq_finish (GabbleConnection *self, GAsyncResult *result, WockyStanza **response, GError **error); const gchar *conn_util_get_bare_self_jid (GabbleConnection *conn); G_END_DECLS #endif /* GABBLE_CONN_UTIL_H */ ����������������������������������������������������������������������������������������������������������������������������������������������������������telepathy-gabble-0.18.2/src/conn-sidecars.c���������������������������������������������������������0000644�0001750�0001750�00000024013�12200204333�020465� 0����������������������������������������������������������������������������������������������������ustar�00smcv����������������������������smcv����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * conn-sidecars.h - Gabble connection implementation of sidecars * Copyright © 2009 Collabora Ltd. * Copyright © 2009 Nokia Corporation * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 "config.h" #include "conn-sidecars.h" #include <telepathy-glib/telepathy-glib.h> #include "extensions/extensions.h" #define DEBUG_FLAG GABBLE_DEBUG_CONNECTION #include "debug.h" #include "plugin-loader.h" #include "gabble/sidecar.h" static void sidecars_conn_status_changed_cb ( GabbleConnection *conn, guint status, guint reason, gpointer unused); void conn_sidecars_init (GabbleConnection *conn) { conn->sidecars = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref); conn->pending_sidecars = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify) g_list_free); g_signal_connect (conn, "status-changed", (GCallback) sidecars_conn_status_changed_cb, NULL); } void conn_sidecars_dispose (GabbleConnection *conn) { g_warn_if_fail (g_hash_table_size (conn->sidecars) == 0); tp_clear_pointer (&conn->sidecars, g_hash_table_unref); g_warn_if_fail (g_hash_table_size (conn->pending_sidecars) == 0); tp_clear_pointer (&conn->pending_sidecars, g_hash_table_unref); } static gchar * make_sidecar_path ( GabbleConnection *conn, const gchar *sidecar_iface) { TpBaseConnection *base = TP_BASE_CONNECTION (conn); return g_strdelimit ( g_strdup_printf ("%s/Sidecar/%s", tp_base_connection_get_object_path (base), sidecar_iface), ".", '/'); } /** * connection_install_sidecar: * * Registers @sidecar on the bus, and returns its object path. */ static gchar * connection_install_sidecar ( GabbleConnection *conn, GabbleSidecar *sidecar, const gchar *sidecar_iface) { TpDBusDaemon *bus = tp_base_connection_get_dbus_daemon ( (TpBaseConnection *) conn); gchar *path = make_sidecar_path (conn, sidecar_iface); tp_dbus_daemon_register_object (bus, path, G_OBJECT (sidecar)); g_hash_table_insert (conn->sidecars, g_strdup (sidecar_iface), g_object_ref (sidecar)); return path; } typedef struct { GabbleConnection *conn; gchar *sidecar_iface; } Grr; static Grr * grr_new ( GabbleConnection *conn, const gchar *sidecar_iface) { Grr *grr = g_slice_new (Grr); grr->conn = g_object_ref (conn); grr->sidecar_iface = g_strdup (sidecar_iface); return grr; } static void grr_free (Grr *grr) { g_object_unref (grr->conn); g_free (grr->sidecar_iface); g_slice_free (Grr, grr); } static void create_sidecar_cb ( GObject *loader_obj, GAsyncResult *result, gpointer user_data) { GabblePluginLoader *loader = GABBLE_PLUGIN_LOADER (loader_obj); Grr *ctx = user_data; GabbleConnection *conn = ctx->conn; const gchar *sidecar_iface = ctx->sidecar_iface; GabbleSidecar *sidecar; GList *contexts; GError *error = NULL; sidecar = gabble_plugin_loader_create_sidecar_finish (loader, result, &error); contexts = g_hash_table_lookup (conn->pending_sidecars, sidecar_iface); if (contexts == NULL) { /* We never use the empty list as a value in pending_sidecars, so this * must mean we've disconnected and already returned. Jettison the * sidecar! */ DEBUG ("creating sidecar %s %s after connection closed; jettisoning!", sidecar_iface, (sidecar != NULL ? "succeeded" : "failed")); goto out; } if (sidecar != NULL) { const gchar *actual_iface = gabble_sidecar_get_interface (sidecar); if (tp_strdiff (ctx->sidecar_iface, actual_iface)) { /* TODO: maybe this lives in the loader? It knows what the plugin is * called. */ g_set_error (&error, TP_ERROR, TP_ERROR_NOT_IMPLEMENTED, "A buggy plugin created a %s sidecar when asked to create %s", actual_iface, ctx->sidecar_iface); } } else /* sidecar == NULL */ { /* If creating the sidecar failed, 'error' should have been set */ g_return_if_fail (error != NULL); } if (error == NULL) { gchar *path = connection_install_sidecar (ctx->conn, sidecar, ctx->sidecar_iface); GHashTable *props = gabble_sidecar_get_immutable_properties (sidecar); GList *l; for (l = contexts; l != NULL; l = l->next) gabble_svc_connection_future_return_from_ensure_sidecar (l->data, path, props); g_hash_table_unref (props); g_free (path); } else { g_list_foreach (contexts, (GFunc) dbus_g_method_return_error, error); } g_hash_table_remove (ctx->conn->pending_sidecars, ctx->sidecar_iface); out: tp_clear_object (&sidecar); g_clear_error (&error); grr_free (ctx); } static void gabble_connection_ensure_sidecar ( GabbleSvcConnectionFUTURE *iface, const gchar *sidecar_iface, DBusGMethodInvocation *context) { GabbleConnection *conn = GABBLE_CONNECTION (iface); TpBaseConnection *base = TP_BASE_CONNECTION (conn); GabbleSidecar *sidecar; gpointer key, value; GError *error = NULL; if (tp_base_connection_is_destroyed (base)) { GError e = { TP_ERROR, TP_ERROR_DISCONNECTED, "This connection has already disconnected" }; DEBUG ("already disconnected, declining request for %s", sidecar_iface); dbus_g_method_return_error (context, &e); return; } if (!tp_dbus_check_valid_interface_name (sidecar_iface, &error)) { error->domain = TP_ERROR; error->code = TP_ERROR_INVALID_ARGUMENT; DEBUG ("%s is malformed: %s", sidecar_iface, error->message); dbus_g_method_return_error (context, error); g_clear_error (&error); return; } sidecar = g_hash_table_lookup (conn->sidecars, sidecar_iface); if (sidecar != NULL) { gchar *path = make_sidecar_path (conn, sidecar_iface); GHashTable *props = gabble_sidecar_get_immutable_properties (sidecar); DEBUG ("sidecar %s already exists at %s", sidecar_iface, path); gabble_svc_connection_future_return_from_ensure_sidecar (context, path, props); g_free (path); g_hash_table_unref (props); return; } if (g_hash_table_lookup_extended (conn->pending_sidecars, sidecar_iface, &key, &value)) { GList *contexts = value; DEBUG ("already awaiting %s, joining a queue of %u", sidecar_iface, g_list_length (contexts)); contexts = g_list_prepend (contexts, context); g_hash_table_steal (conn->pending_sidecars, key); g_hash_table_insert (conn->pending_sidecars, key, contexts); return; } DEBUG ("enqueuing first request for %s", sidecar_iface); g_hash_table_insert (conn->pending_sidecars, g_strdup (sidecar_iface), g_list_prepend (NULL, context)); if (tp_base_connection_get_status (base) == TP_CONNECTION_STATUS_CONNECTED) { GabblePluginLoader *loader = gabble_plugin_loader_dup (); DEBUG ("requesting %s from the plugin loader", sidecar_iface); gabble_plugin_loader_create_sidecar (loader, sidecar_iface, conn, conn->session, create_sidecar_cb, grr_new (conn, sidecar_iface)); g_object_unref (loader); } else { DEBUG ("not yet connected; waiting."); } } static void sidecars_conn_status_changed_cb ( GabbleConnection *conn, guint status, guint reason, gpointer unused) { TpDBusDaemon *bus = tp_base_connection_get_dbus_daemon ( (TpBaseConnection *) conn); GHashTableIter iter; gpointer key, value; if (status == TP_CONNECTION_STATUS_DISCONNECTED) { g_hash_table_iter_init (&iter, conn->sidecars); while (g_hash_table_iter_next (&iter, NULL, &value)) { DEBUG ("removing %s from the bus", gabble_sidecar_get_interface (value)); tp_dbus_daemon_unregister_object (bus, G_OBJECT (value)); } g_hash_table_iter_init (&iter, conn->pending_sidecars); while (g_hash_table_iter_next (&iter, &key, &value)) { const gchar *sidecar_iface = key; GList *contexts = value; GError *error = g_error_new (TP_ERROR, TP_ERROR_CANCELLED, "Disconnected before %s could be created", sidecar_iface); DEBUG ("failing all %u requests for %s", g_list_length (contexts), sidecar_iface); g_list_foreach (contexts, (GFunc) dbus_g_method_return_error, error); g_error_free (error); } g_hash_table_remove_all (conn->sidecars); g_hash_table_remove_all (conn->pending_sidecars); } else if (status == TP_CONNECTION_STATUS_CONNECTED) { GabblePluginLoader *loader = gabble_plugin_loader_dup (); DEBUG ("connected; requesting sidecars from plugins"); g_hash_table_iter_init (&iter, conn->pending_sidecars); while (g_hash_table_iter_next (&iter, &key, NULL)) { const gchar *sidecar_iface = key; DEBUG ("requesting %s from the plugin loader", sidecar_iface); gabble_plugin_loader_create_sidecar (loader, sidecar_iface, conn, conn->session, create_sidecar_cb, grr_new (conn, sidecar_iface)); } g_object_unref (loader); } } void conn_future_iface_init ( gpointer g_iface, gpointer iface_data) { GabbleSvcConnectionFUTUREClass *klass = g_iface; #define IMPLEMENT(x) \ gabble_svc_connection_future_implement_##x (\ klass, gabble_connection_##x) IMPLEMENT (ensure_sidecar); #undef IMPLEMENT } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������telepathy-gabble-0.18.2/src/conn-sidecars.h���������������������������������������������������������0000644�0001750�0001750�00000002301�12200204333�020466� 0����������������������������������������������������������������������������������������������������ustar�00smcv����������������������������smcv����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * conn-sidecars.h - Header for Gabble connection implementation of sidecars * Copyright © 2009 Collabora Ltd. * Copyright © 2009 Nokia Corporation * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 __CONN_SIDECARS_H__ #define __CONN_SIDECARS_H__ #include "connection.h" G_BEGIN_DECLS void conn_sidecars_init (GabbleConnection *conn); void conn_sidecars_dispose (GabbleConnection *conn); void conn_future_iface_init (gpointer g_iface, gpointer iface_data); G_END_DECLS #endif /* __CONN_SIDECARS_H__ */ �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������telepathy-gabble-0.18.2/src/conn-presence.c���������������������������������������������������������0000644�0001750�0001750�00000172407�12226776641�020537� 0����������������������������������������������������������������������������������������������������ustar�00smcv����������������������������smcv����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * conn-presence - Gabble connection presence interface * Copyright (C) 2005-2007 Collabora Ltd. * Copyright (C) 2005-2007 Nokia Corporation * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 "config.h" #include "conn-presence.h" #include "namespaces.h" #include <string.h> #include <stdlib.h> #include <telepathy-glib/telepathy-glib.h> #include <telepathy-glib/telepathy-glib-dbus.h> #include <wocky/wocky.h> #define DEBUG_FLAG GABBLE_DEBUG_CONNECTION #include "extensions/extensions.h" /* for Decloak */ #include "connection.h" #include "debug.h" #include "plugin-loader.h" #include "presence-cache.h" #include "presence.h" #include "roster.h" #include "conn-util.h" #include "util.h" #define GOOGLE_SHARED_STATUS_VERSION "2" typedef enum { INVISIBILITY_METHOD_NONE = 0, INVISIBILITY_METHOD_PRESENCE_INVISIBLE, /* presence type=invisible */ INVISIBILITY_METHOD_PRIVACY, INVISIBILITY_METHOD_INVISIBLE_COMMAND, INVISIBILITY_METHOD_SHARED_STATUS } InvisibilityMethod; struct _GabbleConnectionPresencePrivate { InvisibilityMethod invisibility_method; guint iq_list_push_id; gchar *invisible_list_name; /* Mapping between status "show" strings, and shared statuses */ GHashTable *shared_statuses; /* Map of presence statuses backed by privacy lists. This * will be NULL until we receive a (possibly empty) list of * all lists in get_existing_privacy_lists_cb(). * * gchar *presence_status_name → gchar *privacy_list */ GHashTable *privacy_statuses; /* Are all of the connected resources complying to version 2 */ gboolean shared_status_compat; /* Max length of status message */ gint max_status_message_length; /* Max statuses in a shared status list */ gint max_shared_statuses; /* The shared status IQ handler */ guint iq_shared_status_cb; /* The previous presence when using shared status */ GabblePresenceId previous_shared_status; }; static const TpPresenceStatusOptionalArgumentSpec gabble_status_arguments[] = { { "message", "s", NULL, NULL }, { "priority", "n", NULL, NULL }, { NULL, NULL, NULL, NULL } }; /* order must match PresenceId enum in connection.h */ /* in increasing order of presence */ static const TpPresenceStatusSpec gabble_base_statuses[] = { { "offline", TP_CONNECTION_PRESENCE_TYPE_OFFLINE, FALSE, gabble_status_arguments, NULL, NULL }, { "unknown", TP_CONNECTION_PRESENCE_TYPE_UNKNOWN, FALSE, gabble_status_arguments, NULL, NULL }, { "error", TP_CONNECTION_PRESENCE_TYPE_ERROR, FALSE, gabble_status_arguments, NULL, NULL }, { "hidden", TP_CONNECTION_PRESENCE_TYPE_HIDDEN, TRUE, gabble_status_arguments, NULL, NULL }, { "xa", TP_CONNECTION_PRESENCE_TYPE_EXTENDED_AWAY, TRUE, gabble_status_arguments, NULL, NULL }, { "away", TP_CONNECTION_PRESENCE_TYPE_AWAY, TRUE, gabble_status_arguments, NULL, NULL }, { "dnd", TP_CONNECTION_PRESENCE_TYPE_BUSY, TRUE, gabble_status_arguments, NULL, NULL }, { "available", TP_CONNECTION_PRESENCE_TYPE_AVAILABLE, TRUE, gabble_status_arguments, NULL, NULL }, { "chat", TP_CONNECTION_PRESENCE_TYPE_AVAILABLE, TRUE, gabble_status_arguments, NULL, NULL }, { NULL, 0, FALSE, NULL, NULL, NULL } }; static TpPresenceStatusSpec *gabble_statuses = NULL; /* prototypes */ static void set_xep0186_invisible_cb ( GObject *source, GAsyncResult *set_result, gpointer user_data); static void activate_current_privacy_list_cb ( GObject *source, GAsyncResult *activate_result, gpointer user_data); static void setup_invisible_privacy_list_async (GabbleConnection *self, GAsyncReadyCallback callback, gpointer user_data); static gboolean iq_privacy_list_push_cb ( WockyPorter *porter, WockyStanza *message, gpointer user_data); static void verify_invisible_privacy_list_cb ( GObject *source, GAsyncResult *verify_result, gpointer user_data); static void toggle_presence_visibility_async (GabbleConnection *self, GAsyncReadyCallback callback, gpointer user_data); static gboolean toggle_presence_visibility_finish (GabbleConnection *self, GAsyncResult *result, GError **error); static void toggle_initial_presence_visibility_cb (GObject *source_object, GAsyncResult *result, gpointer user_data); static void activate_current_privacy_list (GabbleConnection *self, GSimpleAsyncResult *result); /* actual code! */ GQuark conn_presence_error_quark (void) { static GQuark quark = 0; if (!quark) quark = g_quark_from_static_string ("conn-presence-error"); return quark; } static GHashTable * construct_contact_statuses_cb (GObject *obj, const GArray *contact_handles, GError **error) { GabbleConnection *self = GABBLE_CONNECTION (obj); TpBaseConnection *base = (TpBaseConnection *) self; guint i; TpHandle handle; GHashTable *contact_statuses, *parameters; TpPresenceStatus *contact_status; GValue *message; GabblePresence *presence; GabblePresenceId status; const gchar *status_message; TpHandleRepoIface *handle_repo = tp_base_connection_get_handles (base, TP_HANDLE_TYPE_CONTACT); if (!tp_handles_are_valid (handle_repo, contact_handles, FALSE, error)) return NULL; contact_statuses = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, (GDestroyNotify) tp_presence_status_free); for (i = 0; i < contact_handles->len; i++) { handle = g_array_index (contact_handles, TpHandle, i); if (handle == tp_base_connection_get_self_handle (base)) presence = self->self_presence; else presence = gabble_presence_cache_get (self->presence_cache, handle); if (presence) { status = presence->status; status_message = presence->status_message; } else { if (gabble_roster_handle_sends_presence_to_us (self->roster, handle)) status = GABBLE_PRESENCE_OFFLINE; else status = GABBLE_PRESENCE_UNKNOWN; status_message = NULL; } parameters = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, (GDestroyNotify) tp_g_value_slice_free); if (status_message != NULL) { message = tp_g_value_slice_new (G_TYPE_STRING); g_value_set_static_string (message, status_message); g_hash_table_insert (parameters, "message", message); } contact_status = tp_presence_status_new (status, parameters); g_hash_table_unref (parameters); g_hash_table_insert (contact_statuses, GUINT_TO_POINTER (handle), contact_status); } return contact_statuses; } /** * conn_presence_emit_presence_update: * @self: A #GabbleConnection * @contact_handles: A zero-terminated array of #TpHandle for * the contacts to emit presence for * * Emits the Telepathy PresenceUpdate signal with the current * stored presence information for the given contact. */ void conn_presence_emit_presence_update ( GabbleConnection *self, const GArray *contact_handles) { GHashTable *contact_statuses; contact_statuses = construct_contact_statuses_cb ((GObject *) self, contact_handles, NULL); tp_presence_mixin_emit_presence_update ((GObject *) self, contact_statuses); g_hash_table_unref (contact_statuses); } /* * emit_presences_changed_for_self: * @self: A #GabbleConnection * * Convenience function for emitting presence update signals on D-Bus for our * self handle. */ static void emit_presences_changed_for_self (GabbleConnection *self) { TpBaseConnection *base = TP_BASE_CONNECTION (self); GArray *handles = g_array_sized_new (FALSE, FALSE, sizeof (TpHandle), 1); TpHandle self_handle; self_handle = tp_base_connection_get_self_handle (base); g_array_insert_val (handles, 0, self_handle); conn_presence_emit_presence_update (self, handles); g_array_unref (handles); } static WockyStanza * build_shared_status_stanza (GabbleConnection *self) { GabblePresence *presence = self->self_presence; GabbleConnectionPresencePrivate *priv = self->presence_priv; const gchar *bare_jid = conn_util_get_bare_self_jid (self); gpointer key, value; GHashTableIter iter; WockyNode *query_node = NULL; WockyStanza *iq = wocky_stanza_build (WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_SET, NULL, bare_jid, '(', "query", ':', NS_GOOGLE_SHARED_STATUS, '*', &query_node, '@', "version", GOOGLE_SHARED_STATUS_VERSION, '(', "status", '$', presence->status_message, ')', '(', "show", '$', presence->status == GABBLE_PRESENCE_DND ? "dnd" : "default", ')', ')', NULL); g_hash_table_iter_init (&iter, priv->shared_statuses); while (g_hash_table_iter_next (&iter, &key, &value)) { gchar **status_iter; gchar **statuses = (gchar **) value; WockyNode *list_node = wocky_node_add_child (query_node, "status-list"); wocky_node_set_attribute (list_node, "show", (const gchar *) key); for (status_iter = statuses; *status_iter != NULL; status_iter++) wocky_node_add_child_with_content (list_node, "status", *status_iter); } wocky_node_set_attribute (wocky_node_add_child (query_node, "invisible"), "value", presence->status == GABBLE_PRESENCE_HIDDEN ? "true" : "false"); return iq; } static gboolean is_presence_away (GabblePresenceId status) { return status == GABBLE_PRESENCE_AWAY || status == GABBLE_PRESENCE_XA; } static void set_shared_status_cb (GObject *source_object, GAsyncResult *res, gpointer user_data) { GSimpleAsyncResult *result = G_SIMPLE_ASYNC_RESULT (user_data); GabbleConnection *self = GABBLE_CONNECTION (source_object); GabbleConnectionPresencePrivate *priv = self->presence_priv; GabblePresence *presence = self->self_presence; GError *error = NULL; if (!conn_util_send_iq_finish (self, res, NULL, &error)) { g_simple_async_result_set_error (result, CONN_PRESENCE_ERROR, CONN_PRESENCE_ERROR_SET_SHARED_STATUS, "error setting Google shared status: %s", error->message); } else { gabble_muc_factory_broadcast_presence (self->muc_factory); if (is_presence_away (priv->previous_shared_status)) { /* To use away and xa we need to send a <presence/> to the server, * but then GTalk also expects us to leave the status using * <presence/> too. */ conn_presence_signal_own_presence (self, NULL, &error); } else if (priv->previous_shared_status == GABBLE_PRESENCE_HIDDEN && is_presence_away (presence->status)) { /* We sent the shared status change to leave the invisibility, so * now we can actually go to away / xa. */ conn_presence_signal_own_presence (self, NULL, &error); emit_presences_changed_for_self (self); } priv->previous_shared_status = presence->status; } g_simple_async_result_complete (result); g_object_unref (result); if (error != NULL) g_error_free (error); } static void insert_presence_to_shared_statuses (GabbleConnection *self) { GabbleConnectionPresencePrivate *priv = self->presence_priv; GabblePresence *presence = self->self_presence; const gchar *show = presence->status == GABBLE_PRESENCE_DND ? "dnd" : "default"; gchar **statuses = g_hash_table_lookup (priv->shared_statuses, show); if (presence->status_message == NULL || is_presence_away (presence->status)) return; if (statuses == NULL) { statuses = g_new0 (gchar *, 2); statuses[0] = g_strdup (presence->status_message); g_hash_table_insert (priv->shared_statuses, g_strdup (show), statuses); } else { guint i; guint list_len = MIN (priv->max_shared_statuses, (gint) g_strv_length (statuses) + 1); gchar **new_statuses = g_new0 (gchar *, list_len + 1); new_statuses[0] = g_strdup (presence->status_message); for (i = 1; i < list_len; i++) new_statuses[i] = g_strdup (statuses[i - 1]); g_hash_table_insert (priv->shared_statuses, g_strdup (show), new_statuses); } } static void set_shared_status (GabbleConnection *self, GSimpleAsyncResult *result) { GabbleConnectionPresencePrivate *priv = self->presence_priv; GabblePresence *presence = self->self_presence; g_object_ref (result); /* Away is treated like idleness in GTalk; it's per connection and not * global. To set the presence as away we use the traditional <presence/>, * but, if we were invisible, we need to first leave invisibility. */ if (!is_presence_away (presence->status) || priv->previous_shared_status == GABBLE_PRESENCE_HIDDEN) { WockyStanza *iq; DEBUG ("shared status invisibility is %savailable", priv->shared_status_compat ? "" : "un"); if (presence->status == GABBLE_PRESENCE_HIDDEN && !priv->shared_status_compat) presence->status = GABBLE_PRESENCE_DND; insert_presence_to_shared_statuses (self); iq = build_shared_status_stanza (self); conn_util_send_iq_async (self, iq, NULL, set_shared_status_cb, result); g_object_unref (iq); } else { gboolean retval; GError *error = NULL; DEBUG ("not updating shared status as it's not supported for away"); retval = conn_presence_signal_own_presence (self, NULL, &error); if (!retval) { g_simple_async_result_set_from_error (result, error); g_error_free (error); } emit_presences_changed_for_self (self); priv->previous_shared_status = presence->status; g_simple_async_result_complete_in_idle (result); g_object_unref (result); } } static void set_shared_status_async (GabbleConnection *self, GAsyncReadyCallback callback, gpointer user_data) { GSimpleAsyncResult *result = g_simple_async_result_new (G_OBJECT (self), callback, user_data, set_shared_status_async); set_shared_status (self, result); g_object_unref (result); } static gboolean set_shared_status_finish (GabbleConnection *self, GAsyncResult *result, GError **error) { wocky_implement_finish_void (self, set_shared_status_async); } static void set_xep0186_invisible (GabbleConnection *self, gboolean invisible, GSimpleAsyncResult *result) { TpBaseConnection *base = (TpBaseConnection *) self; GabbleConnectionPresencePrivate *priv = self->presence_priv; GError *error = NULL; g_object_ref (result); if (!invisible && tp_base_connection_get_status (base) != TP_CONNECTION_STATUS_CONNECTED) { if (priv->privacy_statuses != NULL) { /* A plugin might need to activate a privacy list */ activate_current_privacy_list (self, result); g_object_unref (result); } else { conn_presence_signal_own_presence (self, NULL, &error); g_simple_async_result_complete_in_idle (result); g_object_unref (result); } } else { const gchar *element = invisible ? "invisible" : "visible"; WockyStanza *iq = wocky_stanza_build (WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_SET, NULL, NULL, '(', element, ':', NS_INVISIBLE, ')', NULL); conn_util_send_iq_async (self, iq, NULL, set_xep0186_invisible_cb, result); g_object_unref (iq); } } static void set_xep0186_invisible_cb ( GObject *source, GAsyncResult *set_result, gpointer user_data) { GabbleConnection *conn = GABBLE_CONNECTION (source); GSimpleAsyncResult *result = G_SIMPLE_ASYNC_RESULT (user_data); GError *error = NULL; if (!conn_util_send_iq_finish (conn, set_result, NULL, &error)) { g_simple_async_result_set_error (result, CONN_PRESENCE_ERROR, CONN_PRESENCE_ERROR_SET_INVISIBLE, "error setting XEP-0186 (in)visiblity: %s", error->message); g_clear_error (&error); } else { /* If we've become visible, broadcast our new presence and update our MUC * presences. * * If we've become invisible, we only need to do the latter, but the * server will block the former in any case, so let's not bother adding * complexity. */ conn_presence_signal_own_presence (conn, NULL, &error); } if (error != NULL) { g_simple_async_result_set_from_error (result, error); g_error_free (error); } g_simple_async_result_complete (result); g_object_unref (result); } static void activate_current_privacy_list (GabbleConnection *self, GSimpleAsyncResult *result) { GabbleConnectionPresencePrivate *priv = self->presence_priv; TpBaseConnection *base = (TpBaseConnection *) self; WockyStanza *iq; const gchar *list_name = NULL; gboolean invisible; GabblePresence *presence = self->self_presence; GError *error = NULL; WockyNode *active_node; g_assert (priv->privacy_statuses != NULL); list_name = g_hash_table_lookup (priv->privacy_statuses, gabble_statuses[presence->status].name); invisible = (gabble_statuses[presence->status].presence_type == TP_CONNECTION_PRESENCE_TYPE_HIDDEN); DEBUG ("Privacy status %s, backed by %s", gabble_statuses[presence->status].name, list_name ? list_name : "(no list)"); g_object_ref (result); if (tp_base_connection_get_status (base) == TP_CONNECTION_STATUS_CONNECTED && invisible) { if (!gabble_connection_send_presence (self, WOCKY_STANZA_SUB_TYPE_UNAVAILABLE, NULL, NULL, &error)) goto ERROR; } /* If we're still connecting and there's no list to be set, we don't * need to bother with removing the active list; just shortcut to * signalling our presence. */ else if (list_name == NULL && tp_base_connection_get_status (base) != TP_CONNECTION_STATUS_CONNECTED) { if (!conn_presence_signal_own_presence (self, NULL, &error)) goto ERROR; g_simple_async_result_complete_in_idle (result); g_object_unref (result); return; } iq = wocky_stanza_build (WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_SET, NULL, NULL, '(', "query", ':', NS_PRIVACY, '(', "active", '*', &active_node, ')', ')', NULL); if (list_name != NULL) wocky_node_set_attribute (active_node, "name", list_name); conn_util_send_iq_async (self, iq, NULL, activate_current_privacy_list_cb, result); g_object_unref (iq); return; ERROR: if (error != NULL) { g_simple_async_result_set_from_error (result, error); g_simple_async_result_complete_in_idle (result); g_error_free (error); g_object_unref (result); } } static void activate_current_privacy_list_cb ( GObject *source, GAsyncResult *activate_result, gpointer user_data) { GabbleConnection *conn = GABBLE_CONNECTION (source); GSimpleAsyncResult *result = G_SIMPLE_ASYNC_RESULT (user_data); GError *error = NULL; if (!conn_util_send_iq_finish (conn, activate_result, NULL, &error)) { g_simple_async_result_set_error (result, CONN_PRESENCE_ERROR, CONN_PRESENCE_ERROR_SET_PRIVACY_LIST, "error setting requested privacy list: %s", error->message); g_clear_error (&error); } else { /* Whether we were becoming invisible or visible, we now need to * re-broadcast our presence. */ conn_presence_signal_own_presence (conn, NULL, &error); emit_presences_changed_for_self (conn); } if (error != NULL) { g_simple_async_result_set_from_error (result, error); g_error_free (error); } g_simple_async_result_complete (result); g_object_unref (result); } static void disable_invisible_privacy_list (GabbleConnection *self) { GabbleConnectionPresencePrivate *priv = self->presence_priv; if (priv->invisibility_method == INVISIBILITY_METHOD_PRIVACY) { if (self->features & GABBLE_CONNECTION_FEATURES_PRESENCE_INVISIBLE) priv->invisibility_method = INVISIBILITY_METHOD_PRESENCE_INVISIBLE; else priv->invisibility_method = INVISIBILITY_METHOD_NONE; DEBUG ("Set invisibility method to %s", (priv->invisibility_method == INVISIBILITY_METHOD_PRESENCE_INVISIBLE) ? "presence-invisible" : "none"); } } static void create_invisible_privacy_list_reply_cb ( GObject *source, GAsyncResult *create_result, gpointer user_data) { GabbleConnection *conn = GABBLE_CONNECTION (source); GSimpleAsyncResult *result = G_SIMPLE_ASYNC_RESULT (user_data); GError *error = NULL; if (!conn_util_send_iq_finish (conn, create_result, NULL, &error)) g_simple_async_result_take_error (result, error); g_simple_async_result_complete (result); g_object_unref (result); } static void create_invisible_privacy_list_async (GabbleConnection *self, GAsyncReadyCallback callback, gpointer user_data) { GSimpleAsyncResult *result = g_simple_async_result_new (G_OBJECT (self), callback, user_data, create_invisible_privacy_list_async); WockyStanza *iq = wocky_stanza_build (WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_SET, NULL, NULL, '(', "query", ':', NS_PRIVACY, '(', "list", '@', "name", self->presence_priv->invisible_list_name, '(', "item", '@', "action", "deny", '@', "order", "1", '(', "presence-out", ')', ')', ')', ')', NULL); DEBUG ("Creating '%s'", self->presence_priv->invisible_list_name); conn_util_send_iq_async (self, iq, NULL, create_invisible_privacy_list_reply_cb, result); g_object_unref (iq); } static gboolean create_invisible_privacy_list_finish (GabbleConnection *self, GAsyncResult *result, GError **error) { wocky_implement_finish_void (self, create_invisible_privacy_list_async); } static void create_invisible_privacy_list_cb (GObject *source_object, GAsyncResult *result, gpointer user_data) { GabbleConnection *self = GABBLE_CONNECTION (source_object); GabbleConnectionPresencePrivate *priv = self->presence_priv; GError *error = NULL; if (!create_invisible_privacy_list_finish (self, result, &error)) { DEBUG ("Error creating privacy list: %s", error->message); disable_invisible_privacy_list (self); g_error_free (error); } g_assert (priv->privacy_statuses != NULL); /* "hidden" presence status will be backed by the invisible list */ g_hash_table_insert (priv->privacy_statuses, g_strdup ("hidden"), g_strdup (priv->invisible_list_name)); toggle_presence_visibility_async (self, toggle_initial_presence_visibility_cb, user_data); } static gboolean store_shared_statuses (GabbleConnection *self, WockyNode *query_node) { GabbleConnectionPresencePrivate *priv = self->presence_priv; TpBaseConnection *base = TP_BASE_CONNECTION (self); GabblePresenceId presence_id = self->self_presence->status; const gchar *status_message = NULL; const gchar *min_version = wocky_node_get_attribute (query_node, "status-min-ver"); WockyNodeIter iter; gchar *resource; guint8 prio; gboolean rv; WockyNode *node; gboolean dnd = FALSE; gboolean invisible = FALSE; DEBUG ("status-min-ver %s", min_version); g_object_get (self, "resource", &resource, "priority", &prio, NULL); if (priv->shared_statuses != NULL) g_hash_table_unref (priv->shared_statuses); priv->shared_statuses = g_hash_table_new_full ( g_str_hash, g_str_equal, g_free, (GDestroyNotify) g_strfreev); wocky_node_iter_init (&iter, query_node, NULL, NULL); while (wocky_node_iter_next (&iter, &node)) { if (g_strcmp0 (node->name, "status-list") == 0) { WockyNodeIter list_iter; WockyNode *list_item; GPtrArray *statuses; const gchar *show = wocky_node_get_attribute (node, "show"); if (show == NULL) continue; statuses = g_ptr_array_new (); wocky_node_iter_init (&list_iter, node, "status", NULL); while (wocky_node_iter_next (&list_iter, &list_item)) g_ptr_array_add (statuses, g_strdup (list_item->content)); g_ptr_array_add (statuses, NULL); g_hash_table_insert (priv->shared_statuses, g_strdup (show), g_ptr_array_free (statuses, FALSE)); } else if (g_strcmp0 (node->name, "status") == 0) { status_message = node->content; } else if (g_strcmp0 (node->name, "show") == 0) { dnd = g_strcmp0 (node->content, "dnd") == 0; } else if (g_strcmp0 (node->name, "invisible") == 0) { invisible = g_strcmp0 (wocky_node_get_attribute (node, "value"), "true") == 0; } } /* - status-min-ver == 0 means that at least one resource doesn't support * Google shared status, so we fallback to "dnd". * - status-min-ver == 1 means that all the resources support shared * status, but at least one doesn't support invisibility; we have to fall * fall back to "dnd". * - status-miv-ver == 2 means that all the resources support shared status * with invisibility. * - any other value means that the other resources will have to fall back * to version 2 for us. */ priv->shared_status_compat = (g_strcmp0 (min_version, "0") != 0 && g_strcmp0 (min_version, "1") != 0); if (invisible) { if (priv->shared_status_compat) presence_id = GABBLE_PRESENCE_HIDDEN; else presence_id = GABBLE_PRESENCE_DND; } else if (dnd) { presence_id = GABBLE_PRESENCE_DND; } else { if (presence_id == GABBLE_PRESENCE_DND || presence_id == GABBLE_PRESENCE_HIDDEN) presence_id = GABBLE_PRESENCE_AVAILABLE; } if (tp_base_connection_get_status (base) != TP_CONNECTION_STATUS_CONNECTED) { /* Not connected, override with the local status. */ rv = TRUE; } else if (is_presence_away (self->self_presence->status)) { /* Away presence is not overridden with remote presence because it's * per connection. */ rv = FALSE; } else { /* Update with the remote presence */ rv = gabble_presence_update (self->self_presence, resource, presence_id, status_message, prio, NULL, time (NULL)); } g_free (resource); return rv; } static gboolean iq_shared_status_changed_cb (WockyPorter *porter, WockyStanza *stanza, gpointer user_data) { GabbleConnection *self = GABBLE_CONNECTION (user_data); WockyNode *query_node = wocky_node_get_child_ns ( wocky_stanza_get_top_node (stanza), "query", NS_GOOGLE_SHARED_STATUS); if (store_shared_statuses (self, query_node)) emit_presences_changed_for_self (self); wocky_porter_acknowledge_iq (porter, stanza, NULL); return TRUE; } static gboolean iq_privacy_list_push_cb ( WockyPorter *porter, WockyStanza *message, gpointer user_data) { GabbleConnection *conn = GABBLE_CONNECTION (user_data); WockyNode *list_node, *query_node, *iq; const gchar *list_name; wocky_porter_acknowledge_iq (wocky_session_get_porter (conn->session), message, NULL); iq = wocky_stanza_get_top_node (message); query_node = wocky_node_get_first_child (iq); list_node = wocky_node_get_child (query_node, "list"); list_name = wocky_node_get_attribute (list_node, "name"); if (g_strcmp0 (list_name, conn->presence_priv->invisible_list_name) == 0) setup_invisible_privacy_list_async (conn, NULL, NULL); return TRUE; } /********************************************************************** * get_existing_privacy_lists_async * ↓ * privacy_lists_loaded_cb * ↓ * ↓ inv_list_name = "invisible" unless set to something else by plugins * ↓ * setup_invisible_privacy_list_async * ↓ * verify_invisible_privacy_list_cb─────────────────────────────────────┐ * | | | | * |success | |n/a failure| * | | | | * | | ↓ | * ├────────+─presence_create_invisible_privacy_list(inv_list_name)─────| * | | | * | |invalid | * | ↓ | * ├────────presence_create_invisible_privacy_list("invisible-gabble")──| * | | * | ↓ * | disable_invisible_privacy_list * | | * ├─────────────────────────────────────────────────────────┘ * ↓ * toggle_presence_visibility_async * ↓ * ... **********************************************************************/ static void get_shared_status_cb (GObject *source_object, GAsyncResult *res, gpointer user_data) { GabbleConnection *self = GABBLE_CONNECTION (source_object); GabbleConnectionPresencePrivate *priv = self->presence_priv; GSimpleAsyncResult *result = G_SIMPLE_ASYNC_RESULT (user_data); WockyStanza *iq = NULL; GError *error = NULL; DEBUG (" "); if (!conn_util_send_iq_finish (self, res, &iq, &error)) { DEBUG ("Error getting shared status: %s", error->message); g_simple_async_result_set_from_error (result, error); g_error_free (error); } else { WockyNode *query_node = wocky_node_get_child_ns (wocky_stanza_get_top_node (iq), "query", NS_GOOGLE_SHARED_STATUS); if (query_node != NULL) { const gchar *max_status_message = wocky_node_get_attribute (query_node, "status-max"); const gchar *max_shared = wocky_node_get_attribute (query_node, "status-list-contents-max"); if (max_status_message != NULL) priv->max_status_message_length = (gint) g_ascii_strtoll ( max_status_message, NULL, 10); else priv->max_status_message_length = 0; /* no limit */ if (max_shared != NULL) priv->max_shared_statuses = (gint) g_ascii_strtoll (max_shared, NULL, 10); else priv->max_shared_statuses = 5; /* Safe bet */ store_shared_statuses (self, query_node); } else { g_simple_async_result_set_error (result, CONN_PRESENCE_ERROR, CONN_PRESENCE_ERROR_SET_SHARED_STATUS, "Error retrieving shared status, received empty reply"); } g_object_unref (iq); } g_simple_async_result_complete_in_idle (result); g_object_unref (result); } static void get_shared_status_async (GabbleConnection *self, GAsyncReadyCallback callback, gpointer user_data) { GSimpleAsyncResult *result = g_simple_async_result_new (G_OBJECT (self), callback, user_data, get_shared_status_async); WockyStanza *iq; DEBUG (" "); iq = wocky_stanza_build (WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_GET, NULL, conn_util_get_bare_self_jid (self), '(', "query", ':', NS_GOOGLE_SHARED_STATUS, '@', "version", GOOGLE_SHARED_STATUS_VERSION, ')', NULL); conn_util_send_iq_async (self, iq, NULL, get_shared_status_cb, result); /* We cannot use the chat status with GTalk's shared status. */ if (self->self_presence->status == GABBLE_PRESENCE_CHAT) self->self_presence->status = GABBLE_PRESENCE_AVAILABLE; g_object_unref (iq); } static gboolean get_shared_status_finish (GabbleConnection *self, GAsyncResult *result, GError **error) { wocky_implement_finish_void (self, get_shared_status_async); } static void get_existing_privacy_lists_cb ( GObject *source, GAsyncResult *get_result, gpointer user_data) { GabbleConnection *conn = GABBLE_CONNECTION (source); GSimpleAsyncResult *result = G_SIMPLE_ASYNC_RESULT (user_data); WockyStanza *reply_msg = NULL; WockyNode *query_node; GError *error = NULL; if (!conn_util_send_iq_finish (conn, get_result, &reply_msg, &error)) { DEBUG ("Error getting privacy lists: %s", error->message); g_simple_async_result_set_from_error (result, error); g_error_free (error); goto out; } query_node = wocky_node_get_child_ns ( wocky_stanza_get_top_node (reply_msg), "query", NS_PRIVACY); if (query_node == NULL) { g_simple_async_result_set_error (result, TP_ERROR, TP_ERROR_NETWORK_ERROR, "no <query/> node in 'list privacy lists' reply"); } else { GabbleConnectionPresencePrivate *priv = conn->presence_priv; WockyNode *list_node; WockyNodeIter iter; GabblePluginLoader *loader = gabble_plugin_loader_dup (); /* As we're called only once, privacy_statuses couldn't have been * already initialised. */ g_assert (priv->privacy_statuses == NULL); priv->privacy_statuses = g_hash_table_new_full ( g_str_hash, g_str_equal, g_free, g_free); wocky_node_iter_init (&iter, query_node, "list", NULL); while (wocky_node_iter_next (&iter, &list_node)) { const gchar *list_name = wocky_node_get_attribute (list_node, "name"); const gchar *status_name; status_name = gabble_plugin_loader_presence_status_for_privacy_list (loader, list_name); if (status_name) { DEBUG ("Presence status %s backed by privacy list %s", status_name, list_name); g_hash_table_replace (priv->privacy_statuses, g_strdup (status_name), g_strdup (list_name)); } } } out: g_simple_async_result_complete (result); g_object_unref (result); g_clear_object (&reply_msg); } static void get_existing_privacy_lists_async (GabbleConnection *self, GAsyncReadyCallback callback, gpointer user_data) { GSimpleAsyncResult *result = g_simple_async_result_new (G_OBJECT (self), callback, user_data, get_existing_privacy_lists_async); WockyStanza *iq; iq = wocky_stanza_build (WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_GET, NULL, NULL, '(', "query", ':', NS_PRIVACY, ')', NULL); conn_util_send_iq_async (self, iq, NULL, get_existing_privacy_lists_cb, result); g_object_unref (iq); } static gboolean get_existing_privacy_lists_finish (GabbleConnection *self, GAsyncResult *result, GError **error) { wocky_implement_finish_void (self, get_existing_privacy_lists_async); } static void setup_invisible_privacy_list_async (GabbleConnection *self, GAsyncReadyCallback callback, gpointer user_data) { GabbleConnectionPresencePrivate *priv = self->presence_priv; GSimpleAsyncResult *result = g_simple_async_result_new (G_OBJECT (self), callback, user_data, setup_invisible_privacy_list_async); WockyStanza *iq; if (priv->invisible_list_name == NULL) priv->invisible_list_name = g_strdup ("invisible"); iq = wocky_stanza_build (WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_GET, NULL, NULL, '(', "query", ':', NS_PRIVACY, '(', "list", '@', "name", self->presence_priv->invisible_list_name, ')', ')', NULL); wocky_porter_send_iq_async (wocky_session_get_porter (self->session), iq, NULL, verify_invisible_privacy_list_cb, result); g_object_unref (iq); } static gboolean setup_invisible_privacy_list_finish (GabbleConnection *self, GAsyncResult *result, GError **error) { wocky_implement_finish_void (self, setup_invisible_privacy_list_async); } static gboolean is_valid_invisible_list (WockyNode *list_node) { WockyNode *top_node = NULL; WockyNode *child; WockyNodeIter i; guint top_order = G_MAXUINT; wocky_node_iter_init (&i, list_node, "item", NULL); while (wocky_node_iter_next (&i, &child)) { const gchar *order_str; guint order; gchar *end; order_str = wocky_node_get_attribute (child, "order"); if (order_str == NULL) continue; order = strtoul (order_str, &end, 10); if (*end != '\0') continue; if (order < top_order) { top_order = order; top_node = child; } } if (top_node != NULL) { const gchar *value = wocky_node_get_attribute (top_node, "value"); const gchar *action = wocky_node_get_attribute (top_node, "action"); WockyNode *presence_out = wocky_node_get_child (top_node, "presence-out"); return (value == NULL && g_strcmp0 (action, "deny") == 0 && presence_out != NULL); } return FALSE; } static void verify_invisible_privacy_list_cb ( GObject *source, GAsyncResult *verify_result, gpointer user_data) { GabbleConnection *conn = GABBLE_CONNECTION ( g_async_result_get_source_object (G_ASYNC_RESULT (user_data))); GabbleConnectionPresencePrivate *priv = conn->presence_priv; WockyStanza *reply_msg = NULL; WockyNode *query_node = NULL, *list_node = NULL; GError *error = NULL; reply_msg = wocky_porter_send_iq_finish (WOCKY_PORTER (source), verify_result, &error); if (reply_msg != NULL) query_node = wocky_node_get_child_ns (wocky_stanza_get_top_node (reply_msg), "query", NS_PRIVACY); if (query_node != NULL) list_node = wocky_node_get_child (query_node, "list"); if (reply_msg != NULL && !wocky_stanza_extract_errors (reply_msg, NULL, &error, NULL, NULL)) { if (list_node == NULL || !is_valid_invisible_list (list_node)) { g_free (priv->invisible_list_name); priv->invisible_list_name = g_strdup ("invisible-gabble"); create_invisible_privacy_list_async (conn, create_invisible_privacy_list_cb, user_data); } else { /* "hidden" presence status can be backed by this list */ g_hash_table_insert (conn->presence_priv->privacy_statuses, g_strdup ("hidden"), g_strdup (priv->invisible_list_name)); toggle_presence_visibility_async (conn, toggle_initial_presence_visibility_cb, user_data); } } else if (error->domain == WOCKY_XMPP_ERROR && error->code == WOCKY_XMPP_ERROR_ITEM_NOT_FOUND) { create_invisible_privacy_list_async (conn, create_invisible_privacy_list_cb, user_data); } else { disable_invisible_privacy_list (conn); toggle_presence_visibility_async (conn, toggle_initial_presence_visibility_cb, user_data); } if (error != NULL) g_error_free (error); g_clear_object (&reply_msg); g_clear_object (&conn); } static void initial_presence_setup_cb (GObject *source_object, GAsyncResult *result, gpointer user_data) { GabbleConnection *self = GABBLE_CONNECTION (source_object); GabbleConnectionPresencePrivate *priv = self->presence_priv; GSimpleAsyncResult *external_result = G_SIMPLE_ASYNC_RESULT (user_data); GError *error = NULL; if (!setup_invisible_privacy_list_finish (self, result, &error)) { g_simple_async_result_set_from_error (external_result, error); g_error_free (error); } if (priv->invisibility_method == INVISIBILITY_METHOD_PRIVACY && self->session != NULL) { priv->iq_list_push_id = wocky_c2s_porter_register_handler_from_server ( WOCKY_C2S_PORTER (wocky_session_get_porter (self->session)), WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_SET, WOCKY_PORTER_HANDLER_PRIORITY_NORMAL, iq_privacy_list_push_cb, self, '(', "query", ':', NS_PRIVACY, '(', "list", ')', ')', NULL); } g_simple_async_result_complete_in_idle (external_result); g_object_unref (external_result); } static void toggle_initial_presence_visibility_cb (GObject *source_object, GAsyncResult *result, gpointer user_data) { GabbleConnection *self = GABBLE_CONNECTION (source_object); GSimpleAsyncResult *external_result = G_SIMPLE_ASYNC_RESULT (user_data); GError *error = NULL; if (!toggle_presence_visibility_finish (self, result, &error)) { self->self_presence->status = GABBLE_PRESENCE_DND; g_clear_error (&error); if (!conn_presence_signal_own_presence (self, NULL, &error)) { g_simple_async_result_set_from_error (external_result, error); g_error_free (error); } } g_simple_async_result_complete_in_idle (external_result); g_object_unref (external_result); } static void privacy_lists_loaded_cb (GObject *source_object, GAsyncResult *result, gpointer user_data) { GabbleConnection *self = GABBLE_CONNECTION (source_object); GabbleConnectionPresencePrivate *priv = self->presence_priv; GError *error = NULL; if (get_existing_privacy_lists_finish (self, result, &error)) { /* if the above call succeeded, the server supports privacy * lists, so this should be initialised. */ g_assert (priv->privacy_statuses != NULL); /* If anyone/plugins already set up "hidden" status backing * by a specific list, try to use that instead. If that list * is inadequate, we'll create gabble-specific one. */ priv->invisible_list_name = g_strdup (g_hash_table_lookup (priv->privacy_statuses, "hidden")); if (priv->invisibility_method == INVISIBILITY_METHOD_NONE) priv->invisibility_method = INVISIBILITY_METHOD_PRIVACY; } if (priv->invisibility_method == INVISIBILITY_METHOD_PRIVACY) setup_invisible_privacy_list_async (self, initial_presence_setup_cb, user_data); else toggle_presence_visibility_async (self, toggle_initial_presence_visibility_cb, user_data); } static void shared_status_toggle_initial_presence_visibility_cb (GObject *source_object, GAsyncResult *result, gpointer user_data) { GabbleConnection *self = GABBLE_CONNECTION (source_object); GSimpleAsyncResult *external_result = G_SIMPLE_ASYNC_RESULT (user_data); GError *error = NULL; if (!toggle_presence_visibility_finish (self, result, &error)) { g_simple_async_result_set_from_error (external_result, error); g_clear_error (&error); } else if (self->self_presence->status != GABBLE_PRESENCE_AWAY && self->self_presence->status != GABBLE_PRESENCE_XA) { /* With shared status we send the normal <presence/> only with away and * extended away, but for initial status we need to send <presence/> as * it also contains the caps. */ if (!conn_presence_signal_own_presence (self, NULL, &error)) { g_simple_async_result_set_from_error (external_result, error); g_error_free (error); } } g_simple_async_result_complete_in_idle (external_result); g_object_unref (external_result); } static void shared_status_setup_cb (GObject *source_object, GAsyncResult *result, gpointer user_data) { GError *error = NULL; GabbleConnection *self = GABBLE_CONNECTION (source_object); GabbleConnectionPresencePrivate *priv = self->presence_priv; if (get_shared_status_finish (self, result, &error)) { WockyPorter *porter = wocky_session_get_porter (self->session); priv->invisibility_method = INVISIBILITY_METHOD_SHARED_STATUS; priv->iq_shared_status_cb = wocky_c2s_porter_register_handler_from_server ( WOCKY_C2S_PORTER (porter), WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_SET, WOCKY_PORTER_HANDLER_PRIORITY_NORMAL, iq_shared_status_changed_cb, self, '(', "query", ':', NS_GOOGLE_SHARED_STATUS, ')', NULL); } else { DEBUG ("failed: %s", error->message); g_error_free (error); } toggle_presence_visibility_async (self, shared_status_toggle_initial_presence_visibility_cb, user_data); } void conn_presence_set_initial_presence_async (GabbleConnection *self, GAsyncReadyCallback callback, gpointer user_data) { GabbleConnectionPresencePrivate *priv = self->presence_priv; GSimpleAsyncResult *result = g_simple_async_result_new (G_OBJECT (self), callback, user_data, conn_presence_set_initial_presence_async); if (self->features & GABBLE_CONNECTION_FEATURES_INVISIBLE) priv->invisibility_method = INVISIBILITY_METHOD_INVISIBLE_COMMAND; if (self->features & GABBLE_CONNECTION_FEATURES_GOOGLE_SHARED_STATUS) get_shared_status_async (self, shared_status_setup_cb, result); else get_existing_privacy_lists_async (self, privacy_lists_loaded_cb, result); } gboolean conn_presence_set_initial_presence_finish (GabbleConnection *self, GAsyncResult *result, GError **error) { wocky_implement_finish_void (self, conn_presence_set_initial_presence_async); } /** * conn_presence_statuses: * * Used to retrieve the list of presence statuses supported by connections * consisting of the basic statuses followed by any statuses defined by * plugins. * * Returns: an array of #TpPresenceStatusSpec terminated by a 0 filled member * The array is owned by te connection presence implementation and must not * be altered or freed by anyone else. **/ const TpPresenceStatusSpec * conn_presence_statuses (void) { if (gabble_statuses == NULL) { GabblePluginLoader *loader = gabble_plugin_loader_dup (); gabble_statuses = gabble_plugin_loader_append_statuses ( loader, gabble_base_statuses); g_object_unref (loader); } return gabble_statuses; } static guint get_maximum_status_message_length_cb (GObject *obj) { GabbleConnection *conn = GABBLE_CONNECTION (obj); GabbleConnectionPresencePrivate *priv = conn->presence_priv; return priv->max_status_message_length; } /** * conn_presence_signal_own_presence: * @self: A #GabbleConnection * @to: bare or full JID for directed presence, or NULL * @error: pointer in which to return a GError in case of failure. * * Signal the user's stored presence to @to, or to the jabber server * * Retuns: FALSE if an error occurred */ gboolean conn_presence_signal_own_presence (GabbleConnection *self, const gchar *to, GError **error) { GabbleConnectionPresencePrivate *priv = self->presence_priv; GabblePresence *presence = self->self_presence; TpBaseConnection *base = (TpBaseConnection *) self; WockyStanza *message = gabble_presence_as_message (presence, to); gboolean ret; if (presence->status == GABBLE_PRESENCE_HIDDEN && to == NULL) { if (priv->invisibility_method == INVISIBILITY_METHOD_PRESENCE_INVISIBLE) wocky_node_set_attribute (wocky_stanza_get_top_node (message), "type", "invisible"); /* FIXME: or if sending directed presence, should we add * <show>away</show>? */ } gabble_connection_fill_in_caps (self, message); ret = _gabble_connection_send (self, message, error); g_object_unref (message); /* FIXME: if sending broadcast presence, should we echo it to everyone we * previously sent directed presence to? (Perhaps also GC them after a * while?) */ if (to == NULL && tp_base_connection_get_status (base) == TP_CONNECTION_STATUS_CONNECTED) gabble_muc_factory_broadcast_presence (self->muc_factory); return ret; } gboolean conn_presence_visible_to (GabbleConnection *self, TpHandle recipient) { if (self->self_presence->status == GABBLE_PRESENCE_HIDDEN) return FALSE; if (!gabble_roster_handle_gets_presence_from_us (self->roster, recipient)) return FALSE; /* FIXME: other reasons they might not be able to see our presence? */ return TRUE; } static void activate_current_privacy_list_async (GabbleConnection *self, GAsyncReadyCallback callback, gpointer user_data) { GSimpleAsyncResult *result = g_simple_async_result_new (G_OBJECT (self), callback, user_data, activate_current_privacy_list_async); activate_current_privacy_list (self, result); g_object_unref (result); } static void toggle_presence_visibility_async (GabbleConnection *self, GAsyncReadyCallback callback, gpointer user_data) { GabbleConnectionPresencePrivate *priv = self->presence_priv; GSimpleAsyncResult *result = g_simple_async_result_new (G_OBJECT (self), callback, user_data, toggle_presence_visibility_async); gboolean set_invisible = (self->self_presence->status == GABBLE_PRESENCE_HIDDEN); switch (priv->invisibility_method) { case INVISIBILITY_METHOD_SHARED_STATUS: set_shared_status (self, result); break; case INVISIBILITY_METHOD_INVISIBLE_COMMAND: set_xep0186_invisible (self, set_invisible, result); break; case INVISIBILITY_METHOD_PRIVACY: activate_current_privacy_list (self, result); break; default: { GError *error = NULL; /* If we don't even support XEP-0018, revert to DND */ if (priv->invisibility_method == INVISIBILITY_METHOD_NONE && set_invisible) self->self_presence->status = GABBLE_PRESENCE_DND; if (!conn_presence_signal_own_presence (self, NULL, &error)) { g_simple_async_result_set_from_error (result, error); g_error_free (error); } g_simple_async_result_complete_in_idle (result); } } g_object_unref (result); } static gboolean toggle_presence_visibility_finish ( GabbleConnection *self, GAsyncResult *result, GError **error) { wocky_implement_finish_void (self, toggle_presence_visibility_async); } static void set_shared_status_presence_cb (GObject *source_object, GAsyncResult *res, gpointer user_data) { GabbleConnection *self = GABBLE_CONNECTION (source_object); GError *error = NULL; DEBUG (" "); if (!set_shared_status_finish (self, res, &error)) { DEBUG ("Error setting shared status %s", error->message); g_error_free (error); error = NULL; } emit_presences_changed_for_self (self); } static void toggle_presence_visibility_cb (GObject *source_object, GAsyncResult *res, gpointer user_data) { GabbleConnection *self = GABBLE_CONNECTION (source_object); GError *error = NULL; DEBUG (" "); if (!toggle_presence_visibility_finish (self, res, &error)) { DEBUG ("Error setting visibility, falling back to dnd: %s", error->message); g_error_free (error); error = NULL; self->self_presence->status = GABBLE_PRESENCE_DND; if (!conn_presence_signal_own_presence (self, NULL, &error)) { DEBUG ("Failed to set fallback status: %s", error->message); g_error_free (error); } } emit_presences_changed_for_self (self); } static gboolean set_own_status_cb (GObject *obj, const TpPresenceStatus *status, GError **error) { GabbleConnection *conn = GABBLE_CONNECTION (obj); GabbleConnectionPresencePrivate *priv = conn->presence_priv; TpBaseConnection *base = (TpBaseConnection *) conn; GabblePresenceId i = GABBLE_PRESENCE_AVAILABLE; const gchar *message_str = NULL; gchar *message_truncated = NULL; gchar *resource; gint8 prio; gboolean retval = TRUE; GabblePresenceId prev_status = conn->self_presence->status; g_object_get (conn, "resource", &resource, "priority", &prio, NULL); if (status) { GHashTable *args = status->optional_arguments; GValue *message = NULL, *priority = NULL; i = status->index; /* Workaround for tp-glib not checking whether we support setting * a particular status (can be removed once we depend on tp-glib * with the check enabled). Assumes PresenceId value ordering. */ if (i < GABBLE_PRESENCE_HIDDEN) { g_set_error (error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT, "Status '%s' can not be requested in this connection", gabble_statuses[i].name); retval = FALSE; goto OUT; } if (args != NULL) { message = g_hash_table_lookup (args, "message"); priority = g_hash_table_lookup (args, "priority"); } if (message) { if (!G_VALUE_HOLDS_STRING (message)) { DEBUG ("got a status message which was not a string"); g_set_error (error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT, "Status argument 'message' requires a string"); retval = FALSE; goto OUT; } message_str = g_value_get_string (message); } if (priority) { if (!G_VALUE_HOLDS_INT (priority)) { DEBUG ("got a priority value which was not a signed integer"); g_set_error (error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT, "Status argument 'priority' requires a signed integer"); retval = FALSE; goto OUT; } prio = CLAMP (g_value_get_int (priority), G_MININT8, G_MAXINT8); } } if (message_str && priv->max_status_message_length > 0 && priv->shared_statuses != NULL) { message_truncated = g_strndup (message_str, priv->max_status_message_length); message_str = message_truncated; } if (gabble_presence_update (conn->self_presence, resource, i, message_str, prio, NULL, time (NULL))) { if (tp_base_connection_get_status (base) != TP_CONNECTION_STATUS_CONNECTED) { retval = TRUE; } else if (prev_status != i && (prev_status == GABBLE_PRESENCE_HIDDEN || i == GABBLE_PRESENCE_HIDDEN)) { toggle_presence_visibility_async (conn, toggle_presence_visibility_cb, NULL); } /* if privacy lists are supported, make sure we update the current * list as needed, before signalling own presence */ else if (priv->privacy_statuses != NULL) { activate_current_privacy_list_async (conn, NULL, NULL); } else if (priv->shared_statuses != NULL) { set_shared_status_async (conn, set_shared_status_presence_cb, NULL); } else { retval = conn_presence_signal_own_presence (conn, NULL, error); emit_presences_changed_for_self (conn); } } OUT: g_free (message_truncated); g_free (resource); return retval; } static void connection_presences_updated_cb ( GabblePresenceCache *cache, const GArray *handles, gpointer user_data) { GabbleConnection *conn = GABBLE_CONNECTION (user_data); conn_presence_emit_presence_update (conn, handles); } static void connection_status_changed_cb ( GabbleConnection *conn, TpConnectionStatus status, TpConnectionStatusReason reason, gpointer user_data) { if (status == TP_CONNECTION_STATUS_CONNECTED) emit_presences_changed_for_self (conn); } static gboolean status_available_cb (GObject *obj, guint status) { GabbleConnection *conn = GABBLE_CONNECTION (obj); TpBaseConnection *base = (TpBaseConnection *) conn; GabbleConnectionPresencePrivate *priv = conn->presence_priv; TpConnectionPresenceType presence_type = gabble_statuses[status].presence_type; if (tp_base_connection_get_status (base) != TP_CONNECTION_STATUS_CONNECTED) { /* we just don't know yet */ return TRUE; } /* This relies on the fact the first entries in the statuses table * are from gabble_base_statuses. If index to the statuses table is outside * the gabble_base_statuses table, the status is provided by a plugin. */ if (status >= G_N_ELEMENTS (gabble_base_statuses)) { /* At the moment, plugins can only implement statuses via privacy * lists, so any extra status should be backed by one. If it's not * (or if privacy lists are not supported by the server at all) * by the time we're connected, it's not available. */ if (priv->privacy_statuses != NULL && g_hash_table_lookup (priv->privacy_statuses, gabble_statuses[status].name)) { return TRUE; } else { return FALSE; } } if (presence_type == TP_CONNECTION_PRESENCE_TYPE_HIDDEN && priv->invisibility_method == INVISIBILITY_METHOD_NONE) { /* If we've gone online and found that the server doesn't support * invisible, reject it. */ return FALSE; } else if (status == GABBLE_PRESENCE_CHAT && priv->shared_statuses != NULL) { /* We cannot use the chat status with GTalk's shared status. */ return FALSE; } else { return TRUE; } } TpConnectionPresenceType conn_presence_get_type (GabblePresence *presence) { return gabble_statuses[presence->status].presence_type; } /* We should update this when telepathy-glib supports setting * statuses at constructor time (see * https://bugs.freedesktop.org/show_bug.cgi?id=12896 ). * Until then, gabble_statuses is leaked. */ void conn_presence_class_init (GabbleConnectionClass *klass) { TpPresenceMixinClass *mixin_cls; tp_presence_mixin_class_init ((GObjectClass *) klass, G_STRUCT_OFFSET (GabbleConnectionClass, presence_class), status_available_cb, construct_contact_statuses_cb, set_own_status_cb, conn_presence_statuses ()); mixin_cls = TP_PRESENCE_MIXIN_CLASS (klass); mixin_cls->get_maximum_status_message_length = get_maximum_status_message_length_cb; tp_presence_mixin_simple_presence_init_dbus_properties ( (GObjectClass *) klass); } void conn_presence_init (GabbleConnection *conn) { conn->presence_priv = g_slice_new0 (GabbleConnectionPresencePrivate); conn->presence_priv->previous_shared_status = GABBLE_PRESENCE_UNKNOWN; g_signal_connect (conn->presence_cache, "presences-updated", G_CALLBACK (connection_presences_updated_cb), conn); g_signal_connect (conn, "status-changed", G_CALLBACK (connection_status_changed_cb), conn); conn->presence_priv->invisible_list_name = g_strdup ("invisible"); conn->presence_priv->privacy_statuses = NULL; tp_presence_mixin_init ((GObject *) conn, G_STRUCT_OFFSET (GabbleConnection, presence)); tp_presence_mixin_simple_presence_register_with_contacts_mixin ( G_OBJECT (conn)); } void conn_presence_dispose (GabbleConnection *self) { GabbleConnectionPresencePrivate *priv = self->presence_priv; WockyPorter *porter; if (self->session == NULL) return; porter = wocky_session_get_porter (self->session); if (priv->iq_shared_status_cb != 0) { wocky_porter_unregister_handler (porter, priv->iq_shared_status_cb); priv->iq_shared_status_cb = 0; } if (priv->iq_list_push_id != 0) { wocky_porter_unregister_handler (porter, priv->iq_list_push_id); priv->iq_list_push_id = 0; } } void conn_presence_finalize (GabbleConnection *conn) { GabbleConnectionPresencePrivate *priv = conn->presence_priv; g_free (priv->invisible_list_name); if (priv->privacy_statuses != NULL) g_hash_table_unref (priv->privacy_statuses); if (priv->shared_statuses != NULL) g_hash_table_unref (priv->shared_statuses); g_slice_free (GabbleConnectionPresencePrivate, priv); tp_presence_mixin_finalize ((GObject *) conn); } static void conn_presence_send_directed_presence ( GabbleSvcConnectionInterfaceGabbleDecloak *conn, guint contact, gboolean full, DBusGMethodInvocation *context) { GabbleConnection *self = GABBLE_CONNECTION (conn); TpBaseConnection *base = TP_BASE_CONNECTION (conn); TpHandleRepoIface *contact_handles = tp_base_connection_get_handles (base, TP_HANDLE_TYPE_CONTACT); const gchar *jid = tp_handle_inspect (contact_handles, contact); gboolean ok; GError *error = NULL; g_return_if_fail (jid != NULL); /* We don't strictly respect @full - we'll always send full presence to * people we think ought to be receiving it anyway, because if we didn't, * you could confuse them by sending directed presence that was less * informative than the broadcast presence they already saw. */ if (full || conn_presence_visible_to (self, contact)) { ok = conn_presence_signal_own_presence (self, jid, &error); } else { ok = gabble_connection_send_capabilities (self, jid, &error); } if (ok) { gabble_svc_connection_interface_gabble_decloak_return_from_send_directed_presence (context); } else { dbus_g_method_return_error (context, error); g_error_free (error); } } void conn_decloak_emit_requested (GabbleConnection *conn, TpHandle contact, const gchar *reason, gboolean decloaked) { gabble_svc_connection_interface_gabble_decloak_emit_decloak_requested (conn, contact, reason, decloaked); } void conn_decloak_iface_init (gpointer g_iface, gpointer iface_data) { #define IMPLEMENT(x) \ gabble_svc_connection_interface_gabble_decloak_implement_##x (\ g_iface, conn_presence_##x) IMPLEMENT (send_directed_presence); #undef IMPLEMENT } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������telepathy-gabble-0.18.2/src/conn-presence.h���������������������������������������������������������0000644�0001750�0001750�00000005172�12226776641�020536� 0����������������������������������������������������������������������������������������������������ustar�00smcv����������������������������smcv����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * conn-presence.h - Header for Gabble connection presence interface * Copyright (C) 2005-2007 Collabora Ltd. * Copyright (C) 2005-2007 Nokia Corporation * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 __CONN_PRESENCE_H__ #define __CONN_PRESENCE_H__ #include <glib.h> #include "connection.h" G_BEGIN_DECLS typedef enum { CONN_PRESENCE_ERROR_SET_INVISIBLE = 0, CONN_PRESENCE_ERROR_CREATE_PRIVACY_LIST, CONN_PRESENCE_ERROR_RETRIEVE_PRIVACY_LIST, CONN_PRESENCE_ERROR_INVALID_PRIVACY_LIST, CONN_PRESENCE_ERROR_SET_PRIVACY_LIST, CONN_PRESENCE_ERROR_SET_INVISIBLE_PRESENCE, CONN_PRESENCE_ERROR_SET_SHARED_STATUS } GabbleConnPresenceErrorType; GQuark conn_presence_error_quark (void); #define CONN_PRESENCE_ERROR (conn_presence_error_quark ()) void conn_presence_class_init (GabbleConnectionClass *klass); void conn_presence_init (GabbleConnection *conn); void conn_presence_finalize (GabbleConnection *conn); void conn_presence_dispose (GabbleConnection *self); void conn_presence_iface_init (gpointer g_iface, gpointer iface_data); void conn_presence_emit_presence_update ( GabbleConnection *, const GArray *contact_handles); gboolean conn_presence_signal_own_presence (GabbleConnection *self, const gchar *to, GError **error); gboolean conn_presence_visible_to (GabbleConnection *self, TpHandle recipient); void conn_presence_set_initial_presence_async (GabbleConnection *self, GAsyncReadyCallback callback, gpointer user_data); gboolean conn_presence_set_initial_presence_finish (GabbleConnection *self, GAsyncResult *result, GError **error); void conn_decloak_iface_init (gpointer g_iface, gpointer iface_data); void conn_decloak_emit_requested (GabbleConnection *conn, TpHandle contact, const gchar *reason, gboolean decloaked); TpConnectionPresenceType conn_presence_get_type (GabblePresence *presence); const TpPresenceStatusSpec *conn_presence_statuses (void); G_END_DECLS #endif /* __CONN_PRESENCE_H__ */ ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������telepathy-gabble-0.18.2/src/conn-power-saving.c�����������������������������������������������������0000644�0001750�0001750�00000012370�12200204333�021314� 0����������������������������������������������������������������������������������������������������ustar�00smcv����������������������������smcv����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * conn-power-saving.c - Header for Gabble connection code handling power saving * Copyright (C) 2010 Collabora Ltd. * Copyright (C) 2010 Nokia Corporation * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 "config.h" #include "conn-power-saving.h" #define DEBUG_FLAG GABBLE_DEBUG_CONNECTION #include "debug.h" #include "namespaces.h" #include "util.h" #include "conn-util.h" #include <wocky/wocky.h> enum { PROP_POWER_SAVING_ACTIVE, NUM_OF_PROP, }; typedef struct { DBusGMethodInvocation *dbus_context; gboolean enabling; } ToggleQueueingContext; static void google_queueing_send_command ( GabbleConnection *conn, const gchar *command, GAsyncReadyCallback callback, gpointer user_data) { WockyStanza *stanza = wocky_stanza_build (WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_SET, NULL, NULL, '(', "query", ':', NS_GOOGLE_QUEUE, '(', command, ')', ')', NULL); conn_util_send_iq_async (conn, stanza, NULL, callback, user_data); g_object_unref (stanza); } static void maybe_emit_power_saving_changed (GabbleConnection *self, gboolean enabling) { gboolean enabled; g_object_get (self, "power-saving", &enabled, NULL); if (enabling != enabled) { g_object_set (self, "power-saving", enabling, NULL); tp_svc_connection_interface_power_saving_emit_power_saving_changed ( self, enabling); } } static void toggle_google_queueing_cb (GObject *source_object, GAsyncResult *res, gpointer user_data) { GabbleConnection *self = GABBLE_CONNECTION (source_object); ToggleQueueingContext *queueing_context = (ToggleQueueingContext *) user_data; GError *error = NULL; gboolean enabling; enabling = queueing_context->enabling; if (!conn_util_send_iq_finish (self, res, NULL, &error)) { DEBUG ("Failed to %sable queueing: %s", enabling ? "en" : "dis", error->message); enabling = FALSE; dbus_g_method_return_error (queueing_context->dbus_context, error); g_error_free (error); } else { DEBUG ("%sabled queueing", enabling ? "en" : "dis"); tp_svc_connection_interface_power_saving_return_from_set_power_saving ( queueing_context->dbus_context); if (!enabling) google_queueing_send_command (self, "flush", NULL, NULL); } maybe_emit_power_saving_changed (self, enabling); g_slice_free (ToggleQueueingContext, queueing_context); } static void conn_power_saving_set_power_saving ( TpSvcConnectionInterfacePowerSaving *conn, gboolean enable, DBusGMethodInvocation *context) { GabbleConnection *self = GABBLE_CONNECTION (conn); TpBaseConnection *base = TP_BASE_CONNECTION (self); gboolean enabled; TP_BASE_CONNECTION_ERROR_IF_NOT_CONNECTED (base, context); g_object_get (G_OBJECT (self), "power-saving", &enabled, NULL); if (enable == enabled) { /* no-op */ tp_svc_connection_interface_power_saving_return_from_set_power_saving ( context); return; } DEBUG ("%sabling presence queueing", enable ? "en" : "dis"); /* google:queue is loosely described here: * <http://mail.jabber.org/pipermail/summit/2010-February/000528.html>. Since * April 2011, it is advertised as a stream feature by the Google Talk * server; the development version of M-Link, and possibly other servers, * also implement the protocol and advertise this stream feature. */ if (self->features & GABBLE_CONNECTION_FEATURES_GOOGLE_QUEUE) { ToggleQueueingContext *queueing_context; queueing_context = g_slice_new0 (ToggleQueueingContext); queueing_context->enabling = enable; queueing_context->dbus_context = context; google_queueing_send_command (self, enable ? "enable" : "disable", toggle_google_queueing_cb, queueing_context); } else { /* If the server doesn't support any method of queueing, we can still * do it locally by enabling power save mode on Wocky. */ WockyPorter *porter = gabble_connection_dup_porter (self); wocky_c2s_porter_enable_power_saving_mode (WOCKY_C2S_PORTER (porter), enable); DEBUG ("%sabled local stanza queueing", enable ? "En" : "Dis"); g_object_unref (porter); maybe_emit_power_saving_changed (self, enable); tp_svc_connection_interface_power_saving_return_from_set_power_saving ( context); } } void conn_power_saving_iface_init (gpointer g_iface, gpointer iface_data) { #define IMPLEMENT(x) \ tp_svc_connection_interface_power_saving_implement_##x (\ g_iface, conn_power_saving_##x) IMPLEMENT (set_power_saving); #undef IMPLEMENT } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������telepathy-gabble-0.18.2/src/conn-power-saving.h�����������������������������������������������������0000644�0001750�0001750�00000002176�12200204333�021324� 0����������������������������������������������������������������������������������������������������ustar�00smcv����������������������������smcv����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * conn-power-saving.h - Header for Gabble connection code handling power saving * Copyright (C) 2010 Collabora Ltd. * Copyright (C) 2010 Nokia Corporation * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 GABBLE_CONN_SLACKER_H #define GABBLE_CONN_SLACKER_H #include <glib.h> #include "connection.h" G_BEGIN_DECLS void conn_power_saving_iface_init (gpointer g_iface, gpointer iface_data); G_END_DECLS #endif /* GABBLE_CONN_SLACKER_H */ ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������telepathy-gabble-0.18.2/src/conn-olpc.c�������������������������������������������������������������0000644�0001750�0001750�00000257370�12200204333�017643� 0����������������������������������������������������������������������������������������������������ustar�00smcv����������������������������smcv����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * conn-olpc.c - Gabble OLPC BuddyInfo and ActivityProperties interfaces * Copyright (C) 2007 Collabora Ltd. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 "config.h" #include "conn-olpc.h" #include <string.h> #include <stdlib.h> #include <telepathy-glib/telepathy-glib.h> #define DEBUG_FLAG GABBLE_DEBUG_OLPC #include <gabble/error.h> #include "debug.h" #include "connection.h" #include "muc-channel.h" #include "presence-cache.h" #include "namespaces.h" #include "disco.h" #include "util.h" #include "olpc-activity.h" /* FIXME: At some point we should audit this code to check which assumptions * it does about buddy and activity and if they are still relevant. * For example, we currently allow the creation of activity objects which * don't have an ID. I'm not sure that really make sense. * Or at some place in the code, we allow user to change the ID of an existing * activity object which is probably bong too. */ static gboolean update_activities_properties (GabbleConnection *conn, const gchar *contact, WockyStanza *msg); /* * This function performs a depth-first search on @node to find any element * named @name in namespace @ns. This is usually not what you want because you * don't want a depth-first search of the entire hierarchy: you know at what * level of nesting you expect to find an element. Using this function rather * than wocky_node_get_child_ns() a couple of times opens you up to accepting * wildly misconstructed stanzas. Please think of the kittens. */ static WockyNode * search_for_child ( WockyNode *node, const gchar *name, const gchar *ns) { WockyNode *found, *child; WockyNodeIter i; found = wocky_node_get_child_ns (node, name, ns); if (found != NULL) return found; wocky_node_iter_init (&i, node, NULL, NULL); while (wocky_node_iter_next (&i, &child)) { found = search_for_child (child, name, ns); if (found != NULL) return found; } return NULL; } /* Returns TRUE if it actually contributed something, else FALSE. */ static gboolean activity_info_contribute_properties (GabbleOlpcActivity *activity, WockyNode *parent, gboolean only_public) { WockyNode *props_node; if (activity->id == NULL || activity->properties == NULL) return FALSE; if (only_public && !gabble_olpc_activity_is_visible (activity)) return FALSE; props_node = wocky_node_add_child_ns (parent, "properties", NS_OLPC_ACTIVITY_PROPS); wocky_node_set_attributes (props_node, "room", gabble_olpc_activity_get_room (activity), "activity", activity->id, NULL); lm_message_node_add_children_from_properties (props_node, activity->properties, "property"); return TRUE; } static void decrement_contacts_activities_set_foreach (TpHandleSet *set, TpHandle handle, gpointer data) { GabbleConnection *conn = data; GabbleOlpcActivity *activity = g_hash_table_lookup ( conn->olpc_activities_info, GUINT_TO_POINTER (handle)); g_object_unref (activity); } /* context may be NULL. */ static gboolean check_pep (GabbleConnection *conn, DBusGMethodInvocation *context) { if (!(conn->features & GABBLE_CONNECTION_FEATURES_PEP)) { GError error = { TP_ERROR, TP_ERROR_NETWORK_ERROR, "Server does not support PEP" }; DEBUG ("%s", error.message); if (context != NULL) dbus_g_method_return_error (context, &error); return FALSE; } return TRUE; } static const gchar * inspect_handle (TpBaseConnection *base, DBusGMethodInvocation *context, guint handle, TpHandleRepoIface *handle_repo) { GError *error = NULL; if (!tp_handle_is_valid (handle_repo, handle, &error)) { dbus_g_method_return_error (context, error); g_error_free (error); return NULL; } return tp_handle_inspect (handle_repo, handle); } static const gchar * inspect_contact (TpBaseConnection *base, DBusGMethodInvocation *context, guint contact) { TpHandleRepoIface *contact_repo = tp_base_connection_get_handles ( base, TP_HANDLE_TYPE_CONTACT); return inspect_handle (base, context, contact, contact_repo); } static const gchar * inspect_room (TpBaseConnection *base, DBusGMethodInvocation *context, guint room) { TpHandleRepoIface *room_repo = tp_base_connection_get_handles ( base, TP_HANDLE_TYPE_ROOM); return inspect_handle (base, context, room, room_repo); } /* context may be NULL, since this may be called in response to becoming * connected. */ static gboolean check_publish_reply_msg (WockyStanza *reply_msg, DBusGMethodInvocation *context) { GError *error = NULL; if (wocky_stanza_extract_errors (reply_msg, NULL, &error, NULL, NULL)) { GError *tp_error = NULL; gabble_set_tp_error_from_wocky (error, &tp_error); g_prefix_error (&tp_error, "Failed to publish to the PEP node: "); DEBUG ("%s", tp_error->message); if (context != NULL) dbus_g_method_return_error (context, tp_error); g_error_free (tp_error); g_error_free (error); return FALSE; } else { return TRUE; } } static gboolean check_query_reply_msg (WockyStanza *reply_msg, DBusGMethodInvocation *context) { GError *error = NULL; if (wocky_stanza_extract_errors (reply_msg, NULL, &error, NULL, NULL)) { GError *tp_error = NULL; gabble_set_tp_error_from_wocky (error, &tp_error); g_prefix_error (&tp_error, "Failed to query the PEP node: "); DEBUG ("%s", tp_error->message); if (context != NULL) dbus_g_method_return_error (context, tp_error); g_error_free (tp_error); g_error_free (error); return FALSE; } else { return TRUE; } } typedef struct { GabbleConnection *conn; DBusGMethodInvocation *context; } pubsub_query_ctx; static pubsub_query_ctx * pubsub_query_ctx_new (GabbleConnection *conn, DBusGMethodInvocation *context) { pubsub_query_ctx *ctx = g_slice_new (pubsub_query_ctx); ctx->conn = conn; ctx->context = context; return ctx; } static void pubsub_query_ctx_free (pubsub_query_ctx *ctx) { g_slice_free (pubsub_query_ctx, ctx); } static void get_properties_reply_cb (GObject *source, GAsyncResult *res, gpointer user_data) { pubsub_query_ctx *ctx = (pubsub_query_ctx *) user_data; WockyStanza *reply_msg; GError *error = NULL; GHashTable *properties; WockyNode *node; /* FIXME: can we just pass &node in here to get the <properties/>? */ reply_msg = wocky_pep_service_get_finish (WOCKY_PEP_SERVICE (source), res, NULL, &error); if (reply_msg == NULL) { GError err = { TP_ERROR, TP_ERROR_NETWORK_ERROR, "Failed to send property request to server" }; DEBUG ("Query failed: %s", error->message); dbus_g_method_return_error (ctx->context, &err); g_error_free (error); goto out; } if (!check_query_reply_msg (reply_msg, ctx->context)) goto out; node = search_for_child ( wocky_stanza_get_top_node (reply_msg), "properties", NULL); properties = lm_message_node_extract_properties (node, "property"); gabble_svc_olpc_buddy_info_return_from_get_properties (ctx->context, properties); g_hash_table_unref (properties); out: pubsub_query_ctx_free (ctx); if (reply_msg != NULL) g_object_unref (reply_msg); } static void olpc_buddy_info_get_properties (GabbleSvcOLPCBuddyInfo *iface, guint handle, DBusGMethodInvocation *context) { GabbleConnection *conn = GABBLE_CONNECTION (iface); TpBaseConnection *base = (TpBaseConnection *) conn; const gchar *jid; pubsub_query_ctx *ctx; WockyBareContact *contact; DEBUG ("called"); gabble_connection_ensure_capabilities (conn, gabble_capabilities_get_olpc_notify ()); if (!check_pep (conn, context)) return; jid = inspect_contact (base, context, handle); if (jid == NULL) return; ctx = pubsub_query_ctx_new (conn, context); contact = ensure_bare_contact_from_jid (conn, jid); wocky_pep_service_get_async (conn->pep_olpc_buddy_props, contact, NULL, get_properties_reply_cb, ctx); g_object_unref (contact); } /* context may be NULL. */ static void set_properties_reply_cb (GabbleConnection *conn, WockyStanza *sent_msg, WockyStanza *reply_msg, GObject *object, gpointer user_data) { DBusGMethodInvocation *context = user_data; if (!check_publish_reply_msg (reply_msg, context)) return; if (context != NULL) gabble_svc_olpc_buddy_info_return_from_set_properties (context); } /* context may be NULL, in which case it will be NULL in the reply_cb. */ static void transmit_properties (GabbleConnection *conn, GHashTable *properties, DBusGMethodInvocation *context) { WockyStanza *msg; WockyNode *item, *props_node; gabble_connection_ensure_capabilities (conn, gabble_capabilities_get_olpc_notify ()); if (!check_pep (conn, context)) return; msg = wocky_pep_service_make_publish_stanza (conn->pep_olpc_buddy_props, &item); props_node = wocky_node_add_child_ns (item, "properties", NS_OLPC_BUDDY_PROPS); lm_message_node_add_children_from_properties (props_node, properties, "property"); if (!_gabble_connection_send_with_reply (conn, msg, set_properties_reply_cb, NULL, context, NULL)) { GError error = { TP_ERROR, TP_ERROR_NETWORK_ERROR, "Failed to send property change request to server" }; DEBUG ("%s", error.message); if (context != NULL) dbus_g_method_return_error (context, &error); } g_object_unref (msg); } static GQuark preload_buddy_properties_quark (void) { static GQuark q = 0; if (q == 0) { q = g_quark_from_static_string ("GabbleConnection.preload_buddy_properties_quark"); } return q; } static GQuark invitees_quark (void) { static GQuark q = 0; if (q == 0) { q = g_quark_from_static_string ("GabbleConnection.conn_olpc_invitees_quark"); } return q; } static void gabble_connection_connected_olpc (GabbleConnection *conn) { GHashTable *preload = g_object_steal_qdata ((GObject *) conn, preload_buddy_properties_quark ()); if (preload != NULL) { transmit_properties (conn, preload, NULL); g_hash_table_unref (preload); } } static void olpc_buddy_info_set_properties (GabbleSvcOLPCBuddyInfo *iface, GHashTable *properties, DBusGMethodInvocation *context) { GabbleConnection *conn = GABBLE_CONNECTION (iface); TpBaseConnection *base = (TpBaseConnection *) conn; DEBUG ("called"); if (tp_base_connection_get_status (base) == TP_CONNECTION_STATUS_CONNECTED) { transmit_properties (conn, properties, context); } else { GHashTable *preload; GQuark preload_quark = preload_buddy_properties_quark (); DEBUG ("Not connected: will perform OLPC buddy property update later"); preload = g_object_get_qdata ((GObject *) conn, preload_quark); if (preload != NULL) { /* throw away any already-preloaded properties - SetProperties * is an overwrite, not an update */ g_hash_table_remove_all (preload); } else { preload = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify) tp_g_value_slice_free); g_object_set_qdata_full ((GObject *) conn, preload_quark, preload, (GDestroyNotify) g_hash_table_unref); } tp_g_hash_table_update (preload, properties, (GBoxedCopyFunc) g_strdup, (GBoxedCopyFunc) tp_g_value_slice_dup); gabble_svc_olpc_buddy_info_return_from_set_properties (context); } } static void olpc_buddy_props_pep_node_changed (WockyPepService *pep, WockyBareContact *contact, WockyStanza *stanza, WockyNode *item, GabbleConnection *conn) { TpHandleRepoIface *contact_repo = tp_base_connection_get_handles ( (TpBaseConnection *) conn, TP_HANDLE_TYPE_CONTACT); GHashTable *properties; WockyNode *node; TpBaseConnection *base = (TpBaseConnection *) conn; TpHandle handle; const gchar *jid; jid = wocky_bare_contact_get_jid (contact); handle = tp_handle_ensure (contact_repo, jid, NULL, NULL); if (handle == 0) { DEBUG ("Invalid from: %s", jid); return; } if (handle == tp_base_connection_get_self_handle (base)) /* Ignore echoed pubsub notifications */ return; node = search_for_child ( wocky_stanza_get_top_node (stanza), "properties", NULL); properties = lm_message_node_extract_properties (node, "property"); gabble_svc_olpc_buddy_info_emit_properties_changed (conn, handle, properties); g_hash_table_unref (properties); } static void get_activity_properties_reply_cb (GObject *source, GAsyncResult *res, gpointer user_data) { GabbleConnection *conn = GABBLE_CONNECTION (user_data); const gchar *from; WockyStanza *reply_msg; GError *error = NULL; reply_msg = wocky_pep_service_get_finish (WOCKY_PEP_SERVICE (source), res, NULL, &error); if (reply_msg == NULL) { DEBUG ("Failed to send activity properties request to server: %s", error->message); g_error_free (error); return; } from = wocky_node_get_attribute ( wocky_stanza_get_top_node (reply_msg), "from"); update_activities_properties (conn, from, reply_msg); g_object_unref (reply_msg); } static gboolean remove_activity (gpointer key, gpointer value, gpointer activity) { return activity == value; } static void activity_disposed_cb (gpointer _conn, GObject *activity) { GabbleConnection *conn = GABBLE_CONNECTION (_conn); if (conn->olpc_activities_info == NULL) /* We are disposing */ return; g_hash_table_foreach_remove (conn->olpc_activities_info, remove_activity, activity); } static GabbleOlpcActivity * add_activity_info (GabbleConnection *conn, TpHandle handle) { GabbleOlpcActivity *activity; activity = gabble_olpc_activity_new (conn, handle); g_hash_table_insert (conn->olpc_activities_info, GUINT_TO_POINTER (handle), activity); g_object_weak_ref (G_OBJECT (activity), activity_disposed_cb, conn); return activity; } static GPtrArray * get_buddy_activities (GabbleConnection *conn, TpHandle buddy) { TpIntset *all; gboolean free_all = FALSE; GPtrArray *activities = g_ptr_array_new (); TpHandleSet *invited_activities, *pep_activities; invited_activities = g_hash_table_lookup (conn->olpc_invited_activities, GUINT_TO_POINTER (buddy)); pep_activities = g_hash_table_lookup (conn->olpc_pep_activities, GUINT_TO_POINTER (buddy)); if (invited_activities == NULL) { if (pep_activities == NULL) { all = NULL; } else { all = tp_handle_set_peek (pep_activities); } } else { if (pep_activities == NULL) { all = tp_handle_set_peek (invited_activities); } else { all = tp_intset_union (tp_handle_set_peek (invited_activities), tp_handle_set_peek (pep_activities)); free_all = TRUE; } } if (all != NULL) { TpIntsetFastIter iter; guint element; tp_intset_fast_iter_init (&iter, all); while (tp_intset_fast_iter_next (&iter, &element)) { GabbleOlpcActivity *activity = g_hash_table_lookup ( conn->olpc_activities_info, GUINT_TO_POINTER (element)); GValue gvalue = { 0 }; g_assert (activity != NULL); if (activity->id == NULL) { DEBUG ("... activity #%u has no ID, skipping", element); continue; } g_value_init (&gvalue, GABBLE_STRUCT_TYPE_ACTIVITY); g_value_take_boxed (&gvalue, dbus_g_type_specialized_construct (GABBLE_STRUCT_TYPE_ACTIVITY)); dbus_g_type_struct_set (&gvalue, 0, activity->id, 1, activity->room, G_MAXUINT); DEBUG ("... activity #%u (ID %s)", activity->room, activity->id); g_ptr_array_add (activities, g_value_get_boxed (&gvalue)); } } if (free_all) tp_intset_destroy (all); return activities; } static void extract_activities (GabbleConnection *conn, WockyStanza *msg, TpHandle sender) { WockyNode *activities_node; TpHandleSet *activities_set, *old_activities; TpHandleRepoIface *room_repo = tp_base_connection_get_handles ( (TpBaseConnection *) conn, TP_HANDLE_TYPE_ROOM); WockyNodeIter i; activities_node = search_for_child ( wocky_stanza_get_top_node (msg), "activities", NULL); activities_set = tp_handle_set_new (room_repo); if (activities_node != NULL) { WockyNode *node; wocky_node_iter_init (&i, activities_node, "activity", NULL); while (wocky_node_iter_next (&i, &node)) { const gchar *act_id; const gchar *room; GabbleOlpcActivity *activity; TpHandle room_handle; act_id = wocky_node_get_attribute (node, "type"); if (act_id == NULL) { NODE_DEBUG (node, "No activity ID, skipping"); continue; } room = wocky_node_get_attribute (node, "room"); if (room == NULL) { NODE_DEBUG (node, "No room name, skipping"); continue; } room_handle = tp_handle_ensure (room_repo, room, NULL, NULL); if (room_handle == 0) { DEBUG ("Invalid room name <%s>, skipping", room); continue; } activity = g_hash_table_lookup (conn->olpc_activities_info, GUINT_TO_POINTER (room_handle)); if (activity == NULL) { activity = add_activity_info (conn, room_handle); g_assert (!tp_handle_set_is_member (activities_set, room_handle)); } else { if (tp_handle_set_is_member (activities_set, room_handle)) { NODE_DEBUG (node, "Room advertised twice, skipping"); continue; } g_object_ref (activity); DEBUG ("ref: %s (%d) refcount: %d\n", gabble_olpc_activity_get_room (activity), activity->room, G_OBJECT (activity)->ref_count); } /* pass ownership to the activities_set */ tp_handle_set_add (activities_set, room_handle); if (tp_strdiff (activity->id, act_id)) { DEBUG ("Assigning new ID <%s> to room #%u <%s>", act_id, room_handle, room); g_object_set (activity, "id", act_id, NULL); } } } old_activities = g_hash_table_lookup (conn->olpc_pep_activities, GUINT_TO_POINTER (sender)); if (old_activities != NULL) { /* We decrement the refcount (and free if needed) all the * activities previously announced by this contact. */ tp_handle_set_foreach (old_activities, decrement_contacts_activities_set_foreach, conn); } /* Update the list of activities associated with this contact. */ g_hash_table_insert (conn->olpc_pep_activities, GUINT_TO_POINTER (sender), activities_set); } static void free_activities (GPtrArray *activities) { guint i; for (i = 0; i < activities->len; i++) g_boxed_free (GABBLE_STRUCT_TYPE_ACTIVITY, activities->pdata[i]); g_ptr_array_unref (activities); } static void check_activity_properties (GabbleConnection *conn, GPtrArray *activities, const gchar *from) { /* XXX: dirty hack! * We use PEP instead of pubsub until we have MEP. * When we request activities from a remote contact we need to check * if we already "know" his activities (we have its properties). * If not, we need to explicitely ask to the user to send them to us. * When we'll have MEP we will be able to request activities * propreties from muc's pubsub node and so avoid all this crack. */ gboolean query_needed = FALSE; guint i; for (i = 0; i < activities->len && !query_needed; i++) { GValue pair = {0,}; guint channel; GabbleOlpcActivity *activity; g_value_init (&pair, GABBLE_STRUCT_TYPE_ACTIVITY); g_value_set_static_boxed (&pair, g_ptr_array_index (activities, i)); dbus_g_type_struct_get (&pair, 1, &channel, G_MAXUINT); activity = g_hash_table_lookup (conn->olpc_activities_info, GUINT_TO_POINTER (channel)); if (activity == NULL || activity->properties == NULL) { query_needed = TRUE; } } if (query_needed) { WockyBareContact *contact = ensure_bare_contact_from_jid (conn, from); wocky_pep_service_get_async (conn->pep_olpc_act_props, contact, NULL, get_activity_properties_reply_cb, conn); g_object_unref (contact); } } static void get_activities_reply_cb (GObject *source, GAsyncResult *res, gpointer user_data) { pubsub_query_ctx *ctx = (pubsub_query_ctx *) user_data; WockyStanza *reply_msg; GError *err = NULL; GPtrArray *activities; const gchar *from; TpHandle from_handle; TpHandleRepoIface *contact_repo = tp_base_connection_get_handles ( (TpBaseConnection *) ctx->conn, TP_HANDLE_TYPE_CONTACT); GError *stanza_error = NULL; reply_msg = wocky_pep_service_get_finish (WOCKY_PEP_SERVICE (source), res, NULL, &err); if (reply_msg == NULL) { GError error = { TP_ERROR, TP_ERROR_NETWORK_ERROR, "Failed to send property request to server" }; DEBUG ("Query failed: %s", err->message); dbus_g_method_return_error (ctx->context, &error); g_error_free (err); goto out; } from = wocky_node_get_attribute ( wocky_stanza_get_top_node (reply_msg), "from"); if (from == NULL) { GError error = { TP_ERROR, TP_ERROR_NETWORK_ERROR, "Error in pubsub reply: no sender" }; dbus_g_method_return_error (ctx->context, &error); goto out; } from_handle = tp_handle_lookup (contact_repo, from, NULL, NULL); if (from_handle == 0) { GError error = { TP_ERROR, TP_ERROR_NETWORK_ERROR, "Error in pubsub reply: unknown sender" }; dbus_g_method_return_error (ctx->context, &error); goto out; } if (wocky_stanza_extract_errors (reply_msg, NULL, &stanza_error, NULL, NULL)) { GError *tp_error = NULL; gabble_set_tp_error_from_wocky (stanza_error, &tp_error); g_prefix_error (&tp_error, "Error in pubsub reply: "); dbus_g_method_return_error (ctx->context, tp_error); g_clear_error (&tp_error); g_clear_error (&stanza_error); goto out; } extract_activities (ctx->conn, reply_msg, from_handle); activities = get_buddy_activities (ctx->conn, from_handle); /* FIXME: race between client and PEP */ check_activity_properties (ctx->conn, activities, from); gabble_svc_olpc_buddy_info_return_from_get_activities (ctx->context, activities); free_activities (activities); out: pubsub_query_ctx_free (ctx); if (reply_msg != NULL) g_object_unref (reply_msg); } static void olpc_buddy_info_get_activities (GabbleSvcOLPCBuddyInfo *iface, guint handle, DBusGMethodInvocation *context) { GabbleConnection *conn = GABBLE_CONNECTION (iface); TpBaseConnection *base = (TpBaseConnection *) conn; const gchar *jid; pubsub_query_ctx *ctx; WockyBareContact *contact; DEBUG ("called"); gabble_connection_ensure_capabilities (conn, gabble_capabilities_get_olpc_notify ()); if (!check_pep (conn, context)) return; jid = inspect_contact (base, context, handle); if (jid == NULL) return; ctx = pubsub_query_ctx_new (conn, context); contact = ensure_bare_contact_from_jid (conn, jid); wocky_pep_service_get_async (conn->pep_olpc_activities, contact, NULL, get_activities_reply_cb, ctx); g_object_unref (contact); } /* FIXME: API could be improved */ static gboolean upload_activities_pep (GabbleConnection *conn, GabbleConnectionMsgReplyFunc callback, gpointer user_data, GError **error) { TpBaseConnection *base = (TpBaseConnection *) conn; WockyNode *item, *activities; WockyStanza *msg; TpHandleSet *my_activities = g_hash_table_lookup (conn->olpc_pep_activities, GUINT_TO_POINTER (tp_base_connection_get_self_handle (base))); GError *e = NULL; gboolean ret; msg = wocky_pep_service_make_publish_stanza (conn->pep_olpc_activities, &item); activities = wocky_node_add_child_ns (item, "activities", NS_OLPC_ACTIVITIES); if (my_activities != NULL) { TpIntsetFastIter iter; guint element; tp_intset_fast_iter_init (&iter, tp_handle_set_peek (my_activities)); while (tp_intset_fast_iter_next (&iter, &element)) { GabbleOlpcActivity *activity = g_hash_table_lookup ( conn->olpc_activities_info, GUINT_TO_POINTER (element)); WockyNode *activity_node; g_assert (activity != NULL); if (!gabble_olpc_activity_is_visible (activity)) continue; activity_node = wocky_node_add_child_with_content (activities, "activity", ""); wocky_node_set_attributes (activity_node, "type", activity->id, "room", gabble_olpc_activity_get_room (activity), NULL); } } ret = _gabble_connection_send_with_reply (conn, msg, callback, NULL, user_data, &e); if (!ret) { g_set_error (error, TP_ERROR, TP_ERROR_NETWORK_ERROR, "Failed to send property change request to server: %s", e->message); g_error_free (e); } g_object_unref (msg); return ret; } static void set_activities_reply_cb (GabbleConnection *conn, WockyStanza *sent_msg, WockyStanza *reply_msg, GObject *object, gpointer user_data) { DBusGMethodInvocation *context = user_data; if (!check_publish_reply_msg (reply_msg, context)) return; /* FIXME: emit ActivitiesChanged? */ gabble_svc_olpc_buddy_info_return_from_set_activities (context); } static gboolean add_activity (GabbleConnection *self, const gchar *id, guint channel, GError **error) { TpBaseConnection *base = (TpBaseConnection *) self; TpHandleRepoIface *room_repo = tp_base_connection_get_handles ( base, TP_HANDLE_TYPE_ROOM); TpHandleSet *old_activities = g_hash_table_lookup (self->olpc_pep_activities, GUINT_TO_POINTER (tp_base_connection_get_self_handle (base))); GabbleOlpcActivity *activity; if (!tp_handle_is_valid (room_repo, channel, error)) { DEBUG ("Invalid room handle %d", channel); return FALSE; } if (old_activities != NULL && tp_handle_set_is_member (old_activities, channel)) { *error = g_error_new (TP_ERROR, TP_ERROR_INVALID_ARGUMENT, "Can't set twice the same activity: %s", id); DEBUG ("activity already added: %s", id); return FALSE; } activity = g_hash_table_lookup (self->olpc_activities_info, GUINT_TO_POINTER (channel)); if (activity == NULL) { activity = add_activity_info (self, channel); } g_object_ref (activity); DEBUG ("ref: %s (%d) refcount: %d\n", gabble_olpc_activity_get_room (activity), activity->room, G_OBJECT (activity)->ref_count); g_object_set (activity, "id", id, NULL); return TRUE; } static void olpc_buddy_info_set_activities (GabbleSvcOLPCBuddyInfo *iface, const GPtrArray *activities, DBusGMethodInvocation *context) { GabbleConnection *conn = GABBLE_CONNECTION (iface); TpBaseConnection *base = (TpBaseConnection *) conn; TpHandleRepoIface *room_repo = tp_base_connection_get_handles ( base, TP_HANDLE_TYPE_ROOM); guint i; TpHandleSet *activities_set, *old_activities; DEBUG ("called"); gabble_connection_ensure_capabilities (conn, gabble_capabilities_get_olpc_notify ()); if (!check_pep (conn, context)) return; activities_set = tp_handle_set_new (room_repo); for (i = 0; i < activities->len; i++) { GValue pair = {0,}; gchar *id; guint channel; const gchar *room = NULL; GabbleOlpcActivity *activity; GError *error = NULL; g_value_init (&pair, GABBLE_STRUCT_TYPE_ACTIVITY); g_value_set_static_boxed (&pair, g_ptr_array_index (activities, i)); dbus_g_type_struct_get (&pair, 0, &id, 1, &channel, G_MAXUINT); if (!tp_handle_is_valid (room_repo, channel, &error)) { DEBUG ("Invalid room handle"); dbus_g_method_return_error (context, error); /* We have to unref information previously * refed in this loop */ tp_handle_set_foreach (activities_set, decrement_contacts_activities_set_foreach, conn); /* set_activities failed so we don't unref old activities * of the local user */ tp_handle_set_destroy (activities_set); g_error_free (error); g_free (id); return; } room = tp_handle_inspect (room_repo, channel); activity = g_hash_table_lookup (conn->olpc_activities_info, GUINT_TO_POINTER (channel)); if (activity == NULL) { activity = add_activity_info (conn, channel); } else { if (tp_handle_set_is_member (activities_set, channel)) { error = g_error_new (TP_ERROR, TP_ERROR_INVALID_ARGUMENT, "Can't set twice the same activity: %s", room); DEBUG ("activity already added: %s", room); dbus_g_method_return_error (context, error); /* We have to unref information previously * refed in this loop */ tp_handle_set_foreach (activities_set, decrement_contacts_activities_set_foreach, conn); /* set_activities failed so we don't unref old activities * of the local user */ tp_handle_set_destroy (activities_set); g_error_free (error); g_free (activity); g_free (id); return; } g_object_ref (activity); DEBUG ("ref: %s (%d) refcount: %d\n", gabble_olpc_activity_get_room (activity), activity->room, G_OBJECT (activity)->ref_count); } g_object_set (activity, "id", id, NULL); g_free (id); tp_handle_set_add (activities_set, channel); } old_activities = g_hash_table_lookup (conn->olpc_pep_activities, GUINT_TO_POINTER (tp_base_connection_get_self_handle (base))); if (old_activities != NULL) { /* We decrement the refcount (and free if needed) all the * activities previously announced by our own contact. */ tp_handle_set_foreach (old_activities, decrement_contacts_activities_set_foreach, conn); } /* Update the list of activities associated with our own contact. */ g_hash_table_insert (conn->olpc_pep_activities, GUINT_TO_POINTER (tp_base_connection_get_self_handle (base)), activities_set); if (!upload_activities_pep (conn, set_activities_reply_cb, context, NULL)) { GError error = { TP_ERROR, TP_ERROR_NETWORK_ERROR, "Failed to send property request to server" }; dbus_g_method_return_error (context, &error); } /* FIXME: what if we were advertising properties for things that * we've declared are no longer in our activities list? Strictly speaking * we should probably re-upload our activity properties PEP if that's * the case */ } static void olpc_activities_pep_node_changed (WockyPepService *pep, WockyBareContact *contact, WockyStanza *stanza, WockyNode *item, GabbleConnection *conn) { TpHandleRepoIface *contact_repo = tp_base_connection_get_handles ( (TpBaseConnection *) conn, TP_HANDLE_TYPE_CONTACT); GPtrArray *activities; TpBaseConnection *base = (TpBaseConnection *) conn; TpHandle handle; const gchar *jid; jid = wocky_bare_contact_get_jid (contact); handle = tp_handle_ensure (contact_repo, jid, NULL, NULL); if (handle == 0) { DEBUG ("Invalid from: %s", jid); return; } if (handle != tp_base_connection_get_self_handle (base)) extract_activities (conn, stanza, handle); activities = get_buddy_activities (conn, handle); gabble_svc_olpc_buddy_info_emit_activities_changed (conn, handle, activities); free_activities (activities); } static GabbleOlpcActivity * add_activity_info_in_set (GabbleConnection *conn, TpHandle room_handle, const gchar *from, GHashTable *table) { GabbleOlpcActivity *activity; TpHandle from_handle; TpHandleSet *activities_set; TpHandleRepoIface *contact_repo = tp_base_connection_get_handles ( (TpBaseConnection *) conn, TP_HANDLE_TYPE_CONTACT); TpHandleRepoIface *room_repo = tp_base_connection_get_handles ( (TpBaseConnection *) conn, TP_HANDLE_TYPE_ROOM); from_handle = tp_handle_lookup (contact_repo, from, NULL, NULL); if (from_handle == 0) { DEBUG ("unknown sender"); return NULL; } activity = add_activity_info (conn, room_handle); /* Add activity information in the list of the contact */ activities_set = g_hash_table_lookup (table, GUINT_TO_POINTER ( from_handle)); if (activities_set == NULL) { activities_set = tp_handle_set_new (room_repo); g_hash_table_insert (table, GUINT_TO_POINTER (from_handle), activities_set); } /* add_activity_info_in_set isn't meant to be called if the * activity already existed */ g_assert (!tp_handle_set_is_member (activities_set, room_handle)); /* the set owns the ref of the newly created activity */ tp_handle_set_add (activities_set, room_handle); return activity; } static GabbleOlpcActivity * extract_current_activity (GabbleConnection *conn, WockyNode *node, const gchar *contact, gboolean create_activity) { const gchar *room, *id; GabbleOlpcActivity *activity; TpHandleRepoIface *room_repo = tp_base_connection_get_handles ( (TpBaseConnection *) conn, TP_HANDLE_TYPE_ROOM); TpHandleRepoIface *contact_repo = tp_base_connection_get_handles ( (TpBaseConnection *) conn, TP_HANDLE_TYPE_CONTACT); TpHandle room_handle, contact_handle; if (node == NULL) return NULL; /* For some weird reasons, the PEP protocol use "type" for the activity ID. * We can't change that without breaking compatibility but if there is no * "type" attribute then we can use the "id" one. */ id = wocky_node_get_attribute (node, "type"); if (id == NULL) { id = wocky_node_get_attribute (node, "id"); } room = wocky_node_get_attribute (node, "room"); if (room == NULL || room[0] == '\0') return NULL; room_handle = tp_handle_ensure (room_repo, room, NULL, NULL); if (room_handle == 0) return NULL; contact_handle = tp_handle_lookup (contact_repo, contact, NULL, NULL); if (contact_handle == 0) return NULL; activity = g_hash_table_lookup (conn->olpc_activities_info, GUINT_TO_POINTER (room_handle)); if (activity == NULL && create_activity) { /* Humm we received as current activity an activity we don't know yet. * If the remote user doesn't announce this activity * in his next activities list, information about * it will be freed */ DEBUG ("unknown current activity %s", room); activity = add_activity_info_in_set (conn, room_handle, contact, conn->olpc_pep_activities); } /* update current-activity cache */ if (activity != NULL) { g_object_set (activity, "id", id, NULL); g_hash_table_insert (conn->olpc_current_act, GUINT_TO_POINTER (contact_handle), g_object_ref (activity)); } else { g_hash_table_remove (conn->olpc_current_act, GUINT_TO_POINTER (contact_handle)); } return activity; } static void get_current_activity_reply_cb (GObject *source, GAsyncResult *res, gpointer user_data) { pubsub_query_ctx *ctx = (pubsub_query_ctx *) user_data; WockyStanza *reply_msg; GError *error = NULL; WockyNode *node; const gchar *from; GabbleOlpcActivity *activity; reply_msg = wocky_pep_service_get_finish (WOCKY_PEP_SERVICE (source), res, NULL, &error); if (reply_msg == NULL) { GError err = { TP_ERROR, TP_ERROR_NETWORK_ERROR, "Failed to send property request to server" }; DEBUG ("Query failed: %s", error->message); dbus_g_method_return_error (ctx->context, &err); g_error_free (error); goto out; } if (wocky_stanza_extract_errors (reply_msg, NULL, NULL, NULL, NULL)) { DEBUG ("Failed to query PEP node. No current activity"); gabble_svc_olpc_buddy_info_return_from_get_current_activity (ctx->context, "", 0); goto out; } from = wocky_node_get_attribute ( wocky_stanza_get_top_node (reply_msg), "from"); node = search_for_child ( wocky_stanza_get_top_node (reply_msg), "activity", NULL); activity = extract_current_activity (ctx->conn, node, from, TRUE); if (activity == NULL) { DEBUG ("GetCurrentActivity returns no activity"); gabble_svc_olpc_buddy_info_return_from_get_current_activity (ctx->context, "", 0); } else { DEBUG ("GetCurrentActivity returns (\"%s\", room#%u)", activity->id, activity->room); gabble_svc_olpc_buddy_info_return_from_get_current_activity (ctx->context, activity->id, activity->room); } out: pubsub_query_ctx_free (ctx); if (reply_msg != NULL) g_object_unref (reply_msg); } static void olpc_buddy_info_get_current_activity (GabbleSvcOLPCBuddyInfo *iface, guint handle, DBusGMethodInvocation *context) { GabbleConnection *conn = GABBLE_CONNECTION (iface); TpBaseConnection *base = (TpBaseConnection *) conn; const gchar *jid; GabbleOlpcActivity *activity; pubsub_query_ctx *ctx; WockyBareContact *contact; DEBUG ("called for contact#%u", handle); gabble_connection_ensure_capabilities (conn, gabble_capabilities_get_olpc_notify ()); if (!check_pep (conn, context)) return; jid = inspect_contact (base, context, handle); if (jid == NULL) return; activity = g_hash_table_lookup (conn->olpc_current_act, GUINT_TO_POINTER (handle)); if (activity != NULL) { DEBUG ("found current activity in cache: %s (%u)", activity->id, activity->room); gabble_svc_olpc_buddy_info_return_from_get_current_activity (context, activity->id, activity->room); return; } DEBUG ("current activity not in cache, query PEP node"); ctx = pubsub_query_ctx_new (conn, context); contact = ensure_bare_contact_from_jid (conn, jid); wocky_pep_service_get_async (conn->pep_olpc_current_act, contact, NULL, get_current_activity_reply_cb, ctx); g_object_unref (contact); } static void set_current_activity_reply_cb (GabbleConnection *conn, WockyStanza *sent_msg, WockyStanza *reply_msg, GObject *object, gpointer user_data) { DBusGMethodInvocation *context = user_data; if (!check_publish_reply_msg (reply_msg, context)) return; gabble_svc_olpc_buddy_info_return_from_set_current_activity (context); } /* Check if this activity is in our own activities list */ static gboolean activity_in_own_set (GabbleConnection *conn, const gchar *room) { TpBaseConnection *base = (TpBaseConnection *) conn; TpHandleSet *activities_set; TpHandleRepoIface *room_repo = tp_base_connection_get_handles ( (TpBaseConnection *) conn, TP_HANDLE_TYPE_ROOM); TpHandle room_handle; room_handle = tp_handle_lookup (room_repo, room, NULL, NULL); if (room_handle == 0) /* If activity's information was in the list, we would * have found the handle as Activity keep a ref on it */ return FALSE; activities_set = g_hash_table_lookup (conn->olpc_pep_activities, GUINT_TO_POINTER (tp_base_connection_get_self_handle (base))); if (activities_set == NULL || !tp_handle_set_is_member (activities_set, room_handle)) return FALSE; return TRUE; } static void olpc_buddy_info_set_current_activity (GabbleSvcOLPCBuddyInfo *iface, const gchar *activity, guint channel, DBusGMethodInvocation *context) { GabbleConnection *conn = GABBLE_CONNECTION (iface); TpBaseConnection *base = (TpBaseConnection *) conn; WockyStanza *msg; WockyNode *item, *publish; const gchar *room = ""; DEBUG ("called"); gabble_connection_ensure_capabilities (conn, gabble_capabilities_get_olpc_notify ()); if (!check_pep (conn, context)) return; /* if activity == "" there is no current activity */ if (activity[0] != '\0') { room = inspect_room (base, context, channel); if (room == NULL) return; if (!activity_in_own_set (conn, room)) { GError error = { TP_ERROR, TP_ERROR_INVALID_ARGUMENT, "Can't set an activity as current if you're not announcing it" }; dbus_g_method_return_error (context, &error); return; } } msg = wocky_pep_service_make_publish_stanza (conn->pep_olpc_current_act, &item); publish = wocky_node_add_child_ns (item, "activity", NS_OLPC_CURRENT_ACTIVITY); wocky_node_set_attributes (publish, "type", activity, "room", room, NULL); if (!_gabble_connection_send_with_reply (conn, msg, set_current_activity_reply_cb, NULL, context, NULL)) { GError error = { TP_ERROR, TP_ERROR_NETWORK_ERROR, "Failed to send property change request to server" }; dbus_g_method_return_error (context, &error); } g_object_unref (msg); } static void olpc_current_act_pep_node_changed (WockyPepService *pep, WockyBareContact *contact, WockyStanza *stanza, WockyNode *item, GabbleConnection *conn) { TpHandleRepoIface *contact_repo = tp_base_connection_get_handles ( (TpBaseConnection *) conn, TP_HANDLE_TYPE_CONTACT); TpBaseConnection *base = (TpBaseConnection *) conn; WockyNode *node; GabbleOlpcActivity *activity; TpHandle handle; const gchar *jid; jid = wocky_bare_contact_get_jid (contact); handle = tp_handle_ensure (contact_repo, jid, NULL, NULL); if (handle == 0) { DEBUG ("Invalid from: %s", jid); return; } if (handle == tp_base_connection_get_self_handle (base)) /* Ignore echoed pubsub notifications */ return; node = search_for_child (wocky_stanza_get_top_node (stanza), "activity", NULL); activity = extract_current_activity (conn, node, jid, TRUE); if (activity != NULL) { DEBUG ("emitting CurrentActivityChanged(contact#%u, ID \"%s\", room#%u)", handle, activity->id, activity->room); gabble_svc_olpc_buddy_info_emit_current_activity_changed (conn, handle, activity->id, activity->room); } else { DEBUG ("emitting CurrentActivityChanged(contact#%u, \"\", 0)", handle); gabble_svc_olpc_buddy_info_emit_current_activity_changed (conn, handle, "", 0); } } static void add_activity_reply_cb (GabbleConnection *conn, WockyStanza *sent_msg, WockyStanza *reply_msg, GObject *object, gpointer user_data) { DBusGMethodInvocation *context = user_data; if (!check_publish_reply_msg (reply_msg, context)) return; /* FIXME: emit ActivitiesChanged? */ gabble_svc_olpc_buddy_info_return_from_add_activity (context); } static void olpc_buddy_info_add_activity (GabbleSvcOLPCBuddyInfo *iface, const gchar *id, guint channel, DBusGMethodInvocation *context) { GabbleConnection *self = GABBLE_CONNECTION (iface); TpBaseConnection *base = (TpBaseConnection *) self; TpHandleSet *activities_set = g_hash_table_lookup (self->olpc_pep_activities, GUINT_TO_POINTER (tp_base_connection_get_self_handle (base))); TpHandleRepoIface *room_repo = tp_base_connection_get_handles (base, TP_HANDLE_TYPE_ROOM); GError *error = NULL; gabble_connection_ensure_capabilities (self, gabble_capabilities_get_olpc_notify ()); if (!check_pep (self, context)) return; if (!add_activity (self, id, channel, &error)) { dbus_g_method_return_error (context, error); return; } if (activities_set == NULL) { activities_set = tp_handle_set_new (room_repo); g_hash_table_insert (self->olpc_pep_activities, GUINT_TO_POINTER (tp_base_connection_get_self_handle (base)), activities_set); } tp_handle_set_add (activities_set, channel); if (!upload_activities_pep (self, add_activity_reply_cb, context, NULL)) { error = g_error_new (TP_ERROR, TP_ERROR_NETWORK_ERROR, "Failed to send property request to server"); dbus_g_method_return_error (context, error); } } void olpc_buddy_info_iface_init (gpointer g_iface, gpointer iface_data) { GabbleSvcOLPCBuddyInfoClass *klass = g_iface; #define IMPLEMENT(x) gabble_svc_olpc_buddy_info_implement_##x (\ klass, olpc_buddy_info_##x) IMPLEMENT(get_activities); IMPLEMENT(set_activities); IMPLEMENT(get_properties); IMPLEMENT(set_properties); IMPLEMENT(get_current_activity); IMPLEMENT(set_current_activity); IMPLEMENT(add_activity); #undef IMPLEMENT } /* FIXME: API could be improved */ static gboolean upload_activity_properties_pep (GabbleConnection *conn, GabbleConnectionMsgReplyFunc callback, gpointer user_data, GError **error) { TpBaseConnection *base = (TpBaseConnection *) conn; WockyNode *publish, *item; WockyStanza *msg; GError *e = NULL; gboolean ret; TpHandleSet *my_activities = g_hash_table_lookup (conn->olpc_pep_activities, GUINT_TO_POINTER (tp_base_connection_get_self_handle (base))); msg = wocky_pep_service_make_publish_stanza (conn->pep_olpc_act_props, &item); publish = wocky_node_add_child_ns (item, "activities", NS_OLPC_ACTIVITY_PROPS); if (my_activities != NULL) { TpIntsetFastIter iter; guint element; tp_intset_fast_iter_init (&iter, tp_handle_set_peek (my_activities)); while (tp_intset_fast_iter_next (&iter, &element)) { GabbleOlpcActivity *activity = g_hash_table_lookup ( conn->olpc_activities_info, GUINT_TO_POINTER (element)); activity_info_contribute_properties (activity, publish, TRUE); } } ret = _gabble_connection_send_with_reply (conn, msg, callback, NULL, user_data, &e); if (!ret) { g_set_error (error, TP_ERROR, TP_ERROR_NETWORK_ERROR, "Failed to send property change request to server: %s", e->message); g_error_free (e); } g_object_unref (msg); return ret; } typedef struct { DBusGMethodInvocation *context; gboolean visibility_changed; GabbleOlpcActivity *activity; } set_properties_ctx; static void set_activity_properties_activities_reply_cb (GabbleConnection *conn, WockyStanza *sent_msg, WockyStanza *reply_msg, GObject *object, gpointer user_data) { set_properties_ctx *context = user_data; /* if the SetProperties() call was skipped, both messages are NULL */ g_assert ((sent_msg == NULL) == (reply_msg == NULL)); if (reply_msg != NULL && !check_publish_reply_msg (reply_msg, context->context)) { g_slice_free (set_properties_ctx, context); return; } gabble_svc_olpc_activity_properties_emit_activity_properties_changed ( conn, context->activity->room, context->activity->properties); gabble_svc_olpc_activity_properties_return_from_set_properties ( context->context); g_slice_free (set_properties_ctx, context); return; } static void set_activity_properties_reply_cb (GabbleConnection *conn, WockyStanza *sent_msg, WockyStanza *reply_msg, GObject *object, gpointer user_data) { set_properties_ctx *context = user_data; /* if the SetProperties() call was skipped, both messages are NULL */ g_assert ((sent_msg == NULL) == (reply_msg == NULL)); if (reply_msg != NULL && !check_publish_reply_msg (reply_msg, context->context)) { g_slice_free (set_properties_ctx, context); return; } if (context->visibility_changed) { GError *err = NULL; if (!upload_activities_pep (conn, set_activity_properties_activities_reply_cb, context, &err)) { dbus_g_method_return_error (context->context, err); g_error_free (err); } } else { /* nothing to do, so just "succeed" */ set_activity_properties_activities_reply_cb (conn, NULL, NULL, NULL, context); } } static gboolean refresh_invitations (GabbleConnection *conn, GabbleMucChannel *chan, GabbleOlpcActivity *activity, GError **error) { TpHandleRepoIface *contact_repo = tp_base_connection_get_handles ( (TpBaseConnection *) conn, TP_HANDLE_TYPE_CONTACT); TpHandleSet *invitees = g_object_get_qdata ((GObject *) chan, invitees_quark ()); if (invitees != NULL && tp_handle_set_size (invitees) > 0) { TpIntsetFastIter iter; guint element; tp_intset_fast_iter_init (&iter, tp_handle_set_peek (invitees)); while (tp_intset_fast_iter_next (&iter, &element)) { const gchar *to = tp_handle_inspect (contact_repo, element); WockyStanza *msg = wocky_stanza_build ( WOCKY_STANZA_TYPE_MESSAGE, WOCKY_STANZA_SUB_TYPE_NONE, NULL, to, NULL); activity_info_contribute_properties (activity, wocky_stanza_get_top_node (msg), FALSE); if (!_gabble_connection_send (conn, msg, error)) { DEBUG ("Unable to re-send activity properties to invitee %s", to); g_object_unref (msg); return FALSE; } g_object_unref (msg); } } return TRUE; } static void olpc_activity_properties_set_properties (GabbleSvcOLPCActivityProperties *iface, guint room, GHashTable *properties, DBusGMethodInvocation *context) { GabbleConnection *conn = GABBLE_CONNECTION (iface); TpBaseConnection *base = (TpBaseConnection *) conn; WockyStanza *msg; const gchar *jid; GHashTable *properties_copied; GabbleOlpcActivity *activity; GabbleMucChannel *muc_channel; guint state; gboolean was_visible, is_visible; set_properties_ctx *ctx; GError *err = NULL; DEBUG ("called"); gabble_connection_ensure_capabilities (conn, gabble_capabilities_get_olpc_notify ()); if (!check_pep (conn, context)) return; jid = inspect_room (base, context, room); if (jid == NULL) return; if (!activity_in_own_set (conn, jid)) { GError error = { TP_ERROR, TP_ERROR_INVALID_ARGUMENT, "Can't set properties on an activity if you're not announcing it" }; dbus_g_method_return_error (context, &error); return; } muc_channel = gabble_muc_factory_find_text_channel (conn->muc_factory, room); if (muc_channel != NULL) { g_object_get (muc_channel, "state", &state, NULL); } if (muc_channel == NULL || state != MUC_STATE_JOINED) { GError error = { TP_ERROR, TP_ERROR_INVALID_ARGUMENT, "Can't set properties on an activity if you're not in it" }; dbus_g_method_return_error (context, &error); return; } properties_copied = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify) tp_g_value_slice_free); tp_g_hash_table_update (properties_copied, properties, (GBoxedCopyFunc) g_strdup, (GBoxedCopyFunc) tp_g_value_slice_dup); activity = g_hash_table_lookup (conn->olpc_activities_info, GUINT_TO_POINTER (room)); was_visible = gabble_olpc_activity_is_visible (activity); g_object_set (activity, "properties", properties_copied, NULL); is_visible = gabble_olpc_activity_is_visible (activity); msg = wocky_stanza_build ( WOCKY_STANZA_TYPE_MESSAGE, WOCKY_STANZA_SUB_TYPE_GROUPCHAT, NULL, jid, NULL); activity_info_contribute_properties (activity, wocky_stanza_get_top_node (msg), FALSE); if (!_gabble_connection_send (conn, msg, NULL)) { GError error = { TP_ERROR, TP_ERROR_NETWORK_ERROR, "Failed to send property change notification to chatroom" }; g_object_unref (msg); dbus_g_method_return_error (context, &error); return; } g_object_unref (msg); if (!refresh_invitations (conn, muc_channel, activity, &err)) { dbus_g_method_return_error (context, err); g_error_free (err); return; } ctx = g_slice_new (set_properties_ctx); ctx->context = context; ctx->visibility_changed = (was_visible != is_visible); ctx->activity = activity; if (was_visible || is_visible) { if (!upload_activity_properties_pep (conn, set_activity_properties_reply_cb, ctx, &err)) { g_slice_free (set_properties_ctx, ctx); dbus_g_method_return_error (context, err); g_error_free (err); return; } } else { /* chain straight to the reply callback, which changes our Activities * list */ set_activity_properties_reply_cb (conn, NULL, NULL, NULL, ctx); } } static void olpc_activity_properties_get_properties (GabbleSvcOLPCActivityProperties *iface, guint room, DBusGMethodInvocation *context) { GabbleConnection *conn = GABBLE_CONNECTION (iface); gboolean not_prop = FALSE; GHashTable *properties; GabbleOlpcActivity *activity; DEBUG ("called"); gabble_connection_ensure_capabilities (conn, gabble_capabilities_get_olpc_notify ()); if (!check_pep (conn, context)) return; activity = g_hash_table_lookup (conn->olpc_activities_info, GUINT_TO_POINTER (room)); if (activity == NULL || activity->properties == NULL) { /* no properties */ properties = g_hash_table_new (g_str_hash, g_str_equal); not_prop = TRUE; } else { properties = activity->properties; } gabble_svc_olpc_activity_properties_return_from_get_properties (context, properties); if (not_prop) g_hash_table_unref (properties); } struct _i_hate_g_hash_table_foreach { GHashTable *old_properties; gboolean new_infos; }; static void check_prop_in_old_properties (gpointer key, gpointer value, gpointer user_data) { const gchar *prop = key; GValue *gvalue = value, *old_gvalue; struct _i_hate_g_hash_table_foreach *data = (struct _i_hate_g_hash_table_foreach *) user_data; old_gvalue = g_hash_table_lookup (data->old_properties, prop); if (old_gvalue == NULL) { data->new_infos = TRUE; } else if (G_VALUE_TYPE (gvalue) != G_VALUE_TYPE (old_gvalue)) { data->new_infos = TRUE; } else { if (G_VALUE_TYPE (gvalue) == G_TYPE_STRING) { const gchar *str1, *str2; str1 = g_value_get_string (gvalue); str2 = g_value_get_string (old_gvalue); if (tp_strdiff (str1, str2)) { data->new_infos = TRUE; } } else if (G_VALUE_TYPE (gvalue) == G_TYPE_BOOLEAN) { gboolean bool1, bool2; bool1 = g_value_get_boolean (gvalue); bool2 = g_value_get_boolean (old_gvalue); if (bool1 != bool2) { data->new_infos = TRUE; } } else { /* if in doubt, emit the signal */ data->new_infos = TRUE; } } } static gboolean properties_contains_new_infos (GHashTable *old_properties, GHashTable *new_properties) { struct _i_hate_g_hash_table_foreach data; if (g_hash_table_size (new_properties) > g_hash_table_size (old_properties)) /* New key/value pair(s) */ return TRUE; data.old_properties = old_properties; data.new_infos = FALSE; g_hash_table_foreach (new_properties, check_prop_in_old_properties, &data); return data.new_infos; } static void update_activity_properties (GabbleConnection *conn, const gchar *room, const gchar *contact, WockyNode *properties_node) { GHashTable *new_properties, *old_properties; gboolean new_infos = FALSE; GabbleOlpcActivity *activity; TpHandle room_handle; TpHandleRepoIface *room_repo = tp_base_connection_get_handles ( (TpBaseConnection *) conn, TP_HANDLE_TYPE_ROOM); room_handle = tp_handle_ensure (room_repo, room, NULL, NULL); activity = g_hash_table_lookup (conn->olpc_activities_info, GUINT_TO_POINTER (room_handle)); if (activity == NULL) { DEBUG ("unknown activity: %s", room); if (contact != NULL) { /* Humm we received properties for an activity we don't * know yet. * If the remote user doesn't announce this activity * in his next activities list, information about * it will be freed */ activity = add_activity_info_in_set (conn, room_handle, contact, conn->olpc_pep_activities); } else { activity = add_activity_info (conn, room_handle); } } if (activity == NULL) return; old_properties = activity->properties; new_properties = lm_message_node_extract_properties (properties_node, "property"); if (g_hash_table_size (new_properties) == 0) { g_hash_table_unref (new_properties); return; } if (old_properties == NULL || properties_contains_new_infos (old_properties, new_properties)) { new_infos = TRUE; } g_object_set (activity, "properties", new_properties, NULL); if (new_infos) { /* Only emit the signal if we add new values */ gabble_svc_olpc_activity_properties_emit_activity_properties_changed ( conn, activity->room, new_properties); } } static gboolean update_activities_properties (GabbleConnection *conn, const gchar *contact, WockyStanza *msg) { const gchar *room; WockyNode *node; WockyNodeIter i; WockyNode *properties_node; node = search_for_child ( wocky_stanza_get_top_node (msg), "activities", NULL); if (node == NULL) return FALSE; wocky_node_iter_init (&i, node, "properties", NULL); while (wocky_node_iter_next (&i, &properties_node)) { room = wocky_node_get_attribute (properties_node, "room"); if (room == NULL) continue; update_activity_properties (conn, room, contact, properties_node); } return TRUE; } static void olpc_act_props_pep_node_changed (WockyPepService *pep, WockyBareContact *contact, WockyStanza *stanza, WockyNode *item, GabbleConnection *conn) { TpHandleRepoIface *contact_repo = tp_base_connection_get_handles ( (TpBaseConnection *) conn, TP_HANDLE_TYPE_CONTACT); TpBaseConnection *base = (TpBaseConnection *) conn; TpHandle handle; const gchar *jid; jid = wocky_bare_contact_get_jid (contact); handle = tp_handle_ensure (contact_repo, jid, NULL, NULL); if (handle == 0) { DEBUG ("Invalid from: %s", jid); return; } if (handle == tp_base_connection_get_self_handle (base)) /* Ignore echoed pubsub notifications */ return; update_activities_properties (conn, jid, stanza); } static void connection_status_changed_cb (GabbleConnection *conn, TpConnectionStatus status, TpConnectionStatusReason reason, gpointer user_data) { if (status == TP_CONNECTION_STATUS_CONNECTED) { /* Well, let's do another crack. * We have to cleanup PEP node to avoid to confuse * remote contacts with old properties from a previous session. */ if (!upload_activities_pep (conn, NULL, NULL, NULL)) { DEBUG ("Failed to send PEP activities reset in response to " "initial connection"); } if (!upload_activity_properties_pep (conn, NULL, NULL, NULL)) { DEBUG ("Failed to send PEP activity props reset in response to " "initial connection"); } gabble_connection_connected_olpc (conn); } } static void pseudo_invite_reply_cb (GabbleConnection *conn, WockyStanza *sent_msg, WockyStanza *reply_msg, GObject *object, gpointer user_data) { if (!check_publish_reply_msg (reply_msg, NULL)) { STANZA_DEBUG (reply_msg, "Failed to make PEP change in " "response to pseudo-invitation message"); STANZA_DEBUG (sent_msg, "The failed request was"); } } gboolean conn_olpc_process_activity_properties_message (GabbleConnection *conn, WockyStanza *msg, const gchar *from) { TpBaseConnection *base = (TpBaseConnection *) conn; TpHandleRepoIface *contact_repo = tp_base_connection_get_handles (base, TP_HANDLE_TYPE_CONTACT); TpHandleRepoIface *room_repo = tp_base_connection_get_handles (base, TP_HANDLE_TYPE_ROOM); WockyNode *node = search_for_child ( wocky_stanza_get_top_node (msg), "properties", NS_OLPC_ACTIVITY_PROPS); const gchar *id; TpHandle room_handle, contact_handle = 0; GabbleOlpcActivity *activity; TpHandleSet *their_invites, *our_activities; GHashTable *old_properties, *new_properties; gboolean properties_changed, pep_properties_changed, activities_changed; gboolean was_visible, is_visible; GabbleMucChannel *muc_channel = NULL; /* if no <properties xmlns=...>, then not for us */ if (node == NULL) return FALSE; DEBUG ("Found <properties> node in <message>"); id = wocky_node_get_attribute (node, "activity"); if (id == NULL) { NODE_DEBUG (node, "... activity ID missing - ignoring"); return TRUE; } room_handle = gabble_get_room_handle_from_jid (room_repo, from); if (room_handle != 0) { muc_channel = gabble_muc_factory_find_text_channel (conn->muc_factory, room_handle); } if (muc_channel == NULL) { const gchar *room; DEBUG ("Activity properties message was a pseudo-invitation"); /* FIXME: This is stupid. We should ref the handles in a TpHandleSet * per activity, then we could _ensure this handle */ contact_handle = tp_handle_lookup (contact_repo, from, NULL, NULL); if (contact_handle == 0) { DEBUG ("... contact <%s> unknown - ignoring (FIX THIS)", from); return TRUE; } room = wocky_node_get_attribute (node, "room"); if (room == NULL) { NODE_DEBUG (node, "... room name missing - ignoring"); return TRUE; } DEBUG ("... room <%s>", room); room_handle = tp_handle_ensure (room_repo, room, NULL, NULL); if (room_handle == 0) { DEBUG ("... room <%s> invalid - ignoring", room); return TRUE; } muc_channel = gabble_muc_factory_find_text_channel (conn->muc_factory, room_handle); if (muc_channel != NULL) { guint state; g_object_get (muc_channel, "state", &state, NULL); if (state == MUC_STATE_JOINED) { DEBUG ("Ignoring pseudo-invitation to <%s> - we're already " "there", room); return TRUE; } } } else { TpHandle self_handle; DEBUG ("Activity properties message was in a chatroom"); tp_group_mixin_get_self_handle ((GObject *) muc_channel, &self_handle, NULL); if (tp_handle_lookup (contact_repo, from, NULL, NULL) == self_handle) { DEBUG ("Ignoring echoed activity properties message from myself"); return TRUE; } } activity = g_hash_table_lookup (conn->olpc_activities_info, GUINT_TO_POINTER (room_handle)); if (contact_handle != 0) { their_invites = g_hash_table_lookup (conn->olpc_invited_activities, GUINT_TO_POINTER (contact_handle)); if (their_invites == NULL) { activities_changed = TRUE; their_invites = tp_handle_set_new (room_repo); g_hash_table_insert (conn->olpc_invited_activities, GUINT_TO_POINTER (contact_handle), their_invites); } else { activities_changed = !tp_handle_set_is_member (their_invites, room_handle); } if (activity == NULL) { DEBUG ("... creating new Activity"); activity = add_activity_info (conn, room_handle); tp_handle_set_add (their_invites, room_handle); } else if (!tp_handle_set_is_member (their_invites, room_handle)) { DEBUG ("... it's the first time that contact invited me, " "referencing Activity on their behalf"); g_object_ref (activity); tp_handle_set_add (their_invites, room_handle); } } else { activities_changed = FALSE; /* we're in the room, so it ought to have an Activity ref'd */ g_assert (activity != NULL); } new_properties = lm_message_node_extract_properties (node, "property"); g_assert (new_properties); /* before applying the changes, gather enough information to work out * whether anything changed */ old_properties = activity->properties; was_visible = gabble_olpc_activity_is_visible (activity); properties_changed = old_properties == NULL || properties_contains_new_infos (old_properties, new_properties); /* apply the info we found */ if (tp_strdiff (activity->id, id)) { DEBUG ("... recording new activity ID %s", id); g_object_set (activity, "id", id, NULL); } g_object_set (activity, "properties", new_properties, NULL); /* emit signals and amend our PEP nodes, if necessary */ is_visible = gabble_olpc_activity_is_visible (activity); if (is_visible) { pep_properties_changed = properties_changed || !was_visible; } else { pep_properties_changed = was_visible; } if (properties_changed) gabble_svc_olpc_activity_properties_emit_activity_properties_changed (conn, room_handle, new_properties); if (activities_changed) { GPtrArray *activities; g_assert (contact_handle != 0); activities = get_buddy_activities (conn, contact_handle); gabble_svc_olpc_buddy_info_emit_activities_changed (conn, contact_handle, activities); free_activities (activities); } if (properties_changed && muc_channel != NULL) refresh_invitations (conn, muc_channel, activity, NULL); /* If we're announcing this activity, we might need to change our PEP node */ if (pep_properties_changed) { our_activities = g_hash_table_lookup (conn->olpc_pep_activities, GUINT_TO_POINTER (tp_base_connection_get_self_handle (base))); if (our_activities != NULL && tp_handle_set_is_member (our_activities, room_handle)) { if (!upload_activity_properties_pep (conn, pseudo_invite_reply_cb, NULL, NULL)) { DEBUG ("Failed to send PEP properties change in response to " "properties change message"); } } } if (is_visible != was_visible) { our_activities = g_hash_table_lookup (conn->olpc_pep_activities, GUINT_TO_POINTER (tp_base_connection_get_self_handle (base))); if (our_activities != NULL && tp_handle_set_is_member (our_activities, room_handle)) { if (!upload_activities_pep (conn, pseudo_invite_reply_cb, NULL, NULL)) { DEBUG ("Failed to send PEP activities change in response to " "properties change message"); } } } return TRUE; } static void closed_pep_reply_cb (GabbleConnection *conn, WockyStanza *sent_msg, WockyStanza *reply_msg, GObject *object, gpointer user_data) { if (!check_publish_reply_msg (reply_msg, NULL)) { STANZA_DEBUG (reply_msg, "Failed to make PEP change in " "response to channel closure"); STANZA_DEBUG (sent_msg, "The failed request was"); } } static gboolean revoke_invitations (GabbleConnection *conn, GabbleMucChannel *chan, GabbleOlpcActivity *activity, GError **error) { TpHandleRepoIface *contact_repo = tp_base_connection_get_handles ( (TpBaseConnection *) conn, TP_HANDLE_TYPE_CONTACT); TpHandleSet *invitees = g_object_get_qdata ((GObject *) chan, invitees_quark ()); if (activity->id == NULL) /* this is not a real OLPC activity */ return TRUE; if (invitees != NULL && tp_handle_set_size (invitees) > 0) { TpIntsetFastIter iter; guint element; tp_intset_fast_iter_init (&iter, tp_handle_set_peek (invitees)); DEBUG ("revoke invitations for activity %s", activity->id); while (tp_intset_fast_iter_next (&iter, &element)) { const gchar *to = tp_handle_inspect (contact_repo, element); WockyStanza *msg = wocky_stanza_build ( WOCKY_STANZA_TYPE_MESSAGE, WOCKY_STANZA_SUB_TYPE_NONE, NULL, to, '(', "uninvite", ':', NS_OLPC_ACTIVITY_PROPS, '@', "room", gabble_olpc_activity_get_room (activity), '@', "id", activity->id, ')', NULL); if (!_gabble_connection_send (conn, msg, error)) { DEBUG ("Unable to send activity invitee revocation %s", to); g_object_unref (msg); return FALSE; } g_object_unref (msg); } } return TRUE; } gboolean conn_olpc_process_activity_uninvite_message (GabbleConnection *conn, WockyStanza *msg, const gchar *from) { TpHandleRepoIface *room_repo = tp_base_connection_get_handles ( (TpBaseConnection *) conn, TP_HANDLE_TYPE_ROOM); TpHandleRepoIface *contact_repo = tp_base_connection_get_handles ( (TpBaseConnection *) conn, TP_HANDLE_TYPE_CONTACT); WockyNode *node; const gchar *id, *room; TpHandle room_handle, from_handle; TpHandleSet *rooms; node = search_for_child ( wocky_stanza_get_top_node (msg), "uninvite", NS_OLPC_ACTIVITY_PROPS); /* if no <uninvite xmlns=...>, then not for us */ if (node == NULL) return FALSE; id = wocky_node_get_attribute (node, "id"); if (id == NULL) { DEBUG ("no activity id. Skip"); return TRUE; } room = wocky_node_get_attribute (node, "room"); if (room == NULL) { DEBUG ("no room. Skip"); return TRUE; } room_handle = tp_handle_lookup (room_repo, room, NULL, NULL); if (room_handle == 0) { DEBUG ("room %s unknown", room); return TRUE; } from_handle = tp_handle_lookup (contact_repo, from, NULL, NULL); if (from_handle == 0) { DEBUG ("sender %s unknown", from); return TRUE; } rooms = g_hash_table_lookup (conn->olpc_invited_activities, GUINT_TO_POINTER (from_handle)); if (rooms == NULL) { DEBUG ("No invites associated with contact %d", from_handle); return TRUE; } if (tp_handle_set_remove (rooms, room_handle)) { GabbleOlpcActivity *activity; GPtrArray *activities; activity = g_hash_table_lookup (conn->olpc_activities_info, GUINT_TO_POINTER (room_handle)); if (activity == NULL) { DEBUG ("No info about activity associated with room %s", room); return TRUE; } if (tp_strdiff (id, activity->id)) { DEBUG ("Uninvite's activity id (%s) doesn't match our " "activity id (%s)", id, activity->id); return TRUE; } DEBUG ("remove invite from %s", from); g_object_unref (activity); /* Emit BuddyInfo::ActivitiesChanged */ activities = get_buddy_activities (conn, from_handle); gabble_svc_olpc_buddy_info_emit_activities_changed (conn, from_handle, activities); free_activities (activities); } else { DEBUG ("No invite from %s for activity %s (room %s)", from, id, room); return TRUE; } return TRUE; } static void muc_channel_closed_cb (GabbleMucChannel *chan, GabbleOlpcActivity *activity) { GabbleConnection *conn; TpBaseConnection *base; TpHandleSet *my_activities; gboolean was_in_our_pep = FALSE; /* is the muc channel /actually/ disappearing */ if (!tp_base_channel_is_destroyed (TP_BASE_CHANNEL (chan))) return; g_object_get (activity, "connection", &conn, NULL); base = TP_BASE_CONNECTION (conn); /* Revoke invitations we sent for this activity */ revoke_invitations (conn, chan, activity, NULL); /* remove it from our advertised activities list, unreffing it in the * process if it was in fact advertised */ my_activities = g_hash_table_lookup (conn->olpc_pep_activities, GUINT_TO_POINTER (tp_base_connection_get_self_handle (base))); if (my_activities != NULL) { if (tp_handle_set_remove (my_activities, activity->room)) { was_in_our_pep = gabble_olpc_activity_is_visible (activity); g_object_unref (activity); } } /* unref it again (it was referenced on behalf of the channel) */ g_object_unref (activity); if (was_in_our_pep) { if (!upload_activities_pep (conn, closed_pep_reply_cb, NULL, NULL)) { DEBUG ("Failed to send PEP activities change in response to " "channel close"); } if (!upload_activity_properties_pep (conn, closed_pep_reply_cb, NULL, NULL)) { DEBUG ("Failed to send PEP activity props change in response to " "channel close"); } } g_object_unref (conn); } static void muc_channel_pre_invite_cb (GabbleMucChannel *chan, const gchar *jid, GabbleOlpcActivity *activity) { GabbleConnection *conn; TpHandleRepoIface *contact_repo; GQuark quark = invitees_quark (); TpHandleSet *invitees; /* send them the properties */ WockyStanza *msg; TpHandle handle; GError *error = NULL; g_object_get (activity, "connection", &conn, NULL); contact_repo = tp_base_connection_get_handles ((TpBaseConnection *) conn, TP_HANDLE_TYPE_CONTACT); msg = wocky_stanza_build ( WOCKY_STANZA_TYPE_MESSAGE, WOCKY_STANZA_SUB_TYPE_NONE, NULL, jid, NULL); if (activity_info_contribute_properties (activity, wocky_stanza_get_top_node (msg), FALSE)) { /* not much we can do about errors - but if this fails, the invitation * will too, unless something extremely strange is going on */ if (!_gabble_connection_send (conn, msg, NULL)) { DEBUG ("Unable to send activity properties to invitee"); } } g_object_unref (msg); handle = tp_handle_ensure (contact_repo, jid, NULL, &error); if (handle == 0) { DEBUG ("can't add %s to invitees: %s", jid, error->message); g_error_free (error); g_object_unref (conn); return; } invitees = g_object_get_qdata ((GObject *) chan, quark); if (invitees == NULL) { invitees = tp_handle_set_new (contact_repo); g_object_set_qdata_full ((GObject *) chan, quark, invitees, (GDestroyNotify) tp_handle_set_destroy); } tp_handle_set_add (invitees, handle); g_object_unref (conn); } typedef struct { GabbleConnection *conn; TpHandle room_handle; } remove_invite_foreach_ctx; static void remove_invite_foreach (gpointer key, gpointer value, gpointer user_data) { TpHandle inviter = GPOINTER_TO_UINT (key); TpHandleSet *rooms = (TpHandleSet *) value; remove_invite_foreach_ctx *ctx = (remove_invite_foreach_ctx *) user_data; /* We are now in the activity and so the responsibilty to track * buddies membership is delegated to the PS. At some point, maybe that * should be done by CM's */ if (tp_handle_set_remove (rooms, ctx->room_handle)) { GabbleOlpcActivity *activity; GPtrArray *activities; activity = g_hash_table_lookup (ctx->conn->olpc_activities_info, GUINT_TO_POINTER (ctx->room_handle)); activities = get_buddy_activities (ctx->conn, inviter); gabble_svc_olpc_buddy_info_emit_activities_changed (ctx->conn, inviter, activities); free_activities (activities); g_assert (activity != NULL); DEBUG ("forget invite for activity %s from contact %d", activity->id, inviter); g_object_unref (activity); } } static void forget_activity_invites (GabbleConnection *conn, TpHandle room_handle) { remove_invite_foreach_ctx ctx; ctx.conn = conn; ctx.room_handle = room_handle; g_hash_table_foreach (conn->olpc_invited_activities, remove_invite_foreach, &ctx); } static void muc_channel_contact_join_cb (GabbleMucChannel *chan, TpHandle contact, GabbleOlpcActivity *activity) { GabbleConnection *conn; TpBaseConnection *base; g_object_get (activity, "connection", &conn, NULL); base = TP_BASE_CONNECTION (conn); if (contact == tp_base_connection_get_self_handle (base)) { /* We join the channel, forget about all invites we received about * this activity */ forget_activity_invites (conn, activity->room); } else { GQuark quark = invitees_quark (); TpHandleSet *invitees; invitees = g_object_get_qdata ((GObject *) chan, quark); if (invitees != NULL) { DEBUG ("contact %d joined the muc, remove the invite we sent to him", contact); tp_handle_set_remove (invitees, contact); } } g_object_unref (conn); } static void muc_factory_new_channel_cb (gpointer key, gpointer value, gpointer data) { GabbleConnection *conn = GABBLE_CONNECTION (data); TpExportableChannel *chan = TP_EXPORTABLE_CHANNEL (key); GabbleOlpcActivity *activity; TpHandle room_handle; if (!GABBLE_IS_MUC_CHANNEL (chan)) return; g_object_get (chan, "handle", &room_handle, NULL); /* ref the activity as long as we have a channel open */ activity = g_hash_table_lookup (conn->olpc_activities_info, GUINT_TO_POINTER (room_handle)); if (activity == NULL) { activity = add_activity_info (conn, room_handle); } else { g_object_ref (activity); } g_signal_connect (chan, "closed", G_CALLBACK (muc_channel_closed_cb), activity); g_signal_connect (chan, "pre-invite", G_CALLBACK (muc_channel_pre_invite_cb), activity); g_signal_connect (chan, "contact-join", G_CALLBACK (muc_channel_contact_join_cb), activity); } static void muc_factory_new_channels_cb (GabbleMucFactory *fac, GHashTable *channels, GabbleConnection *conn) { g_hash_table_foreach (channels, muc_factory_new_channel_cb, conn); } static void connection_presence_do_update (GabblePresenceCache *cache, TpHandle handle, GabbleConnection *conn) { GabblePresence *presence; presence = gabble_presence_cache_get (cache, handle); if (presence && presence->status <= GABBLE_PRESENCE_LAST_UNAVAILABLE) { /* Contact becomes unavailable. We have to unref all the information * provided by him */ GPtrArray *empty = g_ptr_array_new (); TpHandleSet *list; list = g_hash_table_lookup (conn->olpc_pep_activities, GUINT_TO_POINTER (handle)); if (list != NULL) tp_handle_set_foreach (list, decrement_contacts_activities_set_foreach, conn); g_hash_table_remove (conn->olpc_pep_activities, GUINT_TO_POINTER (handle)); list = g_hash_table_lookup (conn->olpc_invited_activities, GUINT_TO_POINTER (handle)); if (list != NULL) tp_handle_set_foreach (list, decrement_contacts_activities_set_foreach, conn); g_hash_table_remove (conn->olpc_invited_activities, GUINT_TO_POINTER (handle)); gabble_svc_olpc_buddy_info_emit_activities_changed (conn, handle, empty); g_ptr_array_unref (empty); } } static void connection_presences_updated_cb (GabblePresenceCache *cache, GArray *handles, GabbleConnection *conn) { guint i; for (i = 0; i < handles->len ; i++) { TpHandle handle; handle = g_array_index (handles, TpHandle, i); connection_presence_do_update (cache, handle, conn); } } void conn_olpc_activity_properties_init (GabbleConnection *conn) { /* room TpHandle => borrowed Activity */ conn->olpc_activities_info = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, NULL); /* Activity from PEP * * contact TpHandle => TpHandleSet of room handles, * each representing a reference to an Activity * * Special case: the entry for self_handle is the complete list of * activities, not just those from PEP */ conn->olpc_pep_activities = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, (GDestroyNotify) tp_handle_set_destroy); /* Activity from pseudo-invitations * * contact TpHandle => TpHandleSet of room handles, * each representing a reference to an Activity * * Special case: there is never an entry for self_handle */ conn->olpc_invited_activities = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, (GDestroyNotify) tp_handle_set_destroy); /* Current activity * * contact TpHandle => reffed GabbleOlpcActivity */ conn->olpc_current_act = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, (GDestroyNotify) g_object_unref); g_signal_connect (conn, "status-changed", G_CALLBACK (connection_status_changed_cb), NULL); g_signal_connect (TP_CHANNEL_MANAGER (conn->muc_factory), "new-channels", G_CALLBACK (muc_factory_new_channels_cb), conn); g_signal_connect (conn->presence_cache, "presences-updated", G_CALLBACK (connection_presences_updated_cb), conn); conn->pep_olpc_buddy_props = wocky_pep_service_new (NS_OLPC_BUDDY_PROPS, TRUE); g_signal_connect (conn->pep_olpc_buddy_props, "changed", G_CALLBACK (olpc_buddy_props_pep_node_changed), conn); conn->pep_olpc_activities = wocky_pep_service_new (NS_OLPC_ACTIVITIES, TRUE); g_signal_connect (conn->pep_olpc_activities, "changed", G_CALLBACK (olpc_activities_pep_node_changed), conn); conn->pep_olpc_current_act = wocky_pep_service_new (NS_OLPC_CURRENT_ACTIVITY, TRUE); g_signal_connect (conn->pep_olpc_current_act, "changed", G_CALLBACK (olpc_current_act_pep_node_changed), conn); conn->pep_olpc_act_props = wocky_pep_service_new (NS_OLPC_ACTIVITY_PROPS, TRUE); g_signal_connect (conn->pep_olpc_act_props, "changed", G_CALLBACK (olpc_act_props_pep_node_changed), conn); } static void unref_activities_in_each_set (TpHandle handle, TpHandleSet *set, GabbleConnection *conn) { if (set != NULL) { tp_handle_set_foreach (set, decrement_contacts_activities_set_foreach, conn); } } void conn_olpc_activity_properties_dispose (GabbleConnection *self) { g_hash_table_unref (self->olpc_current_act); self->olpc_current_act = NULL; g_hash_table_foreach (self->olpc_pep_activities, (GHFunc) unref_activities_in_each_set, self); g_hash_table_unref (self->olpc_pep_activities); self->olpc_pep_activities = NULL; g_hash_table_foreach (self->olpc_invited_activities, (GHFunc) unref_activities_in_each_set, self); g_hash_table_unref (self->olpc_invited_activities); self->olpc_invited_activities = NULL; g_hash_table_unref (self->olpc_activities_info); self->olpc_activities_info = NULL; } static GabbleOlpcActivity * find_activity_by_id (GabbleConnection *self, const gchar *activity_id) { GHashTableIter iter; gpointer key, value; g_hash_table_iter_init (&iter, self->olpc_activities_info); while (g_hash_table_iter_next (&iter, &key, &value)) { GabbleOlpcActivity *activity = GABBLE_OLPC_ACTIVITY (value); if (!tp_strdiff (activity->id, activity_id)) return activity; } return NULL; } static void olpc_activity_properties_get_activity (GabbleSvcOLPCActivityProperties *iface, const gchar *activity_id, DBusGMethodInvocation *context) { GabbleConnection *self = GABBLE_CONNECTION (iface); TpBaseConnection *base = (TpBaseConnection *) self; GabbleOlpcActivity *activity; GError *error = NULL; TP_BASE_CONNECTION_ERROR_IF_NOT_CONNECTED (base, context); activity = find_activity_by_id (self, activity_id); if (activity == NULL) { g_set_error (&error, TP_ERROR, TP_ERROR_NOT_AVAILABLE, "Activity unknown: %s", activity_id); goto error; } gabble_svc_olpc_activity_properties_return_from_get_activity (context, activity->room); return; error: dbus_g_method_return_error (context, error); g_error_free (error); } void olpc_activity_properties_iface_init (gpointer g_iface, gpointer iface_data) { GabbleSvcOLPCActivityPropertiesClass *klass = g_iface; #define IMPLEMENT(x) gabble_svc_olpc_activity_properties_implement_##x (\ klass, olpc_activity_properties_##x) IMPLEMENT(get_properties); IMPLEMENT(set_properties); IMPLEMENT(get_activity); #undef IMPLEMENT } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������telepathy-gabble-0.18.2/src/conn-olpc.h�������������������������������������������������������������0000644�0001750�0001750�00000003013�12200204333�017627� 0����������������������������������������������������������������������������������������������������ustar�00smcv����������������������������smcv����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * conn-olpc.h - Header for Gabble OLPC BuddyInfo and ActivityProperties interfaces * Copyright (C) 2007 Collabora Ltd. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 __CONN_OLPC_H__ #define __CONN_OLPC_H__ #include <extensions/extensions.h> #include "connection.h" void olpc_buddy_info_iface_init (gpointer g_iface, gpointer iface_data); void olpc_activity_properties_iface_init (gpointer g_iface, gpointer iface_data); void conn_olpc_activity_properties_init (GabbleConnection *conn); void conn_olpc_activity_properties_dispose (GabbleConnection *conn); gboolean conn_olpc_process_activity_properties_message (GabbleConnection *conn, WockyStanza *msg, const gchar *from); gboolean conn_olpc_process_activity_uninvite_message (GabbleConnection *conn, WockyStanza *msg, const gchar *from); #endif /* __CONN_OLPC_H__ */ ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������telepathy-gabble-0.18.2/src/conn-location.c���������������������������������������������������������0000644�0001750�0001750�00000046456�12200204333�020517� 0����������������������������������������������������������������������������������������������������ustar�00smcv����������������������������smcv����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ #define _GNU_SOURCE #include "config.h" #include "conn-location.h" #include <string.h> #include <stdlib.h> #define DEBUG_FLAG GABBLE_DEBUG_LOCATION #include <telepathy-glib/telepathy-glib.h> #include <telepathy-glib/telepathy-glib-dbus.h> #include <wocky/wocky.h> #include <gabble/gabble.h> #include "debug.h" #include "namespaces.h" #include "presence-cache.h" #include "util.h" typedef struct { gchar *xmpp_name; gchar *tp_name; GType type; } LocationMapping; static const LocationMapping mappings[] = { { "alt", "alt", G_TYPE_DOUBLE }, { "area", "area", G_TYPE_STRING }, { "bearing", "bearing", G_TYPE_DOUBLE }, { "building", "building", G_TYPE_STRING }, { "country", "country", G_TYPE_STRING }, { "description", "description", G_TYPE_STRING }, { "floor", "floor", G_TYPE_STRING }, { "lat", "lat", G_TYPE_DOUBLE }, { "locality", "locality", G_TYPE_STRING }, { "lon", "lon", G_TYPE_DOUBLE }, { "postalcode", "postalcode", G_TYPE_STRING }, { "region", "region", G_TYPE_STRING }, { "room", "room", G_TYPE_STRING }, { "speed", "speed", G_TYPE_DOUBLE }, { "street", "street", G_TYPE_STRING }, { "text", "text", G_TYPE_STRING }, { "timestamp", "timestamp", G_TYPE_INT64 }, { "uri", "uri", G_TYPE_STRING }, { "accuracy", "accuracy", G_TYPE_DOUBLE }, { "countrycode", "countrycode", G_TYPE_STRING }, /* language is a special case as it's not mapped on a node but on the * xml:lang attribute of the 'geoloc' node. */ { NULL, NULL }, }; static GHashTable *xmpp_to_tp = NULL; static GHashTable *tp_to_xmpp = NULL; static void build_mapping_tables (void) { guint i; if (xmpp_to_tp != NULL) return; g_assert (tp_to_xmpp == NULL); xmpp_to_tp = g_hash_table_new (g_str_hash, g_str_equal); tp_to_xmpp = g_hash_table_new (g_str_hash, g_str_equal); for (i = 0; mappings[i].xmpp_name != NULL; i++) { g_hash_table_insert (xmpp_to_tp, mappings[i].xmpp_name, (gpointer) &mappings[i]); g_hash_table_insert (tp_to_xmpp, mappings[i].tp_name, (gpointer) &mappings[i]); } } static gboolean update_location_from_item ( GabbleConnection *conn, TpHandle contact, WockyNode *item_node); /* * get_cached_location: * @conn: a connection * @handle: a handle, which must have been pre-validated. * * Returns: a new ref to a GHashTable containing @handle's location, or %NULL * if we have no cached location. */ static GHashTable * get_cached_location (GabbleConnection *conn, TpHandle handle) { TpBaseConnection *base = (TpBaseConnection *) conn; GHashTable *location; const gchar *jid; TpHandleRepoIface *contact_repo; contact_repo = tp_base_connection_get_handles (base, TP_HANDLE_TYPE_CONTACT); jid = tp_handle_inspect (contact_repo, handle); location = gabble_presence_cache_get_location (conn->presence_cache, handle); if (location != NULL) DEBUG (" - %s: cached", jid); else DEBUG (" - %s: unknown", jid); return location; } static void location_get_locations (TpSvcConnectionInterfaceLocation *iface, const GArray *contacts, DBusGMethodInvocation *context) { GabbleConnection *conn = GABBLE_CONNECTION (iface); TpBaseConnection *base = (TpBaseConnection *) conn; TpHandleRepoIface *contact_handles; guint i; GError *error = NULL; GHashTable *return_locations = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, (GDestroyNotify) g_hash_table_unref); DEBUG ("GetLocation for contacts:"); gabble_connection_ensure_capabilities (conn, gabble_capabilities_get_geoloc_notify ()); /* Validate contacts */ contact_handles = tp_base_connection_get_handles (base, TP_HANDLE_TYPE_CONTACT); if (!tp_handles_are_valid (contact_handles, contacts, TRUE, &error)) { dbus_g_method_return_error (context, error); g_error_free (error); g_hash_table_unref (return_locations); return; } for (i = 0; i < contacts->len; i++) { TpHandle contact = g_array_index (contacts, TpHandle, i); GHashTable *location = get_cached_location (conn, contact); if (location != NULL) g_hash_table_insert (return_locations, GUINT_TO_POINTER (contact), location); } tp_svc_connection_interface_location_return_from_get_locations (context, return_locations); g_hash_table_unref (return_locations); } typedef struct { GabbleConnection *self; TpHandle handle; DBusGMethodInvocation *context; } YetAnotherContextStruct; static void request_location_reply_cb (GObject *source, GAsyncResult *res, gpointer user_data) { YetAnotherContextStruct *ctx = user_data; WockyStanza *reply; WockyNode *item_node; GError *wocky_error = NULL, *tp_error = NULL; reply = wocky_pep_service_get_finish (WOCKY_PEP_SERVICE (source), res, &item_node, &wocky_error); if (reply == NULL || wocky_stanza_extract_errors (reply, NULL, &wocky_error, NULL, NULL)) { DEBUG ("fetching location failed: %s", wocky_error->message); gabble_set_tp_error_from_wocky (wocky_error, &tp_error); dbus_g_method_return_error (ctx->context, tp_error); g_error_free (tp_error); } else { GHashTable *location; if (update_location_from_item (ctx->self, ctx->handle, item_node)) { location = get_cached_location (ctx->self, ctx->handle); /* We just cached a location for this contact, so it should be * non-NULL. */ g_return_if_fail (location != NULL); } else { /* If the location's unparseable, we'll hit this path. That seems * okay. */ location = g_hash_table_new (NULL, NULL); } tp_svc_connection_interface_location_return_from_request_location ( ctx->context, location); g_hash_table_unref (location); } tp_clear_object (&reply); g_object_unref (ctx->self); g_slice_free (YetAnotherContextStruct, ctx); } static void location_request_location ( TpSvcConnectionInterfaceLocation *iface, TpHandle handle, DBusGMethodInvocation *context) { GabbleConnection *self = GABBLE_CONNECTION (iface); TpBaseConnection *base = (TpBaseConnection *) self; TpHandleRepoIface *contact_handles = tp_base_connection_get_handles (base, TP_HANDLE_TYPE_CONTACT); const gchar *jid; WockyBareContact *contact; YetAnotherContextStruct *ctx; GError *error = NULL; if (!tp_handle_is_valid (contact_handles, handle, &error)) { dbus_g_method_return_error (context, error); g_error_free (error); return; } /* Oh! for GDBus. */ ctx = g_slice_new (YetAnotherContextStruct); ctx->self = g_object_ref (self); ctx->handle = handle; ctx->context = context; jid = tp_handle_inspect (contact_handles, handle); contact = ensure_bare_contact_from_jid (self, jid); DEBUG ("fetching location for '%s'", jid); wocky_pep_service_get_async (self->pep_location, contact, NULL, request_location_reply_cb, ctx); g_object_unref (contact); } static gboolean add_to_geoloc_node (const gchar *tp_name, GValue *value, WockyNode *geoloc, GError **err) { LocationMapping *mapping; gchar *str = NULL; /* Map "language" to the xml:lang attribute. */ if (!tp_strdiff (tp_name, "language")) { if (G_VALUE_TYPE (value) != G_TYPE_STRING) { g_set_error (err, TP_ERROR, TP_ERROR_INVALID_ARGUMENT, "expecting string for language value, but got %s", G_VALUE_TYPE_NAME (value)); return FALSE; } wocky_node_set_attribute ( geoloc, "xml:lang", g_value_get_string (value)); return TRUE; } mapping = g_hash_table_lookup (tp_to_xmpp, tp_name); if (mapping == NULL) { DEBUG ("Unknown location key: %s ; skipping", (const gchar *) tp_name); /* We don't raise a D-Bus error if the key is unknown to stay backward * compatible if new keys are added in a future version of the spec. */ return TRUE; } if (G_VALUE_TYPE (value) != mapping->type) { g_set_error (err, TP_ERROR, TP_ERROR_INVALID_ARGUMENT, "'%s' is supposed to be of type %s but is %s", (const char *) tp_name, g_type_name (mapping->type), G_VALUE_TYPE_NAME (value)); return FALSE; } if (G_VALUE_TYPE (value) == G_TYPE_INT64) { GTimeVal timeval; timeval.tv_sec = CLAMP (g_value_get_int64 (value), 0, G_MAXLONG); timeval.tv_usec = 0; str = g_time_val_to_iso8601 (&timeval); } else if (G_VALUE_TYPE (value) == G_TYPE_DOUBLE) { str = g_strdup_printf ("%.6f", g_value_get_double (value)); } else if (G_VALUE_TYPE (value) == G_TYPE_STRING) { str = g_value_dup_string (value); } else /* Keys and their type have been checked */ g_assert_not_reached (); wocky_node_add_child_with_content (geoloc, mapping->xmpp_name, str); DEBUG ("\t - %s: %s", (gchar *) tp_name, str); g_free (str); return TRUE; } static void set_location_sent_cb (GabbleConnection *conn, WockyStanza *sent_msg, WockyStanza *reply_msg, GObject *object, gpointer user_data) { DBusGMethodInvocation *context = user_data; GError *error = NULL; if (!wocky_stanza_extract_errors (reply_msg, NULL, &error, NULL, NULL)) { dbus_g_method_return (context); } else { GError *tp_error = NULL; DEBUG ("SetLocation failed: %s", error->message); gabble_set_tp_error_from_wocky (error, &tp_error); dbus_g_method_return_error (context, tp_error); g_error_free (tp_error); g_error_free (error); } } static void location_set_location (TpSvcConnectionInterfaceLocation *iface, GHashTable *location, DBusGMethodInvocation *context) { GabbleConnection *conn = GABBLE_CONNECTION (iface); WockyStanza *msg; WockyNode *geoloc; WockyNode *item; GHashTableIter iter; gpointer key, value; GError *err = NULL; TP_BASE_CONNECTION_ERROR_IF_NOT_CONNECTED ((TpBaseConnection *) conn, context); if (!(conn->features & GABBLE_CONNECTION_FEATURES_PEP)) { GError error = { TP_ERROR, TP_ERROR_NOT_IMPLEMENTED, "Server does not support PEP, cannot publish geolocation" }; dbus_g_method_return_error (context, &error); return; } gabble_connection_ensure_capabilities (conn, gabble_capabilities_get_geoloc_notify ()); msg = wocky_pep_service_make_publish_stanza (conn->pep_location, &item); geoloc = wocky_node_add_child_ns (item, "geoloc", NS_GEOLOC); DEBUG ("SetLocation to"); build_mapping_tables (); g_hash_table_iter_init (&iter, location); while (g_hash_table_iter_next (&iter, &key, &value)) { if (!add_to_geoloc_node ((const gchar *) key, (GValue *) value, geoloc, &err)) { DEBUG ("%s", err->message); dbus_g_method_return_error (context, err); g_error_free (err); goto out; } } if (!_gabble_connection_send_with_reply (conn, msg, set_location_sent_cb, G_OBJECT (conn), context, NULL)) { GError error = { TP_ERROR, TP_ERROR_NETWORK_ERROR, "Failed to send msg" }; dbus_g_method_return_error (context, &error); } out: g_object_unref (msg); } void location_iface_init (gpointer g_iface, gpointer iface_data) { TpSvcConnectionInterfaceLocationClass *klass = g_iface; #define IMPLEMENT(x) tp_svc_connection_interface_location_implement_##x \ (klass, location_##x) IMPLEMENT(get_locations); IMPLEMENT(set_location); IMPLEMENT(request_location); #undef IMPLEMENT } void conn_location_properties_getter (GObject *object, GQuark interface, GQuark name, GValue *value, gpointer getter_data) { GabbleConnection *conn = GABBLE_CONNECTION (object); if (!tp_strdiff (g_quark_to_string (name), "LocationAccessControlTypes")) { guint access_control_type = TP_RICH_PRESENCE_ACCESS_CONTROL_TYPE_PUBLISH_LIST; GArray *access_control = g_array_sized_new (FALSE, FALSE, sizeof (guint), 1); g_array_append_val (access_control, access_control_type); g_value_take_boxed (value, access_control); } else if (!tp_strdiff (g_quark_to_string (name), "LocationAccessControl")) { GValueArray *access_control = g_value_array_new (2); GValue type = {0,}; GValue variant = {0,}; GValue *allocated_value; /* G_TYPE_UINT is the D-Bus type of TpRichPresenceAccessControlType */ g_value_init (&type, G_TYPE_UINT); g_value_set_uint (&type, TP_RICH_PRESENCE_ACCESS_CONTROL_TYPE_PUBLISH_LIST); g_value_array_append (access_control, &type); g_value_unset (&type); g_value_init (&variant, G_TYPE_VALUE); /* For Publish_List, the variant isn't used, so we set a dummy value, * (guint) 0 */ allocated_value = tp_g_value_slice_new (G_TYPE_UINT); g_value_set_uint (allocated_value, 0); g_value_set_boxed (&variant, allocated_value); g_value_array_append (access_control, &variant); g_value_unset (&variant); tp_g_value_slice_free (allocated_value); g_value_take_boxed (value, access_control); } else if (name == g_quark_from_static_string ("SupportedLocationFeatures")) { TpLocationFeatures flags = 0; if (conn->features & GABBLE_CONNECTION_FEATURES_PEP) flags |= TP_LOCATION_FEATURE_CAN_SET; g_value_set_uint (value, flags); } else { g_assert_not_reached (); } } gboolean conn_location_properties_setter (GObject *object, GQuark interface, GQuark name, const GValue *value, gpointer setter_data, GError **error) { GValueArray *access_control; GValue *access_control_type_value; TpRichPresenceAccessControlType access_control_type; g_return_val_if_fail (interface == TP_IFACE_QUARK_CONNECTION_INTERFACE_LOCATION, FALSE); /* There is only one property with write access. So TpDBusPropertiesMixin * already checked this. */ g_assert (name == g_quark_from_static_string ("LocationAccessControl")); access_control = g_value_get_boxed (value); /* TpDBusPropertiesMixin already checked this */ g_assert (access_control->n_values == 2); access_control_type_value = g_value_array_get_nth (access_control, 0); /* TpDBusPropertiesMixin already checked this */ g_assert (G_VALUE_TYPE (access_control_type_value) == G_TYPE_UINT); access_control_type = g_value_get_uint (access_control_type_value); if (access_control_type != TP_RICH_PRESENCE_ACCESS_CONTROL_TYPE_PUBLISH_LIST) { g_set_error (error, TP_ERROR, TP_ERROR_NOT_IMPLEMENTED, "Access control type not implemented"); return FALSE; } return TRUE; } static gboolean update_location_from_item ( GabbleConnection *conn, TpHandle contact, WockyNode *item_node) { WockyNode *node; GHashTable *location = g_hash_table_new_full (g_direct_hash, g_direct_equal, g_free, (GDestroyNotify) tp_g_value_slice_free); TpHandleRepoIface *contact_repo = tp_base_connection_get_handles ( (TpBaseConnection *) conn, TP_HANDLE_TYPE_CONTACT); const gchar *from = tp_handle_inspect (contact_repo, contact); WockyNodeIter i; WockyNode *subloc_node; const gchar *lang; if (item_node == NULL) return FALSE; node = wocky_node_get_child_ns (item_node, "geoloc", NS_GEOLOC); if (node == NULL) return FALSE; DEBUG ("LocationsUpdate for %s:", from); lang = wocky_node_get_language (node); if (lang != NULL) { g_hash_table_insert (location, g_strdup ("language"), tp_g_value_slice_new_string (lang)); } build_mapping_tables (); wocky_node_iter_init (&i, node, NULL, NULL); while (wocky_node_iter_next (&i, &subloc_node)) { GValue *value = NULL; gchar *xmpp_name; const gchar *str; LocationMapping *mapping; xmpp_name = subloc_node->name; str = subloc_node->content; if (str == NULL) continue; mapping = g_hash_table_lookup (xmpp_to_tp, xmpp_name); if (mapping == NULL) { DEBUG ("Unknown location attribute: %s\n", xmpp_name); continue; } if (mapping->type == G_TYPE_DOUBLE) { gdouble double_value; gchar *end; double_value = g_ascii_strtod (str, &end); if (end == str) continue; value = tp_g_value_slice_new_double (double_value); DEBUG ("\t - %s: %f", xmpp_name, double_value); } else if (strcmp (xmpp_name, "timestamp") == 0) { GTimeVal timeval; if (g_time_val_from_iso8601 (str, &timeval)) { value = tp_g_value_slice_new_int64 (timeval.tv_sec); DEBUG ("\t - %s: %s", xmpp_name, str); } else { DEBUG ("\t - %s: %s: unknown date format", xmpp_name, str); continue; } } else if (mapping->type == G_TYPE_STRING) { value = tp_g_value_slice_new_string (str); DEBUG ("\t - %s: %s", xmpp_name, str); } else { g_assert_not_reached (); } g_hash_table_insert (location, g_strdup (mapping->tp_name), value); } tp_svc_connection_interface_location_emit_location_updated (conn, contact, location); gabble_presence_cache_update_location (conn->presence_cache, contact, location); return TRUE; } static void location_pep_node_changed (WockyPepService *pep, WockyBareContact *contact, WockyStanza *stanza, WockyNode *item_node, GabbleConnection *conn) { TpHandleRepoIface *contact_repo = tp_base_connection_get_handles ( (TpBaseConnection *) conn, TP_HANDLE_TYPE_CONTACT); TpBaseConnection *base = (TpBaseConnection *) conn; TpHandle handle; const gchar *jid; jid = wocky_bare_contact_get_jid (contact); handle = tp_handle_ensure (contact_repo, jid, NULL, NULL); if (handle == 0) { DEBUG ("Invalid from: %s", jid); return; } if (handle == tp_base_connection_get_self_handle (base)) /* Ignore echoed pubsub notifications */ return; update_location_from_item (conn, handle, item_node); } static void conn_location_fill_contact_attributes (GObject *obj, const GArray *contacts, GHashTable *attributes_hash) { GabbleConnection *self = GABBLE_CONNECTION (obj); guint i; for (i = 0; i < contacts->len; i++) { TpHandle handle = g_array_index (contacts, TpHandle, i); GHashTable *location = get_cached_location (self, handle); if (location != NULL) { GValue *val = tp_g_value_slice_new_take_boxed ( TP_HASH_TYPE_STRING_VARIANT_MAP, location); tp_contacts_mixin_set_contact_attribute (attributes_hash, handle, TP_IFACE_CONNECTION_INTERFACE_LOCATION"/location", val); } } } void conn_location_init (GabbleConnection *conn) { tp_contacts_mixin_add_contact_attributes_iface (G_OBJECT (conn), TP_IFACE_CONNECTION_INTERFACE_LOCATION, conn_location_fill_contact_attributes); conn->pep_location = wocky_pep_service_new (NS_GEOLOC, TRUE); g_signal_connect (conn->pep_location, "changed", G_CALLBACK (location_pep_node_changed), conn); } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������telepathy-gabble-0.18.2/src/conn-location.h���������������������������������������������������������0000644�0001750�0001750�00000001104�12200204333�020501� 0����������������������������������������������������������������������������������������������������ustar�00smcv����������������������������smcv����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ #ifndef __CONN_LOCATION_H__ #define __CONN_LOCATION_H__ #include "connection.h" #include <extensions/extensions.h> G_BEGIN_DECLS void location_iface_init (gpointer g_iface, gpointer iface_data); void conn_location_properties_getter (GObject *object, GQuark interface, GQuark name, GValue *value, gpointer getter_data); gboolean conn_location_properties_setter (GObject *object, GQuark interface, GQuark name, const GValue *value, gpointer setter_data, GError **error); void conn_location_init (GabbleConnection *conn); G_END_DECLS #endif /* __CONN_LOCATION_H__ */ ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������telepathy-gabble-0.18.2/src/conn-contact-info.c�����������������������������������������������������0000644�0001750�0001750�00000111711�12200204333�021256� 0����������������������������������������������������������������������������������������������������ustar�00smcv����������������������������smcv����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * conn-contact-info.c - Gabble connection ContactInfo interface * Copyright (C) 2009-2010 Collabora Ltd. * Copyright (C) 2009-2010 Nokia Corporation * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 "config.h" #include "conn-contact-info.h" #include <string.h> #include <telepathy-glib/telepathy-glib.h> #include <telepathy-glib/telepathy-glib-dbus.h> #include "vcard-manager.h" #define DEBUG_FLAG GABBLE_DEBUG_CONNECTION #include "debug.h" #include "util.h" /* Arbitrary lengths for supported fields' types, increase as necessary when * adding new fields */ #define MAX_TYPES 14 #define MAX_ELEMENTS 8 #define MAX_TYPE_PARAM_LEN 8 /* strlen ("internet") in "type=internet" */ typedef enum { /* in Telepathy: one value per field; in XMPP: one value per field */ FIELD_SIMPLE, /* same as FIELD_SIMPLE but may not be repeated */ FIELD_SIMPLE_ONCE, /* in Telepathy: exactly n_elements values; in XMPP: a child element for * each entry in elements, in that order */ FIELD_STRUCTURED, /* same as FIELD_STRUCTURED but may not be repeated */ FIELD_STRUCTURED_ONCE, /* Special cases: */ /* in Telepathy, one multi-line value; in XMPP, a sequence of <LINE>s */ FIELD_LABEL, /* same as FIELD_STRUCTURED except the last element may repeat n times */ FIELD_ORG, /* a field we intentionally ignore */ FIELD_IGNORED } FieldBehaviour; typedef struct { /* Name in XEP-0054, vcard-temp (upper-case as per the DTD) */ const gchar *xmpp_name; /* Name in Telepathy's vCard representation (lower-case), or NULL * to lower-case the XEP-0054 name automatically */ const gchar *vcard_name; /* General type of field */ FieldBehaviour behaviour; /* Telepathy flags for this field (none are applicable to XMPP yet) */ TpContactInfoFieldFlags tp_flags; /* Valid values for the TYPE type-parameter, in upper case */ const gchar * const types[MAX_TYPES]; /* Child elements for structured/repeating fields, in upper case */ const gchar * const elements[MAX_ELEMENTS]; } VCardField; static VCardField known_fields[] = { /* Simple fields */ { "FN", NULL, FIELD_SIMPLE_ONCE, 0, { NULL }, { NULL } }, { "BDAY", NULL, FIELD_SIMPLE, 0, { NULL }, { NULL } }, { "MAILER", NULL, FIELD_SIMPLE, 0, { NULL }, { NULL } }, { "TZ", NULL, FIELD_SIMPLE, 0, { NULL }, { NULL } }, { "TITLE", NULL, FIELD_SIMPLE, 0, { NULL }, { NULL } }, { "ROLE", NULL, FIELD_SIMPLE, 0, { NULL }, { NULL } }, { "NOTE", NULL, FIELD_SIMPLE, 0, { NULL }, { NULL } }, { "PRODID", NULL, FIELD_SIMPLE, 0, { NULL }, { NULL } }, { "REV", NULL, FIELD_SIMPLE, 0, { NULL }, { NULL } }, { "SORT-STRING", NULL, FIELD_SIMPLE, 0, { NULL }, { NULL } }, { "UID", NULL, FIELD_SIMPLE, 0, { NULL }, { NULL } }, { "URL", NULL, FIELD_SIMPLE, 0, { NULL }, { NULL } }, { "NICKNAME", NULL, FIELD_SIMPLE, 0, { NULL }, { NULL } }, /* Simple fields which are Jabber-specific */ { "JABBERID", "x-jabber", FIELD_SIMPLE, 0, { NULL }, { NULL } }, { "DESC", "x-desc", FIELD_SIMPLE, 0, { NULL }, { NULL } }, /* Structured fields */ { "N", NULL, FIELD_STRUCTURED_ONCE, 0, { NULL }, { "FAMILY", "GIVEN", "MIDDLE", "PREFIX", "SUFFIX", NULL } }, { "ADR", NULL, FIELD_STRUCTURED, 0, { "type=home", "type=work", "type=postal", "type=parcel", "type=dom", "type=intl", "type=pref", NULL }, { "POBOX", "EXTADD", "STREET", "LOCALITY", "REGION", "PCODE", "CTRY", NULL } }, { "GEO", NULL, FIELD_STRUCTURED_ONCE, 0, { NULL }, { "LAT", "LON", NULL } }, /* TEL and EMAIL are like structured fields: they have exactly one child * per occurrence */ { "TEL", NULL, FIELD_STRUCTURED, 0, { "type=home", "type=work", "type=voice", "type=fax", "type=pager", "type=msg", "type=cell", "type=video", "type=bbs", "type=modem", "type=isdn", "type=pcs", "type=pref", NULL }, { "NUMBER", NULL } }, { "EMAIL", NULL, FIELD_STRUCTURED, 0, { "type=home", "type=work", "type=internet", "type=pref", "type=x400", NULL }, { "USERID", NULL } }, /* Special cases with their own semantics */ { "LABEL", NULL, FIELD_LABEL, 0, { "type=home", "type=work", "type=postal", "type=parcel", "type=dom", "type=intl", "type=pref", NULL }, { NULL } }, { "ORG", NULL, FIELD_ORG, 0, { NULL }, { NULL } }, /* Things we don't handle: */ /* PHOTO is handled by the Avatar code */ { "PHOTO", NULL, FIELD_IGNORED }, /* KEY: is Base64 (perhaps? hard to tell from the XEP) */ /* LOGO: can be base64 or a URL */ /* SOUND: can be base64, URL, or phonetic (!) */ /* AGENT: is an embedded vCard (!) */ /* CATEGORIES: same vCard encoding as NICKNAME, but split into KEYWORDs * in XMPP; nobody is likely to use it on XMPP */ /* CLASS: if you're putting non-PUBLIC vCards on your XMPP account, * you're probably Doing It Wrong */ { NULL } }; /* static XML element name => static VCardField */ static GHashTable *known_fields_xmpp = NULL; /* g_strdup'd Telepathy pseudo-vCard element name => static VCardField */ static GHashTable *known_fields_vcard = NULL; /* * _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 void _create_contact_field_extended (GPtrArray *contact_info, WockyNode *node, const gchar * const *supported_types, const gchar * const *mandatory_fields) { guint i; WockyNode *child_node; GPtrArray *field_params = NULL; gchar **field_values = NULL; guint supported_types_size = 0; guint mandatory_fields_size = 0; if (supported_types != NULL) supported_types_size = g_strv_length ((gchar **) supported_types); field_params = g_ptr_array_new (); /* we can simply omit a type if not found */ for (i = 0; i < supported_types_size; ++i) { guint j; gchar child_name[MAX_TYPE_PARAM_LEN + 1] = { '\0' }; /* the +5 is to skip over "type=" - all type-parameters we support have * type=, which is verified in conn_contact_info_build_supported_fields */ for (j = 0; j < MAX_TYPE_PARAM_LEN && supported_types[i][j + 5] != '\0'; j++) { child_name[j] = g_ascii_toupper (supported_types[i][j + 5]); } child_node = wocky_node_get_child (node, child_name); if (child_node != NULL) g_ptr_array_add (field_params, (gchar *) supported_types[i]); } g_ptr_array_add (field_params, NULL); if (mandatory_fields != NULL) { mandatory_fields_size = g_strv_length ((gchar **) mandatory_fields); /* the mandatory field values need to be ordered properly */ field_values = g_new0 (gchar *, mandatory_fields_size + 1); for (i = 0; i < mandatory_fields_size; ++i) { child_node = wocky_node_get_child (node, mandatory_fields[i]); if (child_node != NULL) field_values[i] = child_node->content; else field_values[i] = ""; } } _insert_contact_field (contact_info, node->name, (const gchar * const *) field_params->pdata, (const gchar * const *) field_values); /* The strings in both arrays are borrowed, so we just need to free the * arrays themselves. */ g_ptr_array_unref (field_params); g_free (field_values); } static GPtrArray * _parse_vcard (WockyNode *vcard_node, GError **error) { GPtrArray *contact_info = dbus_g_type_specialized_construct ( TP_ARRAY_TYPE_CONTACT_INFO_FIELD_LIST); WockyNodeIter i; WockyNode *node; wocky_node_iter_init (&i, vcard_node, NULL, NULL); while (wocky_node_iter_next (&i, &node)) { const VCardField *field = g_hash_table_lookup (known_fields_xmpp, node->name); if (field == NULL) { DEBUG ("unknown vCard node in XML: %s", node->name); continue; } switch (field->behaviour) { case FIELD_SIMPLE: case FIELD_SIMPLE_ONCE: { const gchar * const field_values[2] = { node->content, NULL }; _insert_contact_field (contact_info, node->name, NULL, field_values); } break; case FIELD_STRUCTURED: case FIELD_STRUCTURED_ONCE: { _create_contact_field_extended (contact_info, node, field->types, field->elements); } break; case FIELD_ORG: { WockyNode *orgname = wocky_node_get_child (node, "ORGNAME"); WockyNodeIter orgunit_iter; WockyNode *orgunit; GPtrArray *field_values; const gchar *value; if (orgname == NULL) { DEBUG ("ignoring <ORG> with no <ORGNAME>"); break; } field_values = g_ptr_array_new (); value = orgname->content; if (value == NULL) value = ""; g_ptr_array_add (field_values, (gpointer) value); wocky_node_iter_init (&orgunit_iter, node, "ORGUNIT", NULL); while (wocky_node_iter_next (&orgunit_iter, &orgunit)) { value = orgunit->content; if (value == NULL) value = ""; g_ptr_array_add (field_values, (gpointer) value); } g_ptr_array_add (field_values, NULL); _insert_contact_field (contact_info, "org", NULL, (const gchar * const *) field_values->pdata); g_ptr_array_unref (field_values); } break; case FIELD_LABEL: { WockyNodeIter line_iter; WockyNode *line_node; gchar *field_values[2] = { NULL, NULL }; GString *text = g_string_new (""); wocky_node_iter_init (&line_iter, node, "LINE", NULL); while (wocky_node_iter_next (&line_iter, &line_node)) { const gchar *line = line_node->content; if (line != NULL) { g_string_append (text, line); } if (line == NULL || ! g_str_has_suffix (line, "\n")) { g_string_append_c (text, '\n'); } } field_values[0] = g_string_free (text, FALSE); _insert_contact_field (contact_info, "label", NULL, (const gchar * const *) field_values); g_free (field_values[0]); } break; case FIELD_IGNORED: break; default: g_assert_not_reached (); } } return contact_info; } static void _emit_contact_info_changed (TpSvcConnectionInterfaceContactInfo *iface, TpHandle contact, WockyNode *vcard_node) { GPtrArray *contact_info; contact_info = _parse_vcard (vcard_node, NULL); if (contact_info == NULL) return; tp_svc_connection_interface_contact_info_emit_contact_info_changed ( iface, contact, contact_info); g_boxed_free (TP_ARRAY_TYPE_CONTACT_INFO_FIELD_LIST, contact_info); } static void _request_vcards_cb (GabbleVCardManager *manager, GabbleVCardManagerRequest *request, TpHandle handle, WockyNode *vcard_node, GError *vcard_error, gpointer user_data) { GabbleConnection *conn = GABBLE_CONNECTION (user_data); g_assert (g_hash_table_lookup (conn->vcard_requests, GUINT_TO_POINTER (handle))); g_hash_table_remove (conn->vcard_requests, GUINT_TO_POINTER (handle)); /* No need to signal ContactInfoChanged here because it'll get done * in vcard_updated. */ } /** * gabble_connection_get_contact_info * * Implements D-Bus method GetContactInfo * on interface org.freedesktop.Telepathy.Connection.Interface.ContactInfo * * @context: The D-Bus invocation context to use to return values * or throw an error. */ static void gabble_connection_get_contact_info ( TpSvcConnectionInterfaceContactInfo *iface, const GArray *contacts, DBusGMethodInvocation *context) { GabbleConnection *self = GABBLE_CONNECTION (iface); TpBaseConnection *base = (TpBaseConnection *) self; TpHandleRepoIface *contacts_repo = tp_base_connection_get_handles (base, TP_HANDLE_TYPE_CONTACT); GError *error = NULL; guint i; GHashTable *ret; TP_BASE_CONNECTION_ERROR_IF_NOT_CONNECTED (TP_BASE_CONNECTION (iface), context); if (!tp_handles_are_valid (contacts_repo, 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); for (i = 0; i < contacts->len; i++) { WockyNode *vcard_node; TpHandle contact = g_array_index (contacts, TpHandle, i); if (gabble_vcard_manager_get_cached (self->vcard_manager, contact, &vcard_node)) { GPtrArray *contact_info = _parse_vcard (vcard_node, NULL); /* we have the cached vcard but it cannot be parsed, skipping */ if (contact_info == NULL) { DEBUG ("contact %d vcard is cached but cannot be parsed, " "skipping.", contact); continue; } g_hash_table_insert (ret, GUINT_TO_POINTER (contact), contact_info); } } 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 _return_from_request_contact_info (WockyNode *vcard_node, GError *vcard_error, DBusGMethodInvocation *context) { GError *error = NULL; GPtrArray *contact_info; if (NULL == vcard_node) { GError tp_error = { TP_ERROR, TP_ERROR_NOT_AVAILABLE, vcard_error->message }; if (vcard_error->domain == WOCKY_XMPP_ERROR) { switch (vcard_error->code) { case WOCKY_XMPP_ERROR_NOT_AUTHORIZED: case WOCKY_XMPP_ERROR_FORBIDDEN: tp_error.code = TP_ERROR_PERMISSION_DENIED; break; case WOCKY_XMPP_ERROR_ITEM_NOT_FOUND: tp_error.code = TP_ERROR_DOES_NOT_EXIST; break; } /* what other mappings make sense here? */ } dbus_g_method_return_error (context, &tp_error); return; } contact_info = _parse_vcard (vcard_node, &error); if (contact_info == NULL) { dbus_g_method_return_error (context, error); g_error_free (error); return; } tp_svc_connection_interface_contact_info_return_from_request_contact_info ( context, contact_info); g_boxed_free (TP_ARRAY_TYPE_CONTACT_INFO_FIELD_LIST, contact_info); } static void _request_vcard_cb (GabbleVCardManager *self, GabbleVCardManagerRequest *request, TpHandle handle, WockyNode *vcard_node, GError *vcard_error, gpointer user_data) { DBusGMethodInvocation *context = user_data; _return_from_request_contact_info (vcard_node, vcard_error, context); } /** * gabble_connection_refresh_contact_info * * Implements D-Bus method RefreshContactInfo * on interface org.freedesktop.Telepathy.Connection.Interface.ContactInfo * * @context: The D-Bus invocation context to use to return values * or throw an error. */ static void gabble_connection_refresh_contact_info (TpSvcConnectionInterfaceContactInfo *iface, const GArray *contacts, DBusGMethodInvocation *context) { GabbleConnection *self = GABBLE_CONNECTION (iface); TpBaseConnection *base = (TpBaseConnection *) self; TpHandleRepoIface *contacts_repo = tp_base_connection_get_handles (base, TP_HANDLE_TYPE_CONTACT); GError *error = NULL; guint i; TP_BASE_CONNECTION_ERROR_IF_NOT_CONNECTED (TP_BASE_CONNECTION (iface), context); if (!tp_handles_are_valid (contacts_repo, contacts, FALSE, &error)) { dbus_g_method_return_error (context, error); g_error_free (error); return; } for (i = 0; i < contacts->len; i++) { TpHandle contact = g_array_index (contacts, TpHandle, i); if (g_hash_table_lookup (self->vcard_requests, GUINT_TO_POINTER (contact)) == NULL) { GabbleVCardManagerRequest *request; gabble_vcard_manager_invalidate_cache (self->vcard_manager, contact); request = gabble_vcard_manager_request (self->vcard_manager, contact, 0, _request_vcards_cb, self, NULL); g_hash_table_insert (self->vcard_requests, GUINT_TO_POINTER (contact), request); } } tp_svc_connection_interface_contact_info_return_from_refresh_contact_info ( context); } /** * gabble_connection_request_contact_info * * Implements D-Bus method RequestContactInfo * on interface org.freedesktop.Telepathy.Connection.Interface.ContactInfo * * @context: The D-Bus invocation context to use to return values * or throw an error. */ static void gabble_connection_request_contact_info (TpSvcConnectionInterfaceContactInfo *iface, guint contact, DBusGMethodInvocation *context) { GabbleConnection *self = GABBLE_CONNECTION (iface); TpBaseConnection *base = (TpBaseConnection *) self; TpHandleRepoIface *contact_handles = tp_base_connection_get_handles (base, TP_HANDLE_TYPE_CONTACT); GError *err = NULL; WockyNode *vcard_node; TP_BASE_CONNECTION_ERROR_IF_NOT_CONNECTED (base, context); if (!tp_handle_is_valid (contact_handles, contact, &err)) { dbus_g_method_return_error (context, err); g_error_free (err); return; } if (gabble_vcard_manager_get_cached (self->vcard_manager, contact, &vcard_node)) _return_from_request_contact_info (vcard_node, NULL, context); else gabble_vcard_manager_request (self->vcard_manager, contact, 0, _request_vcard_cb, context, NULL); } static GabbleVCardManagerEditInfo * conn_contact_info_new_edit (const VCardField *field, const gchar *value, const gchar * const *field_params, GError **error) { GabbleVCardManagerEditInfo *edit_info; GabbleVCardEditType edit_type = GABBLE_VCARD_EDIT_APPEND; const gchar * const *p; if (field->behaviour == FIELD_STRUCTURED_ONCE || field->behaviour == FIELD_SIMPLE_ONCE) edit_type = GABBLE_VCARD_EDIT_REPLACE; edit_info = gabble_vcard_manager_edit_info_new (field->xmpp_name, value, edit_type, NULL); if (field_params == NULL) return edit_info; if (field->types[0] == NULL && field_params[0] != NULL) { g_set_error (error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT, "%s vCard field expects no type-parameters", field->xmpp_name); gabble_vcard_manager_edit_info_free (edit_info); return NULL; } for (p = field_params; *p != NULL; ++p) { guint i; gboolean used = FALSE; for (i = 0; field->types[i] != NULL; i++) { if (!tp_strdiff (field->types[i], *p)) { /* the +5 is to skip over "type=" - all type-parameters we * support have type=, which is verified in * conn_contact_info_build_supported_fields */ gchar *tmp = g_ascii_strup (field->types[i] + 5, -1); gabble_vcard_manager_edit_info_add_child (edit_info, tmp, NULL); g_free (tmp); used = TRUE; break; } } if (!used) { g_set_error (error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT, "%s vCard field does not support type-parameter %s", field->xmpp_name, *p); gabble_vcard_manager_edit_info_free (edit_info); return NULL; } } return edit_info; } static void _set_contact_info_cb (GabbleVCardManager *vcard_manager, GabbleVCardManagerEditRequest *request, WockyNode *vcard_node, GError *vcard_error, gpointer user_data) { DBusGMethodInvocation *context = user_data; if (vcard_node == NULL) { GError tp_error = { TP_ERROR, TP_ERROR_NOT_AVAILABLE, vcard_error->message }; if (vcard_error->domain == WOCKY_XMPP_ERROR) if (vcard_error->code == WOCKY_XMPP_ERROR_BAD_REQUEST || vcard_error->code == WOCKY_XMPP_ERROR_NOT_ACCEPTABLE) tp_error.code = TP_ERROR_INVALID_ARGUMENT; dbus_g_method_return_error (context, &tp_error); } else { tp_svc_connection_interface_contact_info_return_from_set_contact_info ( context); } } /** * gabble_connection_set_contact_info * * Implements D-Bus method SetContactInfo * on interface org.freedesktop.Telepathy.Connection.Interface.ContactInfo * * @context: The D-Bus invocation context to use to return values * or throw an error. */ static void gabble_connection_set_contact_info (TpSvcConnectionInterfaceContactInfo *iface, const GPtrArray *contact_info, DBusGMethodInvocation *context) { GabbleConnection *self = GABBLE_CONNECTION (iface); TpBaseConnection *base = (TpBaseConnection *) self; GList *edits = NULL; guint i; GError *error = NULL; TP_BASE_CONNECTION_ERROR_IF_NOT_CONNECTED (base, context); for (i = 0; i < contact_info->len; i++) { GValueArray *structure = g_ptr_array_index (contact_info, i); guint n_field_values = 0; VCardField *field; const gchar *field_name; const gchar * const *field_params; const gchar * const *field_values; GabbleVCardManagerEditInfo *edit_info; field_name = g_value_get_string (structure->values + 0); field_params = g_value_get_boxed (structure->values + 1); field_values = g_value_get_boxed (structure->values + 2); if (field_values != NULL) n_field_values = g_strv_length ((gchar **) field_values); field = g_hash_table_lookup (known_fields_vcard, field_name); if (field == NULL) { g_set_error (&error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT, "unknown vCard field from D-Bus: %s", field_name); goto finally; } if (!gabble_vcard_manager_can_use_vcard_field (self->vcard_manager, field->xmpp_name)) { g_set_error (&error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT, "%s vCard field is not supported by this server", field->xmpp_name); goto finally; } switch (field->behaviour) { case FIELD_SIMPLE: case FIELD_SIMPLE_ONCE: { if (n_field_values != 1) { g_set_error (&error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT, "%s vCard field expects one value but got %u", field->xmpp_name, n_field_values); goto finally; } edit_info = conn_contact_info_new_edit (field, field_values[0], field_params, &error); if (edit_info == NULL) { goto finally; } } break; case FIELD_STRUCTURED: case FIELD_STRUCTURED_ONCE: { guint n_elements = g_strv_length ((gchar **) field->elements); guint j; if (n_field_values != n_elements) { g_set_error (&error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT, "%s vCard field expects %u values but got %u", field->xmpp_name, n_elements, n_field_values); goto finally; } edit_info = conn_contact_info_new_edit (field, NULL, field_params, &error); if (edit_info == NULL) { goto finally; } for (j = 0; j < n_elements; ++j) gabble_vcard_manager_edit_info_add_child (edit_info, field->elements[j], field_values[j]); } break; case FIELD_ORG: { guint j; if (n_field_values == 0) { g_set_error (&error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT, "ORG vCard field expects at least one value but got 0"); goto finally; } edit_info = conn_contact_info_new_edit (field, NULL, field_params, &error); if (edit_info == NULL) { goto finally; } gabble_vcard_manager_edit_info_add_child (edit_info, "ORGNAME", field_values[0]); for (j = 1; field_values[j] != NULL; j++) { gabble_vcard_manager_edit_info_add_child (edit_info, "ORGUNIT", field_values[j]); } } break; case FIELD_LABEL: { gchar **lines; guint j; if (n_field_values != 1) { g_set_error (&error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT, "%s vCard field expects one value but got %u", field->xmpp_name, n_field_values); goto finally; } edit_info = conn_contact_info_new_edit (field, NULL, field_params, &error); if (edit_info == NULL) { goto finally; } lines = g_strsplit (field_values[0], "\n", 0); for (j = 0; lines[j] != NULL; j++) { /* don't emit a trailing empty line if the label ended * with \n */ if (lines[j][0] == '\0' && lines[j + 1] == NULL) continue; gabble_vcard_manager_edit_info_add_child (edit_info, "LINE", lines[j]); } g_strfreev (lines); } break; default: g_assert_not_reached (); } g_assert (edit_info != NULL); edits = g_list_append (edits, edit_info); } finally: if (error != NULL) { DEBUG ("%s", error->message); g_list_foreach (edits, (GFunc) gabble_vcard_manager_edit_info_free, NULL); dbus_g_method_return_error (context, error); g_error_free (error); } else { edits = g_list_prepend (edits, gabble_vcard_manager_edit_info_new (NULL, NULL, GABBLE_VCARD_EDIT_CLEAR, NULL)); /* fix the alias (if missing) afterwards */ edits = g_list_append (edits, gabble_vcard_manager_edit_info_new (NULL, NULL, GABBLE_VCARD_EDIT_SET_ALIAS, NULL)); gabble_vcard_manager_edit (self->vcard_manager, 0, _set_contact_info_cb, context, G_OBJECT (self), edits); } } static void _vcard_updated (GObject *object, TpHandle contact, gpointer user_data) { GabbleConnection *conn = GABBLE_CONNECTION (user_data); WockyNode *vcard_node; if (conn->vcard_manager != NULL && gabble_vcard_manager_get_cached (conn->vcard_manager, contact, &vcard_node)) { _emit_contact_info_changed ( TP_SVC_CONNECTION_INTERFACE_CONTACT_INFO (conn), contact, vcard_node); } } static GPtrArray * conn_contact_info_build_supported_fields (GabbleConnection *conn, GabbleVCardManager *vcard_manager) { GPtrArray *fields = dbus_g_type_specialized_construct ( TP_ARRAY_TYPE_FIELD_SPECS); VCardField *field; for (field = known_fields; field->xmpp_name != NULL; field++) { GValueArray *va; gchar *vcard_name; guint max_times; guint i; TpContactInfoFieldFlags tp_flags = field->tp_flags; if (field->behaviour == FIELD_IGNORED) continue; /* Shorthand to avoid having to put it in the struct initialization: * on XMPP, there is no field that supports arbitrary type-parameters. * Setting Parameters_Mandatory eliminates the special case that an * empty list means arbitrary parameters. */ if (field->types[0] == NULL) { tp_flags |= TP_CONTACT_INFO_FIELD_FLAG_PARAMETERS_EXACT; } #ifndef G_DISABLE_ASSERT for (i = 0; field->types[i] != NULL; i++) { /* All type-parameters XMPP currently supports are of the form type=, * which is assumed in _create_contact_field_extended and * conn_contact_info_edit_add_type_params */ g_assert (g_str_has_prefix (field->types[i], "type=")); g_assert_cmpuint ((guint) strlen (field->types[i]), <=, MAX_TYPE_PARAM_LEN + 5); } #endif if (!gabble_vcard_manager_can_use_vcard_field (vcard_manager, field->xmpp_name)) { continue; } if (field->vcard_name != NULL) vcard_name = g_strdup (field->vcard_name); else vcard_name = g_ascii_strdown (field->xmpp_name, -1); /* On Google, your full name in your VCard *is* your alias, so * SetAliases and setting FN will do exactly the same thing. As * a result, we should set the Overwritten_By_Nickname flag on * the FN VCard field if we're on Google. * * If we're not on Google, your nickname in your VCard is your * alias and the same stuff applies, so we should set the same * flag. * * Doing this means that UIs can omit fields from ContactInfo * dialogs with this flag set so they can just display one * widget to set the alias. It's confusing to have an entry * widget which gets overridden and can screw up your alias * setting anyway. */ if (conn->features & GABBLE_CONNECTION_FEATURES_GOOGLE_ROSTER) { if (!tp_strdiff (vcard_name, "fn")) tp_flags |= TP_CONTACT_INFO_FIELD_FLAG_OVERWRITTEN_BY_NICKNAME; } else { if (!tp_strdiff (vcard_name, "nickname")) tp_flags |= TP_CONTACT_INFO_FIELD_FLAG_OVERWRITTEN_BY_NICKNAME; } switch (field->behaviour) { case FIELD_SIMPLE_ONCE: case FIELD_STRUCTURED_ONCE: max_times = 1; break; default: max_times = G_MAXUINT32; } va = tp_value_array_build (4, G_TYPE_STRING, vcard_name, G_TYPE_STRV, field->types, G_TYPE_UINT, tp_flags, G_TYPE_UINT, max_times, G_TYPE_INVALID); g_free (vcard_name); g_ptr_array_add (fields, va); } return fields; } void conn_contact_info_class_init (GabbleConnectionClass *klass) { VCardField *field; /* These are never freed; they're only allocated once per run of Gabble. * The destructor in the latter is only set for completeness */ known_fields_xmpp = g_hash_table_new (g_str_hash, g_str_equal); known_fields_vcard = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); for (field = known_fields; field->xmpp_name != NULL; field++) { gchar *vcard_name; if (field->vcard_name != NULL) vcard_name = g_strdup (field->vcard_name); else vcard_name = g_ascii_strdown (field->xmpp_name, -1); g_hash_table_insert (known_fields_xmpp, (gchar *) field->xmpp_name, field); g_hash_table_insert (known_fields_vcard, vcard_name, field); } } static void conn_contact_info_status_changed_cb (GabbleConnection *conn, guint status, guint reason, gpointer user_data G_GNUC_UNUSED) { if (status != TP_CONNECTION_STATUS_CONNECTED) return; if (gabble_vcard_manager_has_limited_vcard_fields (conn->vcard_manager)) { g_boxed_free (TP_ARRAY_TYPE_FIELD_SPECS, conn->contact_info_fields); conn->contact_info_fields = conn_contact_info_build_supported_fields ( conn, conn->vcard_manager); } } static void conn_contact_info_fill_contact_attributes (GObject *obj, const GArray *contacts, GHashTable *attributes_hash) { guint i; GabbleConnection *self = GABBLE_CONNECTION (obj); g_assert (self->vcard_manager != NULL); for (i = 0; i < contacts->len; i++) { TpHandle contact = g_array_index (contacts, TpHandle, i); WockyNode *vcard_node; if (gabble_vcard_manager_get_cached (self->vcard_manager, contact, &vcard_node)) { GPtrArray *contact_info = _parse_vcard (vcard_node, NULL); if (contact_info != NULL) { GValue *val = tp_g_value_slice_new_take_boxed ( TP_ARRAY_TYPE_CONTACT_INFO_FIELD_LIST, contact_info); tp_contacts_mixin_set_contact_attribute (attributes_hash, contact, TP_IFACE_CONNECTION_INTERFACE_CONTACT_INFO"/info", val); } } } } void conn_contact_info_init (GabbleConnection *conn) { g_assert (conn->vcard_manager != NULL); tp_contacts_mixin_add_contact_attributes_iface (G_OBJECT (conn), TP_IFACE_CONNECTION_INTERFACE_CONTACT_INFO, conn_contact_info_fill_contact_attributes); conn->contact_info_fields = conn_contact_info_build_supported_fields (conn, conn->vcard_manager); g_signal_connect (conn->vcard_manager, "vcard-update", G_CALLBACK (_vcard_updated), conn); g_signal_connect (conn, "status-changed", G_CALLBACK (conn_contact_info_status_changed_cb), NULL); } void conn_contact_info_finalize (GabbleConnection *conn) { g_boxed_free (TP_ARRAY_TYPE_FIELD_SPECS, conn->contact_info_fields); conn->contact_info_fields = NULL; } void conn_contact_info_iface_init (gpointer g_iface, gpointer iface_data) { TpSvcConnectionInterfaceContactInfoClass *klass = g_iface; #define IMPLEMENT(x) tp_svc_connection_interface_contact_info_implement_##x (\ klass, gabble_connection_##x) IMPLEMENT(get_contact_info); IMPLEMENT(refresh_contact_info); IMPLEMENT(request_contact_info); IMPLEMENT(set_contact_info); #undef IMPLEMENT } static TpDBusPropertiesMixinPropImpl props[] = { { "ContactInfoFlags", GUINT_TO_POINTER (TP_CONTACT_INFO_FLAG_CAN_SET), NULL }, { "SupportedFields", NULL, NULL }, { NULL } }; TpDBusPropertiesMixinPropImpl *conn_contact_info_properties = props; void conn_contact_info_properties_getter (GObject *object, GQuark interface, GQuark name, GValue *value, gpointer getter_data) { GabbleConnection *conn = GABBLE_CONNECTION (object); GQuark q_supported_fields = g_quark_from_static_string ( "SupportedFields"); if (name == q_supported_fields) { g_value_set_boxed (value, conn->contact_info_fields); } else { g_value_set_uint (value, GPOINTER_TO_UINT (getter_data)); } } �������������������������������������������������������telepathy-gabble-0.18.2/src/conn-contact-info.h�����������������������������������������������������0000644�0001750�0001750�00000002762�12200204333�021270� 0����������������������������������������������������������������������������������������������������ustar�00smcv����������������������������smcv����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * conn-contact-info.h - Header for Gabble connection ContactInfo interface * Copyright (C) 2009-2010 Collabora Ltd. * Copyright (C) 2009-2010 Nokia Corporation * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 __CONN_CONTACT_INFO_H__ #define __CONN_CONTACT_INFO_H__ #include "connection.h" G_BEGIN_DECLS void conn_contact_info_class_init (GabbleConnectionClass *klass); void conn_contact_info_init (GabbleConnection *conn); void conn_contact_info_finalize (GabbleConnection *conn); void conn_contact_info_iface_init (gpointer g_iface, gpointer iface_data); extern TpDBusPropertiesMixinPropImpl *conn_contact_info_properties; void conn_contact_info_properties_getter (GObject *object, GQuark interface, GQuark name, GValue *value, gpointer getter_data); G_END_DECLS #endif /* __CONN_CONTACT_INFO_H__ */ ��������������telepathy-gabble-0.18.2/src/conn-client-types.c�����������������������������������������������������0000644�0001750�0001750�00000015730�12227000321�021316� 0����������������������������������������������������������������������������������������������������ustar�00smcv����������������������������smcv����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * conn-client-types - Gabble client types interface * Copyright (C) 2010 Collabora Ltd. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 "config.h" #include <string.h> #include <stdlib.h> #include <telepathy-glib/telepathy-glib.h> #include <telepathy-glib/telepathy-glib-dbus.h> #include <extensions/extensions.h> #include "conn-client-types.h" #include "disco.h" #include "namespaces.h" #include "presence.h" #include "presence-cache.h" #define DEBUG_FLAG GABBLE_DEBUG_CLIENT_TYPES #include "debug.h" static gboolean get_client_types_from_handle (GabbleConnection *conn, TpHandle handle, gchar ***types_out) { GabblePresence *presence = gabble_presence_cache_get (conn->presence_cache, handle); g_return_val_if_fail (types_out != NULL, FALSE); if (presence == NULL) { /* We have no presence information for this contact; so they have no * known client types. */ static gchar *empty[] = { NULL }; *types_out = g_strdupv (empty); return TRUE; } else { const gchar *res; gchar **types = gabble_presence_get_client_types_array (presence, &res); /* If we don't have any client types for this contact, and a disco * request is in progress, then keep quiet rather than reporting that * they have no client types; when the result comes in, their true client * types will be reported. */ if (types[0] == NULL && gabble_presence_cache_disco_in_progress (conn->presence_cache, handle, res)) { g_strfreev (types); return FALSE; } *types_out = types; return TRUE; } } static void client_types_get_client_types (TpSvcConnectionInterfaceClientTypes *iface, const GArray *contacts, DBusGMethodInvocation *context) { GabbleConnection *conn = GABBLE_CONNECTION (iface); TpBaseConnection *base = (TpBaseConnection *) conn; TpHandleRepoIface *contact_handles; guint i; GHashTable *client_types; GError *error = NULL; /* Validate contacts */ contact_handles = tp_base_connection_get_handles (base, TP_HANDLE_TYPE_CONTACT); if (!tp_handles_are_valid (contact_handles, contacts, TRUE, &error)) { dbus_g_method_return_error (context, error); g_error_free (error); return; } if (DEBUGGING) { DEBUG ("GetClientTypes called on the following handles:"); for (i = 0; i < contacts->len; i++) { DEBUG (" * %u", g_array_index (contacts, TpHandle, i)); } } client_types = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, (GDestroyNotify) g_strfreev); for (i = 0; i < contacts->len; i++) { TpHandle handle = g_array_index (contacts, TpHandle, i); gchar **types; if (!get_client_types_from_handle (conn, handle, &types)) continue; g_hash_table_insert (client_types, GUINT_TO_POINTER (handle), types); } tp_svc_connection_interface_client_types_return_from_get_client_types ( context, client_types); g_hash_table_unref (client_types); } void conn_client_types_iface_init (gpointer g_iface, gpointer iface_data) { TpSvcConnectionInterfaceClientTypesClass *klass = g_iface; #define IMPLEMENT(x) tp_svc_connection_interface_client_types_implement_##x \ (klass, client_types_##x) IMPLEMENT (get_client_types); #undef IMPLEMENT } static void conn_client_types_fill_contact_attributes (GObject *obj, const GArray *contacts, GHashTable *attributes_hash) { GabbleConnection *conn = GABBLE_CONNECTION (obj); guint i; for (i = 0; i < contacts->len; i++) { TpHandle handle = g_array_index (contacts, TpHandle, i); GValue *val; gchar **types; if (!get_client_types_from_handle (conn, handle, &types)) continue; val = tp_g_value_slice_new_take_boxed (G_TYPE_STRV, types); tp_contacts_mixin_set_contact_attribute (attributes_hash, handle, TP_IFACE_CONNECTION_INTERFACE_CLIENT_TYPES "/client-types", val); } } typedef struct { TpHandle handle; GabbleConnection *conn; } UpdatedData; static void updated_data_free (gpointer user_data) { UpdatedData *data = user_data; if (data->conn != NULL) g_object_remove_weak_pointer (G_OBJECT (data->conn), (gpointer *) &data->conn); g_slice_free (UpdatedData, data); } static gboolean idle_timeout (gpointer user_data) { UpdatedData *data = user_data; gchar **types; if (data->conn == NULL) return FALSE; if (get_client_types_from_handle (data->conn, data->handle, &types)) { tp_svc_connection_interface_client_types_emit_client_types_updated ( data->conn, data->handle, (const gchar **) types); g_strfreev (types); } return FALSE; } static void presence_cache_client_types_updated_cb (GabblePresenceCache *presence_cache, TpHandle handle, GabbleConnection *conn) { UpdatedData *data = g_slice_new0 (UpdatedData); data->handle = handle; data->conn = conn; g_object_add_weak_pointer (G_OBJECT (conn), (gpointer *) &data->conn); /* Unfortunately, the client-types-updated signal can be emitted before the * caps URIs have been processed to determine which client types a * freshly-online contact has (and whether a disco request needs to be made * to find out). * * Specifically, gabble_presence_cache_update() may emit client-types-updated * if a presence change causes the "dominant" resource to change and the new * resource has a different set of client types to the previous one. It is * called by gabble_presence_parse_presence_message() just before * _process_caps() is called (within which disco requests are sent if * necessary). It turns out to be very difficult to rearrange things to sort * this out. Moving the emission of the D-Bus signal to an idle allows us to * avoid it when we're actually waiting for a disco response to come in. */ g_idle_add_full (G_PRIORITY_DEFAULT_IDLE, idle_timeout, data, updated_data_free); } void conn_client_types_init (GabbleConnection *conn) { tp_contacts_mixin_add_contact_attributes_iface (G_OBJECT (conn), TP_IFACE_CONNECTION_INTERFACE_CLIENT_TYPES, conn_client_types_fill_contact_attributes); g_signal_connect (conn->presence_cache, "client-types-updated", G_CALLBACK (presence_cache_client_types_updated_cb), conn); } ����������������������������������������telepathy-gabble-0.18.2/src/conn-client-types.h�����������������������������������������������������0000644�0001750�0001750�00000002207�12200204333�021316� 0����������������������������������������������������������������������������������������������������ustar�00smcv����������������������������smcv����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * conn-client-types - Gabble client types interface * Copyright (C) 2010 Collabora Ltd. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 __CONN_CLIENT_TYPES_H__ #define __CONN_CLIENT_TYPES_H__ #include "connection.h" #include <extensions/extensions.h> G_BEGIN_DECLS void conn_client_types_iface_init (gpointer g_iface, gpointer iface_data); void conn_client_types_init (GabbleConnection *conn); G_END_DECLS #endif /* __CONN_CLIENT_TYPES_H__ */ �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������telepathy-gabble-0.18.2/src/conn-avatars.c����������������������������������������������������������0000644�0001750�0001750�00000071461�12227000321�020342� 0����������������������������������������������������������������������������������������������������ustar�00smcv����������������������������smcv����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * conn-avatars.c - Gabble connection avatar interface * Copyright (C) 2005-2010 Collabora Ltd. * Copyright (C) 2005-2010 Nokia Corporation * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 "config.h" #include "conn-avatars.h" #include <string.h> #include <telepathy-glib/telepathy-glib.h> #include <telepathy-glib/telepathy-glib-dbus.h> #include "presence.h" #include "presence-cache.h" #include "conn-presence.h" #include "namespaces.h" #include "vcard-manager.h" #include "util.h" #define DEBUG_FLAG GABBLE_DEBUG_CONNECTION #include "debug.h" /* If the SHA1 has changed, this function will copy it to self_presence, * emit a signal and push it to the server. */ static gboolean update_own_avatar_sha1 (GabbleConnection *conn, const gchar *sha1, GError **out_error) { TpBaseConnection *base = (TpBaseConnection *) conn; GError *error = NULL; /* sha1 can be "" if we know there is no avatar, but must not be NULL here */ g_assert (sha1 != NULL); if (!tp_strdiff (sha1, conn->self_presence->avatar_sha1)) return TRUE; tp_svc_connection_interface_avatars_emit_avatar_updated (conn, tp_base_connection_get_self_handle (base), sha1); g_free (conn->self_presence->avatar_sha1); conn->self_presence->avatar_sha1 = g_strdup (sha1); if (!conn_presence_signal_own_presence (conn, NULL, &error)) { DEBUG ("failed to signal changed avatar sha1 to the server: %s", error->message); g_propagate_error (out_error, error); return FALSE; } return TRUE; } static void connection_avatar_update_cb (GabblePresenceCache *cache, TpHandle handle, const gchar *sha1, gpointer user_data) { GabbleConnection *conn = GABBLE_CONNECTION (user_data); TpBaseConnection *base = (TpBaseConnection *) conn; /* sha1 can be "" if we know there is no avatar, but must not be NULL here */ g_assert (sha1 != NULL); if (handle == tp_base_connection_get_self_handle (base)) update_own_avatar_sha1 (conn, sha1, NULL); else tp_svc_connection_interface_avatars_emit_avatar_updated (conn, handle, sha1); } /* Called when our vCard is first fetched, so we can start putting the * SHA-1 of an existing avatar in our presence. */ static void connection_got_self_initial_avatar_cb (GObject *obj, gchar *sha1, gpointer user_data) { GabbleConnection *conn = GABBLE_CONNECTION (user_data); /* sha1 can be "" if we know there is no avatar, but must not be NULL here */ g_assert (sha1 != NULL); update_own_avatar_sha1 (conn, sha1, NULL); } /* Jabber prescribes no MIME type for avatars, but XEP-0153 says support * for image/png is REQUIRED, with image/jpeg and image/gif RECOMMENDED */ static const char *mimetypes[] = { "image/png", "image/jpeg", "image/gif", NULL }; /* Jabber has no min/max width/height or max size, but XEP-0153 says * you SHOULD use 32-96px either way, and no more than 8K of data */ #define AVATAR_MIN_PX 32 #define AVATAR_REC_PX 64 #define AVATAR_MAX_PX 96 #define AVATAR_MAX_BYTES 8192 /** * gabble_connection_get_avatar_requirements * * Implements D-Bus method GetAvatarRequirements * on interface org.freedesktop.Telepathy.Connection.Interface.Avatars * * @error: Used to return a pointer to a GError detailing any error * that occurred, D-Bus will throw the error only if this * function returns FALSE. * * Returns: TRUE if successful, FALSE if an error was thrown. */ static void gabble_connection_get_avatar_requirements (TpSvcConnectionInterfaceAvatars *iface, DBusGMethodInvocation *context) { TP_BASE_CONNECTION_ERROR_IF_NOT_CONNECTED (TP_BASE_CONNECTION (iface), context); tp_svc_connection_interface_avatars_return_from_get_avatar_requirements ( context, mimetypes, AVATAR_MIN_PX, AVATAR_MIN_PX, AVATAR_MAX_PX, AVATAR_MAX_PX, AVATAR_MAX_BYTES); } /* begin deprecated code */ typedef struct { DBusGMethodInvocation *invocation; gchar **ret; guint my_index; gulong signal_conn; } GetAvatarTokensContext; static void _got_self_avatar_for_get_avatar_tokens (GObject *obj, gchar *sha1, gpointer user_data) { GetAvatarTokensContext *context = (GetAvatarTokensContext *) user_data; g_signal_handler_disconnect (obj, context->signal_conn); g_free (context->ret[context->my_index]); context->ret[context->my_index] = g_strdup (sha1); /* Cast to (const gchar **) necessary because no-one understands 'const' * in C. */ tp_svc_connection_interface_avatars_return_from_get_avatar_tokens ( context->invocation, (const gchar **)context->ret); g_strfreev (context->ret); g_slice_free (GetAvatarTokensContext, context); } /** * gabble_connection_get_avatar_tokens * * Implements D-Bus method GetAvatarTokens * on interface org.freedesktop.Telepathy.Connection.Interface.Avatars * * @context: The D-Bus invocation context to use to return values * or throw an error. */ static void gabble_connection_get_avatar_tokens (TpSvcConnectionInterfaceAvatars *iface, const GArray *contacts, DBusGMethodInvocation *invocation) { GabbleConnection *self = GABBLE_CONNECTION (iface); TpBaseConnection *base = (TpBaseConnection *) self; gboolean wait_for_self_avatar = FALSE; gboolean have_self_avatar; guint i, my_index = 0; gchar **ret; GError *err = NULL; TpHandleRepoIface *contact_handles = tp_base_connection_get_handles (base, TP_HANDLE_TYPE_CONTACT); TP_BASE_CONNECTION_ERROR_IF_NOT_CONNECTED (base, invocation); if (!tp_handles_are_valid (contact_handles, contacts, FALSE, &err)) { dbus_g_method_return_error (invocation, err); g_error_free (err); return; } g_object_get (self->vcard_manager, "have-self-avatar", &have_self_avatar, NULL); ret = g_new0 (gchar *, contacts->len + 1); /* TODO: always call the callback so we can defer presence lookups until * we return the method, then we don't need to strdup the strings we're * returning. */ for (i = 0; i < contacts->len; i++) { TpHandle handle; GabblePresence *presence = NULL; handle = g_array_index (contacts, TpHandle, i); if (tp_base_connection_get_self_handle (base) == handle) { if (have_self_avatar) { presence = self->self_presence; } else { wait_for_self_avatar = TRUE; my_index = i; } } else { presence = gabble_presence_cache_get (self->presence_cache, handle); } if (NULL != presence && NULL != presence->avatar_sha1) ret[i] = g_strdup (presence->avatar_sha1); else ret[i] = g_strdup (""); } if (wait_for_self_avatar) { GetAvatarTokensContext *context = g_slice_new (GetAvatarTokensContext); context->invocation = invocation; context->my_index = my_index; context->ret = ret; context->signal_conn = g_signal_connect (self->vcard_manager, "got-self-initial-avatar", G_CALLBACK (_got_self_avatar_for_get_avatar_tokens), context); return; } /* Cast to (const gchar **) necessary because no-one understands 'const' * in C. */ tp_svc_connection_interface_avatars_return_from_get_avatar_tokens ( invocation, (const gchar **)ret); g_strfreev (ret); } /* end deprecated code */ typedef struct { GabbleConnection *conn; DBusGMethodInvocation *invocation; GHashTable *ret; gulong signal_conn; } GetKnownAvatarTokensContext; static void _got_self_avatar_for_get_known_avatar_tokens (GObject *obj, gchar *sha1, gpointer user_data) { GetKnownAvatarTokensContext *context = (GetKnownAvatarTokensContext *) user_data; TpBaseConnection *base = (TpBaseConnection *) context->conn; g_signal_handler_disconnect (obj, context->signal_conn); g_assert (tp_base_connection_get_self_handle (base) != 0); g_hash_table_insert (context->ret, GUINT_TO_POINTER (tp_base_connection_get_self_handle (base)), g_strdup (sha1)); tp_svc_connection_interface_avatars_return_from_get_known_avatar_tokens ( context->invocation, context->ret); g_hash_table_unref (context->ret); g_slice_free (GetKnownAvatarTokensContext, context); } /** * gabble_connection_get_known_avatar_tokens * * Implements D-Bus method GetKnownAvatarTokens * on interface org.freedesktop.Telepathy.Connection.Interface.Avatars * * @context: The D-Bus invocation context to use to return values * or throw an error. */ static void gabble_connection_get_known_avatar_tokens (TpSvcConnectionInterfaceAvatars *iface, const GArray *contacts, DBusGMethodInvocation *invocation) { GabbleConnection *self = GABBLE_CONNECTION (iface); TpBaseConnection *base = (TpBaseConnection *) self; gboolean wait_for_self_avatar = FALSE; gboolean have_self_avatar; guint i; GHashTable *ret; GError *err = NULL; TpHandleRepoIface *contact_handles = tp_base_connection_get_handles (base, TP_HANDLE_TYPE_CONTACT); TP_BASE_CONNECTION_ERROR_IF_NOT_CONNECTED (base, invocation); if (!tp_handles_are_valid (contact_handles, contacts, FALSE, &err)) { dbus_g_method_return_error (invocation, err); g_error_free (err); return; } g_object_get (self->vcard_manager, "have-self-avatar", &have_self_avatar, NULL); ret = g_hash_table_new_full (NULL, NULL, NULL, g_free); /* TODO: always call the callback so we can defer presence lookups until * we return the method, then we don't need to strdup the strings we're * returning. */ for (i = 0; i < contacts->len; i++) { TpHandle handle; GabblePresence *presence = NULL; handle = g_array_index (contacts, TpHandle, i); if (tp_base_connection_get_self_handle (base) == handle) { if (have_self_avatar) { presence = self->self_presence; } else { wait_for_self_avatar = TRUE; } } else { presence = gabble_presence_cache_get (self->presence_cache, handle); } if (NULL != presence) { if (NULL != presence->avatar_sha1) g_hash_table_insert (ret, GUINT_TO_POINTER (handle), g_strdup (presence->avatar_sha1)); else g_hash_table_insert (ret, GUINT_TO_POINTER (handle), g_strdup ("")); } } if (wait_for_self_avatar) { GetKnownAvatarTokensContext *context = g_slice_new (GetKnownAvatarTokensContext); context->conn = self; context->invocation = invocation; context->ret = ret; context->signal_conn = g_signal_connect (self->vcard_manager, "got-self-initial-avatar", G_CALLBACK (_got_self_avatar_for_get_known_avatar_tokens), context); return; } tp_svc_connection_interface_avatars_return_from_get_known_avatar_tokens ( invocation, ret); g_hash_table_unref (ret); } static gboolean parse_avatar (WockyNode *vcard, const gchar **mime_type, GString **avatar, GError **error) { WockyNode *photo_node; WockyNode *type_node; WockyNode *binval_node; const gchar *binval_value; guchar *st; gsize outlen; photo_node = wocky_node_get_child (vcard, "PHOTO"); if (NULL == photo_node) { g_set_error (error, TP_ERROR, TP_ERROR_NOT_AVAILABLE, "contact vCard has no photo"); return FALSE; } type_node = wocky_node_get_child (photo_node, "TYPE"); if (NULL != type_node) { *mime_type = type_node->content; } else { *mime_type = ""; } binval_node = wocky_node_get_child (photo_node, "BINVAL"); if (NULL == binval_node) { g_set_error (error, TP_ERROR, TP_ERROR_NOT_AVAILABLE, "contact avatar is missing binval node"); return FALSE; } binval_value = binval_node->content; if (NULL == binval_value) { g_set_error (error, TP_ERROR, TP_ERROR_NOT_AVAILABLE, "contact avatar is missing binval content"); return FALSE; } st = g_base64_decode (binval_value, &outlen); *avatar = g_string_new_len ((gchar *) st, outlen); g_free (st); if (NULL == *avatar) { g_set_error (error, TP_ERROR, TP_ERROR_NOT_AVAILABLE, "failed to decode avatar from base64"); return FALSE; } return TRUE; } static void _request_avatar_cb (GabbleVCardManager *self, GabbleVCardManagerRequest *request, TpHandle handle, WockyNode *vcard, GError *vcard_error, gpointer user_data) { DBusGMethodInvocation *context = (DBusGMethodInvocation *) user_data; GabbleConnection *conn; TpBaseConnection *base; const gchar *mime_type = NULL; GArray *arr; GError *error = NULL; GString *avatar = NULL; GabblePresence *presence; g_object_get (self, "connection", &conn, NULL); base = TP_BASE_CONNECTION (conn); if (NULL == vcard) { GError tp_error = { TP_ERROR, TP_ERROR_NOT_AVAILABLE, vcard_error->message }; if (vcard_error->domain == WOCKY_XMPP_ERROR) { switch (vcard_error->code) { case WOCKY_XMPP_ERROR_NOT_AUTHORIZED: case WOCKY_XMPP_ERROR_FORBIDDEN: tp_error.code = TP_ERROR_PERMISSION_DENIED; break; case WOCKY_XMPP_ERROR_ITEM_NOT_FOUND: tp_error.code = TP_ERROR_DOES_NOT_EXIST; break; } /* what other mappings make sense here? */ } dbus_g_method_return_error (context, &tp_error); goto out; } if (!parse_avatar (vcard, &mime_type, &avatar, &error)) { dbus_g_method_return_error (context, error); g_error_free (error); goto out; } if (handle == tp_base_connection_get_self_handle (base)) presence = conn->self_presence; else presence = gabble_presence_cache_get (conn->presence_cache, handle); if (presence != NULL) { gchar *sha1; sha1 = sha1_hex (avatar->str, avatar->len); if (tp_strdiff (presence->avatar_sha1, sha1)) { /* the thinking here is that we have to return an error, because we * can't give the user the vcard they're expecting, which has the * hash from the time that they requested it. */ DEBUG ("treason uncloaked! avatar hash in presence does not match " "avatar in vCard for handle %u", handle); g_set_error (&error, TP_ERROR, TP_ERROR_NOT_AVAILABLE, "avatar hash in presence does not match avatar in vCard"); dbus_g_method_return_error (context, error); g_error_free (error); error = NULL; if (handle == tp_base_connection_get_self_handle (base)) { update_own_avatar_sha1 (conn, sha1, NULL); g_free (sha1); } else { g_free (presence->avatar_sha1); presence->avatar_sha1 = sha1; /* take ownership */ tp_svc_connection_interface_avatars_emit_avatar_updated ( conn, handle, sha1); } goto out; } g_free (sha1); } arr = g_array_new (FALSE, FALSE, sizeof (gchar)); g_array_append_vals (arr, avatar->str, avatar->len); tp_svc_connection_interface_avatars_return_from_request_avatar ( context, arr, mime_type); g_array_unref (arr); out: if (avatar != NULL) g_string_free (avatar, TRUE); g_object_unref (conn); } /** * gabble_connection_request_avatar * * Implements D-Bus method RequestAvatar * on interface org.freedesktop.Telepathy.Connection.Interface.Avatars * * @context: The D-Bus invocation context to use to return values * or throw an error. */ static void gabble_connection_request_avatar (TpSvcConnectionInterfaceAvatars *iface, guint contact, DBusGMethodInvocation *context) { GabbleConnection *self = GABBLE_CONNECTION (iface); TpBaseConnection *base = (TpBaseConnection *) self; TpHandleRepoIface *contact_handles = tp_base_connection_get_handles (base, TP_HANDLE_TYPE_CONTACT); GError *err = NULL; WockyNode *vcard_node; TP_BASE_CONNECTION_ERROR_IF_NOT_CONNECTED (base, context); if (!tp_handle_is_valid (contact_handles, contact, &err)) { dbus_g_method_return_error (context, err); g_error_free (err); return; } if (gabble_vcard_manager_get_cached (self->vcard_manager, contact, &vcard_node)) { _request_avatar_cb (self->vcard_manager, NULL, contact, vcard_node, NULL, context); } else { gabble_vcard_manager_request (self->vcard_manager, contact, 0, _request_avatar_cb, context, NULL); } } static void emit_avatar_retrieved (TpSvcConnectionInterfaceAvatars *iface, TpHandle contact, WockyNode *vcard_node) { const gchar *mime_type; GString *avatar_str; gchar *sha1; GArray *arr; if (!parse_avatar (vcard_node, &mime_type, &avatar_str, NULL)) return; sha1 = sha1_hex (avatar_str->str, avatar_str->len); arr = g_array_new (FALSE, FALSE, sizeof (gchar)); g_array_append_vals (arr, avatar_str->str, avatar_str->len); tp_svc_connection_interface_avatars_emit_avatar_retrieved (iface, contact, sha1, arr, mime_type); g_array_unref (arr); g_free (sha1); g_string_free (avatar_str, TRUE); } /* All references are borrowed */ typedef struct { TpHandle handle; GabbleConnection *conn; TpSvcConnectionInterfaceAvatars *iface; } RequestAvatarsContext; static void request_avatars_cb (GabbleVCardManager *manager, GabbleVCardManagerRequest *request, TpHandle handle, WockyNode *vcard, GError *vcard_error, gpointer user_data) { RequestAvatarsContext *ctx = user_data; g_assert (g_hash_table_lookup (ctx->conn->avatar_requests, GUINT_TO_POINTER (ctx->handle))); g_hash_table_remove (ctx->conn->avatar_requests, GUINT_TO_POINTER (ctx->handle)); if (vcard_error == NULL) emit_avatar_retrieved (ctx->iface, handle, vcard); g_slice_free (RequestAvatarsContext, ctx); } static void gabble_connection_request_avatars (TpSvcConnectionInterfaceAvatars *iface, const GArray *contacts, DBusGMethodInvocation *context) { GabbleConnection *self = GABBLE_CONNECTION (iface); TpBaseConnection *base = (TpBaseConnection *) self; TpHandleRepoIface *contacts_repo = tp_base_connection_get_handles (base, TP_HANDLE_TYPE_CONTACT); GError *error = NULL; guint i; TP_BASE_CONNECTION_ERROR_IF_NOT_CONNECTED (base, context); if (!tp_handles_are_valid (contacts_repo, contacts, FALSE, &error)) { dbus_g_method_return_error (context, error); g_error_free (error); return; } for (i = 0; i < contacts->len; i++) { WockyNode *vcard_node; TpHandle contact = g_array_index (contacts, TpHandle, i); if (gabble_vcard_manager_get_cached (self->vcard_manager, contact, &vcard_node)) { emit_avatar_retrieved (iface, contact, vcard_node); } else { if (NULL == g_hash_table_lookup (self->avatar_requests, GUINT_TO_POINTER (contact))) { RequestAvatarsContext *ctx = g_slice_new (RequestAvatarsContext); ctx->conn = self; ctx->iface = iface; ctx->handle = contact; g_hash_table_insert (self->avatar_requests, GUINT_TO_POINTER (contact), ctx); gabble_vcard_manager_request (self->vcard_manager, contact, 0, request_avatars_cb, ctx, NULL); } } } tp_svc_connection_interface_avatars_return_from_request_avatars (context); } struct _set_avatar_ctx { GabbleConnection *conn; DBusGMethodInvocation *invocation; GString *avatar; }; static void _set_avatar_ctx_free (struct _set_avatar_ctx *ctx) { if (ctx->avatar) g_string_free (ctx->avatar, TRUE); g_free (ctx); } static void _set_avatar_cb2 (GabbleVCardManager *manager, GabbleVCardManagerEditRequest *request, WockyNode *vcard, GError *vcard_error, gpointer user_data) { struct _set_avatar_ctx *ctx = (struct _set_avatar_ctx *) user_data; TpBaseConnection *base = (TpBaseConnection *) ctx->conn; if (NULL == vcard) { GError tp_error = { TP_ERROR, TP_ERROR_NOT_AVAILABLE, vcard_error->message }; /* Google Talk has been observed to return bad-request when the avatar is * too big. It's not clear what other XMPP errors make sense here, or how * to map them. */ if (vcard_error->domain == WOCKY_XMPP_ERROR) if (vcard_error->code == WOCKY_XMPP_ERROR_BAD_REQUEST || vcard_error->code == WOCKY_XMPP_ERROR_NOT_ACCEPTABLE) tp_error.code = TP_ERROR_INVALID_ARGUMENT; dbus_g_method_return_error (ctx->invocation, &tp_error); } else { GabblePresence *presence = ctx->conn->self_presence; GError *error = NULL; g_free (presence->avatar_sha1); if (ctx->avatar) { presence->avatar_sha1 = sha1_hex (ctx->avatar->str, ctx->avatar->len); } else { presence->avatar_sha1 = NULL; } if (conn_presence_signal_own_presence (ctx->conn, NULL, &error)) { tp_svc_connection_interface_avatars_return_from_set_avatar ( ctx->invocation, presence->avatar_sha1); tp_svc_connection_interface_avatars_emit_avatar_updated ( ctx->conn, tp_base_connection_get_self_handle (base), presence->avatar_sha1); } else { dbus_g_method_return_error (ctx->invocation, error); g_error_free (error); } } _set_avatar_ctx_free (ctx); } /** * gabble_connection_set_avatar * * Implements D-Bus method SetAvatar * on interface org.freedesktop.Telepathy.Connection.Interface.Avatars * * @context: The D-Bus invocation context to use to return values * or throw an error. */ static void gabble_connection_set_avatar (TpSvcConnectionInterfaceAvatars *iface, const GArray *avatar, const gchar *mime_type, DBusGMethodInvocation *context) { GabbleConnection *self = GABBLE_CONNECTION (iface); TpBaseConnection *base = (TpBaseConnection *) self; GabbleVCardManagerEditInfo *edit_info; GList *edits = NULL; struct _set_avatar_ctx *ctx; gchar *base64; TP_BASE_CONNECTION_ERROR_IF_NOT_CONNECTED (base, context); ctx = g_new0 (struct _set_avatar_ctx, 1); ctx->conn = self; ctx->invocation = context; if (avatar != NULL && avatar->len > 0) { gint state = 0, save = 0, outlen; /* See the documentation for g_base64_encode_step(). */ guint base64_data_size = (avatar->len / 3 + 1) * 4 + 4; guint base64_line_wrapped_data_size = base64_data_size + (base64_data_size / 72) + 1; ctx->avatar = g_string_new_len (avatar->data, avatar->len); base64 = g_malloc (base64_line_wrapped_data_size); outlen = g_base64_encode_step ((const guchar *) avatar->data, avatar->len, TRUE, base64, &state, &save); outlen += g_base64_encode_close (TRUE, base64 + outlen, &state, &save); base64[outlen] = '\0'; DEBUG ("Replacing avatar"); edit_info = gabble_vcard_manager_edit_info_new ("PHOTO", NULL, GABBLE_VCARD_EDIT_REPLACE, '(', "TYPE", '$', mime_type, ')', '(', "BINVAL", '$', base64, ')', NULL); g_free (base64); } else { DEBUG ("Removing avatar"); edit_info = gabble_vcard_manager_edit_info_new ("PHOTO", NULL, GABBLE_VCARD_EDIT_DELETE, NULL); } edits = g_list_append (edits, edit_info); gabble_vcard_manager_edit (self->vcard_manager, 0, _set_avatar_cb2, ctx, (GObject *) self, edits); } /** * gabble_connection_clear_avatar * * Implements D-Bus method ClearAvatar * on interface org.freedesktop.Telepathy.Connection.Interface.Avatars * * @context: The D-Bus invocation context to use to return values * or throw an error. */ static void gabble_connection_clear_avatar (TpSvcConnectionInterfaceAvatars *iface, DBusGMethodInvocation *context) { gabble_connection_set_avatar (iface, NULL, NULL, context); } static void conn_avatars_fill_contact_attributes (GObject *obj, const GArray *contacts, GHashTable *attributes_hash) { guint i; GabbleConnection *self = GABBLE_CONNECTION(obj); TpBaseConnection *base = (TpBaseConnection *) self; for (i = 0; i < contacts->len; i++) { TpHandle handle = g_array_index (contacts, guint, i); GabblePresence *presence = NULL; if (tp_base_connection_get_self_handle (base) == handle) presence = self->self_presence; else presence = gabble_presence_cache_get (self->presence_cache, handle); if (NULL != presence) { GValue *val = tp_g_value_slice_new (G_TYPE_STRING); if (NULL != presence->avatar_sha1) g_value_set_string (val, presence->avatar_sha1); else g_value_set_string (val, ""); tp_contacts_mixin_set_contact_attribute (attributes_hash, handle, TP_IFACE_CONNECTION_INTERFACE_AVATARS"/token", val); } } } void conn_avatars_init (GabbleConnection *conn) { g_assert (conn->vcard_manager != NULL); g_signal_connect (conn->vcard_manager, "got-self-initial-avatar", G_CALLBACK (connection_got_self_initial_avatar_cb), conn); g_signal_connect (conn->presence_cache, "avatar-update", G_CALLBACK (connection_avatar_update_cb), conn); tp_contacts_mixin_add_contact_attributes_iface (G_OBJECT (conn), TP_IFACE_CONNECTION_INTERFACE_AVATARS, conn_avatars_fill_contact_attributes); } void conn_avatars_iface_init (gpointer g_iface, gpointer iface_data) { TpSvcConnectionInterfaceAvatarsClass *klass = g_iface; #define IMPLEMENT(x) tp_svc_connection_interface_avatars_implement_##x (\ klass, gabble_connection_##x) IMPLEMENT(get_avatar_requirements); IMPLEMENT(get_avatar_tokens); IMPLEMENT(get_known_avatar_tokens); IMPLEMENT(request_avatar); IMPLEMENT(request_avatars); IMPLEMENT(set_avatar); IMPLEMENT(clear_avatar); #undef IMPLEMENT } static TpDBusPropertiesMixinPropImpl props[] = { { "MinimumAvatarWidth", GUINT_TO_POINTER (AVATAR_MIN_PX), NULL }, { "RecommendedAvatarWidth", GUINT_TO_POINTER (AVATAR_REC_PX), NULL }, { "MaximumAvatarWidth", GUINT_TO_POINTER (AVATAR_MAX_PX), NULL }, { "MinimumAvatarHeight", GUINT_TO_POINTER (AVATAR_MIN_PX), NULL }, { "RecommendedAvatarHeight", GUINT_TO_POINTER (AVATAR_REC_PX), NULL }, { "MaximumAvatarHeight", GUINT_TO_POINTER (AVATAR_MAX_PX), NULL }, { "MaximumAvatarBytes", GUINT_TO_POINTER (AVATAR_MAX_BYTES), NULL }, /* special-cased - it's the only one with a non-guint value */ { "SupportedAvatarMIMETypes", NULL, NULL }, { NULL } }; TpDBusPropertiesMixinPropImpl *conn_avatars_properties = props; void conn_avatars_properties_getter (GObject *object, GQuark interface, GQuark name, GValue *value, gpointer getter_data) { GQuark q_mime_types = g_quark_from_static_string ( "SupportedAvatarMIMETypes"); if (name == q_mime_types) { g_value_set_static_boxed (value, mimetypes); } else { g_value_set_uint (value, GPOINTER_TO_UINT (getter_data)); } } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������telepathy-gabble-0.18.2/src/conn-avatars.h����������������������������������������������������������0000644�0001750�0001750�00000002511�12227000321�020335� 0����������������������������������������������������������������������������������������������������ustar�00smcv����������������������������smcv����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * conn-avatars.h - Header for Gabble connection avatar interface * Copyright (C) 2005-2007 Collabora Ltd. * Copyright (C) 2005-2007 Nokia Corporation * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 __CONN_AVATARS_H__ #define __CONN_AVATARS_H__ #include "connection.h" G_BEGIN_DECLS void conn_avatars_init (GabbleConnection *conn); void conn_avatars_iface_init (gpointer g_iface, gpointer iface_data); extern TpDBusPropertiesMixinPropImpl *conn_avatars_properties; void conn_avatars_properties_getter (GObject *object, GQuark interface, GQuark name, GValue *value, gpointer getter_data); G_END_DECLS #endif /* __CONN_AVATARS_H__ */ ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������telepathy-gabble-0.18.2/src/conn-aliasing.c���������������������������������������������������������0000644�0001750�0001750�00000107154�12227000321�020467� 0����������������������������������������������������������������������������������������������������ustar�00smcv����������������������������smcv����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * conn-aliasing.c - Gabble connection aliasing interface * Copyright (C) 2005-2010 Collabora Ltd. * Copyright (C) 2005-2010 Nokia Corporation * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 "config.h" #include "conn-aliasing.h" #include <wocky/wocky.h> #include <telepathy-glib/telepathy-glib.h> #include <telepathy-glib/telepathy-glib-dbus.h> #define DEBUG_FLAG GABBLE_DEBUG_CONNECTION #include "connection.h" #include "debug.h" #include "namespaces.h" #include "presence-cache.h" #include "request-pipeline.h" #include "roster.h" #include "util.h" #include "vcard-manager.h" static void gabble_conn_aliasing_pep_nick_reply_handler ( GabbleConnection *conn, WockyStanza *msg, TpHandle handle); static GQuark gabble_conn_aliasing_pep_alias_quark (void); static GabbleConnectionAliasSource _gabble_connection_get_cached_remote_alias ( GabbleConnection *, TpHandle, gchar **); static void maybe_request_vcard (GabbleConnection *self, TpHandle handle, GabbleConnectionAliasSource source); /* distinct from any strdup()d pointer - used for negative caching */ static const gchar *NO_ALIAS = ""; /** * gabble_connection_get_alias_flags * * Implements D-Bus method GetAliasFlags * on interface org.freedesktop.Telepathy.Connection.Interface.Aliasing * * @error: Used to return a pointer to a GError detailing any error * that occurred, D-Bus will throw the error only if this * function returns FALSE. * * Returns: TRUE if successful, FALSE if an error was thrown. */ static void gabble_connection_get_alias_flags (TpSvcConnectionInterfaceAliasing *iface, DBusGMethodInvocation *context) { TpBaseConnection *base = TP_BASE_CONNECTION (iface); g_assert (GABBLE_IS_CONNECTION (base)); TP_BASE_CONNECTION_ERROR_IF_NOT_CONNECTED (base, context); tp_svc_connection_interface_aliasing_return_from_get_alias_flags ( context, TP_CONNECTION_ALIAS_FLAG_USER_SET); } typedef struct _AliasesRequest AliasesRequest; struct _AliasesRequest { GabbleConnection *conn; DBusGMethodInvocation *request_call; guint pending_vcard_requests; guint pending_pep_requests; GArray *contacts; GabbleVCardManagerRequest **vcard_requests; GabbleRequestPipelineItem **pep_requests; gchar **aliases; }; typedef struct { AliasesRequest *aliases_request; guint index; } AliasRequest; static AliasesRequest * aliases_request_new (GabbleConnection *conn, DBusGMethodInvocation *request_call, const GArray *contacts) { AliasesRequest *request; request = g_slice_new0 (AliasesRequest); request->conn = conn; request->request_call = request_call; request->contacts = g_array_new (FALSE, FALSE, sizeof (TpHandle)); g_array_insert_vals (request->contacts, 0, contacts->data, contacts->len); request->vcard_requests = g_new0 (GabbleVCardManagerRequest *, contacts->len); request->pep_requests = g_new0 (GabbleRequestPipelineItem *, contacts->len); request->aliases = g_new0 (gchar *, contacts->len + 1); return request; } static void aliases_request_free (AliasesRequest *request) { guint i; for (i = 0; i < request->contacts->len; i++) { /* FIXME: what if vcard_manager is NULL? */ if (request->vcard_requests[i] != NULL) gabble_vcard_manager_cancel_request (request->conn->vcard_manager, request->vcard_requests[i]); } g_array_unref (request->contacts); g_free (request->vcard_requests); g_free (request->pep_requests); g_strfreev (request->aliases); g_slice_free (AliasesRequest, request); } static gboolean aliases_request_try_return (AliasesRequest *request) { if (request->pending_vcard_requests == 0 && request->pending_pep_requests == 0) { /* Cast to (const gchar **) necessary because no-one understands 'const' * in C. */ tp_svc_connection_interface_aliasing_return_from_request_aliases ( request->request_call, (const gchar **)request->aliases); return TRUE; } return FALSE; } static void aliases_request_vcard_cb (GabbleVCardManager *manager, GabbleVCardManagerRequest *request, TpHandle handle, WockyNode *vcard, GError *error, gpointer user_data) { AliasesRequest *aliases_request = (AliasesRequest *) user_data; GabbleConnectionAliasSource source; guint i; gboolean found = FALSE; gchar *alias = NULL; g_assert (aliases_request->pending_vcard_requests > 0); /* The index of the vCard request in the vCard request array is the * index of the contact/alias in their respective arrays. */ for (i = 0; i < aliases_request->contacts->len; i++) if (aliases_request->vcard_requests[i] == request) { found = TRUE; break; } g_assert (found); source = _gabble_connection_get_cached_alias (aliases_request->conn, g_array_index (aliases_request->contacts, TpHandle, i), &alias); g_assert (source != GABBLE_CONNECTION_ALIAS_NONE); g_assert (NULL != alias); aliases_request->pending_vcard_requests--; aliases_request->vcard_requests[i] = NULL; aliases_request->aliases[i] = alias; if (aliases_request_try_return (aliases_request)) aliases_request_free (aliases_request); } static void _cache_negatively (GabbleConnection *self, TpHandle handle) { TpBaseConnection *base = (TpBaseConnection *) self; TpHandleRepoIface *contact_handles = tp_base_connection_get_handles (base, TP_HANDLE_TYPE_CONTACT); tp_handle_set_qdata (contact_handles, handle, gabble_conn_aliasing_pep_alias_quark (), (gchar *) NO_ALIAS, NULL); } /* Cache pep if successful */ static void aliases_request_cache_pep (GabbleConnection *self, WockyStanza *msg, TpHandle handle, GError *error) { if (error != NULL) { DEBUG ("Error getting alias from PEP: %s", error->message); _cache_negatively (self, handle); return; } else if (wocky_stanza_extract_errors (msg, NULL, NULL, NULL, NULL)) { STANZA_DEBUG (msg, "Error getting alias from PEP"); _cache_negatively (self, handle); } else { /* Try to extract an alias, caching it if necessary. */ gabble_conn_aliasing_pep_nick_reply_handler (self, msg, handle); } } static void aliases_request_basic_pep_cb (GabbleConnection *self, WockyStanza *msg, gpointer user_data, GError *error) { TpBaseConnection *base = (TpBaseConnection *) self; GabbleConnectionAliasSource source = GABBLE_CONNECTION_ALIAS_NONE; TpHandle handle = GPOINTER_TO_UINT (user_data); aliases_request_cache_pep (self, msg, handle, error); source = _gabble_connection_get_cached_alias (self, handle, NULL); if (source < GABBLE_CONNECTION_ALIAS_FROM_VCARD && tp_base_connection_get_status (base) == TP_CONNECTION_STATUS_CONNECTED && !gabble_vcard_manager_has_cached_alias (self->vcard_manager, handle)) { /* no alias in PEP, get the vcard */ gabble_vcard_manager_request (self->vcard_manager, handle, 0, NULL, NULL, G_OBJECT (self)); } } static void aliases_request_pep_cb (GabbleConnection *self, WockyStanza *msg, gpointer user_data, GError *error) { TpBaseConnection *base = (TpBaseConnection *) self; AliasRequest *alias_request = (AliasRequest *) user_data; AliasesRequest *aliases_request = alias_request->aliases_request; guint index = alias_request->index; TpHandle handle = g_array_index (aliases_request->contacts, TpHandle, index); GabbleConnectionAliasSource source = GABBLE_CONNECTION_ALIAS_NONE; gchar *alias = NULL; aliases_request->pending_pep_requests--; aliases_request->pep_requests[index] = NULL; g_slice_free (AliasRequest, alias_request); aliases_request_cache_pep (self, msg, handle, error); source = _gabble_connection_get_cached_alias (aliases_request->conn, handle, &alias); g_assert (source != GABBLE_CONNECTION_ALIAS_NONE); g_assert (NULL != alias); DEBUG ("Got cached alias %s with priority %u", alias, source); if (source >= GABBLE_CONNECTION_ALIAS_FROM_VCARD || (self->vcard_manager != NULL && gabble_vcard_manager_has_cached_alias (self->vcard_manager, handle))) { aliases_request->aliases[index] = alias; } else if (tp_base_connection_get_status (base) != TP_CONNECTION_STATUS_CONNECTED) { DEBUG ("no longer connected, not chaining up to vCard"); g_free (alias); } else { /* not in PEP and we have no vCard - chain to looking up their vCard */ GabbleVCardManagerRequest *vcard_request = gabble_vcard_manager_request (self->vcard_manager, handle, 0, aliases_request_vcard_cb, aliases_request, G_OBJECT (self)); g_free (alias); aliases_request->vcard_requests[index] = vcard_request; aliases_request->pending_vcard_requests++; } if (aliases_request_try_return (aliases_request)) aliases_request_free (aliases_request); } typedef struct { GabbleRequestPipelineCb callback; gpointer user_data; TpHandleRepoIface *contact_handles; TpHandle handle; } pep_request_ctx; static void pep_request_cb ( GabbleConnection *conn, WockyStanza *msg, gpointer user_data, GError *error) { pep_request_ctx *ctx = user_data; ctx->callback (conn, msg, ctx->user_data, error); g_slice_free (pep_request_ctx, ctx); } /** * @self must have %TP_CONNECTION_STATUS_CONNECTED. */ static GabbleRequestPipelineItem * gabble_do_pep_request (GabbleConnection *self, TpHandle handle, TpHandleRepoIface *contact_handles, GabbleRequestPipelineCb callback, gpointer user_data) { TpBaseConnection *base = (TpBaseConnection *) self; const gchar *to; WockyStanza *msg; GabbleRequestPipelineItem *pep_request; pep_request_ctx *ctx; /* callers must check this... */ g_assert (tp_base_connection_get_status (base) == TP_CONNECTION_STATUS_CONNECTED); /* ... which implies this */ g_assert (self->req_pipeline != NULL); ctx = g_slice_new0 (pep_request_ctx); ctx->callback = callback; ctx->user_data = user_data; ctx->contact_handles = contact_handles; ctx->handle = handle; to = tp_handle_inspect (contact_handles, handle); msg = wocky_stanza_build (WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_GET, NULL, to, '(', "pubsub", ':', NS_PUBSUB, '(', "items", '@', "node", NS_NICK, ')', ')', NULL); pep_request = gabble_request_pipeline_enqueue (self->req_pipeline, msg, 0, pep_request_cb, ctx); g_object_unref (msg); return pep_request; } /** * gabble_connection_request_aliases * * Implements D-Bus method RequestAliases * on interface org.freedesktop.Telepathy.Connection.Interface.Aliasing * * @context: The D-Bus invocation context to use to return values * or throw an error. */ static void gabble_connection_request_aliases (TpSvcConnectionInterfaceAliasing *iface, const GArray *contacts, DBusGMethodInvocation *context) { GabbleConnection *self = GABBLE_CONNECTION (iface); TpBaseConnection *base = (TpBaseConnection *) self; TpHandleRepoIface *contact_handles = tp_base_connection_get_handles (base, TP_HANDLE_TYPE_CONTACT); guint i; AliasesRequest *request; GError *error = NULL; g_assert (GABBLE_IS_CONNECTION (self)); TP_BASE_CONNECTION_ERROR_IF_NOT_CONNECTED (base, context); if (!tp_handles_are_valid (contact_handles, contacts, FALSE, &error)) { dbus_g_method_return_error (context, error); g_error_free (error); return; } request = aliases_request_new (self, context, contacts); for (i = 0; i < contacts->len; i++) { TpHandle handle = g_array_index (contacts, TpHandle, i); GabbleConnectionAliasSource source; GabbleVCardManagerRequest *vcard_request; gchar *alias; source = _gabble_connection_get_cached_alias (self, handle, &alias); g_assert (source != GABBLE_CONNECTION_ALIAS_NONE); g_assert (NULL != alias); if (source >= GABBLE_CONNECTION_ALIAS_FROM_VCARD || gabble_vcard_manager_has_cached_alias (self->vcard_manager, handle)) { /* Either the alias we got was from a vCard or better, or we already * tried getting an alias from a vcard and failed, so there's no * point trying again. */ request->aliases[i] = alias; } else if (self->features & GABBLE_CONNECTION_FEATURES_PEP) { /* FIXME: we shouldn't have to do this, since we should get PEP * events when someone first sends us presence. However, the * current ejabberd PEP implementation doesn't seem to give * us notifications of the initial state. */ AliasRequest *data = g_slice_new (AliasRequest); g_free (alias); data->aliases_request = request; data->index = i; request->pending_pep_requests++; request->pep_requests[i] = gabble_do_pep_request (self, handle, contact_handles, aliases_request_pep_cb, data); } else { DEBUG ("requesting vCard for alias of contact %s", tp_handle_inspect (contact_handles, handle)); g_free (alias); vcard_request = gabble_vcard_manager_request (self->vcard_manager, handle, 0, aliases_request_vcard_cb, request, G_OBJECT (self)); request->vcard_requests[i] = vcard_request; request->pending_vcard_requests++; } } if (aliases_request_try_return (request)) aliases_request_free (request); } static void nick_publish_msg_reply_cb (GabbleConnection *conn, WockyStanza *sent_msg, WockyStanza *reply_msg, GObject *object, gpointer user_data) { #ifdef ENABLE_DEBUG GError *error = NULL; if (wocky_stanza_extract_errors (reply_msg, NULL, &error, NULL, NULL)) { DEBUG ("can't publish nick using PEP: %s: %s", wocky_xmpp_stanza_error_to_string (error), error->message); g_clear_error (&error); } #endif } static gboolean set_one_alias ( GabbleConnection *conn, TpHandle handle, gchar *alias, GError **error) { TpBaseConnection *base = (TpBaseConnection *) conn; TpHandleRepoIface *contact_handles = tp_base_connection_get_handles (base, TP_HANDLE_TYPE_CONTACT); gboolean ret = TRUE; g_assert (tp_base_connection_get_status (base) == TP_CONNECTION_STATUS_CONNECTED); if (tp_str_empty (alias)) alias = NULL; if (!tp_handle_is_valid (contact_handles, handle, error)) { ret = FALSE; } else if (tp_base_connection_get_self_handle (base) == handle) { /* only alter the roster if we're already there, e.g. because someone * added us with another client */ if (gabble_roster_handle_has_entry (conn->roster, handle) && !gabble_roster_handle_set_name (conn->roster, handle, alias, error)) { ret = FALSE; } } else { gchar *remote_alias = NULL; GabbleConnectionAliasSource source = GABBLE_CONNECTION_ALIAS_FROM_ROSTER; if (alias == NULL) { source = _gabble_connection_get_cached_remote_alias (conn, handle, &remote_alias); alias = remote_alias; } ret = gabble_roster_handle_set_name (conn->roster, handle, alias, error); g_free (remote_alias); /* If we don't have a cached remote alias for this contact, try to ask * for one. (Maybe we haven't seen a PEP update or fetched their vCard in * this session?) */ maybe_request_vcard (conn, handle, source); } if (tp_base_connection_get_self_handle (base) == handle) { GabbleVCardManagerEditInfo *edit; GQueue edits = G_QUEUE_INIT; /* User has called SetAliases on themselves - patch their vCard. * FIXME: because SetAliases is currently synchronous, we ignore errors * here, and just let the request happen in the background. */ if (conn->features & GABBLE_CONNECTION_FEATURES_PEP) { /* Publish nick using PEP */ WockyStanza *msg; WockyNode *item; msg = wocky_pep_service_make_publish_stanza (conn->pep_nick, &item); /* Does the right thing if alias == NULL. */ wocky_node_add_child_with_content_ns (item, "nick", alias, NS_NICK); _gabble_connection_send_with_reply (conn, msg, nick_publish_msg_reply_cb, NULL, NULL, NULL); g_object_unref (msg); } if (alias == NULL) /* Deliberately not doing the fall-back-to-FN-on-GTalk dance because * clearing your FN is more serious. */ edit = gabble_vcard_manager_edit_info_new ("NICKNAME", NULL, GABBLE_VCARD_EDIT_DELETE, NULL); else edit = gabble_vcard_manager_edit_info_new (NULL, alias, GABBLE_VCARD_EDIT_SET_ALIAS, NULL); g_queue_push_head (&edits, edit); /* Yes, gabble_vcard_manager_edit steals the list you pass it. */ gabble_vcard_manager_edit (conn->vcard_manager, 0, NULL, NULL, G_OBJECT (conn), edits.head); } return ret; } /** * gabble_connection_set_aliases * * Implements D-Bus method SetAliases * on interface org.freedesktop.Telepathy.Connection.Interface.Aliasing */ static void gabble_connection_set_aliases (TpSvcConnectionInterfaceAliasing *iface, GHashTable *aliases, DBusGMethodInvocation *context) { GabbleConnection *self = GABBLE_CONNECTION (iface); TpBaseConnection *base = (TpBaseConnection *) self; GHashTableIter iter; gpointer key, value; gboolean retval = TRUE; GError *first_error = NULL; g_assert (GABBLE_IS_CONNECTION (self)); TP_BASE_CONNECTION_ERROR_IF_NOT_CONNECTED (base, context); g_hash_table_iter_init (&iter, aliases); while (g_hash_table_iter_next (&iter, &key, &value)) { if (!set_one_alias (self, GPOINTER_TO_UINT (key), value, (first_error == NULL ? &first_error : NULL))) retval = FALSE; } if (retval) { tp_svc_connection_interface_aliasing_return_from_set_aliases ( context); } else { dbus_g_method_return_error (context, first_error); g_error_free (first_error); } } GQuark gabble_conn_aliasing_pep_alias_quark (void) { static GQuark quark = 0; if (G_UNLIKELY (quark == 0)) quark = g_quark_from_static_string ("gabble_conn_aliasing_pep_alias_quark"); return quark; } static gboolean _grab_nickname (GabbleConnection *self, TpHandle handle, WockyNode *node) { TpBaseConnection *base = (TpBaseConnection *) self; TpHandleRepoIface *contact_handles = tp_base_connection_get_handles (base, TP_HANDLE_TYPE_CONTACT); GQuark quark = gabble_conn_aliasing_pep_alias_quark (); const gchar *old, *nickname; node = wocky_node_get_child_ns (node, "nick", NS_NICK); if (NULL == node) { DEBUG ("didn't get a nickname for %s", tp_handle_inspect (contact_handles, handle)); _cache_negatively (self, handle); return FALSE; } nickname = node->content; old = tp_handle_get_qdata (contact_handles, handle, quark); if (tp_strdiff (old, nickname)) { if (nickname == NULL) { DEBUG ("got empty <nick/> node, caching as NO_ALIAS"); _cache_negatively (self, handle); } else { tp_handle_set_qdata (contact_handles, handle, quark, g_strdup (nickname), g_free); } gabble_conn_aliasing_nickname_updated ((GObject *) self, handle, self); } return TRUE; } static void pep_nick_node_changed (WockyPepService *pep, WockyBareContact *contact, WockyStanza *stanza, WockyNode *item, GabbleConnection *conn) { TpHandleRepoIface *contact_repo = tp_base_connection_get_handles ( (TpBaseConnection *) conn, TP_HANDLE_TYPE_CONTACT); TpHandle handle; const gchar *jid; jid = wocky_bare_contact_get_jid (contact); handle = tp_handle_ensure (contact_repo, jid, NULL, NULL); if (handle == 0) { DEBUG ("Invalid from: %s", jid); return; } if (NULL == item) { STANZA_DEBUG (stanza, "PEP event without item node, ignoring"); return; } _grab_nickname (conn, handle, item); } static void gabble_conn_aliasing_pep_nick_reply_handler (GabbleConnection *conn, WockyStanza *msg, TpHandle handle) { WockyNode *pubsub_node, *items_node, *item_node; gboolean found = FALSE; WockyNodeIter i; pubsub_node = wocky_node_get_child_ns ( wocky_stanza_get_top_node (msg), "pubsub", NS_PUBSUB); if (pubsub_node == NULL) { pubsub_node = wocky_node_get_child_ns ( wocky_stanza_get_top_node (msg), "pubsub", NS_PUBSUB "#event"); if (pubsub_node == NULL) { STANZA_DEBUG (msg, "PEP reply with no <pubsub>, ignoring"); _cache_negatively (conn, handle); return; } else { STANZA_DEBUG (msg, "PEP reply from buggy server with #event " "on <pubsub> namespace"); } } items_node = wocky_node_get_child (pubsub_node, "items"); if (items_node == NULL) { STANZA_DEBUG (msg, "No items in PEP reply"); _cache_negatively (conn, handle); return; } wocky_node_iter_init (&i, items_node, NULL, NULL); while (wocky_node_iter_next (&i, &item_node)) { if (_grab_nickname (conn, handle, item_node)) { /* FIXME: does this do the right thing on servers which return * multiple items? ejabberd only returns one anyway */ found = TRUE; break; } } if (!found) { _cache_negatively (conn, handle); } } void gabble_conn_aliasing_nickname_updated (GObject *object, TpHandle handle, gpointer user_data) { GArray *handles; handles = g_array_sized_new (FALSE, FALSE, sizeof (TpHandle), 1); g_array_append_val (handles, handle); gabble_conn_aliasing_nicknames_updated (object, handles, user_data); g_array_unref (handles); } void gabble_conn_aliasing_nicknames_updated (GObject *object, GArray *handles, gpointer user_data) { GabbleConnection *conn = GABBLE_CONNECTION (user_data); GabbleConnectionAliasSource signal_source; GPtrArray *aliases; guint i; g_return_if_fail (handles->len > 0); if (object == user_data) { /* actually PEP */ signal_source = GABBLE_CONNECTION_ALIAS_FROM_PRESENCE; } else if (object == G_OBJECT (conn->roster)) { signal_source = GABBLE_CONNECTION_ALIAS_FROM_ROSTER; } else if (object == G_OBJECT (conn->presence_cache)) { signal_source = GABBLE_CONNECTION_ALIAS_FROM_PRESENCE; } else if (object == G_OBJECT (conn->vcard_manager)) { signal_source = GABBLE_CONNECTION_ALIAS_FROM_VCARD; } else { g_assert_not_reached (); return; } aliases = g_ptr_array_sized_new (handles->len); for (i = 0; i < handles->len; i++) { TpHandle handle = g_array_index (handles, TpHandle, i); GabbleConnectionAliasSource current_source; gchar *alias = NULL; GValue entry = { 0, }; current_source = _gabble_connection_get_cached_alias (conn, handle, &alias); g_assert (current_source != GABBLE_CONNECTION_ALIAS_NONE); /* if the active alias for this handle is already known and from * a higher priority, this signal is not interesting so we do * nothing */ if (signal_source < current_source) { DEBUG ("ignoring boring alias change for handle %u, signal from %u " "but source %u has alias \"%s\"", handle, signal_source, current_source, alias); g_free (alias); continue; } g_value_init (&entry, TP_STRUCT_TYPE_ALIAS_PAIR); g_value_take_boxed (&entry, dbus_g_type_specialized_construct (TP_STRUCT_TYPE_ALIAS_PAIR)); dbus_g_type_struct_set (&entry, 0, handle, 1, alias, G_MAXUINT); g_ptr_array_add (aliases, g_value_get_boxed (&entry)); /* Check whether the roster has an entry for the handle and if so, set * the roster alias so the vCard isn't fetched on every connect. */ if (signal_source < GABBLE_CONNECTION_ALIAS_FROM_ROSTER && gabble_roster_handle_has_entry (conn->roster, handle)) gabble_roster_handle_set_name (conn->roster, handle, alias, NULL); g_free (alias); } if (aliases->len > 0) tp_svc_connection_interface_aliasing_emit_aliases_changed (conn, aliases); for (i = 0; i < aliases->len; i++) g_boxed_free (TP_STRUCT_TYPE_ALIAS_PAIR, g_ptr_array_index (aliases, i)); g_ptr_array_unref (aliases); } static void set_or_clear (gchar **target, gchar *source) { if (target != NULL) *target = source; else g_free (source); } static void maybe_set (gchar **target, const gchar *source) { if (target != NULL) *target = g_strdup (source); } static GabbleConnectionAliasSource get_cached_remote_alias ( GabbleConnection *conn, TpHandleRepoIface *contact_handles, TpHandle handle, const gchar *jid, gchar **alias) { TpBaseConnection *base = (TpBaseConnection *) conn; GabblePresence *pres; const gchar *tmp; gchar *resource; tmp = tp_handle_get_qdata (contact_handles, handle, gabble_conn_aliasing_pep_alias_quark ()); if (tmp != NULL && tmp != NO_ALIAS) { maybe_set (alias, tmp); return GABBLE_CONNECTION_ALIAS_FROM_PRESENCE; } pres = gabble_presence_cache_get (conn->presence_cache, handle); if (NULL != pres && NULL != pres->nickname) { maybe_set (alias, pres->nickname); return GABBLE_CONNECTION_ALIAS_FROM_PRESENCE; } /* XXX: should this be more important than the ones from presence? */ /* if it's our own handle, use alias passed to the connmgr, if any */ if (handle == tp_base_connection_get_self_handle (base)) { gchar *cm_alias; g_object_get (conn, "alias", &cm_alias, NULL); if (cm_alias != NULL) { set_or_clear (alias, cm_alias); return GABBLE_CONNECTION_ALIAS_FROM_CONNMGR; } } /* MUC handles have the nickname in the resource */ if (wocky_decode_jid (jid, NULL, NULL, &resource) && NULL != resource) { set_or_clear (alias, resource); return GABBLE_CONNECTION_ALIAS_FROM_MUC_RESOURCE; } if (conn->vcard_manager != NULL) { /* if we've seen a nickname in their vCard, use that */ tmp = gabble_vcard_manager_get_cached_alias (conn->vcard_manager, handle); if (NULL != tmp) { maybe_set (alias, tmp); return GABBLE_CONNECTION_ALIAS_FROM_VCARD; } } maybe_set (alias, NULL); return GABBLE_CONNECTION_ALIAS_NONE; } /* * _gabble_connection_get_cached_alias: * @conn: a connection * @handle: a handle * @alias: (allow-none): location at which to store @handle's alias. If * provided, it will always be set to a non-NULL, non-empty string, * which the caller must free. * * Gets the best possible alias for @handle, falling back to their JID if * necessary. * * Returns: the source of the alias. */ GabbleConnectionAliasSource _gabble_connection_get_cached_alias (GabbleConnection *conn, TpHandle handle, gchar **alias) { TpBaseConnection *base = (TpBaseConnection *) conn; TpHandleRepoIface *contact_handles = tp_base_connection_get_handles (base, TP_HANDLE_TYPE_CONTACT); const gchar *tmp, *jid; gboolean roster_alias_was_jid = FALSE; GabbleConnectionAliasSource source; g_return_val_if_fail (NULL != conn, GABBLE_CONNECTION_ALIAS_NONE); g_return_val_if_fail (GABBLE_IS_CONNECTION (conn), GABBLE_CONNECTION_ALIAS_NONE); g_return_val_if_fail (tp_handle_is_valid (contact_handles, handle, NULL), GABBLE_CONNECTION_ALIAS_NONE); jid = tp_handle_inspect (contact_handles, handle); g_assert (NULL != jid); tmp = gabble_roster_handle_get_name (conn->roster, handle); if (!tp_strdiff (tmp, jid)) { /* Normally, we prefer whatever we've cached on the roster, to avoid * wasting bandwidth checking for aliases by repeatedly fetching the * vCard, and (more importantly) to prefer anything the local user set * over what the contact says their name is. * * However, if the alias stored on the roster is just the contact's JID, * we check for better aliases that we happen to have received from other * sources (maybe a PEP nick update, or a vCard we've fetched for the * avatar, or whatever). If we can't find anything better, we'll use the * JID, and still say that it came from the roster: this means we don't * defeat negative caching for contacts who genuinely don't have an * alias. */ roster_alias_was_jid = TRUE; } else if (!tp_str_empty (tmp)) { maybe_set (alias, tmp); return GABBLE_CONNECTION_ALIAS_FROM_ROSTER; } source = get_cached_remote_alias (conn, contact_handles, handle, jid, alias); if (source != GABBLE_CONNECTION_ALIAS_NONE) return source; /* otherwise just take their jid, which may have been specified on the roster * as the contact's alias. */ maybe_set (alias, jid); return roster_alias_was_jid ? GABBLE_CONNECTION_ALIAS_FROM_ROSTER : GABBLE_CONNECTION_ALIAS_FROM_JID; } /* * _gabble_connection_get_cached_remote_alias: * @conn: a connection * @handle: a handle * @alias: (allow-none): location at which to store @handle's alias. If * provided, it may be set to %NULL (if @handle has no cached remote * alias) or a non-empty string which the caller must free. * * Gets the best cached alias for @handle as provided by them (such as via PEP * Nicknames, in their vCard, etc), not considering anything the local user has * specified on their roster. * * Returns: the source of the alias, or GABBLE_CONNECTION_ALIAS_NONE if we have * no cached remote alias for @handle */ static GabbleConnectionAliasSource _gabble_connection_get_cached_remote_alias ( GabbleConnection *conn, TpHandle handle, gchar **alias) { TpBaseConnection *base = (TpBaseConnection *) conn; TpHandleRepoIface *contact_handles = tp_base_connection_get_handles (base, TP_HANDLE_TYPE_CONTACT); const gchar *jid = tp_handle_inspect (contact_handles, handle); g_assert (NULL != jid); return get_cached_remote_alias (conn, contact_handles, handle, jid, alias); } static void maybe_request_vcard (GabbleConnection *self, TpHandle handle, GabbleConnectionAliasSource source) { TpBaseConnection *base = (TpBaseConnection *) self; /* If the source wasn't good enough then do a request */ if (source < GABBLE_CONNECTION_ALIAS_FROM_VCARD && tp_base_connection_get_status (base) == TP_CONNECTION_STATUS_CONNECTED && !gabble_vcard_manager_has_cached_alias (self->vcard_manager, handle)) { if (self->features & GABBLE_CONNECTION_FEATURES_PEP) { TpHandleRepoIface *contact_handles = tp_base_connection_get_handles (base, TP_HANDLE_TYPE_CONTACT); gabble_do_pep_request (self, handle, contact_handles, aliases_request_basic_pep_cb, GUINT_TO_POINTER (handle)); } else { gabble_vcard_manager_request (self->vcard_manager, handle, 0, NULL, NULL, G_OBJECT (self)); } } } static void conn_aliasing_fill_contact_attributes (GObject *obj, const GArray *contacts, GHashTable *attributes_hash) { guint i; GabbleConnection *self = GABBLE_CONNECTION(obj); for (i = 0; i < contacts->len; i++) { TpHandle handle = g_array_index (contacts, TpHandle, i); GabbleConnectionAliasSource source; gchar *alias; GValue *val = tp_g_value_slice_new (G_TYPE_STRING); source = _gabble_connection_get_cached_alias (self, handle, &alias); g_assert (alias != NULL); g_value_take_string (val, alias); tp_contacts_mixin_set_contact_attribute (attributes_hash, handle, TP_IFACE_CONNECTION_INTERFACE_ALIASING"/alias", val); maybe_request_vcard (self, handle, source); } } /** * gabble_connection_get_aliases * * Implements D-Bus method GetAliases * on interface org.freedesktop.Telepathy.Connection.Interface.Aliasing * * @context: The D-Bus invocation context to use to return values * or throw an error. */ static void gabble_connection_get_aliases (TpSvcConnectionInterfaceAliasing *iface, const GArray *contacts, DBusGMethodInvocation *context) { GabbleConnection *self = GABBLE_CONNECTION (iface); TpBaseConnection *base = (TpBaseConnection *) self; TpHandleRepoIface *contact_handles = tp_base_connection_get_handles (base, TP_HANDLE_TYPE_CONTACT); GHashTable *result; GError *error = NULL; guint i; g_assert (GABBLE_IS_CONNECTION (self)); TP_BASE_CONNECTION_ERROR_IF_NOT_CONNECTED (base, context); if (!tp_handles_are_valid (contact_handles, contacts, FALSE, &error)) { dbus_g_method_return_error (context, error); g_error_free (error); return; } result = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, g_free); for (i = 0; i < contacts->len; i++) { TpHandle handle = g_array_index (contacts, TpHandle, i); GabbleConnectionAliasSource source; gchar *alias; source = _gabble_connection_get_cached_alias (self, handle, &alias); g_assert (alias != NULL); g_hash_table_insert (result, GUINT_TO_POINTER (handle), alias); maybe_request_vcard (self, handle, source); } tp_svc_connection_interface_aliasing_return_from_get_aliases (context, result); g_hash_table_unref (result); } void conn_aliasing_init (GabbleConnection *conn) { tp_contacts_mixin_add_contact_attributes_iface (G_OBJECT (conn), TP_IFACE_CONNECTION_INTERFACE_ALIASING, conn_aliasing_fill_contact_attributes); conn->pep_nick = wocky_pep_service_new (NS_NICK, TRUE); g_signal_connect (conn->pep_nick, "changed", G_CALLBACK (pep_nick_node_changed), conn); } void conn_aliasing_iface_init (gpointer g_iface, gpointer iface_data) { TpSvcConnectionInterfaceAliasingClass *klass = g_iface; #define IMPLEMENT(x) tp_svc_connection_interface_aliasing_implement_##x (\ klass, gabble_connection_##x) IMPLEMENT(get_alias_flags); IMPLEMENT(request_aliases); IMPLEMENT(get_aliases); IMPLEMENT(set_aliases); #undef IMPLEMENT } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������telepathy-gabble-0.18.2/src/conn-aliasing.h���������������������������������������������������������0000644�0001750�0001750�00000002746�12227000321�020475� 0����������������������������������������������������������������������������������������������������ustar�00smcv����������������������������smcv����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * conn-aliasing.h - Header for Gabble connection aliasing interface * Copyright (C) 2005-2007 Collabora Ltd. * Copyright (C) 2005-2007 Nokia Corporation * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 __CONN_ALIASING_H__ #define __CONN_ALIASING_H__ #include <glib.h> #include "connection.h" G_BEGIN_DECLS void conn_aliasing_init (GabbleConnection *conn); void conn_aliasing_iface_init (gpointer g_iface, gpointer iface_data); void gabble_conn_aliasing_nickname_updated (GObject *object, TpHandle handle, gpointer user_data); void gabble_conn_aliasing_nicknames_updated (GObject *object, GArray *handles, gpointer user_data); GabbleConnectionAliasSource _gabble_connection_get_cached_alias ( GabbleConnection *, TpHandle, gchar **); G_END_DECLS #endif /* __CONN_ALIASING_H__ */ ��������������������������telepathy-gabble-0.18.2/src/conn-addressing.c�������������������������������������������������������0000644�0001750�0001750�00000012735�12227000321�021023� 0����������������������������������������������������������������������������������������������������ustar�00smcv����������������������������smcv����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * conn-addressing.h - Header for Gabble connection code handling addressing. * Copyright (C) 2010 Collabora Ltd. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 "config.h" #include "conn-addressing.h" #include <dbus/dbus-glib-lowlevel.h> #include <telepathy-glib/telepathy-glib.h> #include <telepathy-glib/telepathy-glib-dbus.h> #include "extensions/extensions.h" #include "addressing-util.h" #include "namespaces.h" #include "util.h" static const char *assumed_interfaces[] = { TP_IFACE_CONNECTION, GABBLE_IFACE_CONNECTION_INTERFACE_ADDRESSING, NULL }; static void _fill_contact_attributes (TpHandleRepoIface *contact_repo, TpHandle contact, GHashTable *attributes_hash) { gchar **uris = gabble_uris_for_handle (contact_repo, contact); GHashTable *addresses = gabble_vcard_addresses_for_handle (contact_repo, contact); tp_contacts_mixin_set_contact_attribute (attributes_hash, contact, GABBLE_IFACE_CONNECTION_INTERFACE_ADDRESSING"/uris", tp_g_value_slice_new_take_boxed (G_TYPE_STRV, uris)); tp_contacts_mixin_set_contact_attribute (attributes_hash, contact, GABBLE_IFACE_CONNECTION_INTERFACE_ADDRESSING"/addresses", tp_g_value_slice_new_take_boxed (TP_HASH_TYPE_STRING_STRING_MAP, addresses)); } static void conn_addressing_fill_contact_attributes (GObject *obj, const GArray *contacts, GHashTable *attributes_hash) { guint i; TpHandleRepoIface *contact_repo = tp_base_connection_get_handles ( (TpBaseConnection *) obj, TP_HANDLE_TYPE_CONTACT); for (i = 0; i < contacts->len; i++) { TpHandle contact = g_array_index (contacts, TpHandle, i); _fill_contact_attributes (contact_repo, contact, attributes_hash); } } static void conn_addressing_get_contacts_by_uri (GabbleSvcConnectionInterfaceAddressing *iface, const gchar **uris, const gchar **interfaces, DBusGMethodInvocation *context) { const gchar **uri; TpHandleRepoIface *contact_repo = tp_base_connection_get_handles ( (TpBaseConnection *) iface, TP_HANDLE_TYPE_CONTACT); GHashTable *attributes; GHashTable *requested = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); GArray *handles = g_array_sized_new (TRUE, TRUE, sizeof (TpHandle), g_strv_length ((gchar **) uris)); gchar *sender = dbus_g_method_get_sender (context); for (uri = uris; *uri != NULL; uri++) { TpHandle h = gabble_ensure_handle_from_uri (contact_repo, *uri, NULL); if (h == 0) continue; g_hash_table_insert (requested, g_strdup (*uri), GUINT_TO_POINTER (h)); g_array_append_val (handles, h); } attributes = tp_contacts_mixin_get_contact_attributes (G_OBJECT (iface), handles, interfaces, assumed_interfaces, sender); gabble_svc_connection_interface_addressing_return_from_get_contacts_by_uri ( context, requested, attributes); g_array_unref (handles); g_hash_table_unref (requested); g_hash_table_unref (attributes); g_free (sender); } static void conn_addressing_get_contacts_by_vcard_field (GabbleSvcConnectionInterfaceAddressing *iface, const gchar *field, const gchar **addresses, const gchar **interfaces, DBusGMethodInvocation *context) { const gchar **address; TpHandleRepoIface *contact_repo = tp_base_connection_get_handles ( (TpBaseConnection *) iface, TP_HANDLE_TYPE_CONTACT); GHashTable *attributes; GHashTable *requested = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); GArray *handles = g_array_sized_new (TRUE, TRUE, sizeof (TpHandle), g_strv_length ((gchar **) addresses)); gchar *sender = dbus_g_method_get_sender (context); for (address = addresses; *address != NULL; address++) { TpHandle h = gabble_ensure_handle_from_vcard_address (contact_repo, field, *address, NULL); if (h == 0) continue; g_hash_table_insert (requested, g_strdup (*address), GUINT_TO_POINTER (h)); g_array_append_val (handles, h); } attributes = tp_contacts_mixin_get_contact_attributes (G_OBJECT (iface), handles, interfaces, assumed_interfaces, sender); gabble_svc_connection_interface_addressing_return_from_get_contacts_by_vcard_field ( context, requested, attributes); g_array_unref (handles); g_hash_table_unref (requested); g_hash_table_unref (attributes); g_free (sender); } void conn_addressing_init (GabbleConnection *self) { tp_contacts_mixin_add_contact_attributes_iface (G_OBJECT (self), GABBLE_IFACE_CONNECTION_INTERFACE_ADDRESSING, conn_addressing_fill_contact_attributes); } void conn_addressing_iface_init (gpointer g_iface, gpointer iface_data) { #define IMPLEMENT(x) \ gabble_svc_connection_interface_addressing_implement_##x (\ g_iface, conn_addressing_##x) IMPLEMENT (get_contacts_by_uri); IMPLEMENT (get_contacts_by_vcard_field); #undef IMPLEMENT } �����������������������������������telepathy-gabble-0.18.2/src/conn-addressing.h�������������������������������������������������������0000644�0001750�0001750�00000002216�12200204333�021021� 0����������������������������������������������������������������������������������������������������ustar�00smcv����������������������������smcv����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * conn-addressing.h - Header for Gabble connection code handling addressing. * Copyright (C) 2010 Collabora Ltd. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 GABBLE_CONN_ADDRESSING_H #define GABBLE_CONN_ADDRESSING_H #include <glib.h> #include "connection.h" G_BEGIN_DECLS void conn_addressing_iface_init (gpointer g_iface, gpointer iface_data); void conn_addressing_init (GabbleConnection *self); G_END_DECLS #endif /* GABBLE_CONN_ADDRESSING_H */ ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������telepathy-gabble-0.18.2/src/caps-channel-manager.c��������������������������������������������������0000644�0001750�0001750�00000006141�12200204333�021703� 0����������������������������������������������������������������������������������������������������ustar�00smcv����������������������������smcv����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * caps-channel-manager.c - interface holding capabilities functions for * channel managers * * Copyright (C) 2008 Collabora Ltd. * Copyright (C) 2008 Nokia Corporation * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 "config.h" #include "gabble/caps-channel-manager.h" #include <telepathy-glib/telepathy-glib.h> #define DEBUG_FLAG GABBLE_DEBUG_PRESENCE #include "debug.h" G_DEFINE_INTERFACE (GabbleCapsChannelManager, gabble_caps_channel_manager, TP_TYPE_CHANNEL_MANAGER); /* stub function needed for the G_DEFINE_INTERFACE macro above */ static void gabble_caps_channel_manager_default_init ( GabbleCapsChannelManagerInterface *interface) { } /* Virtual-method wrappers */ void gabble_caps_channel_manager_get_contact_capabilities ( GabbleCapsChannelManager *caps_manager, TpHandle handle, const GabbleCapabilitySet *caps, GPtrArray *arr) { GabbleCapsChannelManagerInterface *iface = GABBLE_CAPS_CHANNEL_MANAGER_GET_INTERFACE (caps_manager); GabbleCapsChannelManagerGetContactCapsFunc method = iface->get_contact_caps; if (method != NULL) { method (caps_manager, handle, caps, arr); } /* ... else assume there are no caps for this kind of channel */ } /** * gabble_caps_channel_manager_represent_client: * @self: a channel manager * @client_name: the name of the client, for any debug messages * @filters: the channel classes accepted by the client, as an array of * GHashTable with string keys and GValue values * @cap_tokens: the handler capability tokens supported by the client * @cap_set: a set into which to merge additional XMPP capabilities * @data_forms: a #GPtrArray of #WockyDataForm objects * * Convert the capabilities of a Telepathy client into XMPP capabilities to be * advertised. * * (The actual XMPP capabilities advertised will be the union of the XMPP * capabilities of every installed client.) */ void gabble_caps_channel_manager_represent_client ( GabbleCapsChannelManager *caps_manager, const gchar *client_name, const GPtrArray *filters, const gchar * const *cap_tokens, GabbleCapabilitySet *cap_set, GPtrArray *data_forms) { GabbleCapsChannelManagerInterface *iface = GABBLE_CAPS_CHANNEL_MANAGER_GET_INTERFACE (caps_manager); GabbleCapsChannelManagerRepresentClientFunc method = iface->represent_client; if (method != NULL) { method (caps_manager, client_name, filters, cap_tokens, cap_set, data_forms); } } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������telepathy-gabble-0.18.2/src/caps-hash.c�������������������������������������������������������������0000644�0001750�0001750�00000007044�12200204333�017611� 0����������������������������������������������������������������������������������������������������ustar�00smcv����������������������������smcv����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * caps-hash.c - Computing verification string hash (XEP-0115 v1.5) * Copyright (C) 2008 Collabora Ltd. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 */ /* Computing verification string hash (XEP-0115 v1.5) * * Gabble does not do anything with dataforms (XEP-0128) included in * capabilities. However, it needs to parse them in order to compute the hash * according to XEP-0115. */ #include "config.h" #include "caps-hash.h" #include <string.h> #include <wocky/wocky.h> #define DEBUG_FLAG GABBLE_DEBUG_PRESENCE #include "gabble/capabilities.h" #include "debug.h" #include "namespaces.h" #include "presence-cache.h" #include "presence.h" #include "util.h" static void ptr_array_add_str (gpointer str, gpointer array) { g_ptr_array_add (array, str); } /** * Compute our hash as defined by the XEP-0115. * * Returns: the hash. The called must free the returned hash with g_free(). */ gchar * caps_hash_compute_from_self_presence (GabbleConnection *self) { GabblePresence *presence = self->self_presence; const GabbleCapabilitySet *cap_set; GPtrArray *features = g_ptr_array_new (); GPtrArray *identities = wocky_disco_identity_array_new (); GPtrArray *data_forms; gchar *str; /* XEP-0030 requires at least 1 identity. We don't need more. */ g_ptr_array_add (identities, wocky_disco_identity_new ("client", CLIENT_TYPE, NULL, PACKAGE_STRING)); cap_set = gabble_presence_peek_caps (presence); gabble_capability_set_foreach (cap_set, ptr_array_add_str, features); data_forms = gabble_presence_peek_data_forms (presence); str = wocky_caps_hash_compute_from_lists (features, identities, data_forms); g_ptr_array_unref (features); wocky_disco_identity_array_free (identities); return str; } /** * Compute the hash as defined by the XEP-0115 from a received GabbleCapabilitySet * * Returns: the hash. The called must free the returned hash with g_free(). */ gchar * gabble_caps_hash_compute_full (const GabbleCapabilitySet *cap_set, const GPtrArray *identities, GPtrArray *data_forms) { GPtrArray *features = g_ptr_array_new (); GPtrArray *identities_copy = ((identities == NULL) ? wocky_disco_identity_array_new () : wocky_disco_identity_array_copy (identities)); gchar *str; gabble_capability_set_foreach (cap_set, ptr_array_add_str, features); str = wocky_caps_hash_compute_from_lists (features, identities_copy, data_forms); g_ptr_array_unref (features); wocky_disco_identity_array_free (identities_copy); return str; } /** * Compute the hash as defined by the XEP-0115 from a received GabbleCapabilitySet * * Returns: the hash. The called must free the returned hash with g_free(). */ gchar * gabble_caps_hash_compute (const GabbleCapabilitySet *cap_set, const GPtrArray *identities) { return gabble_caps_hash_compute_full (cap_set, identities, NULL); } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������telepathy-gabble-0.18.2/src/caps-hash.h�������������������������������������������������������������0000644�0001750�0001750�00000002063�12200204333�017612� 0����������������������������������������������������������������������������������������������������ustar�00smcv����������������������������smcv����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * caps-hash.h - Headers for computing verification string hash (XEP-0115 v1.5) * Copyright (C) 2008 Collabora Ltd. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 __CAPS_HASH_H__ #define __CAPS_HASH_H__ #include "connection.h" #include "gabble/caps-hash.h" gchar *caps_hash_compute_from_self_presence ( GabbleConnection *self); #endif /* __CAPS_HASH_H__ */ �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������telepathy-gabble-0.18.2/src/capabilities.c����������������������������������������������������������0000644�0001750�0001750�00000050000�12200204333�020361� 0����������������������������������������������������������������������������������������������������ustar�00smcv����������������������������smcv����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * capabilities.c - Connection.Interface.Capabilities constants and utilities * Copyright (C) 2005 Collabora Ltd. * Copyright (C) 2005 Nokia Corporation * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 "config.h" #include "gabble/capabilities.h" #include <stdlib.h> #include <string.h> #include <telepathy-glib/telepathy-glib.h> #include <telepathy-glib/telepathy-glib-dbus.h> #define DEBUG_FLAG GABBLE_DEBUG_PRESENCE #include "debug.h" #include "namespaces.h" typedef struct _Feature Feature; struct _Feature { enum { FEATURE_FIXED, FEATURE_OPTIONAL, FEATURE_OLPC } feature_type; gchar *ns; }; static const Feature self_advertised_features[] = { { FEATURE_FIXED, NS_GOOGLE_FEAT_SESSION }, #ifdef ENABLE_VOIP { FEATURE_FIXED, NS_JINGLE_TRANSPORT_RAWUDP }, { FEATURE_FIXED, NS_JINGLE015 }, { FEATURE_FIXED, NS_JINGLE032 }, #endif { FEATURE_FIXED, NS_CHAT_STATES }, { FEATURE_FIXED, NS_NICK }, { FEATURE_FIXED, NS_NICK "+notify" }, { FEATURE_FIXED, NS_SI }, { FEATURE_FIXED, NS_IBB }, { FEATURE_FIXED, NS_TUBES }, { FEATURE_FIXED, NS_BYTESTREAMS }, { FEATURE_FIXED, NS_VERSION }, { FEATURE_FIXED, NS_LAST }, { FEATURE_FIXED, NS_RECEIPTS }, #ifdef ENABLE_FILE_TRANSFER { FEATURE_OPTIONAL, NS_FILE_TRANSFER }, { FEATURE_OPTIONAL, NS_TP_FT_METADATA }, #endif #ifdef ENABLE_VOIP { FEATURE_OPTIONAL, NS_GOOGLE_TRANSPORT_P2P }, { FEATURE_OPTIONAL, NS_JINGLE_TRANSPORT_ICEUDP }, { FEATURE_OPTIONAL, NS_GOOGLE_FEAT_SHARE }, { FEATURE_OPTIONAL, NS_GOOGLE_FEAT_VOICE }, { FEATURE_OPTIONAL, NS_GOOGLE_FEAT_VIDEO }, { FEATURE_OPTIONAL, NS_GOOGLE_FEAT_CAMERA }, { FEATURE_OPTIONAL, NS_JINGLE_DESCRIPTION_AUDIO }, { FEATURE_OPTIONAL, NS_JINGLE_DESCRIPTION_VIDEO }, { FEATURE_OPTIONAL, NS_JINGLE_RTP }, { FEATURE_OPTIONAL, NS_JINGLE_RTP_AUDIO }, { FEATURE_OPTIONAL, NS_JINGLE_RTP_VIDEO }, #endif { FEATURE_OLPC, NS_OLPC_BUDDY_PROPS "+notify" }, { FEATURE_OLPC, NS_OLPC_ACTIVITIES "+notify" }, { FEATURE_OLPC, NS_OLPC_CURRENT_ACTIVITY "+notify" }, { FEATURE_OLPC, NS_OLPC_ACTIVITY_PROPS "+notify" }, { FEATURE_OPTIONAL, NS_GEOLOC "+notify" }, { 0, NULL } }; static const Feature quirks[] = { { 0, QUIRK_OMITS_CONTENT_CREATORS }, { 0, NULL } }; static GabbleCapabilitySet *legacy_caps = NULL; static GabbleCapabilitySet *share_v1_caps = NULL; static GabbleCapabilitySet *voice_v1_caps = NULL; static GabbleCapabilitySet *video_v1_caps = NULL; static GabbleCapabilitySet *camera_v1_caps = NULL; static GabbleCapabilitySet *any_audio_caps = NULL; static GabbleCapabilitySet *any_video_caps = NULL; static GabbleCapabilitySet *any_audio_video_caps = NULL; static GabbleCapabilitySet *any_google_av_caps = NULL; static GabbleCapabilitySet *any_jingle_av_caps = NULL; static GabbleCapabilitySet *any_transport_caps = NULL; static GabbleCapabilitySet *fixed_caps = NULL; static GabbleCapabilitySet *geoloc_caps = NULL; static GabbleCapabilitySet *olpc_caps = NULL; const GabbleCapabilitySet * gabble_capabilities_get_legacy (void) { return legacy_caps; } const GabbleCapabilitySet * gabble_capabilities_get_bundle_share_v1 (void) { return share_v1_caps; } const GabbleCapabilitySet * gabble_capabilities_get_bundle_voice_v1 (void) { return voice_v1_caps; } const GabbleCapabilitySet * gabble_capabilities_get_bundle_video_v1 (void) { return video_v1_caps; } const GabbleCapabilitySet * gabble_capabilities_get_bundle_camera_v1 (void) { return camera_v1_caps; } const GabbleCapabilitySet * gabble_capabilities_get_any_audio (void) { return any_audio_caps; } const GabbleCapabilitySet * gabble_capabilities_get_any_video (void) { return any_video_caps; } const GabbleCapabilitySet * gabble_capabilities_get_any_audio_video (void) { return any_audio_video_caps; } const GabbleCapabilitySet * gabble_capabilities_get_any_google_av (void) { return any_google_av_caps; } const GabbleCapabilitySet * gabble_capabilities_get_any_jingle_av (void) { return any_jingle_av_caps; } const GabbleCapabilitySet * gabble_capabilities_get_any_transport (void) { return any_transport_caps; } const GabbleCapabilitySet * gabble_capabilities_get_fixed_caps (void) { return fixed_caps; } const GabbleCapabilitySet * gabble_capabilities_get_geoloc_notify (void) { return geoloc_caps; } const GabbleCapabilitySet * gabble_capabilities_get_olpc_notify (void) { return olpc_caps; } static gboolean omits_content_creators (WockyNode *identity) { const gchar *name, *suffix; gchar *end; int ver; name = wocky_node_get_attribute (identity, "name"); if (name == NULL) return FALSE; #define PREFIX "Telepathy Gabble 0.7." if (!g_str_has_prefix (name, PREFIX)) return FALSE; suffix = name + strlen (PREFIX); ver = strtol (suffix, &end, 10); if (*end != '\0') return FALSE; /* Gabble versions since 0.7.16 did not send the creator='' attribute for * contents. The bug is fixed in 0.7.29. */ if (ver >= 16 && ver < 29) { DEBUG ("contact is using '%s' which omits 'creator'", name); return TRUE; } else { return FALSE; } } static gsize feature_handles_refcount = 0; /* The handles in this repository are not really handles in the tp-spec sense * of the word; we're just using it as a convenient implementation of a * refcounted string pool. Their string values are either XMPP namespaces, * or "quirk" pseudo-namespaces starting with QUIRK_PREFIX_CHAR (like * QUIRK_OMITS_CONTENT_CREATORS). */ static TpHandleRepoIface *feature_handles = NULL; void gabble_capabilities_init (gpointer conn) { DEBUG ("%p", conn); if (feature_handles_refcount++ == 0) { const Feature *feat; g_assert (feature_handles == NULL); /* TpDynamicHandleRepo wants a handle type, which isn't relevant here * (we're just using it as a string pool). Use an arbitrary handle type * to shut it up. */ feature_handles = tp_dynamic_handle_repo_new (TP_HANDLE_TYPE_CONTACT, NULL, NULL); /* make the pre-cooked bundles */ legacy_caps = gabble_capability_set_new (); for (feat = self_advertised_features; feat->ns != NULL; feat++) { gabble_capability_set_add (legacy_caps, feat->ns); } #ifdef ENABLE_VOIP share_v1_caps = gabble_capability_set_new (); gabble_capability_set_add (share_v1_caps, NS_GOOGLE_FEAT_SHARE); voice_v1_caps = gabble_capability_set_new (); gabble_capability_set_add (voice_v1_caps, NS_GOOGLE_FEAT_VOICE); video_v1_caps = gabble_capability_set_new (); gabble_capability_set_add (video_v1_caps, NS_GOOGLE_FEAT_VIDEO); camera_v1_caps = gabble_capability_set_new (); gabble_capability_set_add (camera_v1_caps, NS_GOOGLE_FEAT_CAMERA); any_audio_caps = gabble_capability_set_new (); gabble_capability_set_add (any_audio_caps, NS_JINGLE_RTP_AUDIO); gabble_capability_set_add (any_audio_caps, NS_JINGLE_DESCRIPTION_AUDIO); gabble_capability_set_add (any_audio_caps, NS_GOOGLE_FEAT_VOICE); any_video_caps = gabble_capability_set_new (); gabble_capability_set_add (any_video_caps, NS_JINGLE_RTP_VIDEO); gabble_capability_set_add (any_video_caps, NS_JINGLE_DESCRIPTION_VIDEO); gabble_capability_set_add (any_video_caps, NS_GOOGLE_FEAT_VIDEO); any_audio_video_caps = gabble_capability_set_copy (any_audio_caps); gabble_capability_set_update (any_audio_video_caps, any_video_caps); any_google_av_caps = gabble_capability_set_new (); gabble_capability_set_add (any_google_av_caps, NS_GOOGLE_FEAT_VOICE); gabble_capability_set_add (any_google_av_caps, NS_GOOGLE_FEAT_VIDEO); any_jingle_av_caps = gabble_capability_set_copy (any_audio_caps); gabble_capability_set_update (any_jingle_av_caps, any_video_caps); gabble_capability_set_exclude (any_jingle_av_caps, any_google_av_caps); any_transport_caps = gabble_capability_set_new (); gabble_capability_set_add (any_transport_caps, NS_GOOGLE_TRANSPORT_P2P); gabble_capability_set_add (any_transport_caps, NS_JINGLE_TRANSPORT_ICEUDP); gabble_capability_set_add (any_transport_caps, NS_JINGLE_TRANSPORT_RAWUDP); #endif fixed_caps = gabble_capability_set_new (); for (feat = self_advertised_features; feat->ns != NULL; feat++) { if (feat->feature_type == FEATURE_FIXED) gabble_capability_set_add (fixed_caps, feat->ns); } geoloc_caps = gabble_capability_set_new (); gabble_capability_set_add (geoloc_caps, NS_GEOLOC "+notify"); olpc_caps = gabble_capability_set_new (); for (feat = self_advertised_features; feat->ns != NULL; feat++) { if (feat->feature_type == FEATURE_OLPC) gabble_capability_set_add (olpc_caps, feat->ns); } } g_assert (feature_handles != NULL); } void gabble_capabilities_finalize (gpointer conn) { DEBUG ("%p", conn); g_assert (feature_handles_refcount > 0); if (--feature_handles_refcount == 0) { gabble_capability_set_free (legacy_caps); #ifdef ENABLE_VOIP gabble_capability_set_free (share_v1_caps); gabble_capability_set_free (voice_v1_caps); gabble_capability_set_free (video_v1_caps); gabble_capability_set_free (camera_v1_caps); gabble_capability_set_free (any_audio_caps); gabble_capability_set_free (any_video_caps); gabble_capability_set_free (any_audio_video_caps); gabble_capability_set_free (any_google_av_caps); gabble_capability_set_free (any_jingle_av_caps); gabble_capability_set_free (any_transport_caps); #endif gabble_capability_set_free (fixed_caps); gabble_capability_set_free (geoloc_caps); gabble_capability_set_free (olpc_caps); legacy_caps = NULL; share_v1_caps = NULL; voice_v1_caps = NULL; video_v1_caps = NULL; camera_v1_caps = NULL; any_audio_caps = NULL; any_video_caps = NULL; any_audio_video_caps = NULL; any_google_av_caps = NULL; any_jingle_av_caps = NULL; any_transport_caps = NULL; fixed_caps = NULL; geoloc_caps = NULL; olpc_caps = NULL; tp_clear_object (&feature_handles); } } struct _GabbleCapabilitySet { TpHandleSet *handles; }; GabbleCapabilitySet * gabble_capability_set_new (void) { GabbleCapabilitySet *ret = g_slice_new0 (GabbleCapabilitySet); g_assert (feature_handles != NULL); ret->handles = tp_handle_set_new (feature_handles); return ret; } GabbleCapabilitySet * gabble_capability_set_new_from_stanza (WockyNode *query_result) { GabbleCapabilitySet *ret; const gchar *var; GSList *ni; g_return_val_if_fail (query_result != NULL, NULL); ret = gabble_capability_set_new (); for (ni = query_result->children; ni != NULL; ni = g_slist_next (ni)) { WockyNode *child = ni->data; if (!tp_strdiff (child->name, "identity")) { if (omits_content_creators (child)) gabble_capability_set_add (ret, QUIRK_OMITS_CONTENT_CREATORS); continue; } if (tp_strdiff (child->name, "feature")) continue; var = wocky_node_get_attribute (child, "var"); if (NULL == var) continue; if (G_UNLIKELY (var[0] == QUIRK_PREFIX_CHAR)) { /* I think not! (It's not allowed in XML...) */ continue; } /* TODO: only store namespaces we understand. */ gabble_capability_set_add (ret, var); } return ret; } GabbleCapabilitySet * gabble_capability_set_copy (const GabbleCapabilitySet *caps) { GabbleCapabilitySet *ret; g_return_val_if_fail (caps != NULL, NULL); ret = gabble_capability_set_new (); gabble_capability_set_update (ret, caps); return ret; } void gabble_capability_set_update (GabbleCapabilitySet *target, const GabbleCapabilitySet *source) { TpIntset *ret; g_return_if_fail (target != NULL); g_return_if_fail (source != NULL); ret = tp_handle_set_update (target->handles, tp_handle_set_peek (source->handles)); tp_intset_destroy (ret); } typedef struct { GSList *deleted; TpHandleSet *intersect_with; } IntersectHelper; static void intersect_helper (TpHandleSet *unused G_GNUC_UNUSED, TpHandle handle, gpointer p) { IntersectHelper *data = p; if (!tp_handle_set_is_member (data->intersect_with, handle)) data->deleted = g_slist_prepend (data->deleted, GUINT_TO_POINTER (handle)); } void gabble_capability_set_intersect (GabbleCapabilitySet *target, const GabbleCapabilitySet *source) { IntersectHelper data = { NULL, NULL }; g_return_if_fail (target != NULL); g_return_if_fail (source != NULL); if (target == source) return; data.intersect_with = source->handles; tp_handle_set_foreach (target->handles, intersect_helper, &data); while (data.deleted != NULL) { DEBUG ("dropping %s", tp_handle_inspect (feature_handles, GPOINTER_TO_UINT (data.deleted->data))); tp_handle_set_remove (target->handles, GPOINTER_TO_UINT (data.deleted->data)); data.deleted = g_slist_delete_link (data.deleted, data.deleted); } } static void remove_from_set (TpHandleSet *unused G_GNUC_UNUSED, TpHandle handle, gpointer handles) { tp_handle_set_remove (handles, handle); } void gabble_capability_set_exclude (GabbleCapabilitySet *caps, const GabbleCapabilitySet *removed) { g_return_if_fail (caps != NULL); g_return_if_fail (removed != NULL); if (caps == removed) { gabble_capability_set_clear (caps); return; } tp_handle_set_foreach (removed->handles, remove_from_set, caps->handles); } void gabble_capability_set_add (GabbleCapabilitySet *caps, const gchar *cap) { TpHandle handle; g_return_if_fail (caps != NULL); g_return_if_fail (cap != NULL); handle = tp_handle_ensure (feature_handles, cap, NULL, NULL); tp_handle_set_add (caps->handles, handle); } gboolean gabble_capability_set_remove (GabbleCapabilitySet *caps, const gchar *cap) { TpHandle handle; g_return_val_if_fail (caps != NULL, FALSE); g_return_val_if_fail (cap != NULL, FALSE); handle = tp_handle_lookup (feature_handles, cap, NULL, NULL); if (handle == 0) return FALSE; return tp_handle_set_remove (caps->handles, handle); } void gabble_capability_set_clear (GabbleCapabilitySet *caps) { g_return_if_fail (caps != NULL); /* There is no tp_handle_set_clear, so do the next best thing */ tp_handle_set_destroy (caps->handles); caps->handles = tp_handle_set_new (feature_handles); } void gabble_capability_set_free (GabbleCapabilitySet *caps) { g_return_if_fail (caps != NULL); tp_handle_set_destroy (caps->handles); g_slice_free (GabbleCapabilitySet, caps); } gint gabble_capability_set_size (const GabbleCapabilitySet *caps) { g_return_val_if_fail (caps != NULL, 0); return tp_handle_set_size (caps->handles); } /* By design, this function can be used as a GabbleCapabilitySetPredicate */ gboolean gabble_capability_set_has (const GabbleCapabilitySet *caps, const gchar *cap) { TpHandle handle; g_return_val_if_fail (caps != NULL, FALSE); g_return_val_if_fail (cap != NULL, FALSE); handle = tp_handle_lookup (feature_handles, cap, NULL, NULL); if (handle == 0) { /* nobody in the whole CM has this capability */ return FALSE; } return tp_handle_set_is_member (caps->handles, handle); } /* By design, this function can be used as a GabbleCapabilitySetPredicate */ gboolean gabble_capability_set_has_one (const GabbleCapabilitySet *caps, const GabbleCapabilitySet *alternatives) { TpIntsetFastIter iter; guint element; g_return_val_if_fail (caps != NULL, FALSE); g_return_val_if_fail (alternatives != NULL, FALSE); tp_intset_fast_iter_init (&iter, tp_handle_set_peek (alternatives->handles)); while (tp_intset_fast_iter_next (&iter, &element)) { if (tp_handle_set_is_member (caps->handles, element)) { return TRUE; } } return FALSE; } /* By design, this function can be used as a GabbleCapabilitySetPredicate */ gboolean gabble_capability_set_at_least (const GabbleCapabilitySet *caps, const GabbleCapabilitySet *query) { TpIntsetFastIter iter; guint element; g_return_val_if_fail (caps != NULL, FALSE); g_return_val_if_fail (query != NULL, FALSE); tp_intset_fast_iter_init (&iter, tp_handle_set_peek (query->handles)); while (tp_intset_fast_iter_next (&iter, &element)) { if (!tp_handle_set_is_member (caps->handles, element)) { return FALSE; } } return TRUE; } gboolean gabble_capability_set_equals (const GabbleCapabilitySet *a, const GabbleCapabilitySet *b) { g_return_val_if_fail (a != NULL, FALSE); g_return_val_if_fail (b != NULL, FALSE); return tp_intset_is_equal (tp_handle_set_peek (a->handles), tp_handle_set_peek (b->handles)); } /* Does not iterate over quirks, only real features. */ void gabble_capability_set_foreach (const GabbleCapabilitySet *caps, GFunc func, gpointer user_data) { TpIntsetFastIter iter; guint element; g_return_if_fail (caps != NULL); g_return_if_fail (func != NULL); tp_intset_fast_iter_init (&iter, tp_handle_set_peek (caps->handles)); while (tp_intset_fast_iter_next (&iter, &element)) { const gchar *var = tp_handle_inspect (feature_handles, element); g_return_if_fail (var != NULL); if (var[0] != QUIRK_PREFIX_CHAR) func ((gchar *) var, user_data); } } static void append_intset (GString *ret, const TpIntset *cap_ints, const gchar *indent) { TpIntsetFastIter iter; guint element; tp_intset_fast_iter_init (&iter, cap_ints); while (tp_intset_fast_iter_next (&iter, &element)) { const gchar *var = tp_handle_inspect (feature_handles, element); g_return_if_fail (var != NULL); if (var[0] == QUIRK_PREFIX_CHAR) { g_string_append_printf (ret, "%sQuirk: %s\n", indent, var + 1); } else { g_string_append_printf (ret, "%sFeature: %s\n", indent, var); } } } gchar * gabble_capability_set_dump (const GabbleCapabilitySet *caps, const gchar *indent) { GString *ret; g_return_val_if_fail (caps != NULL, NULL); if (indent == NULL) indent = ""; ret = g_string_new (indent); g_string_append (ret, "--begin--\n"); append_intset (ret, tp_handle_set_peek (caps->handles), indent); g_string_append (ret, indent); g_string_append (ret, "--end--\n"); return g_string_free (ret, FALSE); } gchar * gabble_capability_set_dump_diff (const GabbleCapabilitySet *old_caps, const GabbleCapabilitySet *new_caps, const gchar *indent) { TpIntset *old_ints, *new_ints, *rem, *add; GString *ret; g_return_val_if_fail (old_caps != NULL, NULL); g_return_val_if_fail (new_caps != NULL, NULL); old_ints = tp_handle_set_peek (old_caps->handles); new_ints = tp_handle_set_peek (new_caps->handles); if (tp_intset_is_equal (old_ints, new_ints)) return g_strdup_printf ("%s--no change--", indent); rem = tp_intset_difference (old_ints, new_ints); add = tp_intset_difference (new_ints, old_ints); ret = g_string_new (""); if (!tp_intset_is_empty (rem)) { g_string_append (ret, indent); g_string_append (ret, "--removed--\n"); append_intset (ret, rem, indent); } if (!tp_intset_is_empty (add)) { g_string_append (ret, indent); g_string_append (ret, "--added--\n"); append_intset (ret, add, indent); } g_string_append (ret, indent); g_string_append (ret, "--end--"); tp_intset_destroy (add); tp_intset_destroy (rem); return g_string_free (ret, FALSE); } telepathy-gabble-0.18.2/src/bytestream-socks5.c�����������������������������������������������������0000644�0001750�0001750�00000161736�12200204333�021337� 0����������������������������������������������������������������������������������������������������ustar�00smcv����������������������������smcv����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * bytestream-socks5.c - Source for GabbleBytestreamSocks5 * Copyright (C) 2006 Youness Alaoui <kakaroto@kakaroto.homelinux.net> * Copyright (C) 2007-2008 Collabora Ltd. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 "config.h" #include "bytestream-socks5.h" #include <gibber/gibber-sockets.h> #include <errno.h> /* on Darwin, net/if.h requires sys/sockets.h, which is included by * gibber-sockets.h; so this must come after that header */ #ifdef HAVE_NET_IF_H # include <net/if.h> #endif #include <string.h> #include <sys/types.h> #ifdef HAVE_UNISTD_H # include <unistd.h> #endif #ifdef HAVE_IFADDRS_H #include <ifaddrs.h> #endif #include <dbus/dbus-glib.h> #include <dbus/dbus-glib-lowlevel.h> #include <telepathy-glib/telepathy-glib.h> #include <telepathy-glib/telepathy-glib-dbus.h> #include <gibber/gibber-transport.h> #include <gibber/gibber-tcp-transport.h> #include <gibber/gibber-listener.h> #define DEBUG_FLAG GABBLE_DEBUG_BYTESTREAM #include "bytestream-factory.h" #include "bytestream-iface.h" #include "connection.h" #include "conn-util.h" #include "debug.h" #include "disco.h" #include "gabble-signals-marshal.h" #include "namespaces.h" #include "util.h" static void bytestream_iface_init (gpointer g_iface, gpointer iface_data); G_DEFINE_TYPE_WITH_CODE (GabbleBytestreamSocks5, gabble_bytestream_socks5, G_TYPE_OBJECT, G_IMPLEMENT_INTERFACE (GABBLE_TYPE_BYTESTREAM_IFACE, bytestream_iface_init)); /* properties */ enum { PROP_CONNECTION = 1, PROP_PEER_HANDLE, PROP_PEER_HANDLE_TYPE, PROP_STREAM_ID, PROP_STREAM_INIT_ID, PROP_PEER_JID, PROP_PEER_RESOURCE, PROP_STATE, PROP_PROTOCOL, PROP_SELF_JID, LAST_PROPERTY }; enum _Socks5State { SOCKS5_STATE_INVALID, SOCKS5_STATE_TARGET_TRYING_CONNECT, SOCKS5_STATE_TARGET_AUTH_REQUEST_SENT, SOCKS5_STATE_TARGET_CONNECT_REQUESTED, SOCKS5_STATE_CONNECTED, SOCKS5_STATE_INITIATOR_OFFER_SENT, SOCKS5_STATE_INITIATOR_AWAITING_AUTH_REQUEST, SOCKS5_STATE_INITIATOR_AWAITING_COMMAND, SOCKS5_STATE_INITIATOR_TRYING_CONNECT, SOCKS5_STATE_INITIATOR_AUTH_REQUEST_SENT, SOCKS5_STATE_INITIATOR_CONNECT_REQUESTED, SOCKS5_STATE_INITIATOR_ACTIVATION_SENT, SOCKS5_STATE_ERROR }; typedef enum _Socks5State Socks5State; /* SOCKS5 commands */ #define SOCKS5_VERSION 0x05 #define SOCKS5_CMD_CONNECT 0x01 #define SOCKS5_RESERVED 0x00 #define SOCKS5_ATYP_DOMAIN 0x03 #define SOCKS5_STATUS_OK 0x00 #define SOCKS5_AUTH_NONE 0x00 #define SHA1_LENGTH 40 #define SOCKS5_CONNECT_LENGTH (7 + SHA1_LENGTH) /* VER + CMD/REP + RSV + ATYP + PORT (2) */ #define SOCKS5_MIN_LENGTH 6 #define CONNECT_REPLY_TIMEOUT 30 #define CONNECT_TIMEOUT 10 struct _Streamhost { gchar *jid; gchar *host; guint16 port; }; typedef struct _Streamhost Streamhost; static Streamhost * streamhost_new (const gchar *jid, const gchar *host, guint16 port) { Streamhost *streamhost; g_return_val_if_fail (jid != NULL, NULL); g_return_val_if_fail (host != NULL, NULL); streamhost = g_slice_new0 (Streamhost); streamhost->jid = g_strdup (jid); streamhost->host = g_strdup (host); streamhost->port = port; return streamhost; } static void streamhost_free (Streamhost *streamhost) { if (streamhost == NULL) return; g_free (streamhost->jid); g_free (streamhost->host); g_slice_free (Streamhost, streamhost); } struct _GabbleBytestreamSocks5Private { GabbleConnection *conn; TpHandle peer_handle; gchar *stream_id; gchar *stream_init_id; gchar *peer_resource; GabbleBytestreamState bytestream_state; gchar *peer_jid; gchar *self_full_jid; gchar *proxy_jid; /* TRUE if the peer of this bytestream is a muc contact */ gboolean muc_contact; /* List of Streamhost */ GSList *streamhosts; /* Connections to streamhosts are async, so we keep the IQ set message * around */ WockyStanza *msg_for_acknowledge_connection; Socks5State socks5_state; GibberTransport *transport; gboolean write_blocked; gboolean read_blocked; GibberListener *listener; guint timer_id; GString *read_buffer; gboolean dispose_has_run; }; #define GABBLE_BYTESTREAM_SOCKS5_GET_PRIVATE(obj) ((obj)->priv) static void socks5_connect (GabbleBytestreamSocks5 *self); static void gabble_bytestream_socks5_close (GabbleBytestreamIface *iface, GError *error); static void socks5_error (GabbleBytestreamSocks5 *self); static void transport_handler (GibberTransport *transport, GibberBuffer *data, gpointer user_data); static void gabble_bytestream_socks5_init (GabbleBytestreamSocks5 *self) { GabbleBytestreamSocks5Private *priv = G_TYPE_INSTANCE_GET_PRIVATE (self, GABBLE_TYPE_BYTESTREAM_SOCKS5, GabbleBytestreamSocks5Private); self->priv = priv; } static void stop_timer (GabbleBytestreamSocks5 *self) { GabbleBytestreamSocks5Private *priv = GABBLE_BYTESTREAM_SOCKS5_GET_PRIVATE ( self); if (priv->timer_id == 0) return; g_source_remove (priv->timer_id); priv->timer_id = 0; } static void gabble_bytestream_socks5_dispose (GObject *object) { GabbleBytestreamSocks5 *self = GABBLE_BYTESTREAM_SOCKS5 (object); GabbleBytestreamSocks5Private *priv = GABBLE_BYTESTREAM_SOCKS5_GET_PRIVATE (self); if (priv->dispose_has_run) return; priv->dispose_has_run = TRUE; stop_timer (self); if (priv->bytestream_state != GABBLE_BYTESTREAM_STATE_CLOSED) { gabble_bytestream_iface_close (GABBLE_BYTESTREAM_IFACE (self), NULL); } tp_clear_object (&priv->transport); tp_clear_object (&priv->listener); G_OBJECT_CLASS (gabble_bytestream_socks5_parent_class)->dispose (object); } static void gabble_bytestream_socks5_finalize (GObject *object) { GabbleBytestreamSocks5 *self = GABBLE_BYTESTREAM_SOCKS5 (object); GabbleBytestreamSocks5Private *priv = GABBLE_BYTESTREAM_SOCKS5_GET_PRIVATE (self); g_free (priv->stream_id); g_free (priv->stream_init_id); g_free (priv->peer_resource); g_free (priv->peer_jid); g_free (priv->self_full_jid); g_free (priv->proxy_jid); g_slist_foreach (priv->streamhosts, (GFunc) streamhost_free, NULL); g_slist_free (priv->streamhosts); G_OBJECT_CLASS (gabble_bytestream_socks5_parent_class)->finalize (object); } static void gabble_bytestream_socks5_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { GabbleBytestreamSocks5 *self = GABBLE_BYTESTREAM_SOCKS5 (object); GabbleBytestreamSocks5Private *priv = GABBLE_BYTESTREAM_SOCKS5_GET_PRIVATE (self); switch (property_id) { case PROP_CONNECTION: g_value_set_object (value, priv->conn); break; case PROP_PEER_HANDLE: g_value_set_uint (value, priv->peer_handle); break; case PROP_PEER_HANDLE_TYPE: g_value_set_uint (value, TP_HANDLE_TYPE_CONTACT); break; case PROP_STREAM_ID: g_value_set_string (value, priv->stream_id); break; case PROP_STREAM_INIT_ID: g_value_set_string (value, priv->stream_init_id); break; case PROP_PEER_RESOURCE: g_value_set_string (value, priv->peer_resource); break; case PROP_PEER_JID: g_value_set_string (value, priv->peer_jid); break; case PROP_STATE: g_value_set_uint (value, priv->bytestream_state); break; case PROP_PROTOCOL: g_value_set_string (value, NS_BYTESTREAMS); break; case PROP_SELF_JID: g_value_set_string (value, priv->self_full_jid); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void gabble_bytestream_socks5_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { GabbleBytestreamSocks5 *self = GABBLE_BYTESTREAM_SOCKS5 (object); GabbleBytestreamSocks5Private *priv = GABBLE_BYTESTREAM_SOCKS5_GET_PRIVATE (self); switch (property_id) { case PROP_CONNECTION: priv->conn = g_value_get_object (value); break; case PROP_PEER_HANDLE: priv->peer_handle = g_value_get_uint (value); break; case PROP_STREAM_ID: g_free (priv->stream_id); priv->stream_id = g_value_dup_string (value); break; case PROP_STREAM_INIT_ID: g_free (priv->stream_init_id); priv->stream_init_id = g_value_dup_string (value); break; case PROP_PEER_RESOURCE: g_free (priv->peer_resource); priv->peer_resource = g_value_dup_string (value); break; case PROP_STATE: if (priv->bytestream_state != g_value_get_uint (value)) { priv->bytestream_state = g_value_get_uint (value); g_signal_emit_by_name (object, "state-changed", priv->bytestream_state); } break; case PROP_SELF_JID: g_free (priv->self_full_jid); priv->self_full_jid = g_value_dup_string (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static GObject * gabble_bytestream_socks5_constructor (GType type, guint n_props, GObjectConstructParam *props) { GObject *obj; GabbleBytestreamSocks5Private *priv; TpBaseConnection *base_conn; TpHandleRepoIface *contact_repo, *room_repo; const gchar *jid; obj = G_OBJECT_CLASS (gabble_bytestream_socks5_parent_class)-> constructor (type, n_props, props); priv = GABBLE_BYTESTREAM_SOCKS5_GET_PRIVATE (GABBLE_BYTESTREAM_SOCKS5 (obj)); g_assert (priv->conn != NULL); g_assert (priv->peer_handle != 0); g_assert (priv->stream_id != NULL); base_conn = TP_BASE_CONNECTION (priv->conn); contact_repo = tp_base_connection_get_handles (base_conn, TP_HANDLE_TYPE_CONTACT); room_repo = tp_base_connection_get_handles (base_conn, TP_HANDLE_TYPE_ROOM); jid = tp_handle_inspect (contact_repo, priv->peer_handle); if (priv->peer_resource != NULL) priv->peer_jid = g_strdup_printf ("%s/%s", jid, priv->peer_resource); else priv->peer_jid = g_strdup (jid); g_assert (priv->self_full_jid != NULL); priv->muc_contact = (gabble_get_room_handle_from_jid (room_repo, priv->peer_jid) != 0); return obj; } static void gabble_bytestream_socks5_class_init ( GabbleBytestreamSocks5Class *gabble_bytestream_socks5_class) { GObjectClass *object_class = G_OBJECT_CLASS (gabble_bytestream_socks5_class); GParamSpec *param_spec; g_type_class_add_private (gabble_bytestream_socks5_class, sizeof (GabbleBytestreamSocks5Private)); object_class->dispose = gabble_bytestream_socks5_dispose; object_class->finalize = gabble_bytestream_socks5_finalize; object_class->get_property = gabble_bytestream_socks5_get_property; object_class->set_property = gabble_bytestream_socks5_set_property; object_class->constructor = gabble_bytestream_socks5_constructor; g_object_class_override_property (object_class, PROP_CONNECTION, "connection"); g_object_class_override_property (object_class, PROP_PEER_HANDLE, "peer-handle"); g_object_class_override_property (object_class, PROP_PEER_HANDLE_TYPE, "peer-handle-type"); g_object_class_override_property (object_class, PROP_STREAM_ID, "stream-id"); g_object_class_override_property (object_class, PROP_PEER_JID, "peer-jid"); g_object_class_override_property (object_class, PROP_STATE, "state"); g_object_class_override_property (object_class, PROP_PROTOCOL, "protocol"); param_spec = g_param_spec_string ( "peer-resource", "Peer resource", "the resource used by the remote peer during the SI, if any", NULL, G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, PROP_PEER_RESOURCE, param_spec); param_spec = g_param_spec_string ( "stream-init-id", "stream init ID", "the iq ID of the SI request, if any", NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, PROP_STREAM_INIT_ID, param_spec); param_spec = g_param_spec_string ( "self-jid", "Our self jid", "Either a contact full jid or a muc jid", NULL, G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, PROP_SELF_JID, param_spec); } static gboolean write_to_transport (GabbleBytestreamSocks5 *self, const gchar *data, guint len, GError **error) { GabbleBytestreamSocks5Private *priv = GABBLE_BYTESTREAM_SOCKS5_GET_PRIVATE (self); if (!gibber_transport_send (priv->transport, (const guint8 *) data, len, error)) { return FALSE; } return TRUE; } static void transport_connected_cb (GibberTransport *transport, GabbleBytestreamSocks5 *self) { GabbleBytestreamSocks5Private *priv = GABBLE_BYTESTREAM_SOCKS5_GET_PRIVATE (self); stop_timer (self); if (priv->socks5_state == SOCKS5_STATE_TARGET_TRYING_CONNECT || priv->socks5_state == SOCKS5_STATE_INITIATOR_TRYING_CONNECT) { gchar msg[3]; DEBUG ("transport is connected. Sending auth request"); msg[0] = SOCKS5_VERSION; /* Number of auth methods we are offering, we support just * SOCKS5_AUTH_NONE */ msg[1] = 1; msg[2] = SOCKS5_AUTH_NONE; write_to_transport (self, msg, 3, NULL); if (priv->socks5_state == SOCKS5_STATE_TARGET_TRYING_CONNECT) priv->socks5_state = SOCKS5_STATE_TARGET_AUTH_REQUEST_SENT; else priv->socks5_state = SOCKS5_STATE_INITIATOR_AUTH_REQUEST_SENT; } } static void transport_disconnected_cb (GibberTransport *transport, GabbleBytestreamSocks5 *self) { stop_timer (self); DEBUG ("Sock5 transport disconnected"); socks5_error (self); } static void change_write_blocked_state (GabbleBytestreamSocks5 *self, gboolean blocked) { GabbleBytestreamSocks5Private *priv = GABBLE_BYTESTREAM_SOCKS5_GET_PRIVATE (self); if (priv->write_blocked == blocked) return; priv->write_blocked = blocked; g_signal_emit_by_name (self, "write-blocked", blocked); } static void socks5_close_transport (GabbleBytestreamSocks5 *self) { GabbleBytestreamSocks5Private *priv = GABBLE_BYTESTREAM_SOCKS5_GET_PRIVATE (self); if (priv->read_buffer != NULL) { g_string_free (priv->read_buffer, TRUE); priv->read_buffer = NULL; } if (priv->transport == NULL) return; g_signal_handlers_disconnect_matched (priv->transport, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, self); tp_clear_object (&priv->transport); } static void bytestream_closed (GabbleBytestreamSocks5 *self) { socks5_close_transport (self); g_object_set (self, "state", GABBLE_BYTESTREAM_STATE_CLOSED, NULL); } static void transport_buffer_empty_cb (GibberTransport *transport, GabbleBytestreamSocks5 *self) { GabbleBytestreamSocks5Private *priv = GABBLE_BYTESTREAM_SOCKS5_GET_PRIVATE (self); if (priv->bytestream_state == GABBLE_BYTESTREAM_STATE_CLOSING) { DEBUG ("buffer is now empty. Bytestream can be closed"); bytestream_closed (self); } else if (priv->write_blocked) { change_write_blocked_state (self, FALSE); } } static void set_transport (GabbleBytestreamSocks5 *self, GibberTransport *transport) { GabbleBytestreamSocks5Private *priv = GABBLE_BYTESTREAM_SOCKS5_GET_PRIVATE (self); priv->transport = g_object_ref (transport); g_assert (priv->read_buffer == NULL); priv->read_buffer = g_string_sized_new (4096); gibber_transport_set_handler (transport, transport_handler, self); g_signal_connect (transport, "connected", G_CALLBACK (transport_connected_cb), self); g_signal_connect (transport, "disconnected", G_CALLBACK (transport_disconnected_cb), self); g_signal_connect (priv->transport, "buffer-empty", G_CALLBACK (transport_buffer_empty_cb), self); } static void socks5_error (GabbleBytestreamSocks5 *self) { GabbleBytestreamSocks5Private *priv = GABBLE_BYTESTREAM_SOCKS5_GET_PRIVATE (self); WockyPorter *porter = wocky_session_get_porter (priv->conn->session); Socks5State previous_state; stop_timer (self); previous_state = priv->socks5_state; priv->socks5_state = SOCKS5_STATE_ERROR; switch (previous_state) { case SOCKS5_STATE_TARGET_TRYING_CONNECT: case SOCKS5_STATE_TARGET_AUTH_REQUEST_SENT: case SOCKS5_STATE_TARGET_CONNECT_REQUESTED: /* The attempt for connect to the streamhost failed */ socks5_close_transport (self); if (priv->streamhosts != NULL) { /* Remove the failed streamhost */ streamhost_free (priv->streamhosts->data); priv->streamhosts = g_slist_delete_link (priv->streamhosts, priv->streamhosts); } if (priv->streamhosts != NULL) { DEBUG ("connection to streamhost failed, trying the next one"); socks5_connect (self); return; } DEBUG ("no more streamhosts to try"); g_signal_emit_by_name (self, "connection-error"); g_assert (priv->msg_for_acknowledge_connection != NULL); wocky_porter_send_iq_error (porter, priv->msg_for_acknowledge_connection, WOCKY_XMPP_ERROR_ITEM_NOT_FOUND, "impossible to connect to any streamhost"); g_object_unref (priv->msg_for_acknowledge_connection); priv->msg_for_acknowledge_connection = NULL; break; case SOCKS5_STATE_INITIATOR_AWAITING_AUTH_REQUEST: case SOCKS5_STATE_INITIATOR_AWAITING_COMMAND: DEBUG ("Something goes wrong during SOCKS5 negotiation. Don't close " "the bytestream yet as the target can still try other streamhosts"); break; default: DEBUG ("error, closing the connection\n"); gabble_bytestream_socks5_close (GABBLE_BYTESTREAM_IFACE (self), NULL); } } static gchar * compute_domain (const gchar *sid, const gchar *initiator, const gchar *target) { gchar *unhashed_domain; gchar *domain; unhashed_domain = g_strconcat (sid, initiator, target, NULL); domain = sha1_hex (unhashed_domain, strlen (unhashed_domain)); g_free (unhashed_domain); return domain; } static gboolean check_domain (const gchar *domain, guint8 len, const gchar *expected) { if (len != SHA1_LENGTH || strncmp (domain, expected, SHA1_LENGTH) != 0) { DEBUG ("Wrong domain hash: %s (expected: %s)", domain, expected); return FALSE; } return TRUE; } static gboolean socks5_timer_cb (gpointer data) { GabbleBytestreamSocks5 *self = GABBLE_BYTESTREAM_SOCKS5 (data); DEBUG ("Timed out; closing SOCKS5 connection"); socks5_error (self); return FALSE; } static void start_timer (GabbleBytestreamSocks5 *self, guint seconds) { GabbleBytestreamSocks5Private *priv = GABBLE_BYTESTREAM_SOCKS5_GET_PRIVATE ( self); g_assert (priv->timer_id == 0); priv->timer_id = g_timeout_add_seconds (seconds, socks5_timer_cb, self); } static void target_got_connect_reply (GabbleBytestreamSocks5 *self) { GabbleBytestreamSocks5Private *priv = GABBLE_BYTESTREAM_SOCKS5_GET_PRIVATE ( self); WockyPorter *porter = wocky_session_get_porter (priv->conn->session); Streamhost *current_streamhost; DEBUG ("Received CONNECT reply. Socks5 stream connected. " "Bytestream is now open"); priv->socks5_state = SOCKS5_STATE_CONNECTED; g_object_set (self, "state", GABBLE_BYTESTREAM_STATE_OPEN, NULL); /* Acknowledge the connection */ current_streamhost = priv->streamhosts->data; wocky_porter_acknowledge_iq (porter, priv->msg_for_acknowledge_connection, '(', "query", ':', NS_BYTESTREAMS, /* streamhost-used informs the other end of the streamhost we * decided to use. In case of a direct connetion this is useless * but if we are using an external proxy we need to know which * one was selected */ '(', "streamhost-used", '@', "jid", current_streamhost->jid, ')', ')', NULL); if (priv->read_blocked) { DEBUG ("reading has been blocked. Blocking now as the socks5 " "negotiation is done"); gibber_transport_block_receiving (priv->transport, TRUE); } } static void socks5_activation_reply_cb ( GObject *source, GAsyncResult *result, gpointer user_data) { TpWeakRef *weak_ref = user_data; GabbleBytestreamSocks5 *self = tp_weak_ref_dup_object (weak_ref); GabbleBytestreamSocks5Private *priv; WockyStanza *reply_msg = NULL; tp_weak_ref_destroy (weak_ref); if (self == NULL) return; priv = self->priv; if (!conn_util_send_iq_finish (GABBLE_CONNECTION (source), result, &reply_msg, NULL)) { DEBUG ("Activation failed"); goto activation_failed; } if (priv->socks5_state != SOCKS5_STATE_INITIATOR_ACTIVATION_SENT) { DEBUG ("We are not waiting for an activation reply (state: %u)", priv->socks5_state); goto activation_failed; } DEBUG ("Proxy activated the bytestream. It's now open"); priv->socks5_state = SOCKS5_STATE_CONNECTED; g_object_set (self, "state", GABBLE_BYTESTREAM_STATE_OPEN, NULL); /* We can read data from the sock5 socket now */ gibber_transport_block_receiving (priv->transport, FALSE); goto out; activation_failed: g_signal_emit_by_name (self, "connection-error"); g_object_set (self, "state", GABBLE_BYTESTREAM_STATE_CLOSED, NULL); out: g_clear_object (&reply_msg); g_object_unref (self); } static void initiator_got_connect_reply (GabbleBytestreamSocks5 *self) { GabbleBytestreamSocks5Private *priv = GABBLE_BYTESTREAM_SOCKS5_GET_PRIVATE ( self); WockyStanza *iq; DEBUG ("Got CONNECT reply. SOCKS5 negotiation with proxy is done. " "Sending activation IQ"); iq = wocky_stanza_build (WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_SET, NULL, priv->proxy_jid, '(', "query", ':', NS_BYTESTREAMS, '@', "sid", priv->stream_id, '(', "activate", '$', priv->peer_jid, ')', ')', NULL); priv->socks5_state = SOCKS5_STATE_INITIATOR_ACTIVATION_SENT; /* Block reading while waiting for the activation reply */ gibber_transport_block_receiving (priv->transport, TRUE); conn_util_send_iq_async (priv->conn, iq, NULL, socks5_activation_reply_cb, tp_weak_ref_new (self, NULL, NULL)); g_object_unref (iq); } /* Process the received data and returns the number of bytes that have been * used */ static gssize socks5_handle_received_data (GabbleBytestreamSocks5 *self, GString *string) { GabbleBytestreamSocks5Private *priv = GABBLE_BYTESTREAM_SOCKS5_GET_PRIVATE (self); gchar msg[SOCKS5_CONNECT_LENGTH]; guint auth_len; guint i; gchar *domain; /* the length of the BND.ADDR field */ guint8 addr_len; gsize len; switch (priv->socks5_state) { case SOCKS5_STATE_TARGET_AUTH_REQUEST_SENT: case SOCKS5_STATE_INITIATOR_AUTH_REQUEST_SENT: /* We sent an authorization request and we are awaiting for a * response, the response is 2 bytes-long */ if (string->len < 2) return 0; if (string->str[0] != SOCKS5_VERSION || string->str[1] != SOCKS5_STATUS_OK) { DEBUG ("Authentication failed"); socks5_error (self); return -1; } /* We have been authorized, let's send a CONNECT command */ DEBUG ("Received auth reply. Sending CONNECT command"); if (priv->socks5_state == SOCKS5_STATE_TARGET_AUTH_REQUEST_SENT) { domain = compute_domain (priv->stream_id, priv->peer_jid, priv->self_full_jid); } else { /* SOCKS5_STATE_INITIATOR_AUTH_REQUEST_SENT */ domain = compute_domain (priv->stream_id, priv->self_full_jid, priv->peer_jid); } msg[0] = SOCKS5_VERSION; msg[1] = SOCKS5_CMD_CONNECT; msg[2] = SOCKS5_RESERVED; msg[3] = SOCKS5_ATYP_DOMAIN; /* Length of a hex SHA1 */ msg[4] = 40; /* Domain name: SHA-1(sid + initiator + target) */ memcpy (&msg[5], domain, 40); /* Port: 0 */ msg[45] = 0x00; msg[46] = 0x00; g_free (domain); write_to_transport (self, msg, SOCKS5_CONNECT_LENGTH, NULL); if (priv->socks5_state == SOCKS5_STATE_TARGET_AUTH_REQUEST_SENT) priv->socks5_state = SOCKS5_STATE_TARGET_CONNECT_REQUESTED; else /* SOCKS5_STATE_INITIATOR_AUTH_REQUEST_SENT */ priv->socks5_state = SOCKS5_STATE_INITIATOR_CONNECT_REQUESTED; /* Older version of Gabble (pre 0.7.22) are bugged and just send 2 * bytes as CONNECT reply. We set a timer to not wait the full reply * forever if we are connected to such Gabble. * Once timed out, the SOCKS5 negotiation will fail and Gabble * will switch to IBB as a fallback. */ start_timer (self, CONNECT_REPLY_TIMEOUT); return 2; case SOCKS5_STATE_TARGET_CONNECT_REQUESTED: case SOCKS5_STATE_INITIATOR_CONNECT_REQUESTED: /* We sent a CONNECT request and are awaiting for the response */ if (string->len < SOCKS5_MIN_LENGTH) return 0; if (string->str[0] != SOCKS5_VERSION || string->str[1] != SOCKS5_STATUS_OK || string->str[2] != SOCKS5_RESERVED) { DEBUG ("Connection refused"); socks5_error (self); return -1; } if (string->str[3] == SOCKS5_ATYP_DOMAIN) { /* correct domain. The first byte of the domain contains its * length */ addr_len = (guint8) string->str[4]; addr_len += 1; } else if (string->str[3] == 0x00) { DEBUG ("Got 0x00 as domain. Pretend it's ok to be able to interop " "with ejabberd < 2.0.2"); addr_len = 0; } else { DEBUG ("Wrong domain"); socks5_error (self); return -1; } if ((guint8) string->len < SOCKS5_MIN_LENGTH + addr_len) /* We didn't receive the full packet yet */ return 0; stop_timer (self); if ( /* first half of the port number */ string->str[4 + addr_len] != 0 || /* second half of the port number */ string->str[5 + addr_len] != 0) { DEBUG ("Connection refused"); socks5_error (self); return -1; } if (priv->socks5_state == SOCKS5_STATE_TARGET_CONNECT_REQUESTED) { domain = compute_domain (priv->stream_id, priv->peer_jid, priv->self_full_jid); } else { /* SOCKS5_STATE_INITIATOR_CONNECT_REQUESTED */ domain = compute_domain (priv->stream_id, priv->self_full_jid, priv->peer_jid); } if (addr_len > 0) { if (!check_domain (&string->str[5], addr_len - 1, domain)) { /* Thanks Pidgin... */ DEBUG ("Ignoring to interop with buggy implementations"); } } g_free (domain); if (priv->socks5_state == SOCKS5_STATE_TARGET_CONNECT_REQUESTED) target_got_connect_reply (self); else /* SOCKS5_STATE_INITIATOR_CONNECT_REQUESTED */ initiator_got_connect_reply (self); return SOCKS5_MIN_LENGTH + addr_len; case SOCKS5_STATE_INITIATOR_AWAITING_AUTH_REQUEST: /* A client connected to us and we are awaiting for the authorization * request (at least 2 bytes) */ if (string->len < 2) return 0; if (string->str[0] != SOCKS5_VERSION) { DEBUG ("Authentication failed"); socks5_error (self); return -1; } /* The auth request string is SOCKS5_VERSION + # of methods + methods */ auth_len = string->str[1] + 2; if (string->len < auth_len) /* We are still receiving some auth method */ return 0; for (i = 2; i < auth_len; i++) { if (string->str[i] == SOCKS5_AUTH_NONE) { /* Authorize the connection */ msg[0] = SOCKS5_VERSION; msg[1] = SOCKS5_AUTH_NONE; DEBUG ("Received auth request. Sending auth reply"); write_to_transport (self, msg, 2, NULL); priv->socks5_state = SOCKS5_STATE_INITIATOR_AWAITING_COMMAND; return auth_len; } } DEBUG ("Unauthenticated access is not supported by the streamhost"); socks5_error (self); return -1; case SOCKS5_STATE_INITIATOR_AWAITING_COMMAND: /* The client has been authorized and we are waiting for a command, * the only one supported by the SOCKS5 bytestreams XEP is * CONNECT with: * - ATYP = DOMAIN * - PORT = 0 * - DOMAIN = SHA1(sid + initiator + target) */ if (string->len < SOCKS5_MIN_LENGTH) return 0; addr_len = (guint8) string->str[4]; /* the first byte is the length */ addr_len += 1; if ((guint8) string->len < SOCKS5_MIN_LENGTH + addr_len) /* We didn't receive the full packet yet */ return 0; if (string->str[0] != SOCKS5_VERSION || string->str[1] != SOCKS5_CMD_CONNECT || string->str[2] != SOCKS5_RESERVED || string->str[3] != SOCKS5_ATYP_DOMAIN || /* first half of the port number */ string->str[4 + addr_len] != 0 || /* second half of the port number */ string->str[5 + addr_len] != 0) { DEBUG ("Invalid SOCKS5 connect message"); socks5_error (self); return -1; } domain = compute_domain (priv->stream_id, priv->self_full_jid, priv->peer_jid); if (!check_domain (&string->str[5], addr_len - 1, domain)) { DEBUG ("Reject connection to prevent spoofing"); socks5_close_transport (self); socks5_error (self); g_free (domain); return -1; } msg[0] = SOCKS5_VERSION; msg[1] = SOCKS5_STATUS_OK; msg[2] = SOCKS5_RESERVED; msg[3] = SOCKS5_ATYP_DOMAIN; msg[4] = SHA1_LENGTH; /* Domain name: SHA-1(sid + initiator + target) */ memcpy (&msg[5], domain, 40); /* Port: 0 */ msg[45] = 0x00; msg[46] = 0x00; g_free (domain); DEBUG ("Received CONNECT cmd. Sending CONNECT reply"); write_to_transport (self, msg, 47, NULL); priv->socks5_state = SOCKS5_STATE_CONNECTED; /* Sock5 is connected but the bytestream is not open yet as we need * to wait for the IQ reply. Stop reading until the bytestream * is open to avoid data loss. */ gibber_transport_block_receiving (priv->transport, TRUE); DEBUG ("sock5 stream connected. Stop to listen for connections"); g_assert (priv->listener != NULL); tp_clear_object (&priv->listener); return SOCKS5_MIN_LENGTH + addr_len; case SOCKS5_STATE_CONNECTED: /* We are connected, everything we receive now is data */ /* store the buffer len because if something went wront in the * data-received callback, the bytestream could be freed and so the * priv->read_buffer */ len = string->len; g_signal_emit_by_name (G_OBJECT (self), "data-received", priv->peer_handle, string); return len; case SOCKS5_STATE_ERROR: /* An error occurred and the channel will be closed in an idle * callback, so let's just throw away the data we receive */ DEBUG ("An error occurred, throwing away received data"); return string->len; case SOCKS5_STATE_TARGET_TRYING_CONNECT: case SOCKS5_STATE_INITIATOR_TRYING_CONNECT: DEBUG ("Impossible to receive data when not yet connected to the " "socket"); break; case SOCKS5_STATE_INITIATOR_OFFER_SENT: DEBUG ("Shouldn't receive data when we just sent the offer"); break; case SOCKS5_STATE_INITIATOR_ACTIVATION_SENT: DEBUG ("Shouldn't receive data before we received activation reply"); break; case SOCKS5_STATE_INVALID: DEBUG ("Invalid SOCKS5 state"); break; } g_assert_not_reached (); return string->len; } static void transport_handler (GibberTransport *transport, GibberBuffer *data, gpointer user_data) { GabbleBytestreamSocks5 *self = GABBLE_BYTESTREAM_SOCKS5 (user_data); GabbleBytestreamSocks5Private *priv = GABBLE_BYTESTREAM_SOCKS5_GET_PRIVATE (self); gssize used_bytes; g_assert (priv->read_buffer != NULL); g_string_append_len (priv->read_buffer, (const gchar *) data->data, data->length); /* If something goes wrong in socks5_handle_received_data, the bytestream * could be closed and disposed. Ref it to artificially keep this bytestream * object alive while we are in this function. */ g_object_ref (self); do { /* socks5_handle_received_data() processes the data and returns the * number of bytes that have been used. 0 means that there is not enough * data to do anything, so we just wait for more data from the socket */ used_bytes = socks5_handle_received_data (self, priv->read_buffer); if (priv->read_buffer == NULL) /* If something did wrong in socks5_handle_received_data, the * bytestream can be closed and so destroyed. */ break; g_string_erase (priv->read_buffer, 0, used_bytes); } while (used_bytes > 0 && priv->read_buffer->len > 0); g_object_unref (self); } static void socks5_connect (GabbleBytestreamSocks5 *self) { GabbleBytestreamSocks5Private *priv = GABBLE_BYTESTREAM_SOCKS5_GET_PRIVATE (self); Streamhost* streamhost; GibberTCPTransport *transport; priv->socks5_state = SOCKS5_STATE_TARGET_TRYING_CONNECT; if (priv->streamhosts != NULL) { streamhost = priv->streamhosts->data; } else { DEBUG ("No more streamhosts to try, closing"); socks5_error (self); return; } DEBUG ("Trying streamhost %s on port %d", streamhost->host, streamhost->port); transport = gibber_tcp_transport_new (); set_transport (self, GIBBER_TRANSPORT (transport)); g_object_unref (transport); /* We don't wait to wait for the TCP timeout is the host is unreachable */ start_timer (self, CONNECT_TIMEOUT); gibber_tcp_transport_connect (transport, streamhost->host, streamhost->port); /* We'll send the auth request once the transport is connected */ } /** * gabble_bytestream_socks5_add_streamhost * * Adds the streamhost as a candidate for connection. */ void gabble_bytestream_socks5_add_streamhost (GabbleBytestreamSocks5 *self, WockyNode *streamhost_node) { GabbleBytestreamSocks5Private *priv = GABBLE_BYTESTREAM_SOCKS5_GET_PRIVATE (self); const gchar *zeroconf; const gchar *jid; const gchar *host; const gchar *portstr; gint64 port; Streamhost *streamhost; g_return_if_fail (!tp_strdiff (streamhost_node->name, "streamhost")); zeroconf = wocky_node_get_attribute (streamhost_node, "zeroconf"); if (zeroconf != NULL) { /* TODO: add suppport for zeroconf */ DEBUG ("zeroconf streamhosts are not supported"); return; } jid = wocky_node_get_attribute (streamhost_node, "jid"); if (jid == NULL) { DEBUG ("streamhost doesn't contain a JID"); return; } host = wocky_node_get_attribute (streamhost_node, "host"); if (host == NULL) { DEBUG ("streamhost doesn't contain a host"); return; } portstr = wocky_node_get_attribute (streamhost_node, "port"); if (portstr == NULL) { DEBUG ("streamhost doesn't contain a port"); return; } port = g_ascii_strtoll (portstr, NULL, 10); if (port <= 0 || port > G_MAXUINT16) { DEBUG ("Invalid port: %s", portstr); return; } if (tp_strdiff (jid, priv->peer_jid) && priv->muc_contact) { DEBUG ("skip streamhost %s (%s:%"G_GINT64_FORMAT "); we don't support relay with muc contact", jid, host, port); return; } DEBUG ("streamhost with jid %s, host %s and port %"G_GINT64_FORMAT" added", jid, host, port); streamhost = streamhost_new (jid, host, port); priv->streamhosts = g_slist_append (priv->streamhosts, streamhost); } /** * gabble_bytestream_socks5_connect_to_streamhost * * Try to connect to a streamhost. */ void gabble_bytestream_socks5_connect_to_streamhost (GabbleBytestreamSocks5 *self, WockyStanza *msg) { GabbleBytestreamSocks5Private *priv = GABBLE_BYTESTREAM_SOCKS5_GET_PRIVATE (self); priv->msg_for_acknowledge_connection = g_object_ref (msg); socks5_connect (self); } /* * gabble_bytestream_socks5_send * * Implements gabble_bytestream_iface_send on GabbleBytestreamIface */ static gboolean gabble_bytestream_socks5_send (GabbleBytestreamIface *iface, guint len, const gchar *str) { GabbleBytestreamSocks5 *self = GABBLE_BYTESTREAM_SOCKS5 (iface); GabbleBytestreamSocks5Private *priv = GABBLE_BYTESTREAM_SOCKS5_GET_PRIVATE (self); GError *error = NULL; if (priv->bytestream_state != GABBLE_BYTESTREAM_STATE_OPEN) { DEBUG ("can't send data through a not open bytestream (state: %d)", priv->bytestream_state); return FALSE; } /* if something goes wrong during the sending, the bytestream could be * closed and so disposed by the bytestream factory. Ref it to keep it * artifically alive if such case happen. */ g_object_ref (self); if (!write_to_transport (self, str, len, &error)) { DEBUG ("sending failed: %s", error->message); g_error_free (error); gabble_bytestream_iface_close (GABBLE_BYTESTREAM_IFACE (self), NULL); g_object_unref (self); return FALSE; } /* If something wennt wrong during the writting, the transport has been closed * and so set to NULL. */ if (priv->transport == NULL) { g_object_unref (self); return FALSE; } /* At this point we know that the bytestream has not been closed */ g_object_unref (self); if (!gibber_transport_buffer_is_empty (priv->transport)) { /* We >don't want to send more data while the buffer isn't empty */ change_write_blocked_state (self, TRUE); } return TRUE; } /* * gabble_bytestream_socks5_accept * * Implements gabble_bytestream_iface_accept on GabbleBytestreamIface */ static void gabble_bytestream_socks5_accept (GabbleBytestreamIface *iface, GabbleBytestreamAugmentSiAcceptReply func, gpointer user_data) { GabbleBytestreamSocks5 *self = GABBLE_BYTESTREAM_SOCKS5 (iface); GabbleBytestreamSocks5Private *priv = GABBLE_BYTESTREAM_SOCKS5_GET_PRIVATE (self); WockyStanza *msg; WockyNode *si; if (priv->bytestream_state != GABBLE_BYTESTREAM_STATE_LOCAL_PENDING) { /* The stream was previoulsy or automatically accepted */ return; } msg = gabble_bytestream_factory_make_accept_iq (priv->peer_jid, priv->stream_init_id, NS_BYTESTREAMS); si = wocky_node_get_child_ns ( wocky_stanza_get_top_node (msg), "si", NS_SI); g_assert (si != NULL); if (func != NULL) { /* let the caller add his profile specific data */ func (si, user_data); } if (_gabble_connection_send (priv->conn, msg, NULL)) { DEBUG ("stream %s with %s is now accepted", priv->stream_id, priv->peer_jid); g_object_set (self, "state", GABBLE_BYTESTREAM_STATE_ACCEPTED, NULL); } g_object_unref (msg); } static void gabble_bytestream_socks5_decline (GabbleBytestreamSocks5 *self, GError *error) { GabbleBytestreamSocks5Private *priv = GABBLE_BYTESTREAM_SOCKS5_GET_PRIVATE (self); WockyStanza *msg; g_return_if_fail (priv->bytestream_state == GABBLE_BYTESTREAM_STATE_LOCAL_PENDING); msg = wocky_stanza_build (WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_ERROR, NULL, priv->peer_jid, '@', "id", priv->stream_init_id, NULL); if (error != NULL) { wocky_stanza_error_to_node (error, wocky_stanza_get_top_node (msg)); } else { GError fallback = { WOCKY_XMPP_ERROR, WOCKY_XMPP_ERROR_FORBIDDEN, "Offer Declined" }; wocky_stanza_error_to_node (&fallback, wocky_stanza_get_top_node (msg)); } _gabble_connection_send (priv->conn, msg, NULL); g_object_unref (msg); g_object_set (self, "state", GABBLE_BYTESTREAM_STATE_CLOSED, NULL); } /* * gabble_bytestream_socks5_close * * Implements gabble_bytestream_iface_close on GabbleBytestreamIface */ static void gabble_bytestream_socks5_close (GabbleBytestreamIface *iface, GError *error) { GabbleBytestreamSocks5 *self = GABBLE_BYTESTREAM_SOCKS5 (iface); GabbleBytestreamSocks5Private *priv = GABBLE_BYTESTREAM_SOCKS5_GET_PRIVATE (self); if (priv->bytestream_state == GABBLE_BYTESTREAM_STATE_CLOSED) /* bytestream already closed, do nothing */ return; if (priv->bytestream_state == GABBLE_BYTESTREAM_STATE_LOCAL_PENDING) { /* Stream was created using SI so we decline the request */ gabble_bytestream_socks5_decline (self, error); } else { g_object_set (self, "state", GABBLE_BYTESTREAM_STATE_CLOSING, NULL); if (priv->transport != NULL && !gibber_transport_buffer_is_empty (priv->transport)) { DEBUG ("Wait transport buffer is empty before close the bytestream"); } else { DEBUG ("Transport buffer is empty, we can close the bytestream"); bytestream_closed (self); } } } static void initiator_connected_to_proxy (GabbleBytestreamSocks5 *self) { GabbleBytestreamSocks5Private *priv = GABBLE_BYTESTREAM_SOCKS5_GET_PRIVATE ( self); GSList *proxies, *l; GabbleSocks5Proxy *proxy = NULL; GibberTCPTransport *transport; proxies = gabble_bytestream_factory_get_socks5_proxies ( priv->conn->bytestream_factory); for (l = proxies; l != NULL; l = g_slist_next (l)) { proxy = (GabbleSocks5Proxy *) l->data; if (!tp_strdiff (proxy->jid, priv->proxy_jid)) break; proxy = NULL; } g_slist_free (proxies); if (proxy == NULL) { DEBUG ("Unknown proxy: %s. Closing the bytestream", priv->proxy_jid); g_signal_emit_by_name (self, "connection-error"); g_object_set (self, "state", GABBLE_BYTESTREAM_STATE_CLOSED, NULL); return; } DEBUG ("connect to proxy: %s (%s:%d)", proxy->jid, proxy->host, proxy->port); priv->socks5_state = SOCKS5_STATE_INITIATOR_TRYING_CONNECT; transport = gibber_tcp_transport_new (); set_transport (self, GIBBER_TRANSPORT (transport)); g_object_unref (transport); gibber_tcp_transport_connect (transport, proxy->host, proxy->port); } static void socks5_init_reply_cb ( GObject *source, GAsyncResult *result, gpointer user_data) { TpWeakRef *weak_ref = user_data; GabbleBytestreamSocks5 *self = tp_weak_ref_dup_object (weak_ref); GabbleBytestreamSocks5Private *priv; WockyStanza *reply_msg = NULL; tp_weak_ref_destroy (weak_ref); if (self == NULL) return; priv = self->priv; if (conn_util_send_iq_finish (GABBLE_CONNECTION (source), result, &reply_msg, NULL)) { WockyNode *query, *streamhost = NULL; const gchar *jid; query = wocky_node_get_child_ns ( wocky_stanza_get_top_node (reply_msg), "query", NS_BYTESTREAMS); if (query != NULL) streamhost = wocky_node_get_child (query, "streamhost-used"); if (streamhost == NULL) { DEBUG ("no streamhost-used has been defined. Closing the bytestream"); goto socks5_init_error; } jid = wocky_node_get_attribute (streamhost, "jid"); if (jid == NULL) { DEBUG ("no jid attribute in streamhost. Closing the bytestream"); goto socks5_init_error; } if (tp_strdiff (jid, priv->self_full_jid)) { DEBUG ("Target is connected to proxy: %s", jid); if (priv->socks5_state != SOCKS5_STATE_INITIATOR_OFFER_SENT) { DEBUG ("We are already in the negotiation process (state: %u). " "Closing the bytestream", priv->socks5_state); goto socks5_init_error; } priv->proxy_jid = g_strdup (jid); initiator_connected_to_proxy (self); goto out; } /* No proxy used */ DEBUG ("Target is connected to us"); if (priv->socks5_state != SOCKS5_STATE_CONNECTED) { DEBUG ("Target claims that the bytestream is open but SOCKS5 is not " "connected (state: %u). Closing the bytestream", priv->socks5_state); goto socks5_init_error; } /* yeah, stream initiated */ DEBUG ("Socks5 stream initiated using stream: %s", jid); g_object_set (self, "state", GABBLE_BYTESTREAM_STATE_OPEN, NULL); /* We can read data from the sock5 socket now */ gibber_transport_block_receiving (priv->transport, FALSE); goto out; } socks5_init_error: DEBUG ("error during Socks5 initiation"); g_signal_emit_by_name (self, "connection-error"); g_object_set (self, "state", GABBLE_BYTESTREAM_STATE_CLOSED, NULL); out: g_clear_object (&reply_msg); g_object_unref (self); } #ifdef G_OS_WIN32 static GSList * get_local_interfaces_ips (void) { gint sockfd; INTERFACE_INFO *iflist = NULL; gsize size = 0; int ret; int error; gsize bytes; gsize num; gsize i; struct sockaddr_in *sa; GSList *ips = NULL; /* FIXME: add IPv6 addresses */ if ((sockfd = socket (AF_INET, SOCK_DGRAM, IPPROTO_IP)) == (int) INVALID_SOCKET) { DEBUG ("Cannot open socket to retrieve interface list"); return NULL; } /* Loop and get each interface the system has, one by one... */ do { size += sizeof (INTERFACE_INFO); /* realloc buffer size until no overflow occurs */ if (NULL == (iflist = realloc (iflist, size))) { DEBUG ("Out of memory while allocation interface configuration" " structure"); closesocket (sockfd); return NULL; } ret = WSAIoctl (sockfd, SIO_GET_INTERFACE_LIST, NULL, 0, iflist, size, (LPDWORD) &bytes, NULL, NULL); error = WSAGetLastError (); if (ret == SOCKET_ERROR && error != WSAEFAULT) { DEBUG ("Cannot retrieve interface list"); closesocket (sockfd); free (iflist); return NULL; } } while (ret == SOCKET_ERROR); num = bytes / sizeof (INTERFACE_INFO); /* Loop throught the interface list and get the IP address of each IF */ for (i = 0; i < num; i++) { /* no ip address from interface that is down */ if ((iflist[i].iiFlags & IFF_UP) == 0) continue; if ((iflist[i].iiFlags & IFF_LOOPBACK) == IFF_LOOPBACK) { DEBUG ("Ignoring loopback interface"); continue; } sa = (struct sockaddr_in *) &(iflist[i].iiAddress); ips = g_slist_prepend (ips, g_strdup (inet_ntoa (sa->sin_addr))); DEBUG ("IP Address: %s", inet_ntoa (sa->sin_addr)); } closesocket (sockfd); free (iflist); return ips; } #else /* get_local_interfaces_ips original code from Farsight 2 (function * fs_interfaces_get_local_ips in /gst-libs/gst/farsight/fs-interfaces.c). * Copyright (C) 2006 Youness Alaoui <kakaroto@kakaroto.homelinux.net> * Copyright (C) 2007 Collabora */ #ifdef HAVE_GETIFADDRS static GSList * get_local_interfaces_ips (void) { struct ifaddrs *ifa, *results; GSList *ips = NULL; if (getifaddrs (&results) < 0) return NULL; /* Loop through the interface list and get the IP address of each IF */ for (ifa = results; ifa; ifa = ifa->ifa_next) { char straddr[INET6_ADDRSTRLEN]; /* no ip address from interface that is down */ if ((ifa->ifa_flags & IFF_UP) == 0) continue; if (ifa->ifa_addr == NULL) continue; if ((ifa->ifa_flags & IFF_LOOPBACK) == IFF_LOOPBACK) { DEBUG ("Ignoring loopback interface"); continue; } if (ifa->ifa_addr->sa_family == AF_INET) { struct sockaddr_in *sa = (struct sockaddr_in *) ifa->ifa_addr; inet_ntop (AF_INET, &sa->sin_addr, straddr, sizeof (straddr)); /* Add IPv4 addresses to the end of the list */ ips = g_slist_append (ips, g_strdup (straddr)); } else if (ifa->ifa_addr->sa_family == AF_INET6) { struct sockaddr_in6 *sa6 = (struct sockaddr_in6 *) ifa->ifa_addr; inet_ntop (AF_INET6, &sa6->sin6_addr, straddr, sizeof (straddr)); if (IN6_IS_ADDR_LINKLOCAL (&sa6->sin6_addr)) { DEBUG ("Ignoring link-local address: %s", straddr); continue; } /* Add IPv6 addresss to the begin of the list */ ips = g_slist_prepend (ips, g_strdup (straddr)); } else { continue; } DEBUG ("Interface: %s", ifa->ifa_name); DEBUG ("IP Address: %s", straddr); } freeifaddrs (results); return ips; } #else /* ! HAVE_GETIFADDRS */ static GSList * get_local_interfaces_ips (void) { gint sockfd; gint size = 0; struct ifreq *ifr; struct ifconf ifc; struct sockaddr_in *sa; GSList *ips = NULL; /* FIXME: add IPv6 addresses */ if ((sockfd = socket (AF_INET, SOCK_DGRAM, IPPROTO_IP)) < 0) { DEBUG ("Cannot open socket to retreive interface list"); return NULL; } ifc.ifc_len = 0; ifc.ifc_req = NULL; /* Loop and get each interface the system has, one by one... */ do { size += sizeof (struct ifreq); /* realloc buffer size until no overflow occurs */ if (NULL == (ifc.ifc_req = realloc (ifc.ifc_req, size))) { DEBUG ("Out of memory while allocation interface configuration" " structure"); close (sockfd); return NULL; } ifc.ifc_len = size; if (ioctl (sockfd, SIOCGIFCONF, &ifc)) { DEBUG ("ioctl SIOCFIFCONF"); close (sockfd); free (ifc.ifc_req); return NULL; } } while (size <= ifc.ifc_len); /* Loop throught the interface list and get the IP address of each IF */ for (ifr = ifc.ifc_req; (gchar *) ifr < (gchar *) ifc.ifc_req + ifc.ifc_len; ++ifr) { if (ioctl (sockfd, SIOCGIFFLAGS, ifr)) { DEBUG ("Unable to get IP information for interface %s. Skipping...", ifr->ifr_name); continue; /* failed to get flags, skip it */ } sa = (struct sockaddr_in *) &ifr->ifr_addr; DEBUG ("Interface: %s", ifr->ifr_name); DEBUG ("IP Address: %s", inet_ntoa (sa->sin_addr)); if ((ifr->ifr_flags & IFF_LOOPBACK) == IFF_LOOPBACK) { DEBUG ("Ignoring loopback interface"); } else { ips = g_slist_prepend (ips, g_strdup (inet_ntoa (sa->sin_addr))); } } close (sockfd); free (ifc.ifc_req); return ips; } #endif /* ! HAVE_GETIFADDRS */ #endif /* ! G_OS_WIN32 */ static void new_connection_cb (GibberListener *listener, GibberTransport *transport, struct sockaddr *addr, guint size, gpointer user_data) { GabbleBytestreamSocks5 *self = GABBLE_BYTESTREAM_SOCKS5 (user_data); GabbleBytestreamSocks5Private *priv = GABBLE_BYTESTREAM_SOCKS5_GET_PRIVATE (self); DEBUG ("New connection..."); priv->socks5_state = SOCKS5_STATE_INITIATOR_AWAITING_AUTH_REQUEST; set_transport (self, transport); } /* * Consumes @ips. */ static void send_streamhosts ( GabbleBytestreamSocks5 *self, GSList *ips, gint port_num) { GabbleBytestreamSocks5Private *priv = self->priv; gchar *port; WockyStanza *msg; WockyNode *query_node; port = g_strdup_printf ("%d", port_num); msg = wocky_stanza_build (WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_SET, NULL, priv->peer_jid, '(', "query", ':', NS_BYTESTREAMS, '@', "sid", priv->stream_id, '@', "mode", "tcp", '*', &query_node, ')', NULL); for (; port_num != 0 && ips != NULL; ips = g_slist_delete_link (ips, ips)) { WockyNode *node = wocky_node_add_child (query_node, "streamhost"); wocky_node_set_attributes (node, "jid", priv->self_full_jid, "host", ips->data, "port", port, NULL); g_free (ips->data); } g_slist_free (ips); g_free (port); if (!priv->muc_contact) { GSList *proxies, *l; proxies = gabble_bytestream_factory_get_socks5_proxies ( priv->conn->bytestream_factory); for (l = proxies; l != NULL; l = g_slist_next (l)) { WockyNode *node = wocky_node_add_child (query_node, "streamhost"); gchar *portstr; GabbleSocks5Proxy *proxy = (GabbleSocks5Proxy *) l->data; portstr = g_strdup_printf ("%d", proxy->port); wocky_node_set_attributes (node, "jid", proxy->jid, "host", proxy->host, "port", portstr, NULL); g_free (portstr); } g_slist_free (proxies); } else { DEBUG ("don't propose to use SOCKS5 relays as we are offering bytestream " "to a muc contact"); } priv->socks5_state = SOCKS5_STATE_INITIATOR_OFFER_SENT; conn_util_send_iq_async (priv->conn, msg, NULL, socks5_init_reply_cb, tp_weak_ref_new (self, NULL, NULL)); g_object_unref (msg); } /* * gabble_bytestream_socks5_initiate * * Implements gabble_bytestream_iface_initiate on GabbleBytestreamIface */ static gboolean gabble_bytestream_socks5_initiate (GabbleBytestreamIface *iface) { GabbleBytestreamSocks5 *self = GABBLE_BYTESTREAM_SOCKS5 (iface); GabbleBytestreamSocks5Private *priv = GABBLE_BYTESTREAM_SOCKS5_GET_PRIVATE (self); GSList *ips; guint port_num = 0; if (priv->bytestream_state != GABBLE_BYTESTREAM_STATE_INITIATING) { DEBUG ("bytestream is not is the initiating state (state %d)", priv->bytestream_state); return FALSE; } ips = get_local_interfaces_ips (); if (ips == NULL) { DEBUG ("Can't get IP addresses; will send empty offer."); } else { g_assert (priv->listener == NULL); priv->listener = gibber_listener_new (); g_signal_connect (priv->listener, "new-connection", G_CALLBACK (new_connection_cb), self); if (!gibber_listener_listen_tcp (priv->listener, 0, NULL)) { DEBUG ("can't listen for incoming connection; will send empty offer."); g_slist_foreach (ips, (GFunc) g_free, NULL); g_slist_free (ips); ips = NULL; } port_num = gibber_listener_get_port (priv->listener); } send_streamhosts (self, ips, port_num); return TRUE; } static void gabble_bytestream_socks5_block_reading (GabbleBytestreamIface *iface, gboolean block) { GabbleBytestreamSocks5 *self = GABBLE_BYTESTREAM_SOCKS5 (iface); GabbleBytestreamSocks5Private *priv = GABBLE_BYTESTREAM_SOCKS5_GET_PRIVATE (self); if (priv->read_blocked == block) return; priv->read_blocked = block; if (priv->transport != NULL) gibber_transport_block_receiving (priv->transport, block); } static void bytestream_iface_init (gpointer g_iface, gpointer iface_data) { GabbleBytestreamIfaceClass *klass = (GabbleBytestreamIfaceClass *) g_iface; klass->initiate = gabble_bytestream_socks5_initiate; klass->send = gabble_bytestream_socks5_send; klass->close = gabble_bytestream_socks5_close; klass->accept = gabble_bytestream_socks5_accept; klass->block_reading = gabble_bytestream_socks5_block_reading; } ����������������������������������telepathy-gabble-0.18.2/src/bytestream-socks5.h�����������������������������������������������������0000644�0001750�0001750�00000005075�12200204333�021335� 0����������������������������������������������������������������������������������������������������ustar�00smcv����������������������������smcv����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * bytestream-socks5.h - Header for GabbleBytestreamSocks5 * Copyright (C) 2007-2008 Collabora Ltd. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 __GABBLE_BYTESTREAM_SOCKS5_H__ #define __GABBLE_BYTESTREAM_SOCKS5_H__ #include <stdlib.h> #include <glib-object.h> #include <wocky/wocky.h> #include <telepathy-glib/telepathy-glib.h> G_BEGIN_DECLS typedef struct _GabbleBytestreamSocks5 GabbleBytestreamSocks5; typedef struct _GabbleBytestreamSocks5Class GabbleBytestreamSocks5Class; typedef struct _GabbleBytestreamSocks5Private GabbleBytestreamSocks5Private; struct _GabbleBytestreamSocks5Class { GObjectClass parent_class; }; struct _GabbleBytestreamSocks5 { GObject parent; GabbleBytestreamSocks5Private *priv; }; GType gabble_bytestream_socks5_get_type (void); /* TYPE MACROS */ #define GABBLE_TYPE_BYTESTREAM_SOCKS5 \ (gabble_bytestream_socks5_get_type ()) #define GABBLE_BYTESTREAM_SOCKS5(obj) \ (G_TYPE_CHECK_INSTANCE_CAST((obj), GABBLE_TYPE_BYTESTREAM_SOCKS5,\ GabbleBytestreamSocks5)) #define GABBLE_BYTESTREAM_SOCKS5_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST((klass), GABBLE_TYPE_BYTESTREAM_SOCKS5,\ GabbleBytestreamSocks5Class)) #define GABBLE_IS_BYTESTREAM_SOCKS5(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE((obj), GABBLE_TYPE_BYTESTREAM_SOCKS5)) #define GABBLE_IS_BYTESTREAM_SOCKS5_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE((klass), GABBLE_TYPE_BYTESTREAM_SOCKS5)) #define GABBLE_BYTESTREAM_SOCKS5_GET_CLASS(obj) \ (G_TYPE_INSTANCE_GET_CLASS ((obj), GABBLE_TYPE_BYTESTREAM_SOCKS5,\ GabbleBytestreamSocks5Class)) void gabble_bytestream_socks5_add_streamhost (GabbleBytestreamSocks5 *socks5, WockyNode *streamhost_node); void gabble_bytestream_socks5_connect_to_streamhost ( GabbleBytestreamSocks5 *socks5, WockyStanza *msg); G_END_DECLS #endif /* #ifndef __GABBLE_BYTESTREAM_SOCKS5_H__ */ �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������telepathy-gabble-0.18.2/src/bytestream-multiple.c���������������������������������������������������0000644�0001750�0001750�00000051027�12200204333�021752� 0����������������������������������������������������������������������������������������������������ustar�00smcv����������������������������smcv����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * bytestream-multiple.c - Source for GabbleBytestreamMultiple * Copyright (C) 2007-2008 Collabora Ltd. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 "config.h" #include "bytestream-multiple.h" #include <dbus/dbus-glib.h> #include <dbus/dbus-glib-lowlevel.h> #include <telepathy-glib/telepathy-glib.h> #include <telepathy-glib/telepathy-glib-dbus.h> #define DEBUG_FLAG GABBLE_DEBUG_BYTESTREAM #include "bytestream-factory.h" #include "bytestream-iface.h" #include "connection.h" #include "debug.h" #include "disco.h" #include "gabble-signals-marshal.h" #include "namespaces.h" #include "util.h" static void bytestream_iface_init (gpointer g_iface, gpointer iface_data); G_DEFINE_TYPE_WITH_CODE (GabbleBytestreamMultiple, gabble_bytestream_multiple, G_TYPE_OBJECT, G_IMPLEMENT_INTERFACE (GABBLE_TYPE_BYTESTREAM_IFACE, bytestream_iface_init)); /* properties */ enum { PROP_CONNECTION = 1, PROP_PEER_HANDLE, PROP_PEER_HANDLE_TYPE, PROP_STREAM_ID, PROP_STREAM_INIT_ID, PROP_PEER_JID, PROP_PEER_RESOURCE, PROP_STATE, PROP_PROTOCOL, PROP_FACTORY, PROP_SELF_JID, LAST_PROPERTY }; struct _GabbleBytestreamMultiplePrivate { GabbleConnection *conn; TpHandle peer_handle; gchar *stream_id; gchar *stream_init_id; gchar *peer_resource; GabbleBytestreamState state; gchar *peer_jid; GabbleBytestreamFactory *factory; gchar *self_full_jid; /* List of (gchar *) containing the NS of a stream method */ GList *fallback_stream_methods; GabbleBytestreamIface *active_bytestream; gboolean read_blocked; gboolean dispose_has_run; }; #define GABBLE_BYTESTREAM_MULTIPLE_GET_PRIVATE(obj) ((obj)->priv) static void bytestream_activate_next (GabbleBytestreamMultiple *self); static void gabble_bytestream_multiple_init (GabbleBytestreamMultiple *self) { GabbleBytestreamMultiplePrivate *priv = G_TYPE_INSTANCE_GET_PRIVATE (self, GABBLE_TYPE_BYTESTREAM_MULTIPLE, GabbleBytestreamMultiplePrivate); self->priv = priv; } static void gabble_bytestream_multiple_dispose (GObject *object) { GabbleBytestreamMultiple *self = GABBLE_BYTESTREAM_MULTIPLE (object); GabbleBytestreamMultiplePrivate *priv = GABBLE_BYTESTREAM_MULTIPLE_GET_PRIVATE (self); if (priv->dispose_has_run) return; priv->dispose_has_run = TRUE; if (priv->state != GABBLE_BYTESTREAM_STATE_CLOSED) { gabble_bytestream_iface_close (GABBLE_BYTESTREAM_IFACE (self), NULL); } G_OBJECT_CLASS (gabble_bytestream_multiple_parent_class)->dispose (object); } static void gabble_bytestream_multiple_finalize (GObject *object) { GabbleBytestreamMultiple *self = GABBLE_BYTESTREAM_MULTIPLE (object); GabbleBytestreamMultiplePrivate *priv = GABBLE_BYTESTREAM_MULTIPLE_GET_PRIVATE (self); GList *l; for (l = priv->fallback_stream_methods; l != NULL; l = g_list_next (l)) g_free (l->data); g_list_free (priv->fallback_stream_methods); g_free (priv->stream_id); g_free (priv->stream_init_id); g_free (priv->peer_resource); g_free (priv->peer_jid); g_free (priv->self_full_jid); G_OBJECT_CLASS (gabble_bytestream_multiple_parent_class)->finalize (object); } static void gabble_bytestream_multiple_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { GabbleBytestreamMultiple *self = GABBLE_BYTESTREAM_MULTIPLE (object); GabbleBytestreamMultiplePrivate *priv = GABBLE_BYTESTREAM_MULTIPLE_GET_PRIVATE (self); switch (property_id) { case PROP_CONNECTION: g_value_set_object (value, priv->conn); break; case PROP_PEER_HANDLE: g_value_set_uint (value, priv->peer_handle); break; case PROP_PEER_HANDLE_TYPE: g_value_set_uint (value, TP_HANDLE_TYPE_CONTACT); break; case PROP_STREAM_ID: g_value_set_string (value, priv->stream_id); break; case PROP_STREAM_INIT_ID: g_value_set_string (value, priv->stream_init_id); break; case PROP_PEER_RESOURCE: g_value_set_string (value, priv->peer_resource); break; case PROP_PEER_JID: g_value_set_string (value, priv->peer_jid); break; case PROP_STATE: g_value_set_uint (value, priv->state); break; case PROP_PROTOCOL: g_value_set_string (value, NS_BYTESTREAMS); break; case PROP_FACTORY: g_value_set_object (value, priv->factory); break; case PROP_SELF_JID: g_value_set_string (value, priv->self_full_jid); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void gabble_bytestream_multiple_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { GabbleBytestreamMultiple *self = GABBLE_BYTESTREAM_MULTIPLE (object); GabbleBytestreamMultiplePrivate *priv = GABBLE_BYTESTREAM_MULTIPLE_GET_PRIVATE (self); switch (property_id) { case PROP_CONNECTION: priv->conn = g_value_get_object (value); break; case PROP_PEER_HANDLE: priv->peer_handle = g_value_get_uint (value); break; case PROP_STREAM_ID: g_free (priv->stream_id); priv->stream_id = g_value_dup_string (value); break; case PROP_STREAM_INIT_ID: g_free (priv->stream_init_id); priv->stream_init_id = g_value_dup_string (value); break; case PROP_PEER_RESOURCE: g_free (priv->peer_resource); priv->peer_resource = g_value_dup_string (value); break; case PROP_STATE: if (priv->state != g_value_get_uint (value)) { priv->state = g_value_get_uint (value); g_signal_emit_by_name (object, "state-changed", priv->state); } break; case PROP_FACTORY: priv->factory = g_value_get_object (value); break; case PROP_SELF_JID: g_free (priv->self_full_jid); priv->self_full_jid = g_value_dup_string (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static GObject * gabble_bytestream_multiple_constructor (GType type, guint n_props, GObjectConstructParam *props) { GObject *obj; GabbleBytestreamMultiplePrivate *priv; TpHandleRepoIface *contact_repo; const gchar *jid; obj = G_OBJECT_CLASS (gabble_bytestream_multiple_parent_class)-> constructor (type, n_props, props); priv = GABBLE_BYTESTREAM_MULTIPLE_GET_PRIVATE ( GABBLE_BYTESTREAM_MULTIPLE (obj)); g_assert (priv->conn != NULL); g_assert (priv->peer_handle != 0); g_assert (priv->stream_id != NULL); contact_repo = tp_base_connection_get_handles ( (TpBaseConnection *) priv->conn, TP_HANDLE_TYPE_CONTACT); jid = tp_handle_inspect (contact_repo, priv->peer_handle); if (priv->peer_resource != NULL) priv->peer_jid = g_strdup_printf ("%s/%s", jid, priv->peer_resource); else priv->peer_jid = g_strdup (jid); g_assert (priv->self_full_jid != NULL); return obj; } static void gabble_bytestream_multiple_class_init ( GabbleBytestreamMultipleClass *gabble_bytestream_multiple_class) { GObjectClass *object_class = G_OBJECT_CLASS (gabble_bytestream_multiple_class); GParamSpec *param_spec; g_type_class_add_private (gabble_bytestream_multiple_class, sizeof (GabbleBytestreamMultiplePrivate)); object_class->dispose = gabble_bytestream_multiple_dispose; object_class->finalize = gabble_bytestream_multiple_finalize; object_class->get_property = gabble_bytestream_multiple_get_property; object_class->set_property = gabble_bytestream_multiple_set_property; object_class->constructor = gabble_bytestream_multiple_constructor; g_object_class_override_property (object_class, PROP_CONNECTION, "connection"); g_object_class_override_property (object_class, PROP_PEER_HANDLE, "peer-handle"); g_object_class_override_property (object_class, PROP_PEER_HANDLE_TYPE, "peer-handle-type"); g_object_class_override_property (object_class, PROP_STREAM_ID, "stream-id"); g_object_class_override_property (object_class, PROP_PEER_JID, "peer-jid"); g_object_class_override_property (object_class, PROP_STATE, "state"); g_object_class_override_property (object_class, PROP_PROTOCOL, "protocol"); param_spec = g_param_spec_string ( "peer-resource", "Peer resource", "the resource used by the remote peer during the SI, if any", NULL, G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, PROP_PEER_RESOURCE, param_spec); param_spec = g_param_spec_string ( "stream-init-id", "stream init ID", "the iq ID of the SI request, if any", NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, PROP_STREAM_INIT_ID, param_spec); param_spec = g_param_spec_object ( "factory", "Factory", "The GabbleBytestreamFactory that created the stream", GABBLE_TYPE_BYTESTREAM_FACTORY, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, PROP_FACTORY, param_spec); param_spec = g_param_spec_string ( "self-jid", "Our self jid", "Either a contact full jid or a muc jid", NULL, G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, PROP_SELF_JID, param_spec); } /* * gabble_bytestream_multiple_send * * Implements gabble_bytestream_iface_send on GabbleBytestreamIface */ static gboolean gabble_bytestream_multiple_send (GabbleBytestreamIface *iface, guint len, const gchar *str) { GabbleBytestreamMultiple *self = GABBLE_BYTESTREAM_MULTIPLE (iface); GabbleBytestreamMultiplePrivate *priv = GABBLE_BYTESTREAM_MULTIPLE_GET_PRIVATE (self); if (priv->state != GABBLE_BYTESTREAM_STATE_OPEN) { DEBUG ("can't send data through a not open bytestream (state: %d)", priv->state); return FALSE; } g_assert (priv->active_bytestream != NULL); return gabble_bytestream_iface_send (priv->active_bytestream, len, str); } /* * gabble_bytestream_multiple_accept * * Implements gabble_bytestream_iface_accept on GabbleBytestreamIface */ static void gabble_bytestream_multiple_accept (GabbleBytestreamIface *iface, GabbleBytestreamAugmentSiAcceptReply func, gpointer user_data) { GabbleBytestreamMultiple *self = GABBLE_BYTESTREAM_MULTIPLE (iface); GabbleBytestreamMultiplePrivate *priv = GABBLE_BYTESTREAM_MULTIPLE_GET_PRIVATE (self); WockyStanza *msg; WockyNode *si; GList *all_methods; gchar *current_method; /* We cannot just call the accept method of the active bytestream because * the result stanza is different if we are using si-multiple */ if (priv->state != GABBLE_BYTESTREAM_STATE_LOCAL_PENDING) { /* The stream was previoulsy or automatically accepted */ return; } g_return_if_fail (priv->active_bytestream != NULL); all_methods = g_list_copy (priv->fallback_stream_methods); g_object_get (priv->active_bytestream, "protocol", ¤t_method, NULL); all_methods = g_list_prepend (all_methods, current_method); msg = gabble_bytestream_factory_make_multi_accept_iq (priv->peer_jid, priv->stream_init_id, all_methods); g_free (current_method); g_list_free (all_methods); si = wocky_node_get_child_ns ( wocky_stanza_get_top_node (msg), "si", NS_SI); g_assert (si != NULL); if (func != NULL) { /* let the caller add his profile specific data */ func (si, user_data); } if (_gabble_connection_send (priv->conn, msg, NULL)) { DEBUG ("stream %s with %s is now accepted", priv->stream_id, priv->peer_jid); g_object_set (priv->active_bytestream, "state", GABBLE_BYTESTREAM_STATE_ACCEPTED, NULL); } g_object_unref (msg); } /* * gabble_bytestream_multiple_close * * Implements gabble_bytestream_iface_close on GabbleBytestreamIface */ static void gabble_bytestream_multiple_close (GabbleBytestreamIface *iface, GError *error) { GabbleBytestreamMultiple *self = GABBLE_BYTESTREAM_MULTIPLE (iface); GabbleBytestreamMultiplePrivate *priv = GABBLE_BYTESTREAM_MULTIPLE_GET_PRIVATE (self); if (priv->state == GABBLE_BYTESTREAM_STATE_CLOSED) /* bytestream already closed, do nothing */ return; if (priv->active_bytestream != NULL) gabble_bytestream_iface_close (priv->active_bytestream, error); else /* It can happen if the bytestream is still empty, i.e. not stream * methods have been added yet */ g_object_set (self, "state", GABBLE_BYTESTREAM_STATE_CLOSED, NULL); } /* * gabble_bytestream_multiple_initiate * * Implements gabble_bytestream_iface_initiate on GabbleBytestreamIface */ static gboolean gabble_bytestream_multiple_initiate (GabbleBytestreamIface *iface) { GabbleBytestreamMultiple *self = GABBLE_BYTESTREAM_MULTIPLE (iface); GabbleBytestreamMultiplePrivate *priv = GABBLE_BYTESTREAM_MULTIPLE_GET_PRIVATE (self); if (priv->state != GABBLE_BYTESTREAM_STATE_INITIATING) { DEBUG ("bytestream is not is the initiating state (state %d)", priv->state); return FALSE; } if (priv->active_bytestream == NULL) { DEBUG ("no bytestreams to initiate"); return FALSE; } return gabble_bytestream_iface_initiate (priv->active_bytestream); } static void bytestream_data_received_cb (GabbleBytestreamIface *bytestream, TpHandle sender, GString *str, gpointer user_data) { GabbleBytestreamMultiple *self = GABBLE_BYTESTREAM_MULTIPLE (user_data); /* Just forward the data */ g_signal_emit_by_name (G_OBJECT (self), "data-received", sender, str); } static void bytestream_state_changed_cb (GabbleBytestreamIface *bytestream, GabbleBytestreamState state, gpointer user_data) { GabbleBytestreamMultiple *self = GABBLE_BYTESTREAM_MULTIPLE (user_data); /* When there is a connection error the state of the sub-bytestream becomes * CLOSED. There is no risk to receive a notification for this kind of * change because the signal handler is previously disconnected in * bytestream_connection_error_cb */ g_object_set (self, "state", state, NULL); } static void bytestream_write_blocked_cb (GabbleBytestreamIface *bytestream, gboolean blocked, gpointer self) { /* Forward signal */ g_signal_emit_by_name (G_OBJECT (self), "write-blocked", blocked); } static void bytestream_connection_error_cb (GabbleBytestreamIface *failed, gpointer user_data) { GabbleBytestreamMultiple *self = GABBLE_BYTESTREAM_MULTIPLE (user_data); GabbleBytestreamMultiplePrivate *priv = GABBLE_BYTESTREAM_MULTIPLE_GET_PRIVATE (self); g_assert (failed == priv->active_bytestream); /* the error signal is only emitted when intiating the bytestream */ g_assert (priv->state == GABBLE_BYTESTREAM_STATE_INITIATING || priv->state == GABBLE_BYTESTREAM_STATE_ACCEPTED); g_signal_handlers_disconnect_by_func (failed, bytestream_connection_error_cb, self); g_signal_handlers_disconnect_by_func (failed, bytestream_data_received_cb, self); g_signal_handlers_disconnect_by_func (failed, bytestream_state_changed_cb, self); g_signal_handlers_disconnect_by_func (failed, bytestream_write_blocked_cb, self); /* We don't have to unref it because the reference is kept by the * factory */ priv->active_bytestream = NULL; if (priv->fallback_stream_methods == NULL) return; DEBUG ("Trying alternative streaming method"); bytestream_activate_next (self); if (priv->state == GABBLE_BYTESTREAM_STATE_INITIATING) /* The previous bytestream failed when initiating it, so now we have to * initiate the new one */ gabble_bytestream_iface_initiate (priv->active_bytestream); } static void bytestream_activate_next (GabbleBytestreamMultiple *self) { GabbleBytestreamMultiplePrivate *priv = GABBLE_BYTESTREAM_MULTIPLE_GET_PRIVATE (self); gchar *stream_method; g_return_if_fail (priv->active_bytestream == NULL); /* The caller has to be sure that there is a fallback method */ g_return_if_fail (priv->fallback_stream_methods != NULL); /* Try the first stream method in the fallback list */ stream_method = priv->fallback_stream_methods->data; priv->fallback_stream_methods = g_list_delete_link ( priv->fallback_stream_methods, priv->fallback_stream_methods); priv->active_bytestream = gabble_bytestream_factory_create_from_method ( priv->factory, stream_method, priv->peer_handle, priv->stream_id, priv->stream_init_id, priv->peer_resource, priv->self_full_jid, priv->state); /* Methods have already been checked so this shouldn't fail */ g_assert (priv->active_bytestream != NULL); g_free (stream_method); /* block the new bytestream if needed */ gabble_bytestream_iface_block_reading (priv->active_bytestream, priv->read_blocked); g_signal_connect (priv->active_bytestream, "connection-error", G_CALLBACK (bytestream_connection_error_cb), self); g_signal_connect (priv->active_bytestream, "data-received", G_CALLBACK (bytestream_data_received_cb), self); g_signal_connect (priv->active_bytestream, "state-changed", G_CALLBACK (bytestream_state_changed_cb), self); g_signal_connect (priv->active_bytestream, "write-blocked", G_CALLBACK (bytestream_write_blocked_cb), self); } /* * gabble_bytestream_multiple_add_bytestream * * Add an alternative stream method. */ void gabble_bytestream_multiple_add_stream_method (GabbleBytestreamMultiple *self, const gchar *method) { GabbleBytestreamMultiplePrivate *priv; g_return_if_fail (GABBLE_IS_BYTESTREAM_MULTIPLE (self)); g_return_if_fail (method != NULL); priv = GABBLE_BYTESTREAM_MULTIPLE_GET_PRIVATE (self); DEBUG ("Add bytestream method %s", method); priv->fallback_stream_methods = g_list_append ( priv->fallback_stream_methods, g_strdup (method)); if (priv->active_bytestream == NULL) bytestream_activate_next (self); } gboolean gabble_bytestream_multiple_has_stream_method (GabbleBytestreamMultiple *self) { GabbleBytestreamMultiplePrivate *priv = GABBLE_BYTESTREAM_MULTIPLE_GET_PRIVATE (self); if (priv->active_bytestream != NULL) return TRUE; return (g_list_length (priv->fallback_stream_methods) != 0); } static void gabble_bytestream_multiple_block_reading (GabbleBytestreamIface *iface, gboolean block) { GabbleBytestreamMultiple *self = GABBLE_BYTESTREAM_MULTIPLE (iface); GabbleBytestreamMultiplePrivate *priv = GABBLE_BYTESTREAM_MULTIPLE_GET_PRIVATE (self); if (priv->read_blocked == block) return; priv->read_blocked = block; g_assert (priv->active_bytestream != NULL); gabble_bytestream_iface_block_reading (priv->active_bytestream, block); } static void bytestream_iface_init (gpointer g_iface, gpointer iface_data) { GabbleBytestreamIfaceClass *klass = (GabbleBytestreamIfaceClass *) g_iface; klass->initiate = gabble_bytestream_multiple_initiate; klass->send = gabble_bytestream_multiple_send; klass->close = gabble_bytestream_multiple_close; klass->accept = gabble_bytestream_multiple_accept; klass->block_reading = gabble_bytestream_multiple_block_reading; } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������telepathy-gabble-0.18.2/src/bytestream-multiple.h���������������������������������������������������0000644�0001750�0001750�00000005101�12200204333�021747� 0����������������������������������������������������������������������������������������������������ustar�00smcv����������������������������smcv����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * bytestream-socks5.h - Header for GabbleBytestreamMultiple * Copyright (C) 2007-2008 Collabora Ltd. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 __GABBLE_BYTESTREAM_MULTIPLE_H__ #define __GABBLE_BYTESTREAM_MULTIPLE_H__ #include <stdlib.h> #include <glib-object.h> #include "bytestream-iface.h" G_BEGIN_DECLS typedef struct _GabbleBytestreamMultiple GabbleBytestreamMultiple; typedef struct _GabbleBytestreamMultipleClass GabbleBytestreamMultipleClass; typedef struct _GabbleBytestreamMultiplePrivate GabbleBytestreamMultiplePrivate; struct _GabbleBytestreamMultipleClass { GObjectClass parent_class; }; struct _GabbleBytestreamMultiple { GObject parent; GabbleBytestreamMultiplePrivate *priv; }; GType gabble_bytestream_multiple_get_type (void); /* TYPE MACROS */ #define GABBLE_TYPE_BYTESTREAM_MULTIPLE \ (gabble_bytestream_multiple_get_type ()) #define GABBLE_BYTESTREAM_MULTIPLE(obj) \ (G_TYPE_CHECK_INSTANCE_CAST((obj), GABBLE_TYPE_BYTESTREAM_MULTIPLE,\ GabbleBytestreamMultiple)) #define GABBLE_BYTESTREAM_MULTIPLE_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST((klass), GABBLE_TYPE_BYTESTREAM_MULTIPLE,\ GabbleBytestreamMultipleClass)) #define GABBLE_IS_BYTESTREAM_MULTIPLE(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE((obj), GABBLE_TYPE_BYTESTREAM_MULTIPLE)) #define GABBLE_IS_BYTESTREAM_MULTIPLE_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE((klass), GABBLE_TYPE_BYTESTREAM_MULTIPLE)) #define GABBLE_BYTESTREAM_MULTIPLE_GET_CLASS(obj) \ (G_TYPE_INSTANCE_GET_CLASS ((obj), GABBLE_TYPE_BYTESTREAM_MULTIPLE,\ GabbleBytestreamMultipleClass)) void gabble_bytestream_multiple_add_stream_method ( GabbleBytestreamMultiple *self, const gchar *method); gboolean gabble_bytestream_multiple_has_stream_method ( GabbleBytestreamMultiple *self); G_END_DECLS #endif /* #ifndef __GABBLE_BYTESTREAM_MULTIPLE_H__ */ ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������telepathy-gabble-0.18.2/src/bytestream-muc.c��������������������������������������������������������0000644�0001750�0001750�00000041155�12200204333�020704� 0����������������������������������������������������������������������������������������������������ustar�00smcv����������������������������smcv����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * bytestream-muc.c - Source for GabbleBytestreamMuc * Copyright (C) 2007 Collabora Ltd. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 "config.h" #include "bytestream-muc.h" #include <string.h> #include <dbus/dbus-glib.h> #include <dbus/dbus-glib-lowlevel.h> #include <telepathy-glib/telepathy-glib.h> #include <telepathy-glib/telepathy-glib-dbus.h> #define DEBUG_FLAG GABBLE_DEBUG_BYTESTREAM #include "bytestream-factory.h" #include "bytestream-iface.h" #include "connection.h" #include "debug.h" #include "disco.h" #include "namespaces.h" #include "util.h" /* 45k gives us 60k after base64 encoding, allowing 4k of header before we hit * ejabberd's default 64k maximum stanza size */ #define MAX_BLOCK_SIZE (1024 * 45) static void bytestream_iface_init (gpointer g_iface, gpointer iface_data); G_DEFINE_TYPE_WITH_CODE (GabbleBytestreamMuc, gabble_bytestream_muc, G_TYPE_OBJECT, G_IMPLEMENT_INTERFACE (GABBLE_TYPE_BYTESTREAM_IFACE, bytestream_iface_init)); /* properties */ enum { PROP_CONNECTION = 1, PROP_PEER_HANDLE, PROP_PEER_HANDLE_TYPE, PROP_STREAM_ID, PROP_PEER_JID, PROP_STATE, PROP_PROTOCOL, LAST_PROPERTY }; struct _GabbleBytestreamMucPrivate { GabbleConnection *conn; TpHandle peer_handle; TpHandleType peer_handle_type; gchar *stream_id; GabbleBytestreamState state; const gchar *peer_jid; /* (gchar *): sender's muc-JID -> (GString *): accumulated message data */ GHashTable *buffers; gboolean dispose_has_run; }; #define GABBLE_BYTESTREAM_MUC_GET_PRIVATE(obj) ((obj)->priv) static void free_buffer (GString *buffer) { g_string_free (buffer, TRUE); } static void gabble_bytestream_muc_init (GabbleBytestreamMuc *self) { GabbleBytestreamMucPrivate *priv = G_TYPE_INSTANCE_GET_PRIVATE (self, GABBLE_TYPE_BYTESTREAM_MUC, GabbleBytestreamMucPrivate); self->priv = priv; priv->buffers = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify) free_buffer); } static void gabble_bytestream_muc_dispose (GObject *object) { GabbleBytestreamMuc *self = GABBLE_BYTESTREAM_MUC (object); GabbleBytestreamMucPrivate *priv = GABBLE_BYTESTREAM_MUC_GET_PRIVATE (self); if (priv->dispose_has_run) return; priv->dispose_has_run = TRUE; if (priv->state != GABBLE_BYTESTREAM_STATE_CLOSED) { gabble_bytestream_iface_close (GABBLE_BYTESTREAM_IFACE (self), NULL); } G_OBJECT_CLASS (gabble_bytestream_muc_parent_class)->dispose (object); } static void gabble_bytestream_muc_finalize (GObject *object) { GabbleBytestreamMuc *self = GABBLE_BYTESTREAM_MUC (object); GabbleBytestreamMucPrivate *priv = GABBLE_BYTESTREAM_MUC_GET_PRIVATE (self); g_free (priv->stream_id); if (priv->buffers != NULL) { g_hash_table_unref (priv->buffers); priv->buffers = NULL; } G_OBJECT_CLASS (gabble_bytestream_muc_parent_class)->finalize (object); } static void gabble_bytestream_muc_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { GabbleBytestreamMuc *self = GABBLE_BYTESTREAM_MUC (object); GabbleBytestreamMucPrivate *priv = GABBLE_BYTESTREAM_MUC_GET_PRIVATE (self); switch (property_id) { case PROP_CONNECTION: g_value_set_object (value, priv->conn); break; case PROP_PEER_HANDLE: g_value_set_uint (value, priv->peer_handle); break; case PROP_PEER_HANDLE_TYPE: g_value_set_uint (value, TP_HANDLE_TYPE_ROOM); break; case PROP_STREAM_ID: g_value_set_string (value, priv->stream_id); break; case PROP_PEER_JID: g_value_set_string (value, priv->peer_jid); break; case PROP_STATE: g_value_set_uint (value, priv->state); break; case PROP_PROTOCOL: g_value_set_string (value, NS_MUC_BYTESTREAM); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void gabble_bytestream_muc_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { GabbleBytestreamMuc *self = GABBLE_BYTESTREAM_MUC (object); GabbleBytestreamMucPrivate *priv = GABBLE_BYTESTREAM_MUC_GET_PRIVATE (self); switch (property_id) { case PROP_CONNECTION: priv->conn = g_value_get_object (value); break; case PROP_PEER_HANDLE: priv->peer_handle = g_value_get_uint (value); break; case PROP_STREAM_ID: g_free (priv->stream_id); priv->stream_id = g_value_dup_string (value); break; case PROP_STATE: if (priv->state != g_value_get_uint (value)) { priv->state = g_value_get_uint (value); g_signal_emit_by_name (object, "state-changed", priv->state); } break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static GObject * gabble_bytestream_muc_constructor (GType type, guint n_props, GObjectConstructParam *props) { GObject *obj; GabbleBytestreamMucPrivate *priv; TpHandleRepoIface *room_repo; obj = G_OBJECT_CLASS (gabble_bytestream_muc_parent_class)-> constructor (type, n_props, props); priv = GABBLE_BYTESTREAM_MUC_GET_PRIVATE (GABBLE_BYTESTREAM_MUC (obj)); g_assert (priv->conn != NULL); g_assert (priv->peer_handle != 0); room_repo = tp_base_connection_get_handles ( (TpBaseConnection *) priv->conn, TP_HANDLE_TYPE_ROOM); priv->peer_jid = tp_handle_inspect (room_repo, priv->peer_handle); return obj; } static void gabble_bytestream_muc_class_init ( GabbleBytestreamMucClass *gabble_bytestream_muc_class) { GObjectClass *object_class = G_OBJECT_CLASS (gabble_bytestream_muc_class); g_type_class_add_private (gabble_bytestream_muc_class, sizeof (GabbleBytestreamMucPrivate)); object_class->dispose = gabble_bytestream_muc_dispose; object_class->finalize = gabble_bytestream_muc_finalize; object_class->get_property = gabble_bytestream_muc_get_property; object_class->set_property = gabble_bytestream_muc_set_property; object_class->constructor = gabble_bytestream_muc_constructor; g_object_class_override_property (object_class, PROP_CONNECTION, "connection"); g_object_class_override_property (object_class, PROP_PEER_HANDLE, "peer-handle"); g_object_class_override_property (object_class, PROP_PEER_HANDLE_TYPE, "peer-handle-type"); g_object_class_override_property (object_class, PROP_STREAM_ID, "stream-id"); g_object_class_override_property (object_class, PROP_PEER_JID, "peer-jid"); g_object_class_override_property (object_class, PROP_STATE, "state"); g_object_class_override_property (object_class, PROP_PROTOCOL, "protocol"); } enum { FRAG_COMPLETE = 0, FRAG_FIRST, FRAG_MIDDLE, FRAG_LAST }; static gboolean send_data_to (GabbleBytestreamMuc *self, const gchar *to, gboolean groupchat, guint len, const gchar *str) { GabbleBytestreamMucPrivate *priv = GABBLE_BYTESTREAM_MUC_GET_PRIVATE (self); guint sent, stanza_count; guint frag; if (priv->state != GABBLE_BYTESTREAM_STATE_OPEN) { DEBUG ("can't send data through a not open bytestream (state: %d)", priv->state); return FALSE; } sent = 0; stanza_count = 0; while (sent < len) { gboolean ret; gchar *encoded; guint send_now; GError *error = NULL; WockyStanza *msg; WockyNode *data = NULL; msg = wocky_stanza_build (WOCKY_STANZA_TYPE_MESSAGE, WOCKY_STANZA_SUB_TYPE_NONE, NULL, to, '(', "data", '*', &data, ':', NS_MUC_BYTESTREAM, '@', "sid", priv->stream_id, ')', GABBLE_AMP_DO_NOT_STORE_SPEC, NULL); g_assert (data != NULL); if (groupchat) { wocky_node_set_attribute (wocky_stanza_get_top_node (msg), "type", "groupchat"); } if ((len - sent) > MAX_BLOCK_SIZE) { /* We can't send all the remaining data in one stanza */ send_now = MAX_BLOCK_SIZE; if (stanza_count == 0) frag = FRAG_FIRST; else frag = FRAG_MIDDLE; } else { /* Send all the remaining data */ send_now = (len - sent); if (stanza_count == 0) frag = FRAG_COMPLETE; else frag = FRAG_LAST; } encoded = g_base64_encode ((const guchar *) str + sent, send_now); wocky_node_set_content (data, encoded); switch (frag) { case FRAG_FIRST: wocky_node_set_attribute (data, "frag", "first"); break; case FRAG_MIDDLE: wocky_node_set_attribute (data, "frag", "middle"); break; case FRAG_LAST: wocky_node_set_attribute (data, "frag", "last"); break; } DEBUG ("send %d bytes", send_now); ret = _gabble_connection_send (priv->conn, msg, &error); g_free (encoded); if (!ret) { DEBUG ("error sending pseusdo IBB Muc stanza: %s", error->message); g_error_free (error); g_object_unref (msg); return FALSE; } sent += send_now; stanza_count++; g_object_unref (msg); } DEBUG ("finished to send %d bytes (%d stanzas needed)", len, stanza_count); return TRUE; } /* * gabble_bytestream_muc_send * * Implements gabble_bytestream_iface_send on GabbleBytestreamIface */ static gboolean gabble_bytestream_muc_send (GabbleBytestreamIface *iface, guint len, const gchar *str) { GabbleBytestreamMuc *self = GABBLE_BYTESTREAM_MUC (iface); GabbleBytestreamMucPrivate *priv = GABBLE_BYTESTREAM_MUC_GET_PRIVATE (self); return send_data_to (self, priv->peer_jid, TRUE, len, str); } void gabble_bytestream_muc_receive (GabbleBytestreamMuc *self, WockyStanza *msg) { GabbleBytestreamMucPrivate *priv = GABBLE_BYTESTREAM_MUC_GET_PRIVATE (self); TpHandleRepoIface *contact_repo = tp_base_connection_get_handles ( (TpBaseConnection *) priv->conn, TP_HANDLE_TYPE_CONTACT); const gchar *from; WockyNode *data; GString *str; guchar *st; gsize outlen; TpHandle sender; GString *buffer; const gchar *frag_val; guint frag; gboolean fully_received = FALSE; /* caller must have checked for this in order to know which bytestream to * route this packet to */ data = wocky_node_get_child_ns ( wocky_stanza_get_top_node (msg), "data", NS_MUC_BYTESTREAM); g_assert (data != NULL); if (priv->state != GABBLE_BYTESTREAM_STATE_OPEN) { DEBUG ("can't receive data through a not open bytestream (state: %d)", priv->state); return; } from = wocky_node_get_attribute ( wocky_stanza_get_top_node (msg), "from"); g_return_if_fail (from != NULL); sender = tp_handle_lookup (contact_repo, from, GUINT_TO_POINTER (GABBLE_JID_ROOM_MEMBER), NULL); if (sender == 0) { DEBUG ("ignoring data in MUC from unknown contact %s", from); return; } frag_val = wocky_node_get_attribute (data, "frag"); if (frag_val == NULL) frag = FRAG_COMPLETE; else if (!tp_strdiff (frag_val, "first")) frag = FRAG_FIRST; else if (!tp_strdiff (frag_val, "middle")) frag = FRAG_MIDDLE; else if (!tp_strdiff (frag_val, "last")) frag = FRAG_LAST; else if (!tp_strdiff (frag_val, "complete")) frag = FRAG_COMPLETE; else { DEBUG ("Invalid frag value: %s", frag_val); return; } st = g_base64_decode (data->content, &outlen); str = g_string_new_len ((const gchar *) st, outlen); g_free (st); if (str == NULL) { DEBUG ("base64 decoding failed"); return; } buffer = g_hash_table_lookup (priv->buffers, from); if (frag == FRAG_COMPLETE) { if (buffer != NULL) { DEBUG ("Drop incomplete buffer of %s. " "Received new unfragmented data", from); g_hash_table_remove (priv->buffers, from); } fully_received = TRUE; } else if (frag == FRAG_FIRST) { if (buffer != NULL) { DEBUG ("Drop incomplete buffer of %s. " "Received first part of new data", from); g_hash_table_remove (priv->buffers, from); } else { DEBUG ("New buffer for %s", from); } g_hash_table_insert (priv->buffers, g_strdup (from), str); } else if (frag == FRAG_MIDDLE) { if (buffer == NULL) { DEBUG ("Drop middle part stanza from %s, first parts not buffered", from); } else { DEBUG ("Append data to buffer of %s (%" G_GSIZE_FORMAT " bytes)", from, str->len); g_string_append_len (buffer, str->str, str->len); } g_string_free (str, TRUE); } else if (frag == FRAG_LAST) { if (buffer == NULL) { DEBUG ("Drop last part stanza from %s, first parts not buffered", from); g_string_free (str, TRUE); } else { DEBUG ("Received last part from %s, buffer flushed", from); g_string_prepend_len (str, buffer->str, buffer->len); g_hash_table_remove (priv->buffers, from); fully_received = TRUE; } } if (fully_received) { DEBUG ("fully received %" G_GSIZE_FORMAT " bytes of data", str->len); g_signal_emit_by_name (G_OBJECT (self), "data-received", sender, str); g_string_free (str, TRUE); } } /* * gabble_bytestream_muc_accept * * Implements gabble_bytestream_iface_accept on GabbleBytestreamIface */ static void gabble_bytestream_muc_accept (GabbleBytestreamIface *iface, GabbleBytestreamAugmentSiAcceptReply func, gpointer user_data) { /* Don't have to accept a muc bytestream */ } /* * gabble_bytestream_muc_close * * Implements gabble_bytestream_iface_close on GabbleBytestreamIface */ static void gabble_bytestream_muc_close (GabbleBytestreamIface *iface, GError *error) { GabbleBytestreamMuc *self = GABBLE_BYTESTREAM_MUC (iface); GabbleBytestreamMucPrivate *priv = GABBLE_BYTESTREAM_MUC_GET_PRIVATE (self); if (priv->state == GABBLE_BYTESTREAM_STATE_CLOSED) /* bytestream already closed, do nothing */ return; g_object_set (self, "state", GABBLE_BYTESTREAM_STATE_CLOSED, NULL); } gboolean gabble_bytestream_muc_send_to (GabbleBytestreamMuc *self, TpHandle contact, guint len, gchar *str) { GabbleBytestreamMucPrivate *priv = GABBLE_BYTESTREAM_MUC_GET_PRIVATE (self); TpHandleRepoIface *contact_repo = tp_base_connection_get_handles ( (TpBaseConnection *) priv->conn, TP_HANDLE_TYPE_CONTACT); const gchar *to; to = tp_handle_inspect (contact_repo, contact); g_return_val_if_fail (g_str_has_prefix (to, priv->peer_jid), FALSE); g_return_val_if_fail (to[strlen (priv->peer_jid)] == '/', FALSE); return send_data_to (self, to, FALSE, len, str); } /* * gabll_bytestream_muc_initiate * * Implements gabble_bytestream_iface_initiate on GabbleBytestreamIface */ static gboolean gabble_bytestream_muc_initiate (GabbleBytestreamIface *iface) { /* Nothing to do */ return TRUE; } static void bytestream_iface_init (gpointer g_iface, gpointer iface_data) { GabbleBytestreamIfaceClass *klass = (GabbleBytestreamIfaceClass *) g_iface; klass->initiate = gabble_bytestream_muc_initiate; klass->send = gabble_bytestream_muc_send; klass->close = gabble_bytestream_muc_close; klass->accept = gabble_bytestream_muc_accept; } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������telepathy-gabble-0.18.2/src/bytestream-muc.h��������������������������������������������������������0000644�0001750�0001750�00000004672�12200204333�020714� 0����������������������������������������������������������������������������������������������������ustar�00smcv����������������������������smcv����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * bytestream-muc.h - Header for GabbleBytestreamMuc * Copyright (C) 2007 Collabora Ltd. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 __GABBLE_BYTESTREAM_MUC_H__ #define __GABBLE_BYTESTREAM_MUC_H__ #include <glib-object.h> #include <wocky/wocky.h> #include <telepathy-glib/telepathy-glib.h> G_BEGIN_DECLS typedef struct _GabbleBytestreamMuc GabbleBytestreamMuc; typedef struct _GabbleBytestreamMucClass GabbleBytestreamMucClass; typedef struct _GabbleBytestreamMucPrivate GabbleBytestreamMucPrivate; struct _GabbleBytestreamMucClass { GObjectClass parent_class; }; struct _GabbleBytestreamMuc { GObject parent; GabbleBytestreamMucPrivate *priv; }; GType gabble_bytestream_muc_get_type (void); /* TYPE MACROS */ #define GABBLE_TYPE_BYTESTREAM_MUC \ (gabble_bytestream_muc_get_type ()) #define GABBLE_BYTESTREAM_MUC(obj) \ (G_TYPE_CHECK_INSTANCE_CAST((obj), GABBLE_TYPE_BYTESTREAM_MUC,\ GabbleBytestreamMuc)) #define GABBLE_BYTESTREAM_MUC_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST((klass), GABBLE_TYPE_BYTESTREAM_MUC,\ GabbleBytestreamMucClass)) #define GABBLE_IS_BYTESTREAM_MUC(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE((obj), GABBLE_TYPE_BYTESTREAM_MUC)) #define GABBLE_IS_BYTESTREAM_MUC_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE((klass), GABBLE_TYPE_BYTESTREAM_MUC)) #define GABBLE_BYTESTREAM_MUC_GET_CLASS(obj) \ (G_TYPE_INSTANCE_GET_CLASS ((obj), GABBLE_TYPE_BYTESTREAM_MUC,\ GabbleBytestreamMucClass)) void gabble_bytestream_muc_receive (GabbleBytestreamMuc *bytestream, WockyStanza *msg); gboolean gabble_bytestream_muc_send_to (GabbleBytestreamMuc *bytestream, TpHandle to, guint len, gchar *str); G_END_DECLS #endif /* #ifndef __GABBLE_BYTESTREAM_MUC_H__ */ ����������������������������������������������������������������������telepathy-gabble-0.18.2/src/bytestream-iface.c������������������������������������������������������0000644�0001750�0001750�00000015142�12200204333�021164� 0����������������������������������������������������������������������������������������������������ustar�00smcv����������������������������smcv����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * bytestream-iface.c - Source for GabbleBytestream interface * Copyright (C) 2007 Collabora Ltd. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 "config.h" #include "bytestream-iface.h" #include "gabble-signals-marshal.h" #include "connection.h" gboolean gabble_bytestream_iface_initiate (GabbleBytestreamIface *self) { gboolean (*virtual_method)(GabbleBytestreamIface *) = GABBLE_BYTESTREAM_IFACE_GET_CLASS (self)->initiate; g_assert (virtual_method != NULL); return virtual_method (self); } gboolean gabble_bytestream_iface_send (GabbleBytestreamIface *self, guint len, const gchar *data) { gboolean (*virtual_method)(GabbleBytestreamIface *, guint, const gchar *) = GABBLE_BYTESTREAM_IFACE_GET_CLASS (self)->send; g_assert (virtual_method != NULL); return virtual_method (self, len, data); } void gabble_bytestream_iface_close (GabbleBytestreamIface *self, GError *error) { void (*virtual_method)(GabbleBytestreamIface *, GError *) = GABBLE_BYTESTREAM_IFACE_GET_CLASS (self)->close; g_assert (virtual_method != NULL); virtual_method (self, error); } void gabble_bytestream_iface_accept (GabbleBytestreamIface *self, GabbleBytestreamAugmentSiAcceptReply func, gpointer user_data) { void (*virtual_method)(GabbleBytestreamIface *, GabbleBytestreamAugmentSiAcceptReply, gpointer) = GABBLE_BYTESTREAM_IFACE_GET_CLASS (self)->accept; g_assert (virtual_method != NULL); virtual_method (self, func, user_data); } static void gabble_bytestream_iface_base_init (gpointer klass) { static gboolean initialized = FALSE; if (!initialized) { GParamSpec *param_spec; param_spec = g_param_spec_object ( "connection", "GabbleConnection object", "Gabble connection object that owns this Bytestream object.", GABBLE_TYPE_CONNECTION, G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); g_object_interface_install_property (klass, param_spec); param_spec = g_param_spec_uint ( "peer-handle", "Peer handle", "The TpHandle of the remote peer involved in this bytestream", 0, G_MAXUINT32, 0, G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); g_object_interface_install_property (klass, param_spec); param_spec = g_param_spec_uint ( "peer-handle-type", "Peer handle type", "The TpHandleType of the remote peer's associated handle", 0, G_MAXUINT32, 0, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); g_object_interface_install_property (klass, param_spec); param_spec = g_param_spec_string ( "stream-id", "stream ID", "the ID of the stream", "", G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); g_object_interface_install_property (klass, param_spec); param_spec = g_param_spec_string ( "peer-jid", "Peer JID", "The JID used by the remote peer during the SI", "", G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); g_object_interface_install_property (klass, param_spec); param_spec = g_param_spec_uint ( "state", "Bytestream state", "An enum (GabbleBytestreamState) signifying the current state of" "this bytestream object", 0, NUM_GABBLE_BYTESTREAM_STATES - 1, GABBLE_BYTESTREAM_STATE_LOCAL_PENDING, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); g_object_interface_install_property (klass, param_spec); param_spec = g_param_spec_string ( "protocol", "protocol", "the name of the protocol implemented by this bytestream", NULL, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); g_object_interface_install_property (klass, param_spec); g_signal_new ("data-received", G_TYPE_FROM_INTERFACE (klass), G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED, 0, NULL, NULL, g_cclosure_marshal_VOID__UINT_POINTER, G_TYPE_NONE, 2, G_TYPE_UINT, G_TYPE_POINTER); g_signal_new ("state-changed", G_TYPE_FROM_INTERFACE (klass), G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED, 0, NULL, NULL, g_cclosure_marshal_VOID__UINT, G_TYPE_NONE, 1, G_TYPE_UINT); g_signal_new ("write-blocked", G_TYPE_FROM_INTERFACE (klass), G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED, 0, NULL, NULL, g_cclosure_marshal_VOID__BOOLEAN, G_TYPE_NONE, 1, G_TYPE_BOOLEAN); g_signal_new ("connection-error", G_TYPE_FROM_INTERFACE (klass), G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED, 0, NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); initialized = TRUE; } } void gabble_bytestream_iface_block_reading (GabbleBytestreamIface *self, gboolean block) { void (*virtual_method)(GabbleBytestreamIface *, gboolean) = GABBLE_BYTESTREAM_IFACE_GET_CLASS (self)->block_reading; if (virtual_method != NULL) virtual_method (self, block); /* else: do nothing. Some bytestreams like IBB can't implement read_block. */ } GType gabble_bytestream_iface_get_type (void) { static GType type = 0; if (type == 0) { static const GTypeInfo info = { sizeof (GabbleBytestreamIfaceClass), gabble_bytestream_iface_base_init, /* base_init */ NULL, /* base_finalize */ NULL, /* class_init */ NULL, /* class_finalize */ NULL, /* class_data */ 0, 0, /* n_preallocs */ NULL /* instance_init */ }; type = g_type_register_static (G_TYPE_INTERFACE, "GabbleBytestreamIface", &info, 0); } return type; } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������telepathy-gabble-0.18.2/src/bytestream-iface.h������������������������������������������������������0000644�0001750�0001750�00000006561�12200204333�021176� 0����������������������������������������������������������������������������������������������������ustar�00smcv����������������������������smcv����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * bytestream-iface.h - Header for GabbleBytestream interface * Copyright (C) 2007 Collabora Ltd. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 __GABBLE_BYTESTREAM_IFACE_H__ #define __GABBLE_BYTESTREAM_IFACE_H__ #include <glib-object.h> #include <wocky/wocky.h> G_BEGIN_DECLS typedef enum { /* Received a SI request, response not yet sent */ GABBLE_BYTESTREAM_STATE_LOCAL_PENDING = 0, /* We accepted SI request. * bytestream specific init steps not yet performed */ GABBLE_BYTESTREAM_STATE_ACCEPTED, /* Remote contact accepted the SI request. * bytestream specific initiation started */ GABBLE_BYTESTREAM_STATE_INITIATING, /* Bytestream open */ GABBLE_BYTESTREAM_STATE_OPEN, GABBLE_BYTESTREAM_STATE_CLOSING, GABBLE_BYTESTREAM_STATE_CLOSED, NUM_GABBLE_BYTESTREAM_STATES, } GabbleBytestreamState; typedef void (* GabbleBytestreamAugmentSiAcceptReply) ( WockyNode *si, gpointer user_data); typedef struct _GabbleBytestreamIface GabbleBytestreamIface; typedef struct _GabbleBytestreamIfaceClass GabbleBytestreamIfaceClass; struct _GabbleBytestreamIfaceClass { GTypeInterface parent; gboolean (*initiate) (GabbleBytestreamIface *bytestream); gboolean (*send) (GabbleBytestreamIface *bytestream, guint len, const gchar *data); void (*close) (GabbleBytestreamIface *bytestream, GError *error); void (*accept) (GabbleBytestreamIface *bytestream, GabbleBytestreamAugmentSiAcceptReply func, gpointer user_data); void (*block_reading) (GabbleBytestreamIface *bytestream, gboolean block); }; GType gabble_bytestream_iface_get_type (void); /* TYPE MACROS */ #define GABBLE_TYPE_BYTESTREAM_IFACE \ (gabble_bytestream_iface_get_type ()) #define GABBLE_BYTESTREAM_IFACE(obj) \ (G_TYPE_CHECK_INSTANCE_CAST((obj), GABBLE_TYPE_BYTESTREAM_IFACE, \ GabbleBytestreamIface)) #define GABBLE_IS_BYTESTREAM_IFACE(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE((obj), GABBLE_TYPE_BYTESTREAM_IFACE)) #define GABBLE_BYTESTREAM_IFACE_GET_CLASS(obj) \ (G_TYPE_INSTANCE_GET_INTERFACE ((obj), GABBLE_TYPE_BYTESTREAM_IFACE,\ GabbleBytestreamIfaceClass)) gboolean gabble_bytestream_iface_initiate (GabbleBytestreamIface *bytestream); gboolean gabble_bytestream_iface_send (GabbleBytestreamIface *bytestream, guint len, const gchar *data); void gabble_bytestream_iface_close (GabbleBytestreamIface *bytestream, GError *error); void gabble_bytestream_iface_accept (GabbleBytestreamIface *bytestream, GabbleBytestreamAugmentSiAcceptReply func, gpointer user_data); void gabble_bytestream_iface_block_reading (GabbleBytestreamIface *bytestream, gboolean block); G_END_DECLS #endif /* #ifndef __GABBLE_BYTESTREAM_IFACE_H__ */ �����������������������������������������������������������������������������������������������������������������������������������������������telepathy-gabble-0.18.2/src/bytestream-ibb.c��������������������������������������������������������0000644�0001750�0001750�00000066604�12200204333�020662� 0����������������������������������������������������������������������������������������������������ustar�00smcv����������������������������smcv����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * bytestream-ibb.c - Source for GabbleBytestreamIBB * Copyright (C) 2007 Collabora Ltd. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 "config.h" #include "bytestream-ibb.h" #include <string.h> #include <dbus/dbus-glib.h> #include <dbus/dbus-glib-lowlevel.h> #include <telepathy-glib/telepathy-glib.h> #include <telepathy-glib/telepathy-glib-dbus.h> #define DEBUG_FLAG GABBLE_DEBUG_BYTESTREAM #include "bytestream-factory.h" #include "bytestream-iface.h" #include "connection.h" #include "conn-util.h" #include "debug.h" #include "disco.h" #include "namespaces.h" #include "util.h" static void bytestream_iface_init (gpointer g_iface, gpointer iface_data); G_DEFINE_TYPE_WITH_CODE (GabbleBytestreamIBB, gabble_bytestream_ibb, G_TYPE_OBJECT, G_IMPLEMENT_INTERFACE (GABBLE_TYPE_BYTESTREAM_IFACE, bytestream_iface_init)); /* properties */ enum { PROP_CONNECTION = 1, PROP_PEER_HANDLE, PROP_PEER_HANDLE_TYPE, PROP_STREAM_ID, PROP_STREAM_INIT_ID, PROP_PEER_JID, PROP_PEER_RESOURCE, PROP_STATE, PROP_PROTOCOL, PROP_BLOCK_SIZE, LAST_PROPERTY }; #define READ_BUFFER_MAX_SIZE (512 * 1024) /* the number of not acked stanzas allowed. Once this number reached, we stop * sending and wait for acks. */ #define WINDOW_SIZE 10 struct _GabbleBytestreamIBBPrivate { GabbleConnection *conn; TpHandle peer_handle; gchar *stream_id; gchar *stream_init_id; gchar *peer_resource; GabbleBytestreamState state; gchar *peer_jid; guint block_size; guint16 seq; guint16 last_seq_recv; WockyStanza *close_iq_to_ack; /* We can't stop receving IBB data so if user wants to block the bytestream * we buffer them until he unblocks it. */ gboolean read_blocked; GString *read_buffer; /* list of reffed (WockyStanza *) */ GSList *received_stanzas_not_acked; /* (WockyStanza *) -> TRUE * We don't keep a ref on the WockyStanza as we just use this table to track * stanzas waiting for reply. The stanza is never used (and so deferenced). */ GHashTable *sent_stanzas_not_acked; GString *write_buffer; gboolean write_blocked; gboolean dispose_has_run; }; #define GABBLE_BYTESTREAM_IBB_GET_PRIVATE(obj) ((obj)->priv) static void gabble_bytestream_ibb_init (GabbleBytestreamIBB *self) { GabbleBytestreamIBBPrivate *priv = G_TYPE_INSTANCE_GET_PRIVATE (self, GABBLE_TYPE_BYTESTREAM_IBB, GabbleBytestreamIBBPrivate); self->priv = priv; priv->read_buffer = NULL; priv->received_stanzas_not_acked = NULL; priv->sent_stanzas_not_acked = g_hash_table_new (g_direct_hash, g_direct_equal); priv->write_buffer = NULL; priv->write_blocked = FALSE; } static void gabble_bytestream_ibb_dispose (GObject *object) { GabbleBytestreamIBB *self = GABBLE_BYTESTREAM_IBB (object); GabbleBytestreamIBBPrivate *priv = GABBLE_BYTESTREAM_IBB_GET_PRIVATE (self); if (priv->dispose_has_run) return; priv->dispose_has_run = TRUE; if (priv->state != GABBLE_BYTESTREAM_STATE_CLOSED) { gabble_bytestream_iface_close (GABBLE_BYTESTREAM_IFACE (self), NULL); } if (priv->close_iq_to_ack != NULL) { _gabble_connection_acknowledge_set_iq (priv->conn, priv->close_iq_to_ack); g_object_unref (priv->close_iq_to_ack); priv->close_iq_to_ack = NULL; } G_OBJECT_CLASS (gabble_bytestream_ibb_parent_class)->dispose (object); } static void gabble_bytestream_ibb_finalize (GObject *object) { GabbleBytestreamIBB *self = GABBLE_BYTESTREAM_IBB (object); GabbleBytestreamIBBPrivate *priv = GABBLE_BYTESTREAM_IBB_GET_PRIVATE (self); g_free (priv->stream_id); g_free (priv->stream_init_id); g_free (priv->peer_resource); g_free (priv->peer_jid); if (priv->read_buffer != NULL) g_string_free (priv->read_buffer, TRUE); if (priv->write_buffer != NULL) g_string_free (priv->write_buffer, TRUE); g_hash_table_unref (priv->sent_stanzas_not_acked); G_OBJECT_CLASS (gabble_bytestream_ibb_parent_class)->finalize (object); } static void gabble_bytestream_ibb_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { GabbleBytestreamIBB *self = GABBLE_BYTESTREAM_IBB (object); GabbleBytestreamIBBPrivate *priv = GABBLE_BYTESTREAM_IBB_GET_PRIVATE (self); switch (property_id) { case PROP_CONNECTION: g_value_set_object (value, priv->conn); break; case PROP_PEER_HANDLE: g_value_set_uint (value, priv->peer_handle); break; case PROP_PEER_HANDLE_TYPE: g_value_set_uint (value, TP_HANDLE_TYPE_CONTACT); break; case PROP_STREAM_ID: g_value_set_string (value, priv->stream_id); break; case PROP_STREAM_INIT_ID: g_value_set_string (value, priv->stream_init_id); break; case PROP_PEER_RESOURCE: g_value_set_string (value, priv->peer_resource); break; case PROP_PEER_JID: g_value_set_string (value, priv->peer_jid); break; case PROP_STATE: g_value_set_uint (value, priv->state); break; case PROP_PROTOCOL: g_value_set_string (value, NS_IBB); break; case PROP_BLOCK_SIZE: g_value_set_uint (value, priv->block_size); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void gabble_bytestream_ibb_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { GabbleBytestreamIBB *self = GABBLE_BYTESTREAM_IBB (object); GabbleBytestreamIBBPrivate *priv = GABBLE_BYTESTREAM_IBB_GET_PRIVATE (self); switch (property_id) { case PROP_CONNECTION: priv->conn = g_value_get_object (value); break; case PROP_PEER_HANDLE: priv->peer_handle = g_value_get_uint (value); break; case PROP_STREAM_ID: g_free (priv->stream_id); priv->stream_id = g_value_dup_string (value); break; case PROP_STREAM_INIT_ID: g_free (priv->stream_init_id); priv->stream_init_id = g_value_dup_string (value); break; case PROP_PEER_RESOURCE: g_free (priv->peer_resource); priv->peer_resource = g_value_dup_string (value); break; case PROP_STATE: if (priv->state != g_value_get_uint (value)) { priv->state = g_value_get_uint (value); g_signal_emit_by_name (object, "state-changed", priv->state); } break; case PROP_BLOCK_SIZE: priv->block_size = g_value_get_uint (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static GObject * gabble_bytestream_ibb_constructor (GType type, guint n_props, GObjectConstructParam *props) { GObject *obj; GabbleBytestreamIBBPrivate *priv; TpHandleRepoIface *contact_repo; const gchar *jid; obj = G_OBJECT_CLASS (gabble_bytestream_ibb_parent_class)-> constructor (type, n_props, props); priv = GABBLE_BYTESTREAM_IBB_GET_PRIVATE (GABBLE_BYTESTREAM_IBB (obj)); g_assert (priv->conn != NULL); g_assert (priv->peer_handle != 0); g_assert (priv->stream_id != NULL); contact_repo = tp_base_connection_get_handles ( (TpBaseConnection *) priv->conn, TP_HANDLE_TYPE_CONTACT); jid = tp_handle_inspect (contact_repo, priv->peer_handle); if (priv->peer_resource != NULL) priv->peer_jid = g_strdup_printf ("%s/%s", jid, priv->peer_resource); else priv->peer_jid = g_strdup (jid); return obj; } static void gabble_bytestream_ibb_class_init ( GabbleBytestreamIBBClass *gabble_bytestream_ibb_class) { GObjectClass *object_class = G_OBJECT_CLASS (gabble_bytestream_ibb_class); GParamSpec *param_spec; g_type_class_add_private (gabble_bytestream_ibb_class, sizeof (GabbleBytestreamIBBPrivate)); object_class->dispose = gabble_bytestream_ibb_dispose; object_class->finalize = gabble_bytestream_ibb_finalize; object_class->get_property = gabble_bytestream_ibb_get_property; object_class->set_property = gabble_bytestream_ibb_set_property; object_class->constructor = gabble_bytestream_ibb_constructor; g_object_class_override_property (object_class, PROP_CONNECTION, "connection"); g_object_class_override_property (object_class, PROP_PEER_HANDLE, "peer-handle"); g_object_class_override_property (object_class, PROP_PEER_HANDLE_TYPE, "peer-handle-type"); g_object_class_override_property (object_class, PROP_STREAM_ID, "stream-id"); g_object_class_override_property (object_class, PROP_PEER_JID, "peer-jid"); g_object_class_override_property (object_class, PROP_STATE, "state"); g_object_class_override_property (object_class, PROP_PROTOCOL, "protocol"); param_spec = g_param_spec_string ( "peer-resource", "Peer resource", "the resource used by the remote peer during the SI, if any", NULL, G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, PROP_PEER_RESOURCE, param_spec); param_spec = g_param_spec_string ( "stream-init-id", "stream init ID", "the iq ID of the SI request, if any", NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, PROP_STREAM_INIT_ID, param_spec); param_spec = g_param_spec_uint ( "block-size", "block size", "Maximum data sent using one stanza as described in XEP-0047", 0, G_MAXUINT32, 4096, G_PARAM_CONSTRUCT | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, PROP_BLOCK_SIZE, param_spec); } static void change_write_blocked_state (GabbleBytestreamIBB *self, gboolean blocked) { GabbleBytestreamIBBPrivate *priv = GABBLE_BYTESTREAM_IBB_GET_PRIVATE (self); if (priv->write_blocked == blocked) return; priv->write_blocked = blocked; g_signal_emit_by_name (self, "write-blocked", blocked); } static void send_close_stanza (GabbleBytestreamIBB *self) { GabbleBytestreamIBBPrivate *priv = GABBLE_BYTESTREAM_IBB_GET_PRIVATE (self); WockyStanza *msg; if (priv->close_iq_to_ack != NULL) { /* We received a close IQ and just need to ACK it */ _gabble_connection_acknowledge_set_iq (priv->conn, priv->close_iq_to_ack); g_object_unref (priv->close_iq_to_ack); priv->close_iq_to_ack = NULL; } DEBUG ("send IBB close stanza"); msg = wocky_stanza_build (WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_SET, NULL, priv->peer_jid, '(', "close", ':', NS_IBB, '@', "sid", priv->stream_id, ')', NULL); /* We don't really care about the answer as the bytestream * is closed anyway. */ _gabble_connection_send_with_reply (priv->conn, msg, NULL, NULL, NULL, NULL); g_object_unref (msg); } static guint send_data (GabbleBytestreamIBB *self, const gchar *str, guint len); static void iq_reply_cb ( GObject *source, GAsyncResult *result, gpointer user_data) { TpWeakRef *weak_ref = user_data; GabbleBytestreamIBB *self = tp_weak_ref_dup_object (weak_ref); /* We don't hold a ref to the outgoing stanza; we just use its address as a * key */ gpointer sent_msg = tp_weak_ref_get_user_data (weak_ref); GabbleBytestreamIBBPrivate *priv; GError *error = NULL; tp_weak_ref_destroy (weak_ref); /* If the channel is already dead, never mind! */ if (self == NULL) return; priv = GABBLE_BYTESTREAM_IBB_GET_PRIVATE (self); g_hash_table_remove (priv->sent_stanzas_not_acked, sent_msg); if (!conn_util_send_iq_finish (GABBLE_CONNECTION (source), result, NULL, &error)) { DEBUG ("error sending IBB stanza: %s #%u '%s'. Closing the bytestream", g_quark_to_string (error->domain), error->code, error->message); g_clear_error (&error); /* FIXME: we should be able to feed this up to the application somehow. */ gabble_bytestream_iface_close (GABBLE_BYTESTREAM_IFACE (self), NULL); } else if (priv->write_buffer != NULL) { guint sent; DEBUG ("A stanza has been acked. Try to flush the buffer"); sent = send_data (self, priv->write_buffer->str, priv->write_buffer->len); if (sent == priv->write_buffer->len) { DEBUG ("buffer has been flushed; unblock write the bytestream"); g_string_free (priv->write_buffer, TRUE); priv->write_buffer = NULL; change_write_blocked_state (self, FALSE); if (priv->state == GABBLE_BYTESTREAM_STATE_CLOSING) { DEBUG ("Can close the bystream now the buffer is flushed"); send_close_stanza (self); g_object_set (self, "state", GABBLE_BYTESTREAM_STATE_CLOSED, NULL); } } else { g_string_erase (priv->write_buffer, 0, sent); DEBUG ("buffer has not been completely flushed; %" G_GSIZE_FORMAT " bytes left", priv->write_buffer->len); } } g_object_unref (self); } static guint send_data (GabbleBytestreamIBB *self, const gchar *str, guint len) { GabbleBytestreamIBBPrivate *priv = GABBLE_BYTESTREAM_IBB_GET_PRIVATE (self); guint sent, stanza_count; sent = 0; stanza_count = 0; while (sent < len) { WockyStanza *iq; guint send_now, remaining; gchar *seq, *encoded; guint nb_stanzas_waiting; remaining = (len - sent); nb_stanzas_waiting = g_hash_table_size (priv->sent_stanzas_not_acked); if (nb_stanzas_waiting >= WINDOW_SIZE) { DEBUG ("Window is full (%u). Stop sending stanzas", nb_stanzas_waiting); break; } /* We can send stanzas */ if (remaining > priv->block_size) { /* We can't send all the remaining data in one stanza */ send_now = priv->block_size; } else { /* Send all the remaining data */ send_now = remaining; } encoded = g_base64_encode ((const guchar *) str + sent, send_now); seq = g_strdup_printf ("%u", priv->seq++); iq = wocky_stanza_build (WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_SET, NULL, priv->peer_jid, '(', "data", '$', encoded, ':', NS_IBB, '@', "sid", priv->stream_id, '@', "seq", seq, ')', NULL); conn_util_send_iq_async (priv->conn, iq, NULL, iq_reply_cb, tp_weak_ref_new (self, iq, NULL)); g_free (encoded); g_free (seq); g_object_unref (iq); g_hash_table_insert (priv->sent_stanzas_not_acked, iq, GUINT_TO_POINTER (TRUE)); DEBUG ("send %d bytes (window size: %u)", send_now, nb_stanzas_waiting + 1); sent += send_now; stanza_count++; } DEBUG ("sent %d bytes (%d stanzas needed)", sent, stanza_count); return sent; } /* * gabble_bytestream_ibb_send * * Implements gabble_bytestream_iface_send on GabbleBytestreamIface */ static gboolean gabble_bytestream_ibb_send (GabbleBytestreamIface *iface, guint len, const gchar *str) { GabbleBytestreamIBB *self = GABBLE_BYTESTREAM_IBB (iface); GabbleBytestreamIBBPrivate *priv = GABBLE_BYTESTREAM_IBB_GET_PRIVATE (self); guint sent; if (priv->state != GABBLE_BYTESTREAM_STATE_OPEN) { DEBUG ("can't send data through a not open bytestream (state: %d)", priv->state); return FALSE; } if (priv->write_blocked) { DEBUG ("sending data while the bytestream was blocked"); } if (priv->write_buffer != NULL) { DEBUG ("Write buffer is not empty. Buffering data"); g_string_append_len (priv->write_buffer, str, len); return TRUE; } sent = send_data (self, str, len); if (sent < len) { guint remaining; DEBUG ("Some data have not been sent. Buffer them and write " "block the bytestream"); remaining = (len - sent); if (priv->write_buffer == NULL) { priv->write_buffer = g_string_new_len (str + sent, remaining); } else { g_string_append_len (priv->write_buffer, str + sent, remaining); } DEBUG ("write buffer size: %" G_GSIZE_FORMAT, priv->write_buffer->len); change_write_blocked_state (self, TRUE); } return TRUE; } void gabble_bytestream_ibb_receive (GabbleBytestreamIBB *self, WockyStanza *msg, gboolean is_iq) { GabbleBytestreamIBBPrivate *priv = GABBLE_BYTESTREAM_IBB_GET_PRIVATE (self); WockyNode *data; GString *str; guchar *st; gsize outlen; TpHandle sender; /* caller must have checked for this in order to know which bytestream to * route this packet to */ data = wocky_node_get_child_ns ( wocky_stanza_get_top_node (msg), "data", NS_IBB); g_assert (data != NULL); if (priv->state != GABBLE_BYTESTREAM_STATE_OPEN) { DEBUG ("can't receive data through a not open bytestream (state: %d)", priv->state); if (is_iq) wocky_porter_send_iq_error ( wocky_session_get_porter (priv->conn->session), msg, WOCKY_XMPP_ERROR_BAD_REQUEST, "IBB bytestream isn't open"); return; } /* Private stream using SI - the bytestream factory has already checked * the sender in order to dispatch to us */ sender = priv->peer_handle; /* FIXME: check sequence number */ st = g_base64_decode (data->content, &outlen); str = g_string_new_len ((gchar *) st, outlen); g_free (st); if (str == NULL) { DEBUG ("base64 decoding failed"); if (is_iq) wocky_porter_send_iq_error ( wocky_session_get_porter (priv->conn->session), msg, WOCKY_XMPP_ERROR_BAD_REQUEST, "base64 decoding failed"); return; } if (priv->read_blocked) { gsize current_buffer_len = 0; DEBUG ("Bytestream is blocked. Buffering data"); if (priv->read_buffer != NULL) current_buffer_len = priv->read_buffer->len; if (current_buffer_len + str->len > READ_BUFFER_MAX_SIZE) { DEBUG ("Buffer is full. Closing the bytestream"); if (is_iq) wocky_porter_send_iq_error ( wocky_session_get_porter (priv->conn->session), msg, WOCKY_XMPP_ERROR_NOT_ACCEPTABLE, "buffer is full"); gabble_bytestream_iface_close (GABBLE_BYTESTREAM_IFACE (self), NULL); g_string_free (str, TRUE); return; } if (priv->read_buffer == NULL) { priv->read_buffer = str; } else { g_string_append_len (priv->read_buffer, str->str, str->len); g_string_free (str, TRUE); } if (is_iq) { priv->received_stanzas_not_acked = g_slist_prepend ( priv->received_stanzas_not_acked, g_object_ref (msg)); } return; } g_signal_emit_by_name (G_OBJECT (self), "data-received", sender, str); g_string_free (str, TRUE); if (is_iq) _gabble_connection_acknowledge_set_iq (priv->conn, msg); return; } /* * gabble_bytestream_ibb_accept * * Implements gabble_bytestream_iface_accept on GabbleBytestreamIface */ static void gabble_bytestream_ibb_accept (GabbleBytestreamIface *iface, GabbleBytestreamAugmentSiAcceptReply func, gpointer user_data) { GabbleBytestreamIBB *self = GABBLE_BYTESTREAM_IBB (iface); GabbleBytestreamIBBPrivate *priv = GABBLE_BYTESTREAM_IBB_GET_PRIVATE (self); WockyStanza *msg; WockyNode *si; if (priv->state != GABBLE_BYTESTREAM_STATE_LOCAL_PENDING) { /* The stream was previoulsy or automatically accepted */ return; } msg = gabble_bytestream_factory_make_accept_iq (priv->peer_jid, priv->stream_init_id, NS_IBB); si = wocky_node_get_child_ns ( wocky_stanza_get_top_node (msg), "si", NS_SI); g_assert (si != NULL); if (func != NULL) { /* let the caller add his profile specific data */ func (si, user_data); } if (_gabble_connection_send (priv->conn, msg, NULL)) { DEBUG ("stream %s with %s is now accepted", priv->stream_id, priv->peer_jid); g_object_set (self, "state", GABBLE_BYTESTREAM_STATE_ACCEPTED, NULL); } g_object_unref (msg); } static void gabble_bytestream_ibb_decline (GabbleBytestreamIBB *self, GError *error) { GabbleBytestreamIBBPrivate *priv = GABBLE_BYTESTREAM_IBB_GET_PRIVATE (self); WockyStanza *msg; g_return_if_fail (priv->state == GABBLE_BYTESTREAM_STATE_LOCAL_PENDING); msg = wocky_stanza_build (WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_ERROR, NULL, priv->peer_jid, '@', "id", priv->stream_init_id, NULL); if (error != NULL) { wocky_stanza_error_to_node (error, wocky_stanza_get_top_node (msg)); } else { GError fallback = { WOCKY_XMPP_ERROR, WOCKY_XMPP_ERROR_FORBIDDEN, "Offer Declined" }; wocky_stanza_error_to_node (&fallback, wocky_stanza_get_top_node (msg)); } _gabble_connection_send (priv->conn, msg, NULL); g_object_unref (msg); g_object_set (self, "state", GABBLE_BYTESTREAM_STATE_CLOSED, NULL); } /* * gabble_bytestream_ibb_close * * Implements gabble_bytestream_iface_close on GabbleBytestreamIface */ static void gabble_bytestream_ibb_close (GabbleBytestreamIface *iface, GError *error) { GabbleBytestreamIBB *self = GABBLE_BYTESTREAM_IBB (iface); GabbleBytestreamIBBPrivate *priv = GABBLE_BYTESTREAM_IBB_GET_PRIVATE (self); WockyPorter *porter = wocky_session_get_porter (priv->conn->session); GSList *l; if (priv->state == GABBLE_BYTESTREAM_STATE_CLOSED) /* bytestream already closed, do nothing */ return; /* Send error for pending IQ's */ priv->received_stanzas_not_acked = g_slist_reverse ( priv->received_stanzas_not_acked); for (l = priv->received_stanzas_not_acked; l != NULL; l = g_slist_next (l)) wocky_porter_send_iq_error (porter, l->data, WOCKY_XMPP_ERROR_ITEM_NOT_FOUND, NULL); g_slist_free (priv->received_stanzas_not_acked); priv->received_stanzas_not_acked = NULL; if (priv->state == GABBLE_BYTESTREAM_STATE_LOCAL_PENDING) { /* Stream was created using SI so we decline the request */ gabble_bytestream_ibb_decline (self, error); } else { if (priv->write_buffer != NULL) { DEBUG ("write buffer is not empty. Wait before sending close stanza"); g_object_set (self, "state", GABBLE_BYTESTREAM_STATE_CLOSING, NULL); } else { send_close_stanza (self); g_object_set (self, "state", GABBLE_BYTESTREAM_STATE_CLOSED, NULL); } } } static void ibb_init_reply_cb (GabbleConnection *conn, WockyStanza *sent_msg, WockyStanza *reply_msg, GObject *obj, gpointer user_data) { GabbleBytestreamIBB *self = GABBLE_BYTESTREAM_IBB (obj); GError *error = NULL; if (!wocky_stanza_extract_errors (reply_msg, NULL, &error, NULL, NULL)) { /* yeah, stream initiated */ DEBUG ("IBB stream initiated"); g_object_set (self, "state", GABBLE_BYTESTREAM_STATE_OPEN, NULL); } else { DEBUG ("error during IBB initiation: %s", error->message); g_clear_error (&error); g_object_set (self, "state", GABBLE_BYTESTREAM_STATE_CLOSED, NULL); } } /* * gabble_bytestream_ibb_initiate * * Implements gabble_bytestream_iface_initiate on GabbleBytestreamIface */ static gboolean gabble_bytestream_ibb_initiate (GabbleBytestreamIface *iface) { GabbleBytestreamIBB *self = GABBLE_BYTESTREAM_IBB (iface); GabbleBytestreamIBBPrivate *priv = GABBLE_BYTESTREAM_IBB_GET_PRIVATE (self); WockyStanza *msg; gchar *block_size; if (priv->state != GABBLE_BYTESTREAM_STATE_INITIATING) { DEBUG ("bytestream is not is the initiating state (state %d", priv->state); return FALSE; } block_size = g_strdup_printf ("%u", priv->block_size); msg = wocky_stanza_build (WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_SET, NULL, priv->peer_jid, '(', "open", ':', NS_IBB, '@', "sid", priv->stream_id, '@', "block-size", block_size, ')', NULL); g_free (block_size); if (!_gabble_connection_send_with_reply (priv->conn, msg, ibb_init_reply_cb, G_OBJECT (self), NULL, NULL)) { DEBUG ("Error when sending IBB init stanza"); g_object_unref (msg); return FALSE; } g_object_unref (msg); return TRUE; } static void gabble_bytestream_ibb_block_reading (GabbleBytestreamIface *iface, gboolean block) { GabbleBytestreamIBB *self = GABBLE_BYTESTREAM_IBB (iface); GabbleBytestreamIBBPrivate *priv = GABBLE_BYTESTREAM_IBB_GET_PRIVATE (self); if (priv->read_blocked == block) return; priv->read_blocked = block; DEBUG ("%s the transport bytestream", block ? "block": "unblock"); if (priv->read_buffer != NULL && !block) { GSList *l; DEBUG ("Bytestream unblocked, flushing the buffer"); g_signal_emit_by_name (G_OBJECT (self), "data-received", priv->peer_handle, priv->read_buffer); g_string_free (priv->read_buffer, TRUE); priv->read_buffer = NULL; /* ack pending stanzas */ priv->received_stanzas_not_acked = g_slist_reverse ( priv->received_stanzas_not_acked); for (l = priv->received_stanzas_not_acked; l != NULL; l = g_slist_next (l)) { WockyStanza *iq = (WockyStanza *) l->data; _gabble_connection_acknowledge_set_iq (priv->conn, iq); g_object_unref (iq); } g_slist_free (priv->received_stanzas_not_acked); priv->received_stanzas_not_acked = NULL; } } void gabble_bytestream_ibb_close_received (GabbleBytestreamIBB *self, WockyStanza *iq) { GabbleBytestreamIBBPrivate *priv = GABBLE_BYTESTREAM_IBB_GET_PRIVATE (self); DEBUG ("received IBB close stanza. Closing bytestream"); priv->close_iq_to_ack = g_object_ref (iq); gabble_bytestream_ibb_close (GABBLE_BYTESTREAM_IFACE (self), NULL); } static void bytestream_iface_init (gpointer g_iface, gpointer iface_data) { GabbleBytestreamIfaceClass *klass = (GabbleBytestreamIfaceClass *) g_iface; klass->initiate = gabble_bytestream_ibb_initiate; klass->send = gabble_bytestream_ibb_send; klass->close = gabble_bytestream_ibb_close; klass->accept = gabble_bytestream_ibb_accept; klass->block_reading = gabble_bytestream_ibb_block_reading; } ����������������������������������������������������������������������������������������������������������������������������telepathy-gabble-0.18.2/src/bytestream-ibb.h��������������������������������������������������������0000644�0001750�0001750�00000004654�12200204333�020664� 0����������������������������������������������������������������������������������������������������ustar�00smcv����������������������������smcv����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * bytestream-ibb.h - Header for GabbleBytestreamIBB * Copyright (C) 2007 Collabora Ltd. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 __GABBLE_BYTESTREAM_IBB_H__ #define __GABBLE_BYTESTREAM_IBB_H__ #include <glib-object.h> #include <wocky/wocky.h> #include <telepathy-glib/telepathy-glib.h> G_BEGIN_DECLS typedef struct _GabbleBytestreamIBB GabbleBytestreamIBB; typedef struct _GabbleBytestreamIBBClass GabbleBytestreamIBBClass; typedef struct _GabbleBytestreamIBBPrivate GabbleBytestreamIBBPrivate; struct _GabbleBytestreamIBBClass { GObjectClass parent_class; }; struct _GabbleBytestreamIBB { GObject parent; GabbleBytestreamIBBPrivate *priv; }; GType gabble_bytestream_ibb_get_type (void); /* TYPE MACROS */ #define GABBLE_TYPE_BYTESTREAM_IBB \ (gabble_bytestream_ibb_get_type ()) #define GABBLE_BYTESTREAM_IBB(obj) \ (G_TYPE_CHECK_INSTANCE_CAST((obj), GABBLE_TYPE_BYTESTREAM_IBB,\ GabbleBytestreamIBB)) #define GABBLE_BYTESTREAM_IBB_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST((klass), GABBLE_TYPE_BYTESTREAM_IBB,\ GabbleBytestreamIBBClass)) #define GABBLE_IS_BYTESTREAM_IBB(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE((obj), GABBLE_TYPE_BYTESTREAM_IBB)) #define GABBLE_IS_BYTESTREAM_IBB_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE((klass), GABBLE_TYPE_BYTESTREAM_IBB)) #define GABBLE_BYTESTREAM_IBB_GET_CLASS(obj) \ (G_TYPE_INSTANCE_GET_CLASS ((obj), GABBLE_TYPE_BYTESTREAM_IBB,\ GabbleBytestreamIBBClass)) void gabble_bytestream_ibb_receive (GabbleBytestreamIBB *ibb, WockyStanza *msg, gboolean is_iq); void gabble_bytestream_ibb_close_received (GabbleBytestreamIBB *ibb, WockyStanza *iq); G_END_DECLS #endif /* #ifndef __GABBLE_BYTESTREAM_IBB_H__ */ ������������������������������������������������������������������������������������telepathy-gabble-0.18.2/src/bytestream-factory.c����������������������������������������������������0000644�0001750�0001750�00000203726�12200204333�021573� 0����������������������������������������������������������������������������������������������������ustar�00smcv����������������������������smcv����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * bytestream-factory.c - Source for GabbleBytestreamFactory * Copyright (C) 2007-2008 Collabora Ltd. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 "config.h" #include "bytestream-factory.h" #include <string.h> #include <stdlib.h> #include <dbus/dbus-glib.h> #include <dbus/dbus-glib-lowlevel.h> #include <wocky/wocky.h> #include <telepathy-glib/telepathy-glib.h> #include <telepathy-glib/telepathy-glib-dbus.h> #define DEBUG_FLAG GABBLE_DEBUG_BYTESTREAM #include "bytestream-ibb.h" #include "bytestream-iface.h" #include "bytestream-muc.h" #include "bytestream-multiple.h" #include "bytestream-socks5.h" #include "connection.h" #include "conn-util.h" #include "debug.h" #include "disco.h" #include "namespaces.h" #include "presence-cache.h" #include "private-tubes-factory.h" #include "util.h" G_DEFINE_TYPE (GabbleBytestreamFactory, gabble_bytestream_factory, G_TYPE_OBJECT); /* The number of proxy we'll try to have at a minimum in the cache. */ #define NB_MIN_SOCKS5_PROXIES 3 #define FALLBACK_PROXY_CACHE_SIZE 5 #define SOCKS5_PROXY_TIMEOUT 10 #define TELEPATHY_PROXIES_SERVICE "proxies.telepathy.im" /* The life time (in seconds) of the proxies list discovered using * TELEPATHY_PROXIES_SERVICE */ /* 6 hours */ #define PROXIES_LIST_LIFE_TIME 6 * 60 * 60 /* properties */ enum { PROP_CONNECTION = 1, LAST_PROPERTY }; typedef struct { gchar *jid; gchar *stream; } BytestreamIdentifier; typedef struct { const gchar *jid; const gchar *stream; } ConstBytestreamIdentifier; static gboolean bytestream_id_equal (gconstpointer v1, gconstpointer v2) { const ConstBytestreamIdentifier *left = v1; const ConstBytestreamIdentifier *right = v2; return (!tp_strdiff (left->jid, right->jid)) && (!tp_strdiff (left->stream, right->stream)); } static guint bytestream_id_hash (gconstpointer v) { const ConstBytestreamIdentifier *bsid = v; return g_str_hash (bsid->jid) ^ g_str_hash (bsid->stream); } static BytestreamIdentifier * bytestream_id_new (GabbleBytestreamIface *bytestream) { BytestreamIdentifier *bsid = g_slice_new (BytestreamIdentifier); g_object_get (bytestream, "stream-id", &(bsid->stream), "peer-jid", &(bsid->jid), NULL); return bsid; } static void bytestream_id_free (gpointer v) { BytestreamIdentifier *bsid = v; g_free (bsid->jid); g_free (bsid->stream); g_slice_free (BytestreamIdentifier, bsid); } static GabbleSocks5Proxy * gabble_socks5_proxy_new (const gchar *jid, const gchar *host, guint16 port) { GabbleSocks5Proxy *proxy; proxy = g_slice_new (GabbleSocks5Proxy); proxy->jid = g_strdup (jid); proxy->host = g_strdup (host); proxy->port = port; return proxy; } static void gabble_socks5_proxy_free (GabbleSocks5Proxy *proxy) { g_free (proxy->jid); g_free (proxy->host); g_slice_free (GabbleSocks5Proxy, proxy); } struct _GabbleBytestreamFactoryPrivate { GabbleConnection *conn; guint iq_si_cb; guint iq_ibb_cb; guint iq_socks5_cb; guint msg_data_cb; /* SI-initiated bytestreams - data sent by normal messages, IQs used to * open and close. * * BytestreamIdentifier -> GabbleBytestreamIBB */ GHashTable *ibb_bytestreams; /* BytestreamIdentifier -> GabbleBytestreamSocks5 */ GHashTable *socks5_bytestreams; /* MUC pseudo-IBB - data sent by groupchat messages, IQs not allowed. * * BytestreamIdentifier -> GabbleBytestreamMuc */ GHashTable *muc_bytestreams; /* SI-initiated bytestreams - real data sent through another bytestream. * * BytestreamIdentifier -> GabbleBytestreamMultiple */ GHashTable *multiple_bytestreams; /* List of GabbleSocks5Proxy discovered on the connection */ GSList *socks5_proxies; /* List of GabbleSocks5Proxy found using the fallback-socks5-proxies param */ GSList *socks5_fallback_proxies; /* List of SOCKS5's jids that have not been queried yet */ GSList *socks5_potential_proxies; /* Next proxy on socks5_potential_proxies that we'll query */ GSList *next_query; /* Time stamp of the proxies list received from TELEPATHY_PROXIES_SERVICE */ GTimeVal proxies_list_stamp; gboolean dispose_has_run; }; #define GABBLE_BYTESTREAM_FACTORY_GET_PRIVATE(obj) ((obj)->priv) static gboolean bytestream_factory_msg_data_cb ( WockyPorter *porter, WockyStanza *msg, gpointer user_data); static gboolean bytestream_factory_iq_si_cb ( WockyPorter *porter, WockyStanza *msg, gpointer user_data); static gboolean bytestream_factory_iq_ibb_cb ( WockyPorter *porter, WockyStanza *msg, gpointer user_data); static gboolean handle_socks5_query_iq ( WockyPorter *porter, WockyStanza *msg, gpointer user_data); static void query_proxies (GabbleBytestreamFactory *self, guint nb_proxies_needed); static GSList * randomize_g_slist (GSList *list); static void gabble_bytestream_factory_init (GabbleBytestreamFactory *self) { GabbleBytestreamFactoryPrivate *priv = G_TYPE_INSTANCE_GET_PRIVATE (self, GABBLE_TYPE_BYTESTREAM_FACTORY, GabbleBytestreamFactoryPrivate); self->priv = priv; priv->ibb_bytestreams = g_hash_table_new_full (bytestream_id_hash, bytestream_id_equal, bytestream_id_free, g_object_unref); priv->muc_bytestreams = g_hash_table_new_full (bytestream_id_hash, bytestream_id_equal, bytestream_id_free, g_object_unref); priv->socks5_bytestreams = g_hash_table_new_full (bytestream_id_hash, bytestream_id_equal, bytestream_id_free, g_object_unref); priv->multiple_bytestreams = g_hash_table_new_full (bytestream_id_hash, bytestream_id_equal, bytestream_id_free, g_object_unref); memset (&priv->proxies_list_stamp, 0, sizeof (GTimeVal)); } static gint cmp_proxy (gconstpointer a, gconstpointer b) { GabbleSocks5Proxy *proxy_a = (GabbleSocks5Proxy *) a; GabbleSocks5Proxy *proxy_b = (GabbleSocks5Proxy *) b; /* Streamhost are identified by their jid */ return strcmp (proxy_a->jid, proxy_b->jid); } static void add_proxy_to_list (GabbleBytestreamFactory *self, GabbleSocks5Proxy *proxy, gboolean fallback) { GabbleBytestreamFactoryPrivate *priv = GABBLE_BYTESTREAM_FACTORY_GET_PRIVATE ( self); GSList **list; GSList *found; if (fallback) { list = &priv->socks5_fallback_proxies; } else { list = &priv->socks5_proxies; } found = g_slist_find_custom (*list, proxy, cmp_proxy); if (found != NULL) { DEBUG ("%s SOCKS5 proxy (%s %s:%d) is already known; " "move it to the head of the list", fallback ? "Fallback": "Discovered", proxy->jid, proxy->host, proxy->port); *list = g_slist_delete_link (*list, found); } else { DEBUG ("Add %s SOCKS5 proxy: %s %s:%d", fallback ? "fallback": "discovered", proxy->jid, proxy->host, proxy->port); if (fallback && g_slist_length (*list) >= FALLBACK_PROXY_CACHE_SIZE) { GSList *last; GabbleSocks5Proxy *oldest; last = g_slist_last (*list); oldest = last->data; DEBUG ("Proxy cache is full, remove the oldest entry (%s)", oldest->jid); *list = g_slist_delete_link (*list, last); gabble_socks5_proxy_free (oldest); } } *list = g_slist_prepend (*list, proxy); } static void socks5_proxy_query_reply_cb (GabbleConnection *conn, WockyStanza *sent_msg, WockyStanza *reply_msg, GObject *obj, gpointer user_data) { GabbleBytestreamFactory *self = GABBLE_BYTESTREAM_FACTORY (obj); GabbleBytestreamFactoryPrivate *priv = GABBLE_BYTESTREAM_FACTORY_GET_PRIVATE ( self); WockyNode *query, *streamhost; const gchar *from; const gchar *jid, *host, *portstr; gint64 port; GabbleSocks5Proxy *proxy; gboolean fallback = GPOINTER_TO_INT (user_data); GSList *found = NULL; from = wocky_node_get_attribute ( wocky_stanza_get_top_node (reply_msg), "from"); if (from == NULL) goto fail; if (wocky_stanza_extract_errors (reply_msg, NULL, NULL, NULL, NULL)) goto fail; query = wocky_node_get_child_ns ( wocky_stanza_get_top_node (reply_msg), "query", NS_BYTESTREAMS); if (query == NULL) goto fail; streamhost = wocky_node_get_child (query, "streamhost"); if (streamhost == NULL) goto fail; jid = wocky_node_get_attribute (streamhost, "jid"); host = wocky_node_get_attribute (streamhost, "host"); portstr = wocky_node_get_attribute (streamhost, "port"); if (jid == NULL || host == NULL || portstr == NULL) goto fail; port = g_ascii_strtoll (portstr, NULL, 10); if (port <= 0 || port > G_MAXUINT16) goto fail; proxy = gabble_socks5_proxy_new (jid, host, port); add_proxy_to_list (self , proxy, fallback); return; fail: if (fallback && from != NULL) { /* Remove the buggy proxy so we won't query it anymore */ found = g_slist_find_custom (priv->socks5_potential_proxies, from, (GCompareFunc) strcmp); if (found != NULL) { DEBUG ("remove proxy %s", from); g_free (found->data); priv->socks5_potential_proxies = g_slist_delete_link ( priv->socks5_potential_proxies, found); } } /* Try to get another proxy as this one failed */ query_proxies (self, 1); } static void send_proxy_query (GabbleBytestreamFactory *self, const gchar *jid, gboolean fallback) { GabbleBytestreamFactoryPrivate *priv = GABBLE_BYTESTREAM_FACTORY_GET_PRIVATE ( self); WockyStanza *query; DEBUG ("send SOCKS5 query to %s", jid); query = wocky_stanza_build (WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_GET, NULL, jid, '(', "query", ':', NS_BYTESTREAMS, ')', NULL); _gabble_connection_send_with_reply (priv->conn, query, socks5_proxy_query_reply_cb, G_OBJECT (self), GINT_TO_POINTER (fallback), NULL); g_object_unref (query); } static void disco_item_found_cb (GabbleDisco *disco, GabbleDiscoItem *item, GabbleBytestreamFactory *self) { if (tp_strdiff (item->category, "proxy") || tp_strdiff (item->type, "bytestreams")) return; send_proxy_query (self, item->jid, FALSE); } static void query_proxies (GabbleBytestreamFactory *self, guint nb_proxies_needed) { GabbleBytestreamFactoryPrivate *priv = GABBLE_BYTESTREAM_FACTORY_GET_PRIVATE ( self); guint i; /* We don't want to query more than once the same proxy */ nb_proxies_needed = MIN (nb_proxies_needed, g_slist_length (priv->socks5_potential_proxies)); for (i = 0; i < nb_proxies_needed; i++) { gchar *jid; if (priv->next_query == NULL) priv->next_query = priv->socks5_potential_proxies; jid = priv->next_query->data; send_proxy_query (self, jid, TRUE); priv->next_query = g_slist_next (priv->next_query); } } static void proxies_disco_cb (GabbleDisco *disco, GabbleDiscoRequest *request, const gchar *j, const gchar *n, WockyNode *query_result, GError *error, gpointer user_data) { GabbleBytestreamFactory *self = GABBLE_BYTESTREAM_FACTORY (user_data); GabbleBytestreamFactoryPrivate *priv = GABBLE_BYTESTREAM_FACTORY_GET_PRIVATE ( self); WockyNodeIter i; WockyNode *node; GSList *new_list = NULL; if (error != NULL) { DEBUG ("disco failed: %s", error->message); return; } wocky_node_iter_init (&i, query_result, "item", NULL); while (wocky_node_iter_next (&i, &node)) { const gchar *jid = wocky_node_get_attribute (node, "jid"); if (jid == NULL) continue; DEBUG ("Discovered proxy %s", jid); new_list = g_slist_prepend (new_list, g_strdup (jid)); } g_get_current_time (&priv->proxies_list_stamp); if (new_list == NULL) return; /* replace the old list by the new one */ g_slist_foreach (priv->socks5_potential_proxies, (GFunc) g_free, NULL); g_slist_free (priv->socks5_potential_proxies); /* randomize the list to not always use the same proxies */ priv->socks5_potential_proxies = randomize_g_slist (new_list); priv->next_query = priv->socks5_potential_proxies; gabble_bytestream_factory_query_socks5_proxies (self); } /* Query TELEPATHY_PROXIES_SERVICE to get a list of proxies */ static void get_proxies_list (GabbleBytestreamFactory *self) { GabbleBytestreamFactoryPrivate *priv = GABBLE_BYTESTREAM_FACTORY_GET_PRIVATE ( self); DEBUG ("Ask %s for proxies", TELEPATHY_PROXIES_SERVICE); gabble_disco_request (priv->conn->disco, GABBLE_DISCO_TYPE_ITEMS, TELEPATHY_PROXIES_SERVICE, NULL, proxies_disco_cb, self, G_OBJECT (self), NULL); } /* ask to the factory to try to find more proxies if needed */ void gabble_bytestream_factory_query_socks5_proxies (GabbleBytestreamFactory *self) { GabbleBytestreamFactoryPrivate *priv = GABBLE_BYTESTREAM_FACTORY_GET_PRIVATE ( self); guint nb_proxies_found; guint nb_proxies_needed; GTimeVal now; if (priv->socks5_potential_proxies == NULL) { DEBUG ("No proxies list; request one"); get_proxies_list (self); return; } g_get_current_time (&now); if (now.tv_sec - priv->proxies_list_stamp.tv_sec > PROXIES_LIST_LIFE_TIME) { DEBUG ("Proxies list has expired; request a new one"); get_proxies_list (self); } nb_proxies_found = g_slist_length (priv->socks5_proxies) + g_slist_length (priv->socks5_fallback_proxies); if (nb_proxies_found >= NB_MIN_SOCKS5_PROXIES) { DEBUG ("we already have discovered enough proxies (%u); " "request just one to refresh our cache", nb_proxies_found); nb_proxies_needed = 1; } else { nb_proxies_needed = NB_MIN_SOCKS5_PROXIES - nb_proxies_found; DEBUG ("Need %u more proxies", nb_proxies_needed); } query_proxies (self, nb_proxies_needed); } static GSList * randomize_g_slist (GSList *list) { guint len; guint i; GSList *new_head, *new_tail; len = g_slist_length (list); if (len <= 1) return list; i = g_random_int_range (0, len); if (i == 0) return list; /* Cut the list at the i th position and make it the new head of the * list */ new_tail = g_slist_nth (list, i - 1); g_assert (new_tail != NULL); new_head = new_tail->next; g_assert (new_head != NULL); new_tail->next = NULL; return g_slist_concat (new_head, list); } static void porter_available_cb ( GabbleConnection *conn, WockyPorter *porter, gpointer user_data) { GabbleBytestreamFactory *self = GABBLE_BYTESTREAM_FACTORY (user_data); GabbleBytestreamFactoryPrivate *priv = self->priv; priv->msg_data_cb = wocky_porter_register_handler_from_anyone (porter, WOCKY_STANZA_TYPE_MESSAGE, WOCKY_STANZA_SUB_TYPE_NONE, WOCKY_PORTER_HANDLER_PRIORITY_MAX, bytestream_factory_msg_data_cb, self, NULL); priv->iq_si_cb = wocky_porter_register_handler_from_anyone (porter, WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_SET, WOCKY_PORTER_HANDLER_PRIORITY_MAX, bytestream_factory_iq_si_cb, self, '(', "si", ':', NS_SI, ')', NULL); priv->iq_ibb_cb = wocky_porter_register_handler_from_anyone (porter, WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_NONE, WOCKY_PORTER_HANDLER_PRIORITY_MAX, bytestream_factory_iq_ibb_cb, self, NULL); priv->iq_socks5_cb = wocky_porter_register_handler_from_anyone (porter, WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_SET, WOCKY_PORTER_HANDLER_PRIORITY_MAX, handle_socks5_query_iq, self, '(', "query", ':', NS_BYTESTREAMS, ')', NULL); } static void conn_status_changed_cb (GabbleConnection *conn, TpConnectionStatus status, TpConnectionStatusReason reason, gpointer user_data) { GabbleBytestreamFactory *self = GABBLE_BYTESTREAM_FACTORY (user_data); GabbleBytestreamFactoryPrivate *priv = GABBLE_BYTESTREAM_FACTORY_GET_PRIVATE ( self); if (status == TP_CONNECTION_STATUS_CONNECTED) { GStrv jids; guint i; /* we can't intialize socks5_potential_proxies in the constructor * because Connection's properties are not set yet at this point */ g_object_get (priv->conn, "fallback-socks5-proxies", &jids, NULL); for (i = 0; jids != NULL && jids[i] != NULL; i++) { priv->socks5_potential_proxies = g_slist_prepend ( priv->socks5_potential_proxies, g_strdup (jids[i])); } /* randomize the list to not always use the same proxies */ priv->socks5_potential_proxies = randomize_g_slist ( priv->socks5_potential_proxies); priv->next_query = priv->socks5_potential_proxies; g_strfreev (jids); } } static GObject * gabble_bytestream_factory_constructor (GType type, guint n_props, GObjectConstructParam *props) { GObject *obj; GabbleBytestreamFactory *self; GabbleBytestreamFactoryPrivate *priv; obj = G_OBJECT_CLASS (gabble_bytestream_factory_parent_class)-> constructor (type, n_props, props); self = GABBLE_BYTESTREAM_FACTORY (obj); priv = GABBLE_BYTESTREAM_FACTORY_GET_PRIVATE (self); /* Track SOCKS5 proxy available on the connection */ gabble_signal_connect_weak (priv->conn->disco, "item-found", G_CALLBACK (disco_item_found_cb), G_OBJECT (self)); gabble_signal_connect_weak (priv->conn, "status-changed", G_CALLBACK (conn_status_changed_cb), G_OBJECT (self)); tp_g_signal_connect_object (priv->conn, "porter-available", G_CALLBACK (porter_available_cb), self, 0); return obj; } static void gabble_bytestream_factory_dispose (GObject *object) { GabbleBytestreamFactory *self = GABBLE_BYTESTREAM_FACTORY (object); GabbleBytestreamFactoryPrivate *priv = GABBLE_BYTESTREAM_FACTORY_GET_PRIVATE (self); GSList *proxies, *l; if (priv->dispose_has_run) return; DEBUG ("dispose called"); priv->dispose_has_run = TRUE; if (priv->msg_data_cb != 0) { WockyPorter *porter = wocky_session_get_porter (priv->conn->session); wocky_porter_unregister_handler (porter, priv->msg_data_cb); wocky_porter_unregister_handler (porter, priv->iq_si_cb); wocky_porter_unregister_handler (porter, priv->iq_ibb_cb); wocky_porter_unregister_handler (porter, priv->iq_socks5_cb); } g_hash_table_unref (priv->ibb_bytestreams); priv->ibb_bytestreams = NULL; g_hash_table_unref (priv->muc_bytestreams); priv->muc_bytestreams = NULL; g_hash_table_unref (priv->socks5_bytestreams); priv->socks5_bytestreams = NULL; g_hash_table_unref (priv->multiple_bytestreams); priv->multiple_bytestreams = NULL; proxies = g_slist_concat (priv->socks5_proxies, priv->socks5_fallback_proxies); for (l = proxies; l != NULL; l = g_slist_next (l)) { GabbleSocks5Proxy *proxy = (GabbleSocks5Proxy *) l->data; gabble_socks5_proxy_free (proxy); } g_slist_free (proxies); priv->socks5_proxies = NULL; priv->socks5_fallback_proxies = NULL; g_slist_foreach (priv->socks5_potential_proxies, (GFunc) g_free, NULL); g_slist_free (priv->socks5_potential_proxies); priv->socks5_potential_proxies = NULL; if (G_OBJECT_CLASS (gabble_bytestream_factory_parent_class)->dispose) G_OBJECT_CLASS (gabble_bytestream_factory_parent_class)->dispose (object); } static void gabble_bytestream_factory_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { GabbleBytestreamFactory *self = GABBLE_BYTESTREAM_FACTORY (object); GabbleBytestreamFactoryPrivate *priv = GABBLE_BYTESTREAM_FACTORY_GET_PRIVATE (self); switch (property_id) { case PROP_CONNECTION: g_value_set_object (value, priv->conn); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void gabble_bytestream_factory_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { GabbleBytestreamFactory *self = GABBLE_BYTESTREAM_FACTORY (object); GabbleBytestreamFactoryPrivate *priv = GABBLE_BYTESTREAM_FACTORY_GET_PRIVATE (self); switch (property_id) { case PROP_CONNECTION: priv->conn = g_value_get_object (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void gabble_bytestream_factory_class_init ( GabbleBytestreamFactoryClass *gabble_bytestream_factory_class) { GObjectClass *object_class = G_OBJECT_CLASS (gabble_bytestream_factory_class); GParamSpec *param_spec; g_type_class_add_private (gabble_bytestream_factory_class, sizeof (GabbleBytestreamFactoryPrivate)); object_class->constructor = gabble_bytestream_factory_constructor; object_class->dispose = gabble_bytestream_factory_dispose; object_class->get_property = gabble_bytestream_factory_get_property; object_class->set_property = gabble_bytestream_factory_set_property; param_spec = g_param_spec_object ( "connection", "GabbleConnection object", "Gabble connection object that owns this bytestream factory object.", GABBLE_TYPE_CONNECTION, G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, PROP_CONNECTION, param_spec); } static void remove_bytestream (GabbleBytestreamFactory *self, GabbleBytestreamIface *bytestream) { GabbleBytestreamFactoryPrivate *priv = GABBLE_BYTESTREAM_FACTORY_GET_PRIVATE (self); BytestreamIdentifier bsid = { NULL, NULL }; guint handle_type; GHashTable *table = NULL; g_object_get (bytestream, "stream-id", &(bsid.stream), "peer-jid", &(bsid.jid), "peer-handle-type", &handle_type, NULL); if (handle_type == TP_HANDLE_TYPE_ROOM) { table = priv->muc_bytestreams; } else { if (GABBLE_IS_BYTESTREAM_IBB (bytestream)) table = priv->ibb_bytestreams; else if (GABBLE_IS_BYTESTREAM_SOCKS5 (bytestream)) table = priv->socks5_bytestreams; else if (GABBLE_IS_BYTESTREAM_MULTIPLE (bytestream)) table = priv->multiple_bytestreams; } if (table == NULL) return; DEBUG ("removing bytestream: <%s> from <%s>", bsid.stream, bsid.jid); g_hash_table_remove (table, &bsid); g_free (bsid.stream); g_free (bsid.jid); } /** * streaminit_parse_request * * Parses a SI request, or returns FALSE if it can't be parsed. * * The items in the linked list of stream methods are borrowed * from the message; their lifetime is only as long as that of the * message. */ static gboolean streaminit_parse_request (WockyStanza *message, WockyNode *si, const gchar **profile, const gchar **from, const gchar **stream_id, const gchar **stream_init_id, const gchar **mime_type, GSList **stream_methods, gboolean *multiple) { WockyNode *iq = wocky_stanza_get_top_node (message); WockyNode *feature, *x, *si_multiple, *field; WockyNodeIter i, j; *stream_init_id = wocky_node_get_attribute (iq, "id"); *from = wocky_node_get_attribute (iq, "from"); if (*from == NULL) { STANZA_DEBUG (message, "got a message without a from field"); return FALSE; } /* Parse <si> */ *stream_id = wocky_node_get_attribute (si, "id"); if (*stream_id == NULL) { STANZA_DEBUG (message, "got a SI request without a stream id field"); return FALSE; } *mime_type = wocky_node_get_attribute (si, "mime-type"); /* if no mime_type is defined, XEP-0095 says to assume "binary/octect-stream" * which is presumably a typo for "application/octet-stream" */ *profile = wocky_node_get_attribute (si, "profile"); if (*profile == NULL) { STANZA_DEBUG (message, "got a SI request without a profile field"); return FALSE; } /* Parse <feature> */ feature = wocky_node_get_child_ns (si, "feature", NS_FEATURENEG); if (feature == NULL) { STANZA_DEBUG (message, "got a SI request without a feature field"); return FALSE; } x = wocky_node_get_child_ns (feature, "x", NS_X_DATA); if (x == NULL) { STANZA_DEBUG (message, "got a SI request without a X data field"); return FALSE; } wocky_node_iter_init (&i, x, NULL, NULL); while (wocky_node_iter_next (&i, &field)) { WockyNode *stream_method; if (tp_strdiff (wocky_node_get_attribute (field, "var"), "stream-method")) /* some future field, ignore it */ continue; if (tp_strdiff (wocky_node_get_attribute (field, "type"), "list-single")) { STANZA_DEBUG (message, "SI request's stream-method field was " "not of type list-single"); return FALSE; } /* Get the stream methods offered */ *stream_methods = NULL; wocky_node_iter_init (&j, field, NULL, NULL); while (wocky_node_iter_next (&j, &stream_method)) { WockyNode *value; const gchar *stream_method_str; value = wocky_node_get_child (stream_method, "value"); if (value == NULL) continue; stream_method_str = value->content; if (!tp_strdiff (stream_method_str, "")) continue; DEBUG ("Got stream-method %s", stream_method_str); /* Append to the stream_methods list */ *stream_methods = g_slist_append (*stream_methods, (gchar *) stream_method_str); } /* no need to parse the rest of the fields, we've found the one we * wanted */ break; } if (*stream_methods == NULL) { STANZA_DEBUG (message, "got a SI request without stream method proposed"); return FALSE; } si_multiple = wocky_node_get_child_ns (si, "si-multiple", NS_SI_MULTIPLE); if (si_multiple == NULL) *multiple = FALSE; else *multiple = TRUE; return TRUE; } /** * gabble_bytestream_factory_make_stream_init_iq * * @full_jid: the full jid of the contact to whom we want to offer the stream * @stream_id: the stream ID of the new stream * @profile: the profile associated with the stream * * Create a SI request IQ as described in XEP-0095. * * The MIME type is not set - the receiving client will assume * application/octet-stream unless the caller sets a MIME type explicitly. */ WockyStanza * gabble_bytestream_factory_make_stream_init_iq (const gchar *full_jid, const gchar *stream_id, const gchar *profile) { return wocky_stanza_build (WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_SET, NULL, full_jid, '(', "si", ':', NS_SI, '@', "id", stream_id, '@', "profile", profile, '(', "feature", ':', NS_FEATURENEG, '(', "x", ':', NS_X_DATA, '@', "type", "form", '(', "field", '@', "var", "stream-method", '@', "type", "list-single", '(', "option", '(', "value", '$', NS_BYTESTREAMS, ')', ')', '(', "option", '(', "value", '$', NS_IBB, ')', ')', ')', ')', ')', '(', "si-multiple", ':', NS_SI_MULTIPLE, ')', ')', NULL); } static gboolean stream_method_supported (const gchar *stream_method) { /* IBB */ if (!tp_strdiff (stream_method, NS_IBB)) return TRUE; /* Sock5 */ if (!tp_strdiff (stream_method, NS_BYTESTREAMS)) return TRUE; return FALSE; } static GabbleBytestreamMultiple *gabble_bytestream_factory_create_multiple ( GabbleBytestreamFactory *self, TpHandle peer_handle, const gchar *stream_id, const gchar *stream_init_id, const gchar *peer_resource, const gchar *self_jid, GabbleBytestreamState state); static GabbleBytestreamIBB *gabble_bytestream_factory_create_ibb ( GabbleBytestreamFactory *fac, TpHandle peer_handle, const gchar *stream_id, const gchar *stream_init_id, const gchar *peer_resource, GabbleBytestreamState state); static GabbleBytestreamSocks5 *gabble_bytestream_factory_create_socks5 ( GabbleBytestreamFactory *fac, TpHandle peer_handle, const gchar *stream_id, const gchar *stream_init_id, const gchar *peer_resource, const gchar *self_jid, GabbleBytestreamState state); static void si_tube_received (GabbleBytestreamFactory *self, WockyStanza *msg, WockyNode *si, GabbleBytestreamIface *bytestream, TpHandle peer_handle, TpHandle room_handle, const gchar *stream_id) { GabbleBytestreamFactoryPrivate *priv = GABBLE_BYTESTREAM_FACTORY_GET_PRIVATE (self); /* A Tubes SI request can be: * - a 1-1 new tube offer * - a 1-1 tube extra bytestream offer * - a muc tube extra bytestream offer */ if (wocky_node_get_child_ns (si, "tube", NS_TUBES) != NULL) { /* The SI request is a tube offer */ gabble_private_tubes_factory_handle_si_tube_request ( priv->conn->private_tubes_factory, bytestream, peer_handle, stream_id, msg); } else if (wocky_node_get_child_ns (si, "stream", NS_TUBES) != NULL) { /* The SI request is an extra bytestream for a 1-1 tube */ gabble_private_tubes_factory_handle_si_stream_request ( priv->conn->private_tubes_factory, bytestream, peer_handle, stream_id, msg); } else if (wocky_node_get_child_ns (si, "muc-stream", NS_TUBES) != NULL) { /* The SI request is an extra bytestream for a muc tube */ if (room_handle == 0) { GError e = { WOCKY_XMPP_ERROR, WOCKY_XMPP_ERROR_BAD_REQUEST, "<muc-stream> is only valid in a MUC context" }; gabble_bytestream_iface_close (bytestream, &e); } else { gabble_muc_factory_handle_si_stream_request (priv->conn->muc_factory, bytestream, room_handle, stream_id, msg); } } else { GError e = { WOCKY_XMPP_ERROR, WOCKY_XMPP_ERROR_BAD_REQUEST, "Invalid tube SI request: expected <tube>, <stream> or " "<muc-stream>" }; /* Invalid tube SI request */ STANZA_DEBUG (msg, e.message); gabble_bytestream_iface_close (bytestream, &e); } } /** * bytestream_factory_iq_si_cb: * * Called by Wocky when we get an incoming <iq>. This handler is concerned * with Stream Initiation requests (XEP-0095). * */ static gboolean bytestream_factory_iq_si_cb ( WockyPorter *porter, WockyStanza *msg, gpointer user_data) { GabbleBytestreamFactory *self = GABBLE_BYTESTREAM_FACTORY (user_data); GabbleBytestreamFactoryPrivate *priv = GABBLE_BYTESTREAM_FACTORY_GET_PRIVATE (self); TpHandleRepoIface *contact_repo = tp_base_connection_get_handles ( (TpBaseConnection *) priv->conn, TP_HANDLE_TYPE_CONTACT); TpHandleRepoIface *room_repo = tp_base_connection_get_handles ( (TpBaseConnection *) priv->conn, TP_HANDLE_TYPE_ROOM); WockyNode *si; TpHandle peer_handle = 0, room_handle; GabbleBytestreamIface *bytestream = NULL; GSList *l; const gchar *profile, *from, *stream_id, *stream_init_id, *mime_type; GSList *stream_methods = NULL; gboolean multiple; gchar *peer_resource = NULL; gchar *self_jid = NULL; si = wocky_node_get_child_ns ( wocky_stanza_get_top_node (msg), "si", NS_SI); g_return_val_if_fail (si != NULL, FALSE); /* after this point, the message is for us, so in all cases we either handle * it or send an error reply */ if (!streaminit_parse_request (msg, si, &profile, &from, &stream_id, &stream_init_id, &mime_type, &stream_methods, &multiple)) { wocky_porter_send_iq_error (porter, msg, WOCKY_XMPP_ERROR_BAD_REQUEST, "failed to parse SI request"); goto out; } DEBUG ("received a SI request"); room_handle = gabble_get_room_handle_from_jid (room_repo, from); if (room_handle == 0) { /* jid is not a muc jid so we need contact's resource */ if (!wocky_decode_jid (from, NULL, NULL, &peer_resource)) { DEBUG ("Got an SI IQ response from a bad JID. Ignoring."); goto out; } if (!peer_resource) { DEBUG ("Got an SI IQ response from a JID without a resource." "Ignoring."); goto out; } peer_handle = tp_handle_ensure (contact_repo, from, NULL, NULL); /* we are not in a muc so our own jid is the one in the 'to' attribute */ self_jid = g_strdup (wocky_node_get_attribute ( wocky_stanza_get_top_node (msg), "to")); } else { /* we are in a muc so need to get our muc jid */ GabbleMucChannel *muc; peer_handle = tp_handle_ensure (contact_repo, from, GUINT_TO_POINTER (GABBLE_JID_ROOM_MEMBER), NULL); muc = gabble_muc_factory_find_text_channel (priv->conn->muc_factory, room_handle); if (muc == NULL) { DEBUG ("Got an IQ from a muc in which we are not. Ignoring."); goto out; } g_object_get (muc, "self-jid", &self_jid, NULL); } if (peer_handle == 0) { wocky_porter_send_iq_error (porter, msg, WOCKY_XMPP_ERROR_JID_MALFORMED, NULL); goto out; } if (multiple) { DEBUG ("Receiver supports multi bytestreams"); bytestream = (GabbleBytestreamIface *) gabble_bytestream_factory_create_multiple (self, peer_handle, stream_id, stream_init_id, peer_resource, self_jid, GABBLE_BYTESTREAM_STATE_LOCAL_PENDING); } /* check stream method */ for (l = stream_methods; l != NULL; l = l->next) { if (multiple) { if (stream_method_supported (l->data)) { DEBUG ("add %s to multi-bytestream methods", (const gchar *) l->data); gabble_bytestream_multiple_add_stream_method ( GABBLE_BYTESTREAM_MULTIPLE (bytestream), l->data); } else { DEBUG ("skip unsupported stream method: %s", (const gchar *) l->data); } } else { /* We create the stream according the stream method chosen. * User has to accept it */ DEBUG ("Receiver doesn't support multi bytestreams. He chose %s", (const gchar *) l->data); bytestream = gabble_bytestream_factory_create_from_method (self, l->data, peer_handle, stream_id, stream_init_id, peer_resource, self_jid, GABBLE_BYTESTREAM_STATE_LOCAL_PENDING); if (bytestream != NULL) break; } } if (bytestream == NULL) { GError error = { WOCKY_SI_ERROR, WOCKY_SI_ERROR_NO_VALID_STREAMS, "" }; DEBUG ("SI request doesn't contain any supported stream methods."); wocky_porter_send_iq_gerror (porter, msg, &error); goto out; } if (multiple) { /* Is there at least one stream method? */ if (!gabble_bytestream_multiple_has_stream_method ( GABBLE_BYTESTREAM_MULTIPLE (bytestream))) { GError e = { WOCKY_SI_ERROR, WOCKY_SI_ERROR_NO_VALID_STREAMS, "" }; DEBUG ("No valid stream method in the multi bytestream. Closing"); gabble_bytestream_iface_close (bytestream, &e); goto out; } } /* Now that we have a bytestream, it's responsible for declining the IQ * if needed. */ /* We inform the right factory we received a SI request */ if (!tp_strdiff (profile, NS_TUBES)) { si_tube_received (self, msg, si, bytestream, peer_handle, room_handle, stream_id); } #ifdef ENABLE_FILE_TRANSFER else if (!tp_strdiff (profile, NS_FILE_TRANSFER)) { gabble_ft_manager_handle_si_request (priv->conn->ft_manager, bytestream, peer_handle, stream_id, msg); } #endif else { GError e = { WOCKY_SI_ERROR, WOCKY_SI_ERROR_BAD_PROFILE, "" }; DEBUG ("SI profile unsupported: %s", profile); gabble_bytestream_iface_close (bytestream, &e); } out: g_slist_free (stream_methods); g_free (peer_resource); g_free (self_jid); return TRUE; } static gboolean handle_ibb_open_iq (GabbleBytestreamFactory *self, WockyStanza *msg) { GabbleBytestreamFactoryPrivate *priv = GABBLE_BYTESTREAM_FACTORY_GET_PRIVATE (self); WockyPorter *porter = wocky_session_get_porter (priv->conn->session); GabbleBytestreamIBB *bytestream; WockyNode *open_node; ConstBytestreamIdentifier bsid = { NULL, NULL }; const gchar *tmp; guint state; WockyStanzaSubType sub_type; wocky_stanza_get_type_info (msg, NULL, &sub_type); if (sub_type != WOCKY_STANZA_SUB_TYPE_SET) return FALSE; open_node = wocky_node_get_child_ns ( wocky_stanza_get_top_node (msg), "open", NS_IBB); if (open_node == NULL) return FALSE; bsid.jid = wocky_node_get_attribute ( wocky_stanza_get_top_node (msg), "from"); if (bsid.jid == NULL) { DEBUG ("got a message without a from field"); wocky_porter_send_iq_error (porter, msg, WOCKY_XMPP_ERROR_BAD_REQUEST, NULL); return TRUE; } bsid.stream = wocky_node_get_attribute (open_node, "sid"); if (bsid.stream == NULL) { DEBUG ("IBB open stanza doesn't contain stream id"); wocky_porter_send_iq_error (porter, msg, WOCKY_XMPP_ERROR_BAD_REQUEST, NULL); return TRUE; } bytestream = g_hash_table_lookup (priv->ibb_bytestreams, &bsid); if (bytestream == NULL) { /* We don't accept streams not previously announced using SI */ DEBUG ("unknown stream: <%s> from <%s>", bsid.stream, bsid.jid); wocky_porter_send_iq_error (porter, msg, WOCKY_XMPP_ERROR_BAD_REQUEST, NULL); return TRUE; } g_object_get (bytestream, "state", &state, NULL); if (state != GABBLE_BYTESTREAM_STATE_ACCEPTED) { /* We don't accept streams not previously accepted using SI */ DEBUG ("unaccepted stream: <%s> from <%s>", bsid.stream, bsid.jid); wocky_porter_send_iq_error (porter, msg, WOCKY_XMPP_ERROR_BAD_REQUEST, NULL); return TRUE; } tmp = wocky_node_get_attribute (open_node, "block-size"); if (tmp != NULL) { guint block_size = strtoul (tmp, NULL, 10); if (block_size > 0) g_object_set (bytestream, "block-size", block_size, NULL); } g_object_set (bytestream, "state", GABBLE_BYTESTREAM_STATE_OPEN, NULL); wocky_porter_acknowledge_iq (porter, msg, NULL); return TRUE; } static gboolean handle_ibb_close_iq (GabbleBytestreamFactory *self, WockyStanza *msg) { GabbleBytestreamFactoryPrivate *priv = GABBLE_BYTESTREAM_FACTORY_GET_PRIVATE (self); WockyPorter *porter = wocky_session_get_porter (priv->conn->session); ConstBytestreamIdentifier bsid = { NULL, NULL }; GabbleBytestreamIBB *bytestream; WockyNode *close_node; WockyStanzaSubType sub_type; wocky_stanza_get_type_info (msg, NULL, &sub_type); if (sub_type != WOCKY_STANZA_SUB_TYPE_SET) return FALSE; close_node = wocky_node_get_child_ns ( wocky_stanza_get_top_node (msg), "close", NS_IBB); if (close_node == NULL) return FALSE; bsid.jid = wocky_node_get_attribute (wocky_stanza_get_top_node (msg), "from"); if (bsid.jid == NULL) { DEBUG ("got a message without a from field"); wocky_porter_send_iq_error (porter, msg, WOCKY_XMPP_ERROR_BAD_REQUEST, "IBB <close> has no 'from' attribute"); return TRUE; } bsid.stream = wocky_node_get_attribute (close_node, "sid"); if (bsid.stream == NULL) { DEBUG ("IBB close stanza doesn't contain stream id"); wocky_porter_send_iq_error (porter, msg, WOCKY_XMPP_ERROR_BAD_REQUEST, "IBB <close> has no stream ID"); return TRUE; } bytestream = g_hash_table_lookup (priv->ibb_bytestreams, &bsid); if (bytestream == NULL) { DEBUG ("unknown stream: <%s> from <%s>", bsid.stream, bsid.jid); wocky_porter_send_iq_error (porter, msg, WOCKY_XMPP_ERROR_ITEM_NOT_FOUND, NULL); } else { gabble_bytestream_ibb_close_received (bytestream, msg); } return TRUE; } /* IBB can be transported over either IQs or messages, so msg can either be * an <iq> or a <message>. If it's an <iq> we need to reply to it. * * Return TRUE if we take responsibility for this message. */ static gboolean handle_ibb_data (GabbleBytestreamFactory *self, WockyStanza *msg, gboolean is_iq) { GabbleBytestreamFactoryPrivate *priv = GABBLE_BYTESTREAM_FACTORY_GET_PRIVATE (self); WockyPorter *porter = wocky_session_get_porter (priv->conn->session); GabbleBytestreamIBB *bytestream = NULL; WockyNode *data; ConstBytestreamIdentifier bsid = { NULL, NULL }; WockyStanzaSubType sub_type; priv = GABBLE_BYTESTREAM_FACTORY_GET_PRIVATE (self); wocky_stanza_get_type_info (msg, NULL, &sub_type); if (is_iq && sub_type != WOCKY_STANZA_SUB_TYPE_SET) return FALSE; data = wocky_node_get_child_ns ( wocky_stanza_get_top_node (msg), "data", NS_IBB); if (data == NULL) return FALSE; bsid.jid = wocky_node_get_attribute (wocky_stanza_get_top_node (msg), "from"); if (bsid.jid == NULL) { DEBUG ("got a message without a from field"); if (is_iq) wocky_porter_send_iq_error (porter, msg, WOCKY_XMPP_ERROR_BAD_REQUEST, "IBB <close> has no 'from' attribute"); return TRUE; } bsid.stream = wocky_node_get_attribute (data, "sid"); if (bsid.stream == NULL) { DEBUG ("got a IBB message data without a stream id field"); if (is_iq) wocky_porter_send_iq_error (porter, msg, WOCKY_XMPP_ERROR_BAD_REQUEST, "IBB <data> needs a stream ID"); return TRUE; } bytestream = g_hash_table_lookup (priv->ibb_bytestreams, &bsid); if (bytestream == NULL) { DEBUG ("unknown stream: <%s> from <%s>", bsid.stream, bsid.jid); if (is_iq) wocky_porter_send_iq_error (porter, msg, WOCKY_XMPP_ERROR_BAD_REQUEST, "IBB <data> has unknown stream ID"); return TRUE; } gabble_bytestream_ibb_receive (bytestream, msg, is_iq); return TRUE; } static gboolean handle_muc_data (GabbleBytestreamFactory *self, WockyStanza *msg) { GabbleBytestreamFactoryPrivate *priv = GABBLE_BYTESTREAM_FACTORY_GET_PRIVATE (self); GabbleBytestreamMuc *bytestream = NULL; WockyNode *data; ConstBytestreamIdentifier bsid = { NULL, NULL }; gchar *room_name; const gchar *from; priv = GABBLE_BYTESTREAM_FACTORY_GET_PRIVATE (self); data = wocky_node_get_child_ns ( wocky_stanza_get_top_node (msg), "data", NS_MUC_BYTESTREAM); if (data == NULL) return FALSE; from = wocky_node_get_attribute ( wocky_stanza_get_top_node (msg), "from"); if (from == NULL) { DEBUG ("got a message without a from field"); return TRUE; } bsid.stream = wocky_node_get_attribute (data, "sid"); if (bsid.stream == NULL) { DEBUG ("got a pseudo IBB muc message data without a stream id field"); return TRUE; } room_name = gabble_remove_resource (from); bsid.jid = room_name; bytestream = g_hash_table_lookup (priv->muc_bytestreams, &bsid); if (bytestream == NULL) { DEBUG ("unknown muc stream: <%s> from <%s>", bsid.stream, bsid.jid); g_free (room_name); return TRUE; } g_assert (GABBLE_IS_BYTESTREAM_MUC (bytestream)); gabble_bytestream_muc_receive (bytestream, msg); g_free (room_name); return TRUE; } /** * bytestream_factory_iq_ibb_cb: * * Called by Wocky when we get an incoming <iq>. * This handler is concerned with IBB iq's. * */ static gboolean bytestream_factory_iq_ibb_cb ( WockyPorter *porter, WockyStanza *msg, gpointer user_data) { GabbleBytestreamFactory *self = GABBLE_BYTESTREAM_FACTORY (user_data); if (handle_ibb_open_iq (self, msg)) return TRUE; if (handle_ibb_close_iq (self, msg)) return TRUE; if (handle_ibb_data (self, msg, TRUE)) return TRUE; return FALSE; } /** * bytestream_factory_msg_data_cb * * Called by Wocky when we get an incoming <message>. * This handler handles IBB data and pseudo IBB Muc data. */ static gboolean bytestream_factory_msg_data_cb ( WockyPorter *porter, WockyStanza *msg, gpointer user_data) { GabbleBytestreamFactory *self = user_data; if (handle_ibb_data (self, msg, FALSE)) return TRUE; if (handle_muc_data (self, msg)) return TRUE; return FALSE; } static gboolean handle_socks5_query_iq ( WockyPorter *porter, WockyStanza *msg, gpointer user_data) { GabbleBytestreamFactory *self = GABBLE_BYTESTREAM_FACTORY (user_data); GabbleBytestreamFactoryPrivate *priv = self->priv; GabbleBytestreamSocks5 *bytestream; WockyNode *query_node, *child_node; ConstBytestreamIdentifier bsid = { NULL, NULL }; const gchar *tmp; WockyNodeIter i; query_node = wocky_node_get_child_ns ( wocky_stanza_get_top_node (msg), "query", NS_BYTESTREAMS); g_return_val_if_fail (query_node != NULL, FALSE); bsid.jid = wocky_node_get_attribute ( wocky_stanza_get_top_node (msg), "from"); if (bsid.jid == NULL) { DEBUG ("got a message without a from field"); wocky_porter_send_iq_error (porter, msg, WOCKY_XMPP_ERROR_BAD_REQUEST, "SOCKS5 <query> has no 'from' attribute"); return TRUE; } bsid.stream = wocky_node_get_attribute (query_node, "sid"); if (bsid.stream == NULL) { DEBUG ("SOCKS5 query stanza doesn't contain stream id"); wocky_porter_send_iq_error (porter, msg, WOCKY_XMPP_ERROR_BAD_REQUEST, "SOCKS5 <query> has no stream ID"); return TRUE; } bytestream = g_hash_table_lookup (priv->socks5_bytestreams, &bsid); if (bytestream == NULL) { /* We don't accept streams not previously announced using SI */ DEBUG ("unknown stream: <%s> from <%s>", bsid.stream, bsid.jid); wocky_porter_send_iq_error (porter, msg, WOCKY_XMPP_ERROR_ITEM_NOT_FOUND, "SOCKS5 <query> has an unknown stream ID"); return TRUE; } tmp = wocky_node_get_attribute (query_node, "mode"); /* If this attribute is missing, the default value of "tcp" MUST be assumed */ if (tmp != NULL && tp_strdiff (tmp, "tcp")) { DEBUG ("non-TCP SOCKS5 bytestreams are not supported"); wocky_porter_send_iq_error (porter, msg, WOCKY_XMPP_ERROR_BAD_REQUEST, "SOCKS5 non-TCP bytestreams are not supported"); return TRUE; } wocky_node_iter_init (&i, query_node, "streamhost", NULL); while (wocky_node_iter_next (&i, &child_node)) { gabble_bytestream_socks5_add_streamhost (bytestream, child_node); } gabble_bytestream_socks5_connect_to_streamhost (bytestream, msg); return TRUE; } GabbleBytestreamFactory * gabble_bytestream_factory_new (GabbleConnection *conn) { GabbleBytestreamFactory *factory; g_return_val_if_fail (GABBLE_IS_CONNECTION (conn), NULL); factory = GABBLE_BYTESTREAM_FACTORY ( g_object_new (GABBLE_TYPE_BYTESTREAM_FACTORY, "connection", conn, NULL)); return factory; } static void bytestream_state_changed_cb (GabbleBytestreamIface *bytestream, GabbleBytestreamState state, gpointer user_data) { GabbleBytestreamFactory *self = GABBLE_BYTESTREAM_FACTORY (user_data); GabbleBytestreamFactoryPrivate *priv = GABBLE_BYTESTREAM_FACTORY_GET_PRIVATE ( self); if (priv->dispose_has_run) return; if (state == GABBLE_BYTESTREAM_STATE_CLOSED) { remove_bytestream (self, bytestream); } } gchar * gabble_bytestream_factory_generate_stream_id (void) { gchar *stream_id; stream_id = g_strdup_printf ("%lu-%u", (unsigned long) time (NULL), g_random_int ()); return stream_id; } GabbleBytestreamIface * gabble_bytestream_factory_create_from_method (GabbleBytestreamFactory *self, const gchar *stream_method, TpHandle peer_handle, const gchar *stream_id, const gchar *stream_init_id, const gchar *peer_resource, const gchar *self_jid, GabbleBytestreamState state) { GabbleBytestreamIface *bytestream = NULL; if (!tp_strdiff (stream_method, NS_IBB)) { bytestream = GABBLE_BYTESTREAM_IFACE ( gabble_bytestream_factory_create_ibb (self, peer_handle, stream_id, stream_init_id, peer_resource, state)); } else if (!tp_strdiff (stream_method, NS_BYTESTREAMS)) { bytestream = GABBLE_BYTESTREAM_IFACE ( gabble_bytestream_factory_create_socks5 (self, peer_handle, stream_id, stream_init_id, peer_resource, self_jid, state)); } return bytestream; } static GabbleBytestreamIBB * gabble_bytestream_factory_create_ibb (GabbleBytestreamFactory *self, TpHandle peer_handle, const gchar *stream_id, const gchar *stream_init_id, const gchar *peer_resource, GabbleBytestreamState state) { GabbleBytestreamFactoryPrivate *priv; GabbleBytestreamIBB *ibb; BytestreamIdentifier *id; g_return_val_if_fail (GABBLE_IS_BYTESTREAM_FACTORY (self), NULL); priv = GABBLE_BYTESTREAM_FACTORY_GET_PRIVATE (self); ibb = g_object_new (GABBLE_TYPE_BYTESTREAM_IBB, "connection", priv->conn, "peer-handle", peer_handle, "stream-id", stream_id, "state", state, "stream-init-id", stream_init_id, "peer-resource", peer_resource, NULL); gabble_signal_connect_weak (ibb, "state-changed", G_CALLBACK (bytestream_state_changed_cb), G_OBJECT (self)); id = bytestream_id_new (GABBLE_BYTESTREAM_IFACE (ibb)); DEBUG ("add IBB bytestream <%s> from <%s>", id->stream, id->jid); g_hash_table_insert (priv->ibb_bytestreams, id, ibb); return ibb; } GabbleBytestreamMuc * gabble_bytestream_factory_create_muc (GabbleBytestreamFactory *self, TpHandle peer_handle, const gchar *stream_id, GabbleBytestreamState state) { GabbleBytestreamFactoryPrivate *priv; GabbleBytestreamMuc *bytestream; BytestreamIdentifier *id; g_return_val_if_fail (GABBLE_IS_BYTESTREAM_FACTORY (self), NULL); priv = GABBLE_BYTESTREAM_FACTORY_GET_PRIVATE (self); bytestream = g_object_new (GABBLE_TYPE_BYTESTREAM_MUC, "connection", priv->conn, "peer-handle", peer_handle, "stream-id", stream_id, "state", state, NULL); gabble_signal_connect_weak (bytestream, "state-changed", G_CALLBACK (bytestream_state_changed_cb), G_OBJECT (self)); id = bytestream_id_new (GABBLE_BYTESTREAM_IFACE (bytestream)); DEBUG ("add muc bytestream <%s> from <%s>", id->stream, id->jid); g_hash_table_insert (priv->muc_bytestreams, id, bytestream); return bytestream; } static GabbleBytestreamSocks5 * gabble_bytestream_factory_create_socks5 (GabbleBytestreamFactory *self, TpHandle peer_handle, const gchar *stream_id, const gchar *stream_init_id, const gchar *peer_resource, const gchar *self_jid, GabbleBytestreamState state) { GabbleBytestreamFactoryPrivate *priv; GabbleBytestreamSocks5 *socks5; BytestreamIdentifier *id; g_return_val_if_fail (GABBLE_IS_BYTESTREAM_FACTORY (self), NULL); priv = GABBLE_BYTESTREAM_FACTORY_GET_PRIVATE (self); socks5 = g_object_new (GABBLE_TYPE_BYTESTREAM_SOCKS5, "connection", priv->conn, "peer-handle", peer_handle, "stream-id", stream_id, "state", state, "stream-init-id", stream_init_id, "peer-resource", peer_resource, "self-jid", self_jid, NULL); gabble_signal_connect_weak (socks5, "state-changed", G_CALLBACK (bytestream_state_changed_cb), G_OBJECT (self)); id = bytestream_id_new (GABBLE_BYTESTREAM_IFACE (socks5)); DEBUG ("add SOCKS5 bytestream <%s> from <%s>", id->stream, id->jid); g_hash_table_insert (priv->socks5_bytestreams, id, socks5); return socks5; } static GabbleBytestreamMultiple * gabble_bytestream_factory_create_multiple (GabbleBytestreamFactory *self, TpHandle peer_handle, const gchar *stream_id, const gchar *stream_init_id, const gchar *peer_resource, const gchar *self_jid, GabbleBytestreamState state) { GabbleBytestreamFactoryPrivate *priv; GabbleBytestreamMultiple *multiple; BytestreamIdentifier *id; g_return_val_if_fail (GABBLE_IS_BYTESTREAM_FACTORY (self), NULL); priv = GABBLE_BYTESTREAM_FACTORY_GET_PRIVATE (self); multiple = g_object_new (GABBLE_TYPE_BYTESTREAM_MULTIPLE, "connection", priv->conn, "peer-handle", peer_handle, "stream-id", stream_id, "state", state, "stream-init-id", stream_init_id, "peer-resource", peer_resource, "factory", self, "self-jid", self_jid, NULL); gabble_signal_connect_weak (multiple, "state-changed", G_CALLBACK (bytestream_state_changed_cb), G_OBJECT (self)); id = bytestream_id_new (GABBLE_BYTESTREAM_IFACE (multiple)); DEBUG ("add multi bytestream <%s> from <%s>", id->stream, id->jid); g_hash_table_insert (priv->multiple_bytestreams, id, multiple); return multiple; } static GabbleBytestreamIface * streaminit_get_multiple_bytestream (GabbleBytestreamFactory *self, WockyStanza *reply_msg, WockyNode *si, const gchar *stream_id, TpHandle peer_handle, const gchar *peer_resource, const gchar *self_jid) { /* If the other client supports si-multiple we have directly a list of * supported methods inside <value/> tags */ WockyNode *si_multi; const gchar *stream_method; GabbleBytestreamMultiple *bytestream = NULL; WockyNode *value; WockyNodeIter i; si_multi = wocky_node_get_child_ns (si, "si-multiple", NS_SI_MULTIPLE); if (si_multi == NULL) return NULL; bytestream = gabble_bytestream_factory_create_multiple (self, peer_handle, stream_id, NULL, peer_resource, self_jid, GABBLE_BYTESTREAM_STATE_INITIATING); wocky_node_iter_init (&i, si_multi, "value", NULL); while (wocky_node_iter_next (&i, &value)) { stream_method = value->content; if (!stream_method_supported (stream_method)) { DEBUG ("got a si-multiple reply with an unsupported " "stream method: %s", stream_method); continue; } gabble_bytestream_multiple_add_stream_method (bytestream, stream_method); } return GABBLE_BYTESTREAM_IFACE (bytestream); } static GabbleBytestreamIface * streaminit_get_bytestream (GabbleBytestreamFactory *self, WockyStanza *reply_msg, WockyNode *si, const gchar *stream_id, TpHandle peer_handle, const gchar *peer_resource, const gchar *self_jid) { WockyNode *feature, *x, *value, *field; GabbleBytestreamIface *bytestream = NULL; const gchar *stream_method; WockyNodeIter i; feature = wocky_node_get_child_ns (si, "feature", NS_FEATURENEG); if (feature == NULL) { STANZA_DEBUG (reply_msg, "got a SI reply without a feature field"); return NULL; } x = wocky_node_get_child_ns (feature, "x", NS_X_DATA); if (x == NULL) { STANZA_DEBUG (reply_msg, "got a SI reply without a x field"); return NULL; } wocky_node_iter_init (&i, x, NULL, NULL); while (wocky_node_iter_next (&i, &field)) { if (tp_strdiff (wocky_node_get_attribute (field, "var"), "stream-method")) /* some future field, ignore it */ continue; value = wocky_node_get_child (field, "value"); if (value == NULL) { STANZA_DEBUG (reply_msg, "SI reply's stream-method field " "doesn't contain stream-method value"); return NULL; } stream_method = value->content; bytestream = gabble_bytestream_factory_create_from_method (self, stream_method, peer_handle, stream_id, NULL, peer_resource, self_jid, GABBLE_BYTESTREAM_STATE_INITIATING); /* no need to parse the rest of the fields, we've found the one we * wanted */ break; } return bytestream; } struct _streaminit_reply_cb_data { GabbleBytestreamFactory *self; gchar *stream_id; GabbleBytestreamFactoryNegotiateReplyFunc func; TpWeakRef *weak_object; }; /* Called when we receive the reply of a SI request */ static void streaminit_reply_cb ( GObject *source, GAsyncResult *result, gpointer user_data) { GabbleConnection *conn = GABBLE_CONNECTION (source); struct _streaminit_reply_cb_data *data = user_data; GabbleBytestreamFactory *self = data->self; GabbleBytestreamFactoryPrivate *priv = self->priv; GabbleBytestreamIface *bytestream = NULL; gchar *peer_resource = NULL; WockyNode *si; const gchar *from; TpHandleRepoIface *contact_repo = tp_base_connection_get_handles ( (TpBaseConnection *) conn, TP_HANDLE_TYPE_CONTACT); TpHandleRepoIface *room_repo = tp_base_connection_get_handles ( (TpBaseConnection *) conn, TP_HANDLE_TYPE_ROOM); TpHandle peer_handle = 0; TpHandle room_handle; gboolean success = FALSE; gchar *self_jid = NULL; GObject *object = tp_weak_ref_dup_object (data->weak_object); WockyStanza *reply_msg = NULL; if (object == NULL) { DEBUG ("Object which requested the bytestream was disposed. Ignoring"); goto END; } if (!conn_util_send_iq_finish (conn, result, &reply_msg, NULL)) { DEBUG ("stream %s declined", data->stream_id); goto END; } /* stream accepted */ from = wocky_node_get_attribute ( wocky_stanza_get_top_node (reply_msg), "from"); if (from == NULL) { STANZA_DEBUG (reply_msg, "got a message without a from field"); goto END; } peer_handle = tp_handle_ensure (contact_repo, from, NULL, NULL); room_handle = gabble_get_room_handle_from_jid (room_repo, from); if (room_handle == 0) { /* jid is not a muc jid so we need contact's resource */ if (!wocky_decode_jid (from, NULL, NULL, &peer_resource)) { DEBUG ("Got an SI request with a bad JID"); goto END; } if (peer_resource == NULL) { DEBUG ("Got an SI request from a JID without a resource; ignoring"); goto END; } /* we are not in a muc so our own jid is the one in the 'to' attribute */ self_jid = g_strdup (wocky_node_get_attribute ( wocky_stanza_get_top_node (reply_msg), "to")); } else { /* we are in a muc so need to get our muc jid */ GabbleMucChannel *muc; muc = gabble_muc_factory_find_text_channel (priv->conn->muc_factory, room_handle); if (muc == NULL) { DEBUG ("Got an IQ from a muc in which we are not. Ignoring"); goto END; } g_object_get (muc, "self-jid", &self_jid, NULL); } si = wocky_node_get_child_ns ( wocky_stanza_get_top_node (reply_msg), "si", NS_SI); if (si == NULL) { STANZA_DEBUG (reply_msg, "got a SI reply without a si field"); goto END; } /* Try to build a multiple bytestream with fallback methods */ bytestream = streaminit_get_multiple_bytestream (self, reply_msg, si, data->stream_id, peer_handle, peer_resource, self_jid); /* FIXME: check if there is at least one stream method */ if (bytestream == NULL) /* The other client doesn't suppport si-multiple, use the normal XEP-095 * method */ bytestream = streaminit_get_bytestream (self, reply_msg, si, data->stream_id, peer_handle, peer_resource, self_jid); if (bytestream == NULL) goto END; DEBUG ("stream %s accepted", data->stream_id); /* Let's start the initiation of the stream */ if (gabble_bytestream_iface_initiate (bytestream)) { /* FIXME: we should really only "succeed" when our <open> succeeds. * It only really matters from the point of view of the data->func */ success = TRUE; } END: if (!success && bytestream != NULL) { remove_bytestream (self, bytestream); bytestream = NULL; } /* user callback */ if (object != NULL) { data->func (bytestream, reply_msg, object, tp_weak_ref_get_user_data (data->weak_object)); g_clear_object (&object); } if (peer_resource != NULL) g_free (peer_resource); g_clear_object (&reply_msg); g_clear_object (&data->self); g_free (self_jid); g_free (data->stream_id); tp_weak_ref_destroy (data->weak_object); g_slice_free (struct _streaminit_reply_cb_data, data); } /* * gabble_bytestream_factory_negotiate_stream: * * @msg: the SI negotiation IQ (created using * gabble_bytestream_factory_make_stream_init_iq) * @stream_id: the stream identifier * @func: the callback to call when we receive the answser of the request * @user_data: user data to pass to the callback * @object: the handler will follow the lifetime of this object, * which means that if the object is destroyed the callback will not be invoked. * * Send a Stream Initiation (XEP-0095) request. */ void gabble_bytestream_factory_negotiate_stream (GabbleBytestreamFactory *self, WockyStanza *msg, const gchar *stream_id, GabbleBytestreamFactoryNegotiateReplyFunc func, gpointer user_data, GObject *object) { GabbleBytestreamFactoryPrivate *priv; struct _streaminit_reply_cb_data *data; g_assert (GABBLE_IS_BYTESTREAM_FACTORY (self)); g_assert (stream_id != NULL); g_assert (func != NULL); g_assert (object != NULL); priv = GABBLE_BYTESTREAM_FACTORY_GET_PRIVATE (self); data = g_slice_new (struct _streaminit_reply_cb_data); data->self = g_object_ref (self); data->stream_id = g_strdup (stream_id); data->func = func; data->weak_object = tp_weak_ref_new (object, user_data, NULL); conn_util_send_iq_async (priv->conn, msg, NULL, streaminit_reply_cb, data); } /* * gabble_bytestream_factory_make_accept_iq * * @full_jid: the full jid of the stream initiator * @stream_init_id: the id of the SI request * @stream_method: the stream method chosen (one of them proposed * in the SI request) * * Create an IQ stanza accepting a stream in response to * a SI request (XEP-0095). * */ WockyStanza * gabble_bytestream_factory_make_accept_iq (const gchar *full_jid, const gchar *stream_init_id, const gchar *stream_method) { return wocky_stanza_build (WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_RESULT, NULL, full_jid, '@', "id", stream_init_id, '(', "si", ':', NS_SI, '(', "feature", ':', NS_FEATURENEG, '(', "x", ':', NS_X_DATA, '@', "type", "submit", '(', "field", '@', "var", "stream-method", '(', "value", '$', stream_method, ')', ')', ')', ')', ')', NULL); } /* * gabble_bytestream_factory_make_multi_accept_iq * * @full_jid: the full jid of the stream initiator * @stream_init_id: the id of the SI request * @stream_methods: a list of the accepted string methods * * Create an IQ stanza accepting a stream in response to * a si-multiple SI request. * */ WockyStanza * gabble_bytestream_factory_make_multi_accept_iq (const gchar *full_jid, const gchar *stream_init_id, GList *stream_methods) { WockyStanza *msg; WockyNode *multi_node; GList *l; msg = wocky_stanza_build (WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_RESULT, NULL, full_jid, '@', "id", stream_init_id, '(', "si", ':', NS_SI, '(', "si-multiple", ':', NS_SI_MULTIPLE, '*', &multi_node, ')', ')', NULL); for (l = stream_methods; l != NULL; l = l->next) { wocky_node_add_child_with_content (multi_node, "value", l->data); } return msg; } GSList * gabble_bytestream_factory_get_socks5_proxies (GabbleBytestreamFactory *self) { GabbleBytestreamFactoryPrivate *priv = GABBLE_BYTESTREAM_FACTORY_GET_PRIVATE ( self); return g_slist_concat (g_slist_copy (priv->socks5_proxies), g_slist_copy (priv->socks5_fallback_proxies)); } ������������������������������������������telepathy-gabble-0.18.2/src/bytestream-factory.h����������������������������������������������������0000644�0001750�0001750�00000010135�12200204333�021566� 0����������������������������������������������������������������������������������������������������ustar�00smcv����������������������������smcv����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * bytestream-factory.h - Header for GabbleBytestreamFactory * Copyright (C) 2007 Collabora Ltd. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 __BYTESTREAM_FACTORY_H__ #define __BYTESTREAM_FACTORY_H__ #include <glib-object.h> #include <telepathy-glib/telepathy-glib.h> #include "types.h" #include "bytestream-iface.h" #include "bytestream-ibb.h" #include "bytestream-muc.h" #include "bytestream-multiple.h" #include "bytestream-socks5.h" #include "connection.h" G_BEGIN_DECLS typedef struct _GabbleBytestreamFactoryClass GabbleBytestreamFactoryClass; typedef struct _GabbleBytestreamFactoryPrivate GabbleBytestreamFactoryPrivate; struct _GabbleBytestreamFactoryClass { GObjectClass parent_class; }; struct _GabbleBytestreamFactory { GObject parent; GabbleBytestreamFactoryPrivate *priv; }; GType gabble_bytestream_factory_get_type (void); /* TYPE MACROS */ #define GABBLE_TYPE_BYTESTREAM_FACTORY \ (gabble_bytestream_factory_get_type ()) #define GABBLE_BYTESTREAM_FACTORY(obj) \ (G_TYPE_CHECK_INSTANCE_CAST((obj), GABBLE_TYPE_BYTESTREAM_FACTORY,\ GabbleBytestreamFactory)) #define GABBLE_BYTESTREAM_FACTORY_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST((klass), GABBLE_TYPE_BYTESTREAM_FACTORY,\ GabbleBytestreamFactoryClass)) #define GABBLE_IS_BYTESTREAM_FACTORY(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE((obj), GABBLE_TYPE_BYTESTREAM_FACTORY)) #define GABBLE_IS_BYTESTREAM_FACTORY_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE((klass), GABBLE_TYPE_BYTESTREAM_FACTORY)) #define GABBLE_BYTESTREAM_FACTORY_GET_CLASS(obj) \ (G_TYPE_INSTANCE_GET_CLASS ((obj), GABBLE_TYPE_BYTESTREAM_FACTORY,\ GabbleBytestreamFactoryClass)) typedef struct { gchar *jid; gchar *host; guint16 port; } GabbleSocks5Proxy; typedef void (* GabbleBytestreamFactoryNegotiateReplyFunc) ( GabbleBytestreamIface *bytestream, WockyStanza *msg, GObject *object, gpointer user_data); GabbleBytestreamFactory *gabble_bytestream_factory_new ( GabbleConnection *conn); GabbleBytestreamMuc *gabble_bytestream_factory_create_muc ( GabbleBytestreamFactory *fac, TpHandle peer_handle, const gchar *stream_id, GabbleBytestreamState state); GabbleBytestreamIface *gabble_bytestream_factory_create_from_method ( GabbleBytestreamFactory *self, const gchar *stream_method, TpHandle peer_handle, const gchar *stream_id, const gchar *stream_init_id, const gchar *peer_resource, const gchar *self_jid, GabbleBytestreamState state); WockyStanza *gabble_bytestream_factory_make_stream_init_iq ( const gchar *full_jid, const gchar *stream_id, const gchar *profile); WockyStanza *gabble_bytestream_factory_make_accept_iq (const gchar *full_jid, const gchar *stream_init_id, const gchar *stream_method); WockyStanza *gabble_bytestream_factory_make_multi_accept_iq ( const gchar *full_jid, const gchar *stream_init_id, GList *stream_methods); void gabble_bytestream_factory_negotiate_stream ( GabbleBytestreamFactory *fac, WockyStanza *msg, const gchar *stream_id, GabbleBytestreamFactoryNegotiateReplyFunc func, gpointer user_data, GObject *object); gchar *gabble_bytestream_factory_generate_stream_id (void); GSList *gabble_bytestream_factory_get_socks5_proxies ( GabbleBytestreamFactory *self); void gabble_bytestream_factory_query_socks5_proxies ( GabbleBytestreamFactory *self); G_END_DECLS #endif /* #ifndef __BYTESTREAM_FACTORY_H__ */ �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������telepathy-gabble-0.18.2/src/auth-manager.c����������������������������������������������������������0000644�0001750�0001750�00000050742�12200204333�020316� 0����������������������������������������������������������������������������������������������������ustar�00smcv����������������������������smcv����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * auth-manager.c - TpChannelManager implementation for auth channels * Copyright (C) 2010 Collabora Ltd. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 "config.h" #include "auth-manager.h" #include <telepathy-glib/telepathy-glib.h> #include <telepathy-glib/telepathy-glib-dbus.h> #include <wocky/wocky.h> #define DEBUG_FLAG GABBLE_DEBUG_AUTH #include "gabble/caps-channel-manager.h" #include "server-sasl-channel.h" #include "connection.h" #include "debug.h" #include "util.h" static void channel_manager_iface_init (gpointer, gpointer); G_DEFINE_TYPE_WITH_CODE (GabbleAuthManager, gabble_auth_manager, WOCKY_TYPE_AUTH_REGISTRY, G_IMPLEMENT_INTERFACE (TP_TYPE_CHANNEL_MANAGER, channel_manager_iface_init); G_IMPLEMENT_INTERFACE (GABBLE_TYPE_CAPS_CHANNEL_MANAGER, NULL)); /* properties */ enum { PROP_CONNECTION = 1, LAST_PROPERTY }; typedef struct { gchar *name; GHashTable *details; TpConnectionStatusReason reason; GError *wocky_error; } SavedError; struct _GabbleAuthManagerPrivate { GabbleConnection *conn; GabbleServerSaslChannel *channel; gulong closed_id; /* TRUE if we are authenticating using our parent class's methods (because we * have a username and password). */ gboolean chaining_up; GSList *mechanisms; gchar *server; gchar *session_id; gchar *username; gboolean allow_plain; gboolean is_secure_channel; SavedError *error; gboolean dispose_has_run; }; static void gabble_auth_manager_init (GabbleAuthManager *self) { self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, GABBLE_TYPE_AUTH_MANAGER, GabbleAuthManagerPrivate); } static void gabble_auth_manager_close_all (GabbleAuthManager *self) { DEBUG ("called"); if (self->priv->channel != NULL) tp_base_channel_close ((TpBaseChannel *) self->priv->channel); /* that results in the signal-driven-object-clearing dance */ g_assert (self->priv->channel == NULL); } static void connection_status_changed_cb (GabbleConnection *conn, guint status, guint reason, GabbleAuthManager *self) { if (status == TP_CONNECTION_STATUS_DISCONNECTED) gabble_auth_manager_close_all (self); } static void auth_channel_closed_cb (GabbleServerSaslChannel *channel, GabbleAuthManager *self) { SavedError tmp = { NULL, NULL, 0, NULL }; tp_channel_manager_emit_channel_closed_for_object (self, TP_EXPORTABLE_CHANNEL (channel)); g_assert (self->priv->channel == channel); /* this is our last chance to find out why it failed */ if (gabble_server_sasl_channel_get_failure_details (channel, &tmp.name, &tmp.details, &tmp.reason, &tmp.wocky_error)) self->priv->error = g_slice_dup (SavedError, &tmp); g_signal_handler_disconnect (self->priv->channel, self->priv->closed_id); tp_clear_object (&self->priv->channel); /* discard info we were holding in case we wanted to fall back */ g_slist_foreach (self->priv->mechanisms, (GFunc) g_free, NULL); tp_clear_pointer (&self->priv->mechanisms, g_slist_free); tp_clear_pointer (&self->priv->server, g_free); tp_clear_pointer (&self->priv->session_id, g_free); tp_clear_pointer (&self->priv->username, g_free); } static void gabble_auth_manager_constructed (GObject *object) { GabbleAuthManager *self = GABBLE_AUTH_MANAGER (object); if (G_OBJECT_CLASS (gabble_auth_manager_parent_class)->constructed != NULL) G_OBJECT_CLASS (gabble_auth_manager_parent_class)->constructed (object); self->priv->dispose_has_run = FALSE; gabble_signal_connect_weak (self->priv->conn, "status-changed", G_CALLBACK (connection_status_changed_cb), object); } static void gabble_auth_manager_dispose (GObject *object) { GabbleAuthManager *self = GABBLE_AUTH_MANAGER (object); GabbleAuthManagerPrivate *priv = self->priv; if (priv->dispose_has_run) return; DEBUG ("dispose called"); priv->dispose_has_run = TRUE; gabble_auth_manager_close_all (self); if (G_OBJECT_CLASS (gabble_auth_manager_parent_class)->dispose) G_OBJECT_CLASS ( gabble_auth_manager_parent_class)->dispose (object); } static void gabble_auth_manager_finalize (GObject *object) { GabbleAuthManager *self = GABBLE_AUTH_MANAGER (object); if (self->priv->error != NULL) { g_free (self->priv->error->name); g_hash_table_unref (self->priv->error->details); g_slice_free (SavedError, self->priv->error); } if (G_OBJECT_CLASS (gabble_auth_manager_parent_class)->finalize) G_OBJECT_CLASS (gabble_auth_manager_parent_class)->finalize (object); } static void gabble_auth_manager_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { GabbleAuthManager *self = GABBLE_AUTH_MANAGER (object); switch (property_id) { case PROP_CONNECTION: g_value_set_object (value, self->priv->conn); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void gabble_auth_manager_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { GabbleAuthManager *self = GABBLE_AUTH_MANAGER (object); switch (property_id) { case PROP_CONNECTION: self->priv->conn = g_value_get_object (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void gabble_auth_manager_start_parent_cb (GObject *self_object, GAsyncResult *result, gpointer user_data) { GabbleAuthManager *self = GABBLE_AUTH_MANAGER (self_object); WockyAuthRegistryStartData *start_data = NULL; GError *error = NULL; if (WOCKY_AUTH_REGISTRY_CLASS (gabble_auth_manager_parent_class)-> start_auth_finish_func (WOCKY_AUTH_REGISTRY (self), result, &start_data, &error)) { g_simple_async_result_set_op_res_gpointer (user_data, start_data, (GDestroyNotify) wocky_auth_registry_start_data_free); } else { g_simple_async_result_set_from_error (user_data, error); g_clear_error (&error); } g_simple_async_result_complete (user_data); g_object_unref (user_data); } static void gabble_auth_manager_start_auth_cb (GObject *channel, GAsyncResult *result, gpointer user_data) { GObject *self_object = g_async_result_get_source_object (user_data); GabbleAuthManager *self = GABBLE_AUTH_MANAGER (self_object); WockyAuthRegistryStartData *start_data = NULL; GError *error = NULL; if (gabble_server_sasl_channel_start_auth_finish ( GABBLE_SERVER_SASL_CHANNEL (channel), result, &start_data, &error)) { if (!tp_strdiff (start_data->mechanism, X_TELEPATHY_PASSWORD)) { /* restart authentication using our own base class */ g_assert (start_data->initial_response != NULL); self->priv->chaining_up = TRUE; WOCKY_AUTH_REGISTRY_CLASS ( gabble_auth_manager_parent_class)->start_auth_async_func ( WOCKY_AUTH_REGISTRY (self), self->priv->mechanisms, self->priv->allow_plain, self->priv->is_secure_channel, self->priv->username, start_data->initial_response->str, self->priv->server, self->priv->session_id, gabble_auth_manager_start_parent_cb, user_data); /* we've transferred ownership of the result */ goto finally; } else { g_simple_async_result_set_op_res_gpointer (user_data, start_data, (GDestroyNotify) wocky_auth_registry_start_data_free); } } else { g_simple_async_result_set_from_error (user_data, error); g_clear_error (&error); } g_simple_async_result_complete (user_data); g_object_unref (user_data); finally: g_object_unref (self_object); } static void gabble_auth_manager_start_auth_async (WockyAuthRegistry *registry, GSList *mechanisms, gboolean allow_plain, gboolean is_secure_channel, const gchar *username, const gchar *password, const gchar *server, const gchar *session_id, GAsyncReadyCallback callback, gpointer user_data) { GabbleAuthManager *self = GABBLE_AUTH_MANAGER (registry); GSimpleAsyncResult *result = g_simple_async_result_new ((GObject *) self, callback, user_data, gabble_auth_manager_start_auth_async); /* assumption: Wocky's API guarantees that we never have more than one * auth request outstanding */ g_assert (self->priv->channel == NULL); if (password == NULL || username == NULL) { GPtrArray *mech_array = g_ptr_array_new (); GSList *iter; if (username == NULL) { g_object_get (self->priv->conn, "username", &self->priv->username, NULL); } else { self->priv->username = g_strdup (username); } for (iter = mechanisms; iter != NULL; iter = iter->next) { self->priv->mechanisms = g_slist_prepend (self->priv->mechanisms, g_strdup (iter->data)); /* skip Wocky-specific pseudo-mechanisms for the D-Bus API */ if (!g_str_has_prefix (iter->data, "X-WOCKY-JABBER-")) g_ptr_array_add (mech_array, iter->data); } if (self->priv->username != NULL && wocky_auth_registry_supports_one_of (registry, mechanisms, allow_plain)) g_ptr_array_add (mech_array, X_TELEPATHY_PASSWORD); g_ptr_array_add (mech_array, NULL); /* we'll use these if we fall back to the base class to use * X-TELEPATHY-PASSWORD */ self->priv->mechanisms = g_slist_reverse (self->priv->mechanisms); self->priv->allow_plain = allow_plain; self->priv->is_secure_channel = is_secure_channel; self->priv->server = g_strdup (server); self->priv->session_id = g_strdup (session_id); self->priv->channel = gabble_server_sasl_channel_new (self->priv->conn, (GStrv) mech_array->pdata, is_secure_channel, session_id); g_ptr_array_unref (mech_array); self->priv->closed_id = tp_g_signal_connect_object (self->priv->channel, "closed", G_CALLBACK (auth_channel_closed_cb), self, 0); gabble_server_sasl_channel_start_auth_async (self->priv->channel, gabble_auth_manager_start_auth_cb, result); g_assert (!tp_base_channel_is_destroyed ( (TpBaseChannel *) self->priv->channel)); g_assert (tp_base_channel_is_registered ( (TpBaseChannel *) self->priv->channel)); tp_channel_manager_emit_new_channel (self, TP_EXPORTABLE_CHANNEL (self->priv->channel), NULL); } else { self->priv->chaining_up = TRUE; WOCKY_AUTH_REGISTRY_CLASS ( gabble_auth_manager_parent_class)->start_auth_async_func ( registry, mechanisms, allow_plain, is_secure_channel, username, password, server, session_id, gabble_auth_manager_start_parent_cb, result); } } static gboolean gabble_auth_manager_start_auth_finish (WockyAuthRegistry *registry, GAsyncResult *result, WockyAuthRegistryStartData **start_data, GError **error) { GabbleAuthManager *self = GABBLE_AUTH_MANAGER (registry); wocky_implement_finish_copy_pointer (self, gabble_auth_manager_start_auth_async, wocky_auth_registry_start_data_dup, start_data); } static void channel_challenge_cb ( GObject *source, GAsyncResult *result, gpointer user_data) { GabbleServerSaslChannel *channel = GABBLE_SERVER_SASL_CHANNEL (source); GSimpleAsyncResult *our_result = G_SIMPLE_ASYNC_RESULT (user_data); GString *response_data = NULL; GError *error = NULL; if (gabble_server_sasl_channel_challenge_finish (channel, result, &response_data, &error)) g_simple_async_result_set_op_res_gpointer (our_result, response_data, (GDestroyNotify) wocky_g_string_free); else g_simple_async_result_take_error (our_result, error); g_simple_async_result_complete (our_result); g_object_unref (our_result); } static void gabble_auth_manager_challenge_async (WockyAuthRegistry *registry, const GString *challenge_data, GAsyncReadyCallback callback, gpointer user_data) { GabbleAuthManager *self = GABBLE_AUTH_MANAGER (registry); if (self->priv->chaining_up) { WOCKY_AUTH_REGISTRY_CLASS ( gabble_auth_manager_parent_class)->challenge_async_func ( registry, challenge_data, callback, user_data); } else { GSimpleAsyncResult *result = g_simple_async_result_new (G_OBJECT (self), callback, user_data, gabble_auth_manager_challenge_async); if (self->priv->channel != NULL) { gabble_server_sasl_channel_challenge_async (self->priv->channel, challenge_data, channel_challenge_cb, result); } else { g_assert (self->priv->error != NULL); g_simple_async_result_set_from_error (result, self->priv->error->wocky_error); g_simple_async_result_complete_in_idle (result); g_object_unref (result); } } } static gboolean gabble_auth_manager_challenge_finish (WockyAuthRegistry *registry, GAsyncResult *result, GString **response, GError **error) { GabbleAuthManager *self = GABBLE_AUTH_MANAGER (registry); if (self->priv->chaining_up) { return WOCKY_AUTH_REGISTRY_CLASS (gabble_auth_manager_parent_class)->challenge_finish_func ( registry, result, response, error); } else { wocky_implement_finish_copy_pointer (self, gabble_auth_manager_challenge_async, wocky_g_string_dup, response); } } static void channel_success_cb ( GObject *source, GAsyncResult *result, gpointer user_data) { GabbleServerSaslChannel *channel = GABBLE_SERVER_SASL_CHANNEL (source); GSimpleAsyncResult *our_result = G_SIMPLE_ASYNC_RESULT (user_data); GError *error = NULL; if (!gabble_server_sasl_channel_success_finish (channel, result, &error)) g_simple_async_result_take_error (our_result, error); g_simple_async_result_complete (our_result); g_object_unref (our_result); } static void gabble_auth_manager_channel_success ( GabbleAuthManager *self, GSimpleAsyncResult *our_result, gboolean need_idle) { GabbleAuthManagerPrivate *priv = self->priv; if (priv->channel != NULL) { gabble_server_sasl_channel_success_async (self->priv->channel, channel_success_cb, our_result); return; } if (priv->error != NULL) g_simple_async_result_set_from_error (our_result, priv->error->wocky_error); if (need_idle) g_simple_async_result_complete_in_idle (our_result); else g_simple_async_result_complete (our_result); g_object_unref (our_result); } static void gabble_auth_manager_success_parent_cb ( GObject *source, GAsyncResult *result, gpointer user_data) { GabbleAuthManager *self = GABBLE_AUTH_MANAGER (source); WockyAuthRegistry *registry = WOCKY_AUTH_REGISTRY (source); GSimpleAsyncResult *our_result = G_SIMPLE_ASYNC_RESULT (user_data); GError *error = NULL; if (!WOCKY_AUTH_REGISTRY_CLASS (gabble_auth_manager_parent_class)-> success_finish_func (registry, result, &error)) { g_simple_async_result_take_error (our_result, error); g_simple_async_result_complete (our_result); g_object_unref (our_result); } else { gabble_auth_manager_channel_success (self, our_result, FALSE); } } static void gabble_auth_manager_success_async (WockyAuthRegistry *registry, GAsyncReadyCallback callback, gpointer user_data) { GabbleAuthManager *self = GABBLE_AUTH_MANAGER (registry); GSimpleAsyncResult *result = g_simple_async_result_new (G_OBJECT (self), callback, user_data, gabble_auth_manager_success_async); /* Annoyingly, in the X-TELEPATHY-PASSWORD case we actually want to both * chain up to the parent class, *and* pass the success notification out to * the client for consistency with other mechanisms. */ if (self->priv->chaining_up) { WOCKY_AUTH_REGISTRY_CLASS ( gabble_auth_manager_parent_class)->success_async_func ( registry, gabble_auth_manager_success_parent_cb, result); } else { gabble_auth_manager_channel_success (self, result, TRUE); } } static gboolean gabble_auth_manager_success_finish (WockyAuthRegistry *registry, GAsyncResult *result, GError **error) { GabbleAuthManager *self = GABBLE_AUTH_MANAGER (registry); wocky_implement_finish_void (self, gabble_auth_manager_success_async); } static void gabble_auth_manager_failure (WockyAuthRegistry *registry, GError *error) { GabbleAuthManager *self = GABBLE_AUTH_MANAGER (registry); if (self->priv->channel != NULL) { gabble_server_sasl_channel_fail (self->priv->channel, error); } if (self->priv->chaining_up) { void (*chain_up)(WockyAuthRegistry *, GError *) = WOCKY_AUTH_REGISTRY_CLASS (gabble_auth_manager_parent_class)-> failure_func; if (chain_up != NULL) chain_up (registry, error); } } static void gabble_auth_manager_class_init (GabbleAuthManagerClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); WockyAuthRegistryClass *registry_class = WOCKY_AUTH_REGISTRY_CLASS (klass); GParamSpec *param_spec; g_type_class_add_private (klass, sizeof (GabbleAuthManagerPrivate)); object_class->constructed = gabble_auth_manager_constructed; object_class->dispose = gabble_auth_manager_dispose; object_class->finalize = gabble_auth_manager_finalize; object_class->get_property = gabble_auth_manager_get_property; object_class->set_property = gabble_auth_manager_set_property; registry_class->start_auth_async_func = gabble_auth_manager_start_auth_async; registry_class->start_auth_finish_func = gabble_auth_manager_start_auth_finish; registry_class->challenge_async_func = gabble_auth_manager_challenge_async; registry_class->challenge_finish_func = gabble_auth_manager_challenge_finish; registry_class->success_async_func = gabble_auth_manager_success_async; registry_class->success_finish_func = gabble_auth_manager_success_finish; registry_class->failure_func = gabble_auth_manager_failure; param_spec = g_param_spec_object ("connection", "GabbleConnection object", "Gabble connection object that owns this manager.", GABBLE_TYPE_CONNECTION, G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, PROP_CONNECTION, param_spec); } static void gabble_auth_manager_foreach_channel (TpChannelManager *manager, TpExportableChannelFunc func, gpointer user_data) { GabbleAuthManager *self = GABBLE_AUTH_MANAGER (manager); if (self->priv->channel != NULL) func (TP_EXPORTABLE_CHANNEL (self->priv->channel), user_data); } static void channel_manager_iface_init (gpointer g_iface, gpointer iface_data) { TpChannelManagerIface *iface = g_iface; iface->foreach_channel = gabble_auth_manager_foreach_channel; /* These channels are not requestable. */ iface->ensure_channel = NULL; iface->create_channel = NULL; iface->request_channel = NULL; iface->foreach_channel_class = NULL; } gboolean gabble_auth_manager_get_failure_details (GabbleAuthManager *self, gchar **dbus_error, GHashTable **details, TpConnectionStatusReason *reason) { if (self->priv->channel != NULL) { return gabble_server_sasl_channel_get_failure_details ( self->priv->channel, dbus_error, details, reason, NULL); } else if (self->priv->error != NULL) { if (dbus_error != NULL) *dbus_error = g_strdup (self->priv->error->name); if (details != NULL) *details = g_hash_table_ref (self->priv->error->details); if (reason != NULL) *reason = self->priv->error->reason; return TRUE; } else { /* failure? what failure? I don't know anything about that :-( */ return FALSE; } } ������������������������������telepathy-gabble-0.18.2/src/auth-manager.h����������������������������������������������������������0000644�0001750�0001750�00000004540�12200204333�020316� 0����������������������������������������������������������������������������������������������������ustar�00smcv����������������������������smcv����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * auth-manager.h - Header for GabbleAuthManager * Copyright (C) 2010 Collabora Ltd. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 __AUTH_MANAGER_H__ #define __AUTH_MANAGER_H__ #include <glib-object.h> #include <wocky/wocky.h> #include <telepathy-glib/telepathy-glib.h> #include <telepathy-glib/telepathy-glib-dbus.h> G_BEGIN_DECLS typedef struct _GabbleAuthManager GabbleAuthManager; typedef struct _GabbleAuthManagerClass GabbleAuthManagerClass; typedef struct _GabbleAuthManagerPrivate GabbleAuthManagerPrivate; struct _GabbleAuthManagerClass { WockyAuthRegistryClass parent_class; }; struct _GabbleAuthManager { WockyAuthRegistry parent; GabbleAuthManagerPrivate *priv; }; GType gabble_auth_manager_get_type (void); /* TYPE MACROS */ #define GABBLE_TYPE_AUTH_MANAGER \ (gabble_auth_manager_get_type ()) #define GABBLE_AUTH_MANAGER(obj) \ (G_TYPE_CHECK_INSTANCE_CAST((obj), GABBLE_TYPE_AUTH_MANAGER, GabbleAuthManager)) #define GABBLE_AUTH_MANAGER_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST((klass), GABBLE_TYPE_AUTH_MANAGER,\ GabbleAuthManagerClass)) #define GABBLE_IS_AUTH_MANAGER(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE((obj), GABBLE_TYPE_AUTH_MANAGER)) #define GABBLE_IS_AUTH_MANAGER_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE((klass), GABBLE_TYPE_AUTH_MANAGER)) #define GABBLE_AUTH_MANAGER_GET_CLASS(obj) \ (G_TYPE_INSTANCE_GET_CLASS ((obj), GABBLE_TYPE_AUTH_MANAGER,\ GabbleAuthManagerClass)) gboolean gabble_auth_manager_get_failure_details (GabbleAuthManager *self, gchar **dbus_error, GHashTable **details, TpConnectionStatusReason *reason); G_END_DECLS #endif /* #ifndef __AUTH_MANAGER_H__ */ ����������������������������������������������������������������������������������������������������������������������������������������������������������������telepathy-gabble-0.18.2/src/addressing-util.c�������������������������������������������������������0000644�0001750�0001750�00000031761�12200204333�021043� 0����������������������������������������������������������������������������������������������������ustar�00smcv����������������������������smcv����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * addressing-util.c - Source for Gabble addressing utility functions * Copyright (C) 2010 Collabora Ltd. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 "config.h" #include "addressing-util.h" #include <stdio.h> #include <stdlib.h> #include <string.h> #include <wocky/wocky.h> #include "connection.h" #include "util.h" static const gchar *addressable_vcard_fields[] = {"x-jabber", "x-facebook-id", NULL}; static const gchar *addressable_uri_schemes[] = {"xmpp", NULL}; const gchar * const * gabble_get_addressable_uri_schemes () { return addressable_uri_schemes; } const gchar * const * gabble_get_addressable_vcard_fields () { return addressable_vcard_fields; } gchar * gabble_normalize_contact_uri (const gchar *uri, GError **error) { gchar *scheme = NULL; gchar *normalized_jid = NULL; gchar *normalized_uri = NULL; g_return_val_if_fail (uri != NULL, NULL); normalized_jid = gabble_uri_to_jid (uri, error); if (normalized_jid == NULL) { goto OUT; } scheme = g_uri_parse_scheme (uri); normalized_uri = gabble_jid_to_uri (scheme, normalized_jid, error); OUT: g_free (scheme); g_free (normalized_jid); return normalized_uri; } gchar * gabble_uri_to_jid (const gchar *uri, GError **error) { gchar *scheme; gchar *normalized_jid = NULL; g_return_val_if_fail (uri != NULL, NULL); scheme = g_uri_parse_scheme (uri); if (scheme == NULL) { g_set_error (error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT, "'%s' is not a valid URI", uri); goto OUT; } else if (g_ascii_strcasecmp (scheme, "xmpp") == 0) { gchar *node = NULL; gchar *domain = NULL; gchar *resource = NULL; if (!gabble_parse_xmpp_uri (uri, &node, &domain, &resource, error)) goto OUT; normalized_jid = gabble_encode_jid (node, domain, resource); g_free (node); g_free (domain); g_free (resource); } else { g_set_error (error, TP_ERROR, TP_ERROR_NOT_IMPLEMENTED, "'%s' URI scheme is not supported by this protocol", scheme); goto OUT; } OUT: g_free (scheme); return normalized_jid; } gchar * gabble_jid_to_uri (const gchar *scheme, const gchar *jid, GError **error) { gchar *normalized_uri = NULL; gchar *node = NULL; gchar *domain = NULL; gchar *resource = NULL; gchar *escaped_node = NULL; gchar *escaped_domain = NULL; gchar *escaped_resource = NULL; gchar *escaped_jid = NULL; gchar *normalized_scheme = NULL; g_return_val_if_fail (scheme != NULL, NULL); if (!wocky_decode_jid (jid, &node, &domain, &resource)) { g_set_error (error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT, "'%s' is not a valid JID", jid); return NULL; } /* convert from "foo?" to "foo%3F" */ if (node) escaped_node = g_uri_escape_string (node, NULL, TRUE); g_assert (domain != NULL); escaped_domain = g_uri_escape_string (domain, NULL, TRUE); if (resource) escaped_resource = g_uri_escape_string (resource, NULL, TRUE); escaped_jid = gabble_encode_jid (escaped_node, escaped_domain, escaped_resource); normalized_scheme = g_ascii_strdown (scheme, -1); normalized_uri = g_strdup_printf ("%s:%s", normalized_scheme, escaped_jid); g_free (node); g_free (domain); g_free (resource); g_free (escaped_node); g_free (escaped_domain); g_free (escaped_resource); g_free (escaped_jid); g_free (normalized_scheme); return normalized_uri; } TpHandle gabble_ensure_handle_from_uri (TpHandleRepoIface *repo, const gchar *uri, GError **error) { TpHandle handle; gchar *jid = gabble_uri_to_jid (uri, error); if (jid == NULL) return 0; handle = tp_handle_ensure (repo, jid, NULL, error); g_free (jid); return handle; } gchar * gabble_normalize_vcard_address (const gchar *vcard_field, const gchar *vcard_address, GError **error) { gchar *normalized_jid = NULL; gchar *normalized_address = NULL; g_return_val_if_fail (vcard_field != NULL, NULL); g_return_val_if_fail (vcard_address != NULL, NULL); normalized_jid = gabble_vcard_address_to_jid (vcard_field, vcard_address, error); if (normalized_jid == NULL) { goto OUT; } normalized_address = gabble_jid_to_vcard_address (vcard_field, normalized_jid, error); OUT: g_free (normalized_jid); return normalized_address; } gchar * gabble_vcard_address_to_jid (const gchar *vcard_field, const gchar *vcard_address, GError **error) { gchar *normalized_jid = NULL; g_return_val_if_fail (vcard_field != NULL, NULL); g_return_val_if_fail (vcard_address != NULL, NULL); if (g_ascii_strcasecmp (vcard_field, "x-jabber") == 0) { GError *gabble_error = NULL; normalized_jid = gabble_normalize_contact (NULL, vcard_address, GUINT_TO_POINTER (GABBLE_JID_GLOBAL), &gabble_error); if (gabble_error != NULL) { g_set_error (error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT, "'%s' is an invalid address: %s", vcard_address, gabble_error->message); g_error_free (gabble_error); } } else if (g_ascii_strcasecmp (vcard_field, "x-facebook-id") == 0) { const gchar *s; s = vcard_address; while (*s && (g_ascii_isdigit (*s))) s++; if (G_UNLIKELY (*s != '\0')) { g_set_error (error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT, "'%s' is an invalid facebook chat address", vcard_address); goto OUT; } normalized_jid = g_strdup_printf ("-%s@chat.facebook.com", vcard_address); } else { g_set_error (error, TP_ERROR, TP_ERROR_NOT_IMPLEMENTED, "'%s' vCard field is not supported by this protocol", vcard_field); } OUT: return normalized_jid; } gchar * gabble_jid_to_vcard_address (const gchar *vcard_field, const gchar *jid, GError **error) { gchar *normalized_address = NULL; g_return_val_if_fail (vcard_field != NULL, NULL); g_return_val_if_fail (jid != NULL, NULL); if (g_ascii_strcasecmp (vcard_field, "x-jabber") == 0) { GError *gabble_error = NULL; normalized_address = gabble_normalize_contact (NULL, jid, GUINT_TO_POINTER (GABBLE_JID_GLOBAL), &gabble_error); if (gabble_error != NULL) { g_set_error (error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT, "'%s' is an invalid address: %s", jid, gabble_error->message); g_error_free (gabble_error); } } else if (g_ascii_strcasecmp (vcard_field, "x-facebook-id") == 0) { gchar *address = g_ascii_strdown (jid, -1); if (address[0] == '-' && g_str_has_suffix (address, "@chat.facebook.com")) { const gchar *at = strchr (address, '@'); const gchar *start_of_number = address + 1; const gchar *s; g_assert (at != NULL); normalized_address = g_strndup (start_of_number, (int) (at - start_of_number)); s = normalized_address; while (*s && (g_ascii_isdigit (*s))) s++; if (G_UNLIKELY (*s != '\0')) { g_set_error (error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT, "'%s' is an invalid facebook chat address", jid); } } else { g_set_error (error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT, "'%s' is an invalid facebook chat address", jid); } g_free (address); } else { g_set_error (error, TP_ERROR, TP_ERROR_NOT_IMPLEMENTED, "'%s' vCard field is not supported by this protocol", vcard_field); } return normalized_address; } TpHandle gabble_ensure_handle_from_vcard_address (TpHandleRepoIface *repo, const gchar *vcard_field, const gchar *vcard_address, GError **error) { gchar *normalized_jid; TpHandle handle; normalized_jid = gabble_vcard_address_to_jid (vcard_field, vcard_address, error); if (normalized_jid == NULL) return 0; handle = tp_handle_ensure (repo, normalized_jid, NULL, error); g_free (normalized_jid); return handle; } gchar ** gabble_uris_for_handle (TpHandleRepoIface *contact_repo, TpHandle contact) { GPtrArray *uris = g_ptr_array_new (); for (const gchar * const *scheme = addressable_uri_schemes; *scheme != NULL; scheme++) { gchar *uri = gabble_uri_for_handle (contact_repo, *scheme, contact); if (uri != NULL) { g_ptr_array_add (uris, uri); } } g_ptr_array_add (uris, NULL); return (gchar **) g_ptr_array_free (uris, FALSE); } GHashTable * gabble_vcard_addresses_for_handle (TpHandleRepoIface *contact_repo, TpHandle contact) { GHashTable *addresses = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, (GDestroyNotify) g_free); for (const gchar * const *field = addressable_vcard_fields; *field != NULL; field++) { gchar *vcard_address = gabble_vcard_address_for_handle (contact_repo, *field, contact); if (vcard_address != NULL) { g_hash_table_insert (addresses, (gpointer) *field, vcard_address); } } return addresses; } gchar * gabble_vcard_address_for_handle (TpHandleRepoIface *contact_repo, const gchar *vcard_field, TpHandle contact) { const gchar *identifier = tp_handle_inspect (contact_repo, contact); return gabble_jid_to_vcard_address (vcard_field, identifier, NULL); } gchar * gabble_uri_for_handle (TpHandleRepoIface *contact_repo, const gchar *scheme, TpHandle contact) { const gchar *identifier = tp_handle_inspect (contact_repo, contact); return gabble_jid_to_uri (scheme, identifier, NULL); } gboolean gabble_parse_xmpp_uri (const gchar *uri, gchar **node, gchar **domain, gchar **resource, GError **error) { gboolean ret = FALSE; gchar *scheme; const gchar *jid; gchar *tmp_node = NULL; gchar *tmp_domain = NULL; gchar *tmp_resource = NULL; gchar *unescaped_node = NULL; gchar *unescaped_domain = NULL; gchar *unescaped_resource = NULL; gchar *unescaped_jid = NULL; gchar *normalized_jid = NULL; GError *gabble_error = NULL; g_return_val_if_fail (uri != NULL, FALSE); g_return_val_if_fail (domain != NULL, FALSE); scheme = g_uri_parse_scheme (uri); if (scheme == NULL) { g_set_error (error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT, "'%s' is not a valid URI", uri); goto OUT; } jid = uri + strlen (scheme) + 1; /* Strip the scheme */ if (!wocky_decode_jid (jid, &tmp_node, &tmp_domain, &tmp_resource)) { g_set_error (error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT, "'%s' is not a valid XMPP URI", uri); goto OUT; } /* convert from "foo%3F" to "foo?" */ if (tmp_node) { unescaped_node = g_uri_unescape_string (tmp_node, NULL); if (unescaped_node == NULL) { g_set_error (error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT, "'%s' is not a valid XMPP URI", uri); goto OUT; } } g_assert (tmp_domain); unescaped_domain = g_uri_unescape_string (tmp_domain, NULL); if (unescaped_domain == NULL) { g_set_error (error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT, "'%s' is not a valid XMPP URI", uri); goto OUT; } if (tmp_resource) { unescaped_resource = g_uri_unescape_string (tmp_resource, NULL); if (unescaped_resource == NULL) { g_set_error (error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT, "'%s' is not a valid XMPP URI", uri); goto OUT; } } unescaped_jid = gabble_encode_jid (unescaped_node, unescaped_domain, unescaped_resource); normalized_jid = gabble_normalize_contact (NULL, unescaped_jid, GUINT_TO_POINTER (GABBLE_JID_GLOBAL), &gabble_error); if (gabble_error != NULL) { g_set_error (error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT, "'%s' is not a valid XMPP URI: %s", uri, gabble_error->message); g_error_free (gabble_error); goto OUT; } if (!wocky_decode_jid (normalized_jid, node, domain, resource)) { g_set_error (error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT, "'%s' is not a valid XMPP URI", uri); goto OUT; } ret = TRUE; OUT: g_free (scheme); g_free (tmp_node); g_free (tmp_domain); g_free (tmp_resource); g_free (unescaped_node); g_free (unescaped_domain); g_free (unescaped_resource); g_free (unescaped_jid); g_free (normalized_jid); return ret; } ���������������telepathy-gabble-0.18.2/src/addressing-util.h�������������������������������������������������������0000644�0001750�0001750�00000005003�12200204333�021036� 0����������������������������������������������������������������������������������������������������ustar�00smcv����������������������������smcv����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * addressing-util.c - Headers for Gabble addressing utility functions * Copyright (C) 2010 Collabora Ltd. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 __GABBLE_UTIL_ADDRESSING_H__ #define __GABBLE_UTIL_ADDRESSING_H__ #include <telepathy-glib/telepathy-glib.h> const gchar * const * gabble_get_addressable_uri_schemes (void); const gchar * const * gabble_get_addressable_vcard_fields (void); gchar * gabble_normalize_contact_uri (const gchar *uri, GError **error); gchar * gabble_uri_to_jid (const gchar *uri, GError **error); gchar * gabble_jid_to_uri (const gchar *scheme, const gchar *jid, GError **error); TpHandle gabble_ensure_handle_from_uri (TpHandleRepoIface *repo, const gchar *uri, GError **error); gchar * gabble_normalize_vcard_address (const gchar *vcard_field, const gchar *vcard_address, GError **error); gchar * gabble_vcard_address_to_jid (const gchar *vcard_field, const gchar *vcard_address, GError **error); gchar * gabble_jid_to_vcard_address (const gchar *vcard_field, const gchar *jid, GError **error); TpHandle gabble_ensure_handle_from_vcard_address (TpHandleRepoIface *repo, const gchar *vcard_field, const gchar *vcard_address, GError **error); gchar **gabble_uris_for_handle (TpHandleRepoIface *contact_repo, TpHandle contact); GHashTable *gabble_vcard_addresses_for_handle (TpHandleRepoIface *contact_repo, TpHandle contact); gchar *gabble_uri_for_handle (TpHandleRepoIface *contact_repo, const gchar *uri_scheme, TpHandle contact); gchar *gabble_vcard_address_for_handle (TpHandleRepoIface *contact_repo, const gchar *vcard_field, TpHandle contact); gboolean gabble_parse_xmpp_uri (const gchar *uri, gchar **node, gchar **domain, gchar **resource, GError **error); #endif /* __GABBLE_UTIL_ADDRESSING_H__ */ �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������telepathy-gabble-0.18.2/src/Makefile.am�������������������������������������������������������������0000644�0001750�0001750�00000020516�12227000321�017631� 0����������������������������������������������������������������������������������������������������ustar�00smcv����������������������������smcv����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������BUILT_SOURCES = \ gabble-signals-marshal.h \ gabble-signals-marshal.c \ gabble-signals-marshal.list \ gabble-enumtypes.h \ gabble-enumtypes.c CLEANFILES = $(BUILT_SOURCES) EXTRA_DIST = libexec_PROGRAMS=telepathy-gabble noinst_PROGRAMS = write-mgr-file libgabble_convenience_la_SOURCES = \ addressing-util.h \ addressing-util.c \ auth-manager.h \ auth-manager.c \ bytestream-factory.h \ bytestream-factory.c \ bytestream-ibb.h \ bytestream-ibb.c \ bytestream-iface.h \ bytestream-iface.c \ bytestream-muc.h \ bytestream-muc.c \ bytestream-multiple.h \ bytestream-multiple.c \ bytestream-socks5.h \ bytestream-socks5.c \ capabilities.c \ caps-hash.h \ caps-hash.c \ caps-channel-manager.c \ conn-addressing.h \ conn-addressing.c \ conn-aliasing.h \ conn-aliasing.c \ conn-avatars.h \ conn-avatars.c \ conn-client-types.h \ conn-client-types.c \ conn-contact-info.h \ conn-contact-info.c \ conn-location.h \ conn-location.c \ conn-olpc.h \ conn-olpc.c \ conn-power-saving.h \ conn-power-saving.c \ conn-presence.h \ conn-presence.c \ conn-sidecars.h \ conn-sidecars.c \ conn-util.h \ conn-util.c \ conn-mail-notif.h \ conn-mail-notif.c \ connection.h \ connection.c \ connection-manager.h \ connection-manager.c \ debug.h \ debug.c \ disco.h \ disco.c \ error.c \ error.h \ gabble.c \ gabble.h \ im-channel.h \ im-channel.c \ im-factory.h \ im-factory.c \ legacy-caps.h \ legacy-caps.c \ message-util.h \ message-util.c \ muc-channel.h \ muc-channel.c \ muc-factory.h \ muc-factory.c \ muc-tube-dbus.h \ muc-tube-dbus.c \ muc-tube-stream.h \ muc-tube-stream.c \ namespaces.h \ olpc-activity.h \ olpc-activity.c \ plugin-loader.h \ plugin-loader.c \ presence.h \ presence.c \ presence-cache.h \ presence-cache.c \ protocol.h \ protocol.c \ private-tubes-factory.h \ private-tubes-factory.c \ request-pipeline.h \ request-pipeline.c \ roster.h \ roster.c \ room-config.h \ room-config.c \ roomlist-channel.h \ roomlist-channel.c \ roomlist-manager.h \ roomlist-manager.c \ search-channel.h \ search-channel.c \ search-manager.h \ search-manager.c \ server-sasl-channel.h \ server-sasl-channel.c \ server-tls-channel.h \ server-tls-channel.c \ server-tls-manager.h \ server-tls-manager.c \ sidecar.c \ tls-certificate.h \ tls-certificate.c \ tube-iface.h \ tube-iface.c \ tube-dbus.h \ tube-dbus.c \ tube-stream.h \ tube-stream.c \ types.h \ util.h \ util.c \ vcard-manager.h \ vcard-manager.c if ENABLE_FILE_TRANSFER libgabble_convenience_la_SOURCES += \ ft-channel.c \ ft-channel.h \ ft-manager.c \ ft-manager.h endif if ENABLE_VOIP libgabble_convenience_la_SOURCES += \ base-call-channel.h \ base-call-channel.c \ call-content.h \ call-content.c \ call-channel.h \ call-channel.c \ call-muc-channel.h \ call-muc-channel.c \ call-member.h \ call-member.c \ call-member-content.h \ call-member-content.c \ call-stream.h \ call-stream.c \ jingle-share.h \ jingle-share.c \ jingle-mint.h \ jingle-mint.c \ jingle-tp-util.h \ jingle-tp-util.c \ media-channel.h \ media-channel-internal.h \ media-channel.c \ media-channel-hold.c \ media-stream.h \ media-stream.c \ media-factory.h \ media-factory.c endif if ENABLE_JINGLE_FILE_TRANSFER libgabble_convenience_la_SOURCES += \ gtalk-file-collection.c \ gtalk-file-collection.h endif enumtype_sources = \ $(top_srcdir)/src/connection.h \ $(top_srcdir)/src/room-config.h \ $(top_srcdir)/src/presence.h libgabble_convenience_la_LIBADD = \ $(top_builddir)/extensions/libgabble-extensions.la \ $(top_builddir)/lib/gibber/libgibber.la \ libgabble-plugins.la \ $(ALL_LIBS) nodist_libgabble_convenience_la_SOURCES = \ $(BUILT_SOURCES) write_mgr_file_SOURCES = write-mgr-file.c write_mgr_file_LDADD = libgabble-convenience.la telepathy_gabble_SOURCES = \ main.c check_c_sources = \ $(telepathy_gabble_SOURCES) \ $(libgabble_convenience_la_SOURCES) \ $(write_mgr_file_SOURCES) include $(top_srcdir)/tools/check-coding-style.mk check-local: check-coding-style telepathy_gabble_LDADD = libgabble-convenience.la telepathy_gabble_LDFLAGS = -export-dynamic noinst_LTLIBRARIES = libgabble-convenience.la pluginexeclib_LTLIBRARIES = libgabble-plugins.la # Gabble's plugin API is not stable yet (it can't be, since neither is Wocky), # so use -release to make the SONAME of the plugin library change with every # Gabble release. libgabble_plugins_la_LDFLAGS = \ -no-undefined \ -release $(VERSION) \ $(NULL) libgabble_plugins_la_LIBADD = \ $(ALL_LIBS) libgabble_plugins_la_SOURCES = \ capabilities.c \ caps-channel-manager.c \ debug.c \ error.c \ plugin.c \ plugin-connection.c \ sidecar.c AM_CFLAGS = $(ERROR_CFLAGS) -I$(top_srcdir) -I$(top_builddir) \ @DBUS_CFLAGS@ @GLIB_CFLAGS@ @WOCKY_CFLAGS@ \ @TP_GLIB_CFLAGS@ \ @SOUP_CFLAGS@ @NICE_CFLAGS@ @GMODULE_CFLAGS@ \ -I $(top_srcdir)/lib -I $(top_builddir)/lib \ -DG_LOG_DOMAIN=\"gabble\" \ -DPLUGIN_DIR=\"$(pluginexecdir)\" # following flag is requied to make getnameinfo work if WINDOWS AM_CFLAGS += -D_WIN32_WINNT=0x0501 endif ALL_LIBS = @DBUS_LIBS@ @GLIB_LIBS@ @WOCKY_LIBS@ @TP_GLIB_LIBS@ \ @SOUP_LIBS@ @NICE_LIBS@ @GMODULE_LIBS@ # build gibber first all: gibber gibber: @${MAKE} -C $(top_builddir)/lib/gibber libgibber.la .PHONY: gibber gabble-signals-marshal.list: $(libgabble_convenience_la_SOURCES) Makefile.am @( cd $(srcdir) && \ sed -n -e 's/.*gabble_marshal_\([[:upper:][:digit:]]*__[[:upper:][:digit:]_]*\).*/\1/p' \ $(libgabble_convenience_la_SOURCES) ) \ | sed -e 's/__/:/' -e 'y/_/,/' | sort -u > $@.tmp @if cmp -s $@.tmp $@; then \ rm $@.tmp; \ else \ mv $@.tmp $@; \ fi %-signals-marshal.h: %-signals-marshal.list Makefile.am $(AM_V_GEN)glib-genmarshal --header --prefix=$(subst -,_,$*)_marshal $< > $@ %-signals-marshal.c: %-signals-marshal.list Makefile.am $(AM_V_GEN){ echo '#include "$*-signals-marshal.h"' && \ glib-genmarshal --body --prefix=$(subst -,_,$*)_marshal $< ; \ } > $@ # rules for making the glib enum objects gabble-enumtypes.h: $(enumtype_sources) Makefile.in $(AM_V_GEN)glib-mkenums \ --fhead "#ifndef __GABBLE_ENUM_TYPES_H__\n#define __GABBLE_ENUM_TYPES_H__\n\n#include <glib-object.h>\n\nG_BEGIN_DECLS\n" \ --fprod "/* enumerations from \"@filename@\" */\n" \ --vhead "GType @enum_name@_get_type (void);\n#define GABBLE_TYPE_@ENUMSHORT@ (@enum_name@_get_type())\n" \ --ftail "G_END_DECLS\n\n#endif /* __GABBLE_ENUM_TYPES_H__ */" \ $(enumtype_sources) > $@ gabble-enumtypes.c: $(enumtype_sources) Makefile.in $(AM_V_GEN)glib-mkenums \ --fhead "#include \"config.h\"\n" \ --fhead "#include <$*.h>" \ --fprod "\n/* enumerations from \"@filename@\" */\n#include \"@filename@\"" \ --vhead "GType\n@enum_name@_get_type (void)\n{\n static GType etype = 0;\n if (etype == 0) {\n static const G@Type@Value values[] = {" \ --vprod " { @VALUENAME@, \"@VALUENAME@\", \"@valuenick@\" }," \ --vtail " { 0, NULL, NULL }\n };\n etype = g_@type@_register_static (\"@EnumName@\", values);\n }\n return etype;\n}\n" \ $(enumtype_sources) > $@ Android.mk: Makefile.am $(BUILT_SOURCES) androgenizer -:PROJECT telepathy-gabble \ -:REL_TOP $(top_srcdir) -:ABS_TOP $(abs_top_srcdir) \ -:SHARED gabble-plugins -:TAGS eng debug \ -:SOURCES $(libgabble_plugins_la_SOURCES) \ -:CFLAGS $(DEFS) $(CFLAGS) $(DEFAULT_INCLUDES) $(INCLUDES) \ $(AM_CFLAGS) \ -:CPPFLAGS $(CPPFLAGS) $(AM_CPPFLAGS) \ -:LDFLAGS $(libgabble_plugins_la_LDFLAGS) \ $(libgabble_plugins_la_LIBADD) \ -:SHARED telepathy-gabble -:TAGS eng debug \ -:SOURCES $(libgabble_convenience_la_SOURCES) \ $(nodist_libgabble_convenience_la_SOURCES) main.c \ -:CFLAGS $(DEFS) $(CFLAGS) $(DEFAULT_INCLUDES) $(INCLUDES) \ $(AM_CFLAGS) -DBUILD_AS_ANDROID_SERVICE \ -:CPPFLAGS $(CPPFLAGS) $(AM_CPPFLAGS) $(telepathy_gabble_LDFLAGS) \ -:LDFLAGS $(telepathy_gabble_LDADD) $(libgabble_convenience_la_LIBADD) \ $(lib_LTLIBRARIES) \ > $@ ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������telepathy-gabble-0.18.2/src/Makefile.in�������������������������������������������������������������0000644�0001750�0001750�00000133041�12312536074�017656� 0����������������������������������������������������������������������������������������������������ustar�00smcv����������������������������smcv����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Makefile.in generated by automake 1.14.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2013 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ VPATH = @srcdir@ am__is_gnu_make = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)' am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ libexec_PROGRAMS = telepathy-gabble$(EXEEXT) noinst_PROGRAMS = write-mgr-file$(EXEEXT) @ENABLE_FILE_TRANSFER_TRUE@am__append_1 = \ @ENABLE_FILE_TRANSFER_TRUE@ ft-channel.c \ @ENABLE_FILE_TRANSFER_TRUE@ ft-channel.h \ @ENABLE_FILE_TRANSFER_TRUE@ ft-manager.c \ @ENABLE_FILE_TRANSFER_TRUE@ ft-manager.h @ENABLE_VOIP_TRUE@am__append_2 = \ @ENABLE_VOIP_TRUE@ base-call-channel.h \ @ENABLE_VOIP_TRUE@ base-call-channel.c \ @ENABLE_VOIP_TRUE@ call-content.h \ @ENABLE_VOIP_TRUE@ call-content.c \ @ENABLE_VOIP_TRUE@ call-channel.h \ @ENABLE_VOIP_TRUE@ call-channel.c \ @ENABLE_VOIP_TRUE@ call-muc-channel.h \ @ENABLE_VOIP_TRUE@ call-muc-channel.c \ @ENABLE_VOIP_TRUE@ call-member.h \ @ENABLE_VOIP_TRUE@ call-member.c \ @ENABLE_VOIP_TRUE@ call-member-content.h \ @ENABLE_VOIP_TRUE@ call-member-content.c \ @ENABLE_VOIP_TRUE@ call-stream.h \ @ENABLE_VOIP_TRUE@ call-stream.c \ @ENABLE_VOIP_TRUE@ jingle-share.h \ @ENABLE_VOIP_TRUE@ jingle-share.c \ @ENABLE_VOIP_TRUE@ jingle-mint.h \ @ENABLE_VOIP_TRUE@ jingle-mint.c \ @ENABLE_VOIP_TRUE@ jingle-tp-util.h \ @ENABLE_VOIP_TRUE@ jingle-tp-util.c \ @ENABLE_VOIP_TRUE@ media-channel.h \ @ENABLE_VOIP_TRUE@ media-channel-internal.h \ @ENABLE_VOIP_TRUE@ media-channel.c \ @ENABLE_VOIP_TRUE@ media-channel-hold.c \ @ENABLE_VOIP_TRUE@ media-stream.h \ @ENABLE_VOIP_TRUE@ media-stream.c \ @ENABLE_VOIP_TRUE@ media-factory.h \ @ENABLE_VOIP_TRUE@ media-factory.c @ENABLE_JINGLE_FILE_TRANSFER_TRUE@am__append_3 = \ @ENABLE_JINGLE_FILE_TRANSFER_TRUE@ gtalk-file-collection.c \ @ENABLE_JINGLE_FILE_TRANSFER_TRUE@ gtalk-file-collection.h DIST_COMMON = $(top_srcdir)/tools/check-coding-style.mk \ $(srcdir)/Makefile.in $(srcdir)/Makefile.am \ $(top_srcdir)/depcomp # following flag is requied to make getnameinfo work @WINDOWS_TRUE@am__append_4 = -D_WIN32_WINNT=0x0501 subdir = src ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/ax_config_dir.m4 \ $(top_srcdir)/m4/compiler.m4 $(top_srcdir)/m4/libtool.m4 \ $(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \ $(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \ $(top_srcdir)/m4/tp-compiler-flag.m4 \ $(top_srcdir)/m4/tp-compiler-warnings.m4 \ $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; am__vpath_adj = case $$p in \ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ *) f=$$p;; \ esac; am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; am__install_max = 40 am__nobase_strip_setup = \ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` am__nobase_strip = \ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" am__nobase_list = $(am__nobase_strip_setup); \ for p in $$list; do echo "$$p $$p"; done | \ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ if (++n[$$2] == $(am__install_max)) \ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ END { for (dir in files) print dir, files[dir] }' am__base_list = \ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' am__uninstall_files_from_dir = { \ test -z "$$files" \ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ $(am__cd) "$$dir" && rm -f $$files; }; \ } am__installdirs = "$(DESTDIR)$(pluginexeclibdir)" \ "$(DESTDIR)$(libexecdir)" LTLIBRARIES = $(noinst_LTLIBRARIES) $(pluginexeclib_LTLIBRARIES) am__DEPENDENCIES_1 = libgabble_convenience_la_DEPENDENCIES = \ $(top_builddir)/extensions/libgabble-extensions.la \ $(top_builddir)/lib/gibber/libgibber.la libgabble-plugins.la \ $(am__DEPENDENCIES_1) am__libgabble_convenience_la_SOURCES_DIST = addressing-util.h \ addressing-util.c auth-manager.h auth-manager.c \ bytestream-factory.h bytestream-factory.c bytestream-ibb.h \ bytestream-ibb.c bytestream-iface.h bytestream-iface.c \ bytestream-muc.h bytestream-muc.c bytestream-multiple.h \ bytestream-multiple.c bytestream-socks5.h bytestream-socks5.c \ capabilities.c caps-hash.h caps-hash.c caps-channel-manager.c \ conn-addressing.h conn-addressing.c conn-aliasing.h \ conn-aliasing.c conn-avatars.h conn-avatars.c \ conn-client-types.h conn-client-types.c conn-contact-info.h \ conn-contact-info.c conn-location.h conn-location.c \ conn-olpc.h conn-olpc.c conn-power-saving.h \ conn-power-saving.c conn-presence.h conn-presence.c \ conn-sidecars.h conn-sidecars.c conn-util.h conn-util.c \ conn-mail-notif.h conn-mail-notif.c connection.h connection.c \ connection-manager.h connection-manager.c debug.h debug.c \ disco.h disco.c error.c error.h gabble.c gabble.h im-channel.h \ im-channel.c im-factory.h im-factory.c legacy-caps.h \ legacy-caps.c message-util.h message-util.c muc-channel.h \ muc-channel.c muc-factory.h muc-factory.c muc-tube-dbus.h \ muc-tube-dbus.c muc-tube-stream.h muc-tube-stream.c \ namespaces.h olpc-activity.h olpc-activity.c plugin-loader.h \ plugin-loader.c presence.h presence.c presence-cache.h \ presence-cache.c protocol.h protocol.c private-tubes-factory.h \ private-tubes-factory.c request-pipeline.h request-pipeline.c \ roster.h roster.c room-config.h room-config.c \ roomlist-channel.h roomlist-channel.c roomlist-manager.h \ roomlist-manager.c search-channel.h search-channel.c \ search-manager.h search-manager.c server-sasl-channel.h \ server-sasl-channel.c server-tls-channel.h \ server-tls-channel.c server-tls-manager.h server-tls-manager.c \ sidecar.c tls-certificate.h tls-certificate.c tube-iface.h \ tube-iface.c tube-dbus.h tube-dbus.c tube-stream.h \ tube-stream.c types.h util.h util.c vcard-manager.h \ vcard-manager.c ft-channel.c ft-channel.h ft-manager.c \ ft-manager.h base-call-channel.h base-call-channel.c \ call-content.h call-content.c call-channel.h call-channel.c \ call-muc-channel.h call-muc-channel.c call-member.h \ call-member.c call-member-content.h call-member-content.c \ call-stream.h call-stream.c jingle-share.h jingle-share.c \ jingle-mint.h jingle-mint.c jingle-tp-util.h jingle-tp-util.c \ media-channel.h media-channel-internal.h media-channel.c \ media-channel-hold.c media-stream.h media-stream.c \ media-factory.h media-factory.c gtalk-file-collection.c \ gtalk-file-collection.h @ENABLE_FILE_TRANSFER_TRUE@am__objects_1 = ft-channel.lo ft-manager.lo @ENABLE_VOIP_TRUE@am__objects_2 = base-call-channel.lo call-content.lo \ @ENABLE_VOIP_TRUE@ call-channel.lo call-muc-channel.lo \ @ENABLE_VOIP_TRUE@ call-member.lo call-member-content.lo \ @ENABLE_VOIP_TRUE@ call-stream.lo jingle-share.lo \ @ENABLE_VOIP_TRUE@ jingle-mint.lo jingle-tp-util.lo \ @ENABLE_VOIP_TRUE@ media-channel.lo media-channel-hold.lo \ @ENABLE_VOIP_TRUE@ media-stream.lo media-factory.lo @ENABLE_JINGLE_FILE_TRANSFER_TRUE@am__objects_3 = \ @ENABLE_JINGLE_FILE_TRANSFER_TRUE@ gtalk-file-collection.lo am_libgabble_convenience_la_OBJECTS = addressing-util.lo \ auth-manager.lo bytestream-factory.lo bytestream-ibb.lo \ bytestream-iface.lo bytestream-muc.lo bytestream-multiple.lo \ bytestream-socks5.lo capabilities.lo caps-hash.lo \ caps-channel-manager.lo conn-addressing.lo conn-aliasing.lo \ conn-avatars.lo conn-client-types.lo conn-contact-info.lo \ conn-location.lo conn-olpc.lo conn-power-saving.lo \ conn-presence.lo conn-sidecars.lo conn-util.lo \ conn-mail-notif.lo connection.lo connection-manager.lo \ debug.lo disco.lo error.lo gabble.lo im-channel.lo \ im-factory.lo legacy-caps.lo message-util.lo muc-channel.lo \ muc-factory.lo muc-tube-dbus.lo muc-tube-stream.lo \ olpc-activity.lo plugin-loader.lo presence.lo \ presence-cache.lo protocol.lo private-tubes-factory.lo \ request-pipeline.lo roster.lo room-config.lo \ roomlist-channel.lo roomlist-manager.lo search-channel.lo \ search-manager.lo server-sasl-channel.lo server-tls-channel.lo \ server-tls-manager.lo sidecar.lo tls-certificate.lo \ tube-iface.lo tube-dbus.lo tube-stream.lo util.lo \ vcard-manager.lo $(am__objects_1) $(am__objects_2) \ $(am__objects_3) am__objects_4 = gabble-signals-marshal.lo gabble-enumtypes.lo nodist_libgabble_convenience_la_OBJECTS = $(am__objects_4) libgabble_convenience_la_OBJECTS = \ $(am_libgabble_convenience_la_OBJECTS) \ $(nodist_libgabble_convenience_la_OBJECTS) AM_V_lt = $(am__v_lt_@AM_V@) am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) am__v_lt_0 = --silent am__v_lt_1 = libgabble_plugins_la_DEPENDENCIES = $(am__DEPENDENCIES_1) am_libgabble_plugins_la_OBJECTS = capabilities.lo \ caps-channel-manager.lo debug.lo error.lo plugin.lo \ plugin-connection.lo sidecar.lo libgabble_plugins_la_OBJECTS = $(am_libgabble_plugins_la_OBJECTS) libgabble_plugins_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ $(AM_CFLAGS) $(CFLAGS) $(libgabble_plugins_la_LDFLAGS) \ $(LDFLAGS) -o $@ PROGRAMS = $(libexec_PROGRAMS) $(noinst_PROGRAMS) am_telepathy_gabble_OBJECTS = main.$(OBJEXT) telepathy_gabble_OBJECTS = $(am_telepathy_gabble_OBJECTS) telepathy_gabble_DEPENDENCIES = libgabble-convenience.la telepathy_gabble_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ $(AM_CFLAGS) $(CFLAGS) $(telepathy_gabble_LDFLAGS) $(LDFLAGS) \ -o $@ am_write_mgr_file_OBJECTS = write-mgr-file.$(OBJEXT) write_mgr_file_OBJECTS = $(am_write_mgr_file_OBJECTS) write_mgr_file_DEPENDENCIES = libgabble-convenience.la AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) depcomp = $(SHELL) $(top_srcdir)/depcomp am__depfiles_maybe = depfiles am__mv = mv -f COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ $(AM_CFLAGS) $(CFLAGS) AM_V_CC = $(am__v_CC_@AM_V@) am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) am__v_CC_0 = @echo " CC " $@; am__v_CC_1 = CCLD = $(CC) LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ $(AM_LDFLAGS) $(LDFLAGS) -o $@ AM_V_CCLD = $(am__v_CCLD_@AM_V@) am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) am__v_CCLD_0 = @echo " CCLD " $@; am__v_CCLD_1 = SOURCES = $(libgabble_convenience_la_SOURCES) \ $(nodist_libgabble_convenience_la_SOURCES) \ $(libgabble_plugins_la_SOURCES) $(telepathy_gabble_SOURCES) \ $(write_mgr_file_SOURCES) DIST_SOURCES = $(am__libgabble_convenience_la_SOURCES_DIST) \ $(libgabble_plugins_la_SOURCES) $(telepathy_gabble_SOURCES) \ $(write_mgr_file_SOURCES) am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) # Read a list of newline-separated strings from the standard input, # and print each of them once, without duplicates. Input order is # *not* preserved. am__uniquify_input = $(AWK) '\ BEGIN { nonempty = 0; } \ { items[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in items) print i; }; } \ ' # Make sure the list of sources is unique. This is necessary because, # e.g., the same source file might be shared among _SOURCES variables # for different programs/libraries. am__define_uniq_tagged_files = \ list='$(am__tagged_files)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | $(am__uniquify_input)` ETAGS = etags CTAGS = ctags DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ AR = @AR@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CFLAGS = @CFLAGS@ CLIENT_TYPE = @CLIENT_TYPE@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CYGPATH_W = @CYGPATH_W@ DBUS_CFLAGS = @DBUS_CFLAGS@ DBUS_LIBS = @DBUS_LIBS@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DLLTOOL = @DLLTOOL@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ ENABLE_CODING_STYLE_CHECKS = @ENABLE_CODING_STYLE_CHECKS@ ENABLE_PLUGINS = @ENABLE_PLUGINS@ ERROR_CFLAGS = @ERROR_CFLAGS@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ GLIB_CFLAGS = @GLIB_CFLAGS@ GLIB_GENMARSHAL = @GLIB_GENMARSHAL@ GLIB_LIBS = @GLIB_LIBS@ GMODULE_CFLAGS = @GMODULE_CFLAGS@ GMODULE_LIBS = @GMODULE_LIBS@ GREP = @GREP@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ LD = @LD@ LDFLAGS = @LDFLAGS@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBTOOL = @LIBTOOL@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBOBJS = @LTLIBOBJS@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ NICE_CFLAGS = @NICE_CFLAGS@ NICE_LIBS = @NICE_LIBS@ NM = @NM@ NMEDIT = @NMEDIT@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ PYTHON = @PYTHON@ PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@ PYTHON_PLATFORM = @PYTHON_PLATFORM@ PYTHON_PREFIX = @PYTHON_PREFIX@ PYTHON_VERSION = @PYTHON_VERSION@ RANLIB = @RANLIB@ SED = @SED@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ SOUP_CFLAGS = @SOUP_CFLAGS@ SOUP_LIBS = @SOUP_LIBS@ STRIP = @STRIP@ TEST_PYTHON = @TEST_PYTHON@ TP_GLIB_CFLAGS = @TP_GLIB_CFLAGS@ TP_GLIB_LIBS = @TP_GLIB_LIBS@ VERSION = @VERSION@ WOCKY_CFLAGS = @WOCKY_CFLAGS@ WOCKY_LIBS = @WOCKY_LIBS@ XSLTPROC = @XSLTPROC@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ gabbletestsdir = @gabbletestsdir@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ mandir = @mandir@ mkdir_p = @mkdir_p@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ pkgpyexecdir = @pkgpyexecdir@ pkgpythondir = @pkgpythondir@ pluginexecdir = @pluginexecdir@ pluginexeclibdir = @pluginexeclibdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ pyexecdir = @pyexecdir@ pythondir = @pythondir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ srcdir = @srcdir@ sysconfdir = @sysconfdir@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ BUILT_SOURCES = \ gabble-signals-marshal.h \ gabble-signals-marshal.c \ gabble-signals-marshal.list \ gabble-enumtypes.h \ gabble-enumtypes.c CLEANFILES = $(BUILT_SOURCES) EXTRA_DIST = libgabble_convenience_la_SOURCES = addressing-util.h addressing-util.c \ auth-manager.h auth-manager.c bytestream-factory.h \ bytestream-factory.c bytestream-ibb.h bytestream-ibb.c \ bytestream-iface.h bytestream-iface.c bytestream-muc.h \ bytestream-muc.c bytestream-multiple.h bytestream-multiple.c \ bytestream-socks5.h bytestream-socks5.c capabilities.c \ caps-hash.h caps-hash.c caps-channel-manager.c \ conn-addressing.h conn-addressing.c conn-aliasing.h \ conn-aliasing.c conn-avatars.h conn-avatars.c \ conn-client-types.h conn-client-types.c conn-contact-info.h \ conn-contact-info.c conn-location.h conn-location.c \ conn-olpc.h conn-olpc.c conn-power-saving.h \ conn-power-saving.c conn-presence.h conn-presence.c \ conn-sidecars.h conn-sidecars.c conn-util.h conn-util.c \ conn-mail-notif.h conn-mail-notif.c connection.h connection.c \ connection-manager.h connection-manager.c debug.h debug.c \ disco.h disco.c error.c error.h gabble.c gabble.h im-channel.h \ im-channel.c im-factory.h im-factory.c legacy-caps.h \ legacy-caps.c message-util.h message-util.c muc-channel.h \ muc-channel.c muc-factory.h muc-factory.c muc-tube-dbus.h \ muc-tube-dbus.c muc-tube-stream.h muc-tube-stream.c \ namespaces.h olpc-activity.h olpc-activity.c plugin-loader.h \ plugin-loader.c presence.h presence.c presence-cache.h \ presence-cache.c protocol.h protocol.c private-tubes-factory.h \ private-tubes-factory.c request-pipeline.h request-pipeline.c \ roster.h roster.c room-config.h room-config.c \ roomlist-channel.h roomlist-channel.c roomlist-manager.h \ roomlist-manager.c search-channel.h search-channel.c \ search-manager.h search-manager.c server-sasl-channel.h \ server-sasl-channel.c server-tls-channel.h \ server-tls-channel.c server-tls-manager.h server-tls-manager.c \ sidecar.c tls-certificate.h tls-certificate.c tube-iface.h \ tube-iface.c tube-dbus.h tube-dbus.c tube-stream.h \ tube-stream.c types.h util.h util.c vcard-manager.h \ vcard-manager.c $(am__append_1) $(am__append_2) \ $(am__append_3) enumtype_sources = \ $(top_srcdir)/src/connection.h \ $(top_srcdir)/src/room-config.h \ $(top_srcdir)/src/presence.h libgabble_convenience_la_LIBADD = \ $(top_builddir)/extensions/libgabble-extensions.la \ $(top_builddir)/lib/gibber/libgibber.la \ libgabble-plugins.la \ $(ALL_LIBS) nodist_libgabble_convenience_la_SOURCES = \ $(BUILT_SOURCES) write_mgr_file_SOURCES = write-mgr-file.c write_mgr_file_LDADD = libgabble-convenience.la telepathy_gabble_SOURCES = \ main.c check_c_sources = \ $(telepathy_gabble_SOURCES) \ $(libgabble_convenience_la_SOURCES) \ $(write_mgr_file_SOURCES) telepathy_gabble_LDADD = libgabble-convenience.la telepathy_gabble_LDFLAGS = -export-dynamic noinst_LTLIBRARIES = libgabble-convenience.la pluginexeclib_LTLIBRARIES = libgabble-plugins.la # Gabble's plugin API is not stable yet (it can't be, since neither is Wocky), # so use -release to make the SONAME of the plugin library change with every # Gabble release. libgabble_plugins_la_LDFLAGS = \ -no-undefined \ -release $(VERSION) \ $(NULL) libgabble_plugins_la_LIBADD = \ $(ALL_LIBS) libgabble_plugins_la_SOURCES = \ capabilities.c \ caps-channel-manager.c \ debug.c \ error.c \ plugin.c \ plugin-connection.c \ sidecar.c AM_CFLAGS = $(ERROR_CFLAGS) -I$(top_srcdir) -I$(top_builddir) \ @DBUS_CFLAGS@ @GLIB_CFLAGS@ @WOCKY_CFLAGS@ @TP_GLIB_CFLAGS@ \ @SOUP_CFLAGS@ @NICE_CFLAGS@ @GMODULE_CFLAGS@ -I \ $(top_srcdir)/lib -I $(top_builddir)/lib \ -DG_LOG_DOMAIN=\"gabble\" -DPLUGIN_DIR=\"$(pluginexecdir)\" \ $(am__append_4) ALL_LIBS = @DBUS_LIBS@ @GLIB_LIBS@ @WOCKY_LIBS@ @TP_GLIB_LIBS@ \ @SOUP_LIBS@ @NICE_LIBS@ @GMODULE_LIBS@ all: $(BUILT_SOURCES) $(MAKE) $(AM_MAKEFLAGS) all-am .SUFFIXES: .SUFFIXES: .c .lo .o .obj $(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(top_srcdir)/tools/check-coding-style.mk $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu src/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --gnu src/Makefile .PRECIOUS: Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ esac; $(top_srcdir)/tools/check-coding-style.mk: $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): clean-noinstLTLIBRARIES: -test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES) @list='$(noinst_LTLIBRARIES)'; \ locs=`for p in $$list; do echo $$p; done | \ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ sort -u`; \ test -z "$$locs" || { \ echo rm -f $${locs}; \ rm -f $${locs}; \ } install-pluginexeclibLTLIBRARIES: $(pluginexeclib_LTLIBRARIES) @$(NORMAL_INSTALL) @list='$(pluginexeclib_LTLIBRARIES)'; test -n "$(pluginexeclibdir)" || list=; \ list2=; for p in $$list; do \ if test -f $$p; then \ list2="$$list2 $$p"; \ else :; fi; \ done; \ test -z "$$list2" || { \ echo " $(MKDIR_P) '$(DESTDIR)$(pluginexeclibdir)'"; \ $(MKDIR_P) "$(DESTDIR)$(pluginexeclibdir)" || exit 1; \ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(pluginexeclibdir)'"; \ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(pluginexeclibdir)"; \ } uninstall-pluginexeclibLTLIBRARIES: @$(NORMAL_UNINSTALL) @list='$(pluginexeclib_LTLIBRARIES)'; test -n "$(pluginexeclibdir)" || list=; \ for p in $$list; do \ $(am__strip_dir) \ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(pluginexeclibdir)/$$f'"; \ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(pluginexeclibdir)/$$f"; \ done clean-pluginexeclibLTLIBRARIES: -test -z "$(pluginexeclib_LTLIBRARIES)" || rm -f $(pluginexeclib_LTLIBRARIES) @list='$(pluginexeclib_LTLIBRARIES)'; \ locs=`for p in $$list; do echo $$p; done | \ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ sort -u`; \ test -z "$$locs" || { \ echo rm -f $${locs}; \ rm -f $${locs}; \ } libgabble-convenience.la: $(libgabble_convenience_la_OBJECTS) $(libgabble_convenience_la_DEPENDENCIES) $(EXTRA_libgabble_convenience_la_DEPENDENCIES) $(AM_V_CCLD)$(LINK) $(libgabble_convenience_la_OBJECTS) $(libgabble_convenience_la_LIBADD) $(LIBS) libgabble-plugins.la: $(libgabble_plugins_la_OBJECTS) $(libgabble_plugins_la_DEPENDENCIES) $(EXTRA_libgabble_plugins_la_DEPENDENCIES) $(AM_V_CCLD)$(libgabble_plugins_la_LINK) -rpath $(pluginexeclibdir) $(libgabble_plugins_la_OBJECTS) $(libgabble_plugins_la_LIBADD) $(LIBS) install-libexecPROGRAMS: $(libexec_PROGRAMS) @$(NORMAL_INSTALL) @list='$(libexec_PROGRAMS)'; test -n "$(libexecdir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(libexecdir)'"; \ $(MKDIR_P) "$(DESTDIR)$(libexecdir)" || exit 1; \ fi; \ for p in $$list; do echo "$$p $$p"; done | \ sed 's/$(EXEEXT)$$//' | \ while read p p1; do if test -f $$p \ || test -f $$p1 \ ; then echo "$$p"; echo "$$p"; else :; fi; \ done | \ sed -e 'p;s,.*/,,;n;h' \ -e 's|.*|.|' \ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \ sed 'N;N;N;s,\n, ,g' | \ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \ if ($$2 == $$4) files[d] = files[d] " " $$1; \ else { print "f", $$3 "/" $$4, $$1; } } \ END { for (d in files) print "f", d, files[d] }' | \ while read type dir files; do \ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \ test -z "$$files" || { \ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(libexecdir)$$dir'"; \ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(libexecdir)$$dir" || exit $$?; \ } \ ; done uninstall-libexecPROGRAMS: @$(NORMAL_UNINSTALL) @list='$(libexec_PROGRAMS)'; test -n "$(libexecdir)" || list=; \ files=`for p in $$list; do echo "$$p"; done | \ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \ -e 's/$$/$(EXEEXT)/' \ `; \ test -n "$$list" || exit 0; \ echo " ( cd '$(DESTDIR)$(libexecdir)' && rm -f" $$files ")"; \ cd "$(DESTDIR)$(libexecdir)" && rm -f $$files clean-libexecPROGRAMS: @list='$(libexec_PROGRAMS)'; test -n "$$list" || exit 0; \ echo " rm -f" $$list; \ rm -f $$list || exit $$?; \ test -n "$(EXEEXT)" || exit 0; \ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \ echo " rm -f" $$list; \ rm -f $$list clean-noinstPROGRAMS: @list='$(noinst_PROGRAMS)'; test -n "$$list" || exit 0; \ echo " rm -f" $$list; \ rm -f $$list || exit $$?; \ test -n "$(EXEEXT)" || exit 0; \ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \ echo " rm -f" $$list; \ rm -f $$list telepathy-gabble$(EXEEXT): $(telepathy_gabble_OBJECTS) $(telepathy_gabble_DEPENDENCIES) $(EXTRA_telepathy_gabble_DEPENDENCIES) @rm -f telepathy-gabble$(EXEEXT) $(AM_V_CCLD)$(telepathy_gabble_LINK) $(telepathy_gabble_OBJECTS) $(telepathy_gabble_LDADD) $(LIBS) write-mgr-file$(EXEEXT): $(write_mgr_file_OBJECTS) $(write_mgr_file_DEPENDENCIES) $(EXTRA_write_mgr_file_DEPENDENCIES) @rm -f write-mgr-file$(EXEEXT) $(AM_V_CCLD)$(LINK) $(write_mgr_file_OBJECTS) $(write_mgr_file_LDADD) $(LIBS) mostlyclean-compile: -rm -f *.$(OBJEXT) distclean-compile: -rm -f *.tab.c @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/addressing-util.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/auth-manager.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/base-call-channel.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/bytestream-factory.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/bytestream-ibb.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/bytestream-iface.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/bytestream-muc.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/bytestream-multiple.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/bytestream-socks5.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/call-channel.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/call-content.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/call-member-content.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/call-member.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/call-muc-channel.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/call-stream.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/capabilities.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/caps-channel-manager.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/caps-hash.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/conn-addressing.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/conn-aliasing.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/conn-avatars.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/conn-client-types.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/conn-contact-info.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/conn-location.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/conn-mail-notif.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/conn-olpc.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/conn-power-saving.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/conn-presence.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/conn-sidecars.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/conn-util.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/connection-manager.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/connection.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/debug.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/disco.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/error.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ft-channel.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ft-manager.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gabble-enumtypes.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gabble-signals-marshal.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gabble.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gtalk-file-collection.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/im-channel.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/im-factory.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/jingle-mint.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/jingle-share.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/jingle-tp-util.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/legacy-caps.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/main.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/media-channel-hold.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/media-channel.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/media-factory.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/media-stream.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/message-util.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/muc-channel.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/muc-factory.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/muc-tube-dbus.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/muc-tube-stream.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/olpc-activity.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/plugin-connection.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/plugin-loader.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/plugin.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/presence-cache.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/presence.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/private-tubes-factory.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/protocol.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/request-pipeline.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/room-config.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/roomlist-channel.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/roomlist-manager.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/roster.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/search-channel.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/search-manager.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/server-sasl-channel.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/server-tls-channel.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/server-tls-manager.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sidecar.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tls-certificate.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tube-dbus.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tube-iface.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tube-stream.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/util.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/vcard-manager.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/write-mgr-file.Po@am__quote@ .c.o: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< .c.obj: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` .c.lo: @am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs ID: $(am__tagged_files) $(am__define_uniq_tagged_files); mkid -fID $$unique tags: tags-am TAGS: tags tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) set x; \ here=`pwd`; \ $(am__define_uniq_tagged_files); \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ if test $$# -gt 0; then \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ "$$@" $$unique; \ else \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ $$unique; \ fi; \ fi ctags: ctags-am CTAGS: ctags ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) $(am__define_uniq_tagged_files); \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" cscopelist: cscopelist-am cscopelist-am: $(am__tagged_files) list='$(am__tagged_files)'; \ case "$(srcdir)" in \ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ *) sdir=$(subdir)/$(srcdir) ;; \ esac; \ for i in $$list; do \ if test -f "$$i"; then \ echo "$(subdir)/$$i"; \ else \ echo "$$sdir/$$i"; \ fi; \ done >> $(top_builddir)/cscope.files distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags distdir: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done check-am: all-am $(MAKE) $(AM_MAKEFLAGS) check-local check: $(BUILT_SOURCES) $(MAKE) $(AM_MAKEFLAGS) check-am all-am: Makefile $(LTLIBRARIES) $(PROGRAMS) installdirs: for dir in "$(DESTDIR)$(pluginexeclibdir)" "$(DESTDIR)$(libexecdir)"; do \ test -z "$$dir" || $(MKDIR_P) "$$dir"; \ done install: $(BUILT_SOURCES) $(MAKE) $(AM_MAKEFLAGS) install-am install-exec: install-exec-am install-data: install-data-am uninstall: uninstall-am install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-am install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: clean-generic: -test -z "$(CLEANFILES)" || rm -f $(CLEANFILES) distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." -test -z "$(BUILT_SOURCES)" || rm -f $(BUILT_SOURCES) clean: clean-am clean-am: clean-generic clean-libexecPROGRAMS clean-libtool \ clean-noinstLTLIBRARIES clean-noinstPROGRAMS \ clean-pluginexeclibLTLIBRARIES mostlyclean-am distclean: distclean-am -rm -rf ./$(DEPDIR) -rm -f Makefile distclean-am: clean-am distclean-compile distclean-generic \ distclean-tags dvi: dvi-am dvi-am: html: html-am html-am: info: info-am info-am: install-data-am: install-dvi: install-dvi-am install-dvi-am: install-exec-am: install-libexecPROGRAMS \ install-pluginexeclibLTLIBRARIES install-html: install-html-am install-html-am: install-info: install-info-am install-info-am: install-man: install-pdf: install-pdf-am install-pdf-am: install-ps: install-ps-am install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-am -rm -rf ./$(DEPDIR) -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-am mostlyclean-am: mostlyclean-compile mostlyclean-generic \ mostlyclean-libtool pdf: pdf-am pdf-am: ps: ps-am ps-am: uninstall-am: uninstall-libexecPROGRAMS \ uninstall-pluginexeclibLTLIBRARIES .MAKE: all check check-am install install-am install-strip .PHONY: CTAGS GTAGS TAGS all all-am check check-am check-local clean \ clean-generic clean-libexecPROGRAMS clean-libtool \ clean-noinstLTLIBRARIES clean-noinstPROGRAMS \ clean-pluginexeclibLTLIBRARIES cscopelist-am ctags ctags-am \ distclean distclean-compile distclean-generic \ distclean-libtool distclean-tags distdir dvi dvi-am html \ html-am info info-am install install-am install-data \ install-data-am install-dvi install-dvi-am install-exec \ install-exec-am install-html install-html-am install-info \ install-info-am install-libexecPROGRAMS install-man \ install-pdf install-pdf-am install-pluginexeclibLTLIBRARIES \ install-ps install-ps-am install-strip installcheck \ installcheck-am installdirs maintainer-clean \ maintainer-clean-generic mostlyclean mostlyclean-compile \ mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ tags tags-am uninstall uninstall-am uninstall-libexecPROGRAMS \ uninstall-pluginexeclibLTLIBRARIES check-coding-style: @fail=0; \ if test -n "$(check_misc_sources)"; then \ tools_dir=$(top_srcdir)/tools \ sh $(top_srcdir)/tools/check-misc.sh \ $(addprefix $(srcdir)/,$(check_misc_sources)) || fail=1; \ fi; \ if test -n "$(check_c_sources)"; then \ tools_dir=$(top_srcdir)/tools \ sh $(top_srcdir)/tools/check-c-style.sh \ $(addprefix $(srcdir)/,$(check_c_sources)) || fail=1; \ fi;\ if test yes = "$(ENABLE_CODING_STYLE_CHECKS)"; then \ exit "$$fail";\ else \ exit 0;\ fi check-local: check-coding-style # build gibber first all: gibber gibber: @${MAKE} -C $(top_builddir)/lib/gibber libgibber.la .PHONY: gibber gabble-signals-marshal.list: $(libgabble_convenience_la_SOURCES) Makefile.am @( cd $(srcdir) && \ sed -n -e 's/.*gabble_marshal_\([[:upper:][:digit:]]*__[[:upper:][:digit:]_]*\).*/\1/p' \ $(libgabble_convenience_la_SOURCES) ) \ | sed -e 's/__/:/' -e 'y/_/,/' | sort -u > $@.tmp @if cmp -s $@.tmp $@; then \ rm $@.tmp; \ else \ mv $@.tmp $@; \ fi %-signals-marshal.h: %-signals-marshal.list Makefile.am $(AM_V_GEN)glib-genmarshal --header --prefix=$(subst -,_,$*)_marshal $< > $@ %-signals-marshal.c: %-signals-marshal.list Makefile.am $(AM_V_GEN){ echo '#include "$*-signals-marshal.h"' && \ glib-genmarshal --body --prefix=$(subst -,_,$*)_marshal $< ; \ } > $@ # rules for making the glib enum objects gabble-enumtypes.h: $(enumtype_sources) Makefile.in $(AM_V_GEN)glib-mkenums \ --fhead "#ifndef __GABBLE_ENUM_TYPES_H__\n#define __GABBLE_ENUM_TYPES_H__\n\n#include <glib-object.h>\n\nG_BEGIN_DECLS\n" \ --fprod "/* enumerations from \"@filename@\" */\n" \ --vhead "GType @enum_name@_get_type (void);\n#define GABBLE_TYPE_@ENUMSHORT@ (@enum_name@_get_type())\n" \ --ftail "G_END_DECLS\n\n#endif /* __GABBLE_ENUM_TYPES_H__ */" \ $(enumtype_sources) > $@ gabble-enumtypes.c: $(enumtype_sources) Makefile.in $(AM_V_GEN)glib-mkenums \ --fhead "#include \"config.h\"\n" \ --fhead "#include <$*.h>" \ --fprod "\n/* enumerations from \"@filename@\" */\n#include \"@filename@\"" \ --vhead "GType\n@enum_name@_get_type (void)\n{\n static GType etype = 0;\n if (etype == 0) {\n static const G@Type@Value values[] = {" \ --vprod " { @VALUENAME@, \"@VALUENAME@\", \"@valuenick@\" }," \ --vtail " { 0, NULL, NULL }\n };\n etype = g_@type@_register_static (\"@EnumName@\", values);\n }\n return etype;\n}\n" \ $(enumtype_sources) > $@ Android.mk: Makefile.am $(BUILT_SOURCES) androgenizer -:PROJECT telepathy-gabble \ -:REL_TOP $(top_srcdir) -:ABS_TOP $(abs_top_srcdir) \ -:SHARED gabble-plugins -:TAGS eng debug \ -:SOURCES $(libgabble_plugins_la_SOURCES) \ -:CFLAGS $(DEFS) $(CFLAGS) $(DEFAULT_INCLUDES) $(INCLUDES) \ $(AM_CFLAGS) \ -:CPPFLAGS $(CPPFLAGS) $(AM_CPPFLAGS) \ -:LDFLAGS $(libgabble_plugins_la_LDFLAGS) \ $(libgabble_plugins_la_LIBADD) \ -:SHARED telepathy-gabble -:TAGS eng debug \ -:SOURCES $(libgabble_convenience_la_SOURCES) \ $(nodist_libgabble_convenience_la_SOURCES) main.c \ -:CFLAGS $(DEFS) $(CFLAGS) $(DEFAULT_INCLUDES) $(INCLUDES) \ $(AM_CFLAGS) -DBUILD_AS_ANDROID_SERVICE \ -:CPPFLAGS $(CPPFLAGS) $(AM_CPPFLAGS) $(telepathy_gabble_LDFLAGS) \ -:LDFLAGS $(telepathy_gabble_LDADD) $(libgabble_convenience_la_LIBADD) \ $(lib_LTLIBRARIES) \ > $@ # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������telepathy-gabble-0.18.2/lib/������������������������������������������������������������������������0000755�0001750�0001750�00000000000�12312537050�015561� 5����������������������������������������������������������������������������������������������������ustar�00smcv����������������������������smcv����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������telepathy-gabble-0.18.2/lib/gibber/�����������������������������������������������������������������0000755�0001750�0001750�00000000000�12312537050�017013� 5����������������������������������������������������������������������������������������������������ustar�00smcv����������������������������smcv����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������telepathy-gabble-0.18.2/lib/gibber/gibber-util.c����������������������������������������������������0000644�0001750�0001750�00000004240�12200204333�021353� 0����������������������������������������������������������������������������������������������������ustar�00smcv����������������������������smcv����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * gibber-util.c - Code for Gibber utility functions * Copyright (C) 2007 Collabora Ltd. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 <config.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #ifdef HAVE_UNISTD_H # include <unistd.h> #endif #include "gibber-util.h" void gibber_normalize_address (struct sockaddr_storage *addr) { struct sockaddr_in *s4 = (struct sockaddr_in *) addr; struct sockaddr_in6 *s6 = (struct sockaddr_in6 *) addr; if (s6->sin6_family == AF_INET6 && IN6_IS_ADDR_V4MAPPED (&(s6->sin6_addr))) { /* Normalize to ipv4 address */ guint32 addr_big_endian; guint16 port; memcpy (&addr_big_endian, s6->sin6_addr.s6_addr + 12, 4); port = s6->sin6_port; s4->sin_family = AF_INET; s4->sin_addr.s_addr = addr_big_endian; s4->sin_port = port; } } /** * gibber_strdiff: * @left: The first string to compare (may be NULL) * @right: The second string to compare (may be NULL) * * Return %TRUE if the given strings are different. Unlike #strcmp this * function will handle null pointers, treating them as distinct from any * string. * * Returns: %FALSE if @left and @right are both %NULL, or if * neither is %NULL and both have the same contents; %TRUE otherwise */ gboolean gibber_strdiff (const gchar *left, const gchar *right) { if ((NULL == left) != (NULL == right)) return TRUE; else if (left == right) return FALSE; else return (0 != strcmp (left, right)); } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������telepathy-gabble-0.18.2/lib/gibber/gibber-util.h����������������������������������������������������0000644�0001750�0001750�00000002161�12200204333�021360� 0����������������������������������������������������������������������������������������������������ustar�00smcv����������������������������smcv����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * gibber-util.h - Header for Gibber utility functions * Copyright (C) 2007 Collabora Ltd. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 __GIBBER_UTIL_H__ #define __GIBBER_UTIL_H__ #include "gibber-sockets.h" #include <glib.h> G_BEGIN_DECLS void gibber_normalize_address (struct sockaddr_storage *addr); gboolean gibber_strdiff (const gchar *left, const gchar *right); G_END_DECLS #endif /* #ifndef __GIBBER_UTIL_H__ */ ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������telepathy-gabble-0.18.2/lib/gibber/gibber-sockets-win32.h�������������������������������������������0000644�0001750�0001750�00000002154�12200204333�023020� 0����������������������������������������������������������������������������������������������������ustar�00smcv����������������������������smcv����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* To be included by gibber-sockets.h only. * Do not include this header directly. */ /* * gibber-sockets-win32.h - meta-header for assorted semi-portable socket code * Copyright (C) 2009 Collabora Ltd. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 <winsock2.h> #include <ws2tcpip.h> #include <windows.h> #include <winbase.h> /* Winsock makes some inappropriately-namespaced definitions */ #undef ERROR #undef interface ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������telepathy-gabble-0.18.2/lib/gibber/gibber-sockets-unix.h��������������������������������������������0000644�0001750�0001750�00000003552�12200204333�023044� 0����������������������������������������������������������������������������������������������������ustar�00smcv����������������������������smcv����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* To be included by gibber-sockets.h only. * Do not include this header directly. */ /* * gibber-sockets-unix.h - meta-header for assorted semi-portable socket code * Copyright (C) 2009 Collabora Ltd. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 <config.h> /* prerequisite for stdlib.h on Darwin etc., according to autoconf.info */ #include <stdio.h> /* prerequisite for sys/socket.h on Darwin, according to autoconf.info */ #include <stdlib.h> /* prerequisite for all sorts of things */ #include <sys/types.h> #ifdef HAVE_ARPA_INET_H # include <arpa/inet.h> #endif #ifdef HAVE_ARPA_NAMESER_H # include <arpa/nameser.h> #endif #ifdef HAVE_FCNTL_H # include <fcntl.h> #endif #ifdef HAVE_NETDB_H # include <netdb.h> #endif #ifdef HAVE_NETINET_IN_H # include <netinet/in.h> #endif #ifdef HAVE_RESOLV_H /* autoconf.info recommends putting sys/types.h, netinet/in.h, * arpa/nameser.h, netdb.h before this one; if you re-order this header, * please keep that true */ # include <resolv.h> #endif #ifdef HAVE_SYS_IOCTL_H # include <sys/ioctl.h> #endif #ifdef HAVE_SYS_UN_H # include <sys/un.h> #endif #ifdef HAVE_SYS_SOCKET_H # include <sys/socket.h> #endif ������������������������������������������������������������������������������������������������������������������������������������������������������telepathy-gabble-0.18.2/lib/gibber/gibber-sockets.h�������������������������������������������������0000644�0001750�0001750�00000003003�12200204333�022052� 0����������������������������������������������������������������������������������������������������ustar�00smcv����������������������������smcv����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * gibber-sockets.h - meta-header for assorted semi-portable socket code * Copyright (C) 2009 Collabora Ltd. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 <config.h> #ifndef GIBBER_SOCKETS_H #define GIBBER_SOCKETS_H #include <glib.h> #ifdef G_OS_WIN32 # include "gibber-sockets-win32.h" #else # include "gibber-sockets-unix.h" #endif G_BEGIN_DECLS gboolean gibber_connect_errno_requires_retry (int err); gboolean gibber_socket_errno_is_eafnosupport (void); gboolean gibber_socket_errno_is_eaddrinuse (void); void gibber_socket_set_error (GError **error, const gchar *context, GQuark domain, gint code); gint gibber_socket_errno (void); const gchar *gibber_socket_strerror (void); GIOChannel *gibber_io_channel_new_from_socket (gint sockfd); void gibber_socket_set_nonblocking (gint sockfd); G_END_DECLS #endif �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������telepathy-gabble-0.18.2/lib/gibber/gibber-sockets.c�������������������������������������������������0000644�0001750�0001750�00000004751�12200204333�022060� 0����������������������������������������������������������������������������������������������������ustar�00smcv����������������������������smcv����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * gibber-sockets.c - basic portability wrappers for BSD/Winsock differences * * Copyright (C) 2009 Collabora Ltd. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 <config.h> #include "gibber-sockets.h" #include <errno.h> #define DEBUG_FLAG DEBUG_NET #include "gibber-debug.h" gboolean gibber_connect_errno_requires_retry (int err) { #ifdef G_OS_WIN32 return (err == WSAEINPROGRESS || err == WSAEALREADY || err == WSAEWOULDBLOCK || err == WSAEINVAL); #else return (err == EINPROGRESS || err == EALREADY); #endif } gint gibber_socket_errno (void) { #ifdef G_OS_WIN32 return WSAGetLastError (); #else return errno; #endif } const gchar * gibber_socket_strerror (void) { #ifdef G_OS_WIN32 return "[no strerror() in winsock :-(]"; #else return g_strerror (errno); #endif } gboolean gibber_socket_errno_is_eafnosupport (void) { #ifdef G_OS_WIN32 return (WSAGetLastError () == WSAEAFNOSUPPORT); #else return (errno == EAFNOSUPPORT); #endif } gboolean gibber_socket_errno_is_eaddrinuse (void) { #ifdef G_OS_WIN32 return (WSAGetLastError () == WSAEADDRINUSE); #else return (errno == EADDRINUSE); #endif } void gibber_socket_set_error (GError **error, const gchar *context, GQuark domain, gint code) { gint err = gibber_socket_errno (); const gchar *str = gibber_socket_strerror (); DEBUG ("%s: #%d %s", context, err, str); g_set_error (error, domain, code, "%s: #%d %s", context, err, str); } GIOChannel * gibber_io_channel_new_from_socket (gint sockfd) { #ifdef G_OS_WIN32 return g_io_channel_win32_new_socket (sockfd); #else return g_io_channel_unix_new (sockfd); #endif } void gibber_socket_set_nonblocking (gint sockfd) { #ifdef G_OS_WIN32 u_long please_dont_block = 1; ioctlsocket (sockfd, FIONBIO, &please_dont_block); #else fcntl (sockfd, F_SETFL, O_NONBLOCK); #endif } �����������������������telepathy-gabble-0.18.2/lib/gibber/gibber-listener.h������������������������������������������������0000644�0001750�0001750�00000005755�12200204333�022244� 0����������������������������������������������������������������������������������������������������ustar�00smcv����������������������������smcv����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * gibber-listener.h - Header for GibberListener * Copyright (C) 2007, 2008 Collabora Ltd. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 _GIBBER_LISTENER_H_ #define _GIBBER_LISTENER_H_ #include <glib-object.h> G_BEGIN_DECLS GQuark gibber_listener_error_quark (void); #define GIBBER_LISTENER_ERROR \ gibber_listener_error_quark () typedef enum { GIBBER_LISTENER_ERROR_ALREADY_LISTENING, GIBBER_LISTENER_ERROR_ADDRESS_IN_USE, GIBBER_LISTENER_ERROR_FAMILY_NOT_SUPPORTED, GIBBER_LISTENER_ERROR_FAILED, } GibberListenerError; typedef enum { GIBBER_AF_IPV4, GIBBER_AF_IPV6, GIBBER_AF_ANY } GibberAddressFamily; typedef struct _GibberListener GibberListener; typedef struct _GibberListenerClass GibberListenerClass; struct _GibberListenerClass { GObjectClass parent_class; }; struct _GibberListener { GObject parent; gpointer priv; }; GType gibber_listener_get_type (void); /* TYPE MACROS */ #define GIBBER_TYPE_LISTENER \ (gibber_listener_get_type ()) #define GIBBER_LISTENER(obj) \ (G_TYPE_CHECK_INSTANCE_CAST((obj), GIBBER_TYPE_LISTENER,\ GibberListener)) #define GIBBER_LISTENER_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST((klass), GIBBER_TYPE_LISTENER,\ GibberListenerClass)) #define GIBBER_IS_LISTENER(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE((obj), GIBBER_TYPE_LISTENER)) #define GIBBER_IS_LISTENER_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE((klass), GIBBER_TYPE_LISTENER)) #define GIBBER_LISTENER_GET_CLASS(obj) \ (G_TYPE_INSTANCE_GET_CLASS ((obj), GIBBER_TYPE_LISTENER,\ GibberListenerClass)) GibberListener *gibber_listener_new (void); gboolean gibber_listener_listen_tcp (GibberListener *listener, int port, GError **error); gboolean gibber_listener_listen_tcp_af (GibberListener *listener, int port, GibberAddressFamily family, GError **error); gboolean gibber_listener_listen_tcp_loopback (GibberListener *listener, int port, GError **error); gboolean gibber_listener_listen_tcp_loopback_af (GibberListener *listener, int port, GibberAddressFamily family, GError **error); gboolean gibber_listener_listen_socket (GibberListener *listener, gchar *path, gboolean abstract, GError **error); int gibber_listener_get_port (GibberListener *listener); G_END_DECLS #endif /* #ifndef _GIBBER_LISTENER_H_ */ �������������������telepathy-gabble-0.18.2/lib/gibber/gibber-listener.c������������������������������������������������0000644�0001750�0001750�00000032265�12200204333�022233� 0����������������������������������������������������������������������������������������������������ustar�00smcv����������������������������smcv����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * gibber-listener.c - Source for GibberListener * Copyright (C) 2007,2008 Collabora Ltd. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 <config.h> #include <stdlib.h> #include <string.h> #include <time.h> #include <sys/types.h> #include <errno.h> #include <stdio.h> #include "gibber-sockets.h" #ifdef HAVE_UNISTD_H # include <unistd.h> #endif #include <glib.h> #include "gibber-listener.h" #include "gibber-fd-transport.h" #include "gibber-unix-transport.h" #include "gibber-util.h" #define DEBUG_FLAG DEBUG_NET #include "gibber-debug.h" #include "gibber-signals-marshal.h" G_DEFINE_TYPE (GibberListener, gibber_listener, \ G_TYPE_OBJECT); /* signals */ enum { NEW_CONNECTION, LAST_SIGNAL }; static guint signals[LAST_SIGNAL] = {0}; typedef struct { GIOChannel *listener; guint io_watch_in; } Listener; typedef struct _GibberListenerPrivate GibberListenerPrivate; struct _GibberListenerPrivate { GSList *listeners; /* Don't allow to listen again if it is already listening */ gboolean listening; int port; gboolean dispose_has_run; }; #define GIBBER_LISTENER_GET_PRIVATE(obj) \ ((GibberListenerPrivate *) obj->priv) GQuark gibber_listener_error_quark (void) { static GQuark quark = 0; if (!quark) quark = g_quark_from_static_string ( "gibber_listener_error"); return quark; } static gboolean unimplemented (GError **error) { g_set_error (error, GIBBER_LISTENER_ERROR, GIBBER_LISTENER_ERROR_FAILED, "Unimplemented"); return FALSE; } static void gibber_listener_init (GibberListener *self) { GibberListenerPrivate *priv = G_TYPE_INSTANCE_GET_PRIVATE (self, GIBBER_TYPE_LISTENER, GibberListenerPrivate); self->priv = priv; priv->dispose_has_run = FALSE; } static void gibber_listeners_clean_listeners (GibberListener *self) { GibberListenerPrivate *priv = GIBBER_LISTENER_GET_PRIVATE (self); GSList *t; for (t = priv->listeners ; t != NULL ; t = g_slist_delete_link (t, t)) { Listener *l = (Listener *) t->data; g_io_channel_unref (l->listener); g_source_remove (l->io_watch_in); g_slice_free (Listener, l); } priv->listeners = NULL; priv->listening = FALSE; priv->port = 0; } static void gibber_listener_dispose (GObject *object) { GibberListener *self = GIBBER_LISTENER (object); gibber_listeners_clean_listeners (self); G_OBJECT_CLASS (gibber_listener_parent_class)->dispose ( object); } static void gibber_listener_class_init ( GibberListenerClass *gibber_listener_class) { GObjectClass *object_class = G_OBJECT_CLASS ( gibber_listener_class); g_type_class_add_private (gibber_listener_class, sizeof (GibberListenerPrivate)); object_class->dispose = gibber_listener_dispose; signals[NEW_CONNECTION] = g_signal_new ( "new-connection", G_OBJECT_CLASS_TYPE (gibber_listener_class), G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED, 0, NULL, NULL, _gibber_signals_marshal_VOID__OBJECT_POINTER_UINT, G_TYPE_NONE, 3, GIBBER_TYPE_TRANSPORT, G_TYPE_POINTER, G_TYPE_UINT); } GibberListener * gibber_listener_new (void) { return g_object_new (GIBBER_TYPE_LISTENER, NULL); } static gboolean listener_io_in_cb (GIOChannel *source, GIOCondition condition, gpointer user_data) { GibberListener *self = GIBBER_LISTENER (user_data); GibberFdTransport *transport; int fd, nfd; int ret; char host[NI_MAXHOST]; char port[NI_MAXSERV]; struct sockaddr_storage addr; socklen_t addrlen = sizeof (struct sockaddr_storage); fd = g_io_channel_unix_get_fd (source); nfd = accept (fd, (struct sockaddr *) &addr, &addrlen); gibber_normalize_address (&addr); #ifdef GIBBER_TYPE_UNIX_TRANSPORT if (addr.ss_family == AF_UNIX) { transport = GIBBER_FD_TRANSPORT (gibber_unix_transport_new_from_fd (nfd)); /* UNIX sockets doesn't have port */ ret = getnameinfo ((struct sockaddr *) &addr, addrlen, host, NI_MAXHOST, NULL, 0, NI_NUMERICHOST); port[0] = '\0'; } else #endif { transport = g_object_new (GIBBER_TYPE_FD_TRANSPORT, NULL); gibber_fd_transport_set_fd (transport, nfd, TRUE); ret = getnameinfo ((struct sockaddr *) &addr, addrlen, host, NI_MAXHOST, port, NI_MAXSERV, NI_NUMERICHOST | NI_NUMERICSERV); } if (ret == 0) { if (port[0] != '\0') DEBUG ("New connection from %s port %s", host, port); else DEBUG ("New connection from %s", host); } else { DEBUG("New connection..."); } g_signal_emit (self, signals[NEW_CONNECTION], 0, transport, &addr, (guint) addrlen); g_object_unref (transport); return TRUE; } static gboolean add_listener (GibberListener *self, int family, int type, int protocol, struct sockaddr *address, socklen_t addrlen, GError **error) { #define BACKLOG 5 int fd = -1, ret, yes = 1; Listener *l; GibberListenerPrivate *priv = GIBBER_LISTENER_GET_PRIVATE (self); char name [NI_MAXHOST], portname[NI_MAXSERV]; union { struct sockaddr addr; struct sockaddr_in in; struct sockaddr_in6 in6; struct sockaddr_storage storage; } baddress; socklen_t baddrlen = sizeof (baddress); fd = socket (family, type, protocol); if (fd == -1) { gibber_socket_set_error (error, "socket failed", GIBBER_LISTENER_ERROR, gibber_socket_errno_is_eafnosupport () ? GIBBER_LISTENER_ERROR_FAMILY_NOT_SUPPORTED : GIBBER_LISTENER_ERROR_FAILED); goto error; } ret = setsockopt (fd, SOL_SOCKET, SO_REUSEADDR, (char *) &yes, sizeof (int)); if (ret == -1) { gibber_socket_set_error (error, "setsockopt failed", GIBBER_LISTENER_ERROR, GIBBER_LISTENER_ERROR_FAILED); goto error; } #ifdef IPV6_V6ONLY if (family == AF_INET6) { ret = setsockopt (fd, IPPROTO_IPV6, IPV6_V6ONLY, &yes, sizeof (int)); if (ret == -1) { gibber_socket_set_error (error, "setsockopt failed", GIBBER_LISTENER_ERROR, GIBBER_LISTENER_ERROR_FAILED); goto error; } } #endif ret = bind (fd, address, addrlen); if (ret < 0) { gibber_socket_set_error (error, "bind failed", GIBBER_LISTENER_ERROR, gibber_socket_errno_is_eaddrinuse () ? GIBBER_LISTENER_ERROR_ADDRESS_IN_USE : GIBBER_LISTENER_ERROR_FAILED); goto error; } ret = listen (fd, BACKLOG); if (ret == -1) { gibber_socket_set_error (error, "listen failed", GIBBER_LISTENER_ERROR, gibber_socket_errno_is_eaddrinuse () ? GIBBER_LISTENER_ERROR_ADDRESS_IN_USE : GIBBER_LISTENER_ERROR_FAILED); goto error; } ret = getsockname (fd, &baddress.addr, &baddrlen); if (ret == -1) { gibber_socket_set_error (error, "getsockname failed", GIBBER_LISTENER_ERROR, GIBBER_LISTENER_ERROR_FAILED); goto error; } getnameinfo (&baddress.addr, baddrlen, name, sizeof (name), portname, sizeof (portname), NI_NUMERICHOST | NI_NUMERICSERV); DEBUG ( "Listening on %s port %s...", name, portname); switch (family) { case AF_INET: priv->port = g_ntohs (baddress.in.sin_port); break; case AF_INET6: priv->port = g_ntohs (baddress.in6.sin6_port); break; default: priv->port = 0; break; } l = g_slice_new (Listener); l->listener = gibber_io_channel_new_from_socket (fd); g_io_channel_set_close_on_unref (l->listener, TRUE); l->io_watch_in = g_io_add_watch (l->listener, G_IO_IN, listener_io_in_cb, self); priv->listeners = g_slist_append (priv->listeners, l); return TRUE; error: if (fd > 0) close (fd); return FALSE; } /* port: if 0, choose a random port */ static gboolean listen_tcp_af (GibberListener *listener, int port, GibberAddressFamily family, gboolean loopback, GError **error) { GibberListenerPrivate *priv = GIBBER_LISTENER_GET_PRIVATE (listener); struct addrinfo req, *ans = NULL, *a; int ret; gchar sport[6]; if (priv->listening) { g_set_error (error, GIBBER_LISTENER_ERROR, GIBBER_LISTENER_ERROR_ALREADY_LISTENING, "GibberListener is already listening"); return FALSE; } memset (&req, 0, sizeof (req)); if (!loopback) req.ai_flags = AI_PASSIVE; switch (family) { case GIBBER_AF_IPV4: req.ai_family = AF_INET; break; case GIBBER_AF_IPV6: req.ai_family = AF_INET6; break; case GIBBER_AF_ANY: req.ai_family = AF_UNSPEC; break; } req.ai_socktype = SOCK_STREAM; req.ai_protocol = IPPROTO_TCP; g_snprintf (sport, 6, "%d", port); ret = getaddrinfo (NULL, sport, &req, &ans); if (ret != 0) { DEBUG ("getaddrinfo failed: %s", gai_strerror (ret)); g_set_error (error, GIBBER_LISTENER_ERROR, GIBBER_LISTENER_ERROR_FAILED, "%s", gai_strerror (ret)); goto error; } priv->port = 0; for (a = ans ; a != NULL ; a = a->ai_next) { union { struct sockaddr *addr; struct sockaddr_storage *storage; struct sockaddr_in *in; struct sockaddr_in6 *in6; } addr; gboolean ret_; GError *terror = NULL; addr.addr = a->ai_addr; /* the caller let us choose a port and we are not in the first round */ if (port == 0 && priv->port != 0) { if (a->ai_family == AF_INET) addr.in->sin_port = g_htons (priv->port); else if (a->ai_family == AF_INET6) addr.in6->sin6_port = g_htons (priv->port); else g_assert_not_reached (); } ret_ = add_listener (listener, a->ai_family, a->ai_socktype, a->ai_protocol, a->ai_addr, a->ai_addrlen, &terror); if (ret_ == FALSE) { gboolean fatal = !g_error_matches (terror, GIBBER_LISTENER_ERROR, GIBBER_LISTENER_ERROR_FAMILY_NOT_SUPPORTED); /* let error always point to the last error */ g_clear_error (error); g_propagate_error (error, terror); if (fatal) goto error; } else { /* add_listener succeeded: don't allow to listen again */ priv->listening = TRUE; } } /* If all listeners failed, report the last error */ if (priv->listeners == NULL) goto error; /* There was an error at some point, but it was not fatal. ignore it */ g_clear_error (error); freeaddrinfo (ans); return TRUE; error: gibber_listeners_clean_listeners (listener); if (ans != NULL) freeaddrinfo (ans); return FALSE; } gboolean gibber_listener_listen_tcp (GibberListener *listener, int port, GError **error) { return gibber_listener_listen_tcp_af (listener, port, GIBBER_AF_ANY, error); } gboolean gibber_listener_listen_tcp_af (GibberListener *listener, int port, GibberAddressFamily family, GError **error) { return listen_tcp_af (listener, port, family, FALSE, error); } gboolean gibber_listener_listen_tcp_loopback (GibberListener *listener, int port, GError **error) { return gibber_listener_listen_tcp_loopback_af (listener, port, GIBBER_AF_ANY, error); } gboolean gibber_listener_listen_tcp_loopback_af (GibberListener *listener, int port, GibberAddressFamily family, GError **error) { return listen_tcp_af (listener, port, family, TRUE, error); } gboolean gibber_listener_listen_socket (GibberListener *listener, gchar *path, gboolean abstract, GError **error) { GibberListenerPrivate *priv = GIBBER_LISTENER_GET_PRIVATE (listener); #ifdef GIBBER_TYPE_UNIX_TRANSPORT struct sockaddr_un addr; int ret; #endif if (priv->listening) { g_set_error (error, GIBBER_LISTENER_ERROR, GIBBER_LISTENER_ERROR_ALREADY_LISTENING, "GibberListener is already listening"); return FALSE; } #ifdef GIBBER_TYPE_UNIX_TRANSPORT if (abstract) return unimplemented (error); memset (&addr, 0, sizeof (addr)); addr.sun_family = PF_UNIX; snprintf (addr.sun_path, sizeof (addr.sun_path) - 1, "%s", path); ret = add_listener (listener, AF_UNIX, SOCK_STREAM, 0, (struct sockaddr *) &addr, sizeof (addr), error); if (ret == TRUE) { /* add_listener succeeded: don't allow to listen again */ priv->listening = TRUE; } return ret; #else /* Unix transport not supported */ return unimplemented (error); #endif /* Unix transport not supported */ } int gibber_listener_get_port (GibberListener *listener) { GibberListenerPrivate *priv = GIBBER_LISTENER_GET_PRIVATE (listener); return priv->port; } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������telepathy-gabble-0.18.2/lib/gibber/gibber-linklocal-transport.h�������������������������������������0000644�0001750�0001750�00000005310�12200204333�024404� 0����������������������������������������������������������������������������������������������������ustar�00smcv����������������������������smcv����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * gibber-linklocal-transport.h - Header for GibberLLTransport * Copyright (C) 2006 Collabora Ltd. * @author: Sjoerd Simons <sjoerd@luon.net> * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 __GIBBER_LL_TRANSPORT_H__ #define __GIBBER_LL_TRANSPORT_H__ #include <glib-object.h> #include "gibber-fd-transport.h" G_BEGIN_DECLS GQuark gibber_ll_transport_error_quark (void); #define GIBBER_LL_TRANSPORT_ERROR gibber_ll_transport_error_quark() typedef enum { GIBBER_LL_TRANSPORT_ERROR_CONNECT_FAILED, GIBBER_LL_TRANSPORT_ERROR_FAILED, } GibberLLTransportError; typedef struct _GibberLLTransport GibberLLTransport; typedef struct _GibberLLTransportClass GibberLLTransportClass; struct _GibberLLTransportClass { GibberFdTransportClass parent_class; }; struct _GibberLLTransport { GibberFdTransport parent; }; GType gibber_ll_transport_get_type (void); /* TYPE MACROS */ #define GIBBER_TYPE_LL_TRANSPORT \ (gibber_ll_transport_get_type ()) #define GIBBER_LL_TRANSPORT(obj) \ (G_TYPE_CHECK_INSTANCE_CAST((obj), GIBBER_TYPE_LL_TRANSPORT, \ GibberLLTransport)) #define GIBBER_LL_TRANSPORT_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST((klass), GIBBER_TYPE_LL_TRANSPORT, \ GibberLLTransportClass)) #define GIBBER_IS_LL_TRANSPORT(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE((obj), GIBBER_TYPE_LL_TRANSPORT)) #define GIBBER_IS_LL_TRANSPORT_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE((klass), GIBBER_TYPE_LL_TRANSPORT)) #define GIBBER_LL_TRANSPORT_GET_CLASS(obj) \ (G_TYPE_INSTANCE_GET_CLASS ((obj), GIBBER_TYPE_LL_TRANSPORT, \ GibberLLTransportClass)) GibberLLTransport * gibber_ll_transport_new (void); void gibber_ll_transport_open_fd (GibberLLTransport *connection, int fd); gboolean gibber_ll_transport_open_sockaddr (GibberLLTransport *connection, struct sockaddr_storage *addr, GError **error); gboolean gibber_ll_transport_is_incoming (GibberLLTransport *connection); void gibber_ll_transport_set_incoming (GibberLLTransport *connetion, gboolean incoming); G_END_DECLS #endif /* #ifndef __GIBBER_LL_TRANSPORT_H__*/ ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������telepathy-gabble-0.18.2/lib/gibber/gibber-linklocal-transport.c�������������������������������������0000644�0001750�0001750�00000012745�12200204333�024411� 0����������������������������������������������������������������������������������������������������ustar�00smcv����������������������������smcv����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * gibber-linklocal-transport.c - Source for GibberLLTransport * Copyright (C) 2006 Collabora Ltd. * @author: Sjoerd Simons <sjoerd@luon.net> * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 <config.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <errno.h> #ifdef HAVE_UNISTD_H # include <unistd.h> #endif #include "gibber-sockets.h" #include "gibber-linklocal-transport.h" #include "gibber-util.h" #define DEBUG_FLAG DEBUG_NET #include "gibber-debug.h" /* Buffer size used for reading input */ #define BUFSIZE 1024 G_DEFINE_TYPE(GibberLLTransport, gibber_ll_transport, GIBBER_TYPE_FD_TRANSPORT) GQuark gibber_ll_transport_error_quark (void) { static GQuark quark = 0; if (!quark) quark = g_quark_from_static_string ("gibber_linklocal_transport_error"); return quark; } /* private structure */ typedef struct _GibberLLTransportPrivate GibberLLTransportPrivate; struct _GibberLLTransportPrivate { gboolean incoming; gboolean dispose_has_run; }; #define GIBBER_LL_TRANSPORT_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GIBBER_TYPE_LL_TRANSPORT, GibberLLTransportPrivate)) static void gibber_ll_transport_finalize (GObject *object); static void gibber_ll_transport_init (GibberLLTransport *self) { GibberLLTransportPrivate *priv = GIBBER_LL_TRANSPORT_GET_PRIVATE (self); priv->incoming = FALSE; } static void gibber_ll_transport_dispose (GObject *object); static void gibber_ll_transport_class_init (GibberLLTransportClass *gibber_ll_transport_class) { GObjectClass *object_class = G_OBJECT_CLASS (gibber_ll_transport_class); g_type_class_add_private (gibber_ll_transport_class, sizeof (GibberLLTransportPrivate)); object_class->dispose = gibber_ll_transport_dispose; object_class->finalize = gibber_ll_transport_finalize; } void gibber_ll_transport_dispose (GObject *object) { GibberLLTransport *self = GIBBER_LL_TRANSPORT (object); GibberLLTransportPrivate *priv = GIBBER_LL_TRANSPORT_GET_PRIVATE (self); if (priv->dispose_has_run) return; priv->dispose_has_run = TRUE; if (G_OBJECT_CLASS (gibber_ll_transport_parent_class)->dispose) G_OBJECT_CLASS (gibber_ll_transport_parent_class)->dispose (object); } void gibber_ll_transport_finalize (GObject *object) { G_OBJECT_CLASS (gibber_ll_transport_parent_class)->finalize (object); } GibberLLTransport * gibber_ll_transport_new (void) { return g_object_new (GIBBER_TYPE_LL_TRANSPORT, NULL); } void gibber_ll_transport_open_fd (GibberLLTransport *transport, int fd) { GibberLLTransportPrivate *priv = GIBBER_LL_TRANSPORT_GET_PRIVATE (transport); priv->incoming = TRUE; gibber_transport_set_state (GIBBER_TRANSPORT (transport), GIBBER_TRANSPORT_CONNECTING); gibber_fd_transport_set_fd (GIBBER_FD_TRANSPORT (transport), fd, TRUE); } gboolean gibber_ll_transport_open_sockaddr (GibberLLTransport *transport, struct sockaddr_storage *addr, GError **error) { GibberLLTransportPrivate *priv = GIBBER_LL_TRANSPORT_GET_PRIVATE (transport); char host[NI_MAXHOST]; char port[NI_MAXSERV]; int fd; int ret; g_assert (!priv->incoming); gibber_transport_set_state (GIBBER_TRANSPORT(transport), GIBBER_TRANSPORT_CONNECTING); if (getnameinfo ((struct sockaddr *)addr, sizeof (struct sockaddr_storage), host, NI_MAXHOST, port, NI_MAXSERV, NI_NUMERICHOST | NI_NUMERICSERV) == 0) { DEBUG("Trying to connect to %s port %s", host, port); } else { DEBUG("Connecting.."); } fd = socket (addr->ss_family, SOCK_STREAM, 0); if (fd < 0) { g_set_error (error, GIBBER_LL_TRANSPORT_ERROR, GIBBER_LL_TRANSPORT_ERROR_FAILED, "Getting socket failed: %s", g_strerror (errno)); DEBUG("Getting socket failed: %s", strerror(errno)); goto failed; } ret = connect (fd, (struct sockaddr *)addr, sizeof (struct sockaddr_storage)); if (ret < 0) { g_set_error (error, GIBBER_LL_TRANSPORT_ERROR, GIBBER_LL_TRANSPORT_ERROR_CONNECT_FAILED, "Connect failed: %s", g_strerror (errno)); DEBUG("Connecting failed: %s", strerror (errno)); goto failed; } gibber_fd_transport_set_fd (GIBBER_FD_TRANSPORT (transport), fd, TRUE); return TRUE; failed: gibber_transport_set_state (GIBBER_TRANSPORT (transport), GIBBER_TRANSPORT_DISCONNECTED); if (fd >= 0) { close (fd); } return FALSE; } gboolean gibber_ll_transport_is_incoming (GibberLLTransport *transport) { GibberLLTransportPrivate *priv = GIBBER_LL_TRANSPORT_GET_PRIVATE (transport); return priv->incoming; } void gibber_ll_transport_set_incoming (GibberLLTransport *transport, gboolean incoming) { GibberLLTransportPrivate *priv = GIBBER_LL_TRANSPORT_GET_PRIVATE (transport); g_assert ( GIBBER_TRANSPORT (transport)->state == GIBBER_TRANSPORT_DISCONNECTED); priv->incoming = incoming; } ���������������������������telepathy-gabble-0.18.2/lib/gibber/gibber-unix-transport.h������������������������������������������0000644�0001750�0001750�00000007010�12200204333�023416� 0����������������������������������������������������������������������������������������������������ustar�00smcv����������������������������smcv����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * gibber-unix-transport.h - Header for GibberUnixTransport * Copyright (C) 2006, 2008 Collabora Ltd. * @author: Sjoerd Simons <sjoerd@luon.net> * @author: Alban Crequy <alban.crequy@collabora.co.uk> * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 __GIBBER_UNIX_TRANSPORT_H__ #define __GIBBER_UNIX_TRANSPORT_H__ #include <glib-object.h> #ifdef G_OS_UNIX #include <errno.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/types.h> #include <sys/socket.h> #include <arpa/inet.h> #include <unistd.h> #include <sys/un.h> #include "gibber-fd-transport.h" G_BEGIN_DECLS GQuark gibber_unix_transport_error_quark (void); #define GIBBER_UNIX_TRANSPORT_ERROR gibber_unix_transport_error_quark() typedef enum { GIBBER_UNIX_TRANSPORT_ERROR_CONNECT_FAILED, GIBBER_UNIX_TRANSPORT_ERROR_FAILED, GIBBER_UNIX_TRANSPORT_ERROR_NO_CREDENTIALS, } GibberUnixTransportError; typedef struct _GibberUnixTransport GibberUnixTransport; typedef struct _GibberUnixTransportClass GibberUnixTransportClass; struct _GibberUnixTransportClass { GibberFdTransportClass parent_class; }; struct _GibberUnixTransport { GibberFdTransport parent; }; GType gibber_unix_transport_get_type (void); /* TYPE MACROS */ #define GIBBER_TYPE_UNIX_TRANSPORT \ (gibber_unix_transport_get_type ()) #define GIBBER_UNIX_TRANSPORT(obj) \ (G_TYPE_CHECK_INSTANCE_CAST((obj), GIBBER_TYPE_UNIX_TRANSPORT, \ GibberUnixTransport)) #define GIBBER_UNIX_TRANSPORT_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST((klass), GIBBER_TYPE_UNIX_TRANSPORT, \ GibberUnixTransportClass)) #define GIBBER_IS_UNIX_TRANSPORT(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE((obj), GIBBER_TYPE_UNIX_TRANSPORT)) #define GIBBER_IS_UNIX_TRANSPORT_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE((klass), GIBBER_TYPE_UNIX_TRANSPORT)) #define GIBBER_UNIX_TRANSPORT_GET_CLASS(obj) \ (G_TYPE_INSTANCE_GET_CLASS ((obj), GIBBER_TYPE_UNIX_TRANSPORT, \ GibberUnixTransportClass)) gboolean gibber_unix_transport_supports_credentials (void); GibberUnixTransport * gibber_unix_transport_new (void); GibberUnixTransport * gibber_unix_transport_new_from_fd (int fd); gboolean gibber_unix_transport_connect (GibberUnixTransport *transport, const gchar *path, GError **error); gboolean gibber_unix_transport_send_credentials (GibberUnixTransport *transport, const guint8 *data, gsize size); typedef struct { pid_t pid; uid_t uid; gid_t gid; } GibberCredentials; typedef void (*GibberUnixTransportRecvCredentialsCb) ( GibberUnixTransport *transport, GibberBuffer *buffer, GibberCredentials *credentials, GError *error, gpointer user_data); gboolean gibber_unix_transport_recv_credentials (GibberUnixTransport *transport, GibberUnixTransportRecvCredentialsCb callback, gpointer user_data); G_END_DECLS #endif /* G_OS_UNIX */ #endif /* #ifndef __GIBBER_UNIX_TRANSPORT_H__*/ ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������telepathy-gabble-0.18.2/lib/gibber/gibber-unix-transport.c������������������������������������������0000644�0001750�0001750�00000024676�12200204333�023432� 0����������������������������������������������������������������������������������������������������ustar�00smcv����������������������������smcv����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * gibber-linklocal-transport.c - Source for GibberLLTransport * Copyright (C) 2006, 2008 Collabora Ltd. * @author: Sjoerd Simons <sjoerd@luon.net> * @author: Alban Crequy <alban.crequy@collabora.co.uk> * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 "config.h" #include <glib.h> #ifdef G_OS_UNIX /* If you claim to be Unix but you don't have these headers, you may have * already lost. */ #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <fcntl.h> #include <string.h> #include <errno.h> #include <sys/types.h> #include <sys/socket.h> #include <sys/un.h> #include "gibber-unix-transport.h" #include "gibber-util.h" #define DEBUG_FLAG DEBUG_NET #include "gibber-debug.h" G_DEFINE_TYPE(GibberUnixTransport, gibber_unix_transport, \ GIBBER_TYPE_FD_TRANSPORT) GQuark gibber_unix_transport_error_quark (void) { static GQuark quark = 0; if (!quark) quark = g_quark_from_static_string ("gibber_unix_transport_error"); return quark; } /* private structure */ typedef struct _GibberUnixTransportPrivate GibberUnixTransportPrivate; struct _GibberUnixTransportPrivate { gboolean incoming; GibberUnixTransportRecvCredentialsCb recv_creds_cb; gpointer recv_creds_data; gboolean dispose_has_run; }; #define GIBBER_UNIX_TRANSPORT_GET_PRIVATE(o) \ (G_TYPE_INSTANCE_GET_PRIVATE ((o), GIBBER_TYPE_UNIX_TRANSPORT, \ GibberUnixTransportPrivate)) static void gibber_unix_transport_finalize (GObject *object); static void gibber_unix_transport_init (GibberUnixTransport *self) { GibberUnixTransportPrivate *priv = GIBBER_UNIX_TRANSPORT_GET_PRIVATE (self); priv->incoming = FALSE; } static void gibber_unix_transport_dispose (GObject *object); static GibberFdIOResult gibber_unix_transport_read ( GibberFdTransport *transport, GIOChannel *channel, GError **error); static void gibber_unix_transport_class_init ( GibberUnixTransportClass *gibber_unix_transport_class) { GObjectClass *object_class = G_OBJECT_CLASS (gibber_unix_transport_class); GibberFdTransportClass *fd_class = GIBBER_FD_TRANSPORT_CLASS ( gibber_unix_transport_class); g_type_class_add_private (gibber_unix_transport_class, sizeof (GibberUnixTransportPrivate)); object_class->dispose = gibber_unix_transport_dispose; object_class->finalize = gibber_unix_transport_finalize; /* override GibberFdTransport's read */ fd_class->read = gibber_unix_transport_read; } void gibber_unix_transport_dispose (GObject *object) { GibberUnixTransport *self = GIBBER_UNIX_TRANSPORT (object); GibberUnixTransportPrivate *priv = GIBBER_UNIX_TRANSPORT_GET_PRIVATE (self); if (priv->dispose_has_run) return; priv->dispose_has_run = TRUE; priv->recv_creds_cb = NULL; priv->recv_creds_data = NULL; if (G_OBJECT_CLASS (gibber_unix_transport_parent_class)->dispose) G_OBJECT_CLASS (gibber_unix_transport_parent_class)->dispose (object); } void gibber_unix_transport_finalize (GObject *object) { G_OBJECT_CLASS (gibber_unix_transport_parent_class)->finalize (object); } GibberUnixTransport * gibber_unix_transport_new (void) { return g_object_new (GIBBER_TYPE_UNIX_TRANSPORT, NULL); } gboolean gibber_unix_transport_connect (GibberUnixTransport *transport, const gchar *path, GError **error) { union { struct sockaddr_un un; struct sockaddr addr; } addr; int fd; gibber_transport_set_state (GIBBER_TRANSPORT (transport), GIBBER_TRANSPORT_CONNECTING); memset (&addr, 0, sizeof (addr)); fd = socket (PF_UNIX, SOCK_STREAM, 0); if (fd == -1) { DEBUG ("Error creating socket: %s", g_strerror (errno)); g_set_error (error, GIBBER_UNIX_TRANSPORT_ERROR, GIBBER_UNIX_TRANSPORT_ERROR_CONNECT_FAILED, "Error creating socket: %s", g_strerror (errno)); goto failed; } addr.un.sun_family = PF_UNIX; g_strlcpy (addr.un.sun_path, path, sizeof (addr.un.sun_path)); if (connect (fd, &addr.addr, sizeof (addr.un)) == -1) { g_set_error (error, GIBBER_UNIX_TRANSPORT_ERROR, GIBBER_UNIX_TRANSPORT_ERROR_CONNECT_FAILED, "Error connecting socket: %s", g_strerror (errno)); DEBUG ("Error connecting socket: %s", g_strerror (errno)); goto failed; } DEBUG ("Connected to socket"); gibber_fd_transport_set_fd (GIBBER_FD_TRANSPORT (transport), fd, TRUE); return TRUE; failed: g_assert (error != NULL); gibber_transport_emit_error (GIBBER_TRANSPORT(transport), *error); gibber_transport_set_state (GIBBER_TRANSPORT (transport), GIBBER_TRANSPORT_DISCONNECTED); return FALSE; } GibberUnixTransport * gibber_unix_transport_new_from_fd (int fd) { GibberUnixTransport *transport; transport = gibber_unix_transport_new (); gibber_fd_transport_set_fd (GIBBER_FD_TRANSPORT (transport), fd, TRUE); return transport; } /* Patches that reimplement these functions for non-Linux would be welcome * (please file a bug) */ #if defined(__linux__) gboolean gibber_unix_transport_supports_credentials (void) { return TRUE; } gboolean gibber_unix_transport_send_credentials (GibberUnixTransport *transport, const guint8 *data, gsize size) { int fd, ret; struct ucred *cred; struct msghdr msg; struct cmsghdr *ch; struct iovec iov; char buffer[CMSG_SPACE (sizeof (struct ucred))]; DEBUG ("send credentials"); fd = GIBBER_FD_TRANSPORT (transport)->fd; /* Set the message payload */ memset (&iov, 0, sizeof (iov)); iov.iov_base = (void *) data; iov.iov_len = size; memset (&msg, 0, sizeof (msg)); msg.msg_iov = &iov; msg.msg_iovlen = 1; msg.msg_control = buffer; msg.msg_controllen = sizeof (buffer); memset (buffer, 0, sizeof (buffer)); /* Set the credentials */ ch = CMSG_FIRSTHDR (&msg); ch->cmsg_len = CMSG_LEN (sizeof (struct ucred)); ch->cmsg_level = SOL_SOCKET; ch->cmsg_type = SCM_CREDENTIALS; cred = (struct ucred *) CMSG_DATA (ch); cred->pid = getpid (); cred->uid = getuid (); cred->gid = getgid (); ret = sendmsg (fd, &msg, 0); if (ret == -1) { DEBUG ("sendmsg failed: %s", g_strerror (errno)); return FALSE; } return TRUE; } #define BUFSIZE 1024 static GibberFdIOResult gibber_unix_transport_read (GibberFdTransport *transport, GIOChannel *channel, GError **error) { GibberUnixTransport *self = GIBBER_UNIX_TRANSPORT (transport); GibberUnixTransportPrivate *priv = GIBBER_UNIX_TRANSPORT_GET_PRIVATE (self); int fd; guint8 buffer[BUFSIZE]; ssize_t bytes_read; GibberBuffer buf; struct iovec iov; struct msghdr msg; char control[CMSG_SPACE (sizeof (struct ucred))]; struct cmsghdr *ch; struct ucred *cred; int opt; if (priv->recv_creds_cb == NULL) return gibber_fd_transport_read (transport, channel, error); /* We are waiting for credentials */ fd = transport->fd; /* set SO_PASSCRED flag */ opt = 1; setsockopt (fd, SOL_SOCKET, SO_PASSCRED, &opt, sizeof (opt)); memset (buffer, 0, sizeof (buffer)); memset (&iov, 0, sizeof (iov)); iov.iov_base = buffer; iov.iov_len = sizeof (buffer); memset (&msg, 0, sizeof (msg)); msg.msg_iov = &iov; msg.msg_iovlen = 1; msg.msg_control = control; msg.msg_controllen = sizeof (control); bytes_read = recvmsg (fd, &msg, 0); if (bytes_read == -1) { GError *err = NULL; g_set_error_literal (&err, G_IO_CHANNEL_ERROR, g_io_channel_error_from_errno (errno), "recvmsg failed"); priv->recv_creds_cb (self, NULL, NULL, err, priv->recv_creds_data); g_propagate_error (error, err); priv->recv_creds_cb = NULL; priv->recv_creds_data = NULL; return GIBBER_FD_IO_RESULT_ERROR; } /* unset SO_PASSCRED flag */ opt = 0; setsockopt (fd, SOL_SOCKET, SO_PASSCRED, &opt, sizeof (opt)); buf.data = buffer; buf.length = bytes_read; /* extract the credentials */ ch = CMSG_FIRSTHDR (&msg); if (ch == NULL) { GError *err = NULL; DEBUG ("Message doesn't contain credentials"); g_set_error_literal (&err, GIBBER_UNIX_TRANSPORT_ERROR, GIBBER_UNIX_TRANSPORT_ERROR_NO_CREDENTIALS, "no credentials received"); priv->recv_creds_cb (self, &buf, NULL, err, priv->recv_creds_data); g_error_free (err); } else { GibberCredentials credentials; cred = (struct ucred *) CMSG_DATA (ch); credentials.pid = cred->pid; credentials.uid = cred->uid; credentials.gid = cred->gid; priv->recv_creds_cb (self, &buf, &credentials, NULL, priv->recv_creds_data); } priv->recv_creds_cb = NULL; priv->recv_creds_data = NULL; return GIBBER_FD_IO_RESULT_SUCCESS; } gboolean gibber_unix_transport_recv_credentials (GibberUnixTransport *self, GibberUnixTransportRecvCredentialsCb callback, gpointer user_data) { GibberUnixTransportPrivate *priv = GIBBER_UNIX_TRANSPORT_GET_PRIVATE (self); if (priv->recv_creds_cb != NULL) { DEBUG ("already waiting for credentials"); return FALSE; } priv->recv_creds_cb = callback; priv->recv_creds_data = user_data; return TRUE; } #else /* OSs where we have no implementation */ gboolean gibber_unix_transport_supports_credentials (void) { return FALSE; } gboolean gibber_unix_transport_recv_credentials (GibberUnixTransport *self, GibberUnixTransportRecvCredentialsCb callback, gpointer user_data) { DEBUG ("stub implementation, failing"); return FALSE; } gboolean gibber_unix_transport_send_credentials (GibberUnixTransport *transport, const guint8 *data, gsize size) { DEBUG ("stub implementation, failing"); return FALSE; } static GibberFdIOResult gibber_unix_transport_read (GibberFdTransport *transport, GIOChannel *channel, GError **error) { return gibber_fd_transport_read (transport, channel, error); } #endif /* OSs where we have no implementation of credentials */ #endif /* G_OS_UNIX */ ������������������������������������������������������������������telepathy-gabble-0.18.2/lib/gibber/gibber-tcp-transport.h�������������������������������������������0000644�0001750�0001750�00000004343�12200204333�023227� 0����������������������������������������������������������������������������������������������������ustar�00smcv����������������������������smcv����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * gibber-tcp-transport.h - Header for GibberTCPTransport * Copyright (C) 2006 Collabora Ltd. * @author Sjoerd Simons <sjoerd@luon.net> * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 __GIBBER_TCP_TRANSPORT_H__ #define __GIBBER_TCP_TRANSPORT_H__ #include <glib-object.h> #include "gibber-fd-transport.h" G_BEGIN_DECLS typedef struct _GibberTCPTransport GibberTCPTransport; typedef struct _GibberTCPTransportClass GibberTCPTransportClass; struct _GibberTCPTransportClass { GibberFdTransportClass parent_class; }; struct _GibberTCPTransport { GibberFdTransport parent; }; GType gibber_tcp_transport_get_type (void); /* TYPE MACROS */ #define GIBBER_TYPE_TCP_TRANSPORT \ (gibber_tcp_transport_get_type ()) #define GIBBER_TCP_TRANSPORT(obj) \ (G_TYPE_CHECK_INSTANCE_CAST((obj), GIBBER_TYPE_TCP_TRANSPORT, \ GibberTCPTransport)) #define GIBBER_TCP_TRANSPORT_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST((klass), GIBBER_TYPE_TCP_TRANSPORT, \ GibberTCPTransportClass)) #define GIBBER_IS_TCP_TRANSPORT(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE((obj), GIBBER_TYPE_TCP_TRANSPORT)) #define GIBBER_IS_TCP_TRANSPORT_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE((klass), GIBBER_TYPE_TCP_TRANSPORT)) #define GIBBER_TCP_TRANSPORT_GET_CLASS(obj) \ (G_TYPE_INSTANCE_GET_CLASS ((obj), GIBBER_TYPE_TCP_TRANSPORT, \ GibberTCPTransportClass)) GibberTCPTransport * gibber_tcp_transport_new (void); void gibber_tcp_transport_connect (GibberTCPTransport *tcp_transport, const gchar *host, guint16 port); G_END_DECLS #endif /* #ifndef __GIBBER_TCP_TRANSPORT_H__*/ ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������telepathy-gabble-0.18.2/lib/gibber/gibber-tcp-transport.c�������������������������������������������0000644�0001750�0001750�00000017727�12200204333�023234� 0����������������������������������������������������������������������������������������������������ustar�00smcv����������������������������smcv����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * gibber-tcp-transport.c - Source for GibberTCPTransport * Copyright (C) 2006 Collabora Ltd. * @author Sjoerd Simons <sjoerd@luon.net> * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 <config.h> #include <stdio.h> #include <stdlib.h> #include <errno.h> #include <string.h> #ifdef HAVE_UNISTD_H # include <unistd.h> #endif #include "gibber-sockets.h" #include "gibber-tcp-transport.h" #define DEBUG_FLAG DEBUG_NET #include "gibber-debug.h" #include <gio/gio.h> #include "errno.h" G_DEFINE_TYPE(GibberTCPTransport, gibber_tcp_transport, GIBBER_TYPE_FD_TRANSPORT) /* private structure */ typedef struct _GibberTCPTransportPrivate GibberTCPTransportPrivate; struct _GibberTCPTransportPrivate { GIOChannel *channel; GList *addresses; guint16 port; guint watch_in; gboolean dispose_has_run; }; #define GIBBER_TCP_TRANSPORT_GET_PRIVATE(o) \ (G_TYPE_INSTANCE_GET_PRIVATE ((o), GIBBER_TYPE_TCP_TRANSPORT, \ GibberTCPTransportPrivate)) static void gibber_tcp_transport_init (GibberTCPTransport *obj) { /* GibberTCPTransportPrivate *priv = GIBBER_TCP_TRANSPORT_GET_PRIVATE (obj); */ /* allocate any data required by the object here */ } static void gibber_tcp_transport_dispose (GObject *object); static void gibber_tcp_transport_finalize (GObject *object); static void gibber_tcp_transport_class_init ( GibberTCPTransportClass *gibber_tcp_transport_class) { GObjectClass *object_class = G_OBJECT_CLASS (gibber_tcp_transport_class); g_type_class_add_private (gibber_tcp_transport_class, sizeof (GibberTCPTransportPrivate)); object_class->dispose = gibber_tcp_transport_dispose; object_class->finalize = gibber_tcp_transport_finalize; } static void clean_connect_attempt (GibberTCPTransport *self) { GibberTCPTransportPrivate *priv = GIBBER_TCP_TRANSPORT_GET_PRIVATE ( self); if (priv->watch_in != 0) { g_source_remove (priv->watch_in); priv->watch_in = 0; } if (priv->channel != NULL) { g_io_channel_unref (priv->channel); priv->channel = NULL; } } static void clean_all_connect_attempts (GibberTCPTransport *self) { GibberTCPTransportPrivate *priv = GIBBER_TCP_TRANSPORT_GET_PRIVATE ( self); clean_connect_attempt (self); g_resolver_free_addresses (priv->addresses); priv->addresses = NULL; } void gibber_tcp_transport_dispose (GObject *object) { GibberTCPTransport *self = GIBBER_TCP_TRANSPORT (object); GibberTCPTransportPrivate *priv = GIBBER_TCP_TRANSPORT_GET_PRIVATE (self); if (priv->dispose_has_run) return; priv->dispose_has_run = TRUE; clean_all_connect_attempts (self); /* release any references held by the object here */ if (G_OBJECT_CLASS (gibber_tcp_transport_parent_class)->dispose) G_OBJECT_CLASS (gibber_tcp_transport_parent_class)->dispose (object); } void gibber_tcp_transport_finalize (GObject *object) { /* GibberTCPTransport *self = GIBBER_TCP_TRANSPORT (object); GibberTCPTransportPrivate *priv = GIBBER_TCP_TRANSPORT_GET_PRIVATE (self); */ /* free any data held directly by the object here */ G_OBJECT_CLASS (gibber_tcp_transport_parent_class)->finalize (object); } GibberTCPTransport * gibber_tcp_transport_new () { return g_object_new (GIBBER_TYPE_TCP_TRANSPORT, NULL); } static void new_connect_attempt (GibberTCPTransport *self); static gboolean try_to_connect (GibberTCPTransport *self) { GibberTCPTransportPrivate *priv = GIBBER_TCP_TRANSPORT_GET_PRIVATE ( self); GSocketAddress *gaddr; struct sockaddr_storage addr; gssize native_size; gint fd; int ret; int err; gboolean connected = FALSE; g_assert (priv->channel != NULL); fd = g_io_channel_unix_get_fd (priv->channel); gaddr = g_inet_socket_address_new (G_INET_ADDRESS (priv->addresses->data), priv->port); native_size = g_socket_address_get_native_size (gaddr); /* _get_native_size() really shouldn't fail... */ g_return_val_if_fail (native_size > 0, FALSE); /* ...and if sockaddr_storage isn't big enough, we're basically screwed. */ g_return_val_if_fail ((gsize) native_size <= sizeof (addr), FALSE); g_socket_address_to_native (gaddr, &addr, sizeof (addr), NULL); ret = connect (fd, (struct sockaddr *)&addr, (gsize) native_size); #ifdef G_OS_WIN32 err = WSAGetLastError (); connected = (ret == 0 || err == WSAEISCONN); #else err = errno; connected = (ret == 0); #endif g_object_unref (gaddr); if (connected) { DEBUG ("connect succeeded"); clean_all_connect_attempts (self); gibber_fd_transport_set_fd (GIBBER_FD_TRANSPORT (self), fd, TRUE); return FALSE; } if (gibber_connect_errno_requires_retry (err)) { /* We have to wait longer */ return TRUE; } clean_connect_attempt (self); g_object_unref (priv->addresses->data); priv->addresses = g_list_remove_link (priv->addresses, priv->addresses); new_connect_attempt (self); return FALSE; } static gboolean _channel_io (GIOChannel *source, GIOCondition condition, gpointer data) { GibberTCPTransport *self = GIBBER_TCP_TRANSPORT (data); return try_to_connect (self); } static void new_connect_attempt (GibberTCPTransport *self) { GibberTCPTransportPrivate *priv = GIBBER_TCP_TRANSPORT_GET_PRIVATE ( self); int fd; GSocketAddress *gaddr; gchar *tmpstr; if (priv->addresses == NULL) { /* no more candidate to try */ DEBUG ("connection failed"); goto failed; } tmpstr = g_inet_address_to_string (G_INET_ADDRESS (priv->addresses->data)); DEBUG ("Trying %s port %d...", tmpstr, priv->port); g_free (tmpstr); gaddr = g_inet_socket_address_new (G_INET_ADDRESS (priv->addresses->data), priv->port); fd = socket (g_socket_address_get_family (gaddr), SOCK_STREAM, IPPROTO_TCP); g_object_unref (gaddr); if (fd < 0) { DEBUG("socket failed: #%d %s", gibber_socket_errno (), gibber_socket_strerror ()); goto failed; } gibber_socket_set_nonblocking (fd); priv->channel = gibber_io_channel_new_from_socket (fd); g_io_channel_set_close_on_unref (priv->channel, FALSE); g_io_channel_set_encoding (priv->channel, NULL, NULL); g_io_channel_set_buffered (priv->channel, FALSE); priv->watch_in = g_io_add_watch (priv->channel, G_IO_IN | G_IO_PRI | G_IO_OUT, _channel_io, self); try_to_connect (self); return; failed: clean_all_connect_attempts (self); gibber_transport_set_state (GIBBER_TRANSPORT (self), GIBBER_TRANSPORT_DISCONNECTED); } void gibber_tcp_transport_connect (GibberTCPTransport *tcp_transport, const gchar *host, guint16 port) { GibberTCPTransportPrivate *priv = GIBBER_TCP_TRANSPORT_GET_PRIVATE ( tcp_transport); GResolver *resolver = g_resolver_get_default (); GError *error = NULL; gibber_transport_set_state (GIBBER_TRANSPORT (tcp_transport), GIBBER_TRANSPORT_CONNECTING); priv->port = port; g_assert (priv->addresses == NULL); g_assert (priv->channel == NULL); priv->addresses = g_resolver_lookup_by_name (resolver, host, NULL, &error); if (priv->addresses == NULL) { DEBUG("Address lookup failed: %s", error->message); gibber_transport_set_state (GIBBER_TRANSPORT (tcp_transport), GIBBER_TRANSPORT_DISCONNECTED); g_error_free (error); goto out; } new_connect_attempt (tcp_transport); out: g_object_unref (resolver); } �����������������������������������������telepathy-gabble-0.18.2/lib/gibber/gibber-fd-transport.h��������������������������������������������0000644�0001750�0001750�00000006023�12200204333�023027� 0����������������������������������������������������������������������������������������������������ustar�00smcv����������������������������smcv����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * gibber-fd-transport.h - Header for GibberFdTransport * Copyright (C) 2006 Collabora Ltd. * @author: Sjoerd Simons <sjoerd@luon.net> * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 __GIBBER_FD_TRANSPORT_H__ #define __GIBBER_FD_TRANSPORT_H__ #include <glib-object.h> #include "gibber-sockets.h" #include "gibber-transport.h" typedef enum { GIBBER_FD_IO_RESULT_SUCCESS, GIBBER_FD_IO_RESULT_AGAIN, GIBBER_FD_IO_RESULT_ERROR, GIBBER_FD_IO_RESULT_EOF, } GibberFdIOResult; G_BEGIN_DECLS GQuark gibber_fd_transport_error_quark (void); #define GIBBER_FD_TRANSPORT_ERROR gibber_fd_transport_error_quark() typedef enum { GIBBER_FD_TRANSPORT_ERROR_PIPE, GIBBER_FD_TRANSPORT_ERROR_FAILED, } GibberFdTransportError; typedef struct _GibberFdTransport GibberFdTransport; typedef struct _GibberFdTransportClass GibberFdTransportClass; struct _GibberFdTransportClass { GibberTransportClass parent_class; /* Called when fd is ready for reading */ GibberFdIOResult (*read) (GibberFdTransport *fd_transport, GIOChannel *channel, GError **error); /* Called when something needs to be written*/ GibberFdIOResult (*write) (GibberFdTransport *fd_transport, GIOChannel *channel, const guint8 *data, int len, gsize *written, GError **error); }; struct _GibberFdTransport { GibberTransport parent; int fd; }; GType gibber_fd_transport_get_type (void); /* TYPE MACROS */ #define GIBBER_TYPE_FD_TRANSPORT \ (gibber_fd_transport_get_type ()) #define GIBBER_FD_TRANSPORT(obj) \ (G_TYPE_CHECK_INSTANCE_CAST((obj), GIBBER_TYPE_FD_TRANSPORT, \ GibberFdTransport)) #define GIBBER_FD_TRANSPORT_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST((klass), GIBBER_TYPE_FD_TRANSPORT, \ GibberFdTransportClass)) #define GIBBER_IS_FD_TRANSPORT(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE((obj), GIBBER_TYPE_FD_TRANSPORT)) #define GIBBER_IS_FD_TRANSPORT_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE((klass), GIBBER_TYPE_FD_TRANSPORT)) #define GIBBER_FD_TRANSPORT_GET_CLASS(obj) \ (G_TYPE_INSTANCE_GET_CLASS ((obj), GIBBER_TYPE_FD_TRANSPORT, \ GibberFdTransportClass)) void gibber_fd_transport_set_fd (GibberFdTransport *fd_transport, int fd, gboolean is_socket); GibberFdIOResult gibber_fd_transport_read (GibberFdTransport *transport, GIOChannel *channel, GError **error); G_END_DECLS #endif /* #ifndef __GIBBER_FD_TRANSPORT_H__*/ �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������telepathy-gabble-0.18.2/lib/gibber/gibber-fd-transport.c��������������������������������������������0000644�0001750�0001750�00000037261�12200204333�023032� 0����������������������������������������������������������������������������������������������������ustar�00smcv����������������������������smcv����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * gibber-fd-transport.c - Source for GibberFdTransport * Copyright (C) 2006-2007 Collabora Ltd. * @author: Sjoerd Simons <sjoerd.simons@collabora.co.uk> * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 <config.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <errno.h> #ifdef HAVE_UNISTD_H # include <unistd.h> #endif #include "gibber-sockets.h" #include "gibber-fd-transport.h" #define DEBUG_FLAG DEBUG_NET #include "gibber-debug.h" static gboolean _channel_io_out (GIOChannel *source, GIOCondition condition, gpointer data); static gboolean gibber_fd_transport_send (GibberTransport *transport, const guint8 *data, gsize size, GError **error); static void gibber_fd_transport_disconnect (GibberTransport *transport); static gboolean gibber_fd_transport_get_peeraddr (GibberTransport *transport, struct sockaddr_storage *addr, socklen_t *len); static gboolean gibber_fd_transport_get_sockaddr (GibberTransport *transport, struct sockaddr_storage *addr, socklen_t *len); static void _do_disconnect (GibberFdTransport *self); static gboolean gibber_fd_transport_buffer_is_empty ( GibberTransport *transport); static void gibber_fd_transport_block_receiving (GibberTransport *transport, gboolean block); G_DEFINE_TYPE(GibberFdTransport, gibber_fd_transport, GIBBER_TYPE_TRANSPORT) GQuark gibber_fd_transport_error_quark (void) { static GQuark quark = 0; if (!quark) quark = g_quark_from_static_string ("gibber_fd_transport_error"); return quark; } /* private structure */ typedef struct _GibberFdTransportPrivate GibberFdTransportPrivate; struct _GibberFdTransportPrivate { GIOChannel *channel; gboolean dispose_has_run; guint watch_in; guint watch_out; guint watch_err; GString *output_buffer; gboolean receiving_blocked; }; #define GIBBER_FD_TRANSPORT_GET_PRIVATE(o) \ (G_TYPE_INSTANCE_GET_PRIVATE ((o), GIBBER_TYPE_FD_TRANSPORT, \ GibberFdTransportPrivate)) static void gibber_fd_transport_init (GibberFdTransport *self) { GibberFdTransportPrivate *priv = GIBBER_FD_TRANSPORT_GET_PRIVATE (self); self->fd = -1; priv->channel = NULL; priv->output_buffer = NULL; priv->watch_in = 0; priv->watch_out = 0; priv->watch_err = 0; } static void gibber_fd_transport_dispose (GObject *object); static void gibber_fd_transport_finalize (GObject *object); static GibberFdIOResult gibber_fd_transport_write ( GibberFdTransport *fd_transport, GIOChannel *channel, const guint8 *data, int len, gsize *written, GError **error); static void gibber_fd_transport_class_init ( GibberFdTransportClass *gibber_fd_transport_class) { GObjectClass *object_class = G_OBJECT_CLASS (gibber_fd_transport_class); GibberTransportClass *transport_class = GIBBER_TRANSPORT_CLASS (gibber_fd_transport_class); g_type_class_add_private (gibber_fd_transport_class, sizeof (GibberFdTransportPrivate)); object_class->dispose = gibber_fd_transport_dispose; object_class->finalize = gibber_fd_transport_finalize; transport_class->send = gibber_fd_transport_send; transport_class->disconnect = gibber_fd_transport_disconnect; transport_class->get_peeraddr = gibber_fd_transport_get_peeraddr; transport_class->get_sockaddr = gibber_fd_transport_get_sockaddr; transport_class->buffer_is_empty = gibber_fd_transport_buffer_is_empty; transport_class->block_receiving = gibber_fd_transport_block_receiving; gibber_fd_transport_class->read = gibber_fd_transport_read; gibber_fd_transport_class->write = gibber_fd_transport_write; } void gibber_fd_transport_dispose (GObject *object) { GibberFdTransport *self = GIBBER_FD_TRANSPORT (object); GibberFdTransportPrivate *priv = GIBBER_FD_TRANSPORT_GET_PRIVATE (self); if (priv->dispose_has_run) return; priv->dispose_has_run = TRUE; _do_disconnect (self); if (G_OBJECT_CLASS (gibber_fd_transport_parent_class)->dispose) G_OBJECT_CLASS (gibber_fd_transport_parent_class)->dispose (object); } void gibber_fd_transport_finalize (GObject *object) { G_OBJECT_CLASS (gibber_fd_transport_parent_class)->finalize (object); } static void _do_disconnect (GibberFdTransport *self) { GibberFdTransportPrivate *priv = GIBBER_FD_TRANSPORT_GET_PRIVATE (self); if (GIBBER_TRANSPORT(self)->state == GIBBER_TRANSPORT_DISCONNECTED) { return; } DEBUG ("Closing the fd transport"); if (priv->channel != NULL) { if (priv->watch_in != 0) g_source_remove (priv->watch_in); if (priv->watch_out) g_source_remove (priv->watch_out); if (priv->watch_err) g_source_remove (priv->watch_err); g_io_channel_shutdown (priv->channel, FALSE, NULL); g_io_channel_unref (priv->channel); priv->channel = NULL; } else { close (self->fd); } self->fd = -1; if (priv->output_buffer) { g_string_free (priv->output_buffer, TRUE); priv->output_buffer = NULL; } if (!priv->dispose_has_run) /* If we are disposing we don't care about the state anymore */ gibber_transport_set_state (GIBBER_TRANSPORT (self), GIBBER_TRANSPORT_DISCONNECTED); } static gboolean _try_write (GibberFdTransport *self, const guint8 *data, int len, gsize *written, GError **err) { GibberFdTransportPrivate *priv = GIBBER_FD_TRANSPORT_GET_PRIVATE (self); GibberFdTransportClass *cls = GIBBER_FD_TRANSPORT_GET_CLASS (self); GibberFdIOResult result; GError *error = NULL; result = cls->write (self, priv->channel, data, len, written, &error); switch (result) { case GIBBER_FD_IO_RESULT_SUCCESS: case GIBBER_FD_IO_RESULT_AGAIN: break; case GIBBER_FD_IO_RESULT_ERROR: gibber_transport_emit_error (GIBBER_TRANSPORT (self), error); /* fallthrough */ case GIBBER_FD_IO_RESULT_EOF: DEBUG ("Writing data failed, closing the transport"); _do_disconnect (self); g_propagate_error (err, error); return FALSE; break; } return TRUE; } static gboolean _writeout (GibberFdTransport *self, const guint8 *data, gsize len, GError **error) { GibberFdTransportPrivate *priv = GIBBER_FD_TRANSPORT_GET_PRIVATE (self); gsize written = 0; DEBUG ("Writing out %" G_GSIZE_FORMAT " bytes", len); if (priv->output_buffer == NULL || priv->output_buffer->len == 0) { /* We've got nothing buffer yet so try to write out directly */ if (!_try_write (self, data, len, &written, error)) { return FALSE; } } if (written == len) { gibber_transport_emit_buffer_empty (GIBBER_TRANSPORT (self)); return TRUE; } if (priv->output_buffer) { g_string_append_len (priv->output_buffer, (gchar *) data + written, len - written); } else { priv->output_buffer = g_string_new_len ((gchar *) data + written, len - written); } if (!priv->watch_out) { priv->watch_out = g_io_add_watch (priv->channel, G_IO_OUT, _channel_io_out, self); } return TRUE; } static gboolean _channel_io_in (GIOChannel *source, GIOCondition condition, gpointer data) { GibberFdTransport *self = GIBBER_FD_TRANSPORT (data); GibberFdTransportPrivate *priv = GIBBER_FD_TRANSPORT_GET_PRIVATE (self); GibberFdIOResult result; GError *error = NULL; GibberFdTransportClass *cls = GIBBER_FD_TRANSPORT_GET_CLASS(self); result = cls->read (self, priv->channel, &error); switch (result) { case GIBBER_FD_IO_RESULT_SUCCESS: case GIBBER_FD_IO_RESULT_AGAIN: break; case GIBBER_FD_IO_RESULT_ERROR: gibber_transport_emit_error (GIBBER_TRANSPORT(self), error); /* Deliberately falling through */ case GIBBER_FD_IO_RESULT_EOF: DEBUG("Failed to read from the transport, closing.."); _do_disconnect (self); return FALSE; } return TRUE; } static gboolean _channel_io_out (GIOChannel *source, GIOCondition condition, gpointer data) { GibberFdTransport *self = GIBBER_FD_TRANSPORT (data); GibberFdTransportPrivate *priv = GIBBER_FD_TRANSPORT_GET_PRIVATE (self); gsize written; g_assert (priv->output_buffer); if (!_try_write (self, (guint8 *) priv->output_buffer->str, priv->output_buffer->len, &written, NULL)) { return FALSE; } if (written > 0 ) { priv->output_buffer = g_string_erase (priv->output_buffer, 0, written); } if (priv->output_buffer->len == 0) { priv->watch_out = 0; gibber_transport_emit_buffer_empty (GIBBER_TRANSPORT (self)); return FALSE; } return TRUE; } static gboolean _channel_io_err (GIOChannel *source, GIOCondition condition, gpointer data) { GibberFdTransport *self = GIBBER_FD_TRANSPORT (data); GError *error = NULL; gint code; const gchar *msg; if (condition & G_IO_ERR) { DEBUG ("Error on GIOChannel.Closing the transport"); /* We can't use g_io_channel_error_from_errno because it seems errno is * not always set when we got a G_IO_ERR. */ code = GIBBER_FD_TRANSPORT_ERROR_FAILED; msg = "Error on GIOChannel"; } else { g_assert_not_reached (); } error = g_error_new_literal (GIBBER_FD_TRANSPORT_ERROR, code, msg); gibber_transport_emit_error (GIBBER_TRANSPORT (self), error); g_error_free (error); _do_disconnect (self); return FALSE; } #ifdef G_OS_WIN32 /* workaround for GLib bug #338943 */ static gboolean _channel_io_in_dispatcher (GIOChannel *source, GIOCondition condition, gpointer data) { if (condition & G_IO_ERR) { return _channel_io_err (source, condition, data); } if (condition == G_IO_IN) { return _channel_io_in (source, condition, data); } g_assert_not_reached (); } #endif /* Default read and write implementations */ static GibberFdIOResult gibber_fd_transport_write (GibberFdTransport *fd_transport, GIOChannel *channel, const guint8 *data, int len, gsize *written, GError **error) { GIOStatus status; status = g_io_channel_write_chars (channel, (gchar *) data, len, written, error); switch (status) { case G_IO_STATUS_NORMAL: return GIBBER_FD_IO_RESULT_SUCCESS; case G_IO_STATUS_AGAIN: return GIBBER_FD_IO_RESULT_AGAIN; case G_IO_STATUS_ERROR: return GIBBER_FD_IO_RESULT_ERROR; case G_IO_STATUS_EOF: return GIBBER_FD_IO_RESULT_EOF; } g_assert_not_reached (); } #define BUFSIZE 1024 GibberFdIOResult gibber_fd_transport_read (GibberFdTransport *transport, GIOChannel *channel, GError **error) { guint8 buf[BUFSIZE + 1]; GIOStatus status; gsize bytes_read; status = g_io_channel_read_chars (channel, (gchar *) buf, BUFSIZE, &bytes_read, error); switch (status) { case G_IO_STATUS_NORMAL: buf[bytes_read] = '\0'; DEBUG ("Received %" G_GSIZE_FORMAT " bytes", bytes_read); gibber_transport_received_data (GIBBER_TRANSPORT (transport), buf, bytes_read); return GIBBER_FD_IO_RESULT_SUCCESS; case G_IO_STATUS_ERROR: return GIBBER_FD_IO_RESULT_ERROR; case G_IO_STATUS_EOF: return GIBBER_FD_IO_RESULT_EOF; case G_IO_STATUS_AGAIN: return GIBBER_FD_IO_RESULT_AGAIN; } g_assert_not_reached (); } void gibber_fd_transport_set_fd (GibberFdTransport *self, int fd, gboolean is_socket) { GibberFdTransportPrivate *priv = GIBBER_FD_TRANSPORT_GET_PRIVATE (self); g_assert (self->fd == -1 && fd >= 0); self->fd = fd; if (is_socket) { gibber_socket_set_nonblocking (fd); priv->channel = gibber_io_channel_new_from_socket (fd); } else { #ifndef G_OS_WIN32 fcntl (fd, F_SETFL, O_NONBLOCK); #endif priv->channel = g_io_channel_unix_new (fd); } g_io_channel_set_close_on_unref (priv->channel, TRUE); g_io_channel_set_encoding (priv->channel, NULL, NULL); g_io_channel_set_buffered (priv->channel, FALSE); #ifdef G_OS_WIN32 /* workaround for GLib bug #338943 */ if (!priv->receiving_blocked) { priv->watch_in = g_io_add_watch (priv->channel, G_IO_IN | G_IO_ERR, _channel_io_in_dispatcher, self); } else { priv->watch_err = g_io_add_watch (priv->channel, G_IO_ERR, _channel_io_err, self); } #else if (!priv->receiving_blocked) { priv->watch_in = g_io_add_watch (priv->channel, G_IO_IN, _channel_io_in, self); } priv->watch_err = g_io_add_watch (priv->channel, G_IO_ERR, _channel_io_err, self); #endif gibber_transport_set_state (GIBBER_TRANSPORT(self), GIBBER_TRANSPORT_CONNECTED); } gboolean gibber_fd_transport_send (GibberTransport *transport, const guint8 *data, gsize size, GError **error) { return _writeout (GIBBER_FD_TRANSPORT (transport), data, size, error); } void gibber_fd_transport_disconnect (GibberTransport *transport) { DEBUG("Connection close requested"); _do_disconnect (GIBBER_FD_TRANSPORT (transport)); } static gboolean gibber_fd_transport_get_peeraddr (GibberTransport *transport, struct sockaddr_storage *addr, socklen_t *len) { GibberFdTransport *self = GIBBER_FD_TRANSPORT (transport); if (self->fd == -1) { DEBUG ("Someone requested the sockaddr while we're not connected"); return FALSE; } *len = sizeof (struct sockaddr_storage); return (getpeername (self->fd, (struct sockaddr *) addr, len) == 0); } static gboolean gibber_fd_transport_get_sockaddr (GibberTransport *transport, struct sockaddr_storage *addr, socklen_t *len) { GibberFdTransport *self = GIBBER_FD_TRANSPORT (transport); if (self->fd == -1) { DEBUG ("Someone requested the sockaddr while we're not connected"); return FALSE; } *len = sizeof (struct sockaddr_storage); return (getsockname (self->fd, (struct sockaddr *) addr, len) == 0); } static gboolean gibber_fd_transport_buffer_is_empty (GibberTransport *transport) { GibberFdTransport *self = GIBBER_FD_TRANSPORT (transport); GibberFdTransportPrivate *priv = GIBBER_FD_TRANSPORT_GET_PRIVATE (self); return (priv->output_buffer == NULL || priv->output_buffer->len == 0); } static void gibber_fd_transport_block_receiving (GibberTransport *transport, gboolean block) { GibberFdTransport *self = GIBBER_FD_TRANSPORT (transport); GibberFdTransportPrivate *priv = GIBBER_FD_TRANSPORT_GET_PRIVATE (self); if (block && priv->watch_in != 0) { DEBUG ("block receiving from the transport"); g_source_remove (priv->watch_in); priv->watch_in = 0; } else if (!block && priv->watch_in == 0) { DEBUG ("unblock receiving from the transport"); if (priv->channel != NULL) { #ifdef G_OS_WIN32 /* workaround for GLib bug #338943 */ if (priv->watch_err) { g_source_remove (priv->watch_err); priv->watch_err = 0; } priv->watch_in = g_io_add_watch (priv->channel, G_IO_IN | G_IO_ERR, _channel_io_in_dispatcher, self); #else priv->watch_in = g_io_add_watch (priv->channel, G_IO_IN, _channel_io_in, self); #endif } /* else the transport isn't connected yet */ } priv->receiving_blocked = block; } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������telepathy-gabble-0.18.2/lib/gibber/gibber-transport.h�����������������������������������������������0000644�0001750�0001750�00000010652�12200204333�022443� 0����������������������������������������������������������������������������������������������������ustar�00smcv����������������������������smcv����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * gibber-transport.h - Header for GibberTransport * Copyright (C) 2006 Collabora Ltd. * @author Sjoerd Simons <sjoerd@luon.net> * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 __GIBBER_TRANSPORT_H__ #define __GIBBER_TRANSPORT_H__ #include <glib-object.h> #include "gibber-sockets.h" G_BEGIN_DECLS typedef enum { GIBBER_TRANSPORT_DISCONNECTED = 0, GIBBER_TRANSPORT_CONNECTING, GIBBER_TRANSPORT_CONNECTED, GIBBER_TRANSPORT_DISCONNECTING, } GibberTransportState; typedef struct _GibberTransport GibberTransport; typedef struct _GibberTransportClass GibberTransportClass; typedef struct _GibberBuffer GibberBuffer; typedef void (*GibberHandlerFunc) (GibberTransport *transport, GibberBuffer *buffer, gpointer user_data); struct _GibberBuffer { const guint8 *data; gsize length; }; struct _GibberTransportClass { GObjectClass parent_class; gboolean (*send) (GibberTransport *transport, const guint8 *data, gsize length, GError **error); void (*disconnect) (GibberTransport *transport); gboolean (*get_peeraddr) (GibberTransport *transport, struct sockaddr_storage *addr, socklen_t *len); gboolean (*get_sockaddr) (GibberTransport *transport, struct sockaddr_storage *addr, socklen_t *len); gboolean (*buffer_is_empty) (GibberTransport *transport); void (*block_receiving) (GibberTransport *transport, gboolean block); }; struct _GibberTransport { GObject parent; GibberTransportState state; /* Maximum packet size for transports where it matters, 0 otherwise */ gsize max_packet_size; /* FIXME Should be private... */ GibberHandlerFunc handler; gpointer user_data; }; GType gibber_transport_get_type (void); /* TYPE MACROS */ #define GIBBER_TYPE_TRANSPORT (gibber_transport_get_type ()) #define GIBBER_TRANSPORT(obj) \ (G_TYPE_CHECK_INSTANCE_CAST((obj), GIBBER_TYPE_TRANSPORT, GibberTransport)) #define GIBBER_TRANSPORT_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST((klass), GIBBER_TYPE_TRANSPORT, \ GibberTransportClass)) #define GIBBER_IS_TRANSPORT(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE((obj), GIBBER_TYPE_TRANSPORT)) #define GIBBER_IS_TRANSPORT_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE((klass), GIBBER_TYPE_TRANSPORT)) #define GIBBER_TRANSPORT_GET_CLASS(obj) \ (G_TYPE_INSTANCE_GET_CLASS ((obj), GIBBER_TYPE_TRANSPORT, \ GibberTransportClass)) /* Utility functions for the classes based on GibberTransport */ void gibber_transport_received_data (GibberTransport *transport, const guint8 *data, gsize length); void gibber_transport_received_data_custom (GibberTransport *transport, GibberBuffer *buffer); void gibber_transport_set_state (GibberTransport *transport, GibberTransportState state); void gibber_transport_emit_error (GibberTransport *transport, GError *error); /* Public api */ GibberTransportState gibber_transport_get_state (GibberTransport *transport); gboolean gibber_transport_send (GibberTransport *transport, const guint8 *data, gsize size, GError **error); void gibber_transport_disconnect (GibberTransport *transport); void gibber_transport_set_handler (GibberTransport *transport, GibberHandlerFunc func, gpointer user_data); gboolean gibber_transport_get_peeraddr (GibberTransport *transport, struct sockaddr_storage *addr, socklen_t *len); gboolean gibber_transport_get_sockaddr (GibberTransport *transport, struct sockaddr_storage *addr, socklen_t *len); gboolean gibber_transport_buffer_is_empty (GibberTransport *transport); void gibber_transport_emit_buffer_empty (GibberTransport *transport); void gibber_transport_block_receiving (GibberTransport *transport, gboolean block); G_END_DECLS #endif /* #ifndef __GIBBER_TRANSPORT_H__*/ ��������������������������������������������������������������������������������������telepathy-gabble-0.18.2/lib/gibber/gibber-transport.c�����������������������������������������������0000644�0001750�0001750�00000020073�12200204333�022434� 0����������������������������������������������������������������������������������������������������ustar�00smcv����������������������������smcv����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * gibber-transport.c - Source for GibberTransport * Copyright (C) 2006 Collabora Ltd. * @author Sjoerd Simons <sjoerd@luon.net> * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 "config.h" #include <stdio.h> #include <stdlib.h> #include "gibber-transport.h" #include "gibber-signals-marshal.h" #define DEBUG_FLAG DEBUG_TRANSPORT #include "gibber-debug.h" G_DEFINE_TYPE(GibberTransport, gibber_transport, G_TYPE_OBJECT) /* signal enum */ enum { CONNECTED, CONNECTING, DISCONNECTED, DISCONNECTING, ERROR, BUFFER_EMPTY, LAST_SIGNAL }; static guint signals[LAST_SIGNAL] = {0}; /* private structure */ typedef struct _GibberTransportPrivate GibberTransportPrivate; struct _GibberTransportPrivate { gboolean dispose_has_run; }; #define GIBBER_TRANSPORT_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GIBBER_TYPE_TRANSPORT, GibberTransportPrivate)) static void gibber_transport_init (GibberTransport *obj) { obj->state = GIBBER_TRANSPORT_DISCONNECTED; obj->handler = NULL; } static void gibber_transport_dispose (GObject *object); static void gibber_transport_finalize (GObject *object); static void gibber_transport_class_init (GibberTransportClass *gibber_transport_class) { GObjectClass *object_class = G_OBJECT_CLASS (gibber_transport_class); g_type_class_add_private (gibber_transport_class, sizeof (GibberTransportPrivate)); object_class->dispose = gibber_transport_dispose; object_class->finalize = gibber_transport_finalize; signals[BUFFER_EMPTY] = g_signal_new ("buffer-empty", G_OBJECT_CLASS_TYPE (gibber_transport_class), G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED, 0, NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); signals[CONNECTED] = g_signal_new ("connected", G_OBJECT_CLASS_TYPE (gibber_transport_class), G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED, 0, NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); signals[CONNECTING] = g_signal_new ("connecting", G_OBJECT_CLASS_TYPE (gibber_transport_class), G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED, 0, NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); signals[DISCONNECTING] = g_signal_new ("disconnecting", G_OBJECT_CLASS_TYPE (gibber_transport_class), G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED, 0, NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); signals[DISCONNECTED] = g_signal_new ("disconnected", G_OBJECT_CLASS_TYPE (gibber_transport_class), G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED, 0, NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); signals[ERROR] = g_signal_new ("error", G_OBJECT_CLASS_TYPE (gibber_transport_class), G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED, 0, NULL, NULL, _gibber_signals_marshal_VOID__UINT_INT_STRING, G_TYPE_NONE, 3, G_TYPE_UINT, G_TYPE_INT, G_TYPE_STRING); } void gibber_transport_dispose (GObject *object) { GibberTransport *self = GIBBER_TRANSPORT (object); GibberTransportPrivate *priv = GIBBER_TRANSPORT_GET_PRIVATE (self); if (priv->dispose_has_run) return; priv->dispose_has_run = TRUE; /* release any references held by the object here */ if (G_OBJECT_CLASS (gibber_transport_parent_class)->dispose) G_OBJECT_CLASS (gibber_transport_parent_class)->dispose (object); } void gibber_transport_finalize (GObject *object) { G_OBJECT_CLASS (gibber_transport_parent_class)->finalize (object); } void gibber_transport_received_data (GibberTransport *transport, const guint8 *data, gsize length) { GibberBuffer buffer; buffer.length = length; buffer.data = data; gibber_transport_received_data_custom (transport, &buffer); } void gibber_transport_received_data_custom (GibberTransport *transport, GibberBuffer *buffer) { if (G_UNLIKELY (transport->handler == NULL)) { DEBUG("No handler for transport, dropping data!"); } else { transport->handler (transport, buffer, transport->user_data); } } void gibber_transport_set_state (GibberTransport *transport, GibberTransportState state) { if (state != transport->state) { transport->state = state; switch (state) { case GIBBER_TRANSPORT_DISCONNECTED: g_signal_emit (transport, signals[DISCONNECTED], 0); break; case GIBBER_TRANSPORT_CONNECTING: g_signal_emit (transport, signals[CONNECTING], 0); break; case GIBBER_TRANSPORT_CONNECTED: g_signal_emit (transport, signals[CONNECTED], 0); break; case GIBBER_TRANSPORT_DISCONNECTING: g_signal_emit (transport, signals[DISCONNECTING], 0); break; } } } GibberTransportState gibber_transport_get_state (GibberTransport *transport) { return transport->state; } void gibber_transport_emit_error (GibberTransport *transport, GError *error) { DEBUG ("Transport error: %s", error->message); g_signal_emit (transport, signals[ERROR], 0, error->domain, error->code, error->message); } gboolean gibber_transport_send (GibberTransport *transport, const guint8 *data, gsize size, GError **error) { GibberTransportClass *cls = GIBBER_TRANSPORT_GET_CLASS (transport); g_assert (transport->state == GIBBER_TRANSPORT_CONNECTED); return cls->send (transport, data, size, error); } void gibber_transport_disconnect (GibberTransport *transport) { GibberTransportClass *cls = GIBBER_TRANSPORT_GET_CLASS (transport); return cls->disconnect (transport); } void gibber_transport_set_handler (GibberTransport *transport, GibberHandlerFunc func, gpointer user_data) { g_assert (transport->handler == NULL || func == NULL); transport->handler = func; transport->user_data = user_data; } gboolean gibber_transport_get_peeraddr (GibberTransport *transport, struct sockaddr_storage *addr, socklen_t *len) { GibberTransportClass *cls = GIBBER_TRANSPORT_GET_CLASS (transport); if (cls->get_peeraddr != NULL) return cls->get_peeraddr (transport, addr, len); return FALSE; } gboolean gibber_transport_get_sockaddr (GibberTransport *transport, struct sockaddr_storage *addr, socklen_t *len) { GibberTransportClass *cls = GIBBER_TRANSPORT_GET_CLASS (transport); if (cls->get_sockaddr != NULL) return cls->get_sockaddr (transport, addr, len); return FALSE; } gboolean gibber_transport_buffer_is_empty (GibberTransport *transport) { GibberTransportClass *cls = GIBBER_TRANSPORT_GET_CLASS (transport); g_assert (cls->buffer_is_empty != NULL); return cls->buffer_is_empty (transport); } void gibber_transport_emit_buffer_empty (GibberTransport *transport) { g_signal_emit (transport, signals[BUFFER_EMPTY], 0); } void gibber_transport_block_receiving (GibberTransport *transport, gboolean block) { GibberTransportClass *cls = GIBBER_TRANSPORT_GET_CLASS (transport); g_assert (cls->block_receiving != NULL); cls->block_receiving (transport, block); } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������telepathy-gabble-0.18.2/lib/gibber/gibber-debug.h���������������������������������������������������0000644�0001750�0001750�00000003240�12200204333�021470� 0����������������������������������������������������������������������������������������������������ustar�00smcv����������������������������smcv����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ #ifndef __DEBUG_H__ #define __DEBUG_H__ #include "config.h" #include <glib.h> #if 0 #include "gibber-xmpp-stanza.h" #endif G_BEGIN_DECLS #ifdef ENABLE_DEBUG typedef enum { DEBUG_TRANSPORT = 1 << 0, DEBUG_NET = 1 << 1, DEBUG_XMPP_READER = 1 << 2, DEBUG_XMPP_WRITER = 1 << 3, DEBUG_SASL = 1 << 4, DEBUG_SSL = 1 << 5, DEBUG_RMULTICAST = 1 << 6, DEBUG_RMULTICAST_SENDER = 1 << 7, DEBUG_MUC_CONNECTION = 1 << 8, DEBUG_BYTESTREAM = 1 << 9, DEBUG_FILE_TRANSFER = 1 << 10, } DebugFlags; #define DEBUG_XMPP (DEBUG_XMPP_READER | DEBUG_XMPP_WRITER) void gibber_debug_set_flags_from_env (void); void gibber_debug_set_flags (DebugFlags flags); gboolean gibber_debug_flag_is_set (DebugFlags flag); void gibber_debug (DebugFlags flag, const gchar *format, ...) G_GNUC_PRINTF (2, 3); #if 0 void gibber_debug_stanza (DebugFlags flag, GibberXmppStanza *stanza, const gchar *format, ...) G_GNUC_PRINTF (3, 4); #endif #ifdef DEBUG_FLAG #define DEBUG(format, ...) \ G_STMT_START { \ gibber_debug (DEBUG_FLAG, "%s: " format, G_STRFUNC, ##__VA_ARGS__); \ } G_STMT_END #define DEBUG_STANZA(stanza, format, ...) \ G_STMT_START { \ gibber_debug_stanza (DEBUG_FLAG, stanza, "%s: " format, G_STRFUNC,\ ##__VA_ARGS__); \ } G_STMT_END #define DEBUGGING (debug_flag_is_set (DEBUG_FLAG)) #endif /* DEBUG_FLAG */ #else /* ENABLE_DEBUG */ #ifdef DEBUG_FLAG #define DEBUG(format, ...) G_STMT_START { } G_STMT_END #define DEBUG_STANZA(stanza, format, ...) G_STMT_START { } G_STMT_END #define DEBUGGING (0) #endif /* DEBUG_FLAG */ #endif /* ENABLE_DEBUG */ G_END_DECLS #endif ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������telepathy-gabble-0.18.2/lib/gibber/gibber-debug.c���������������������������������������������������0000644�0001750�0001750�00000004440�12200204333�021466� 0����������������������������������������������������������������������������������������������������ustar�00smcv����������������������������smcv����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#include "config.h" #include <stdarg.h> #include <glib.h> #include "gibber-debug.h" #ifdef ENABLE_DEBUG static DebugFlags flags = 0; static gboolean initialized = FALSE; static GDebugKey keys[] = { { "transport", DEBUG_TRANSPORT }, { "net", DEBUG_NET }, { "xmpp", DEBUG_XMPP }, { "xmpp-reader", DEBUG_XMPP_READER }, { "xmpp-writer", DEBUG_XMPP_WRITER }, { "sasl", DEBUG_SASL }, { "ssl", DEBUG_SSL }, { "rmulticast", DEBUG_RMULTICAST }, { "rmulticast-sender", DEBUG_RMULTICAST_SENDER }, { "muc-connection", DEBUG_MUC_CONNECTION }, { "bytestream", DEBUG_BYTESTREAM }, { "ft", DEBUG_FILE_TRANSFER }, { "all", ~0 }, { 0, }, }; void gibber_debug_set_flags_from_env () { guint nkeys; const gchar *flags_string; for (nkeys = 0; keys[nkeys].value; nkeys++); flags_string = g_getenv ("GIBBER_DEBUG"); if (flags_string) gibber_debug_set_flags (g_parse_debug_string (flags_string, keys, nkeys)); initialized = TRUE; } void gibber_debug_set_flags (DebugFlags new_flags) { flags |= new_flags; initialized = TRUE; } gboolean gibber_debug_flag_is_set (DebugFlags flag) { return flag & flags; } void gibber_debug (DebugFlags flag, const gchar *format, ...) { if (G_UNLIKELY(!initialized)) gibber_debug_set_flags_from_env (); if (flag & flags) { va_list args; va_start (args, format); g_logv (G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, format, args); va_end (args); } } #if 0 void gibber_debug_stanza (DebugFlags flag, GibberXmppStanza *stanza, const gchar *format, ...) { if (G_UNLIKELY(!initialized)) gibber_debug_set_flags_from_env (); if (flag & flags) { va_list args; gchar *msg, *node_str; va_start (args, format); msg = g_strdup_vprintf (format, args); va_end (args); node_str = gibber_xmpp_node_to_string (stanza->node); g_log (G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, "%s\n%s", msg, node_str); g_free (msg); g_free (node_str); } } #endif #endif ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������telepathy-gabble-0.18.2/lib/gibber/Makefile.am������������������������������������������������������0000644�0001750�0001750�00000004657�12200204333�021052� 0����������������������������������������������������������������������������������������������������ustar�00smcv����������������������������smcv����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������noinst_LTLIBRARIES = libgibber.la BUILT_SOURCES = \ gibber-signals-marshal.list \ gibber-signals-marshal.h \ gibber-signals-marshal.c OUR_SOURCES = \ gibber-debug.c \ gibber-debug.h \ gibber-transport.c \ gibber-transport.h \ gibber-fd-transport.c \ gibber-fd-transport.h \ gibber-tcp-transport.c \ gibber-tcp-transport.h \ gibber-unix-transport.c \ gibber-unix-transport.h \ gibber-linklocal-transport.c \ gibber-linklocal-transport.h \ gibber-listener.c \ gibber-listener.h \ gibber-sockets.c \ gibber-sockets.h \ gibber-sockets-unix.h \ gibber-sockets-win32.h \ gibber-util.h \ gibber-util.c libgibber_la_SOURCES = $(OUR_SOURCES) $(BUILT_SOURCES) # Coding style checks check_c_sources = \ $(OUR_SOURCES) include $(top_srcdir)/tools/check-coding-style.mk check-local: check-coding-style CLEANFILES=$(BUILT_SOURCES) dist-hook: $(shell for x in $(BUILT_SOURCES); do rm -f $(distdir)/$$x ; done) gibber-signals-marshal.list: $(OUR_SOURCES) Makefile.am @( cd $(srcdir) && \ sed -n -e 's/.*_gibber_signals_marshal_\([[:upper:][:digit:]]*__[[:upper:][:digit:]_]*\).*/\1/p' \ $(OUR_SOURCES) ) \ | sed -e 's/__/:/' -e 'y/_/,/' | sort -u > $@.tmp @if cmp -s $@.tmp $@; then \ rm $@.tmp; \ touch $@; \ else \ mv $@.tmp $@; \ fi %-signals-marshal.h: %-signals-marshal.list Makefile.am $(AM_V_GEN)glib-genmarshal --header --prefix=_$(subst -,_,$*)_signals_marshal $< > $@ %-signals-marshal.c: %-signals-marshal.list Makefile.am $(AM_V_GEN){ echo '#include "$*-signals-marshal.h"' && \ glib-genmarshal --body --prefix=_$(subst -,_,$*)_signals_marshal $< ; \ } > $@ AM_CFLAGS = $(ERROR_CFLAGS) $(GCOV_CFLAGS) @GLIB_CFLAGS@ @GMODULE_CFLAGS@ -fno-strict-aliasing # following flag is requied to make getnameinfo work if WINDOWS AM_CFLAGS += -D_WIN32_WINNT=0x0501 endif AM_LDFLAGS = $(GCOV_LIBS) @GLIB_LIBS@ Android.mk: Makefile.am $(BUILT_SOURCES) androgenizer -:PROJECT telepathy-gabble -:STATIC gibber -:TAGS eng debug \ -:REL_TOP $(top_srcdir) -:ABS_TOP $(abs_top_srcdir) \ -:SOURCES $(libgibber_la_SOURCES) \ -:CFLAGS $(DEFS) $(CFLAGS) $(DEFAULT_INCLUDES) $(INCLUDES) \ $(AM_CFLAGS) \ -:CPPFLAGS $(CPPFLAGS) $(AM_CPPFLAGS) \ -:LDFLAGS $(AM_LDFLAGS) \ > $@ ���������������������������������������������������������������������������������telepathy-gabble-0.18.2/lib/gibber/Makefile.in������������������������������������������������������0000644�0001750�0001750�00000054472�12312536074�021101� 0����������������������������������������������������������������������������������������������������ustar�00smcv����������������������������smcv����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Makefile.in generated by automake 1.14.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2013 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ VPATH = @srcdir@ am__is_gnu_make = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)' am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ DIST_COMMON = $(top_srcdir)/tools/check-coding-style.mk \ $(srcdir)/Makefile.in $(srcdir)/Makefile.am \ $(top_srcdir)/depcomp # following flag is requied to make getnameinfo work @WINDOWS_TRUE@am__append_1 = -D_WIN32_WINNT=0x0501 subdir = lib/gibber ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/ax_config_dir.m4 \ $(top_srcdir)/m4/compiler.m4 $(top_srcdir)/m4/libtool.m4 \ $(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \ $(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \ $(top_srcdir)/m4/tp-compiler-flag.m4 \ $(top_srcdir)/m4/tp-compiler-warnings.m4 \ $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = LTLIBRARIES = $(noinst_LTLIBRARIES) libgibber_la_LIBADD = am__objects_1 = gibber-debug.lo gibber-transport.lo \ gibber-fd-transport.lo gibber-tcp-transport.lo \ gibber-unix-transport.lo gibber-linklocal-transport.lo \ gibber-listener.lo gibber-sockets.lo gibber-util.lo am__objects_2 = gibber-signals-marshal.lo am_libgibber_la_OBJECTS = $(am__objects_1) $(am__objects_2) libgibber_la_OBJECTS = $(am_libgibber_la_OBJECTS) AM_V_lt = $(am__v_lt_@AM_V@) am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) am__v_lt_0 = --silent am__v_lt_1 = AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) depcomp = $(SHELL) $(top_srcdir)/depcomp am__depfiles_maybe = depfiles am__mv = mv -f COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ $(AM_CFLAGS) $(CFLAGS) AM_V_CC = $(am__v_CC_@AM_V@) am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) am__v_CC_0 = @echo " CC " $@; am__v_CC_1 = CCLD = $(CC) LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ $(AM_LDFLAGS) $(LDFLAGS) -o $@ AM_V_CCLD = $(am__v_CCLD_@AM_V@) am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) am__v_CCLD_0 = @echo " CCLD " $@; am__v_CCLD_1 = SOURCES = $(libgibber_la_SOURCES) DIST_SOURCES = $(libgibber_la_SOURCES) am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) # Read a list of newline-separated strings from the standard input, # and print each of them once, without duplicates. Input order is # *not* preserved. am__uniquify_input = $(AWK) '\ BEGIN { nonempty = 0; } \ { items[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in items) print i; }; } \ ' # Make sure the list of sources is unique. This is necessary because, # e.g., the same source file might be shared among _SOURCES variables # for different programs/libraries. am__define_uniq_tagged_files = \ list='$(am__tagged_files)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | $(am__uniquify_input)` ETAGS = etags CTAGS = ctags DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ AR = @AR@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CFLAGS = @CFLAGS@ CLIENT_TYPE = @CLIENT_TYPE@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CYGPATH_W = @CYGPATH_W@ DBUS_CFLAGS = @DBUS_CFLAGS@ DBUS_LIBS = @DBUS_LIBS@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DLLTOOL = @DLLTOOL@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ ENABLE_CODING_STYLE_CHECKS = @ENABLE_CODING_STYLE_CHECKS@ ENABLE_PLUGINS = @ENABLE_PLUGINS@ ERROR_CFLAGS = @ERROR_CFLAGS@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ GLIB_CFLAGS = @GLIB_CFLAGS@ GLIB_GENMARSHAL = @GLIB_GENMARSHAL@ GLIB_LIBS = @GLIB_LIBS@ GMODULE_CFLAGS = @GMODULE_CFLAGS@ GMODULE_LIBS = @GMODULE_LIBS@ GREP = @GREP@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ LD = @LD@ LDFLAGS = @LDFLAGS@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBTOOL = @LIBTOOL@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBOBJS = @LTLIBOBJS@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ NICE_CFLAGS = @NICE_CFLAGS@ NICE_LIBS = @NICE_LIBS@ NM = @NM@ NMEDIT = @NMEDIT@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ PYTHON = @PYTHON@ PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@ PYTHON_PLATFORM = @PYTHON_PLATFORM@ PYTHON_PREFIX = @PYTHON_PREFIX@ PYTHON_VERSION = @PYTHON_VERSION@ RANLIB = @RANLIB@ SED = @SED@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ SOUP_CFLAGS = @SOUP_CFLAGS@ SOUP_LIBS = @SOUP_LIBS@ STRIP = @STRIP@ TEST_PYTHON = @TEST_PYTHON@ TP_GLIB_CFLAGS = @TP_GLIB_CFLAGS@ TP_GLIB_LIBS = @TP_GLIB_LIBS@ VERSION = @VERSION@ WOCKY_CFLAGS = @WOCKY_CFLAGS@ WOCKY_LIBS = @WOCKY_LIBS@ XSLTPROC = @XSLTPROC@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ gabbletestsdir = @gabbletestsdir@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ mandir = @mandir@ mkdir_p = @mkdir_p@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ pkgpyexecdir = @pkgpyexecdir@ pkgpythondir = @pkgpythondir@ pluginexecdir = @pluginexecdir@ pluginexeclibdir = @pluginexeclibdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ pyexecdir = @pyexecdir@ pythondir = @pythondir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ srcdir = @srcdir@ sysconfdir = @sysconfdir@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ noinst_LTLIBRARIES = libgibber.la BUILT_SOURCES = \ gibber-signals-marshal.list \ gibber-signals-marshal.h \ gibber-signals-marshal.c OUR_SOURCES = \ gibber-debug.c \ gibber-debug.h \ gibber-transport.c \ gibber-transport.h \ gibber-fd-transport.c \ gibber-fd-transport.h \ gibber-tcp-transport.c \ gibber-tcp-transport.h \ gibber-unix-transport.c \ gibber-unix-transport.h \ gibber-linklocal-transport.c \ gibber-linklocal-transport.h \ gibber-listener.c \ gibber-listener.h \ gibber-sockets.c \ gibber-sockets.h \ gibber-sockets-unix.h \ gibber-sockets-win32.h \ gibber-util.h \ gibber-util.c libgibber_la_SOURCES = $(OUR_SOURCES) $(BUILT_SOURCES) # Coding style checks check_c_sources = \ $(OUR_SOURCES) CLEANFILES = $(BUILT_SOURCES) AM_CFLAGS = $(ERROR_CFLAGS) $(GCOV_CFLAGS) @GLIB_CFLAGS@ \ @GMODULE_CFLAGS@ -fno-strict-aliasing $(am__append_1) AM_LDFLAGS = $(GCOV_LIBS) @GLIB_LIBS@ all: $(BUILT_SOURCES) $(MAKE) $(AM_MAKEFLAGS) all-am .SUFFIXES: .SUFFIXES: .c .lo .o .obj $(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(top_srcdir)/tools/check-coding-style.mk $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu lib/gibber/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --gnu lib/gibber/Makefile .PRECIOUS: Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ esac; $(top_srcdir)/tools/check-coding-style.mk: $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): clean-noinstLTLIBRARIES: -test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES) @list='$(noinst_LTLIBRARIES)'; \ locs=`for p in $$list; do echo $$p; done | \ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ sort -u`; \ test -z "$$locs" || { \ echo rm -f $${locs}; \ rm -f $${locs}; \ } libgibber.la: $(libgibber_la_OBJECTS) $(libgibber_la_DEPENDENCIES) $(EXTRA_libgibber_la_DEPENDENCIES) $(AM_V_CCLD)$(LINK) $(libgibber_la_OBJECTS) $(libgibber_la_LIBADD) $(LIBS) mostlyclean-compile: -rm -f *.$(OBJEXT) distclean-compile: -rm -f *.tab.c @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gibber-debug.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gibber-fd-transport.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gibber-linklocal-transport.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gibber-listener.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gibber-signals-marshal.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gibber-sockets.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gibber-tcp-transport.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gibber-transport.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gibber-unix-transport.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gibber-util.Plo@am__quote@ .c.o: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< .c.obj: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` .c.lo: @am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs ID: $(am__tagged_files) $(am__define_uniq_tagged_files); mkid -fID $$unique tags: tags-am TAGS: tags tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) set x; \ here=`pwd`; \ $(am__define_uniq_tagged_files); \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ if test $$# -gt 0; then \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ "$$@" $$unique; \ else \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ $$unique; \ fi; \ fi ctags: ctags-am CTAGS: ctags ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) $(am__define_uniq_tagged_files); \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" cscopelist: cscopelist-am cscopelist-am: $(am__tagged_files) list='$(am__tagged_files)'; \ case "$(srcdir)" in \ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ *) sdir=$(subdir)/$(srcdir) ;; \ esac; \ for i in $$list; do \ if test -f "$$i"; then \ echo "$(subdir)/$$i"; \ else \ echo "$$sdir/$$i"; \ fi; \ done >> $(top_builddir)/cscope.files distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags distdir: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done $(MAKE) $(AM_MAKEFLAGS) \ top_distdir="$(top_distdir)" distdir="$(distdir)" \ dist-hook check-am: all-am $(MAKE) $(AM_MAKEFLAGS) check-local check: $(BUILT_SOURCES) $(MAKE) $(AM_MAKEFLAGS) check-am all-am: Makefile $(LTLIBRARIES) installdirs: install: $(BUILT_SOURCES) $(MAKE) $(AM_MAKEFLAGS) install-am install-exec: install-exec-am install-data: install-data-am uninstall: uninstall-am install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-am install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: clean-generic: -test -z "$(CLEANFILES)" || rm -f $(CLEANFILES) distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." -test -z "$(BUILT_SOURCES)" || rm -f $(BUILT_SOURCES) clean: clean-am clean-am: clean-generic clean-libtool clean-noinstLTLIBRARIES \ mostlyclean-am distclean: distclean-am -rm -rf ./$(DEPDIR) -rm -f Makefile distclean-am: clean-am distclean-compile distclean-generic \ distclean-tags dvi: dvi-am dvi-am: html: html-am html-am: info: info-am info-am: install-data-am: install-dvi: install-dvi-am install-dvi-am: install-exec-am: install-html: install-html-am install-html-am: install-info: install-info-am install-info-am: install-man: install-pdf: install-pdf-am install-pdf-am: install-ps: install-ps-am install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-am -rm -rf ./$(DEPDIR) -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-am mostlyclean-am: mostlyclean-compile mostlyclean-generic \ mostlyclean-libtool pdf: pdf-am pdf-am: ps: ps-am ps-am: uninstall-am: .MAKE: all check check-am install install-am install-strip .PHONY: CTAGS GTAGS TAGS all all-am check check-am check-local clean \ clean-generic clean-libtool clean-noinstLTLIBRARIES \ cscopelist-am ctags ctags-am dist-hook distclean \ distclean-compile distclean-generic distclean-libtool \ distclean-tags distdir dvi dvi-am html html-am info info-am \ install install-am install-data install-data-am install-dvi \ install-dvi-am install-exec install-exec-am install-html \ install-html-am install-info install-info-am install-man \ install-pdf install-pdf-am install-ps install-ps-am \ install-strip installcheck installcheck-am installdirs \ maintainer-clean maintainer-clean-generic mostlyclean \ mostlyclean-compile mostlyclean-generic mostlyclean-libtool \ pdf pdf-am ps ps-am tags tags-am uninstall uninstall-am check-coding-style: @fail=0; \ if test -n "$(check_misc_sources)"; then \ tools_dir=$(top_srcdir)/tools \ sh $(top_srcdir)/tools/check-misc.sh \ $(addprefix $(srcdir)/,$(check_misc_sources)) || fail=1; \ fi; \ if test -n "$(check_c_sources)"; then \ tools_dir=$(top_srcdir)/tools \ sh $(top_srcdir)/tools/check-c-style.sh \ $(addprefix $(srcdir)/,$(check_c_sources)) || fail=1; \ fi;\ if test yes = "$(ENABLE_CODING_STYLE_CHECKS)"; then \ exit "$$fail";\ else \ exit 0;\ fi check-local: check-coding-style dist-hook: $(shell for x in $(BUILT_SOURCES); do rm -f $(distdir)/$$x ; done) gibber-signals-marshal.list: $(OUR_SOURCES) Makefile.am @( cd $(srcdir) && \ sed -n -e 's/.*_gibber_signals_marshal_\([[:upper:][:digit:]]*__[[:upper:][:digit:]_]*\).*/\1/p' \ $(OUR_SOURCES) ) \ | sed -e 's/__/:/' -e 'y/_/,/' | sort -u > $@.tmp @if cmp -s $@.tmp $@; then \ rm $@.tmp; \ touch $@; \ else \ mv $@.tmp $@; \ fi %-signals-marshal.h: %-signals-marshal.list Makefile.am $(AM_V_GEN)glib-genmarshal --header --prefix=_$(subst -,_,$*)_signals_marshal $< > $@ %-signals-marshal.c: %-signals-marshal.list Makefile.am $(AM_V_GEN){ echo '#include "$*-signals-marshal.h"' && \ glib-genmarshal --body --prefix=_$(subst -,_,$*)_signals_marshal $< ; \ } > $@ Android.mk: Makefile.am $(BUILT_SOURCES) androgenizer -:PROJECT telepathy-gabble -:STATIC gibber -:TAGS eng debug \ -:REL_TOP $(top_srcdir) -:ABS_TOP $(abs_top_srcdir) \ -:SOURCES $(libgibber_la_SOURCES) \ -:CFLAGS $(DEFS) $(CFLAGS) $(DEFAULT_INCLUDES) $(INCLUDES) \ $(AM_CFLAGS) \ -:CPPFLAGS $(CPPFLAGS) $(AM_CPPFLAGS) \ -:LDFLAGS $(AM_LDFLAGS) \ > $@ # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������telepathy-gabble-0.18.2/lib/ext/��������������������������������������������������������������������0000755�0001750�0001750�00000000000�12312537047�016367� 5����������������������������������������������������������������������������������������������������ustar�00smcv����������������������������smcv����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������telepathy-gabble-0.18.2/lib/ext/wocky/��������������������������������������������������������������0000755�0001750�0001750�00000000000�12312537050�017515� 5����������������������������������������������������������������������������������������������������ustar�00smcv����������������������������smcv����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������telepathy-gabble-0.18.2/lib/ext/wocky/gtk-doc.make��������������������������������������������������0000644�0001750�0001750�00000022260�12300232454�021703� 0����������������������������������������������������������������������������������������������������ustar�00smcv����������������������������smcv����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# -*- mode: makefile -*- #################################### # Everything below here is generic # #################################### if GTK_DOC_USE_LIBTOOL GTKDOC_CC = $(LIBTOOL) --tag=CC --mode=compile $(CC) $(INCLUDES) $(GTKDOC_DEPS_CFLAGS) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) GTKDOC_LD = $(LIBTOOL) --tag=CC --mode=link $(CC) $(GTKDOC_DEPS_LIBS) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) GTKDOC_RUN = $(LIBTOOL) --mode=execute else GTKDOC_CC = $(CC) $(INCLUDES) $(GTKDOC_DEPS_CFLAGS) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) GTKDOC_LD = $(CC) $(GTKDOC_DEPS_LIBS) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) GTKDOC_RUN = endif # We set GPATH here; this gives us semantics for GNU make # which are more like other make's VPATH, when it comes to # whether a source that is a target of one rule is then # searched for in VPATH/GPATH. # GPATH = $(srcdir) TARGET_DIR=$(HTML_DIR)/$(DOC_MODULE) SETUP_FILES = \ $(content_files) \ $(DOC_MAIN_SGML_FILE) \ $(DOC_MODULE)-sections.txt \ $(DOC_MODULE)-overrides.txt EXTRA_DIST = \ $(HTML_IMAGES) \ $(SETUP_FILES) DOC_STAMPS=setup-build.stamp scan-build.stamp sgml-build.stamp \ html-build.stamp pdf-build.stamp \ sgml.stamp html.stamp pdf.stamp SCANOBJ_FILES = \ $(DOC_MODULE).args \ $(DOC_MODULE).hierarchy \ $(DOC_MODULE).interfaces \ $(DOC_MODULE).prerequisites \ $(DOC_MODULE).signals REPORT_FILES = \ $(DOC_MODULE)-undocumented.txt \ $(DOC_MODULE)-undeclared.txt \ $(DOC_MODULE)-unused.txt gtkdoc-check.test: Makefile $(AM_V_GEN)echo "#!/bin/sh -e" > $@; \ echo "$(GTKDOC_CHECK_PATH) || exit 1" >> $@; \ chmod +x $@ CLEANFILES = $(SCANOBJ_FILES) $(REPORT_FILES) $(DOC_STAMPS) gtkdoc-check.test if GTK_DOC_BUILD_HTML HTML_BUILD_STAMP=html-build.stamp else HTML_BUILD_STAMP= endif if GTK_DOC_BUILD_PDF PDF_BUILD_STAMP=pdf-build.stamp else PDF_BUILD_STAMP= endif all-gtk-doc: $(HTML_BUILD_STAMP) $(PDF_BUILD_STAMP) .PHONY: all-gtk-doc if ENABLE_GTK_DOC all-local: all-gtk-doc endif docs: $(HTML_BUILD_STAMP) $(PDF_BUILD_STAMP) $(REPORT_FILES): sgml-build.stamp #### setup #### GTK_DOC_V_SETUP=$(GTK_DOC_V_SETUP_$(V)) GTK_DOC_V_SETUP_=$(GTK_DOC_V_SETUP_$(AM_DEFAULT_VERBOSITY)) GTK_DOC_V_SETUP_0=@echo " DOC Preparing build"; setup-build.stamp: -$(GTK_DOC_V_SETUP)if test "$(abs_srcdir)" != "$(abs_builddir)" ; then \ files=`echo $(SETUP_FILES) $(expand_content_files) $(DOC_MODULE).types`; \ if test "x$$files" != "x" ; then \ for file in $$files ; do \ destdir=`dirname $(abs_builddir)/$$file`; \ test -d "$$destdir" || mkdir -p "$$destdir"; \ test -f $(abs_srcdir)/$$file && \ cp -pf $(abs_srcdir)/$$file $(abs_builddir)/$$file || true; \ done; \ fi; \ fi $(AM_V_at)touch setup-build.stamp #### scan #### GTK_DOC_V_SCAN=$(GTK_DOC_V_SCAN_$(V)) GTK_DOC_V_SCAN_=$(GTK_DOC_V_SCAN_$(AM_DEFAULT_VERBOSITY)) GTK_DOC_V_SCAN_0=@echo " DOC Scanning header files"; GTK_DOC_V_INTROSPECT=$(GTK_DOC_V_INTROSPECT_$(V)) GTK_DOC_V_INTROSPECT_=$(GTK_DOC_V_INTROSPECT_$(AM_DEFAULT_VERBOSITY)) GTK_DOC_V_INTROSPECT_0=@echo " DOC Introspecting gobjects"; scan-build.stamp: setup-build.stamp $(HFILE_GLOB) $(CFILE_GLOB) $(GTK_DOC_V_SCAN)_source_dir='' ; \ for i in $(DOC_SOURCE_DIR) ; do \ _source_dir="$${_source_dir} --source-dir=$$i" ; \ done ; \ gtkdoc-scan --module=$(DOC_MODULE) --ignore-headers="$(IGNORE_HFILES)" $${_source_dir} $(SCAN_OPTIONS) $(EXTRA_HFILES) $(GTK_DOC_V_INTROSPECT)if grep -l '^..*$$' $(DOC_MODULE).types > /dev/null 2>&1 ; then \ scanobj_options=""; \ gtkdoc-scangobj 2>&1 --help | grep >/dev/null "\-\-verbose"; \ if test "$(?)" = "0"; then \ if test "x$(V)" = "x1"; then \ scanobj_options="--verbose"; \ fi; \ fi; \ CC="$(GTKDOC_CC)" LD="$(GTKDOC_LD)" RUN="$(GTKDOC_RUN)" CFLAGS="$(GTKDOC_CFLAGS) $(CFLAGS)" LDFLAGS="$(GTKDOC_LIBS) $(LDFLAGS)" \ gtkdoc-scangobj $(SCANGOBJ_OPTIONS) $$scanobj_options --module=$(DOC_MODULE); \ else \ for i in $(SCANOBJ_FILES) ; do \ test -f $$i || touch $$i ; \ done \ fi $(AM_V_at)touch scan-build.stamp $(DOC_MODULE)-decl.txt $(SCANOBJ_FILES) $(DOC_MODULE)-sections.txt $(DOC_MODULE)-overrides.txt: scan-build.stamp @true #### xml #### GTK_DOC_V_XML=$(GTK_DOC_V_XML_$(V)) GTK_DOC_V_XML_=$(GTK_DOC_V_XML_$(AM_DEFAULT_VERBOSITY)) GTK_DOC_V_XML_0=@echo " DOC Building XML"; sgml-build.stamp: setup-build.stamp $(DOC_MODULE)-decl.txt $(SCANOBJ_FILES) $(DOC_MODULE)-sections.txt $(DOC_MODULE)-overrides.txt $(expand_content_files) $(GTK_DOC_V_XML)_source_dir='' ; \ for i in $(DOC_SOURCE_DIR) ; do \ _source_dir="$${_source_dir} --source-dir=$$i" ; \ done ; \ gtkdoc-mkdb --module=$(DOC_MODULE) --output-format=xml --expand-content-files="$(expand_content_files)" --main-sgml-file=$(DOC_MAIN_SGML_FILE) $${_source_dir} $(MKDB_OPTIONS) $(AM_V_at)touch sgml-build.stamp sgml.stamp: sgml-build.stamp @true #### html #### GTK_DOC_V_HTML=$(GTK_DOC_V_HTML_$(V)) GTK_DOC_V_HTML_=$(GTK_DOC_V_HTML_$(AM_DEFAULT_VERBOSITY)) GTK_DOC_V_HTML_0=@echo " DOC Building HTML"; GTK_DOC_V_XREF=$(GTK_DOC_V_XREF_$(V)) GTK_DOC_V_XREF_=$(GTK_DOC_V_XREF_$(AM_DEFAULT_VERBOSITY)) GTK_DOC_V_XREF_0=@echo " DOC Fixing cross-references"; html-build.stamp: sgml.stamp $(DOC_MAIN_SGML_FILE) $(content_files) $(GTK_DOC_V_HTML)rm -rf html && mkdir html && \ mkhtml_options=""; \ gtkdoc-mkhtml 2>&1 --help | grep >/dev/null "\-\-verbose"; \ if test "$(?)" = "0"; then \ if test "x$(V)" = "x1"; then \ mkhtml_options="$$mkhtml_options --verbose"; \ fi; \ fi; \ gtkdoc-mkhtml 2>&1 --help | grep >/dev/null "\-\-path"; \ if test "$(?)" = "0"; then \ mkhtml_options="$$mkhtml_options --path=\"$(abs_srcdir)\""; \ fi; \ cd html && gtkdoc-mkhtml $$mkhtml_options $(MKHTML_OPTIONS) $(DOC_MODULE) ../$(DOC_MAIN_SGML_FILE) -@test "x$(HTML_IMAGES)" = "x" || \ for file in $(HTML_IMAGES) ; do \ if test -f $(abs_srcdir)/$$file ; then \ cp $(abs_srcdir)/$$file $(abs_builddir)/html; \ fi; \ if test -f $(abs_builddir)/$$file ; then \ cp $(abs_builddir)/$$file $(abs_builddir)/html; \ fi; \ done; $(GTK_DOC_V_XREF)gtkdoc-fixxref --module=$(DOC_MODULE) --module-dir=html --html-dir=$(HTML_DIR) $(FIXXREF_OPTIONS) $(AM_V_at)touch html-build.stamp #### pdf #### GTK_DOC_V_PDF=$(GTK_DOC_V_PDF_$(V)) GTK_DOC_V_PDF_=$(GTK_DOC_V_PDF_$(AM_DEFAULT_VERBOSITY)) GTK_DOC_V_PDF_0=@echo " DOC Building PDF"; pdf-build.stamp: sgml.stamp $(DOC_MAIN_SGML_FILE) $(content_files) $(GTK_DOC_V_PDF)rm -f $(DOC_MODULE).pdf && \ mkpdf_options=""; \ gtkdoc-mkpdf 2>&1 --help | grep >/dev/null "\-\-verbose"; \ if test "$(?)" = "0"; then \ if test "x$(V)" = "x1"; then \ mkpdf_options="$$mkpdf_options --verbose"; \ fi; \ fi; \ if test "x$(HTML_IMAGES)" != "x"; then \ for img in $(HTML_IMAGES); do \ part=`dirname $$img`; \ echo $$mkpdf_options | grep >/dev/null "\-\-imgdir=$$part "; \ if test $$? != 0; then \ mkpdf_options="$$mkpdf_options --imgdir=$$part"; \ fi; \ done; \ fi; \ gtkdoc-mkpdf --path="$(abs_srcdir)" $$mkpdf_options $(DOC_MODULE) $(DOC_MAIN_SGML_FILE) $(MKPDF_OPTIONS) $(AM_V_at)touch pdf-build.stamp ############## clean-local: @rm -f *~ *.bak @rm -rf .libs @if echo $(SCAN_OPTIONS) | grep -q "\-\-rebuild-types" ; then \ rm -f $(DOC_MODULE).types; \ fi distclean-local: @rm -rf xml html $(REPORT_FILES) $(DOC_MODULE).pdf \ $(DOC_MODULE)-decl-list.txt $(DOC_MODULE)-decl.txt @if test "$(abs_srcdir)" != "$(abs_builddir)" ; then \ rm -f $(SETUP_FILES) $(expand_content_files) $(DOC_MODULE).types; \ fi maintainer-clean-local: @rm -rf xml html install-data-local: @installfiles=`echo $(builddir)/html/*`; \ if test "$$installfiles" = '$(builddir)/html/*'; \ then echo 1>&2 'Nothing to install' ; \ else \ if test -n "$(DOC_MODULE_VERSION)"; then \ installdir="$(DESTDIR)$(TARGET_DIR)-$(DOC_MODULE_VERSION)"; \ else \ installdir="$(DESTDIR)$(TARGET_DIR)"; \ fi; \ $(mkinstalldirs) $${installdir} ; \ for i in $$installfiles; do \ echo ' $(INSTALL_DATA) '$$i ; \ $(INSTALL_DATA) $$i $${installdir}; \ done; \ if test -n "$(DOC_MODULE_VERSION)"; then \ mv -f $${installdir}/$(DOC_MODULE).devhelp2 \ $${installdir}/$(DOC_MODULE)-$(DOC_MODULE_VERSION).devhelp2; \ fi; \ $(GTKDOC_REBASE) --relative --dest-dir=$(DESTDIR) --html-dir=$${installdir}; \ fi uninstall-local: @if test -n "$(DOC_MODULE_VERSION)"; then \ installdir="$(DESTDIR)$(TARGET_DIR)-$(DOC_MODULE_VERSION)"; \ else \ installdir="$(DESTDIR)$(TARGET_DIR)"; \ fi; \ rm -rf $${installdir} # # Require gtk-doc when making dist # if HAVE_GTK_DOC dist-check-gtkdoc: docs else dist-check-gtkdoc: @echo "*** gtk-doc is needed to run 'make dist'. ***" @echo "*** gtk-doc was not found when 'configure' ran. ***" @echo "*** please install gtk-doc and rerun 'configure'. ***" @false endif dist-hook: dist-check-gtkdoc all-gtk-doc dist-hook-local @mkdir $(distdir)/html @cp ./html/* $(distdir)/html @-cp ./$(DOC_MODULE).pdf $(distdir)/ @-cp ./$(DOC_MODULE).types $(distdir)/ @-cp ./$(DOC_MODULE)-sections.txt $(distdir)/ @cd $(distdir) && rm -f $(DISTCLEANFILES) @$(GTKDOC_REBASE) --online --relative --html-dir=$(distdir)/html .PHONY : dist-hook-local docs ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������telepathy-gabble-0.18.2/lib/ext/wocky/docs/���������������������������������������������������������0000755�0001750�0001750�00000000000�12312537050�020445� 5����������������������������������������������������������������������������������������������������ustar�00smcv����������������������������smcv����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������telepathy-gabble-0.18.2/lib/ext/wocky/docs/reference/�����������������������������������������������0000755�0001750�0001750�00000000000�12312537050�022403� 5����������������������������������������������������������������������������������������������������ustar�00smcv����������������������������smcv����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������telepathy-gabble-0.18.2/lib/ext/wocky/docs/reference/html/������������������������������������������0000755�0001750�0001750�00000000000�12312537050�023347� 5����������������������������������������������������������������������������������������������������ustar�00smcv����������������������������smcv����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������telepathy-gabble-0.18.2/lib/ext/wocky/docs/reference/html/annotation-glossary.html������������������0000644�0001750�0001750�00000006221�12312537050�030251� 0����������������������������������������������������������������������������������������������������ustar�00smcv����������������������������smcv����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>Wocky Reference Manual: Annotation Glossary

Annotation Glossary

A

allow-none

NULL is ok, both for passing and for returning.

E

element-type

Generics and defining elements of containers and arrays.

O

out

Parameter for returning results. Default is transfer full.

T

transfer full

Free data after the code is done.

transfer none

Don't free data after the code is done.

type

Override the parsed C type with given type.

telepathy-gabble-0.18.2/lib/ext/wocky/docs/reference/html/api-index-full.html0000644000175000017500000076403512312537050027072 0ustar00smcvsmcv00000000000000 Wocky Reference Manual: API Index

API Index

A

wocky_absolutize_path, function in wocky-utils
WockyAuthAuthDataFunc, user_function in WockyAuthHandler
WockyAuthError, enum in WockyAuthRegistry
WockyAuthHandlerIface, struct in WockyAuthHandler
WockyAuthInitialResponseFunc, user_function in WockyAuthHandler
WockyAuthRegistryChallengeAsyncFunc, user_function in WockyAuthRegistry
WockyAuthRegistryChallengeFinishFunc, user_function in WockyAuthRegistry
WockyAuthRegistryClass, struct in WockyAuthRegistry
WockyAuthRegistryFailureFunc, user_function in WockyAuthRegistry
WockyAuthRegistryStartAuthAsyncFunc, user_function in WockyAuthRegistry
WockyAuthRegistryStartAuthFinishFunc, user_function in WockyAuthRegistry
WockyAuthRegistryStartData, struct in WockyAuthRegistry
WockyAuthRegistrySuccessAsyncFunc, user_function in WockyAuthRegistry
WockyAuthRegistrySuccessFinishFunc, user_function in WockyAuthRegistry
WockyAuthSuccessFunc, user_function in WockyAuthHandler
WOCKY_AUTH_ERROR, macro in WockyAuthRegistry
wocky_auth_error_quark, function in WockyAuthRegistry
wocky_auth_handler_get_initial_response, function in WockyAuthHandler
wocky_auth_handler_get_mechanism, function in WockyAuthHandler
wocky_auth_handler_handle_auth_data, function in WockyAuthHandler
wocky_auth_handler_handle_success, function in WockyAuthHandler
wocky_auth_handler_is_plain, function in WockyAuthHandler
WOCKY_AUTH_MECH_JABBER_DIGEST, macro in WockyAuthRegistry
WOCKY_AUTH_MECH_JABBER_PASSWORD, macro in WockyAuthRegistry
WOCKY_AUTH_MECH_SASL_DIGEST_MD5, macro in WockyAuthRegistry
WOCKY_AUTH_MECH_SASL_PLAIN, macro in WockyAuthRegistry
WOCKY_AUTH_MECH_SASL_SCRAM_SHA_1, macro in WockyAuthRegistry
wocky_auth_registry_add_handler, function in WockyAuthRegistry
wocky_auth_registry_challenge_async, function in WockyAuthRegistry
wocky_auth_registry_challenge_finish, function in WockyAuthRegistry
wocky_auth_registry_failure, function in WockyAuthRegistry
wocky_auth_registry_new, function in WockyAuthRegistry
wocky_auth_registry_start_auth_async, function in WockyAuthRegistry
wocky_auth_registry_start_auth_finish, function in WockyAuthRegistry
wocky_auth_registry_start_data_dup, function in WockyAuthRegistry
wocky_auth_registry_start_data_free, function in WockyAuthRegistry
wocky_auth_registry_start_data_new, function in WockyAuthRegistry
wocky_auth_registry_success_async, function in WockyAuthRegistry
wocky_auth_registry_success_finish, function in WockyAuthRegistry
wocky_auth_registry_supports_one_of, function in WockyAuthRegistry

B

WockyBareContact, struct in wocky-types
WockyBareContact:groups, object property in wocky-types
WockyBareContact:jid, object property in wocky-types
WockyBareContact:name, object property in wocky-types
WockyBareContact:subscription, object property in wocky-types
WockyBareContactClass, struct in WockyBareContact
wocky_bare_contact_add_group, function in WockyBareContact
wocky_bare_contact_add_resource, function in WockyBareContact
wocky_bare_contact_copy, function in WockyBareContact
wocky_bare_contact_debug_print, function in WockyBareContact
wocky_bare_contact_equal, function in WockyBareContact
wocky_bare_contact_get_groups, function in WockyBareContact
wocky_bare_contact_get_jid, function in WockyBareContact
wocky_bare_contact_get_name, function in WockyBareContact
wocky_bare_contact_get_resources, function in WockyBareContact
wocky_bare_contact_get_subscription, function in WockyBareContact
wocky_bare_contact_in_group, function in WockyBareContact
wocky_bare_contact_new, function in WockyBareContact
wocky_bare_contact_remove_group, function in WockyBareContact
wocky_bare_contact_set_groups, function in WockyBareContact
wocky_bare_contact_set_name, function in WockyBareContact
wocky_bare_contact_set_subscription, function in WockyBareContact

C

WockyC2SPorter, struct in WockyC2SPorter
WockyC2SPorterClass, struct in WockyC2SPorter
wocky_c2s_porter_enable_power_saving_mode, function in WockyC2SPorter
wocky_c2s_porter_new, function in WockyC2SPorter
wocky_c2s_porter_register_handler_from_server, function in WockyC2SPorter
wocky_c2s_porter_register_handler_from_server_by_stanza, function in WockyC2SPorter
wocky_c2s_porter_register_handler_from_server_va, function in WockyC2SPorter
wocky_c2s_porter_send_whitespace_ping_async, function in WockyC2SPorter
wocky_c2s_porter_send_whitespace_ping_finish, function in WockyC2SPorter
WockyCapsCache, struct in WockyCapsCache
WockyCapsCache:path, object property in WockyCapsCache
WockyCapsCacheClass, struct in WockyCapsCache
wocky_caps_cache_dup_shared, function in WockyCapsCache
wocky_caps_cache_free_shared, function in WockyCapsCache
wocky_caps_cache_insert, function in WockyCapsCache
wocky_caps_cache_lookup, function in WockyCapsCache
wocky_caps_cache_new, function in WockyCapsCache
wocky_caps_hash_compute_from_lists, function in WockyCapsHash
wocky_caps_hash_compute_from_node, function in WockyCapsHash
wocky_compose_jid, function in wocky-utils
WockyConnector::connection-established, object signal in WockyConnector
WockyConnector:auth-registry, object property in WockyConnector
WockyConnector:email, object property in WockyConnector
WockyConnector:encrypted-plain-auth-ok, object property in WockyConnector
WockyConnector:features, object property in WockyConnector
WockyConnector:identity, object property in WockyConnector
WockyConnector:jid, object property in WockyConnector
WockyConnector:legacy, object property in WockyConnector
WockyConnector:old-ssl, object property in WockyConnector
WockyConnector:password, object property in WockyConnector
WockyConnector:plaintext-auth-allowed, object property in WockyConnector
WockyConnector:resource, object property in WockyConnector
WockyConnector:session-id, object property in WockyConnector
WockyConnector:tls-handler, object property in WockyConnector
WockyConnector:tls-required, object property in WockyConnector
WockyConnector:xmpp-port, object property in WockyConnector
WockyConnector:xmpp-server, object property in WockyConnector
WockyConnectorClass, struct in WockyConnector
WockyConnectorError, enum in WockyConnector
wocky_connector_connect_async, function in WockyConnector
wocky_connector_connect_finish, function in WockyConnector
WOCKY_CONNECTOR_ERROR, macro in WockyConnector
wocky_connector_error_quark, function in WockyConnector
wocky_connector_new, function in WockyConnector
wocky_connector_register_async, function in WockyConnector
wocky_connector_register_finish, function in WockyConnector
wocky_connector_set_auth_registry, function in WockyConnector
wocky_connector_unregister_async, function in WockyConnector
wocky_connector_unregister_finish, function in WockyConnector
WockyContactClass, struct in WockyContact
WockyContactDupJidImpl, user_function in WockyContact
WockyContactFactory::bare-contact-added, object signal in WockyContactFactory
WockyContactFactory::ll-contact-added, object signal in WockyContactFactory
WockyContactFactory::resource-contact-added, object signal in WockyContactFactory
WockyContactFactoryClass, struct in WockyContactFactory
wocky_contact_dup_jid, function in WockyContact
wocky_contact_factory_add_ll_contact, function in WockyContactFactory
wocky_contact_factory_ensure_bare_contact, function in WockyContactFactory
wocky_contact_factory_ensure_ll_contact, function in WockyContactFactory
wocky_contact_factory_ensure_resource_contact, function in WockyContactFactory
wocky_contact_factory_get_ll_contacts, function in WockyContactFactory
wocky_contact_factory_lookup_bare_contact, function in WockyContactFactory
wocky_contact_factory_lookup_ll_contact, function in WockyContactFactory
wocky_contact_factory_lookup_resource_contact, function in WockyContactFactory
wocky_contact_factory_new, function in WockyContactFactory

D

WockyDataForm, struct in WockyDataForm
WockyDataForm:instructions, object property in WockyDataForm
WockyDataForm:title, object property in WockyDataForm
WockyDataFormClass, struct in WockyDataForm
WockyDataFormError, enum in WockyDataForm
WockyDataFormField, struct in WockyDataForm
WockyDataFormFieldOption, struct in WockyDataForm
WockyDataFormFieldType, enum in WockyDataForm
wocky_data_form_add_to_node, function in WockyDataForm
WOCKY_DATA_FORM_ERROR, macro in WockyDataForm
wocky_data_form_error_quark, function in WockyDataForm
wocky_data_form_field_cmp, function in WockyDataForm
wocky_data_form_get_instructions, function in WockyDataForm
wocky_data_form_get_title, function in WockyDataForm
wocky_data_form_new_from_form, function in WockyDataForm
wocky_data_form_new_from_node, function in WockyDataForm
wocky_data_form_parse_result, function in WockyDataForm
wocky_data_form_set_boolean, function in WockyDataForm
wocky_data_form_set_string, function in WockyDataForm
wocky_data_form_set_strv, function in WockyDataForm
wocky_data_form_set_type, function in WockyDataForm
wocky_data_form_submit, function in WockyDataForm
DEBUG, macro in wocky-debug-internal
wocky_debug, function in wocky-debug-internal
WockyDebugFlags, enum in wocky-debug
DEBUGGING, macro in wocky-debug-internal
wocky_debug_flag_is_set, function in wocky-debug-internal
wocky_debug_node, function in wocky-debug-internal
DEBUG_NODE, macro in wocky-debug-internal
wocky_debug_node_tree, function in wocky-debug-internal
DEBUG_NODE_TREE, macro in wocky-debug-internal
wocky_debug_set_flags, function in wocky-debug
wocky_debug_set_flags_from_env, function in wocky-debug-internal
wocky_debug_stanza, function in wocky-debug-internal
DEBUG_STANZA, macro in wocky-debug-internal
wocky_debug_valist, function in wocky-debug-internal
WOCKY_DEBUG_XMPP, macro in wocky-debug
wocky_decode_jid, function in wocky-utils
wocky_deinit, function in wocky
WockyDiscoIdentity, struct in WockyDiscoIdentity
wocky_disco_identity_array_copy, function in WockyDiscoIdentity
wocky_disco_identity_array_free, function in WockyDiscoIdentity
wocky_disco_identity_array_new, function in WockyDiscoIdentity
wocky_disco_identity_cmp, function in WockyDiscoIdentity
wocky_disco_identity_copy, function in WockyDiscoIdentity
wocky_disco_identity_free, function in WockyDiscoIdentity
wocky_disco_identity_new, function in WockyDiscoIdentity

E

wocky_enum_from_nick, function in wocky-utils
wocky_enum_to_nick, function in wocky-utils

G

WockyGoogleRelayResolver, struct in wocky-google-relay
WOCKY_GOOGLE_NS_AUTH, macro in wocky-namespaces
wocky_google_relay_resolver_destroy, function in wocky-google-relay
wocky_google_relay_resolver_new, function in wocky-google-relay
wocky_google_relay_resolver_resolve, function in wocky-google-relay
wocky_g_string_dup, function in wocky-utils
wocky_g_string_free, function in wocky-utils
wocky_g_value_slice_dup, function in wocky-utils
wocky_g_value_slice_free, function in wocky-utils
wocky_g_value_slice_new, function in wocky-utils
wocky_g_value_slice_new_boolean, function in wocky-utils
wocky_g_value_slice_new_boxed, function in wocky-utils
wocky_g_value_slice_new_double, function in wocky-utils
wocky_g_value_slice_new_int, function in wocky-utils
wocky_g_value_slice_new_int64, function in wocky-utils
wocky_g_value_slice_new_static_boxed, function in wocky-utils
wocky_g_value_slice_new_static_string, function in wocky-utils
wocky_g_value_slice_new_string, function in wocky-utils
wocky_g_value_slice_new_take_boxed, function in wocky-utils
wocky_g_value_slice_new_take_string, function in wocky-utils
wocky_g_value_slice_new_uint, function in wocky-utils
wocky_g_value_slice_new_uint64, function in wocky-utils

H

WockyHeartbeatCallback, user_function in wocky-heartbeat-source
wocky_heartbeat_source_new, function in wocky-heartbeat-source
wocky_heartbeat_source_update_interval, function in wocky-heartbeat-source
WOCKY_H_INSIDE, macro in wocky

I

wocky_implement_finish_copy_pointer, macro in wocky-utils
wocky_implement_finish_return_copy_pointer, macro in wocky-utils
wocky_implement_finish_return_pointer, macro in wocky-utils
wocky_implement_finish_void, macro in wocky-utils
wocky_init, function in wocky

J

WockyJabberAuth:auth-registry, object property in WockyJabberAuth
WockyJabberAuth:connection, object property in WockyJabberAuth
WockyJabberAuth:password, object property in WockyJabberAuth
WockyJabberAuth:resource, object property in WockyJabberAuth
WockyJabberAuth:session-id, object property in WockyJabberAuth
WockyJabberAuth:username, object property in WockyJabberAuth
WockyJabberAuthClass, struct in WockyJabberAuth
wocky_jabber_auth_add_handler, function in WockyJabberAuth
wocky_jabber_auth_authenticate_async, function in WockyJabberAuth
wocky_jabber_auth_authenticate_finish, function in WockyJabberAuth
wocky_jabber_auth_digest_new, function in wocky-jabber-auth-digest
wocky_jabber_auth_new, function in WockyJabberAuth
wocky_jabber_auth_password_new, function in wocky-jabber-auth-password
WOCKY_JABBER_NS_AUTH, macro in wocky-namespaces
WOCKY_JABBER_NS_AUTH_FEATURE, macro in wocky-namespaces
WockyJingleAction, enum in wocky-jingle-types
WockyJingleCandidate, struct in wocky-jingle-types
WockyJingleCandidateType, enum in wocky-jingle-types
WockyJingleCodec, struct in WockyJingleMediaRtp
WockyJingleContent, struct in wocky-jingle-types
WockyJingleContent::completed, object signal in wocky-jingle-types
WockyJingleContent::new-candidates, object signal in wocky-jingle-types
WockyJingleContent::new-share-channel, object signal in wocky-jingle-types
WockyJingleContent::ready, object signal in wocky-jingle-types
WockyJingleContent::removed, object signal in wocky-jingle-types
WockyJingleContent:content-ns, object property in wocky-jingle-types
WockyJingleContent:disposition, object property in wocky-jingle-types
WockyJingleContent:locally-created, object property in wocky-jingle-types
WockyJingleContent:name, object property in wocky-jingle-types
WockyJingleContent:senders, object property in wocky-jingle-types
WockyJingleContent:session, object property in wocky-jingle-types
WockyJingleContent:state, object property in wocky-jingle-types
WockyJingleContent:transport-ns, object property in wocky-jingle-types
WockyJingleContentSenders, enum in wocky-jingle-types
WockyJingleContentState, enum in WockyJingleContent
WockyJingleDialect, enum in wocky-jingle-types
WockyJingleError, enum in wocky-xmpp-error
WockyJingleFactory, struct in wocky-jingle-types
WockyJingleFactory::new-session, object signal in wocky-jingle-types
WockyJingleFactory::query-cap, object signal in wocky-jingle-types
WockyJingleFactory:session, object property in wocky-jingle-types
WockyJingleFeedbackMessage, struct in WockyJingleMediaRtp
WockyJingleInfo::stun-server-changed, object signal in WockyJingleInfo
WockyJingleInfo:porter, object property in WockyJingleInfo
WockyJingleInfoRelaySessionCb, user_function in WockyJingleInfo
WockyJingleMediaDescription, struct in WockyJingleMediaRtp
WockyJingleMediaRtp, struct in wocky-jingle-types
WockyJingleMediaRtp::remote-media-description, object signal in wocky-jingle-types
WockyJingleMediaRtp:media-type, object property in wocky-jingle-types
WockyJingleMediaRtp:remote-mute, object property in wocky-jingle-types
WockyJingleMediaType, enum in WockyJingleContent
WockyJingleReason, enum in wocky-jingle-types
WockyJingleRelay, struct in WockyJingleInfo
WockyJingleRelayType, enum in WockyJingleInfo
WockyJingleRtpHeaderExtension, struct in WockyJingleMediaRtp
WockyJingleSession, struct in wocky-jingle-types
WockyJingleSession::about-to-initiate, object signal in wocky-jingle-types
WockyJingleSession::content-rejected, object signal in wocky-jingle-types
WockyJingleSession::new-content, object signal in wocky-jingle-types
WockyJingleSession::query-cap, object signal in wocky-jingle-types
WockyJingleSession::remote-state-changed, object signal in wocky-jingle-types
WockyJingleSession::terminated, object signal in wocky-jingle-types
WockyJingleSession:dialect, object property in wocky-jingle-types
WockyJingleSession:jingle-factory, object property in wocky-jingle-types
WockyJingleSession:local-hold, object property in wocky-jingle-types
WockyJingleSession:local-initiator, object property in wocky-jingle-types
WockyJingleSession:peer-contact, object property in wocky-jingle-types
WockyJingleSession:porter, object property in wocky-jingle-types
WockyJingleSession:remote-hold, object property in wocky-jingle-types
WockyJingleSession:remote-ringing, object property in wocky-jingle-types
WockyJingleSession:session-id, object property in wocky-jingle-types
WockyJingleSession:state, object property in wocky-jingle-types
WockyJingleState, enum in wocky-jingle-types
WockyJingleTransportGoogle, struct in wocky-jingle-types
WockyJingleTransportGoogle::new-candidates, object signal in wocky-jingle-types
WockyJingleTransportGoogle:content, object property in wocky-jingle-types
WockyJingleTransportGoogle:state, object property in wocky-jingle-types
WockyJingleTransportGoogle:transport-ns, object property in wocky-jingle-types
WockyJingleTransportIceUdp, struct in wocky-jingle-types
WockyJingleTransportIceUdp::new-candidates, object signal in wocky-jingle-types
WockyJingleTransportIceUdp:content, object property in wocky-jingle-types
WockyJingleTransportIceUdp:state, object property in wocky-jingle-types
WockyJingleTransportIceUdp:transport-ns, object property in wocky-jingle-types
WockyJingleTransportIface:content, object property in WockyJingleTransportIface
WockyJingleTransportIface:state, object property in WockyJingleTransportIface
WockyJingleTransportIface:transport-ns, object property in WockyJingleTransportIface
WockyJingleTransportProtocol, enum in wocky-jingle-types
WockyJingleTransportRawUdp, struct in wocky-jingle-types
WockyJingleTransportRawUdp::new-candidates, object signal in wocky-jingle-types
WockyJingleTransportRawUdp:content, object property in wocky-jingle-types
WockyJingleTransportRawUdp:state, object property in wocky-jingle-types
WockyJingleTransportRawUdp:transport-ns, object property in wocky-jingle-types
WockyJingleTransportState, enum in WockyJingleTransportIface
WockyJingleTransportType, enum in wocky-jingle-types
wocky_jingle_candidate_free, function in WockyJingleTransportIface
wocky_jingle_candidate_new, function in WockyJingleTransportIface
wocky_jingle_content_add_candidates, function in WockyJingleContent
wocky_jingle_content_change_direction, function in WockyJingleContent
wocky_jingle_content_create_share_channel, function in WockyJingleContent
wocky_jingle_content_creator_is_initiator, function in WockyJingleContent
wocky_jingle_content_get_credentials, function in WockyJingleContent
wocky_jingle_content_get_disposition, function in WockyJingleContent
wocky_jingle_content_get_local_candidates, function in WockyJingleContent
wocky_jingle_content_get_name, function in WockyJingleContent
wocky_jingle_content_get_ns, function in WockyJingleContent
wocky_jingle_content_get_remote_candidates, function in WockyJingleContent
wocky_jingle_content_get_transport_ns, function in WockyJingleContent
wocky_jingle_content_get_transport_type, function in WockyJingleContent
wocky_jingle_content_inject_candidates, function in WockyJingleContent
wocky_jingle_content_is_created_by_us, function in WockyJingleContent
wocky_jingle_content_is_ready, function in WockyJingleContent
wocky_jingle_content_maybe_send_description, function in WockyJingleContent
wocky_jingle_content_parse_accept, function in WockyJingleContent
wocky_jingle_content_parse_add, function in WockyJingleContent
wocky_jingle_content_parse_description_info, function in WockyJingleContent
wocky_jingle_content_parse_info, function in WockyJingleContent
wocky_jingle_content_parse_transport_info, function in WockyJingleContent
wocky_jingle_content_produce_node, function in WockyJingleContent
wocky_jingle_content_receiving, function in WockyJingleContent
wocky_jingle_content_reject, function in WockyJingleContent
wocky_jingle_content_remove, function in WockyJingleContent
wocky_jingle_content_request_receiving, function in WockyJingleContent
wocky_jingle_content_retransmit_candidates, function in WockyJingleContent
wocky_jingle_content_sending, function in WockyJingleContent
wocky_jingle_content_send_complete, function in WockyJingleContent
wocky_jingle_content_set_sending, function in WockyJingleContent
wocky_jingle_content_set_transport_state, function in WockyJingleContent
wocky_jingle_content_update_senders, function in WockyJingleContent
WOCKY_JINGLE_ERROR, macro in wocky-xmpp-error
wocky_jingle_error_quark, function in wocky-xmpp-error
wocky_jingle_factory_create_session, function in WockyJingleFactory
wocky_jingle_factory_get_jingle_info, function in WockyJingleFactory
wocky_jingle_factory_lookup_content_type, function in WockyJingleFactory
wocky_jingle_factory_lookup_transport, function in WockyJingleFactory
wocky_jingle_factory_new, function in WockyJingleFactory
wocky_jingle_factory_register_content_type, function in WockyJingleFactory
wocky_jingle_factory_register_transport, function in WockyJingleFactory
wocky_jingle_factory_stop, function in WockyJingleFactory
wocky_jingle_feedback_message_free, function in WockyJingleMediaRtp
wocky_jingle_feedback_message_new, function in WockyJingleMediaRtp
wocky_jingle_info_create_google_relay_session, function in WockyJingleInfo
wocky_jingle_info_get_google_relay_token, function in WockyJingleInfo
wocky_jingle_info_get_stun_servers, function in WockyJingleInfo
wocky_jingle_info_new, function in WockyJingleInfo
wocky_jingle_info_send_request, function in WockyJingleInfo
wocky_jingle_info_set_test_mode, function in WockyJingleInfo
wocky_jingle_info_take_stun_server, function in WockyJingleInfo
wocky_jingle_media_description_copy, function in WockyJingleMediaRtp
wocky_jingle_media_description_free, function in WockyJingleMediaRtp
wocky_jingle_media_description_new, function in WockyJingleMediaRtp
wocky_jingle_media_description_simplify, function in WockyJingleMediaRtp
jingle_media_rtp_codec_free, function in WockyJingleMediaRtp
jingle_media_rtp_codec_new, function in WockyJingleMediaRtp
jingle_media_rtp_compare_codecs, function in WockyJingleMediaRtp
jingle_media_rtp_copy_codecs, function in WockyJingleMediaRtp
jingle_media_rtp_free_codecs, function in WockyJingleMediaRtp
wocky_jingle_media_rtp_get_remote_media_description, function in WockyJingleMediaRtp
jingle_media_rtp_register, function in WockyJingleMediaRtp
jingle_media_rtp_set_local_media_description, function in WockyJingleMediaRtp
wocky_jingle_relay_free, function in WockyJingleInfo
wocky_jingle_relay_new, function in WockyJingleInfo
wocky_jingle_rtp_header_extension_free, function in WockyJingleMediaRtp
wocky_jingle_rtp_header_extension_new, function in WockyJingleMediaRtp
wocky_jingle_session_accept, function in WockyJingleSession
wocky_jingle_session_acknowledge_iq, function in WockyJingleSession
wocky_jingle_session_add_content, function in WockyJingleSession
wocky_jingle_session_can_modify_contents, function in WockyJingleSession
wocky_jingle_session_defines_action, function in WockyJingleSession
wocky_jingle_session_detect, function in WockyJingleSession
wocky_jingle_session_get_contents, function in WockyJingleSession
wocky_jingle_session_get_content_type, function in WockyJingleSession
wocky_jingle_session_get_dialect, function in WockyJingleSession
wocky_jingle_session_get_factory, function in WockyJingleSession
wocky_jingle_session_get_initiator, function in WockyJingleSession
wocky_jingle_session_get_peer_contact, function in WockyJingleSession
wocky_jingle_session_get_peer_jid, function in WockyJingleSession
wocky_jingle_session_get_peer_resource, function in WockyJingleSession
wocky_jingle_session_get_porter, function in WockyJingleSession
wocky_jingle_session_get_reason_name, function in WockyJingleSession
wocky_jingle_session_get_remote_hold, function in WockyJingleSession
wocky_jingle_session_get_remote_ringing, function in WockyJingleSession
wocky_jingle_session_get_sid, function in WockyJingleSession
wocky_jingle_session_new, function in WockyJingleSession
wocky_jingle_session_new_message, function in WockyJingleSession
wocky_jingle_session_parse, function in WockyJingleSession
wocky_jingle_session_peer_has_cap, function in WockyJingleSession
wocky_jingle_session_remove_content, function in WockyJingleSession
wocky_jingle_session_send, function in WockyJingleSession
wocky_jingle_session_set_local_hold, function in WockyJingleSession
wocky_jingle_session_terminate, function in WockyJingleSession
jingle_transport_free_candidates, function in WockyJingleTransportIface
jingle_transport_get_credentials, function in WockyJingleTransportIface
jingle_transport_google_register, function in WockyJingleTransportGoogle
jingle_transport_google_set_component_name, function in WockyJingleTransportGoogle
jingle_transport_iceudp_register, function in WockyJingleTransportIceUdp
wocky_jingle_transport_iface_can_accept, function in WockyJingleTransportIface
wocky_jingle_transport_iface_get_local_candidates, function in WockyJingleTransportIface
wocky_jingle_transport_iface_get_remote_candidates, function in WockyJingleTransportIface
wocky_jingle_transport_iface_get_transport_type, function in WockyJingleTransportIface
wocky_jingle_transport_iface_inject_candidates, function in WockyJingleTransportIface
wocky_jingle_transport_iface_new, function in WockyJingleTransportIface
wocky_jingle_transport_iface_new_local_candidates, function in WockyJingleTransportIface
wocky_jingle_transport_iface_parse_candidates, function in WockyJingleTransportIface
wocky_jingle_transport_iface_send_candidates, function in WockyJingleTransportIface
jingle_transport_rawudp_register, function in WockyJingleTransportRawUdp

L

wocky_list_deep_copy, function in wocky-utils
WockyLLConnectionFactoryError, enum in WockyLLConnectionFactory
WockyLLConnector:connection, object property in WockyLLConnector
WockyLLConnector:incoming, object property in WockyLLConnector
WockyLLConnector:local-jid, object property in WockyLLConnector
WockyLLConnector:remote-jid, object property in WockyLLConnector
WockyLLConnector:stream, object property in WockyLLConnector
WockyLLConnectorError, enum in WockyLLConnector
WockyLLContact, struct in wocky-types
WockyLLContact:jid, object property in wocky-types
WockyLLContactGetAddressesImpl, user_function in WockyLLContact
WOCKY_LL_CONNECTION_FACTORY_ERROR, macro in WockyLLConnectionFactory
wocky_ll_connection_factory_error_quark, function in WockyLLConnectionFactory
wocky_ll_connection_factory_make_connection_async, function in WockyLLConnectionFactory
wocky_ll_connection_factory_make_connection_finish, function in WockyLLConnectionFactory
wocky_ll_connection_factory_new, function in WockyLLConnectionFactory
WOCKY_LL_CONNECTOR_ERROR, macro in WockyLLConnector
wocky_ll_connector_error_quark, function in WockyLLConnector
wocky_ll_connector_finish, function in WockyLLConnector
wocky_ll_connector_incoming_async, function in WockyLLConnector
wocky_ll_connector_outgoing_async, function in WockyLLConnector
wocky_ll_contact_equal, function in WockyLLContact
wocky_ll_contact_get_addresses, function in WockyLLContact
wocky_ll_contact_get_jid, function in WockyLLContact
wocky_ll_contact_has_address, function in WockyLLContact
wocky_ll_contact_new, function in WockyLLContact
WockyLoopbackStream:input-stream, object property in WockyLoopbackStream
WockyLoopbackStream:output-stream, object property in WockyLoopbackStream
wocky_loopback_stream_new, function in WockyLoopbackStream

M

WockyMetaPorter:contact-factory, object property in WockyMetaPorter
WockyMetaPorterError, enum in WockyMetaPorter
wocky_meta_porter_borrow_connection, function in WockyMetaPorter
WOCKY_META_PORTER_ERROR, macro in WockyMetaPorter
wocky_meta_porter_error_quark, function in WockyMetaPorter
wocky_meta_porter_get_port, function in WockyMetaPorter
wocky_meta_porter_hold, function in WockyMetaPorter
wocky_meta_porter_new, function in WockyMetaPorter
wocky_meta_porter_open_async, function in WockyMetaPorter
wocky_meta_porter_open_finish, function in WockyMetaPorter
wocky_meta_porter_set_jid, function in WockyMetaPorter
wocky_meta_porter_unhold, function in WockyMetaPorter
WockyMuc::error, object signal in WockyMuc
WockyMuc::fill-presence, object signal in WockyMuc
WockyMuc::joined, object signal in WockyMuc
WockyMuc::left, object signal in WockyMuc
WockyMuc::message, object signal in WockyMuc
WockyMuc::message-error, object signal in WockyMuc
WockyMuc::nick-change, object signal in WockyMuc
WockyMuc::own-presence, object signal in WockyMuc
WockyMuc::parted, object signal in WockyMuc
WockyMuc::permissions, object signal in WockyMuc
WockyMuc::presence, object signal in WockyMuc
WockyMuc:affiliation, object property in WockyMuc
WockyMuc:category, object property in WockyMuc
WockyMuc:description, object property in WockyMuc
WockyMuc:jid, object property in WockyMuc
WockyMuc:muc-flags, object property in WockyMuc
WockyMuc:name, object property in WockyMuc
WockyMuc:nickname, object property in WockyMuc
WockyMuc:password, object property in WockyMuc
WockyMuc:porter, object property in WockyMuc
WockyMuc:reserved-nick, object property in WockyMuc
WockyMuc:role, object property in WockyMuc
WockyMuc:room, object property in WockyMuc
WockyMuc:service, object property in WockyMuc
WockyMuc:status-message, object property in WockyMuc
WockyMuc:type, object property in WockyMuc
WockyMuc:user, object property in WockyMuc
WockyMucAffiliation, enum in WockyMuc
WockyMucClass, struct in WockyMuc
WockyMucFeature, enum in WockyMuc
WockyMucMember, struct in WockyMuc
WockyMucMsgState, enum in WockyMuc
WockyMucMsgType, enum in WockyMuc
WockyMucRole, enum in WockyMuc
WockyMucState, enum in WockyMuc
WockyMucStatusCode, enum in WockyMuc
wocky_muc_affiliation, function in WockyMuc
wocky_muc_create_presence, function in WockyMuc
wocky_muc_disco_info_async, function in WockyMuc
wocky_muc_disco_info_finish, function in WockyMuc
wocky_muc_get_state, function in WockyMuc
wocky_muc_jid, function in WockyMuc
wocky_muc_join, function in WockyMuc
wocky_muc_members, function in WockyMuc
wocky_muc_role, function in WockyMuc
wocky_muc_user, function in WockyMuc

N

WockyNode, struct in WockyNode
WockyNodeBuildTag, enum in WockyNode
WockyNodeIter, struct in WockyNode
WockyNodeTree, struct in wocky-types
WockyNodeTree:top-node, object property in wocky-types
WockyNodeTreeClass, struct in WockyNodeTree
wocky_node_add_build, function in WockyNode
wocky_node_add_build_va, function in WockyNode
wocky_node_add_child, function in WockyNode
wocky_node_add_child_ns, function in WockyNode
wocky_node_add_child_ns_q, function in WockyNode
wocky_node_add_child_with_content, function in WockyNode
wocky_node_add_child_with_content_ns, function in WockyNode
wocky_node_add_child_with_content_ns_q, function in WockyNode
wocky_node_add_node_tree, function in WockyNode
wocky_node_append_content, function in WockyNode
wocky_node_append_content_n, function in WockyNode
wocky_node_attribute_ns_get_prefix_from_quark, function in WockyNode
wocky_node_attribute_ns_get_prefix_from_urn, function in WockyNode
wocky_node_attribute_ns_set_prefix, function in WockyNode
wocky_node_deinit, function in WockyNode
wocky_node_each_attribute, function in WockyNode
wocky_node_each_attr_func, user_function in WockyNode
wocky_node_each_child, function in WockyNode
wocky_node_each_child_func, user_function in WockyNode
wocky_node_equal, function in WockyNode
wocky_node_free, function in WockyNode
wocky_node_get_attribute, function in WockyNode
wocky_node_get_attribute_ns, function in WockyNode
wocky_node_get_child, function in WockyNode
wocky_node_get_child_ns, function in WockyNode
wocky_node_get_content_from_child, function in WockyNode
wocky_node_get_content_from_child_ns, function in WockyNode
wocky_node_get_first_child, function in WockyNode
wocky_node_get_first_child_ns, function in WockyNode
wocky_node_get_language, function in WockyNode
wocky_node_get_ns, function in WockyNode
wocky_node_has_ns, function in WockyNode
wocky_node_has_ns_q, function in WockyNode
wocky_node_init, function in WockyNode
wocky_node_is_superset, function in WockyNode
wocky_node_iter_init, function in WockyNode
wocky_node_iter_next, function in WockyNode
wocky_node_iter_remove, function in WockyNode
wocky_node_matches, function in WockyNode
wocky_node_matches_q, function in WockyNode
wocky_node_new, function in WockyNode
wocky_node_prepend_node_tree, function in WockyNode
wocky_node_set_attribute, function in WockyNode
wocky_node_set_attributes, function in WockyNode
wocky_node_set_attribute_n, function in WockyNode
wocky_node_set_attribute_ns, function in WockyNode
wocky_node_set_attribute_n_ns, function in WockyNode
wocky_node_set_content, function in WockyNode
wocky_node_set_language, function in WockyNode
wocky_node_set_language_n, function in WockyNode
wocky_node_to_string, function in WockyNode
wocky_node_tree_get_top_node, function in WockyNodeTree
wocky_node_tree_new, function in WockyNodeTree
wocky_node_tree_new_from_node, function in WockyNodeTree
wocky_node_tree_new_va, function in WockyNodeTree
wocky_normalise_jid, function in wocky-utils
WOCKY_NS_CHATSTATE, macro in wocky-namespaces
WOCKY_NS_DISCO_INFO, macro in wocky-namespaces
WOCKY_NS_DISCO_ITEMS, macro in wocky-namespaces
WOCKY_NS_GOOGLE_SESSION_PHONE, macro in wocky-namespaces
WOCKY_NS_GOOGLE_SESSION_VIDEO, macro in wocky-namespaces
WOCKY_NS_MUC, macro in wocky-namespaces
WOCKY_NS_MUC_ADMIN, macro in wocky-namespaces
WOCKY_NS_MUC_OWNER, macro in wocky-namespaces
WOCKY_NS_MUC_UNIQUE, macro in wocky-namespaces
WOCKY_NS_MUC_USER, macro in wocky-namespaces
WOCKY_NS_VCARD_TEMP, macro in wocky-namespaces
WOCKY_NS_VCARD_TEMP_UPDATE, macro in wocky-namespaces
WOCKY_N_JINGLE_RELAY_TYPES, macro in WockyJingleInfo

P

WockyPepService, struct in WockyPepService
WockyPepService::changed, object signal in WockyPepService
WockyPepService:node, object property in WockyPepService
WockyPepService:subscribe, object property in WockyPepService
WockyPepServiceClass, struct in WockyPepService
wocky_pep_service_get_async, function in WockyPepService
wocky_pep_service_get_finish, function in WockyPepService
wocky_pep_service_make_publish_stanza, function in WockyPepService
wocky_pep_service_new, function in WockyPepService
wocky_pep_service_start, function in WockyPepService
WockyPing:ping-interval, object property in WockyPing
WockyPing:porter, object property in WockyPing
WockyPingClass, struct in WockyPing
wocky_ping_error_quark, function in WockyPing
wocky_ping_new, function in WockyPing
WockyPorter::closing, object signal in WockyPorter
WockyPorter::remote-closed, object signal in WockyPorter
WockyPorter::remote-error, object signal in WockyPorter
WockyPorter::sending, object signal in WockyPorter
WockyPorter:bare-jid, object property in WockyPorter
WockyPorter:connection, object property in WockyPorter
WockyPorter:full-jid, object property in WockyPorter
WockyPorter:resource, object property in WockyPorter
WockyPorterError, enum in WockyPorter
WockyPorterHandlerFunc, user_function in WockyPorter
WockyPorterInterface, struct in WockyPorter
wocky_porter_acknowledge_iq, function in WockyPorter
wocky_porter_close_async, function in WockyPorter
wocky_porter_close_finish, function in WockyPorter
WOCKY_PORTER_ERROR, macro in WockyPorter
wocky_porter_error_quark, function in WockyPorter
wocky_porter_force_close_async, function in WockyPorter
wocky_porter_force_close_finish, function in WockyPorter
wocky_porter_get_bare_jid, function in WockyPorter
wocky_porter_get_full_jid, function in WockyPorter
wocky_porter_get_resource, function in WockyPorter
WOCKY_PORTER_HANDLER_PRIORITY_MAX, macro in WockyPorter
WOCKY_PORTER_HANDLER_PRIORITY_MIN, macro in WockyPorter
WOCKY_PORTER_HANDLER_PRIORITY_NORMAL, macro in WockyPorter
wocky_porter_register_handler_from, function in WockyPorter
wocky_porter_register_handler_from_anyone, function in WockyPorter
wocky_porter_register_handler_from_anyone_by_stanza, function in WockyPorter
wocky_porter_register_handler_from_anyone_va, function in WockyPorter
wocky_porter_register_handler_from_by_stanza, function in WockyPorter
wocky_porter_register_handler_from_va, function in WockyPorter
wocky_porter_send, function in WockyPorter
wocky_porter_send_async, function in WockyPorter
wocky_porter_send_finish, function in WockyPorter
wocky_porter_send_iq_async, function in WockyPorter
wocky_porter_send_iq_error, function in WockyPorter
wocky_porter_send_iq_finish, function in WockyPorter
wocky_porter_send_iq_gerror, function in WockyPorter
wocky_porter_start, function in WockyPorter
wocky_porter_unregister_handler, function in WockyPorter
WockyPubsubAffiliationState, enum in WockyPubsubNode
WockyPubsubNode, struct in wocky-types
WockyPubsubNode::deleted, object signal in wocky-types
WockyPubsubNode::event-received, object signal in wocky-types
WockyPubsubNode::subscription-state-changed, object signal in wocky-types
WockyPubsubNode:name, object property in wocky-types
WockyPubsubNode:service, object property in wocky-types
WockyPubsubNodeClass, struct in WockyPubsubNode
WockyPubsubNodeEventHandler, user_function in wocky-pubsub-node-internal
WockyPubsubNodeEventMapping, struct in wocky-pubsub-node-internal
WockyPubsubService::event-received, object signal in WockyPubsubService
WockyPubsubService::node-deleted, object signal in WockyPubsubService
WockyPubsubService::subscription-state-changed, object signal in WockyPubsubService
WockyPubsubService:jid, object property in WockyPubsubService
WockyPubsubService:session, object property in WockyPubsubService
WockyPubsubServiceError, enum in WockyPubsubService
WockyPubsubSubscriptionState, enum in WockyPubsubService
wocky_pubsub_affiliation_copy, function in WockyPubsubNode
wocky_pubsub_affiliation_free, function in WockyPubsubNode
wocky_pubsub_affiliation_list_copy, function in WockyPubsubNode
wocky_pubsub_affiliation_list_free, function in WockyPubsubNode
wocky_pubsub_affiliation_new, function in WockyPubsubNode
wocky_pubsub_distill_ambivalent_iq_reply, function in wocky-pubsub-helpers
wocky_pubsub_distill_iq_reply, function in wocky-pubsub-helpers
wocky_pubsub_distill_stanza, function in wocky-pubsub-helpers
wocky_pubsub_distill_void_iq_reply, function in wocky-pubsub-helpers
wocky_pubsub_make_event_stanza, function in wocky-pubsub-helpers
wocky_pubsub_make_publish_stanza, function in wocky-pubsub-helpers
wocky_pubsub_make_stanza, function in wocky-pubsub-helpers
wocky_pubsub_node_delete_async, function in WockyPubsubNode
wocky_pubsub_node_delete_finish, function in WockyPubsubNode
wocky_pubsub_node_get_configuration_async, function in WockyPubsubNode
wocky_pubsub_node_get_configuration_finish, function in WockyPubsubNode
wocky_pubsub_node_get_name, function in WockyPubsubNode
wocky_pubsub_node_get_porter, function in wocky-pubsub-node-protected
wocky_pubsub_node_list_affiliates_async, function in WockyPubsubNode
wocky_pubsub_node_list_affiliates_finish, function in WockyPubsubNode
wocky_pubsub_node_list_subscribers_async, function in WockyPubsubNode
wocky_pubsub_node_list_subscribers_finish, function in WockyPubsubNode
wocky_pubsub_node_make_delete_stanza, function in wocky-pubsub-node-protected
wocky_pubsub_node_make_get_configuration_stanza, function in wocky-pubsub-node-protected
wocky_pubsub_node_make_list_affiliates_stanza, function in wocky-pubsub-node-protected
wocky_pubsub_node_make_list_subscribers_stanza, function in wocky-pubsub-node-protected
wocky_pubsub_node_make_modify_affiliates_stanza, function in wocky-pubsub-node-protected
wocky_pubsub_node_make_publish_stanza, function in WockyPubsubNode
wocky_pubsub_node_make_subscribe_stanza, function in wocky-pubsub-node-protected
wocky_pubsub_node_make_unsubscribe_stanza, function in wocky-pubsub-node-protected
wocky_pubsub_node_modify_affiliates_async, function in WockyPubsubNode
wocky_pubsub_node_modify_affiliates_finish, function in WockyPubsubNode
wocky_pubsub_node_parse_affiliations, function in wocky-pubsub-node-protected
wocky_pubsub_node_subscribe_async, function in WockyPubsubNode
wocky_pubsub_node_subscribe_finish, function in WockyPubsubNode
wocky_pubsub_node_unsubscribe_async, function in WockyPubsubNode
wocky_pubsub_node_unsubscribe_finish, function in WockyPubsubNode
wocky_pubsub_service_create_create_node_stanza, function in wocky-pubsub-service-protected
wocky_pubsub_service_create_node_async, function in WockyPubsubService
wocky_pubsub_service_create_node_finish, function in WockyPubsubService
wocky_pubsub_service_create_retrieve_subscriptions_stanza, function in wocky-pubsub-service-protected
wocky_pubsub_service_ensure_node, function in WockyPubsubService
WOCKY_PUBSUB_SERVICE_ERROR, macro in WockyPubsubService
wocky_pubsub_service_error_quark, function in WockyPubsubService
wocky_pubsub_service_get_default_node_configuration_async, function in WockyPubsubService
wocky_pubsub_service_get_default_node_configuration_finish, function in WockyPubsubService
wocky_pubsub_service_get_porter, function in wocky-pubsub-service-protected
wocky_pubsub_service_handle_create_node_reply, function in wocky-pubsub-service-protected
wocky_pubsub_service_lookup_node, function in WockyPubsubService
wocky_pubsub_service_new, function in WockyPubsubService
wocky_pubsub_service_parse_subscription, function in wocky-pubsub-service-protected
wocky_pubsub_service_parse_subscriptions, function in wocky-pubsub-service-protected
wocky_pubsub_service_retrieve_subscriptions_async, function in WockyPubsubService
wocky_pubsub_service_retrieve_subscriptions_finish, function in WockyPubsubService
wocky_pubsub_subscription_copy, function in WockyPubsubService
wocky_pubsub_subscription_free, function in WockyPubsubService
wocky_pubsub_subscription_list_copy, function in WockyPubsubService
wocky_pubsub_subscription_list_free, function in WockyPubsubService
wocky_pubsub_subscription_new, function in WockyPubsubService

Q

WOCKY_QUIRK_ANDROID_GTALK_CLIENT, macro in wocky-namespaces
WOCKY_QUIRK_GOOGLE_WEBMAIL_CLIENT, macro in wocky-namespaces
WOCKY_QUIRK_OMITS_CONTENT_CREATORS, macro in wocky-namespaces

R

WockyResourceContact, struct in wocky-types
WockyResourceContact:bare-contact, object property in wocky-types
WockyResourceContact:resource, object property in wocky-types
WockyResourceContactClass, struct in WockyResourceContact
wocky_resource_contact_equal, function in WockyResourceContact
wocky_resource_contact_get_bare_contact, function in WockyResourceContact
wocky_resource_contact_get_resource, function in WockyResourceContact
wocky_resource_contact_new, function in WockyResourceContact
WockyRoster::added, object signal in WockyRoster
WockyRoster::removed, object signal in WockyRoster
WockyRoster:session, object property in WockyRoster
WockyRosterClass, struct in WockyRoster
WockyRosterError, enum in WockyRoster
WockyRosterSubscriptionFlags, enum in WockyRoster
wocky_roster_add_contact_async, function in WockyRoster
wocky_roster_add_contact_finish, function in WockyRoster
wocky_roster_change_contact_name_async, function in WockyRoster
wocky_roster_change_contact_name_finish, function in WockyRoster
wocky_roster_contact_add_group_async, function in WockyRoster
wocky_roster_contact_add_group_finish, function in WockyRoster
wocky_roster_contact_remove_group_async, function in WockyRoster
wocky_roster_contact_remove_group_finish, function in WockyRoster
WOCKY_ROSTER_ERROR, macro in WockyRoster
wocky_roster_error_quark, function in WockyRoster
wocky_roster_fetch_roster_async, function in WockyRoster
wocky_roster_fetch_roster_finish, function in WockyRoster
wocky_roster_get_all_contacts, function in WockyRoster
wocky_roster_get_contact, function in WockyRoster
wocky_roster_new, function in WockyRoster
wocky_roster_remove_contact_async, function in WockyRoster
wocky_roster_remove_contact_finish, function in WockyRoster
wocky_roster_subscription_to_string, function in WockyRoster

S

WockySaslAuth:auth-registry, object property in WockySaslAuth
WockySaslAuth:connection, object property in WockySaslAuth
WockySaslAuth:password, object property in WockySaslAuth
WockySaslAuth:server, object property in WockySaslAuth
WockySaslAuth:username, object property in WockySaslAuth
WockySaslAuthClass, struct in WockySaslAuth
wocky_sasl_auth_add_handler, function in WockySaslAuth
wocky_sasl_auth_authenticate_async, function in WockySaslAuth
wocky_sasl_auth_authenticate_finish, function in WockySaslAuth
wocky_sasl_auth_new, function in WockySaslAuth
sasl_calculate_hmac_sha1, function in wocky-sasl-utils
wocky_sasl_digest_md5_new, function in wocky-sasl-digest-md5
sasl_generate_base64_nonce, function in wocky-sasl-utils
wocky_sasl_plain_new, function in wocky-sasl-plain
wocky_sasl_scram_new, function in wocky-sasl-scram
wocky_send_ll_pep_event, function in wocky-pubsub-helpers
WockySession, struct in wocky-types
WockySession:connection, object property in wocky-types
WockySession:contact-factory, object property in wocky-types
WockySession:full-jid, object property in wocky-types
WockySession:porter, object property in wocky-types
WockySessionClass, struct in WockySession
wocky_session_get_contact_factory, function in WockySession
wocky_session_get_jid, function in WockySession
wocky_session_get_porter, function in WockySession
wocky_session_new_ll, function in WockySession
wocky_session_new_with_connection, function in WockySession
wocky_session_set_jid, function in WockySession
wocky_session_start, function in WockySession
WOCKY_SHA1_BLOCK_SIZE, macro in wocky-sasl-utils
WOCKY_SHA1_DIGEST_SIZE, macro in wocky-sasl-utils
WockySIError, enum in wocky-xmpp-error
WOCKY_SI_ERROR, macro in wocky-xmpp-error
wocky_si_error_quark, function in wocky-xmpp-error
WockyStanzaClass, struct in WockyStanza
WockyStanzaSubType, enum in WockyStanza
WockyStanzaType, enum in WockyStanza
wocky_stanza_build, function in WockyStanza
wocky_stanza_build_iq_error, function in WockyStanza
wocky_stanza_build_iq_error_va, function in WockyStanza
wocky_stanza_build_iq_result, function in WockyStanza
wocky_stanza_build_iq_result_va, function in WockyStanza
wocky_stanza_build_to_contact, function in WockyStanza
wocky_stanza_build_va, function in WockyStanza
wocky_stanza_copy, function in WockyStanza
wocky_stanza_error_to_node, function in wocky-xmpp-error
wocky_stanza_extract_errors, function in WockyStanza
wocky_stanza_extract_stream_error, function in WockyStanza
wocky_stanza_get_from, function in WockyStanza
wocky_stanza_get_from_contact, function in WockyStanza
wocky_stanza_get_to, function in WockyStanza
wocky_stanza_get_top_node, function in WockyStanza
wocky_stanza_get_to_contact, function in WockyStanza
wocky_stanza_get_type_info, function in WockyStanza
wocky_stanza_has_type, function in WockyStanza
wocky_stanza_new, function in WockyStanza
wocky_stanza_set_from_contact, function in WockyStanza
wocky_stanza_set_to_contact, function in WockyStanza
wocky_strdiff, function in wocky-utils
WockyStunServer, struct in WockyJingleInfo
WockyStunServerSource, enum in wocky-jingle-info-internal

T

WOCKY_TELEPATHY_NS_CAPS, macro in wocky-namespaces
WOCKY_TELEPATHY_NS_CLIQUE, macro in wocky-namespaces
WOCKY_TELEPATHY_NS_OLPC_ACTIVITY_PROPS, macro in wocky-namespaces
WOCKY_TELEPATHY_NS_TUBES, macro in wocky-namespaces
WockyTLSCertStatus, enum in Wocky OpenSSL TLS
WockyTLSCertType, enum in Wocky OpenSSL TLS
WockyTLSConnection:session, object property in Wocky OpenSSL TLS
WockyTLSConnector:tls-handler, object property in WockyTLSConnector
WockyTLSConnectorClass, struct in WockyTLSConnector
WockyTLSHandler:ignore-ssl-errors, object property in WockyTLSHandler
WockyTLSHandlerClass, struct in WockyTLSHandler
WockyTLSHandlerVerifyAsyncFunc, user_function in WockyTLSHandler
WockyTLSHandlerVerifyFinishFunc, user_function in WockyTLSHandler
WockyTLSSession:base-stream, object property in Wocky OpenSSL TLS
WockyTLSSession:dh-bits, object property in Wocky OpenSSL TLS
WockyTLSSession:server, object property in Wocky OpenSSL TLS
WockyTLSSession:x509-cert, object property in Wocky OpenSSL TLS
WockyTLSSession:x509-key, object property in Wocky OpenSSL TLS
WockyTLSVerificationLevel, enum in Wocky OpenSSL TLS
WOCKY_TLS_CERT_ERROR, macro in Wocky OpenSSL TLS
wocky_tls_cert_error_quark, function in Wocky OpenSSL TLS
wocky_tls_connector_new, function in WockyTLSConnector
wocky_tls_connector_secure_async, function in WockyTLSConnector
wocky_tls_connector_secure_finish, function in WockyTLSConnector
WOCKY_TLS_ERROR, macro in Wocky OpenSSL TLS
wocky_tls_error_quark, function in Wocky OpenSSL TLS
wocky_tls_handler_add_ca, function in WockyTLSHandler
wocky_tls_handler_add_crl, function in WockyTLSHandler
wocky_tls_handler_forget_cas, function in WockyTLSHandler
wocky_tls_handler_get_cas, function in WockyTLSHandler
wocky_tls_handler_get_crl, function in WockyTLSHandler
wocky_tls_handler_new, function in WockyTLSHandler
wocky_tls_handler_verify_async, function in WockyTLSHandler
wocky_tls_handler_verify_finish, function in WockyTLSHandler
wocky_tls_session_add_ca, function in Wocky OpenSSL TLS
wocky_tls_session_add_crl, function in Wocky OpenSSL TLS
wocky_tls_session_get_peers_certificate, function in Wocky OpenSSL TLS
wocky_tls_session_handshake, function in Wocky OpenSSL TLS
wocky_tls_session_handshake_async, function in Wocky OpenSSL TLS
wocky_tls_session_handshake_finish, function in Wocky OpenSSL TLS
wocky_tls_session_new, function in Wocky OpenSSL TLS
wocky_tls_session_server_new, function in Wocky OpenSSL TLS
wocky_tls_session_verify_peer, function in Wocky OpenSSL TLS

W

WOCKY_W3C_NS_XHTML, macro in wocky-namespaces

X

WockyXep0115Capabilities::capabilities-changed, object signal in WockyXep0115Capabilities
WockyXep0115CapabilitiesGetDataFormsFunc, user_function in WockyXep0115Capabilities
WockyXep0115CapabilitiesHasFeatureFunc, user_function in WockyXep0115Capabilities
WockyXep0115CapabilitiesInterface, struct in WockyXep0115Capabilities
WOCKY_XEP77_NS_REGISTER, macro in wocky-namespaces
wocky_xep_0115_capabilities_get_data_forms, function in WockyXep0115Capabilities
wocky_xep_0115_capabilities_has_feature, function in WockyXep0115Capabilities
WockyXmppConnection:base-stream, object property in WockyXmppConnection
WockyXmppConnectionClass, struct in WockyXmppConnection
WockyXmppConnectionError, enum in WockyXmppConnection
WockyXmppError, enum in wocky-xmpp-error
WockyXmppErrorDomain, struct in wocky-xmpp-error
WockyXmppErrorSpecialization, struct in wocky-xmpp-error
WockyXmppErrorType, enum in wocky-xmpp-error
WockyXmppReader:default-namespace, object property in WockyXmppReader
WockyXmppReader:from, object property in WockyXmppReader
WockyXmppReader:id, object property in WockyXmppReader
WockyXmppReader:lang, object property in WockyXmppReader
WockyXmppReader:streaming-mode, object property in WockyXmppReader
WockyXmppReader:to, object property in WockyXmppReader
WockyXmppReader:version, object property in WockyXmppReader
WockyXmppReaderClass, struct in WockyXmppReader
WockyXmppReaderError, enum in WockyXmppReader
WockyXmppReaderState, enum in WockyXmppReader
WockyXmppStreamError, enum in wocky-xmpp-error
WockyXmppWriter:streaming-mode, object property in WockyXmppWriter
WockyXmppWriterClass, struct in WockyXmppWriter
WOCKY_XMPP_CONNECTION_ERROR, macro in WockyXmppConnection
wocky_xmpp_connection_error_quark, function in WockyXmppConnection
wocky_xmpp_connection_force_close_async, function in WockyXmppConnection
wocky_xmpp_connection_force_close_finish, function in WockyXmppConnection
wocky_xmpp_connection_new, function in WockyXmppConnection
wocky_xmpp_connection_new_id, function in WockyXmppConnection
wocky_xmpp_connection_recv_open_async, function in WockyXmppConnection
wocky_xmpp_connection_recv_open_finish, function in WockyXmppConnection
wocky_xmpp_connection_recv_stanza_async, function in WockyXmppConnection
wocky_xmpp_connection_recv_stanza_finish, function in WockyXmppConnection
wocky_xmpp_connection_reset, function in WockyXmppConnection
wocky_xmpp_connection_send_close_async, function in WockyXmppConnection
wocky_xmpp_connection_send_close_finish, function in WockyXmppConnection
wocky_xmpp_connection_send_open_async, function in WockyXmppConnection
wocky_xmpp_connection_send_open_finish, function in WockyXmppConnection
wocky_xmpp_connection_send_stanza_async, function in WockyXmppConnection
wocky_xmpp_connection_send_stanza_finish, function in WockyXmppConnection
wocky_xmpp_connection_send_whitespace_ping_async, function in WockyXmppConnection
wocky_xmpp_connection_send_whitespace_ping_finish, function in WockyXmppConnection
WOCKY_XMPP_ERROR, macro in wocky-xmpp-error
wocky_xmpp_error_deinit, function in wocky-xmpp-error
wocky_xmpp_error_description, function in wocky-xmpp-error
wocky_xmpp_error_extract, function in wocky-xmpp-error
wocky_xmpp_error_init, function in wocky-xmpp-error
wocky_xmpp_error_quark, function in wocky-xmpp-error
wocky_xmpp_error_register_domain, function in wocky-xmpp-error
wocky_xmpp_error_string, function in wocky-xmpp-error
WOCKY_XMPP_NS_AMP, macro in wocky-namespaces
WOCKY_XMPP_NS_BIND, macro in wocky-namespaces
WOCKY_XMPP_NS_DATA, macro in wocky-namespaces
WOCKY_XMPP_NS_DELAY, macro in wocky-namespaces
WOCKY_XMPP_NS_EVENT, macro in wocky-namespaces
WOCKY_XMPP_NS_FEATURENEG, macro in wocky-namespaces
WOCKY_XMPP_NS_GOOGLE_JINGLE_INFO, macro in wocky-namespaces
WOCKY_XMPP_NS_GOOGLE_SESSION, macro in wocky-namespaces
WOCKY_XMPP_NS_GOOGLE_SESSION_PHONE, macro in wocky-namespaces
WOCKY_XMPP_NS_GOOGLE_SESSION_SHARE, macro in wocky-namespaces
WOCKY_XMPP_NS_GOOGLE_SESSION_VIDEO, macro in wocky-namespaces
WOCKY_XMPP_NS_GOOGLE_TRANSPORT_P2P, macro in wocky-namespaces
WOCKY_XMPP_NS_IBB, macro in wocky-namespaces
WOCKY_XMPP_NS_IQ_OOB, macro in wocky-namespaces
WOCKY_XMPP_NS_JABBER_CLIENT, macro in wocky-namespaces
WOCKY_XMPP_NS_JINGLE, macro in wocky-namespaces
WOCKY_XMPP_NS_JINGLE015, macro in wocky-namespaces
WOCKY_XMPP_NS_JINGLE_DESCRIPTION_AUDIO, macro in wocky-namespaces
WOCKY_XMPP_NS_JINGLE_DESCRIPTION_VIDEO, macro in wocky-namespaces
WOCKY_XMPP_NS_JINGLE_ERRORS, macro in wocky-namespaces
WOCKY_XMPP_NS_JINGLE_RTCP_FB, macro in wocky-namespaces
WOCKY_XMPP_NS_JINGLE_RTP, macro in wocky-namespaces
WOCKY_XMPP_NS_JINGLE_RTP_AUDIO, macro in wocky-namespaces
WOCKY_XMPP_NS_JINGLE_RTP_ERRORS, macro in wocky-namespaces
WOCKY_XMPP_NS_JINGLE_RTP_HDREXT, macro in wocky-namespaces
WOCKY_XMPP_NS_JINGLE_RTP_INFO, macro in wocky-namespaces
WOCKY_XMPP_NS_JINGLE_RTP_VIDEO, macro in wocky-namespaces
WOCKY_XMPP_NS_JINGLE_TRANSPORT_ICEUDP, macro in wocky-namespaces
WOCKY_XMPP_NS_JINGLE_TRANSPORT_RAWUDP, macro in wocky-namespaces
WOCKY_XMPP_NS_PING, macro in wocky-namespaces
WOCKY_XMPP_NS_PUBSUB, macro in wocky-namespaces
WOCKY_XMPP_NS_PUBSUB_ERRORS, macro in wocky-namespaces
WOCKY_XMPP_NS_PUBSUB_EVENT, macro in wocky-namespaces
WOCKY_XMPP_NS_PUBSUB_NODE_CONFIG, macro in wocky-namespaces
WOCKY_XMPP_NS_PUBSUB_OWNER, macro in wocky-namespaces
WOCKY_XMPP_NS_ROSTER, macro in wocky-namespaces
WOCKY_XMPP_NS_SASL_AUTH, macro in wocky-namespaces
WOCKY_XMPP_NS_SESSION, macro in wocky-namespaces
WOCKY_XMPP_NS_SI, macro in wocky-namespaces
WOCKY_XMPP_NS_STANZAS, macro in wocky-namespaces
WOCKY_XMPP_NS_STREAM, macro in wocky-namespaces
WOCKY_XMPP_NS_STREAMS, macro in wocky-namespaces
WOCKY_XMPP_NS_TLS, macro in wocky-namespaces
WOCKY_XMPP_NS_XHTML_IM, macro in wocky-namespaces
WOCKY_XMPP_NS_X_OOB, macro in wocky-namespaces
WOCKY_XMPP_READER_ERROR, macro in WockyXmppReader
wocky_xmpp_reader_error_quark, function in WockyXmppReader
wocky_xmpp_reader_get_error, function in WockyXmppReader
wocky_xmpp_reader_get_state, function in WockyXmppReader
wocky_xmpp_reader_new, function in WockyXmppReader
wocky_xmpp_reader_new_no_stream, function in WockyXmppReader
wocky_xmpp_reader_new_no_stream_ns, function in WockyXmppReader
wocky_xmpp_reader_peek_stanza, function in WockyXmppReader
wocky_xmpp_reader_pop_stanza, function in WockyXmppReader
wocky_xmpp_reader_push, function in WockyXmppReader
wocky_xmpp_reader_reset, function in WockyXmppReader
wocky_xmpp_stanza_error_to_string, function in wocky-xmpp-error
WOCKY_XMPP_STREAM_ERROR, macro in wocky-xmpp-error
wocky_xmpp_stream_error_from_node, function in wocky-xmpp-error
wocky_xmpp_stream_error_quark, function in wocky-xmpp-error
wocky_xmpp_writer_flush, function in WockyXmppWriter
wocky_xmpp_writer_new, function in WockyXmppWriter
wocky_xmpp_writer_new_no_stream, function in WockyXmppWriter
wocky_xmpp_writer_stream_close, function in WockyXmppWriter
wocky_xmpp_writer_stream_open, function in WockyXmppWriter
wocky_xmpp_writer_write_node_tree, function in WockyXmppWriter
wocky_xmpp_writer_write_stanza, function in WockyXmppWriter
telepathy-gabble-0.18.2/lib/ext/wocky/docs/reference/html/ch01.html0000644000175000017500000002421012312537050024767 0ustar00smcvsmcv00000000000000 Wocky Reference Manual: API Reference

API Reference

WockyAuthHandler
WockyAuthRegistry
WockyBareContact — Wrapper around a roster item.
WockyC2SPorterWrapper around a WockyXmppConnection providing a higher level API.
WockyCapsCache
WockyCapsHash — Utilities for computing verification string hash
WockyConnector — Low-level XMPP connection generator.
WockyContactFactorycreates and looks up WockyContact objects
WockyContact
WockyDataForm — An object to represent an XMPP data form
wocky-debug
wocky-enumtypes
wocky
wocky-heartbeat-source
wocky-http-proxy
WockyDiscoIdentity — Structure holding XMPP disco identity information.
WockyJabberAuth
wocky-jabber-auth-digest
wocky-jabber-auth-password
WockyMetaPorter
WockyMuc — multi-user chat rooms
wocky-namespaces
WockyNode — representation of a XMPP node
WockyNodeTree
WockyPepService — Object to represent a single PEP service
WockyPing — support for pings/keepalives
WockyPorter
wocky-pubsub-helpers
WockyPubsubNode
wocky-pubsub-node-protected
WockyPubsubService
wocky-pubsub-service-protected
WockyResourceContact
WockyRoster — TODO
WockySaslAuth
wocky-sasl-digest-md5
wocky-sasl-utils
wocky-sasl-plain
wocky-sasl-scram
WockySession
WockyStanza
WockyTLSConnector
Wocky OpenSSL TLS — Establish TLS sessions
WockyTLSHandler
wocky-utils
WockyXmppConnection — Low-level XMPP connection.
wocky-xmpp-error
WockyXmppReader — Xmpp XML to stanza deserializer
WockyXmppWriter — Xmpp stanza to XML serializer
WockyJingleContent
WockyJingleFactory
wocky-jingle-info-internal
WockyJingleInfo
WockyJingleMediaRtp
WockyJingleSession
WockyJingleTransportGoogle
WockyJingleTransportIceUdp
WockyJingleTransportIface
WockyJingleTransportRawUdp
wocky-jingle-types
telepathy-gabble-0.18.2/lib/ext/wocky/docs/reference/html/index.html0000644000175000017500000002354312312537050025353 0ustar00smcvsmcv00000000000000 Wocky Reference Manual: Wocky Reference Manual

API Reference
WockyAuthHandler
WockyAuthRegistry
WockyBareContact — Wrapper around a roster item.
WockyC2SPorterWrapper around a WockyXmppConnection providing a higher level API.
WockyCapsCache
WockyCapsHash — Utilities for computing verification string hash
WockyConnector — Low-level XMPP connection generator.
WockyContactFactorycreates and looks up WockyContact objects
WockyContact
WockyDataForm — An object to represent an XMPP data form
wocky-debug
wocky-enumtypes
wocky
wocky-heartbeat-source
wocky-http-proxy
WockyDiscoIdentity — Structure holding XMPP disco identity information.
WockyJabberAuth
wocky-jabber-auth-digest
wocky-jabber-auth-password
WockyMetaPorter
WockyMuc — multi-user chat rooms
wocky-namespaces
WockyNode — representation of a XMPP node
WockyNodeTree
WockyPepService — Object to represent a single PEP service
WockyPing — support for pings/keepalives
WockyPorter
wocky-pubsub-helpers
WockyPubsubNode
wocky-pubsub-node-protected
WockyPubsubService
wocky-pubsub-service-protected
WockyResourceContact
WockyRoster — TODO
WockySaslAuth
wocky-sasl-digest-md5
wocky-sasl-utils
wocky-sasl-plain
wocky-sasl-scram
WockySession
WockyStanza
WockyTLSConnector
Wocky OpenSSL TLS — Establish TLS sessions
WockyTLSHandler
wocky-utils
WockyXmppConnection — Low-level XMPP connection.
wocky-xmpp-error
WockyXmppReader — Xmpp XML to stanza deserializer
WockyXmppWriter — Xmpp stanza to XML serializer
WockyJingleContent
WockyJingleFactory
wocky-jingle-info-internal
WockyJingleInfo
WockyJingleMediaRtp
WockyJingleSession
WockyJingleTransportGoogle
WockyJingleTransportIceUdp
WockyJingleTransportIface
WockyJingleTransportRawUdp
wocky-jingle-types
Object Hierarchy
API Index
Annotation Glossary
telepathy-gabble-0.18.2/lib/ext/wocky/docs/reference/html/object-tree.html0000644000175000017500000003036212312537050026444 0ustar00smcvsmcv00000000000000 Wocky Reference Manual: Object Hierarchy

Object Hierarchy

    GObject
    ├── WockyAuthRegistry
    ├── WockyContact
       ├── WockyBareContact
       ├── WockyLLContact
       ╰── WockyResourceContact
    ├── WockyC2SPorter
    ├── WockyCapsCache
    ├── WockyConnector
    ├── WockyContactFactory
    ├── WockyDataForm
    ├── WockyJabberAuth
    ├── WockyJingleContent
       ╰── WockyJingleMediaRtp
    ├── WockyJingleFactory
    ├── WockyJingleInfo
    ├── WockyJingleSession
    ├── WockyJingleTransportGoogle
    ├── WockyJingleTransportIceUdp
    ├── WockyJingleTransportRawUdp
    ├── WockyLLConnectionFactory
    ├── WockyLLConnector
    ├── GIOStream
       ├── WockyLoopbackStream
       ╰── WockyTLSConnection
    ├── WockyMetaPorter
    ├── WockyMuc
    ├── WockyNodeTree
       ╰── WockyStanza
    ├── WockyPepService
    ├── WockyPing
    ├── WockyPubsubNode
    ├── WockyPubsubService
    ├── WockyRoster
    ├── WockySaslAuth
    ├── WockySession
    ├── WockyTLSConnector
    ├── WockyTLSHandler
    ├── WockyTLSSession
    ├── WockyXmppConnection
    ├── WockyXmppReader
    ├── WockyXmppWriter
    ╰── WockyHttpProxy
    GInterface
    ├── WockyAuthHandler
    ├── WockyPorter
    ├── WockyJingleTransportIface
    ╰── WockyXep0115Capabilities
    GEnum
    ├── WockyAuthError
    ├── WockyConnectorError
    ├── WockyDataFormError
    ├── WockyDataFormFieldType
    ├── WockyJingleError
    ├── WockyJingleReason
    ├── WockyMucAffiliation
    ├── WockyMucMsgState
    ├── WockyMucMsgType
    ├── WockyMucRole
    ├── WockyMucState
    ├── WockyPubsubAffiliationState
    ├── WockyPubsubServiceError
    ├── WockyPubsubSubscriptionState
    ├── WockySIError
    ├── WockyStunServerSource
    ├── WockyTLSCertStatus
    ├── WockyTLSCertType
    ├── WockyTLSVerificationLevel
    ├── WockyXmppError
    ├── WockyXmppErrorType
    ├── WockyXmppReaderError
    ├── WockyXmppReaderState
    ╰── WockyXmppStreamError
    GBoxed
    ├── WockyDiscoIdentity
    ├── WockyPubsubAffiliation
    ╰── WockyPubsubSubscription
    GFlags
    ├── WockyMucFeature
    ╰── WockyMucStatusCode
telepathy-gabble-0.18.2/lib/ext/wocky/docs/reference/html/WockyAuthHandler.html0000644000175000017500000007224212312537050027460 0ustar00smcvsmcv00000000000000 Wocky Reference Manual: WockyAuthHandler

WockyAuthHandler

WockyAuthHandler

Types and Values

Object Hierarchy

    GInterface
    ╰── WockyAuthHandler

Description

Functions

WockyAuthInitialResponseFunc ()

gboolean
(*WockyAuthInitialResponseFunc) (WockyAuthHandler *handler,
                                 GString **initial_data,
                                 GError **error);

Called when authentication begins, if the mechanism allows a response to an implicit challenge during AUTH initiation (which, in XMPP, corresponds to sending the <auth/> stanza to the server).

The function should return TRUE on success, and optionally set the initial_data to a string if there is initial data to send. On error, it should return FALSE and set error .

Parameters

handler

a WockyAuthHandler object

 

initial_data

a GString location to fill with the initial data.

[out]

error

an optional location for a GError to fill, or NULL

 

Returns

TRUE on success, otherwise FALSE


WockyAuthAuthDataFunc ()

gboolean
(*WockyAuthAuthDataFunc) (WockyAuthHandler *handler,
                          const GString *data,
                          GString **response,
                          GError **error);

WockyAuthSuccessFunc ()

gboolean
(*WockyAuthSuccessFunc) (WockyAuthHandler *handler,
                         GError **error);

Called when a <success/> stanza is received during authentication. If no error is returned, then authentication is considered finished. (Typically, an error is only raised if the

<success/> stanza was received earlier than

expected)

Parameters

handler

a WockyAuthHandler object

 

error

an optional location for a GError to fill, or NULL

 

Returns

TRUE on success, otherwise FALSE


wocky_auth_handler_get_mechanism ()

const gchar *
wocky_auth_handler_get_mechanism (WockyAuthHandler *handler);

Returns the name of the SASL mechanism handler implements.

Parameters

handler

a handler for a SASL mechanism.

 

Returns

the name of the SASL mechanism handler implements.


wocky_auth_handler_is_plain ()

gboolean
wocky_auth_handler_is_plain (WockyAuthHandler *handler);

Checks whether handler sends secrets in plaintext. This may be used to decide whether to use handler on an insecure XMPP connection.

Parameters

handler

a handler for a SASL mechanism.

 

Returns

TRUE if handler sends secrets in plaintext.


wocky_auth_handler_get_initial_response ()

gboolean
wocky_auth_handler_get_initial_response
                               (WockyAuthHandler *handler,
                                GString **initial_data,
                                GError **error);

Called when authentication begins to fetch the initial data to send to the server in the <auth/> stanza.

If this function returns TRUE, initial_data will be non-NULL if handler provides an initial response, and NULL otherwise.

Parameters

handler

a handler for a SASL mechanism

 

initial_data

initial data to send to the server, if any.

[out][transfer full]

error

an optional location for a GError to fill, or NULL

 

Returns

TRUE on success; FALSE otherwise.


wocky_auth_handler_handle_auth_data ()

gboolean
wocky_auth_handler_handle_auth_data (WockyAuthHandler *handler,
                                     const GString *data,
                                     GString **response,
                                     GError **error);

Asks handler to respond to a <challenge/> stanza or a

<success/> with data. On success, handler will put

response data into response , Base64-encoding it if appropriate.

Parameters

handler

a WockyAuthHandler object

 

data

the challenge string

 

response

a location to fill with a challenge response in a GString.

[out][transfer full]

error

an optional location for a GError to fill, or NULL

 

Returns

TRUE on success, otherwise FALSE


wocky_auth_handler_handle_success ()

gboolean
wocky_auth_handler_handle_success (WockyAuthHandler *handler,
                                   GError **error);

Called when a <success/> stanza is received during authentication. If no error is returned, then authentication is considered finished. (Typically, an error is only raised if the

<success/> stanza was received earlier than

expected)

Parameters

handler

a WockyAuthHandler object

 

error

an optional location for a GError to fill, or NULL

 

Returns

TRUE on success, otherwise FALSE

Types and Values

struct WockyAuthHandlerIface

struct WockyAuthHandlerIface {
    GTypeInterface parent;
    gchar *mechanism;
    gboolean plain;
    WockyAuthInitialResponseFunc initial_response_func;
    WockyAuthAuthDataFunc auth_data_func;
    WockyAuthSuccessFunc success_func;
};

Members

GTypeInterface parent;

The parent interface.

 

gchar *mechanism;

The AUTH mechanism which this handler responds to challenges for.

 

gboolean plain;

Whether the mechanism this handler handles sends secrets in plaintext.

 

WockyAuthInitialResponseFunc initial_response_func;

Called when the initial <auth /> stanza is generated

 

WockyAuthAuthDataFunc auth_data_func;

Called when any authentication data from the server is received

 

WockyAuthSuccessFunc success_func;

Called when a <success/> stanza is received.

 
telepathy-gabble-0.18.2/lib/ext/wocky/docs/reference/html/WockyAuthRegistry.html0000644000175000017500000017270612312537050027721 0ustar00smcvsmcv00000000000000 Wocky Reference Manual: WockyAuthRegistry

WockyAuthRegistry

WockyAuthRegistry

Object Hierarchy

    GEnum
    ╰── WockyAuthError
    GObject
    ╰── WockyAuthRegistry

Description

Functions

wocky_auth_error_quark ()

GQuark
wocky_auth_error_quark (void);

WockyAuthRegistryStartAuthAsyncFunc ()

void
(*WockyAuthRegistryStartAuthAsyncFunc)
                               (WockyAuthRegistry *self,
                                GSList *mechanisms,
                                gboolean allow_plain,
                                gboolean is_secure_channel,
                                const gchar *username,
                                const gchar *password,
                                const gchar *server,
                                const gchar *session_id,
                                GAsyncReadyCallback callback,
                                gpointer user_data);

Starts a async authentication: chooses mechanism and gets initial data. The default function chooses a WockyAuthHandler by which mechanism it supports and gets the initial data from the chosen handler.

Parameters

self

a WockyAuthRegistry object

 

mechanisms

a list of avahilable mechanisms

 

allow_plain

TRUE if PLAIN is allowed, otherwise FALSE

 

is_secure_channel

TRUE if channel is secure, otherwise FALSE

 

username

the username

 

password

the password

 

server

the server

 

session_id

the session ID

 

callback

a callback to be called when finished

 

user_data

data to pass to callback

 

WockyAuthRegistryStartAuthFinishFunc ()

gboolean
(*WockyAuthRegistryStartAuthFinishFunc)
                               (WockyAuthRegistry *self,
                                GAsyncResult *result,
                                WockyAuthRegistryStartData **start_data,
                                GError **error);

Called to finish the GAsyncResult task for authentication start. By default, it extracts a WockyAuthRegistryStartData pointer from a given GSimpleAsyncResult and copies it to the out param.

Parameters

self

a WockyAuthRegistry object

 

result

a GAsyncResult object

 

start_data

a location to fill with a WockyAuthRegistryStartData structure

 

error

a location to fill with a GError if an error is hit, or NULL

 

Returns

TRUE on success, otherwise FALSE


WockyAuthRegistryChallengeAsyncFunc ()

void
(*WockyAuthRegistryChallengeAsyncFunc)
                               (WockyAuthRegistry *self,
                                const GString *challenge_data,
                                GAsyncReadyCallback callback,
                                gpointer user_data);

Recieves a challenge and asynchronously provides a reply. By default the challenge is passed on to the chosen WockyAuthHandler.

Parameters

self

a WockyAuthRegistry object

 

challenge_data

the challenge data string

 

callback

a callback to call when finished

 

user_data

data to pass to callback

 

WockyAuthRegistryChallengeFinishFunc ()

gboolean
(*WockyAuthRegistryChallengeFinishFunc)
                               (WockyAuthRegistry *self,
                                GAsyncResult *result,
                                GString **response,
                                GError **error);

Finishes a GAsyncResult from WockyAuthRegistryChallengeAsyncFunc. By default it extracts a GString response from the given GSimpleAsyncResult and copies it to the out param.

Parameters

self

a WockyAuthRegistry object

 

result

a GAsyncResult object

 

response

a location to be filled with the response string

 

error

a location to fill with a GError if an error is hit, or NULL

 

Returns

TRUE on success, otherwise FALSE


WockyAuthRegistrySuccessAsyncFunc ()

void
(*WockyAuthRegistrySuccessAsyncFunc) (WockyAuthRegistry *self,
                                      GAsyncReadyCallback callback,
                                      gpointer user_data);

Notifies the registry of authentication success, and allows a last ditch attempt at aborting the authentication at the client's discretion.

Parameters

self

a WockyAuthRegistry object

 

callback

a callback to be called when finished

 

user_data

data to pass to callback

 

WockyAuthRegistrySuccessFinishFunc ()

gboolean
(*WockyAuthRegistrySuccessFinishFunc) (WockyAuthRegistry *self,
                                       GAsyncResult *result,
                                       GError **error);

Finishes a GAsyncResult from WockyAuthRegistrySuccessAsyncFunc. It checks for any errors set on the given GSimpleAsyncResult, copies the GError to an out param and returns FALSE if there was an error.

Parameters

self

a WockyAuthRegistry object

 

result

a GAsyncResult object

 

error

a location to fill with a GError if an error is hit, or NULL

 

Returns

TRUE on success, otherwise FALSE


WockyAuthRegistryFailureFunc ()

void
(*WockyAuthRegistryFailureFunc) (WockyAuthRegistry *self,
                                 GError *error);

Notifies the client of a server-side error. By default this is not implemented.

Parameters

self

a WockyAuthRegistry object

 

error

a GError describing the failure

 

wocky_auth_registry_new ()

WockyAuthRegistry *
wocky_auth_registry_new (void);

wocky_auth_registry_start_auth_async ()

void
wocky_auth_registry_start_auth_async (WockyAuthRegistry *self,
                                      GSList *mechanisms,
                                      gboolean allow_plain,
                                      gboolean is_secure_channel,
                                      const gchar *username,
                                      const gchar *password,
                                      const gchar *server,
                                      const gchar *session_id,
                                      GAsyncReadyCallback callback,
                                      gpointer user_data);

wocky_auth_registry_start_auth_finish ()

gboolean
wocky_auth_registry_start_auth_finish (WockyAuthRegistry *self,
                                       GAsyncResult *result,
                                       WockyAuthRegistryStartData **start_data,
                                       GError **error);

wocky_auth_registry_challenge_async ()

void
wocky_auth_registry_challenge_async (WockyAuthRegistry *self,
                                     const GString *challenge_data,
                                     GAsyncReadyCallback callback,
                                     gpointer user_data);

wocky_auth_registry_challenge_finish ()

gboolean
wocky_auth_registry_challenge_finish (WockyAuthRegistry *self,
                                      GAsyncResult *res,
                                      GString **response,
                                      GError **error);

wocky_auth_registry_success_async ()

void
wocky_auth_registry_success_async (WockyAuthRegistry *self,
                                   GAsyncReadyCallback callback,
                                   gpointer user_data);

wocky_auth_registry_success_finish ()

gboolean
wocky_auth_registry_success_finish (WockyAuthRegistry *self,
                                    GAsyncResult *res,
                                    GError **error);

wocky_auth_registry_add_handler ()

void
wocky_auth_registry_add_handler (WockyAuthRegistry *self,
                                 WockyAuthHandler *handler);

wocky_auth_registry_start_data_free ()

void
wocky_auth_registry_start_data_free (WockyAuthRegistryStartData *start_data);

wocky_auth_registry_start_data_new ()

WockyAuthRegistryStartData *
wocky_auth_registry_start_data_new (const gchar *mechanism,
                                    const GString *initial_response);

wocky_auth_registry_start_data_dup ()

WockyAuthRegistryStartData *
wocky_auth_registry_start_data_dup (WockyAuthRegistryStartData *start_data);

wocky_auth_registry_failure ()

void
wocky_auth_registry_failure (WockyAuthRegistry *self,
                             GError *error);

wocky_auth_registry_supports_one_of ()

gboolean
wocky_auth_registry_supports_one_of (WockyAuthRegistry *self,
                                     GSList *mechanisms,
                                     gboolean allow_plain);

Checks whether at least one of mechanisms is supported by Wocky. At present, Wocky itself only implements password-based authentication mechanisms.

Parameters

self

a WockyAuthRegistry

 

allow_plain

Whether auth in plain text is allowed

 

mechanisms

a GSList of gchar* of auth mechanisms

 

Returns

TRUE if one of the mechanisms is supported by wocky, FALSE otherwise.

Types and Values

WOCKY_AUTH_ERROR

#define             WOCKY_AUTH_ERROR

enum WockyAuthError

WockyAuthRegistry specific errors.

Members

WOCKY_AUTH_ERROR_INIT_FAILED

Failed to initialize our auth support

 

WOCKY_AUTH_ERROR_NOT_SUPPORTED

Server doesn't support this authentication method

 

WOCKY_AUTH_ERROR_NO_SUPPORTED_MECHANISMS

Server doesn't support any mechanisms that we support

 

WOCKY_AUTH_ERROR_NETWORK

Couldn't send our stanzas to the server

 

WOCKY_AUTH_ERROR_INVALID_REPLY

Server sent an invalid reply

 

WOCKY_AUTH_ERROR_NO_CREDENTIALS

Failure to provide user credentials

 

WOCKY_AUTH_ERROR_FAILURE

Server sent a failure

 

WOCKY_AUTH_ERROR_CONNRESET

disconnected

 

WOCKY_AUTH_ERROR_STREAM

XMPP stream error while authing

 

WOCKY_AUTH_ERROR_RESOURCE_CONFLICT

Resource conflict (relevant in in jabber auth)

 

WOCKY_AUTH_ERROR_NOT_AUTHORIZED

Provided credentials are not valid

 

WOCKY_AUTH_MECH_JABBER_DIGEST

#define WOCKY_AUTH_MECH_JABBER_DIGEST "X-WOCKY-JABBER-DIGEST"

WOCKY_AUTH_MECH_JABBER_PASSWORD

#define WOCKY_AUTH_MECH_JABBER_PASSWORD "X-WOCKY-JABBER-PASSWORD"

WOCKY_AUTH_MECH_SASL_DIGEST_MD5

#define WOCKY_AUTH_MECH_SASL_DIGEST_MD5 "DIGEST-MD5"

WOCKY_AUTH_MECH_SASL_PLAIN

#define WOCKY_AUTH_MECH_SASL_PLAIN "PLAIN"

WOCKY_AUTH_MECH_SASL_SCRAM_SHA_1

#define WOCKY_AUTH_MECH_SASL_SCRAM_SHA_1 "SCRAM-SHA-1"

WockyAuthRegistryStartData

typedef struct {
  gchar *mechanism;
  GString *initial_response;
} WockyAuthRegistryStartData;

A structure to hold the mechanism and response data.

Members

gchar *mechanism;

the name of the mechanism

 

GString *initial_response;

the data in the response

 

struct WockyAuthRegistryClass

struct WockyAuthRegistryClass {
  WockyAuthRegistryStartAuthAsyncFunc start_auth_async_func;
  WockyAuthRegistryStartAuthFinishFunc start_auth_finish_func;

  WockyAuthRegistryChallengeAsyncFunc challenge_async_func;
  WockyAuthRegistryChallengeFinishFunc challenge_finish_func;

  WockyAuthRegistrySuccessAsyncFunc success_async_func;
  WockyAuthRegistrySuccessFinishFunc success_finish_func;

  WockyAuthRegistryFailureFunc failure_func;
};

The class of a WockyAuthRegistry.

Members

WockyAuthRegistryStartAuthAsyncFunc start_auth_async_func;

a function to call to start an asynchronous start auth operation; see wocky_auth_registry_start_auth_async() for more details.

 

WockyAuthRegistryStartAuthFinishFunc start_auth_finish_func;

a function to call to finish an asynchronous start auth operation; see wocky_auth_registry_start_auth_finish() for more details.

 

WockyAuthRegistryChallengeAsyncFunc challenge_async_func;

a function to call to start an asynchronous challenge operation; see wocky_auth_registry_challenge_async() for more details.

 

WockyAuthRegistryChallengeFinishFunc challenge_finish_func;

a function to call to finish an asynchronous challenge operation; see wocky_auth_registry_challenge_finish() for more details.

 

WockyAuthRegistrySuccessAsyncFunc success_async_func;

a function to call to start an asynchronous success operation; see wocky_auth_registry_success_async() for more details.

 

WockyAuthRegistrySuccessFinishFunc success_finish_func;

a function to call to finish an asynchronous success operation; see wocky_auth_registry_success_finish() for more details.

 

WockyAuthRegistryFailureFunc failure_func;

a function to call on failure; see wocky_auth_registry_failure() for more details.

 
telepathy-gabble-0.18.2/lib/ext/wocky/docs/reference/html/WockyBareContact.html0000644000175000017500000011232512312537050027443 0ustar00smcvsmcv00000000000000 Wocky Reference Manual: WockyBareContact

WockyBareContact

WockyBareContact — Wrapper around a roster item.

Properties

GStrv groups Read / Write / Construct
gchar * jid Read / Write / Construct Only
gchar * name Read / Write / Construct
guint subscription Read / Write / Construct

Types and Values

Object Hierarchy

    GObject
    ╰── WockyContact
        ╰── WockyBareContact

Includes

#include <wocky/wocky-bare-contact.h>

Description

Stores information regarding a roster item and provides a higher level API for altering its details.

Functions

wocky_bare_contact_new ()

WockyBareContact *
wocky_bare_contact_new (const gchar *jid);

Creates a new WockyBareContact for a given JID.

Parameters

jid

the JID of the contact to create

 

Returns

a newly constructed WockyBareContact


wocky_bare_contact_get_jid ()

const gchar *
wocky_bare_contact_get_jid (WockyBareContact *contact);

Returns the JID of the contact wrapped by contact .

Parameters

contact

a WockyBareContact instance

 

Returns

contact 's JID.


wocky_bare_contact_get_name ()

const gchar *
wocky_bare_contact_get_name (WockyBareContact *contact);

Returns the name of the contact wrapped by contact .

Parameters

contact

WockyBareContact instance

 

Returns

contact 's name


wocky_bare_contact_set_name ()

void
wocky_bare_contact_set_name (WockyBareContact *contact,
                             const gchar *name);

Sets contact 's name to name .

Parameters

contact

a WockyBareContact instance

 

name

the name to set contact

 

wocky_bare_contact_get_subscription ()

WockyRosterSubscriptionFlags
wocky_bare_contact_get_subscription (WockyBareContact *contact);

Gets the subscription type contact has.

Parameters

contact

a WockyBareContact instance

 

Returns

contact 's subscription.


wocky_bare_contact_set_subscription ()

void
wocky_bare_contact_set_subscription (WockyBareContact *contact,
                                     WockyRosterSubscriptionFlags subscription);

Sets the subscription of contact .

Parameters

contact

a WockyBareContact instance

 

subscription

the new subscription type

 

wocky_bare_contact_get_groups ()

const gchar * const *
wocky_bare_contact_get_groups (WockyBareContact *contact);

Returns the list of the groups of contact .

Parameters

contact

a WockyBareContact instance

 

Returns

a list of contact 's groups


wocky_bare_contact_set_groups ()

void
wocky_bare_contact_set_groups (WockyBareContact *contact,
                               gchar **groups);

Sets contact 's groups.

Parameters

contact

a WockyBareContact instance

 

groups

a list of groups

 

wocky_bare_contact_equal ()

gboolean
wocky_bare_contact_equal (WockyBareContact *a,
                          WockyBareContact *b);

Compares whether two WockyBareContact instances refer to the same roster item.

Parameters

a

a WockyBareContact instance

 

b

a WockyBareContact instance to compare with a

 

Returns

TRUE if the two contacts match.


wocky_bare_contact_add_group ()

void
wocky_bare_contact_add_group (WockyBareContact *contact,
                              const gchar *group);

Adds group to contact's groups.

Parameters

contact

a WockyBareContact instance

 

group

a group

 

wocky_bare_contact_in_group ()

gboolean
wocky_bare_contact_in_group (WockyBareContact *contact,
                             const gchar *group);

Determines whether the given contact is in group .

Parameters

contact

a WockyBareContact instance

 

group

a group

 

Returns

TRUE if the contact is in the given group.


wocky_bare_contact_remove_group ()

void
wocky_bare_contact_remove_group (WockyBareContact *contact,
                                 const gchar *group);

Removes group from the contact's groups.

Parameters

contact

a WockyBareContact instance

 

group

a group

 

wocky_bare_contact_copy ()

WockyBareContact *
wocky_bare_contact_copy (WockyBareContact *contact);

Convenience function to obtain a copy of the given WockyBareContact.

Parameters

contact

a WockyBareContact instance

 

Returns

a newly created WockyBareContact which is a copy of the given one.


wocky_bare_contact_debug_print ()

void
wocky_bare_contact_debug_print (WockyBareContact *contact);

Prints debug information for the given WockyBareContact.

Parameters

contact

a WockyBareContact instance

 

wocky_bare_contact_add_resource ()

void
wocky_bare_contact_add_resource (WockyBareContact *contact,
                                 WockyResourceContact *resource);

Adds resource to the contact's resources. The WockyBareContact instance doesn't assume a reference to resource .

Parameters

contact

a WockyBareContact instance

 

resource

a WockyResourceContact instance

 

wocky_bare_contact_get_resources ()

GSList *
wocky_bare_contact_get_resources (WockyBareContact *contact);

Gets a GSList of all the contact's resources. You should call g_slist_free on the list when done with it.

Parameters

contact

a WockyBareContact instance

 

Returns

a GSList of WockyResourceContact objects.

Types and Values

struct WockyBareContactClass

struct WockyBareContactClass {
};

The class of a WockyBareContact.

Property Details

The “groups” property

  “groups”                   GStrv

A list of the contact's groups, according to the roster.

Flags: Read / Write / Construct


The “jid” property

  “jid”                      gchar *

The contact's bare JID, according to the roster.

Flags: Read / Write / Construct Only

Default value: ""


The “name” property

  “name”                     gchar *

The contact's name, according to the roster.

Flags: Read / Write / Construct

Default value: ""


The “subscription” property

  “subscription”             guint

The subscription type of the contact, according to the roster.

Flags: Read / Write / Construct

Allowed values: <= 3

Default value: 0

telepathy-gabble-0.18.2/lib/ext/wocky/docs/reference/html/WockyC2SPorter.html0000644000175000017500000010560012312537050027037 0ustar00smcvsmcv00000000000000 Wocky Reference Manual: WockyC2SPorter

WockyC2SPorter

WockyC2SPorter — Wrapper around a WockyXmppConnection providing a higher level API.

Types and Values

Object Hierarchy

    GObject
    ╰── WockyC2SPorter

Implemented Interfaces

WockyC2SPorter implements

Description

Sends and receives WockyStanza from an underlying WockyXmppConnection.

Functions

wocky_c2s_porter_new ()

WockyPorter *
wocky_c2s_porter_new (WockyXmppConnection *connection,
                      const gchar *full_jid);

Convenience function to create a new WockyC2SPorter.

Parameters

connection

WockyXmppConnection which will be used to receive and send WockyStanza

 

full_jid

the full JID of the user

 

Returns

a new WockyPorter.


wocky_c2s_porter_send_whitespace_ping_async ()

void
wocky_c2s_porter_send_whitespace_ping_async
                               (WockyC2SPorter *self,
                                GCancellable *cancellable,
                                GAsyncReadyCallback callback,
                                gpointer user_data);

Request asynchronous sending of a whitespace ping. When the operation is finished callback will be called. You can then call wocky_c2s_porter_send_whitespace_ping_finish() to get the result of the operation. No pings are sent if there are already other stanzas or pings being sent when this function is called; it would be useless.

Parameters

self

a WockyC2SPorter

 

cancellable

optional GCancellable object, NULL to ignore.

 

callback

callback to call when the request is satisfied.

 

user_data

the data to pass to callback function.

 

wocky_c2s_porter_send_whitespace_ping_finish ()

gboolean
wocky_c2s_porter_send_whitespace_ping_finish
                               (WockyC2SPorter *self,
                                GAsyncResult *result,
                                GError **error);

Finishes sending a whitespace ping.

Parameters

self

a WockyC2SPorter

 

result

a GAsyncResult.

 

error

a GError location to store the error occuring, or NULL to ignore.

 

Returns

TRUE if the ping was succesfully sent, FALSE on error.


wocky_c2s_porter_register_handler_from_server_va ()

guint
wocky_c2s_porter_register_handler_from_server_va
                               (WockyC2SPorter *self,
                                WockyStanzaType type,
                                WockyStanzaSubType sub_type,
                                guint priority,
                                WockyPorterHandlerFunc callback,
                                gpointer user_data,
                                va_list ap);

A va_list version of wocky_c2s_porter_register_handler_from_server(); see that function for more details.

Parameters

self

A WockyC2SPorter instance (passed to callback ).

 

type

The type of stanza to be handled, or WOCKY_STANZA_TYPE_NONE to match any type of stanza.

 

sub_type

The subtype of stanza to be handled, or WOCKY_STANZA_SUB_TYPE_NONE to match any type of stanza.

 

priority

a priority between WOCKY_PORTER_HANDLER_PRIORITY_MIN and WOCKY_PORTER_HANDLER_PRIORITY_MAX (often WOCKY_PORTER_HANDLER_PRIORITY_NORMAL). Handlers with a higher priority (larger number) are called first.

 

callback

A WockyPorterHandlerFunc, which should return FALSE to decline the stanza (Wocky will continue to the next handler, if any), or TRUE to stop further processing.

 

user_data

Passed to callback .

 

ap

a wocky_stanza_build() specification. The handler will match a stanza only if the stanza received is a superset of the one passed to this function, as per wocky_node_is_superset().

 

Returns

a non-zero ID for use with wocky_porter_unregister_handler().


wocky_c2s_porter_register_handler_from_server_by_stanza ()

guint
wocky_c2s_porter_register_handler_from_server_by_stanza
                               (WockyC2SPorter *self,
                                WockyStanzaType type,
                                WockyStanzaSubType sub_type,
                                guint priority,
                                WockyPorterHandlerFunc callback,
                                gpointer user_data,
                                WockyStanza *stanza);

A WockyStanza version of wocky_c2s_porter_register_handler_from_server(); see that function for more details.

Parameters

self

A WockyC2SPorter instance (passed to callback ).

 

type

The type of stanza to be handled, or WOCKY_STANZA_TYPE_NONE to match any type of stanza.

 

sub_type

The subtype of stanza to be handled, or WOCKY_STANZA_SUB_TYPE_NONE to match any type of stanza.

 

priority

a priority between WOCKY_PORTER_HANDLER_PRIORITY_MIN and WOCKY_PORTER_HANDLER_PRIORITY_MAX (often WOCKY_PORTER_HANDLER_PRIORITY_NORMAL). Handlers with a higher priority (larger number) are called first.

 

callback

A WockyPorterHandlerFunc, which should return FALSE to decline the stanza (Wocky will continue to the next handler, if any), or TRUE to stop further processing.

 

user_data

Passed to callback .

 

stanza

a WockyStanza. The handler will match a stanza only if the stanza received is a superset of the one passed to this function, as per wocky_node_is_superset().

 

Returns

a non-zero ID for use with wocky_porter_unregister_handler().


wocky_c2s_porter_register_handler_from_server ()

guint
wocky_c2s_porter_register_handler_from_server
                               (WockyC2SPorter *self,
                                WockyStanzaType type,
                                WockyStanzaSubType sub_type,
                                guint priority,
                                WockyPorterHandlerFunc callback,
                                gpointer user_data,
                                ...);

Registers a handler for incoming stanzas from the local user's server; that is, stanzas with no "from" attribute, or where the sender is the user's own bare or full JID.

For example, to register a handler for roster pushes, call:

1
2
3
4
5
6
id = wocky_c2s_porter_register_handler_from_server (porter,
  WOCKY_STANZA_TYPE_MESSAGE, WOCKY_STANZA_SUB_TYPE_SET,
  WOCKY_PORTER_HANDLER_PRIORITY_NORMAL, roster_push_received_cb, NULL,
  '(',
    "query", ':', WOCKY_XMPP_NS_ROSTER,
  ')', NULL);

Parameters

self

A WockyC2SPorter instance (passed to callback ).

 

type

The type of stanza to be handled, or WOCKY_STANZA_TYPE_NONE to match any type of stanza.

 

sub_type

The subtype of stanza to be handled, or WOCKY_STANZA_SUB_TYPE_NONE to match any type of stanza.

 

priority

a priority between WOCKY_PORTER_HANDLER_PRIORITY_MIN and WOCKY_PORTER_HANDLER_PRIORITY_MAX (often WOCKY_PORTER_HANDLER_PRIORITY_NORMAL). Handlers with a higher priority (larger number) are called first.

 

callback

A WockyPorterHandlerFunc, which should return FALSE to decline the stanza (Wocky will continue to the next handler, if any), or TRUE to stop further processing.

 

user_data

Passed to callback .

 

...

a wocky_stanza_build() specification. The handler will match a stanza only if the stanza received is a superset of the one passed to this function, as per wocky_node_is_superset().

 

Returns

a non-zero ID for use with wocky_porter_unregister_handler().


wocky_c2s_porter_enable_power_saving_mode ()

void
wocky_c2s_porter_enable_power_saving_mode
                               (WockyC2SPorter *porter,
                                gboolean enable);

Enable or disable power saving. In power saving mode, Wocky will attempt to queue "uninteresting" stanza until it is either manually flushed, until important stanza arrives, or until the power saving mode is disabled.

Queueable stanzas are:

  • <presence/> and <presence type="unavailable"/>;
  • PEP updates for a hardcoded list of namespaces.

Whenever stanza is handled, all previously queued stanzas (if any) are handled as well, in the order they arrived. This preserves stanza ordering.

Note that exiting the power saving mode will immediately handle any queued stanzas.

Parameters

porter

a WockyC2SPorter

 

enable

A boolean specifying whether power saving mode should be used

 

Types and Values

struct WockyC2SPorterClass

struct WockyC2SPorterClass {
};

The class of a WockyC2SPorter.


struct WockyC2SPorter

struct WockyC2SPorter;

An object providing a convenient wrapper around a WockyXmppConnection to send and receive stanzas.

telepathy-gabble-0.18.2/lib/ext/wocky/docs/reference/html/WockyCapsCache.html0000644000175000017500000003601712312537050027073 0ustar00smcvsmcv00000000000000 Wocky Reference Manual: WockyCapsCache

WockyCapsCache

WockyCapsCache

Properties

gchar * path Read / Write / Construct Only

Types and Values

Object Hierarchy

    GObject
    ╰── WockyCapsCache

Description

Functions

wocky_caps_cache_lookup ()

WockyNodeTree *
wocky_caps_cache_lookup (WockyCapsCache *self,
                         const gchar *node);

Look up node in the caps cache self . The caller is responsible for unreffing the returned WockyNodeTree.

Parameters

self

a WockyCapsCache

 

node

the node to look up in the cache

 

Returns

a WockyNodeTree if node was found in the cache, or NULL if a match was not found


wocky_caps_cache_insert ()

void
wocky_caps_cache_insert (WockyCapsCache *self,
                         const gchar *node,
                         WockyNodeTree *query_node);

Adds a new item to the caps cache. node is associated with query_node so that subsequent calls to wocky_caps_cache_lookup() with the same node value will return query_node .

Parameters

self

a WockyCapsCache

 

node

the capability node

 

query_node

the query WockyNodeTree associated with node

 

wocky_caps_cache_new ()

WockyCapsCache *
wocky_caps_cache_new (const gchar *path);

Convenience function to create a new WockyCapsCache.

Parameters

path

full path to where the cache SQLite database should be stored

 

Returns

a new WockyCapsCache.


wocky_caps_cache_dup_shared ()

WockyCapsCache *
wocky_caps_cache_dup_shared (void);

Returns a new or existing WockyCapsCache object.

The returned WockyCapsCache is cached; the same WockyCapsCache object will be returned by this function repeatedly in the same process. At the end of the process, the caller should call wocky_caps_cache_free_shared() to shared the shared WockyCapsCache object.

Returns

a new, or cached, WockyCapsCache.


wocky_caps_cache_free_shared ()

void
wocky_caps_cache_free_shared (void);

Free the shared WockyCapsCache instance which was created by calling wocky_caps_cache_dup_shared(), or do nothing if said function was not called.

Types and Values

struct WockyCapsCache

struct WockyCapsCache;

An object providing a permanent cache for capabilities.


struct WockyCapsCacheClass

struct WockyCapsCacheClass {
};

The class of a WockyCapsCache.

Property Details

The “path” property

  “path”                     gchar *

The path on disk to the SQLite database where this WockyCapsCache stores its information.

Flags: Read / Write / Construct Only

Default value: NULL

telepathy-gabble-0.18.2/lib/ext/wocky/docs/reference/html/WockyConnector.html0000644000175000017500000017220212312537050027210 0ustar00smcvsmcv00000000000000 Wocky Reference Manual: WockyConnector

WockyConnector

WockyConnector — Low-level XMPP connection generator.

Properties

WockyAuthRegistry * auth-registry Read / Write / Construct Only
gchar * email Read / Write
gboolean encrypted-plain-auth-ok Read / Write / Construct
WockyStanza * features Read
gchar * identity Read
gchar * jid Read / Write
gboolean legacy Read / Write / Construct
gboolean old-ssl Read / Write / Construct
gchar * password Read / Write
gboolean plaintext-auth-allowed Read / Write / Construct
gchar * resource Read / Write / Construct Only
gchar * session-id Read
WockyTLSHandler * tls-handler Read / Write / Construct Only
gboolean tls-required Read / Write / Construct
guint xmpp-port Read / Write / Construct
gchar * xmpp-server Read / Write

Types and Values

Object Hierarchy

    GEnum
    ╰── WockyConnectorError
    GObject
    ╰── WockyConnector

Includes

#include <wocky/wocky-connector.h>

Description

See: RFC3920 XEP-0077

Sends and receives WockyStanzas from an underlying GIOStream. negotiating TLS if possible and completing authentication with the server by the "most suitable" method available. Returns a WockyXmppConnection object to the user on successful completion.

Can also be used to register or unregister an account: When unregistering (cancelling) an account, a WockyXmppConnection is NOT returned - a gboolean value indicating success or failure is returned instead.

The WOCKY_DEBUG tag for this module is "connector".

The flow of control during connection is roughly as follows: (registration/cancellation flows are not represented with here)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
tcp_srv_connected
│
├→ tcp_host_connected
│  ↓
└→ maybe_old_ssl
   ↓
   xmpp_init ←─────────────────┬──┐
   ↓                           │  │
   xmpp_init_sent_cb           │  │
   ↓                           │  │
   xmpp_init_recv_cb           │  │
   │ ↓                         │  │
   │ xmpp_features_cb          │  │
   │ │ │ ↓                     │  │
   │ │ │ tls_module_secure_cb ─┘  │             ①
   │ │ ↓                      │             ↑
   │ │ sasl_request_auth      │             jabber_auth_done
   │ │ ↓                      │             ↑
   │ │ sasl_auth_done ────────┴─[no sasl]─→ jabber_request_auth
   │ ↓                                      ↑
   │ iq_bind_resource                       │
   │ ↓                                      │
   │ iq_bind_resource_sent_cb               │
   │ ↓                                      │
   │ iq_bind_resource_recv_cb               │
   │ ↓                                      │
   │ ①                                      │
   └──────────[old auth]────────────────────┘

   ①
   ↓
   establish_session ─────────→ success
   ↓                              ↑
   establish_session_sent_cb      │
   ↓                              │
   establish_session_recv_cb ─────┘

Functions

wocky_connector_error_quark ()

GQuark
wocky_connector_error_quark (void);

WOCKY_CONNECTOR_ERROR

#define WOCKY_CONNECTOR_ERROR (wocky_connector_error_quark ())

Get access to the error quark of the connector.


wocky_connector_connect_finish ()

WockyXmppConnection *
wocky_connector_connect_finish (WockyConnector *self,
                                GAsyncResult *res,
                                gchar **jid,
                                gchar **sid,
                                GError **error);

Called by the callback passed to wocky_connector_connect_async().

Parameters

self

a WockyConnector instance.

 

res

a GAsyncResult (from your wocky_connector_connect_async() callback).

 

jid

(NULL to ignore) the user JID from the server is stored here.

 

sid

(NULL to ignore) the Session ID is stored here.

 

error

(NULL to ignore) the GError (if any) is sored here.

 

Returns

a WockyXmppConnection instance (success), or NULL (failure).


wocky_connector_register_finish ()

WockyXmppConnection *
wocky_connector_register_finish (WockyConnector *self,
                                 GAsyncResult *res,
                                 gchar **jid,
                                 gchar **sid,
                                 GError **error);

Called by the callback passed to wocky_connector_register_async().

Parameters

self

a WockyConnector instance.

 

res

a GAsyncResult (from your wocky_connector_register_async() callback).

 

jid

(NULL to ignore) the JID in effect after connection is stored here.

 

sid

(NULL to ignore) the Session ID after connection is stored here.

 

error

(NULL to ignore) the GError (if any) is stored here.

 

Returns

a WockyXmppConnection instance (success), or NULL (failure).


wocky_connector_connect_async ()

void
wocky_connector_connect_async (WockyConnector *self,
                               GCancellable *cancellable,
                               GAsyncReadyCallback cb,
                               gpointer user_data);

Connect to the account/server specified by the self . cb should invoke wocky_connector_connect_finish().

Parameters

self

a WockyConnector instance.

 

cancellable

an GCancellable, or NULL

 

cb

a GAsyncReadyCallback to call when the operation completes.

 

user_data

a gpointer to pass to the callback.

 

wocky_connector_new ()

WockyConnector *
wocky_connector_new (const gchar *jid,
                     const gchar *pass,
                     const gchar *resource,
                     WockyAuthRegistry *auth_registry,
                     WockyTLSHandler *tls_handler);

Connect to the account/server specified by self . To set other WockyConnector properties, use g_object_new() instead.

Parameters

jid

a JID (user AT domain).

 

pass

the password.

 

resource

the resource (sans '/'), or NULL to autogenerate one.

 

auth_registry

a WockyAuthRegistry, or NULL

 

tls_handler

a WockyTLSHandler, or NULL

 

Returns

a WockyConnector instance which can be used to connect to, register or cancel an account


wocky_connector_register_async ()

void
wocky_connector_register_async (WockyConnector *self,
                                GCancellable *cancellable,
                                GAsyncReadyCallback cb,
                                gpointer user_data);

Connect to the account/server specified by self , register (set up) the account there and then log in to it. cb should invoke wocky_connector_register_finish().

Parameters

self

a WockyConnector instance.

 

cancellable

an GCancellable, or NULL

 

cb

a GAsyncReadyCallback to call when the operation completes.

 

user_data

a gpointer to pass to the callback cb .

 

wocky_connector_unregister_async ()

void
wocky_connector_unregister_async (WockyConnector *self,
                                  GCancellable *cancellable,
                                  GAsyncReadyCallback cb,
                                  gpointer user_data);

Connect to the account/server specified by self , and unregister (cancel) the account there. cb should invoke wocky_connector_unregister_finish().

Parameters

self

a WockyConnector instance.

 

cancellable

an GCancellable, or NULL

 

cb

a GAsyncReadyCallback to call when the operation completes.

 

user_data

a gpointer to pass to the callback cb .

 

wocky_connector_unregister_finish ()

gboolean
wocky_connector_unregister_finish (WockyConnector *self,
                                   GAsyncResult *res,
                                   GError **error);

Called by the callback passed to wocky_connector_unregister_async().

Parameters

self

a WockyConnector instance.

 

res

a GAsyncResult (from the wocky_connector_unregister_async() callback).

 

error

(NULL to ignore) the GError (if any) is stored here.

 

Returns

a gboolean value TRUE (success), or FALSE (failure).


wocky_connector_set_auth_registry ()

void
wocky_connector_set_auth_registry (WockyConnector *self,
                                   WockyAuthRegistry *registry);

Types and Values

enum WockyConnectorError

The WockyConnector specific errors that can occur while connecting.

Members

WOCKY_CONNECTOR_ERROR_UNKNOWN

Unexpected error condition

 

WOCKY_CONNECTOR_ERROR_IN_PROGRESS

Connection already underway

 

WOCKY_CONNECTOR_ERROR_BAD_JID

JID is invalid

 

WOCKY_CONNECTOR_ERROR_NON_XMPP_V1_SERVER

XMPP version < 1

 

WOCKY_CONNECTOR_ERROR_BAD_FEATURES

Feature stanza invalid

 

WOCKY_CONNECTOR_ERROR_TLS_UNAVAILABLE

TLS unavailable

 

WOCKY_CONNECTOR_ERROR_TLS_REFUSED

TLS refused by server

 

WOCKY_CONNECTOR_ERROR_TLS_SESSION_FAILED

TLS handshake failed

 

WOCKY_CONNECTOR_ERROR_BIND_UNAVAILABLE

Bind not available

 

WOCKY_CONNECTOR_ERROR_BIND_FAILED

Bind failed

 

WOCKY_CONNECTOR_ERROR_BIND_INVALID

Bind args invalid

 

WOCKY_CONNECTOR_ERROR_BIND_DENIED

Bind not allowed

 

WOCKY_CONNECTOR_ERROR_BIND_CONFLICT

Bind resource in use

 

WOCKY_CONNECTOR_ERROR_BIND_REJECTED

Bind error (generic)

 

WOCKY_CONNECTOR_ERROR_SESSION_FAILED

Session failed

 

WOCKY_CONNECTOR_ERROR_SESSION_DENIED

Session refused by server

 

WOCKY_CONNECTOR_ERROR_SESSION_CONFLICT

Session not allowed

 

WOCKY_CONNECTOR_ERROR_SESSION_REJECTED

Session error

 

WOCKY_CONNECTOR_ERROR_INSECURE

Insufficent security for requested operation

 

WOCKY_CONNECTOR_ERROR_REGISTRATION_FAILED

Account registration error

 

WOCKY_CONNECTOR_ERROR_REGISTRATION_UNAVAILABLE

Account registration not available

 

WOCKY_CONNECTOR_ERROR_REGISTRATION_UNSUPPORTED

Account registration not implemented

 

WOCKY_CONNECTOR_ERROR_REGISTRATION_EMPTY

Account registration makes no sense

 

WOCKY_CONNECTOR_ERROR_REGISTRATION_CONFLICT

Account already registered

 

WOCKY_CONNECTOR_ERROR_REGISTRATION_REJECTED

Account registration rejected

 

WOCKY_CONNECTOR_ERROR_UNREGISTER_FAILED

Account cancellation failed

 

WOCKY_CONNECTOR_ERROR_UNREGISTER_DENIED

Account cancellation refused

 

struct WockyConnectorClass

struct WockyConnectorClass {
};

The class of a WockyConnector.

Property Details

The “auth-registry” property

  “auth-registry”            WockyAuthRegistry *

An authentication registry that holds handlers for different authentication mechanisms, arbitrates mechanism selection and relays challenges and responses between the handlers and the connection.

Flags: Read / Write / Construct Only


The “email” property

  “email”                    gchar *

The XMPP account's email address (optional, MAY be required by the server if we are registering an account, not required otherwise).

Flags: Read / Write

Default value: NULL


The “encrypted-plain-auth-ok” property

  “encrypted-plain-auth-ok”  gboolean

Whether PLAINTEXT auth is ok when encrypted.

Flags: Read / Write / Construct

Default value: TRUE


The “features” property

  “features”                 WockyStanza *

A WockyStanza instance, the last WockyStanza instance received by the connector during the connection procedure (there may be several, the most recent one always being the one we should refer to).

Flags: Read


The “identity” property

  “identity”                 gchar *

JID + resource (a AT b SLASH c) that is in effect _after_ a successful resource binding operation. This is NOT guaranteed to be related to the JID specified in the original “jid” property. The resource, in particular, is often different, and with gtalk the domain is often different.

Flags: Read

Default value: NULL


The “jid” property

  “jid”                      gchar *

The XMPP account's JID (with or without a /resource).

Flags: Read / Write

Default value: NULL


The “legacy” property

  “legacy”                   gboolean

Whether to attempt old-style (non-SASL) jabber auth.

Flags: Read / Write / Construct

Default value: FALSE


The “old-ssl” property

  “old-ssl”                  gboolean

Whether to use old-style SSL-at-connect-time encryption rather than the more modern STARTTLS approach.

Flags: Read / Write / Construct

Default value: FALSE


The “password” property

  “password”                 gchar *

XMPP Account password.

Flags: Read / Write

Default value: NULL


The “plaintext-auth-allowed” property

  “plaintext-auth-allowed”   gboolean

Whether auth info can be sent in the clear (eg PLAINTEXT auth). This is independent of any encryption (TLS, SSL) that has been negotiated.

Flags: Read / Write / Construct

Default value: FALSE


The “resource” property

  “resource”                 gchar *

The resource (sans '/') for this connection. If NULL or the empty string, Wocky will let the server decide. Even if you specify a particular resource, the server may modify it.

Flags: Read / Write / Construct Only

Default value: NULL


The “session-id” property

  “session-id”               gchar *

The Session ID supplied by the server upon successfully connecting. May be useful later on as some XEPs suggest this value should be used at various stages as part of a hash or as an ID.

Flags: Read

Default value: NULL


The “tls-handler” property

  “tls-handler”              WockyTLSHandler *

A TLS handler that carries out the interactive verification of the TLS certitificates provided by the server.

Flags: Read / Write / Construct Only


The “tls-required” property

  “tls-required”             gboolean

Whether we require successful tls/ssl negotiation to continue.

Flags: Read / Write / Construct

Default value: TRUE


The “xmpp-port” property

  “xmpp-port”                guint

Optional XMPP connect port. Any DNS SRV record will be ignored if this is set. (So the host will be either the WockyConnector:xmpp-server property or the domain part of the JID, in descending order of preference)

Flags: Read / Write / Construct

Allowed values: <= 65535

Default value: 0


The “xmpp-server” property

  “xmpp-server”              gchar *

Optional XMPP connect server. Any DNS SRV record and the host specified in “jid” will be ignored if this is set. May be a hostname (fully qualified or otherwise), a dotted quad or an ipv6 address.

Flags: Read / Write

Default value: NULL

Signal Details

The “connection-established” signal

void
user_function (WockyConnector    *connection,
               GSocketConnection *arg1,
               gpointer           user_data)

Emitted as soon as a connection to the remote server has been established. This can be useful if you want to do something unusual to the connection early in its lifetime not supported by the WockyConnector APIs.

As the connection process has only just started and the stream not even opened yet, no data must be sent over connection . This signal is merely intended to set esoteric socket options (such as TCP_NODELAY) on the connection.

Parameters

connection

the GSocketConnection

 

user_data

user data set when the signal handler was connected.

 

Flags: Run Last

telepathy-gabble-0.18.2/lib/ext/wocky/docs/reference/html/WockyContactFactory.html0000644000175000017500000007326012312537050030205 0ustar00smcvsmcv00000000000000 Wocky Reference Manual: WockyContactFactory

WockyContactFactory

WockyContactFactory — creates and looks up WockyContact objects

Types and Values

Object Hierarchy

    GObject
    ╰── WockyContactFactory

Includes

#include <wocky/wocky-contact-factory.h>

Description

Provides a way to create WockyContact objects. The objects created this way are cached by the factory and you can eventually look them up without creating them again.

Functions

wocky_contact_factory_new ()

WockyContactFactory *
wocky_contact_factory_new (void);

Convenience function to create a new WockyContactFactory object.

Returns

a newly created instance of WockyContactFactory


wocky_contact_factory_ensure_bare_contact ()

WockyBareContact *
wocky_contact_factory_ensure_bare_contact
                               (WockyContactFactory *factory,
                                const gchar *bare_jid);

Returns an instance of WockyBareContact for bare_jid . The factory cache is used, but if the contact is not found in the cache, a new WockyBareContact is created and cached for future use.

Parameters

factory

a WockyContactFactory instance

 

bare_jid

the JID of a bare contact

 

Returns

a new reference to a WockyBareContact instance, which the caller is expected to release with g_object_unref() after use.


wocky_contact_factory_lookup_bare_contact ()

WockyBareContact *
wocky_contact_factory_lookup_bare_contact
                               (WockyContactFactory *factory,
                                const gchar *bare_jid);

Looks up if there's a WockyBareContact for bare_jid in the cache, and returns it if it's found.

Parameters

factory

a WockyContactFactory instance

 

bare_jid

the JID of a bare contact

 

Returns

a borrowed WockyBareContact instance (which the caller should reference with g_object_ref() if it will be kept), or NULL if the contact is not found.


wocky_contact_factory_ensure_resource_contact ()

WockyResourceContact *
wocky_contact_factory_ensure_resource_contact
                               (WockyContactFactory *factory,
                                const gchar *full_jid);

Returns an instance of WockyResourceContact for full_jid . The factory cache is used, but if the resource is not found in the cache, a new WockyResourceContact is created and cached for future use.

Parameters

factory

a WockyContactFactory instance

 

full_jid

the full JID of a resource

 

Returns

a new reference to a WockyResourceContact instance, which the caller is expected to release with g_object_unref() after use.


wocky_contact_factory_lookup_resource_contact ()

WockyResourceContact *
wocky_contact_factory_lookup_resource_contact
                               (WockyContactFactory *factory,
                                const gchar *full_jid);

Looks up if there's a WockyResourceContact for full_jid in the cache, and returns it if it's found.

Parameters

factory

a WockyContactFactory instance

 

full_jid

the full JID of a resource

 

Returns

a borrowed WockyResourceContact instance (which the caller should reference with g_object_ref() if it will be kept), or NULL if the contact is not found.


wocky_contact_factory_ensure_ll_contact ()

WockyLLContact *
wocky_contact_factory_ensure_ll_contact
                               (WockyContactFactory *factory,
                                const gchar *jid);

Returns an instance of WockyLLContact for jid . The factory cache is used, but if the contact is not found in the cache, a new WockyLLContact is created and cached for future use.

Parameters

factory

a WockyContactFactory instance

 

jid

the JID of a contact

 

Returns

a new reference to a WockyLLContact instance, which the caller is expected to release with g_object_unref() after use.


wocky_contact_factory_lookup_ll_contact ()

WockyLLContact *
wocky_contact_factory_lookup_ll_contact
                               (WockyContactFactory *factory,
                                const gchar *jid);

Looks up if there's a WockyLLContact for jid in the cache, and returns it if it's found.

Parameters

factory

a WockyContactFactory instance

 

jid

the JID of a contact

 

Returns

a borrowed WockyLLContact instance (which the caller should reference with g_object_ref() if it will be kept), or NULL if the contact is not found.


wocky_contact_factory_add_ll_contact ()

void
wocky_contact_factory_add_ll_contact (WockyContactFactory *factory,
                                      WockyLLContact *contact);

Adds contact to the contact factory.

Parameters

factory

a WockyContactFactory instance

 

contact

a WockyLLContact

 

wocky_contact_factory_get_ll_contacts ()

GList *
wocky_contact_factory_get_ll_contacts (WockyContactFactory *factory);

Parameters

factory

a WockyContactFactory instance

 

Returns

a newly allocated GList of WockyLLContacts which should be freed using g_list_free().

Types and Values

struct WockyContactFactoryClass

struct WockyContactFactoryClass {
};

The class of a WockyContactFactory.

Signal Details

The “bare-contact-added” signal

void
user_function (WockyContactFactory *wockycontactfactory,
               GObject             *arg1,
               gpointer             user_data)

Flags: Run Last


The “ll-contact-added” signal

void
user_function (WockyContactFactory *wockycontactfactory,
               GObject             *arg1,
               gpointer             user_data)

Flags: Run Last


The “resource-contact-added” signal

void
user_function (WockyContactFactory *wockycontactfactory,
               GObject             *arg1,
               gpointer             user_data)

Flags: Run Last

telepathy-gabble-0.18.2/lib/ext/wocky/docs/reference/html/WockyContact.html0000644000175000017500000001366712312537050026662 0ustar00smcvsmcv00000000000000 Wocky Reference Manual: WockyContact

WockyContact

WockyContact

Types and Values

Object Hierarchy

    GObject
    ╰── WockyContact
        ├── WockyBareContact
        ├── WockyLLContact
        ╰── WockyResourceContact

Includes

#include <wocky/wocky-contact.h>

Description

Functions

WockyContactDupJidImpl ()

gchar *
(*WockyContactDupJidImpl) (WockyContact *self);

wocky_contact_dup_jid ()

gchar *
wocky_contact_dup_jid (WockyContact *self);

Types and Values

struct WockyContactClass

struct WockyContactClass {
};

The class of a WockyContact.

telepathy-gabble-0.18.2/lib/ext/wocky/docs/reference/html/WockyDataForm.html0000644000175000017500000012544412312537050026761 0ustar00smcvsmcv00000000000000 Wocky Reference Manual: WockyDataForm

WockyDataForm

WockyDataForm — An object to represent an XMPP data form

Properties

gchar * instructions Read / Write / Construct Only
gchar * title Read / Write / Construct Only

Object Hierarchy

    GEnum
    ├── WockyDataFormError
    ╰── WockyDataFormFieldType
    GObject
    ╰── WockyDataForm

Includes

#include <wocky/wocky-data-form.h>

Description

An object that represents an XMPP data form as described in XEP-0004.

Functions

wocky_data_form_error_quark ()

GQuark
wocky_data_form_error_quark (void);

WOCKY_DATA_FORM_ERROR

#define WOCKY_DATA_FORM_ERROR (wocky_data_form_error_quark ())

wocky_data_form_new_from_form ()

WockyDataForm *
wocky_data_form_new_from_form (WockyNode *node,
                               GError **error);

wocky_data_form_new_from_node ()

WockyDataForm *
wocky_data_form_new_from_node (WockyNode *x,
                               GError **error);

wocky_data_form_set_type ()

gboolean
wocky_data_form_set_type (WockyDataForm *self,
                          const gchar *form_type);

Creates a hidden FORM_TYPE field in self and sets its value to form_type . This is intended only to be used on empty forms created for blind submission.

Parameters

self

a WockyDataForm

 

form_type

the URI to use as the FORM_TYPE field; may not be NULL

 

Returns

TRUE if the form's type was set; FALSE if the form already had a type.


wocky_data_form_set_boolean ()

gboolean
wocky_data_form_set_boolean (WockyDataForm *self,
                             const gchar *field_name,
                             gboolean field_value,
                             gboolean create_if_missing);

Parameters

self

a data form

 

field_name

the name of a boolean field of self

 

field_value

the value to fill in for field_name

 

create_if_missing

if no field named field_name exists, create it

 

Returns

TRUE if the field was successfully filled in; FALSE if the field did not exist or does not accept a boolean


wocky_data_form_set_string ()

gboolean
wocky_data_form_set_string (WockyDataForm *self,
                            const gchar *field_name,
                            const gchar *field_value,
                            gboolean create_if_missing);

Parameters

self

a data form

 

field_name

the name of a string field of self

 

field_value

the value to fill in for field_name

 

create_if_missing

if no field named field_name exists, create it

 

Returns

TRUE if the field was successfully filled in; FALSE if the field did not exist or does not accept a string


wocky_data_form_set_strv ()

gboolean
wocky_data_form_set_strv (WockyDataForm *self,
                          const gchar *field_name,
                          const gchar * const *field_values,
                          gboolean create_if_missing);

wocky_data_form_submit ()

void
wocky_data_form_submit (WockyDataForm *self,
                        WockyNode *node);

Adds a node tree which submits self based on the current values set on self 's fields.

Parameters

self

a data form

 

node

a node to which to add a form submission

 

wocky_data_form_parse_result ()

gboolean
wocky_data_form_parse_result (WockyDataForm *self,
                              WockyNode *node,
                              GError **error);

wocky_data_form_get_title ()

const gchar *
wocky_data_form_get_title (WockyDataForm *self);

wocky_data_form_get_instructions ()

const gchar *
wocky_data_form_get_instructions (WockyDataForm *self);

wocky_data_form_field_cmp ()

gint
wocky_data_form_field_cmp (const WockyDataFormField *left,
                           const WockyDataFormField *right);

wocky_data_form_add_to_node ()

void
wocky_data_form_add_to_node (WockyDataForm *self,
                             WockyNode *node);

Adds a node tree with default values of self based on the defaults set on each field when first created.

This function is for adding a data form to an existing node, like the query node of a disco response. wocky_data_form_submit(), in contrast, is for adding a node tree which submits the data form based on the current values set on its fields.

Parameters

self

the WockyDataForm object

 

node

a node to which to add the form

 

Types and Values

enum WockyDataFormFieldType

Data form field types, as documented in XEP-0004 §3.3.

Members

WOCKY_DATA_FORM_FIELD_TYPE_UNSPECIFIED

Unspecified field type

 

WOCKY_DATA_FORM_FIELD_TYPE_BOOLEAN

Boolean field type

 

WOCKY_DATA_FORM_FIELD_TYPE_FIXED

Fixed description field type

 

WOCKY_DATA_FORM_FIELD_TYPE_HIDDEN

Hidden field type

 

WOCKY_DATA_FORM_FIELD_TYPE_JID_MULTI

A list of multiple JIDs

 

WOCKY_DATA_FORM_FIELD_TYPE_JID_SINGLE

A single JID

 

WOCKY_DATA_FORM_FIELD_TYPE_LIST_MULTI

Many options to choose one or more from

 

WOCKY_DATA_FORM_FIELD_TYPE_LIST_SINGLE

Many options to choose one from

 

WOCKY_DATA_FORM_FIELD_TYPE_TEXT_MULTI

Multiple lines of text

 

WOCKY_DATA_FORM_FIELD_TYPE_TEXT_PRIVATE

A single line of text that should be obscured (by, say, asterisks)

 

WOCKY_DATA_FORM_FIELD_TYPE_TEXT_SINGLE

A single line of text

 

struct WockyDataFormFieldOption

struct WockyDataFormFieldOption {
  gchar *label;
  gchar *value;
};

A single data form field option.

Members

gchar *label;

the option label

 

gchar *value;

the option value

 

struct WockyDataFormField

struct WockyDataFormField {
  WockyDataFormFieldType type;
  gchar *var;
  gchar *label;
  gchar *desc;
  gboolean required;
  GValue *default_value;
  gchar **raw_value_contents;
  GValue *value;
  /* for LIST_MULTI and LIST_SINGLE only.
   * List of (WockyDataFormFieldOption *)*/
  GSList *options;
};

Details about a single data form field in a WockyDataForm.

Members

WockyDataFormFieldType type;

the type of the field

 

gchar *var;

the field name

 

gchar *label;

the label of the field

 

gchar *desc;

the description of the field

 

gboolean required;

TRUE if the field is required, otherwise FALSE

 

GValue *default_value;

the default of the field

 

gchar **raw_value_contents;

a NULL-terminated array holding the literal value(s) as specified in the original XML. For example, this might be something like

{ "1", NULL }

or

{ "false", NULL }

for a WOCKY_DATA_FORM_FIELD_TYPE_BOOLEAN field, or

{ "hi", "there", NULL }

for a WOCKY_DATA_FORM_FIELD_TYPE_TEXT_MULTI field.

 

GValue *value;

the field value

 

GSList *options;

a GSList of WockyDataFormFieldOptions if type if WOCKY_DATA_FORM_FIELD_TYPE_LIST_MULTI or WOCKY_DATA_FORM_FIELD_TYPE_LIST_SINGLE

 

enum WockyDataFormError

WockyDataForm specific errors.

Members

WOCKY_DATA_FORM_ERROR_NOT_FORM

Node is not a data form

 

WOCKY_DATA_FORM_ERROR_WRONG_TYPE

Data form is of the wrong type

 

struct WockyDataFormClass

struct WockyDataFormClass {
};

The class of a WockyDataForm.


struct WockyDataForm

struct WockyDataForm {
  /* (gchar *) owned by the WockyDataFormField =>
   * borrowed (WockyDataFormField *) */
  GHashTable *fields;
  /* list containing owned (WockyDataFormField *) in the order they
   * have been presented in the form */
  GSList *fields_list;

  /* list of GSList * of (WockyDataFormField *), representing one or more sets
   * of results */
  GSList *results;
};

An object that represents an XMPP data form as described in XEP-0004.

Members

GHashTable *fields;

a GHashTable of strings to WockyDataFormFields

 

GSList *fields_list;

a list of WockyDataFormFields in the order they have been presented in the form

 

GSList *results;

a list of GSLists of WockyDataFormFields representing one or more sets of result.

 

Property Details

The “instructions” property

  “instructions”             gchar *

Instructions.

Flags: Read / Write / Construct Only

Default value: NULL


The “title” property

  “title”                    gchar *

Title.

Flags: Read / Write / Construct Only

Default value: NULL

telepathy-gabble-0.18.2/lib/ext/wocky/docs/reference/html/WockyDiscoIdentity.html0000644000175000017500000005105512312537050030033 0ustar00smcvsmcv00000000000000 Wocky Reference Manual: WockyDiscoIdentity

WockyDiscoIdentity

WockyDiscoIdentity — Structure holding XMPP disco identity information.

Types and Values

Object Hierarchy

    GBoxed
    ╰── WockyDiscoIdentity

Description

Contains information regarding the identity information in disco replies, as described in XEP-0030.

Functions

wocky_disco_identity_new ()

WockyDiscoIdentity *
wocky_disco_identity_new (const gchar *category,
                          const gchar *type,
                          const gchar *lang,
                          const gchar *name);

Parameters

category

disco category

 

type

disco type

 

lang

disco language

 

name

disco name

 

Returns

a new WockyDiscoIdentity which should be freed using wocky_disco_identity_free().


wocky_disco_identity_copy ()

WockyDiscoIdentity *
wocky_disco_identity_copy (const WockyDiscoIdentity *source);

Creates a new WockyDiscoIdentity structure with the data given by source . The copy also copies the internal data so source can be freed after this function is called.

Parameters

source

the WockyDiscoIdentity to copy

 

Returns

a new WockyDiscoIdentity which is a deep copy of source


wocky_disco_identity_free ()

void
wocky_disco_identity_free (WockyDiscoIdentity *identity);

Frees the memory used by identity .

Parameters

identity

a WockyDiscoIdentity

 

wocky_disco_identity_cmp ()

gint
wocky_disco_identity_cmp (WockyDiscoIdentity *left,
                          WockyDiscoIdentity *right);

Compares left and right . It returns an integer less than, equal to, or greater than zero if left is found, respectively, to be less than, to match, or be greater than right.

This function can be casted to a GCompareFunc to sort a list of WockyDiscoIdentity structures.

Parameters

left

a WockyDiscoIdentity

 

right

a WockyDiscoIdentity

 

Returns

the result of comparing left and right


wocky_disco_identity_array_new ()

GPtrArray *
wocky_disco_identity_array_new (void);

Creates a new array of WockyDiscoIdentity structures.

Returns

A newly instantiated array. wocky_disco_identity_array_free() should beq used to free the memory allocated by this array. See: wocky_disco_identity_array_free()


wocky_disco_identity_array_copy ()

GPtrArray *
wocky_disco_identity_array_copy (const GPtrArray *source);

Copies an array of WockyDiscoIdentity objects. The returned array contains new copies of the contents of the source array.

Parameters

source

The source array to be copied.

 

Returns

A newly instantiated array with new copies of the contents of the source array. See: wocky_disco_identity_array_new()


wocky_disco_identity_array_free ()

void
wocky_disco_identity_array_free (GPtrArray *arr);

Frees an array of WockyDiscoIdentity objects created with wocky_disco_identity_array_new() or returned by wocky_disco_identity_array_copy().

Note that if this method is called with an array created with g_ptr_array_new(), the caller should also free the array contents.

See: wocky_disco_identity_array_new(), wocky_disco_identity_array_copy()

Parameters

arr

Array to be freed.

 

Types and Values

struct WockyDiscoIdentity

struct WockyDiscoIdentity {
  gchar *category;
  gchar *type;
  gchar *lang;
  gchar *name;
};

A structure used to hold information regarding an identity from a disco reply as described in XEP-0030.

Members

gchar *category;

the identity category

 

gchar *type;

the identity type

 

gchar *lang;

the identity language

 

gchar *name;

the identity name

 
telepathy-gabble-0.18.2/lib/ext/wocky/docs/reference/html/WockyJabberAuth.html0000644000175000017500000003547112312537050027273 0ustar00smcvsmcv00000000000000 Wocky Reference Manual: WockyJabberAuth

WockyJabberAuth

WockyJabberAuth

Properties

WockyAuthRegistry * auth-registry Read / Write / Construct Only
WockyXmppConnection * connection Read / Write / Construct Only
gchar * password Write / Construct
gchar * resource Write / Construct
gchar * session-id Read / Write / Construct
gchar * username Write / Construct

Types and Values

Object Hierarchy

    GObject
    ╰── WockyJabberAuth

Description

Functions

wocky_jabber_auth_new ()

WockyJabberAuth *
wocky_jabber_auth_new (const gchar *server,
                       const gchar *username,
                       const gchar *resource,
                       const gchar *password,
                       WockyXmppConnection *connection,
                       WockyAuthRegistry *auth_registry);

wocky_jabber_auth_add_handler ()

void
wocky_jabber_auth_add_handler (WockyJabberAuth *self,
                               WockyAuthHandler *handler);

wocky_jabber_auth_authenticate_async ()

void
wocky_jabber_auth_authenticate_async (WockyJabberAuth *self,
                                      gboolean allow_plain,
                                      gboolean is_secure,
                                      GCancellable *cancellable,
                                      GAsyncReadyCallback callback,
                                      gpointer user_data);

wocky_jabber_auth_authenticate_finish ()

gboolean
wocky_jabber_auth_authenticate_finish (WockyJabberAuth *self,
                                       GAsyncResult *result,
                                       GError **error);

Types and Values

struct WockyJabberAuthClass

struct WockyJabberAuthClass {
};

The class of a WockyJabberAuth.

Property Details

The “auth-registry” property

  “auth-registry”            WockyAuthRegistry *

Authentication Registry.

Flags: Read / Write / Construct Only


The “connection” property

  “connection”               WockyXmppConnection *

The Xmpp connection to user.

Flags: Read / Write / Construct Only


The “password” property

  “password”                 gchar *

The password to authenticate with.

Flags: Write / Construct

Default value: NULL


The “resource” property

  “resource”                 gchar *

The XMPP resource to bind to.

Flags: Write / Construct

Default value: NULL


The “session-id” property

  “session-id”               gchar *

The XMPP session ID.

Flags: Read / Write / Construct

Default value: NULL


The “username” property

  “username”                 gchar *

The username to authenticate with.

Flags: Write / Construct

Default value: NULL

telepathy-gabble-0.18.2/lib/ext/wocky/docs/reference/html/WockyJingleContent.html0000644000175000017500000015554212312537050030031 0ustar00smcvsmcv00000000000000 Wocky Reference Manual: WockyJingleContent

WockyJingleContent

WockyJingleContent

Functions

void wocky_jingle_content_parse_add ()
void wocky_jingle_content_update_senders ()
void wocky_jingle_content_produce_node ()
void wocky_jingle_content_parse_accept ()
void wocky_jingle_content_parse_info ()
void wocky_jingle_content_parse_transport_info ()
void wocky_jingle_content_parse_description_info ()
guint wocky_jingle_content_create_share_channel ()
void wocky_jingle_content_add_candidates ()
gboolean wocky_jingle_content_is_ready ()
void wocky_jingle_content_set_transport_state ()
void wocky_jingle_content_remove ()
void wocky_jingle_content_reject ()
GList * wocky_jingle_content_get_remote_candidates ()
GList * wocky_jingle_content_get_local_candidates ()
gboolean wocky_jingle_content_get_credentials ()
gboolean wocky_jingle_content_change_direction ()
void wocky_jingle_content_retransmit_candidates ()
void wocky_jingle_content_inject_candidates ()
gboolean wocky_jingle_content_is_created_by_us ()
gboolean wocky_jingle_content_creator_is_initiator ()
const gchar * wocky_jingle_content_get_name ()
const gchar * wocky_jingle_content_get_ns ()
const gchar * wocky_jingle_content_get_disposition ()
WockyJingleTransportType wocky_jingle_content_get_transport_type ()
const gchar * wocky_jingle_content_get_transport_ns ()
void wocky_jingle_content_maybe_send_description ()
gboolean wocky_jingle_content_sending ()
gboolean wocky_jingle_content_receiving ()
void wocky_jingle_content_set_sending ()
void wocky_jingle_content_request_receiving ()
void wocky_jingle_content_send_complete ()

Properties

gchar * content-ns Read / Write
gchar * disposition Read / Write
gboolean locally-created Read
gchar * name Read / Write / Construct Only
guint senders Read / Write
WockyJingleSession * session Read / Write / Construct Only
guint state Read / Write
gchar * transport-ns Read / Write

Object Hierarchy

    GObject
    ╰── WockyJingleContent
        ╰── WockyJingleMediaRtp

Description

Functions

wocky_jingle_content_parse_add ()

void
wocky_jingle_content_parse_add (WockyJingleContent *c,
                                WockyNode *content_node,
                                gboolean google_mode,
                                GError **error);

wocky_jingle_content_update_senders ()

void
wocky_jingle_content_update_senders (WockyJingleContent *c,
                                     WockyNode *content_node,
                                     GError **error);

wocky_jingle_content_produce_node ()

void
wocky_jingle_content_produce_node (WockyJingleContent *c,
                                   WockyNode *parent,
                                   gboolean include_description,
                                   gboolean include_transport,
                                   WockyNode **trans_node_out);

wocky_jingle_content_parse_accept ()

void
wocky_jingle_content_parse_accept (WockyJingleContent *c,
                                   WockyNode *content_node,
                                   gboolean google_mode,
                                   GError **error);

wocky_jingle_content_parse_info ()

void
wocky_jingle_content_parse_info (WockyJingleContent *c,
                                 WockyNode *content_node,
                                 GError **error);

wocky_jingle_content_parse_transport_info ()

void
wocky_jingle_content_parse_transport_info
                               (WockyJingleContent *self,
                                WockyNode *trans_node,
                                GError **error);

wocky_jingle_content_parse_description_info ()

void
wocky_jingle_content_parse_description_info
                               (WockyJingleContent *self,
                                WockyNode *trans_node,
                                GError **error);

wocky_jingle_content_create_share_channel ()

guint
wocky_jingle_content_create_share_channel
                               (WockyJingleContent *self,
                                const gchar *name);

wocky_jingle_content_add_candidates ()

void
wocky_jingle_content_add_candidates (WockyJingleContent *self,
                                     GList *li);

Adds the candidates listed in li to the content, communicating them to the peer if appropriate.

Parameters

self

the content

 

li

a list of WockyJingleCandidate structs, allocated with wocky_jingle_candidate_new().

[element-type WockyJingleCandidate][transfer full]

wocky_jingle_content_is_ready ()

gboolean
wocky_jingle_content_is_ready (WockyJingleContent *self);

wocky_jingle_content_set_transport_state ()

void
wocky_jingle_content_set_transport_state
                               (WockyJingleContent *content,
                                WockyJingleTransportState state);

wocky_jingle_content_remove ()

void
wocky_jingle_content_remove (WockyJingleContent *c,
                             gboolean signal_peer);

wocky_jingle_content_reject ()

void
wocky_jingle_content_reject (WockyJingleContent *c,
                             WockyJingleReason reason);

wocky_jingle_content_get_remote_candidates ()

GList *
wocky_jingle_content_get_remote_candidates
                               (WockyJingleContent *c);

wocky_jingle_content_get_local_candidates ()

GList *
wocky_jingle_content_get_local_candidates
                               (WockyJingleContent *c);

wocky_jingle_content_get_credentials ()

gboolean
wocky_jingle_content_get_credentials (WockyJingleContent *c,
                                      gchar **ufrag,
                                      gchar **pwd);

wocky_jingle_content_change_direction ()

gboolean
wocky_jingle_content_change_direction (WockyJingleContent *c,
                                       WockyJingleContentSenders senders);

wocky_jingle_content_retransmit_candidates ()

void
wocky_jingle_content_retransmit_candidates
                               (WockyJingleContent *self,
                                gboolean all);

wocky_jingle_content_inject_candidates ()

void
wocky_jingle_content_inject_candidates
                               (WockyJingleContent *self,
                                WockyNode *transport_node);

wocky_jingle_content_is_created_by_us ()

gboolean
wocky_jingle_content_is_created_by_us (WockyJingleContent *c);

wocky_jingle_content_creator_is_initiator ()

gboolean
wocky_jingle_content_creator_is_initiator
                               (WockyJingleContent *c);

wocky_jingle_content_get_name ()

const gchar *
wocky_jingle_content_get_name (WockyJingleContent *self);

wocky_jingle_content_get_ns ()

const gchar *
wocky_jingle_content_get_ns (WockyJingleContent *self);

wocky_jingle_content_get_disposition ()

const gchar *
wocky_jingle_content_get_disposition (WockyJingleContent *self);

wocky_jingle_content_get_transport_type ()

WockyJingleTransportType
wocky_jingle_content_get_transport_type
                               (WockyJingleContent *c);

wocky_jingle_content_get_transport_ns ()

const gchar *
wocky_jingle_content_get_transport_ns (WockyJingleContent *self);

wocky_jingle_content_maybe_send_description ()

void
wocky_jingle_content_maybe_send_description
                               (WockyJingleContent *self);

wocky_jingle_content_sending ()

gboolean
wocky_jingle_content_sending (WockyJingleContent *self);

wocky_jingle_content_receiving ()

gboolean
wocky_jingle_content_receiving (WockyJingleContent *self);

wocky_jingle_content_set_sending ()

void
wocky_jingle_content_set_sending (WockyJingleContent *self,
                                  gboolean send);

wocky_jingle_content_request_receiving ()

void
wocky_jingle_content_request_receiving
                               (WockyJingleContent *self,
                                gboolean receive);

wocky_jingle_content_send_complete ()

void
wocky_jingle_content_send_complete (WockyJingleContent *self);

Types and Values

enum WockyJingleMediaType

Members

WOCKY_JINGLE_MEDIA_TYPE_NONE

   

WOCKY_JINGLE_MEDIA_TYPE_AUDIO

   

WOCKY_JINGLE_MEDIA_TYPE_VIDEO

   

enum WockyJingleContentState

Members

WOCKY_JINGLE_CONTENT_STATE_EMPTY

   

WOCKY_JINGLE_CONTENT_STATE_NEW

   

WOCKY_JINGLE_CONTENT_STATE_SENT

   

WOCKY_JINGLE_CONTENT_STATE_ACKNOWLEDGED

   

WOCKY_JINGLE_CONTENT_STATE_REMOVING

   

WockyJingleCandidate

typedef struct {
  WockyJingleTransportProtocol protocol;
  WockyJingleCandidateType type;

  gchar *id;
  gchar *address;
  int port;
  int component;
  int generation;

  int preference;
  gchar *username;
  gchar *password;
  int network;
} WockyJingleCandidate;

Property Details

The “content-ns” property

  “content-ns”               gchar *

Namespace identifying the content type.

Flags: Read / Write

Default value: NULL


The “disposition” property

  “disposition”              gchar *

Distinguishes between 'session' and other contents.

Flags: Read / Write

Default value: NULL


The “locally-created” property

  “locally-created”          gboolean

True if the content was created by the local client.

Flags: Read

Default value: FALSE


The “name” property

  “name”                     gchar *

A unique content name in the session.

Flags: Read / Write / Construct Only

Default value: NULL


The “senders” property

  “senders”                  guint

Valid senders for the stream.

Flags: Read / Write

Default value: 0


The “session” property

  “session”                  WockyJingleSession *

Jingle session object that owns this content.

Flags: Read / Write / Construct Only


The “state” property

  “state”                    guint

The current state that the content is in.

Flags: Read / Write

Default value: 0


The “transport-ns” property

  “transport-ns”             gchar *

Namespace identifying the transport type.

Flags: Read / Write

Default value: NULL

Signal Details

The “completed” signal

void
user_function (WockyJingleContent *wockyjinglecontent,
               gpointer            user_data)

Flags: Run Last


The “new-candidates” signal

void
user_function (WockyJingleContent *content,
               gpointer            candidates,
               gpointer            user_data)

Emitted when new candidates are received from the peer.

Parameters

content

the content

 

candidates

a GList of new candidates.

[type GList][element-type WockyJingleCandidate]

user_data

user data set when the signal handler was connected.

 

Flags: Run Last


The “new-share-channel” signal

void
user_function (WockyJingleContent *wockyjinglecontent,
               gchar              *arg1,
               guint               arg2,
               gpointer            user_data)

Flags: Run Last


The “ready” signal

void
user_function (WockyJingleContent *wockyjinglecontent,
               gpointer            user_data)

Flags: Has Details


The “removed” signal

void
user_function (WockyJingleContent *wockyjinglecontent,
               gpointer            user_data)

Flags: Has Details

telepathy-gabble-0.18.2/lib/ext/wocky/docs/reference/html/WockyJingleFactory.html0000644000175000017500000004512712312537050030023 0ustar00smcvsmcv00000000000000 Wocky Reference Manual: WockyJingleFactory

WockyJingleFactory

WockyJingleFactory

Properties

WockySession * session Read / Write / Construct Only

Object Hierarchy

    GObject
    ╰── WockyJingleFactory

Description

Functions

wocky_jingle_factory_new ()

WockyJingleFactory *
wocky_jingle_factory_new (WockySession *session);

wocky_jingle_factory_stop ()

void
wocky_jingle_factory_stop (WockyJingleFactory *self);

wocky_jingle_factory_register_content_type ()

void
wocky_jingle_factory_register_content_type
                               (WockyJingleFactory *self,
                                gchar *xmlns,
                                GType content_type);

wocky_jingle_factory_lookup_content_type ()

GType
wocky_jingle_factory_lookup_content_type
                               (WockyJingleFactory *self,
                                const gchar *xmlns);

wocky_jingle_factory_register_transport ()

void
wocky_jingle_factory_register_transport
                               (WockyJingleFactory *self,
                                gchar *xmlns,
                                GType transport_type);

wocky_jingle_factory_lookup_transport ()

GType
wocky_jingle_factory_lookup_transport (WockyJingleFactory *self,
                                       const gchar *xmlns);

wocky_jingle_factory_create_session ()

WockyJingleSession *
wocky_jingle_factory_create_session (WockyJingleFactory *fac,
                                     const gchar *jid,
                                     WockyJingleDialect dialect,
                                     gboolean local_hold);

Creates a new WockyJingleSession to the specified contact. Note that the session will not be initiated until at least one content is added with wocky_jingle_session_add_content(), and those contents are ready.

You would typically determine which dialect to use from the peer's capabilities.

Parameters

fac

the factory

 

jid

the full JID (typically including a resource) to establish a session with

 

dialect

the variant of the Jingle protocol to use

 

local_hold

whether the call should start out on hold; if in doubt, pass FALSE

 

Returns

the new session, which will not be NULL.

[transfer none]


wocky_jingle_factory_get_jingle_info ()

WockyJingleInfo *
wocky_jingle_factory_get_jingle_info (WockyJingleFactory *fac);

Types and Values

Property Details

The “session” property

  “session”                  WockySession *

WockySession to listen for Jingle sessions on.

Flags: Read / Write / Construct Only

Signal Details

The “new-session” signal

void
user_function (WockyJingleFactory *wockyjinglefactory,
               WockyJingleSession *arg1,
               gboolean            arg2,
               gpointer            user_data)

Flags: Run Last


The “query-cap” signal

gboolean
user_function (WockyJingleFactory *wockyjinglefactory,
               WockyContact       *arg1,
               gchar              *arg2,
               gpointer            user_data)

Flags: Run Last

telepathy-gabble-0.18.2/lib/ext/wocky/docs/reference/html/WockyJingleInfo.html0000644000175000017500000004624212312537050027306 0ustar00smcvsmcv00000000000000 Wocky Reference Manual: WockyJingleInfo

WockyJingleInfo

WockyJingleInfo

Properties

WockyC2SPorter * porter Read / Write / Construct Only

Object Hierarchy

    GObject
    ╰── WockyJingleInfo

Description

Functions

wocky_jingle_info_new ()

WockyJingleInfo *
wocky_jingle_info_new (WockyPorter *porter);

wocky_jingle_info_take_stun_server ()

void
wocky_jingle_info_take_stun_server (WockyJingleInfo *self,
                                    gchar *stun_server,
                                    guint16 stun_port,
                                    gboolean is_fallback);

wocky_jingle_info_send_request ()

void
wocky_jingle_info_send_request (WockyJingleInfo *self,
                                gboolean google_jingleinfo_supported);

wocky_jingle_info_get_stun_servers ()

GList *
wocky_jingle_info_get_stun_servers (WockyJingleInfo *self);

wocky_jingle_info_get_google_relay_token ()

const gchar *
wocky_jingle_info_get_google_relay_token
                               (WockyJingleInfo *self);

wocky_jingle_relay_new ()

WockyJingleRelay *
wocky_jingle_relay_new (WockyJingleRelayType type,
                        const gchar *ip,
                        guint port,
                        const gchar *username,
                        const gchar *password,
                        guint component);

wocky_jingle_relay_free ()

void
wocky_jingle_relay_free (WockyJingleRelay *relay);

WockyJingleInfoRelaySessionCb ()

void
(*WockyJingleInfoRelaySessionCb) (GPtrArray *relays,
                                  gpointer user_data);

wocky_jingle_info_create_google_relay_session ()

void
wocky_jingle_info_create_google_relay_session
                               (WockyJingleInfo *self,
                                guint components,
                                WockyJingleInfoRelaySessionCb callback,
                                gpointer user_data);

wocky_jingle_info_set_test_mode ()

void
wocky_jingle_info_set_test_mode (void);

Types and Values

WockyStunServer

typedef struct {
    gchar *address;
    guint16 port;
} WockyStunServer;

enum WockyJingleRelayType

Members

WOCKY_JINGLE_RELAY_TYPE_UDP

   

WOCKY_JINGLE_RELAY_TYPE_TCP

   

WOCKY_JINGLE_RELAY_TYPE_TLS

   

WOCKY_N_JINGLE_RELAY_TYPES

#define WOCKY_N_JINGLE_RELAY_TYPES 3

WockyJingleRelay

typedef struct {
    WockyJingleRelayType type;
    gchar *ip;
    guint port;
    gchar *username;
    gchar *password;
    guint component;
} WockyJingleRelay;

Property Details

The “porter” property

  “porter”                   WockyC2SPorter *

Porter for the current connection.

Flags: Read / Write / Construct Only

Signal Details

The “stun-server-changed” signal

void
user_function (WockyJingleInfo *wockyjingleinfo,
               gchar           *arg1,
               guint            arg2,
               gpointer         user_data)

Flags: Run Last

telepathy-gabble-0.18.2/lib/ext/wocky/docs/reference/html/WockyJingleMediaRtp.html0000644000175000017500000011333712312537050030120 0ustar00smcvsmcv00000000000000 Wocky Reference Manual: WockyJingleMediaRtp

WockyJingleMediaRtp

WockyJingleMediaRtp

Properties

guint media-type Read / Write / Construct Only
gboolean remote-mute Read / Write

Object Hierarchy

    GObject
    ╰── WockyJingleContent
        ╰── WockyJingleMediaRtp

Description

Functions

jingle_media_rtp_register ()

void
jingle_media_rtp_register (WockyJingleFactory *factory);

jingle_media_rtp_set_local_media_description ()

gboolean
jingle_media_rtp_set_local_media_description
                               (WockyJingleMediaRtp *self,
                                WockyJingleMediaDescription *md,
                                gboolean ready,
                                GError **error);

Sets or updates the media description (codecs, feedback messages, etc) for self .

Parameters

self

a content in an RTP session

 

md

new media description for this content.

[transfer full]

ready

whether the codecs can regarded as ready to sent from now on

 

error

used to return a WOCKY_XMPP_ERROR if the codec update is illegal.

 

Returns

TRUE if no description was previously set, or if the update is compatible with the existing description; FALSE if the update is illegal (due to adding previously-unknown codecs or renaming an existing codec, for example)


wocky_jingle_media_rtp_get_remote_media_description ()

WockyJingleMediaDescription *
wocky_jingle_media_rtp_get_remote_media_description
                               (WockyJingleMediaRtp *self);

Gets the current remote media description, if known. The “remote-media-description” signal is emitted when this value changes.

Parameters

self

the RTP content

 

Returns

the current remote media description, which may be NULL for outgoing calls until it is first received.

[transfer none]


jingle_media_rtp_codec_new ()

WockyJingleCodec *
jingle_media_rtp_codec_new (guint id,
                            const gchar *name,
                            guint clockrate,
                            guint channels,
                            GHashTable *params);

Creates a new structure describing a codec, suitable for including in a WockyJingleMediaDescription.

Parameters

id

a codec ID, as specified in tables 4 and 5 of RFC 3551.

 

name

the codec's name. This is optional if id is one of the statically-defined codec identifiers, and required if id is in the range 96–127. (This is not enforced by this library.).

[allow-none]

clockrate

the clock rate for this codec, or 0 to not specify a clock rate.

 

channels

the number of channels, or 0 to leave this unspecified (which the peer should interpret as the default value, 1).

 

params

parameters for this codec. This is referenced, not copied, so you should avoid modifying this parameter after calling this function.

[element-type utf8 utf8][transfer none][allow-none]

Returns

the codec description.

[transfer full]


jingle_media_rtp_codec_free ()

void
jingle_media_rtp_codec_free (WockyJingleCodec *p);

jingle_media_rtp_free_codecs ()

void
jingle_media_rtp_free_codecs (GList *codecs);

jingle_media_rtp_copy_codecs ()

GList *
jingle_media_rtp_copy_codecs (GList *codecs);

jingle_media_rtp_compare_codecs ()

gboolean
jingle_media_rtp_compare_codecs (GList *old,
                                 GList *new,
                                 GList **changed,
                                 GError **e);

wocky_jingle_media_description_new ()

WockyJingleMediaDescription *
wocky_jingle_media_description_new (void);

Allocates a new media description. You should fill in all the fields yourself.

Returns

a new, empty, media description


wocky_jingle_media_description_free ()

void
wocky_jingle_media_description_free (WockyJingleMediaDescription *md);

wocky_jingle_media_description_copy ()

WockyJingleMediaDescription *
wocky_jingle_media_description_copy (WockyJingleMediaDescription *md);

Performs a deep copy of a media description.

Parameters

md

a media description

 

Returns

a deep copy of md .

[transfer full]


wocky_jingle_rtp_header_extension_new ()

WockyJingleRtpHeaderExtension *
wocky_jingle_rtp_header_extension_new (guint id,
                                       WockyJingleContentSenders senders,
                                       const gchar *uri);

wocky_jingle_rtp_header_extension_free ()

void
wocky_jingle_rtp_header_extension_free
                               (WockyJingleRtpHeaderExtension *hdrext);

wocky_jingle_feedback_message_new ()

WockyJingleFeedbackMessage *
wocky_jingle_feedback_message_new (const gchar *type,
                                   const gchar *subtype);

wocky_jingle_feedback_message_free ()

void
wocky_jingle_feedback_message_free (WockyJingleFeedbackMessage *fb);

wocky_jingle_media_description_simplify ()

void
wocky_jingle_media_description_simplify
                               (WockyJingleMediaDescription *md);

Removes duplicated Feedback message and put them in the global structure

This function will iterate over every codec in a description and look for feedback messages that are exactly the same in every codec and will instead put the in the list in the description and remove them from the childs. This limits the amount of duplication in the resulting XML.

Parameters

md

a description to simplify

 

Types and Values

WockyJingleCodec

typedef struct {
  guint id;
  gchar *name;
  guint clockrate;
  guint channels;
  GHashTable *params;
  guint trr_int;
  GList *feedback_msgs;
} WockyJingleCodec;

WockyJingleFeedbackMessage

typedef struct {
  gchar *type;
  gchar *subtype;
} WockyJingleFeedbackMessage;

WockyJingleRtpHeaderExtension

typedef struct {
  guint id;
  WockyJingleContentSenders senders;
  gchar *uri;
} WockyJingleRtpHeaderExtension;

WockyJingleMediaDescription

typedef struct {
  GList *codecs;
  GList *hdrexts;
  guint trr_int;
  GList *feedback_msgs;
} WockyJingleMediaDescription;

Media description for a WockyJingleMediaRtp content.

Members

GList *codecs;

a list of WockyJingleCodecs, allocated with jingle_media_rtp_codec_new()

 

GList *hdrexts;

a list of WockyJingleRtpHeaderExtensions, allocated with wocky_jingle_rtp_header_extension_new()

 

guint trr_int;

number of milliseconds between regular RTCP reports

 

GList *feedback_msgs;

a list of WockyJingleFeedbackMessages, allocated with wocky_jingle_feedback_message_new()

 

Property Details

The “media-type” property

  “media-type”               guint

Media type.

Flags: Read / Write / Construct Only

Default value: 0


The “remote-mute” property

  “remote-mute”              gboolean

TRUE if the peer has muted this stream.

Flags: Read / Write

Default value: FALSE

Signal Details

The “remote-media-description” signal

void
user_function (WockyJingleMediaRtp *content,
               gpointer             md,
               gpointer             user_data)

Emitted when the remote media description is received or subsequently updated.

Parameters

content

the RTP content

 

md

a WockyJingleMediaDescription

 

user_data

user data set when the signal handler was connected.

 

Flags: Run Last

telepathy-gabble-0.18.2/lib/ext/wocky/docs/reference/html/WockyJingleSession.html0000644000175000017500000016405412312537050030040 0ustar00smcvsmcv00000000000000 Wocky Reference Manual: WockyJingleSession

WockyJingleSession

WockyJingleSession

Properties

guint dialect Read / Write
WockyJingleFactory * jingle-factory Read / Write / Construct Only
gboolean local-hold Read / Write
gboolean local-initiator Read / Write / Construct Only
WockyContact * peer-contact Read / Write / Construct Only
WockyPorter * porter Read / Write / Construct Only
gboolean remote-hold Read
gboolean remote-ringing Read
gchar * session-id Read / Write / Construct Only
guint state Read / Write

Object Hierarchy

    GObject
    ╰── WockyJingleSession

Description

Functions

wocky_jingle_session_new ()

WockyJingleSession *
wocky_jingle_session_new (WockyJingleFactory *factory,
                          WockyPorter *porter,
                          const gchar *session_id,
                          gboolean local_initiator,
                          WockyContact *peer,
                          WockyJingleDialect dialect,
                          gboolean local_hold);

wocky_jingle_session_detect ()

const gchar *
wocky_jingle_session_detect (WockyStanza *stanza,
                             WockyJingleAction *action,
                             WockyJingleDialect *dialect);

wocky_jingle_session_parse ()

gboolean
wocky_jingle_session_parse (WockyJingleSession *sess,
                            WockyJingleAction action,
                            WockyStanza *stanza,
                            GError **error);

wocky_jingle_session_new_message ()

WockyStanza *
wocky_jingle_session_new_message (WockyJingleSession *sess,
                                  WockyJingleAction action,
                                  WockyNode **sess_node);

wocky_jingle_session_accept ()

void
wocky_jingle_session_accept (WockyJingleSession *sess);

For incoming calls, accepts the call. For outgoing calls, indicates that the initial contents for the call have been created and the offer can be sent to the peer.

The acceptance or offer will only be signalled to the peer once all contents are ready (as returned by wocky_jingle_content_is_ready()). For an RTP session with WockyJingleMediaRtp contents, this translates to a media description and transport candidates having been provided to all contents.

Parameters

sess

the session.

 

wocky_jingle_session_terminate ()

gboolean
wocky_jingle_session_terminate (WockyJingleSession *sess,
                                WockyJingleReason reason,
                                const gchar *text,
                                GError **error);

Ends a session.

If called for an outgoing session which has not yet been signalled to the peer (perhaps because wocky_jingle_session_accept() has not been called, or codecs or candidates have not been provided), the session will quietly terminate without the peer hearing anything about it.

If called for an already-terminated session, this is a no-op.

Parameters

sess

the session

 

reason

the reason the session should be terminated

 

text

human-readable information about why the session terminated.

[allow-none]

error

Unused, because this function never fails.

 

Returns

TRUE.


wocky_jingle_session_remove_content ()

void
wocky_jingle_session_remove_content (WockyJingleSession *sess,
                                     WockyJingleContent *c);

wocky_jingle_session_add_content ()

WockyJingleContent *
wocky_jingle_session_add_content (WockyJingleSession *sess,
                                  WockyJingleMediaType mtype,
                                  WockyJingleContentSenders senders,
                                  const char *name,
                                  const gchar *content_ns,
                                  const gchar *transport_ns);

Adds a content to the session. Once it has its codecs and transport candidates filled in, it will be signalled to the peer (either as part of the session-initiate, if it has not been sent yet, or as a content-add if sess has already been initiated).

Legal values for content_ns and transport_ns depend on the Jingle dialect in use for this session (and in some cases on mtype ); sensible values depend on the peer's capabilities.

Parameters

sess

the session

 

mtype

what kind of media will be exchanged on the content

 

senders

which directions media should initially flow in.

 

name

a descriptive name to use for the content; this is typically not shown to users.

[allow-none]

content_ns

the namespace to use for the content's description

 

transport_ns

the namespace of the media transport to use for the call

 

Returns

the new content, which is guaranteed not to be NULL.

[transfer none]


wocky_jingle_session_get_content_type ()

GType
wocky_jingle_session_get_content_type (WockyJingleSession *Param1);

wocky_jingle_session_get_contents ()

GList *
wocky_jingle_session_get_contents (WockyJingleSession *sess);

wocky_jingle_session_get_peer_resource ()

const gchar *
wocky_jingle_session_get_peer_resource
                               (WockyJingleSession *sess);

wocky_jingle_session_get_initiator ()

const gchar *
wocky_jingle_session_get_initiator (WockyJingleSession *sess);

wocky_jingle_session_get_sid ()

const gchar *
wocky_jingle_session_get_sid (WockyJingleSession *sess);

wocky_jingle_session_get_dialect ()

WockyJingleDialect
wocky_jingle_session_get_dialect (WockyJingleSession *sess);

wocky_jingle_session_can_modify_contents ()

gboolean
wocky_jingle_session_can_modify_contents
                               (WockyJingleSession *sess);

wocky_jingle_session_peer_has_cap ()

gboolean
wocky_jingle_session_peer_has_cap (WockyJingleSession *self,
                                   const gchar *cap_or_quirk);

wocky_jingle_session_send ()

void
wocky_jingle_session_send (WockyJingleSession *sess,
                           WockyStanza *stanza);

A shorthand for sending a Jingle IQ without waiting for the reply.

Parameters

sess

a session

 

stanza

a stanza, of which this function will take ownership.

[transfer full]

wocky_jingle_session_set_local_hold ()

void
wocky_jingle_session_set_local_hold (WockyJingleSession *sess,
                                     gboolean held);

wocky_jingle_session_get_remote_hold ()

gboolean
wocky_jingle_session_get_remote_hold (WockyJingleSession *sess);

wocky_jingle_session_get_remote_ringing ()

gboolean
wocky_jingle_session_get_remote_ringing
                               (WockyJingleSession *sess);

wocky_jingle_session_defines_action ()

gboolean
wocky_jingle_session_defines_action (WockyJingleSession *sess,
                                     WockyJingleAction action);

wocky_jingle_session_get_peer_contact ()

WockyContact *
wocky_jingle_session_get_peer_contact (WockyJingleSession *self);

wocky_jingle_session_get_peer_jid ()

const gchar *
wocky_jingle_session_get_peer_jid (WockyJingleSession *sess);

wocky_jingle_session_get_reason_name ()

const gchar *
wocky_jingle_session_get_reason_name (WockyJingleReason reason);

wocky_jingle_session_get_factory ()

WockyJingleFactory *
wocky_jingle_session_get_factory (WockyJingleSession *self);

wocky_jingle_session_get_porter ()

WockyPorter *
wocky_jingle_session_get_porter (WockyJingleSession *self);

wocky_jingle_session_acknowledge_iq ()

void
wocky_jingle_session_acknowledge_iq (WockyJingleSession *self,
                                     WockyStanza *stanza);

Types and Values

Property Details

The “dialect” property

  “dialect”                  guint

Jingle dialect used for this session.

Flags: Read / Write

Default value: 0


The “jingle-factory” property

  “jingle-factory”           WockyJingleFactory *

The Jingle factory which created this session.

Flags: Read / Write / Construct Only


The “local-hold” property

  “local-hold”               gboolean

TRUE if we've placed the peer on hold.

Flags: Read / Write

Default value: FALSE


The “local-initiator” property

  “local-initiator”          gboolean

Specifies if local end initiated the session.

Flags: Read / Write / Construct Only

Default value: TRUE


The “peer-contact” property

  “peer-contact”             WockyContact *

The WockyContact representing the other party in the session. Note that if this is a WockyBareContact (as opposed to a WockyResourceContact) the session is with the contact's bare JID.

Flags: Read / Write / Construct Only


The “porter” property

  “porter”                   WockyPorter *

The WockyPorter for the current connection.

Flags: Read / Write / Construct Only


The “remote-hold” property

  “remote-hold”              gboolean

TRUE if the peer has placed us on hold.

Flags: Read

Default value: FALSE


The “remote-ringing” property

  “remote-ringing”           gboolean

TRUE if the peer's client is ringing.

Flags: Read

Default value: FALSE


The “session-id” property

  “session-id”               gchar *

A unique session identifier used throughout all communication.

Flags: Read / Write / Construct Only

Default value: NULL


The “state” property

  “state”                    guint

The current state that the session is in.

Flags: Read / Write

Default value: 0

Signal Details

The “about-to-initiate” signal

void
user_function (WockyJingleSession *wockyjinglesession,
               gpointer            user_data)

Flags: Run Last


The “content-rejected” signal

void
user_function (WockyJingleSession *wockyjinglesession,
               GObject            *arg1,
               guint               arg2,
               gchar              *arg3,
               gpointer            user_data)

Flags: Run Last


The “new-content” signal

void
user_function (WockyJingleSession *wockyjinglesession,
               GObject            *arg1,
               gpointer            user_data)

Flags: Run Last


The “query-cap” signal

gboolean
user_function (WockyJingleSession *wockyjinglesession,
               WockyContact       *arg1,
               gchar              *arg2,
               gpointer            user_data)

Flags: Run Last


The “remote-state-changed” signal

void
user_function (WockyJingleSession *wockyjinglesession,
               gpointer            user_data)

Flags: Run Last


The “terminated” signal

void
user_function (WockyJingleSession *session,
               gboolean            locally_terminated,
               guint               reason,
               gchar              *text,
               gpointer            user_data)

Emitted when the session ends, just after “state” moves to WOCKY_JINGLE_STATE_ENDED.

Parameters

session

the session

 

locally_terminated

TRUE if the session ended due to a call to wocky_jingle_session_terminate(); FALSE if the peer ended the session.

 

reason

a WockyJingleReason describing why the session terminated

 

text

a possibly-NULL human-readable string describing why the session terminated

 

user_data

user data set when the signal handler was connected.

 

Flags: Run Last

telepathy-gabble-0.18.2/lib/ext/wocky/docs/reference/html/WockyJingleTransportGoogle.html0000644000175000017500000002513512312537050031542 0ustar00smcvsmcv00000000000000 Wocky Reference Manual: WockyJingleTransportGoogle

WockyJingleTransportGoogle

WockyJingleTransportGoogle

Properties

WockyJingleContent * content Read / Write / Construct Only
guint state Read / Write
gchar * transport-ns Read / Write / Construct Only

Signals

Object Hierarchy

    GObject
    ╰── WockyJingleTransportGoogle

Implemented Interfaces

WockyJingleTransportGoogle implements

Description

Functions

jingle_transport_google_register ()

void
jingle_transport_google_register (WockyJingleFactory *factory);

jingle_transport_google_set_component_name ()

gboolean
jingle_transport_google_set_component_name
                               (WockyJingleTransportGoogle *transport,
                                const gchar *name,
                                guint component_id);

Types and Values

Property Details

The “content” property

  “content”                  WockyJingleContent *

Jingle content object using this transport.

Flags: Read / Write / Construct Only


The “state” property

  “state”                    guint

Enum specifying the connection state of the transport.

Flags: Read / Write

Allowed values: <= 2

Default value: 0


The “transport-ns” property

  “transport-ns”             gchar *

Namespace identifying the transport type.

Flags: Read / Write / Construct Only

Default value: NULL

Signal Details

The “new-candidates” signal

void
user_function (WockyJingleTransportGoogle *wockyjingletransportgoogle,
               gpointer                    arg1,
               gpointer                    user_data)

Flags: Run Last

telepathy-gabble-0.18.2/lib/ext/wocky/docs/reference/html/WockyJingleTransportIceUdp.html0000644000175000017500000002231212312537050031471 0ustar00smcvsmcv00000000000000 Wocky Reference Manual: WockyJingleTransportIceUdp

WockyJingleTransportIceUdp

WockyJingleTransportIceUdp

Properties

WockyJingleContent * content Read / Write / Construct Only
guint state Read / Write
gchar * transport-ns Read / Write / Construct Only

Signals

Object Hierarchy

    GObject
    ╰── WockyJingleTransportIceUdp

Implemented Interfaces

WockyJingleTransportIceUdp implements

Description

Functions

jingle_transport_iceudp_register ()

void
jingle_transport_iceudp_register (WockyJingleFactory *factory);

Types and Values

Property Details

The “content” property

  “content”                  WockyJingleContent *

Jingle content object using this transport.

Flags: Read / Write / Construct Only


The “state” property

  “state”                    guint

Enum specifying the connection state of the transport.

Flags: Read / Write

Allowed values: <= 2

Default value: 0


The “transport-ns” property

  “transport-ns”             gchar *

Namespace identifying the transport type.

Flags: Read / Write / Construct Only

Default value: NULL

Signal Details

The “new-candidates” signal

void
user_function (WockyJingleTransportIceUdp *wockyjingletransporticeudp,
               gpointer                    arg1,
               gpointer                    user_data)

Flags: Run Last

telepathy-gabble-0.18.2/lib/ext/wocky/docs/reference/html/WockyJingleTransportIface.html0000644000175000017500000005556012312537050031342 0ustar00smcvsmcv00000000000000 Wocky Reference Manual: WockyJingleTransportIface

WockyJingleTransportIface

WockyJingleTransportIface

Properties

WockyJingleContent * content Read / Write / Construct Only
guint state Read / Write
gchar * transport-ns Read / Write / Construct Only

Types and Values

Object Hierarchy

    GInterface
    ╰── WockyJingleTransportIface

Description

Functions

wocky_jingle_transport_iface_parse_candidates ()

void
wocky_jingle_transport_iface_parse_candidates
                               (WockyJingleTransportIface *Param1,
                                WockyNode *Param2,
                                GError **Param3);

wocky_jingle_transport_iface_new_local_candidates ()

void
wocky_jingle_transport_iface_new_local_candidates
                               (WockyJingleTransportIface *self,
                                GList *candidates);

wocky_jingle_transport_iface_inject_candidates ()

void
wocky_jingle_transport_iface_inject_candidates
                               (WockyJingleTransportIface *self,
                                WockyNode *transport_node);

wocky_jingle_transport_iface_send_candidates ()

void
wocky_jingle_transport_iface_send_candidates
                               (WockyJingleTransportIface *self,
                                gboolean all);

wocky_jingle_transport_iface_can_accept ()

gboolean
wocky_jingle_transport_iface_can_accept
                               (WockyJingleTransportIface *self);

wocky_jingle_transport_iface_get_remote_candidates ()

GList *
wocky_jingle_transport_iface_get_remote_candidates
                               (WockyJingleTransportIface *Param1);

wocky_jingle_transport_iface_get_local_candidates ()

GList *
wocky_jingle_transport_iface_get_local_candidates
                               (WockyJingleTransportIface *Param1);

wocky_jingle_transport_iface_get_transport_type ()

WockyJingleTransportType
wocky_jingle_transport_iface_get_transport_type
                               (WockyJingleTransportIface *Param1);

jingle_transport_get_credentials ()

gboolean
jingle_transport_get_credentials (WockyJingleTransportIface *Param1,
                                  gchar **ufrag,
                                  gchar **pwd);

wocky_jingle_transport_iface_new ()

WockyJingleTransportIface *
wocky_jingle_transport_iface_new (GType type,
                                  WockyJingleContent *content,
                                  const gchar *transport_ns);

wocky_jingle_candidate_new ()

WockyJingleCandidate *
wocky_jingle_candidate_new (WockyJingleTransportProtocol protocol,
                            WockyJingleCandidateType type,
                            const gchar *id,
                            int component,
                            const gchar *address,
                            int port,
                            int generation,
                            int preference,
                            const gchar *username,
                            const gchar *password,
                            int network);

wocky_jingle_candidate_free ()

void
wocky_jingle_candidate_free (WockyJingleCandidate *c);

jingle_transport_free_candidates ()

void
jingle_transport_free_candidates (GList *candidates);

Types and Values

enum WockyJingleTransportState

Members

WOCKY_JINGLE_TRANSPORT_STATE_DISCONNECTED

   

WOCKY_JINGLE_TRANSPORT_STATE_CONNECTING

   

WOCKY_JINGLE_TRANSPORT_STATE_CONNECTED

   

Property Details

The “content” property

  “content”                  WockyJingleContent *

Jingle content that's using this jingle transport object.

Flags: Read / Write / Construct Only


The “state” property

  “state”                    guint

Enum specifying the connection state of the transport.

Flags: Read / Write

Allowed values: <= 2

Default value: 0


The “transport-ns” property

  “transport-ns”             gchar *

Namespace identifying the transport type.

Flags: Read / Write / Construct Only

Default value: NULL

telepathy-gabble-0.18.2/lib/ext/wocky/docs/reference/html/WockyJingleTransportRawUdp.html0000644000175000017500000002227612312537050031533 0ustar00smcvsmcv00000000000000 Wocky Reference Manual: WockyJingleTransportRawUdp

WockyJingleTransportRawUdp

WockyJingleTransportRawUdp

Properties

WockyJingleContent * content Read / Write / Construct Only
guint state Read / Write
gchar * transport-ns Read / Write / Construct Only

Signals

Object Hierarchy

    GObject
    ╰── WockyJingleTransportRawUdp

Implemented Interfaces

WockyJingleTransportRawUdp implements

Description

Functions

jingle_transport_rawudp_register ()

void
jingle_transport_rawudp_register (WockyJingleFactory *factory);

Types and Values

Property Details

The “content” property

  “content”                  WockyJingleContent *

Jingle content object using this transport.

Flags: Read / Write / Construct Only


The “state” property

  “state”                    guint

Enum specifying the connection state of the transport.

Flags: Read / Write

Allowed values: <= 2

Default value: 0


The “transport-ns” property

  “transport-ns”             gchar *

Namespace identifying the transport type.

Flags: Read / Write / Construct Only

Default value: NULL

Signal Details

The “new-candidates” signal

void
user_function (WockyJingleTransportRawUdp *wockyjingletransportrawudp,
               gpointer                    arg1,
               gpointer                    user_data)

Flags: Run Last

telepathy-gabble-0.18.2/lib/ext/wocky/docs/reference/html/WockyMetaPorter.html0000644000175000017500000006700412312537050027343 0ustar00smcvsmcv00000000000000 Wocky Reference Manual: WockyMetaPorter

WockyMetaPorter

WockyMetaPorter

Properties

WockyContactFactory * contact-factory Read / Write / Construct Only

Types and Values

Object Hierarchy

    GObject
    ╰── WockyMetaPorter

Description

Functions

wocky_meta_porter_error_quark ()

GQuark
wocky_meta_porter_error_quark (void);

WOCKY_META_PORTER_ERROR

#define WOCKY_META_PORTER_ERROR (wocky_meta_porter_error_quark ())

wocky_meta_porter_new ()

WockyPorter *
wocky_meta_porter_new (const gchar *jid,
                       WockyContactFactory *contact_factory);

Convenience function to create a new WockyMetaPorter object. The JID can be set later by using wocky_meta_porter_set_jid().

Parameters

jid

the JID of the local user, or NULL

 

contact_factory

a WockyContactFactory object

 

Returns

a new WockyMetaPorter


wocky_meta_porter_get_port ()

guint16
wocky_meta_porter_get_port (WockyMetaPorter *porter);

Returns the port porter is listening in on for new incoming XMPP connections, or 0 if it has not been started yet with wocky_porter_start().

Parameters

porter

a WockyMetaPorter

 

Returns

the port porter is listening in on for new incoming XMPP connections, or 0 if it has not been started.


wocky_meta_porter_hold ()

void
wocky_meta_porter_hold (WockyMetaPorter *porter,
                        WockyContact *contact);

Increases the hold count of the porter to contact by one. This means that if there is a connection open to contact then it will not disconnected after a timeout. Note that calling this function does not mean a connection will be opened. The hold count on a contact survives across connections.

To decrement the hold count of the porter to contact , one must call wocky_meta_porter_unhold().

Parameters

porter

a WockyMetaPorter

 

contact

a WockyContact

 

wocky_meta_porter_unhold ()

void
wocky_meta_porter_unhold (WockyMetaPorter *porter,
                          WockyContact *contact);

Decreases the hold count of the porter to contact by one. This means that if there is a connection open to contact and the hold count is zero, a connection timeout will be started.

Parameters

porter

a WockyMetaPorter

 

contact

a WockyContact

 

wocky_meta_porter_set_jid ()

void
wocky_meta_porter_set_jid (WockyMetaPorter *porter,
                           const gchar *jid);

Changes the local JID according to porter . Note that this function can only be called once, and only if NULL was passed to wocky_meta_porter_new() when creating porter . Calling it again will be a no-op.

Parameters

porter

a WockyMetaPorter

 

jid

a new JID

 

wocky_meta_porter_open_async ()

void
wocky_meta_porter_open_async (WockyMetaPorter *porter,
                              WockyLLContact *contact,
                              GCancellable *cancellable,
                              GAsyncReadyCallback callback,
                              gpointer user_data);

Make an asynchronous request to open a connection to contact if one is not already open. The hold count of the porter to contact will be incrememented and so after completion wocky_meta_porter_unhold() should be called on contact to release the hold.

When the request is complete, callback will be called and the user should call wocky_meta_porter_open_finish() to finish the request.

Parameters

porter

a WockyMetaPorter

 

contact

the WockyLLContact

 

cancellable

an optional GCancellable, or NULL

 

callback

a callback to be called

 

user_data

data for callback

 

wocky_meta_porter_open_finish ()

gboolean
wocky_meta_porter_open_finish (WockyMetaPorter *porter,
                               GAsyncResult *result,
                               GError **error);

Finishes an asynchronous request to open a connection if one is not already open. See wocky_meta_porter_open_async() for more details.

Parameters

porter

a WockyMetaPorter

 

result

the GAsyncResult

 

error

an optional GError location to store an error message

 

Returns

TRUE if the operation was a success, otherwise FALSE


wocky_meta_porter_borrow_connection ()

GSocketConnection *
wocky_meta_porter_borrow_connection (WockyMetaPorter *porter,
                                     WockyLLContact *contact);

Borrow the GSocketConnection of the porter to contact , if one exists, otherwise NULL will be returned.

Note that the connection returned should be reffed using g_object_ref() if it needs to be kept. However, it will still be operated on by the underlying WockyXmppConnection object so can close spontaneously unless wocky_meta_porter_hold() is called with contact .

Parameters

porter

a WockyMetaPorter

 

contact

the WockyContact

 

Returns

the GSocketConnection or NULL if no connection is open

Types and Values

enum WockyMetaPorterError

Members

WOCKY_META_PORTER_ERROR_NO_CONTACT_ADDRESS

   

WOCKY_META_PORTER_ERROR_FAILED_TO_CLOSE

   

Property Details

The “contact-factory” property

  “contact-factory”          WockyContactFactory *

The WockyContactFactory object in use by this meta porter.

Flags: Read / Write / Construct Only

telepathy-gabble-0.18.2/lib/ext/wocky/docs/reference/html/WockyMuc.html0000644000175000017500000024464612312537050026016 0ustar00smcvsmcv00000000000000 Wocky Reference Manual: WockyMuc

WockyMuc

WockyMuc — multi-user chat rooms

Properties

WockyMucAffiliation affiliation Read
gchar * category Read
gchar * description Read
gchar * jid Read / Write
gulong muc-flags Read
gchar * name Read
gchar * nickname Read
gchar * password Read / Write
WockyPorter * porter Read / Write / Construct Only
gchar * reserved-nick Read
guint role Read
gchar * room Read
gchar * service Read
gchar * status-message Read
gchar * type Read
gchar * user Read / Write / Construct Only

Object Hierarchy

    GEnum
    ├── WockyMucAffiliation
    ├── WockyMucMsgState
    ├── WockyMucMsgType
    ├── WockyMucRole
    ╰── WockyMucState
    GFlags
    ├── WockyMucFeature
    ╰── WockyMucStatusCode
    GObject
    ╰── WockyMuc

Includes

#include <wocky/wocky.h>

Description

Represents a multi-user chat room. Because the MUC protocol is so terrible, you will find yourself consulting XEP-0045 and shedding more than a few tears while using this class.

Functions

wocky_muc_disco_info_async ()

void
wocky_muc_disco_info_async (WockyMuc *muc,
                            GAsyncReadyCallback callback,
                            GCancellable *cancel,
                            gpointer data);

wocky_muc_disco_info_finish ()

gboolean
wocky_muc_disco_info_finish (WockyMuc *muc,
                             GAsyncResult *res,
                             GError **error);

wocky_muc_create_presence ()

WockyStanza *
wocky_muc_create_presence (WockyMuc *muc,
                           WockyStanzaSubType type,
                           const gchar *status);

wocky_muc_join ()

void
wocky_muc_join (WockyMuc *muc,
                GCancellable *cancel);

wocky_muc_jid ()

const gchar *
wocky_muc_jid (WockyMuc *muc);

wocky_muc_user ()

const gchar *
wocky_muc_user (WockyMuc *muc);

wocky_muc_role ()

WockyMucRole
wocky_muc_role (WockyMuc *muc);

wocky_muc_affiliation ()

WockyMucAffiliation
wocky_muc_affiliation (WockyMuc *muc);

wocky_muc_members ()

GHashTable *
wocky_muc_members (WockyMuc *muc);

wocky_muc_get_state ()

WockyMucState
wocky_muc_get_state (WockyMuc *muc);

Types and Values

enum WockyMucStatusCode

MUC status codes, as defined by XEP-0045 §15.6.

Members

WOCKY_MUC_CODE_UNKNOWN

Unknown code

 

WOCKY_MUC_CODE_ONYMOUS

Room entered is not anonymous

 

WOCKY_MUC_CODE_AF_CHANGE_OOB

Affiliation changed when not present

 

WOCKY_MUC_CODE_CFG_SHOW_UNAVAILABLE

Unavailable members visible

 

WOCKY_MUC_CODE_CFG_HIDE_UNAVAILABLE

Unavailable members invisible

 

WOCKY_MUC_CODE_CFG_NONPRIVACY

Non-privacy config change

 

WOCKY_MUC_CODE_OWN_PRESENCE

User's own presence

 

WOCKY_MUC_CODE_CFG_LOGGING_ENABLED

Logging enabled

 

WOCKY_MUC_CODE_CFG_LOGGING_DISABLED

Logging disabled

 

WOCKY_MUC_CODE_CFG_ONYMOUS

Room is now non-anonymous

 

WOCKY_MUC_CODE_CFG_SEMIONYMOUS

Room is now semi-anonymous

 

WOCKY_MUC_CODE_CFG_ANONYMOUS

Room is now fully-anonymous

 

WOCKY_MUC_CODE_NEW_ROOM

Room created (eg by joining)

 

WOCKY_MUC_CODE_NICK_CHANGE_FORCED

Service enforced nick change

 

WOCKY_MUC_CODE_BANNED

User has been banned

 

WOCKY_MUC_CODE_NICK_CHANGE_USER

User's nick changed

 

WOCKY_MUC_CODE_KICKED

Kicked from the room

 

WOCKY_MUC_CODE_KICKED_AFFILIATION

Kicked (affiliation change)

 

WOCKY_MUC_CODE_KICKED_ROOM_PRIVATISED

Kicked (room is now members-only)

 

WOCKY_MUC_CODE_KICKED_SHUTDOWN

Kicked (shutdown)

 

enum WockyMucRole

WockyMuc roles as described in XEP-0045 §5.1.

Members

WOCKY_MUC_ROLE_NONE

no role

 

WOCKY_MUC_ROLE_VISITOR

visitor role

 

WOCKY_MUC_ROLE_PARTICIPANT

participant role

 

WOCKY_MUC_ROLE_MODERATOR

moderator role

 

enum WockyMucAffiliation

WockyMuc affiliations as described in XEP-0045 §5.2.

Members

WOCKY_MUC_AFFILIATION_OUTCAST

outcast affiliation

 

WOCKY_MUC_AFFILIATION_NONE

no affiliation

 

WOCKY_MUC_AFFILIATION_MEMBER

member affiliation

 

WOCKY_MUC_AFFILIATION_ADMIN

admin affiliation

 

WOCKY_MUC_AFFILIATION_OWNER

owner affiliation

 

enum WockyMucFeature

WockyMuc feature flags.

Members

WOCKY_MUC_MODERN

the MUC is modern, as documented in XEP-0045

 

WOCKY_MUC_FORM_REGISTER

the MUC has support for the mucregister FORM_TYPE

 

WOCKY_MUC_FORM_ROOMCONFIG

the MUC has support for the mucregister FORM_TYPE

 

WOCKY_MUC_FORM_ROOMINFO

the MUC has support for the mucregister FORM_TYPE

 

WOCKY_MUC_HIDDEN

the MUC is hidden

 

WOCKY_MUC_MEMBERSONLY

only members can join this MUC

 

WOCKY_MUC_MODERATED

the MUC is moderated

 

WOCKY_MUC_NONANONYMOUS

the MUC is non-anonymous

 

WOCKY_MUC_OPEN

the MUC is open

 

WOCKY_MUC_PASSWORDPROTECTED

the MUC is password protected

 

WOCKY_MUC_PERSISTENT

the MUC is persistent

 

WOCKY_MUC_PUBLIC

the MUC is public

 

WOCKY_MUC_ROOMS

the MUC has a list of MUC rooms

 

WOCKY_MUC_SEMIANONYMOUS

the MUC is semi-anonymous

 

WOCKY_MUC_TEMPORARY

the MUC is temporary

 

WOCKY_MUC_UNMODERATED

the MUC is unmoderated

 

WOCKY_MUC_UNSECURED

the MUC is unsecured

 

WOCKY_MUC_OBSOLETE

the MUC has obsolete groupchat 1.0 features

 

enum WockyMucMsgType

XMPP MUC message types.

Members

WOCKY_MUC_MSG_NONE

no message type

 

WOCKY_MUC_MSG_NORMAL

a normal message

 

WOCKY_MUC_MSG_ACTION

an action message

 

WOCKY_MUC_MSG_NOTICE

a notice message

 

enum WockyMucMsgState

XMPP MUC message states as documeted in XEP-0085.

Members

WOCKY_MUC_MSG_STATE_NONE

no message state applies

 

WOCKY_MUC_MSG_STATE_ACTIVE

the contact in the MUC is active

 

WOCKY_MUC_MSG_STATE_COMPOSING

the contact in the MUC is composing a message

 

WOCKY_MUC_MSG_STATE_INACTIVE

the contact in the MUC is inactive

 

WOCKY_MUC_MSG_STATE_PAUSED

the contact in the MUC has paused composing a message

 

enum WockyMucState

WockyMuc states.

Members

WOCKY_MUC_CREATED

the WockyMuc has been created

 

WOCKY_MUC_INITIATED

the MUC has been initiated on the server

 

WOCKY_MUC_AUTH

the user is authenticating with the MUC

 

WOCKY_MUC_JOINED

the user has joined the MUC and can chat

 

WOCKY_MUC_ENDED

the MUC has ended

 

WockyMucMember

typedef struct {
  gchar *from;   /* room@service/nick     */
  gchar *jid;    /* owner@domain/resource */
  gchar *nick;   /* nick */
  WockyMucRole role;
  WockyMucAffiliation affiliation;
  gchar *status; /* user set status string */
  WockyStanza *presence_stanza;
} WockyMucMember;

Members

gchar *from;

the JID of the member (room@server/nick)

 

gchar *jid;

the JID of the owner (owner@domain/resource)

 

gchar *nick;

the nickname of the member

 

WockyMucRole role;

the WockyMucRole of the member

 

WockyMucAffiliation affiliation;

the WockyMucAffiliation of the member

 

gchar *status;

the user set status string

 

WockyStanza *presence_stanza;

the WockyStanza that was received regarding the member's presence

 

struct WockyMucClass

struct WockyMucClass {
};

The class of a WockyMuc.

Property Details

The “affiliation” property

  “affiliation”              WockyMucAffiliation

The affiliation of the user with the MUC room.

Flags: Read

Default value: WOCKY_MUC_AFFILIATION_NONE


The “category” property

  “category”                 gchar *

Category of the MUC, usually "conference".

Flags: Read

Default value: NULL


The “description” property

  “description”              gchar *

The long description oof the room.

Flags: Read

Default value: NULL


The “jid” property

  “jid”                      gchar *

Full room@service/nick JID of the MUC room.

Flags: Read / Write

Default value: NULL


The “muc-flags” property

  “muc-flags”                gulong

ORed set of WockyMucFeature MUC property flags.

Flags: Read


The “name” property

  “name”                     gchar *

The human-readable name of the room (usually a short label).

Flags: Read

Default value: NULL


The “nickname” property

  “nickname”                 gchar *

The user's in-room nickname.

Flags: Read

Default value: NULL


The “password” property

  “password”                 gchar *

User's MUC room password.

Flags: Read / Write

Default value: NULL


The “porter” property

  “porter”                   WockyPorter *

The WockyPorter instance doing all the actual XMPP interaction.

Flags: Read / Write / Construct Only


The “reserved-nick” property

  “reserved-nick”            gchar *

The user's reserved in-room nickname, if any.

Flags: Read

Default value: NULL


The “role” property

  “role”                     guint

The role (WockyMucRole) of the user in the MUC room.

Flags: Read

Allowed values: <= 3

Default value: 0


The “room” property

  “room”                     gchar *

The node part of the MUC room JID.

Flags: Read

Default value: NULL


The “service” property

  “service”                  gchar *

The service (domain) part of the MUC JID.

Flags: Read

Default value: NULL


The “status-message” property

  “status-message”           gchar *

User's MUC status message.

Flags: Read

Default value: NULL


The “type” property

  “type”                     gchar *

Type of the MUC, eg "text".

Flags: Read

Default value: NULL


The “user” property

  “user”                     gchar *

Full JID of the user (node@domain/resource) who is connecting.

Flags: Read / Write / Construct Only

Default value: NULL

Signal Details

The “error” signal

void
user_function (WockyMuc          *muc,
               WockyStanza       *stanza,
               WockyXmppErrorType error_type,
               GError            *error,
               gpointer           user_data)

Emitted when a presence error is received from the MUC, which is generally in response to trying to join the MUC.

Parameters

muc

the MUC

 

stanza

the presence stanza

 

error_type

the type of error

 

error

an error in domain WOCKY_XMPP_ERROR, whose message (if not NULL) is a human-readable message from the server

 

user_data

user data set when the signal handler was connected.

 

Flags: Run Last


The “fill-presence” signal

void
user_function (WockyMuc    *wockymuc,
               WockyStanza *arg1,
               gpointer     user_data)

Flags: Run Last


The “joined” signal

void
user_function (WockyMuc    *muc,
               WockyStanza *stanza,
               guint        codes,
               gpointer     user_data)

Emitted when the local user successfully joins muc .

Parameters

muc

the MUC

 

stanza

the presence stanza

 

codes

bitwise OR of WockyMucStatusCode flags with miscellaneous information about the MUC

 

user_data

user data set when the signal handler was connected.

 

Flags: Run Last


The “left” signal

void
user_function (WockyMuc    *muc,
               WockyStanza *stanza,
               guint        codes,
               gpointer     member,
               gchar       *actor,
               gchar       *reason,
               gchar       *message,
               gpointer     user_data)

Emitted when another participant leaves, or is kicked from, the MUC

Parameters

muc

the MUC

 

stanza

the presence stanza

 

codes

bitwise OR of WockyMucStatusCode flags describing why member left the MUC

 

member

the (now ex-)member of the MUC who left

 

actor

if member was removed from the MUC by another participant, that participant's JID

 

reason

if member was removed from the MUC by another participant, a human-readable reason given by that participant

 

message

a parting message provided by member , or NULL

 

user_data

user data set when the signal handler was connected.

 

Flags: Run Last


The “message” signal

void
user_function (WockyMuc        *muc,
               WockyStanza     *stanza,
               WockyMucMsgType  message_type,
               gchar           *id,
               GDateTime       *timestamp,
               gpointer         sender,
               gchar           *body,
               gchar           *subject,
               WockyMucMsgState state,
               gpointer         user_data)

Emitted when a non-error message stanza is received. This may indicate:

  • if body is not NULL, a message sent by sender to the MUC;
  • or, if subject is not NULL, sender changed the subject of the MUC;
  • additionally, that sender is typing, or maybe stopped typing, depending on state.

Parameters

muc

the MUC

 

stanza

the incoming message stanza

 

message_type

the message's type

 

id

the stanza's identifier (which may be NULL if neither the sender nor the MUC specified one)

 

timestamp

for messages received as scrollback when joining the MUC, the time the message was sent; NULL for messages received while in the MUC

 

sender

a WockyMucMember struct describing the sender of the message

 

body

the body of the message, or NULL

 

subject

the new subject for the MUC, or NULL

 

state

whether sender is currently typing.

 

user_data

user data set when the signal handler was connected.

 

Flags: Run Last


The “message-error” signal

void
user_function (WockyMuc          *muc,
               WockyStanza       *stanza,
               WockyMucMsgType    message_type,
               gchar             *id,
               GDateTime         *timestamp,
               gpointer           member,
               gchar             *body,
               WockyXmppErrorType error_type,
               GError            *error,
               gpointer           user_data)

Emitted when we receive an error from the MUC in response to sending a message stanza to the MUC.

Parameters

muc

the MUC

 

stanza

the incoming WOCKY_STANZA_SUB_TYPE_ERROR message

 

message_type

the type of the message which was rejected

 

id

the identifier for the original message and this error (which may be NULL)

 

timestamp

the timestamp attached to the original message, which is probably NULL because timestamps are only attached to scrollback messages

 

member

a WockyMucMember struct describing the sender of the original message (which is, we presume, us)

 

body

the body of the message which failed to send

 

error_type

the type of error

 

error

an error in domain WOCKY_XMPP_ERROR, whose message (if not NULL) is a human-readable message from the server

 

user_data

user data set when the signal handler was connected.

 

Flags: Run Last


The “nick-change” signal

void
user_function (WockyMuc    *wockymuc,
               WockyStanza *arg1,
               guint        arg2,
               gpointer     user_data)

Flags: Run Last


The “own-presence” signal

void
user_function (WockyMuc    *wockymuc,
               WockyStanza *arg1,
               guint        arg2,
               gpointer     user_data)

Flags: Run Last


The “parted” signal

void
user_function (WockyMuc    *muc,
               WockyStanza *stanza,
               guint        codes,
               gchar       *actor,
               gchar       *reason,
               gchar       *message,
               gpointer     user_data)

Emitted when the local user leaves the MUC, whether by choice or by force.

Parameters

muc

the MUC

 

stanza

the presence stanza

 

codes

bitwise OR of WockyMucStatusCode flags describing why the user left the MUC

 

actor

if the user was removed from the MUC by another participant, that participant's JID

 

reason

if the user was removed from the MUC by another participant, a human-readable reason given by that participant

 

message

a parting message we provided to other participants, or NULL

 

user_data

user data set when the signal handler was connected.

 

Flags: Run Last


The “permissions” signal

void
user_function (WockyMuc    *muc,
               WockyStanza *stanza,
               guint        codes,
               gchar       *actor_jid,
               gchar       *reason,
               gpointer     user_data)

Emitted when our permissions within the MUC are changed.

Parameters

muc

the muc

 

stanza

the presence stanza heralding the change

 

codes

bitwise OR of WockyMucStatusCode flags

 

actor_jid

the JID of the user who changed our permissions, or NULL

 

reason

a human-readable reason for the change, or NULL

 

user_data

user data set when the signal handler was connected.

 

Flags: Run Last


The “presence” signal

void
user_function (WockyMuc    *wockymuc,
               WockyStanza *arg1,
               guint        arg2,
               gpointer     arg3,
               gpointer     user_data)

Flags: Run Last

telepathy-gabble-0.18.2/lib/ext/wocky/docs/reference/html/WockyNodeTree.html0000644000175000017500000003005712312537050026764 0ustar00smcvsmcv00000000000000 Wocky Reference Manual: WockyNodeTree

WockyNodeTree

WockyNodeTree

Properties

gpointer top-node Read / Write / Construct Only

Types and Values

Object Hierarchy

    GObject
    ╰── WockyNodeTree
        ╰── WockyStanza

Description

Functions

wocky_node_tree_new ()

WockyNodeTree *
wocky_node_tree_new (const gchar *name,
                     const gchar *ns,
                     ...);

Build a node-tree from a list of arguments. Example:

Example 2. 

1
2
3
4
5
wocky_node_tree_new ("html", "http://www.w3.org/1999/xhtml",
   '(', "body", '@', "textcolor", "red",
      '$', "Wocky wooo",
   ')',
  NULL);

Parameters

name

The name of the toplevel node

 

ns

The namespace of the toplevel node

 

...

the description of the node tree to build, terminated with NULL

 

Returns

a new node-tree object


wocky_node_tree_new_va ()

WockyNodeTree *
wocky_node_tree_new_va (const gchar *name,
                        const char *ns,
                        va_list va);

wocky_node_tree_new_from_node ()

WockyNodeTree *
wocky_node_tree_new_from_node (WockyNode *node);

Build a new WockyNodeTree that contains a copy of the given node.

Parameters

node

The node to copy

 

Returns

a new node-tree object


wocky_node_tree_get_top_node ()

WockyNode *
wocky_node_tree_get_top_node (WockyNodeTree *self);

Types and Values

struct WockyNodeTreeClass

struct WockyNodeTreeClass {
};

The class of a WockyNodeTree.

Property Details

The “top-node” property

  “top-node”                 gpointer

The topmost node of the node-tree.

Flags: Read / Write / Construct Only

telepathy-gabble-0.18.2/lib/ext/wocky/docs/reference/html/WockyPepService.html0000644000175000017500000006106512312537050027327 0ustar00smcvsmcv00000000000000 Wocky Reference Manual: WockyPepService

WockyPepService

WockyPepService — Object to represent a single PEP service

Properties

gchar * node Read / Write / Construct Only
gboolean subscribe Read / Write / Construct Only

Signals

Types and Values

Object Hierarchy

    GObject
    ╰── WockyPepService

Includes

#include <wocky/wocky-pep-service.h>

Description

Object to aid with looking up PEP nodes and listening for changes.

Functions

wocky_pep_service_new ()

WockyPepService *
wocky_pep_service_new (const gchar *node,
                       gboolean subscribe);

A convenience function to create a new WockyPepService object.

Parameters

node

the namespace of the PEP node

 

subscribe

TRUE if Wocky is to subscribe to the notifications of the node, otherwise FALSE

 

Returns

a new WockyPepService


wocky_pep_service_start ()

void
wocky_pep_service_start (WockyPepService *self,
                         WockySession *session);

Start listening to the PEP node node and signal changes by using “changed”.

Parameters

self

a WockyPepService object

 

session

a WockySession object

 

wocky_pep_service_get_async ()

void
wocky_pep_service_get_async (WockyPepService *self,
                             WockyBareContact *contact,
                             GCancellable *cancellable,
                             GAsyncReadyCallback callback,
                             gpointer user_data);

Starts an asynchronous operation to get the PEP node, “node”.

When the operation is complete, callback will be called and the function should call wocky_pep_service_get_finish().

Parameters

self

a WockyPepService object

 

contact

a WockyBareContact object

 

cancellable

an optional GCancellable object, or NULL

 

callback

a function to call when the node is retrieved

 

user_data

user data for callback

 

wocky_pep_service_get_finish ()

WockyStanza *
wocky_pep_service_get_finish (WockyPepService *self,
                              GAsyncResult *result,
                              WockyNode **item,
                              GError **error);

Finishes an asynchronous operation to get the PEP node, “node”. For more details, see wocky_pep_service_get_async().

Parameters

self

a WockyPepService object

 

result

a GAsyncResult

 

item

on success, the first <item> element in the result, or NULL if self has no published items.

[out][allow-none]

error

a location to store a GError if an error occurs

 

Returns

the WockyStanza retrieved from getting the PEP node.


wocky_pep_service_make_publish_stanza ()

WockyStanza *
wocky_pep_service_make_publish_stanza (WockyPepService *self,
                                       WockyNode **item);

Generates a new IQ type='set' PEP publish stanza.

Parameters

self

a WockyPepService

 

item

a location to store the item WockyNode, or NULL

 

Returns

a new WockyStanza PEP publish stanza; free with g_object_unref()

Types and Values

struct WockyPepServiceClass

struct WockyPepServiceClass {
};

The class of a WockyPepService.


struct WockyPepService

struct WockyPepService;

Object to aid with looking up PEP nodes and listening for changes.

Property Details

The “node” property

  “node”                     gchar *

Namespace of the PEP node.

Flags: Read / Write / Construct Only

Default value: NULL


The “subscribe” property

  “subscribe”                gboolean

TRUE if Wocky is to subscribe to the notifications of the node.

Flags: Read / Write / Construct Only

Default value: FALSE

Signal Details

The “changed” signal

void
user_function (WockyPepService  *self,
               WockyBareContact *contact,
               WockyStanza      *stanza,
               gpointer          item,
               gpointer          user_data)

Emitted when the node value changes.

Parameters

self

a WockyPepService object

 

contact

the WockyBareContact who changed the node

 

stanza

the WockyStanza

 

item

the first—and typically only—<item> element in stanza , or NULL if there is none.

 

user_data

user data set when the signal handler was connected.

 

Flags: Has Details

telepathy-gabble-0.18.2/lib/ext/wocky/docs/reference/html/WockyPing.html0000644000175000017500000001657612312537050026166 0ustar00smcvsmcv00000000000000 Wocky Reference Manual: WockyPing

WockyPing

WockyPing — support for pings/keepalives

Properties

guint ping-interval Read / Write / Construct
WockyC2SPorter * porter Read / Write / Construct Only

Types and Values

Object Hierarchy

    GObject
    ╰── WockyPing

Description

Support for XEP-0199 pings.

Functions

wocky_ping_error_quark ()

GQuark
wocky_ping_error_quark (void);

wocky_ping_new ()

WockyPing *
wocky_ping_new (WockyC2SPorter *porter,
                guint interval);

Types and Values

struct WockyPingClass

struct WockyPingClass {
};

The class of a WockyPing.

Property Details

The “ping-interval” property

  “ping-interval”            guint

keepalive ping interval in seconds, or 0 to disable.

Flags: Read / Write / Construct

Default value: 0


The “porter” property

  “porter”                   WockyC2SPorter *

the wocky porter to set up keepalive pings on.

Flags: Read / Write / Construct Only

telepathy-gabble-0.18.2/lib/ext/wocky/docs/reference/html/WockyPorter.html0000644000175000017500000037262712312537050026546 0ustar00smcvsmcv00000000000000 Wocky Reference Manual: WockyPorter

WockyPorter

WockyPorter

Properties

gchar * bare-jid Read
WockyXmppConnection * connection Read / Write / Construct Only
gchar * full-jid Read / Write / Construct Only
gchar * resource Read

Object Hierarchy

    GInterface
    ╰── WockyPorter

Description

Functions

wocky_porter_error_quark ()

GQuark
wocky_porter_error_quark (void);

Get the error quark used by the porter.

Returns

the quark for porter errors.


WOCKY_PORTER_ERROR

#define WOCKY_PORTER_ERROR (wocky_porter_error_quark ())

Get access to the error quark of the xmpp porter.


WOCKY_PORTER_HANDLER_PRIORITY_NORMAL

#define WOCKY_PORTER_HANDLER_PRIORITY_NORMAL (guint) (G_MAXUINT / 2)

WockyPorterHandlerFunc ()

gboolean
(*WockyPorterHandlerFunc) (WockyPorter *porter,
                           WockyStanza *stanza,
                           gpointer user_data);

Handler called when a matching stanza has been received by the WockyPorter.

If a handler returns TRUE, this means that it has taken responsibility for handling the stanza and (if applicable) sending a reply.

If a handler returns FALSE, this indicates that it has declined to process the stanza. The next handler (if any) is invoked.

A handler must not assume that stanza will continue to exist after the handler has returned, unless it has taken a reference to stanza using g_object_ref().

Parameters

porter

the WockyPorter dispatching the WockyStanza

 

stanza

the WockyStanza being dispatched

 

user_data

the data passed when the handler has been registered

 

Returns

TRUE if the stanza has been handled, FALSE if not


wocky_porter_start ()

void
wocky_porter_start (WockyPorter *porter);

Start a WockyPorter to make it read and dispatch incoming stanzas.

Parameters

porter

a WockyPorter

 

wocky_porter_get_full_jid ()

const gchar *
wocky_porter_get_full_jid (WockyPorter *self);

Parameters

self

a porter

 

Returns

the value of “full-jid”.

[transfer none]


wocky_porter_get_bare_jid ()

const gchar *
wocky_porter_get_bare_jid (WockyPorter *self);

Parameters

self

a porter

 

Returns

the value of “bare-jid”.

[transfer none]


wocky_porter_get_resource ()

const gchar *
wocky_porter_get_resource (WockyPorter *self);

Parameters

self

a porter

 

Returns

the value of “resource”.

[transfer none]


wocky_porter_send_async ()

void
wocky_porter_send_async (WockyPorter *porter,
                         WockyStanza *stanza,
                         GCancellable *cancellable,
                         GAsyncReadyCallback callback,
                         gpointer user_data);

Request asynchronous sending of a WockyStanza. When the stanza has been sent callback will be called. You can then call wocky_porter_send_finish() to get the result of the operation.

Parameters

porter

a WockyPorter

 

stanza

the WockyStanza to send

 

cancellable

optional GCancellable object, NULL to ignore

 

callback

callback to call when the request is satisfied

 

user_data

the data to pass to callback function

 

wocky_porter_send_finish ()

gboolean
wocky_porter_send_finish (WockyPorter *porter,
                          GAsyncResult *result,
                          GError **error);

Finishes sending a WockyStanza.

Parameters

porter

a WockyPorter

 

result

a GAsyncResult

 

error

a GError location to store the error occuring, or NULL to ignore.

 

Returns

TRUE on success or FALSE on error.


wocky_porter_send ()

void
wocky_porter_send (WockyPorter *porter,
                   WockyStanza *stanza);

Send a WockyStanza. This is a convenient function to not have to call wocky_porter_send_async() with lot of NULL arguments if you don't care to know when the stanza has been actually sent.

Parameters

porter

a WockyPorter

 

stanza

the WockyStanza to send

 

wocky_porter_register_handler_from_va ()

guint
wocky_porter_register_handler_from_va (WockyPorter *self,
                                       WockyStanzaType type,
                                       WockyStanzaSubType sub_type,
                                       const gchar *from,
                                       guint priority,
                                       WockyPorterHandlerFunc callback,
                                       gpointer user_data,
                                       va_list ap);

A va_list version of wocky_porter_register_handler_from(); see that function for more details.

Parameters

self

A WockyPorter instance (passed to callback ).

 

type

The type of stanza to be handled, or WOCKY_STANZA_TYPE_NONE to match any type of stanza.

 

sub_type

The subtype of stanza to be handled, or WOCKY_STANZA_SUB_TYPE_NONE to match any type of stanza.

 

from

the JID whose messages this handler is intended for (may not be NULL)

 

priority

a priority between WOCKY_PORTER_HANDLER_PRIORITY_MIN and WOCKY_PORTER_HANDLER_PRIORITY_MAX (often WOCKY_PORTER_HANDLER_PRIORITY_NORMAL). Handlers with a higher priority (larger number) are called first.

 

callback

A WockyPorterHandlerFunc, which should return FALSE to decline the stanza (Wocky will continue to the next handler, if any), or TRUE to stop further processing.

 

user_data

Passed to callback .

 

ap

a wocky_stanza_build() specification. The handler will match a stanza only if the stanza received is a superset of the one passed to this function, as per wocky_node_is_superset().

 

Returns

a non-zero ID for use with wocky_porter_unregister_handler().


wocky_porter_register_handler_from_by_stanza ()

guint
wocky_porter_register_handler_from_by_stanza
                               (WockyPorter *self,
                                WockyStanzaType type,
                                WockyStanzaSubType sub_type,
                                const gchar *from,
                                guint priority,
                                WockyPorterHandlerFunc callback,
                                gpointer user_data,
                                WockyStanza *stanza);

A WockyStanza version of wocky_porter_register_handler_from(); see that function for more details.

Parameters

self

A WockyPorter instance (passed to callback ).

 

type

The type of stanza to be handled, or WOCKY_STANZA_TYPE_NONE to match any type of stanza.

 

sub_type

The subtype of stanza to be handled, or WOCKY_STANZA_SUB_TYPE_NONE to match any type of stanza.

 

from

the JID whose messages this handler is intended for (may not be NULL)

 

priority

a priority between WOCKY_PORTER_HANDLER_PRIORITY_MIN and WOCKY_PORTER_HANDLER_PRIORITY_MAX (often WOCKY_PORTER_HANDLER_PRIORITY_NORMAL). Handlers with a higher priority (larger number) are called first.

 

callback

A WockyPorterHandlerFunc, which should return FALSE to decline the stanza (Wocky will continue to the next handler, if any), or TRUE to stop further processing.

 

user_data

Passed to callback .

 

stanza

a WockyStanza. The handler will match a stanza only if the stanza received is a superset of the one passed to this function, as per wocky_node_is_superset().

 

Returns

a non-zero ID for use with wocky_porter_unregister_handler().


wocky_porter_register_handler_from ()

guint
wocky_porter_register_handler_from (WockyPorter *self,
                                    WockyStanzaType type,
                                    WockyStanzaSubType sub_type,
                                    const gchar *from,
                                    guint priority,
                                    WockyPorterHandlerFunc callback,
                                    gpointer user_data,
                                    ...);

Register a new stanza handler. Stanza handlers are called when the Porter receives a new stanza matching the rules of the handler. Matching handlers are sorted by priority and are called until one claims to have handled the stanza (by returning TRUE).

If from is a bare JID, then the resource of the JID in the from attribute will be ignored: In other words, a handler registered against a bare JID will match all stanzas from a JID with the same node and domain:

"foo@bar.org" will match "foo@bar.org", "foo@bar.org/moose" and so forth.

To register an IQ handler from Juliet for all the Jingle stanzas related to one Jingle session:

1
2
3
4
5
6
7
8
9
id = wocky_porter_register_handler_from (porter,
  WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_NONE,
  "juliet@example.com/Balcony",
  WOCKY_PORTER_HANDLER_PRIORITY_NORMAL,
  jingle_cb,
  '(', "jingle",
    ':', "urn:xmpp:jingle:1",
    '@', "sid", "my_sid",
  ')', NULL);

To match stanzas from any sender, see wocky_porter_register_handler_from_anyone(). If the porter is a WockyC2SPorter, one can match stanzas sent by the server; see wocky_c2s_porter_register_handler_from_server().

Parameters

self

A WockyPorter instance (passed to callback ).

 

type

The type of stanza to be handled, or WOCKY_STANZA_TYPE_NONE to match any type of stanza.

 

sub_type

The subtype of stanza to be handled, or WOCKY_STANZA_SUB_TYPE_NONE to match any type of stanza.

 

from

the JID whose messages this handler is intended for (may not be NULL)

 

priority

a priority between WOCKY_PORTER_HANDLER_PRIORITY_MIN and WOCKY_PORTER_HANDLER_PRIORITY_MAX (often WOCKY_PORTER_HANDLER_PRIORITY_NORMAL). Handlers with a higher priority (larger number) are called first.

 

callback

A WockyPorterHandlerFunc, which should return FALSE to decline the stanza (Wocky will continue to the next handler, if any), or TRUE to stop further processing.

 

user_data

Passed to callback .

 

...

a wocky_stanza_build() specification. The handler will match a stanza only if the stanza received is a superset of the one passed to this function, as per wocky_node_is_superset().

 

Returns

a non-zero ID for use with wocky_porter_unregister_handler().


wocky_porter_register_handler_from_anyone_va ()

guint
wocky_porter_register_handler_from_anyone_va
                               (WockyPorter *self,
                                WockyStanzaType type,
                                WockyStanzaSubType sub_type,
                                guint priority,
                                WockyPorterHandlerFunc callback,
                                gpointer user_data,
                                va_list ap);

A va_list version of wocky_porter_register_handler_from_anyone(); see that function for more details.

Parameters

self

A WockyPorter instance (passed to callback ).

 

type

The type of stanza to be handled, or WOCKY_STANZA_TYPE_NONE to match any type of stanza.

 

sub_type

The subtype of stanza to be handled, or WOCKY_STANZA_SUB_TYPE_NONE to match any type of stanza.

 

priority

a priority between WOCKY_PORTER_HANDLER_PRIORITY_MIN and WOCKY_PORTER_HANDLER_PRIORITY_MAX (often WOCKY_PORTER_HANDLER_PRIORITY_NORMAL). Handlers with a higher priority (larger number) are called first.

 

callback

A WockyPorterHandlerFunc, which should return FALSE to decline the stanza (Wocky will continue to the next handler, if any), or TRUE to stop further processing.

 

user_data

Passed to callback .

 

ap

a wocky_stanza_build() specification. The handler will match a stanza only if the stanza received is a superset of the one passed to this function, as per wocky_node_is_superset().

 

Returns

a non-zero ID for use with wocky_porter_unregister_handler().


wocky_porter_register_handler_from_anyone_by_stanza ()

guint
wocky_porter_register_handler_from_anyone_by_stanza
                               (WockyPorter *self,
                                WockyStanzaType type,
                                WockyStanzaSubType sub_type,
                                guint priority,
                                WockyPorterHandlerFunc callback,
                                gpointer user_data,
                                WockyStanza *stanza);

A WockyStanza version of wocky_porter_register_handler_from_anyone(); see that function for more details.

Parameters

self

A WockyPorter instance (passed to callback ).

 

type

The type of stanza to be handled, or WOCKY_STANZA_TYPE_NONE to match any type of stanza.

 

sub_type

The subtype of stanza to be handled, or WOCKY_STANZA_SUB_TYPE_NONE to match any type of stanza.

 

priority

a priority between WOCKY_PORTER_HANDLER_PRIORITY_MIN and WOCKY_PORTER_HANDLER_PRIORITY_MAX (often WOCKY_PORTER_HANDLER_PRIORITY_NORMAL). Handlers with a higher priority (larger number) are called first.

 

callback

A WockyPorterHandlerFunc, which should return FALSE to decline the stanza (Wocky will continue to the next handler, if any), or TRUE to stop further processing.

 

user_data

Passed to callback .

 

stanza

a WockyStanza. The handler will match a stanza only if the stanza received is a superset of the one passed to this function, as per wocky_node_is_superset().

 

Returns

a non-zero ID for use with wocky_porter_unregister_handler().


wocky_porter_register_handler_from_anyone ()

guint
wocky_porter_register_handler_from_anyone
                               (WockyPorter *self,
                                WockyStanzaType type,
                                WockyStanzaSubType sub_type,
                                guint priority,
                                WockyPorterHandlerFunc callback,
                                gpointer user_data,
                                ...);

Registers a handler for incoming stanzas from anyone, including those where the from attribute is missing.

For example, to register a handler matching all message stanzas received from anyone, call:

1
2
3
4
id = wocky_porter_register_handler (porter,
  WOCKY_STANZA_TYPE_MESSAGE, WOCKY_STANZA_SUB_TYPE_NONE, NULL,
  WOCKY_PORTER_HANDLER_PRIORITY_NORMAL, message_received_cb, NULL,
  NULL);

As a more interesting example, the following matches incoming PEP notifications for contacts' geolocation information:

1
2
3
4
5
6
7
8
9
10
11
id = wocky_porter_register_handler_from_anyone (porter,
   WOCKY_STANZA_TYPE_MESSAGE, WOCKY_STANZA_SUB_TYPE_NONE,
   WOCKY_PORTER_HANDLER_PRIORITY_MAX,
   msg_event_cb, self,
   '(', "event",
     ':', WOCKY_XMPP_NS_PUBSUB_EVENT,
     '(', "items",
       '@', "node", "http://jabber.org/protocol/geoloc",
     ')',
   ')',
   NULL);

Parameters

self

A WockyPorter instance (passed to callback ).

 

type

The type of stanza to be handled, or WOCKY_STANZA_TYPE_NONE to match any type of stanza.

 

sub_type

The subtype of stanza to be handled, or WOCKY_STANZA_SUB_TYPE_NONE to match any type of stanza.

 

priority

a priority between WOCKY_PORTER_HANDLER_PRIORITY_MIN and WOCKY_PORTER_HANDLER_PRIORITY_MAX (often WOCKY_PORTER_HANDLER_PRIORITY_NORMAL). Handlers with a higher priority (larger number) are called first.

 

callback

A WockyPorterHandlerFunc, which should return FALSE to decline the stanza (Wocky will continue to the next handler, if any), or TRUE to stop further processing.

 

user_data

Passed to callback .

 

...

a wocky_stanza_build() specification. The handler will match a stanza only if the stanza received is a superset of the one passed to this function, as per wocky_node_is_superset().

 

Returns

a non-zero ID for use with wocky_porter_unregister_handler().


wocky_porter_unregister_handler ()

void
wocky_porter_unregister_handler (WockyPorter *porter,
                                 guint id);

Unregister a registered handler. This handler won't be called when receiving stanzas anymore.

Parameters

porter

a WockyPorter

 

id

the id of the handler to unregister

 

wocky_porter_close_async ()

void
wocky_porter_close_async (WockyPorter *porter,
                          GCancellable *cancellable,
                          GAsyncReadyCallback callback,
                          gpointer user_data);

Request asynchronous closing of a WockyPorter. This fires the WockyPorter::closing signal, flushes the sending queue, closes the XMPP stream and waits that the other side closes the XMPP stream as well. When this is done, callback is called. You can then call wocky_porter_close_finish() to get the result of the operation.

Parameters

porter

a WockyPorter

 

cancellable

optional GCancellable object, NULL to ignore

 

callback

callback to call when the request is satisfied

 

user_data

the data to pass to callback function

 

wocky_porter_close_finish ()

gboolean
wocky_porter_close_finish (WockyPorter *porter,
                           GAsyncResult *result,
                           GError **error);

Finishes a close operation.

Parameters

porter

a WockyPorter

 

result

a GAsyncResult

 

error

a GError location to store the error occuring, or NULL to ignore.

 

Returns

TRUE on success or FALSE on error.


wocky_porter_send_iq_async ()

void
wocky_porter_send_iq_async (WockyPorter *porter,
                            WockyStanza *stanza,
                            GCancellable *cancellable,
                            GAsyncReadyCallback callback,
                            gpointer user_data);

Request asynchronous sending of a WockyStanza of type WOCKY_STANZA_TYPE_IQ and sub-type WOCKY_STANZA_SUB_TYPE_GET or WOCKY_STANZA_SUB_TYPE_SET. When the reply to this IQ has been received callback will be called. You can then call wocky_porter_send_iq_finish to get the reply stanza.

Parameters

porter

a WockyPorter

 

stanza

the WockyStanza to send

 

cancellable

optional GCancellable object, NULL to ignore

 

callback

callback to call when the request is satisfied

 

user_data

the data to pass to callback function

 

wocky_porter_send_iq_finish ()

WockyStanza *
wocky_porter_send_iq_finish (WockyPorter *porter,
                             GAsyncResult *result,
                             GError **error);

Get the reply of an IQ query.

Parameters

porter

a WockyPorter

 

result

a GAsyncResult

 

error

a GError location to store the error occuring, or NULL to ignore.

 

Returns

a reffed WockyStanza on success, NULL on error


wocky_porter_acknowledge_iq ()

void
wocky_porter_acknowledge_iq (WockyPorter *porter,
                             WockyStanza *stanza,
                             ...);

Sends an acknowledgement for stanza back to the sender, as a shorthand for calling wocky_stanza_build_iq_result() and wocky_porter_send().

Parameters

porter

a WockyPorter

 

stanza

a stanza of type WOCKY_STANZA_TYPE_IQ and sub-type either WOCKY_STANZA_SUB_TYPE_SET or WOCKY_STANZA_SUB_TYPE_GET

 

...

a wocky_stanza_build() specification; pass NULL to include no body in the reply.

 

wocky_porter_send_iq_error ()

void
wocky_porter_send_iq_error (WockyPorter *porter,
                            WockyStanza *stanza,
                            WockyXmppError error_code,
                            const gchar *message);

Sends an error reply for stanza back to its sender, with the given error_code and message , and including the child element from the original stanza.

To send error replies with more detailed error elements, see wocky_porter_send_iq_gerror(), or use wocky_stanza_build_iq_error() and wocky_porter_send() directly, possibly using wocky_stanza_error_to_node() to construct the error element.

Parameters

porter

the porter whence stanza came

 

stanza

a stanza of type WOCKY_STANZA_TYPE_IQ and sub-type either WOCKY_STANZA_SUB_TYPE_SET or WOCKY_STANZA_SUB_TYPE_GET

 

error_code

an XMPP Core stanza error code

 

message

an optional error message to include with the reply.

[allow-none]

wocky_porter_send_iq_gerror ()

void
wocky_porter_send_iq_gerror (WockyPorter *porter,
                             WockyStanza *stanza,
                             const GError *error);

Sends an error reply for stanza back to its sender, building the

<error/> element from the given error. To send error

replies with simple XMPP Core stanza errors in the WOCKY_XMPP_ERROR domain, wocky_porter_send_iq_error() may be more convenient to use.

Parameters

porter

the porter whence stanza came

 

stanza

a stanza of type WOCKY_STANZA_TYPE_IQ and sub-type either WOCKY_STANZA_SUB_TYPE_SET or WOCKY_STANZA_SUB_TYPE_GET

 

error

an error whose domain is either WOCKY_XMPP_ERROR, some other stanza error domain supplied with Wocky (such as WOCKY_JINGLE_ERROR or WOCKY_SI_ERROR), or a custom domain registered with wocky_xmpp_error_register_domain()

 

wocky_porter_force_close_async ()

void
wocky_porter_force_close_async (WockyPorter *porter,
                                GCancellable *cancellable,
                                GAsyncReadyCallback callback,
                                gpointer user_data);

Force the WockyPorter to close the TCP connection of the underlying WockyXmppConnection. If a close operation is pending, it will be completed with the WOCKY_PORTER_ERROR_FORCIBLY_CLOSED error. When the connection has been closed, callback will be called. You can then call wocky_porter_force_close_finish() to get the result of the operation.

Parameters

porter

a WockyPorter

 

cancellable

optional GCancellable object, NULL to ignore

 

callback

callback to call when the request is satisfied

 

user_data

the data to pass to callback function

 

wocky_porter_force_close_finish ()

gboolean
wocky_porter_force_close_finish (WockyPorter *porter,
                                 GAsyncResult *result,
                                 GError **error);

Finishes a force close operation.

Parameters

porter

a WockyPorter

 

result

a GAsyncResult

 

error

a GError location to store the error occuring, or NULL to ignore.

 

Returns

TRUE on success or FALSE on error.

Types and Values

enum WockyPorterError

The WockyPorter specific errors.

Members

WOCKY_PORTER_ERROR_NOT_STARTED

The WockyPorter has not been started yet

 

WOCKY_PORTER_ERROR_CLOSING

The WockyPorter is closing

 

WOCKY_PORTER_ERROR_CLOSED

The WockyPorter is closed

 

WOCKY_PORTER_ERROR_NOT_IQ

The WockyStanza is not an IQ

 

WOCKY_PORTER_ERROR_FORCIBLY_CLOSED

The WockyPorter has been forced to close

 

WOCKY_PORTER_HANDLER_PRIORITY_MIN

#define WOCKY_PORTER_HANDLER_PRIORITY_MIN 0

WOCKY_PORTER_HANDLER_PRIORITY_MAX

#define WOCKY_PORTER_HANDLER_PRIORITY_MAX G_MAXUINT

struct WockyPorterInterface

struct WockyPorterInterface {
  GTypeInterface parent_iface;

  const gchar * (*get_full_jid) (WockyPorter *self);
  const gchar * (*get_bare_jid) (WockyPorter *self);
  const gchar * (*get_resource) (WockyPorter *self);

  void (*start) (WockyPorter *porter);

  void (*send_async) (WockyPorter *porter,
      WockyStanza *stanza,
      GCancellable *cancellable,
      GAsyncReadyCallback callback,
      gpointer user_data);

  gboolean (*send_finish) (WockyPorter *porter,
      GAsyncResult *result,
      GError **error);

  guint (*register_handler_from_by_stanza) (
      WockyPorter *self,
      WockyStanzaType type,
      WockyStanzaSubType sub_type,
      const gchar *from,
      guint priority,
      WockyPorterHandlerFunc callback,
      gpointer user_data,
      WockyStanza *stanza);

  guint (*register_handler_from_anyone_by_stanza) (
      WockyPorter *self,
      WockyStanzaType type,
      WockyStanzaSubType sub_type,
      guint priority,
      WockyPorterHandlerFunc callback,
      gpointer user_data,
      WockyStanza *stanza);

  void (*unregister_handler) (WockyPorter *self,
      guint id);

  void (*close_async) (WockyPorter *self,
      GCancellable *cancellable,
      GAsyncReadyCallback callback,
      gpointer user_data);

  gboolean (*close_finish) (WockyPorter *self,
      GAsyncResult *result,
      GError **error);

  void (*send_iq_async) (WockyPorter *porter,
      WockyStanza *stanza,
      GCancellable *cancellable,
      GAsyncReadyCallback callback,
      gpointer user_data);

  WockyStanza * (*send_iq_finish) (WockyPorter *porter,
      GAsyncResult *result,
      GError **error);

  void (*force_close_async) (WockyPorter *porter,
      GCancellable *cancellable,
      GAsyncReadyCallback callback,
      gpointer user_data);

  gboolean (*force_close_finish) (WockyPorter *porter,
      GAsyncResult *result,
      GError **error);
};

The vtable for a porter implementation.

Members

GTypeInterface parent_iface;

Fields shared with GTypeInterface.

 

get_full_jid ()

Return the full JID of the user according to the porter; see wocky_porter_get_full_jid() for more details.

 

get_bare_jid ()

Return the bare JID of the user according to the porter; see wocky_porter_get_full_jid() for more details.

 

get_resource ()

Return the resource of the user according to the porter; see wocky_porter_get_full_jid() for more details.

 

start ()

Start the porter; see wocky_porter_start() for more details.

 

send_async ()

Start an asynchronous stanza send operation; see wocky_porter_send_async() for more details.

 

send_finish ()

Finish an asynchronous stanza send operation; see wocky_porter_send_finish() for more details.

 

register_handler_from_by_stanza ()

Register a stanza handler from a specific contact; see wocky_porter_register_handler_from_by_stanza() for more details.

 

register_handler_from_anyone_by_stanza ()

Register a stanza hander from any contact; see wocky_porter_register_handler_from_anyone_by_stanza() for more details.

 

unregister_handler ()

Unregister a stanza handler; see wocky_porter_unregister_handler() for more details.

 

close_async ()

Start an asynchronous porter close operation; see wocky_porter_close_async() for more details.

 

close_finish ()

Finish an asynchronous porter close operation; see wocky_porter_close_finish() for more details.

 

send_iq_async ()

Start an asynchronous IQ stanza send operation; see wocky_porter_send_iq_async() for more details.

 

send_iq_finish ()

Finish an asynchronous IQ stanza send operation; see wocky_porter_send_iq_finish() for more details.

 

force_close_async ()

Start an asynchronous porter force close operation; see wocky_porter_force_close_async() for more details.

 

force_close_finish ()

Finish an asynchronous porter force close operation; see wocky_porter_force_close_finish() for more details.

 

Property Details

The “bare-jid” property

  “bare-jid”                 gchar *

The user's bare JID (node@domain).

Flags: Read

Default value: NULL


The “connection” property

  “connection”               WockyXmppConnection *

The underlying WockyXmppConnection wrapped by the WockyPorter

Flags: Read / Write / Construct Only


The “full-jid” property

  “full-jid”                 gchar *

The user's full JID (node@domain/resource).

Flags: Read / Write / Construct Only

Default value: NULL


The “resource” property

  “resource”                 gchar *

The resource part of the user's full JID, or NULL if their full JID does not contain a resource at all.

Flags: Read

Default value: NULL

Signal Details

The “closing” signal

void
user_function (WockyPorter *porter,
               gpointer     user_data)

The ::closing signal is emitted when the WockyPorter starts to close its XMPP connection. Once this signal has been emitted, the WockyPorter can't be used to send stanzas any more.

Parameters

porter

the object on which the signal is emitted

 

user_data

user data set when the signal handler was connected.

 

Flags: Run Last


The “remote-closed” signal

void
user_function (WockyPorter *porter,
               gpointer     user_data)

The ::remote-closed signal is emitted when the other side closed the XMPP stream.

Parameters

porter

the object on which the signal is emitted

 

user_data

user data set when the signal handler was connected.

 

Flags: Run Last


The “remote-error” signal

void
user_function (WockyPorter *porter,
               guint        domain,
               gint         code,
               gchar       *message,
               gpointer     user_data)

The ::remote-error signal is emitted when an error has been detected on the XMPP stream.

Parameters

porter

the object on which the signal is emitted

 

domain

error domain (a GQuark)

 

code

error code

 

message

human-readable informative error message

 

user_data

user data set when the signal handler was connected.

 

Flags: Run Last


The “sending” signal

void
user_function (WockyPorter *porter,
               WockyStanza *stanza,
               gpointer     user_data)

The ::sending signal is emitted whenever WockyPorter sends data on the XMPP connection.

Parameters

porter

the object on which the signal is emitted

 

stanza

the WockyStanza being sent, or NULL if porter is just sending whitespace

 

user_data

user data set when the signal handler was connected.

 

Flags: Run Last

telepathy-gabble-0.18.2/lib/ext/wocky/docs/reference/html/WockyPubsubNode.html0000644000175000017500000020670212312537050027327 0ustar00smcvsmcv00000000000000 Wocky Reference Manual: WockyPubsubNode

WockyPubsubNode

WockyPubsubNode

Properties

gchar * name Read / Write / Construct Only
WockyPubsubService * service Read / Write / Construct Only

Types and Values

Object Hierarchy

    GBoxed
    ╰── WockyPubsubAffiliation
    GEnum
    ╰── WockyPubsubAffiliationState
    GObject
    ╰── WockyPubsubNode

Description

Functions

wocky_pubsub_node_get_name ()

const gchar *
wocky_pubsub_node_get_name (WockyPubsubNode *self);

wocky_pubsub_node_make_publish_stanza ()

WockyStanza *
wocky_pubsub_node_make_publish_stanza (WockyPubsubNode *self,
                                       WockyNode **pubsub_out,
                                       WockyNode **publish_out,
                                       WockyNode **item_out);

wocky_pubsub_node_subscribe_async ()

void
wocky_pubsub_node_subscribe_async (WockyPubsubNode *self,
                                   const gchar *jid,
                                   GCancellable *cancellable,
                                   GAsyncReadyCallback callback,
                                   gpointer user_data);

Attempts to subscribe to self .

Parameters

self

a pubsub node

 

jid

the JID to use as the subscribed JID (usually the connection's bare or full JID); may not be NULL

 

cancellable

optional GCancellable object, NULL to ignore

 

callback

a callback to call when the request is completed

 

user_data

data to pass to callback

 

wocky_pubsub_node_subscribe_finish ()

WockyPubsubSubscription *
wocky_pubsub_node_subscribe_finish (WockyPubsubNode *self,
                                    GAsyncResult *result,
                                    GError **error);

wocky_pubsub_node_unsubscribe_async ()

void
wocky_pubsub_node_unsubscribe_async (WockyPubsubNode *self,
                                     const gchar *jid,
                                     const gchar *subid,
                                     GCancellable *cancellable,
                                     GAsyncReadyCallback callback,
                                     gpointer user_data);

Attempts to unsubscribe from self .

Parameters

self

a pubsub node

 

jid

the JID subscribed to self (usually the connection's bare or full JID); may not be NULL

 

subid

the identifier associated with the subscription

 

cancellable

optional GCancellable object, NULL to ignore

 

callback

a callback to call when the request is completed

 

user_data

data to pass to callback

 

wocky_pubsub_node_unsubscribe_finish ()

gboolean
wocky_pubsub_node_unsubscribe_finish (WockyPubsubNode *self,
                                      GAsyncResult *result,
                                      GError **error);

wocky_pubsub_node_delete_async ()

void
wocky_pubsub_node_delete_async (WockyPubsubNode *self,
                                GCancellable *cancellable,
                                GAsyncReadyCallback callback,
                                gpointer user_data);

wocky_pubsub_node_delete_finish ()

gboolean
wocky_pubsub_node_delete_finish (WockyPubsubNode *self,
                                 GAsyncResult *result,
                                 GError **error);

wocky_pubsub_node_list_subscribers_async ()

void
wocky_pubsub_node_list_subscribers_async
                               (WockyPubsubNode *self,
                                GCancellable *cancellable,
                                GAsyncReadyCallback callback,
                                gpointer user_data);

Retrieves the list of subscriptions to a node you own. callback may complete the call using wocky_pubsub_node_list_subscribers_finish().

(A note on naming: this is §8.8.1 — Retrieve Subscriptions List — in XEP-0060, not to be confused with §5.6 — Retrieve Subscriptions. The different terminology in Wocky is intended to help disambiguate!)

Parameters

self

a pubsub node

 

cancellable

optional GCancellable object

 

callback

function to call when the subscribers have been retrieved or an error has occured

 

user_data

data to pass to callback .

 

wocky_pubsub_node_list_subscribers_finish ()

gboolean
wocky_pubsub_node_list_subscribers_finish
                               (WockyPubsubNode *self,
                                GAsyncResult *result,
                                GList **subscribers,
                                GError **error);

Completes a call to wocky_pubsub_node_list_subscribers_async(). The list returned in subscribers should be freed with wocky_pubsub_subscription_list_free() when it is no longer needed.

Parameters

self

a pubsub node

 

result

the result passed to a callback

 

subscribers

location at which to store a list of WockyPubsubSubscription pointers, or NULL

 

error

location at which to store an error, or NULL

 

Returns

TRUE if the list of subscribers was successfully retrieved; FALSE and sets error if an error occured.


wocky_pubsub_affiliation_new ()

WockyPubsubAffiliation *
wocky_pubsub_affiliation_new (WockyPubsubNode *node,
                              const gchar *jid,
                              WockyPubsubAffiliationState state);

Parameters

node

a node

 

jid

the JID affiliated to node

 

state

the state of jid 's affiliation to node

 

Returns

a new structure representing an affiliation, which should ultimately be freed with wocky_pubsub_affiliation_free()


wocky_pubsub_affiliation_copy ()

WockyPubsubAffiliation *
wocky_pubsub_affiliation_copy (WockyPubsubAffiliation *aff);

Parameters

aff

an existing affiliation structure

 

Returns

a duplicate of aff ; the duplicate should ultimately be freed with wocky_pubsub_affiliation_free()


wocky_pubsub_affiliation_free ()

void
wocky_pubsub_affiliation_free (WockyPubsubAffiliation *aff);

Frees an affiliation, previously allocated with wocky_pubsub_affiliation_new() or wocky_pubsub_affiliation_copy()

Parameters

aff

an affiliation

 

wocky_pubsub_affiliation_list_copy ()

GList *
wocky_pubsub_affiliation_list_copy (GList *affs);

Shorthand for manually copying affs , duplicating each element with wocky_pubsub_affiliation_copy().

Parameters

affs

a list of WockyPubsubAffiliation

 

Returns

a deep copy of affs , which should ultimately be freed with wocky_pubsub_affiliation_list_free().


wocky_pubsub_affiliation_list_free ()

void
wocky_pubsub_affiliation_list_free (GList *affs);

Frees a list of WockyPubsubAffiliation structures, as shorthand for calling wocky_pubsub_affiliation_free() for each element, followed by g_list_free().

Parameters

affs

a list of WockyPubsubAffiliation

 

wocky_pubsub_node_list_affiliates_async ()

void
wocky_pubsub_node_list_affiliates_async
                               (WockyPubsubNode *self,
                                GCancellable *cancellable,
                                GAsyncReadyCallback callback,
                                gpointer user_data);

Retrieves the list of entities affilied to a node you own. callback may complete the call using wocky_pubsub_node_list_affiliates_finish().

(A note on naming: this is §8.9.1 — Retrieve Affiliations List — in XEP-0060, not to be confused with §5.7 — Retrieve Affiliations. The slightly different terminology in Wocky is intended to help disambiguate!)

Parameters

self

a pubsub node

 

cancellable

optional GCancellable object

 

callback

function to call when the affiliates have been retrieved or an error has occured

 

user_data

data to pass to callback .

 

wocky_pubsub_node_list_affiliates_finish ()

gboolean
wocky_pubsub_node_list_affiliates_finish
                               (WockyPubsubNode *self,
                                GAsyncResult *result,
                                GList **affiliates,
                                GError **error);

Completes a call to wocky_pubsub_node_list_affiliates_async(). The list returned in affiliates should be freed with wocky_pubsub_affiliation_list_free() when it is no longer needed.

Parameters

self

a pubsub node

 

result

the result passed to a callback

 

affiliates

location at which to store a list of WockyPubsubAffiliation pointers, or NULL

 

error

location at which to store an error, or NULL

 

Returns

TRUE if the list of subscribers was successfully retrieved; FALSE and sets error if an error occured.


wocky_pubsub_node_modify_affiliates_async ()

void
wocky_pubsub_node_modify_affiliates_async
                               (WockyPubsubNode *self,
                                GList *affiliates,
                                GCancellable *cancellable,
                                GAsyncReadyCallback callback,
                                gpointer user_data);

Modifies the entities affiliated to a node that you own.

Parameters

self

a pubsub node

 

affiliates

a list of WockyPubsubAffiliation structures, describing only the affiliations which should be changed.

 

cancellable

optional GCancellable object, NULL to ignore

 

callback

a callback to call when the request is completed

 

user_data

data to pass to callback

 

wocky_pubsub_node_modify_affiliates_finish ()

gboolean
wocky_pubsub_node_modify_affiliates_finish
                               (WockyPubsubNode *self,
                                GAsyncResult *result,
                                GError **error);

Complete a call to wocky_pubsub_node_modify_affiliates_async().

Parameters

self

a node

 

result

the result

 

error

location at which to store an error, if one occurred.

 

Returns

TRUE if the affiliates were successfully modified; FALSE and sets error otherwise.


wocky_pubsub_node_get_configuration_async ()

void
wocky_pubsub_node_get_configuration_async
                               (WockyPubsubNode *self,
                                GCancellable *cancellable,
                                GAsyncReadyCallback callback,
                                gpointer user_data);

Retrieves the current configuration for a node owned by the user.

Parameters

self

a node

 

cancellable

optional GCancellable object, NULL to ignore

 

callback

a callback to call when the request is completed

 

user_data

data to pass to callback

 

wocky_pubsub_node_get_configuration_finish ()

WockyDataForm *
wocky_pubsub_node_get_configuration_finish
                               (WockyPubsubNode *self,
                                GAsyncResult *result,
                                GError **error);

Complete a call to wocky_pubsub_node_get_configuration_async().

Parameters

self

a node

 

result

the result

 

error

location at which to store an error, if one occurred.

 

Returns

a form representing the node configuration on success; NULL and sets error otherwise

Types and Values

struct WockyPubsubNodeClass

struct WockyPubsubNodeClass {
};

The class of a WockyPubsubNode.


enum WockyPubsubAffiliationState

Possible affiliations to a PubSub node, which determine privileges an entity has. See XEP-0060 §4.1 for the details.

Members

WOCKY_PUBSUB_AFFILIATION_OWNER

Owner

 

WOCKY_PUBSUB_AFFILIATION_PUBLISHER

Publisher

 

WOCKY_PUBSUB_AFFILIATION_PUBLISH_ONLY

Publish-Only

 

WOCKY_PUBSUB_AFFILIATION_MEMBER

Member

 

WOCKY_PUBSUB_AFFILIATION_NONE

None

 

WOCKY_PUBSUB_AFFILIATION_OUTCAST

Outcast

 

Property Details

The “name” property

  “name”                     gchar *

The name of the pubsub node.

Flags: Read / Write / Construct Only

Default value: NULL


The “service” property

  “service”                  WockyPubsubService *

the Wocky Pubsub service associated with this pubsub node.

Flags: Read / Write / Construct Only

Signal Details

The “deleted” signal

void
user_function (WockyPubsubNode *node,
               WockyStanza     *stanza,
               gpointer         event_node,
               gpointer         delete_node,
               gpointer         user_data)

Emitted when a notification of this node's deletion is received from the server.

Parameters

node

a pubsub node

 

stanza

the message/event stanza in its entirety

 

event_node

the event node from stanza

 

delete_node

the delete node from stanza

 

user_data

user data set when the signal handler was connected.

 

Flags: Run Last


The “event-received” signal

void
user_function (WockyPubsubNode *node,
               WockyStanza     *event_stanza,
               gpointer         event_node,
               gpointer         items_node,
               gpointer         items,
               gpointer         user_data)

Parameters

node

a pubsub node

 

event_stanza

the message/event stanza in its entirity

 

event_node

the event node from the stanza

 

items_node

the items node from the stanza

 

items

a list of WockyNode *s for each item child of items_node

 

user_data

user data set when the signal handler was connected.

 

The “subscription-state-changed” signal

void
user_function (WockyPubsubNode         *node,
               WockyStanza             *stanza,
               gpointer                 event_node,
               gpointer                 subscription_node,
               WockyPubsubSubscription *subscription,
               gpointer                 user_data)

Parameters

node

a pubsub node

 

stanza

the message/event stanza in its entirety

 

event_node

the event node from stanza

 

subscription_node

the subscription node from stanza

 

subscription

subscription information parsed from subscription_node

 

user_data

user data set when the signal handler was connected.

 

Flags: Run Last

telepathy-gabble-0.18.2/lib/ext/wocky/docs/reference/html/WockyPubsubService.html0000644000175000017500000012076212312537050030043 0ustar00smcvsmcv00000000000000 Wocky Reference Manual: WockyPubsubService

WockyPubsubService

WockyPubsubService

Properties

gchar * jid Read / Write / Construct Only
WockySession * session Read / Write / Construct Only

Object Hierarchy

    GBoxed
    ╰── WockyPubsubSubscription
    GEnum
    ├── WockyPubsubServiceError
    ╰── WockyPubsubSubscriptionState
    GObject
    ╰── WockyPubsubService

Description

Functions

wocky_pubsub_service_error_quark ()

GQuark
wocky_pubsub_service_error_quark (void);

WOCKY_PUBSUB_SERVICE_ERROR

#define WOCKY_PUBSUB_SERVICE_ERROR (wocky_pubsub_service_error_quark ())

wocky_pubsub_service_new ()

WockyPubsubService *
wocky_pubsub_service_new (WockySession *session,
                          const gchar *jid);

wocky_pubsub_service_ensure_node ()

WockyPubsubNode *
wocky_pubsub_service_ensure_node (WockyPubsubService *self,
                                  const gchar *name);

Fetches or creates an object representing a node on the pubsub service. Note that this does not ensure that a node exists on the server; it merely ensures a local representation.

Parameters

self

a pubsub service

 

name

the name of a node on self

 

Returns

a new reference to an object representing a node named name on self


wocky_pubsub_service_lookup_node ()

WockyPubsubNode *
wocky_pubsub_service_lookup_node (WockyPubsubService *self,
                                  const gchar *name);

Fetches an object representing a node on a pubsub service, if the object already exists; if not, returns NULL. Note that this does not check whether a node exists on the server; it only checks for a local representation.

Parameters

self

a pubsub service

 

name

the name of a node on self

 

Returns

a borrowed reference to a node, or NULL


wocky_pubsub_service_get_default_node_configuration_async ()

void
wocky_pubsub_service_get_default_node_configuration_async
                               (WockyPubsubService *self,
                                GCancellable *cancellable,
                                GAsyncReadyCallback callback,
                                gpointer user_data);

wocky_pubsub_service_get_default_node_configuration_finish ()

WockyDataForm *
wocky_pubsub_service_get_default_node_configuration_finish
                               (WockyPubsubService *self,
                                GAsyncResult *result,
                                GError **error);

wocky_pubsub_service_retrieve_subscriptions_async ()

void
wocky_pubsub_service_retrieve_subscriptions_async
                               (WockyPubsubService *self,
                                WockyPubsubNode *node,
                                GCancellable *cancellable,
                                GAsyncReadyCallback callback,
                                gpointer user_data);

wocky_pubsub_service_retrieve_subscriptions_finish ()

gboolean
wocky_pubsub_service_retrieve_subscriptions_finish
                               (WockyPubsubService *self,
                                GAsyncResult *result,
                                GList **subscriptions,
                                GError **error);

wocky_pubsub_service_create_node_async ()

void
wocky_pubsub_service_create_node_async
                               (WockyPubsubService *self,
                                const gchar *name,
                                WockyDataForm *config,
                                GCancellable *cancellable,
                                GAsyncReadyCallback callback,
                                gpointer user_data);

wocky_pubsub_service_create_node_finish ()

WockyPubsubNode *
wocky_pubsub_service_create_node_finish
                               (WockyPubsubService *self,
                                GAsyncResult *result,
                                GError **error);

wocky_pubsub_subscription_new ()

WockyPubsubSubscription *
wocky_pubsub_subscription_new (WockyPubsubNode *node,
                               const gchar *jid,
                               WockyPubsubSubscriptionState state,
                               const gchar *subid);

wocky_pubsub_subscription_copy ()

WockyPubsubSubscription *
wocky_pubsub_subscription_copy (WockyPubsubSubscription *sub);

wocky_pubsub_subscription_free ()

void
wocky_pubsub_subscription_free (WockyPubsubSubscription *sub);

wocky_pubsub_subscription_list_copy ()

GList *
wocky_pubsub_subscription_list_copy (GList *subs);

wocky_pubsub_subscription_list_free ()

void
wocky_pubsub_subscription_list_free (GList *subs);

Types and Values

enum WockyPubsubServiceError

WockyPubsubService specific errors.

Members

WOCKY_PUBSUB_SERVICE_ERROR_WRONG_REPLY

A wrong reply was received

 

enum WockyPubsubSubscriptionState

Describes the state of a subscription to a node. Definitions are taken from

XEP-0060 §4.2.

Members

WOCKY_PUBSUB_SUBSCRIPTION_NONE

The node MUST NOT send event notifications or payloads to the Entity.

 

WOCKY_PUBSUB_SUBSCRIPTION_PENDING

An entity has requested to subscribe to a node and the request has not yet been approved by a node owner. The node MUST NOT send event notifications or payloads to the entity while it is in this state.

 

WOCKY_PUBSUB_SUBSCRIPTION_SUBSCRIBED

An entity has subscribed but its subscription options have not yet been configured. The node MAY send event notifications or payloads to the entity while it is in this state. The service MAY timeout unconfigured subscriptions.

 

WOCKY_PUBSUB_SUBSCRIPTION_UNCONFIGURED

An entity is subscribed to a node. The node MUST send all event notifications (and, if configured, payloads) to the entity while it is in this state (subject to subscriber configuration and content filtering).

 

Property Details

The “jid” property

  “jid”                      gchar *

The jid of the pubsub service.

Flags: Read / Write / Construct Only

Default value: NULL


The “session” property

  “session”                  WockySession *

the Wocky Session associated with this pubsub service.

Flags: Read / Write / Construct Only

Signal Details

The “event-received” signal

void
user_function (WockyPubsubService *service,
               WockyPubsubNode    *node,
               WockyStanza        *event_stanza,
               gpointer            event_node,
               gpointer            items_node,
               gpointer            items,
               gpointer            user_data)

Emitted when an event is received for a node.

Parameters

service

a pubsub service

 

node

the node on service for which an event has been received wire

 

event_stanza

the message/event stanza in its entirity

 

event_node

the event node from the stanza

 

items_node

the items node from the stanza

 

items

a list of WockyNode *s for each item child of items_node

 

user_data

user data set when the signal handler was connected.

 

The “node-deleted” signal

void
user_function (WockyPubsubService *node,
               WockyPubsubNode    *stanza,
               WockyStanza        *event_node,
               gpointer            delete_node,
               gpointer            arg4,
               gpointer            user_data)

Emitted when a notification of a node's deletion is received from the server.

Parameters

node

a pubsub node

 

stanza

the message/event stanza in its entirety

 

event_node

the event node from stanza

 

delete_node

the delete node from stanza

 

user_data

user data set when the signal handler was connected.

 

Flags: Run Last


The “subscription-state-changed” signal

void
user_function (WockyPubsubService      *service,
               WockyPubsubNode         *node,
               WockyStanza             *stanza,
               gpointer                 event_node,
               gpointer                 subscription_node,
               WockyPubsubSubscription *subscription,
               gpointer                 user_data)

Parameters

service

a pubsub service

 

node

a pubsub node for which the subscription state has changed

 

stanza

the message/event stanza in its entirety

 

event_node

the event node from stanza

 

subscription_node

the subscription node from stanza

 

subscription

subscription information parsed from subscription_node

 

user_data

user data set when the signal handler was connected.

 

Flags: Run Last

telepathy-gabble-0.18.2/lib/ext/wocky/docs/reference/html/WockyResourceContact.html0000644000175000017500000002522512312537050030363 0ustar00smcvsmcv00000000000000 Wocky Reference Manual: WockyResourceContact

WockyResourceContact

WockyResourceContact

Properties

WockyBareContact * bare-contact Read / Write / Construct Only
gchar * resource Read / Write / Construct Only

Types and Values

Object Hierarchy

    GObject
    ╰── WockyContact
        ╰── WockyResourceContact

Includes

#include <wocky/wocky-resource-contact.h>

Description

Functions

wocky_resource_contact_new ()

WockyResourceContact *
wocky_resource_contact_new (WockyBareContact *bare,
                            const gchar *resource);

wocky_resource_contact_get_resource ()

const gchar *
wocky_resource_contact_get_resource (WockyResourceContact *contact);

wocky_resource_contact_get_bare_contact ()

WockyBareContact *
wocky_resource_contact_get_bare_contact
                               (WockyResourceContact *contact);

wocky_resource_contact_equal ()

gboolean
wocky_resource_contact_equal (WockyResourceContact *a,
                              WockyResourceContact *b);

Types and Values

struct WockyResourceContactClass

struct WockyResourceContactClass {
};

The class of a WockyResourceContact.

Property Details

The “bare-contact” property

  “bare-contact”             WockyBareContact *

The WockyBareContact associated with this WockyResourceContact

Flags: Read / Write / Construct Only


The “resource” property

  “resource”                 gchar *

The resource of the contact.

Flags: Read / Write / Construct Only

Default value: NULL

telepathy-gabble-0.18.2/lib/ext/wocky/docs/reference/html/WockyRoster.html0000644000175000017500000010337212312537050026536 0ustar00smcvsmcv00000000000000 Wocky Reference Manual: WockyRoster

WockyRoster

WockyRoster — TODO

Properties

WockySession * session Read / Write / Construct Only

Signals

Object Hierarchy

    GObject
    ╰── WockyRoster

Description

TODO

Functions

wocky_roster_error_quark ()

GQuark
wocky_roster_error_quark (void);

Get the error quark used by the roster.

Returns

the quark for roster errors.


WOCKY_ROSTER_ERROR

#define WOCKY_ROSTER_ERROR (wocky_roster_error_quark ())

Get access to the error quark of the roster.


wocky_roster_new ()

WockyRoster *
wocky_roster_new (WockySession *session);

wocky_roster_fetch_roster_async ()

void
wocky_roster_fetch_roster_async (WockyRoster *self,
                                 GCancellable *cancellable,
                                 GAsyncReadyCallback callback,
                                 gpointer user_data);

wocky_roster_fetch_roster_finish ()

gboolean
wocky_roster_fetch_roster_finish (WockyRoster *self,
                                  GAsyncResult *result,
                                  GError **error);

wocky_roster_get_contact ()

WockyBareContact *
wocky_roster_get_contact (WockyRoster *self,
                          const gchar *jid);

wocky_roster_get_all_contacts ()

GSList *
wocky_roster_get_all_contacts (WockyRoster *self);

wocky_roster_add_contact_async ()

void
wocky_roster_add_contact_async (WockyRoster *self,
                                const gchar *jid,
                                const gchar *name,
                                const gchar * const *groups,
                                GCancellable *cancellable,
                                GAsyncReadyCallback callback,
                                gpointer user_data);

wocky_roster_add_contact_finish ()

gboolean
wocky_roster_add_contact_finish (WockyRoster *self,
                                 GAsyncResult *result,
                                 GError **error);

wocky_roster_remove_contact_async ()

void
wocky_roster_remove_contact_async (WockyRoster *self,
                                   WockyBareContact *contact,
                                   GCancellable *cancellable,
                                   GAsyncReadyCallback callback,
                                   gpointer user_data);

wocky_roster_remove_contact_finish ()

gboolean
wocky_roster_remove_contact_finish (WockyRoster *self,
                                    GAsyncResult *result,
                                    GError **error);

wocky_roster_change_contact_name_async ()

void
wocky_roster_change_contact_name_async
                               (WockyRoster *self,
                                WockyBareContact *contact,
                                const gchar *name,
                                GCancellable *cancellable,
                                GAsyncReadyCallback callback,
                                gpointer user_data);

wocky_roster_change_contact_name_finish ()

gboolean
wocky_roster_change_contact_name_finish
                               (WockyRoster *self,
                                GAsyncResult *result,
                                GError **error);

wocky_roster_contact_add_group_async ()

void
wocky_roster_contact_add_group_async (WockyRoster *self,
                                      WockyBareContact *contact,
                                      const gchar *group,
                                      GCancellable *cancellable,
                                      GAsyncReadyCallback callback,
                                      gpointer user_data);

wocky_roster_contact_add_group_finish ()

gboolean
wocky_roster_contact_add_group_finish (WockyRoster *self,
                                       GAsyncResult *result,
                                       GError **error);

wocky_roster_contact_remove_group_async ()

void
wocky_roster_contact_remove_group_async
                               (WockyRoster *self,
                                WockyBareContact *contact,
                                const gchar *group,
                                GCancellable *cancellable,
                                GAsyncReadyCallback callback,
                                gpointer user_data);

wocky_roster_contact_remove_group_finish ()

gboolean
wocky_roster_contact_remove_group_finish
                               (WockyRoster *self,
                                GAsyncResult *result,
                                GError **error);

wocky_roster_subscription_to_string ()

const gchar *
wocky_roster_subscription_to_string (WockyRosterSubscriptionFlags subscription);

Types and Values

struct WockyRosterClass

struct WockyRosterClass {
};

The class of a WockyRoster.


enum WockyRosterSubscriptionFlags

Flags to document the subscription information between contacts.

Members

WOCKY_ROSTER_SUBSCRIPTION_TYPE_NONE

the user does not have a subscription to the contact's presence information, and the contact does not have a subscription to the user's presence information

 

WOCKY_ROSTER_SUBSCRIPTION_TYPE_TO

the user has a subscription to the contact's presence information, but the contact does not have a subscription to the user's presence information

 

WOCKY_ROSTER_SUBSCRIPTION_TYPE_FROM

the contact has a subscription to the user's presence information, but the user does not have a subscription to the contact's presence information

 

WOCKY_ROSTER_SUBSCRIPTION_TYPE_BOTH

both the user and the contact have subscriptions to each other's presence information

 

enum WockyRosterError

The WockyRosterError specific errors.

Members

WOCKY_ROSTER_ERROR_INVALID_STANZA

received an invalid roster stanza from the server

 

WOCKY_ROSTER_ERROR_NOT_IN_ROSTER

the contact is not in the roster

 

Property Details

The “session” property

  “session”                  WockySession *

the wocky session used by this roster.

Flags: Read / Write / Construct Only

Signal Details

The “added” signal

void
user_function (WockyRoster *wockyroster,
               GObject     *arg1,
               gpointer     user_data)

Flags: Run Last


The “removed” signal

void
user_function (WockyRoster *wockyroster,
               GObject     *arg1,
               gpointer     user_data)

Flags: Run Last

telepathy-gabble-0.18.2/lib/ext/wocky/docs/reference/html/WockySaslAuth.html0000644000175000017500000003344712312537050027011 0ustar00smcvsmcv00000000000000 Wocky Reference Manual: WockySaslAuth

WockySaslAuth

WockySaslAuth

Properties

WockyAuthRegistry * auth-registry Read / Write / Construct Only
WockyXmppConnection * connection Read / Write / Construct Only
gchar * password Write / Construct
gchar * server Read / Write / Construct
gchar * username Write / Construct

Types and Values

Object Hierarchy

    GObject
    ╰── WockySaslAuth

Description

Functions

wocky_sasl_auth_new ()

WockySaslAuth *
wocky_sasl_auth_new (const gchar *server,
                     const gchar *username,
                     const gchar *password,
                     WockyXmppConnection *connection,
                     WockyAuthRegistry *auth_registry);

wocky_sasl_auth_add_handler ()

void
wocky_sasl_auth_add_handler (WockySaslAuth *sasl,
                             WockyAuthHandler *handler);

wocky_sasl_auth_authenticate_async ()

void
wocky_sasl_auth_authenticate_async (WockySaslAuth *sasl,
                                    WockyStanza *features,
                                    gboolean allow_plain,
                                    gboolean is_secure,
                                    GCancellable *cancellable,
                                    GAsyncReadyCallback callback,
                                    gpointer user_data);

wocky_sasl_auth_authenticate_finish ()

gboolean
wocky_sasl_auth_authenticate_finish (WockySaslAuth *sasl,
                                     GAsyncResult *result,
                                     GError **error);

Types and Values

struct WockySaslAuthClass

struct WockySaslAuthClass {
};

The class of a WockySaslAuth.

Property Details

The “auth-registry” property

  “auth-registry”            WockyAuthRegistry *

Authentication Registry.

Flags: Read / Write / Construct Only


The “connection” property

  “connection”               WockyXmppConnection *

The Xmpp connection to user.

Flags: Read / Write / Construct Only


The “password” property

  “password”                 gchar *

The password to authenticate with.

Flags: Write / Construct

Default value: NULL


The “server” property

  “server”                   gchar *

The name of the server.

Flags: Read / Write / Construct

Default value: NULL


The “username” property

  “username”                 gchar *

The username to authenticate with.

Flags: Write / Construct

Default value: NULL

telepathy-gabble-0.18.2/lib/ext/wocky/docs/reference/html/WockySession.html0000644000175000017500000003231212312537050026676 0ustar00smcvsmcv00000000000000 Wocky Reference Manual: WockySession

WockySession

WockySession

Properties

WockyXmppConnection * connection Read / Write / Construct Only
WockyContactFactory * contact-factory Read
gchar * full-jid Read / Write / Construct Only
WockyPorter * porter Read

Types and Values

Object Hierarchy

    GObject
    ╰── WockySession

Includes

#include <wocky/wocky-session.h>

Description

Functions

wocky_session_new_ll ()

WockySession *
wocky_session_new_ll (const gchar *full_jid);

wocky_session_new_with_connection ()

WockySession *
wocky_session_new_with_connection (WockyXmppConnection *conn,
                                   const gchar *full_jid);

wocky_session_start ()

void
wocky_session_start (WockySession *session);

wocky_session_get_porter ()

WockyPorter *
wocky_session_get_porter (WockySession *session);

wocky_session_get_contact_factory ()

WockyContactFactory *
wocky_session_get_contact_factory (WockySession *session);

wocky_session_set_jid ()

void
wocky_session_set_jid (WockySession *session,
                       const gchar *jid);

wocky_session_get_jid ()

const gchar *
wocky_session_get_jid (WockySession *session);

Types and Values

struct WockySessionClass

struct WockySessionClass {
};

The class of a WockySession.

Property Details

The “connection” property

  “connection”               WockyXmppConnection *

The WockyXmppConnection associated with this session.

Flags: Read / Write / Construct Only


The “contact-factory” property

  “contact-factory”          WockyContactFactory *

The WockyContactFactory associated with this session.

Flags: Read


The “full-jid” property

  “full-jid”                 gchar *

The user's JID in this session.

Flags: Read / Write / Construct Only

Default value: NULL


The “porter” property

  “porter”                   WockyPorter *

The WockyPorter associated with this session.

Flags: Read

telepathy-gabble-0.18.2/lib/ext/wocky/docs/reference/html/WockyStanza.html0000644000175000017500000014402112312537050026514 0ustar00smcvsmcv00000000000000 Wocky Reference Manual: WockyStanza

WockyStanza

WockyStanza

Types and Values

Object Hierarchy

    GObject
    ╰── WockyNodeTree
        ╰── WockyStanza

Description

Functions

wocky_stanza_new ()

WockyStanza *
wocky_stanza_new (const gchar *name,
                  const gchar *ns);

wocky_stanza_copy ()

WockyStanza *
wocky_stanza_copy (WockyStanza *old);

wocky_stanza_get_top_node ()

WockyNode *
wocky_stanza_get_top_node (WockyStanza *self);

Parameters

self

a stanza

 

Returns

A pointer to the topmost node of the stanza


wocky_stanza_build ()

WockyStanza *
wocky_stanza_build (WockyStanzaType type,
                    WockyStanzaSubType sub_type,
                    const gchar *from,
                    const gchar *to,
                    ...);

Build a XMPP stanza from a list of arguments. For example, the following invocation:

1
2
3
4
5
6
7
8
9
10
11
wocky_stanza_build (
   WOCKY_STANZA_TYPE_MESSAGE, WOCKY_STANZA_SUB_TYPE_NONE,
   "alice@<!-- -->collabora.co.uk", "bob@<!-- -->collabora.co.uk",
   WOCKY_NODE_START, "html",
     WOCKY_NODE_XMLNS, "http://www.w3.org/1999/xhtml",
     WOCKY_NODE, "body",
       WOCKY_NODE_ATTRIBUTE, "textcolor", "red",
       WOCKY_NODE_TEXT, "Telepathy rocks!",
     WOCKY_NODE_END,
   WOCKY_NODE_END,
  NULL);

produces this stanza:

1
2
3
4
5
6
7
<message from='alice@<!-- -->collabora.co.uk' to='bob@<!-- -->collabora.co.uk'>
  <html xmlns='http://www.w3.org/1999/xhtml'>
    <body textcolor='red'>
      Telepathy rocks!
    </body>
  </html>
</message>

You may optionally use mnemonic ASCII characters in place of the build tags, to better reflect the structure of the stanza in C source. For example, the above stanza could be written as:

1
2
3
4
5
6
7
8
9
wocky_stanza_build (
   WOCKY_STANZA_TYPE_MESSAGE, WOCKY_STANZA_SUB_TYPE_NONE,
   "alice@<!-- -->collabora.co.uk", "bob@<!-- -->collabora.co.uk",
   '(', "html", ':', "http://www.w3.org/1999/xhtml",
     '(', "body", '@', "textcolor", "red",
       '$', "Telepathy rocks!",
     ')',
   ')'
  NULL);

Parameters

type

The type of stanza to build

 

sub_type

The stanza's subtype; valid values depend on type . (For instance, WOCKY_STANZA_TYPE_IQ can use WOCKY_STANZA_SUB_TYPE_GET, but not WOCKY_STANZA_SUB_TYPE_SUBSCRIBED.)

 

from

The sender's JID, or NULL to leave it unspecified.

 

to

The target's JID, or NULL to leave it unspecified.

 

...

the description of the stanza to build, terminated with NULL

 

Returns

a new stanza object


wocky_stanza_build_to_contact ()

WockyStanza *
wocky_stanza_build_to_contact (WockyStanzaType type,
                               WockyStanzaSubType sub_type,
                               const gchar *from,
                               WockyContact *to,
                               ...);

wocky_stanza_get_type_info ()

void
wocky_stanza_get_type_info (WockyStanza *stanza,
                            WockyStanzaType *type,
                            WockyStanzaSubType *sub_type);

wocky_stanza_has_type ()

gboolean
wocky_stanza_has_type (WockyStanza *stanza,
                       WockyStanzaType expected_type);

wocky_stanza_get_from ()

const gchar *
wocky_stanza_get_from (WockyStanza *self);

Parameters

self

a stanza

 

Returns

The sender of self , or NULL if no sender was specified.


wocky_stanza_get_to ()

const gchar *
wocky_stanza_get_to (WockyStanza *self);

Parameters

self

a stanza

 

Returns

The recipient of self , or NULL if no recipient was specified.


wocky_stanza_build_va ()

WockyStanza *
wocky_stanza_build_va (WockyStanzaType type,
                       WockyStanzaSubType sub_type,
                       const gchar *from,
                       const gchar *to,
                       va_list ap);

wocky_stanza_build_iq_result ()

WockyStanza *
wocky_stanza_build_iq_result (WockyStanza *iq,
                              ...);

wocky_stanza_build_iq_result_va ()

WockyStanza *
wocky_stanza_build_iq_result_va (WockyStanza *iq,
                                 va_list ap);

wocky_stanza_build_iq_error ()

WockyStanza *
wocky_stanza_build_iq_error (WockyStanza *iq,
                             ...);

Builds an error reply to iq containing the given body. This function also adds the child element of iq to the reply, as recommended by RFC3920 §9.2.3 ‘IQ Semantics’.

No <error/> element is added to the reply. To add a standard stanza error, plus message, consider using wocky_stanza_error_to_node(). To add a more complicated error with an application-specific condition, specify it when calling this function. For example:

1
2
3
4
5
6
7
8
WockyStanza *reply = wocky_stanza_build_iq_error (iq,
   '(', "error",
     '@', "type", "cancel",
     '(', "feature-not-implemented", ':', WOCKY_XMPP_NS_STANZAS, ')',
     '(', "unsupported", ':', WOCKY_XMPP_NS_PUBSUB_ERRORS,
       '@', "feature", "subscribe",
     ')',
   ')', NULL);

Parameters

iq

a stanza of type WOCKY_STANZA_TYPE_IQ and sub-type either WOCKY_STANZA_SUB_TYPE_SET or WOCKY_STANZA_SUB_TYPE_GET

 

...

a wocky_stanza_build() specification

 

Returns

an error reply for iq


wocky_stanza_build_iq_error_va ()

WockyStanza *
wocky_stanza_build_iq_error_va (WockyStanza *iq,
                                va_list ap);

wocky_stanza_extract_errors ()

gboolean
wocky_stanza_extract_errors (WockyStanza *stanza,
                             WockyXmppErrorType *type,
                             GError **core,
                             GError **specialized,
                             WockyNode **specialized_node);

Given a message, iq or presence stanza with type='error', breaks it down into values describing the error. type and core are guaranteed to be set; specialized and specialized_node will be set if a recognised application-specific error is found, and the latter will be set to NULL if no application-specific error is found.

Any or all of the out parameters may be NULL to ignore the value. The value stored in specialized_node is borrowed from stanza , and is only valid as long as the latter is alive.

Parameters

stanza

a message/iq/presence stanza

 

type

location at which to store the error type

 

core

location at which to store an error in the domain WOCKY_XMPP_ERROR

 

specialized

location at which to store an error in an application-specific domain, if one is found

 

specialized_node

location at which to store the node representing an application-specific error, if one is found

 

Returns

TRUE if the stanza had type='error'; FALSE otherwise


wocky_stanza_extract_stream_error ()

gboolean
wocky_stanza_extract_stream_error (WockyStanza *stanza,
                                   GError **stream_error);

Parameters

stanza

a stanza

 

stream_error

location at which to store an error in domain WOCKY_XMPP_STREAM_ERROR, if one is found.

 

Returns

TRUE and sets stream_error if the stanza was indeed a stream error.


wocky_stanza_get_to_contact ()

WockyContact *
wocky_stanza_get_to_contact (WockyStanza *self);

wocky_stanza_get_from_contact ()

WockyContact *
wocky_stanza_get_from_contact (WockyStanza *self);

wocky_stanza_set_to_contact ()

void
wocky_stanza_set_to_contact (WockyStanza *self,
                             WockyContact *contact);

wocky_stanza_set_from_contact ()

void
wocky_stanza_set_from_contact (WockyStanza *self,
                               WockyContact *contact);

Types and Values

struct WockyStanzaClass

struct WockyStanzaClass {
};

The class of a WockyStanza.


enum WockyStanzaType

XMPP stanza types.

Members

WOCKY_STANZA_TYPE_NONE

no stanza type

 

WOCKY_STANZA_TYPE_MESSAGE

<message/> stanza  

WOCKY_STANZA_TYPE_PRESENCE

<presence/> stanza  

WOCKY_STANZA_TYPE_IQ

<iq/> stanza  

WOCKY_STANZA_TYPE_STREAM

<stream/> stanza  

WOCKY_STANZA_TYPE_STREAM_FEATURES

<stream:features/> stanza  

WOCKY_STANZA_TYPE_AUTH

<auth/> stanza  

WOCKY_STANZA_TYPE_CHALLENGE

<challenge/> stanza  

WOCKY_STANZA_TYPE_RESPONSE

<response/> stanza  

WOCKY_STANZA_TYPE_SUCCESS

<success/> stanza  

WOCKY_STANZA_TYPE_FAILURE

<failure/> stanza  

WOCKY_STANZA_TYPE_STREAM_ERROR

<stream:error/> stanza  

WOCKY_STANZA_TYPE_UNKNOWN

unknown stanza type

 

enum WockyStanzaSubType

XMPP stanza sub types.

Members

WOCKY_STANZA_SUB_TYPE_NONE

no sub type

 

WOCKY_STANZA_SUB_TYPE_AVAILABLE

"available" stanza sub type

 

WOCKY_STANZA_SUB_TYPE_NORMAL

"normal" stanza sub type

 

WOCKY_STANZA_SUB_TYPE_CHAT

"chat" stanza sub type

 

WOCKY_STANZA_SUB_TYPE_GROUPCHAT

"groupchat" stanza sub type

 

WOCKY_STANZA_SUB_TYPE_HEADLINE

"headline" stanza sub type

 

WOCKY_STANZA_SUB_TYPE_UNAVAILABLE

"unavailable" stanza sub type

 

WOCKY_STANZA_SUB_TYPE_PROBE

"probe" stanza sub type

 

WOCKY_STANZA_SUB_TYPE_SUBSCRIBE

"subscribe" stanza sub type

 

WOCKY_STANZA_SUB_TYPE_UNSUBSCRIBE

"unsubscribe" stanza sub type

 

WOCKY_STANZA_SUB_TYPE_SUBSCRIBED

"subscribed" stanza sub type

 

WOCKY_STANZA_SUB_TYPE_UNSUBSCRIBED

"unsubscribed" stanza sub type

 

WOCKY_STANZA_SUB_TYPE_GET

"get" stanza sub type

 

WOCKY_STANZA_SUB_TYPE_SET

"set" stanza sub type

 

WOCKY_STANZA_SUB_TYPE_RESULT

"result" stanza sub type

 

WOCKY_STANZA_SUB_TYPE_ERROR

"error" stanza sub type

 

WOCKY_STANZA_SUB_TYPE_UNKNOWN

unknown stanza sub type

 
telepathy-gabble-0.18.2/lib/ext/wocky/docs/reference/html/WockyTLSConnector.html0000644000175000017500000002307512312537050027576 0ustar00smcvsmcv00000000000000 Wocky Reference Manual: WockyTLSConnector

WockyTLSConnector

WockyTLSConnector

Properties

WockyTLSHandler * tls-handler Read / Write / Construct Only

Types and Values

Object Hierarchy

    GObject
    ╰── WockyTLSConnector

Description

Functions

wocky_tls_connector_new ()

WockyTLSConnector *
wocky_tls_connector_new (WockyTLSHandler *handler);

wocky_tls_connector_secure_async ()

void
wocky_tls_connector_secure_async (WockyTLSConnector *self,
                                  WockyXmppConnection *connection,
                                  gboolean old_style_ssl,
                                  const gchar *peername,
                                  GStrv extra_identities,
                                  GCancellable *cancellable,
                                  GAsyncReadyCallback callback,
                                  gpointer user_data);

wocky_tls_connector_secure_finish ()

WockyXmppConnection *
wocky_tls_connector_secure_finish (WockyTLSConnector *self,
                                   GAsyncResult *res,
                                   GError **error);

Types and Values

struct WockyTLSConnectorClass

struct WockyTLSConnectorClass {
};

The class of a WockyTLSConnector.

Property Details

The “tls-handler” property

  “tls-handler”              WockyTLSHandler *

The WockyTLSHandler object used for the TLS handshake.

Flags: Read / Write / Construct Only

telepathy-gabble-0.18.2/lib/ext/wocky/docs/reference/html/WockyTLSHandler.html0000644000175000017500000006231112312537050027215 0ustar00smcvsmcv00000000000000 Wocky Reference Manual: WockyTLSHandler

WockyTLSHandler

WockyTLSHandler

Properties

gboolean ignore-ssl-errors Read / Write / Construct

Types and Values

Object Hierarchy

    GObject
    ╰── WockyTLSHandler

Description

Functions

WockyTLSHandlerVerifyAsyncFunc ()

void
(*WockyTLSHandlerVerifyAsyncFunc) (WockyTLSHandler *self,
                                   WockyTLSSession *tls_session,
                                   const gchar *peername,
                                   GStrv extra_identities,
                                   GAsyncReadyCallback callback,
                                   gpointer user_data);

WockyTLSHandlerVerifyFinishFunc ()

gboolean
(*WockyTLSHandlerVerifyFinishFunc) (WockyTLSHandler *self,
                                    GAsyncResult *res,
                                    GError **error);

wocky_tls_handler_new ()

WockyTLSHandler *
wocky_tls_handler_new (gboolean ignore_ssl_errors);

wocky_tls_handler_verify_async ()

void
wocky_tls_handler_verify_async (WockyTLSHandler *self,
                                WockyTLSSession *tls_session,
                                const gchar *peername,
                                GStrv extra_identities,
                                GAsyncReadyCallback callback,
                                gpointer user_data);

wocky_tls_handler_verify_finish ()

gboolean
wocky_tls_handler_verify_finish (WockyTLSHandler *self,
                                 GAsyncResult *result,
                                 GError **error);

wocky_tls_handler_add_ca ()

gboolean
wocky_tls_handler_add_ca (WockyTLSHandler *self,
                          const gchar *path);

Adds a single CA certificate, or directory full of CA certificates, to the set used to check certificates. By default, Wocky will check the system-wide certificate directory (as determined at compile time), so you need only add additional CA paths if you want to trust additional CAs.

Parameters

self

a WockyTLSHandler instance

 

path

a path to a directory or file containing PEM encoded CA certificates

 

Returns

TRUE if path could be resolved to an absolute path. Note that this does not indicate that there was actually a file or directory there or that any CAs were actually found. The CAs won't actually be loaded until just before the TLS session setup is attempted.


wocky_tls_handler_forget_cas ()

void
wocky_tls_handler_forget_cas (WockyTLSHandler *self);

Removes all known locations for CA certificates, including the system-wide certificate directory and any paths added by previous calls to wocky_tls_handler_add_ca(). This is only useful if you want Wocky to distrust your system CAs for some reason.

Parameters

self

a WockyTLSHandler instance

 

wocky_tls_handler_add_crl ()

gboolean
wocky_tls_handler_add_crl (WockyTLSHandler *self,
                           const gchar *path);

Adds a single certificate revocation list file, or a directory of CRLs, to the set used to check certificates. Unlike for CA certificates, there is typically no good default path, so no CRLs are used by default. The path to use depends on the CRL-management software you use; dirmngr (for example) will cache CRLs in /var/cache/dirmngr/crls.d.

Parameters

self

a WockyTLSHandler instance

 

path

a path to a directory or file containing PEM encoded CRL certificates

 

Returns

TRUE if path could be resolved to an absolute path. Note that this does not indicate that there was actually a file or directory there or that any CRLs were actually found. The CRLs won't actually be loaded until just before the TLS session setup is attempted.


wocky_tls_handler_get_cas ()

GSList *
wocky_tls_handler_get_cas (WockyTLSHandler *self);

Gets the CA certificate search path, including any extra paths added with wocky_tls_handler_add_ca().

Parameters

self

a WockyTLSHandler instance

 

Returns

the paths to search for CA certificates.

[transfer none][element-type utf8]


wocky_tls_handler_get_crl ()

GSList *
wocky_tls_handler_get_crl (WockyTLSHandler *self);

Gets the CRL search path, consisting of all paths added with wocky_tls_handler_add_crl().

Parameters

self

a WockyTLSHandler instance

 

Returns

the CRL search path.

[transfer none][element-type utf8]

Types and Values

struct WockyTLSHandlerClass

struct WockyTLSHandlerClass {
  WockyTLSHandlerVerifyAsyncFunc verify_async_func;
  WockyTLSHandlerVerifyFinishFunc verify_finish_func;
};

The class of a WockyTLSHandler.

Members

WockyTLSHandlerVerifyAsyncFunc verify_async_func;

a function to call to start an asychronous verify operation; see wocky_tls_handler_verify_async() for more details

 

WockyTLSHandlerVerifyFinishFunc verify_finish_func;

a function to call to finish an asychronous verify operation; see wocky_tls_handler_verify_finish() for more details

 

Property Details

The “ignore-ssl-errors” property

  “ignore-ssl-errors”        gboolean

Whether to ignore recoverable SSL errors (certificate insecurity/expiry etc).

Flags: Read / Write / Construct

Default value: FALSE

telepathy-gabble-0.18.2/lib/ext/wocky/docs/reference/html/wocky-WockyCapsHash.html0000644000175000017500000002051712312537050030103 0ustar00smcvsmcv00000000000000 Wocky Reference Manual: WockyCapsHash

WockyCapsHash

WockyCapsHash — Utilities for computing verification string hash

Object Hierarchy


Description

Computes verification string hashes according to XEP-0115 v1.5

Functions

wocky_caps_hash_compute_from_node ()

gchar *
wocky_caps_hash_compute_from_node (WockyNode *node);

Compute the hash as defined by the XEP-0115 from a received WockyNode.

node should be the top-level node from a disco response such as the example given in XEP-0115 §5.3 "Complex Generation Example".

Parameters

node

a WockyNode

 

Returns

the hash. The called must free the returned hash with g_free().


wocky_caps_hash_compute_from_lists ()

gchar *
wocky_caps_hash_compute_from_lists (GPtrArray *features,
                                    GPtrArray *identities,
                                    GPtrArray *dataforms);

Compute the hash as defined by the XEP-0115 from a list of features, identities and dataforms.

Parameters

features

a GPtrArray of strings of features

 

identities

a GPtrArray of WockyDiscoIdentity structures

 

dataforms

a GPtrArray of WockyDataForm objects, or NULL

 

Returns

a newly allocated string of the caps hash which should be freed using g_free()

Types and Values

telepathy-gabble-0.18.2/lib/ext/wocky/docs/reference/html/wocky-wocky-debug.html0000644000175000017500000001140012312537050027603 0ustar00smcvsmcv00000000000000 Wocky Reference Manual: wocky-debug

wocky-debug

wocky-debug

Functions

Types and Values

Object Hierarchy


Description

Functions

WOCKY_DEBUG_XMPP

#define WOCKY_DEBUG_XMPP (WOCKY_DEBUG_XMPP_READER | WOCKY_DEBUG_XMPP_WRITER)

wocky_debug_set_flags ()

void
wocky_debug_set_flags (WockyDebugFlags flags);

Types and Values

enum WockyDebugFlags

Members

telepathy-gabble-0.18.2/lib/ext/wocky/docs/reference/html/wocky-wocky-enumtypes.html0000644000175000017500000000514612312537050030560 0ustar00smcvsmcv00000000000000 Wocky Reference Manual: wocky-enumtypes

wocky-enumtypes

wocky-enumtypes

Object Hierarchy


Description

Functions

Types and Values

telepathy-gabble-0.18.2/lib/ext/wocky/docs/reference/html/wocky-wocky-heartbeat-source.html0000644000175000017500000002054312312537050031762 0ustar00smcvsmcv00000000000000 Wocky Reference Manual: wocky-heartbeat-source

wocky-heartbeat-source

wocky-heartbeat-source

Object Hierarchy


Description

Functions

WockyHeartbeatCallback ()

void
(*WockyHeartbeatCallback) (gpointer user_data);

wocky_heartbeat_source_new ()

GSource *
wocky_heartbeat_source_new (guint max_interval);

Creates a source which calls its callback at least every max_interval seconds. This is similar to g_timeout_source_new_seconds(), except that the callback may be called slightly earlier than requested, in sync with other periodic network activity (from other XMPP connections, or other applications entirely).

When calling g_source_set_callback() on this source, the supplied callback's signature should match WockyHeartbeatCallback.

Parameters

max_interval

the maximum interval between calls to the source's callback, in seconds. Pass 0 to prevent the callback being called.

 

Returns

the newly-created source.


wocky_heartbeat_source_update_interval ()

void
wocky_heartbeat_source_update_interval
                               (GSource *source,
                                guint max_interval);

Updates the interval between calls to source 's callback. The new interval may not take effect until after the next call to the callback.

Parameters

source

a source returned by wocky_heartbeat_source_new()

 

max_interval

the new maximum interval between calls to the source's callback, in seconds. Pass 0 to stop the callback being called.

 

Types and Values

telepathy-gabble-0.18.2/lib/ext/wocky/docs/reference/html/wocky-wocky.html0000644000175000017500000001170712312537050026531 0ustar00smcvsmcv00000000000000 Wocky Reference Manual: wocky

wocky

wocky

Functions

void wocky_init ()
void wocky_deinit ()

Types and Values

#define WOCKY_H_INSIDE

Object Hierarchy


Description

Functions

wocky_init ()

void
wocky_init (void);

Initializes the Wocky library.

This function should be called before calling any other Wocky functions.


wocky_deinit ()

void
wocky_deinit (void);

Clean up any resources created by Wocky in wocky_init().

It is normally not needed to call this function in a normal application as the resources will automatically be freed when the program terminates. This function is therefore mostly used by testsuites and other memory profiling tools.

After this call Wocky (including this method) should not be used anymore.

Types and Values

WOCKY_H_INSIDE

#define WOCKY_H_INSIDE
telepathy-gabble-0.18.2/lib/ext/wocky/docs/reference/html/wocky-wocky-http-proxy.html0000644000175000017500000000556712312537050030674 0ustar00smcvsmcv00000000000000 Wocky Reference Manual: wocky-http-proxy

wocky-http-proxy

wocky-http-proxy

Object Hierarchy

    GObject
    ╰── WockyHttpProxy

Description

Functions

Types and Values

telepathy-gabble-0.18.2/lib/ext/wocky/docs/reference/html/wocky-wocky-jabber-auth-digest.html0000644000175000017500000000774412312537050032176 0ustar00smcvsmcv00000000000000 Wocky Reference Manual: wocky-jabber-auth-digest

wocky-jabber-auth-digest

wocky-jabber-auth-digest

Functions

WockyJabberAuthDigest * wocky_jabber_auth_digest_new ()

Object Hierarchy


Description

Functions

wocky_jabber_auth_digest_new ()

WockyJabberAuthDigest *
wocky_jabber_auth_digest_new (const gchar *server,
                              const gchar *password);

Types and Values

telepathy-gabble-0.18.2/lib/ext/wocky/docs/reference/html/wocky-wocky-jabber-auth-password.html0000644000175000017500000000747412312537050032561 0ustar00smcvsmcv00000000000000 Wocky Reference Manual: wocky-jabber-auth-password

wocky-jabber-auth-password

wocky-jabber-auth-password

Functions

WockyJabberAuthPassword * wocky_jabber_auth_password_new ()

Object Hierarchy


Description

Functions

wocky_jabber_auth_password_new ()

WockyJabberAuthPassword *
wocky_jabber_auth_password_new (const gchar *password);

Types and Values

telepathy-gabble-0.18.2/lib/ext/wocky/docs/reference/html/wocky-wocky-jingle-info-internal.html0000644000175000017500000001027312312537050032537 0ustar00smcvsmcv00000000000000 Wocky Reference Manual: wocky-jingle-info-internal

wocky-jingle-info-internal

wocky-jingle-info-internal

Types and Values

Object Hierarchy

    GEnum
    ╰── WockyStunServerSource

Description

Functions

Types and Values

enum WockyStunServerSource

Members

WOCKY_STUN_SERVER_USER_SPECIFIED

   

WOCKY_STUN_SERVER_DISCOVERED

   

WOCKY_STUN_SERVER_FALLBACK

   
telepathy-gabble-0.18.2/lib/ext/wocky/docs/reference/html/wocky-wocky-jingle-types.html0000644000175000017500000022661512312537050031147 0ustar00smcvsmcv00000000000000 Wocky Reference Manual: wocky-jingle-types

wocky-jingle-types

wocky-jingle-types

Properties

WockyJingleContent * content Read / Write / Construct Only
guint state Read / Write
gchar * transport-ns Read / Write / Construct Only
WockyJingleContent * content Read / Write / Construct Only
guint state Read / Write
gchar * transport-ns Read / Write / Construct Only
WockySession * session Read / Write / Construct Only
gchar * content-ns Read / Write
gchar * disposition Read / Write
gboolean locally-created Read
gchar * name Read / Write / Construct Only
guint senders Read / Write
WockyJingleSession * session Read / Write / Construct Only
guint state Read / Write
gchar * transport-ns Read / Write
WockyJingleContent * content Read / Write / Construct Only
guint state Read / Write
gchar * transport-ns Read / Write / Construct Only
guint media-type Read / Write / Construct Only
gboolean remote-mute Read / Write
guint dialect Read / Write
WockyJingleFactory * jingle-factory Read / Write / Construct Only
gboolean local-hold Read / Write
gboolean local-initiator Read / Write / Construct Only
WockyContact * peer-contact Read / Write / Construct Only
WockyPorter * porter Read / Write / Construct Only
gboolean remote-hold Read
gboolean remote-ringing Read
gchar * session-id Read / Write / Construct Only
guint state Read / Write

Object Hierarchy

    GEnum
    ╰── WockyJingleReason
    GObject
    ├── WockyJingleContent
       ╰── WockyJingleMediaRtp
    ├── WockyJingleContent
       ╰── WockyJingleMediaRtp
    ├── WockyJingleFactory
    ├── WockyJingleSession
    ├── WockyJingleTransportGoogle
    ├── WockyJingleTransportIceUdp
    ╰── WockyJingleTransportRawUdp

Implemented Interfaces

WockyJingleTransportIceUdp implements

WockyJingleTransportGoogle implements

WockyJingleTransportRawUdp implements

Description

Functions

Types and Values

enum WockyJingleDialect

Members

WOCKY_JINGLE_DIALECT_ERROR

   

WOCKY_JINGLE_DIALECT_GTALK3

   

WOCKY_JINGLE_DIALECT_GTALK4

   

WOCKY_JINGLE_DIALECT_V015

   

WOCKY_JINGLE_DIALECT_V032

   

enum WockyJingleState

Possible states of a WockyJingleSession.

Members

WOCKY_JINGLE_STATE_PENDING_CREATED

on outgoing sessions, no offer has been sent to the peer yet.

 

WOCKY_JINGLE_STATE_PENDING_INITIATE_SENT

on outgoing sessions, we have sent the session-initiate and are awaiting the peer's acknowledgement.

 

WOCKY_JINGLE_STATE_PENDING_INITIATED

on outgoing sessions, the peer has received our session-initiate and we're waiting for them to accept; on incoming sessions, the peer is waiting for us to accept.

 

WOCKY_JINGLE_STATE_PENDING_ACCEPT_SENT

on incoming sessions, we have sent session-accept and are waiting for the peer to acknowledge it.

 

WOCKY_JINGLE_STATE_ACTIVE

the session is active.

 

WOCKY_JINGLE_STATE_ENDED

the session has ended. The “terminated” signal describes how the session ended.

 

enum WockyJingleAction

Members

WOCKY_JINGLE_ACTION_UNKNOWN

   

WOCKY_JINGLE_ACTION_CONTENT_ACCEPT

   

WOCKY_JINGLE_ACTION_CONTENT_ADD

   

WOCKY_JINGLE_ACTION_CONTENT_MODIFY

   

WOCKY_JINGLE_ACTION_CONTENT_REMOVE

   

WOCKY_JINGLE_ACTION_CONTENT_REPLACE

   

WOCKY_JINGLE_ACTION_CONTENT_REJECT

   

WOCKY_JINGLE_ACTION_SESSION_ACCEPT

   

WOCKY_JINGLE_ACTION_SESSION_INFO

   

WOCKY_JINGLE_ACTION_SESSION_INITIATE

   

WOCKY_JINGLE_ACTION_SESSION_TERMINATE

   

WOCKY_JINGLE_ACTION_TRANSPORT_INFO

   

WOCKY_JINGLE_ACTION_TRANSPORT_ACCEPT

   

WOCKY_JINGLE_ACTION_DESCRIPTION_INFO

   

WOCKY_JINGLE_ACTION_INFO

   

enum WockyJingleContentSenders

Members

WOCKY_JINGLE_CONTENT_SENDERS_NONE

   

WOCKY_JINGLE_CONTENT_SENDERS_INITIATOR

   

WOCKY_JINGLE_CONTENT_SENDERS_RESPONDER

   

WOCKY_JINGLE_CONTENT_SENDERS_BOTH

   

enum WockyJingleTransportType

Members

JINGLE_TRANSPORT_UNKNOWN

   

JINGLE_TRANSPORT_GOOGLE_P2P

   

JINGLE_TRANSPORT_RAW_UDP

   

JINGLE_TRANSPORT_ICE_UDP

   

enum WockyJingleTransportProtocol

Members

WOCKY_JINGLE_TRANSPORT_PROTOCOL_UDP

   

WOCKY_JINGLE_TRANSPORT_PROTOCOL_TCP

   

enum WockyJingleCandidateType

Members

WOCKY_JINGLE_CANDIDATE_TYPE_LOCAL

   

WOCKY_JINGLE_CANDIDATE_TYPE_STUN

   

WOCKY_JINGLE_CANDIDATE_TYPE_RELAY

   

enum WockyJingleReason

The reason for a Jingle action occurring—specifically, the reason for terminating a call. See XEP-0166 Jingle §7.4 for definitions of the codes.

Members

WOCKY_JINGLE_REASON_UNKNOWN

no known reason

 

WOCKY_JINGLE_REASON_ALTERNATIVE_SESSION

   

WOCKY_JINGLE_REASON_BUSY

   

WOCKY_JINGLE_REASON_CANCEL

   

WOCKY_JINGLE_REASON_CONNECTIVITY_ERROR

   

WOCKY_JINGLE_REASON_DECLINE

   

WOCKY_JINGLE_REASON_EXPIRED

   

WOCKY_JINGLE_REASON_FAILED_APPLICATION

   

WOCKY_JINGLE_REASON_FAILED_TRANSPORT

   

WOCKY_JINGLE_REASON_GENERAL_ERROR

   

WOCKY_JINGLE_REASON_GONE

   

WOCKY_JINGLE_REASON_INCOMPATIBLE_PARAMETERS

   

WOCKY_JINGLE_REASON_MEDIA_ERROR

   

WOCKY_JINGLE_REASON_SECURITY_ERROR

   

WOCKY_JINGLE_REASON_SUCCESS

   

WOCKY_JINGLE_REASON_TIMEOUT

   

WOCKY_JINGLE_REASON_UNSUPPORTED_APPLICATIONS

   

WOCKY_JINGLE_REASON_UNSUPPORTED_TRANSPORTS

   

WockyJingleTransportIceUdp

typedef struct _WockyJingleTransportIceUdp WockyJingleTransportIceUdp;

WockyJingleTransportGoogle

typedef struct _WockyJingleTransportGoogle WockyJingleTransportGoogle;

WockyJingleFactory

typedef struct _WockyJingleFactory WockyJingleFactory;

WockyJingleContent

typedef struct _WockyJingleContent WockyJingleContent;

WockyJingleCandidate

typedef struct {
  WockyJingleTransportProtocol protocol;
  WockyJingleCandidateType type;

  gchar *id;
  gchar *address;
  int port;
  int component;
  int generation;

  int preference;
  gchar *username;
  gchar *password;
  int network;
} WockyJingleCandidate;

WockyJingleTransportRawUdp

typedef struct _WockyJingleTransportRawUdp WockyJingleTransportRawUdp;

WockyJingleMediaRtp

typedef struct _WockyJingleMediaRtp WockyJingleMediaRtp;

WockyJingleSession

typedef struct _WockyJingleSession WockyJingleSession;

Property Details

The “content” property

  “content”                  WockyJingleContent *

Jingle content object using this transport.

Flags: Read / Write / Construct Only


The “state” property

  “state”                    guint

Enum specifying the connection state of the transport.

Flags: Read / Write

Allowed values: <= 2

Default value: 0


The “transport-ns” property

  “transport-ns”             gchar *

Namespace identifying the transport type.

Flags: Read / Write / Construct Only

Default value: NULL


The “content” property

  “content”                  WockyJingleContent *

Jingle content object using this transport.

Flags: Read / Write / Construct Only


The “state” property

  “state”                    guint

Enum specifying the connection state of the transport.

Flags: Read / Write

Allowed values: <= 2

Default value: 0


The “transport-ns” property

  “transport-ns”             gchar *

Namespace identifying the transport type.

Flags: Read / Write / Construct Only

Default value: NULL


The “session” property

  “session”                  WockySession *

WockySession to listen for Jingle sessions on.

Flags: Read / Write / Construct Only


The “content-ns” property

  “content-ns”               gchar *

Namespace identifying the content type.

Flags: Read / Write

Default value: NULL


The “disposition” property

  “disposition”              gchar *

Distinguishes between 'session' and other contents.

Flags: Read / Write

Default value: NULL


The “locally-created” property

  “locally-created”          gboolean

True if the content was created by the local client.

Flags: Read

Default value: FALSE


The “name” property

  “name”                     gchar *

A unique content name in the session.

Flags: Read / Write / Construct Only

Default value: NULL


The “senders” property

  “senders”                  guint

Valid senders for the stream.

Flags: Read / Write

Default value: 0


The “session” property

  “session”                  WockyJingleSession *

Jingle session object that owns this content.

Flags: Read / Write / Construct Only


The “state” property

  “state”                    guint

The current state that the content is in.

Flags: Read / Write

Default value: 0


The “transport-ns” property

  “transport-ns”             gchar *

Namespace identifying the transport type.

Flags: Read / Write

Default value: NULL


The “content” property

  “content”                  WockyJingleContent *

Jingle content object using this transport.

Flags: Read / Write / Construct Only


The “state” property

  “state”                    guint

Enum specifying the connection state of the transport.

Flags: Read / Write

Allowed values: <= 2

Default value: 0


The “transport-ns” property

  “transport-ns”             gchar *

Namespace identifying the transport type.

Flags: Read / Write / Construct Only

Default value: NULL


The “media-type” property

  “media-type”               guint

Media type.

Flags: Read / Write / Construct Only

Default value: 0


The “remote-mute” property

  “remote-mute”              gboolean

TRUE if the peer has muted this stream.

Flags: Read / Write

Default value: FALSE


The “dialect” property

  “dialect”                  guint

Jingle dialect used for this session.

Flags: Read / Write

Default value: 0


The “jingle-factory” property

  “jingle-factory”           WockyJingleFactory *

The Jingle factory which created this session.

Flags: Read / Write / Construct Only


The “local-hold” property

  “local-hold”               gboolean

TRUE if we've placed the peer on hold.

Flags: Read / Write

Default value: FALSE


The “local-initiator” property

  “local-initiator”          gboolean

Specifies if local end initiated the session.

Flags: Read / Write / Construct Only

Default value: TRUE


The “peer-contact” property

  “peer-contact”             WockyContact *

The WockyContact representing the other party in the session. Note that if this is a WockyBareContact (as opposed to a WockyResourceContact) the session is with the contact's bare JID.

Flags: Read / Write / Construct Only


The “porter” property

  “porter”                   WockyPorter *

The WockyPorter for the current connection.

Flags: Read / Write / Construct Only


The “remote-hold” property

  “remote-hold”              gboolean

TRUE if the peer has placed us on hold.

Flags: Read

Default value: FALSE


The “remote-ringing” property

  “remote-ringing”           gboolean

TRUE if the peer's client is ringing.

Flags: Read

Default value: FALSE


The “session-id” property

  “session-id”               gchar *

A unique session identifier used throughout all communication.

Flags: Read / Write / Construct Only

Default value: NULL


The “state” property

  “state”                    guint

The current state that the session is in.

Flags: Read / Write

Default value: 0

Signal Details

The “new-candidates” signal

void
user_function (WockyJingleTransportIceUdp *wockyjingletransporticeudp,
               gpointer                    arg1,
               gpointer                    user_data)

Flags: Run Last


The “new-candidates” signal

void
user_function (WockyJingleTransportGoogle *wockyjingletransportgoogle,
               gpointer                    arg1,
               gpointer                    user_data)

Flags: Run Last


The “new-session” signal

void
user_function (WockyJingleFactory *wockyjinglefactory,
               WockyJingleSession *arg1,
               gboolean            arg2,
               gpointer            user_data)

Flags: Run Last


The “query-cap” signal

gboolean
user_function (WockyJingleFactory *wockyjinglefactory,
               WockyContact       *arg1,
               gchar              *arg2,
               gpointer            user_data)

Flags: Run Last


The “completed” signal

void
user_function (WockyJingleContent *wockyjinglecontent,
               gpointer            user_data)

Flags: Run Last


The “new-candidates” signal

void
user_function (WockyJingleContent *content,
               gpointer            candidates,
               gpointer            user_data)

Emitted when new candidates are received from the peer.

Parameters

content

the content

 

candidates

a GList of new candidates.

[type GList][element-type WockyJingleCandidate]

user_data

user data set when the signal handler was connected.

 

Flags: Run Last


The “new-share-channel” signal

void
user_function (WockyJingleContent *wockyjinglecontent,
               gchar              *arg1,
               guint               arg2,
               gpointer            user_data)

Flags: Run Last


The “ready” signal

void
user_function (WockyJingleContent *wockyjinglecontent,
               gpointer            user_data)

Flags: Has Details


The “removed” signal

void
user_function (WockyJingleContent *wockyjinglecontent,
               gpointer            user_data)

Flags: Has Details


The “new-candidates” signal

void
user_function (WockyJingleTransportRawUdp *wockyjingletransportrawudp,
               gpointer                    arg1,
               gpointer                    user_data)

Flags: Run Last


The “remote-media-description” signal

void
user_function (WockyJingleMediaRtp *content,
               gpointer             md,
               gpointer             user_data)

Emitted when the remote media description is received or subsequently updated.

Parameters

content

the RTP content

 

md

a WockyJingleMediaDescription

 

user_data

user data set when the signal handler was connected.

 

Flags: Run Last


The “about-to-initiate” signal

void
user_function (WockyJingleSession *wockyjinglesession,
               gpointer            user_data)

Flags: Run Last


The “content-rejected” signal

void
user_function (WockyJingleSession *wockyjinglesession,
               GObject            *arg1,
               guint               arg2,
               gchar              *arg3,
               gpointer            user_data)

Flags: Run Last


The “new-content” signal

void
user_function (WockyJingleSession *wockyjinglesession,
               GObject            *arg1,
               gpointer            user_data)

Flags: Run Last


The “query-cap” signal

gboolean
user_function (WockyJingleSession *wockyjinglesession,
               WockyContact       *arg1,
               gchar              *arg2,
               gpointer            user_data)

Flags: Run Last


The “remote-state-changed” signal

void
user_function (WockyJingleSession *wockyjinglesession,
               gpointer            user_data)

Flags: Run Last


The “terminated” signal

void
user_function (WockyJingleSession *session,
               gboolean            locally_terminated,
               guint               reason,
               gchar              *text,
               gpointer            user_data)

Emitted when the session ends, just after “state” moves to WOCKY_JINGLE_STATE_ENDED.

Parameters

session

the session

 

locally_terminated

TRUE if the session ended due to a call to wocky_jingle_session_terminate(); FALSE if the peer ended the session.

 

reason

a WockyJingleReason describing why the session terminated

 

text

a possibly-NULL human-readable string describing why the session terminated

 

user_data

user data set when the signal handler was connected.

 

Flags: Run Last

telepathy-gabble-0.18.2/lib/ext/wocky/docs/reference/html/wocky-wocky-namespaces.html0000644000175000017500000007714312312537050030654 0ustar00smcvsmcv00000000000000 Wocky Reference Manual: wocky-namespaces

wocky-namespaces

wocky-namespaces

Types and Values

#define WOCKY_XMPP_NS_JABBER_CLIENT
#define WOCKY_XMPP_NS_STREAM
#define WOCKY_XMPP_NS_STREAMS
#define WOCKY_XMPP_NS_BIND
#define WOCKY_XMPP_NS_SESSION
#define WOCKY_XMPP_NS_TLS
#define WOCKY_XMPP_NS_SASL_AUTH
#define WOCKY_NS_DISCO_INFO
#define WOCKY_NS_DISCO_ITEMS
#define WOCKY_XMPP_NS_XHTML_IM
#define WOCKY_XMPP_NS_IBB
#define WOCKY_XMPP_NS_AMP
#define WOCKY_W3C_NS_XHTML
#define WOCKY_TELEPATHY_NS_CAPS
#define WOCKY_TELEPATHY_NS_TUBES
#define WOCKY_TELEPATHY_NS_OLPC_ACTIVITY_PROPS
#define WOCKY_XMPP_NS_SI
#define WOCKY_XMPP_NS_FEATURENEG
#define WOCKY_XMPP_NS_DATA
#define WOCKY_XMPP_NS_EVENT
#define WOCKY_XMPP_NS_DELAY
#define WOCKY_XMPP_NS_STANZAS
#define WOCKY_XMPP_NS_IQ_OOB
#define WOCKY_XMPP_NS_X_OOB
#define WOCKY_TELEPATHY_NS_CLIQUE
#define WOCKY_XEP77_NS_REGISTER
#define WOCKY_XMPP_NS_JINGLE015
#define WOCKY_XMPP_NS_JINGLE_DESCRIPTION_AUDIO
#define WOCKY_XMPP_NS_JINGLE_DESCRIPTION_VIDEO
#define WOCKY_XMPP_NS_JINGLE
#define WOCKY_XMPP_NS_JINGLE_ERRORS
#define WOCKY_XMPP_NS_JINGLE_RTP
#define WOCKY_XMPP_NS_JINGLE_RTP_ERRORS
#define WOCKY_XMPP_NS_JINGLE_RTP_INFO
#define WOCKY_XMPP_NS_JINGLE_RTP_AUDIO
#define WOCKY_XMPP_NS_JINGLE_RTP_VIDEO
#define WOCKY_XMPP_NS_JINGLE_RTCP_FB
#define WOCKY_XMPP_NS_JINGLE_RTP_HDREXT
#define WOCKY_XMPP_NS_GOOGLE_SESSION
#define WOCKY_XMPP_NS_GOOGLE_SESSION_PHONE
#define WOCKY_XMPP_NS_GOOGLE_SESSION_VIDEO
#define WOCKY_XMPP_NS_GOOGLE_SESSION_SHARE
#define WOCKY_XMPP_NS_GOOGLE_TRANSPORT_P2P
#define WOCKY_XMPP_NS_JINGLE_TRANSPORT_RAWUDP
#define WOCKY_XMPP_NS_JINGLE_TRANSPORT_ICEUDP
#define WOCKY_QUIRK_OMITS_CONTENT_CREATORS
#define WOCKY_QUIRK_GOOGLE_WEBMAIL_CLIENT
#define WOCKY_QUIRK_ANDROID_GTALK_CLIENT
#define WOCKY_XMPP_NS_GOOGLE_JINGLE_INFO
#define WOCKY_JABBER_NS_AUTH
#define WOCKY_JABBER_NS_AUTH_FEATURE
#define WOCKY_GOOGLE_NS_AUTH
#define WOCKY_XMPP_NS_ROSTER
#define WOCKY_XMPP_NS_PUBSUB
#define WOCKY_XMPP_NS_PUBSUB_EVENT
#define WOCKY_XMPP_NS_PUBSUB_OWNER
#define WOCKY_XMPP_NS_PUBSUB_NODE_CONFIG
#define WOCKY_XMPP_NS_PUBSUB_ERRORS
#define WOCKY_XMPP_NS_PING
#define WOCKY_NS_MUC
#define WOCKY_NS_MUC_USER
#define WOCKY_NS_MUC_ADMIN
#define WOCKY_NS_MUC_OWNER
#define WOCKY_NS_MUC_UNIQUE
#define WOCKY_NS_CHATSTATE
#define WOCKY_NS_GOOGLE_SESSION_PHONE
#define WOCKY_NS_GOOGLE_SESSION_VIDEO
#define WOCKY_NS_VCARD_TEMP
#define WOCKY_NS_VCARD_TEMP_UPDATE

Object Hierarchy


Description

Functions

Types and Values

WOCKY_XMPP_NS_JABBER_CLIENT

#define             WOCKY_XMPP_NS_JABBER_CLIENT

WOCKY_XMPP_NS_STREAM

#define             WOCKY_XMPP_NS_STREAM

WOCKY_XMPP_NS_STREAMS

#define             WOCKY_XMPP_NS_STREAMS

WOCKY_XMPP_NS_BIND

#define             WOCKY_XMPP_NS_BIND

WOCKY_XMPP_NS_SESSION

#define             WOCKY_XMPP_NS_SESSION

WOCKY_XMPP_NS_TLS

#define             WOCKY_XMPP_NS_TLS

WOCKY_XMPP_NS_SASL_AUTH

#define             WOCKY_XMPP_NS_SASL_AUTH

WOCKY_NS_DISCO_INFO

#define             WOCKY_NS_DISCO_INFO

WOCKY_NS_DISCO_ITEMS

#define             WOCKY_NS_DISCO_ITEMS

WOCKY_XMPP_NS_XHTML_IM

#define             WOCKY_XMPP_NS_XHTML_IM

WOCKY_XMPP_NS_IBB

#define             WOCKY_XMPP_NS_IBB

WOCKY_XMPP_NS_AMP

#define             WOCKY_XMPP_NS_AMP

WOCKY_W3C_NS_XHTML

#define             WOCKY_W3C_NS_XHTML

WOCKY_TELEPATHY_NS_CAPS

#define             WOCKY_TELEPATHY_NS_CAPS

WOCKY_TELEPATHY_NS_TUBES

#define             WOCKY_TELEPATHY_NS_TUBES

WOCKY_TELEPATHY_NS_OLPC_ACTIVITY_PROPS

#define             WOCKY_TELEPATHY_NS_OLPC_ACTIVITY_PROPS

WOCKY_XMPP_NS_SI

#define             WOCKY_XMPP_NS_SI

WOCKY_XMPP_NS_FEATURENEG

#define             WOCKY_XMPP_NS_FEATURENEG

WOCKY_XMPP_NS_DATA

#define             WOCKY_XMPP_NS_DATA

WOCKY_XMPP_NS_EVENT

#define             WOCKY_XMPP_NS_EVENT

WOCKY_XMPP_NS_DELAY

#define             WOCKY_XMPP_NS_DELAY

WOCKY_XMPP_NS_STANZAS

#define             WOCKY_XMPP_NS_STANZAS

WOCKY_XMPP_NS_IQ_OOB

#define             WOCKY_XMPP_NS_IQ_OOB

WOCKY_XMPP_NS_X_OOB

#define             WOCKY_XMPP_NS_X_OOB

WOCKY_TELEPATHY_NS_CLIQUE

#define             WOCKY_TELEPATHY_NS_CLIQUE

WOCKY_XEP77_NS_REGISTER

#define             WOCKY_XEP77_NS_REGISTER

WOCKY_XMPP_NS_JINGLE015

#define             WOCKY_XMPP_NS_JINGLE015

WOCKY_XMPP_NS_JINGLE_DESCRIPTION_AUDIO

#define             WOCKY_XMPP_NS_JINGLE_DESCRIPTION_AUDIO

WOCKY_XMPP_NS_JINGLE_DESCRIPTION_VIDEO

#define             WOCKY_XMPP_NS_JINGLE_DESCRIPTION_VIDEO

WOCKY_XMPP_NS_JINGLE

#define             WOCKY_XMPP_NS_JINGLE

WOCKY_XMPP_NS_JINGLE_ERRORS

#define             WOCKY_XMPP_NS_JINGLE_ERRORS

WOCKY_XMPP_NS_JINGLE_RTP

#define             WOCKY_XMPP_NS_JINGLE_RTP

WOCKY_XMPP_NS_JINGLE_RTP_ERRORS

#define             WOCKY_XMPP_NS_JINGLE_RTP_ERRORS

WOCKY_XMPP_NS_JINGLE_RTP_INFO

#define             WOCKY_XMPP_NS_JINGLE_RTP_INFO

WOCKY_XMPP_NS_JINGLE_RTP_AUDIO

#define             WOCKY_XMPP_NS_JINGLE_RTP_AUDIO

WOCKY_XMPP_NS_JINGLE_RTP_VIDEO

#define             WOCKY_XMPP_NS_JINGLE_RTP_VIDEO

WOCKY_XMPP_NS_JINGLE_RTCP_FB

#define WOCKY_XMPP_NS_JINGLE_RTCP_FB       "urn:xmpp:jingle:apps:rtp:rtcp-fb:0"

WOCKY_XMPP_NS_JINGLE_RTP_HDREXT

#define WOCKY_XMPP_NS_JINGLE_RTP_HDREXT    "urn:xmpp:jingle:apps:rtp:rtp-hdrext:0"

WOCKY_XMPP_NS_GOOGLE_SESSION

#define WOCKY_XMPP_NS_GOOGLE_SESSION       "http://www.google.com/session"

WOCKY_XMPP_NS_GOOGLE_SESSION_PHONE

#define WOCKY_XMPP_NS_GOOGLE_SESSION_PHONE "http://www.google.com/session/phone"

WOCKY_XMPP_NS_GOOGLE_SESSION_VIDEO

#define WOCKY_XMPP_NS_GOOGLE_SESSION_VIDEO "http://www.google.com/session/video"

WOCKY_XMPP_NS_GOOGLE_SESSION_SHARE

#define WOCKY_XMPP_NS_GOOGLE_SESSION_SHARE "http://www.google.com/session/share"

WOCKY_XMPP_NS_GOOGLE_TRANSPORT_P2P

#define WOCKY_XMPP_NS_GOOGLE_TRANSPORT_P2P "http://www.google.com/transport/p2p"

WOCKY_XMPP_NS_JINGLE_TRANSPORT_RAWUDP

#define WOCKY_XMPP_NS_JINGLE_TRANSPORT_RAWUDP "urn:xmpp:jingle:transports:raw-udp:1"

WOCKY_XMPP_NS_JINGLE_TRANSPORT_ICEUDP

#define WOCKY_XMPP_NS_JINGLE_TRANSPORT_ICEUDP "urn:xmpp:jingle:transports:ice-udp:1"

WOCKY_QUIRK_OMITS_CONTENT_CREATORS

#define WOCKY_QUIRK_OMITS_CONTENT_CREATORS "\x07omits-content-creators"

WOCKY_QUIRK_GOOGLE_WEBMAIL_CLIENT

#define WOCKY_QUIRK_GOOGLE_WEBMAIL_CLIENT "\x07google-webmail-client"

WOCKY_QUIRK_ANDROID_GTALK_CLIENT

#define WOCKY_QUIRK_ANDROID_GTALK_CLIENT "\x07android-gtalk-client"

WOCKY_XMPP_NS_GOOGLE_JINGLE_INFO

#define WOCKY_XMPP_NS_GOOGLE_JINGLE_INFO   "google:jingleinfo"

WOCKY_JABBER_NS_AUTH

#define             WOCKY_JABBER_NS_AUTH

WOCKY_JABBER_NS_AUTH_FEATURE

#define             WOCKY_JABBER_NS_AUTH_FEATURE

WOCKY_GOOGLE_NS_AUTH

#define             WOCKY_GOOGLE_NS_AUTH

WOCKY_XMPP_NS_ROSTER

#define             WOCKY_XMPP_NS_ROSTER

WOCKY_XMPP_NS_PUBSUB

#define             WOCKY_XMPP_NS_PUBSUB

WOCKY_XMPP_NS_PUBSUB_EVENT

#define             WOCKY_XMPP_NS_PUBSUB_EVENT

WOCKY_XMPP_NS_PUBSUB_OWNER

#define             WOCKY_XMPP_NS_PUBSUB_OWNER

WOCKY_XMPP_NS_PUBSUB_NODE_CONFIG

#define             WOCKY_XMPP_NS_PUBSUB_NODE_CONFIG

WOCKY_XMPP_NS_PUBSUB_ERRORS

#define             WOCKY_XMPP_NS_PUBSUB_ERRORS

WOCKY_XMPP_NS_PING

#define             WOCKY_XMPP_NS_PING

WOCKY_NS_MUC

#define             WOCKY_NS_MUC

WOCKY_NS_MUC_USER

#define             WOCKY_NS_MUC_USER

WOCKY_NS_MUC_ADMIN

#define             WOCKY_NS_MUC_ADMIN

WOCKY_NS_MUC_OWNER

#define             WOCKY_NS_MUC_OWNER

WOCKY_NS_MUC_UNIQUE

#define             WOCKY_NS_MUC_UNIQUE

WOCKY_NS_CHATSTATE

#define             WOCKY_NS_CHATSTATE

WOCKY_NS_GOOGLE_SESSION_PHONE

#define             WOCKY_NS_GOOGLE_SESSION_PHONE

WOCKY_NS_GOOGLE_SESSION_VIDEO

#define             WOCKY_NS_GOOGLE_SESSION_VIDEO

WOCKY_NS_VCARD_TEMP

#define WOCKY_NS_VCARD_TEMP           "vcard-temp"

WOCKY_NS_VCARD_TEMP_UPDATE

#define WOCKY_NS_VCARD_TEMP_UPDATE    "vcard-temp:x:update"
telepathy-gabble-0.18.2/lib/ext/wocky/docs/reference/html/wocky-WockyNode.html0000644000175000017500000036146412312537050027307 0ustar00smcvsmcv00000000000000 Wocky Reference Manual: WockyNode

WockyNode

WockyNode — representation of a XMPP node

Functions

gboolean (*wocky_node_each_attr_func) ()
gboolean (*wocky_node_each_child_func) ()
void wocky_node_each_attribute ()
void wocky_node_each_child ()
const gchar * wocky_node_get_attribute ()
const gchar * wocky_node_get_attribute_ns ()
void wocky_node_set_attribute ()
void wocky_node_set_attributes ()
void wocky_node_set_attribute_ns ()
void wocky_node_set_attribute_n ()
void wocky_node_set_attribute_n_ns ()
const gchar * wocky_node_attribute_ns_get_prefix_from_urn ()
const gchar * wocky_node_attribute_ns_get_prefix_from_quark ()
void wocky_node_attribute_ns_set_prefix ()
WockyNode * wocky_node_get_child ()
WockyNode * wocky_node_get_child_ns ()
WockyNode * wocky_node_get_first_child ()
WockyNode * wocky_node_get_first_child_ns ()
const gchar * wocky_node_get_content_from_child ()
const gchar * wocky_node_get_content_from_child_ns ()
WockyNode * wocky_node_add_child ()
WockyNode * wocky_node_add_child_ns ()
WockyNode * wocky_node_add_child_ns_q ()
WockyNode * wocky_node_add_child_with_content ()
WockyNode * wocky_node_add_child_with_content_ns ()
WockyNode * wocky_node_add_child_with_content_ns_q ()
const gchar * wocky_node_get_ns ()
gboolean wocky_node_has_ns ()
gboolean wocky_node_has_ns_q ()
gboolean wocky_node_matches_q ()
gboolean wocky_node_matches ()
const gchar * wocky_node_get_language ()
void wocky_node_set_language ()
void wocky_node_set_language_n ()
void wocky_node_set_content ()
void wocky_node_append_content ()
void wocky_node_append_content_n ()
gchar * wocky_node_to_string ()
WockyNode * wocky_node_new ()
void wocky_node_free ()
gboolean wocky_node_equal ()
gboolean wocky_node_is_superset ()
void wocky_node_iter_init ()
gboolean wocky_node_iter_next ()
void wocky_node_iter_remove ()
void wocky_node_add_build ()
void wocky_node_add_build_va ()
WockyNode * wocky_node_add_node_tree ()
WockyNode * wocky_node_prepend_node_tree ()
void wocky_node_init ()
void wocky_node_deinit ()

Types and Values

Object Hierarchy


Includes

#include <wocky/wocky-node.h>

Description

Low level representation of a XMPP node. Provides ways to set various parameters on the node, such as content, language, namespaces and prefixes. It also offers methods to lookup children of a node.

Functions

wocky_node_each_attr_func ()

gboolean
(*wocky_node_each_attr_func) (const gchar *key,
                              const gchar *value,
                              const gchar *pref,
                              const gchar *ns,
                              gpointer user_data);

Specifies the type of functions passed to wocky_node_each_attribute().

Parameters

key

the attribute's key

 

value

the attribute's value

 

pref

the attribute's prefix

 

ns

the attribute's namespace

 

user_data

user data passed to wocky_node_each_attribute()

 

Returns

FALSE to stop further attributes from being examined.


wocky_node_each_child_func ()

gboolean
(*wocky_node_each_child_func) (WockyNode *node,
                               gpointer user_data);

Specifies the type of functions passed to wocky_node_each_child().

Parameters

node

a WockyNode

 

user_data

user data passed to wocky_node_each_child()

 

Returns

FALSE to stop further children from being examined.


wocky_node_each_attribute ()

void
wocky_node_each_attribute (WockyNode *node,
                           wocky_node_each_attr_func func,
                           gpointer user_data);

Calls a function for each attribute of a WockyNode.

Parameters

node

a WockyNode

 

func

the function to be called on each node's attribute

 

user_data

user data to pass to the function

 

wocky_node_each_child ()

void
wocky_node_each_child (WockyNode *node,
                       wocky_node_each_child_func func,
                       gpointer user_data);

Calls a function for each child of a WockyNode.

Parameters

node

a WockyNode

 

func

the function to be called on each node's child

 

user_data

user data to pass to the function

 

wocky_node_get_attribute ()

const gchar *
wocky_node_get_attribute (WockyNode *node,
                          const gchar *key);

Returns the value of an attribute in a WockyNode.

Parameters

node

a WockyNode

 

key

the attribute name

 

Returns

the value of the attribute key , or NULL if node doesn't have such attribute.


wocky_node_get_attribute_ns ()

const gchar *
wocky_node_get_attribute_ns (WockyNode *node,
                             const gchar *key,
                             const gchar *ns);

Returns the value of an attribute in a WockyNode, limiting the search within a specific namespace. If the namespace is NULL, this is equivalent to wocky_node_get_attribute().

Parameters

node

a WockyNode

 

key

the attribute name

 

ns

the namespace to search within, or NULL

 

Returns

the value of the attribute key , or NULL if node doesn't have such attribute in ns .


wocky_node_set_attribute ()

void
wocky_node_set_attribute (WockyNode *node,
                          const gchar *key,
                          const gchar *value);

Sets an attribute in a WockyNode to a specific value.

Parameters

node

a WockyNode

 

key

the attribute name to set

 

value

the value to set

 

wocky_node_set_attributes ()

void
wocky_node_set_attributes (WockyNode *node,
                           const gchar *key,
                           ...);

Sets attributes in a WockyNode to specific values.

Parameters

node

a WockyNode

 

key

the attribute name to set

 

...

pairs of keys and values, terminated by NULL

 

wocky_node_set_attribute_ns ()

void
wocky_node_set_attribute_ns (WockyNode *node,
                             const gchar *key,
                             const gchar *value,
                             const gchar *ns);

Sets an attribute in a WockyNode, within a specific namespace. If the namespace is NULL, this is equivalent to wocky_node_set_attribute().

Parameters

node

a WockyNode

 

key

the attribute name to set

 

value

the value to set

 

ns

a namespace, or NULL

 

wocky_node_set_attribute_n ()

void
wocky_node_set_attribute_n (WockyNode *node,
                            const gchar *key,
                            const gchar *value,
                            gsize value_size);

Sets a new attribute to a WockyNode, with the supplied values.

Parameters

node

a WockyNode

 

key

the attribute to set

 

value

the value to set

 

value_size

the number of bytes of value to set as a value

 

wocky_node_set_attribute_n_ns ()

void
wocky_node_set_attribute_n_ns (WockyNode *node,
                               const gchar *key,
                               const gchar *value,
                               gsize value_size,
                               const gchar *ns);

Sets a new attribute to a WockyNode, with the supplied values. If the namespace is NULL, this is equivalent to wocky_node_set_attribute_n().

Parameters

node

a WockyNode

 

key

the attribute to set

 

value

the value to set

 

value_size

the number of bytes of value to set as a value

 

ns

a namespace, or NULL

 

wocky_node_attribute_ns_get_prefix_from_urn ()

const gchar *
wocky_node_attribute_ns_get_prefix_from_urn
                               (const gchar *urn);

Gets the prefix of the namespace identified by the URN.

Parameters

urn

a string containing an URN

 

Returns

a string containing the prefix of the namespace urn .


wocky_node_attribute_ns_get_prefix_from_quark ()

const gchar *
wocky_node_attribute_ns_get_prefix_from_quark
                               (GQuark ns);

Gets the prefix of the namespace identified by the quark.

Parameters

ns

a quark corresponding to an XML namespace URN

 

Returns

a string containing the prefix of the namespace ns .


wocky_node_attribute_ns_set_prefix ()

void
wocky_node_attribute_ns_set_prefix (GQuark ns,
                                    const gchar *prefix);

Sets a desired prefix for a namespace.

Parameters

ns

a GQuark

 

prefix

a string containing the desired prefix

 

wocky_node_get_child ()

WockyNode *
wocky_node_get_child (WockyNode *node,
                      const gchar *name);

Gets a child of a node, searching by name.

Parameters

node

a WockyNode

 

name

the name of the child to get

 

Returns

a WockyNode.


wocky_node_get_child_ns ()

WockyNode *
wocky_node_get_child_ns (WockyNode *node,
                         const gchar *name,
                         const gchar *ns);

Gets the child of a node, searching by name and limiting the search to the specified namespace. If the namespace is NULL, this is equivalent to wocky_node_get_child()

Parameters

node

a WockyNode

 

name

the name of the child to get

 

ns

the namespace of the child to get, or NULL

 

Returns

a WockyNode.


wocky_node_get_first_child ()

WockyNode *
wocky_node_get_first_child (WockyNode *node);

Convenience function to return the first child of a WockyNode.

Parameters

node

a WockyNode

 

Returns

a WockyNode, or NULL if node has no children.


wocky_node_get_first_child_ns ()

WockyNode *
wocky_node_get_first_child_ns (WockyNode *node,
                               const gchar *ns);

Returns the first child of node whose namespace is ns , saving you the bother of faffing around with a WockyNodeIter.

Parameters

node

a WockyNode

 

ns

the namespace of the child node you seek.

 

Returns

the first child of node whose namespace is ns , or NULL if none is found.


wocky_node_get_content_from_child ()

const gchar *
wocky_node_get_content_from_child (WockyNode *node,
                                   const gchar *name);

Retrieves the content from a child of a node, if it exists.

Parameters

node

a WockyNode

 

name

the name of the child whose content to retrieve

 

Returns

the content of the child of node named name , or NULL if node has no such child.


wocky_node_get_content_from_child_ns ()

const gchar *
wocky_node_get_content_from_child_ns (WockyNode *node,
                                      const gchar *name,
                                      const gchar *ns);

Retrieves the content from a child of a node, if it exists.

Parameters

node

a WockyNode

 

name

the name of the child whose content to retrieve

 

ns

the namespace of the child whose content to retrieve

 

Returns

the content of the child of node named name in ns , or NULL if node has no such child.


wocky_node_add_child ()

WockyNode *
wocky_node_add_child (WockyNode *node,
                      const gchar *name);

Adds a WockyNode with the specified name to an already existing node.

Parameters

node

a WockyNode

 

name

the name of the child to add

 

Returns

the newly added WockyNode.


wocky_node_add_child_ns ()

WockyNode *
wocky_node_add_child_ns (WockyNode *node,
                         const gchar *name,
                         const gchar *ns);

Adds a WockyNode with the specified name to an already existing node, under the specified namespace. If the namespace is NULL, this is equivalent to wocky_node_add_child().

Parameters

node

a WockyNode

 

name

the name of the child to add

 

ns

a namespace

 

Returns

the newly added WockyNode.


wocky_node_add_child_ns_q ()

WockyNode *
wocky_node_add_child_ns_q (WockyNode *node,
                           const gchar *name,
                           GQuark ns);

Adds a WockyNode with the specified name to an already existing node, under the specified namespace. If the namespace is 0, this is equivalent to wocky_node_add_child().

Parameters

node

a WockyNode

 

name

the name of the child to add

 

ns

a namespace

 

Returns

the newly added WockyNode.


wocky_node_add_child_with_content ()

WockyNode *
wocky_node_add_child_with_content (WockyNode *node,
                                   const gchar *name,
                                   const char *content);

Adds a WockyNode with the specified name and containing the specified content to an already existing node.

Parameters

node

a WockyNode

 

name

the name of the child to add

 

content

the content of the child to add

 

Returns

the newly added WockyNode.


wocky_node_add_child_with_content_ns ()

WockyNode *
wocky_node_add_child_with_content_ns (WockyNode *node,
                                      const gchar *name,
                                      const gchar *content,
                                      const gchar *ns);

Adds a WockyNode with the specified name and the specified content to an already existing node, under the specified namespace. If the namespace is NULL, this is equivalent to wocky_node_add_child_with_content().

Parameters

node

a WockyNode

 

name

the name of the child to add

 

content

the content of the child to add

 

ns

a namespace

 

Returns

the newly added WockyNode.


wocky_node_add_child_with_content_ns_q ()

WockyNode *
wocky_node_add_child_with_content_ns_q
                               (WockyNode *node,
                                const gchar *name,
                                const gchar *content,
                                GQuark ns);

Adds a WockyNode with the specified name and the specified content to an already existing node, under the specified namespace. If the namespace is 0, this is equivalent to wocky_node_add_child_with_content().

Parameters

node

a WockyNode

 

name

the name of the child to add

 

content

the content of the child to add

 

ns

a namespace

 

Returns

the newly added WockyNode.


wocky_node_get_ns ()

const gchar *
wocky_node_get_ns (WockyNode *node);

Gets the namespace of a WockyNode

Parameters

node

a WockyNode

 

Returns

a string containing the namespace of the node.


wocky_node_has_ns ()

gboolean
wocky_node_has_ns (WockyNode *node,
                   const gchar *ns);

wocky_node_has_ns_q ()

gboolean
wocky_node_has_ns_q (WockyNode *node,
                     GQuark ns);

wocky_node_matches_q ()

gboolean
wocky_node_matches_q (WockyNode *node,
                      const gchar *name,
                      GQuark ns);

Checks whether a node has a particular name and namespace.

Parameters

node

a WockyNode

 

name

the expected element name, which may not be NULL

 

ns

the expected element namespace, which may not be 0

 

Returns

TRUE if node is named name , in namespace ns .


wocky_node_matches ()

gboolean
wocky_node_matches (WockyNode *node,
                    const gchar *name,
                    const gchar *ns);

Checks whether a node has a particular name and namespace.

Parameters

node

a WockyNode

 

name

the expected element name, which may not be NULL

 

ns

the expected element namespace, which may not be NULL

 

Returns

TRUE if node is named name , in namespace ns .


wocky_node_get_language ()

const gchar *
wocky_node_get_language (WockyNode *node);

Gets the language of a WockyNode

Parameters

node

a WockyNode

 

Returns

a string containing the language of the node.


wocky_node_set_language ()

void
wocky_node_set_language (WockyNode *node,
                         const gchar *lang);

Sets the language of a WockyNode.

Parameters

node

a WockyNode

 

lang

a NULL-terminated string containing the language

 

wocky_node_set_language_n ()

void
wocky_node_set_language_n (WockyNode *node,
                           const gchar *lang,
                           gsize lang_size);

Sets the language of a WockyNode.

Parameters

node

a WockyNode

 

lang

a language

 

lang_size

the length of lang , in bytes.

 

wocky_node_set_content ()

void
wocky_node_set_content (WockyNode *node,
                        const gchar *content);

Sets the content of a WockyNode.

Parameters

node

a WockyNode

 

content

the content to set to the node

 

wocky_node_append_content ()

void
wocky_node_append_content (WockyNode *node,
                           const gchar *content);

Appends some content to the content of a WockyNode.

Parameters

node

a WockyNode

 

content

the content to append to the node

 

wocky_node_append_content_n ()

void
wocky_node_append_content_n (WockyNode *node,
                             const gchar *content,
                             gsize size);

Appends a specified number of content bytes to the content of a WockyNode.

Parameters

node

a WockyNode

 

content

the content to append to the node

 

size

the size of the content to append

 

wocky_node_to_string ()

gchar *
wocky_node_to_string (WockyNode *node);

Obtains a string representation of a WockyNode.

Parameters

node

a WockyNode

 

Returns

a newly allocated string containing a serialization of the node.


wocky_node_new ()

WockyNode *
wocky_node_new (const char *name,
                const gchar *ns);

Convenience function which creates a WockyNode and sets its name to name .

Parameters

name

the node's name (may not be NULL)

 

ns

the nodes namespace (may not be NULL)

 

Returns

a newly allocated WockyNode.


wocky_node_free ()

void
wocky_node_free (WockyNode *node);

Convenience function that frees the passed in WockyNode and all of its children.

Parameters

node

a WockyNode.

 

wocky_node_equal ()

gboolean
wocky_node_equal (WockyNode *node0,
                  WockyNode *node1);

Compares two WockyNodes for equality.

Parameters

node0

a WockyNode

 

node1

a WockyNode to compare to node0

 

Returns

TRUE if the two nodes are equal.


wocky_node_is_superset ()

gboolean
wocky_node_is_superset (WockyNode *node,
                        WockyNode *subset);

Parameters

node

the WockyNode to test

 

subset

the supposed subset

 

Returns

TRUE if node is a superset of subset .


wocky_node_iter_init ()

void
wocky_node_iter_init (WockyNodeIter *iter,
                      WockyNode *node,
                      const gchar *name,
                      const gchar *ns);

Initializes an iterator that can be used to iterate over the children of node , filtered by name and ns

1
2
3
4
5
6
7
8
9
10
WockyNodeIter iter;
WockyNode *child;

wocky_node_iter_init (&iter, wocky_stanza_get_top_node (stanza),
   "payload-type",
   WOCKY_XMPP_NS_JINGLE_RTP);
while (wocky_node_iter_next (iter, &child))
  {
    /<!-- -->* do something with the child *<!-- -->/
  }

Parameters

iter

unitialized iterator

 

node

Node whose children to iterate over

 

name

Name to filter on or NULL

 

ns

namespace to filter on or NULL

 

wocky_node_iter_next ()

gboolean
wocky_node_iter_next (WockyNodeIter *iter,
                      WockyNode **next);

Advances iter to the next child that matches its filter. if FALSE is returned next is not set and the iterator becomes invalid

Parameters

iter

an initialized WockyNodeIter

 

next

a location to store the next child

 

Returns

FALSE if the last child has been reached


wocky_node_iter_remove ()

void
wocky_node_iter_remove (WockyNodeIter *iter);

Removes and frees the node returned by the last call to wocky_node_iter_next() from its parent. Can only be called after wocky_node_iter_next() returned TRUE, and cannot be called more than once per successful call to wocky_node_iter_next().

Parameters

iter

an initialized WockyNodeIter

 

wocky_node_add_build ()

void
wocky_node_add_build (WockyNode *node,
                      ...);

Add a node subtree to an existing parent node.

Example 1. 

1
2
3
4
5
wocky_node_add_build (node,
   '(', "body",
       '$', "Telepathy rocks!",
   ')',
  NULL);

The above examples adds the following subtree under the given node:

<body>
   Telepathy rocks!
</body>

Parameters

node

The node under which to add a new subtree

 

...

the description of the stanza to build, terminated with NULL

 

wocky_node_add_build_va ()

void
wocky_node_add_build_va (WockyNode *node,
                         va_list va);

wocky_node_add_node_tree ()

WockyNode *
wocky_node_add_node_tree (WockyNode *node,
                          WockyNodeTree *tree);

Copies the nodes from tree , and appends them to node 's children.

Parameters

node

A node

 

tree

The node tree to add

 

Returns

the root of the copy of tree added to node .


wocky_node_prepend_node_tree ()

WockyNode *
wocky_node_prepend_node_tree (WockyNode *node,
                              WockyNodeTree *tree);

Copies the nodes from tree , and inserts them as the first child of node , before any existing children.

Parameters

node

a node

 

tree

the node tree to prepend to node 's children

 

Returns

the root of the copy of tree added to node .


wocky_node_init ()

void
wocky_node_init (void);

Initializes the caches used by WockyNode. This should be always called before using WockyNode structs.


wocky_node_deinit ()

void
wocky_node_deinit (void);

Releases all the resources used by the WockyNode caches.

Types and Values

enum WockyNodeBuildTag

Tags for building a stanza using wocky_stanza_build() or wocky_node_add_build().

Members

WOCKY_NODE_START

Start of a node

 

WOCKY_NODE_TEXT

Text content of a node

 

WOCKY_NODE_END

End of a node

 

WOCKY_NODE_ATTRIBUTE

A node attribute

 

WOCKY_NODE_XMLNS

A node XML namespace

 

WOCKY_NODE_ASSIGN_TO

a WockyNode to assign

 

WOCKY_NODE_LANGUAGE

xml:lang of a node

 

struct WockyNode

struct WockyNode {
  gchar *name;
  gchar *content;
};

A single WockyNode structure that relates to an element in an XMPP stanza.

Members

gchar *name;

name of the node

 

gchar *content;

content of the node

 

WockyNodeIter

typedef struct {
} WockyNodeIter;

Iterate over a node's children. See wocky_node_iter_init() for more details.

telepathy-gabble-0.18.2/lib/ext/wocky/docs/reference/html/wocky-Wocky-OpenSSL-TLS.html0000644000175000017500000007063112312537050030413 0ustar00smcvsmcv00000000000000 Wocky Reference Manual: Wocky OpenSSL TLS

Wocky OpenSSL TLS

Wocky OpenSSL TLS — Establish TLS sessions

Properties

WockyTLSSession * session Write / Construct Only
GIOStream * base-stream Write / Construct Only
guint dh-bits Write / Construct Only
gboolean server Write / Construct Only
gchar * x509-cert Write / Construct Only
gchar * x509-key Write / Construct Only

Object Hierarchy

    GEnum
    ├── WockyTLSCertStatus
    ├── WockyTLSCertType
    ╰── WockyTLSVerificationLevel
    GObject
    ├── GIOStream
       ╰── WockyTLSConnection
    ╰── WockyTLSSession

Description

The WOCKY_TLS_DEBUG_LEVEL environment variable can be used to print debug output from OpenSSL. To enable it, set it to a value from 1 to 9. Higher values will print more information.

Increasing the value past certain thresholds will also trigger increased debugging output from within wocky-openssl.c as well.

Functions

wocky_tls_cert_error_quark ()

GQuark
wocky_tls_cert_error_quark (void);

WOCKY_TLS_CERT_ERROR

#define WOCKY_TLS_CERT_ERROR (wocky_tls_cert_error_quark ())

wocky_tls_error_quark ()

GQuark
wocky_tls_error_quark (void);

WOCKY_TLS_ERROR

#define WOCKY_TLS_ERROR (wocky_tls_error_quark ())

wocky_tls_session_verify_peer ()

int
wocky_tls_session_verify_peer (WockyTLSSession *session,
                               const gchar *peername,
                               GStrv extra_identities,
                               WockyTLSVerificationLevel level,
                               WockyTLSCertStatus *status);

wocky_tls_session_get_peers_certificate ()

GPtrArray *
wocky_tls_session_get_peers_certificate
                               (WockyTLSSession *session,
                                WockyTLSCertType *type);

wocky_tls_session_handshake ()

WockyTLSConnection *
wocky_tls_session_handshake (WockyTLSSession *session,
                             GCancellable *cancellable,
                             GError **error);

wocky_tls_session_handshake_async ()

void
wocky_tls_session_handshake_async (WockyTLSSession *session,
                                   gint io_priority,
                                   GCancellable *cancellable,
                                   GAsyncReadyCallback callback,
                                   gpointer user_data);

wocky_tls_session_handshake_finish ()

WockyTLSConnection *
wocky_tls_session_handshake_finish (WockyTLSSession *session,
                                    GAsyncResult *result,
                                    GError **error);

wocky_tls_session_add_ca ()

void
wocky_tls_session_add_ca (WockyTLSSession *session,
                          const gchar *path);

wocky_tls_session_add_crl ()

void
wocky_tls_session_add_crl (WockyTLSSession *session,
                           const gchar *path);

wocky_tls_session_new ()

WockyTLSSession *
wocky_tls_session_new (GIOStream *stream);

wocky_tls_session_server_new ()

WockyTLSSession *
wocky_tls_session_server_new (GIOStream *stream,
                              guint dhbits,
                              const gchar *key,
                              const gchar *cert);

Create a new TLS server session

Parameters

stream

a GIOStream on which we expect to receive the client TLS handshake

 

dhbits

size of the DH parameters

 

key

the path to the X509 PEM key file

 

cert

the path to the X509 PEM certificate

 

Returns

a WockyTLSSession object

Types and Values

enum WockyTLSVerificationLevel

Members

WOCKY_TLS_VERIFY_STRICT

   

WOCKY_TLS_VERIFY_NORMAL

   

WOCKY_TLS_VERIFY_LENIENT

   

enum WockyTLSCertStatus

Members

WOCKY_TLS_CERT_OK

   

WOCKY_TLS_CERT_INVALID

   

WOCKY_TLS_CERT_NAME_MISMATCH

   

WOCKY_TLS_CERT_REVOKED

   

WOCKY_TLS_CERT_SIGNER_UNKNOWN

   

WOCKY_TLS_CERT_SIGNER_UNAUTHORISED

   

WOCKY_TLS_CERT_INSECURE

   

WOCKY_TLS_CERT_NOT_ACTIVE

   

WOCKY_TLS_CERT_EXPIRED

   

WOCKY_TLS_CERT_NO_CERTIFICATE

   

WOCKY_TLS_CERT_MAYBE_DOS

   

WOCKY_TLS_CERT_INTERNAL_ERROR

   

WOCKY_TLS_CERT_UNKNOWN_ERROR

   

enum WockyTLSCertType

Members

WOCKY_TLS_CERT_TYPE_NONE

   

WOCKY_TLS_CERT_TYPE_X509

   

WOCKY_TLS_CERT_TYPE_OPENPGP

   

Property Details

The “session” property

  “session”                  WockyTLSSession *

the TLS session object for this connection.

Flags: Write / Construct Only


The “base-stream” property

  “base-stream”              GIOStream *

the stream that TLS communicates over.

Flags: Write / Construct Only


The “dh-bits” property

  “dh-bits”                  guint

Diffie-Hellmann bits: 768, 1024, 2048, 3072 0r 4096.

Flags: Write / Construct Only

Allowed values: [768,4096]

Default value: 1024


The “server” property

  “server”                   gboolean

whether this is a server.

Flags: Write / Construct Only

Default value: FALSE


The “x509-cert” property

  “x509-cert”                gchar *

x509 PEM certificate file.

Flags: Write / Construct Only

Default value: NULL


The “x509-key” property

  “x509-key”                 gchar *

x509 PEM key file.

Flags: Write / Construct Only

Default value: NULL

telepathy-gabble-0.18.2/lib/ext/wocky/docs/reference/html/wocky-wocky-pubsub-helpers.html0000644000175000017500000010670612312537050031473 0ustar00smcvsmcv00000000000000 Wocky Reference Manual: wocky-pubsub-helpers

wocky-pubsub-helpers

wocky-pubsub-helpers

Object Hierarchy


Description

Functions

wocky_pubsub_make_event_stanza ()

WockyStanza *
wocky_pubsub_make_event_stanza (const gchar *node,
                                const gchar *from,
                                WockyNode **item_out);

Generates a new message stanza to send to other contacts about an updated PEP node.

Note that this should only be used in link-local connections. Regular pubsub consists of making a publish stanza with wocky_pubsub_make_publish_stanza() and sending it to your own server. The server will then send the event stanza on to your contacts who have the appropriate capability.

Parameters

node

the the name of the pubsub node; may not be NULL

 

from

a JID to use as the 'from' attribute, or NULL

 

item_out

a location to store the item WockyNode, or NULL

 

Returns

a new WockyStanza pubsub event stanza; free with g_object_unref()


wocky_pubsub_make_stanza ()

WockyStanza *
wocky_pubsub_make_stanza (const gchar *service,
                          WockyStanzaSubType sub_type,
                          const gchar *pubsub_ns,
                          const gchar *action_name,
                          WockyNode **pubsub_node,
                          WockyNode **action_node);

Parameters

service

the JID of a PubSub service, or NULL

 

sub_type

WOCKY_STANZA_SUB_TYPE_SET or WOCKY_STANZA_SUB_TYPE_GET, as you wish

 

pubsub_ns

the namespace for the <pubsub/> node of the stanza

 

action_name

the action node to add to <pubsub/>

 

pubsub_node

address at which to store a pointer to the <pubsub/> node

 

action_node

address at wihch to store a pointer to the <action /> node

 

Returns

a new iq[type=sub_type ]/pubsub/action stanza


wocky_pubsub_make_publish_stanza ()

WockyStanza *
wocky_pubsub_make_publish_stanza (const gchar *service,
                                  const gchar *node,
                                  WockyNode **pubsub_out,
                                  WockyNode **publish_out,
                                  WockyNode **item_out);

Parameters

service

the JID of a PubSub service, or NULL

 

node

the name of a node on service ; may not be NULL

 

pubsub_out

address at which to store a pointer to the <pubsub/> node

 

publish_out

address at which to store a pointer to the <publish/> node

 

item_out

address at which to store a pointer to the <item/> node

 

Returns

a new iq[type='set']/pubsub/publish/item stanza


wocky_send_ll_pep_event ()

void
wocky_send_ll_pep_event (WockySession *session,
                         WockyStanza *stanza);

Send a PEP event to all link-local contacts interested in receiving it.

Parameters

session

the WockySession to send on

 

stanza

the PEP event stanza to send

 

wocky_pubsub_distill_iq_reply ()

gboolean
wocky_pubsub_distill_iq_reply (GObject *source,
                               GAsyncResult *res,
                               const gchar *pubsub_ns,
                               const gchar *child_name,
                               WockyNodeTree **child_out,
                               GError **error);

Helper function to finish a wocky_porter_send_iq_async() operation and extract a particular pubsub child from the resulting reply, if needed.

Parameters

source

a WockyPorter instance

 

res

a result passed to the callback for wocky_porter_send_iq_async()

 

pubsub_ns

the namespace of the <pubsub/> node expected in this reply (such as WOCKY_XMPP_NS_PUBSUB), or NULL if one is not expected

 

child_name

the name of the child of <pubsub/> expected in this reply (such as "subscriptions"); ignored if pubsub_ns is NULL

 

child_out

location at which to store a reference to the node tree at child_name , or NULL if you don't need it.

 

error

location at which to store an error if the call to wocky_porter_send_iq_async() returned an error, or if the reply was an error

 

Returns

TRUE if the desired pubsub child was found; FALSE if sending the IQ failed, the reply had type='error', or the pubsub child was not found, with error set appropriately.


wocky_pubsub_distill_ambivalent_iq_reply ()

gboolean
wocky_pubsub_distill_ambivalent_iq_reply
                               (GObject *source,
                                GAsyncResult *res,
                                const gchar *pubsub_ns,
                                const gchar *child_name,
                                WockyNodeTree **child_out,
                                GError **error);

Helper function to finish a wocky_porter_send_iq_async() operation and extract a particular pubsub child from the resulting reply, if it is present. This is like wocky_pubsub_distill_iq_reply(), but is ambivalent as to whether the <pubsub/> structure has to be included.

Parameters

source

a WockyPorter instance

 

res

a result passed to the callback for wocky_porter_send_iq_async()

 

pubsub_ns

the namespace of the <pubsub/> node accepted in this reply (such as WOCKY_XMPP_NS_PUBSUB)

 

child_name

the name of the child of <pubsub/> accepted in this reply (such as "subscriptions")

 

child_out

location at which to store a reference to the node tree at child_name , if it is found, or to be set to NULL if it is not found

 

error

location at which to store an error if the call to wocky_porter_send_iq_async() returned an error, or if the reply was an error

 

Returns

TRUE if the IQ was a success; FALSE if sending the IQ failed or the reply had type='error', with error set appropriately.


wocky_pubsub_distill_void_iq_reply ()

gboolean
wocky_pubsub_distill_void_iq_reply (GObject *source,
                                    GAsyncResult *res,
                                    GError **error);

Helper function to finish a wocky_porter_send_iq_async() operation where no pubsub child is expected in the resulting reply.

Parameters

source

a WockyPorter instance

 

res

a result passed to the callback for wocky_porter_send_iq_async()

 

error

location at which to store an error if the call to wocky_porter_send_iq_async() returned an error, or if the reply was an error

 

Returns

TRUE if the IQ was a success; FALSE if sending the IQ failed or the reply had type='error', with error set appropriately.


wocky_pubsub_distill_stanza ()

gboolean
wocky_pubsub_distill_stanza (WockyStanza *result,
                             const gchar *pubsub_ns,
                             const gchar *child_name,
                             gboolean body_optional,
                             WockyNodeTree **child_out,
                             GError **error);

Helper function to extract a particular pubsub child node from a reply, if it is present. If body_optional is FALSE, the <pubsub><child_name /></pubsub> tree being absent is not considered an error: child_out is set to NULL and the function returns TRUE.

If you are happy to delegate calling wocky_porter_send_iq_finish() and extracting stanza errors, you would probably be better served by one of wocky_pubsub_distill_iq_reply() or wocky_pubsub_distill_ambivalent_iq_reply().

Parameters

result

an iq type='result'

 

pubsub_ns

the namespace of the <pubsub/> node expected in this reply (such as WOCKY_XMPP_NS_PUBSUB)

 

child_name

the name of the child of <pubsub/> expected in this reply (such as "subscriptions")

 

body_optional

If TRUE, the child being absent is not considered an error

 

child_out

location at which to store a reference to the node tree at child_name , if it is found, or to be set to NULL if it is not.

 

error

location at which to store an error if the child node is not found and body_optional is FALSE

 

Returns

TRUE if the child was found or was optional; FALSE with error set otherwise.

Types and Values

telepathy-gabble-0.18.2/lib/ext/wocky/docs/reference/html/wocky-wocky-pubsub-node-protected.html0000644000175000017500000004444612312537050032747 0ustar00smcvsmcv00000000000000 Wocky Reference Manual: wocky-pubsub-node-protected

wocky-pubsub-node-protected

wocky-pubsub-node-protected

Object Hierarchy


Description

Functions

wocky_pubsub_node_get_porter ()

WockyPorter *
wocky_pubsub_node_get_porter (WockyPubsubNode *self);

wocky_pubsub_node_make_subscribe_stanza ()

WockyStanza *
wocky_pubsub_node_make_subscribe_stanza
                               (WockyPubsubNode *self,
                                const gchar *jid,
                                WockyNode **pubsub_node,
                                WockyNode **subscribe_node);

wocky_pubsub_node_make_unsubscribe_stanza ()

WockyStanza *
wocky_pubsub_node_make_unsubscribe_stanza
                               (WockyPubsubNode *self,
                                const gchar *jid,
                                const gchar *subid,
                                WockyNode **pubsub_node,
                                WockyNode **unsubscribe_node);

wocky_pubsub_node_make_delete_stanza ()

WockyStanza *
wocky_pubsub_node_make_delete_stanza (WockyPubsubNode *self,
                                      WockyNode **pubsub_node,
                                      WockyNode **delete_node);

wocky_pubsub_node_make_list_subscribers_stanza ()

WockyStanza *
wocky_pubsub_node_make_list_subscribers_stanza
                               (WockyPubsubNode *self,
                                WockyNode **pubsub_node,
                                WockyNode **subscriptions_node);

wocky_pubsub_node_make_list_affiliates_stanza ()

WockyStanza *
wocky_pubsub_node_make_list_affiliates_stanza
                               (WockyPubsubNode *self,
                                WockyNode **pubsub_node,
                                WockyNode **affiliations_node);

wocky_pubsub_node_parse_affiliations ()

GList *
wocky_pubsub_node_parse_affiliations (WockyPubsubNode *self,
                                      WockyNode *affiliations_node);

wocky_pubsub_node_make_modify_affiliates_stanza ()

WockyStanza *
wocky_pubsub_node_make_modify_affiliates_stanza
                               (WockyPubsubNode *self,
                                GList *affiliates,
                                WockyNode **pubsub_node,
                                WockyNode **affiliations_node);

Parameters

self

a pubsub node

 

affiliates

a list of WockyPubsubAffiliation structures, describing only the affiliations which should be changed.

 

pubsub_node

location at which to store a pointer to the <pubsub/> node, or NULL

 

affiliations_node

location at which to store a pointer to the <affiliations/> node, or NULL

 

Returns

an IQ stanza to modify the entities affiliated to a node that you own.


wocky_pubsub_node_make_get_configuration_stanza ()

WockyStanza *
wocky_pubsub_node_make_get_configuration_stanza
                               (WockyPubsubNode *self,
                                WockyNode **pubsub_node,
                                WockyNode **configure_node);

Parameters

self

a pubsub node

 

pubsub_node

location at which to store a pointer to the <pubsub/> node, or NULL

 

configure_node

location at which to store a pointer to the <configure/> node, or NULL

 

Returns

an IQ stanza to retrieve the configuration of self

Types and Values

telepathy-gabble-0.18.2/lib/ext/wocky/docs/reference/html/wocky-wocky-pubsub-service-protected.html0000644000175000017500000003375312312537050033461 0ustar00smcvsmcv00000000000000 Wocky Reference Manual: wocky-pubsub-service-protected

wocky-pubsub-service-protected

wocky-pubsub-service-protected

Object Hierarchy


Description

Functions

wocky_pubsub_service_create_retrieve_subscriptions_stanza ()

WockyStanza *
wocky_pubsub_service_create_retrieve_subscriptions_stanza
                               (WockyPubsubService *self,
                                WockyPubsubNode *node,
                                WockyNode **pubsub_node,
                                WockyNode **subscriptions_node);

wocky_pubsub_service_parse_subscription ()

WockyPubsubSubscription *
wocky_pubsub_service_parse_subscription
                               (WockyPubsubService *self,
                                WockyNode *subscription_node,
                                const gchar *parent_node_attr,
                                GError **error);

wocky_pubsub_service_parse_subscriptions ()

GList *
wocky_pubsub_service_parse_subscriptions
                               (WockyPubsubService *self,
                                WockyNode *subscriptions_node,
                                GList **subscription_nodes);

wocky_pubsub_service_create_create_node_stanza ()

WockyStanza *
wocky_pubsub_service_create_create_node_stanza
                               (WockyPubsubService *self,
                                const gchar *name,
                                WockyDataForm *config,
                                WockyNode **pubsub_node,
                                WockyNode **create_node);

wocky_pubsub_service_handle_create_node_reply ()

WockyPubsubNode *
wocky_pubsub_service_handle_create_node_reply
                               (WockyPubsubService *self,
                                WockyNodeTree *create_tree,
                                const gchar *requested_name,
                                GError **error);

Handles the body of a reply to a create node request. This is ever-so-slightly involved, because the server is allowed to omit the body of the reply if you specified a node name and it created a node with that name, but it may also tell you "hey, you asked for 'ringo', but I gave you 'george'". Good times.

Parameters

self

a pubsub service

 

create_tree

the <create/> tree from the reply to an attempt to create a node, or NULL if none was present in the reply.

 

requested_name

the name we asked the server to use for the node, or NULL if we requested an instant node

 

error

location at which to store an error

 

Returns

a pubsub node if the reply made sense, or NULL with error set if not.


wocky_pubsub_service_get_porter ()

WockyPorter *
wocky_pubsub_service_get_porter (WockyPubsubService *self);

Types and Values

telepathy-gabble-0.18.2/lib/ext/wocky/docs/reference/html/wocky-wocky-sasl-digest-md5.html0000644000175000017500000001011112312537050031415 0ustar00smcvsmcv00000000000000 Wocky Reference Manual: wocky-sasl-digest-md5

wocky-sasl-digest-md5

wocky-sasl-digest-md5

Functions

WockySaslDigestMd5 * wocky_sasl_digest_md5_new ()

Object Hierarchy


Description

Functions

wocky_sasl_digest_md5_new ()

WockySaslDigestMd5 *
wocky_sasl_digest_md5_new (const gchar *server,
                           const gchar *username,
                           const gchar *password);

Types and Values

telepathy-gabble-0.18.2/lib/ext/wocky/docs/reference/html/wocky-wocky-sasl-plain.html0000644000175000017500000000745112312537050030573 0ustar00smcvsmcv00000000000000 Wocky Reference Manual: wocky-sasl-plain

wocky-sasl-plain

wocky-sasl-plain

Functions

WockySaslPlain * wocky_sasl_plain_new ()

Object Hierarchy


Description

Functions

wocky_sasl_plain_new ()

WockySaslPlain *
wocky_sasl_plain_new (const gchar *username,
                      const gchar *password);

Types and Values

telepathy-gabble-0.18.2/lib/ext/wocky/docs/reference/html/wocky-wocky-sasl-scram.html0000644000175000017500000000772512312537050030601 0ustar00smcvsmcv00000000000000 Wocky Reference Manual: wocky-sasl-scram

wocky-sasl-scram

wocky-sasl-scram

Functions

WockySaslScram * wocky_sasl_scram_new ()

Object Hierarchy


Description

Functions

wocky_sasl_scram_new ()

WockySaslScram *
wocky_sasl_scram_new (const gchar *server,
                      const gchar *username,
                      const gchar *password);

Types and Values

telepathy-gabble-0.18.2/lib/ext/wocky/docs/reference/html/wocky-wocky-sasl-utils.html0000644000175000017500000001350012312537050030620 0ustar00smcvsmcv00000000000000 Wocky Reference Manual: wocky-sasl-utils

wocky-sasl-utils

wocky-sasl-utils

Types and Values

Object Hierarchy


Description

Functions

sasl_generate_base64_nonce ()

gchar *
sasl_generate_base64_nonce (void);

sasl_calculate_hmac_sha1 ()

GByteArray *
sasl_calculate_hmac_sha1 (guint8 *key,
                          gsize key_len,
                          guint8 *text,
                          gsize text_len);

Types and Values

WOCKY_SHA1_BLOCK_SIZE

#define WOCKY_SHA1_BLOCK_SIZE 64

WOCKY_SHA1_DIGEST_SIZE

#define WOCKY_SHA1_DIGEST_SIZE 20
telepathy-gabble-0.18.2/lib/ext/wocky/docs/reference/html/wocky-wocky-utils.html0000644000175000017500000020210212312537050027656 0ustar00smcvsmcv00000000000000 Wocky Reference Manual: wocky-utils

wocky-utils

wocky-utils

Object Hierarchy


Description

Functions

wocky_strdiff ()

gboolean
wocky_strdiff (const gchar *left,
               const gchar *right);

Return TRUE if the given strings are different. Unlike strcmp this function will handle null pointers, treating them as distinct from any string.

Parameters

left

The first string to compare (may be NULL)

 

right

The second string to compare (may be NULL)

 

Returns

FALSE if left and right are both NULL, or if neither is NULL and both have the same contents; TRUE otherwise


wocky_normalise_jid ()

gchar *
wocky_normalise_jid (const gchar *jid);

Parameters

jid

a JID

 

Returns

a normalised JID, using the same rules as wocky_decode_jid(), or NULL if the JID could not be sensibly decoded. This value should be freed when you are done with it.


wocky_decode_jid ()

gboolean
wocky_decode_jid (const gchar *jid,
                  gchar **node,
                  gchar **domain,
                  gchar **resource);

If jid is valid, returns TRUE and sets the caller's node , domain and resource pointers. node and resource will be set to NULL if the respective part is not present in jid . If jid is invalid, sets node , domain and resource to NULL and returns FALSE.

In theory, the returned parts will be normalised as specified in RFC 6122 (XMPP Address Format); in practice, Wocky does not fully implement the normalisation and validation algorithms. FIXME: Do nodeprep/resourceprep and length checking.

Parameters

jid

a JID

 

node

address to store the normalised localpart of the JID.

[allow-none]

domain

address to store the normalised domainpart of the JID.

[allow-none]

resource

address to store the resourcepart of the JID

 

Returns

TRUE if the JID is valid


wocky_compose_jid ()

gchar *
wocky_compose_jid (const gchar *node,
                   const gchar *domain,
                   const gchar *resource);

Composes a JID from its parts. If node is empty or NULL, the '@' separator is also omitted; if resource is empty or NULL, the '/' separator is also omitted. node and domain are assumed to have already been normalised.

Parameters

node

the node part of a JID, possibly empty or NULL.

[allow-none]

domain

the non-NULL domain part of a JID

 

resource

the resource part of a JID, possibly empty or NULL.

[allow-none]

Returns

a JID constructed from node , domain and resource


wocky_g_value_slice_new ()

GValue *
wocky_g_value_slice_new (GType type);

Slice-allocate an empty GValue. wocky_g_value_slice_new_boolean() and similar functions are likely to be more convenient to use for the types supported.

Parameters

type

The type desired for the new GValue

 

Returns

a newly allocated, newly initialized GValue, to be freed with wocky_g_value_slice_free() or g_slice_free().

Since 0.5.14


wocky_g_value_slice_new_boolean ()

GValue *
wocky_g_value_slice_new_boolean (gboolean b);

Slice-allocate and initialize a GValue. This function is convenient to use when constructing hash tables from string to GValue, for example.

Parameters

b

a boolean value

 

Returns

a GValue of type G_TYPE_BOOLEAN with value b , to be freed with wocky_g_value_slice_free() or g_slice_free()

Since 0.7.27


wocky_g_value_slice_new_int ()

GValue *
wocky_g_value_slice_new_int (gint n);

Slice-allocate and initialize a GValue. This function is convenient to use when constructing hash tables from string to GValue, for example.

Parameters

n

an integer

 

Returns

a GValue of type G_TYPE_INT with value n , to be freed with wocky_g_value_slice_free() or g_slice_free()

Since 0.7.27


wocky_g_value_slice_new_int64 ()

GValue *
wocky_g_value_slice_new_int64 (gint64 n);

Slice-allocate and initialize a GValue. This function is convenient to use when constructing hash tables from string to GValue, for example.

Parameters

n

a 64-bit integer

 

Returns

a GValue of type G_TYPE_INT64 with value n , to be freed with wocky_g_value_slice_free() or g_slice_free()

Since 0.7.27


wocky_g_value_slice_new_uint ()

GValue *
wocky_g_value_slice_new_uint (guint n);

Slice-allocate and initialize a GValue. This function is convenient to use when constructing hash tables from string to GValue, for example.

Parameters

n

an unsigned integer

 

Returns

a GValue of type G_TYPE_UINT with value n , to be freed with wocky_g_value_slice_free() or g_slice_free()

Since 0.7.27


wocky_g_value_slice_new_uint64 ()

GValue *
wocky_g_value_slice_new_uint64 (guint64 n);

Slice-allocate and initialize a GValue. This function is convenient to use when constructing hash tables from string to GValue, for example.

Parameters

n

a 64-bit unsigned integer

 

Returns

a GValue of type G_TYPE_UINT64 with value n , to be freed with wocky_g_value_slice_free() or g_slice_free()

Since 0.7.27


wocky_g_value_slice_new_double ()

GValue *
wocky_g_value_slice_new_double (double d);

Slice-allocate and initialize a GValue. This function is convenient to use when constructing hash tables from string to GValue, for example.

Parameters

d

a number

 

Returns

a GValue of type G_TYPE_DOUBLE with value n , to be freed with wocky_g_value_slice_free() or g_slice_free()

Since 0.7.27


wocky_g_value_slice_new_string ()

GValue *
wocky_g_value_slice_new_string (const gchar *string);

Slice-allocate and initialize a GValue. This function is convenient to use when constructing hash tables from string to GValue, for example.

Parameters

string

a string to be copied into the value

 

Returns

a GValue of type G_TYPE_STRING whose value is a copy of string , to be freed with wocky_g_value_slice_free() or g_slice_free()

Since 0.7.27


wocky_g_value_slice_new_static_string ()

GValue *
wocky_g_value_slice_new_static_string (const gchar *string);

Slice-allocate and initialize a GValue. This function is convenient to use when constructing hash tables from string to GValue, for example.

Parameters

string

a static string which must remain valid forever, to be pointed to by the value

 

Returns

a GValue of type G_TYPE_STRING whose value is string , to be freed with wocky_g_value_slice_free() or g_slice_free()

Since 0.7.27


wocky_g_value_slice_new_take_string ()

GValue *
wocky_g_value_slice_new_take_string (gchar *string);

Slice-allocate and initialize a GValue. This function is convenient to use when constructing hash tables from string to GValue, for example.

Parameters

string

a string which will be freed with g_free() by the returned GValue (the caller must own it before calling this function, but no longer owns it after this function returns)

 

Returns

a GValue of type G_TYPE_STRING whose value is string , to be freed with wocky_g_value_slice_free() or g_slice_free()

Since 0.7.27


wocky_g_value_slice_new_boxed ()

GValue *
wocky_g_value_slice_new_boxed (GType type,
                               gconstpointer p);

Slice-allocate and initialize a GValue. This function is convenient to use when constructing hash tables from string to GValue, for example.

Parameters

type

a boxed type

 

p

a pointer of type type , which will be copied

 

Returns

a GValue of type type whose value is a copy of p , to be freed with wocky_g_value_slice_free() or g_slice_free()

Since 0.7.27


wocky_g_value_slice_new_static_boxed ()

GValue *
wocky_g_value_slice_new_static_boxed (GType type,
                                      gconstpointer p);

Slice-allocate and initialize a GValue. This function is convenient to use when constructing hash tables from string to GValue, for example.

Parameters

type

a boxed type

 

p

a pointer of type type , which must remain valid forever

 

Returns

a GValue of type type whose value is p , to be freed with wocky_g_value_slice_free() or g_slice_free()

Since 0.7.27


wocky_g_value_slice_new_take_boxed ()

GValue *
wocky_g_value_slice_new_take_boxed (GType type,
                                    gpointer p);

Slice-allocate and initialize a GValue. This function is convenient to use when constructing hash tables from string to GValue, for example.

Parameters

type

a boxed type

 

p

a pointer of type type which will be freed with g_boxed_free() by the returned GValue (the caller must own it before calling this function, but no longer owns it after this function returns)

 

Returns

a GValue of type type whose value is p , to be freed with wocky_g_value_slice_free() or g_slice_free()

Since 0.7.27


wocky_g_value_slice_free ()

void
wocky_g_value_slice_free (GValue *value);

Unset and free a slice-allocated GValue.

(GDestroyNotify) wocky_g_value_slice_free can be used as a destructor for values in a GHashTable, for example.

Parameters

value

A GValue which was allocated with the g_slice API

 

wocky_g_value_slice_dup ()

GValue *
wocky_g_value_slice_dup (const GValue *value);

Parameters

value

A GValue

 

Returns

a newly allocated copy of value , to be freed with wocky_g_value_slice_free() or g_slice_free().

Since 0.5.14


wocky_enum_from_nick ()

gboolean
wocky_enum_from_nick (GType enum_type,
                      const gchar *nick,
                      gint *value);

Parameters

enum_type

the GType of a subtype of GEnum

 

nick

a non-NULL string purporting to be the nickname of a value of enum_type

 

value

the address at which to store the value of enum_type corresponding to nick if this functions returns TRUE; if this function returns FALSE, this variable will be left untouched.

 

Returns

TRUE if nick is a member of enum_type , or FALSE otherwise


wocky_enum_to_nick ()

const gchar *
wocky_enum_to_nick (GType enum_type,
                    gint value);

Parameters

enum_type

the GType of a subtype of GEnum

 

value

a value of enum_type

 

Returns

the nickname of value , or NULL if it is not, in fact, a value of enum_type


wocky_absolutize_path ()

gchar *
wocky_absolutize_path (const gchar *path);

Return an absolute form of path . This cleans up duplicate slashes, "." or ".." path segments, etc., and prepends g_get_current_dir() if necessary, but does not necessarily resolve symlinks.

Parameters

path

an absolute or relative path

 

Returns

an absolute path which must be freed with g_free(), or possibly NULL for invalid filenames


wocky_list_deep_copy ()

GList *
wocky_list_deep_copy (GBoxedCopyFunc copy,
                      GList *items);

wocky_g_string_dup ()

GString *
wocky_g_string_dup (const GString *str);

wocky_g_string_free ()

void
wocky_g_string_free (GString *str);

wocky_implement_finish_void()

#define             wocky_implement_finish_void(source, tag)

wocky_implement_finish_copy_pointer()

#define             wocky_implement_finish_copy_pointer(source, tag, copy_func, \
                out_param)

wocky_implement_finish_return_copy_pointer()

#define             wocky_implement_finish_return_copy_pointer(source, tag, \
                copy_func)

wocky_implement_finish_return_pointer()

#define             wocky_implement_finish_return_pointer(source, tag)

Types and Values

telepathy-gabble-0.18.2/lib/ext/wocky/docs/reference/html/wocky-wocky-xmpp-error.html0000644000175000017500000015154112312537050030643 0ustar00smcvsmcv00000000000000 Wocky Reference Manual: wocky-xmpp-error

wocky-xmpp-error

wocky-xmpp-error

Object Hierarchy

    GEnum
    ├── WockyJingleError
    ├── WockySIError
    ├── WockyXmppError
    ├── WockyXmppErrorType
    ╰── WockyXmppStreamError

Description

Functions

wocky_xmpp_error_quark ()

GQuark
wocky_xmpp_error_quark (void);

WOCKY_XMPP_ERROR

#define WOCKY_XMPP_ERROR (wocky_xmpp_error_quark ())

wocky_xmpp_error_register_domain ()

void
wocky_xmpp_error_register_domain (WockyXmppErrorDomain *domain);

Registers a new set of application-specific stanza errors. This allows GErrors in that domain to be passed to wocky_stanza_error_to_node(), and to be recognized and returned by wocky_xmpp_error_extract() (and wocky_stanza_extract_errors(), by extension).

Parameters

domain

a description of the error domain

 

wocky_jingle_error_quark ()

GQuark
wocky_jingle_error_quark (void);

WOCKY_JINGLE_ERROR

#define WOCKY_JINGLE_ERROR (wocky_jingle_error_quark ())

wocky_si_error_quark ()

GQuark
wocky_si_error_quark (void);

WOCKY_SI_ERROR

#define WOCKY_SI_ERROR (wocky_si_error_quark ())

wocky_xmpp_stream_error_quark ()

GQuark
wocky_xmpp_stream_error_quark (void);

Get the error quark used for stream errors

Returns

the quark for stream errors.


WOCKY_XMPP_STREAM_ERROR

#define WOCKY_XMPP_STREAM_ERROR (wocky_xmpp_stream_error_quark ())

Get access to the error quark of the xmpp stream errors.


wocky_xmpp_error_string ()

const gchar *
wocky_xmpp_error_string (WockyXmppError error);

Parameters

error

a core stanza error

 

Returns

the name of the tag corresponding to error


wocky_xmpp_error_description ()

const gchar *
wocky_xmpp_error_description (WockyXmppError error);

Parameters

error

a core stanza error

 

Returns

a description of the error, in English, as specified in XMPP Core


wocky_xmpp_stream_error_from_node ()

GError *
wocky_xmpp_stream_error_from_node (WockyNode *error);

Parameters

error

the root node of a WOCKY_STANZA_TYPE_STREAM_ERROR stanza

 

Returns

a GError in the WOCKY_XMPP_STREAM_ERROR domain.


wocky_stanza_error_to_node ()

WockyNode *
wocky_stanza_error_to_node (const GError *error,
                            WockyNode *parent_node);

wocky_xmpp_stanza_error_to_string ()

const gchar *
wocky_xmpp_stanza_error_to_string (GError *error);

Returns the name of the XMPP stanza error element represented by error . This is intended for use in debugging messages, with GErrors returned by wocky_stanza_extract_errors().

Parameters

error

an error in the domain WOCKY_XMPP_ERROR, or another domain registered with wocky_xmpp_error_register_domain() (such as WOCKY_JINGLE_ERROR).

 

Returns

the error code as a string, or NULL if

error->domain is not known to Wocky.


wocky_xmpp_error_extract ()

void
wocky_xmpp_error_extract (WockyNode *error,
                          WockyXmppErrorType *type,
                          GError **core,
                          GError **specialized,
                          WockyNode **specialized_node);

Given an <error/> node, breaks it down into values describing the error. type and core are guaranteed to be set; specialized and specialized_node will be set if a recognised application-specific error is found, and the latter will be set to NULL if no application-specific error is found.

Any or all of the out parameters may be NULL to ignore the value. The value stored in specialized_node is borrowed from stanza , and is only valid as long as the latter is alive.

Parameters

error

the <error/> child of a stanza with type='error'

 

type

location at which to store the error type

 

core

location at which to store an error in the domain WOCKY_XMPP_ERROR

 

specialized

location at which to store an error in an application-specific domain, if one is found

 

specialized_node

location at which to store the node representing an application-specific error, if one is found

 

wocky_xmpp_error_init ()

void
wocky_xmpp_error_init (void);

wocky_xmpp_error_deinit ()

void
wocky_xmpp_error_deinit (void);

Types and Values

enum WockyXmppErrorType

XMPP error types as described in RFC 3920 §9.3.2.

Members

WOCKY_XMPP_ERROR_TYPE_CANCEL

do not retry (the error is unrecoverable)

 

WOCKY_XMPP_ERROR_TYPE_CONTINUE

proceed (the condition was only a warning)

 

WOCKY_XMPP_ERROR_TYPE_MODIFY

retry after changing the data sent

 

WOCKY_XMPP_ERROR_TYPE_AUTH

retry after providing credentials

 

WOCKY_XMPP_ERROR_TYPE_WAIT

retry after waiting (the error is temporary)

 

enum WockyXmppError

Possible stanza-level errors, as defined by RFC 6210 §8.3.3.

Members

WOCKY_XMPP_ERROR_UNDEFINED_CONDITION

the error condition is not one of those defined by the other conditions in this list

 

WOCKY_XMPP_ERROR_REDIRECT

the recipient or server is redirecting requests for this information to another entity

 

WOCKY_XMPP_ERROR_GONE

the recipient or server can no longer be contacted at this address

 

WOCKY_XMPP_ERROR_BAD_REQUEST

the sender has sent XML that is malformed or that cannot be processed

 

WOCKY_XMPP_ERROR_UNEXPECTED_REQUEST

the recipient or server understood the request but was not expecting it at this time

 

WOCKY_XMPP_ERROR_JID_MALFORMED

the sending entity has provided or communicated an XMPP address

 

WOCKY_XMPP_ERROR_NOT_AUTHORIZED

the sender must provide proper credentials before being allowed to perform the action, or has provided improper credentials

 

WOCKY_XMPP_ERROR_PAYMENT_REQUIRED

the requesting entity is not authorized to access the requested service because payment is required. This code is no longer defined in RFC 6120, the current version of XMPP Core. It's preserved here for interoperability, but new applications should not send it.

 

WOCKY_XMPP_ERROR_FORBIDDEN

the requesting entity does not possess the required permissions to perform the action

 

WOCKY_XMPP_ERROR_ITEM_NOT_FOUND

the addressed JID or item requested cannot be found

 

WOCKY_XMPP_ERROR_RECIPIENT_UNAVAILABLE

the intended recipient is temporarily unavailable

 

WOCKY_XMPP_ERROR_REMOTE_SERVER_NOT_FOUND

a remote server or service specified as part or all of the JID of the intended recipient does not exist

 

WOCKY_XMPP_ERROR_NOT_ALLOWED

the recipient or server does not allow any entity to perform the action

 

WOCKY_XMPP_ERROR_NOT_ACCEPTABLE

the recipient or server understands the request but is refusing to process it because it does not meet criteria defined by the recipient or server

 

WOCKY_XMPP_ERROR_REGISTRATION_REQUIRED

the requesting entity is not authorized to access the requested service because registration is required

 

WOCKY_XMPP_ERROR_SUBSCRIPTION_REQUIRED

the requesting entity is not authorized to access the requested service because a subscription is required

 

WOCKY_XMPP_ERROR_REMOTE_SERVER_TIMEOUT

a remote server or service specified as part or all of the JID of the intended recipient (or required to fulfill a request) could not be contacted within a reasonable amount of time

 

WOCKY_XMPP_ERROR_CONFLICT

access cannot be granted because an existing resource or session exists with the same name or address

 

WOCKY_XMPP_ERROR_INTERNAL_SERVER_ERROR

the server could not process the stanza because of a misconfiguration or an otherwise-undefined internal server error

 

WOCKY_XMPP_ERROR_RESOURCE_CONSTRAINT

the server or recipient lacks the system resources necessary to service the request

 

WOCKY_XMPP_ERROR_FEATURE_NOT_IMPLEMENTED

the feature requested is not implemented by the recipient or server and therefore cannot be processed

 

WOCKY_XMPP_ERROR_SERVICE_UNAVAILABLE

the server or recipient does not currently provide the requested service

 

WOCKY_XMPP_ERROR_POLICY_VIOLATION

the entity has violated some local service policy (e.g., a message contains words that are prohibited by the service) and the server MAY choose to specify the policy as the text of the error or in an application-specific condition element; the associated error type SHOULD be WOCKY_XMPP_ERROR_TYPE_MODIFY or WOCKY_XMPP_ERROR_TYPE_WAIT depending on the policy being violated.

 

struct WockyXmppErrorSpecialization

struct WockyXmppErrorSpecialization {
  const gchar *description;
  WockyXmppError specializes;
  gboolean override_type;
  WockyXmppErrorType type;
};

A struct to represent a specialization of an existing WockyXmppError member.

Members

const gchar *description;

description of the error

 

WockyXmppError specializes;

which WockyXmppError this error specializes

 

gboolean override_type;

TRUE if type should be used, or FALSE if the default error type for specializes should be used

 

WockyXmppErrorType type;

the XMPP error type

 

struct WockyXmppErrorDomain

struct WockyXmppErrorDomain {
  GQuark domain;
  GType enum_type;
  WockyXmppErrorSpecialization *codes;
};

A struct to represent extra XMPP error domains added.

Members

GQuark domain;

a GQuark of the error domain

 

GType enum_type;

the GType of the error enum

 

WockyXmppErrorSpecialization *codes;

a NULL-terminated array of of WockyXmppErrorSpecializations

 

enum WockyJingleError

Jingle specific errors.

Members

WOCKY_JINGLE_ERROR_OUT_OF_ORDER

the request cannot occur at this point in the state machine

 

WOCKY_JINGLE_ERROR_TIE_BREAK

the request is rejected because it was sent while the initiator was awaiting a reply on a similar request

 

WOCKY_JINGLE_ERROR_UNKNOWN_SESSION

the 'sid' attribute specifies a session that is unknown to the recipient

 

WOCKY_JINGLE_ERROR_UNSUPPORTED_INFO

the recipient does not support the informational payload of a session-info action.

 

enum WockySIError

SI specific errors.

Members

WOCKY_SI_ERROR_NO_VALID_STREAMS

none of the available streams are acceptable

 

WOCKY_SI_ERROR_BAD_PROFILE

the profile is not understood or invalid

 

enum WockyXmppStreamError

Stream-level error conditions as described in RFC 3920 §4.7.3.

Members

WOCKY_XMPP_STREAM_ERROR_BAD_FORMAT

the entity has sent XML that cannot be processed

 

WOCKY_XMPP_STREAM_ERROR_BAD_NAMESPACE_PREFIX

the entity has sent a namespace prefix that is unsupported, or has sent no namespace prefix on an element that requires such a prefix

 

WOCKY_XMPP_STREAM_ERROR_CONFLICT

the server is closing the active stream for this entity because a new stream has been initiated that conflicts with the existing stream

 

WOCKY_XMPP_STREAM_ERROR_CONNECTION_TIMEOUT

the entity has not generated any traffic over the stream for some period of time

 

WOCKY_XMPP_STREAM_ERROR_HOST_GONE

the value of the 'to' attribute provided by the initiating entity in the stream header corresponds to a hostname that is no longer hosted by the server

 

WOCKY_XMPP_STREAM_ERROR_HOST_UNKNOWN

the value of the 'to' attribute provided by the initiating entity in the stream header does not correspond to a hostname that is hosted by the server

 

WOCKY_XMPP_STREAM_ERROR_IMPROPER_ADDRESSING

a stanza sent between two servers lacks a 'to' or 'from' attribute (or the attribute has no value)

 

WOCKY_XMPP_STREAM_ERROR_INTERNAL_SERVER_ERROR

the server has experienced a misconfiguration or an otherwise-undefined internal error that prevents it from servicing the stream

 

WOCKY_XMPP_STREAM_ERROR_INVALID_FROM

the JID or hostname provided in a 'from' address does not match an authorized JID or validated domain negotiated between servers via SASL or dialback, or between a client and a server via authentication and resource binding

 

WOCKY_XMPP_STREAM_ERROR_INVALID_ID

the stream ID or dialback ID is invalid or does not match an ID previously provided

 

WOCKY_XMPP_STREAM_ERROR_INVALID_NAMESPACE

the streams namespace name is something other than "http://etherx.jabber.org/streams" or the dialback namespace name is something other than "jabber:server:dialback"

 

WOCKY_XMPP_STREAM_ERROR_INVALID_XML

the entity has sent invalid XML over the stream to a server that performs validation

 

WOCKY_XMPP_STREAM_ERROR_NOT_AUTHORIZED

the entity has attempted to send data before the stream has been authenticated, or otherwise is not authorized to perform an action related to stream negotiation

 

WOCKY_XMPP_STREAM_ERROR_POLICY_VIOLATION

the entity has violated some local service policy

 

WOCKY_XMPP_STREAM_ERROR_REMOTE_CONNECTION_FAILED

the server is unable to properly connect to a remote entity that is required for authentication or authorization

 

WOCKY_XMPP_STREAM_ERROR_RESOURCE_CONSTRAINT

the server lacks the system resources necessary to service the stream

 

WOCKY_XMPP_STREAM_ERROR_RESTRICTED_XML

the entity has attempted to send restricted XML features such as a comment, processing instruction, DTD, entity reference, or unescaped character

 

WOCKY_XMPP_STREAM_ERROR_SEE_OTHER_HOST

the server will not provide service to the initiating entity but is redirecting traffic to another host

 

WOCKY_XMPP_STREAM_ERROR_SYSTEM_SHUTDOWN

the server is being shut down and all active streams are being closed

 

WOCKY_XMPP_STREAM_ERROR_UNDEFINED_CONDITION

the error condition is not one of those defined by the other conditions in this list

 

WOCKY_XMPP_STREAM_ERROR_UNSUPPORTED_ENCODING

the initiating entity has encoded the stream in an encoding that is not supported by the server

 

WOCKY_XMPP_STREAM_ERROR_UNSUPPORTED_STANZA_TYPE

the initiating entity has sent a first-level child of the stream that is not supported by the server

 

WOCKY_XMPP_STREAM_ERROR_UNSUPPORTED_VERSION

the value of the 'version' attribute provided by the initiating entity in the stream header specifies a version of XMPP that is not supported by the server

 

WOCKY_XMPP_STREAM_ERROR_XML_NOT_WELL_FORMED

the initiating entity has sent XML that is not well-formed

 

WOCKY_XMPP_STREAM_ERROR_UNKNOWN

an unknown stream error

 
telepathy-gabble-0.18.2/lib/ext/wocky/docs/reference/html/WockyXmppConnection.html0000644000175000017500000015642312312537050030231 0ustar00smcvsmcv00000000000000 Wocky Reference Manual: WockyXmppConnection

WockyXmppConnection

WockyXmppConnection — Low-level XMPP connection.

Properties

GIOStream * base-stream Read / Write / Construct Only

Types and Values

Object Hierarchy

    GObject
    ╰── WockyXmppConnection

Description

Sends and receives WockyStanzas from an underlying GIOStream.

Functions

wocky_xmpp_connection_error_quark ()

GQuark
wocky_xmpp_connection_error_quark (void);

Get the error quark used by the connection.

Returns

the quark for connection errors.


WOCKY_XMPP_CONNECTION_ERROR

#define WOCKY_XMPP_CONNECTION_ERROR (wocky_xmpp_connection_error_quark ())

Get access to the error quark of the xmpp connection.


wocky_xmpp_connection_new ()

WockyXmppConnection *
wocky_xmpp_connection_new (GIOStream *stream);

Convenience function to create a new WockyXmppConnection.

Parameters

stream

GIOStream over wich all the data will be sent/received.

 

Returns

a new WockyXmppConnection.


wocky_xmpp_connection_send_open_async ()

void
wocky_xmpp_connection_send_open_async (WockyXmppConnection *connection,
                                       const gchar *to,
                                       const gchar *from,
                                       const gchar *version,
                                       const gchar *lang,
                                       const gchar *id,
                                       GCancellable *cancellable,
                                       GAsyncReadyCallback callback,
                                       gpointer user_data);

Request asynchronous sending of an XMPP stream opening over the stream. When the operation is finished callback will be called. You can then call wocky_xmpp_connection_send_open_finish() to get the result of the operation.

Parameters

connection

a WockyXmppConnection.

 

to

destination in the XMPP opening (can be NULL).

 

from

sender in the XMPP opening (can be NULL).

 

version

XMPP version sent (can be NULL).

 

lang

language sent (can be NULL).

 

id

XMPP Stream ID, if any, or NULL

 

cancellable

optional GCancellable object, NULL to ignore.

 

callback

callback to call when the request is satisfied.

 

user_data

the data to pass to callback function.

 

wocky_xmpp_connection_send_open_finish ()

gboolean
wocky_xmpp_connection_send_open_finish
                               (WockyXmppConnection *connection,
                                GAsyncResult *result,
                                GError **error);

Finishes sending a stream opening.

Parameters

connection

a WockyXmppConnection.

 

result

a GAsyncResult.

 

error

a GError location to store the error occuring, or NULL to ignore.

 

Returns

TRUE if the opening was succesfully sent, FALSE on error.


wocky_xmpp_connection_recv_open_async ()

void
wocky_xmpp_connection_recv_open_async (WockyXmppConnection *connection,
                                       GCancellable *cancellable,
                                       GAsyncReadyCallback callback,
                                       gpointer user_data);

Request asynchronous receiving of an XMPP stream opening over the stream. When the operation is finished callback will be called. You can then call wocky_xmpp_connection_recv_open_finish() to get the result of the operation.

Parameters

connection

a WockyXmppConnection.

 

cancellable

optional GCancellable object, NULL to ignore.

 

callback

callback to call when the request is satisfied.

 

user_data

the data to pass to callback function.

 

wocky_xmpp_connection_recv_open_finish ()

gboolean
wocky_xmpp_connection_recv_open_finish
                               (WockyXmppConnection *connection,
                                GAsyncResult *result,
                                gchar **to,
                                gchar **from,
                                gchar **version,
                                gchar **lang,
                                gchar **id,
                                GError **error);

Finishes receiving a stream opening.

Parameters

connection

a WockyXmppConnection.

 

result

a GAsyncResult.

 

to

Optional location to store the to attribute in the XMPP open stanza will be stored (free after usage).

 

from

Optional location to store the from attribute in the XMPP open stanza will be stored (free after usage).

 

version

Optional location to store the version attribute in the XMPP open stanza will be stored (free after usage).

 

lang

Optional location to store the lang attribute in the XMPP open stanza will be stored (free after usage).

 

id

Optional location to store the Session ID of the XMPP stream (free after usage)

 

error

a GError location to store the error occuring, or NULL to ignore.

 

Returns

TRUE if the opening was succesfully received, FALSE on error.


wocky_xmpp_connection_send_stanza_async ()

void
wocky_xmpp_connection_send_stanza_async
                               (WockyXmppConnection *connection,
                                WockyStanza *stanza,
                                GCancellable *cancellable,
                                GAsyncReadyCallback callback,
                                gpointer user_data);

Request asynchronous sending of a WockyStanza. When the operation is finished callback will be called. You can then call wocky_xmpp_connection_send_stanza_finish() to get the result of the operation.

Can only be called after wocky_xmpp_connection_send_open_async has finished its operation.

Parameters

connection

a WockyXmppConnection

 

stanza

WockyStanza to send.

 

cancellable

optional GCancellable object, NULL to ignore.

 

callback

callback to call when the request is satisfied.

 

user_data

the data to pass to callback function.

 

wocky_xmpp_connection_send_stanza_finish ()

gboolean
wocky_xmpp_connection_send_stanza_finish
                               (WockyXmppConnection *connection,
                                GAsyncResult *result,
                                GError **error);

Finishes sending a stanza.

Parameters

connection

a WockyXmppConnection.

 

result

a GAsyncResult.

 

error

a GError location to store the error occuring, or NULL to ignore.

 

Returns

TRUE if the stanza was succesfully sent, FALSE on error.


wocky_xmpp_connection_recv_stanza_async ()

void
wocky_xmpp_connection_recv_stanza_async
                               (WockyXmppConnection *connection,
                                GCancellable *cancellable,
                                GAsyncReadyCallback callback,
                                gpointer user_data);

Asynchronous receive a WockyStanza. When the operation is finished callback will be called. You can then call wocky_xmpp_connection_recv_stanza_finish() to get the result of the operation.

Can only be called after wocky_xmpp_connection_recv_open_async has finished its operation.

Parameters

connection

a WockyXmppConnection

 

cancellable

optional GCancellable object, NULL to ignore.

 

callback

callback to call when the request is satisfied.

 

user_data

the data to pass to callback function.

 

wocky_xmpp_connection_recv_stanza_finish ()

WockyStanza *
wocky_xmpp_connection_recv_stanza_finish
                               (WockyXmppConnection *connection,
                                GAsyncResult *result,
                                GError **error);

Finishes receiving a stanza

Parameters

connection

a WockyXmppConnection.

 

result

a GAsyncResult.

 

error

a GError location to store the error occuring, or NULL to ignore.

 

Returns

A WockyStanza or NULL on error (unref after usage)


wocky_xmpp_connection_send_close_async ()

void
wocky_xmpp_connection_send_close_async
                               (WockyXmppConnection *connection,
                                GCancellable *cancellable,
                                GAsyncReadyCallback callback,
                                gpointer user_data);

Request asynchronous sending of an XMPP stream close. When the operation is finished callback will be called. You can then call wocky_xmpp_connection_send_close_finish() to get the result of the operation.

Can only be called after wocky_xmpp_connection_send_open_async has finished its operation.

Parameters

connection

a WockyXmppConnection.

 

cancellable

optional GCancellable object, NULL to ignore.

 

callback

callback to call when the request is satisfied.

 

user_data

the data to pass to callback function.

 

wocky_xmpp_connection_send_close_finish ()

gboolean
wocky_xmpp_connection_send_close_finish
                               (WockyXmppConnection *connection,
                                GAsyncResult *result,
                                GError **error);

Finishes send the xmpp stream close.

Parameters

connection

a WockyXmppConnection.

 

result

a GAsyncResult.

 

error

a GError location to store the error occuring, or NULL to ignore.

 

Returns

TRUE on success or FALSE on error.


wocky_xmpp_connection_force_close_async ()

void
wocky_xmpp_connection_force_close_async
                               (WockyXmppConnection *connection,
                                GCancellable *cancellable,
                                GAsyncReadyCallback callback,
                                gpointer user_data);

wocky_xmpp_connection_force_close_finish ()

gboolean
wocky_xmpp_connection_force_close_finish
                               (WockyXmppConnection *connection,
                                GAsyncResult *result,
                                GError **error);

wocky_xmpp_connection_send_whitespace_ping_async ()

void
wocky_xmpp_connection_send_whitespace_ping_async
                               (WockyXmppConnection *connection,
                                GCancellable *cancellable,
                                GAsyncReadyCallback callback,
                                gpointer user_data);

Request asynchronous sending of a whitespace ping. When the operation is finished callback will be called. You can then call wocky_xmpp_connection_send_whitespace_ping_finish() to get the result of the operation.

Can only be called after wocky_xmpp_connection_send_open_async has finished its operation.

Parameters

connection

a WockyXmppConnection

 

cancellable

optional GCancellable object, NULL to ignore.

 

callback

callback to call when the request is satisfied.

 

user_data

the data to pass to callback function.

 

wocky_xmpp_connection_send_whitespace_ping_finish ()

gboolean
wocky_xmpp_connection_send_whitespace_ping_finish
                               (WockyXmppConnection *connection,
                                GAsyncResult *result,
                                GError **error);

Finishes sending a whitespace ping.

Parameters

connection

a WockyXmppConnection.

 

result

a GAsyncResult.

 

error

a GError location to store the error occuring, or NULL to ignore.

 

Returns

TRUE if the ping was succesfully sent, FALSE on error.


wocky_xmpp_connection_reset ()

void
wocky_xmpp_connection_reset (WockyXmppConnection *connection);

Reset the XMPP Connection. After the reset the connection is back in its initial state (as if wocky_xmpp_connection_send_open_async() and wocky_xmpp_connection_recv_open_async() were never called).

Parameters

connection

a WockyXmppConnection.

 

wocky_xmpp_connection_new_id ()

gchar *
wocky_xmpp_connection_new_id (WockyXmppConnection *self);

Parameters

self

a WockyXmppConnection.

 

Returns

A short unique string for usage as the id attribute on a stanza (free after usage).

Types and Values

enum WockyXmppConnectionError

The WockyXmppConnection specific errors that can occur while reading a stream.

Members

WOCKY_XMPP_CONNECTION_ERROR_EOS

Connection got closed before receiving an XMPP stream close.

 

WOCKY_XMPP_CONNECTION_ERROR_CLOSED

Other side closed the xmpp stream.

 

WOCKY_XMPP_CONNECTION_ERROR_NOT_OPEN

Trying to send or receive while the connection isn't open.

 

WOCKY_XMPP_CONNECTION_ERROR_IS_CLOSED

Trying to send or receive while the connection is closed.

 

WOCKY_XMPP_CONNECTION_ERROR_IS_OPEN

Trying to send or receive the connection opening when it's already open

 

struct WockyXmppConnectionClass

struct WockyXmppConnectionClass {
};

The class of a WockyXmppConnection.

Property Details

The “base-stream” property

  “base-stream”              GIOStream *

the stream that the XMPP connection communicates over.

Flags: Read / Write / Construct Only

telepathy-gabble-0.18.2/lib/ext/wocky/docs/reference/html/WockyXmppReader.html0000644000175000017500000007206412312537050027332 0ustar00smcvsmcv00000000000000 Wocky Reference Manual: WockyXmppReader

WockyXmppReader

WockyXmppReader — Xmpp XML to stanza deserializer

Properties

gchar * default-namespace Read / Write / Construct Only
gchar * from Read
gchar * id Read
gchar * lang Read
gboolean streaming-mode Read / Write / Construct Only
gchar * to Read
gchar * version Read

Object Hierarchy

    GEnum
    ├── WockyXmppReaderError
    ╰── WockyXmppReaderState
    GObject
    ╰── WockyXmppReader

Description

The WockyXmppReader deserializes XML to WockyStanzas, misc, other

Functions

wocky_xmpp_reader_error_quark ()

GQuark
wocky_xmpp_reader_error_quark (void);

Get the error quark used by the reader.

Returns

the quark for reader errors.


WOCKY_XMPP_READER_ERROR

#define WOCKY_XMPP_READER_ERROR (wocky_xmpp_reader_error_quark ())

Get access to the error quark of the reader.


wocky_xmpp_reader_new ()

WockyXmppReader *
wocky_xmpp_reader_new (void);

Convenience function to create a new WockyXmppReader.

Returns

a new WockyXmppReader


wocky_xmpp_reader_new_no_stream ()

WockyXmppReader *
wocky_xmpp_reader_new_no_stream (void);

Convenience function to create a new WockyXmppReader that has streaming mode disabled.

Returns

a new WockyXmppReader in non-streaming mode


wocky_xmpp_reader_new_no_stream_ns ()

WockyXmppReader *
wocky_xmpp_reader_new_no_stream_ns (const gchar *default_namespace);

Create a new WockyXmppReader, with “streaming-mode” disabled and the specified “default-namespace”.

Parameters

default_namespace

default XML namespace to apply to the top-level element

 

Returns

a new WockyXmppReader in non-streaming mode.

[transfer full]


wocky_xmpp_reader_get_state ()

WockyXmppReaderState
wocky_xmpp_reader_get_state (WockyXmppReader *reader);

Parameters

reader

a WockyXmppReader

 

Returns

The current state of the reader


wocky_xmpp_reader_push ()

void
wocky_xmpp_reader_push (WockyXmppReader *reader,
                        const guint8 *data,
                        gsize length);

Push an amount of data to parse.

Parameters

reader

a WockyXmppReader

 

data

Data to read

 

length

Size of data

 

wocky_xmpp_reader_pop_stanza ()

WockyStanza *
wocky_xmpp_reader_pop_stanza (WockyXmppReader *reader);

Gets one WockyStanza out of the reader or NULL if there are no available stanzas.

Parameters

reader

a WockyXmppReader

 

Returns

One WockyStanza or NULL if there are no available stanzas. Caller owns the returned stanza.


wocky_xmpp_reader_peek_stanza ()

WockyStanza *
wocky_xmpp_reader_peek_stanza (WockyXmppReader *reader);

Returns the first WockyStanza available from reader or NULL if there are no available stanzas. The stanza is not removed from the readers queue

Parameters

reader

a WockyXmppReader

 

Returns

One WockyStanza or NULL if there are no available stanzas. The stanza is owned by the WockyXmppReader


wocky_xmpp_reader_get_error ()

GError *
wocky_xmpp_reader_get_error (WockyXmppReader *reader);

Get the error from the reader

Parameters

reader

a WockyXmppReader

 

Returns

A copy of the error as encountered by the reader or NULL if there was no error. Free after use.


wocky_xmpp_reader_reset ()

void
wocky_xmpp_reader_reset (WockyXmppReader *reader);

Reset the xml parser.

Parameters

reader

a WockyXmppReader

 

Types and Values

struct WockyXmppReaderClass

struct WockyXmppReaderClass {
};

The class of a WockyXmppReader.


enum WockyXmppReaderState

The possible states a reader can be in.

Members

WOCKY_XMPP_READER_STATE_INITIAL

initial state

 

WOCKY_XMPP_READER_STATE_OPENED

stream is open

 

WOCKY_XMPP_READER_STATE_CLOSED

stream has been closed

 

WOCKY_XMPP_READER_STATE_ERROR

stream reader hit an error

 

enum WockyXmppReaderError

The different errors that can occur while reading a stream

Members

WOCKY_XMPP_READER_ERROR_INVALID_STREAM_START

invalid start of xmpp stream

 

WOCKY_XMPP_READER_ERROR_PARSE_ERROR

error in parsing the XML

 

Property Details

The “default-namespace” property

  “default-namespace”        gchar *

The default namespace for the root element of the document. Only meaningful if streaming-mode is FALSE.

Flags: Read / Write / Construct Only

Default value: ""


The “from” property

  “from”                     gchar *

from attribute in the xml stream opening.

Flags: Read

Default value: NULL


The “id” property

  “id”                       gchar *

id attribute in the xml stream opening.

Flags: Read

Default value: NULL


The “lang” property

  “lang”                     gchar *

xml:lang attribute in the xml stream opening.

Flags: Read

Default value: NULL


The “streaming-mode” property

  “streaming-mode”           gboolean

Whether the xml to be read is one big stream or separate documents.

Flags: Read / Write / Construct Only

Default value: TRUE


The “to” property

  “to”                       gchar *

to attribute in the xml stream opening.

Flags: Read

Default value: NULL


The “version” property

  “version”                  gchar *

version attribute in the xml stream opening.

Flags: Read

Default value: NULL

telepathy-gabble-0.18.2/lib/ext/wocky/docs/reference/html/WockyXmppWriter.html0000644000175000017500000004716412312537050027407 0ustar00smcvsmcv00000000000000 Wocky Reference Manual: WockyXmppWriter

WockyXmppWriter

WockyXmppWriter — Xmpp stanza to XML serializer

Properties

gboolean streaming-mode Read / Write / Construct Only

Types and Values

Object Hierarchy

    GObject
    ╰── WockyXmppWriter

Description

The WockyXmppWriter serializes WockyStanzas and XMPP stream opening and closing to raw XML. The various functions provide a pointer to an internal buffer, which remains valid until the next call to the writer.

Functions

wocky_xmpp_writer_new ()

WockyXmppWriter *
wocky_xmpp_writer_new (void);

Convenience function to create a new WockyXmppWriter.

Returns

a new WockyXmppWriter


wocky_xmpp_writer_new_no_stream ()

WockyXmppWriter *
wocky_xmpp_writer_new_no_stream (void);

Convenience function to create a new WockyXmppWriter that has streaming mode disabled.

Returns

a new WockyXmppWriter in non-streaming mode


wocky_xmpp_writer_stream_open ()

void
wocky_xmpp_writer_stream_open (WockyXmppWriter *writer,
                               const gchar *to,
                               const gchar *from,
                               const gchar *version,
                               const gchar *lang,
                               const gchar *id,
                               const guint8 **data,
                               gsize *length);

Create the XML opening header of an XMPP stream. The result is available in the data buffer. The buffer is only valid until the next call to a function the writer.

This function can only be called in streaming mode.

Parameters

writer

a WockyXmppWriter

 

to

the target of the stream opening (usually the xmpp server name)

 

from

the sender of the stream opening (usually the jid of the client)

 

version

XMPP version

 

lang

default XMPP stream language

 

id

XMPP Stream ID, if any, or NULL

 

data

location to store a pointer to the data buffer

 

length

length of the data buffer

 

wocky_xmpp_writer_stream_close ()

void
wocky_xmpp_writer_stream_close (WockyXmppWriter *writer,
                                const guint8 **data,
                                gsize *length);

Create the XML closing footer of an XMPP stream . The result is available in the data buffer. The buffer is only valid until the next call to a function

This function can only be called in streaming mode.

Parameters

writer

a WockyXmppWriter

 

data

location to store a pointer to the data buffer

 

length

length of the data buffer

 

wocky_xmpp_writer_write_stanza ()

void
wocky_xmpp_writer_write_stanza (WockyXmppWriter *writer,
                                WockyStanza *stanza,
                                const guint8 **data,
                                gsize *length);

Serialize the stanza to XML. The result is available in the data buffer. The buffer is only valid until the next call to a function

Parameters

writer

a WockyXmppWriter

 

stanza

the stanza to serialize

 

data

location to store a pointer to the data buffer

 

length

length of the data buffer

 

wocky_xmpp_writer_write_node_tree ()

void
wocky_xmpp_writer_write_node_tree (WockyXmppWriter *writer,
                                   WockyNodeTree *tree,
                                   const guint8 **data,
                                   gsize *length);

Serialize the tree to XML. The result is available in the data buffer. The buffer is only valid until the next call to a function. This function may only be called in non-streaming mode.

Parameters

writer

a WockyXmppWriter

 

tree

the node tree to serialize

 

data

location to store a pointer to the data buffer

 

length

length of the data buffer

 

wocky_xmpp_writer_flush ()

void
wocky_xmpp_writer_flush (WockyXmppWriter *writer);

Flushes and frees the internal data buffer

Parameters

writer

a WockyXmppWriter

 

Types and Values

struct WockyXmppWriterClass

struct WockyXmppWriterClass {
};

The class of a WockyXmppWriter.

Property Details

The “streaming-mode” property

  “streaming-mode”           gboolean

Whether the xml to be written is one big stream or separate documents.

Flags: Read / Write / Construct Only

Default value: TRUE

telepathy-gabble-0.18.2/lib/ext/wocky/docs/reference/html/wocky.devhelp20000644000175000017500000037255512312537050026157 0ustar00smcvsmcv00000000000000 telepathy-gabble-0.18.2/lib/ext/wocky/docs/reference/html/up.png0000644000175000017500000000040412312537050024477 0ustar00smcvsmcv00000000000000PNG  IHDRabKGD pHYs B(xtIME IDAT81 @D{xa;$]r =JR1, Sd-}0̟oL:m-QO[ k TzMޠL,:ךu!tK; Pp Ot@l/̵*l}IENDB`telepathy-gabble-0.18.2/lib/ext/wocky/docs/reference/html/up-insensitive.png0000644000175000017500000000056612312537050027046 0ustar00smcvsmcv00000000000000PNG  IHDRabKGD pHYs B(xtIMEwIIDAT8?/Qϙ?[u$VHTDۈBM+! dd > dl > dt { padding-top: 0.25em; padding-bottom: 0.25em; } dl.toc > dt { padding-top: 1em; padding-bottom: 0.5em; font-weight: bold; } .parameter { font-style: normal; } .footer { padding-top: 3.5em; /* tango:aluminium 3 */ color: #babdb6; text-align: center; font-size: 80%; } .informalfigure, .figure { margin: 1em; } .informalexample, .example { margin-top: 1em; margin-bottom: 1em; } .warning { /* tango:orange 0/1 */ background: #ffeed9; background: rgba(252, 175, 62, 0.1); border-color: #ffb04f; border-color: rgba(252, 175, 62, 0.2); } .note { /* tango:chameleon 0/0.5 */ background: #d8ffb2; background: rgba(138, 226, 52, 0.1); border-color: #abf562; border-color: rgba(138, 226, 52, 0.2); } div.blockquote { border-color: #eeeeec; } .note, .warning, div.blockquote { padding: 0.5em; border-width: 1px; border-style: solid; margin: 2em; } .note p, .warning p { margin: 0; } div.warning h3.title, div.note h3.title { display: none; } p + div.section { margin-top: 1em; } div.refnamediv, div.refsynopsisdiv, div.refsect1, div.refsect2, div.toc, div.section { margin-bottom: 1em; } /* blob links */ h2 .extralinks, h3 .extralinks { float: right; /* tango:aluminium 3 */ color: #babdb6; font-size: 80%; font-weight: normal; } .lineart { color: #d3d7cf; font-weight: normal; } .annotation { /* tango:aluminium 5 */ color: #555753; font-weight: normal; } .structfield { font-style: normal; font-weight: normal; } /* code listings */ .listing_code .programlisting .cbracket { color: #a40000; } /* tango: scarlet red 3 */ .listing_code .programlisting .comment { color: #a1a39d; } /* tango: aluminium 4 */ .listing_code .programlisting .function { color: #000000; font-weight: bold; } .listing_code .programlisting .function a { color: #11326b; font-weight: bold; } /* tango: sky blue 4 */ .listing_code .programlisting .keyword { color: #4e9a06; } /* tango: chameleon 3 */ .listing_code .programlisting .linenum { color: #babdb6; } /* tango: aluminium 3 */ .listing_code .programlisting .normal { color: #000000; } .listing_code .programlisting .number { color: #75507b; } /* tango: plum 2 */ .listing_code .programlisting .preproc { color: #204a87; } /* tango: sky blue 3 */ .listing_code .programlisting .string { color: #c17d11; } /* tango: chocolate 2 */ .listing_code .programlisting .type { color: #000000; } .listing_code .programlisting .type a { color: #11326b; } /* tango: sky blue 4 */ .listing_code .programlisting .symbol { color: #ce5c00; } /* tango: orange 3 */ .listing_frame { /* tango:sky blue 1 */ border: solid 1px #729fcf; border: solid 1px rgba(114, 159, 207, 0.2); padding: 0px; } .listing_lines, .listing_code { margin-top: 0px; margin-bottom: 0px; padding: 0.5em; } .listing_lines { /* this just adds visual clutter and takes precious room from small screens */ display: none; } .listing_lines { /* tango:sky blue 0.5 */ background: #a6c5e3; background: rgba(114, 159, 207, 0.2); /* tango:aluminium 6 */ color: #2e3436; } .listing_code { /* tango:sky blue 0 */ background: #e6f3ff; background: rgba(114, 159, 207, 0.1); } .listing_code .programlisting { /* override from previous */ border: none 0px; padding: 0px; background: none; } .listing_lines pre, .listing_code pre { margin: 0px; } telepathy-gabble-0.18.2/lib/ext/wocky/docs/reference/html/right.png0000644000175000017500000000040512312537050025171 0ustar00smcvsmcv00000000000000PNG  IHDRabKGD pHYs B(xtIME!GIDAT8үa?MIdEr,-hAIl ry}sX6 !9#D r$-Br$G"$;WZ&!cq \`軀O=QoufIENDB`telepathy-gabble-0.18.2/lib/ext/wocky/docs/reference/html/right-insensitive.png0000644000175000017500000000056512312537050027536 0ustar00smcvsmcv00000000000000PNG  IHDRabKGD pHYs B(xtIME ^IDAT8͒J` /S_$AqrW(>m"]\(49.Nd39{eM#MSIιEiHz|3{̲l3,KkV'@EEQlwyiq]Kh4:mĦ,;ts\aR5/7'Wps׭I,K1=0j0Wg> PU𻤝0 ]?qCҫιg~kA_IENDB`telepathy-gabble-0.18.2/lib/ext/wocky/docs/reference/html/left.png0000644000175000017500000000040612312537050025007 0ustar00smcvsmcv00000000000000PNG  IHDRabKGD pHYs B(xtIME,`m;IDAT8үa?DAPY\$[p+IIMlf('}Mpy{_ޥ}^q xZ <=Yj) <04\~+Pl#",Qϑp Iǐlsw>[/]_i03IENDB`telepathy-gabble-0.18.2/lib/ext/wocky/docs/reference/html/left-insensitive.png0000644000175000017500000000061312312537050027345 0ustar00smcvsmcv00000000000000PNG  IHDRabKGD pHYs B(xtIMEƫqIDAT8͒NQ@pds`*4@W@ A!Ԇ@6^ 5hxIH R`sQp̙339B|sKEQTK@۝΁i^~Wʆ`0TJ6TcYn6A ƀ~߱>}ǭs; lYkwr 5U= /" "uU=ɲlArDzp5I4^E+P3Ɯq_p ̥iUYp=#IENDB`telepathy-gabble-0.18.2/lib/ext/wocky/docs/reference/html/index.sgml0000644000175000017500000047672612312537050025370 0ustar00smcvsmcv00000000000000 telepathy-gabble-0.18.2/lib/ext/wocky/docs/reference/html/home.png0000644000175000017500000000040012312537050024777 0ustar00smcvsmcv00000000000000PNG  IHDRabKGD pHYs B(xtIME &IDAT8ҽ Aߞf`n v`6`/`Yܡ`f&k$,} 0b+ԸaQW~b O e{y N[L}.piBzmm o.I]7^[;%:VIENDB`telepathy-gabble-0.18.2/lib/ext/wocky/docs/reference/wocky-overrides.txt0000644000175000017500000000000012200204546026263 0ustar00smcvsmcv00000000000000telepathy-gabble-0.18.2/lib/ext/wocky/docs/reference/wocky-docs.sgml0000644000175000017500000000743412201202754025356 0ustar00smcvsmcv00000000000000 ]> Wocky Reference Manual API Reference Object Hierarchy API Index telepathy-gabble-0.18.2/lib/ext/wocky/docs/reference/Makefile.am0000644000175000017500000000733512200204546024444 0ustar00smcvsmcv00000000000000## Process this file with automake to produce Makefile.in # We require automake 1.6 at least. AUTOMAKE_OPTIONS = 1.6 # This is a blank Makefile.am for using gtk-doc. # Copy this to your project's API docs directory and modify the variables to # suit your project. See the GTK+ Makefiles in gtk+/docs/reference for examples # of using the various options. # The name of the module, e.g. 'glib'. DOC_MODULE= wocky # Uncomment for versioned docs and specify the version of the module, e.g. '2'. #DOC_MODULE_VERSION=2 # The top-level SGML file. You can change this if you want to. DOC_MAIN_SGML_FILE=$(DOC_MODULE)-docs.sgml # Directories containing the source code, relative to $(srcdir). # gtk-doc will search all .c and .h files beneath these paths # for inline comments documenting functions and macros. # e.g. DOC_SOURCE_DIR=../../../gtk ../../../gdk DOC_SOURCE_DIR=$(abs_top_srcdir)/wocky $(abs_top_builddir)/wocky # Extra options to pass to gtkdoc-scangobj. Not normally needed. SCANGOBJ_OPTIONS= # Extra options to supply to gtkdoc-scan. # e.g. SCAN_OPTIONS=--deprecated-guards="GTK_DISABLE_DEPRECATED" SCAN_OPTIONS=--rebuild-types --rebuild-sections # Extra options to supply to gtkdoc-mkdb. # e.g. MKDB_OPTIONS=--xml-mode --output-format=xml MKDB_OPTIONS=--sgml-mode --output-format=xml # Extra options to supply to gtkdoc-mktmpl # e.g. MKTMPL_OPTIONS=--only-section-tmpl MKTMPL_OPTIONS= # Extra options to supply to gtkdoc-mkhtml MKHTML_OPTIONS= # Extra options to supply to gtkdoc-fixref. Not normally needed. # e.g. FIXXREF_OPTIONS=--extra-dir=../gdk-pixbuf/html --extra-dir=../gdk/html FIXXREF_OPTIONS= # If we're building out of tree, we need to scan wocky/ under the builddir as # well as under the sourcedir (which is specified above with DOC_SOURCE_DIR) to # pick up the enumtypes. # # If we're building in-tree, well, we'd just better hope gtkdoc doesn't mind # scanning stuff twice. SCAN_OPTIONS += --source-dir=$(abs_top_builddir)/wocky MKDB_OPTIONS += --source-dir=$(abs_top_builddir)/wocky # Used for dependencies. The docs will be rebuilt if any of these change. # e.g. HFILE_GLOB=$(top_srcdir)/gtk/*.h # e.g. CFILE_GLOB=$(top_srcdir)/gtk/*.c HFILE_GLOB=$(top_srcdir)/wocky/*.h $(top_builddir)/wocky/*.h CFILE_GLOB=$(top_srcdir)/wocky/*.c $(top_builddir)/wocky/*.c # Header files to ignore when scanning. # e.g. IGNORE_HFILES=gtkdebug.h gtkintl.h IGNORE_HFILES= # Images to copy into HTML directory. # e.g. HTML_IMAGES=$(top_srcdir)/gtk/stock-icons/stock_about_24.png HTML_IMAGES= # Extra SGML files that are included by $(DOC_MAIN_SGML_FILE). # e.g. content_files=running.sgml building.sgml changes-2.0.sgml content_files= # SGML files where gtk-doc abbrevations (#GtkWidget) are expanded # These files must be listed here *and* in content_files # e.g. expand_content_files=running.sgml expand_content_files= # CFLAGS and LDFLAGS for compiling gtkdoc-scangobj with your library. # Only needed if you are using gtkdoc-scangobj to dynamically query widget # signals and properties. # e.g. GTKDOC_CFLAGS=-I$(top_srcdir) -I$(top_builddir) $(GTK_DEBUG_FLAGS) # e.g. GTKDOC_LIBS=$(top_builddir)/gtk/$(gtktargetlib) GTKDOC_CFLAGS=-I$(top_srcdir) -I$(top_builddir) @GLIB_CFLAGS@ @SQLITE_CFLAGS@ GTKDOC_LIBS= @GLIB_LIBS@ @SQLITE_LIBS@ $(top_builddir)/wocky/libwocky.la # This includes the standard gtk-doc make rules, copied by gtkdocize. include $(top_srcdir)/gtk-doc.make # Other files to distribute # e.g. EXTRA_DIST += version.xml.in EXTRA_DIST += # Files not to distribute # for --rebuild-types in $(SCAN_OPTIONS), e.g. $(DOC_MODULE).types # for --rebuild-sections in $(SCAN_OPTIONS) e.g. $(DOC_MODULE)-sections.txt DISTCLEANFILES = wocky-sections.txt wocky.types # Comment this out if you want your docs-status tested during 'make check' #TESTS = $(GTKDOC_CHECK) telepathy-gabble-0.18.2/lib/ext/wocky/docs/reference/Makefile.in0000644000175000017500000006507512312536107024467 0ustar00smcvsmcv00000000000000# Makefile.in generated by automake 1.14.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2013 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ # -*- mode: makefile -*- #################################### # Everything below here is generic # #################################### VPATH = @srcdir@ am__is_gnu_make = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)' am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ DIST_COMMON = $(top_srcdir)/gtk-doc.make $(srcdir)/Makefile.in \ $(srcdir)/Makefile.am subdir = docs/reference ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/as-compiler-flag.m4 \ $(top_srcdir)/m4/gtk-doc.m4 $(top_srcdir)/m4/libtool.m4 \ $(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \ $(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \ $(top_srcdir)/m4/tp-compiler-flag.m4 \ $(top_srcdir)/m4/tp-compiler-warnings.m4 \ $(top_srcdir)/m4/wocky-gcov.m4 $(top_srcdir)/m4/wocky-lcov.m4 \ $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = SOURCES = DIST_SOURCES = am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ AR = @AR@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CC = @CC@ CCAS = @CCAS@ CCASDEPMODE = @CCASDEPMODE@ CCASFLAGS = @CCASFLAGS@ CCDEPMODE = @CCDEPMODE@ CFLAGS = @CFLAGS@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CXXFLAGS = @CXXFLAGS@ CYGPATH_W = @CYGPATH_W@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DLLTOOL = @DLLTOOL@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ ENABLE_CODING_STYLE_CHECKS = @ENABLE_CODING_STYLE_CHECKS@ ERROR_CFLAGS = @ERROR_CFLAGS@ EXEEXT = @EXEEXT@ FFLAGS = @FFLAGS@ FGREP = @FGREP@ GCOV = @GCOV@ GCOV_CFLAGS = @GCOV_CFLAGS@ GCOV_LIBS = @GCOV_LIBS@ GLIB_CFLAGS = @GLIB_CFLAGS@ GLIB_GENMARSHAL = @GLIB_GENMARSHAL@ GLIB_LIBS = @GLIB_LIBS@ GNUTLS_FOR_STREAM_CIPHERS_CFLAGS = @GNUTLS_FOR_STREAM_CIPHERS_CFLAGS@ GNUTLS_FOR_STREAM_CIPHERS_LIBS = @GNUTLS_FOR_STREAM_CIPHERS_LIBS@ GREP = @GREP@ GTKDOC_CHECK = @GTKDOC_CHECK@ GTKDOC_CHECK_PATH = @GTKDOC_CHECK_PATH@ GTKDOC_DEPS_CFLAGS = @GTKDOC_DEPS_CFLAGS@ GTKDOC_DEPS_LIBS = @GTKDOC_DEPS_LIBS@ GTKDOC_MKPDF = @GTKDOC_MKPDF@ GTKDOC_REBASE = @GTKDOC_REBASE@ HEADER_DIR = @HEADER_DIR@ HTML_DIR = @HTML_DIR@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ LCOV_PATH = @LCOV_PATH@ LD = @LD@ LDFLAGS = @LDFLAGS@ LIBIPHB_CFLAGS = @LIBIPHB_CFLAGS@ LIBIPHB_LIBS = @LIBIPHB_LIBS@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBSASL2_CFLAGS = @LIBSASL2_CFLAGS@ LIBSASL2_LIBS = @LIBSASL2_LIBS@ LIBTOOL = @LIBTOOL@ LIBXML2_CFLAGS = @LIBXML2_CFLAGS@ LIBXML2_LIBS = @LIBXML2_LIBS@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBOBJS = @LTLIBOBJS@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ MOSTLYCLEANFILES = @MOSTLYCLEANFILES@ NM = @NM@ NMEDIT = @NMEDIT@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ RANLIB = @RANLIB@ SED = @SED@ SET_MAKE = @SET_MAKE@ SHARED_SUFFIX = @SHARED_SUFFIX@ SHELL = @SHELL@ SOUP_CFLAGS = @SOUP_CFLAGS@ SOUP_LIBS = @SOUP_LIBS@ SQLITE_CFLAGS = @SQLITE_CFLAGS@ SQLITE_LIBS = @SQLITE_LIBS@ STRIP = @STRIP@ TLS_CFLAGS = @TLS_CFLAGS@ TLS_LIBS = @TLS_LIBS@ VERSION = @VERSION@ WOCKY_CFLAGS = @WOCKY_CFLAGS@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ have_gcov = @have_gcov@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ mandir = @mandir@ mkdir_p = @mkdir_p@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ srcdir = @srcdir@ sysconfdir = @sysconfdir@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ # We require automake 1.6 at least. AUTOMAKE_OPTIONS = 1.6 # This is a blank Makefile.am for using gtk-doc. # Copy this to your project's API docs directory and modify the variables to # suit your project. See the GTK+ Makefiles in gtk+/docs/reference for examples # of using the various options. # The name of the module, e.g. 'glib'. DOC_MODULE = wocky # Uncomment for versioned docs and specify the version of the module, e.g. '2'. #DOC_MODULE_VERSION=2 # The top-level SGML file. You can change this if you want to. DOC_MAIN_SGML_FILE = $(DOC_MODULE)-docs.sgml # Directories containing the source code, relative to $(srcdir). # gtk-doc will search all .c and .h files beneath these paths # for inline comments documenting functions and macros. # e.g. DOC_SOURCE_DIR=../../../gtk ../../../gdk DOC_SOURCE_DIR = $(abs_top_srcdir)/wocky $(abs_top_builddir)/wocky # Extra options to pass to gtkdoc-scangobj. Not normally needed. SCANGOBJ_OPTIONS = # Extra options to supply to gtkdoc-scan. # e.g. SCAN_OPTIONS=--deprecated-guards="GTK_DISABLE_DEPRECATED" # If we're building out of tree, we need to scan wocky/ under the builddir as # well as under the sourcedir (which is specified above with DOC_SOURCE_DIR) to # pick up the enumtypes. # # If we're building in-tree, well, we'd just better hope gtkdoc doesn't mind # scanning stuff twice. SCAN_OPTIONS = --rebuild-types --rebuild-sections \ --source-dir=$(abs_top_builddir)/wocky # Extra options to supply to gtkdoc-mkdb. # e.g. MKDB_OPTIONS=--xml-mode --output-format=xml MKDB_OPTIONS = --sgml-mode --output-format=xml \ --source-dir=$(abs_top_builddir)/wocky # Extra options to supply to gtkdoc-mktmpl # e.g. MKTMPL_OPTIONS=--only-section-tmpl MKTMPL_OPTIONS = # Extra options to supply to gtkdoc-mkhtml MKHTML_OPTIONS = # Extra options to supply to gtkdoc-fixref. Not normally needed. # e.g. FIXXREF_OPTIONS=--extra-dir=../gdk-pixbuf/html --extra-dir=../gdk/html FIXXREF_OPTIONS = # Used for dependencies. The docs will be rebuilt if any of these change. # e.g. HFILE_GLOB=$(top_srcdir)/gtk/*.h # e.g. CFILE_GLOB=$(top_srcdir)/gtk/*.c HFILE_GLOB = $(top_srcdir)/wocky/*.h $(top_builddir)/wocky/*.h CFILE_GLOB = $(top_srcdir)/wocky/*.c $(top_builddir)/wocky/*.c # Header files to ignore when scanning. # e.g. IGNORE_HFILES=gtkdebug.h gtkintl.h IGNORE_HFILES = # Images to copy into HTML directory. # e.g. HTML_IMAGES=$(top_srcdir)/gtk/stock-icons/stock_about_24.png HTML_IMAGES = # Extra SGML files that are included by $(DOC_MAIN_SGML_FILE). # e.g. content_files=running.sgml building.sgml changes-2.0.sgml content_files = # SGML files where gtk-doc abbrevations (#GtkWidget) are expanded # These files must be listed here *and* in content_files # e.g. expand_content_files=running.sgml expand_content_files = # CFLAGS and LDFLAGS for compiling gtkdoc-scangobj with your library. # Only needed if you are using gtkdoc-scangobj to dynamically query widget # signals and properties. # e.g. GTKDOC_CFLAGS=-I$(top_srcdir) -I$(top_builddir) $(GTK_DEBUG_FLAGS) # e.g. GTKDOC_LIBS=$(top_builddir)/gtk/$(gtktargetlib) GTKDOC_CFLAGS = -I$(top_srcdir) -I$(top_builddir) @GLIB_CFLAGS@ @SQLITE_CFLAGS@ GTKDOC_LIBS = @GLIB_LIBS@ @SQLITE_LIBS@ $(top_builddir)/wocky/libwocky.la @GTK_DOC_USE_LIBTOOL_FALSE@GTKDOC_CC = $(CC) $(INCLUDES) $(GTKDOC_DEPS_CFLAGS) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) @GTK_DOC_USE_LIBTOOL_TRUE@GTKDOC_CC = $(LIBTOOL) --tag=CC --mode=compile $(CC) $(INCLUDES) $(GTKDOC_DEPS_CFLAGS) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) @GTK_DOC_USE_LIBTOOL_FALSE@GTKDOC_LD = $(CC) $(GTKDOC_DEPS_LIBS) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) @GTK_DOC_USE_LIBTOOL_TRUE@GTKDOC_LD = $(LIBTOOL) --tag=CC --mode=link $(CC) $(GTKDOC_DEPS_LIBS) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) @GTK_DOC_USE_LIBTOOL_FALSE@GTKDOC_RUN = @GTK_DOC_USE_LIBTOOL_TRUE@GTKDOC_RUN = $(LIBTOOL) --mode=execute # We set GPATH here; this gives us semantics for GNU make # which are more like other make's VPATH, when it comes to # whether a source that is a target of one rule is then # searched for in VPATH/GPATH. # GPATH = $(srcdir) TARGET_DIR = $(HTML_DIR)/$(DOC_MODULE) SETUP_FILES = \ $(content_files) \ $(DOC_MAIN_SGML_FILE) \ $(DOC_MODULE)-sections.txt \ $(DOC_MODULE)-overrides.txt # This includes the standard gtk-doc make rules, copied by gtkdocize. # Other files to distribute # e.g. EXTRA_DIST += version.xml.in EXTRA_DIST = $(HTML_IMAGES) $(SETUP_FILES) DOC_STAMPS = setup-build.stamp scan-build.stamp sgml-build.stamp \ html-build.stamp pdf-build.stamp \ sgml.stamp html.stamp pdf.stamp SCANOBJ_FILES = \ $(DOC_MODULE).args \ $(DOC_MODULE).hierarchy \ $(DOC_MODULE).interfaces \ $(DOC_MODULE).prerequisites \ $(DOC_MODULE).signals REPORT_FILES = \ $(DOC_MODULE)-undocumented.txt \ $(DOC_MODULE)-undeclared.txt \ $(DOC_MODULE)-unused.txt CLEANFILES = $(SCANOBJ_FILES) $(REPORT_FILES) $(DOC_STAMPS) gtkdoc-check.test @GTK_DOC_BUILD_HTML_FALSE@HTML_BUILD_STAMP = @GTK_DOC_BUILD_HTML_TRUE@HTML_BUILD_STAMP = html-build.stamp @GTK_DOC_BUILD_PDF_FALSE@PDF_BUILD_STAMP = @GTK_DOC_BUILD_PDF_TRUE@PDF_BUILD_STAMP = pdf-build.stamp #### setup #### GTK_DOC_V_SETUP = $(GTK_DOC_V_SETUP_$(V)) GTK_DOC_V_SETUP_ = $(GTK_DOC_V_SETUP_$(AM_DEFAULT_VERBOSITY)) GTK_DOC_V_SETUP_0 = @echo " DOC Preparing build"; #### scan #### GTK_DOC_V_SCAN = $(GTK_DOC_V_SCAN_$(V)) GTK_DOC_V_SCAN_ = $(GTK_DOC_V_SCAN_$(AM_DEFAULT_VERBOSITY)) GTK_DOC_V_SCAN_0 = @echo " DOC Scanning header files"; GTK_DOC_V_INTROSPECT = $(GTK_DOC_V_INTROSPECT_$(V)) GTK_DOC_V_INTROSPECT_ = $(GTK_DOC_V_INTROSPECT_$(AM_DEFAULT_VERBOSITY)) GTK_DOC_V_INTROSPECT_0 = @echo " DOC Introspecting gobjects"; #### xml #### GTK_DOC_V_XML = $(GTK_DOC_V_XML_$(V)) GTK_DOC_V_XML_ = $(GTK_DOC_V_XML_$(AM_DEFAULT_VERBOSITY)) GTK_DOC_V_XML_0 = @echo " DOC Building XML"; #### html #### GTK_DOC_V_HTML = $(GTK_DOC_V_HTML_$(V)) GTK_DOC_V_HTML_ = $(GTK_DOC_V_HTML_$(AM_DEFAULT_VERBOSITY)) GTK_DOC_V_HTML_0 = @echo " DOC Building HTML"; GTK_DOC_V_XREF = $(GTK_DOC_V_XREF_$(V)) GTK_DOC_V_XREF_ = $(GTK_DOC_V_XREF_$(AM_DEFAULT_VERBOSITY)) GTK_DOC_V_XREF_0 = @echo " DOC Fixing cross-references"; #### pdf #### GTK_DOC_V_PDF = $(GTK_DOC_V_PDF_$(V)) GTK_DOC_V_PDF_ = $(GTK_DOC_V_PDF_$(AM_DEFAULT_VERBOSITY)) GTK_DOC_V_PDF_0 = @echo " DOC Building PDF"; # Files not to distribute # for --rebuild-types in $(SCAN_OPTIONS), e.g. $(DOC_MODULE).types # for --rebuild-sections in $(SCAN_OPTIONS) e.g. $(DOC_MODULE)-sections.txt DISTCLEANFILES = wocky-sections.txt wocky.types all: all-am .SUFFIXES: $(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(top_srcdir)/gtk-doc.make $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu docs/reference/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --gnu docs/reference/Makefile .PRECIOUS: Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ esac; $(top_srcdir)/gtk-doc.make: $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs tags TAGS: ctags CTAGS: cscope cscopelist: distdir: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done $(MAKE) $(AM_MAKEFLAGS) \ top_distdir="$(top_distdir)" distdir="$(distdir)" \ dist-hook check-am: all-am check: check-am @ENABLE_GTK_DOC_FALSE@all-local: all-am: Makefile all-local installdirs: install: install-am install-exec: install-exec-am install-data: install-data-am uninstall: uninstall-am install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-am install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: -test -z "$(MOSTLYCLEANFILES)" || rm -f $(MOSTLYCLEANFILES) clean-generic: -test -z "$(CLEANFILES)" || rm -f $(CLEANFILES) distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) -test -z "$(DISTCLEANFILES)" || rm -f $(DISTCLEANFILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." clean: clean-am clean-am: clean-generic clean-libtool clean-local mostlyclean-am distclean: distclean-am -rm -f Makefile distclean-am: clean-am distclean-generic distclean-local dvi: dvi-am dvi-am: html: html-am html-am: info: info-am info-am: install-data-am: install-data-local install-dvi: install-dvi-am install-dvi-am: install-exec-am: install-html: install-html-am install-html-am: install-info: install-info-am install-info-am: install-man: install-pdf: install-pdf-am install-pdf-am: install-ps: install-ps-am install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-am -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic \ maintainer-clean-local mostlyclean: mostlyclean-am mostlyclean-am: mostlyclean-generic mostlyclean-libtool pdf: pdf-am pdf-am: ps: ps-am ps-am: uninstall-am: uninstall-local .MAKE: install-am install-strip .PHONY: all all-am all-local check check-am clean clean-generic \ clean-libtool clean-local cscopelist-am ctags-am dist-hook \ distclean distclean-generic distclean-libtool distclean-local \ distdir dvi dvi-am html html-am info info-am install \ install-am install-data install-data-am install-data-local \ install-dvi install-dvi-am install-exec install-exec-am \ install-html install-html-am install-info install-info-am \ install-man install-pdf install-pdf-am install-ps \ install-ps-am install-strip installcheck installcheck-am \ installdirs maintainer-clean maintainer-clean-generic \ maintainer-clean-local mostlyclean mostlyclean-generic \ mostlyclean-libtool pdf pdf-am ps ps-am tags-am uninstall \ uninstall-am uninstall-local gtkdoc-check.test: Makefile $(AM_V_GEN)echo "#!/bin/sh -e" > $@; \ echo "$(GTKDOC_CHECK_PATH) || exit 1" >> $@; \ chmod +x $@ all-gtk-doc: $(HTML_BUILD_STAMP) $(PDF_BUILD_STAMP) .PHONY: all-gtk-doc @ENABLE_GTK_DOC_TRUE@all-local: all-gtk-doc docs: $(HTML_BUILD_STAMP) $(PDF_BUILD_STAMP) $(REPORT_FILES): sgml-build.stamp setup-build.stamp: -$(GTK_DOC_V_SETUP)if test "$(abs_srcdir)" != "$(abs_builddir)" ; then \ files=`echo $(SETUP_FILES) $(expand_content_files) $(DOC_MODULE).types`; \ if test "x$$files" != "x" ; then \ for file in $$files ; do \ destdir=`dirname $(abs_builddir)/$$file`; \ test -d "$$destdir" || mkdir -p "$$destdir"; \ test -f $(abs_srcdir)/$$file && \ cp -pf $(abs_srcdir)/$$file $(abs_builddir)/$$file || true; \ done; \ fi; \ fi $(AM_V_at)touch setup-build.stamp scan-build.stamp: setup-build.stamp $(HFILE_GLOB) $(CFILE_GLOB) $(GTK_DOC_V_SCAN)_source_dir='' ; \ for i in $(DOC_SOURCE_DIR) ; do \ _source_dir="$${_source_dir} --source-dir=$$i" ; \ done ; \ gtkdoc-scan --module=$(DOC_MODULE) --ignore-headers="$(IGNORE_HFILES)" $${_source_dir} $(SCAN_OPTIONS) $(EXTRA_HFILES) $(GTK_DOC_V_INTROSPECT)if grep -l '^..*$$' $(DOC_MODULE).types > /dev/null 2>&1 ; then \ scanobj_options=""; \ gtkdoc-scangobj 2>&1 --help | grep >/dev/null "\-\-verbose"; \ if test "$(?)" = "0"; then \ if test "x$(V)" = "x1"; then \ scanobj_options="--verbose"; \ fi; \ fi; \ CC="$(GTKDOC_CC)" LD="$(GTKDOC_LD)" RUN="$(GTKDOC_RUN)" CFLAGS="$(GTKDOC_CFLAGS) $(CFLAGS)" LDFLAGS="$(GTKDOC_LIBS) $(LDFLAGS)" \ gtkdoc-scangobj $(SCANGOBJ_OPTIONS) $$scanobj_options --module=$(DOC_MODULE); \ else \ for i in $(SCANOBJ_FILES) ; do \ test -f $$i || touch $$i ; \ done \ fi $(AM_V_at)touch scan-build.stamp $(DOC_MODULE)-decl.txt $(SCANOBJ_FILES) $(DOC_MODULE)-sections.txt $(DOC_MODULE)-overrides.txt: scan-build.stamp @true sgml-build.stamp: setup-build.stamp $(DOC_MODULE)-decl.txt $(SCANOBJ_FILES) $(DOC_MODULE)-sections.txt $(DOC_MODULE)-overrides.txt $(expand_content_files) $(GTK_DOC_V_XML)_source_dir='' ; \ for i in $(DOC_SOURCE_DIR) ; do \ _source_dir="$${_source_dir} --source-dir=$$i" ; \ done ; \ gtkdoc-mkdb --module=$(DOC_MODULE) --output-format=xml --expand-content-files="$(expand_content_files)" --main-sgml-file=$(DOC_MAIN_SGML_FILE) $${_source_dir} $(MKDB_OPTIONS) $(AM_V_at)touch sgml-build.stamp sgml.stamp: sgml-build.stamp @true html-build.stamp: sgml.stamp $(DOC_MAIN_SGML_FILE) $(content_files) $(GTK_DOC_V_HTML)rm -rf html && mkdir html && \ mkhtml_options=""; \ gtkdoc-mkhtml 2>&1 --help | grep >/dev/null "\-\-verbose"; \ if test "$(?)" = "0"; then \ if test "x$(V)" = "x1"; then \ mkhtml_options="$$mkhtml_options --verbose"; \ fi; \ fi; \ gtkdoc-mkhtml 2>&1 --help | grep >/dev/null "\-\-path"; \ if test "$(?)" = "0"; then \ mkhtml_options="$$mkhtml_options --path=\"$(abs_srcdir)\""; \ fi; \ cd html && gtkdoc-mkhtml $$mkhtml_options $(MKHTML_OPTIONS) $(DOC_MODULE) ../$(DOC_MAIN_SGML_FILE) -@test "x$(HTML_IMAGES)" = "x" || \ for file in $(HTML_IMAGES) ; do \ if test -f $(abs_srcdir)/$$file ; then \ cp $(abs_srcdir)/$$file $(abs_builddir)/html; \ fi; \ if test -f $(abs_builddir)/$$file ; then \ cp $(abs_builddir)/$$file $(abs_builddir)/html; \ fi; \ done; $(GTK_DOC_V_XREF)gtkdoc-fixxref --module=$(DOC_MODULE) --module-dir=html --html-dir=$(HTML_DIR) $(FIXXREF_OPTIONS) $(AM_V_at)touch html-build.stamp pdf-build.stamp: sgml.stamp $(DOC_MAIN_SGML_FILE) $(content_files) $(GTK_DOC_V_PDF)rm -f $(DOC_MODULE).pdf && \ mkpdf_options=""; \ gtkdoc-mkpdf 2>&1 --help | grep >/dev/null "\-\-verbose"; \ if test "$(?)" = "0"; then \ if test "x$(V)" = "x1"; then \ mkpdf_options="$$mkpdf_options --verbose"; \ fi; \ fi; \ if test "x$(HTML_IMAGES)" != "x"; then \ for img in $(HTML_IMAGES); do \ part=`dirname $$img`; \ echo $$mkpdf_options | grep >/dev/null "\-\-imgdir=$$part "; \ if test $$? != 0; then \ mkpdf_options="$$mkpdf_options --imgdir=$$part"; \ fi; \ done; \ fi; \ gtkdoc-mkpdf --path="$(abs_srcdir)" $$mkpdf_options $(DOC_MODULE) $(DOC_MAIN_SGML_FILE) $(MKPDF_OPTIONS) $(AM_V_at)touch pdf-build.stamp ############## clean-local: @rm -f *~ *.bak @rm -rf .libs @if echo $(SCAN_OPTIONS) | grep -q "\-\-rebuild-types" ; then \ rm -f $(DOC_MODULE).types; \ fi distclean-local: @rm -rf xml html $(REPORT_FILES) $(DOC_MODULE).pdf \ $(DOC_MODULE)-decl-list.txt $(DOC_MODULE)-decl.txt @if test "$(abs_srcdir)" != "$(abs_builddir)" ; then \ rm -f $(SETUP_FILES) $(expand_content_files) $(DOC_MODULE).types; \ fi maintainer-clean-local: @rm -rf xml html install-data-local: @installfiles=`echo $(builddir)/html/*`; \ if test "$$installfiles" = '$(builddir)/html/*'; \ then echo 1>&2 'Nothing to install' ; \ else \ if test -n "$(DOC_MODULE_VERSION)"; then \ installdir="$(DESTDIR)$(TARGET_DIR)-$(DOC_MODULE_VERSION)"; \ else \ installdir="$(DESTDIR)$(TARGET_DIR)"; \ fi; \ $(mkinstalldirs) $${installdir} ; \ for i in $$installfiles; do \ echo ' $(INSTALL_DATA) '$$i ; \ $(INSTALL_DATA) $$i $${installdir}; \ done; \ if test -n "$(DOC_MODULE_VERSION)"; then \ mv -f $${installdir}/$(DOC_MODULE).devhelp2 \ $${installdir}/$(DOC_MODULE)-$(DOC_MODULE_VERSION).devhelp2; \ fi; \ $(GTKDOC_REBASE) --relative --dest-dir=$(DESTDIR) --html-dir=$${installdir}; \ fi uninstall-local: @if test -n "$(DOC_MODULE_VERSION)"; then \ installdir="$(DESTDIR)$(TARGET_DIR)-$(DOC_MODULE_VERSION)"; \ else \ installdir="$(DESTDIR)$(TARGET_DIR)"; \ fi; \ rm -rf $${installdir} # # Require gtk-doc when making dist # @HAVE_GTK_DOC_TRUE@dist-check-gtkdoc: docs @HAVE_GTK_DOC_FALSE@dist-check-gtkdoc: @HAVE_GTK_DOC_FALSE@ @echo "*** gtk-doc is needed to run 'make dist'. ***" @HAVE_GTK_DOC_FALSE@ @echo "*** gtk-doc was not found when 'configure' ran. ***" @HAVE_GTK_DOC_FALSE@ @echo "*** please install gtk-doc and rerun 'configure'. ***" @HAVE_GTK_DOC_FALSE@ @false dist-hook: dist-check-gtkdoc all-gtk-doc dist-hook-local @mkdir $(distdir)/html @cp ./html/* $(distdir)/html @-cp ./$(DOC_MODULE).pdf $(distdir)/ @-cp ./$(DOC_MODULE).types $(distdir)/ @-cp ./$(DOC_MODULE)-sections.txt $(distdir)/ @cd $(distdir) && rm -f $(DISTCLEANFILES) @$(GTKDOC_REBASE) --online --relative --html-dir=$(distdir)/html .PHONY : dist-hook-local docs # Comment this out if you want your docs-status tested during 'make check' #TESTS = $(GTKDOC_CHECK) # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: telepathy-gabble-0.18.2/lib/ext/wocky/docs/Makefile.am0000644000175000017500000000002412200204546022472 0ustar00smcvsmcv00000000000000SUBDIRS = reference telepathy-gabble-0.18.2/lib/ext/wocky/docs/Makefile.in0000644000175000017500000004632412312536107022525 0ustar00smcvsmcv00000000000000# Makefile.in generated by automake 1.14.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2013 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ VPATH = @srcdir@ am__is_gnu_make = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)' am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ subdir = docs DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/as-compiler-flag.m4 \ $(top_srcdir)/m4/gtk-doc.m4 $(top_srcdir)/m4/libtool.m4 \ $(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \ $(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \ $(top_srcdir)/m4/tp-compiler-flag.m4 \ $(top_srcdir)/m4/tp-compiler-warnings.m4 \ $(top_srcdir)/m4/wocky-gcov.m4 $(top_srcdir)/m4/wocky-lcov.m4 \ $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = SOURCES = DIST_SOURCES = RECURSIVE_TARGETS = all-recursive check-recursive cscopelist-recursive \ ctags-recursive dvi-recursive html-recursive info-recursive \ install-data-recursive install-dvi-recursive \ install-exec-recursive install-html-recursive \ install-info-recursive install-pdf-recursive \ install-ps-recursive install-recursive installcheck-recursive \ installdirs-recursive pdf-recursive ps-recursive \ tags-recursive uninstall-recursive am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive \ distclean-recursive maintainer-clean-recursive am__recursive_targets = \ $(RECURSIVE_TARGETS) \ $(RECURSIVE_CLEAN_TARGETS) \ $(am__extra_recursive_targets) AM_RECURSIVE_TARGETS = $(am__recursive_targets:-recursive=) TAGS CTAGS \ distdir am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) # Read a list of newline-separated strings from the standard input, # and print each of them once, without duplicates. Input order is # *not* preserved. am__uniquify_input = $(AWK) '\ BEGIN { nonempty = 0; } \ { items[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in items) print i; }; } \ ' # Make sure the list of sources is unique. This is necessary because, # e.g., the same source file might be shared among _SOURCES variables # for different programs/libraries. am__define_uniq_tagged_files = \ list='$(am__tagged_files)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | $(am__uniquify_input)` ETAGS = etags CTAGS = ctags DIST_SUBDIRS = $(SUBDIRS) DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) am__relativize = \ dir0=`pwd`; \ sed_first='s,^\([^/]*\)/.*$$,\1,'; \ sed_rest='s,^[^/]*/*,,'; \ sed_last='s,^.*/\([^/]*\)$$,\1,'; \ sed_butlast='s,/*[^/]*$$,,'; \ while test -n "$$dir1"; do \ first=`echo "$$dir1" | sed -e "$$sed_first"`; \ if test "$$first" != "."; then \ if test "$$first" = ".."; then \ dir2=`echo "$$dir0" | sed -e "$$sed_last"`/"$$dir2"; \ dir0=`echo "$$dir0" | sed -e "$$sed_butlast"`; \ else \ first2=`echo "$$dir2" | sed -e "$$sed_first"`; \ if test "$$first2" = "$$first"; then \ dir2=`echo "$$dir2" | sed -e "$$sed_rest"`; \ else \ dir2="../$$dir2"; \ fi; \ dir0="$$dir0"/"$$first"; \ fi; \ fi; \ dir1=`echo "$$dir1" | sed -e "$$sed_rest"`; \ done; \ reldir="$$dir2" ACLOCAL = @ACLOCAL@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ AR = @AR@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CC = @CC@ CCAS = @CCAS@ CCASDEPMODE = @CCASDEPMODE@ CCASFLAGS = @CCASFLAGS@ CCDEPMODE = @CCDEPMODE@ CFLAGS = @CFLAGS@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CXXFLAGS = @CXXFLAGS@ CYGPATH_W = @CYGPATH_W@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DLLTOOL = @DLLTOOL@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ ENABLE_CODING_STYLE_CHECKS = @ENABLE_CODING_STYLE_CHECKS@ ERROR_CFLAGS = @ERROR_CFLAGS@ EXEEXT = @EXEEXT@ FFLAGS = @FFLAGS@ FGREP = @FGREP@ GCOV = @GCOV@ GCOV_CFLAGS = @GCOV_CFLAGS@ GCOV_LIBS = @GCOV_LIBS@ GLIB_CFLAGS = @GLIB_CFLAGS@ GLIB_GENMARSHAL = @GLIB_GENMARSHAL@ GLIB_LIBS = @GLIB_LIBS@ GNUTLS_FOR_STREAM_CIPHERS_CFLAGS = @GNUTLS_FOR_STREAM_CIPHERS_CFLAGS@ GNUTLS_FOR_STREAM_CIPHERS_LIBS = @GNUTLS_FOR_STREAM_CIPHERS_LIBS@ GREP = @GREP@ GTKDOC_CHECK = @GTKDOC_CHECK@ GTKDOC_CHECK_PATH = @GTKDOC_CHECK_PATH@ GTKDOC_DEPS_CFLAGS = @GTKDOC_DEPS_CFLAGS@ GTKDOC_DEPS_LIBS = @GTKDOC_DEPS_LIBS@ GTKDOC_MKPDF = @GTKDOC_MKPDF@ GTKDOC_REBASE = @GTKDOC_REBASE@ HEADER_DIR = @HEADER_DIR@ HTML_DIR = @HTML_DIR@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ LCOV_PATH = @LCOV_PATH@ LD = @LD@ LDFLAGS = @LDFLAGS@ LIBIPHB_CFLAGS = @LIBIPHB_CFLAGS@ LIBIPHB_LIBS = @LIBIPHB_LIBS@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBSASL2_CFLAGS = @LIBSASL2_CFLAGS@ LIBSASL2_LIBS = @LIBSASL2_LIBS@ LIBTOOL = @LIBTOOL@ LIBXML2_CFLAGS = @LIBXML2_CFLAGS@ LIBXML2_LIBS = @LIBXML2_LIBS@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBOBJS = @LTLIBOBJS@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ MOSTLYCLEANFILES = @MOSTLYCLEANFILES@ NM = @NM@ NMEDIT = @NMEDIT@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ RANLIB = @RANLIB@ SED = @SED@ SET_MAKE = @SET_MAKE@ SHARED_SUFFIX = @SHARED_SUFFIX@ SHELL = @SHELL@ SOUP_CFLAGS = @SOUP_CFLAGS@ SOUP_LIBS = @SOUP_LIBS@ SQLITE_CFLAGS = @SQLITE_CFLAGS@ SQLITE_LIBS = @SQLITE_LIBS@ STRIP = @STRIP@ TLS_CFLAGS = @TLS_CFLAGS@ TLS_LIBS = @TLS_LIBS@ VERSION = @VERSION@ WOCKY_CFLAGS = @WOCKY_CFLAGS@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ have_gcov = @have_gcov@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ mandir = @mandir@ mkdir_p = @mkdir_p@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ srcdir = @srcdir@ sysconfdir = @sysconfdir@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ SUBDIRS = reference all: all-recursive .SUFFIXES: $(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu docs/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --gnu docs/Makefile .PRECIOUS: Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs # This directory's subdirectories are mostly independent; you can cd # into them and run 'make' without going through this Makefile. # To change the values of 'make' variables: instead of editing Makefiles, # (1) if the variable is set in 'config.status', edit 'config.status' # (which will cause the Makefiles to be regenerated when you run 'make'); # (2) otherwise, pass the desired values on the 'make' command line. $(am__recursive_targets): @fail=; \ if $(am__make_keepgoing); then \ failcom='fail=yes'; \ else \ failcom='exit 1'; \ fi; \ dot_seen=no; \ target=`echo $@ | sed s/-recursive//`; \ case "$@" in \ distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \ *) list='$(SUBDIRS)' ;; \ esac; \ for subdir in $$list; do \ echo "Making $$target in $$subdir"; \ if test "$$subdir" = "."; then \ dot_seen=yes; \ local_target="$$target-am"; \ else \ local_target="$$target"; \ fi; \ ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ || eval $$failcom; \ done; \ if test "$$dot_seen" = "no"; then \ $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \ fi; test -z "$$fail" ID: $(am__tagged_files) $(am__define_uniq_tagged_files); mkid -fID $$unique tags: tags-recursive TAGS: tags tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) set x; \ here=`pwd`; \ if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \ include_option=--etags-include; \ empty_fix=.; \ else \ include_option=--include; \ empty_fix=; \ fi; \ list='$(SUBDIRS)'; for subdir in $$list; do \ if test "$$subdir" = .; then :; else \ test ! -f $$subdir/TAGS || \ set "$$@" "$$include_option=$$here/$$subdir/TAGS"; \ fi; \ done; \ $(am__define_uniq_tagged_files); \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ if test $$# -gt 0; then \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ "$$@" $$unique; \ else \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ $$unique; \ fi; \ fi ctags: ctags-recursive CTAGS: ctags ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) $(am__define_uniq_tagged_files); \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" cscopelist: cscopelist-recursive cscopelist-am: $(am__tagged_files) list='$(am__tagged_files)'; \ case "$(srcdir)" in \ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ *) sdir=$(subdir)/$(srcdir) ;; \ esac; \ for i in $$list; do \ if test -f "$$i"; then \ echo "$(subdir)/$$i"; \ else \ echo "$$sdir/$$i"; \ fi; \ done >> $(top_builddir)/cscope.files distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags distdir: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done @list='$(DIST_SUBDIRS)'; for subdir in $$list; do \ if test "$$subdir" = .; then :; else \ $(am__make_dryrun) \ || test -d "$(distdir)/$$subdir" \ || $(MKDIR_P) "$(distdir)/$$subdir" \ || exit 1; \ dir1=$$subdir; dir2="$(distdir)/$$subdir"; \ $(am__relativize); \ new_distdir=$$reldir; \ dir1=$$subdir; dir2="$(top_distdir)"; \ $(am__relativize); \ new_top_distdir=$$reldir; \ echo " (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) top_distdir="$$new_top_distdir" distdir="$$new_distdir" \\"; \ echo " am__remove_distdir=: am__skip_length_check=: am__skip_mode_fix=: distdir)"; \ ($(am__cd) $$subdir && \ $(MAKE) $(AM_MAKEFLAGS) \ top_distdir="$$new_top_distdir" \ distdir="$$new_distdir" \ am__remove_distdir=: \ am__skip_length_check=: \ am__skip_mode_fix=: \ distdir) \ || exit 1; \ fi; \ done check-am: all-am check: check-recursive all-am: Makefile installdirs: installdirs-recursive installdirs-am: install: install-recursive install-exec: install-exec-recursive install-data: install-data-recursive uninstall: uninstall-recursive install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-recursive install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: -test -z "$(MOSTLYCLEANFILES)" || rm -f $(MOSTLYCLEANFILES) clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." clean: clean-recursive clean-am: clean-generic clean-libtool mostlyclean-am distclean: distclean-recursive -rm -f Makefile distclean-am: clean-am distclean-generic distclean-tags dvi: dvi-recursive dvi-am: html: html-recursive html-am: info: info-recursive info-am: install-data-am: install-dvi: install-dvi-recursive install-dvi-am: install-exec-am: install-html: install-html-recursive install-html-am: install-info: install-info-recursive install-info-am: install-man: install-pdf: install-pdf-recursive install-pdf-am: install-ps: install-ps-recursive install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-recursive -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-recursive mostlyclean-am: mostlyclean-generic mostlyclean-libtool pdf: pdf-recursive pdf-am: ps: ps-recursive ps-am: uninstall-am: .MAKE: $(am__recursive_targets) install-am install-strip .PHONY: $(am__recursive_targets) CTAGS GTAGS TAGS all all-am check \ check-am clean clean-generic clean-libtool cscopelist-am ctags \ ctags-am distclean distclean-generic distclean-libtool \ distclean-tags distdir dvi dvi-am html html-am info info-am \ install install-am install-data install-data-am install-dvi \ install-dvi-am install-exec install-exec-am install-html \ install-html-am install-info install-info-am install-man \ install-pdf install-pdf-am install-ps install-ps-am \ install-strip installcheck installcheck-am installdirs \ installdirs-am maintainer-clean maintainer-clean-generic \ mostlyclean mostlyclean-generic mostlyclean-libtool pdf pdf-am \ ps ps-am tags tags-am uninstall uninstall-am # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: telepathy-gabble-0.18.2/lib/ext/wocky/tests/0000755000175000017500000000000012312537050020657 5ustar00smcvsmcv00000000000000telepathy-gabble-0.18.2/lib/ext/wocky/tests/summarise-tests.py0000755000175000017500000000241112200204546024374 0ustar00smcvsmcv00000000000000#!/usr/bin/env python import sys import xml.dom.minidom as minidom def process(testbinary): cases = 0 passes = 0 failures = [] for e in testbinary.childNodes: if e.nodeType != e.ELEMENT_NODE or e.localName != 'testcase': continue if e.hasAttribute('skipped'): continue path = e.getAttribute("path") cases = cases + 1 status = e.getElementsByTagName('status')[0] if status.getAttribute('result') != 'success': failures += [ path ] return (cases, failures) doc = minidom.parse(sys.argv[1]) okay = True tests = {} for e in doc.childNodes[0].childNodes: if e.nodeType != e.ELEMENT_NODE or e.localName != 'testbinary': continue path = e.getAttribute("path") cases, failures = process(e) ocases, ofailures = tests.get (path, [ 0, []]) tests[path] = [ ocases + cases, ofailures + failures ] for name, [cases, failures] in tests.iteritems(): if failures == []: result = 'PASS' else: result = 'FAIL' okay = False print "%s: %s: %u/%u tests passed" % (result, name, cases - len (failures), cases) for f in failures: print "\tFailure: %s" % f if not okay: print "Disaster! Calamity!" sys.exit(1) telepathy-gabble-0.18.2/lib/ext/wocky/tests/connector-test-plan.txt0000644000175000017500000001201612200204546025314 0ustar00smcvsmcv00000000000000Tests: Syntactic validity: Invalid JID (empty) → Bad JID error Invalid JID (no domain) → Bad JID error ============================================================================ Connection details: DOMAIN = latter part of JID HOST = specific host provided by user SRV = host + port from SRV record SRV │ Host │ Port │ Expected Attempt: ────┼──────┼──────┼────────────────────────────── 0 │ 0 │ 0 │ connect to DOMAIN + 5222 0 │ 0 │ 1 │ connect to DOMAIN + port 0 │ 1 │ 0 │ connect to HOST + 5222 0 │ 1 │ 1 │ connect to specified HOST+PORT 0 │ ☠ │ 0 │ duff host. die. 0 │ ☠ │ 1 │ duff host. die. 1 │ 0 │ 0 │ connect to SRV 1 │ 0 │ 1 │ connect to DOMAIN + PORT 1 │ 1 │ 0 │ connect to HOST + 5222 1 │ 1 │ 1 │ connect to HOST + PORT 1 │ ☠ │ 0 │ duff host. die. 1 │ ☠ │ 1 │ duff host. die. ☠ │ 0 │ 0 │ duff SRV. die. ☠ │ 0 │ 1 │ connect to DOMAIN + PORT ☠ │ 1 │ 0 │ connect to HOST + 5222 ☠ │ 1 │ 1 │ connect to HOST + port ☠ │ ☠ │ 0 │ duff HOST. die. ☠ │ ☠ │ 1 │ duff HOST. die. ============================================================================ Feature Requirements/Support tests: +TLS = TLS provided by server -TLS = TLS not provided INSEC AUTH = Insecure auth channels permitted TLS PLAIN = Insecure auth over TLS permitted DIGEST = + → digest auth available; - → only PLAIN available Tests flagged with * need to be repeated 4 times, with good auth details, a bad password, a bad user and with both wrong. The rest should fail before we get that far. ────┬────────────┬────────────┬─────────────────┬────────────────────────┐ TLS │ INSEC AUTH │ TLS PLAIN │ Server Features │ Attempted Action(s) │ 0 │ 0 │ x │ -TLS-DIGEST │ auth failure │ 0 │ 0 │ x │ -TLS+DIGEST │ digest auth * │ 0 │ 1 │ x │ -TLS-DIGEST │ plain auth * │ 0 │ 1 │ x │ -TLS+DIGEST │ digest auth * │ ────┼────────────┼────────────┼─────────────────┼────────────────────────┤ 1 │ 0 │ 0 │ -TLS │ TLS neg failure │ 1 │ 0 │ 1 │ -TLS │ TLS neg failure │ 1 │ 1 │ 0 │ -TLS │ TLS neg failure │ 1 │ 1 │ 1 │ -TLS │ TLS neg failure │ ────┼────────────┼────────────┼─────────────────┼────────────────────────┤ 0 │ 0 │ 0 │ +TLS-DIGEST │ TLS auth failure │ 0 │ 0 │ 1 │ +TLS-DIGEST │ TLS plain auth * │ 0 │ 1 │ 0 │ +TLS-DIGEST │ TLS plain auth * │ 0 │ 1 │ 1 │ +TLS-DIGEST │ TLS plain auth * │ 1 │ 0 │ 0 │ +TLS-DIGEST │ TLS auth failure │ 1 │ 0 │ 1 │ +TLS-DIGEST │ TLS plain auth * │ 1 │ 1 │ 0 │ +TLS-DIGEST │ TLS plain auth * │ 1 │ 1 │ 1 │ +TLS-DIGEST │ TLS plain auth * │ ────┼────────────┼────────────┼─────────────────┼────────────────────────┤ 0 │ 0 │ 0 │ +TLS+DIGEST │ TLS digest auth * │ 0 │ 0 │ 1 │ +TLS+DIGEST │ TLS digest auth * │ 0 │ 1 │ 0 │ +TLS+DIGEST │ TLS digest auth * │ 0 │ 1 │ 1 │ +TLS+DIGEST │ TLS digest auth * │ 1 │ 0 │ 0 │ +TLS+DIGEST │ TLS digest auth * │ 1 │ 0 │ 1 │ +TLS+DIGEST │ TLS digest auth * │ 1 │ 1 │ 0 │ +TLS+DIGEST │ TLS digest auth * │ 1 │ 1 │ 0 │ +TLS+DIGEST │ TLS digest auth * │ ────┴────────────┴────────────┴─────────────────┴────────────────────────┘ telepathy-gabble-0.18.2/lib/ext/wocky/tests/tp-glib.supp0000644000175000017500000001020112200204546023115 0ustar00smcvsmcv00000000000000# Valgrind error suppression file # ============================= libc ================================== { ld.so initialization + selinux Memcheck:Leak ... fun:_dl_init obj:/lib/ld-*.so } { dlopen initialization, triggered by handle-leak-debug code Memcheck:Leak ... fun:__libc_dlopen_mode fun:init fun:backtrace fun:handle_leak_debug_bt fun:dynamic_ensure_handle fun:tp_handle_ensure } # ============================= GLib ================================== { g_set_prgname copies its argument Memcheck:Leak ... fun:g_set_prgname } { one g_get_charset per child^Wprocess Memcheck:Leak ... fun:g_get_charset } { GQuarks can't be freed Memcheck:Leak ... fun:g_quark_from_static_string } { GQuarks can't be freed Memcheck:Leak ... fun:g_quark_from_string } { interned strings can't be freed Memcheck:Leak ... fun:g_intern_string } { interned strings can't be freed Memcheck:Leak ... fun:g_intern_static_string } { shared global default g_main_context Memcheck:Leak ... fun:g_main_context_new fun:g_main_context_default } { GTest initialization Memcheck:Leak ... fun:g_test_init fun:main } { GTest admin Memcheck:Leak ... fun:g_test_add_vtable } { GTest pseudorandomness Memcheck:Leak ... fun:g_rand_new_with_seed_array fun:test_run_seed ... fun:g_test_run } { GSLice initialization Memcheck:Leak ... fun:g_malloc0 fun:g_slice_init_nomessage fun:g_slice_alloc } # ============================= GObject =============================== { g_type_init Memcheck:Leak ... fun:g_type_init } { g_type_register_static Memcheck:Leak ... fun:g_type_register_static } # ============================= dbus-glib ============================= { dbus-glib, https://bugs.freedesktop.org/show_bug.cgi?id=14125 Memcheck:Addr4 fun:g_hash_table_foreach obj:/usr/lib/libdbus-glib-1.so.2.1.0 fun:g_object_run_dispose } { registering marshallers is permanent Memcheck:Leak ... fun:dbus_g_object_register_marshaller_array fun:dbus_g_object_register_marshaller } { dbus-glib specialized GTypes are permanent Memcheck:Leak ... fun:dbus_g_type_specialized_init } { libdbus shared connection Memcheck:Leak ... fun:dbus_g_bus_get } { dbus-gobject registrations aren't freed unless we fall off the bus Memcheck:Leak ... fun:g_slist_append fun:dbus_g_connection_register_g_object } { DBusGProxy slots aren't freed unless we fall off the bus Memcheck:Leak ... fun:dbus_connection_allocate_data_slot ... fun:dbus_g_proxy_constructor } { error registrations are for life, not just for Christmas Memcheck:Leak ... fun:dbus_g_error_domain_register } # ============================= telepathy-glib ======================== { tp_dbus_daemon_constructor @daemons once per DBusConnection Memcheck:Leak ... fun:g_slice_alloc fun:tp_dbus_daemon_constructor } { tp_proxy_subclass_add_error_mapping refs the enum Memcheck:Leak ... fun:g_type_class_ref fun:tp_proxy_subclass_add_error_mapping } { tp_proxy_or_subclass_hook_on_interface_add never frees its list Memcheck:Leak ... fun:tp_proxy_or_subclass_hook_on_interface_add } { tp_dbus_daemon_constructor filter not freed til we fall off the bus Memcheck:Leak ... fun:dbus_connection_add_filter fun:tp_dbus_daemon_constructor } # ============================= unclassified ========================== { creating param specs in tp_proxy_class_intern_init Memcheck:Leak fun:memalign fun:posix_memalign fun:slab_allocator_alloc_chunk fun:g_slice_alloc fun:g_slice_alloc0 fun:g_type_create_instance fun:g_param_spec_internal fun:g_param_spec_string } { ld.so initialization on glibc 2.9 Memcheck:Cond fun:_dl_relocate_object fun:dl_main fun:_dl_sysdep_start fun:_dl_start obj:/lib/ld-2.9.so } { ld.so initialization on glibc 2.9 Memcheck:Cond fun:strlen fun:_dl_init_paths fun:dl_main fun:_dl_sysdep_start fun:_dl_start obj:/lib/ld-2.9.so } telepathy-gabble-0.18.2/lib/ext/wocky/tests/gabble.supp0000644000175000017500000000702212200204546023002 0ustar00smcvsmcv00000000000000# Gabble leaks { we leak one default resource hash per process Memcheck:Leak ... fun:g_compute_checksum_for_data fun:sha1_hex fun:gabble_connection_constructed } { Test resolver leaks the records that were added Memcheck:Leak ... fun:test_resolver_add_A } # Glib type registration one-time leaks { g_type_init_with_debug_flags leaks one-time per registered type Memcheck:Leak ... fun:g_type_init_with_debug_flags } { g_type_register_fundamental, same story Memcheck:Leak ... fun:g_type_register_fundamental } { Various mixins set type qdata, types stay alive Memcheck:Leak ... fun:g_type_set_qdata } { Information about static interface lives forever Memcheck:Leak ... fun:g_type_add_interface_static } { Type prerequisites Memcheck:Leak ... fun:g_type_interface_add_prerequisite } { Various memory is never freed when first initializing a type class Memcheck:Leak ... fun:g_type_class_ref } # Glib mainloop one time leaks { Default main context stays alive an keeps an array around for pending fds Memcheck:Leak fun:malloc fun:g_malloc fun:g_main_context_iterate } { Default main context stays alive an keeps an array for pending dispatches Memcheck:Leak ... fun:g_ptr_array_add fun:g_main_context_check fun:g_main_context_iterate } { Global hashtable of signal handlers, memory allocated when resized Memcheck:Leak fun:calloc fun:g_malloc0 fun:g_hash_table_remove_internal fun:g_signal_handlers_destroy } { g_main_loop_run constructs a GStaticPrivate GMainDispatch Memcheck:Leak ... fun:get_dispatch } # glib one-time initialisaton of various bits { Random seed initialization Memcheck:Leak ... fun:g_rand_new fun:g_random_int_range } { GDataSet has a global hashtable that leaks per process Memcheck:Leak ... fun:g_data_initialize } { GIO has a static mapping to various connection factories Memcheck:Leak ... fun:g_socket_connection_factory_register_type } { GLib has a static copy of the userdir Memcheck:Leak ... fun:g_init_user_config_dir } { Caching of the tmp location Memcheck:Leak ... fun:g_get_any_init_do } { thread init causes g_get_language_name to cache stuff Memcheck:Leak ... fun:g_get_language_names } { Thread initialisation Memcheck:Leak ... fun:g_private_new_posix_impl } { Thread initialisation Memcheck:Leak ... fun:g_thread_init_glib } # telepathy-glib leaks the dbus connection, which causes dbus to have some # stuff around on exit... { the subtree that stores objects is reallocated in _register_g_object Memcheck:Leak ... fun:dbus_g_connection_register_g_object } { As we leak a connection, the corresponding dataslots bookkeeping is leaked Memcheck:Leak ... fun:dbus_realloc fun:_dbus_data_slot_allocator_alloc fun:tp_dbus_daemon_constructor } { As we leak a connection, the corresponding dataslots bookkeeping is leaked Memcheck:Leak ... fun:dbus_realloc fun:_dbus_data_slot_list_set fun:dbus_connection_set_data fun:tp_dbus_daemon_constructor } # dbus-glib type registration one-time leaks { dbus-glib specialized GTypes are permanent Memcheck:Leak ... fun:lookup_or_register_specialized } { dbus-glib object type information leaks Memcheck:Leak ... fun:dbus_g_object_type_install_info } # misc library one-time leaks { global gnutls data Memcheck:Leak ... fun:gnutls_global_init } { selinux, we just don't know Memcheck:Leak fun:malloc fun:getdelim obj:/lib/libselinux.so.1 } telepathy-gabble-0.18.2/lib/ext/wocky/tests/threadlocal.supp0000644000175000017500000000013612200204546024047 0ustar00smcvsmcv00000000000000{ Memcheck:Leak fun:calloc fun:_dl_allocate_tls fun:pthread_create@@* } telepathy-gabble-0.18.2/lib/ext/wocky/tests/wocky-xmpp-readwrite-test.c0000644000175000017500000001276712200204546026114 0ustar00smcvsmcv00000000000000#ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include #include "wocky-test-helper.h" #define TO "example.net" #define FROM "julliet@example.com" #define XMPP_VERSION "1.0" #define LANG "en" #define DUMMY_NS "urn:wocky:test:blah:blah:blah" static WockyStanza * create_stanza (void) { WockyStanza *stanza; WockyNode *html; WockyNode *head; stanza = wocky_stanza_build (WOCKY_STANZA_TYPE_MESSAGE, WOCKY_STANZA_SUB_TYPE_CHAT, "juliet@example.com", "romeo@example.net", '(', "html", ':', "http://www.w3.org/1999/xhtml", '(', "body", '$', "Art thou not Romeo, and a Montague?", ')', ')', NULL); html = wocky_node_get_child (wocky_stanza_get_top_node (stanza), "html"); head = wocky_node_add_child (html, "head"); wocky_node_set_attribute_ns (head, "rev", "0xbad1dea", DUMMY_NS); return stanza; } static void test_readwrite (void) { WockyXmppReader *reader; WockyXmppWriter *writer; WockyStanza *received = NULL, *sent; const guint8 *data; gsize length; gchar *to, *from, *version, *lang; int i; writer = wocky_xmpp_writer_new (); reader = wocky_xmpp_reader_new (); g_assert (wocky_xmpp_reader_get_state (reader) == WOCKY_XMPP_READER_STATE_INITIAL); wocky_xmpp_writer_stream_open (writer, TO, FROM, XMPP_VERSION, LANG, NULL, &data, &length); wocky_xmpp_reader_push (reader, data, length); g_assert (wocky_xmpp_reader_get_state (reader) == WOCKY_XMPP_READER_STATE_OPENED); g_object_get (reader, "to", &to, "from", &from, "version", &version, "lang", &lang, NULL); g_assert (!wocky_strdiff (to, TO)); g_assert (!wocky_strdiff (from, FROM)); g_assert (!wocky_strdiff (version, XMPP_VERSION)); g_assert (!wocky_strdiff (lang, LANG)); g_free (to); g_free (from); g_free (version); g_free (lang); sent = create_stanza (); for (i = 0; i < 3 ; i++) { WockyNode *html; WockyNode *head; const gchar *attr_recv = NULL; const gchar *attr_send = NULL; const gchar *attr_none = NULL; g_assert (wocky_xmpp_reader_get_state (reader) == WOCKY_XMPP_READER_STATE_OPENED); wocky_xmpp_writer_write_stanza (writer, sent, &data, &length); wocky_xmpp_reader_push (reader, data, length); received = wocky_xmpp_reader_pop_stanza (reader); g_assert (received != NULL); test_assert_stanzas_equal (sent, received); html = wocky_node_get_child (wocky_stanza_get_top_node (received), "html"); head = wocky_node_get_child (html, "head"); attr_recv = wocky_node_get_attribute_ns (head, "rev", DUMMY_NS); attr_none = wocky_node_get_attribute_ns (head, "rev", DUMMY_NS ":x"); html = wocky_node_get_child (wocky_stanza_get_top_node (sent), "html"); head = wocky_node_get_child (html, "head"); attr_send = wocky_node_get_attribute_ns (head, "rev", DUMMY_NS); g_assert (attr_none == NULL); g_assert (attr_recv != NULL); g_assert (attr_send != NULL); g_assert (!strcmp (attr_send, attr_recv)); g_object_unref (received); /* No more stanzas in the queue */ received = wocky_xmpp_reader_pop_stanza (reader); g_assert (received == NULL); } wocky_xmpp_writer_write_stanza (writer, sent, &data, &length); wocky_xmpp_reader_push (reader, data, length); wocky_xmpp_writer_stream_close (writer, &data, &length); wocky_xmpp_reader_push (reader, data, length); /* Stream state should stay open until we popped the last stanza */ g_assert (wocky_xmpp_reader_get_state (reader) == WOCKY_XMPP_READER_STATE_OPENED); received = wocky_xmpp_reader_pop_stanza (reader); g_assert (received != NULL); test_assert_stanzas_equal (sent, received); g_object_unref (received); /* Last stanza pop, stream should be closed */ g_assert (wocky_xmpp_reader_get_state (reader) == WOCKY_XMPP_READER_STATE_CLOSED); /* No more stanzas in the queue */ received = wocky_xmpp_reader_pop_stanza (reader); g_assert (received == NULL); g_object_unref (sent); g_object_unref (reader); g_object_unref (writer); } static void test_readwrite_nostream (void) { WockyXmppReader *reader; WockyXmppWriter *writer; WockyStanza *received = NULL, *sent; const guint8 *data; gsize length; int i; writer = wocky_xmpp_writer_new_no_stream (); reader = wocky_xmpp_reader_new_no_stream (); sent = create_stanza (); for (i = 0 ; i < 3 ; i++) { g_assert (wocky_xmpp_reader_get_state (reader) == WOCKY_XMPP_READER_STATE_OPENED); wocky_xmpp_writer_write_stanza (writer, sent, &data, &length); wocky_xmpp_reader_push (reader, data, length); g_assert (wocky_xmpp_reader_get_state (reader) == WOCKY_XMPP_READER_STATE_OPENED); received = wocky_xmpp_reader_pop_stanza (reader); g_assert (received != NULL); test_assert_stanzas_equal (sent, received); g_assert (wocky_xmpp_reader_get_state (reader) == WOCKY_XMPP_READER_STATE_CLOSED); wocky_xmpp_reader_reset (reader); g_object_unref (received); } g_object_unref (sent); g_object_unref (reader); g_object_unref (writer); } int main (int argc, char **argv) { int result; test_init (argc, argv); g_test_add_func ("/xmpp-readwrite/readwrite", test_readwrite); g_test_add_func ("/xmpp-readwrite/readwrite-nostream", test_readwrite_nostream); result = g_test_run (); test_deinit (); return result; } telepathy-gabble-0.18.2/lib/ext/wocky/tests/wocky-xmpp-reader-test.c0000644000175000017500000003666012201202754025366 0ustar00smcvsmcv00000000000000#ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include #include "wocky-test-helper.h" #define HEADER \ " " \ " " #define FOOTER " " #define BROKEN_HEADER \ " " \ " " #define HEADER_WITH_UNQUALIFIED_LANG \ " " \ " " #define BROKEN_MESSAGE \ " " \ " Art thou not Romeo, and a Montague? " \ " " #define MESSAGE_CHUNK0 \ " " \ " Art thou not Romeo, " #define MESSAGE_CHUNK1 \ " and a Montague? " \ " " #define VCARD_MESSAGE \ " " \ " " \ " Peter Saint-Andre " \ " " \ " Saint-Andre " \ " Peter " \ " " \ " " \ " stpeter " \ " http://www.xmpp.org/xsf/people/stpeter.shtml " \ " 1966-08-06 " \ " " \ " XMPP Standards Foundation " \ " " \ " " \ " Executive Director " \ " Patron Saint " \ " stpeter@jabber.org " \ " " \ " " #define VALID_NAMESPACE "http://garden.with.spaces" #define INVALID_NAMESPACE_MESSAGE \ " "\ " " \ " " \ " " \ " " #define WHITESPACE_PADDED_BODY " The Wench is Dead! " #define MESSAGE_WITH_WHITESPACE_PADDED_BODY \ " " \ " " WHITESPACE_PADDED_BODY "" \ " " #define WHITESPACE_ONLY_BODY " " #define MESSAGE_WITH_WHITESPACE_ONLY_BODY \ " " \ " " WHITESPACE_ONLY_BODY "" \ " " #define U_FDEF "\xe7\xb7\xaf" /* a non-character */ #define REPLACE "\xef\xbf\xbd" /* U+FFFD REPLACEMENT CHARACTER */ #define MONKEY "\xf0\x9f\x99\x88" /* U+1F648 SEE-NO-EVIL MONKEY */ #define NON_CHARACTER_CODEPOINTS U_FDEF MONKEY U_FDEF #define NON_CHARACTER_CODEPOINTS_REPLACEMENT REPLACE MONKEY REPLACE #define MESSAGE_WITH_NON_CHARACTER_CODEPOINTS \ " " \ " " NON_CHARACTER_CODEPOINTS "" \ " " static void test_stream_no_stanzas (void) { WockyXmppReader *reader; GError *error = NULL; reader = wocky_xmpp_reader_new (); g_assert (wocky_xmpp_reader_get_state (reader) == WOCKY_XMPP_READER_STATE_INITIAL); wocky_xmpp_reader_push (reader, (guint8 *) HEADER FOOTER, strlen (HEADER FOOTER)); g_assert (wocky_xmpp_reader_get_state (reader) == WOCKY_XMPP_READER_STATE_CLOSED); g_assert (wocky_xmpp_reader_peek_stanza (reader) == NULL); g_assert (wocky_xmpp_reader_get_state (reader) == WOCKY_XMPP_READER_STATE_CLOSED); g_assert (wocky_xmpp_reader_pop_stanza (reader) == NULL); g_assert (wocky_xmpp_reader_get_state (reader) == WOCKY_XMPP_READER_STATE_CLOSED); error = wocky_xmpp_reader_get_error (reader); g_assert_no_error (error); g_object_unref (reader); } static void test_stream_open_error (void) { WockyXmppReader *reader; GError *error = NULL; reader = wocky_xmpp_reader_new (); g_assert (wocky_xmpp_reader_get_state (reader) == WOCKY_XMPP_READER_STATE_INITIAL); wocky_xmpp_reader_push (reader, (guint8 *) BROKEN_HEADER, strlen (BROKEN_HEADER)); g_assert (wocky_xmpp_reader_get_state (reader) == WOCKY_XMPP_READER_STATE_ERROR); error = wocky_xmpp_reader_get_error (reader); g_assert_error (error, WOCKY_XMPP_READER_ERROR, WOCKY_XMPP_READER_ERROR_INVALID_STREAM_START); g_error_free (error); g_object_unref (reader); } static void test_stream_open_unqualified_lang (void) { WockyXmppReader *reader = wocky_xmpp_reader_new (); g_assert (wocky_xmpp_reader_get_state (reader) == WOCKY_XMPP_READER_STATE_INITIAL); wocky_xmpp_reader_push (reader, (guint8 *) HEADER_WITH_UNQUALIFIED_LANG, strlen (HEADER_WITH_UNQUALIFIED_LANG)); g_assert (wocky_xmpp_reader_get_state (reader) == WOCKY_XMPP_READER_STATE_OPENED); g_object_unref (reader); } static void test_parse_error (void) { WockyXmppReader *reader; GError *error = NULL; reader = wocky_xmpp_reader_new (); g_assert (wocky_xmpp_reader_get_state (reader) == WOCKY_XMPP_READER_STATE_INITIAL); wocky_xmpp_reader_push (reader, (guint8 *) HEADER, strlen (HEADER)); g_assert (wocky_xmpp_reader_get_state (reader) == WOCKY_XMPP_READER_STATE_OPENED); wocky_xmpp_reader_push (reader, (guint8 *) BROKEN_MESSAGE, strlen (BROKEN_MESSAGE)); g_assert (wocky_xmpp_reader_get_state (reader) == WOCKY_XMPP_READER_STATE_ERROR); g_assert (wocky_xmpp_reader_peek_stanza (reader) == NULL); g_assert (wocky_xmpp_reader_get_state (reader) == WOCKY_XMPP_READER_STATE_ERROR); g_assert (wocky_xmpp_reader_pop_stanza (reader) == NULL); g_assert (wocky_xmpp_reader_get_state (reader) == WOCKY_XMPP_READER_STATE_ERROR); error = wocky_xmpp_reader_get_error (reader); g_assert_error (error, WOCKY_XMPP_READER_ERROR, WOCKY_XMPP_READER_ERROR_PARSE_ERROR); g_error_free (error); g_object_unref (reader); } static void test_no_stream_parse_message (WockyXmppReader *reader) { WockyStanza *stanza; g_assert (wocky_xmpp_reader_get_state (reader) == WOCKY_XMPP_READER_STATE_OPENED); wocky_xmpp_reader_push (reader, (guint8 *) MESSAGE_CHUNK0, strlen (MESSAGE_CHUNK0)); g_assert (wocky_xmpp_reader_pop_stanza (reader) == NULL); g_assert (wocky_xmpp_reader_get_state (reader) == WOCKY_XMPP_READER_STATE_OPENED); wocky_xmpp_reader_push (reader, (guint8 *) MESSAGE_CHUNK1, strlen (MESSAGE_CHUNK1)); g_assert (wocky_xmpp_reader_get_state (reader) == WOCKY_XMPP_READER_STATE_OPENED); g_assert ((stanza = wocky_xmpp_reader_peek_stanza (reader)) != NULL); g_assert ((stanza = wocky_xmpp_reader_pop_stanza (reader)) != NULL); g_assert (wocky_xmpp_reader_get_state (reader) == WOCKY_XMPP_READER_STATE_CLOSED); g_object_unref (stanza); } static void test_no_stream_hunks (void) { WockyXmppReader *reader; reader = wocky_xmpp_reader_new_no_stream (); test_no_stream_parse_message (reader); g_object_unref (reader); } static void test_no_stream_reset (void) { WockyXmppReader *reader; reader = wocky_xmpp_reader_new_no_stream (); /* whole message, reset, whole message, reset */ test_no_stream_parse_message (reader); wocky_xmpp_reader_reset (reader); test_no_stream_parse_message (reader); wocky_xmpp_reader_reset (reader); /* push half a message and reset the parser*/ g_assert (wocky_xmpp_reader_get_state (reader) == WOCKY_XMPP_READER_STATE_OPENED); wocky_xmpp_reader_push (reader, (guint8 *) MESSAGE_CHUNK0, strlen (MESSAGE_CHUNK0)); wocky_xmpp_reader_reset (reader); /* And push a whole message through again */ test_no_stream_parse_message (reader); g_object_unref (reader); } /* libXML2 doesn't like the vcard-temp namespace test if we can still correctly parse it */ static void test_vcard_namespace (void) { WockyXmppReader *reader; WockyStanza *stanza; reader = wocky_xmpp_reader_new_no_stream (); wocky_xmpp_reader_push (reader, (guint8 *) VCARD_MESSAGE, strlen (VCARD_MESSAGE)); g_assert ((stanza = wocky_xmpp_reader_pop_stanza (reader)) != NULL); g_assert (wocky_xmpp_reader_get_state (reader) == WOCKY_XMPP_READER_STATE_CLOSED); g_object_unref (stanza); g_object_unref (reader); } static void test_invalid_namespace (void) { WockyXmppReader *reader; WockyStanza *stanza; reader = wocky_xmpp_reader_new_no_stream (); wocky_xmpp_reader_push (reader, (guint8 *) INVALID_NAMESPACE_MESSAGE, strlen (INVALID_NAMESPACE_MESSAGE)); g_assert ((stanza = wocky_xmpp_reader_pop_stanza (reader)) != NULL); g_assert (wocky_xmpp_reader_get_state (reader) == WOCKY_XMPP_READER_STATE_CLOSED); g_assert_cmpstr (VALID_NAMESPACE, ==, wocky_node_get_ns ( wocky_node_get_child (wocky_stanza_get_top_node (stanza), "branch"))); g_object_unref (stanza); g_object_unref (reader); } /* Helper function for the whitespace body tests */ static void test_body_with_alternative ( const gchar *xml, const gchar *expected_body_text, const gchar *alt_body_text) { WockyXmppReader *reader = wocky_xmpp_reader_new_no_stream (); WockyStanza *stanza; WockyNode *body; wocky_xmpp_reader_push (reader, (guint8 *) xml, strlen (xml)); stanza = wocky_xmpp_reader_pop_stanza (reader); g_assert (stanza != NULL); body = wocky_node_get_child (wocky_stanza_get_top_node (stanza), "body"); g_assert (body != NULL); g_assert (g_utf8_validate (body->content, -1, NULL)); if (alt_body_text == NULL) { g_assert_cmpstr (body->content, ==, expected_body_text); } else { if (wocky_strdiff (body->content, expected_body_text) && wocky_strdiff (body->content, alt_body_text)) { g_error ("Body text «%s» was neither «%s» nor «%s»", body->content, expected_body_text, alt_body_text); } } g_object_unref (stanza); g_object_unref (reader); } static void test_body (const gchar *xml, const gchar *exp) { test_body_with_alternative (xml, exp, NULL); } /* Test that whitespace around the text contents of a message isn't ignored */ static void test_whitespace_padding (void) { test_body (MESSAGE_WITH_WHITESPACE_PADDED_BODY, WHITESPACE_PADDED_BODY); } /* Test that a message body consisting entirely of whitespace isn't ignored */ static void test_whitespace_only (void) { test_body (MESSAGE_WITH_WHITESPACE_ONLY_BODY, WHITESPACE_ONLY_BODY); } /* Test that a message body containing non-character codepoints is * handled "appropriately". Older GLib replaces them with U+FFFD, * newer GLib keeps them as-is. */ static void test_non_character_codepoints (void) { test_body_with_alternative (MESSAGE_WITH_NON_CHARACTER_CODEPOINTS, NON_CHARACTER_CODEPOINTS, NON_CHARACTER_CODEPOINTS_REPLACEMENT); } static void check_namespaces ( WockyXmppReader *reader, const gchar *xml, const gchar *expected_namespace) { WockyStanza *stanza; WockyNode *top_node, *body_node; wocky_xmpp_reader_push (reader, (const guint8 *) xml, strlen (xml)); stanza = wocky_xmpp_reader_pop_stanza (reader); g_assert (stanza != NULL); top_node = wocky_stanza_get_top_node (stanza); g_assert (top_node != NULL); g_assert_cmpstr (expected_namespace, ==, wocky_node_get_ns (top_node)); body_node = wocky_node_get_first_child (top_node); g_assert (body_node != NULL); g_assert_cmpstr (expected_namespace, ==, wocky_node_get_ns (body_node)); g_object_unref (stanza); wocky_xmpp_reader_reset (reader); } static void test_no_stream_default_namespace ( WockyXmppReader *reader, const gchar *expected_default_namespace) { /* Regardless of the reader's default namespace, a root node with an * explicitly-specified namespace should get that namespace. */ check_namespaces (reader, "" "hai", WOCKY_XMPP_NS_JABBER_CLIENT); /* What namespace the nodes here end up in depends on the reader's default. */ check_namespaces (reader, "hai", expected_default_namespace); } static void test_no_stream_default_default_namespace (void) { WockyXmppReader *reader = wocky_xmpp_reader_new_no_stream (); /* WockyXmppReader defaults to the empty namespace. */ test_no_stream_default_namespace (reader, ""); g_object_unref (reader); } static void test_no_stream_specified_default_namespace (void) { #define WEIRD "wocky:weird:namespace" WockyXmppReader *reader = wocky_xmpp_reader_new_no_stream_ns (WEIRD); test_no_stream_default_namespace (reader, WEIRD); g_object_unref (reader); #undef WEIRD } int main (int argc, char **argv) { int result; test_init (argc, argv); g_test_add_func ("/xmpp-reader/stream-no-stanzas", test_stream_no_stanzas); g_test_add_func ("/xmpp-reader/stream-open-error", test_stream_open_error); g_test_add_func ("/xmpp-reader/stream-open-unqualified-lang", test_stream_open_unqualified_lang); g_test_add_func ("/xmpp-reader/parse-error", test_parse_error); g_test_add_func ("/xmpp-reader/no-stream-hunks", test_no_stream_hunks); g_test_add_func ("/xmpp-reader/no-stream-resetting", test_no_stream_reset); g_test_add_func ("/xmpp-reader/vcard-namespace", test_vcard_namespace); g_test_add_func ("/xmpp-reader/invalid-namespace", test_invalid_namespace); g_test_add_func ("/xmpp-reader/whitespace-padding", test_whitespace_padding); g_test_add_func ("/xmpp-reader/whitespace-only", test_whitespace_only); g_test_add_func ("/xmpp-reader/utf-non-character-codepoints", test_non_character_codepoints); g_test_add_func ("/xmpp-reader/no-stream-default-default-namespace", test_no_stream_default_default_namespace); g_test_add_func ("/xmpp-reader/no-stream-specified-default-namespace", test_no_stream_specified_default_namespace); result = g_test_run (); test_deinit (); return result; } telepathy-gabble-0.18.2/lib/ext/wocky/tests/wocky-xmpp-node-test.c0000644000175000017500000003263612200204546025050 0ustar00smcvsmcv00000000000000#ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include #include "wocky-test-helper.h" #define DUMMY_NS_A "urn:wocky:test:dummy:namespace:a" #define DUMMY_NS_B "urn:wocky:test:dummy:namespace:b" static void test_node_equal (void) { WockyStanza *a, *b; /* Simple IQ node */ a = wocky_stanza_build (WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_SET, "juliet@example.com", "romeo@example.org", NULL); test_assert_stanzas_equal (a, a); /* Same as 'a' but with an ID attribute */ b = wocky_stanza_build (WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_SET, "juliet@example.com", "romeo@example.org", '@', "id", "one", NULL); test_assert_stanzas_equal (b, b); test_assert_stanzas_not_equal (a, b); test_assert_stanzas_not_equal (b, a); g_object_unref (a); g_object_unref (b); } static void test_node_add_build (void) { WockyNode *n, *child; n = wocky_node_new ("testtree", DUMMY_NS_A); wocky_node_add_build (n, '(', "testnode", '@', "test", "attribute", '$', "testcontent", ')', NULL); g_assert_cmpint (g_slist_length (n->children), ==, 1); child = wocky_node_get_first_child (n); g_assert_cmpstr (child->name, ==, "testnode"); g_assert_cmpstr (wocky_node_get_ns (child), ==, DUMMY_NS_A); g_assert_cmpstr (child->content, ==, "testcontent"); g_assert_cmpint (g_slist_length (child->attributes), ==, 1); g_assert_cmpstr (wocky_node_get_attribute (child, "test"), ==, "attribute"); wocky_node_free (n); } static void test_set_attribute (void) { WockyStanza *a, *b, *c; a = wocky_stanza_build (WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_SET, "juliet@example.com", "romeo@example.org", NULL); b = wocky_stanza_build (WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_SET, "juliet@example.com", "romeo@example.org", '@', "foo", "badger", NULL); test_assert_stanzas_not_equal (a, b); wocky_node_set_attribute (wocky_stanza_get_top_node (a), "foo", "badger"); test_assert_stanzas_equal (a, b); c = wocky_stanza_build (WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_SET, "juliet@example.com", "romeo@example.org", '@', "foo", "snake", NULL); test_assert_stanzas_not_equal (b, c); wocky_node_set_attribute (wocky_stanza_get_top_node (b), "foo", "snake"); test_assert_stanzas_equal (b, c); g_object_unref (a); g_object_unref (b); g_object_unref (c); } static gboolean _check_attr_prefix (const gchar *urn, const gchar *prefix, const gchar *attr, const gchar *xml) { gboolean rval = FALSE; gchar *ns_str_a = g_strdup_printf (" xmlns:%s='%s'", prefix, urn); gchar *ns_str_b = g_strdup_printf (" xmlns:%s=\"%s\"", prefix, urn); gchar *attr_str = g_strdup_printf (" %s:%s=", prefix, attr); rval = ((strstr (xml, ns_str_a) != NULL) || (strstr (xml, ns_str_a) != NULL)) && (strstr (xml, attr_str) != NULL); g_free (ns_str_a); g_free (ns_str_b); g_free (attr_str); return rval; } static void test_append_content_n (void) { WockyStanza *a; const gchar *content = "badger badger badger"; guint i; size_t l; a = wocky_stanza_new ("message", WOCKY_XMPP_NS_JABBER_CLIENT); l = strlen (content); /* Append content byte by byte */ for (i = 0; i < l; i++) { wocky_node_append_content_n (wocky_stanza_get_top_node (a), content + i, 1); } g_assert (!wocky_strdiff (wocky_stanza_get_top_node (a)->content, content)); g_object_unref (a); } static void test_set_attribute_ns (void) { WockyStanza *sa; WockyStanza *sb; WockyNode *na; WockyNode *nb; const gchar *ca; const gchar *cb; const gchar *cx; const gchar *cy; gchar *xml_a; gchar *xml_b; gchar *pa; gchar *pb; GQuark qa; sa = wocky_stanza_build (WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_SET, "juliet@example.com", "romeo@example.org", NULL); sb = wocky_stanza_build (WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_SET, "juliet@example.com", "romeo@example.org", NULL); na = wocky_stanza_get_top_node (sa); nb = wocky_stanza_get_top_node (sb); test_assert_nodes_equal (na, nb); /* *********************************************************************** */ wocky_node_set_attribute_ns (na, "one", "1", DUMMY_NS_A); ca = wocky_node_get_attribute_ns (na, "one", DUMMY_NS_A); cb = wocky_node_get_attribute_ns (nb, "one", DUMMY_NS_A); cx = wocky_node_get_attribute_ns (na, "one", DUMMY_NS_B); cy = wocky_node_get_attribute (na, "one"); test_assert_nodes_not_equal (na, nb); g_assert (ca != NULL); g_assert (cb == NULL); g_assert (cx == NULL); g_assert (cy != NULL); g_assert (!strcmp (ca, "1")); /* *********************************************************************** */ /* set the attribute in the second node to make them equal again */ wocky_node_set_attribute_ns (nb, "one", "1", DUMMY_NS_A); ca = wocky_node_get_attribute_ns (na, "one", DUMMY_NS_A); cb = wocky_node_get_attribute_ns (nb, "one", DUMMY_NS_A); cx = wocky_node_get_attribute_ns (na, "one", DUMMY_NS_B); cy = wocky_node_get_attribute (na, "one"); test_assert_nodes_equal (na, nb); g_assert (ca != NULL); g_assert (cb != NULL); g_assert (cx == NULL); g_assert (cy != NULL); g_assert (!strcmp (ca, "1")); g_assert (!strcmp (ca, cb)); wocky_node_set_attribute_ns (nb, "one", "1", DUMMY_NS_A); cb = wocky_node_get_attribute_ns (nb, "one", DUMMY_NS_A); test_assert_nodes_equal (na, nb); g_assert (cb != NULL); g_assert (!strcmp (ca, cb)); /* *********************************************************************** */ /* change the namespaced atttribute */ wocky_node_set_attribute_ns (na, "one", "2", DUMMY_NS_A); ca = wocky_node_get_attribute_ns (na, "one", DUMMY_NS_A); cb = wocky_node_get_attribute_ns (nb, "one", DUMMY_NS_A); cx = wocky_node_get_attribute_ns (na, "one", DUMMY_NS_B); cy = wocky_node_get_attribute (na, "one"); test_assert_nodes_not_equal (na, nb); g_assert (ca != NULL); g_assert (cb != NULL); g_assert (cx == NULL); g_assert (cy != NULL); g_assert (!strcmp (ca, "2")); g_assert (strcmp (ca, cb)); /* *********************************************************************** */ /* add another attribute in a different namespace */ wocky_node_set_attribute_ns (na, "one", "3", DUMMY_NS_B); ca = wocky_node_get_attribute_ns (na, "one", DUMMY_NS_A); cb = wocky_node_get_attribute_ns (nb, "one", DUMMY_NS_A); cx = wocky_node_get_attribute_ns (na, "one", DUMMY_NS_B); cy = wocky_node_get_attribute (na, "one"); test_assert_nodes_not_equal (na, nb); g_assert (ca != NULL); g_assert (cb != NULL); g_assert (cx != NULL); g_assert (cy != NULL); g_assert (!strcmp (ca, "2")); g_assert (!strcmp (cx, "3")); g_assert (strcmp (ca, cb)); /* *********************************************************************** */ /* swap out the prefix for another one */ /* then check to see the right prefixes were assigned */ qa = g_quark_from_string (DUMMY_NS_B); xml_a = wocky_node_to_string (na); pa = g_strdup (wocky_node_attribute_ns_get_prefix_from_urn (DUMMY_NS_B)); pb = g_strdup (wocky_node_attribute_ns_get_prefix_from_quark (qa)); g_assert (!strcmp (pa, pb)); g_free (pb); /* change the prefix and re-write the attribute */ wocky_node_attribute_ns_set_prefix (qa, "moose"); wocky_node_set_attribute_ns (na, "one", "1", DUMMY_NS_B); xml_b = wocky_node_to_string (na); pb = g_strdup (wocky_node_attribute_ns_get_prefix_from_quark (qa)); g_assert (strcmp (pa, pb)); g_assert (_check_attr_prefix (DUMMY_NS_B, pa, "one", xml_a)); g_assert (_check_attr_prefix (DUMMY_NS_B, pb, "one", xml_b)); g_free (pa); g_free (pb); g_free (xml_a); g_free (xml_b); /* *********************************************************************** */ wocky_node_set_attribute_ns (na, "one", "4", DUMMY_NS_B); cx = wocky_node_get_attribute_ns (na, "one", DUMMY_NS_B); g_assert (cx != NULL); g_assert (!strcmp (cx, "4")); g_object_unref (sa); g_object_unref (sb); } static void do_test_iteration (WockyNodeIter *iter, const gchar **names) { WockyNode *node; int i = 0; while (wocky_node_iter_next (iter, &node)) { g_assert (names[i] != NULL && "Unexpected node"); g_assert_cmpstr (names[i], ==, wocky_node_get_attribute (node, "name")); i++; } g_assert (names[i] == NULL && "Expected more nodes"); } static void test_node_iteration (void) { WockyStanza *stanza; WockyNodeIter iter; const gchar *all[] = { "SPEEX", "THEORA", "GSM", "H264", "VIDEO?", "other", NULL }; const gchar *payloads[] = { "SPEEX", "THEORA", "GSM", "H264", NULL }; const gchar *audio[] = { "SPEEX", "GSM", NULL }; const gchar *video[] = { "THEORA", "H264", NULL }; const gchar *video_ns[] = { "THEORA", "H264", "VIDEO?", NULL }; const gchar *nothing[] = { NULL }; stanza = wocky_stanza_build (WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_SET, "to", "from", '(', "payload-type", ':', WOCKY_NS_GOOGLE_SESSION_PHONE, '@', "name", "SPEEX", ')', '(', "payload-type", ':', WOCKY_NS_GOOGLE_SESSION_VIDEO, '@', "name", "THEORA", ')', '(', "payload-type", ':', WOCKY_NS_GOOGLE_SESSION_PHONE, '@', "name", "GSM", ')', '(', "payload-type", ':', WOCKY_NS_GOOGLE_SESSION_VIDEO, '@', "name", "H264", ')', '(', "video", ':', WOCKY_NS_GOOGLE_SESSION_VIDEO, '@', "name", "VIDEO?", ')', '(', "misc", '@', "name", "other", ')', NULL); /* All children */ wocky_node_iter_init (&iter, wocky_stanza_get_top_node (stanza), NULL, NULL); do_test_iteration (&iter, all); /* Only the payloads */ wocky_node_iter_init (&iter, wocky_stanza_get_top_node (stanza), "payload-type", NULL); do_test_iteration (&iter, payloads); /* Only phone payloads */ wocky_node_iter_init (&iter, wocky_stanza_get_top_node (stanza), "payload-type", WOCKY_NS_GOOGLE_SESSION_PHONE); do_test_iteration (&iter, audio); /* Only nodes with the phone namespace */ wocky_node_iter_init (&iter, wocky_stanza_get_top_node (stanza), NULL, WOCKY_NS_GOOGLE_SESSION_PHONE); do_test_iteration (&iter, audio); /* only video payloads */ wocky_node_iter_init (&iter, wocky_stanza_get_top_node (stanza), "payload-type", WOCKY_NS_GOOGLE_SESSION_VIDEO); do_test_iteration (&iter, video); /* only nodes with the video namespace */ wocky_node_iter_init (&iter, wocky_stanza_get_top_node (stanza), NULL, WOCKY_NS_GOOGLE_SESSION_VIDEO); do_test_iteration (&iter, video_ns); /* nothing */ wocky_node_iter_init (&iter, wocky_stanza_get_top_node (stanza), "badgers", NULL); do_test_iteration (&iter, nothing); wocky_node_iter_init (&iter, wocky_stanza_get_top_node (stanza), NULL, "snakes"); do_test_iteration (&iter, nothing); g_object_unref (stanza); } static void test_node_iter_remove (void) { WockyNode *top, *child; WockyNodeTree *tree = wocky_node_tree_new ("foo", "wocky:test", '*', &top, '(', "remove-me", ')', '(', "remove-me", ')', '(', "preserve-me", ')', '(', "remove-me", ')', '(', "preserve-me", ')', '(', "remove-me", ')', '(', "remove-me", ')', NULL); WockyNodeIter iter; wocky_node_iter_init (&iter, top, "remove-me", NULL); while (wocky_node_iter_next (&iter, NULL)) wocky_node_iter_remove (&iter); g_assert_cmpuint (g_slist_length (top->children), ==, 2); wocky_node_iter_init (&iter, top, NULL, NULL); while (wocky_node_iter_next (&iter, &child)) g_assert_cmpstr (child->name, ==, "preserve-me"); g_object_unref (tree); } static void test_get_first_child (void) { WockyNodeTree *tree = wocky_node_tree_new ("my-elixir", "my:poison", '(', "th5", ')', '(', "holomovement", ':', "chinese:lantern", ')', '(', "th5", ':', "chinese:lantern", ')', NULL); WockyNode *top = wocky_node_tree_get_top_node (tree); WockyNode *n; n = wocky_node_get_first_child (top); g_assert (n != NULL); g_assert_cmpstr ("th5", ==, n->name); g_assert_cmpstr ("my:poison", ==, wocky_node_get_ns (n)); n = wocky_node_get_child (top, "th5"); g_assert (n != NULL); g_assert_cmpstr ("th5", ==, n->name); g_assert_cmpstr ("my:poison", ==, wocky_node_get_ns (n)); n = wocky_node_get_child_ns (top, "th5", "chinese:lantern"); g_assert (n != NULL); g_assert_cmpstr ("th5", ==, n->name); g_assert_cmpstr ("chinese:lantern", ==, wocky_node_get_ns (n)); n = wocky_node_get_first_child_ns (top, "chinese:lantern"); g_assert (n != NULL); g_assert_cmpstr ("holomovement", ==, n->name); g_assert_cmpstr ("chinese:lantern", ==, wocky_node_get_ns (n)); g_object_unref (tree); } int main (int argc, char **argv) { int result; test_init (argc, argv); g_test_add_func ("/xmpp-node/node-equal", test_node_equal); g_test_add_func ("/xmpp-node/add-build", test_node_add_build); g_test_add_func ("/xmpp-node/set-attribute", test_set_attribute); g_test_add_func ("/xmpp-node/append-content-n", test_append_content_n); g_test_add_func ("/xmpp-node/set-attribute-ns", test_set_attribute_ns); g_test_add_func ("/xmpp-node/node-iterator", test_node_iteration); g_test_add_func ("/xmpp-node/node-iterator-remove", test_node_iter_remove); g_test_add_func ("/xmpp-node/get-first-child", test_get_first_child); result = g_test_run (); test_deinit (); return result; } telepathy-gabble-0.18.2/lib/ext/wocky/tests/wocky-xmpp-connection-test.c0000644000175000017500000005713112200204546026257 0ustar00smcvsmcv00000000000000#ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include #include "wocky-test-stream.h" #include "wocky-test-helper.h" #define SIMPLE_MESSAGE \ " " \ " " \ " " \ " Art thou not Romeo, and a Montague? " \ " " \ "" static void test_instantiation (void) { WockyXmppConnection *connection; WockyTestStream *stream;; stream = g_object_new (WOCKY_TYPE_TEST_STREAM, NULL); connection = wocky_xmpp_connection_new (stream->stream0); g_assert (connection != NULL); g_object_unref (connection); g_object_unref (stream); } /* Simple message test */ static void stanza_received_cb (GObject *source, GAsyncResult *res, gpointer user_data) { WockyXmppConnection *connection = WOCKY_XMPP_CONNECTION (source); WockyStanza *s; test_data_t *data = (test_data_t *) user_data; GError *error = NULL; s = wocky_xmpp_connection_recv_stanza_finish (connection, res, &error); if (!data->parsed_stanza) { g_assert (s != NULL); data->parsed_stanza = TRUE; wocky_xmpp_connection_recv_stanza_async (WOCKY_XMPP_CONNECTION (source), NULL, stanza_received_cb, data); g_object_unref (s); } else { g_assert (s == NULL); g_main_loop_quit (data->loop); g_error_free (error); } } static void received_open_cb (GObject *source, GAsyncResult *res, gpointer user_data) { WockyXmppConnection *conn = WOCKY_XMPP_CONNECTION (source); if (!wocky_xmpp_connection_recv_open_finish (conn, res, NULL, NULL, NULL, NULL, NULL, NULL)) g_assert_not_reached (); wocky_xmpp_connection_recv_stanza_async (WOCKY_XMPP_CONNECTION (source), NULL, stanza_received_cb, user_data); } #define CHUNK_SIZE 13 static void test_recv_simple_message (void) { WockyXmppConnection *connection; WockyTestStream *stream; gsize len; gsize offset = 0; gchar message[] = SIMPLE_MESSAGE; GMainLoop *loop = NULL; test_data_t data = { NULL, FALSE }; loop = g_main_loop_new (NULL, FALSE); len = strlen (message); stream = g_object_new (WOCKY_TYPE_TEST_STREAM, NULL); connection = wocky_xmpp_connection_new (stream->stream0); g_timeout_add (1000, test_timeout_cb, NULL); data.loop = loop; wocky_xmpp_connection_recv_open_async (connection, NULL, received_open_cb, &data); while (offset < len) { guint l = MIN (len - offset, CHUNK_SIZE); while (g_main_context_iteration (NULL, FALSE)) ; g_output_stream_write_all (stream->stream1_output, message + offset, l, NULL, NULL, NULL); offset += l; } g_main_loop_run (loop); g_main_loop_unref (loop); g_object_unref (stream); g_object_unref (connection); } /* simple send message testing */ static void send_stanza_received_cb (GObject *source, GAsyncResult *res, gpointer user_data) { WockyXmppConnection *connection = WOCKY_XMPP_CONNECTION (source); WockyStanza *s; test_data_t *data = (test_data_t *) user_data; GError *error = NULL; s = wocky_xmpp_connection_recv_stanza_finish (connection, res, &error); g_assert (s != NULL); g_assert (!data->parsed_stanza); data->parsed_stanza = TRUE; g_object_unref (s); data->outstanding--; g_main_loop_quit (data->loop); } static void send_stanza_cb (GObject *source, GAsyncResult *res, gpointer user_data) { test_data_t *data = (test_data_t *) user_data; g_assert (wocky_xmpp_connection_send_stanza_finish ( WOCKY_XMPP_CONNECTION (source), res, NULL)); data->outstanding--; g_main_loop_quit (data->loop); } static void test_send_simple_message (void) { WockyStanza *s; test_data_t *test = setup_test (); test_open_connection (test); s = wocky_stanza_build (WOCKY_STANZA_TYPE_MESSAGE, WOCKY_STANZA_SUB_TYPE_CHAT, "juliet@example.com", "romeo@example.net", '(', "html", ':', "http://www.w3.org/1999/xhtml", '(', "body", '$', "Art thou not Romeo, and a Montague?", ')', ')', NULL); wocky_xmpp_connection_send_stanza_async (WOCKY_XMPP_CONNECTION (test->in), s, NULL, send_stanza_cb, test); wocky_xmpp_connection_recv_stanza_async (WOCKY_XMPP_CONNECTION (test->out), NULL, send_stanza_received_cb, test); test->outstanding += 2; test_wait_pending (test); test_close_connection (test); g_object_unref (s); teardown_test (test); } /* Test for various error codes */ static void error_pending_open_received_cb (GObject *source, GAsyncResult *result, gpointer user_data) { test_data_t *test = (test_data_t *) user_data; g_assert (wocky_xmpp_connection_recv_open_finish ( WOCKY_XMPP_CONNECTION (source), result, NULL, NULL, NULL, NULL, NULL, NULL)); test->outstanding--; g_main_loop_quit (test->loop); } static void error_pending_recv_open_pending_cb (GObject *source, GAsyncResult *result, gpointer user_data) { test_data_t *test = (test_data_t *) user_data; GError *error = NULL; WockyXmppConnection *conn = WOCKY_XMPP_CONNECTION (source); g_assert (!wocky_xmpp_connection_recv_open_finish ( conn, result, NULL, NULL, NULL, NULL, NULL, &error)); g_assert_error (error, G_IO_ERROR, G_IO_ERROR_PENDING); test->outstanding--; g_main_loop_quit (test->loop); g_error_free (error); } static void error_pending_stanza_received_cb (GObject *source, GAsyncResult *result, gpointer user_data) { test_data_t *test = (test_data_t *) user_data; WockyStanza *s; g_assert ((s = wocky_xmpp_connection_recv_stanza_finish ( WOCKY_XMPP_CONNECTION (source), result, NULL)) != NULL); test->outstanding--; g_main_loop_quit (test->loop); g_object_unref (s); } static void error_pending_recv_stanza_pending_cb (GObject *source, GAsyncResult *result, gpointer user_data) { test_data_t *test = (test_data_t *) user_data; GError *error = NULL; g_assert (wocky_xmpp_connection_recv_stanza_finish ( WOCKY_XMPP_CONNECTION (source), result, &error) == NULL); g_assert_error (error, G_IO_ERROR, G_IO_ERROR_PENDING); test->outstanding--; g_main_loop_quit (test->loop); g_error_free (error); } static void error_pending_open_sent_cb (GObject *source, GAsyncResult *result, gpointer user_data) { test_data_t *test = (test_data_t *) user_data; g_assert (wocky_xmpp_connection_send_open_finish ( WOCKY_XMPP_CONNECTION (source), result, NULL)); test->outstanding--; g_main_loop_quit (test->loop); } static void error_pending_open_pending_cb (GObject *source, GAsyncResult *result, gpointer user_data) { test_data_t *test = (test_data_t *) user_data; GError *error = NULL; g_assert (!wocky_xmpp_connection_send_open_finish ( WOCKY_XMPP_CONNECTION (source), result, &error)); g_assert_error (error, G_IO_ERROR, G_IO_ERROR_PENDING); test->outstanding--; g_main_loop_quit (test->loop); g_error_free (error); } static void error_pending_stanza_sent_cb (GObject *source, GAsyncResult *result, gpointer user_data) { test_data_t *test = (test_data_t *) user_data; g_assert (wocky_xmpp_connection_send_stanza_finish ( WOCKY_XMPP_CONNECTION (source), result, NULL)); test->outstanding--; g_main_loop_quit (test->loop); } static void error_pending_stanza_pending_cb (GObject *source, GAsyncResult *result, gpointer user_data) { test_data_t *test = (test_data_t *) user_data; GError *error = NULL; g_assert (!wocky_xmpp_connection_send_stanza_finish ( WOCKY_XMPP_CONNECTION (source), result, &error)); g_assert_error (error, G_IO_ERROR, G_IO_ERROR_PENDING); test->outstanding--; g_main_loop_quit (test->loop); g_error_free (error); } static void error_pending_close_sent_cb (GObject *source, GAsyncResult *result, gpointer user_data) { test_data_t *test = (test_data_t *) user_data; g_assert (wocky_xmpp_connection_send_close_finish ( WOCKY_XMPP_CONNECTION (source), result, NULL)); test->outstanding--; g_main_loop_quit (test->loop); } static void error_pending_close_pending_cb (GObject *source, GAsyncResult *result, gpointer user_data) { test_data_t *test = (test_data_t *) user_data; GError *error = NULL; g_assert (!wocky_xmpp_connection_send_close_finish ( WOCKY_XMPP_CONNECTION (source), result, &error)); g_assert_error (error, G_IO_ERROR, G_IO_ERROR_PENDING); test->outstanding--; g_main_loop_quit (test->loop); g_error_free (error); } static void test_error_pending_send_pending (test_data_t *test) { WockyStanza *stanza; stanza = wocky_stanza_build (WOCKY_STANZA_TYPE_MESSAGE, WOCKY_STANZA_SUB_TYPE_CHAT, "a"," b", NULL); /* should get a _PENDING error */ wocky_xmpp_connection_send_open_async (test->in, NULL, NULL, NULL, NULL, NULL, NULL, error_pending_open_pending_cb, test); /* should get a _PENDING error */ wocky_xmpp_connection_send_stanza_async (test->in, stanza, NULL, error_pending_stanza_pending_cb, test); /* should get a _PENDING error */ wocky_xmpp_connection_send_close_async (test->in, NULL, error_pending_close_pending_cb, test); test->outstanding += 3; g_object_unref (stanza); } static void test_error_pending (void) { test_data_t *test = setup_test (); WockyStanza *stanza; stanza = wocky_stanza_build (WOCKY_STANZA_TYPE_MESSAGE, WOCKY_STANZA_SUB_TYPE_CHAT, "a"," b", NULL); wocky_xmpp_connection_recv_open_async (test->out, NULL, error_pending_open_received_cb, test); test->outstanding++; wocky_xmpp_connection_recv_open_async (test->out, NULL, error_pending_open_pending_cb, test); test->outstanding++; g_main_loop_run (test->loop); wocky_xmpp_connection_recv_stanza_async (test->out, NULL, error_pending_recv_stanza_pending_cb, test); test->outstanding++; g_main_loop_run (test->loop); /* Should succeed */ wocky_xmpp_connection_send_open_async (test->in, NULL, NULL, NULL, NULL, NULL, NULL, error_pending_open_sent_cb, test); test->outstanding++; test_error_pending_send_pending (test); test_wait_pending (test); wocky_xmpp_connection_recv_stanza_async (test->out, NULL, error_pending_stanza_received_cb, test); test->outstanding++; wocky_xmpp_connection_recv_open_async (test->out, NULL, error_pending_recv_open_pending_cb, test); test->outstanding++; g_main_loop_run (test->loop); wocky_xmpp_connection_recv_stanza_async (test->out, NULL, error_pending_recv_stanza_pending_cb, test); test->outstanding++; g_main_loop_run (test->loop); /* should succeed */ wocky_xmpp_connection_send_stanza_async (test->in, stanza, NULL, error_pending_stanza_sent_cb, test); test->outstanding++; test_error_pending_send_pending (test); test_wait_pending (test); /* should succeed */ wocky_xmpp_connection_send_close_async (test->in, NULL, error_pending_close_sent_cb, test); test->outstanding++; test_error_pending_send_pending (test); test_wait_pending (test); teardown_test (test); g_object_unref (stanza); } /* not open errors */ static void error_not_open_send_stanza_cb (GObject *source, GAsyncResult *result, gpointer user_data) { test_data_t *test = (test_data_t *) user_data; GError *error = NULL; g_assert (!wocky_xmpp_connection_send_stanza_finish ( WOCKY_XMPP_CONNECTION (source), result, &error)); g_assert_error (error, WOCKY_XMPP_CONNECTION_ERROR, WOCKY_XMPP_CONNECTION_ERROR_NOT_OPEN); g_error_free (error); test->outstanding--; g_main_loop_quit (test->loop); } static void error_not_open_send_close_cb (GObject *source, GAsyncResult *result, gpointer user_data) { test_data_t *test = (test_data_t *) user_data; GError *error = NULL; g_assert (!wocky_xmpp_connection_send_close_finish ( WOCKY_XMPP_CONNECTION (source), result, &error)); g_assert_error (error, WOCKY_XMPP_CONNECTION_ERROR, WOCKY_XMPP_CONNECTION_ERROR_NOT_OPEN); g_error_free (error); test->outstanding--; g_main_loop_quit (test->loop); } static void error_not_open_recv_stanza_cb (GObject *source, GAsyncResult *result, gpointer user_data) { test_data_t *test = (test_data_t *) user_data; GError *error = NULL; g_assert (wocky_xmpp_connection_recv_stanza_finish ( WOCKY_XMPP_CONNECTION (source), result, &error) == NULL); g_assert_error (error, WOCKY_XMPP_CONNECTION_ERROR, WOCKY_XMPP_CONNECTION_ERROR_NOT_OPEN); g_error_free (error); test->outstanding--; g_main_loop_quit (test->loop); } static void test_error_not_open (void) { test_data_t *test = setup_test (); WockyStanza *stanza; stanza = wocky_stanza_build (WOCKY_STANZA_TYPE_MESSAGE, WOCKY_STANZA_SUB_TYPE_CHAT, "a"," b", NULL); wocky_xmpp_connection_send_stanza_async (test->in, stanza, NULL, error_not_open_send_stanza_cb, test); wocky_xmpp_connection_send_close_async (test->in, NULL, error_not_open_send_close_cb, test); wocky_xmpp_connection_recv_stanza_async (test->in, NULL, error_not_open_recv_stanza_cb, test); test->outstanding = 3; test_wait_pending (test); teardown_test (test); g_object_unref (stanza); } /* is open tests */ static void error_is_open_send_open_cb (GObject *source, GAsyncResult *result, gpointer user_data) { test_data_t *test = (test_data_t *) user_data; GError *error = NULL; g_assert (!wocky_xmpp_connection_send_open_finish ( WOCKY_XMPP_CONNECTION (source), result, &error)); g_assert_error (error, WOCKY_XMPP_CONNECTION_ERROR, WOCKY_XMPP_CONNECTION_ERROR_IS_OPEN); g_error_free (error); test->outstanding--; g_main_loop_quit (test->loop); } static void is_open_send_open_cb (GObject *source, GAsyncResult *result, gpointer user_data) { g_assert (wocky_xmpp_connection_send_open_finish ( WOCKY_XMPP_CONNECTION (source), result, NULL)); wocky_xmpp_connection_send_open_async (WOCKY_XMPP_CONNECTION (source), NULL, NULL, NULL, NULL, NULL, NULL, error_is_open_send_open_cb, user_data); } static void error_is_open_recv_open_cb (GObject *source, GAsyncResult *result, gpointer user_data) { test_data_t *test = (test_data_t *) user_data; GError *error = NULL; g_assert (!wocky_xmpp_connection_recv_open_finish ( WOCKY_XMPP_CONNECTION (source), result, NULL, NULL, NULL, NULL, NULL, &error)); g_assert_error (error, WOCKY_XMPP_CONNECTION_ERROR, WOCKY_XMPP_CONNECTION_ERROR_IS_OPEN); g_error_free (error); test->outstanding--; g_main_loop_quit (test->loop); } static void is_open_recv_open_cb (GObject *source, GAsyncResult *result, gpointer user_data) { WockyXmppConnection *conn = WOCKY_XMPP_CONNECTION (source); g_assert (wocky_xmpp_connection_recv_open_finish ( conn, result, NULL, NULL, NULL, NULL, NULL, NULL)); wocky_xmpp_connection_recv_open_async (WOCKY_XMPP_CONNECTION (source), NULL, error_is_open_recv_open_cb, user_data); } static void is_open_send_close_cb (GObject *source, GAsyncResult *result, gpointer user_data) { test_data_t *test = (test_data_t *) user_data; g_assert (wocky_xmpp_connection_send_close_finish ( WOCKY_XMPP_CONNECTION (source), result, NULL)); test->outstanding--; g_main_loop_quit (test->loop); } static void is_open_recv_close_cb (GObject *source, GAsyncResult *result, gpointer user_data) { test_data_t *test = (test_data_t *) user_data; GError *error = NULL; g_assert (wocky_xmpp_connection_recv_stanza_finish ( WOCKY_XMPP_CONNECTION (source), result, &error) == NULL); g_assert_error (error, WOCKY_XMPP_CONNECTION_ERROR, WOCKY_XMPP_CONNECTION_ERROR_CLOSED); g_error_free (error); test->outstanding--; g_main_loop_quit (test->loop); } static void error_is_closed_send_open_cb (GObject *source, GAsyncResult *result, gpointer user_data) { test_data_t *test = (test_data_t *) user_data; GError *error = NULL; g_assert (!wocky_xmpp_connection_send_open_finish ( WOCKY_XMPP_CONNECTION (source), result, &error)); g_assert_error (error, WOCKY_XMPP_CONNECTION_ERROR, WOCKY_XMPP_CONNECTION_ERROR_IS_CLOSED); g_error_free (error); test->outstanding--; g_main_loop_quit (test->loop); } static void error_is_closed_send_stanza_cb (GObject *source, GAsyncResult *result, gpointer user_data) { test_data_t *test = (test_data_t *) user_data; GError *error = NULL; g_assert (!wocky_xmpp_connection_send_stanza_finish ( WOCKY_XMPP_CONNECTION (source), result, &error)); g_assert_error (error, WOCKY_XMPP_CONNECTION_ERROR, WOCKY_XMPP_CONNECTION_ERROR_IS_CLOSED); g_error_free (error); test->outstanding--; g_main_loop_quit (test->loop); } static void error_is_closed_send_close_cb (GObject *source, GAsyncResult *result, gpointer user_data) { test_data_t *test = (test_data_t *) user_data; GError *error = NULL; g_assert (!wocky_xmpp_connection_send_close_finish ( WOCKY_XMPP_CONNECTION (source), result, &error)); g_assert_error (error, WOCKY_XMPP_CONNECTION_ERROR, WOCKY_XMPP_CONNECTION_ERROR_IS_CLOSED); g_error_free (error); test->outstanding--; g_main_loop_quit (test->loop); } static void error_is_closed_recv_open_cb (GObject *source, GAsyncResult *result, gpointer user_data) { test_data_t *test = (test_data_t *) user_data; GError *error = NULL; g_assert (!wocky_xmpp_connection_recv_open_finish ( WOCKY_XMPP_CONNECTION (source), result, NULL, NULL, NULL, NULL, NULL, &error)); g_assert_error (error, WOCKY_XMPP_CONNECTION_ERROR, WOCKY_XMPP_CONNECTION_ERROR_IS_CLOSED); g_error_free (error); test->outstanding--; g_main_loop_quit (test->loop); } static void error_is_closed_recv_stanza_cb (GObject *source, GAsyncResult *result, gpointer user_data) { test_data_t *test = (test_data_t *) user_data; GError *error = NULL; g_assert (wocky_xmpp_connection_recv_stanza_finish ( WOCKY_XMPP_CONNECTION (source), result, &error) == NULL); g_assert_error (error, WOCKY_XMPP_CONNECTION_ERROR, WOCKY_XMPP_CONNECTION_ERROR_IS_CLOSED); g_error_free (error); test->outstanding--; g_main_loop_quit (test->loop); } static void test_error_is_open_or_closed (void) { test_data_t *test = setup_test (); WockyStanza *stanza; stanza = wocky_stanza_build (WOCKY_STANZA_TYPE_MESSAGE, WOCKY_STANZA_SUB_TYPE_CHAT, "a"," b", NULL); wocky_xmpp_connection_send_open_async (WOCKY_XMPP_CONNECTION (test->in), NULL, NULL, NULL, NULL, NULL, NULL, is_open_send_open_cb, test); wocky_xmpp_connection_recv_open_async (WOCKY_XMPP_CONNECTION (test->out), NULL, is_open_recv_open_cb, test); test->outstanding = 2; test_wait_pending (test); /* Input and output side are open, so they can be closed */ wocky_xmpp_connection_send_close_async (WOCKY_XMPP_CONNECTION (test->in), NULL, is_open_send_close_cb, test); wocky_xmpp_connection_recv_stanza_async (WOCKY_XMPP_CONNECTION (test->out), NULL, is_open_recv_close_cb, test); test->outstanding = 2; test_wait_pending (test); /* both sides are closed, all calls should yield _IS_CLOSED errors */ wocky_xmpp_connection_send_open_async (WOCKY_XMPP_CONNECTION (test->in), NULL, NULL, NULL, NULL, NULL, NULL, error_is_closed_send_open_cb, test); wocky_xmpp_connection_send_stanza_async (WOCKY_XMPP_CONNECTION (test->in), stanza, NULL, error_is_closed_send_stanza_cb, test); wocky_xmpp_connection_send_close_async (WOCKY_XMPP_CONNECTION (test->in), NULL, error_is_closed_send_close_cb, test); wocky_xmpp_connection_recv_open_async (WOCKY_XMPP_CONNECTION (test->out), NULL, error_is_closed_recv_open_cb, test); wocky_xmpp_connection_recv_stanza_async (WOCKY_XMPP_CONNECTION (test->out), NULL, error_is_closed_recv_stanza_cb, test); test->outstanding = 5; test_wait_pending (test); teardown_test (test); g_object_unref (stanza); } /* Send cancelled */ static void recv_cancelled_cb (GObject *source, GAsyncResult *res, gpointer user_data) { test_data_t *data = (test_data_t *) user_data; GError *error = NULL; g_assert (!wocky_xmpp_connection_send_stanza_finish ( WOCKY_XMPP_CONNECTION (source), res, &error)); g_assert_error (error, G_IO_ERROR, G_IO_ERROR_CANCELLED); data->outstanding--; g_main_loop_quit (data->loop); g_error_free (error); } static void test_recv_cancel (void) { WockyStanza *stanza; test_data_t *test = setup_test (); GCancellable *cancellable; test_open_connection (test); stanza = wocky_stanza_build (WOCKY_STANZA_TYPE_MESSAGE, WOCKY_STANZA_SUB_TYPE_CHAT, "a"," b", NULL); cancellable = g_cancellable_new (); wocky_xmpp_connection_recv_stanza_async (WOCKY_XMPP_CONNECTION (test->out), cancellable, recv_cancelled_cb, test); g_cancellable_cancel (cancellable); test->outstanding++; test_wait_pending (test); g_object_unref (stanza); g_object_unref (cancellable); test_close_connection (test); teardown_test (test); } /* Send message in a big chunk */ static void test_recv_simple_message_in_one_chunk (void) { WockyXmppConnection *connection; WockyTestStream *stream; gsize len; gchar message[] = SIMPLE_MESSAGE; GMainLoop *loop = NULL; test_data_t data = { NULL, FALSE }; loop = g_main_loop_new (NULL, FALSE); len = strlen (message); stream = g_object_new (WOCKY_TYPE_TEST_STREAM, NULL); connection = wocky_xmpp_connection_new (stream->stream0); g_timeout_add (1000, test_timeout_cb, NULL); data.loop = loop; g_output_stream_write_all (stream->stream1_output, message, len, NULL, NULL, NULL); wocky_xmpp_connection_recv_open_async (connection, NULL, received_open_cb, &data); g_main_loop_run (loop); g_main_loop_unref (loop); g_object_unref (stream); g_object_unref (connection); } /* test force close */ static void force_close_cb (GObject *source, GAsyncResult *res, gpointer user_data) { WockyXmppConnection *conn = WOCKY_XMPP_CONNECTION (source); test_data_t *data = (test_data_t *) user_data; g_assert (wocky_xmpp_connection_force_close_finish (conn, res, NULL)); g_main_loop_quit (data->loop); } static void test_force_close (void) { WockyXmppConnection *connection; WockyTestStream *stream; GMainLoop *loop = NULL; test_data_t data = { NULL, FALSE }; loop = g_main_loop_new (NULL, FALSE); stream = g_object_new (WOCKY_TYPE_TEST_STREAM, NULL); connection = wocky_xmpp_connection_new (stream->stream0); g_timeout_add (1000, test_timeout_cb, NULL); data.loop = loop; wocky_xmpp_connection_force_close_async (connection, NULL, force_close_cb, &data); g_main_loop_run (loop); g_main_loop_unref (loop); g_object_unref (stream); g_object_unref (connection); } int main (int argc, char **argv) { int result; test_init (argc, argv); g_test_add_func ("/xmpp-connection/initiation", test_instantiation); g_test_add_func ("/xmpp-connection/recv-simple-message", test_recv_simple_message); g_test_add_func ("/xmpp-connection/send-simple-message", test_send_simple_message); g_test_add_func ("/xmpp-connection/error-pending", test_error_pending); g_test_add_func ("/xmpp-connection/error-not-open", test_error_not_open); g_test_add_func ("/xmpp-connection/error-is-open-or-closed", test_error_is_open_or_closed); g_test_add_func ("/xmpp-connection/recv-cancel", test_recv_cancel); g_test_add_func ("/xmpp-connection/recv-simple-message-in-one-chunk", test_recv_simple_message_in_one_chunk); g_test_add_func ("/xmpp-connection/force-close", test_force_close); result = g_test_run (); test_deinit (); return result; } telepathy-gabble-0.18.2/lib/ext/wocky/tests/wocky-utils-test.c0000644000175000017500000000317412200204546024274 0ustar00smcvsmcv00000000000000/* * wocky-utils-test.c - Tests for utility code * * Copyright © 2010 Collabora Ltd. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include static void test_compose_jid (void) { gchar *s = NULL; g_assert_cmpstr ((s = wocky_compose_jid ("juliet", "example.com", "Balcony")), ==, "juliet@example.com/Balcony"); g_free (s); g_assert_cmpstr ((s = wocky_compose_jid ("juliet", "example.com", NULL)), ==, "juliet@example.com"); g_free (s); g_assert_cmpstr ((s = wocky_compose_jid (NULL, "example.com", NULL)), ==, "example.com"); g_free (s); g_assert_cmpstr ((s = wocky_compose_jid (NULL, "example.com", "Server")), ==, "example.com/Server"); g_free (s); } int main (int argc, char **argv) { g_test_init (&argc, &argv, NULL); g_test_add_func ("/utils/compose-jid", test_compose_jid); return g_test_run (); } telepathy-gabble-0.18.2/lib/ext/wocky/tests/wocky-tls-test.c0000644000175000017500000002202212200204546023727 0ustar00smcvsmcv00000000000000#include #include #include #include "config.h" #include #include #include "wocky-test-stream.h" #include "wocky-test-helper.h" #define BUF_SIZE 8192 #define TEST_SSL_DATA_A "badgerbadgerbadger" #define TEST_SSL_DATA_B "mushroom, mushroom" #define TEST_SSL_DATA_LEN sizeof (TEST_SSL_DATA_A) static const gchar *test_data[] = { TEST_SSL_DATA_A, TEST_SSL_DATA_B, NULL }; typedef struct { test_data_t *test; char cli_buf[BUF_SIZE]; char srv_buf[BUF_SIZE]; char *cli_send; gsize cli_send_len; gsize cli_sent; WockyTLSConnection *client; WockyTLSConnection *server; GString *cli_data; GString *srv_data; guint read_op_count; guint write_op_count; gboolean in_read; } ssl_test_t; /* ************************************************************************ */ /* client callbacks */ static void client_write_cb (GObject *source, GAsyncResult *result, gpointer data) { GOutputStream *output = G_OUTPUT_STREAM (source); ssl_test_t *ssl_test = data; GError *error = NULL; gsize ret; ret = g_output_stream_write_finish (output, result, &error); g_assert_cmpint (ret, <=, ssl_test->cli_send_len - ssl_test->cli_sent); g_assert_no_error (error); ssl_test->cli_sent += ret; if (ssl_test->cli_sent == ssl_test->cli_send_len) { ssl_test->test->outstanding--; g_main_loop_quit (ssl_test->test->loop); return; } g_output_stream_write_async (output, ssl_test->cli_send + ssl_test->cli_sent, ssl_test->cli_send_len - ssl_test->cli_sent, G_PRIORITY_DEFAULT, ssl_test->test->cancellable, client_write_cb, data); } static void client_read_cb (GObject *source, GAsyncResult *result, gpointer data) { GInputStream *input = G_INPUT_STREAM (source); ssl_test_t *ssl_test = data; GError *error = NULL; gssize count = g_input_stream_read_finish (input, result, &error); g_assert (!ssl_test->in_read); g_assert_no_error (error); g_assert_cmpint (count, ==, TEST_SSL_DATA_LEN); ssl_test->read_op_count++; g_string_append_len (ssl_test->cli_data, ssl_test->cli_buf, count); switch (ssl_test->read_op_count) { case 1: /* we've read at least one ssl record at this point, combine the others */ wocky_test_stream_set_mode (ssl_test->test->stream->stream0_input, WOCK_TEST_STREAM_READ_COMBINE); break; case 2: #ifdef USING_OPENSSL /* Our openssl backend should have read all ssl records by now and * thus shouldn't request more data from the input stream. The GnuTLS * backend requests more granularily what it needs so will still read * from the input stream */ wocky_test_input_stream_set_read_error ( ssl_test->test->stream->stream0_input); #endif /* USING_OPENSSL */ break; case 3: case 4: break; case 5: { GIOStream *io = G_IO_STREAM (ssl_test->client); GOutputStream *output = g_io_stream_get_output_stream (io); g_output_stream_write_async (output, ssl_test->cli_send, ssl_test->cli_send_len, G_PRIORITY_DEFAULT, ssl_test->test->cancellable, client_write_cb, data); return; } default: g_error ("Read too many records: test broken?"); } ssl_test->in_read = TRUE; g_input_stream_read_async (input, ssl_test->cli_buf, BUF_SIZE, G_PRIORITY_DEFAULT, ssl_test->test->cancellable, client_read_cb, data); ssl_test->in_read = FALSE; } static void client_handshake_cb (GObject *source, GAsyncResult *result, gpointer data) { GInputStream *input; WockyTLSSession *session = WOCKY_TLS_SESSION (source); ssl_test_t *ssl_test = data; ssl_test->client = wocky_tls_session_handshake_finish (session, result, NULL); input = g_io_stream_get_input_stream (G_IO_STREAM (ssl_test->client)); ssl_test->in_read = TRUE; g_input_stream_read_async (input, ssl_test->cli_buf, BUF_SIZE, G_PRIORITY_DEFAULT, ssl_test->test->cancellable, client_read_cb, data); ssl_test->in_read = FALSE; } /* ************************************************************************ */ /* server callbacks */ static void server_read_cb (GObject *source, GAsyncResult *result, gpointer data) { GInputStream *input = G_INPUT_STREAM (source); ssl_test_t *ssl_test = data; gssize count = g_input_stream_read_finish (input, result, NULL); g_string_append_len (ssl_test->srv_data, ssl_test->srv_buf, count); if (ssl_test->srv_data->len < ssl_test->cli_send_len) { g_input_stream_read_async (input, ssl_test->srv_buf, BUF_SIZE, G_PRIORITY_DEFAULT, ssl_test->test->cancellable, server_read_cb, data); } else { ssl_test->test->outstanding--; g_main_loop_quit (ssl_test->test->loop); } } static void server_write_cb (GObject *source, GAsyncResult *result, gpointer data) { ssl_test_t *ssl_test = data; gsize written; GOutputStream *output = G_OUTPUT_STREAM (source); GIOStream *io = G_IO_STREAM (ssl_test->server); GInputStream *input = g_io_stream_get_input_stream (io); written = g_output_stream_write_finish (output, result, NULL); g_assert_cmpint (written, ==, TEST_SSL_DATA_LEN); ssl_test->write_op_count++; if (ssl_test->write_op_count < 5) { const char *payload = test_data[ssl_test->write_op_count & 1]; g_output_stream_write_async (output, payload, TEST_SSL_DATA_LEN, G_PRIORITY_LOW, ssl_test->test->cancellable, server_write_cb, data); } else { wocky_test_stream_cork (ssl_test->test->stream->stream0_input, FALSE); g_input_stream_read_async (input, ssl_test->srv_buf, BUF_SIZE, G_PRIORITY_DEFAULT, ssl_test->test->cancellable, server_read_cb, data); } } static void server_handshake_cb (GObject *source, GAsyncResult *result, gpointer data) { GOutputStream *output; WockyTLSSession *session = WOCKY_TLS_SESSION (source); ssl_test_t *ssl_test = data; GError *error = NULL; ssl_test->server = wocky_tls_session_handshake_finish (session, result, &error); g_assert_no_error (error); g_assert (ssl_test->server != NULL); output = g_io_stream_get_output_stream (G_IO_STREAM (ssl_test->server)); /* cork the client reading stream */ wocky_test_stream_cork (ssl_test->test->stream->stream0_input, TRUE); g_output_stream_write_async (output, TEST_SSL_DATA_A, TEST_SSL_DATA_LEN, G_PRIORITY_LOW, ssl_test->test->cancellable, server_write_cb, data); } /* ************************************************************************ */ /* test(s) */ static void setup_ssl_test (ssl_test_t *ssl_test, test_data_t *test) { guint x; /* the tests currently rely on all the chunks being the same size */ /* note that we include the terminating \0 in the payload */ for (x = 0; test_data[x] != NULL; x++) g_assert (strlen (test_data[x]) == (TEST_SSL_DATA_LEN - 1)); ssl_test->test = test; ssl_test->cli_data = g_string_new (""); ssl_test->srv_data = g_string_new (""); } static void teardown_ssl_test (ssl_test_t *ssl_test) { g_free (ssl_test->cli_send); g_string_free (ssl_test->cli_data, TRUE); g_string_free (ssl_test->srv_data, TRUE); g_io_stream_close (G_IO_STREAM (ssl_test->server), NULL, NULL); g_io_stream_close (G_IO_STREAM (ssl_test->client), NULL, NULL); g_object_unref (ssl_test->server); g_object_unref (ssl_test->client); } static void test_tls_handshake_rw (void) { ssl_test_t ssl_test = { NULL, } ; test_data_t *test = setup_test (); WockyTLSSession *client = wocky_tls_session_new (test->stream->stream0); WockyTLSSession *server = wocky_tls_session_server_new ( test->stream->stream1, 1024, TLS_SERVER_KEY_FILE, TLS_SERVER_CRT_FILE); gsize expected = TEST_SSL_DATA_LEN * 5; gchar *target = TEST_SSL_DATA_A "\0" TEST_SSL_DATA_B "\0" TEST_SSL_DATA_A "\0" TEST_SSL_DATA_B "\0" TEST_SSL_DATA_A; ssl_test.cli_send = g_malloc0 (32 * 1024); while (ssl_test.cli_send_len + 4 < 32 * 1024) { guint32 r = g_random_int (); memcpy (ssl_test.cli_send + ssl_test.cli_send_len, &r, 4); ssl_test.cli_send_len += 4; } setup_ssl_test (&ssl_test, test); wocky_tls_session_handshake_async (client, G_PRIORITY_DEFAULT, test->cancellable, client_handshake_cb, &ssl_test); test->outstanding += 1; wocky_tls_session_handshake_async (server, G_PRIORITY_DEFAULT, test->cancellable, server_handshake_cb, &ssl_test); test->outstanding += 1; test_wait_pending (test); g_assert_cmpint (test->outstanding, ==, 0); g_assert_cmpint (ssl_test.cli_data->len, ==, expected); g_assert_cmpint (ssl_test.srv_data->len, ==, ssl_test.cli_send_len); g_assert (!memcmp (ssl_test.cli_data->str, target, expected)); g_assert (!memcmp (ssl_test.srv_data->str, ssl_test.cli_send, ssl_test.cli_send_len)); teardown_test (test); teardown_ssl_test (&ssl_test); g_object_unref (client); g_object_unref (server); } int main (int argc, char **argv) { int result; test_init (argc, argv); g_test_add_func ("/tls/handshake+rw", test_tls_handshake_rw); result = g_test_run (); test_deinit (); return result; } telepathy-gabble-0.18.2/lib/ext/wocky/tests/wocky-test-sasl-handler.h0000644000175000017500000000222312200204546025510 0ustar00smcvsmcv00000000000000#ifndef _WOCKY_TEST_SASL_HANDLER_H #define _WOCKY_TEST_SASL_HANDLER_H #include G_BEGIN_DECLS #define WOCKY_TYPE_TEST_SASL_HANDLER wocky_test_sasl_handler_get_type() #define WOCKY_TEST_SASL_HANDLER(obj) \ (G_TYPE_CHECK_INSTANCE_CAST ((obj), WOCKY_TYPE_TEST_SASL_HANDLER, \ WockyTestSaslHandler)) #define WOCKY_TEST_SASL_HANDLER_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST ((klass), WOCKY_TYPE_TEST_SASL_HANDLER, \ WockyTestSaslHandlerClass)) #define WOCKY_IS_TEST_SASL_HANDLER(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), WOCKY_TYPE_TEST_SASL_HANDLER)) #define WOCKY_IS_TEST_SASL_HANDLER_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE ((klass), WOCKY_TYPE_TEST_SASL_HANDLER)) #define WOCKY_TEST_SASL_HANDLER_GET_CLASS(obj) \ (G_TYPE_INSTANCE_GET_CLASS ((obj), WOCKY_TYPE_TEST_SASL_HANDLER, \ WockyTestSaslHandlerClass)) typedef struct { GObject parent; } WockyTestSaslHandler; typedef struct { GObjectClass parent_class; } WockyTestSaslHandlerClass; GType wocky_test_sasl_handler_get_type (void); WockyTestSaslHandler* wocky_test_sasl_handler_new (void); G_END_DECLS #endif /* _WOCKY_TEST_SASL_HANDLER_H */ telepathy-gabble-0.18.2/lib/ext/wocky/tests/wocky-test-sasl-handler.c0000644000175000017500000000216012200204546025503 0ustar00smcvsmcv00000000000000#ifdef HAVE_CONFIG_H #include "config.h" #endif #include "wocky-test-sasl-handler.h" #include static void auth_handler_iface_init (gpointer g_iface); G_DEFINE_TYPE_WITH_CODE (WockyTestSaslHandler, wocky_test_sasl_handler, G_TYPE_OBJECT, G_IMPLEMENT_INTERFACE (WOCKY_TYPE_AUTH_HANDLER, auth_handler_iface_init)) static void wocky_test_sasl_handler_class_init (WockyTestSaslHandlerClass *klass) { } static void wocky_test_sasl_handler_init (WockyTestSaslHandler *self) { } static gboolean test_initial_response (WockyAuthHandler *handler, GString **initial_data, GError **error); static void auth_handler_iface_init (gpointer g_iface) { WockyAuthHandlerIface *iface = g_iface; iface->mechanism = "X-TEST"; iface->plain = FALSE; iface->initial_response_func = test_initial_response; } WockyTestSaslHandler * wocky_test_sasl_handler_new (void) { return g_object_new (WOCKY_TYPE_TEST_SASL_HANDLER, NULL); } static gboolean test_initial_response (WockyAuthHandler *handler, GString **initial_data, GError **error) { *initial_data = g_string_new ("open sesame"); return TRUE; } telepathy-gabble-0.18.2/lib/ext/wocky/tests/wocky-test-sasl-auth.c0000644000175000017500000002254412200204546025037 0ustar00smcvsmcv00000000000000#ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include "wocky-test-sasl-auth-server.h" #include "wocky-test-sasl-handler.h" #include "wocky-test-stream.h" #include "wocky-test-helper.h" #include typedef struct { gchar *description; gchar *mech; gboolean allow_plain; GQuark domain; int code; ServerProblem problem; gboolean wrong_username; gboolean wrong_password; const gchar *username; const gchar *password; const gchar *servername; } test_t; GMainLoop *mainloop; GIOStream *xmpp_connection; WockyXmppConnection *conn; WockySaslAuth *sasl = NULL; gboolean authenticated = FALSE; gboolean run_done = FALSE; test_t *current_test = NULL; GError *error = NULL; static void post_auth_recv_stanza (GObject *source, GAsyncResult *result, gpointer user_data) { WockyStanza *stanza; GError *e = NULL; /* ignore all stanza until close */ stanza = wocky_xmpp_connection_recv_stanza_finish ( WOCKY_XMPP_CONNECTION (source), result, &e); if (stanza != NULL) { g_object_unref (stanza); wocky_xmpp_connection_recv_stanza_async ( WOCKY_XMPP_CONNECTION (source), NULL, post_auth_recv_stanza, user_data); } else { g_assert_error (e, WOCKY_XMPP_CONNECTION_ERROR, WOCKY_XMPP_CONNECTION_ERROR_CLOSED); g_error_free (e); run_done = TRUE; g_main_loop_quit (mainloop); } } static void post_auth_close_sent (GObject *source, GAsyncResult *result, gpointer user_data) { g_assert (wocky_xmpp_connection_send_close_finish ( WOCKY_XMPP_CONNECTION (source), result, NULL)); wocky_xmpp_connection_recv_stanza_async (WOCKY_XMPP_CONNECTION (source), NULL, post_auth_recv_stanza, user_data); } static void post_auth_open_received (GObject *source, GAsyncResult *result, gpointer user_data) { g_assert (wocky_xmpp_connection_recv_open_finish ( WOCKY_XMPP_CONNECTION (source), result, NULL, NULL, NULL, NULL, NULL, NULL)); wocky_xmpp_connection_send_close_async (WOCKY_XMPP_CONNECTION (source), NULL, post_auth_close_sent, user_data); } static void post_auth_open_sent (GObject *source, GAsyncResult *result, gpointer user_data) { g_assert (wocky_xmpp_connection_send_open_finish ( WOCKY_XMPP_CONNECTION (source), result, NULL)); wocky_xmpp_connection_recv_open_async (WOCKY_XMPP_CONNECTION (source), NULL, post_auth_open_received, user_data); } static void sasl_auth_finished_cb (GObject *source, GAsyncResult *res, gpointer user_data) { GError *auth_error = NULL; test_t *test = (test_t *) user_data; if (wocky_sasl_auth_authenticate_finish (WOCKY_SASL_AUTH (source), res, &auth_error)) { authenticated = TRUE; wocky_xmpp_connection_reset (conn); wocky_xmpp_connection_send_open_async (conn, test->servername, NULL, "1.0", NULL, NULL, NULL, post_auth_open_sent, NULL); } else { error = auth_error; run_done = TRUE; g_main_loop_quit (mainloop); } } static void feature_stanza_received (GObject *source, GAsyncResult *res, gpointer user_data) { WockyStanza *stanza; WockyAuthHandler *test_handler; WockyAuthRegistry *auth_registry; test_t *test = (test_t *) user_data; stanza = wocky_xmpp_connection_recv_stanza_finish ( WOCKY_XMPP_CONNECTION (source), res, NULL); g_assert (stanza != NULL); g_assert (sasl == NULL); sasl = wocky_sasl_auth_new (test->servername, test->wrong_username ? "wrong" : test->username, test->wrong_password ? "wrong" : test->password, WOCKY_XMPP_CONNECTION (source), NULL); g_object_get (sasl, "auth-registry", &auth_registry, NULL); test_handler = WOCKY_AUTH_HANDLER (wocky_test_sasl_handler_new ()); wocky_auth_registry_add_handler (auth_registry, test_handler); g_object_unref (test_handler); g_object_unref (auth_registry); wocky_sasl_auth_authenticate_async (sasl, stanza, current_test->allow_plain, FALSE, NULL, sasl_auth_finished_cb, test); g_object_unref (stanza); } static void stream_open_received (GObject *source, GAsyncResult *res, gpointer user_data) { g_assert (wocky_xmpp_connection_recv_open_finish ( WOCKY_XMPP_CONNECTION (source), res, NULL, NULL, NULL, NULL, NULL, NULL)); /* Get the features stanza and wait for the connection closing*/ wocky_xmpp_connection_recv_stanza_async (WOCKY_XMPP_CONNECTION (source), NULL, feature_stanza_received, user_data); } static void stream_open_sent (GObject *source, GAsyncResult *res, gpointer user_data) { g_assert (wocky_xmpp_connection_send_open_finish ( WOCKY_XMPP_CONNECTION (source), res, NULL)); wocky_xmpp_connection_recv_open_async (WOCKY_XMPP_CONNECTION (source), NULL, stream_open_received, user_data); } static void run_test (gconstpointer user_data) { TestSaslAuthServer *server; WockyTestStream *stream; test_t *test = (test_t *) user_data; stream = g_object_new (WOCKY_TYPE_TEST_STREAM, NULL); server = test_sasl_auth_server_new (stream->stream0, test->mech, test->username, test->password, test->servername, test->problem, TRUE); authenticated = FALSE; run_done = FALSE; current_test = test; xmpp_connection = stream->stream1; conn = wocky_xmpp_connection_new (xmpp_connection); wocky_xmpp_connection_send_open_async (conn, test->servername, NULL, "1.0", NULL, NULL, NULL, stream_open_sent, test); if (!run_done) { g_main_loop_run (mainloop); } if (sasl != NULL) { g_object_unref (sasl); sasl = NULL; } test_sasl_auth_server_stop (server); g_object_unref (server); g_object_unref (stream); g_object_unref (conn); if (test->domain == 0) { g_assert (authenticated); g_assert_no_error (error); } else { g_assert (!authenticated); g_assert_error (error, test->domain, test->code); } if (error != NULL) g_error_free (error); error = NULL; } #define SUCCESS(desc, mech, allow_plain) \ { desc, mech, allow_plain, 0, 0, SERVER_PROBLEM_NO_PROBLEM, FALSE, FALSE, \ "test", "test123", NULL } #define FAIL(desc, mech, allow_plain, domain, code, problem) \ { desc, mech, allow_plain, domain, code, problem, FALSE, FALSE, \ "test", "test123", NULL } int main (int argc, char **argv) { int result; test_t tests[] = { SUCCESS("/xmpp-sasl/normal-auth", NULL, TRUE), SUCCESS("/xmpp-sasl/no-plain", NULL, FALSE), SUCCESS("/xmpp-sasl/only-plain", "PLAIN", TRUE), SUCCESS("/xmpp-sasl/only-digest-md5", "DIGEST-MD5", TRUE), { "/xmpp-sasl/digest-md5-final-data-in-success", "DIGEST-MD5", TRUE, 0, 0, SERVER_PROBLEM_FINAL_DATA_IN_SUCCESS, FALSE, FALSE, "test", "test123", NULL }, FAIL("/xmpp-sasl/no-supported-mechs", "NONSENSE", TRUE, WOCKY_AUTH_ERROR, WOCKY_AUTH_ERROR_NO_SUPPORTED_MECHANISMS, SERVER_PROBLEM_NO_PROBLEM), FAIL("/xmpp-sasl/refuse-plain-only", "PLAIN", FALSE, WOCKY_AUTH_ERROR, WOCKY_AUTH_ERROR_NO_SUPPORTED_MECHANISMS, SERVER_PROBLEM_NO_PROBLEM), FAIL("/xmpp-sasl/no-sasl-support", NULL, TRUE, WOCKY_AUTH_ERROR, WOCKY_AUTH_ERROR_NOT_SUPPORTED, SERVER_PROBLEM_NO_SASL), { "/xmpp-sasl/wrong-username-plain", "PLAIN", TRUE, WOCKY_AUTH_ERROR, WOCKY_AUTH_ERROR_FAILURE, SERVER_PROBLEM_INVALID_USERNAME, TRUE, FALSE, "test", "test123" }, { "/xmpp-sasl/wrong-username-md5", "DIGEST-MD5", TRUE, WOCKY_AUTH_ERROR, WOCKY_AUTH_ERROR_FAILURE, SERVER_PROBLEM_INVALID_USERNAME, TRUE, FALSE, "test", "test123" }, { "/xmpp-sasl/wrong-password-plain", "PLAIN", TRUE, WOCKY_AUTH_ERROR, WOCKY_AUTH_ERROR_FAILURE, SERVER_PROBLEM_INVALID_PASSWORD, FALSE, TRUE, "test", "test123" }, { "/xmpp-sasl/wrong-password-md5", "DIGEST-MD5", TRUE, WOCKY_AUTH_ERROR, WOCKY_AUTH_ERROR_FAILURE, SERVER_PROBLEM_INVALID_PASSWORD, FALSE, TRUE, "test", "test123" }, /* Redo the MD5-DIGEST test with a username, password and realm that * happens to generate a \0 byte in the md5 hash of * 'username:realm:password'. This used to trigger a bug in the sasl helper * when calculating the A! part of the response MD5-DIGEST hash */ { "/xmpp-sasl/digest-md5-A1-null-byte", "DIGEST-MD5", TRUE, 0, 0, SERVER_PROBLEM_NO_PROBLEM, FALSE, FALSE, "moose", "something", "cass-x200s" }, /* Redo the MD5-DIGEST test with extra whitespace in the challenge */ { "/xmpp-sasl/digest-md5-spaced-challenge", "DIGEST-MD5", FALSE, 0, 0, SERVER_PROBLEM_SPACE_CHALLENGE, FALSE, FALSE, "moose", "something", "cass-x200s" }, /* Redo the MD5-DIGEST test with extra \ escapes in the challenge */ { "/xmpp-sasl/digest-md5-slash-challenge", "DIGEST-MD5", FALSE, 0, 0, SERVER_PROBLEM_SLASH_CHALLENGE, FALSE, FALSE, "moose", "something", "cass-x200s" }, { "/xmpp-sasl/external-handler-fail", "X-TEST", FALSE, WOCKY_AUTH_ERROR, WOCKY_AUTH_ERROR_FAILURE, SERVER_PROBLEM_INVALID_PASSWORD, FALSE, FALSE, "dave", "daisy daisy", "hal" }, { "/xmpp-sasl/external-handler-succeed", "X-TEST", FALSE, 0, 0, SERVER_PROBLEM_NO_PROBLEM, FALSE, FALSE, "ripley", "open sesame", "mother" }, }; test_init (argc, argv); mainloop = g_main_loop_new (NULL, FALSE); for (guint i = 0; i < G_N_ELEMENTS (tests); i++) g_test_add_data_func (tests[i].description, &tests[i], run_test); result = g_test_run (); test_deinit (); return result; } telepathy-gabble-0.18.2/lib/ext/wocky/tests/wocky-stanza-test.c0000644000175000017500000006011612200204546024433 0ustar00smcvsmcv00000000000000#ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include #include "wocky-test-helper.h" static void test_copy (void) { WockyStanza *iq, *copy, *expected; iq = wocky_stanza_build (WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_GET, "juliet@example.com", "romeo@example.net", '@', "id", "one", '(', "query", ':', "http://jabber.org/protocol/disco#items", ')', NULL); /* just to make sure */ expected = wocky_stanza_build (WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_GET, "juliet@example.com", "romeo@example.net", '@', "id", "one", '(', "query", ':', "http://jabber.org/protocol/disco#items", ')', NULL); copy = wocky_stanza_copy (iq); g_assert (copy != NULL); test_assert_stanzas_equal (iq, copy); test_assert_stanzas_equal (expected, copy); g_object_unref (iq); g_object_unref (copy); g_object_unref (expected); } static void test_build_iq_result_simple_ack (void) { WockyStanza *iq, *reply, *expected; iq = wocky_stanza_build (WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_GET, "juliet@example.com", "romeo@example.net", '@', "id", "one", '(', "query", ':', "http://jabber.org/protocol/disco#items", ')', NULL); /* Send a simple ACK */ expected = wocky_stanza_build (WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_RESULT, "romeo@example.net", "juliet@example.com", '@', "id", "one", NULL); reply = wocky_stanza_build_iq_result (iq, NULL); g_assert (reply != NULL); test_assert_stanzas_equal (reply, expected); g_object_unref (reply); g_object_unref (expected); g_object_unref (iq); } static void test_build_iq_result_complex_reply (void) { WockyStanza *iq, *reply, *expected; iq = wocky_stanza_build (WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_GET, "juliet@example.com", "romeo@example.net", '@', "id", "one", '(', "query", ':', "http://jabber.org/protocol/disco#items", ')', NULL); /* Send a more complex reply */ expected = wocky_stanza_build (WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_RESULT, "romeo@example.net", "juliet@example.com", '@', "id", "one", '(', "query", ':', "http://jabber.org/protocol/disco#items", '(', "item", '@', "jid", "streamhostproxy.example.net", '@', "name", "Bytestreams Proxy", ')', ')', NULL); reply = wocky_stanza_build_iq_result (iq, '(', "query", ':', "http://jabber.org/protocol/disco#items", '(', "item", '@', "jid", "streamhostproxy.example.net", '@', "name", "Bytestreams Proxy", ')', ')', NULL); g_assert (reply != NULL); test_assert_stanzas_equal (reply, expected); g_object_unref (reply); g_object_unref (expected); g_object_unref (iq); } static void test_build_iq_result_no_to_attr (void) { WockyStanza *iq, *reply, *expected; /* Send a reply to an IQ with no "to" attribute. */ iq = wocky_stanza_build (WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_GET, "juliet@example.com", NULL, '@', "id", "one", '(', "query", ':', "http://jabber.org/protocol/disco#items", ')', NULL); /* Send a simple ACK */ expected = wocky_stanza_build (WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_RESULT, NULL, "juliet@example.com", '@', "id", "one", NULL); reply = wocky_stanza_build_iq_result (iq, NULL); g_assert (reply != NULL); test_assert_stanzas_equal (reply, expected); g_object_unref (reply); g_object_unref (expected); g_object_unref (iq); } static void test_build_iq_error_simple_error (void) { WockyStanza *iq, *reply, *expected; iq = wocky_stanza_build (WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_GET, "juliet@example.com", "romeo@example.net", '@', "id", "one", '(', "query", ':', "http://jabber.org/protocol/disco#items", ')', NULL); /* Send a simple error */ expected = wocky_stanza_build (WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_ERROR, "romeo@example.net", "juliet@example.com", '@', "id", "one", '(', "query", ':', "http://jabber.org/protocol/disco#items", ')', NULL); reply = wocky_stanza_build_iq_error (iq, NULL); g_assert (reply != NULL); test_assert_stanzas_equal (reply, expected); g_object_unref (reply); g_object_unref (expected); g_object_unref (iq); } static void test_build_iq_error_complex (void) { WockyStanza *iq, *reply, *expected; iq = wocky_stanza_build (WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_GET, "juliet@example.com", "romeo@example.net", '@', "id", "one", '(', "query", ':', "http://jabber.org/protocol/disco#items", ')', NULL); /* Send a more complex reply */ expected = wocky_stanza_build (WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_ERROR, "romeo@example.net", "juliet@example.com", '@', "id", "one", '(', "query", ':', "http://jabber.org/protocol/disco#items", ')', '(', "error", '@', "code", "403", '@', "type", "auth", ')', NULL); reply = wocky_stanza_build_iq_error (iq, '(', "error", '@', "code", "403", '@', "type", "auth", ')', NULL); g_assert (reply != NULL); test_assert_stanzas_equal (reply, expected); g_object_unref (reply); g_object_unref (expected); g_object_unref (iq); } static void check_error (WockyStanza *stanza, GQuark domain, gint code, const gchar *msg) { GError *error = NULL; g_assert (wocky_stanza_extract_stream_error (stanza, &error)); g_assert_error (error, domain, code); g_assert_cmpstr (error->message, ==, msg); g_error_free (error); } static void test_extract_stanza_error (void) { WockyStanza *stanza; GError *error = NULL; /* Valid stream error without message */ stanza = wocky_stanza_build (WOCKY_STANZA_TYPE_STREAM_ERROR, WOCKY_STANZA_SUB_TYPE_NONE, NULL, NULL, ':', WOCKY_XMPP_NS_STREAM, '(', "conflict", ':', WOCKY_XMPP_NS_STREAMS, ')', NULL); check_error (stanza, WOCKY_XMPP_STREAM_ERROR, WOCKY_XMPP_STREAM_ERROR_CONFLICT, ""); g_object_unref (stanza); /* Valid stream error with message */ stanza = wocky_stanza_build (WOCKY_STANZA_TYPE_STREAM_ERROR, WOCKY_STANZA_SUB_TYPE_NONE, NULL, NULL, ':', WOCKY_XMPP_NS_STREAM, '(', "system-shutdown", ':', WOCKY_XMPP_NS_STREAMS, ')', '(', "text", ':', WOCKY_XMPP_NS_STREAMS, '$', "bye bye", ')', NULL); check_error (stanza, WOCKY_XMPP_STREAM_ERROR, WOCKY_XMPP_STREAM_ERROR_SYSTEM_SHUTDOWN, "bye bye"); g_object_unref (stanza); /* Unknown stream error */ stanza = wocky_stanza_build (WOCKY_STANZA_TYPE_STREAM_ERROR, WOCKY_STANZA_SUB_TYPE_NONE, NULL, NULL, ':', WOCKY_XMPP_NS_STREAM, '(', "badger", ':', WOCKY_XMPP_NS_STREAMS, ')', NULL); check_error (stanza, WOCKY_XMPP_STREAM_ERROR, WOCKY_XMPP_STREAM_ERROR_UNKNOWN, ""); g_object_unref (stanza); /* Not an error */ stanza = wocky_stanza_build (WOCKY_STANZA_TYPE_MESSAGE, WOCKY_STANZA_SUB_TYPE_NONE, NULL, NULL, ':', WOCKY_XMPP_NS_STREAM, NULL); g_assert (!wocky_stanza_extract_stream_error (stanza, &error)); g_assert_no_error (error); g_object_unref (stanza); } static void test_extract_errors_not_error (void) { WockyStanza *stanza; WockyXmppErrorType type; GError *core = NULL, *specialized = NULL; WockyNode *specialized_node = NULL; gboolean ret; /* As a prelude, check that it does the right thing for non-errors. */ stanza = wocky_stanza_build ( WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_GET, "from", "to", '(', "hello-thar", ')', NULL); ret = wocky_stanza_extract_errors (stanza, &type, &core, &specialized, &specialized_node); g_assert (!ret); g_assert_no_error (core); g_assert_no_error (specialized); g_assert (specialized_node == NULL); g_object_unref (stanza); } static void test_extract_errors_without_description (void) { WockyStanza *stanza; WockyXmppErrorType type; GError *core = NULL, *specialized = NULL; WockyNode *specialized_node = NULL; gboolean ret; /* Test a boring error with no description */ stanza = wocky_stanza_build ( WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_ERROR, "from", "to", '(', "error", '@', "type", "modify", '(', "bad-request", ':', WOCKY_XMPP_NS_STANZAS, ')', ')', NULL); ret = wocky_stanza_extract_errors (stanza, &type, &core, &specialized, &specialized_node); g_assert (ret); g_assert_cmpuint (type, ==, WOCKY_XMPP_ERROR_TYPE_MODIFY); g_assert_error (core, WOCKY_XMPP_ERROR, WOCKY_XMPP_ERROR_BAD_REQUEST); g_assert_cmpstr (core->message, ==, ""); g_clear_error (&core); g_assert_no_error (specialized); g_assert (specialized_node == NULL); g_object_unref (stanza); } static void test_extract_errors_with_text (void) { WockyStanza *stanza; const gchar *description = "I am a sentence."; WockyXmppErrorType type; GError *core = NULL, *specialized = NULL; WockyNode *specialized_node = NULL; /* Now a different error with some text */ stanza = wocky_stanza_build ( WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_ERROR, "from", "to", '(', "error", '@', "type", "cancel", '(', "item-not-found", ':', WOCKY_XMPP_NS_STANZAS, ')', '(', "text", ':', WOCKY_XMPP_NS_STANZAS, '$', description, ')', ')', NULL); wocky_stanza_extract_errors (stanza, &type, &core, &specialized, &specialized_node); g_assert_cmpuint (type, ==, WOCKY_XMPP_ERROR_TYPE_CANCEL); g_assert_error (core, WOCKY_XMPP_ERROR, WOCKY_XMPP_ERROR_ITEM_NOT_FOUND); g_assert_cmpstr (core->message, ==, description); g_clear_error (&core); g_assert_no_error (specialized); g_assert (specialized_node == NULL); g_object_unref (stanza); } static void test_extract_errors_application_specific_unknown (void) { WockyStanza *stanza; WockyXmppErrorType type; GError *core = NULL, *specialized = NULL; WockyNode *specialized_node = NULL; /* Another error, with an application-specific element we don't understand */ stanza = wocky_stanza_build ( WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_ERROR, "from", "to", '(', "error", '@', "type", "cancel", '(', "subscription-required", ':', WOCKY_XMPP_NS_STANZAS, ')', '(', "buy-a-private-cloud", ':', "http://example.com/angry-cloud", ')', ')', NULL); wocky_stanza_extract_errors (stanza, &type, &core, &specialized, &specialized_node); g_assert_cmpuint (type, ==, WOCKY_XMPP_ERROR_TYPE_CANCEL); g_assert_error (core, WOCKY_XMPP_ERROR, WOCKY_XMPP_ERROR_SUBSCRIPTION_REQUIRED); g_assert_cmpstr (core->message, ==, ""); g_clear_error (&core); g_assert_no_error (specialized); /* The namespace is not registered, @specialized is not set. However we * assume that any other namespace element is a specialized error, and it * should get returned in @specialized_node */ g_assert (specialized_node); g_assert_cmpstr (specialized_node->name, ==, "buy-a-private-cloud"); g_assert_cmpstr (wocky_node_get_ns (specialized_node), ==, "http://example.com/angry-cloud"); g_object_unref (stanza); } static void test_extract_errors_jingle_error (void) { WockyStanza *stanza; const gchar *description = "I am a sentence."; WockyXmppErrorType type; GError *core = NULL, *specialized = NULL; WockyNode *specialized_node = NULL; /* A Jingle error! With the child nodes in an erratic order */ stanza = wocky_stanza_build ( WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_ERROR, "from", "to", '(', "error", '@', "type", "cancel", '(', "tie-break", ':', WOCKY_XMPP_NS_JINGLE_ERRORS, ')', '(', "text", ':', WOCKY_XMPP_NS_STANZAS, '$', description, ')', '(', "conflict", ':', WOCKY_XMPP_NS_STANZAS, ')', ')', NULL); wocky_stanza_extract_errors (stanza, &type, &core, &specialized, &specialized_node); g_assert_cmpuint (type, ==, WOCKY_XMPP_ERROR_TYPE_CANCEL); g_assert_error (core, WOCKY_XMPP_ERROR, WOCKY_XMPP_ERROR_CONFLICT); g_assert_cmpstr (core->message, ==, description); g_clear_error (&core); g_assert_error (specialized, WOCKY_JINGLE_ERROR, WOCKY_JINGLE_ERROR_TIE_BREAK); g_assert_cmpstr (specialized->message, ==, description); g_clear_error (&specialized); g_assert (specialized_node != NULL); g_assert_cmpstr (specialized_node->name, ==, "tie-break"); /* With the same stanza, let's try ignoring all out params: */ wocky_stanza_extract_errors (stanza, NULL, NULL, NULL, NULL); g_object_unref (stanza); } static void test_extract_errors_extra_application_specific (void) { WockyStanza *stanza; const gchar *description = "I am a sentence."; WockyXmppErrorType type; GError *core = NULL, *specialized = NULL; WockyNode *specialized_node = NULL; /* Jingle error! + Bogus extra app specific error, which should be ignored */ stanza = wocky_stanza_build ( WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_ERROR, "from", "to", '(', "error", '@', "type", "cancel", '(', "tie-break", ':', WOCKY_XMPP_NS_JINGLE_ERRORS, ')', '(', "out-of-order", ':', WOCKY_XMPP_NS_JINGLE_ERRORS, ')', '(', "text", ':', WOCKY_XMPP_NS_STANZAS, '$', description, ')', '(', "conflict", ':', WOCKY_XMPP_NS_STANZAS, ')', ')', NULL); wocky_stanza_extract_errors (stanza, &type, &core, &specialized, &specialized_node); g_assert_cmpuint (type, ==, WOCKY_XMPP_ERROR_TYPE_CANCEL); g_assert_error (core, WOCKY_XMPP_ERROR, WOCKY_XMPP_ERROR_CONFLICT); g_assert_cmpstr (core->message, ==, description); g_clear_error (&core); g_assert_error (specialized, WOCKY_JINGLE_ERROR, WOCKY_JINGLE_ERROR_TIE_BREAK); g_assert_cmpstr (specialized->message, ==, description); g_clear_error (&specialized); g_assert (specialized_node != NULL); g_assert_cmpstr (specialized_node->name, ==, "tie-break"); g_object_unref (stanza); } static void test_extract_errors_legacy_code (void) { WockyStanza *stanza; WockyXmppErrorType type; GError *core = NULL, *specialized = NULL; WockyNode *specialized_node = NULL; /* How about a legacy error code? */ stanza = wocky_stanza_build ( WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_ERROR, "from", "to", '(', "error", '@', "code", "408", ')', NULL); wocky_stanza_extract_errors (stanza, &type, &core, &specialized, &specialized_node); /* XEP-0086 §3 says that 408 maps to remote-server-timeout, type=wait */ g_assert_cmpuint (type, ==, WOCKY_XMPP_ERROR_TYPE_WAIT); g_assert_error (core, WOCKY_XMPP_ERROR, WOCKY_XMPP_ERROR_REMOTE_SERVER_TIMEOUT); /* No assertion about the message. As an implementation detail, it's probably * the definition of r-s-t from XMPP Core. */ g_clear_error (&core); g_assert_no_error (specialized); g_assert (specialized_node == NULL); g_object_unref (stanza); } static void test_extract_errors_unrecognised_condition (void) { WockyNodeTree *error_tree; WockyXmppErrorType type; GError *core = NULL, *specialized = NULL; WockyNode *specialized_node = NULL; const gchar *text = "The room is currently overactive, please try again later"; /* I got a error back from prosody with type='wait', but * Wocky didn't know about policy-violation (which was introduced in * RFC6120). Not only did it ignore , it also ignored * type='wait' and returned the default, WOCKY_XMPP_ERROR_TYPE_CANCEL. */ g_test_bug ("43166#c9"); error_tree = wocky_node_tree_new ("error", WOCKY_XMPP_NS_JABBER_CLIENT, '@', "type", "wait", '(', "typo-violation", ':', WOCKY_XMPP_NS_STANZAS, ')', '(', "text", ':', WOCKY_XMPP_NS_STANZAS, '$', text, ')', NULL); wocky_xmpp_error_extract (wocky_node_tree_get_top_node (error_tree), &type, &core, &specialized, &specialized_node); g_assert_cmpuint (type, ==, WOCKY_XMPP_ERROR_TYPE_WAIT); /* Wocky should default to undefined-condition when the server returns an * unknown core error element. */ g_assert_error (core, WOCKY_XMPP_ERROR, WOCKY_XMPP_ERROR_UNDEFINED_CONDITION); g_assert_cmpstr (core->message, ==, text); g_clear_error (&core); /* The unrecognised error element was in the :xmpp-stanzas namespace, so it * shouldn't be returned as a specialized error. */ g_assert_no_error (specialized); g_assert (specialized_node == NULL); g_object_unref (error_tree); } static void test_extract_errors_no_sense (void) { WockyStanza *stanza; WockyXmppErrorType type; GError *core = NULL, *specialized = NULL; WockyNode *specialized_node = NULL; /* An error that makes no sense */ stanza = wocky_stanza_build ( WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_ERROR, "from", "to", '(', "error", '@', "aoeu", "snth", '(', "hoobily-lala-whee", ')', '(', "møøse", ':', WOCKY_XMPP_NS_STANZAS, ')', ')', NULL); wocky_stanza_extract_errors (stanza, &type, &core, &specialized, &specialized_node); /* 'cancel' is the most sensible default if we have no idea. */ g_assert_cmpuint (type, ==, WOCKY_XMPP_ERROR_TYPE_CANCEL); g_assert_error (core, WOCKY_XMPP_ERROR, WOCKY_XMPP_ERROR_UNDEFINED_CONDITION); g_clear_error (&core); g_assert_no_error (specialized); g_assert (specialized_node != NULL); g_assert_cmpstr (specialized_node->name, ==, "hoobily-lala-whee"); g_object_unref (stanza); } static void test_extract_errors_not_really (void) { WockyStanza *stanza; WockyXmppErrorType type; GError *core = NULL, *specialized = NULL; WockyNode *specialized_node = NULL; /* And finally, a stanza with type='error' but no at all... */ stanza = wocky_stanza_build ( WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_ERROR, "from", "to", NULL); wocky_stanza_extract_errors (stanza, &type, &core, &specialized, &specialized_node); /* 'cancel' is the most sensible default if we have no idea. */ g_assert_cmpuint (type, ==, WOCKY_XMPP_ERROR_TYPE_CANCEL); g_assert_error (core, WOCKY_XMPP_ERROR, WOCKY_XMPP_ERROR_UNDEFINED_CONDITION); g_clear_error (&core); g_assert_no_error (specialized); g_assert (specialized_node == NULL); g_object_unref (stanza); } #define assert_cmperr(e1, e2) \ G_STMT_START { \ g_assert_error (e1, e2->domain, e2->code); \ g_assert_cmpstr (e1->message, ==, e2->message); \ } G_STMT_END static void test_stanza_error_to_node_core (void) { GError *e = NULL; GError *core = NULL, *specialized = NULL; const gchar *description = "bzzzt"; WockyStanza *stanza, *expected; /* An XMPP Core stanza error */ g_set_error_literal (&e, WOCKY_XMPP_ERROR, WOCKY_XMPP_ERROR_REMOTE_SERVER_TIMEOUT, description); stanza = wocky_stanza_build ( WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_ERROR, "from", "to", NULL); wocky_stanza_error_to_node (e, wocky_stanza_get_top_node (stanza)); expected = wocky_stanza_build ( WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_ERROR, "from", "to", '(', "error", '@', "type", "wait", '@', "code", "504", /* Per XEP-0086 */ '(', "remote-server-timeout", ':', WOCKY_XMPP_NS_STANZAS, ')', '(', "text", ':', WOCKY_XMPP_NS_STANZAS, '$', description, ')', ')', NULL); test_assert_stanzas_equal (stanza, expected); /* Let's see how it roundtrips: */ wocky_stanza_extract_errors (stanza, NULL, &core, &specialized, NULL); assert_cmperr (e, core); g_assert_no_error (specialized); g_object_unref (stanza); g_object_unref (expected); g_clear_error (&e); g_clear_error (&core); } static void test_stanza_error_to_node_jingle (void) { GError *e = NULL; GError *core = NULL, *specialized = NULL; const gchar *description = "bzzzt"; WockyStanza *stanza, *expected; /* How about a nice game of Jingle? */ g_set_error_literal (&e, WOCKY_JINGLE_ERROR, WOCKY_JINGLE_ERROR_UNKNOWN_SESSION, description); stanza = wocky_stanza_build ( WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_ERROR, "from", "to", NULL); wocky_stanza_error_to_node (e, wocky_stanza_get_top_node (stanza)); expected = wocky_stanza_build ( WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_ERROR, "from", "to", '(', "error", '@', "type", "cancel", '@', "code", "404", /* Per XEP-0086 */ '(', "item-not-found", ':', WOCKY_XMPP_NS_STANZAS, ')', '(', "unknown-session", ':', WOCKY_XMPP_NS_JINGLE_ERRORS, ')', '(', "text", ':', WOCKY_XMPP_NS_STANZAS, '$', description, ')', ')', NULL); test_assert_stanzas_equal (stanza, expected); /* Let's see how it roundtrips: */ wocky_stanza_extract_errors (stanza, NULL, &core, &specialized, NULL); g_assert_error (core, WOCKY_XMPP_ERROR, WOCKY_XMPP_ERROR_ITEM_NOT_FOUND); assert_cmperr (e, specialized); g_object_unref (stanza); g_object_unref (expected); g_clear_error (&e); g_clear_error (&core); g_clear_error (&specialized); } static void test_unknown ( gconstpointer name_null_ns) { const gchar *name = name_null_ns; const gchar *ns = name + strlen (name) + 1; WockyStanza *stanza = wocky_stanza_new (name, ns); WockyStanzaType type; wocky_stanza_get_type_info (stanza, &type, NULL); g_assert_cmpuint (type, ==, WOCKY_STANZA_TYPE_UNKNOWN); g_object_unref (stanza); } int main (int argc, char **argv) { int result; test_init (argc, argv); g_test_add_func ("/xmpp-stanza/copy", test_copy); g_test_add_func ("/xmpp-stanza/iq-result/build-simple-ack", test_build_iq_result_simple_ack); g_test_add_func ("/xmpp-stanza/iq-result/build-complex-reply", test_build_iq_result_complex_reply); g_test_add_func ("/xmpp-stanza/iq-result/build-no-to-attr", test_build_iq_result_no_to_attr); g_test_add_func ("/xmpp-stanza/errors/build-simple", test_build_iq_error_simple_error); g_test_add_func ("/xmpp-stanza/errors/build-complex", test_build_iq_error_complex); g_test_add_func ("/xmpp-stanza/errors/extract-stanza", test_extract_stanza_error); g_test_add_func ("/xmpp-stanza/errors/not-error", test_extract_errors_not_error); g_test_add_func ("/xmpp-stanza/errors/without-description", test_extract_errors_without_description); g_test_add_func ("/xmpp-stanza/errors/with-text", test_extract_errors_with_text); g_test_add_func ("/xmpp-stanza/errors/application-specific-unknown", test_extract_errors_application_specific_unknown); g_test_add_func ("/xmpp-stanza/errors/jingle-error", test_extract_errors_jingle_error); g_test_add_func ("/xmpp-stanza/errors/extra-application-specific", test_extract_errors_extra_application_specific); g_test_add_func ("/xmpp-stanza/errors/legacy-code", test_extract_errors_legacy_code); g_test_add_func ("/xmpp-stanza/errors/unrecognised-condition", test_extract_errors_unrecognised_condition); g_test_add_func ("/xmpp-stanza/errors/no-sense", test_extract_errors_no_sense); g_test_add_func ("/xmpp-stanza/errors/not-really", test_extract_errors_not_really); g_test_add_func ("/xmpp-stanza/errors/stanza-to-node", test_stanza_error_to_node_core); g_test_add_func ("/xmpp-stanza/errors/stanza-to-node-jingle", test_stanza_error_to_node_jingle); g_test_add_data_func ("/xmpp-stanza/types/unknown-stanza-type", "this-will-never-be-real\0" WOCKY_XMPP_NS_JABBER_CLIENT, test_unknown); g_test_add_data_func ("/xmpp-stanza/types/wrong-namespaces", "challenge\0this:is:not:the:sasl:namespace", test_unknown); result = g_test_run (); test_deinit (); return result; } telepathy-gabble-0.18.2/lib/ext/wocky/tests/wocky-session-test.c0000644000175000017500000000330012200204546024606 0ustar00smcvsmcv00000000000000#ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include #include "wocky-test-helper.h" static void test_instantiation (void) { WockyTestStream *stream; WockyXmppConnection *connection; WockySession *session; stream = g_object_new (WOCKY_TYPE_TEST_STREAM, NULL); connection = wocky_xmpp_connection_new (stream->stream0); session = wocky_session_new_with_connection (connection, "example.com"); g_assert (session != NULL); g_assert (WOCKY_IS_SESSION (session)); g_object_unref (stream); g_object_unref (connection); g_object_unref (session); } static void test_get_porter (void) { test_data_t *test = setup_test (); WockySession *session; WockyPorter *porter; session = wocky_session_new_with_connection (test->in, "example.com"); porter = wocky_session_get_porter (session); g_assert (WOCKY_IS_PORTER (porter)); g_object_unref (session); teardown_test (test); } static void test_get_contact_factory (void) { test_data_t *test = setup_test (); WockySession *session; WockyContactFactory *factory; session = wocky_session_new_with_connection (test->in, "example.com"); factory = wocky_session_get_contact_factory (session); g_assert (WOCKY_IS_CONTACT_FACTORY (factory)); g_object_unref (session); teardown_test (test); } int main (int argc, char **argv) { int result; test_init (argc, argv); g_test_add_func ("/session/instantiation", test_instantiation); g_test_add_func ("/session/get-porter", test_get_porter); g_test_add_func ("/session/get-contact-factory", test_get_contact_factory); result = g_test_run (); test_deinit (); return result; } telepathy-gabble-0.18.2/lib/ext/wocky/tests/wocky-scram-sha1-test.c0000644000175000017500000000742212200204546025073 0ustar00smcvsmcv00000000000000/* * wocky-scram-sha1-test.c - Test for wocky scram-sha1 * Copyright (C) 2010 Sjoerd Simons * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include "wocky-test-helper.h" typedef struct { guint32 seed; gchar *server; gchar *user; gchar *password; gchar *client_initial_response; gchar *server_initial_response; gchar *client_final_response; gchar *server_final_response; } testcase; static void test_scram_sha1 (testcase *test) { WockyAuthHandler *scram; GString *out, *in; GError *error = NULL; g_random_set_seed (test->seed); scram = WOCKY_AUTH_HANDLER (wocky_sasl_scram_new ( test->server, test->user, test->password)); g_assert (wocky_auth_handler_get_initial_response (scram, &out, &error)); g_assert_no_error (error); g_assert_cmpstr (test->client_initial_response, ==, out->str); g_string_free (out, TRUE); /* Initial challenge, response */ in = g_string_new (test->server_initial_response); (wocky_auth_handler_handle_auth_data (scram, in, &out, &error)); g_assert_no_error (error); g_assert_cmpstr (test->client_final_response, ==, out->str); g_string_free (in, TRUE); g_string_free (out, TRUE); in = g_string_new (test->server_final_response); out = NULL; g_assert (wocky_auth_handler_handle_auth_data (scram, in, &out, &error)); g_assert_no_error (error); g_assert (out == NULL); g_string_free (in, TRUE); g_assert (wocky_auth_handler_handle_success (scram, &error)); g_assert_no_error (error); g_object_unref (scram); } /* Some static tests as generated by trail of our current implementation, which * has been tested against real servers. These testcases are mostly here * because to prevent regressions and differences of output on different * platforms */ static testcase tests[] = { { 42, "example.com", "harry", "blaat", /* client first */ "n,,n=harry,r=ZtzhX7M96stcA2LzDpX1Lmr0Y7tH1JnHvK5BmRQsy5g=", /* server first */ "r=ZtzhX7M96stcA2LzDpX1Lmr0Y7tH1JnHvK5BmRQsy5g=deadfish," "s=snakes==,i=128", /* client final */ "c=biws,r=ZtzhX7M96stcA2LzDpX1Lmr0Y7tH1JnHvK5BmRQsy5g=deadfish," "p=0933cKmf/GHBIh+GYTROuRT78zM=", /* server final */ "v=zW58Ag+pg9C8lGkof3wb7/jvQQQ=" }, { 0xcafe, "eagle.co.uk", "watson", "dna", /* client first */ "n,,n=watson,r=xHkPPK3a6sJZYzbEJVq965vi70Mt9ZaFPI3PdSj8SEU=", /* server first */ "r=xHkPPK3a6sJZYzbEJVq965vi70Mt9ZaFPI3PdSj8SEU=doublehelix," "s=francis=,i=4096", /* client final */ "c=biws," "r=xHkPPK3a6sJZYzbEJVq965vi70Mt9ZaFPI3PdSj8SEU=doublehelix," "p=V4VHXC1JL++UDZWRg82mcP16Hdc=", /* server final */ "v=LYJrt3+nDh0XOEjKZC6DRccsnG0=" }, { 0, } }; int main (int argc, char **argv) { int i; test_init (argc, argv); for (i = 0; tests[i].seed != 0; i++) { gchar *name = g_strdup_printf ("/scram-sha1/test-%d", i); g_test_add_data_func (name, tests + i, (void (*)(const void *)) test_scram_sha1); g_free (name); } return g_test_run (); } telepathy-gabble-0.18.2/lib/ext/wocky/tests/wocky-sasl-utils-test.c0000644000175000017500000001315512200204546025234 0ustar00smcvsmcv00000000000000/* * wocky-sasl-utils-test.c - Test sasl utilities * Copyright (C) 2010 Sjoerd Simons * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include typedef struct { guint8 *key; gsize key_len; guint8 *data; gsize data_len; guint8 *result; } digest_test; /* HMAC-SHA1 test vectors as per RFC 2202 */ /* Test 1 */ guint8 key_sha1_test1[] = { 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b }; guint8 result_sha1_test1[] = { 0xb6, 0x17, 0x31, 0x86, 0x55, 0x05, 0x72, 0x64, 0xe2, 0x8b, 0xc0, 0xb6, 0xfb, 0x37, 0x8c, 0x8e, 0xf1, 0x46, 0xbe, 0x00 }; /* Test 2 */ guint8 result_sha1_test2[] = { 0xef, 0xfc, 0xdf, 0x6a, 0xe5, 0xeb, 0x2f, 0xa2, 0xd2, 0x74, 0x16, 0xd5, 0xf1, 0x84, 0xdf, 0x9c, 0x25, 0x9a, 0x7c, 0x79 }; /* Test 3 */ guint8 key_sha1_test3[] = { 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa }; guint8 data_sha1_test3[] = { 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd }; guint8 result_sha1_test3[] = { 0x12, 0x5d, 0x73, 0x42, 0xb9, 0xac, 0x11, 0xcd, 0x91, 0xa3, 0x9a, 0xf4, 0x8a, 0xa1, 0x7b, 0x4f, 0x63, 0xf1, 0x75, 0xd3 }; /* Test 4 */ guint8 key_sha1_test4[] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19 }; guint8 data_sha1_test4[] = { 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd }; guint8 result_sha1_test4[] = { 0x4c, 0x90, 0x07, 0xf4, 0x02, 0x62, 0x50, 0xc6, 0xbc, 0x84, 0x14, 0xf9, 0xbf, 0x50, 0xc8, 0x6c, 0x2d, 0x72, 0x35, 0xda }; /* Test 5 */ guint8 key_sha1_test5[] = { 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c }; guint8 result_sha1_test5[] = { 0x4c, 0x1a, 0x03, 0x42, 0x4b, 0x55, 0xe0, 0x7f, 0xe7, 0xf2, 0x7b, 0xe1, 0xd5, 0x8b, 0xb9, 0x32, 0x4a, 0x9a, 0x5a, 0x04 }; /* Test 6 & 7*/ guint8 key_sha1_test6_7[] = { 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa }; guint8 result_sha1_test6[] = { 0xaa, 0x4a, 0xe5, 0xe1, 0x52, 0x72, 0xd0, 0x0e, 0x95, 0x70, 0x56, 0x37, 0xce, 0x8a, 0x3b, 0x55, 0xed, 0x40, 0x21, 0x12 }; guint8 result_sha1_test7[] = { 0xe8, 0xe9, 0x9d, 0xf, 0x45, 0x23, 0x7d, 0x78, 0x6d, 0x6b, 0xba, 0xa7, 0x96, 0x5c, 0x78, 0x8, 0xbb, 0xff, 0x1a, 0x91 }; digest_test hmac_sha1_tests[] = { { key_sha1_test1, 20, (guint8 *) "Hi There", 8, result_sha1_test1 }, { (guint8 *) "Jefe", 4, (guint8 *) "what do ya want for nothing?", 28, result_sha1_test2 }, { key_sha1_test3, 20, data_sha1_test3, 50, result_sha1_test3 }, { key_sha1_test4, 25, data_sha1_test4, 50, result_sha1_test4 }, { key_sha1_test5, 20, (guint8 *) "Test With Truncation", 20, result_sha1_test5 }, { key_sha1_test6_7, 80, (guint8 *) "Test Using Larger Than Block-Size Key - Hash Key First", 54, result_sha1_test6 }, { key_sha1_test6_7, 80, (guint8 *) "Test Using Larger Than Block-Size Key and Larger" \ " Than One Block-Size Data", 73, result_sha1_test7, }, { NULL, 0, NULL, 0, NULL }, }; static void test_sasl_utils_hmac_sha1 (digest_test *t) { GByteArray *result = sasl_calculate_hmac_sha1 (t->key, t->key_len, t->data, t->data_len); int i; for (i = 0; i < g_checksum_type_get_length (G_CHECKSUM_SHA1); i++) g_assert_cmphex (result->data[i], ==, t->result[i]); g_byte_array_unref (result); } int main (int argc, char **argv) { int i; g_test_init (&argc, &argv, NULL); for (i = 0 ; hmac_sha1_tests[i].key_len > 0 ; i++) { gchar *name = g_strdup_printf ("/sasl-utils/hmac-sha1-%d", i + 1); g_test_add_data_func (name, hmac_sha1_tests + i, (void (*)(const void *)) test_sasl_utils_hmac_sha1); g_free (name); } return g_test_run (); } telepathy-gabble-0.18.2/lib/ext/wocky/tests/wocky-roster-test.c0000644000175000017500000016733212200204546024461 0ustar00smcvsmcv00000000000000#ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include #include "wocky-test-stream.h" #include "wocky-test-helper.h" /* Test to instantiate a WockyRoster object */ static void test_instantiation (void) { WockyRoster *roster; WockyXmppConnection *connection; WockyTestStream *stream; WockySession *session; stream = g_object_new (WOCKY_TYPE_TEST_STREAM, NULL); connection = wocky_xmpp_connection_new (stream->stream0); session = wocky_session_new_with_connection (connection, "example.com"); roster = wocky_roster_new (session); g_assert (roster != NULL); g_object_unref (roster); g_object_unref (session); g_object_unref (connection); g_object_unref (stream); } /* Test if the Roster sends the right IQ query when fetching the roster */ static gboolean fetch_roster_send_iq_cb (WockyPorter *porter, WockyStanza *stanza, gpointer user_data) { test_data_t *test = (test_data_t *) user_data; WockyStanzaType type; WockyStanzaSubType sub_type; WockyNode *node; WockyStanza *reply; const char *id; /* Make sure stanza is as expected. */ wocky_stanza_get_type_info (stanza, &type, &sub_type); g_assert (type == WOCKY_STANZA_TYPE_IQ); g_assert (sub_type == WOCKY_STANZA_SUB_TYPE_GET); node = wocky_node_get_child (wocky_stanza_get_top_node (stanza), "query"); g_assert (wocky_stanza_get_top_node (stanza) != NULL); g_assert (!wocky_strdiff (wocky_node_get_ns (node), "jabber:iq:roster")); id = wocky_node_get_attribute (wocky_stanza_get_top_node (stanza), "id"); g_assert (id != NULL); reply = wocky_stanza_build (WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_RESULT, NULL, NULL, '@', "id", id, '(', "query", ':', "jabber:iq:roster", '(', "item", '@', "jid", "romeo@example.net", '@', "name", "Romeo", '@', "subscription", "both", '(', "group", '$', "Friends", ')', ')', ')', NULL); wocky_porter_send (porter, reply); g_object_unref (reply); test->outstanding--; g_main_loop_quit (test->loop); return TRUE; } static void fetch_roster_fetched_cb (GObject *source_object, GAsyncResult *res, gpointer user_data) { test_data_t *test = (test_data_t *) user_data; g_return_if_fail (wocky_roster_fetch_roster_finish ( WOCKY_ROSTER (source_object), res, NULL)); test->outstanding--; g_main_loop_quit (test->loop); } static void test_fetch_roster_send_iq (void) { WockyRoster *roster; test_data_t *test = setup_test (); test_open_both_connections (test); wocky_porter_register_handler_from_anyone (test->sched_out, WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_GET, WOCKY_PORTER_HANDLER_PRIORITY_MAX, fetch_roster_send_iq_cb, test, NULL); wocky_porter_start (test->sched_out); wocky_session_start (test->session_in); roster = wocky_roster_new (test->session_in); wocky_roster_fetch_roster_async (roster, NULL, fetch_roster_fetched_cb, test); test->outstanding += 2; test_wait_pending (test); test_close_both_porters (test); g_object_unref (roster); teardown_test (test); } /* Test if the Roster object is properly populated when receiving its fetch * reply */ static WockyBareContact * create_romeo (void) { const gchar *groups[] = { "Friends", NULL }; return g_object_new (WOCKY_TYPE_BARE_CONTACT, "jid", "romeo@example.net", "name", "Romeo", "subscription", WOCKY_ROSTER_SUBSCRIPTION_TYPE_BOTH, "groups", groups, NULL); } static WockyBareContact * create_juliet (void) { const gchar *groups[] = { "Friends", "Girlz", NULL }; return g_object_new (WOCKY_TYPE_BARE_CONTACT, "jid", "juliet@example.net", "name", "Juliet", "subscription", WOCKY_ROSTER_SUBSCRIPTION_TYPE_TO, "groups", groups, NULL); } static int find_contact (gconstpointer a, gconstpointer b) { if (wocky_bare_contact_equal (WOCKY_BARE_CONTACT (a), WOCKY_BARE_CONTACT (b))) return 0; return 1; } static void fetch_roster_reply_roster_cb (GObject *source_object, GAsyncResult *res, gpointer user_data) { test_data_t *test = (test_data_t *) user_data; WockyBareContact *contact; WockyRoster *roster = WOCKY_ROSTER (source_object); WockyBareContact *romeo, *juliet; GSList *contacts; g_return_if_fail (wocky_roster_fetch_roster_finish (roster, res, NULL)); contacts = wocky_roster_get_all_contacts (roster); g_assert_cmpuint (g_slist_length (contacts), ==, 2); contact = wocky_roster_get_contact (roster, "romeo@example.net"); romeo = create_romeo (); g_assert (wocky_bare_contact_equal (contact, romeo)); g_assert (g_slist_find_custom (contacts, romeo, find_contact) != NULL); g_object_unref (romeo); contact = wocky_roster_get_contact (roster, "juliet@example.net"); juliet = create_juliet (); g_assert (wocky_bare_contact_equal (contact, juliet)); g_assert (g_slist_find_custom (contacts, juliet, find_contact) != NULL); g_object_unref (juliet); g_slist_foreach (contacts, (GFunc) g_object_unref, NULL); g_slist_free (contacts); test->outstanding--; g_main_loop_quit (test->loop); } static gboolean fetch_roster_reply_cb (WockyPorter *porter, WockyStanza *stanza, gpointer user_data) { WockyStanza *reply; /* We're acting like the server here. The client doesn't need to send a * "from" attribute, and in fact it doesn't when fetch_roster is called. It * is left up to the server to know which client is the user and then throw * in a correct to attribute. Here we're just adding a from attribute so the * IQ result builder doesn't complain. */ if (wocky_stanza_get_from (stanza) == NULL) wocky_node_set_attribute (wocky_stanza_get_top_node (stanza), "from", "juliet@example.com/balcony"); reply = wocky_stanza_build_iq_result (stanza, '(', "query", ':', "jabber:iq:roster", /* Romeo */ '(', "item", '@', "jid", "romeo@example.net", '@', "name", "Romeo", '@', "subscription", "both", '(', "group", '$', "Friends", ')', /* Juliet */ ')', '(', "item", '@', "jid", "juliet@example.net", '@', "name", "Juliet", '@', "subscription", "to", '(', "group", '$', "Friends", ')', '(', "group", '$', "Girlz", ')', ')', ')', NULL); wocky_porter_send (porter, reply); g_object_unref (reply); return TRUE; } static WockyRoster * create_initial_roster (test_data_t *test) { WockyRoster *roster; wocky_porter_register_handler_from_anyone (test->sched_out, WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_GET, WOCKY_PORTER_HANDLER_PRIORITY_MAX, fetch_roster_reply_cb, test, NULL); wocky_porter_start (test->sched_out); wocky_session_start (test->session_in); roster = wocky_roster_new (test->session_in); wocky_roster_fetch_roster_async (roster, NULL, fetch_roster_reply_roster_cb, test); test->outstanding++; test_wait_pending (test); return roster; } static void test_fetch_roster_reply (void) { WockyRoster *roster; test_data_t *test = setup_test (); test_open_both_connections (test); roster = create_initial_roster (test); test_close_both_porters (test); g_object_unref (roster); teardown_test (test); } /* Test if roster is properly upgraded when a contact is added to it */ static WockyBareContact * create_nurse (void) { const gchar *groups[] = { NULL }; return g_object_new (WOCKY_TYPE_BARE_CONTACT, "jid", "nurse@example.net", "name", "Nurse", "subscription", WOCKY_ROSTER_SUBSCRIPTION_TYPE_NONE, "groups", groups, NULL); } static void roster_added_cb (WockyRoster *roster, WockyBareContact *contact, test_data_t *test) { WockyBareContact *nurse; GSList *contacts; /* Is that the right contact? */ nurse = create_nurse (); g_assert (wocky_bare_contact_equal (contact, nurse)); /* Check if the contact has been added to the roster */ g_assert (wocky_roster_get_contact (roster, "nurse@example.net") == contact); contacts = wocky_roster_get_all_contacts (roster); g_assert (g_slist_find_custom (contacts, nurse, (GCompareFunc) find_contact)); g_object_unref (nurse); g_slist_foreach (contacts, (GFunc) g_object_unref, NULL); g_slist_free (contacts); test->outstanding--; g_main_loop_quit (test->loop); } static void roster_update_reply_cb (GObject *source, GAsyncResult *res, gpointer user_data) { test_data_t *test = (test_data_t *) user_data; WockyStanza *reply; WockyStanzaType type; WockyStanzaSubType sub_type; reply = wocky_porter_send_iq_finish (WOCKY_PORTER (source), res, NULL); g_assert (reply != NULL); wocky_stanza_get_type_info (reply, &type, &sub_type); g_assert (type == WOCKY_STANZA_TYPE_IQ); g_assert (sub_type == WOCKY_STANZA_SUB_TYPE_RESULT); g_object_unref (reply); test->outstanding--; g_main_loop_quit (test->loop); } static void send_roster_update (test_data_t *test, const gchar *jid, const gchar *name, const gchar *subscription, const gchar **groups) { WockyStanza *iq; WockyNode *item; guint i; iq = wocky_stanza_build (WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_SET, NULL, NULL, '(', "query", ':', WOCKY_XMPP_NS_ROSTER, '(', "item", '*', &item, ')', ')', NULL); if (jid != NULL) wocky_node_set_attribute (item, "jid", jid); if (name != NULL) wocky_node_set_attribute (item, "name", name); if (subscription != NULL) wocky_node_set_attribute (item, "subscription", subscription); for (i = 0; groups != NULL && groups[i] != NULL; i++) { WockyNode *node; node = wocky_node_add_child (item, "group"); wocky_node_set_content (node, groups[i]); } wocky_porter_send_iq_async (test->sched_out, iq, NULL, roster_update_reply_cb, test); g_object_unref (iq); test->outstanding++; } static void test_roster_upgrade_add (void) { WockyRoster *roster; test_data_t *test = setup_test (); const gchar *no_group[] = { NULL }; test_open_both_connections (test); roster = create_initial_roster (test); g_signal_connect (roster, "added", G_CALLBACK (roster_added_cb), test); test->outstanding++; send_roster_update (test, "nurse@example.net", "Nurse", "none", no_group); test_wait_pending (test); test_close_both_porters (test); g_object_unref (roster); teardown_test (test); } /* Test if roster is properly upgraded when a contact is removed from it */ static void roster_removed_cb (WockyRoster *roster, WockyBareContact *contact, test_data_t *test) { WockyBareContact *romeo; GSList *contacts; /* Is that the right contact? */ romeo = create_romeo (); g_assert (wocky_bare_contact_equal (contact, romeo)); /* Check if the contact has been removed from the roster */ g_assert (wocky_roster_get_contact (roster, "romeo@example.net") == NULL); contacts = wocky_roster_get_all_contacts (roster); g_assert (g_slist_find_custom (contacts, romeo, (GCompareFunc) find_contact) == NULL); g_object_unref (romeo); g_slist_foreach (contacts, (GFunc) g_object_unref, NULL); g_slist_free (contacts); test->outstanding--; g_main_loop_quit (test->loop); } static void test_roster_upgrade_remove (void) { WockyRoster *roster; test_data_t *test = setup_test (); const gchar *no_group[] = { NULL }; test_open_both_connections (test); roster = create_initial_roster (test); g_signal_connect (roster, "removed", G_CALLBACK (roster_removed_cb), test); test->outstanding++; send_roster_update (test, "romeo@example.net", NULL, "remove", no_group); test_wait_pending (test); test_close_both_porters (test); g_object_unref (roster); teardown_test (test); } /* Test if WockyBareContact objects are properly upgraded */ static void contact_notify_cb (WockyBareContact *contact, GParamSpec *pspec, test_data_t *test) { test->outstanding--; g_main_loop_quit (test->loop); } static void test_roster_upgrade_change (void) { WockyRoster *roster; test_data_t *test = setup_test (); GSList *contacts, *l; WockyBareContact *romeo, *contact; const gchar *groups_init[] = { "Friends", NULL }; const gchar *groups[] = { "Badger", NULL }; test_open_both_connections (test); roster = create_initial_roster (test); contact = wocky_roster_get_contact (roster, "romeo@example.net"); g_assert (contact != NULL); romeo = create_romeo (); contacts = wocky_roster_get_all_contacts (roster); for (l = contacts; l != NULL; l = g_slist_next (l)) { g_signal_connect (l->data, "notify", G_CALLBACK (contact_notify_cb), test); } g_slist_foreach (contacts, (GFunc) g_object_unref, NULL); g_slist_free (contacts); /* change name */ test->outstanding++; send_roster_update (test, "romeo@example.net", "Romeooo", "both", groups_init); test_wait_pending (test); /* Name has been changed */ wocky_bare_contact_set_name (romeo, "Romeooo"); g_assert (wocky_bare_contact_equal (contact, romeo)); /* change subscription */ test->outstanding++; send_roster_update (test, "romeo@example.net", "Romeooo", "to", groups_init); test_wait_pending (test); /* Subscription has been changed */ wocky_bare_contact_set_subscription (romeo, WOCKY_ROSTER_SUBSCRIPTION_TYPE_TO); g_assert (wocky_bare_contact_equal (contact, romeo)); /* change groups */ test->outstanding++; send_roster_update (test, "romeo@example.net", "Romeooo", "to", groups); test_wait_pending (test); /* Groups have been changed */ wocky_bare_contact_set_groups (romeo, (gchar **) groups); g_assert (wocky_bare_contact_equal (contact, romeo)); g_object_unref (romeo); test_close_both_porters (test); g_object_unref (roster); teardown_test (test); } static void ack_iq (WockyPorter *porter, WockyStanza *stanza) { WockyStanza *reply; const gchar *id; id = wocky_node_get_attribute (wocky_stanza_get_top_node (stanza), "id"); g_assert (id != NULL); reply = wocky_stanza_build (WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_RESULT, NULL, NULL, '@', "id", id, NULL); wocky_porter_send (porter, reply); g_object_unref (reply); } /* Test adding a contact to the roster */ static void check_edit_roster_stanza (WockyStanza *stanza, const gchar *jid, const gchar *name, const gchar *subscription, const gchar **groups) { WockyStanzaType type; WockyStanzaSubType sub_type; WockyNode *node; GSList *l; guint i; GHashTable *expected_groups; wocky_stanza_get_type_info (stanza, &type, &sub_type); g_assert (type == WOCKY_STANZA_TYPE_IQ); g_assert (sub_type == WOCKY_STANZA_SUB_TYPE_SET); node = wocky_node_get_child_ns (wocky_stanza_get_top_node (stanza), "query", WOCKY_XMPP_NS_ROSTER); g_assert (node != NULL); node = wocky_node_get_child (node, "item"); g_assert (node != NULL); g_assert (!wocky_strdiff (wocky_node_get_attribute (node, "jid"), jid)); if (name != NULL) g_assert (!wocky_strdiff (wocky_node_get_attribute (node, "name"), name)); else g_assert (wocky_node_get_attribute (node, "name") == NULL); if (subscription != NULL) g_assert (!wocky_strdiff (wocky_node_get_attribute (node, "subscription"), subscription)); else g_assert (wocky_node_get_attribute (node, "subscription") == NULL); if (groups == NULL) { /* No group children */ g_assert_cmpuint (g_slist_length (node->children), == , 0); return; } expected_groups = g_hash_table_new (g_str_hash, g_str_equal); for (i = 0; groups[i] != NULL; i++) { g_hash_table_insert (expected_groups, (gchar *) groups[i], GUINT_TO_POINTER (TRUE)); } for (l = node->children; l != NULL; l = g_slist_next (l)) { WockyNode *group = (WockyNode *) l->data; g_assert (!wocky_strdiff (group->name, "group")); g_assert (g_hash_table_remove (expected_groups, group->content)); } g_assert (g_hash_table_size (expected_groups) == 0); g_hash_table_unref (expected_groups); } static void check_add_contact_stanza (WockyStanza *stanza, const gchar *jid, const gchar *name, const gchar **groups) { check_edit_roster_stanza (stanza, jid, name, NULL, groups); } static gboolean first_add = TRUE; static gboolean add_contact_send_iq_cb (WockyPorter *porter, WockyStanza *stanza, gpointer user_data) { test_data_t *test = (test_data_t *) user_data; const gchar *groups[] = { "Friends", "Badger", NULL }; if (first_add) { check_add_contact_stanza (stanza, "mercutio@example.net", "Mercutio", groups); send_roster_update (test, "mercutio@example.net", "Mercutio", "none", groups); first_add = FALSE; } else { /* the second time the name is changed */ check_add_contact_stanza (stanza, "mercutio@example.net", "Badger", groups); send_roster_update (test, "mercutio@example.net", "Badger", "none", groups); } /* Ack the IQ */ ack_iq (porter, stanza); test->outstanding--; g_main_loop_quit (test->loop); return TRUE; } static void contact_added_cb (GObject *source, GAsyncResult *res, gpointer user_data) { test_data_t *test = (test_data_t *) user_data; WockyRoster *roster = WOCKY_ROSTER (source); g_assert (wocky_roster_add_contact_finish (roster, res, NULL)); test->outstanding--; g_main_loop_quit (test->loop); } static WockyBareContact * create_mercutio (void) { const gchar *groups[] = { "Friends", "Badger", NULL }; return g_object_new (WOCKY_TYPE_BARE_CONTACT, "jid", "mercutio@example.net", "name", "Mercutio", "groups", groups, NULL); } static void test_roster_add_contact (void) { WockyRoster *roster; test_data_t *test = setup_test (); WockyBareContact *mercutio, *contact; const gchar *groups[] = { "Friends", "Badger", NULL }; test_open_both_connections (test); roster = create_initial_roster (test); wocky_porter_register_handler_from_anyone (test->sched_out, WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_SET, WOCKY_PORTER_HANDLER_PRIORITY_MAX, add_contact_send_iq_cb, test, '(', "query", ':', WOCKY_XMPP_NS_ROSTER, ')', NULL); mercutio = create_mercutio (); /* Add the Mercutio to our roster */ wocky_roster_add_contact_async (roster, "mercutio@example.net", "Mercutio", groups, NULL, contact_added_cb, test); test->outstanding += 2; test_wait_pending (test); /* check if the contact has been actually added */ contact = wocky_roster_get_contact (roster, "mercutio@example.net"); g_assert (wocky_bare_contact_equal (contact, mercutio)); /* try to re-add the same contact. Operation succeeds immediately */ wocky_roster_add_contact_async (roster, "mercutio@example.net", "Mercutio", groups, NULL, contact_added_cb, test); test->outstanding += 1; test_wait_pending (test); /* try to re-add the same contact but with a different name. The name is * changed */ wocky_roster_add_contact_async (roster, "mercutio@example.net", "Badger", groups, NULL, contact_added_cb, test); test->outstanding += 2; test_wait_pending (test); /* check if the contact has been updated */ contact = wocky_roster_get_contact (roster, "mercutio@example.net"); wocky_bare_contact_set_name (mercutio, "Badger"); g_assert (wocky_bare_contact_equal (contact, mercutio)); test_close_both_porters (test); g_object_unref (mercutio); g_object_unref (roster); teardown_test (test); } static void check_remove_contact_stanza (WockyStanza *stanza, const gchar *jid) { check_edit_roster_stanza (stanza, jid, NULL, "remove", NULL); } /* Test removing a contact from the roster */ static gboolean remove_contact_send_iq_cb (WockyPorter *porter, WockyStanza *stanza, gpointer user_data) { test_data_t *test = (test_data_t *) user_data; const gchar *no_group[] = { NULL }; check_remove_contact_stanza (stanza, "romeo@example.net"); send_roster_update (test, "romeo@example.net", NULL, "remove", no_group); /* Ack the IQ */ ack_iq (porter, stanza); test->outstanding--; g_main_loop_quit (test->loop); return TRUE; } static void contact_removed_cb (GObject *source, GAsyncResult *res, gpointer user_data) { test_data_t *test = (test_data_t *) user_data; WockyRoster *roster = WOCKY_ROSTER (source); g_assert (wocky_roster_remove_contact_finish (roster, res, NULL)); test->outstanding--; g_main_loop_quit (test->loop); } static void test_roster_remove_contact (void) { WockyRoster *roster; test_data_t *test = setup_test (); WockyBareContact *contact; test_open_both_connections (test); roster = create_initial_roster (test); contact = wocky_roster_get_contact (roster, "romeo@example.net"); g_assert (contact != NULL); wocky_porter_register_handler_from_anyone (test->sched_out, WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_SET, WOCKY_PORTER_HANDLER_PRIORITY_MAX, remove_contact_send_iq_cb, test, '(', "query", ':', WOCKY_XMPP_NS_ROSTER, ')', NULL); /* Keep a ref on the contact as the roster will release its ref when * removing it */ g_object_ref (contact); wocky_roster_remove_contact_async (roster, contact, NULL, contact_removed_cb, test); test->outstanding += 2; test_wait_pending (test); /* check if the contact has actually been removed */ g_assert (wocky_roster_get_contact (roster, "romeo@example.net") == NULL); /* try to re-remove the same contact. Operation succeeds immediately */ wocky_roster_remove_contact_async (roster, contact, NULL, contact_removed_cb, test); test->outstanding += 1; test_wait_pending (test); test_close_both_porters (test); g_object_unref (roster); g_object_unref (contact); teardown_test (test); } /* test changing the name of a roster item */ static gboolean change_name_send_iq_cb (WockyPorter *porter, WockyStanza *stanza, gpointer user_data) { test_data_t *test = (test_data_t *) user_data; WockyStanzaType type; WockyStanzaSubType sub_type; WockyNode *node; const gchar *group[] = { "Friends", NULL }; /* Make sure stanza is as expected. */ wocky_stanza_get_type_info (stanza, &type, &sub_type); g_assert (type == WOCKY_STANZA_TYPE_IQ); g_assert (sub_type == WOCKY_STANZA_SUB_TYPE_SET); node = wocky_node_get_child_ns (wocky_stanza_get_top_node (stanza), "query", WOCKY_XMPP_NS_ROSTER); g_assert (node != NULL); node = wocky_node_get_child (node, "item"); g_assert (node != NULL); g_assert (!wocky_strdiff (wocky_node_get_attribute (node, "jid"), "romeo@example.net")); g_assert (!wocky_strdiff (wocky_node_get_attribute (node, "name"), "Badger")); g_assert (!wocky_strdiff (wocky_node_get_attribute (node, "subscription"), "both")); g_assert_cmpuint (g_slist_length (node->children), ==, 1); node = wocky_node_get_child (node, "group"); g_assert (node != NULL); g_assert (!wocky_strdiff (node->content, "Friends")); send_roster_update (test, "romeo@example.net", "Badger", "both", group); /* Ack the IQ */ ack_iq (porter, stanza); test->outstanding--; g_main_loop_quit (test->loop); return TRUE; } static void contact_name_changed_cb (GObject *source, GAsyncResult *res, gpointer user_data) { test_data_t *test = (test_data_t *) user_data; WockyRoster *roster = WOCKY_ROSTER (source); g_assert (wocky_roster_change_contact_name_finish (roster, res, NULL)); test->outstanding--; g_main_loop_quit (test->loop); } static void contact_name_changed_not_in_roster_cb (GObject *source, GAsyncResult *res, gpointer user_data) { test_data_t *test = (test_data_t *) user_data; WockyRoster *roster = WOCKY_ROSTER (source); GError *error = NULL; g_assert (!wocky_roster_change_contact_name_finish (roster, res, &error)); g_assert_error (error, WOCKY_ROSTER_ERROR, WOCKY_ROSTER_ERROR_NOT_IN_ROSTER); g_error_free (error); test->outstanding--; g_main_loop_quit (test->loop); } static void test_roster_change_name (void) { WockyRoster *roster; test_data_t *test = setup_test (); WockyBareContact *contact, *romeo, *mercutio; test_open_both_connections (test); roster = create_initial_roster (test); romeo = create_romeo (); contact = wocky_roster_get_contact (roster, "romeo@example.net"); g_assert (contact != NULL); g_assert (wocky_bare_contact_equal (contact, romeo)); wocky_porter_register_handler_from_anyone (test->sched_out, WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_SET, WOCKY_PORTER_HANDLER_PRIORITY_MAX, change_name_send_iq_cb, test, '(', "query", ':', WOCKY_XMPP_NS_ROSTER, ')', NULL); wocky_roster_change_contact_name_async (roster, contact, "Badger", NULL, contact_name_changed_cb, test); test->outstanding += 2; test_wait_pending (test); /* check if the contact name has actually been change */ wocky_bare_contact_set_name (romeo, "Badger"); g_assert (wocky_bare_contact_equal (contact, romeo)); /* Retry to do the same change; operation succeeds immediately */ wocky_roster_change_contact_name_async (roster, contact, "Badger", NULL, contact_name_changed_cb, test); test->outstanding += 1; test_wait_pending (test); /* try to change name of a contact which is not in the roster */ mercutio = create_mercutio (); wocky_roster_change_contact_name_async (roster, mercutio, "Badger", NULL, contact_name_changed_not_in_roster_cb, test); test->outstanding += 1; test_wait_pending (test); test_close_both_porters (test); g_object_unref (roster); g_object_unref (romeo); g_object_unref (mercutio); teardown_test (test); } /* test adding a group to a contact */ static gboolean add_group_send_iq_cb (WockyPorter *porter, WockyStanza *stanza, gpointer user_data) { test_data_t *test = (test_data_t *) user_data; WockyStanzaType type; WockyStanzaSubType sub_type; WockyNode *node; const gchar *groups[] = { "Friends", "Badger", NULL }; GSList *l; gboolean group_friend = FALSE, group_badger = FALSE; /* Make sure stanza is as expected. */ wocky_stanza_get_type_info (stanza, &type, &sub_type); g_assert (type == WOCKY_STANZA_TYPE_IQ); g_assert (sub_type == WOCKY_STANZA_SUB_TYPE_SET); node = wocky_node_get_child_ns (wocky_stanza_get_top_node (stanza), "query", WOCKY_XMPP_NS_ROSTER); g_assert (node != NULL); node = wocky_node_get_child (node, "item"); g_assert (node != NULL); g_assert (!wocky_strdiff (wocky_node_get_attribute (node, "jid"), "romeo@example.net")); g_assert (!wocky_strdiff (wocky_node_get_attribute (node, "name"), "Romeo")); g_assert (!wocky_strdiff (wocky_node_get_attribute (node, "subscription"), "both")); g_assert_cmpuint (g_slist_length (node->children), ==, 2); for (l = node->children; l != NULL; l = g_slist_next (l)) { WockyNode *group = (WockyNode *) l->data; g_assert (!wocky_strdiff (group->name, "group")); if (!wocky_strdiff (group->content, "Friends")) group_friend = TRUE; else if (!wocky_strdiff (group->content, "Badger")) group_badger = TRUE; else g_assert_not_reached (); } g_assert (group_friend && group_badger); send_roster_update (test, "romeo@example.net", "Romeo", "both", groups); /* Ack the IQ */ ack_iq (porter, stanza); test->outstanding--; g_main_loop_quit (test->loop); return TRUE; } static void contact_group_added_cb (GObject *source, GAsyncResult *res, gpointer user_data) { test_data_t *test = (test_data_t *) user_data; WockyRoster *roster = WOCKY_ROSTER (source); g_assert (wocky_roster_contact_add_group_finish (roster, res, NULL)); test->outstanding--; g_main_loop_quit (test->loop); } static void contact_group_added_not_in_roster_cb (GObject *source, GAsyncResult *res, gpointer user_data) { test_data_t *test = (test_data_t *) user_data; WockyRoster *roster = WOCKY_ROSTER (source); GError *error = NULL; g_assert (!wocky_roster_contact_add_group_finish (roster, res, &error)); g_assert_error (error, WOCKY_ROSTER_ERROR, WOCKY_ROSTER_ERROR_NOT_IN_ROSTER); g_error_free (error); test->outstanding--; g_main_loop_quit (test->loop); } static void test_contact_add_group (void) { WockyRoster *roster; test_data_t *test = setup_test (); WockyBareContact *contact, *romeo, *mercutio; test_open_both_connections (test); roster = create_initial_roster (test); romeo = create_romeo (); contact = wocky_roster_get_contact (roster, "romeo@example.net"); g_assert (contact != NULL); g_assert (wocky_bare_contact_equal (contact, romeo)); wocky_porter_register_handler_from_anyone (test->sched_out, WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_SET, WOCKY_PORTER_HANDLER_PRIORITY_MAX, add_group_send_iq_cb, test, '(', "query", ':', WOCKY_XMPP_NS_ROSTER, ')', NULL); wocky_roster_contact_add_group_async (roster, contact, "Badger", NULL, contact_group_added_cb, test); test->outstanding += 2; test_wait_pending (test); /* check if the group has actually been added */ wocky_bare_contact_add_group (romeo, "Badger"); g_assert (wocky_bare_contact_equal (contact, romeo)); /* Retry to do the same change; operation succeeds immediately */ wocky_roster_contact_add_group_async (roster, contact, "Badger", NULL, contact_group_added_cb, test); test->outstanding += 1; test_wait_pending (test); /* try to add a group to a contact which is not in the roster */ mercutio = create_mercutio (); wocky_roster_contact_add_group_async (roster, mercutio, "Badger", NULL, contact_group_added_not_in_roster_cb, test); test->outstanding += 1; test_wait_pending (test); test_close_both_porters (test); g_object_unref (roster); g_object_unref (romeo); g_object_unref (mercutio); teardown_test (test); } /* test removing a group from a contact */ static gboolean remove_group_send_iq_cb (WockyPorter *porter, WockyStanza *stanza, gpointer user_data) { test_data_t *test = (test_data_t *) user_data; WockyStanzaType type; WockyStanzaSubType sub_type; WockyNode *node; const gchar *groups[] = { NULL }; /* Make sure stanza is as expected. */ wocky_stanza_get_type_info (stanza, &type, &sub_type); g_assert (type == WOCKY_STANZA_TYPE_IQ); g_assert (sub_type == WOCKY_STANZA_SUB_TYPE_SET); node = wocky_node_get_child_ns (wocky_stanza_get_top_node (stanza), "query", WOCKY_XMPP_NS_ROSTER); g_assert (node != NULL); node = wocky_node_get_child (node, "item"); g_assert (node != NULL); g_assert (!wocky_strdiff (wocky_node_get_attribute (node, "jid"), "romeo@example.net")); g_assert (!wocky_strdiff (wocky_node_get_attribute (node, "name"), "Romeo")); g_assert (!wocky_strdiff (wocky_node_get_attribute (node, "subscription"), "both")); g_assert_cmpuint (g_slist_length (node->children), ==, 0); send_roster_update (test, "romeo@example.net", "Romeo", "both", groups); /* Ack the IQ */ ack_iq (porter, stanza); test->outstanding--; g_main_loop_quit (test->loop); return TRUE; } static void contact_group_removed_cb (GObject *source, GAsyncResult *res, gpointer user_data) { test_data_t *test = (test_data_t *) user_data; WockyRoster *roster = WOCKY_ROSTER (source); g_assert (wocky_roster_contact_remove_group_finish (roster, res, NULL)); test->outstanding--; g_main_loop_quit (test->loop); } static void contact_group_removed_not_in_roster_cb (GObject *source, GAsyncResult *res, gpointer user_data) { test_data_t *test = (test_data_t *) user_data; WockyRoster *roster = WOCKY_ROSTER (source); GError *error = NULL; g_assert (!wocky_roster_contact_remove_group_finish (roster, res, &error)); g_assert_error (error, WOCKY_ROSTER_ERROR, WOCKY_ROSTER_ERROR_NOT_IN_ROSTER); g_error_free (error); test->outstanding--; g_main_loop_quit (test->loop); } static void test_contact_remove_group (void) { WockyRoster *roster; test_data_t *test = setup_test (); WockyBareContact *contact, *romeo, *mercutio; test_open_both_connections (test); roster = create_initial_roster (test); romeo = create_romeo (); contact = wocky_roster_get_contact (roster, "romeo@example.net"); g_assert (contact != NULL); g_assert (wocky_bare_contact_equal (contact, romeo)); wocky_porter_register_handler_from_anyone (test->sched_out, WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_SET, WOCKY_PORTER_HANDLER_PRIORITY_MAX, remove_group_send_iq_cb, test, '(', "query", ':', WOCKY_XMPP_NS_ROSTER, ')', NULL); wocky_roster_contact_remove_group_async (roster, contact, "Friends", NULL, contact_group_removed_cb, test); test->outstanding += 2; test_wait_pending (test); /* check if the group has actually been added */ wocky_bare_contact_remove_group (romeo, "Friends"); g_assert (wocky_bare_contact_equal (contact, romeo)); /* Retry to do the same change; operation succeeds immediately */ wocky_roster_contact_remove_group_async (roster, contact, "Friends", NULL, contact_group_removed_cb, test); test->outstanding += 1; test_wait_pending (test); /* try to remove a group from a contact which is not in the roster */ mercutio = create_mercutio (); wocky_roster_contact_remove_group_async (roster, mercutio, "Friends", NULL, contact_group_removed_not_in_roster_cb, test); test->outstanding += 1; test_wait_pending (test); test_close_both_porters (test); g_object_unref (roster); g_object_unref (romeo); g_object_unref (mercutio); teardown_test (test); } /* Remove a contact and re-add it before the remove operation is completed */ static WockyStanza *received_iq = NULL; static gboolean iq_set_cb (WockyPorter *porter, WockyStanza *stanza, gpointer user_data) { test_data_t *test = (test_data_t *) user_data; g_assert (received_iq == NULL); received_iq = g_object_ref (stanza); test->outstanding--; g_main_loop_quit (test->loop); return TRUE; } static void test_remove_contact_re_add (void) { WockyRoster *roster; test_data_t *test = setup_test (); WockyBareContact *contact, *romeo; const gchar *groups[] = { "Friends", NULL }; const gchar *no_group[] = { NULL }; test_open_both_connections (test); roster = create_initial_roster (test); contact = wocky_roster_get_contact (roster, "romeo@example.net"); g_assert (contact != NULL); wocky_porter_register_handler_from_anyone (test->sched_out, WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_SET, WOCKY_PORTER_HANDLER_PRIORITY_MAX, iq_set_cb, test, '(', "query", ':', WOCKY_XMPP_NS_ROSTER, ')', NULL); wocky_roster_remove_contact_async (roster, contact, NULL, contact_removed_cb, test); test->outstanding += 1; test_wait_pending (test); g_assert (received_iq != NULL); /* The IQ has been sent but the server didn't send the upgrade and the reply * yet. * Now try to re-add the contact */ check_remove_contact_stanza (received_iq, "romeo@example.net"); wocky_roster_add_contact_async (roster, "romeo@example.net", "Romeo", groups, NULL, contact_added_cb, test); /* Now the server send the roster upgrade and reply to the remove IQ */ send_roster_update (test, "romeo@example.net", NULL, "remove", no_group); ack_iq (test->sched_out, received_iq); g_object_unref (received_iq); received_iq = NULL; /* Wait for: * - completion of the remove_contact operation * - server receives the "add contact" IQ */ test->outstanding += 2; test_wait_pending (test); /* At this point, the contact has been removed */ g_assert (wocky_roster_get_contact (roster, "romeo@example.net") == NULL); check_add_contact_stanza (received_iq, "romeo@example.net", "Romeo", groups); /* Now the server send the roster upgrade and reply to the add IQ */ send_roster_update (test, "romeo@example.net", "Romeo", "none", groups); ack_iq (test->sched_out, received_iq); g_object_unref (received_iq); received_iq = NULL; /* Wait for completion of the add_contact operation */ test->outstanding += 1; test_wait_pending (test); /* Check that the contact is back */ contact = wocky_roster_get_contact (roster, "romeo@example.net"); g_assert (contact != NULL); romeo = create_romeo (); wocky_bare_contact_set_subscription (romeo, WOCKY_ROSTER_SUBSCRIPTION_TYPE_NONE); g_assert (wocky_bare_contact_equal (contact, romeo)); test_close_both_porters (test); g_object_unref (romeo); g_object_unref (roster); teardown_test (test); } /* Remove a contact and then try to edit it before the remove operation is * completed */ static void test_remove_contact_edit (void) { WockyRoster *roster; test_data_t *test = setup_test (); WockyBareContact *contact; const gchar *no_group[] = { NULL }; test_open_both_connections (test); roster = create_initial_roster (test); contact = wocky_roster_get_contact (roster, "romeo@example.net"); g_assert (contact != NULL); wocky_porter_register_handler_from_anyone (test->sched_out, WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_SET, WOCKY_PORTER_HANDLER_PRIORITY_MAX, iq_set_cb, test, '(', "query", ':', WOCKY_XMPP_NS_ROSTER, ')', NULL); /* Keep a ref on the contact as the roster will release its ref when * removing it */ g_object_ref (contact); wocky_roster_remove_contact_async (roster, contact, NULL, contact_removed_cb, test); test->outstanding += 1; test_wait_pending (test); g_assert (received_iq != NULL); /* The IQ has been sent but the server didn't send the upgrade and the reply * yet. * Now try to re-add the contact */ check_remove_contact_stanza (received_iq, "romeo@example.net"); /* Try to change the name of the contact we are removing */ wocky_roster_change_contact_name_async (roster, contact, "Badger", NULL, contact_name_changed_not_in_roster_cb, test); /* Now the server send the roster upgrade and reply to the remove IQ */ send_roster_update (test, "romeo@example.net", NULL, "remove", no_group); ack_iq (test->sched_out, received_iq); g_object_unref (received_iq); received_iq = NULL; /* Wait for: * - completion of the remove_contact operation * - completion (with an error) for the change_name operation */ test->outstanding += 2; test_wait_pending (test); /* At this point, the contact has been removed */ g_assert (wocky_roster_get_contact (roster, "romeo@example.net") == NULL); test_close_both_porters (test); g_object_unref (contact); g_object_unref (roster); teardown_test (test); } /* Queue some edit operations on the same contact */ static void test_multi_contact_edit (void) { WockyRoster *roster; test_data_t *test = setup_test (); WockyBareContact *contact, *juliet; const gchar *groups[] = { "Friends", "Girlz", NULL }; const gchar *groups_changed[] = { "Friends", "School", NULL }; test_open_both_connections (test); roster = create_initial_roster (test); contact = wocky_roster_get_contact (roster, "juliet@example.net"); g_assert (contact != NULL); juliet = create_juliet (); wocky_porter_register_handler_from_anyone (test->sched_out, WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_SET, WOCKY_PORTER_HANDLER_PRIORITY_MAX, iq_set_cb, test, '(', "query", ':', WOCKY_XMPP_NS_ROSTER, ')', NULL); /* Change's Romeo's name */ wocky_roster_change_contact_name_async (roster, contact, "Badger", NULL, contact_name_changed_cb, test); test->outstanding += 1; test_wait_pending (test); g_assert (received_iq != NULL); /* The IQ has been sent but the server didn't send the upgrade and the reply * yet */ g_assert (wocky_bare_contact_equal (contact, juliet)); check_edit_roster_stanza (received_iq, "juliet@example.net", "Badger", "to", groups); /* Try to re-change the name of the contact */ wocky_roster_change_contact_name_async (roster, contact, "Snake", NULL, contact_name_changed_cb, test); /* Add two groups */ wocky_roster_contact_add_group_async (roster, contact, "Hacker", NULL, contact_group_added_cb, test); wocky_roster_contact_add_group_async (roster, contact, "School", NULL, contact_group_added_cb, test); /* Remove a group we just added */ wocky_roster_contact_remove_group_async (roster, contact, "Hacker", NULL, contact_group_removed_cb, test); /* Remove the 2 default groups */ wocky_roster_contact_remove_group_async (roster, contact, "Friends", NULL, contact_group_removed_cb, test); wocky_roster_contact_remove_group_async (roster, contact, "Girlz", NULL, contact_group_removed_cb, test); /* Re-add a removed group */ wocky_roster_contact_add_group_async (roster, contact, "Friends", NULL, contact_group_added_cb, test); /* Now the server sends the roster upgrade and reply to the remove IQ */ send_roster_update (test, "juliet@example.net", "Badger", "to", groups); ack_iq (test->sched_out, received_iq); g_object_unref (received_iq); received_iq = NULL; /* Wait for: * - completion of the first change_name operation * - server receives the second edit IQ */ test->outstanding += 2; test_wait_pending (test); /* At this point, the contact has still his initial name */ wocky_bare_contact_set_name (juliet, "Badger"); g_assert (wocky_bare_contact_equal (contact, juliet)); check_edit_roster_stanza (received_iq, "juliet@example.net", "Snake", "to", groups_changed); /* Now the server send the roster upgrade and reply to the add IQ */ send_roster_update (test, "juliet@example.net", "Snake", "to", groups_changed); ack_iq (test->sched_out, received_iq); g_object_unref (received_iq); received_iq = NULL; /* Wait for completion of: * - the second change_name (Snake) * - the first add_group (Hacker) * - the second add_group (School) * - the first remove_group (Hacker) * - the second remove_group (Friends) * - the third remove_group (Girlz) * - the third add_group (Friends) */ test->outstanding += 7; test_wait_pending (test); /* Check that the contact is back */ wocky_bare_contact_set_name (juliet, "Snake"); wocky_bare_contact_set_groups (juliet, (gchar **) groups_changed); g_assert (wocky_bare_contact_equal (contact, juliet)); test_close_both_porters (test); g_object_unref (roster); g_object_unref (juliet); teardown_test (test); } /* test editing a contact and then remove it from the roster */ static void test_edit_contact_remove (void) { WockyRoster *roster; test_data_t *test = setup_test (); WockyBareContact *contact; const gchar *groups[] = { "Friends", NULL }; test_open_both_connections (test); roster = create_initial_roster (test); contact = wocky_roster_get_contact (roster, "romeo@example.net"); g_assert (contact != NULL); wocky_porter_register_handler_from_anyone (test->sched_out, WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_SET, WOCKY_PORTER_HANDLER_PRIORITY_MAX, iq_set_cb, test, '(', "query", ':', WOCKY_XMPP_NS_ROSTER, ')', NULL); /* change contact's name */ wocky_roster_change_contact_name_async (roster, contact, "Badger", NULL, contact_name_changed_cb, test); test->outstanding += 1; test_wait_pending (test); g_assert (received_iq != NULL); /* The IQ has been sent but the server didn't send the upgrade and the reply * yet. * Now try to re-add the contact */ check_edit_roster_stanza (received_iq, "romeo@example.net", "Badger", "both", groups); /* remove the contact */ wocky_roster_remove_contact_async (roster, contact, NULL, contact_removed_cb, test); /* Now the server send the roster upgrade and reply to the change name IQ */ send_roster_update (test, "romeo@example.net", "Badger", "both", groups); ack_iq (test->sched_out, received_iq); g_object_unref (received_iq); received_iq = NULL; /* Wait for: * - completion of the change name operation * - server receives the "remove contact" IQ */ test->outstanding += 2; test_wait_pending (test); /* At this point, the contact has not been removed yet */ g_assert (wocky_roster_get_contact (roster, "romeo@example.net") != NULL); check_remove_contact_stanza (received_iq, "romeo@example.net"); /* Now the server send the roster upgrade and reply to the remove IQ */ send_roster_update (test, "romeo@example.net", NULL, "remove", NULL); ack_iq (test->sched_out, received_iq); g_object_unref (received_iq); received_iq = NULL; /* Wait for completion of the remove_contact operation */ test->outstanding += 1; test_wait_pending (test); /* Contact is now removed */ g_assert (wocky_roster_get_contact (roster, "romeo@example.net") == NULL); test_close_both_porters (test); g_object_unref (roster); teardown_test (test); } /* Change twice to the same name */ static void test_change_name_twice (void) { WockyRoster *roster; test_data_t *test = setup_test (); WockyBareContact *contact, *romeo; const gchar *groups[] = { "Friends", NULL }; test_open_both_connections (test); roster = create_initial_roster (test); contact = wocky_roster_get_contact (roster, "romeo@example.net"); g_assert (contact != NULL); romeo = create_romeo (); wocky_porter_register_handler_from_anyone (test->sched_out, WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_SET, WOCKY_PORTER_HANDLER_PRIORITY_MAX, iq_set_cb, test, '(', "query", ':', WOCKY_XMPP_NS_ROSTER, ')', NULL); /* Change's Romeo's name */ wocky_roster_change_contact_name_async (roster, contact, "Badger", NULL, contact_name_changed_cb, test); test->outstanding += 1; test_wait_pending (test); g_assert (received_iq != NULL); /* The IQ has been sent but the server didn't send the upgrade and the reply * yet */ g_assert (wocky_bare_contact_equal (contact, romeo)); check_edit_roster_stanza (received_iq, "romeo@example.net", "Badger", "both", groups); /* Try to reset the same name */ wocky_roster_change_contact_name_async (roster, contact, "Badger", NULL, contact_name_changed_cb, test); /* Now the server sends the roster upgrade and reply to the first IQ */ send_roster_update (test, "romeo@example.net", "Badger", "both", groups); ack_iq (test->sched_out, received_iq); g_object_unref (received_iq); received_iq = NULL; /* Wait for completion of the 2 change_name operations. * No IQ has been sent for the second as no change was needed. */ test->outstanding += 2; test_wait_pending (test); g_assert (received_iq == NULL); /* Name has been changed */ wocky_bare_contact_set_name (romeo, "Badger"); g_assert (wocky_bare_contact_equal (contact, romeo)); test_close_both_porters (test); g_object_unref (roster); g_object_unref (romeo); teardown_test (test); } /* Remove twice the same contact */ static void test_remove_contact_twice (void) { WockyRoster *roster; test_data_t *test = setup_test (); WockyBareContact *contact; const gchar *no_group[] = { NULL }; test_open_both_connections (test); roster = create_initial_roster (test); contact = wocky_roster_get_contact (roster, "romeo@example.net"); g_assert (contact != NULL); wocky_porter_register_handler_from_anyone (test->sched_out, WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_SET, WOCKY_PORTER_HANDLER_PRIORITY_MAX, iq_set_cb, test, '(', "query", ':', WOCKY_XMPP_NS_ROSTER, ')', NULL); /* Keep a ref on the contact as the roster will release its ref when * removing it */ g_object_ref (contact); wocky_roster_remove_contact_async (roster, contact, NULL, contact_removed_cb, test); test->outstanding += 1; test_wait_pending (test); g_assert (received_iq != NULL); /* The IQ has been sent but the server didn't send the upgrade and the reply * yet */ check_remove_contact_stanza (received_iq, "romeo@example.net"); /* Re-ask to remove the contact */ wocky_roster_remove_contact_async (roster, contact, NULL, contact_removed_cb, test); /* Now the server send the roster upgrade and reply to the remove IQ */ send_roster_update (test, "romeo@example.net", NULL, "remove", no_group); ack_iq (test->sched_out, received_iq); g_object_unref (received_iq); received_iq = NULL; /* Wait for completion of the 2 remove operations. No IQ has been sent for * the second as no change was needed. */ test->outstanding += 2; test_wait_pending (test); g_assert (received_iq == NULL); /* At this point, the contact has been removed */ g_assert (wocky_roster_get_contact (roster, "romeo@example.net") == NULL); test_close_both_porters (test); g_object_unref (contact); g_object_unref (roster); teardown_test (test); } /* Change name of a contact and try to remove and re-add it while change * operation is running */ static void test_change_name_remove_add (void) { WockyRoster *roster; test_data_t *test = setup_test (); WockyBareContact *contact, *romeo; const gchar *groups[] = { "Friends", NULL }; test_open_both_connections (test); roster = create_initial_roster (test); contact = wocky_roster_get_contact (roster, "romeo@example.net"); g_assert (contact != NULL); romeo = create_romeo (); wocky_porter_register_handler_from_anyone (test->sched_out, WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_SET, WOCKY_PORTER_HANDLER_PRIORITY_MAX, iq_set_cb, test, '(', "query", ':', WOCKY_XMPP_NS_ROSTER, ')', NULL); /* Change's Romeo's name */ wocky_roster_change_contact_name_async (roster, contact, "Badger", NULL, contact_name_changed_cb, test); test->outstanding += 1; test_wait_pending (test); g_assert (received_iq != NULL); /* The IQ has been sent but the server didn't send the upgrade and the reply * yet */ g_assert (wocky_bare_contact_equal (contact, romeo)); check_edit_roster_stanza (received_iq, "romeo@example.net", "Badger", "both", groups); /* Remove the contact */ wocky_roster_remove_contact_async (roster, contact, NULL, contact_removed_cb, test); /* Change our mind and re-add it */ wocky_roster_add_contact_async (roster, "romeo@example.net", "Badger", groups, NULL, contact_added_cb, test); /* Now the server sends the roster upgrade and reply to the first IQ */ send_roster_update (test, "romeo@example.net", "Badger", "both", groups); ack_iq (test->sched_out, received_iq); g_object_unref (received_iq); received_iq = NULL; /* Wait for completion of: * - the change name operation * - the remove operation * - the add operation * No IQ has been sent for the add and remove as no change was needed. */ test->outstanding += 3; test_wait_pending (test); g_assert (received_iq == NULL); /* Name has been changed */ wocky_bare_contact_set_name (romeo, "Badger"); g_assert (wocky_bare_contact_equal (contact, romeo)); test_close_both_porters (test); g_object_unref (roster); g_object_unref (romeo); teardown_test (test); } /* add 2 groups to the same contact */ static void test_add_two_groups (void) { WockyRoster *roster; test_data_t *test = setup_test (); WockyBareContact *contact, *romeo; const gchar *groups2[] = { "Friends", "School", NULL }; const gchar *groups3[] = { "Friends", "School", "Hackers", NULL }; test_open_both_connections (test); roster = create_initial_roster (test); contact = wocky_roster_get_contact (roster, "romeo@example.net"); g_assert (contact != NULL); romeo = create_romeo (); wocky_porter_register_handler_from_anyone (test->sched_out, WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_SET, WOCKY_PORTER_HANDLER_PRIORITY_MAX, iq_set_cb, test, '(', "query", ':', WOCKY_XMPP_NS_ROSTER, ')', NULL); /* Add a group to Romeo */ wocky_roster_contact_add_group_async (roster, contact, "School", NULL, contact_group_added_cb, test); test->outstanding += 1; test_wait_pending (test); g_assert (received_iq != NULL); /* The IQ has been sent but the server didn't send the upgrade and the reply * yet */ g_assert (wocky_bare_contact_equal (contact, romeo)); check_edit_roster_stanza (received_iq, "romeo@example.net", "Romeo", "both", groups2); /* Add another group */ wocky_roster_contact_add_group_async (roster, contact, "Hackers", NULL, contact_group_added_cb, test); /* Now the server sends the roster upgrade and reply to the first IQ */ send_roster_update (test, "romeo@example.net", "Romeo", "both", groups2); ack_iq (test->sched_out, received_iq); g_object_unref (received_iq); received_iq = NULL; /* Wait that: * - the first add_group operation is completed * - the server receives the IQ for the second add_group */ test->outstanding += 2; test_wait_pending (test); wocky_bare_contact_set_groups (romeo, (gchar **) groups2); g_assert (wocky_bare_contact_equal (contact, romeo)); check_edit_roster_stanza (received_iq, "romeo@example.net", "Romeo", "both", groups3); /* Server sends the roster upgrade and reply to the first IQ */ send_roster_update (test, "romeo@example.net", "Romeo", "both", groups3); ack_iq (test->sched_out, received_iq); g_object_unref (received_iq); received_iq = NULL; /* Wait second add_group operation is completed */ test->outstanding += 1; test_wait_pending (test); wocky_bare_contact_set_groups (romeo, (gchar **) groups3); g_assert (wocky_bare_contact_equal (contact, romeo)); test_close_both_porters (test); g_object_unref (roster); g_object_unref (romeo); teardown_test (test); } /* remove 2 groups from the same contact */ static void test_remove_two_groups (void) { WockyRoster *roster; test_data_t *test = setup_test (); WockyBareContact *contact, *juliet; const gchar *groups[] = { "Friends", NULL }; const gchar *no_group[] = { NULL }; test_open_both_connections (test); roster = create_initial_roster (test); contact = wocky_roster_get_contact (roster, "juliet@example.net"); g_assert (contact != NULL); juliet = create_juliet (); wocky_porter_register_handler_from_anyone (test->sched_out, WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_SET, WOCKY_PORTER_HANDLER_PRIORITY_MAX, iq_set_cb, test, '(', "query", ':', WOCKY_XMPP_NS_ROSTER, ')', NULL); /* Remove a group from Juliet */ wocky_roster_contact_remove_group_async (roster, contact, "Girlz", NULL, contact_group_removed_cb, test); test->outstanding += 1; test_wait_pending (test); g_assert (received_iq != NULL); /* The IQ has been sent but the server didn't send the upgrade and the reply * yet */ g_assert (wocky_bare_contact_equal (contact, juliet)); check_edit_roster_stanza (received_iq, "juliet@example.net", "Juliet", "to", groups); /* remove another group */ wocky_roster_contact_remove_group_async (roster, contact, "Friends", NULL, contact_group_removed_cb, test); /* Now the server sends the roster upgrade and reply to the first IQ */ send_roster_update (test, "juliet@example.net", "Juliet", "to", groups); ack_iq (test->sched_out, received_iq); g_object_unref (received_iq); received_iq = NULL; /* Wait that: * - the first remove_group operation is completed * - the server receives the IQ for the second remove_group */ test->outstanding += 2; test_wait_pending (test); wocky_bare_contact_set_groups (juliet, (gchar **) groups); g_assert (wocky_bare_contact_equal (contact, juliet)); check_edit_roster_stanza (received_iq, "juliet@example.net", "Juliet", "to", no_group); /* Server sends the roster upgrade and reply to the first IQ */ send_roster_update (test, "juliet@example.net", "Juliet", "to", no_group); ack_iq (test->sched_out, received_iq); g_object_unref (received_iq); received_iq = NULL; /* Wait second remove_group operation is completed */ test->outstanding += 1; test_wait_pending (test); wocky_bare_contact_set_groups (juliet, (gchar **) no_group); g_assert (wocky_bare_contact_equal (contact, juliet)); test_close_both_porters (test); g_object_unref (roster); g_object_unref (juliet); teardown_test (test); } /* Try to add twice the same contact */ static void test_add_contact_twice (void) { WockyRoster *roster; test_data_t *test = setup_test (); WockyBareContact *mercutio, *contact; const gchar *groups[] = { "Friends", "Badger", NULL }; test_open_both_connections (test); roster = create_initial_roster (test); wocky_porter_register_handler_from_anyone (test->sched_out, WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_SET, WOCKY_PORTER_HANDLER_PRIORITY_MAX, iq_set_cb, test, '(', "query", ':', WOCKY_XMPP_NS_ROSTER, ')', NULL); mercutio = create_mercutio (); /* Add the Mercutio to our roster */ wocky_roster_add_contact_async (roster, "mercutio@example.net", "Mercutio", groups, NULL, contact_added_cb, test); test->outstanding += 1; test_wait_pending (test); g_assert (received_iq != NULL); /* The IQ has been sent but the server didn't send the upgrade and the reply * yet */ /* contact is not added yet */ g_assert (wocky_roster_get_contact (roster, "mercutio@example.net") == NULL); check_add_contact_stanza (received_iq, "mercutio@example.net", "Mercutio", groups); /* Try to re-add the same contact */ wocky_roster_add_contact_async (roster, "mercutio@example.net", "Mercutio", groups, NULL, contact_added_cb, test); /* Now the server sends the roster upgrade and reply to the first IQ */ send_roster_update (test, "mercutio@example.net", "Mercutio", "none", groups); ack_iq (test->sched_out, received_iq); g_object_unref (received_iq); received_iq = NULL; /* Wait that the 2 add_contact operation are completed. No IQ is sent for * the second as nothing has changed. */ test->outstanding += 2; test_wait_pending (test); /* check if the contact has been actually added */ contact = wocky_roster_get_contact (roster, "mercutio@example.net"); g_assert (wocky_bare_contact_equal (contact, mercutio)); test_close_both_porters (test); g_object_unref (mercutio); g_object_unref (roster); teardown_test (test); } int main (int argc, char **argv) { int result; test_init (argc, argv); /* basic */ g_test_add_func ("/xmpp-roster/instantiation", test_instantiation); /* roster fetching */ g_test_add_func ("/xmpp-roster/fetch-roster-send-iq", test_fetch_roster_send_iq); g_test_add_func ("/xmpp-roster/fetch-roster-reply", test_fetch_roster_reply); /* receive upgrade from server */ g_test_add_func ("/xmpp-roster/roster-upgrade-add", test_roster_upgrade_add); g_test_add_func ("/xmpp-roster/roster-upgrade-remove", test_roster_upgrade_remove); g_test_add_func ("/xmpp-roster/roster-upgrade-change", test_roster_upgrade_change); /* edit roster */ g_test_add_func ("/xmpp-roster/roster-add-contact", test_roster_add_contact); g_test_add_func ("/xmpp-roster/roster-remove-contact", test_roster_remove_contact); g_test_add_func ("/xmpp-roster/roster-change-name", test_roster_change_name); g_test_add_func ("/xmpp-roster/contact-add-group", test_contact_add_group); g_test_add_func ("/xmpp-roster/contact-remove-group", test_contact_remove_group); /* start a edit operation while another edit operation is running */ g_test_add_func ("/xmpp-roster/remove-contact-re-add", test_remove_contact_re_add); g_test_add_func ("/xmpp-roster/remove-contact-edit", test_remove_contact_edit); g_test_add_func ("/xmpp-roster/multi-contact-edit", test_multi_contact_edit); g_test_add_func ("/xmpp-roster/edit-contact-remove", test_edit_contact_remove); g_test_add_func ("/xmpp-roster/change-name-twice", test_change_name_twice); g_test_add_func ("/xmpp-roster/remove-contact-twice", test_remove_contact_twice); g_test_add_func ("/xmpp-roster/change-name-add-remove", test_change_name_remove_add); g_test_add_func ("/xmpp-roster/add-two-groups", test_add_two_groups); g_test_add_func ("/xmpp-roster/remove-two-groups", test_remove_two_groups); g_test_add_func ("/xmpp-roster/add-contact-twice", test_add_contact_twice); result = g_test_run (); test_deinit (); return result; } telepathy-gabble-0.18.2/lib/ext/wocky/tests/wocky-resource-contact-test.c0000644000175000017500000000620212200204546026407 0ustar00smcvsmcv00000000000000#ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include #include "wocky-test-helper.h" static void test_instantiation (void) { WockyBareContact *bare_contact; WockyResourceContact *resource_contact; bare_contact = g_object_new (WOCKY_TYPE_BARE_CONTACT, "jid", "juliet@example.net", NULL); resource_contact = g_object_new (WOCKY_TYPE_RESOURCE_CONTACT, "resource", "Balcony", "bare-contact", bare_contact, NULL); g_assert (WOCKY_IS_RESOURCE_CONTACT (resource_contact)); /* WockyResourceContact is a sub-class of WockyContact */ g_assert (WOCKY_IS_CONTACT (resource_contact)); g_object_unref (resource_contact); resource_contact = wocky_resource_contact_new (bare_contact, "Balcony"); g_assert (WOCKY_IS_RESOURCE_CONTACT (resource_contact)); g_assert (WOCKY_IS_CONTACT (resource_contact)); g_object_unref (resource_contact); g_object_unref (bare_contact); } static void test_get_resource (void) { WockyBareContact *bare_contact; WockyResourceContact *resource_contact; bare_contact = g_object_new (WOCKY_TYPE_BARE_CONTACT, "jid", "juliet@example.net", NULL); resource_contact = wocky_resource_contact_new (bare_contact, "Balcony"); g_assert (!wocky_strdiff ( wocky_resource_contact_get_resource (resource_contact), "Balcony")); g_object_unref (bare_contact); g_object_unref (resource_contact); } static void test_get_bare_contact (void) { WockyBareContact *bare_contact; WockyResourceContact *resource_contact; bare_contact = g_object_new (WOCKY_TYPE_BARE_CONTACT, "jid", "juliet@example.net", NULL); resource_contact = wocky_resource_contact_new (bare_contact, "Balcony"); g_assert (wocky_resource_contact_get_bare_contact (resource_contact) == bare_contact); g_object_unref (bare_contact); g_object_unref (resource_contact); } static void test_equal (void) { WockyBareContact *a, *b; WockyResourceContact *a_1, *a_2, *b_1; a = wocky_bare_contact_new ("a@example.net"); a_1 = wocky_resource_contact_new (a, "Resource1"); g_assert (wocky_resource_contact_equal (a_1, a_1)); g_assert (!wocky_resource_contact_equal (a_1, NULL)); g_assert (!wocky_resource_contact_equal (NULL, a_1)); a_2 = wocky_resource_contact_new (a, "Resource2"); g_assert (!wocky_resource_contact_equal (a_1, a_2)); b = wocky_bare_contact_new ("b@example.net"); b_1 = wocky_resource_contact_new (b, "Resource1"); g_assert (!wocky_resource_contact_equal (b_1, a_1)); g_assert (!wocky_resource_contact_equal (b_1, a_2)); g_object_unref (a); g_object_unref (b); g_object_unref (a_1); g_object_unref (a_2); g_object_unref (b_1); } int main (int argc, char **argv) { int result; test_init (argc, argv); g_test_add_func ("/resource-contact/instantiation", test_instantiation); g_test_add_func ("/resource-contact/get-resource", test_get_resource); g_test_add_func ("/resource-contact/get-bare-contact", test_get_bare_contact); g_test_add_func ("/resource-contact/equal", test_equal); result = g_test_run (); test_deinit (); return result; } telepathy-gabble-0.18.2/lib/ext/wocky/tests/wocky-pubsub-service-test.c0000644000175000017500000005672012200204546026077 0ustar00smcvsmcv00000000000000#ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include #include "wocky-pubsub-test-helpers.h" #include "wocky-test-helper.h" #include "wocky-test-stream.h" /* Test instantiating a WockyPubsubService object */ static WockySession * create_session (void) { WockyXmppConnection *connection; WockyTestStream *stream; WockySession *session; stream = g_object_new (WOCKY_TYPE_TEST_STREAM, NULL); connection = wocky_xmpp_connection_new (stream->stream0); session = wocky_session_new_with_connection (connection, "example.com"); g_object_unref (connection); g_object_unref (stream); return session; } static void test_instantiation (void) { WockyPubsubService *pubsub; WockySession *session; session = create_session (); pubsub = wocky_pubsub_service_new (session, "pubsub.localhost"); g_assert (pubsub != NULL); g_object_unref (pubsub); g_object_unref (session); } /* Test wocky_pubsub_service_ensure_node */ static void test_ensure_node (void) { WockyPubsubService *pubsub; WockySession *session; WockyPubsubNode *node; session = create_session (); pubsub = wocky_pubsub_service_new (session, "pubsub.localhost"); node = wocky_pubsub_service_lookup_node (pubsub, "node1"); g_assert (node == NULL); node = wocky_pubsub_service_ensure_node (pubsub, "node1"); g_assert (node != NULL); node = wocky_pubsub_service_lookup_node (pubsub, "node1"); g_assert (node != NULL); /* destroy the node */ g_object_unref (node); node = wocky_pubsub_service_lookup_node (pubsub, "node1"); g_assert (node == NULL); g_object_unref (pubsub); g_object_unref (session); } /* Test wocky_pubsub_service_get_default_node_configuration_async */ static gboolean test_get_default_node_configuration_iq_cb (WockyPorter *porter, WockyStanza *stanza, gpointer user_data) { test_data_t *test = (test_data_t *) user_data; WockyStanza *reply; reply = wocky_stanza_build_iq_result (stanza, '(', "pubsub", ':', WOCKY_XMPP_NS_PUBSUB_OWNER, '(', "default", '(', "x", ':', WOCKY_XMPP_NS_DATA, '@', "type", "form", '(', "field", '@', "type", "hidden", '@', "var", "FORM_TYPE", '(', "value", '$', WOCKY_XMPP_NS_PUBSUB_NODE_CONFIG, ')', ')', '(', "field", '@', "var", "pubsub#title", '@', "type", "text-single", '@', "label", "Title", ')', '(', "field", '@', "var", "pubsub#deliver_notifications", '@', "type", "boolean", '@', "label", "Deliver event notifications", ')', ')', ')', ')', NULL); wocky_porter_send (porter, reply); g_object_unref (reply); test->outstanding--; g_main_loop_quit (test->loop); return TRUE; } static void test_get_default_node_configuration_cb (GObject *source, GAsyncResult *res, gpointer user_data) { test_data_t *test = (test_data_t *) user_data; WockyDataForm *form; WockyDataFormField *field; form = wocky_pubsub_service_get_default_node_configuration_finish ( WOCKY_PUBSUB_SERVICE (source), res, NULL); g_assert (form != NULL); field = g_hash_table_lookup (form->fields, "pubsub#title"); g_assert (field != NULL); field = g_hash_table_lookup (form->fields, "pubsub#deliver_notifications"); g_assert (field != NULL); g_object_unref (form); test->outstanding--; g_main_loop_quit (test->loop); } static void get_default_node_configuration_test (WockyPorterHandlerFunc iq_cb, GAsyncReadyCallback get_cb) { test_data_t *test = setup_test (); WockyPubsubService *pubsub; test_open_both_connections (test); wocky_porter_start (test->sched_out); wocky_session_start (test->session_in); pubsub = wocky_pubsub_service_new (test->session_in, "pubsub.localhost"); wocky_porter_register_handler_from_anyone (test->sched_out, WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_GET, WOCKY_PORTER_HANDLER_PRIORITY_MAX, iq_cb, test, '(', "pubsub", ':', WOCKY_XMPP_NS_PUBSUB_OWNER, '(', "default", ')', ')', NULL); wocky_pubsub_service_get_default_node_configuration_async (pubsub, NULL, get_cb, test); test->outstanding += 2; test_wait_pending (test); g_object_unref (pubsub); test_close_both_porters (test); teardown_test (test); } static void test_get_default_node_configuration (void) { get_default_node_configuration_test ( test_get_default_node_configuration_iq_cb, test_get_default_node_configuration_cb); } /* Try to retrieve default node configuration and get a insufficient * privileges error */ static gboolean test_get_default_node_configuration_insufficient_iq_cb (WockyPorter *porter, WockyStanza *stanza, gpointer user_data) { test_data_t *test = (test_data_t *) user_data; WockyStanza *reply; reply = wocky_stanza_build_iq_error (stanza, '(', "error", '@', "type", "auth", '(', "forbidden", ':', WOCKY_XMPP_NS_STANZAS, ')', ')', NULL); wocky_porter_send (porter, reply); g_object_unref (reply); test->outstanding--; g_main_loop_quit (test->loop); return TRUE; } static void test_get_default_node_configuration_insufficient_cb (GObject *source, GAsyncResult *res, gpointer user_data) { test_data_t *test = (test_data_t *) user_data; WockyDataForm *form; GError *error = NULL; form = wocky_pubsub_service_get_default_node_configuration_finish ( WOCKY_PUBSUB_SERVICE (source), res, &error); g_assert (form == NULL); g_assert_error (error, WOCKY_XMPP_ERROR, WOCKY_XMPP_ERROR_FORBIDDEN); g_error_free (error); test->outstanding--; g_main_loop_quit (test->loop); } static void test_get_default_node_configuration_insufficient (void) { get_default_node_configuration_test ( test_get_default_node_configuration_insufficient_iq_cb, test_get_default_node_configuration_insufficient_cb); } /* Create a node with default config */ static gboolean test_create_node_no_config_iq_cb (WockyPorter *porter, WockyStanza *stanza, gpointer user_data) { test_data_t *test = (test_data_t *) user_data; WockyStanza *reply; WockyNode *node; node = wocky_node_get_child_ns (wocky_stanza_get_top_node (stanza), "pubsub", WOCKY_XMPP_NS_PUBSUB); g_assert (node != NULL); node = wocky_node_get_child (node, "create"); g_assert (node != NULL); g_assert (!wocky_strdiff (wocky_node_get_attribute (node, "node"), "node1")); reply = wocky_stanza_build_iq_result (stanza, NULL); wocky_porter_send (porter, reply); g_object_unref (reply); test->outstanding--; g_main_loop_quit (test->loop); return TRUE; } static void test_create_node_no_config_cb (GObject *source, GAsyncResult *res, gpointer user_data) { test_data_t *test = (test_data_t *) user_data; WockyPubsubNode *node; node = wocky_pubsub_service_create_node_finish (WOCKY_PUBSUB_SERVICE (source), res, NULL); g_assert (node != NULL); g_assert (!wocky_strdiff (wocky_pubsub_node_get_name (node), "node1")); g_object_unref (node); test->outstanding--; g_main_loop_quit (test->loop); } static void create_node_test (WockyPorterHandlerFunc iq_cb, GAsyncReadyCallback create_cb, const gchar *node_name) { test_data_t *test = setup_test (); WockyPubsubService *pubsub; test_open_both_connections (test); wocky_porter_start (test->sched_out); wocky_session_start (test->session_in); pubsub = wocky_pubsub_service_new (test->session_in, "pubsub.localhost"); wocky_porter_register_handler_from_anyone (test->sched_out, WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_SET, WOCKY_PORTER_HANDLER_PRIORITY_MAX, iq_cb, test, '(', "pubsub", ':', WOCKY_XMPP_NS_PUBSUB, '(', "create", ')', ')', NULL); wocky_pubsub_service_create_node_async (pubsub, node_name, NULL, NULL, create_cb, test); test->outstanding += 2; test_wait_pending (test); g_object_unref (pubsub); test_close_both_porters (test); teardown_test (test); } static void test_create_node_no_config (void) { create_node_test (test_create_node_no_config_iq_cb, test_create_node_no_config_cb, "node1"); } /* creation of a node fails because service does not support node creation */ static gboolean test_create_node_unsupported_iq_cb (WockyPorter *porter, WockyStanza *stanza, gpointer user_data) { test_data_t *test = (test_data_t *) user_data; WockyStanza *reply; reply = wocky_stanza_build_iq_error (stanza, '(', "error", '@', "type", "cancel", '(', "feature-not-implemented", ':', WOCKY_XMPP_NS_STANZAS, ')', '(', "unsupported", ':', WOCKY_XMPP_NS_PUBSUB_ERRORS, '@', "feature", "create-nodes", ')', ')', NULL); wocky_porter_send (porter, reply); g_object_unref (reply); test->outstanding--; g_main_loop_quit (test->loop); return TRUE; } static void test_create_node_unsupported_cb (GObject *source, GAsyncResult *res, gpointer user_data) { test_data_t *test = (test_data_t *) user_data; WockyPubsubNode *node; GError *error = NULL; node = wocky_pubsub_service_create_node_finish (WOCKY_PUBSUB_SERVICE (source), res, &error); g_assert (node == NULL); g_assert_error (error, WOCKY_XMPP_ERROR, WOCKY_XMPP_ERROR_FEATURE_NOT_IMPLEMENTED); g_error_free (error); test->outstanding--; g_main_loop_quit (test->loop); } static void test_create_node_unsupported (void) { create_node_test (test_create_node_unsupported_iq_cb, test_create_node_unsupported_cb, "node1"); } /* Create an instant node (no name passed) */ static gboolean test_create_instant_node_iq_cb (WockyPorter *porter, WockyStanza *stanza, gpointer user_data) { test_data_t *test = (test_data_t *) user_data; WockyStanza *reply; reply = wocky_stanza_build_iq_result (stanza, '(', "pubsub", ':', WOCKY_XMPP_NS_PUBSUB, '(', "create", '@', "node", "instant_node", ')', ')', NULL); wocky_porter_send (porter, reply); g_object_unref (reply); test->outstanding--; g_main_loop_quit (test->loop); return TRUE; } static void test_create_instant_node_cb (GObject *source, GAsyncResult *res, gpointer user_data) { test_data_t *test = (test_data_t *) user_data; WockyPubsubNode *node; node = wocky_pubsub_service_create_node_finish (WOCKY_PUBSUB_SERVICE (source), res, NULL); g_assert (node != NULL); g_assert (!wocky_strdiff (wocky_pubsub_node_get_name (node), "instant_node")); g_object_unref (node); test->outstanding--; g_main_loop_quit (test->loop); } static void test_create_instant_node (void) { create_node_test (test_create_instant_node_iq_cb, test_create_instant_node_cb, NULL); } /* Ask for a node with one name, get one back with another */ static gboolean test_create_node_renamed_iq_cb (WockyPorter *porter, WockyStanza *stanza, gpointer user_data) { test_data_t *test = (test_data_t *) user_data; WockyStanza *reply; WockyNode *node; node = wocky_node_get_child_ns (wocky_stanza_get_top_node (stanza), "pubsub", WOCKY_XMPP_NS_PUBSUB); g_assert (node != NULL); node = wocky_node_get_child (node, "create"); g_assert (node != NULL); g_assert (!wocky_strdiff (wocky_node_get_attribute (node, "node"), "node1")); reply = wocky_stanza_build_iq_result (stanza, '(', "pubsub", ':', WOCKY_XMPP_NS_PUBSUB, '(', "create", '@', "node", "metal-bird", ')', ')', NULL); wocky_porter_send (porter, reply); g_object_unref (reply); test->outstanding--; g_main_loop_quit (test->loop); return TRUE; } static void test_create_node_renamed_cb (GObject *source, GAsyncResult *res, gpointer user_data) { test_data_t *test = (test_data_t *) user_data; WockyPubsubNode *node; node = wocky_pubsub_service_create_node_finish (WOCKY_PUBSUB_SERVICE (source), res, NULL); g_assert (node != NULL); g_assert_cmpstr (wocky_pubsub_node_get_name (node), ==, "metal-bird"); g_object_unref (node); test->outstanding--; g_main_loop_quit (test->loop); } static void test_create_node_renamed (void) { create_node_test (test_create_node_renamed_iq_cb, test_create_node_renamed_cb, "node1"); } /* Create a node with configuration */ static void test_create_node_config_config_cb (GObject *source, GAsyncResult *res, gpointer user_data) { test_data_t *test = (test_data_t *) user_data; WockyDataForm *form; gboolean set_succeeded; form = wocky_pubsub_service_get_default_node_configuration_finish ( WOCKY_PUBSUB_SERVICE (source), res, NULL); g_assert (form != NULL); set_succeeded = wocky_data_form_set_string (form, "pubsub#title", "Badger", FALSE); g_assert (set_succeeded); set_succeeded = wocky_data_form_set_boolean (form, "pubsub#deliver_notifications", FALSE, FALSE); g_assert (set_succeeded); wocky_pubsub_service_create_node_async (WOCKY_PUBSUB_SERVICE (source), "node1", form, NULL, test_create_node_no_config_cb, test); g_object_unref (form); test->outstanding--; g_main_loop_quit (test->loop); } static gboolean test_create_node_config_create_iq_cb (WockyPorter *porter, WockyStanza *stanza, gpointer user_data) { test_data_t *test = (test_data_t *) user_data; WockyStanza *reply; WockyNode *node; GSList *l; gboolean form_type = FALSE, title = FALSE, notif = FALSE; node = wocky_node_get_child_ns (wocky_stanza_get_top_node (stanza), "pubsub", WOCKY_XMPP_NS_PUBSUB); g_assert (node != NULL); node = wocky_node_get_child (node, "configure"); g_assert (node != NULL); node = wocky_node_get_child_ns (node, "x", WOCKY_XMPP_NS_DATA); g_assert (node != NULL); for (l = node->children; l != NULL; l = g_slist_next (l)) { WockyNode *field = l->data; const gchar *type, *var, *value = NULL; WockyNode *v; g_assert (!wocky_strdiff (field->name, "field")); var = wocky_node_get_attribute (field, "var"); type = wocky_node_get_attribute (field, "type"); v = wocky_node_get_child (field, "value"); g_assert (v != NULL); value = v->content; if (!wocky_strdiff (var, "FORM_TYPE")) { g_assert (!wocky_strdiff (type, "hidden")); g_assert (!wocky_strdiff (value, WOCKY_XMPP_NS_PUBSUB_NODE_CONFIG)); form_type = TRUE; } else if (!wocky_strdiff (var, "pubsub#title")) { g_assert (!wocky_strdiff (type, "text-single")); g_assert (!wocky_strdiff (value, "Badger")); title = TRUE; } else if (!wocky_strdiff (var, "pubsub#deliver_notifications")) { g_assert (!wocky_strdiff (type, "boolean")); g_assert (!wocky_strdiff (value, "0")); notif = TRUE; } else g_assert_not_reached (); } g_assert (form_type && title && notif); reply = wocky_stanza_build_iq_result (stanza, NULL); wocky_porter_send (porter, reply); g_object_unref (reply); test->outstanding--; g_main_loop_quit (test->loop); return TRUE; } static void test_create_node_config (void) { test_data_t *test = setup_test (); WockyPubsubService *pubsub; test_open_both_connections (test); wocky_porter_start (test->sched_out); wocky_session_start (test->session_in); pubsub = wocky_pubsub_service_new (test->session_in, "pubsub.localhost"); wocky_porter_register_handler_from_anyone (test->sched_out, WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_GET, WOCKY_PORTER_HANDLER_PRIORITY_MAX, test_get_default_node_configuration_iq_cb, test, '(', "pubsub", ':', WOCKY_XMPP_NS_PUBSUB_OWNER, '(', "default", ')', ')', NULL); wocky_porter_register_handler_from_anyone (test->sched_out, WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_SET, WOCKY_PORTER_HANDLER_PRIORITY_MAX, test_create_node_config_create_iq_cb, test, '(', "pubsub", ':', WOCKY_XMPP_NS_PUBSUB, '(', "create", ')', ')', NULL); wocky_pubsub_service_get_default_node_configuration_async (pubsub, NULL, test_create_node_config_config_cb, test); test->outstanding += 4; test_wait_pending (test); test_close_both_porters (test); teardown_test (test); g_object_unref (pubsub); } /* Four examples taken from §5.6 Retrieve Subscriptions */ typedef enum { MODE_NORMAL, MODE_NO_SUBSCRIPTIONS, MODE_BZZT, MODE_AT_NODE } RetrieveSubscriptionsMode; typedef struct { test_data_t *test; RetrieveSubscriptionsMode mode; } RetrieveSubscriptionsCtx; static CannedSubscriptions normal_subs[] = { { "node1", "francisco@denmark.lit", "subscribed", WOCKY_PUBSUB_SUBSCRIPTION_SUBSCRIBED, NULL }, { "node2", "francisco@denmark.lit/bonghits", "subscribed", WOCKY_PUBSUB_SUBSCRIPTION_SUBSCRIBED, NULL }, { "node5", "francisco@denmark.lit", "unconfigured", WOCKY_PUBSUB_SUBSCRIPTION_UNCONFIGURED, NULL }, { "node6", "francisco@denmark.lit", "pending", WOCKY_PUBSUB_SUBSCRIPTION_PENDING, NULL }, { NULL, } }; static CannedSubscriptions bonghit_subs[] = { { "bonghits", "bernardo@denmark.lit", "subscribed", WOCKY_PUBSUB_SUBSCRIPTION_SUBSCRIBED, "123-abc" }, { "bonghits", "bernardo@denmark.lit/i-am-poorly-read", "subscribed", WOCKY_PUBSUB_SUBSCRIPTION_SUBSCRIBED, "004-yyy" }, { NULL, } }; static WockyStanza * make_subscriptions_response (WockyStanza *stanza, const gchar *node, CannedSubscriptions *subs) { WockyStanza *reply; WockyNode *s; reply = wocky_stanza_build_iq_result (stanza, '(', "pubsub", ':', WOCKY_XMPP_NS_PUBSUB, '(', "subscriptions", '*', &s, ')', ')', NULL); if (node != NULL) wocky_node_set_attribute (s, "node", node); test_pubsub_add_subscription_nodes (s, subs, (node == NULL)); return reply; } static gboolean test_retrieve_subscriptions_iq_cb ( WockyPorter *porter, WockyStanza *stanza, gpointer user_data) { RetrieveSubscriptionsCtx *ctx = user_data; test_data_t *test = ctx->test; WockyStanza *reply, *expected; WockyNode *subscriptions; expected = wocky_stanza_build ( WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_GET, NULL, "pubsub.localhost", '(', "pubsub", ':', WOCKY_XMPP_NS_PUBSUB, '(', "subscriptions", '*', &subscriptions, ')', ')', NULL); if (ctx->mode == MODE_AT_NODE) wocky_node_set_attribute (subscriptions, "node", "bonghits"); test_assert_stanzas_equal_no_id (stanza, expected); g_object_unref (expected); switch (ctx->mode) { case MODE_NORMAL: reply = make_subscriptions_response (stanza, NULL, normal_subs); break; case MODE_NO_SUBSCRIPTIONS: reply = make_subscriptions_response (stanza, NULL, NULL); break; case MODE_BZZT: { GError e = { WOCKY_XMPP_ERROR, WOCKY_XMPP_ERROR_FEATURE_NOT_IMPLEMENTED, "FIXME: " }; reply = wocky_stanza_build_iq_error (stanza, NULL); wocky_stanza_error_to_node (&e, wocky_stanza_get_top_node (reply)); break; } case MODE_AT_NODE: reply = make_subscriptions_response (stanza, "bonghits", bonghit_subs); break; default: g_assert_not_reached (); } wocky_porter_send (porter, reply); g_object_unref (reply); test->outstanding--; g_main_loop_quit (test->loop); return TRUE; } static void check_subscriptions ( GObject *source, GAsyncResult *res, CannedSubscriptions *expected_subs) { GList *subscriptions; g_assert (wocky_pubsub_service_retrieve_subscriptions_finish ( WOCKY_PUBSUB_SERVICE (source), res, &subscriptions, NULL)); test_pubsub_check_and_free_subscriptions (subscriptions, expected_subs); } static void retrieve_subscriptions_cb (GObject *source, GAsyncResult *res, gpointer user_data) { RetrieveSubscriptionsCtx *ctx = user_data; test_data_t *test = ctx->test; GError *error = NULL; switch (ctx->mode) { case MODE_NORMAL: check_subscriptions (source, res, normal_subs); break; case MODE_NO_SUBSCRIPTIONS: check_subscriptions (source, res, normal_subs + 4); break; case MODE_BZZT: g_assert (!wocky_pubsub_service_retrieve_subscriptions_finish ( WOCKY_PUBSUB_SERVICE (source), res, NULL, &error)); /* FIXME: moar detail */ g_assert_error (error, WOCKY_XMPP_ERROR, WOCKY_XMPP_ERROR_FEATURE_NOT_IMPLEMENTED); g_clear_error (&error); break; case MODE_AT_NODE: check_subscriptions (source, res, bonghit_subs); break; default: g_assert_not_reached (); } test->outstanding--; g_main_loop_quit (test->loop); } static void test_retrieve_subscriptions (gconstpointer mode_) { test_data_t *test = setup_test (); RetrieveSubscriptionsMode mode = GPOINTER_TO_UINT (mode_); RetrieveSubscriptionsCtx ctx = { test, mode }; WockyPubsubService *pubsub; WockyPubsubNode *node = NULL; test_open_both_connections (test); wocky_porter_start (test->sched_out); wocky_session_start (test->session_in); pubsub = wocky_pubsub_service_new (test->session_in, "pubsub.localhost"); wocky_porter_register_handler_from_anyone (test->sched_out, WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_GET, WOCKY_PORTER_HANDLER_PRIORITY_MAX, test_retrieve_subscriptions_iq_cb, &ctx, '(', "pubsub", ':', WOCKY_XMPP_NS_PUBSUB, '(', "subscriptions", ')', ')', NULL); if (mode == MODE_AT_NODE) node = wocky_pubsub_service_ensure_node (pubsub, "bonghits"); wocky_pubsub_service_retrieve_subscriptions_async (pubsub, node, NULL, retrieve_subscriptions_cb, &ctx); test->outstanding += 2; test_wait_pending (test); if (node != NULL) g_object_unref (node); test_close_both_porters (test); teardown_test (test); g_object_unref (pubsub); } int main (int argc, char **argv) { int result; test_init (argc, argv); g_test_add_func ("/pubsub-service/instantiation", test_instantiation); g_test_add_func ("/pubsub-service/ensure_node", test_ensure_node); g_test_add_func ("/pubsub-service/get-default-node-configuration", test_get_default_node_configuration); g_test_add_func ( "/pubsub-service/get-default-node-configuration-insufficient", test_get_default_node_configuration_insufficient); g_test_add_data_func ("/pubsub-service/retrieve-subscriptions/normal", GUINT_TO_POINTER (MODE_NORMAL), test_retrieve_subscriptions); g_test_add_data_func ("/pubsub-service/retrieve-subscriptions/none", GUINT_TO_POINTER (MODE_NO_SUBSCRIPTIONS), test_retrieve_subscriptions); g_test_add_data_func ("/pubsub-service/retrieve-subscriptions/error", GUINT_TO_POINTER (MODE_BZZT), test_retrieve_subscriptions); g_test_add_data_func ("/pubsub-service/retrieve-subscriptions/for-node", GUINT_TO_POINTER (MODE_AT_NODE), test_retrieve_subscriptions); g_test_add_func ("/pubsub-service/create-node-no-config", test_create_node_no_config); g_test_add_func ("/pubsub-service/create-node-unsupported", test_create_node_unsupported); g_test_add_func ("/pubsub-service/create-instant-node", test_create_instant_node); g_test_add_func ("/pubsub-service/create-node-renamed", test_create_node_renamed); g_test_add_func ("/pubsub-service/create-node-config", test_create_node_config); result = g_test_run (); test_deinit (); return result; } telepathy-gabble-0.18.2/lib/ext/wocky/tests/wocky-pubsub-node-test.c0000644000175000017500000010727412200204546025365 0ustar00smcvsmcv00000000000000#ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include #include "wocky-pubsub-test-helpers.h" #include "wocky-test-helper.h" #include "wocky-test-stream.h" /* Test instantiating a WockyPubsubNode object */ static void test_instantiation (void) { WockyPubsubService *pubsub; WockyXmppConnection *connection; WockyTestStream *stream; WockySession *session; WockyPubsubNode *node; stream = g_object_new (WOCKY_TYPE_TEST_STREAM, NULL); connection = wocky_xmpp_connection_new (stream->stream0); session = wocky_session_new_with_connection (connection, "example.com"); pubsub = wocky_pubsub_service_new (session, "pubsub.localhost"); g_assert (pubsub != NULL); node = wocky_pubsub_service_ensure_node (pubsub, "node1"); g_assert (node != NULL); g_assert (!wocky_strdiff (wocky_pubsub_node_get_name (node), "node1")); g_object_unref (node); g_object_unref (pubsub); g_object_unref (session); g_object_unref (connection); g_object_unref (stream); } /* test wocky_pubsub_node_make_publish_stanza() */ static void test_make_publish_stanza (void) { WockyPubsubService *pubsub; WockyXmppConnection *connection; WockyTestStream *stream; WockySession *session; WockyPubsubNode *node; WockyStanza *stanza, *expected; WockyNode *pubsub_node, *publish, *item; stream = g_object_new (WOCKY_TYPE_TEST_STREAM, NULL); connection = wocky_xmpp_connection_new (stream->stream0); session = wocky_session_new_with_connection (connection, "example.com"); pubsub = wocky_pubsub_service_new (session, "pubsub.localhost"); node = wocky_pubsub_service_ensure_node (pubsub, "track1"); stanza = wocky_pubsub_node_make_publish_stanza (node, &pubsub_node, &publish, &item); g_assert (stanza != NULL); g_assert (pubsub_node != NULL); g_assert (publish != NULL); g_assert (item != NULL); /* I've embraced and extended pubsub, and want to put stuff on the * and nodes... */ wocky_node_set_attribute (pubsub_node, "gig", "tomorrow"); wocky_node_set_attribute (publish, "kaki", "king"); /* Oh, and I should probably publish something. */ wocky_node_add_child_with_content_ns (item, "castle", "bone chaos", "urn:example:songs"); expected = wocky_stanza_build (WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_SET, NULL, "pubsub.localhost", '(', "pubsub", ':', WOCKY_XMPP_NS_PUBSUB, '@', "gig", "tomorrow", '(', "publish", '@', "kaki", "king", '@', "node", "track1", '(', "item", '(', "castle", ':', "urn:example:songs", '$', "bone chaos", ')', ')', ')', ')', NULL); test_assert_stanzas_equal (stanza, expected); g_object_unref (expected); g_object_unref (stanza); g_object_unref (node); g_object_unref (pubsub); g_object_unref (session); g_object_unref (connection); g_object_unref (stream); } /* Test subscribing to a node. */ static gboolean test_subscribe_iq_cb (WockyPorter *porter, WockyStanza *stanza, gpointer user_data) { test_data_t *test = (test_data_t *) user_data; WockyStanza *reply; reply = wocky_stanza_build_iq_result (stanza, '(', "pubsub", ':', WOCKY_XMPP_NS_PUBSUB, '(', "subscription", '@', "node", "node1", '@', "jid", "mighty@pirate.lit", '@', "subscription", "subscribed", ')', ')', NULL); wocky_porter_send (porter, reply); g_object_unref (reply); test->outstanding--; g_main_loop_quit (test->loop); return TRUE; } static void test_subscribe_cb (GObject *source, GAsyncResult *res, gpointer user_data) { WockyPubsubNode *node = WOCKY_PUBSUB_NODE (source); test_data_t *test = (test_data_t *) user_data; WockyPubsubSubscription *sub; sub = wocky_pubsub_node_subscribe_finish (WOCKY_PUBSUB_NODE (source), res, NULL); g_assert (sub != NULL); /* the node name should be the same. */ g_assert_cmpstr (wocky_pubsub_node_get_name (sub->node), ==, wocky_pubsub_node_get_name (node)); /* in fact, they should be the same node. */ g_assert (sub->node == node); wocky_pubsub_subscription_free (sub); test->outstanding--; g_main_loop_quit (test->loop); } static void test_subscribe (void) { test_data_t *test = setup_test (); WockyPubsubService *pubsub; WockyPubsubNode *node; test_open_both_connections (test); wocky_porter_start (test->sched_out); wocky_session_start (test->session_in); pubsub = wocky_pubsub_service_new (test->session_in, "pubsub.localhost"); wocky_porter_register_handler_from_anyone (test->sched_out, WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_SET, WOCKY_PORTER_HANDLER_PRIORITY_MAX, test_subscribe_iq_cb, test, '(', "pubsub", ':', WOCKY_XMPP_NS_PUBSUB, '(', "subscribe", '@', "node", "node1", '@', "jid", "mighty@pirate.lit", ')', ')', NULL); node = wocky_pubsub_service_ensure_node (pubsub, "node1"); g_assert (node != NULL); wocky_pubsub_node_subscribe_async (node, "mighty@pirate.lit", NULL, test_subscribe_cb, test); test->outstanding += 2; test_wait_pending (test); g_object_unref (node); g_object_unref (pubsub); test_close_both_porters (test); teardown_test (test); } /* Test unsubscribing from a node. */ typedef struct { test_data_t *test; gboolean expect_subid; } TestUnsubscribeCtx; #define EXPECTED_SUBID "⚞♥⚟" static gboolean test_unsubscribe_iq_cb (WockyPorter *porter, WockyStanza *stanza, gpointer user_data) { TestUnsubscribeCtx *ctx = user_data; test_data_t *test = ctx->test; WockyNode *unsubscribe; const gchar *subid; WockyStanza *reply; unsubscribe = wocky_node_get_child ( wocky_node_get_child_ns (wocky_stanza_get_top_node (stanza), "pubsub", WOCKY_XMPP_NS_PUBSUB), "unsubscribe"); g_assert (unsubscribe != NULL); subid = wocky_node_get_attribute (unsubscribe, "subid"); if (ctx->expect_subid) g_assert_cmpstr (EXPECTED_SUBID, ==, subid); else g_assert_cmpstr (NULL, ==, subid); reply = wocky_stanza_build_iq_result (stanza, NULL); wocky_porter_send (porter, reply); g_object_unref (reply); test->outstanding--; g_main_loop_quit (test->loop); return TRUE; } static void test_unsubscribe_cb (GObject *source, GAsyncResult *res, gpointer user_data) { TestUnsubscribeCtx *ctx = user_data; test_data_t *test = ctx->test; gboolean ret; ret = wocky_pubsub_node_unsubscribe_finish (WOCKY_PUBSUB_NODE (source), res, NULL); g_assert (ret); test->outstanding--; g_main_loop_quit (test->loop); } static void test_unsubscribe (void) { test_data_t *test = setup_test (); TestUnsubscribeCtx ctx = { test, }; WockyPubsubService *pubsub; WockyPubsubNode *node; test_open_both_connections (test); wocky_porter_start (test->sched_out); wocky_session_start (test->session_in); pubsub = wocky_pubsub_service_new (test->session_in, "pubsub.localhost"); wocky_porter_register_handler_from_anyone (test->sched_out, WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_SET, WOCKY_PORTER_HANDLER_PRIORITY_MAX, test_unsubscribe_iq_cb, &ctx, '(', "pubsub", ':', WOCKY_XMPP_NS_PUBSUB, '(', "unsubscribe", '@', "node", "node1", '@', "jid", "mighty@pirate.lit", ')', ')', NULL); node = wocky_pubsub_service_ensure_node (pubsub, "node1"); g_assert (node != NULL); /* first, test unsubscribing with no subid */ ctx.expect_subid = FALSE; wocky_pubsub_node_unsubscribe_async (node, "mighty@pirate.lit", NULL, NULL, test_unsubscribe_cb, &ctx); test->outstanding += 2; test_wait_pending (test); /* then test unsubscribing with a subid */ ctx.expect_subid = TRUE; wocky_pubsub_node_unsubscribe_async (node, "mighty@pirate.lit", EXPECTED_SUBID, NULL, test_unsubscribe_cb, &ctx); test->outstanding += 2; test_wait_pending (test); g_object_unref (node); g_object_unref (pubsub); test_close_both_porters (test); teardown_test (test); } /* test wocky_pubsub_node_delete_async */ static gboolean test_delete_iq_cb (WockyPorter *porter, WockyStanza *stanza, gpointer user_data) { test_data_t *test = (test_data_t *) user_data; WockyStanza *reply; reply = wocky_stanza_build_iq_result (stanza, NULL); wocky_porter_send (porter, reply); g_object_unref (reply); test->outstanding--; g_main_loop_quit (test->loop); return TRUE; } static void test_delete_cb (GObject *source, GAsyncResult *res, gpointer user_data) { test_data_t *test = (test_data_t *) user_data; g_assert (wocky_pubsub_node_delete_finish (WOCKY_PUBSUB_NODE (source), res, NULL)); test->outstanding--; g_main_loop_quit (test->loop); } static void test_delete (void) { test_data_t *test = setup_test (); WockyPubsubService *pubsub; WockyPubsubNode *node; test_open_both_connections (test); wocky_porter_start (test->sched_out); wocky_session_start (test->session_in); pubsub = wocky_pubsub_service_new (test->session_in, "pubsub.localhost"); wocky_porter_register_handler_from_anyone (test->sched_out, WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_SET, WOCKY_PORTER_HANDLER_PRIORITY_MAX, test_delete_iq_cb, test, '(', "pubsub", ':', WOCKY_XMPP_NS_PUBSUB_OWNER, '(', "delete", '@', "node", "node1", ')', ')', NULL); node = wocky_pubsub_service_ensure_node (pubsub, "node1"); g_assert (node != NULL); wocky_pubsub_node_delete_async (node, NULL, test_delete_cb, test); test->outstanding += 2; test_wait_pending (test); g_object_unref (node); g_object_unref (pubsub); test_close_both_porters (test); teardown_test (test); } /* Test retrieving a list of subscribers. See XEP-0060 §8.8.1 Retrieve * Subscriptions List * */ static CannedSubscriptions example_183[] = { { "princely_musings", "hamlet@denmark.lit", "subscribed", WOCKY_PUBSUB_SUBSCRIPTION_SUBSCRIBED, NULL }, { "princely_musings", "polonius@denmark.lit", "unconfigured", WOCKY_PUBSUB_SUBSCRIPTION_UNCONFIGURED, NULL }, { "princely_musings", "bernardo@denmark.lit", "subscribed", WOCKY_PUBSUB_SUBSCRIPTION_SUBSCRIBED, "123-abc" }, { "princely_musings", "bernardo@denmark.lit", "subscribed", WOCKY_PUBSUB_SUBSCRIPTION_SUBSCRIBED, "004-yyy" }, { NULL, } }; static gboolean test_list_subscribers_iq_cb (WockyPorter *porter, WockyStanza *stanza, gpointer user_data) { test_data_t *test = (test_data_t *) user_data; WockyStanza *expected, *reply; WockyNode *subscriptions; expected = wocky_stanza_build (WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_GET, NULL, "pubsub.localhost", '(', "pubsub", ':', WOCKY_XMPP_NS_PUBSUB_OWNER, '(', "subscriptions", '@', "node", "princely_musings", ')', ')', NULL); test_assert_stanzas_equal_no_id (stanza, expected); g_object_unref (expected); reply = wocky_stanza_build_iq_result (stanza, '(', "pubsub", ':', WOCKY_XMPP_NS_PUBSUB_OWNER, '(', "subscriptions", '@', "node", "princely_musings", '*', &subscriptions, ')', ')', NULL); test_pubsub_add_subscription_nodes (subscriptions, example_183, FALSE); wocky_porter_send (porter, reply); g_object_unref (reply); test->outstanding--; g_main_loop_quit (test->loop); return TRUE; } static void test_list_subscribers_cb (GObject *source, GAsyncResult *res, gpointer user_data) { test_data_t *test = (test_data_t *) user_data; GList *subscribers; g_assert (wocky_pubsub_node_list_subscribers_finish ( WOCKY_PUBSUB_NODE (source), res, &subscribers, NULL)); test_pubsub_check_and_free_subscriptions (subscribers, example_183); test->outstanding--; g_main_loop_quit (test->loop); } static void test_list_subscribers (void) { test_data_t *test = setup_test (); WockyPubsubService *pubsub; WockyPubsubNode *node; test_open_both_connections (test); wocky_porter_start (test->sched_out); wocky_session_start (test->session_in); pubsub = wocky_pubsub_service_new (test->session_in, "pubsub.localhost"); wocky_porter_register_handler_from_anyone (test->sched_out, WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_GET, WOCKY_PORTER_HANDLER_PRIORITY_MAX, test_list_subscribers_iq_cb, test, '(', "pubsub", ':', WOCKY_XMPP_NS_PUBSUB_OWNER, '(', "subscriptions", '@', "node", "princely_musings", ')', ')', NULL); node = wocky_pubsub_service_ensure_node (pubsub, "princely_musings"); g_assert (node != NULL); wocky_pubsub_node_list_subscribers_async (node, NULL, test_list_subscribers_cb, test); test->outstanding += 2; test_wait_pending (test); g_object_unref (node); g_object_unref (pubsub); test_close_both_porters (test); teardown_test (test); } /* Test retrieving a list of entities affiliated to a node you own. See * XEP-0060 §8.9.1 Retrieve Affiliations List * */ static gboolean test_list_affiliates_iq_cb (WockyPorter *porter, WockyStanza *stanza, gpointer user_data) { test_data_t *test = (test_data_t *) user_data; WockyStanza *expected, *reply; expected = wocky_stanza_build (WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_GET, NULL, "pubsub.localhost", '(', "pubsub", ':', WOCKY_XMPP_NS_PUBSUB_OWNER, '(', "affiliations", '@', "node", "princely_musings", ')', ')', NULL); test_assert_stanzas_equal_no_id (stanza, expected); g_object_unref (expected); reply = wocky_stanza_build_iq_result (stanza, '(', "pubsub", ':', WOCKY_XMPP_NS_PUBSUB_OWNER, '(', "affiliations", '@', "node", "princely_musings", '(', "affiliation", '@', "jid", "hamlet@denmark.lit", '@', "affiliation", "owner", ')', '(', "affiliation", '@', "jid", "polonius@denmark.lit", '@', "affiliation", "outcast", ')', ')', ')', NULL); wocky_porter_send (porter, reply); g_object_unref (reply); test->outstanding--; g_main_loop_quit (test->loop); return TRUE; } static void test_list_affiliates_cb (GObject *source, GAsyncResult *res, gpointer user_data) { test_data_t *test = (test_data_t *) user_data; GList *affiliates; WockyPubsubAffiliation *aff; g_assert (wocky_pubsub_node_list_affiliates_finish ( WOCKY_PUBSUB_NODE (source), res, &affiliates, NULL)); g_assert_cmpuint (2, ==, g_list_length (affiliates)); aff = affiliates->data; g_assert_cmpstr ("princely_musings", ==, wocky_pubsub_node_get_name (aff->node)); g_assert_cmpstr ("hamlet@denmark.lit", ==, aff->jid); g_assert_cmpuint (WOCKY_PUBSUB_AFFILIATION_OWNER, ==, aff->state); aff = affiliates->next->data; g_assert_cmpstr ("princely_musings", ==, wocky_pubsub_node_get_name (aff->node)); g_assert_cmpstr ("polonius@denmark.lit", ==, aff->jid); g_assert_cmpuint (WOCKY_PUBSUB_AFFILIATION_OUTCAST, ==, aff->state); wocky_pubsub_affiliation_list_free (affiliates); test->outstanding--; g_main_loop_quit (test->loop); } static void test_list_affiliates (void) { test_data_t *test = setup_test (); WockyPubsubService *pubsub; WockyPubsubNode *node; test_open_both_connections (test); wocky_porter_start (test->sched_out); wocky_session_start (test->session_in); pubsub = wocky_pubsub_service_new (test->session_in, "pubsub.localhost"); wocky_porter_register_handler_from_anyone (test->sched_out, WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_GET, WOCKY_PORTER_HANDLER_PRIORITY_MAX, test_list_affiliates_iq_cb, test, '(', "pubsub", ':', WOCKY_XMPP_NS_PUBSUB_OWNER, '(', "affiliations", '@', "node", "princely_musings", ')', ')', NULL); node = wocky_pubsub_service_ensure_node (pubsub, "princely_musings"); g_assert (node != NULL); wocky_pubsub_node_list_affiliates_async (node, NULL, test_list_affiliates_cb, test); test->outstanding += 2; test_wait_pending (test); g_object_unref (node); g_object_unref (pubsub); test_close_both_porters (test); teardown_test (test); } /* Test modifying the entities affiliated to a node that you own. See §8.9.2 * Modify Affiliation. */ static gboolean test_modify_affiliates_iq_cb (WockyPorter *porter, WockyStanza *stanza, gpointer user_data) { test_data_t *test = (test_data_t *) user_data; WockyStanza *expected, *reply; expected = wocky_stanza_build (WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_SET, NULL, "pubsub.localhost", '(', "pubsub", ':', WOCKY_XMPP_NS_PUBSUB_OWNER, '(', "affiliations", '@', "node", "princely_musings", '(', "affiliation", '@', "jid", "hamlet@denmark.lit", '@', "affiliation", "none", ')', '(', "affiliation", '@', "jid", "polonius@denmark.lit", '@', "affiliation", "none", ')', '(', "affiliation", '@', "jid", "bard@shakespeare.lit", '@', "affiliation", "publisher", ')', ')', ')', NULL); test_assert_stanzas_equal_no_id (stanza, expected); g_object_unref (expected); reply = wocky_stanza_build_iq_result (stanza, NULL); wocky_porter_send (porter, reply); g_object_unref (reply); test->outstanding--; g_main_loop_quit (test->loop); return TRUE; } static void test_modify_affiliates_cb (GObject *source, GAsyncResult *res, gpointer user_data) { test_data_t *test = (test_data_t *) user_data; g_assert (wocky_pubsub_node_modify_affiliates_finish ( WOCKY_PUBSUB_NODE (source), res, NULL)); test->outstanding--; g_main_loop_quit (test->loop); } static void test_modify_affiliates (void) { test_data_t *test = setup_test (); WockyPubsubService *pubsub; WockyPubsubNode *node; GList *snakes = NULL; test_open_both_connections (test); wocky_porter_start (test->sched_out); wocky_session_start (test->session_in); pubsub = wocky_pubsub_service_new (test->session_in, "pubsub.localhost"); wocky_porter_register_handler_from_anyone (test->sched_out, WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_SET, WOCKY_PORTER_HANDLER_PRIORITY_MAX, test_modify_affiliates_iq_cb, test, '(', "pubsub", ':', WOCKY_XMPP_NS_PUBSUB_OWNER, '(', "affiliations", '@', "node", "princely_musings", ')', ')', NULL); node = wocky_pubsub_service_ensure_node (pubsub, "princely_musings"); g_assert (node != NULL); snakes = g_list_append (snakes, wocky_pubsub_affiliation_new (node, "hamlet@denmark.lit", WOCKY_PUBSUB_AFFILIATION_NONE)); snakes = g_list_append (snakes, wocky_pubsub_affiliation_new (node, "polonius@denmark.lit", WOCKY_PUBSUB_AFFILIATION_NONE)); snakes = g_list_append (snakes, wocky_pubsub_affiliation_new (node, "bard@shakespeare.lit", WOCKY_PUBSUB_AFFILIATION_PUBLISHER)); wocky_pubsub_node_modify_affiliates_async (node, snakes, NULL, test_modify_affiliates_cb, test); wocky_pubsub_affiliation_list_free (snakes); snakes = NULL; test->outstanding += 2; test_wait_pending (test); g_object_unref (node); g_object_unref (pubsub); test_close_both_porters (test); teardown_test (test); } /* Tests retrieving a node's current configuration. * * Since data forms are reasonably exhaustively tested elsewhere, this test * uses a very simple configuration form. */ static gboolean test_get_configuration_iq_cb (WockyPorter *porter, WockyStanza *stanza, gpointer user_data) { test_data_t *test = (test_data_t *) user_data; WockyStanza *expected, *reply; expected = wocky_stanza_build (WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_GET, NULL, "pubsub.localhost", '(', "pubsub", ':', WOCKY_XMPP_NS_PUBSUB_OWNER, '(', "configure", '@', "node", "princely_musings", ')', ')', NULL); test_assert_stanzas_equal_no_id (stanza, expected); g_object_unref (expected); reply = wocky_stanza_build_iq_result (stanza, '(', "pubsub", ':', WOCKY_XMPP_NS_PUBSUB_OWNER, '(', "configure", '(', "x", ':', WOCKY_XMPP_NS_DATA, '@', "type", "form", '(', "field", '@', "type", "hidden", '@', "var", "FORM_TYPE", '(', "value", '$', WOCKY_XMPP_NS_PUBSUB_NODE_CONFIG, ')', ')', '(', "field", '@', "var", "pubsub#title", '@', "type", "text-single", '(', "value", '$', "Hello thar", ')', ')', '(', "field", '@', "var", "pubsub#deliver_notifications", '@', "type", "boolean", '@', "label", "Deliver event notifications", '(', "value", '$', "1", ')', ')', ')', ')', ')', NULL); wocky_porter_send (porter, reply); g_object_unref (reply); test->outstanding--; g_main_loop_quit (test->loop); return TRUE; } static void test_get_configuration_cb (GObject *source, GAsyncResult *res, gpointer user_data) { test_data_t *test = (test_data_t *) user_data; WockyDataForm *form; GError *error = NULL; form = wocky_pubsub_node_get_configuration_finish ( WOCKY_PUBSUB_NODE (source), res, &error); g_assert_no_error (error); g_assert (form != NULL); /* Don't bother testing too much, it's tested elsewhere. */ g_assert_cmpuint (3, ==, g_hash_table_size (form->fields)); g_object_unref (form); test->outstanding--; g_main_loop_quit (test->loop); } static void test_get_configuration (void) { test_data_t *test = setup_test (); WockyPubsubService *pubsub; WockyPubsubNode *node; test_open_both_connections (test); wocky_porter_start (test->sched_out); wocky_session_start (test->session_in); pubsub = wocky_pubsub_service_new (test->session_in, "pubsub.localhost"); wocky_porter_register_handler_from_anyone (test->sched_out, WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_GET, WOCKY_PORTER_HANDLER_PRIORITY_MAX, test_get_configuration_iq_cb, test, '(', "pubsub", ':', WOCKY_XMPP_NS_PUBSUB_OWNER, '(', "configure", '@', "node", "princely_musings", ')', ')', NULL); node = wocky_pubsub_service_ensure_node (pubsub, "princely_musings"); g_assert (node != NULL); wocky_pubsub_node_get_configuration_async (node, NULL, test_get_configuration_cb, test); test->outstanding += 2; test_wait_pending (test); g_object_unref (node); g_object_unref (pubsub); test_close_both_porters (test); teardown_test (test); } /* Test that the 'event-received' signals are fired when we expect them to be. */ gboolean service_event_received; gboolean node_event_received; WockyPubsubNode *expected_node; static void service_event_received_cb (WockyPubsubService *service, WockyPubsubNode *node, WockyStanza *event_stanza, WockyNode *event_node, WockyNode *items_node, GList *items, test_data_t *test) { WockyNode *item; /* Check that we're not winding up with multiple nodes for the same thing. */ if (expected_node != NULL) g_assert (node == expected_node); g_assert_cmpstr ("event", ==, event_node->name); g_assert_cmpstr ("items", ==, items_node->name); g_assert_cmpuint (2, ==, g_list_length (items)); item = g_list_nth_data (items, 0); g_assert_cmpstr ("item", ==, item->name); g_assert_cmpstr ("1", ==, wocky_node_get_attribute (item, "id")); item = g_list_nth_data (items, 1); g_assert_cmpstr ("item", ==, item->name); g_assert_cmpstr ("snakes", ==, wocky_node_get_attribute (item, "id")); test->outstanding--; g_main_loop_quit (test->loop); service_event_received = TRUE; } static void node_event_received_cb (WockyPubsubNode *node, WockyStanza *event_stanza, WockyNode *event_node, WockyNode *items_node, GList *items, test_data_t *test) { WockyNode *item; g_assert_cmpstr ("event", ==, event_node->name); g_assert_cmpstr ("items", ==, items_node->name); g_assert_cmpuint (2, ==, g_list_length (items)); item = g_list_nth_data (items, 0); g_assert_cmpstr ("item", ==, item->name); g_assert_cmpstr ("1", ==, wocky_node_get_attribute (item, "id")); item = g_list_nth_data (items, 1); g_assert_cmpstr ("item", ==, item->name); g_assert_cmpstr ("snakes", ==, wocky_node_get_attribute (item, "id")); test->outstanding--; g_main_loop_quit (test->loop); node_event_received = TRUE; } static void send_pubsub_event (WockyPorter *porter, const gchar *service, const gchar *node) { WockyStanza *stanza; stanza = wocky_stanza_build (WOCKY_STANZA_TYPE_MESSAGE, WOCKY_STANZA_SUB_TYPE_NONE, service, NULL, '(', "event", ':', WOCKY_XMPP_NS_PUBSUB_EVENT, '(', "items", '@', "node", node, '(', "item", '@', "id", "1", '(', "payload", ')', ')', '(', "item", '@', "id", "snakes", '(', "payload", ')', ')', ')', ')', NULL); wocky_porter_send (porter, stanza); g_object_unref (stanza); } static void test_receive_event (void) { test_data_t *test = setup_test (); WockyPubsubService *pubsub; WockyPubsubNode *node; test_open_both_connections (test); wocky_porter_start (test->sched_out); wocky_porter_start (test->sched_in); pubsub = wocky_pubsub_service_new (test->session_out, "pubsub.localhost"); node = wocky_pubsub_service_ensure_node (pubsub, "lol"); g_signal_connect (pubsub, "event-received", (GCallback) service_event_received_cb, test); g_signal_connect (node, "event-received", (GCallback) node_event_received_cb, test); /* send event from the right service for that node */ node_event_received = FALSE; service_event_received = FALSE; expected_node = node; send_pubsub_event (test->sched_in, "pubsub.localhost", "lol"); test->outstanding += 2; test_wait_pending (test); g_assert (node_event_received); g_assert (service_event_received); node_event_received = FALSE; service_event_received = FALSE; /* send event from the right service on a different node */ expected_node = NULL; send_pubsub_event (test->sched_in, "pubsub.localhost", "whut"); test->outstanding += 1; test_wait_pending (test); g_assert (!node_event_received); g_assert (service_event_received); service_event_received = FALSE; /* send event from a different service, on a node with the same name */ send_pubsub_event (test->sched_in, "pubsub.elsewhere", "lol"); g_object_unref (node); g_object_unref (pubsub); /* send event from the right service and node, after we dropped our ref to * the node and service. nothing else should be keeping it hanging around, so * our signal handlers should have been disconnected. */ send_pubsub_event (test->sched_in, "pubsub.localhost", "lol"); test_close_both_porters (test); teardown_test (test); /* None of the subsequent events should have triggered event-received. */ g_assert (!node_event_received); g_assert (!service_event_received); } /* /pubsub-node/subscription-state-changed */ typedef struct { test_data_t *test; const gchar *expecting_service_ssc_received_for; gboolean expecting_node_ssc_received; WockyPubsubSubscriptionState expected_state; } TestSSCCtx; static void send_subscription_state_change (WockyPorter *porter, const gchar *service, const gchar *node, const gchar *state) { WockyStanza *stanza; stanza = wocky_stanza_build (WOCKY_STANZA_TYPE_MESSAGE, WOCKY_STANZA_SUB_TYPE_NONE, service, NULL, '(', "event", ':', WOCKY_XMPP_NS_PUBSUB_EVENT, '(', "subscription", '@', "node", node, '@', "jid", "mighty@pirate.lit", '@', "subscription", state, ')', ')', NULL); wocky_porter_send (porter, stanza); g_object_unref (stanza); } static void service_subscription_state_changed_cb ( WockyPubsubService *service, WockyPubsubNode *node, WockyStanza *stanza, WockyNode *event_node, WockyNode *subscription_node, WockyPubsubSubscription *subscription, TestSSCCtx *ctx) { g_assert (ctx->expecting_service_ssc_received_for != NULL); g_assert_cmpstr (wocky_pubsub_node_get_name (node), ==, ctx->expecting_service_ssc_received_for); g_assert_cmpstr (event_node->name, ==, "event"); g_assert_cmpstr (wocky_node_get_ns (event_node), ==, WOCKY_XMPP_NS_PUBSUB_EVENT); g_assert_cmpstr (subscription_node->name, ==, "subscription"); g_assert (subscription->node == node); g_assert_cmpstr (subscription->jid, ==, "mighty@pirate.lit"); g_assert_cmpuint (subscription->state, ==, ctx->expected_state); g_assert (subscription->subid == NULL); ctx->expecting_service_ssc_received_for = NULL; ctx->test->outstanding--; g_main_loop_quit (ctx->test->loop); } static void node_subscription_state_changed_cb ( WockyPubsubNode *node, WockyStanza *stanza, WockyNode *event_node, WockyNode *subscription_node, WockyPubsubSubscription *subscription, TestSSCCtx *ctx) { g_assert (ctx->expecting_node_ssc_received); ctx->expecting_node_ssc_received = FALSE; g_assert_cmpstr (wocky_pubsub_node_get_name (node), ==, "dairy-farmer"); g_assert_cmpstr (event_node->name, ==, "event"); g_assert_cmpstr (wocky_node_get_ns (event_node), ==, WOCKY_XMPP_NS_PUBSUB_EVENT); g_assert_cmpstr (subscription_node->name, ==, "subscription"); g_assert (subscription->node == node); g_assert_cmpstr (subscription->jid, ==, "mighty@pirate.lit"); g_assert_cmpuint (subscription->state, ==, ctx->expected_state); g_assert (subscription->subid == NULL); ctx->test->outstanding--; g_main_loop_quit (ctx->test->loop); } static void test_subscription_state_changed (void) { test_data_t *test = setup_test (); TestSSCCtx ctx = { test, FALSE, FALSE, WOCKY_PUBSUB_SUBSCRIPTION_NONE }; WockyPubsubService *pubsub; WockyPubsubNode *node; test_open_both_connections (test); wocky_porter_start (test->sched_out); wocky_porter_start (test->sched_in); pubsub = wocky_pubsub_service_new (test->session_out, "pubsub.localhost"); g_signal_connect (pubsub, "subscription-state-changed", (GCallback) service_subscription_state_changed_cb, &ctx); node = wocky_pubsub_service_ensure_node (pubsub, "dairy-farmer"); g_signal_connect (node, "subscription-state-changed", (GCallback) node_subscription_state_changed_cb, &ctx); /* Send a subscription change notification for a different node. */ send_subscription_state_change (test->sched_in, "pubsub.localhost", "cow", "pending"); ctx.expecting_service_ssc_received_for = "cow"; ctx.expecting_node_ssc_received = FALSE; ctx.expected_state = WOCKY_PUBSUB_SUBSCRIPTION_PENDING; test->outstanding += 1; test_wait_pending (test); /* Send a subscription change notification for @node. */ send_subscription_state_change (test->sched_in, "pubsub.localhost", "dairy-farmer", "unconfigured"); ctx.expecting_service_ssc_received_for = "dairy-farmer"; ctx.expecting_node_ssc_received = TRUE; ctx.expected_state = WOCKY_PUBSUB_SUBSCRIPTION_UNCONFIGURED; test->outstanding += 2; test_wait_pending (test); g_assert (!ctx.expecting_node_ssc_received); test_close_both_porters (test); teardown_test (test); g_object_unref (node); g_object_unref (pubsub); } /* /pubsub-node/deleted */ typedef struct { test_data_t *test; const gchar *expecting_service_node_deleted_for; gboolean expecting_node_deleted; } TestDeletedCtx; static void send_deleted (WockyPorter *porter, const gchar *service, const gchar *node) { WockyStanza *stanza; stanza = wocky_stanza_build (WOCKY_STANZA_TYPE_MESSAGE, WOCKY_STANZA_SUB_TYPE_NONE, service, NULL, '(', "event", ':', WOCKY_XMPP_NS_PUBSUB_EVENT, '(', "delete", '@', "node", node, ')', ')', NULL); wocky_porter_send (porter, stanza); g_object_unref (stanza); } static void service_node_deleted_cb ( WockyPubsubService *service, WockyPubsubNode *node, WockyStanza *stanza, WockyNode *event_node, WockyNode *delete_node, TestDeletedCtx *ctx) { g_assert (ctx->expecting_service_node_deleted_for != NULL); g_assert_cmpstr (wocky_pubsub_node_get_name (node), ==, ctx->expecting_service_node_deleted_for); g_assert_cmpstr (event_node->name, ==, "event"); g_assert_cmpstr (wocky_node_get_ns (event_node), ==, WOCKY_XMPP_NS_PUBSUB_EVENT); g_assert_cmpstr (delete_node->name, ==, "delete"); ctx->expecting_service_node_deleted_for = NULL; ctx->test->outstanding--; g_main_loop_quit (ctx->test->loop); } static void node_deleted_cb ( WockyPubsubNode *node, WockyStanza *stanza, WockyNode *event_node, WockyNode *delete_node, TestDeletedCtx *ctx) { g_assert (ctx->expecting_node_deleted); ctx->expecting_node_deleted = FALSE; g_assert_cmpstr (wocky_pubsub_node_get_name (node), ==, "dairy-farmer"); g_assert_cmpstr (event_node->name, ==, "event"); g_assert_cmpstr (wocky_node_get_ns (event_node), ==, WOCKY_XMPP_NS_PUBSUB_EVENT); g_assert_cmpstr (delete_node->name, ==, "delete"); ctx->test->outstanding--; g_main_loop_quit (ctx->test->loop); } static void test_deleted (void) { test_data_t *test = setup_test (); TestDeletedCtx ctx = { test, FALSE, FALSE }; WockyPubsubService *pubsub; WockyPubsubNode *node; test_open_both_connections (test); wocky_porter_start (test->sched_out); wocky_porter_start (test->sched_in); pubsub = wocky_pubsub_service_new (test->session_out, "pubsub.localhost"); g_signal_connect (pubsub, "node-deleted", (GCallback) service_node_deleted_cb, &ctx); node = wocky_pubsub_service_ensure_node (pubsub, "dairy-farmer"); g_signal_connect (node, "deleted", (GCallback) node_deleted_cb, &ctx); /* Send a deletion notification for a different node. */ send_deleted (test->sched_in, "pubsub.localhost", "cow"); ctx.expecting_service_node_deleted_for = "cow"; ctx.expecting_node_deleted = FALSE; test->outstanding += 1; test_wait_pending (test); g_assert (ctx.expecting_service_node_deleted_for == NULL); /* Send a subscription change notification for @node. */ send_deleted (test->sched_in, "pubsub.localhost", "dairy-farmer"); ctx.expecting_service_node_deleted_for = "dairy-farmer"; ctx.expecting_node_deleted = TRUE; test->outstanding += 2; test_wait_pending (test); g_assert (ctx.expecting_service_node_deleted_for == NULL); g_assert (!ctx.expecting_node_deleted); test_close_both_porters (test); teardown_test (test); g_object_unref (node); g_object_unref (pubsub); } int main (int argc, char **argv) { int result; test_init (argc, argv); g_test_add_func ("/pubsub-node/instantiation", test_instantiation); g_test_add_func ("/pubsub-node/make-publish-stanza", test_make_publish_stanza); g_test_add_func ("/pubsub-node/subscribe", test_subscribe); g_test_add_func ("/pubsub-node/unsubscribe", test_unsubscribe); g_test_add_func ("/pubsub-node/delete", test_delete); g_test_add_func ("/pubsub-node/list-subscribers", test_list_subscribers); g_test_add_func ("/pubsub-node/list-affiliates", test_list_affiliates); g_test_add_func ("/pubsub-node/modify-affiliates", test_modify_affiliates); g_test_add_func ("/pubsub-node/get-configuration", test_get_configuration); g_test_add_func ("/pubsub-node/receive-event", test_receive_event); g_test_add_func ("/pubsub-node/subscription-state-changed", test_subscription_state_changed); g_test_add_func ("/pubsub-node/deleted", test_deleted); result = g_test_run (); test_deinit (); return result; } telepathy-gabble-0.18.2/lib/ext/wocky/tests/wocky-pubsub-test-helpers.h0000644000175000017500000000106412200204546026075 0ustar00smcvsmcv00000000000000#ifndef WOCKY_PUBSUB_TEST_HELPERS_H #define WOCKY_PUBSUB_TEST_HELPERS_H #include #include typedef struct { const gchar *node; const gchar *jid; const gchar *subscription; WockyPubsubSubscriptionState state; const gchar *subid; } CannedSubscriptions; void test_pubsub_add_subscription_nodes ( WockyNode *subscriptions_node, CannedSubscriptions *subs, gboolean include_node); void test_pubsub_check_and_free_subscriptions ( GList *subscriptions, const CannedSubscriptions *expected_subs); #endif telepathy-gabble-0.18.2/lib/ext/wocky/tests/wocky-pubsub-test-helpers.c0000644000175000017500000000266312200204546026076 0ustar00smcvsmcv00000000000000#ifdef HAVE_CONFIG_H #include "config.h" #endif #include "wocky-pubsub-test-helpers.h" #include void test_pubsub_add_subscription_nodes ( WockyNode *subscriptions_node, CannedSubscriptions *subs, gboolean include_node) { CannedSubscriptions *l; for (l = subs; l != NULL && l->node != NULL; l++) { WockyNode *sub = wocky_node_add_child (subscriptions_node, "subscription"); if (include_node) wocky_node_set_attribute (sub, "node", l->node); wocky_node_set_attribute (sub, "jid", l->jid); wocky_node_set_attribute (sub, "subscription", l->subscription); if (l->subid != NULL) wocky_node_set_attribute (sub, "subid", l->subid); } } void test_pubsub_check_and_free_subscriptions ( GList *subscriptions, const CannedSubscriptions *expected_subs) { GList *l; guint i = 0; for (l = subscriptions; l != NULL; l = l->next, i++) { WockyPubsubSubscription *sub = l->data; g_assert (expected_subs[i].jid != NULL); g_assert_cmpstr (expected_subs[i].jid, ==, sub->jid); g_assert_cmpstr (expected_subs[i].node, ==, wocky_pubsub_node_get_name (sub->node)); g_assert_cmpuint (expected_subs[i].state, ==, sub->state); g_assert_cmpstr (expected_subs[i].subid, ==, sub->subid); } g_assert_cmpstr (expected_subs[i].jid, ==, NULL); wocky_pubsub_subscription_list_free (subscriptions); } telepathy-gabble-0.18.2/lib/ext/wocky/tests/wocky-porter-test.c0000644000175000017500000030662412213345474024467 0ustar00smcvsmcv00000000000000#ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include #include "wocky-test-stream.h" #include "wocky-test-helper.h" static void test_instantiation (void) { WockyPorter *porter; WockyXmppConnection *connection; WockyTestStream *stream;; stream = g_object_new (WOCKY_TYPE_TEST_STREAM, NULL); connection = wocky_xmpp_connection_new (stream->stream0); porter = wocky_c2s_porter_new (connection, "juliet@example.com/Balcony"); g_assert (porter != NULL); g_assert_cmpstr (wocky_porter_get_full_jid (porter), ==, "juliet@example.com/Balcony"); g_assert_cmpstr (wocky_porter_get_bare_jid (porter), ==, "juliet@example.com"); g_assert_cmpstr (wocky_porter_get_resource (porter), ==, "Balcony"); g_object_unref (porter); g_object_unref (connection); g_object_unref (stream); } /* send testing */ static void send_stanza_received_cb (GObject *source, GAsyncResult *res, gpointer user_data) { WockyXmppConnection *connection = WOCKY_XMPP_CONNECTION (source); WockyStanza *s; test_data_t *data = (test_data_t *) user_data; GError *error = NULL; WockyStanza *expected; s = wocky_xmpp_connection_recv_stanza_finish (connection, res, &error); g_assert (s != NULL); expected = g_queue_pop_head (data->expected_stanzas); g_assert (expected != NULL); test_assert_stanzas_equal (s, expected); if (g_queue_get_length (data->expected_stanzas) > 0) { /* We need to receive more stanzas */ wocky_xmpp_connection_recv_stanza_async ( WOCKY_XMPP_CONNECTION (source), NULL, send_stanza_received_cb, user_data); data->outstanding++; } g_object_unref (s); g_object_unref (expected); data->outstanding--; g_main_loop_quit (data->loop); } static void send_stanza_cb (GObject *source, GAsyncResult *res, gpointer user_data) { test_data_t *data = (test_data_t *) user_data; g_assert (wocky_porter_send_finish ( WOCKY_PORTER (source), res, NULL)); data->outstanding--; g_main_loop_quit (data->loop); } static void send_stanza_cancelled_cb (GObject *source, GAsyncResult *res, gpointer user_data) { test_data_t *data = (test_data_t *) user_data; GError *error = NULL; g_assert (!wocky_porter_send_finish ( WOCKY_PORTER (source), res, &error)); g_assert (error->domain == G_IO_ERROR); g_assert (error->code == G_IO_ERROR_CANCELLED); g_error_free (error); data->outstanding--; g_main_loop_quit (data->loop); } static void test_send (void) { test_data_t *test = setup_test (); WockyStanza *s; test_open_connection (test); /* Send a stanza */ s = wocky_stanza_build (WOCKY_STANZA_TYPE_MESSAGE, WOCKY_STANZA_SUB_TYPE_CHAT, "juliet@example.com", "romeo@example.net", NULL); wocky_porter_send_async (test->sched_in, s, NULL, send_stanza_cb, test); g_queue_push_tail (test->expected_stanzas, s); test->outstanding++; /* Send a stanza */ s = wocky_stanza_build (WOCKY_STANZA_TYPE_MESSAGE, WOCKY_STANZA_SUB_TYPE_CHAT, "juliet@example.com", "tybalt@example.net", NULL); wocky_porter_send_async (test->sched_in, s, NULL, send_stanza_cb, test); g_queue_push_tail (test->expected_stanzas, s); test->outstanding++; /* Send two stanzas and cancel them immediately */ s = wocky_stanza_build (WOCKY_STANZA_TYPE_MESSAGE, WOCKY_STANZA_SUB_TYPE_CHAT, "juliet@example.com", "peter@example.net", NULL); wocky_porter_send_async (test->sched_in, s, test->cancellable, send_stanza_cancelled_cb, test); g_object_unref (s); test->outstanding++; s = wocky_stanza_build (WOCKY_STANZA_TYPE_MESSAGE, WOCKY_STANZA_SUB_TYPE_CHAT, "juliet@example.com", "samson@example.net", NULL); wocky_porter_send_async (test->sched_in, s, test->cancellable, send_stanza_cancelled_cb, test); g_object_unref (s); test->outstanding++; /* the stanza are not added to expected_stanzas as it was cancelled */ g_cancellable_cancel (test->cancellable); /* ... and a second (using the simple send method) */ s = wocky_stanza_build (WOCKY_STANZA_TYPE_MESSAGE, WOCKY_STANZA_SUB_TYPE_CHAT, "juliet@example.com", "nurse@example.net", NULL); wocky_porter_send (test->sched_in, s); g_queue_push_tail (test->expected_stanzas, s); test->outstanding++; /* Send a last stanza using the full method so we are sure that all async * sending operation have been finished. This is important because * test_close_connection() will have to use this function to close the * connection. If there is still a pending sending operation, it will fail. */ s = wocky_stanza_build (WOCKY_STANZA_TYPE_MESSAGE, WOCKY_STANZA_SUB_TYPE_CHAT, "juliet@example.com", "tybalt@example.net", NULL); wocky_porter_send_async (test->sched_in, s, NULL, send_stanza_cb, test); g_queue_push_tail (test->expected_stanzas, s); test->outstanding++; wocky_xmpp_connection_recv_stanza_async (test->out, NULL, send_stanza_received_cb, test); test_wait_pending (test); test_close_connection (test); teardown_test (test); } /* receive testing */ static gboolean test_receive_stanza_received_cb (WockyPorter *porter, WockyStanza *stanza, gpointer user_data) { test_data_t *test = (test_data_t *) user_data; test_expected_stanza_received (test, stanza); return TRUE; } static void sched_close_cb (GObject *source, GAsyncResult *res, gpointer user_data) { test_data_t *test = (test_data_t *) user_data; g_assert (wocky_porter_close_finish ( WOCKY_PORTER (source), res, NULL)); test->outstanding--; g_main_loop_quit (test->loop); } static void close_sent_cb (GObject *source, GAsyncResult *res, gpointer user_data) { test_data_t *test = (test_data_t *) user_data; g_assert (wocky_xmpp_connection_send_close_finish ( WOCKY_XMPP_CONNECTION (source), res, NULL)); test->outstanding--; g_main_loop_quit (test->loop); } static void wait_close_cb (GObject *source, GAsyncResult *res, gpointer user_data) { test_data_t *test = (test_data_t *) user_data; WockyXmppConnection *connection = WOCKY_XMPP_CONNECTION (source); WockyStanza *s; GError *error = NULL; s = wocky_xmpp_connection_recv_stanza_finish (connection, res, &error); g_assert (s == NULL); /* connection has been disconnected */ g_assert_error (error, WOCKY_XMPP_CONNECTION_ERROR, WOCKY_XMPP_CONNECTION_ERROR_CLOSED); g_error_free (error); /* close on our side */ wocky_xmpp_connection_send_close_async (connection, NULL, close_sent_cb, test); /* Don't decrement test->outstanding as we are waiting for another * callback */ g_main_loop_quit (test->loop); } static void test_receive (void) { test_data_t *test = setup_test (); WockyStanza *s; test_open_both_connections (test); /* Send a stanza */ s = wocky_stanza_build (WOCKY_STANZA_TYPE_MESSAGE, WOCKY_STANZA_SUB_TYPE_CHAT, "juliet@example.com", "romeo@example.net", NULL); wocky_porter_send_async (test->sched_in, s, NULL, send_stanza_cb, test); g_queue_push_tail (test->expected_stanzas, s); /* We are waiting for the stanza to be sent and received on the other * side */ test->outstanding += 2; wocky_porter_register_handler_from_anyone (test->sched_out, WOCKY_STANZA_TYPE_MESSAGE, WOCKY_STANZA_SUB_TYPE_NONE, 0, test_receive_stanza_received_cb, test, NULL); wocky_porter_start (test->sched_out); test_wait_pending (test); test_close_porter (test); teardown_test (test); } /* filter testing */ static gboolean test_filter_iq_received_cb (WockyPorter *porter, WockyStanza *stanza, gpointer user_data) { test_data_t *test = (test_data_t *) user_data; test_expected_stanza_received (test, stanza); return TRUE; } static gboolean test_filter_presence_received_cb (WockyPorter *porter, WockyStanza *stanza, gpointer user_data) { /* We didn't send any presence stanza so this callback shouldn't be * called */ g_assert_not_reached (); return TRUE; } static void test_filter (void) { test_data_t *test = setup_test (); WockyStanza *msg, *iq; test_open_both_connections (test); /* register an IQ filter */ wocky_porter_register_handler_from_anyone (test->sched_out, WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_NONE, 0, test_filter_iq_received_cb, test, NULL); /* register a presence filter */ wocky_porter_register_handler_from_anyone (test->sched_out, WOCKY_STANZA_TYPE_PRESENCE, WOCKY_STANZA_SUB_TYPE_NONE, 0, test_filter_presence_received_cb, test, NULL); wocky_porter_start (test->sched_out); /* Send a message */ msg = wocky_stanza_build (WOCKY_STANZA_TYPE_MESSAGE, WOCKY_STANZA_SUB_TYPE_CHAT, "juliet@example.com", "romeo@example.net", NULL); wocky_porter_send (test->sched_in, msg); /* We don't expect this stanza as we didn't register any message filter */ /* Send an IQ */ iq = wocky_stanza_build (WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_GET, "juliet@example.com", "romeo@example.net", NULL); wocky_porter_send (test->sched_in, iq); /* We expect to receive this stanza */ g_queue_push_tail (test->expected_stanzas, iq); test->outstanding++; test_wait_pending (test); g_object_unref (msg); test_close_porter (test); teardown_test (test); } /* test if the send queue is flushed before closing the connection */ static void test_close_stanza_received_cb (GObject *source, GAsyncResult *res, gpointer user_data) { WockyXmppConnection *connection = WOCKY_XMPP_CONNECTION (source); WockyStanza *s; test_data_t *test = (test_data_t *) user_data; GError *error = NULL; s = wocky_xmpp_connection_recv_stanza_finish (connection, res, &error); if (g_queue_get_length (test->expected_stanzas) > 0) { WockyStanza *expected; g_assert (s != NULL); expected = g_queue_pop_head (test->expected_stanzas); g_assert (expected != NULL); test_assert_stanzas_equal (s, expected); wocky_xmpp_connection_recv_stanza_async (connection, NULL, test_close_stanza_received_cb, user_data); test->outstanding++; g_object_unref (s); g_object_unref (expected); } else { g_assert_error (error, WOCKY_XMPP_CONNECTION_ERROR, WOCKY_XMPP_CONNECTION_ERROR_CLOSED); g_error_free (error); /* close on our side */ wocky_xmpp_connection_send_close_async (connection, NULL, close_sent_cb, test); } test->outstanding--; g_main_loop_quit (test->loop); } static void test_close_sched_close_cb (GObject *source, GAsyncResult *res, gpointer user_data) { test_data_t *test = (test_data_t *) user_data; g_assert (wocky_porter_close_finish ( WOCKY_PORTER (source), res, NULL)); test->outstanding--; g_main_loop_quit (test->loop); } static void closing_cb (WockyPorter *porter, test_data_t *test) { test->outstanding--; g_main_loop_quit (test->loop); } static void test_close_flush (void) { test_data_t *test = setup_test (); WockyStanza *s; test_open_both_connections (test); wocky_porter_start (test->sched_in); s = wocky_stanza_build (WOCKY_STANZA_TYPE_MESSAGE, WOCKY_STANZA_SUB_TYPE_CHAT, "juliet@example.com", "romeo@example.net", NULL); wocky_porter_send (test->sched_in, s); g_queue_push_tail (test->expected_stanzas, s); test->outstanding++; wocky_xmpp_connection_recv_stanza_async (test->out, NULL, test_close_stanza_received_cb, test); g_signal_connect (test->sched_in, "closing", G_CALLBACK (closing_cb), test); wocky_porter_close_async (test->sched_in, NULL, test_close_sched_close_cb, test); test->outstanding += 3; test_wait_pending (test); teardown_test (test); } /* test if the right error is raised when trying to close a not started * porter */ static void test_close_not_started_cb (GObject *source, GAsyncResult *res, gpointer user_data) { test_data_t *test = (test_data_t *) user_data; GError *error = NULL; g_assert (!wocky_porter_close_finish ( WOCKY_PORTER (source), res, &error)); g_assert_error (error, WOCKY_PORTER_ERROR, WOCKY_PORTER_ERROR_NOT_STARTED); g_error_free (error); test->outstanding--; g_main_loop_quit (test->loop); } static void test_close_not_started (void) { test_data_t *test = setup_test (); test_open_both_connections (test); wocky_porter_close_async (test->sched_in, NULL, test_close_not_started_cb, test); test->outstanding++; test_wait_pending (test); wocky_xmpp_connection_recv_stanza_async (test->out, NULL, wait_close_cb, test); wocky_porter_start (test->sched_in); wocky_porter_close_async (test->sched_in, NULL, sched_close_cb, test); test->outstanding += 2; test_wait_pending (test); teardown_test (test); } /* test if the right error is raised when trying to close the porter * twice */ static void test_close_twice_cb (GObject *source, GAsyncResult *res, gpointer user_data) { test_data_t *test = (test_data_t *) user_data; GError *error = NULL; g_assert (!wocky_porter_close_finish ( WOCKY_PORTER (source), res, &error)); g_assert_error (error, G_IO_ERROR, G_IO_ERROR_PENDING); g_error_free (error); test->outstanding--; g_main_loop_quit (test->loop); } static void test_close_twice_cb2 (GObject *source, GAsyncResult *res, gpointer user_data) { test_data_t *test = (test_data_t *) user_data; GError *error = NULL; g_assert (!wocky_porter_close_finish ( WOCKY_PORTER (source), res, &error)); g_assert_error (error, WOCKY_PORTER_ERROR, WOCKY_PORTER_ERROR_CLOSED); g_error_free (error); test->outstanding--; g_main_loop_quit (test->loop); } static void test_close_twice (void) { test_data_t *test = setup_test (); test_open_both_connections (test); wocky_xmpp_connection_recv_stanza_async (test->out, NULL, wait_close_cb, test); wocky_porter_start (test->sched_in); wocky_porter_close_async (test->sched_in, NULL, sched_close_cb, test); wocky_porter_close_async (test->sched_in, NULL, test_close_twice_cb, test); test->outstanding += 3; test_wait_pending (test); /* Retry now that the porter has been closed */ wocky_porter_close_async (test->sched_in, NULL, test_close_twice_cb2, test); test->outstanding++; test_wait_pending (test); teardown_test (test); } /* Test if the remote-closed signal is emitted when the other side closes his * XMPP connection */ static void remote_closed_cb (WockyPorter *porter, test_data_t *test) { test->outstanding--; g_main_loop_quit (test->loop); } static void test_remote_close_in_close_send_cb (GObject *source, GAsyncResult *res, gpointer user_data) { test_data_t *data = (test_data_t *) user_data; g_assert (wocky_xmpp_connection_send_close_finish ( WOCKY_XMPP_CONNECTION (source), res, NULL)); data->outstanding--; g_main_loop_quit (data->loop); } static void test_remote_close (void) { test_data_t *test = setup_test (); test_open_both_connections (test); wocky_porter_start (test->sched_out); g_signal_connect (test->sched_out, "remote-closed", G_CALLBACK (remote_closed_cb), test); test->outstanding++; wocky_xmpp_connection_send_close_async ( WOCKY_XMPP_CONNECTION (test->in), NULL, test_remote_close_in_close_send_cb, test); test->outstanding++; test_wait_pending (test); wocky_porter_close_async (test->sched_out, NULL, sched_close_cb, test); test->outstanding++; test_wait_pending (test); teardown_test (test); } /* Test cancelling a close operation */ static void sched_close_cancelled_cb (GObject *source, GAsyncResult *res, gpointer user_data) { test_data_t *test = (test_data_t *) user_data; GError *error = NULL; g_assert (!wocky_porter_close_finish ( WOCKY_PORTER (source), res, &error)); g_assert_error (error, G_IO_ERROR, G_IO_ERROR_CANCELLED); g_error_free (error); test->outstanding--; g_main_loop_quit (test->loop); } static void test_close_cancel_force_closed_cb (GObject *source, GAsyncResult *res, gpointer user_data) { test_data_t *test = (test_data_t *) user_data; GError *error = NULL; wocky_porter_force_close_finish ( WOCKY_PORTER (source), res, &error); g_assert_no_error (error); test->outstanding--; g_main_loop_quit (test->loop); } static void test_close_cancel (void) { test_data_t *test = setup_test (); wocky_test_stream_set_write_mode (test->stream->stream0_output, WOCKY_TEST_STREAM_WRITE_COMPLETE); test_open_both_connections (test); wocky_porter_start (test->sched_out); wocky_xmpp_connection_recv_stanza_async (test->in, NULL, wait_close_cb, test); wocky_porter_close_async (test->sched_out, test->cancellable, sched_close_cancelled_cb, test); g_cancellable_cancel (test->cancellable); test->outstanding += 2; test_wait_pending (test); wocky_porter_force_close_async (test->sched_out, NULL, test_close_cancel_force_closed_cb, test); test->outstanding++; test_wait_pending (test); teardown_test (test); } /* Test if the remote-error signal is fired when porter got a read error */ static void remote_error_cb (WockyPorter *porter, GQuark domain, guint code, const gchar *message, test_data_t *test) { GError *err = g_error_new_literal (domain, code, message); g_assert_error (err, G_IO_ERROR, G_IO_ERROR_FAILED); g_error_free (err); test->outstanding--; g_main_loop_quit (test->loop); } static void test_reading_error (void) { test_data_t *test = setup_test (); test_open_both_connections (test); g_signal_connect (test->sched_out, "remote-error", G_CALLBACK (remote_error_cb), test); test->outstanding++; wocky_test_input_stream_set_read_error (test->stream->stream1_input); wocky_porter_start (test->sched_out); test_wait_pending (test); test_close_porter (test); teardown_test (test); } /* Test if the right error is raised when trying to send a stanza through a * closed porter */ static void test_send_closing_cb (GObject *source, GAsyncResult *res, gpointer user_data) { test_data_t *data = (test_data_t *) user_data; GError *error = NULL; g_assert (!wocky_porter_send_finish ( WOCKY_PORTER (source), res, &error)); g_assert_error (error, WOCKY_PORTER_ERROR, WOCKY_PORTER_ERROR_CLOSING); g_error_free (error); data->outstanding--; g_main_loop_quit (data->loop); } static void test_send_closed_cb (GObject *source, GAsyncResult *res, gpointer user_data) { test_data_t *data = (test_data_t *) user_data; GError *error = NULL; g_assert (!wocky_porter_send_finish ( WOCKY_PORTER (source), res, &error)); g_assert_error (error, WOCKY_XMPP_CONNECTION_ERROR, WOCKY_XMPP_CONNECTION_ERROR_IS_CLOSED); g_error_free (error); data->outstanding--; g_main_loop_quit (data->loop); } static void test_send_closed (void) { test_data_t *test = setup_test (); WockyStanza *s; test_open_both_connections (test); wocky_porter_start (test->sched_in); wocky_xmpp_connection_recv_stanza_async (test->out, NULL, wait_close_cb, test); wocky_porter_close_async (test->sched_in, NULL, sched_close_cb, test); s = wocky_stanza_build (WOCKY_STANZA_TYPE_MESSAGE, WOCKY_STANZA_SUB_TYPE_CHAT, "juliet@example.com", "romeo@example.net", NULL); /* try to send a stanza while closing */ wocky_porter_send_async (test->sched_in, s, NULL, test_send_closing_cb, test); test->outstanding += 3; test_wait_pending (test); /* try to send a stanza after the closing */ wocky_porter_send_async (test->sched_in, s, NULL, test_send_closed_cb, test); g_object_unref (s); test->outstanding++; test_wait_pending (test); teardown_test (test); } /* test if the handler with the higher priority is called */ static void send_stanza (test_data_t *test, WockyStanza *stanza, gboolean expected) { wocky_porter_send (test->sched_in, stanza); if (expected) { g_queue_push_tail (test->expected_stanzas, stanza); test->outstanding++; } else { g_object_unref (stanza); } test_wait_pending (test); } static gboolean test_handler_priority_5 (WockyPorter *porter, WockyStanza *stanza, gpointer user_data) { /* This handler has the lowest priority and is not supposed to be called */ g_assert_not_reached (); return TRUE; } static gboolean test_handler_priority_10 (WockyPorter *porter, WockyStanza *stanza, gpointer user_data) { test_data_t *test = (test_data_t *) user_data; WockyStanzaSubType sub_type; test_expected_stanza_received (test, stanza); wocky_stanza_get_type_info (stanza, NULL, &sub_type); /* This handler is supposed to only handle the get stanza */ g_assert (sub_type == WOCKY_STANZA_SUB_TYPE_GET); return TRUE; } static gboolean test_handler_priority_15 (WockyPorter *porter, WockyStanza *stanza, gpointer user_data) { test_data_t *test = (test_data_t *) user_data; test_expected_stanza_received (test, stanza); return TRUE; } static void test_handler_priority (void) { test_data_t *test = setup_test (); WockyStanza *iq; test_open_both_connections (test); /* register an IQ handler with a priority of 10 */ wocky_porter_register_handler_from_anyone (test->sched_out, WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_NONE, 10, test_handler_priority_10, test, NULL); /* register an IQ handler with a priority of 5 */ wocky_porter_register_handler_from_anyone (test->sched_out, WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_NONE, 5, test_handler_priority_5, test, NULL); wocky_porter_start (test->sched_out); /* Send a 'get' IQ */ iq = wocky_stanza_build (WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_GET, "juliet@example.com", "romeo@example.net", NULL); send_stanza (test, iq, TRUE); /* register an IQ handler with a priority of 15 */ wocky_porter_register_handler_from_anyone (test->sched_out, WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_NONE, 15, test_handler_priority_15, test, NULL); /* Send a 'set' IQ */ iq = wocky_stanza_build (WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_SET, "juliet@example.com", "romeo@example.net", NULL); send_stanza (test, iq, TRUE); test_close_porter (test); teardown_test (test); } /* Test unregistering a handler */ static gboolean test_unregister_handler_10 (WockyPorter *porter, WockyStanza *stanza, gpointer user_data) { /* this handler is unregistred so shouldn't called */ g_assert_not_reached (); return TRUE; } static gboolean test_unregister_handler_5 (WockyPorter *porter, WockyStanza *stanza, gpointer user_data) { test_data_t *test = (test_data_t *) user_data; test_expected_stanza_received (test, stanza); return TRUE; } static void test_unregister_handler (void) { test_data_t *test = setup_test (); WockyStanza *iq; guint id; test_open_both_connections (test); /* register an IQ handler with a priority of 10 */ id = wocky_porter_register_handler_from_anyone (test->sched_out, WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_NONE, 10, test_unregister_handler_10, test, NULL); /* register an IQ handler with a priority of 5 */ wocky_porter_register_handler_from_anyone (test->sched_out, WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_NONE, 5, test_unregister_handler_5, test, NULL); wocky_porter_start (test->sched_out); /* unregister the first handler */ wocky_porter_unregister_handler (test->sched_out, id); /* Send a 'get' IQ */ iq = wocky_stanza_build (WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_GET, "juliet@example.com", "romeo@example.net", NULL); send_stanza (test, iq, TRUE); test_close_porter (test); teardown_test (test); } /* test registering a handler using a bare JID as filter criteria */ static gboolean test_handler_bare_jid_cb (WockyPorter *porter, WockyStanza *stanza, gpointer user_data) { test_data_t *test = (test_data_t *) user_data; test_expected_stanza_received (test, stanza); return TRUE; } static void test_handler_bare_jid (void) { test_data_t *test = setup_test (); WockyStanza *iq; test_open_both_connections (test); /* register an IQ handler for all IQ from a bare jid */ wocky_porter_register_handler_from (test->sched_out, WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_NONE, "juliet@example.com", 0, test_handler_bare_jid_cb, test, NULL); wocky_porter_start (test->sched_out); /* Send a 'get' IQ from the bare jid */ iq = wocky_stanza_build (WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_GET, "juliet@example.com", "romeo@example.net", NULL); send_stanza (test, iq, TRUE); /* Send a 'get' IQ from another contact */ iq = wocky_stanza_build (WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_GET, "samson@example.com/House", "romeo@example.net", NULL); send_stanza (test, iq, FALSE); /* Send a 'get' IQ from the bare jid + resource */ iq = wocky_stanza_build (WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_GET, "juliet@example.com/Pub", "romeo@example.net", NULL); send_stanza (test, iq, TRUE); test_close_porter (test); teardown_test (test); } /* test registering a handler using a full JID as filter criteria */ static gboolean test_handler_full_jid_cb (WockyPorter *porter, WockyStanza *stanza, gpointer user_data) { test_data_t *test = (test_data_t *) user_data; test_expected_stanza_received (test, stanza); return TRUE; } static void test_handler_full_jid (void) { test_data_t *test = setup_test (); WockyStanza *iq; test_open_both_connections (test); /* register an IQ handler for all IQ from a bare jid */ wocky_porter_register_handler_from (test->sched_out, WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_NONE, "juliet@example.com/Pub", 0, test_handler_full_jid_cb, test, NULL); wocky_porter_start (test->sched_out); /* Send a 'get' IQ from the bare jid */ iq = wocky_stanza_build (WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_GET, "juliet@example.com", "romeo@example.net", NULL); send_stanza (test, iq, FALSE); /* Send a 'get' IQ from another contact */ iq = wocky_stanza_build (WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_GET, "samson@example.com/House", "romeo@example.net", NULL); send_stanza (test, iq, FALSE); /* Send a 'get' IQ from the bare jid + resource */ iq = wocky_stanza_build (WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_GET, "juliet@example.com/Pub", "romeo@example.net", NULL); send_stanza (test, iq, TRUE); test_close_porter (test); teardown_test (test); } /* test registering a handler using a stanza as filter criteria */ static gboolean test_handler_stanza_jingle_cb (WockyPorter *porter, WockyStanza *stanza, gpointer user_data) { test_data_t *test = (test_data_t *) user_data; const gchar *id; test_expected_stanza_received (test, stanza); id = wocky_node_get_attribute (wocky_stanza_get_top_node (stanza), "id"); g_assert (!wocky_strdiff (id, "3") || !wocky_strdiff (id, "4")); return TRUE; } static gboolean test_handler_stanza_terminate_cb (WockyPorter *porter, WockyStanza *stanza, gpointer user_data) { test_data_t *test = (test_data_t *) user_data; const gchar *id; test_expected_stanza_received (test, stanza); id = wocky_node_get_attribute (wocky_stanza_get_top_node (stanza), "id"); g_assert (!wocky_strdiff (id, "5")); return TRUE; } static void test_handler_stanza (void) { test_data_t *test = setup_test (); WockyStanza *iq; test_open_both_connections (test); /* register an IQ handler for all the jingle stanzas related to one jingle * session */ wocky_porter_register_handler_from (test->sched_out, WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_NONE, "juliet@example.com", 0, test_handler_stanza_jingle_cb, test, '(', "jingle", ':', "urn:xmpp:jingle:1", '@', "sid", "my_sid", ')', NULL); wocky_porter_start (test->sched_out); wocky_porter_start (test->sched_in); /* Send a not jingle IQ */ iq = wocky_stanza_build (WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_SET, "juliet@example.com", "romeo@example.net", '@', "id", "1", NULL); send_stanza (test, iq, FALSE); /* Send a jingle IQ but related to another session */ iq = wocky_stanza_build (WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_SET, "juliet@example.com", "romeo@example.net", '@', "id", "2", '(', "jingle", ':', "urn:xmpp:jingle:1", '@', "sid", "another_sid", ')', NULL); send_stanza (test, iq, FALSE); /* Send a jingle IQ with the right sid but from the wrong contact */ iq = wocky_stanza_build (WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_SET, "tybalt@example.com", "romeo@example.net", '@', "id", "2", '(', "jingle", ':', "urn:xmpp:jingle:1", '@', "sid", "my_sid", ')', NULL); send_stanza (test, iq, FALSE); /* Send a jingle IQ related to the right session */ iq = wocky_stanza_build (WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_SET, "juliet@example.com", "romeo@example.net", '@', "id", "3", '(', "jingle", ':', "urn:xmpp:jingle:1", '@', "sid", "my_sid", ')', NULL); send_stanza (test, iq, TRUE); /* register a new IQ handler,with higher priority, handling session-terminate * with a specific test message */ wocky_porter_register_handler_from (test->sched_out, WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_NONE, "juliet@example.com", 10, test_handler_stanza_terminate_cb, test, '(', "jingle", ':', "urn:xmpp:jingle:1", '@', "action", "session-terminate", '(', "reason", '(', "success", ')', '(', "test", '$', "Sorry, gotta go!", ')', ')', ')', NULL); /* Send a session-terminate with the wrong message */ iq = wocky_stanza_build (WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_SET, "juliet@example.com", "romeo@example.net", '@', "id", "4", '(', "jingle", ':', "urn:xmpp:jingle:1", '@', "sid", "my_sid", '@', "action", "session-terminate", '(', "reason", '(', "success", ')', '(', "test", '$', "Bye Bye", ')', ')', ')', NULL); send_stanza (test, iq, TRUE); /* Send a session-terminate with the right message */ iq = wocky_stanza_build (WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_SET, "juliet@example.com", "romeo@example.net", '@', "id", "5", '(', "jingle", ':', "urn:xmpp:jingle:1", '@', "sid", "my_sid", '@', "action", "session-terminate", '(', "reason", '(', "success", ')', '(', "test", '$', "Sorry, gotta go!", ')', ')', ')', NULL); send_stanza (test, iq, TRUE); test_close_both_porters (test); teardown_test (test); } /* Cancel the sending of a stanza after it has been received */ static gboolean test_cancel_sent_stanza_cb (WockyPorter *porter, WockyStanza *stanza, gpointer user_data) { test_data_t *test = (test_data_t *) user_data; test_expected_stanza_received (test, stanza); test_cancel_in_idle (test->cancellable); return TRUE; } static void test_cancel_sent_stanza_cancelled (GObject *source, GAsyncResult *res, gpointer user_data) { test_data_t *test = (test_data_t *) user_data; /* Stanza has already be sent to _finish success */ g_assert (wocky_porter_send_finish ( WOCKY_PORTER (source), res, NULL)); test->outstanding--; g_main_loop_quit (test->loop); } static void test_cancel_sent_stanza (void) { test_data_t *test = setup_test (); WockyStanza *stanza; test_open_both_connections (test); wocky_porter_start (test->sched_out); wocky_porter_start (test->sched_in); /* register a message handler */ wocky_porter_register_handler_from_anyone (test->sched_out, WOCKY_STANZA_TYPE_MESSAGE, WOCKY_STANZA_SUB_TYPE_NONE, 0, test_cancel_sent_stanza_cb, test, NULL); stanza = wocky_stanza_build (WOCKY_STANZA_TYPE_MESSAGE, WOCKY_STANZA_SUB_TYPE_NONE, "juliet@example.com", "romeo@example.net", NULL); wocky_porter_send_async (test->sched_in, stanza, test->cancellable, test_cancel_sent_stanza_cancelled, test); g_queue_push_tail (test->expected_stanzas, stanza); test->outstanding += 2; test_wait_pending (test); test_close_both_porters (test); teardown_test (test); } /* Test if the error is correctly propagated when a writing error occurs */ static void test_writing_error_cb (GObject *source, GAsyncResult *res, gpointer user_data) { test_data_t *test = (test_data_t *) user_data; GError *error = NULL; g_assert (!wocky_porter_send_finish ( WOCKY_PORTER (source), res, &error)); g_assert_error (error, G_IO_ERROR, G_IO_ERROR_FAILED); g_error_free (error); test->outstanding--; g_main_loop_quit (test->loop); } static void test_writing_error (void) { test_data_t *test = setup_test (); WockyStanza *s; test_open_connection (test); wocky_test_output_stream_set_write_error (test->stream->stream0_output); s = wocky_stanza_build (WOCKY_STANZA_TYPE_MESSAGE, WOCKY_STANZA_SUB_TYPE_CHAT, "juliet@example.com", "romeo@example.net", NULL); test->outstanding++; wocky_porter_send_async (test->sched_in, s, NULL, test_writing_error_cb, test); test_wait_pending (test); g_object_unref (s); teardown_test (test); } /* Test send with reply */ static void test_send_iq_sent_cb (GObject *source, GAsyncResult *res, gpointer user_data) { test_data_t *data = (test_data_t *) user_data; g_assert (wocky_porter_send_finish ( WOCKY_PORTER (source), res, NULL)); data->outstanding--; g_main_loop_quit (data->loop); } static gboolean test_send_iq_cb (WockyPorter *porter, WockyStanza *stanza, gpointer user_data) { test_data_t *test = (test_data_t *) user_data; WockyStanza *reply; const gchar *id; gboolean cancelled; WockyStanzaSubType sub_type; test_expected_stanza_received (test, stanza); id = wocky_node_get_attribute (wocky_stanza_get_top_node (stanza), "id"); wocky_stanza_get_type_info (stanza, NULL, &sub_type); /* Reply of the "set" IQ is not expected as we are going to cancel it */ cancelled = (sub_type == WOCKY_STANZA_SUB_TYPE_SET); if (cancelled) g_cancellable_cancel (test->cancellable); /* Send a spoofed reply; should be ignored */ reply = wocky_stanza_build (WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_RESULT, "oscar@example.net", "juliet@example.com", '@', "id", id, NULL); wocky_porter_send (porter, reply); g_object_unref (reply); /* Send a reply without 'id' attribute; should be ignored */ reply = wocky_stanza_build (WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_RESULT, "romeo@example.net", "juliet@example.com", NULL); wocky_porter_send (porter, reply); g_object_unref (reply); /* Send reply */ reply = wocky_stanza_build (WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_RESULT, "romeo@example.net", "juliet@example.com", '@', "id", id, NULL); wocky_porter_send_async (porter, reply, NULL, test_send_iq_sent_cb, test); if (!cancelled) g_queue_push_tail (test->expected_stanzas, reply); else g_object_unref (reply); test->outstanding++; return TRUE; } static gboolean test_send_iq_abnormal_cb (WockyPorter *porter, WockyStanza *stanza, gpointer user_data) { test_data_t *test = (test_data_t *) user_data; WockyStanza *reply; const gchar *id; WockyStanzaSubType sub_type; test_expected_stanza_received (test, stanza); id = wocky_node_get_attribute (wocky_stanza_get_top_node (stanza), "id"); wocky_stanza_get_type_info (stanza, NULL, &sub_type); /* Send a spoofed reply; should be ignored */ reply = wocky_stanza_build (WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_RESULT, "oscar@example.net", "juliet@example.com", '@', "id", id, NULL); wocky_porter_send (porter, reply); g_object_unref (reply); /* Send a reply without 'id' attribute; should be ignored */ reply = wocky_stanza_build (WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_RESULT, "rOmeO@examplE.neT", "juLiet@Example.cOm", NULL); wocky_porter_send (porter, reply); g_object_unref (reply); /* Send reply */ reply = wocky_stanza_build (WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_RESULT, "roMeo@eXampLe.net", "JulieT@ExamplE.com", '@', "id", id, NULL); wocky_porter_send_async (porter, reply, NULL, test_send_iq_sent_cb, test); g_queue_push_tail (test->expected_stanzas, reply); test->outstanding++; return TRUE; } static void test_send_iq_reply_cb (GObject *source, GAsyncResult *res, gpointer user_data) { test_data_t *test = (test_data_t *) user_data; WockyStanza *reply; reply = wocky_porter_send_iq_finish (WOCKY_PORTER (source), res, NULL); g_assert (reply != NULL); test_expected_stanza_received (test, reply); g_object_unref (reply); } static void test_send_iq_cancelled_cb (GObject *source, GAsyncResult *res, gpointer user_data) { test_data_t *test = (test_data_t *) user_data; WockyStanza *reply; GError *error = NULL; reply = wocky_porter_send_iq_finish (WOCKY_PORTER (source), res, &error); g_assert (reply == NULL); g_assert_error (error, G_IO_ERROR, G_IO_ERROR_CANCELLED); g_error_free (error); test->outstanding--; g_main_loop_quit (test->loop); } static void test_send_iq (void) { test_data_t *test = setup_test (); WockyStanza *iq; test_open_both_connections (test); wocky_porter_start (test->sched_out); wocky_porter_start (test->sched_in); /* register an IQ handler */ wocky_porter_register_handler_from_anyone (test->sched_out, WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_NONE, 0, test_send_iq_cb, test, NULL); /* Send an IQ query. We are going to cancel it after it has been received * but before we receive the reply so the callback won't be called.*/ iq = wocky_stanza_build (WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_SET, "juliet@example.com", "romeo@example.net", NULL); wocky_porter_send_iq_async (test->sched_in, iq, test->cancellable, test_send_iq_cancelled_cb, test); g_queue_push_tail (test->expected_stanzas, iq); test->outstanding += 2; test_wait_pending (test); /* Send an IQ query */ iq = wocky_stanza_build (WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_GET, "juliet@example.com", "romeo@example.net", '@', "id", "1", NULL); wocky_porter_send_iq_async (test->sched_in, iq, NULL, test_send_iq_reply_cb, test); g_queue_push_tail (test->expected_stanzas, iq); test->outstanding += 2; test_wait_pending (test); test_close_both_porters (test); teardown_test (test); } static gboolean test_acknowledge_iq_acknowledge_cb ( WockyPorter *porter, WockyStanza *stanza, gpointer user_data) { test_data_t *test = user_data; wocky_porter_acknowledge_iq (porter, stanza, '(', "sup-dawg", '@', "lions", "tigers", ')', NULL); test->outstanding--; g_main_loop_quit (test->loop); return TRUE; } static void test_iq_reply_no_id_cb ( GObject *source, GAsyncResult *result, gpointer user_data) { WockyPorter *porter = WOCKY_PORTER (source); test_data_t *test = user_data; WockyStanza *reply; WockyStanza *expected_reply; GError *error = NULL; reply = wocky_porter_send_iq_finish (porter, result, &error); g_assert_no_error (error); g_assert (reply != NULL); expected_reply = g_queue_pop_head (test->expected_stanzas); g_assert (expected_reply != NULL); /* If we got the reply dispatched to us, the ID was correct — this is tested * elsewhere. So we don't need to test it again here. */ test_assert_stanzas_equal_no_id (reply, expected_reply); g_object_unref (reply); g_object_unref (expected_reply); test->outstanding--; g_main_loop_quit (test->loop); } /* Tests wocky_porter_acknowledge_iq(). */ static void test_acknowledge_iq (void) { test_data_t *test = setup_test (); WockyStanza *iq, *expected_reply; test_open_both_connections (test); wocky_porter_start (test->sched_out); wocky_porter_start (test->sched_in); wocky_porter_register_handler_from_anyone (test->sched_out, WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_GET, 0, test_acknowledge_iq_acknowledge_cb, test, NULL); /* We re-construct expected_reply for every test because… * test_assert_stanzas_equal_no_id() modifies the stanzas it's comparing to * add an id='' to the one which doesn't have one. */ /* Send a legal IQ (with a single child element). */ iq = wocky_stanza_build (WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_GET, "juliet@example.com", "romeo@example.net", '(', "sup-dawg", ')', NULL); expected_reply = wocky_stanza_build ( WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_RESULT, "romeo@example.net", "juliet@example.com", '(', "sup-dawg", '@', "lions", "tigers", ')', NULL); wocky_porter_send_iq_async (test->sched_in, iq, NULL, test_iq_reply_no_id_cb, test); test->outstanding += 2; g_queue_push_tail (test->expected_stanzas, expected_reply); g_object_unref (iq); /* Send an illegal IQ with two child elements. We expect that * wocky_porter_acknowledge_iq() should cope. */ iq = wocky_stanza_build (WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_GET, "juliet@example.com", "romeo@example.net", '(', "sup-dawg", ')', '(', "i-heard-you-like-stanzas", ')', NULL); expected_reply = wocky_stanza_build ( WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_RESULT, "romeo@example.net", "juliet@example.com", '(', "sup-dawg", '@', "lions", "tigers", ')', NULL); wocky_porter_send_iq_async (test->sched_in, iq, NULL, test_iq_reply_no_id_cb, test); test->outstanding += 2; g_queue_push_tail (test->expected_stanzas, expected_reply); g_object_unref (iq); /* Send another illegal IQ, with no child element at all. Obviously in real * life it should be nacked, but wocky_porter_acknowledge_iq() should still * cope. */ iq = wocky_stanza_build (WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_GET, "juliet@example.com", "romeo@example.net", NULL); expected_reply = wocky_stanza_build ( WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_RESULT, "romeo@example.net", "juliet@example.com", '(', "sup-dawg", '@', "lions", "tigers", ')', NULL); wocky_porter_send_iq_async (test->sched_in, iq, NULL, test_iq_reply_no_id_cb, test); test->outstanding += 2; g_queue_push_tail (test->expected_stanzas, expected_reply); g_object_unref (iq); /* Finally, send an IQ that doesn't have an id='' attribute. This is really * illegal, but wocky_porter_acknowledge_iq() needs to deal because it * happens in practice. */ iq = wocky_stanza_build (WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_GET, "juliet@example.com", "romeo@example.net", NULL); wocky_porter_send (test->sched_in, iq); /* In this case, we only expect the recipient's callback to fire. There's no * way for it to send us an IQ back, so we don't need to wait for a reply * there. */ test->outstanding += 1; g_object_unref (iq); /* Off we go! */ test_wait_pending (test); test_close_both_porters (test); teardown_test (test); } static gboolean test_send_iq_error_nak_cb ( WockyPorter *porter, WockyStanza *stanza, gpointer user_data) { test_data_t *test = user_data; wocky_porter_send_iq_error (porter, stanza, WOCKY_XMPP_ERROR_BAD_REQUEST, "bye bye beautiful"); test->outstanding--; g_main_loop_quit (test->loop); return TRUE; } /* Tests wocky_porter_send_iq_error(). */ static void test_send_iq_error (void) { test_data_t *test = setup_test (); WockyStanza *iq, *expected_reply; test_open_both_connections (test); wocky_porter_start (test->sched_out); wocky_porter_start (test->sched_in); wocky_porter_register_handler_from_anyone (test->sched_out, WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_GET, 0, test_send_iq_error_nak_cb, test, NULL); /* Send a legal IQ (with a single child element). */ iq = wocky_stanza_build (WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_GET, "juliet@example.com", "romeo@example.net", '(', "sup-dawg", ')', NULL); expected_reply = wocky_stanza_build ( WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_ERROR, "romeo@example.net", "juliet@example.com", '(', "sup-dawg", ')', '(', "error", '@', "code", "400", '@', "type", "modify", '(', "bad-request", ':', WOCKY_XMPP_NS_STANZAS, ')', '(', "text", ':', WOCKY_XMPP_NS_STANZAS, '$', "bye bye beautiful", ')', ')', NULL); wocky_porter_send_iq_async (test->sched_in, iq, NULL, test_iq_reply_no_id_cb, test); test->outstanding += 2; g_queue_push_tail (test->expected_stanzas, expected_reply); g_object_unref (iq); /* Send an illegal IQ with two child elements. We expect that * wocky_porter_send_iq_error() should cope by just picking the first one. */ iq = wocky_stanza_build (WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_GET, "juliet@example.com", "romeo@example.net", '(', "sup-dawg", ')', '(', "i-heard-you-like-stanzas", ')', NULL); expected_reply = wocky_stanza_build ( WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_ERROR, "romeo@example.net", "juliet@example.com", '(', "sup-dawg", ')', '(', "error", '@', "code", "400", '@', "type", "modify", '(', "bad-request", ':', WOCKY_XMPP_NS_STANZAS, ')', '(', "text", ':', WOCKY_XMPP_NS_STANZAS, '$', "bye bye beautiful", ')', ')', NULL); wocky_porter_send_iq_async (test->sched_in, iq, NULL, test_iq_reply_no_id_cb, test); test->outstanding += 2; g_queue_push_tail (test->expected_stanzas, expected_reply); g_object_unref (iq); /* Send another illegal IQ, with no child element at all. * wocky_porter_send_iq_error() should not blow up. */ iq = wocky_stanza_build (WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_GET, "juliet@example.com", "romeo@example.net", NULL); expected_reply = wocky_stanza_build ( WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_ERROR, "romeo@example.net", "juliet@example.com", '(', "error", '@', "code", "400", '@', "type", "modify", '(', "bad-request", ':', WOCKY_XMPP_NS_STANZAS, ')', '(', "text", ':', WOCKY_XMPP_NS_STANZAS, '$', "bye bye beautiful", ')', ')', NULL); wocky_porter_send_iq_async (test->sched_in, iq, NULL, test_iq_reply_no_id_cb, test); test->outstanding += 2; g_queue_push_tail (test->expected_stanzas, expected_reply); g_object_unref (iq); /* Finally, send an IQ that doesn't have an id='' attribute. This is really * illegal, but wocky_porter_send_iq_error() needs to deal because it * happens in practice. */ iq = wocky_stanza_build (WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_GET, "juliet@example.com", "romeo@example.net", NULL); wocky_porter_send (test->sched_in, iq); /* In this case, we only expect the recipient's callback to fire. There's no * way for it to send us an IQ back, so we don't need to wait for a reply * there. */ test->outstanding += 1; g_object_unref (iq); test_wait_pending (test); test_close_both_porters (test); teardown_test (test); } typedef struct { test_data_t *test; GError error; } TestSendIqGErrorCtx; static gboolean test_send_iq_gerror_nak_cb ( WockyPorter *porter, WockyStanza *stanza, gpointer user_data) { TestSendIqGErrorCtx *ctx = user_data; wocky_porter_send_iq_gerror (porter, stanza, &ctx->error); ctx->test->outstanding--; g_main_loop_quit (ctx->test->loop); return TRUE; } /* Tests wocky_porter_send_iq_gerror(). */ static void test_send_iq_gerror (void) { test_data_t *test = setup_test (); TestSendIqGErrorCtx ctx = { test, }; WockyStanza *iq, *expected_reply; test_open_both_connections (test); wocky_porter_start (test->sched_out); wocky_porter_start (test->sched_in); wocky_porter_register_handler_from_anyone (test->sched_out, WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_GET, 0, test_send_iq_gerror_nak_cb, &ctx, NULL); iq = wocky_stanza_build (WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_GET, "juliet@example.com", "romeo@example.net", '(', "sup-dawg", ')', NULL); /* Test responding with a simple error */ ctx.error.domain = WOCKY_XMPP_ERROR; ctx.error.code = WOCKY_XMPP_ERROR_UNEXPECTED_REQUEST; ctx.error.message = "i'm twelve years old and what is this?"; expected_reply = wocky_stanza_build ( WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_ERROR, "romeo@example.net", "juliet@example.com", '(', "sup-dawg", ')', '(', "error", '@', "code", "400", '@', "type", "wait", '(', "unexpected-request", ':', WOCKY_XMPP_NS_STANZAS, ')', '(', "text", ':', WOCKY_XMPP_NS_STANZAS, '$', ctx.error.message, ')', ')', NULL); wocky_porter_send_iq_async (test->sched_in, iq, NULL, test_iq_reply_no_id_cb, test); test->outstanding += 2; g_queue_push_tail (test->expected_stanzas, expected_reply); test_wait_pending (test); /* Test responding with an application-specific error */ ctx.error.domain = WOCKY_JINGLE_ERROR; ctx.error.code = WOCKY_JINGLE_ERROR_OUT_OF_ORDER; ctx.error.message = "i'm twelve years old and what is this?"; expected_reply = wocky_stanza_build ( WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_ERROR, "romeo@example.net", "juliet@example.com", '(', "sup-dawg", ')', '(', "error", '@', "code", "400", '@', "type", "wait", '(', "unexpected-request", ':', WOCKY_XMPP_NS_STANZAS, ')', '(', "out-of-order", ':', WOCKY_XMPP_NS_JINGLE_ERRORS, ')', '(', "text", ':', WOCKY_XMPP_NS_STANZAS, '$', ctx.error.message, ')', ')', NULL); wocky_porter_send_iq_async (test->sched_in, iq, NULL, test_iq_reply_no_id_cb, test); test->outstanding += 2; g_queue_push_tail (test->expected_stanzas, expected_reply); test_wait_pending (test); g_object_unref (iq); test_close_both_porters (test); teardown_test (test); } static void test_send_iq_abnormal (void) { test_data_t *test = setup_test (); WockyStanza *iq; test_open_both_connections (test); wocky_porter_start (test->sched_out); wocky_porter_start (test->sched_in); /* register an IQ handler (to send both the good and spoofed reply) */ wocky_porter_register_handler_from_anyone (test->sched_out, WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_NONE, 0, test_send_iq_abnormal_cb, test, NULL); /* Send an IQ query */ iq = wocky_stanza_build (WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_GET, "julIet@exampLe.com", "RoMeO@eXample.net", '@', "id", "1", NULL); wocky_porter_send_iq_async (test->sched_in, iq, NULL, test_send_iq_reply_cb, test); g_queue_push_tail (test->expected_stanzas, iq); test->outstanding += 2; test_wait_pending (test); test_close_both_porters (test); teardown_test (test); } /* Test if the error is correctly propagated when a writing error occurs while * sending an IQ */ static void test_send_iq_error_cb (GObject *source, GAsyncResult *res, gpointer user_data) { test_data_t *test = (test_data_t *) user_data; GError *error = NULL; g_assert (!wocky_porter_send_iq_finish ( WOCKY_PORTER (source), res, &error)); g_assert_error (error, G_IO_ERROR, G_IO_ERROR_FAILED); g_error_free (error); test->outstanding--; g_main_loop_quit (test->loop); } static void test_error_while_sending_iq (void) { test_data_t *test = setup_test (); WockyStanza *iq; test_open_connection (test); wocky_test_output_stream_set_write_error (test->stream->stream0_output); iq = wocky_stanza_build (WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_GET, "juliet@example.com", "romeo@example.net", '@', "id", "one", NULL); test->outstanding++; wocky_porter_send_iq_async (test->sched_in, iq, NULL, test_send_iq_error_cb, test); test_wait_pending (test); g_object_unref (iq); teardown_test (test); } /* Test implementing a filter using handlers */ static gboolean test_handler_filter_get_filter (WockyPorter *porter, WockyStanza *stanza, gpointer user_data) { test_data_t *test = (test_data_t *) user_data; WockyStanzaSubType sub_type; gboolean result; wocky_stanza_get_type_info (stanza, NULL, &sub_type); if (sub_type == WOCKY_STANZA_SUB_TYPE_GET) { /* We filter 'get' IQ. Return TRUE to say that we handled this stanza so * the handling process will be stopped */ result = TRUE; } else { /* We don't handle this stanza so the other callback will be called */ g_queue_push_tail (test->expected_stanzas, stanza); g_object_ref (stanza); test->outstanding++; result = FALSE; } test_expected_stanza_received (test, stanza); return result; } static gboolean test_handler_filter_cb (WockyPorter *porter, WockyStanza *stanza, gpointer user_data) { test_data_t *test = (test_data_t *) user_data; test_expected_stanza_received (test, stanza); return TRUE; } static void test_handler_filter (void) { test_data_t *test = setup_test (); WockyStanza *iq; test_open_both_connections (test); /* register an IQ handler which will act as a filter */ wocky_porter_register_handler_from_anyone (test->sched_out, WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_NONE, 10, test_handler_filter_get_filter, test, NULL); /* register another handler with a smaller priority which will be called * after the filter */ wocky_porter_register_handler_from_anyone (test->sched_out, WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_NONE, 5, test_handler_filter_cb, test, NULL); wocky_porter_start (test->sched_out); /* Send a 'get' IQ that will be filtered */ iq = wocky_stanza_build (WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_GET, "juliet@example.com", "romeo@example.net", NULL); send_stanza (test, iq, TRUE); /* Send a 'set' IQ that won't be filtered */ iq = wocky_stanza_build (WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_SET, "juliet@example.com", "romeo@example.net", NULL); send_stanza (test, iq, TRUE); test_close_porter (test); teardown_test (test); } static void unhandled_iq_reply_cb ( GObject *source, GAsyncResult *result, gpointer user_data) { WockyPorter *porter = WOCKY_PORTER (source); test_data_t *test = user_data; GError *error = NULL; WockyStanza *reply = wocky_porter_send_iq_finish (porter, result, &error); gboolean is_error; WockyXmppErrorType type; GError *core = NULL; GError *specialized = NULL; WockyNode *specialized_node; g_assert_no_error (error); g_assert (reply != NULL); is_error = wocky_stanza_extract_errors (reply, &type, &core, &specialized, &specialized_node); /* The reply should have type='error'. */ g_assert (is_error); g_assert_cmpuint (type, ==, WOCKY_XMPP_ERROR_TYPE_CANCEL); g_assert_error (core, WOCKY_XMPP_ERROR, WOCKY_XMPP_ERROR_SERVICE_UNAVAILABLE); /* There should be no non-XMPP Core error condition. */ g_assert_no_error (specialized); g_assert (specialized_node == NULL); g_clear_error (&core); g_object_unref (reply); test->outstanding--; g_main_loop_quit (test->loop); } static void test_unhandled_iq (void) { test_data_t *test = setup_test (); WockyStanza *iq = wocky_stanza_build ( WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_GET, NULL, NULL, '(', "framed-photograph", ':', "http://kimjongillookingatthings.tumblr.com", ')', NULL); test_open_both_connections (test); wocky_porter_start (test->sched_out); wocky_porter_start (test->sched_in); wocky_porter_send_iq_async (test->sched_out, iq, NULL, unhandled_iq_reply_cb, test); test->outstanding++; test_wait_pending (test); g_object_unref (iq); test_close_both_porters (test); teardown_test (test); } static gboolean test_handler_filter_from_juliet_cb (WockyPorter *porter, WockyStanza *stanza, gpointer user_data) { test_data_t *test = (test_data_t *) user_data; const gchar *from; from = wocky_stanza_get_from (stanza); g_assert (!wocky_strdiff (from, "juliet@example.com")); test_expected_stanza_received (test, stanza); return TRUE; } static gboolean test_handler_filter_from_anyone_cb (WockyPorter *porter, WockyStanza *stanza, gpointer user_data) { test_data_t *test = (test_data_t *) user_data; test_expected_stanza_received (test, stanza); return TRUE; } static void test_handler_filter_from (void) { test_data_t *test = setup_test (); WockyStanza *iq; test_open_both_connections (test); /* Register a handler for IQs with from=juliet@example.com */ wocky_porter_register_handler_from (test->sched_out, WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_NONE, "juliet@example.com", 10, test_handler_filter_from_juliet_cb, test, NULL); /* Register another handler, at a lower priority, for IQs from anyone */ wocky_porter_register_handler_from_anyone (test->sched_out, WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_NONE, 5, test_handler_filter_from_anyone_cb, test, NULL); wocky_porter_start (test->sched_out); /* Send an IQ that will be filtered by from_juliet only */ iq = wocky_stanza_build (WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_GET, "juliet@example.com", "romeo@example.net", NULL); send_stanza (test, iq, TRUE); /* Send an IQ that will be filtered by from_null only */ iq = wocky_stanza_build (WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_GET, "romeo@example.com", "juliet@example.net", NULL); send_stanza (test, iq, TRUE); /* Send an IQ that will be filtered by from_null only */ iq = wocky_stanza_build (WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_GET, NULL, "romeo@example.net", NULL); send_stanza (test, iq, TRUE); test_close_porter (test); teardown_test (test); } /* test if the right error is raised when trying to send an invalid IQ */ static void test_send_invalid_iq_cb (GObject *source, GAsyncResult *res, gpointer user_data) { test_data_t *test = (test_data_t *) user_data; WockyStanza *reply; GError *error = NULL; reply = wocky_porter_send_iq_finish (WOCKY_PORTER (source), res, &error); g_assert (reply == NULL); g_assert_error (error, WOCKY_PORTER_ERROR, WOCKY_PORTER_ERROR_NOT_IQ); g_error_free (error); test->outstanding--; g_main_loop_quit (test->loop); } static void test_send_invalid_iq (void) { test_data_t *test = setup_test (); WockyStanza *iq; test_open_both_connections (test); wocky_porter_start (test->sched_out); /* Try to send a message as an IQ */ iq = wocky_stanza_build (WOCKY_STANZA_TYPE_MESSAGE, WOCKY_STANZA_SUB_TYPE_NONE, "juliet@example.com", "romeo@example.net", NULL); wocky_porter_send_iq_async (test->sched_in, iq, test->cancellable, test_send_invalid_iq_cb, test); g_object_unref (iq); test->outstanding++; /* Try to send an IQ reply */ iq = wocky_stanza_build (WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_RESULT, "juliet@example.com", "romeo@example.net", NULL); wocky_porter_send_iq_async (test->sched_in, iq, test->cancellable, test_send_invalid_iq_cb, test); g_object_unref (iq); test->outstanding++; test_wait_pending (test); test_close_porter (test); teardown_test (test); } /* Test sending IQ's to the server (no 'to' attribute). The JID we believe we * have matters, here. */ static gboolean test_send_iq_server_received_cb (WockyPorter *porter, WockyStanza *iq, gpointer user_data) { test_data_t *test = (test_data_t *) user_data; WockyStanza *reply; WockyNode *node; const gchar *id; const gchar *from; test_expected_stanza_received (test, iq); node = wocky_stanza_get_top_node (iq); id = wocky_node_get_attribute (node, "id"); if (wocky_node_get_child (node, "first") != NULL) /* No from attribute */ from = NULL; else if (wocky_node_get_child (node, "second") != NULL) /* bare JID */ from = "juliet@example.com"; else if (wocky_node_get_child (node, "third") != NULL) /* full JID */ from = "juliet@example.com/Balcony"; else g_assert_not_reached (); /* Send reply */ reply = wocky_stanza_build (WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_RESULT, from, "juliet@example.com/Balcony", '@', "id", id, NULL); wocky_porter_send_async (porter, reply, NULL, test_send_iq_sent_cb, test); g_queue_push_tail (test->expected_stanzas, reply); test->outstanding++; return TRUE; } static void test_send_iq_server (void) { /* In this test "in" is Juliet, and "out" is her server */ test_data_t *test = setup_test_with_jids ("juliet@example.com/Balcony", "example.com"); WockyStanza *iq; const gchar *node[] = { "first", "second", "third", NULL }; guint i; test_open_both_connections (test); wocky_porter_start (test->sched_out); wocky_porter_start (test->sched_in); /* register an IQ handler */ wocky_porter_register_handler_from_anyone (test->sched_out, WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_GET, WOCKY_PORTER_HANDLER_PRIORITY_NORMAL, test_send_iq_server_received_cb, test, NULL); /* From XMPP RFC: * "When a server generates a stanza from the server itself for delivery to * a connected client (e.g., in the context of data storage services * provided by the server on behalf of the client), the stanza MUST either * (1) not include a 'from' attribute or (2) include a 'from' attribute * whose value is the account's bare JID () or client's full * JID ()". * * Each reply will test one of these 3 options. */ for (i = 0; node[i] != NULL; i++) { iq = wocky_stanza_build (WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_GET, "juliet@example.com", NULL, '(', node[i], ')', NULL); wocky_porter_send_iq_async (test->sched_in, iq, test->cancellable, test_send_iq_reply_cb, test); g_queue_push_tail (test->expected_stanzas, iq); test->outstanding += 2; test_wait_pending (test); } /* The same, but sending to our own bare JID. For instance, when we query * disco#info on our own bare JID on Prosody 0.6.1, the reply has no 'from' * attribute. */ for (i = 0; node[i] != NULL; i++) { iq = wocky_stanza_build (WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_GET, "juliet@example.com", "JULIET@EXAMPLE.COM", '(', node[i], ')', NULL); wocky_porter_send_iq_async (test->sched_in, iq, test->cancellable, test_send_iq_reply_cb, test); g_queue_push_tail (test->expected_stanzas, iq); test->outstanding += 2; test_wait_pending (test); } test_close_both_porters (test); teardown_test (test); } /* Unref the porter in the async close callback */ static void test_unref_when_closed_cb (GObject *source, GAsyncResult *res, gpointer user_data) { test_data_t *test = (test_data_t *) user_data; g_assert (wocky_porter_close_finish ( WOCKY_PORTER (source), res, NULL)); /* Porter has been closed, unref it */ g_object_unref (test->session_in); test->session_in = NULL; test->outstanding--; g_main_loop_quit (test->loop); } static void test_unref_when_closed (void) { test_data_t *test = setup_test (); test_open_both_connections (test); wocky_porter_start (test->sched_in); wocky_xmpp_connection_recv_stanza_async (test->out, NULL, test_close_stanza_received_cb, test); wocky_porter_close_async (test->sched_in, NULL, test_unref_when_closed_cb, test); test->outstanding += 3; test_wait_pending (test); teardown_test (test); } /* Both sides try to close the connection at the same time */ static void test_close_simultanously_recv_stanza_cb (GObject *source, GAsyncResult *res, gpointer user_data) { test_data_t *test = (test_data_t *) user_data; WockyStanza *s; GError *error = NULL; s = wocky_xmpp_connection_recv_stanza_finish (WOCKY_XMPP_CONNECTION (source), res, &error); g_assert (s == NULL); g_assert_error (error, WOCKY_XMPP_CONNECTION_ERROR, WOCKY_XMPP_CONNECTION_ERROR_CLOSED); g_error_free (error); test->outstanding--; g_main_loop_quit (test->loop); } static void test_close_simultanously (void) { test_data_t *test = setup_test (); test_open_both_connections (test); wocky_porter_start (test->sched_in); /* Sent close from one side */ wocky_xmpp_connection_send_close_async (test->out, NULL, close_sent_cb, test); /* .. and from the other */ wocky_porter_close_async (test->sched_in, NULL, test_unref_when_closed_cb, test); /* Wait that the 'in' side received the close */ test->outstanding += 2; test_wait_pending (test); /* Now read the close on the 'out' side */ wocky_xmpp_connection_recv_stanza_async (test->out, NULL, test_close_simultanously_recv_stanza_cb, test); test->outstanding++; test_wait_pending (test); teardown_test (test); } /* We sent our close stanza but a reading error occurs (as a disconnection for * example) before the other side sends his close */ static void test_close_error_cb (GObject *source, GAsyncResult *res, gpointer user_data) { test_data_t *test = (test_data_t *) user_data; GError *error = NULL; g_assert (!wocky_porter_close_finish ( WOCKY_PORTER (source), res, &error)); g_assert_error (error, G_IO_ERROR, G_IO_ERROR_FAILED); g_error_free (error); test->outstanding--; g_main_loop_quit (test->loop); } static void test_close_error (void) { test_data_t *test = setup_test (); wocky_test_stream_set_write_mode (test->stream->stream0_output, WOCKY_TEST_STREAM_WRITE_COMPLETE); test_open_both_connections (test); wocky_porter_start (test->sched_in); /* Sent close */ wocky_porter_close_async (test->sched_in, NULL, test_close_error_cb, test); wocky_xmpp_connection_recv_stanza_async (test->out, NULL, test_close_simultanously_recv_stanza_cb, test); /* Wait that the 'out' side received the close */ test->outstanding += 1; test_wait_pending (test); /* Something goes wrong */ wocky_test_input_stream_set_read_error (test->stream->stream0_input); /* The close operation is completed with an error */ test->outstanding += 1; test_wait_pending (test); teardown_test (test); } /* Try to send an IQ using a closing porter and cancel it immediately */ static void test_cancel_iq_closing (void) { test_data_t *test = setup_test (); WockyStanza *iq; test_open_both_connections (test); wocky_porter_start (test->sched_in); /* Start to close the porter */ wocky_porter_close_async (test->sched_in, NULL, test_close_sched_close_cb, test); /* Try to send a stanza using the closing porter */ iq = wocky_stanza_build (WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_SET, "juliet@example.com", "romeo@example.net", NULL); wocky_porter_send_iq_async (test->sched_in, iq, test->cancellable, test_send_closing_cb, test); /* Cancel the sending */ g_cancellable_cancel (test->cancellable); test->outstanding += 1; test_wait_pending (test); /* Make the call to wocky_porter_close_async() finish... */ wocky_xmpp_connection_send_close_async (test->out, NULL, close_sent_cb, test); test->outstanding += 2; test_wait_pending (test); g_object_unref (iq); teardown_test (test); } /* test stream errors */ static void test_stream_error_force_close_cb (GObject *source, GAsyncResult *res, gpointer user_data) { test_data_t *test = (test_data_t *) user_data; GError *error = NULL; wocky_porter_force_close_finish ( WOCKY_PORTER (source), res, &error); g_assert_no_error (error); test->outstanding--; g_main_loop_quit (test->loop); } static void test_stream_error_cb (WockyPorter *porter, GQuark domain, guint code, const gchar *message, test_data_t *test) { GError *err = g_error_new_literal (domain, code, message); g_assert_error (err, WOCKY_XMPP_STREAM_ERROR, WOCKY_XMPP_STREAM_ERROR_CONFLICT); g_error_free (err); /* force closing of the porter */ wocky_porter_force_close_async (porter, NULL, test_stream_error_force_close_cb, test); } static void test_stream_error (void) { test_data_t *test = setup_test (); WockyStanza *error; test_open_both_connections (test); wocky_porter_start (test->sched_out); g_signal_connect (test->sched_out, "remote-error", G_CALLBACK (test_stream_error_cb), test); test->outstanding++; /* Try to send a stanza using the closing porter */ error = wocky_stanza_build (WOCKY_STANZA_TYPE_STREAM_ERROR, WOCKY_STANZA_SUB_TYPE_NONE, NULL, NULL, ':', WOCKY_XMPP_NS_STREAM, '(', "conflict", ':', WOCKY_XMPP_NS_STREAMS, ')', NULL); wocky_porter_send_async (test->sched_in, error, NULL, send_stanza_cb, test); test->outstanding++; test_wait_pending (test); g_object_unref (error); teardown_test (test); } /* test wocky_porter_close_force */ static void test_close_force_stanza_sent_cb (GObject *source, GAsyncResult *res, gpointer user_data) { test_data_t *data = (test_data_t *) user_data; GError *error = NULL; g_assert (!wocky_porter_send_finish ( WOCKY_PORTER (source), res, &error)); g_assert_error (error, WOCKY_PORTER_ERROR, WOCKY_PORTER_ERROR_FORCIBLY_CLOSED); data->outstanding--; g_error_free (error); g_main_loop_quit (data->loop); } static void test_close_force_closed_cb (GObject *source, GAsyncResult *res, gpointer user_data) { test_data_t *test = (test_data_t *) user_data; GError *error = NULL; g_assert (!wocky_porter_close_finish ( WOCKY_PORTER (source), res, &error)); g_assert_error (error, WOCKY_PORTER_ERROR, WOCKY_PORTER_ERROR_FORCIBLY_CLOSED); test->outstanding--; g_error_free (error); g_main_loop_quit (test->loop); } static void test_close_force_force_closed_cb (GObject *source, GAsyncResult *res, gpointer user_data) { test_data_t *test = (test_data_t *) user_data; GError *error = NULL; wocky_porter_force_close_finish ( WOCKY_PORTER (source), res, &error); g_assert_no_error (error); test->outstanding--; g_main_loop_quit (test->loop); } static void test_close_force_closing_cb (WockyPorter *porter, test_data_t *test) { static gboolean fired = FALSE; g_assert (!fired); fired = TRUE; test->outstanding--; g_main_loop_quit (test->loop); } static void test_close_force (void) { test_data_t *test = setup_test (); WockyStanza *s; wocky_test_stream_set_write_mode (test->stream->stream0_output, WOCKY_TEST_STREAM_WRITE_COMPLETE); test_open_both_connections (test); wocky_porter_start (test->sched_in); /* Try to send a stanza; it will never reach the other side */ s = wocky_stanza_build (WOCKY_STANZA_TYPE_MESSAGE, WOCKY_STANZA_SUB_TYPE_CHAT, "juliet@example.com", "romeo@example.net", NULL); g_signal_connect (test->sched_in, "closing", G_CALLBACK (test_close_force_closing_cb), test); wocky_porter_send_async (test->sched_in, s, NULL, test_close_force_stanza_sent_cb, test); /* Try to properly close the connection; we'll give up before it has been * done */ wocky_porter_close_async (test->sched_in, NULL, test_close_force_closed_cb, test); /* force closing */ wocky_porter_force_close_async (test->sched_in, NULL, test_close_force_force_closed_cb, test); test->outstanding += 4; test_wait_pending (test); g_object_unref (s); teardown_test (test); } /* call force_close after an error appeared on the connection */ static void test_close_force_after_error_error_cb (WockyPorter *porter, GQuark domain, guint code, const gchar *message, test_data_t *test) { GError *err = g_error_new_literal (domain, code, message); g_assert_error (err, WOCKY_XMPP_STREAM_ERROR, WOCKY_XMPP_STREAM_ERROR_CONFLICT); g_error_free (err); test->outstanding--; g_main_loop_quit (test->loop); } static void test_close_force_after_error (void) { test_data_t *test = setup_test (); WockyStanza *error; test_open_both_connections (test); wocky_porter_start (test->sched_out); g_signal_connect (test->sched_out, "remote-error", G_CALLBACK (test_close_force_after_error_error_cb), test); test->outstanding++; error = wocky_stanza_build (WOCKY_STANZA_TYPE_STREAM_ERROR, WOCKY_STANZA_SUB_TYPE_NONE, NULL, NULL, ':', WOCKY_XMPP_NS_STREAM, '(', "conflict", ':', WOCKY_XMPP_NS_STREAMS, ')', NULL); wocky_porter_send_async (test->sched_in, error, NULL, send_stanza_cb, test); test->outstanding++; test_wait_pending (test); /* Stream error has been handled, now force closing */ wocky_porter_force_close_async (test->sched_out, NULL, test_close_force_force_closed_cb, test); test->outstanding++; test_wait_pending (test); g_object_unref (error); teardown_test (test); } /* Test calling force_close after close has been called and the close stream * stanza has been sent */ static void test_close_force_after_close_sent_stanza_cb (GObject *source, GAsyncResult *res, gpointer user_data) { test_data_t *test = (test_data_t *) user_data; WockyXmppConnection *connection = WOCKY_XMPP_CONNECTION (source); WockyStanza *s; GError *error = NULL; s = wocky_xmpp_connection_recv_stanza_finish (connection, res, &error); g_assert (s == NULL); /* connection has been disconnected */ g_assert_error (error, WOCKY_XMPP_CONNECTION_ERROR, WOCKY_XMPP_CONNECTION_ERROR_CLOSED); g_error_free (error); test->outstanding--; g_main_loop_quit (test->loop); } static void test_close_force_after_close_sent (void) { test_data_t *test = setup_test (); test_open_both_connections (test); wocky_porter_start (test->sched_in); wocky_xmpp_connection_recv_stanza_async (test->out, NULL, test_close_force_after_close_sent_stanza_cb, test); /* Try to properly close the connection; we'll give up before it has been * done */ wocky_porter_close_async (test->sched_in, NULL, test_close_force_closed_cb, test); /* Wait for the close stanza */ test->outstanding++; test_wait_pending (test); /* force closing */ wocky_porter_force_close_async (test->sched_in, NULL, test_close_force_force_closed_cb, test); test->outstanding += 2; test_wait_pending (test); teardown_test (test); } /* The remote connection is closed while we are waiting for an IQ reply */ static void open_connections_and_send_one_iq (test_data_t *test, GAsyncReadyCallback send_iq_callback) { WockyStanza *iq; test_open_both_connections (test); wocky_porter_start (test->sched_out); wocky_porter_start (test->sched_in); /* register an IQ handler */ wocky_porter_register_handler_from_anyone (test->sched_out, WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_NONE, 0, test_receive_stanza_received_cb, test, NULL); /* Send an IQ query */ iq = wocky_stanza_build (WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_GET, "juliet@example.com", "romeo@example.net", '@', "id", "1", NULL); wocky_porter_send_iq_async (test->sched_in, iq, NULL, send_iq_callback, test); g_queue_push_tail (test->expected_stanzas, iq); /* wait that the IQ has been received */ test->outstanding += 1; test_wait_pending (test); } static void test_wait_iq_reply_close_reply_cb (GObject *source, GAsyncResult *res, gpointer user_data) { test_data_t *test = (test_data_t *) user_data; WockyStanza *reply; GError *error = NULL; reply = wocky_porter_send_iq_finish (WOCKY_PORTER (source), res, &error); g_assert (reply == NULL); g_assert_error (error, WOCKY_XMPP_CONNECTION_ERROR, WOCKY_XMPP_CONNECTION_ERROR_CLOSED); g_error_free (error); test->outstanding--; g_main_loop_quit (test->loop); } static void test_wait_iq_reply_close (void) { test_data_t *test = setup_test (); open_connections_and_send_one_iq (test, test_wait_iq_reply_close_reply_cb); /* the other side closes the connection (and so won't send the IQ reply) */ wocky_porter_close_async (test->sched_out, NULL, test_close_sched_close_cb, test); /* the send IQ operation is finished (with an error) */ test->outstanding += 1; test_wait_pending (test); wocky_porter_close_async (test->sched_in, NULL, test_close_sched_close_cb, test); /* the 2 close operations are completed */ test->outstanding += 2; test_wait_pending (test); teardown_test (test); } /* Send an IQ and then force the closing of the connection before we received * the reply */ static void test_wait_iq_reply_force_close_reply_cb (GObject *source, GAsyncResult *res, gpointer user_data) { test_data_t *test = (test_data_t *) user_data; WockyStanza *reply; GError *error = NULL; reply = wocky_porter_send_iq_finish (WOCKY_PORTER (source), res, &error); g_assert (reply == NULL); g_assert_error (error, WOCKY_PORTER_ERROR, WOCKY_PORTER_ERROR_FORCIBLY_CLOSED); g_error_free (error); test->outstanding--; g_main_loop_quit (test->loop); } static void test_wait_iq_reply_force_close (void) { test_data_t *test = setup_test (); open_connections_and_send_one_iq (test, test_wait_iq_reply_force_close_reply_cb); /* force closing of our connection */ wocky_porter_force_close_async (test->sched_in, NULL, test_close_force_force_closed_cb, test); /* the send IQ operation is finished (with an error) and the force close * operation is completed */ test->outstanding += 2; test_wait_pending (test); wocky_porter_force_close_async (test->sched_out, NULL, test_close_force_force_closed_cb, test); test->outstanding += 1; test_wait_pending (test); teardown_test (test); } /* this tries to catch the case where we receive a remote-error, which * * results in a wocky_porter_force_close_async in the connected signal * * handler but the internal stanza_received_cb _also_ attempts to force * * a shutdown: when correctly functioning, only one of these will result * * in a force close operation and the other will noop. * * Typical failure cases are the shutdown not happening at all or being * * attempted twice and failing because the porter can only support one * * force close attempt (both indicate broken logic in the porter) */ static void test_remote_error (void) { test_data_t *test = setup_test (); WockyStanza *error = wocky_stanza_build (WOCKY_STANZA_TYPE_STREAM_ERROR, WOCKY_STANZA_SUB_TYPE_NONE, NULL, NULL, ':', WOCKY_XMPP_NS_STREAM, '(', "conflict", ':', WOCKY_XMPP_NS_STREAMS, ')', NULL); test_open_both_connections (test); wocky_porter_start (test->sched_out); wocky_porter_start (test->sched_in); /* this callback will force_close_async the OUT porter, * * and decrement test->outstanding when it has done so */ g_signal_connect (test->sched_out, "remote-error", G_CALLBACK (test_stream_error_cb), test); /* this pumps a stream error through the IN (server) porter * * which triggers the remote-error signal from the OUT porter */ wocky_porter_send_async (test->sched_in, error, NULL, send_stanza_cb, test); test->outstanding++; test_wait_pending (test); test->outstanding++; /* this is for the signal connect above */ /* this closes the IN (server) porter so that the test doesn't fail (we * * don't really care about the IN porter for the purposes of this test) */ wocky_porter_force_close_async (test->sched_in, NULL, test_close_force_force_closed_cb, test); test->outstanding++; /* now wait for the IN porter to shutdown and the remote-error callback * * to shut down the OUT porter */ test_wait_pending (test); g_object_unref (error); teardown_test (test); } /* Herein lies a regression test for a bug where, if a stanza had been passed * by the porter to the XmppConnection but not actually sent when the porter * was unreffed, the subsequent callback from the XmppConnection would crash * us. */ static gboolean idle_main_loop_quit (gpointer user_data) { g_main_loop_quit (user_data); return FALSE; } static void send_and_disconnect (void) { test_data_t *test = setup_test (); WockyStanza *lions = wocky_stanza_build (WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_GET, NULL, NULL, '(', "dummy", ':', "xmpp:stanza", '(', "nothing-to-see-here", ')', ')', NULL); GMainLoop *loop = g_main_loop_ref (test->loop); /* We try to send any old stanza. */ wocky_porter_send_async (test->sched_in, lions, NULL, NULL, NULL); /* Without giving the mainloop a chance to spin to call any callbacks at all, * we tear everything down, including the porters. This will make sched_in * add an idle to dispatch the (non-existent) callback for the stanza sent * above, saying that sending it failed. */ teardown_test (test); /* Now we spin the main loop until all (higher-priority) idles have fired. * * The bug we're testing for occured because the porter didn't keep itself * alive for the duration of the wocky_xmpp_connection_send_stanza_async() * call. So, when it'd finished calling the callback above, it'd die, and * then the send_stanza() operations would finish, and the porter's callback * would try to use the newly-freed porter and choke. */ g_idle_add_full (G_PRIORITY_LOW, idle_main_loop_quit, loop, NULL); g_main_loop_run (loop); g_main_loop_unref (loop); g_object_unref (lions); } static gboolean got_stanza_for_example_com ( WockyPorter *porter, WockyStanza *stanza, gpointer user_data) { test_data_t *test = user_data; g_assert_cmpstr (wocky_stanza_get_from (stanza), ==, "example.com"); test->outstanding--; g_main_loop_quit (test->loop); return TRUE; } /* This is a regression test for a bug where registering a handler for a JID * with no node part was equivalent to registering a handler with from=NULL; * that is, we'd erroneously pass stanzas from *any* server to the handler * function even if it explicitly specified a JID which was just a domain, as * opposed to a JID with an '@' sign in it. */ static void handler_for_domain (void) { test_data_t *test = setup_test (); WockyStanza *irrelevant, *relevant; test_open_both_connections (test); wocky_porter_start (test->sched_out); wocky_porter_start (test->sched_in); wocky_porter_register_handler_from (test->sched_in, WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_GET, "example.com", WOCKY_PORTER_HANDLER_PRIORITY_NORMAL, got_stanza_for_example_com, test, NULL); /* Send a stanza from some other random jid (at example.com, for the sake of * argument). The porter should ignore this stanza. */ irrelevant = wocky_stanza_build (WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_GET, "lol@example.com", NULL, '(', "this-is-bullshit", ')', NULL); wocky_porter_send (test->sched_out, irrelevant); g_object_unref (irrelevant); relevant = wocky_stanza_build (WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_GET, "example.com", NULL, '(', "i-am-a-fan-of-cocaine", ')', NULL); wocky_porter_send (test->sched_out, relevant); g_object_unref (relevant); test->outstanding += 1; test_wait_pending (test); test_close_both_porters (test); teardown_test (test); } static gboolean got_stanza_from_anyone ( WockyPorter *porter, WockyStanza *stanza, gpointer user_data) { test_data_t *test = user_data; WockyNode *top = wocky_stanza_get_top_node (stanza); WockyNode *query = wocky_node_get_first_child (top); g_assert_cmpstr (query->name, ==, "anyone"); test->outstanding--; g_main_loop_quit (test->loop); return TRUE; } static gboolean got_stanza_from_ourself ( WockyPorter *porter, WockyStanza *stanza, gpointer user_data) { test_data_t *test = user_data; WockyNode *top = wocky_stanza_get_top_node (stanza); WockyNode *query = wocky_node_get_first_child (top); g_assert_cmpstr (query->name, ==, "ourself"); test->outstanding--; g_main_loop_quit (test->loop); return TRUE; } static gboolean got_stanza_from_server ( WockyPorter *porter, WockyStanza *stanza, gpointer user_data) { test_data_t *test = user_data; WockyNode *top = wocky_stanza_get_top_node (stanza); WockyNode *query = wocky_node_get_first_child (top); g_assert_cmpstr (query->name, ==, "server"); test->outstanding--; g_main_loop_quit (test->loop); return TRUE; } static void send_query_from ( test_data_t *test, const gchar *from, const gchar *query) { WockyStanza *s = wocky_stanza_build (WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_GET, from, NULL, '(', query, ')', NULL); wocky_porter_send (test->sched_out, s); g_object_unref (s); test->outstanding += 1; test_wait_pending (test); } static void handler_from_anyone (void) { test_data_t *test = setup_test_with_jids ("juliet@capulet.lit/Balcony", "capulet.lit"); test_open_both_connections (test); wocky_porter_start (test->sched_out); wocky_porter_start (test->sched_in); wocky_c2s_porter_register_handler_from_server ( WOCKY_C2S_PORTER (test->sched_in), WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_GET, WOCKY_PORTER_HANDLER_PRIORITY_NORMAL + 10, got_stanza_from_server, test, NULL); /* A catch-all IQ get handler. */ wocky_porter_register_handler_from_anyone (test->sched_in, WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_GET, WOCKY_PORTER_HANDLER_PRIORITY_NORMAL, got_stanza_from_anyone, test, NULL); /* And, for completeness, a handler for IQs sent by any incarnation * of ourself, at a lower priority to the handler for stanzas from the * server, but a higher priority to the catch-all handler. */ wocky_porter_register_handler_from (test->sched_in, WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_GET, "juliet@capulet.lit", WOCKY_PORTER_HANDLER_PRIORITY_NORMAL + 5, got_stanza_from_ourself, test, NULL); /* All of the handlers assert on the name of the first child node, and then * return TRUE to prevent the stanza being handed to a lower-priority * handler. */ /* A stanza from a contact on a completely different server should be picked * up only by the general handler. */ send_query_from (test, "romeo@montague.lit/Garden", "anyone"); /* A stanza from a contact on our server should be picked up only by the * general handler (irrespective of whether they have a resource). */ send_query_from (test, "tybalt@capulet.lit", "anyone"); send_query_from (test, "tybalt@capulet.lit/FIXME", "anyone"); /* A stanza from our server's domain should be matched by * got_stanza_from_server(). See fd.o#39057. */ send_query_from (test, "capulet.lit", "server"); /* On the other hand, a stanza with no sender should be picked up by * got_stanza_from_server(). */ send_query_from (test, NULL, "server"); /* Similarly, stanzas from our bare JID should be handed to * got_stanza_from_server(). Because that function returns TRUE, the stanza * should not be handed to got_stanza_from_ourself(). */ send_query_from (test, "juliet@capulet.lit", "server"); send_query_from (test, "jULIet@cAPUlet.lIT", "server"); /* Similarly, stanzas from our own full JID go to got_stanza_from_server. */ send_query_from (test, "juliet@capulet.lit/Balcony", "server"); send_query_from (test, "JUlIet@CAPulet.LIt/Balcony", "server"); /* But stanzas from our other resources should go to * got_stanza_from_ourself(). */ send_query_from (test, "juliet@capulet.lit/FIXME", "ourself"); /* Heh, heh, resources are case-sensitive */ send_query_from (test, "juliet@capulet.lit/balcony", "ourself"); /* Meanwhile, back in communist russia: */ /* КАПУЛЭТ капулэт */ test_close_both_porters (test); teardown_test (test); } static void closed_cb (GObject *source, GAsyncResult *result, gpointer user_data) { WockyPorter *porter = WOCKY_PORTER (source); test_data_t *test = user_data; gboolean ret; GError *error = NULL; ret = wocky_porter_close_finish (porter, result, &error); g_assert_no_error (error); g_assert (ret); test->outstanding--; g_main_loop_quit (test->loop); } static void sent_stanza_cb (GObject *source, GAsyncResult *result, gpointer user_data) { WockyPorter *porter = WOCKY_PORTER (source); test_data_t *test = user_data; gboolean ret; GError *error = NULL; ret = wocky_porter_send_finish (porter, result, &error); g_assert_no_error (error); g_assert (ret); /* Close up both porters. There's no reason why either of these operations * should fail. */ wocky_porter_close_async (test->sched_out, NULL, closed_cb, test); wocky_porter_close_async (test->sched_in, NULL, closed_cb, test); } static void close_from_send_callback (void) { test_data_t *test = setup_test (); WockyStanza *stanza = wocky_stanza_build (WOCKY_STANZA_TYPE_MESSAGE, WOCKY_STANZA_SUB_TYPE_NONE, NULL, NULL, '(', "body", '$', "I am made of chalk.", ')', NULL); /* Fire up porters in both directions. */ test_open_both_connections (test); wocky_porter_start (test->sched_in); wocky_porter_start (test->sched_out); /* Send a stanza. Once it's been safely sent, we should be able to close up * the connection in both directions without any trouble. */ wocky_porter_send_async (test->sched_in, stanza, NULL, sent_stanza_cb, test); g_object_unref (stanza); /* The two outstanding events are both porters ultimately closing * successfully. */ test->outstanding += 2; test_wait_pending (test); teardown_test (test); } /* Callbacks used in send_from_send_callback() */ static gboolean message_received_cb ( WockyPorter *porter, WockyStanza *stanza, gpointer user_data) { test_data_t *test = user_data; test_expected_stanza_received (test, stanza); return TRUE; } static void sent_second_or_third_stanza_cb ( GObject *source, GAsyncResult *result, gpointer user_data) { test_data_t *test = user_data; GError *error = NULL; wocky_porter_send_finish (WOCKY_PORTER (source), result, &error); g_assert_no_error (error); test->outstanding--; g_main_loop_quit (test->loop); } static void sent_first_stanza_cb ( GObject *source, GAsyncResult *result, gpointer user_data) { test_data_t *test = user_data; WockyStanza *third_stanza; GError *error = NULL; wocky_porter_send_finish (WOCKY_PORTER (source), result, &error); g_assert_no_error (error); third_stanza = wocky_stanza_build (WOCKY_STANZA_TYPE_MESSAGE, WOCKY_STANZA_SUB_TYPE_NONE, NULL, NULL, '(', "body", '$', "I am made of dur butter.", ')', NULL); wocky_porter_send_async (test->sched_in, third_stanza, NULL, sent_second_or_third_stanza_cb, test); g_queue_push_tail (test->expected_stanzas, third_stanza); /* One for the callback; one for the receiving end. */ test->outstanding += 2; test->outstanding--; g_main_loop_quit (test->loop); } static void send_from_send_callback (void) { test_data_t *test = setup_test (); WockyStanza *stanza; test_open_both_connections (test); wocky_porter_start (test->sched_in); wocky_porter_start (test->sched_out); wocky_porter_register_handler_from_anyone (test->sched_out, WOCKY_STANZA_TYPE_MESSAGE, WOCKY_STANZA_SUB_TYPE_NONE, WOCKY_PORTER_HANDLER_PRIORITY_NORMAL, message_received_cb, test, '(', "body", ')', NULL); /* Send a stanza; in the callback for this stanza, we'll send another stanza. */ stanza = wocky_stanza_build (WOCKY_STANZA_TYPE_MESSAGE, WOCKY_STANZA_SUB_TYPE_NONE, NULL, NULL, '(', "body", '$', "I am made of chalk.", ')', NULL); wocky_porter_send_async (test->sched_in, stanza, NULL, sent_first_stanza_cb, test); g_queue_push_tail (test->expected_stanzas, stanza); /* One for the callback; one for the receiving end. */ test->outstanding += 2; /* But before we've had a chance to send that one, send a second. */ stanza = wocky_stanza_build (WOCKY_STANZA_TYPE_MESSAGE, WOCKY_STANZA_SUB_TYPE_NONE, NULL, NULL, '(', "body", '$', "I am made of jelly.", ')', NULL); wocky_porter_send_async (test->sched_in, stanza, NULL, sent_second_or_third_stanza_cb, test); g_queue_push_tail (test->expected_stanzas, stanza); /* One for the callback; one for the receiving end. */ test->outstanding += 2; test_wait_pending (test); test_close_both_porters (test); teardown_test (test); } static gboolean test_reply_from_domain_handler_cb (WockyPorter *porter, WockyStanza *stanza, gpointer user_data) { test_data_t *test = (test_data_t *) user_data; WockyStanza *reply; const gchar *id; test_expected_stanza_received (test, stanza); id = wocky_node_get_attribute (wocky_stanza_get_top_node (stanza), "id"); /* Reply with from="domain" */ reply = wocky_stanza_build (WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_RESULT, "example.com", "juliet@example.com", '@', "id", id, NULL); wocky_porter_send_async (porter, reply, NULL, test_send_iq_sent_cb, test); g_queue_push_tail (test->expected_stanzas, reply); test->outstanding++; return TRUE; } static void test_reply_from_domain (void) { test_data_t *test = setup_test (); WockyStanza *iq; g_test_bug ("39057"); /* Testing that when we send an iq to server, it can reply from the domain instead of full/bare jid. This happens with xmpp.messenger.live.com. ... ... */ test_open_both_connections (test); wocky_porter_start (test->sched_out); wocky_porter_start (test->sched_in); /* register an IQ handler */ wocky_porter_register_handler_from_anyone (test->sched_out, WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_NONE, 0, test_reply_from_domain_handler_cb, test, NULL); /* Send an IQ query */ iq = wocky_stanza_build (WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_GET, NULL, NULL, '@', "id", "1", NULL); wocky_porter_send_iq_async (test->sched_in, iq, NULL, test_send_iq_reply_cb, test); g_queue_push_tail (test->expected_stanzas, iq); test->outstanding += 2; test_wait_pending (test); test_close_both_porters (test); teardown_test (test); } /* Callbacks used in wildcard_handlers() */ const gchar * const ROMEO = "romeo@montague.lit"; const gchar * const JULIET = "juliet@montague.lit"; static gboolean any_stanza_received_from_romeo_cb ( WockyPorter *porter, WockyStanza *stanza, gpointer user_data) { test_data_t *test = user_data; g_assert_cmpstr (wocky_stanza_get_from (stanza), ==, ROMEO); test->outstanding--; g_main_loop_quit (test->loop); return FALSE; } static gboolean any_stanza_received_from_server_cb ( WockyPorter *porter, WockyStanza *stanza, gpointer user_data) { test_data_t *test = user_data; g_assert_cmpstr (wocky_stanza_get_from (stanza), ==, NULL); test->outstanding--; g_main_loop_quit (test->loop); return FALSE; } static gboolean any_stanza_received_from_anyone_cb ( WockyPorter *porter, WockyStanza *stanza, gpointer user_data) { test_data_t *test = user_data; test->outstanding--; g_main_loop_quit (test->loop); return FALSE; } static void wildcard_handlers (void) { test_data_t *test = setup_test (); WockyStanza *stanza; test_open_both_connections (test); wocky_porter_start (test->sched_in); wocky_porter_start (test->sched_out); wocky_porter_register_handler_from_anyone (test->sched_out, WOCKY_STANZA_TYPE_NONE, WOCKY_STANZA_SUB_TYPE_NONE, WOCKY_PORTER_HANDLER_PRIORITY_NORMAL, any_stanza_received_from_anyone_cb, test, NULL); wocky_porter_register_handler_from (test->sched_out, WOCKY_STANZA_TYPE_NONE, WOCKY_STANZA_SUB_TYPE_NONE, ROMEO, WOCKY_PORTER_HANDLER_PRIORITY_NORMAL, any_stanza_received_from_romeo_cb, test, NULL); wocky_c2s_porter_register_handler_from_server ( WOCKY_C2S_PORTER (test->sched_out), WOCKY_STANZA_TYPE_NONE, WOCKY_STANZA_SUB_TYPE_NONE, WOCKY_PORTER_HANDLER_PRIORITY_NORMAL, any_stanza_received_from_server_cb, test, NULL); stanza = wocky_stanza_build (WOCKY_STANZA_TYPE_MESSAGE, WOCKY_STANZA_SUB_TYPE_HEADLINE, ROMEO, NULL, NULL); wocky_porter_send (test->sched_in, stanza); g_object_unref (stanza); test->outstanding += 2; stanza = wocky_stanza_build (WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_GET, JULIET, NULL, NULL); wocky_porter_send (test->sched_in, stanza); g_object_unref (stanza); test->outstanding += 1; stanza = wocky_stanza_build (WOCKY_STANZA_TYPE_STREAM_FEATURES, WOCKY_STANZA_SUB_TYPE_NONE, NULL, NULL, NULL); wocky_porter_send (test->sched_in, stanza); g_object_unref (stanza); test->outstanding += 2; test_wait_pending (test); test_close_both_porters (test); teardown_test (test); } int main (int argc, char **argv) { int result; test_init (argc, argv); g_test_add_func ("/xmpp-porter/initiation", test_instantiation); g_test_add_func ("/xmpp-porter/send", test_send); g_test_add_func ("/xmpp-porter/receive", test_receive); g_test_add_func ("/xmpp-porter/filter", test_filter); g_test_add_func ("/xmpp-porter/close-flush", test_close_flush); g_test_add_func ("/xmpp-porter/close-not-started", test_close_not_started); g_test_add_func ("/xmpp-porter/close-twice", test_close_twice); g_test_add_func ("/xmpp-porter/remote-close", test_remote_close); g_test_add_func ("/xmpp-porter/close-cancel", test_close_cancel); g_test_add_func ("/xmpp-porter/reading-error", test_reading_error); g_test_add_func ("/xmpp-porter/send-closed", test_send_closed); g_test_add_func ("/xmpp-porter/handler-priority", test_handler_priority); g_test_add_func ("/xmpp-porter/unregister-handler", test_unregister_handler); g_test_add_func ("/xmpp-porter/handler-bare-jid", test_handler_bare_jid); g_test_add_func ("/xmpp-porter/handler-bare-jid", test_handler_full_jid); g_test_add_func ("/xmpp-porter/handler-stanza", test_handler_stanza); g_test_add_func ("/xmpp-porter/cancel-sent-stanza", test_cancel_sent_stanza); g_test_add_func ("/xmpp-porter/writing-error", test_writing_error); g_test_add_func ("/xmpp-porter/send-iq", test_send_iq); g_test_add_func ("/xmpp-porter/acknowledge-iq", test_acknowledge_iq); g_test_add_func ("/xmpp-porter/send-iq-error", test_send_iq_error); g_test_add_func ("/xmpp-porter/send-iq-gerror", test_send_iq_gerror); g_test_add_func ("/xmpp-porter/send-iq-denormalised", test_send_iq_abnormal); g_test_add_func ("/xmpp-porter/error-while-sending-iq", test_error_while_sending_iq); g_test_add_func ("/xmpp-porter/handler-filter", test_handler_filter); g_test_add_func ("/xmpp-porter/unhandled-iq", test_unhandled_iq); g_test_add_func ("/xmpp-porter/send-invalid-iq", test_send_invalid_iq); g_test_add_func ("/xmpp-porter/handler-filter-from", test_handler_filter_from); g_test_add_func ("/xmpp-porter/send-iq-server", test_send_iq_server); g_test_add_func ("/xmpp-porter/unref-when-closed", test_unref_when_closed); g_test_add_func ("/xmpp-porter/close-simultanously", test_close_simultanously); g_test_add_func ("/xmpp-porter/close-error", test_close_error); g_test_add_func ("/xmpp-porter/cancel-iq-closing", test_cancel_iq_closing); g_test_add_func ("/xmpp-porter/stream-error", test_stream_error); g_test_add_func ("/xmpp-porter/close-force", test_close_force); g_test_add_func ("/xmpp-porter/close-force-after-error", test_close_force_after_error); g_test_add_func ("/xmpp-porter/close-force-after-close-sent", test_close_force_after_close_sent); g_test_add_func ("/xmpp-porter/wait-iq-reply-close", test_wait_iq_reply_close); g_test_add_func ("/xmpp-porter/wait-iq-reply-force-close", test_wait_iq_reply_force_close); g_test_add_func ("/xmpp-porter/avoid-double-force-close", test_remote_error); g_test_add_func ("/xmpp-porter/send-and-disconnect", send_and_disconnect); g_test_add_func ("/xmpp-porter/handler-for-domain", handler_for_domain); g_test_add_func ("/xmpp-porter/handler-from-anyone", handler_from_anyone); g_test_add_func ("/xmpp-porter/close-from-send-callback", close_from_send_callback); g_test_add_func ("/xmpp-porter/send-from-send-callback", send_from_send_callback); g_test_add_func ("/xmpp-porter/reply-from-domain", test_reply_from_domain); g_test_add_func ("/xmpp-porter/wildcard-handlers", wildcard_handlers); result = g_test_run (); test_deinit (); return result; } telepathy-gabble-0.18.2/lib/ext/wocky/tests/wocky-ping-test.c0000644000175000017500000000772512200204546024077 0ustar00smcvsmcv00000000000000#ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include #include "wocky-test-stream.h" #include "wocky-test-helper.h" #define PING_COUNT 2 #define PING_INTERVAL 1 /* We expect PING_COUNT pings, followed by disabling pings and waiting for * PING_COUNT * PING_INTERVAL to see if we get any pings we didn't want, * followed by turning pings back on again and testing if we get any. The +1 is * a fudge factor. ;-) */ #define TOTAL_TIMEOUT (PING_COUNT * PING_INTERVAL + 1) * 3 static void ping_recv_cb (const gchar *buff, gsize len, gpointer user_data) { test_data_t *data = user_data; gchar *tmp_buff; /* There is no g_assert_cmpnstr */ tmp_buff = g_strndup (buff, len); g_assert_cmpstr (tmp_buff, ==, " "); g_free (tmp_buff); g_assert_cmpuint (data->outstanding, >, 0); data->outstanding--; g_main_loop_quit (data->loop); } static gboolean we_have_waited_long_enough (gpointer user_data) { test_data_t *test = user_data; g_assert_cmpuint (test->outstanding, ==, 1); test->outstanding--; g_main_loop_quit (test->loop); return FALSE; } static void test_periodic_ping (void) { WockyPing *ping; test_data_t *test = setup_test_with_timeout (TOTAL_TIMEOUT); WockyXmppConnection *connection; GIOStream *stream; GInputStream *stream_in; g_assert (WOCKY_IS_C2S_PORTER (test->sched_in)); /* First, we ping every n seconds */ ping = wocky_ping_new (WOCKY_C2S_PORTER (test->sched_in), PING_INTERVAL); test_open_both_connections (test); wocky_porter_start (test->sched_in); wocky_porter_start (test->sched_out); g_object_get (test->sched_out, "connection", &connection, NULL); g_object_get (connection, "base-stream", &stream, NULL); stream_in = g_io_stream_get_input_stream (stream); g_object_unref (stream); g_object_unref (connection); wocky_test_stream_set_direct_read_callback (stream_in, ping_recv_cb, test); test->outstanding += PING_COUNT; test_wait_pending (test); /* Now, we disable pings, and wait briefly to see if we get any pings. */ g_object_set (ping, "ping-interval", 0, NULL); g_timeout_add_seconds (PING_INTERVAL * PING_COUNT, we_have_waited_long_enough, test); test->outstanding = 1; test_wait_pending (test); /* And then we enable pings again, and wait for one more. */ g_object_set (ping, "ping-interval", PING_INTERVAL, NULL); test->outstanding += 1; test_wait_pending (test); wocky_test_stream_set_direct_read_callback (stream_in, NULL, NULL); test_close_both_porters (test); g_object_unref (ping); teardown_test (test); } static void send_ping_cb (GObject *source, GAsyncResult *res, gpointer user_data) { test_data_t *data = (test_data_t *) user_data; WockyStanza *reply; reply = wocky_porter_send_iq_finish (WOCKY_PORTER (source), res, NULL); g_assert (reply != NULL); g_object_unref (reply); data->outstanding--; g_main_loop_quit (data->loop); } static void test_pong (void) { WockyStanza *s; WockyPing *ping; test_data_t *test = setup_test (); g_assert (WOCKY_IS_C2S_PORTER (test->sched_in)); ping = wocky_ping_new (WOCKY_C2S_PORTER (test->sched_in), 0); test_open_both_connections (test); wocky_porter_start (test->sched_in); wocky_porter_start (test->sched_out); /* Server pings us */ s = wocky_stanza_build (WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_GET, "capulet.lit", "juliet@capulet.lit/balcony", '(', "ping", ':', WOCKY_XMPP_NS_PING, ')', NULL); wocky_porter_send_iq_async (test->sched_out, s, NULL, send_ping_cb, test); g_object_unref (s); test->outstanding++; test_wait_pending (test); test_close_both_porters (test); g_object_unref (ping); teardown_test (test); } int main (int argc, char **argv) { int result; test_init (argc, argv); g_test_add_func ("/xmpp-ping/pong", test_pong); g_test_add_func ("/xmpp-ping/periodic", test_periodic_ping); result = g_test_run (); test_deinit (); return result; } telepathy-gabble-0.18.2/lib/ext/wocky/tests/wocky-pep-service-test.c0000644000175000017500000001765412200204546025366 0ustar00smcvsmcv00000000000000#ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include #include "wocky-test-stream.h" #include "wocky-test-helper.h" #define TEST_NODE1 "http://test.com/node1" #define TEST_NODE2 "http://test.com/node2" /* Test to instantiate a WockyPepService object */ static void test_instantiation (void) { WockyPepService *pep; pep = wocky_pep_service_new ("http://test.com/badger", FALSE); g_assert (pep != NULL); g_object_unref (pep); } /* Test that the 'changed' signal is properly fired */ gboolean event_received; static void test_changed_signal_cb (WockyPepService *pep, WockyBareContact *contact, WockyStanza *stanza, WockyNode *item, test_data_t *test) { const gchar *id = wocky_node_get_attribute ( wocky_stanza_get_top_node (stanza), "id"); g_assert_cmpstr (wocky_bare_contact_get_jid (contact), ==, "alice@example.org"); /* the id happens to hold the number of children; we expect to get the * first one if there is more than one. */ if (wocky_strdiff (id, "0")) { g_assert (item != NULL); g_assert_cmpstr (item->name, ==, "item"); g_assert_cmpstr (wocky_node_get_attribute (item, "id"), ==, "1"); } else { g_assert (item == NULL); } test->outstanding--; g_main_loop_quit (test->loop); event_received = TRUE; } static void send_pep_event (WockyPorter *porter, const gchar *node, guint n_items) { WockyStanza *stanza; WockyNode *items; gchar *n_items_str = g_strdup_printf ("%d", n_items); guint i; stanza = wocky_stanza_build (WOCKY_STANZA_TYPE_MESSAGE, WOCKY_STANZA_SUB_TYPE_NONE, "alice@example.org", NULL, /* This is a hint for test_changed_signal_cb. */ '@', "id", n_items_str, '(', "event", ':', WOCKY_XMPP_NS_PUBSUB_EVENT, '(', "items", '@', "node", node, '*', &items, ')', ')', NULL); for (i = 1; i <= n_items; i++) { gchar *i_str = g_strdup_printf ("%d", i); wocky_node_add_build (items, '(', "item", '@', "id", i_str, '(', "payload", ')', ')', NULL); g_free (i_str); } wocky_porter_send (porter, stanza); g_object_unref (stanza); g_free (n_items_str); } static void test_changed_signal (void) { test_data_t *test = setup_test (); WockyPepService *pep; pep = wocky_pep_service_new (TEST_NODE1, FALSE); test_open_both_connections (test); g_signal_connect (pep, "changed", G_CALLBACK (test_changed_signal_cb), test); wocky_pep_service_start (pep, test->session_out); wocky_porter_start (test->sched_out); wocky_porter_start (test->sched_in); /* send events on the right node */ event_received = FALSE; send_pep_event (test->sched_in, TEST_NODE1, 0); send_pep_event (test->sched_in, TEST_NODE1, 1); send_pep_event (test->sched_in, TEST_NODE1, 2); test->outstanding += 3; test_wait_pending (test); g_assert (event_received); event_received = FALSE; /* send event on the wrong node */ send_pep_event (test->sched_in, TEST_NODE2, 1); g_object_unref (pep); /* send event to the right node after the PEP service has been destroyed */ send_pep_event (test->sched_in, TEST_NODE1, 1); test_close_both_porters (test); teardown_test (test); g_assert (!event_received); } /* Test wocky_pep_service_get_async */ static void test_send_query_cb (GObject *source_object, GAsyncResult *res, gpointer user_data) { test_data_t *test = (test_data_t *) user_data; WockyStanza *reply; WockyNode *item; WockyStanzaType type; WockyStanzaSubType sub_type; reply = wocky_pep_service_get_finish (WOCKY_PEP_SERVICE (source_object), res, &item, NULL); g_assert (reply != NULL); wocky_stanza_get_type_info (reply, &type, &sub_type); g_assert (type == WOCKY_STANZA_TYPE_IQ); g_assert (sub_type == WOCKY_STANZA_SUB_TYPE_RESULT); g_assert (item != NULL); g_assert_cmpstr (item->name, ==, "item"); g_assert_cmpstr (wocky_node_get_attribute (item, "id"), ==, "1"); g_object_unref (reply); test->outstanding--; g_main_loop_quit (test->loop); } static gboolean test_send_query_stanza_received_cb (WockyPorter *porter, WockyStanza *stanza, gpointer user_data) { test_data_t *test = (test_data_t *) user_data; WockyStanza *reply; reply = wocky_stanza_build_iq_result (stanza, '(', "pubsub", ':', WOCKY_XMPP_NS_PUBSUB, '(', "items", '@', "node", "node1", '(', "item", '@', "id", "1", '(', "payload", ')', ')', ')', ')', NULL); wocky_porter_send (porter, reply); g_object_unref (reply); test->outstanding--; g_main_loop_quit (test->loop); return TRUE; } static void test_send_query_failed_cb (GObject *source_object, GAsyncResult *res, gpointer user_data) { test_data_t *test = (test_data_t *) user_data; WockyStanza *reply; GError *error = NULL; reply = wocky_pep_service_get_finish (WOCKY_PEP_SERVICE (source_object), res, NULL, &error); g_assert (reply == NULL); g_assert_error (error, WOCKY_XMPP_CONNECTION_ERROR, WOCKY_XMPP_CONNECTION_ERROR_CLOSED); g_clear_error (&error); test->outstanding--; g_main_loop_quit (test->loop); } static void test_get (void) { test_data_t *test = setup_test (); WockyPepService *pep; WockyContactFactory *contact_factory; WockyBareContact *contact; guint handler_id; pep = wocky_pep_service_new (TEST_NODE1, FALSE); test_open_both_connections (test); wocky_pep_service_start (pep, test->session_in); wocky_porter_start (test->sched_out); wocky_porter_start (test->sched_in); handler_id = wocky_porter_register_handler_from_anyone (test->sched_out, WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_GET, WOCKY_PORTER_HANDLER_PRIORITY_MAX, test_send_query_stanza_received_cb, test, NULL); contact_factory = wocky_session_get_contact_factory (test->session_in); contact = wocky_contact_factory_ensure_bare_contact (contact_factory, "juliet@example.org"); wocky_pep_service_get_async (pep, contact, NULL, test_send_query_cb, test); test->outstanding += 2; test_wait_pending (test); /* Regression test for a bug where wocky_pep_service_get_async's callback * would crash if sending the IQ failed. */ wocky_porter_unregister_handler (test->sched_out, handler_id); wocky_pep_service_get_async (pep, contact, NULL, test_send_query_failed_cb, test); test->outstanding += 1; test_close_both_porters (test); g_object_unref (contact); g_object_unref (pep); teardown_test (test); } /* Test wocky_pep_service_make_publish_stanza */ static void test_make_publish_stanza (void) { WockyPepService *pep; WockyStanza *stanza; WockyNode *item = NULL, *n; WockyStanzaType type; WockyStanzaSubType sub_type; pep = wocky_pep_service_new (TEST_NODE1, FALSE); stanza = wocky_pep_service_make_publish_stanza (pep, &item); g_assert (stanza != NULL); wocky_stanza_get_type_info (stanza, &type, &sub_type); g_assert (type == WOCKY_STANZA_TYPE_IQ); g_assert (sub_type == WOCKY_STANZA_SUB_TYPE_SET); n = wocky_node_get_child_ns (wocky_stanza_get_top_node (stanza), "pubsub", WOCKY_XMPP_NS_PUBSUB); g_assert (n != NULL); n = wocky_node_get_child (n, "publish"); g_assert (n != NULL); g_assert (!wocky_strdiff (wocky_node_get_attribute (n, "node"), TEST_NODE1)); n = wocky_node_get_child (n, "item"); g_assert (n != NULL); g_assert (n == item); g_object_unref (stanza); g_object_unref (pep); } int main (int argc, char **argv) { int result; test_init (argc, argv); g_test_add_func ("/pep-service/instantiation", test_instantiation); g_test_add_func ("/pep-service/changed-signal", test_changed_signal); g_test_add_func ("/pep-service/get", test_get); g_test_add_func ("/pep-service/make-publish-stanza", test_make_publish_stanza); result = g_test_run (); test_deinit (); return result; } telepathy-gabble-0.18.2/lib/ext/wocky/tests/wocky-node-tree-test.c0000644000175000017500000000501112200204546025006 0ustar00smcvsmcv00000000000000#ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include #include "wocky-test-helper.h" static void test_build_simple_tree (void) { WockyNodeTree *tree; WockyNode *n; tree = wocky_node_tree_new ("lions", "animals", '(', "distribution", '$', "Only in kenya", ')', NULL); g_assert (tree != NULL); n = wocky_node_tree_get_top_node (tree); g_assert (n != NULL); g_assert_cmpstr (n->name, ==, "lions"); g_assert_cmpstr (wocky_node_get_ns (n), ==, "animals"); g_assert_cmpint (g_slist_length (n->children), ==, 1); n = wocky_node_get_first_child (n); g_assert (n != NULL); g_assert_cmpstr (n->name, ==, "distribution"); g_assert_cmpstr (wocky_node_get_ns (n), ==, "animals"); g_assert_cmpstr (n->content, ==, "Only in kenya"); g_object_unref (tree); } static void test_tree_from_node (void) { WockyNodeTree *a, *b; a = wocky_node_tree_new ("noms", "foodstocks", '(', "item", '@', "origin", "Italy", '$', "Plum cake", ')', NULL); b = wocky_node_tree_new_from_node (wocky_node_tree_get_top_node (a)); test_assert_nodes_equal (wocky_node_tree_get_top_node (a), wocky_node_tree_get_top_node (b)); g_object_unref (a); g_object_unref (b); } static void test_node_add_tree (void) { WockyNodeTree *origin, *ashes, *destination; WockyNode *ash, *top; WockyNode *ash_copy; origin = wocky_node_tree_new ("Eyjafjallajökull", "urn:wocky:lol:ísland", '(', "æsc", '*', &ash, '@', "type", "vulcanic", '(', "description", '$', "Black and smokey", ')', ')', NULL); ashes = wocky_node_tree_new_from_node (ash); destination = wocky_node_tree_new ("europe", "urn:wocky:lol:noplanesforyou", '*', &top, NULL); ash_copy = wocky_node_add_node_tree (top, ashes); test_assert_nodes_equal (ash_copy, ash); test_assert_nodes_equal ( wocky_node_get_first_child (wocky_node_tree_get_top_node (destination)), wocky_node_get_first_child (wocky_node_tree_get_top_node (origin))); g_object_unref (origin); g_object_unref (ashes); g_object_unref (destination); } int main (int argc, char **argv) { int result; test_init (argc, argv); g_test_add_func ("/xmpp-node-tree/simple-tree", test_build_simple_tree); g_test_add_func ("/xmpp-node-tree/tree-from-node", test_tree_from_node); g_test_add_func ("/xmpp-node-tree/node-add-tree", test_node_add_tree); result = g_test_run (); test_deinit (); return result; } telepathy-gabble-0.18.2/lib/ext/wocky/tests/wocky-loopback-test.c0000644000175000017500000001046312200204546024725 0ustar00smcvsmcv00000000000000#ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include "wocky-test-helper.h" /* I'm not happy about this, but the existing test stuff really relies * on having multiple streams */ typedef struct { test_data_t data; GIOStream *stream; WockyXmppConnection *conn; WockySession *session; WockyPorter *porter; } loopback_test_t; static gboolean test_timeout (gpointer data) { g_test_message ("Timeout reached :("); g_assert_not_reached (); return FALSE; } static loopback_test_t * setup (void) { loopback_test_t *test = g_slice_new0 (loopback_test_t); test->data.loop = g_main_loop_new (NULL, FALSE); test->data.cancellable = g_cancellable_new (); test->data.timeout_id = g_timeout_add_seconds (10, test_timeout, NULL); test->data.expected_stanzas = g_queue_new (); test->stream = wocky_loopback_stream_new (); test->conn = wocky_xmpp_connection_new (test->stream); test->session = wocky_session_new_with_connection (test->conn, "example.com"); test->porter = wocky_session_get_porter (test->session); return test; } static void send_received_open_cb (GObject *source, GAsyncResult *res, gpointer user_data) { WockyXmppConnection *conn = WOCKY_XMPP_CONNECTION (source); loopback_test_t *d = (loopback_test_t *) user_data; g_assert (wocky_xmpp_connection_recv_open_finish (conn, res, NULL, NULL, NULL, NULL, NULL, NULL)); wocky_session_start (d->session); d->data.outstanding--; g_main_loop_quit (d->data.loop); } static void send_open_cb (GObject *source, GAsyncResult *res, gpointer user_data) { WockyXmppConnection *conn = WOCKY_XMPP_CONNECTION (source); g_assert (wocky_xmpp_connection_send_open_finish (conn, res, NULL)); wocky_xmpp_connection_recv_open_async (conn, NULL, send_received_open_cb, user_data); } static void start_test (loopback_test_t *test) { wocky_xmpp_connection_send_open_async (test->conn, NULL, NULL, NULL, NULL, NULL, NULL, send_open_cb, test); test->data.outstanding++; test_wait_pending (&(test->data)); } static void close_cb (GObject *source, GAsyncResult *res, gpointer user_data) { loopback_test_t *test = user_data; g_assert (wocky_porter_close_finish (WOCKY_PORTER (source), res, NULL)); test->data.outstanding--; g_main_loop_quit (test->data.loop); g_main_loop_unref (test->data.loop); g_object_unref (test->session); g_object_unref (test->conn); g_object_unref (test->data.cancellable); g_source_remove (test->data.timeout_id); g_assert (g_queue_get_length (test->data.expected_stanzas) == 0); g_queue_free (test->data.expected_stanzas); g_slice_free (loopback_test_t, test); } static void cleanup (loopback_test_t *test) { wocky_porter_close_async (test->porter, NULL, close_cb, test); test->data.outstanding++; test_wait_pending (&(test->data)); } static void send_stanza_cb (GObject *source, GAsyncResult *res, gpointer user_data) { test_data_t *data = (test_data_t *) user_data; g_assert (wocky_porter_send_finish ( WOCKY_PORTER (source), res, NULL)); data->outstanding--; g_main_loop_quit (data->loop); } /* receive testing */ static gboolean test_receive_stanza_received_cb (WockyPorter *porter, WockyStanza *stanza, gpointer user_data) { test_data_t *test = (test_data_t *) user_data; test_expected_stanza_received (test, stanza); return TRUE; } static void test_receive (void) { loopback_test_t *test = setup (); WockyStanza *s; start_test (test); wocky_porter_register_handler_from_anyone (test->porter, WOCKY_STANZA_TYPE_MESSAGE, WOCKY_STANZA_SUB_TYPE_NONE, 0, test_receive_stanza_received_cb, test, NULL); /* Send a stanza */ s = wocky_stanza_build (WOCKY_STANZA_TYPE_MESSAGE, WOCKY_STANZA_SUB_TYPE_CHAT, "juliet@example.com", "romeo@example.net", NULL); wocky_porter_send_async (test->porter, s, NULL, send_stanza_cb, test); g_queue_push_tail (test->data.expected_stanzas, s); /* We are waiting for the stanza to be sent and received on the other * side */ test->data.outstanding += 2; test_wait_pending (&(test->data)); cleanup (test); } int main (int argc, char **argv) { int result; test_init (argc, argv); g_test_add_func ("/loopback-porter/receive", test_receive); result = g_test_run (); test_deinit (); return result; } telepathy-gabble-0.18.2/lib/ext/wocky/tests/wocky-jid-validation-test.c0000644000175000017500000000344012200204546026026 0ustar00smcvsmcv00000000000000#ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include "wocky-test-helper.h" static void valid (gconstpointer data) { const gchar *jid = data; g_assert (wocky_decode_jid (jid, NULL, NULL, NULL)); } static void invalid (gconstpointer data) { const gchar *jid = data; g_assert (!wocky_decode_jid (jid, NULL, NULL, NULL)); } int main (int argc, char **argv) { int result; test_init (argc, argv); g_test_add_data_func ("/jid/valid/simple", "foo@bar.com/baz", valid); g_test_add_data_func ("/jid/valid/no-resource", "foo@bar.com", valid); g_test_add_data_func ("/jid/valid/no-node", "bar.com/baz", valid); g_test_add_data_func ("/jid/valid/just-domain", "bar.com", valid); g_test_add_data_func ("/jid/valid/ip", "foo@127.0.0.1", valid); g_test_add_data_func ("/jid/valid/ipv6", "foo@2:0:1cfe:face:b00c::3", valid); g_test_add_data_func ("/jid/valid/russian", "Анна@Каренина.ru", valid); g_test_add_data_func ("/jid/invalid/empty", "", invalid); g_test_add_data_func ("/jid/invalid/just-node", "foo@", invalid); g_test_add_data_func ("/jid/invalid/node-and-resource", "foo@/lol", invalid); g_test_add_data_func ("/jid/invalid/just-resource", "/lol", invalid); g_test_add_data_func ("/jid/invalid/garbage", "(*&$(*)", invalid); g_test_add_data_func ("/jid/invalid/space-domain", "foo bar", invalid); g_test_add_data_func ("/jid/invalid/two-ats", "squid@cat@battle", invalid); #if 0 /* These are improperly accepted. */ g_test_add_data_func ("/jid/invalid/space-node", "i am a@fish", invalid); /* U+00A0 NO-BREAK SPACE is in Table C.1.2, which is forbidden in domains. */ g_test_add_data_func ("/jid/invalid/nbsp-domain", "foo bar", invalid); #endif result = g_test_run (); test_deinit (); return result; } telepathy-gabble-0.18.2/lib/ext/wocky/tests/wocky-http-proxy-test.c0000644000175000017500000002334412200204546025273 0ustar00smcvsmcv00000000000000#ifdef HAVE_CONFIG_H #include "config.h" #endif #include "wocky-test-stream.h" #include "wocky-test-helper.h" #include #include /* WockyHttpProxy isn't public API, so we need to be a bit sneaky to get the * header. */ #define WOCKY_COMPILATION #include #undef WOCKY_COMPILATION typedef enum { DOMAIN_NONE = 0, DOMAIN_G_IO_ERROR } HttpErrorDomain; typedef struct { const gchar *path; const gchar *reply; HttpErrorDomain domain; gint code; const gchar *username; const gchar *password; } HttpTestCase; typedef struct { GMainLoop *mainloop; GMainLoop *thread_mainloop; GCancellable *cancellable; GCancellable *thread_cancellable; GThread *thread; GSocketListener *listener; guint16 port; const HttpTestCase *test_case; } HttpTestData; static HttpTestCase test_cases[] = { { "/http-proxy/close-by-peer", "", DOMAIN_G_IO_ERROR, G_IO_ERROR_PROXY_FAILED }, { "/http-proxy/bad-reply", "BAD REPLY", DOMAIN_G_IO_ERROR, G_IO_ERROR_PROXY_FAILED }, { "/http-proxy/very-short-reply", "HTTP/1.\r\n\r\n", DOMAIN_G_IO_ERROR, G_IO_ERROR_PROXY_FAILED }, { "/http-proxy/short-reply", "HTTP/1.0\r\n\r\n", DOMAIN_G_IO_ERROR, G_IO_ERROR_PROXY_FAILED }, { "/http-proxy/http-404", "HTTP/1.0 404 Not Found\r\n" "Content-Type: text/html; charset=UTF-8\r\n" "Content-Length: 27\r\n" "\r\n" "

Hello

", DOMAIN_G_IO_ERROR, G_IO_ERROR_PROXY_FAILED }, { "/http-proxy/need-authentication", "HTTP/1.0 407 Proxy Authentication Required\r\n" "Content-Type: text/html; charset=UTF-8\r\n" "Content-Length: 27\r\n" "\r\n" "

Hello

", DOMAIN_G_IO_ERROR, G_IO_ERROR_PROXY_NEED_AUTH }, { "/http-proxy/success", "HTTP/1.0 200 OK\r\n" "\r\n", DOMAIN_NONE, 0 }, { "/http-proxy/authentication-failed", "HTTP/1.0 407 Proxy Authentication Required\r\n" "Content-Type: text/html; charset=UTF-8\r\n" "Content-Length: 27\r\n" "\r\n" "

Hello

", DOMAIN_G_IO_ERROR, G_IO_ERROR_PROXY_AUTH_FAILED, "username", "bad-password" }, { "/http-proxy/authenticated", "HTTP/1.0 200 OK\r\n" "\r\n", DOMAIN_NONE, 0, "Aladdin", "open sesame"}, }; static HttpTestData * tearup (const HttpTestCase *test_case) { HttpTestData *data; data = g_new0 (HttpTestData, 1); data->mainloop = g_main_loop_new (NULL, FALSE); data->cancellable = g_cancellable_new (); data->thread_cancellable = g_cancellable_new (); data->listener = g_socket_listener_new (); data->port = g_socket_listener_add_any_inet_port (data->listener, NULL, NULL); g_assert_cmpuint (data->port, !=, 0); data->test_case = test_case; return data; } static void run_in_thread (HttpTestData *data, GThreadFunc func) { data->thread = g_thread_new ("server_thread", func, data); g_assert (data->thread != NULL); } static gboolean tear_down_idle_cb (gpointer user_data) { HttpTestData *data = user_data; g_main_loop_quit (data->mainloop); return FALSE; } static void teardown (HttpTestData *data) { if (!g_cancellable_is_cancelled (data->cancellable)) g_cancellable_cancel (data->cancellable); if (!g_cancellable_is_cancelled (data->thread_cancellable)) g_cancellable_cancel (data->thread_cancellable); if (data->thread) g_thread_join (data->thread); if (g_main_loop_is_running (data->mainloop)) g_main_loop_quit (data->mainloop); g_idle_add_full (G_PRIORITY_LOW, tear_down_idle_cb, data, NULL); g_main_loop_run (data->mainloop); g_object_unref (data->cancellable); g_object_unref (data->thread_cancellable); g_object_unref (data->listener); g_main_loop_unref (data->mainloop); g_free (data); } static gboolean str_has_prefix_case (const gchar *str, const gchar *prefix) { return g_ascii_strncasecmp (prefix, str, strlen (prefix)) == 0; } static void test_http_proxy_instantiation (void) { GProxy *proxy; proxy = g_proxy_get_default_for_protocol ("http"); g_assert (G_IS_PROXY (proxy)); g_assert (WOCKY_IS_HTTP_PROXY (proxy)); g_object_unref (proxy); } static gpointer server_thread (gpointer user_data) { HttpTestData *data = user_data; GSocketConnection *conn; GDataInputStream *data_in; GOutputStream *out; gchar *buffer; gint has_host = 0; gint has_user_agent = 0; gint has_cred = 0; conn = g_socket_listener_accept (data->listener, NULL, data->thread_cancellable, NULL); g_assert (conn != NULL); data_in = g_data_input_stream_new ( g_io_stream_get_input_stream (G_IO_STREAM (conn))); g_data_input_stream_set_newline_type (data_in, G_DATA_STREAM_NEWLINE_TYPE_CR_LF); buffer = g_data_input_stream_read_line (data_in, NULL, data->thread_cancellable, NULL); g_assert_cmpstr (buffer, ==, "CONNECT to:443 HTTP/1.0"); do { g_free (buffer); buffer = g_data_input_stream_read_line (data_in, NULL, data->thread_cancellable, NULL); g_assert (buffer != NULL); if (str_has_prefix_case (buffer, "Host:")) { has_host++; g_assert_cmpstr (buffer, ==, "Host: to:443"); } else if (str_has_prefix_case (buffer, "User-Agent:")) has_user_agent++; else if (str_has_prefix_case (buffer, "Proxy-Authorization:")) { gchar *cred; gchar *base64_cred; const gchar *received_cred; has_cred++; g_assert (data->test_case->username != NULL); g_assert (data->test_case->password != NULL); cred = g_strdup_printf ("%s:%s", data->test_case->username, data->test_case->password); base64_cred = g_base64_encode ((guchar *) cred, strlen (cred)); g_free (cred); received_cred = buffer + 20; while (*received_cred == ' ') received_cred++; g_assert_cmpstr (base64_cred, ==, received_cred); g_free (base64_cred); } } while (buffer[0] != '\0'); g_assert_cmpuint (has_host, ==, 1); g_assert_cmpuint (has_user_agent, ==, 1); if (data->test_case->username != NULL) g_assert_cmpuint (has_cred, ==, 1); else g_assert_cmpuint (has_cred, ==, 0); g_free (buffer); out = g_io_stream_get_output_stream (G_IO_STREAM (conn)); g_assert (g_output_stream_write_all (out, data->test_case->reply, strlen (data->test_case->reply), NULL, data->thread_cancellable, NULL)); g_object_unref (data_in); g_object_unref (conn); return NULL; } static GQuark get_error_domain (HttpErrorDomain id) { GQuark domain = 0; switch (id) { case DOMAIN_G_IO_ERROR: domain = G_IO_ERROR; break; default: g_assert_not_reached (); } return domain; } static GSocketAddress * create_proxy_address (HttpTestData *data) { GSocketAddress *proxy_address; GInetAddress *inet_address; inet_address = g_inet_address_new_loopback (G_SOCKET_FAMILY_IPV4); proxy_address = g_proxy_address_new (inet_address, data->port, "http", "to", 443, data->test_case->username, data->test_case->password); g_object_unref (inet_address); return proxy_address; } static void check_result (const HttpTestCase *test_case, GSocketConnection *connection, GError *error) { if (test_case->domain != DOMAIN_NONE) { g_assert_error (error, get_error_domain (test_case->domain), test_case->code); g_error_free (error); } else { g_assert_no_error (error); g_object_unref (connection); } } static void test_http_proxy_with_data (gconstpointer user_data) { const HttpTestCase *test_case = user_data; HttpTestData *data; GSocketClient *client; GSocketAddress *proxy_address; GSocketConnection *connection; GError *error = NULL; data = tearup (test_case); run_in_thread (data, server_thread); client = g_socket_client_new (); proxy_address = create_proxy_address (data); connection = g_socket_client_connect (client, G_SOCKET_CONNECTABLE (proxy_address), data->cancellable, &error); g_object_unref (proxy_address); g_object_unref (client); check_result (test_case, connection, error); teardown (data); } static void connect_cb (GObject *source, GAsyncResult *result, gpointer user_data) { HttpTestData *data = user_data; GSocketConnection *connection; GError *error = NULL; connection = g_socket_client_connect_finish (G_SOCKET_CLIENT (source), result, &error); check_result (data->test_case, connection, error); g_main_loop_quit (data->mainloop); } static void test_http_proxy_with_data_async (gconstpointer user_data) { const HttpTestCase *test_case = user_data; HttpTestData *data; GSocketClient *client; GSocketAddress *proxy_address; data = tearup (test_case); run_in_thread (data, server_thread); client = g_socket_client_new (); proxy_address = create_proxy_address (data); g_socket_client_connect_async (client, G_SOCKET_CONNECTABLE (proxy_address), data->cancellable, connect_cb, data); g_object_unref (client); g_object_unref (proxy_address); g_main_loop_run (data->mainloop); teardown (data); } int main (int argc, char **argv) { int result; guint i; test_init (argc, argv); _wocky_http_proxy_get_type (); g_test_add_func ("/http-proxy/instantiation", test_http_proxy_instantiation); for (i = 0; i < G_N_ELEMENTS (test_cases); i++) { gchar *async_path; g_test_add_data_func (test_cases[i].path, test_cases + i, test_http_proxy_with_data); async_path = g_strdup_printf ("%s-async", test_cases[i].path); g_test_add_data_func (async_path, test_cases + i, test_http_proxy_with_data_async); g_free (async_path); } result = g_test_run (); test_deinit (); return result; } telepathy-gabble-0.18.2/lib/ext/wocky/tests/wocky-dummy-xmpp-server.c0000644000175000017500000000464712201202754025606 0ustar00smcvsmcv00000000000000#ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include #include #include #include #include #include "wocky-test-connector-server.h" GMainLoop *loop; static gboolean server_quit (GIOChannel *channel, GIOCondition cond, gpointer data) { g_main_loop_quit (loop); return FALSE; } static gboolean client_connected (GIOChannel *channel, GIOCondition cond, gpointer data) { struct sockaddr_in client; socklen_t clen = sizeof (client); int ssock = g_io_channel_unix_get_fd (channel); int csock = accept (ssock, (struct sockaddr *)&client, &clen); GSocket *gsock = g_socket_new_from_fd (csock, NULL); ConnectorProblem cproblem = { 0, }; GSocketConnection *gconn; pid_t pid = 0; TestConnectorServer *server; if (csock < 0) { perror ("accept() failed"); g_warning ("accept() failed on socket that should have been ready."); return TRUE; } switch ((pid = fork ())) { case -1: perror ("Failed to spawn child process"); g_main_loop_quit (loop); break; case 0: while (g_source_remove_by_user_data (loop)); g_io_channel_shutdown (channel, TRUE, NULL); gconn = g_object_new (G_TYPE_SOCKET_CONNECTION, "socket", gsock, NULL); server = test_connector_server_new (G_IO_STREAM (gconn), NULL, "foo", "bar", "1.0", &cproblem, SERVER_PROBLEM_NO_PROBLEM, CERT_STANDARD); test_connector_server_start (server); return FALSE; default: g_socket_close (gsock, NULL); return TRUE; } return FALSE; } int main (int argc, char **argv) { int ssock; int reuse = 1; struct sockaddr_in server; GIOChannel *channel; memset (&server, 0, sizeof (server)); g_type_init (); loop = g_main_loop_new (NULL, FALSE); server.sin_family = AF_INET; inet_aton ("127.0.0.1", &server.sin_addr); server.sin_port = htons (5222); ssock = socket (AF_INET, SOCK_STREAM, IPPROTO_TCP); setsockopt (ssock, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof (reuse)); bind (ssock, (struct sockaddr *)&server, sizeof (server)); listen (ssock, 1024); channel = g_io_channel_unix_new (ssock); g_io_add_watch (channel, G_IO_IN|G_IO_PRI, client_connected, loop); g_io_add_watch (channel, G_IO_ERR|G_IO_NVAL, server_quit, loop); g_main_loop_run (loop); } telepathy-gabble-0.18.2/lib/ext/wocky/tests/wocky-data-form-test.c0000644000175000017500000006651212200204546025013 0ustar00smcvsmcv00000000000000#ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include #include "wocky-test-helper.h" static void test_new_from_form (void) { WockyStanza *stanza; WockyNode *node; WockyDataForm *form; GError *error = NULL; stanza = wocky_stanza_build ( WOCKY_STANZA_TYPE_IQ,WOCKY_STANZA_SUB_TYPE_RESULT, NULL, NULL, NULL); /* node doesn't contain a form */ form = wocky_data_form_new_from_form (wocky_stanza_get_top_node (stanza), &error); g_assert (form == NULL); g_assert_error (error, WOCKY_DATA_FORM_ERROR, WOCKY_DATA_FORM_ERROR_NOT_FORM); g_clear_error (&error); /* add 'x' node */ node = wocky_node_add_child_ns (wocky_stanza_get_top_node (stanza), "x", WOCKY_XMPP_NS_DATA); /* the x node doesn't have a 'type' attribute */ form = wocky_data_form_new_from_form (wocky_stanza_get_top_node (stanza), &error); g_assert (form == NULL); g_assert_error (error, WOCKY_DATA_FORM_ERROR, WOCKY_DATA_FORM_ERROR_WRONG_TYPE); g_clear_error (&error); /* set wrong type */ wocky_node_set_attribute (node, "type", "badger"); form = wocky_data_form_new_from_form (wocky_stanza_get_top_node (stanza), &error); g_assert (form == NULL); g_assert_error (error, WOCKY_DATA_FORM_ERROR, WOCKY_DATA_FORM_ERROR_WRONG_TYPE); g_clear_error (&error); /* set the right type */ wocky_node_set_attribute (node, "type", "form"); form = wocky_data_form_new_from_form (wocky_stanza_get_top_node (stanza), &error); g_assert (form != NULL); g_assert_no_error (error); g_object_unref (form); g_object_unref (stanza); } static WockyStanza * create_bot_creation_form_stanza (void) { /* This stanza is inspired from Example 2 of XEP-0004: Data Forms */ return wocky_stanza_build ( WOCKY_STANZA_TYPE_IQ,WOCKY_STANZA_SUB_TYPE_RESULT, NULL, NULL, '(', "x", ':', WOCKY_XMPP_NS_DATA, '@', "type", "form", '(', "title", '$', "My Title", ')', '(', "instructions", '$', "Badger", ')', /* hidden field */ '(', "field", '@', "type", "hidden", '@', "var", "FORM_TYPE", '(', "value", '$', "jabber:bot", ')', ')', /* fixed field */ '(', "field", '@', "type", "fixed", '(', "value", '$', "fixed value", ')', ')', /* text-single field */ '(', "field", '@', "type", "text-single", '@', "var", "botname", '@', "label", "The name of your bot", ')', /* field with no type. type='' is only a SHOULD; the default is * text-single */ '(', "field", '@', "var", "pseudonym", '@', "label", "Your bot's name at the weekend", ')', /* text-multi field */ '(', "field", '@', "type", "text-multi", '@', "var", "description", '@', "label", "Helpful description of your bot", ')', /* boolean field */ '(', "field", '@', "type", "boolean", '@', "var", "public", '@', "label", "Public bot?", '(', "required", ')', '(', "value", '$', "false", ')', ')', /* text-private field */ '(', "field", '@', "type", "text-private", '@', "var", "password", '@', "label", "Password for special access", ')', /* list-multi field */ '(', "field", '@', "type", "list-multi", '@', "var", "features", '@', "label", "What features will the bot support?", '(', "option", '@', "label", "Contests", '(', "value", '$', "contests", ')', ')', '(', "option", '@', "label", "News", '(', "value", '$', "news", ')', ')', '(', "option", '@', "label", "Polls", '(', "value", '$', "polls", ')', ')', '(', "option", '@', "label", "Reminders", '(', "value", '$', "reminders", ')', ')', '(', "option", '@', "label", "Search", '(', "value", '$', "search", ')', ')', '(', "value", '$', "news", ')', '(', "value", '$', "search", ')', ')', /* list-single field */ '(', "field", '@', "type", "list-single", '@', "var", "maxsubs", '@', "label", "Maximum number of subscribers", '(', "value", '$', "20", ')', '(', "option", '@', "label", "10", '(', "value", '$', "10", ')', ')', '(', "option", '@', "label", "20", '(', "value", '$', "20", ')', ')', '(', "option", '@', "label", "30", '(', "value", '$', "30", ')', ')', '(', "option", '@', "label", "50", '(', "value", '$', "50", ')', ')', '(', "option", '@', "label", "100", '(', "value", '$', "100", ')', ')', '(', "option", '@', "label", "None", '(', "value", '$', "none", ')', ')', ')', /* jid-multi field */ '(', "field", '@', "type", "jid-multi", '@', "var", "invitelist", '@', "label", "People to invite", '(', "desc", '$', "Tell friends", ')', ')', /* jid-single field */ '(', "field", '@', "type", "jid-single", '@', "var", "botjid", '@', "label", "The JID of the bot", ')', ')', NULL); } static void test_parse_form (void) { WockyStanza *stanza; WockyDataForm *form; GSList *l; /* used to check that fields are stored in the right order */ WockyDataFormField expected_types[] = { { WOCKY_DATA_FORM_FIELD_TYPE_HIDDEN, "FORM_TYPE", NULL, NULL, FALSE, NULL, NULL, NULL }, { WOCKY_DATA_FORM_FIELD_TYPE_FIXED, NULL, NULL, NULL, FALSE, NULL, NULL, NULL }, { WOCKY_DATA_FORM_FIELD_TYPE_TEXT_SINGLE, "botname", "The name of your bot", NULL, FALSE, NULL, NULL, NULL }, { WOCKY_DATA_FORM_FIELD_TYPE_TEXT_SINGLE, "pseudonym", "Your bot's name at the weekend", NULL, FALSE, NULL, NULL, NULL }, { WOCKY_DATA_FORM_FIELD_TYPE_TEXT_MULTI, "description", "Helpful description of your bot", NULL, FALSE, NULL, NULL, NULL }, { WOCKY_DATA_FORM_FIELD_TYPE_BOOLEAN, "public", "Public bot?", NULL, TRUE, NULL, NULL, NULL }, { WOCKY_DATA_FORM_FIELD_TYPE_TEXT_PRIVATE, "password", "Password for special access", NULL, FALSE, NULL, NULL, NULL }, { WOCKY_DATA_FORM_FIELD_TYPE_LIST_MULTI, "features", "What features will the bot support?", NULL, FALSE, NULL, NULL, NULL }, { WOCKY_DATA_FORM_FIELD_TYPE_LIST_SINGLE, "maxsubs", "Maximum number of subscribers", NULL, FALSE, NULL, NULL, NULL }, { WOCKY_DATA_FORM_FIELD_TYPE_JID_MULTI, "invitelist", "People to invite", "Tell friends", FALSE, NULL, NULL, NULL }, { WOCKY_DATA_FORM_FIELD_TYPE_JID_SINGLE, "botjid", "The JID of the bot", NULL, FALSE, NULL, NULL, NULL }, }; guint i; WockyDataFormField *field; GStrv strv; WockyDataFormFieldOption features_options[] = { { "Contests", "contests" }, { "News", "news" }, { "Polls", "polls" }, { "Reminders", "reminders" }, { "Search", "search" }, }; WockyDataFormFieldOption maxsubs_options[] = { { "10", "10" }, { "20", "20" }, { "30", "30" }, { "50", "50" }, { "100", "100" }, { "None", "none" }, }; stanza = create_bot_creation_form_stanza (); form = wocky_data_form_new_from_form (wocky_stanza_get_top_node (stanza), NULL); g_assert (form != NULL); g_object_unref (stanza); g_assert_cmpstr (wocky_data_form_get_title (form), ==, "My Title"); g_assert_cmpstr (wocky_data_form_get_instructions (form), ==, "Badger"); g_assert_cmpuint (g_slist_length (form->fields_list), ==, 11); for (l = form->fields_list, i = 0; l != NULL; l = g_slist_next (l), i++) { field = l->data; g_assert (field != NULL); g_assert_cmpuint (field->type, ==, expected_types[i].type); g_assert_cmpstr (field->var, ==, expected_types[i].var); g_assert_cmpstr (field->label, ==, expected_types[i].label); g_assert_cmpstr (field->desc, ==, expected_types[i].desc); g_assert (field->required == expected_types[i].required); g_assert (field->value == NULL); } g_assert_cmpuint (g_hash_table_size (form->fields), ==, 10); /* check hidden field */ field = g_hash_table_lookup (form->fields, "FORM_TYPE"); g_assert (field != NULL); g_assert (G_VALUE_TYPE (field->default_value) == G_TYPE_STRING); g_assert_cmpstr (g_value_get_string (field->default_value), ==, "jabber:bot"); g_assert (field->options == NULL); /* check text-single field */ field = g_hash_table_lookup (form->fields, "botname"); g_assert (field != NULL); g_assert (field->default_value == NULL); g_assert (field->options == NULL); /* check implicitly text-single field */ field = g_hash_table_lookup (form->fields, "pseudonym"); g_assert (field != NULL); g_assert (field->default_value == NULL); g_assert (field->options == NULL); /* check text-multi field */ field = g_hash_table_lookup (form->fields, "description"); g_assert (field != NULL); g_assert (field->default_value == NULL); g_assert (field->options == NULL); /* check boolean field */ field = g_hash_table_lookup (form->fields, "public"); g_assert (field != NULL); g_assert (G_VALUE_TYPE (field->default_value) == G_TYPE_BOOLEAN); g_assert (!g_value_get_boolean (field->default_value)); g_assert (field->options == NULL); /* check text-private field */ field = g_hash_table_lookup (form->fields, "password"); g_assert (field != NULL); g_assert (field->default_value == NULL); g_assert (field->options == NULL); /* check list-multi field */ field = g_hash_table_lookup (form->fields, "features"); g_assert (field != NULL); g_assert (G_VALUE_TYPE (field->default_value) == G_TYPE_STRV); strv = g_value_get_boxed (field->default_value); g_assert_cmpuint (g_strv_length (strv), ==, 2); g_assert_cmpstr (strv[0], ==, "news"); g_assert_cmpstr (strv[1], ==, "search"); g_assert_cmpuint (g_slist_length (field->options), ==, 5); for (l = field->options, i = 0; l != NULL; l = g_slist_next (l), i++) { WockyDataFormFieldOption *option = l->data; g_assert_cmpstr (option->value, ==, features_options[i].value); g_assert_cmpstr (option->label, ==, features_options[i].label); } /* check list-single field */ field = g_hash_table_lookup (form->fields, "maxsubs"); g_assert (field != NULL); g_assert (G_VALUE_TYPE (field->default_value) == G_TYPE_STRING); g_assert_cmpstr (g_value_get_string (field->default_value), ==, "20"); g_assert_cmpuint (g_slist_length (field->options), ==, 6); for (l = field->options, i = 0; l != NULL; l = g_slist_next (l), i++) { WockyDataFormFieldOption *option = l->data; g_assert_cmpstr (option->value, ==, maxsubs_options[i].value); g_assert_cmpstr (option->label, ==, maxsubs_options[i].label); } /* check jid-multi field */ field = g_hash_table_lookup (form->fields, "invitelist"); g_assert (field != NULL); g_assert (field->default_value == NULL); g_assert (field->options == NULL); /* check boolean field */ field = g_hash_table_lookup (form->fields, "botjid"); g_assert (field != NULL); g_assert (field->default_value == NULL); g_assert (field->options == NULL); g_object_unref (form); } static void test_raw_value_contents (void) { WockyStanza *stanza; WockyDataForm *form; const gchar *description[] = { "Badger", "Mushroom", "Snake", NULL }; gboolean set_succeeded; /* Create fields without WockyNode to trigger this bug: * https://bugs.freedesktop.org/show_bug.cgi?id=43584 */ form = g_object_new (WOCKY_TYPE_DATA_FORM, NULL); g_assert (form != NULL); /* set text-single field */ set_succeeded = wocky_data_form_set_string (form, "botname", "The Jabber Google Bot", FALSE); g_assert (!set_succeeded); set_succeeded = wocky_data_form_set_string (form, "botname", "The Jabber Google Bot", TRUE); g_assert (set_succeeded); /* set text-multi field */ set_succeeded = wocky_data_form_set_strv (form, "description", description, FALSE); g_assert (!set_succeeded); set_succeeded = wocky_data_form_set_strv (form, "description", description, TRUE); g_assert (set_succeeded); /* set boolean field */ set_succeeded = wocky_data_form_set_boolean (form, "public", FALSE, FALSE); g_assert (!set_succeeded); set_succeeded = wocky_data_form_set_boolean (form, "public", FALSE, TRUE); g_assert (set_succeeded); stanza = wocky_stanza_build ( WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_SET, NULL, NULL, NULL); wocky_data_form_add_to_node (form, wocky_stanza_get_top_node (stanza)); g_object_unref (stanza); g_object_unref (form); } static void test_submit (void) { WockyStanza *stanza; WockyDataForm *form; WockyNode *x; GSList *l; const gchar *description[] = { "Badger", "Mushroom", "Snake", NULL }; const gchar *features[] = { "news", "search", NULL }; const gchar *invitees[] = { "juliet@example.org", "romeo@example.org", NULL }; gboolean set_succeeded; stanza = create_bot_creation_form_stanza (); form = wocky_data_form_new_from_form (wocky_stanza_get_top_node (stanza), NULL); g_assert (form != NULL); g_object_unref (stanza); /* set text-single field */ set_succeeded = wocky_data_form_set_string (form, "botname", "The Jabber Google Bot", FALSE); g_assert (set_succeeded); /* set text-multi field */ set_succeeded = wocky_data_form_set_strv (form, "description", description, FALSE); g_assert (set_succeeded); /* set boolean field */ set_succeeded = wocky_data_form_set_boolean (form, "public", FALSE, FALSE); g_assert (set_succeeded); /* set text-private field */ set_succeeded = wocky_data_form_set_string (form, "password", "S3cr1t", FALSE); g_assert (set_succeeded); /* set list-multi field */ set_succeeded = wocky_data_form_set_strv (form, "features", features, FALSE); g_assert (set_succeeded); /* set list-single field */ set_succeeded = wocky_data_form_set_string (form, "maxsubs", "20", FALSE); g_assert (set_succeeded); /* set jid-multi field */ set_succeeded = wocky_data_form_set_strv (form, "invitelist", invitees, FALSE); g_assert (set_succeeded); /* set jid-single field */ set_succeeded = wocky_data_form_set_string (form, "botjid", "bobot@example.org", FALSE); g_assert (set_succeeded); stanza = wocky_stanza_build ( WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_SET, NULL, NULL, NULL); wocky_data_form_submit (form, wocky_stanza_get_top_node (stanza)); x = wocky_node_get_child_ns (wocky_stanza_get_top_node (stanza), "x", WOCKY_XMPP_NS_DATA); g_assert (x != NULL); g_assert_cmpstr (wocky_node_get_attribute (x, "type"), ==, "submit"); for (l = x->children; l != NULL; l = g_slist_next (l)) { WockyNode *v, *node = l->data; const gchar *var, *type, *value = NULL; g_assert_cmpstr (node->name, ==, "field"); var = wocky_node_get_attribute (node, "var"); g_assert (var != NULL); type = wocky_node_get_attribute (node, "type"); v = wocky_node_get_child (node, "value"); if (v != NULL) value = v->content; if (!wocky_strdiff (var, "FORM_TYPE")) { g_assert_cmpstr (type, ==, "hidden"); g_assert_cmpstr (value, ==, "jabber:bot"); } else if (!wocky_strdiff (var, "botname")) { g_assert_cmpstr (type, ==, "text-single"); g_assert_cmpstr (value, ==, "The Jabber Google Bot"); } else if (!wocky_strdiff (var, "description")) { GSList *m; gboolean badger = FALSE, mushroom = FALSE, snake = FALSE; g_assert_cmpstr (type, ==, "text-multi"); for (m = node->children; m != NULL; m = g_slist_next (m)) { WockyNode *tmp = m->data; g_assert_cmpstr (tmp->name, ==, "value"); if (!wocky_strdiff (tmp->content, "Badger")) badger = TRUE; else if (!wocky_strdiff (tmp->content, "Mushroom")) mushroom = TRUE; else if (!wocky_strdiff (tmp->content, "Snake")) snake = TRUE; else g_assert_not_reached (); } g_assert (badger && mushroom && snake); } else if (!wocky_strdiff (var, "public")) { g_assert_cmpstr (type, ==, "boolean"); g_assert_cmpstr (value, ==, "0"); } else if (!wocky_strdiff (var, "password")) { g_assert_cmpstr (type, ==, "text-private"); g_assert_cmpstr (value, ==, "S3cr1t"); } else if (!wocky_strdiff (var, "features")) { GSList *m; gboolean news = FALSE, search = FALSE; g_assert_cmpstr (type, ==, "list-multi"); for (m = node->children; m != NULL; m = g_slist_next (m)) { WockyNode *tmp = m->data; g_assert_cmpstr (tmp->name, ==, "value"); if (!wocky_strdiff (tmp->content, "news")) news = TRUE; else if (!wocky_strdiff (tmp->content, "search")) search = TRUE; else g_assert_not_reached (); } g_assert (news && search); } else if (!wocky_strdiff (var, "maxsubs")) { g_assert_cmpstr (type, ==, "list-single"); g_assert_cmpstr (value, ==, "20"); } else if (!wocky_strdiff (var, "invitelist")) { GSList *m; gboolean juliet = FALSE, romeo = FALSE; g_assert_cmpstr (type, ==, "jid-multi"); for (m = node->children; m != NULL; m = g_slist_next (m)) { WockyNode *tmp = m->data; g_assert_cmpstr (tmp->name, ==, "value"); if (!wocky_strdiff (tmp->content, "juliet@example.org")) juliet = TRUE; else if (!wocky_strdiff (tmp->content, "romeo@example.org")) romeo = TRUE; else g_assert_not_reached (); } g_assert (juliet && romeo); } else if (!wocky_strdiff (var, "botjid")) { g_assert_cmpstr (type, ==, "jid-single"); g_assert_cmpstr (value, ==, "bobot@example.org"); } else g_assert_not_reached (); } g_object_unref (stanza); g_object_unref (form); } /* Test creating and submitting a form response blindly, without first asking * the server for the form fields. */ static void test_submit_blindly (void) { WockyDataForm *form = g_object_new (WOCKY_TYPE_DATA_FORM, NULL); const gchar * const the_xx[] = { "Romy", "Oliver", "Jamie", NULL }; gboolean succeeded; WockyStanza *stanza, *expected; /* We didn't actually parse a form, so it doesn't have any pre-defined * fields. Thus, the setters should all fail if we don't tell them to create * the fields if missing. */ succeeded = wocky_data_form_set_string (form, "band-name", "The XX", FALSE); g_assert (!succeeded); succeeded = wocky_data_form_set_strv (form, "band-members", the_xx, FALSE); g_assert (!succeeded); succeeded = wocky_data_form_set_boolean (form, "is-meh", TRUE, FALSE); g_assert (!succeeded); g_assert (form->fields_list == NULL); g_assert_cmpuint (0, ==, g_hash_table_size (form->fields)); /* Since the form doesn't have a FORM_TYPE yet, we should be able to set it. */ succeeded = wocky_data_form_set_type (form, "http://example.com/band-info"); g_assert (succeeded); /* But now that it does have one, we shouldn't be able to change it. */ succeeded = wocky_data_form_set_type (form, "stoats"); g_assert (!succeeded); /* If we forcibly create the fields we care about, setting them should * succeed, and they should show up when we submit the form! */ succeeded = wocky_data_form_set_string (form, "band-name", "The XX", TRUE); g_assert (succeeded); succeeded = wocky_data_form_set_strv (form, "band-members", the_xx, TRUE); g_assert (succeeded); succeeded = wocky_data_form_set_boolean (form, "is-meh", TRUE, TRUE); g_assert (succeeded); stanza = wocky_stanza_build (WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_SET, NULL, NULL, NULL); wocky_data_form_submit (form, wocky_stanza_get_top_node (stanza)); expected = wocky_stanza_build (WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_SET, NULL, NULL, '(', "x", ':', WOCKY_XMPP_NS_DATA, '@', "type", "submit", '(', "field", '@', "type", "hidden", '@', "var", "FORM_TYPE", '(', "value", '$', "http://example.com/band-info", ')', ')', '(', "field", '@', "var", "band-name", '(', "value", '$', "The XX", ')', ')', '(', "field", '@', "var", "band-members", '(', "value", '$', "Romy", ')', '(', "value", '$', "Oliver", ')', '(', "value", '$', "Jamie", ')', ')', '(', "field", '@', "var", "is-meh", '(', "value", '$', "1", ')', ')', ')', NULL); test_assert_stanzas_equal_no_id (expected, stanza); g_object_unref (expected); g_object_unref (stanza); g_object_unref (form); } static WockyStanza * create_search_form_stanza (void) { /* This stanza is inspired from Example 6 of XEP-0004: Data Forms */ return wocky_stanza_build ( WOCKY_STANZA_TYPE_IQ,WOCKY_STANZA_SUB_TYPE_RESULT, NULL, NULL, '(', "x", ':', WOCKY_XMPP_NS_DATA, '@', "type", "form", '(', "title", '$', "My Title", ')', '(', "instructions", '$', "Badger", ')', '(', "field", '@', "type", "text-single", '@', "var", "search_request", ')', ')', NULL); } static void test_parse_multi_result (void) { WockyStanza *stanza; WockyDataForm *form; GSList *l; gboolean item1 = FALSE, item2 = FALSE; stanza = create_search_form_stanza (); form = wocky_data_form_new_from_form (wocky_stanza_get_top_node (stanza), NULL); g_assert (form != NULL); g_object_unref (stanza); /* create the result stanza */ stanza = wocky_stanza_build ( WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_RESULT, NULL, NULL, '(', "x", ':', WOCKY_XMPP_NS_DATA, '@', "type", "result", '(', "title", '$', "Search result", ')', '(', "reported", '(', "field", '@', "var", "name", '@', "type", "text-single", ')', '(', "field", '@', "var", "url", '@', "type", "text-single", ')', ')', /* first item */ '(', "item", '(', "field", '@', "var", "name", '(', "value", '$', "name1", ')', ')', '(', "field", '@', "var", "url", '(', "value", '$', "url1", ')', ')', ')', /* second item */ '(', "item", '(', "field", '@', "var", "name", '(', "value", '$', "name2", ')', ')', '(', "field", '@', "var", "url", '(', "value", '$', "url2", ')', ')', ')', ')', NULL); g_assert (wocky_data_form_parse_result (form, wocky_stanza_get_top_node (stanza), NULL)); g_object_unref (stanza); g_assert_cmpuint (g_slist_length (form->results), ==, 2); for (l = form->results; l != NULL; l = g_slist_next (l)) { GSList *result = l->data, *m; gboolean name = FALSE, url = FALSE; for (m = result; m != NULL; m = g_slist_next (m)) { WockyDataFormField *field = m->data; if (!wocky_strdiff (field->var, "name")) { if (!wocky_strdiff (g_value_get_string (field->value), "name1")) item1 = TRUE; else if (!wocky_strdiff (g_value_get_string (field->value), "name2")) item2 = TRUE; else g_assert_not_reached (); name = TRUE; } else if (!wocky_strdiff (field->var, "url")) { if (item2) g_assert_cmpstr (g_value_get_string (field->value), ==, "url2"); else if (item1) g_assert_cmpstr (g_value_get_string (field->value), ==, "url1"); else g_assert_not_reached (); url = TRUE; } else g_assert_not_reached (); } g_assert (name && url); } g_assert (item1 && item2); g_object_unref (form); } static void test_parse_single_result (void) { WockyStanza *stanza; WockyDataForm *form; GSList *result, *l; gboolean form_type = FALSE, botname = FALSE; stanza = create_bot_creation_form_stanza (); form = wocky_data_form_new_from_form (wocky_stanza_get_top_node (stanza), NULL); g_assert (form != NULL); g_object_unref (stanza); /* create the result stanza */ stanza = wocky_stanza_build ( WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_RESULT, NULL, NULL, '(', "x", ':', WOCKY_XMPP_NS_DATA, '@', "type", "result", /* hidden field */ '(', "field", '@', "type", "hidden", '@', "var", "FORM_TYPE", '(', "value", '$', "jabber:bot", ')', ')', /* text-single field */ '(', "field", '@', "type", "text-single", '@', "var", "botname", '(', "value", '$', "The Bot", ')', ')', ')', NULL); g_assert (wocky_data_form_parse_result (form, wocky_stanza_get_top_node (stanza), NULL)); g_object_unref (stanza); g_assert_cmpuint (g_slist_length (form->results), ==, 1); result = form->results->data; g_assert_cmpuint (g_slist_length (result), ==, 2); for (l = result; l != NULL; l = g_slist_next (l)) { WockyDataFormField *field = l->data; if (!wocky_strdiff (field->var, "FORM_TYPE")) { g_assert_cmpstr (g_value_get_string (field->value), ==, "jabber:bot"); g_assert (field->type == WOCKY_DATA_FORM_FIELD_TYPE_HIDDEN); form_type = TRUE; } else if (!wocky_strdiff (field->var, "botname")) { g_assert_cmpstr (g_value_get_string (field->value), ==, "The Bot"); g_assert (field->type == WOCKY_DATA_FORM_FIELD_TYPE_TEXT_SINGLE); botname = TRUE; } else g_assert_not_reached (); } g_assert (form_type && botname); g_object_unref (form); } int main (int argc, char **argv) { int result; test_init (argc, argv); g_test_add_func ("/data-form/instantiation", test_new_from_form); g_test_add_func ("/data-form/parse-form", test_parse_form); g_test_add_func ("/data-form/raw_value_contents", test_raw_value_contents); g_test_add_func ("/data-form/submit", test_submit); g_test_add_func ("/data-form/submit-blindly", test_submit_blindly); g_test_add_func ("/data-form/parse-multi-result", test_parse_multi_result); g_test_add_func ("/data-form/parse-single-result", test_parse_single_result); result = g_test_run (); test_deinit (); return result; } telepathy-gabble-0.18.2/lib/ext/wocky/tests/wocky-contact-factory-test.c0000644000175000017500000001124512200204546026232 0ustar00smcvsmcv00000000000000#ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include #include "wocky-test-helper.h" static void test_instantiation (void) { WockyContactFactory *factory; factory = wocky_contact_factory_new (); g_object_unref (factory); } gboolean add_signal_received = FALSE; static void bare_contact_added (WockyContactFactory *factory, WockyBareContact *contact, gpointer data) { add_signal_received = TRUE; } static void test_ensure_bare_contact (void) { WockyContactFactory *factory; WockyBareContact *juliet, *a, *b; factory = wocky_contact_factory_new (); juliet = wocky_bare_contact_new ("juliet@example.org"); add_signal_received = FALSE; g_signal_connect (factory, "bare-contact-added", G_CALLBACK (bare_contact_added), NULL); a = wocky_contact_factory_ensure_bare_contact (factory, "juliet@example.org"); g_assert (wocky_bare_contact_equal (a, juliet)); g_assert (add_signal_received); b = wocky_contact_factory_ensure_bare_contact (factory, "juliet@example.org"); g_assert (a == b); g_object_unref (factory); g_object_unref (juliet); g_object_unref (a); g_object_unref (b); } static void test_lookup_bare_contact (void) { WockyContactFactory *factory; WockyBareContact *contact; factory = wocky_contact_factory_new (); g_assert (wocky_contact_factory_lookup_bare_contact (factory, "juliet@example.org") == NULL); contact = wocky_contact_factory_ensure_bare_contact (factory, "juliet@example.org"); g_assert (wocky_contact_factory_lookup_bare_contact (factory, "juliet@example.org") != NULL); g_object_unref (contact); /* contact has been disposed and so is not in the factory anymore */ g_assert (wocky_contact_factory_lookup_bare_contact (factory, "juliet@example.org") == NULL); g_object_unref (factory); } static void resource_contact_added (WockyContactFactory *factory, WockyBareContact *contact, gpointer data) { add_signal_received = TRUE; } static void test_ensure_resource_contact (void) { WockyContactFactory *factory; WockyBareContact *juliet, *bare; WockyResourceContact *juliet_balcony, *juliet_pub, *a, *b; GSList *resources; factory = wocky_contact_factory_new (); juliet = wocky_bare_contact_new ("juliet@example.org"); juliet_balcony = wocky_resource_contact_new (juliet, "Balcony"); juliet_pub = wocky_resource_contact_new (juliet, "Pub"); add_signal_received = FALSE; g_signal_connect (factory, "resource-contact-added", G_CALLBACK (resource_contact_added), NULL); /* Bare contact isn't in the factory yet */ a = wocky_contact_factory_ensure_resource_contact (factory, "juliet@example.org/Balcony"); g_assert (wocky_resource_contact_equal (a, juliet_balcony)); g_assert (add_signal_received); bare = wocky_contact_factory_lookup_bare_contact (factory, "juliet@example.org"); g_assert (bare != NULL); /* Resource has been added to the bare contact */ resources = wocky_bare_contact_get_resources (bare); g_assert_cmpuint (g_slist_length (resources), ==, 1); g_assert (g_slist_find (resources, a) != NULL); g_slist_free (resources); /* Bare contact is already in the factory */ b = wocky_contact_factory_ensure_resource_contact (factory, "juliet@example.org/Pub"); g_assert (wocky_resource_contact_equal (b, juliet_pub)); g_object_unref (factory); g_object_unref (juliet); g_object_unref (juliet_balcony); g_object_unref (juliet_pub); g_object_unref (a); g_object_unref (b); } static void test_lookup_resource_contact (void) { WockyContactFactory *factory; WockyResourceContact *contact; factory = wocky_contact_factory_new (); g_assert (wocky_contact_factory_lookup_resource_contact (factory, "juliet@example.org/Balcony") == NULL); contact = wocky_contact_factory_ensure_resource_contact (factory, "juliet@example.org/Balcony"); g_assert (wocky_contact_factory_lookup_resource_contact (factory, "juliet@example.org/Balcony") != NULL); g_object_unref (factory); g_object_unref (contact); } int main (int argc, char **argv) { int result; test_init (argc, argv); g_test_add_func ("/contact-factory/instantiation", test_instantiation); g_test_add_func ("/contact-factory/ensure-bare-contact", test_ensure_bare_contact); g_test_add_func ("/contact-factory/lookup-bare-contact", test_lookup_bare_contact); g_test_add_func ("/contact-factory/ensure-resource-contact", test_ensure_resource_contact); g_test_add_func ("/contact-factory/lookup-resource-contact", test_lookup_resource_contact); result = g_test_run (); test_deinit (); return result; } telepathy-gabble-0.18.2/lib/ext/wocky/tests/test-resolver.h0000644000175000017500000000416612200204546023652 0ustar00smcvsmcv00000000000000/* * test-resolver.c - Source for TestResolver * Copyright © 2009 Collabora Ltd. * @author Vivek Dasmohapatra * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 __TEST_RESOLVER_H__ #define __TEST_RESOLVER_H__ #include #include G_BEGIN_DECLS GType test_resolver_get_type (void); #define TEST_TYPE_RESOLVER (test_resolver_get_type ()) #define TEST_RESOLVER(o) \ (G_TYPE_CHECK_INSTANCE_CAST ((o), TEST_TYPE_RESOLVER, TestResolver)) #define TEST_RESOLVER_CLASS(k) \ (G_TYPE_CHECK_CLASS_CAST((k), TEST_TYPE_RESOLVER, TestResolverClass)) #define TEST_IS_RESOLVER(o) \ (G_TYPE_CHECK_INSTANCE_TYPE ((o), TEST_TYPE_RESOLVER)) #define TEST_IS_RESOLVER_CLASS(k) \ (G_TYPE_CHECK_CLASS_TYPE ((k), TEST_TYPE_RESOLVER)) #define TEST_RESOLVER_GET_CLASS(o) \ (G_TYPE_INSTANCE_GET_CLASS ((o), TEST_TYPE_RESOLVER, TestResolverClass)) typedef struct { GResolver parent_instance; GResolver *real_resolver; GList *fake_A; GList *fake_SRV; } TestResolver; typedef struct { GResolverClass parent_class; } TestResolverClass; void test_resolver_reset (TestResolver *tr); gboolean test_resolver_add_A (TestResolver *tr, const char *hostname, const char *addr); gboolean test_resolver_add_SRV (TestResolver *tr, const char *service, const char *protocol, const char *domain, const char *addr, guint16 port); G_END_DECLS #endif /* __TEST_RESOLVER_H__ */ telepathy-gabble-0.18.2/lib/ext/wocky/tests/test-resolver.c0000644000175000017500000002367012200204546023646 0ustar00smcvsmcv00000000000000/* * test-resolver.c - Source for TestResolver * Copyright © 2009 Collabora Ltd. * @author Vivek Dasmohapatra * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 */ /* this code largely culled from gunixresolver.c in glib and modified to * make a dummy resolver we can insert duff records in on the fly */ /* examples: * GResolver *original; * GResolver *kludged; * original = g_resolver_get_default (); * kludged = g_object_new (TEST_TYPE_RESOLVER, "real-resolver", original, NULL); * g_resolver_set_default (kludged); * test_resolver_add_SRV (TEST_RESOLVER (kludged), * "xmpp-client", "tcp", "jabber.earth.li", "localhost", 1337); * test_resolver_add_A (TEST_RESOLVER (kludged), "localhost", "127.0.1.1"); */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #ifdef G_OS_WIN32 #include #include #else #include #include #include #endif #include "test-resolver.h" #ifdef G_LOG_DOMAIN #undef G_LOG_DOMAIN #endif #define G_LOG_DOMAIN "test-resolver" enum { PROP_REAL_RESOLVER = 1, }; typedef struct _fake_host { char *key; char *addr; } fake_host; typedef struct _fake_serv { char *key; GSrvTarget *srv; } fake_serv; G_DEFINE_TYPE (TestResolver, test_resolver, G_TYPE_RESOLVER); /* ************************************************************************* */ static gchar * _service_rrname (const char *service, const char *protocol, const char *domain) { gchar *rrname, *ascii_domain = NULL; if (g_hostname_is_non_ascii (domain)) domain = ascii_domain = g_hostname_to_ascii (domain); rrname = g_strdup_printf ("_%s._%s.%s", service, protocol, domain); g_free (ascii_domain); return rrname; } static GList * find_fake_services (TestResolver *tr, const char *name) { GList *fake = NULL; GList *rval = NULL; for (fake = tr->fake_SRV; fake; fake = fake->next) { fake_serv *entry = fake->data; if (entry && !g_strcmp0 (entry->key, name)) rval = g_list_append (rval, g_srv_target_copy (entry->srv)); } return rval; } static GList * find_fake_hosts (TestResolver *tr, const char *name) { GList *fake = NULL; GList *rval = NULL; for (fake = tr->fake_A; fake; fake = fake->next) { fake_host *entry = fake->data; if (entry && !g_strcmp0 (entry->key, name)) rval = g_list_append (rval, g_inet_address_new_from_string (entry->addr)); } return rval; } static GList * srv_target_list_copy (GList *addr) { GList *copy = NULL; GList *l; for (l = addr; l != NULL; l = l->next) copy = g_list_prepend (copy, g_srv_target_copy (l->data)); return g_list_reverse (copy); } static void srv_target_list_free (GList *addr) { g_list_foreach (addr, (GFunc) g_srv_target_free, NULL); g_list_free (addr); } static GList * object_list_copy (GList *objs) { g_list_foreach (objs, (GFunc) g_object_ref, NULL); return g_list_copy (objs); } static void object_list_free (GList *objs) { g_list_foreach (objs, (GFunc) g_object_unref, NULL); g_list_free (objs); } static void lookup_service_async (GResolver *resolver, const char *rr, GCancellable *cancellable, GAsyncReadyCallback cb, gpointer data) { TestResolver *tr = TEST_RESOLVER (resolver); GList *addr = find_fake_services (tr, rr); GObject *source = G_OBJECT (resolver); GSimpleAsyncResult *res = NULL; #ifdef DEBUG_FAKEDNS GList *x; #endif if (addr != NULL) { #ifdef DEBUG_FAKEDNS for (x = addr; x; x = x->next) g_debug ("FAKE SRV: addr: %s; port: %d; prio: %d; weight: %d;\n", g_srv_target_get_hostname ((GSrvTarget *) x->data), g_srv_target_get_port ((GSrvTarget *) x->data), g_srv_target_get_priority ((GSrvTarget *) x->data), g_srv_target_get_weight ((GSrvTarget *) x->data)); #endif res = g_simple_async_result_new (source, cb, data, lookup_service_async); } else { res = g_simple_async_result_new_error (source, cb, data, G_RESOLVER_ERROR, G_RESOLVER_ERROR_NOT_FOUND, "No fake SRV record registered"); } g_simple_async_result_set_op_res_gpointer (res, addr, (GDestroyNotify) srv_target_list_free); g_simple_async_result_complete (res); g_object_unref (res); } static GList * lookup_service_finish (GResolver *resolver, GAsyncResult *result, GError **error) { GList *res = NULL; GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (result); if (g_simple_async_result_propagate_error (simple, error)) return NULL; res = g_simple_async_result_get_op_res_gpointer (simple); return srv_target_list_copy (res); } static void lookup_by_name_async (GResolver *resolver, const gchar *hostname, GCancellable *cancellable, GAsyncReadyCallback cb, gpointer data) { TestResolver *tr = TEST_RESOLVER (resolver); GList *addr = find_fake_hosts (tr, hostname); GObject *source = G_OBJECT (resolver); GSimpleAsyncResult *res = NULL; #ifdef DEBUG_FAKEDNS GList *x; char a[32]; #endif if (addr != NULL) { #ifdef DEBUG_FAKEDNS for (x = addr; x; x = x->next) g_debug ("FAKE HOST: addr: %s;\n", inet_ntop (AF_INET, g_inet_address_to_bytes (x->data), a, sizeof (a))); #endif res = g_simple_async_result_new (source, cb, data, lookup_by_name_async); } else { res = g_simple_async_result_new_error (source, cb, data, G_RESOLVER_ERROR, G_RESOLVER_ERROR_NOT_FOUND, "No fake hostname record registered"); } g_simple_async_result_set_op_res_gpointer (res, addr, (GDestroyNotify) object_list_free); g_simple_async_result_complete_in_idle (res); g_object_unref (res); } static GList * lookup_by_name_finish (GResolver *resolver, GAsyncResult *result, GError **error) { GList *res = NULL; GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (result); if (g_simple_async_result_propagate_error (simple, error)) return NULL; res = g_simple_async_result_get_op_res_gpointer (simple); return object_list_copy (res); } /* ************************************************************************* */ static void test_resolver_init (TestResolver *tr) { } static void test_resolver_set_property (GObject *object, guint propid, const GValue *value, GParamSpec *pspec) { TestResolver *resolver = TEST_RESOLVER (object); switch (propid) { case PROP_REAL_RESOLVER: if (resolver->real_resolver != NULL) g_object_unref (resolver->real_resolver); resolver->real_resolver = g_value_dup_object (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, propid, pspec); break; } } static void test_resolver_get_property (GObject *object, guint propid, GValue *value, GParamSpec *pspec) { TestResolver *resolver = TEST_RESOLVER (object); switch (propid) { case PROP_REAL_RESOLVER: g_value_set_object (value, resolver->real_resolver); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, propid, pspec); break; } } static void test_resolver_class_init (TestResolverClass *klass) { GResolverClass *resolver_class = G_RESOLVER_CLASS (klass); GObjectClass *object_class = G_OBJECT_CLASS (klass); GParamSpec *spec; object_class->set_property = test_resolver_set_property; object_class->get_property = test_resolver_get_property; spec = g_param_spec_object ("real-resolver", "real-resolver", "The real resolver to use when we don't have a kludge entry", G_TYPE_RESOLVER, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT); g_object_class_install_property (object_class, PROP_REAL_RESOLVER, spec); resolver_class->lookup_by_name_async = lookup_by_name_async; resolver_class->lookup_by_name_finish = lookup_by_name_finish; resolver_class->lookup_service_async = lookup_service_async; resolver_class->lookup_service_finish = lookup_service_finish; } void test_resolver_reset (TestResolver *tr) { GList *fake = NULL; for (fake = tr->fake_A; fake; fake = fake->next) { fake_host *entry = fake->data; g_free (entry->key); g_free (entry->addr); g_free (entry); } g_list_free (tr->fake_A); tr->fake_A = NULL; for (fake = tr->fake_SRV; fake; fake = fake->next) { fake_serv *entry = fake->data; g_free (entry->key); g_srv_target_free (entry->srv); g_free (entry); } g_list_free (tr->fake_SRV); tr->fake_SRV = NULL; } gboolean test_resolver_add_A (TestResolver *tr, const char *hostname, const char *addr) { fake_host *entry = g_new0( fake_host, 1 ); entry->key = g_strdup (hostname); entry->addr = g_strdup (addr); tr->fake_A = g_list_append (tr->fake_A, entry); return TRUE; } gboolean test_resolver_add_SRV (TestResolver *tr, const char *service, const char *protocol, const char *domain, const char *addr, guint16 port) { char *key = _service_rrname (service, protocol, domain); fake_serv *entry = g_new0 (fake_serv, 1); GSrvTarget *serv = g_srv_target_new (addr, port, 0, 0); entry->key = key; entry->srv = serv; tr->fake_SRV = g_list_append (tr->fake_SRV, entry); return TRUE; } telepathy-gabble-0.18.2/lib/ext/wocky/tests/wocky-test-connector-server.h0000644000175000017500000001412712200204546026437 0ustar00smcvsmcv00000000000000/* * wocky-test-sasl-auth-server.h - Header for TestSaslAuthServer * Copyright (C) 2006 Collabora Ltd. * @author Sjoerd Simons * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 __TEST_CONNECTOR_SERVER_H__ #define __TEST_CONNECTOR_SERVER_H__ #include #include #include "wocky-test-sasl-auth-server.h" G_BEGIN_DECLS #define CONNPROBLEM(x) (0x1 << x) typedef enum { XMPP_PROBLEM_NONE = 0, XMPP_PROBLEM_BAD_XMPP = CONNPROBLEM (0), XMPP_PROBLEM_NO_TLS = CONNPROBLEM (1), XMPP_PROBLEM_TLS_REFUSED = CONNPROBLEM (2), XMPP_PROBLEM_FEATURES = CONNPROBLEM (3), XMPP_PROBLEM_OLD_SERVER = CONNPROBLEM (4), XMPP_PROBLEM_WEAK_SSL = CONNPROBLEM (5), XMPP_PROBLEM_OLD_SSL = CONNPROBLEM (6), XMPP_PROBLEM_OTHER_HOST = CONNPROBLEM (7), XMPP_PROBLEM_TLS_LOAD = CONNPROBLEM (8), XMPP_PROBLEM_NO_SESSION = CONNPROBLEM (10), XMPP_PROBLEM_CANNOT_BIND = CONNPROBLEM (11), XMPP_PROBLEM_OLD_AUTH_FEATURE = CONNPROBLEM (12), XMPP_PROBLEM_SEE_OTHER_HOST = CONNPROBLEM (13), } XmppProblem; typedef enum { BIND_PROBLEM_NONE = 0, BIND_PROBLEM_INVALID = CONNPROBLEM(0), BIND_PROBLEM_DENIED = CONNPROBLEM(1), BIND_PROBLEM_CONFLICT = CONNPROBLEM(2), BIND_PROBLEM_CLASH = CONNPROBLEM(3), BIND_PROBLEM_REJECTED = CONNPROBLEM(4), BIND_PROBLEM_FAILED = CONNPROBLEM(5), BIND_PROBLEM_NO_JID = CONNPROBLEM(6), BIND_PROBLEM_NONSENSE = CONNPROBLEM(7), } BindProblem; typedef enum { SESSION_PROBLEM_NONE = 0, SESSION_PROBLEM_NO_SESSION = CONNPROBLEM(0), SESSION_PROBLEM_FAILED = CONNPROBLEM(1), SESSION_PROBLEM_DENIED = CONNPROBLEM(2), SESSION_PROBLEM_CONFLICT = CONNPROBLEM(3), SESSION_PROBLEM_REJECTED = CONNPROBLEM(4), SESSION_PROBLEM_NONSENSE = CONNPROBLEM(5), } SessionProblem; typedef enum { SERVER_DEATH_NONE = 0, SERVER_DEATH_SERVER_START = CONNPROBLEM(0), SERVER_DEATH_CLIENT_OPEN = CONNPROBLEM(1), SERVER_DEATH_SERVER_OPEN = CONNPROBLEM(2), SERVER_DEATH_FEATURES = CONNPROBLEM(3), SERVER_DEATH_TLS_NEG = CONNPROBLEM(4), } ServerDeath; typedef enum { JABBER_PROBLEM_NONE = 0, JABBER_PROBLEM_AUTH_REJECT = CONNPROBLEM (0), JABBER_PROBLEM_AUTH_BIND = CONNPROBLEM (1), JABBER_PROBLEM_AUTH_PARTIAL = CONNPROBLEM (2), JABBER_PROBLEM_AUTH_FAILED = CONNPROBLEM (3), JABBER_PROBLEM_AUTH_STRANGE = CONNPROBLEM (4), JABBER_PROBLEM_AUTH_NIH = CONNPROBLEM (5), JABBER_PROBLEM_AUTH_NONSENSE = CONNPROBLEM (6), } JabberProblem; typedef enum { XEP77_PROBLEM_NONE = 0, XEP77_PROBLEM_ALREADY = CONNPROBLEM(0), XEP77_PROBLEM_FAIL_CONFLICT = CONNPROBLEM(1), XEP77_PROBLEM_FAIL_REJECTED = CONNPROBLEM(2), XEP77_PROBLEM_NOT_AVAILABLE = CONNPROBLEM(3), XEP77_PROBLEM_QUERY_NONSENSE = CONNPROBLEM(4), XEP77_PROBLEM_QUERY_ALREADY = CONNPROBLEM(5), XEP77_PROBLEM_NO_ARGS = CONNPROBLEM(6), XEP77_PROBLEM_EMAIL_ARG = CONNPROBLEM(7), XEP77_PROBLEM_STRANGE_ARG = CONNPROBLEM(8), XEP77_PROBLEM_CANCEL_REJECTED = CONNPROBLEM(9), XEP77_PROBLEM_CANCEL_DISABLED = CONNPROBLEM(10), XEP77_PROBLEM_CANCEL_FAILED = CONNPROBLEM(11), XEP77_PROBLEM_CANCEL_STREAM = CONNPROBLEM(12), } XEP77Problem; typedef enum { CERT_STANDARD, CERT_EXPIRED, CERT_NOT_YET, CERT_UNKNOWN, CERT_SELFSIGN, CERT_REVOKED, CERT_WILDCARD, CERT_BADWILD, CERT_NONE, } CertSet; typedef struct { XmppProblem xmpp; BindProblem bind; SessionProblem session; ServerDeath death; JabberProblem jabber; XEP77Problem xep77; } ConnectorProblem; typedef struct _TestConnectorServer TestConnectorServer; typedef struct _TestConnectorServerClass TestConnectorServerClass; typedef struct _TestConnectorServerPrivate TestConnectorServerPrivate; struct _TestConnectorServerClass { GObjectClass parent_class; }; struct _TestConnectorServer { GObject parent; TestConnectorServerPrivate *priv; }; GType test_connector_server_get_type (void); /* TYPE MACROS */ #define TEST_TYPE_CONNECTOR_SERVER \ (test_connector_server_get_type ()) #define TEST_CONNECTOR_SERVER(obj) \ (G_TYPE_CHECK_INSTANCE_CAST((obj), TEST_TYPE_CONNECTOR_SERVER, \ TestConnectorServer)) #define TEST_CONNECTOR_SERVER_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST((klass), TEST_TYPE_CONNECTOR_SERVER, \ TestConnectorServerClass)) #define TEST_IS_CONNECTOR_SERVER(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE((obj), TEST_TYPE_CONNECTOR_SERVER)) #define TEST_IS_CONNECTOR_SERVER_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE((klass), TEST_TYPE_CONNECTOR_SERVER)) #define TEST_CONNECTOR_SERVER_GET_CLASS(obj) \ (G_TYPE_INSTANCE_GET_CLASS ((obj), TEST_TYPE_CONNECTOR_SERVER, \ TestConnectorServerClass)) TestConnectorServer * test_connector_server_new (GIOStream *stream, gchar *mech, const gchar *user, const gchar *pass, const gchar *version, ConnectorProblem *problem, ServerProblem sasl_problem, CertSet cert); void test_connector_server_start (TestConnectorServer *self); void test_connector_server_set_other_host (TestConnectorServer *self, const gchar *host, guint port); void test_connector_server_teardown (TestConnectorServer *self, GAsyncReadyCallback callback, gpointer user_data); gboolean test_connector_server_teardown_finish (TestConnectorServer *self, GAsyncResult *result, GError *error); const gchar *test_connector_server_get_used_mech (TestConnectorServer *self); G_END_DECLS #endif /* #ifndef __TEST_CONNECTOR_SERVER_H__*/ telepathy-gabble-0.18.2/lib/ext/wocky/tests/wocky-test-connector-server.c0000644000175000017500000014111212200204546026425 0ustar00smcvsmcv00000000000000/* * wocky-test-connector-server.c - Source for TestConnectorServer * Copyright © 2009 Collabora Ltd. * @author Vivek Dasmohapatra * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 "config.h" #include #include #include #include #include #include #include "wocky-test-connector-server.h" #include #define INITIAL_STREAM_ID "0-HAI" /* We're being a bit naughty here by including wocky-debug.h, but we're * internal *enough*. */ #define WOCKY_DEBUG_FLAG WOCKY_DEBUG_CONNECTOR #define WOCKY_COMPILATION #include #undef WOCKY_COMPILATION G_DEFINE_TYPE (TestConnectorServer, test_connector_server, G_TYPE_OBJECT); typedef void (*stanza_func)(TestConnectorServer *self, WockyStanza *xml); typedef struct _stanza_handler stanza_handler; struct _stanza_handler { const gchar *ns; const gchar *name; stanza_func func; }; typedef struct _iq_handler iq_handler; struct _iq_handler { WockyStanzaSubType subtype; const gchar *payload; const gchar *ns; stanza_func func; }; static void xmpp_init (GObject *source, GAsyncResult *result, gpointer data); static void starttls (GObject *source, GAsyncResult *result, gpointer data); static void finished (GObject *source, GAsyncResult *, gpointer data); static void quit (GObject *source, GAsyncResult *result, gpointer data); static void server_enc_outstanding (TestConnectorServer *self); static gboolean server_dec_outstanding (TestConnectorServer *self); /* ************************************************************************* */ /* test connector server object definition */ typedef enum { SERVER_STATE_START, SERVER_STATE_CLIENT_OPENED, SERVER_STATE_SERVER_OPENED, SERVER_STATE_FEATURES_SENT } server_state; static struct { CertSet set; const gchar *key; const gchar *crt; } certs[] = { { CERT_STANDARD, TLS_SERVER_KEY_FILE, TLS_SERVER_CRT_FILE }, { CERT_EXPIRED, TLS_EXP_KEY_FILE, TLS_EXP_CRT_FILE }, { CERT_NOT_YET, TLS_NEW_KEY_FILE, TLS_NEW_CRT_FILE }, { CERT_UNKNOWN, TLS_UNKNOWN_KEY_FILE, TLS_UNKNOWN_CRT_FILE }, { CERT_SELFSIGN, TLS_SS_KEY_FILE, TLS_SS_CRT_FILE }, { CERT_REVOKED, TLS_REV_KEY_FILE, TLS_REV_CRT_FILE }, { CERT_WILDCARD, TLS_WILD_KEY_FILE, TLS_WILD_CRT_FILE }, { CERT_BADWILD, TLS_BADWILD_KEY_FILE, TLS_BADWILD_CRT_FILE }, { CERT_NONE, NULL, NULL } }; struct _TestConnectorServerPrivate { gboolean dispose_has_run; WockyXmppConnection *conn; GIOStream *stream; server_state state; gboolean tls_started; gboolean authed; TestSaslAuthServer *sasl; gchar *mech; gchar *user; gchar *pass; gchar *version; gchar *used_mech; CertSet cert; WockyTLSSession *tls_sess; GCancellable *cancellable; gint outstanding; GSimpleAsyncResult *teardown_result; struct { ServerProblem sasl; ConnectorProblem *connector; } problem; gchar *other_host; guint other_port; }; static void test_connector_server_dispose (GObject *object) { TestConnectorServer *self = TEST_CONNECTOR_SERVER (object); TestConnectorServerPrivate *priv = self->priv; if (priv->dispose_has_run) return; priv->dispose_has_run = TRUE; /* release any references held by the object here */ if (priv->conn != NULL) g_object_unref (priv->conn); priv->conn = NULL; if (priv->stream != NULL) g_object_unref (priv->stream); priv->stream = NULL; if (priv->sasl != NULL) g_object_unref (priv->sasl); priv->sasl = NULL; if (priv->tls_sess != NULL) g_object_unref (priv->tls_sess); priv->tls_sess = NULL; if (G_OBJECT_CLASS (test_connector_server_parent_class)->dispose) G_OBJECT_CLASS (test_connector_server_parent_class)->dispose (object); } static void test_connector_server_finalise (GObject *object) { TestConnectorServer *self = TEST_CONNECTOR_SERVER (object); TestConnectorServerPrivate *priv = self->priv; /* free any data held directly by the object here */ g_free (priv->mech); g_free (priv->user); g_free (priv->pass); g_free (priv->version); g_free (priv->used_mech); G_OBJECT_CLASS (test_connector_server_parent_class)->finalize (object); } static void test_connector_server_init (TestConnectorServer *self) { TestConnectorServerPrivate *priv; self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, TEST_TYPE_CONNECTOR_SERVER, TestConnectorServerPrivate); priv = self->priv; priv->tls_started = FALSE; priv->authed = FALSE; priv->cancellable = g_cancellable_new (); } static void test_connector_server_class_init (TestConnectorServerClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); g_type_class_add_private (klass, sizeof (TestConnectorServerPrivate)); object_class->dispose = test_connector_server_dispose; object_class->finalize = test_connector_server_finalise; } /* ************************************************************************* */ /* xmpp stanza handling: */ static void xmpp_handler (GObject *source, GAsyncResult *result, gpointer user_data); static void handle_auth (TestConnectorServer *self, WockyStanza *xml); static void handle_starttls (TestConnectorServer *self, WockyStanza *xml); static void after_auth (GObject *source, GAsyncResult *res, gpointer data); static void xmpp_close (GObject *source, GAsyncResult *result, gpointer data); static void xmpp_closed (GObject *source, GAsyncResult *result, gpointer data); static void iq_get_query_JABBER_AUTH (TestConnectorServer *self, WockyStanza *xml); static void iq_set_query_JABBER_AUTH (TestConnectorServer *self, WockyStanza *xml); static void iq_set_bind_XMPP_BIND (TestConnectorServer *self, WockyStanza *xml); static void iq_set_session_XMPP_SESSION (TestConnectorServer *self, WockyStanza *xml); static void iq_get_query_XEP77_REGISTER (TestConnectorServer *self, WockyStanza *xml); static void iq_set_query_XEP77_REGISTER (TestConnectorServer *self, WockyStanza *xml); static void iq_sent (GObject *source, GAsyncResult *result, gpointer data); static void iq_sent_unregistered (GObject *source, GAsyncResult *result, gpointer data); #define HANDLER(ns,x) { WOCKY_XMPP_NS_##ns, #x, handle_##x } static stanza_handler handlers[] = { HANDLER (SASL_AUTH, auth), HANDLER (TLS, starttls), { NULL, NULL, NULL } }; #define IQH(S,s,name,nsp,ns) \ { WOCKY_STANZA_SUB_TYPE_##S, #name, WOCKY_##nsp##_NS_##ns, \ iq_##s##_##name##_##nsp##_##ns } static iq_handler iq_handlers[] = { IQH (SET, set, bind, XMPP, BIND), IQH (SET, set, session, XMPP, SESSION), IQH (GET, get, query, JABBER, AUTH), IQH (SET, set, query, JABBER, AUTH), IQH (GET, get, query, XEP77, REGISTER), IQH (SET, set, query, XEP77, REGISTER), { WOCKY_STANZA_SUB_TYPE_NONE, NULL, NULL, NULL } }; /* ************************************************************************* */ /* error stanza */ static WockyStanza * error_stanza (const gchar *cond, const gchar *msg, gboolean extended) { WockyStanza *error = wocky_stanza_new ("error", WOCKY_XMPP_NS_STREAM); WockyNode *node = wocky_stanza_get_top_node (error); wocky_node_add_child_ns (node, cond, WOCKY_XMPP_NS_STREAMS); if ((msg != NULL) && (*msg != '\0')) wocky_node_add_child_with_content_ns (node, "text", msg, WOCKY_XMPP_NS_STREAMS); if (extended) wocky_node_add_child_with_content_ns (node, "something", "blah", "urn:ietf:a:namespace:I:made:up"); return error; } /* ************************************************************************* */ static void iq_set_query_XEP77_REGISTER (TestConnectorServer *self, WockyStanza *xml) { TestConnectorServerPrivate *priv = self->priv; WockyXmppConnection *conn = priv->conn; WockyStanza *iq = NULL; WockyNode *env = wocky_stanza_get_top_node (xml); WockyNode *query = wocky_node_get_child (env, "query"); const gchar *id = wocky_node_get_attribute (env, "id"); gpointer cb = iq_sent; DEBUG (""); if (priv->problem.connector->xep77 & XEP77_PROBLEM_ALREADY) { iq = wocky_stanza_build (WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_RESULT, NULL, NULL, '@', "id", id, '(', "query", ':', WOCKY_XEP77_NS_REGISTER, '(', "registered", ')', '(', "username", '$', "foo", ')', '(', "password", '$', "bar", ')', ')', NULL); } else if (priv->problem.connector->xep77 & XEP77_PROBLEM_FAIL_CONFLICT) { iq = wocky_stanza_build (WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_ERROR, NULL, NULL, '@', "id", id, '(', "error", '@', "type", "cancel", '(', "conflict", ':', WOCKY_XMPP_NS_STANZAS, ')', ')', NULL); } else if (priv->problem.connector->xep77 & XEP77_PROBLEM_FAIL_REJECTED) { iq = wocky_stanza_build (WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_ERROR, NULL, NULL, '@', "id", id, '(', "error", '@', "type", "modify", '(', "not-acceptable", ':', WOCKY_XMPP_NS_STANZAS, ')', ')', NULL); } else { if (wocky_node_get_child (query, "remove") == NULL) { iq = wocky_stanza_build (WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_RESULT, NULL, NULL, '@', "id", id, NULL); } else { XEP77Problem problem = priv->problem.connector->xep77; XEP77Problem p = XEP77_PROBLEM_NONE; DEBUG ("handling CANCEL"); if ((p = problem & XEP77_PROBLEM_CANCEL_REJECTED) || (p = problem & XEP77_PROBLEM_CANCEL_DISABLED) || (p = problem & XEP77_PROBLEM_CANCEL_FAILED)) { const gchar *error = NULL; const gchar *etype = NULL; const gchar *ecode = NULL; switch (p) { case XEP77_PROBLEM_CANCEL_REJECTED: error = "bad-request"; etype = "modify"; ecode = "400"; break; case XEP77_PROBLEM_CANCEL_DISABLED: error = "not-allowed"; etype = "cancel"; ecode = "405"; break; default: error = "forbidden"; etype = "cancel"; ecode = "401"; } DEBUG ("error: %s/%s", error, etype); iq = wocky_stanza_build (WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_ERROR, NULL, NULL, '@', "id", id, '(', "error", '@', "type", etype, '@', "code", ecode, '(', error, ':', WOCKY_XMPP_NS_STANZAS, ')', ')', NULL); } else { if (priv->problem.connector->xep77 & XEP77_PROBLEM_CANCEL_STREAM) { iq = error_stanza ("not-authorized", NULL, FALSE); cb = finished; } else { cb = iq_sent_unregistered; iq = wocky_stanza_build (WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_RESULT, NULL, NULL, '@', "id", id, NULL); } } } } server_enc_outstanding (self); wocky_xmpp_connection_send_stanza_async (conn, iq, priv->cancellable, cb, self); g_object_unref (xml); g_object_unref (iq); } static void iq_get_query_XEP77_REGISTER (TestConnectorServer *self, WockyStanza *xml) { TestConnectorServerPrivate *priv = self->priv; WockyXmppConnection *conn = priv->conn; WockyStanza *iq = NULL; WockyNode *env = wocky_stanza_get_top_node (xml); WockyNode *query = NULL; const gchar *id = wocky_node_get_attribute (env, "id"); DEBUG (""); if (priv->problem.connector->xep77 & XEP77_PROBLEM_NOT_AVAILABLE) { iq = wocky_stanza_build (WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_ERROR, NULL, NULL, '@', "id", id, '(', "error", '@', "type", "cancel", '(', "service-unavailable", ':', WOCKY_XMPP_NS_STANZAS, ')', ')', NULL); } else if (priv->problem.connector->xep77 & XEP77_PROBLEM_QUERY_NONSENSE) { iq = wocky_stanza_build (WOCKY_STANZA_TYPE_MESSAGE, WOCKY_STANZA_SUB_TYPE_NONE, NULL, NULL, '@', "id", id, '(', "plankton", ':', WOCKY_XEP77_NS_REGISTER, ')', NULL); } else if (priv->problem.connector->xep77 & XEP77_PROBLEM_QUERY_ALREADY) { iq = wocky_stanza_build (WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_RESULT, NULL, NULL, '@', "id", id, '(', "query", ':', WOCKY_XEP77_NS_REGISTER, '(', "registered", ')', '(', "username", '$', "foo", ')', '(', "password", '$', "bar", ')', ')', NULL); } else { iq = wocky_stanza_build (WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_RESULT, NULL, NULL, '@', "id", id, '(', "query", ':', WOCKY_XEP77_NS_REGISTER, '*', &query, ')', NULL); if (!(priv->problem.connector->xep77 & XEP77_PROBLEM_NO_ARGS)) { wocky_node_add_child (query, "username"); wocky_node_add_child (query, "password"); if (priv->problem.connector->xep77 & XEP77_PROBLEM_EMAIL_ARG) wocky_node_add_child (query, "email"); if (priv->problem.connector->xep77 & XEP77_PROBLEM_STRANGE_ARG) wocky_node_add_child (query, "wildebeest"); } } server_enc_outstanding (self); wocky_xmpp_connection_send_stanza_async (conn, iq, NULL, iq_sent, self); g_object_unref (xml); g_object_unref (iq); } static void iq_get_query_JABBER_AUTH (TestConnectorServer *self, WockyStanza *xml) { TestConnectorServerPrivate *priv = self->priv; WockyXmppConnection *conn = priv->conn; WockyStanza *iq = NULL; WockyNode *env = wocky_stanza_get_top_node (xml); const gchar *id = wocky_node_get_attribute (env, "id"); WockyNode *query = wocky_node_get_child (env, "query"); WockyNode *user = (query != NULL) ? wocky_node_get_child (query, "username") : NULL; const gchar *name = (user != NULL) ? user->content : NULL; DEBUG (""); if (priv->problem.connector->jabber & JABBER_PROBLEM_AUTH_NIH) { iq = wocky_stanza_build (WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_ERROR, NULL, NULL, '@', "id", id, '(', "error", '@', "type", "cancel", '(', "service-unavailable", ':', WOCKY_XMPP_NS_STANZAS, ')', ')', NULL); } else if (name == NULL || *name == '\0') { iq = wocky_stanza_build (WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_ERROR, NULL, NULL, '@', "id", id, '(', "error", '@', "type", "modify", '(', "not-acceptable", ':', WOCKY_XMPP_NS_STANZAS, ')', '(', "text", ':', WOCKY_XMPP_NS_STANZAS, '$', "You must include the username in the initial IQ get to work " "around a bug in jabberd 1.4. See " "https://bugs.freedesktop.org/show_bug.cgi?id=24013", ')', ')', NULL); } else if (priv->mech != NULL) { iq = wocky_stanza_build (WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_RESULT, NULL, NULL, '@', "id", id, '(', "query", ':', WOCKY_JABBER_NS_AUTH, '(', "username", ')', '(', priv->mech, ')', '(', "resource", ')', ')', NULL); } else { iq = wocky_stanza_build (WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_RESULT, NULL, NULL, '@', "id", id, '(', "query", ':', WOCKY_JABBER_NS_AUTH, '(', "username", ')', '(', "password", ')', '(', "resource", ')', '(', "digest", ')', ')', NULL); } DEBUG ("responding to iq get"); server_enc_outstanding (self); wocky_xmpp_connection_send_stanza_async (conn, iq, priv->cancellable, iq_sent, self); DEBUG ("sent iq get response"); g_object_unref (xml); g_object_unref (iq); } static void iq_set_query_JABBER_AUTH (TestConnectorServer *self, WockyStanza *xml) { TestConnectorServerPrivate *priv = self->priv; WockyXmppConnection *conn = priv->conn; WockyStanza *iq = NULL; WockyNode *env = wocky_stanza_get_top_node (xml); WockyNode *qry = wocky_node_get_child (env, "query"); JabberProblem problems = priv->problem.connector->jabber; JabberProblem jp = JABBER_PROBLEM_NONE; WockyNode *username = wocky_node_get_child (qry, "username"); WockyNode *password = wocky_node_get_child (qry, "password"); WockyNode *resource = wocky_node_get_child (qry, "resource"); WockyNode *sha1hash = wocky_node_get_child (qry, "digest"); const gchar *id = wocky_node_get_attribute (env, "id"); DEBUG (""); if (username == NULL || resource == NULL) problems |= JABBER_PROBLEM_AUTH_PARTIAL; else if (password != NULL) { if (wocky_strdiff (priv->user, username->content) || wocky_strdiff (priv->pass, password->content)) problems |= JABBER_PROBLEM_AUTH_REJECT; } else if (sha1hash != NULL) { gchar *hsrc = g_strconcat (INITIAL_STREAM_ID, priv->pass, NULL); gchar *sha1 = g_compute_checksum_for_string (G_CHECKSUM_SHA1, hsrc, -1); DEBUG ("checksum: %s vs %s", sha1, sha1hash->content); if (wocky_strdiff (priv->user, username->content) || wocky_strdiff (sha1, sha1hash->content)) problems |= JABBER_PROBLEM_AUTH_REJECT; g_free (hsrc); g_free (sha1); } else problems |= JABBER_PROBLEM_AUTH_PARTIAL; if ((jp = problems & JABBER_PROBLEM_AUTH_REJECT) || (jp = problems & JABBER_PROBLEM_AUTH_BIND) || (jp = problems & JABBER_PROBLEM_AUTH_PARTIAL) || (jp = problems & JABBER_PROBLEM_AUTH_FAILED)) { const gchar *error = NULL; const gchar *etype = NULL; const gchar *ecode = NULL; switch (jp) { case JABBER_PROBLEM_AUTH_REJECT: error = "not-authorized"; etype = "auth"; ecode = "401"; break; case JABBER_PROBLEM_AUTH_BIND: error = "conflict"; etype = "cancel"; ecode = "409"; break; case JABBER_PROBLEM_AUTH_PARTIAL: error = "not-acceptable"; etype = "modify"; ecode = "406"; break; default: error = "bad-request"; etype = "modify"; ecode = "500"; break; } DEBUG ("error: %s/%s", error, etype); iq = wocky_stanza_build (WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_ERROR, NULL, NULL, '@', "id", id, '(', "error", '@', "type", etype, '@', "code", ecode, '(', error, ':', WOCKY_XMPP_NS_STANZAS, ')', ')', NULL); } else if (problems & JABBER_PROBLEM_AUTH_STRANGE) { DEBUG ("auth WEIRD"); iq = wocky_stanza_build (WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_SET, NULL, NULL, '@', "id", id, '(', "surstromming", ':', WOCKY_XMPP_NS_BIND, ')', NULL); } else if (problems & JABBER_PROBLEM_AUTH_NONSENSE) { DEBUG ("auth NONSENSE"); iq = wocky_stanza_build (WOCKY_STANZA_TYPE_MESSAGE, WOCKY_STANZA_SUB_TYPE_NONE, NULL, NULL, '@', "id", id, '(', "surstromming", ':', WOCKY_XMPP_NS_BIND, ')', NULL); } else { DEBUG ("auth OK"); iq = wocky_stanza_build (WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_RESULT, NULL, NULL, '@', "id", id, NULL); } server_enc_outstanding (self); wocky_xmpp_connection_send_stanza_async (conn, iq, priv->cancellable, iq_sent, self); g_object_unref (iq); g_object_unref (xml); } static void iq_set_bind_XMPP_BIND (TestConnectorServer *self, WockyStanza *xml) { TestConnectorServerPrivate *priv = self->priv; WockyXmppConnection *conn = priv->conn; WockyStanza *iq = NULL; BindProblem problems = priv->problem.connector->bind; BindProblem bp = BIND_PROBLEM_NONE; DEBUG(""); if ((bp = problems & BIND_PROBLEM_INVALID) || (bp = problems & BIND_PROBLEM_DENIED) || (bp = problems & BIND_PROBLEM_CONFLICT) || (bp = problems & BIND_PROBLEM_REJECTED)) { const gchar *error = NULL; const gchar *etype = NULL; switch (bp) { case BIND_PROBLEM_INVALID: error = "bad-request"; etype = "modify"; break; case BIND_PROBLEM_DENIED: error = "not-allowed"; etype = "cancel"; break; case BIND_PROBLEM_CONFLICT: error = "conflict"; etype = "cancel"; break; default: error = "badger-badger-badger-mushroom"; etype = "moomins"; } iq = wocky_stanza_build (WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_ERROR, NULL, NULL, '(', "bind", ':', WOCKY_XMPP_NS_BIND, ')', '(', "error", '@', "type", etype, '(', error, ':', WOCKY_XMPP_NS_STANZAS, ')', ')', NULL); } else if (problems & BIND_PROBLEM_FAILED) { /* deliberately nonsensical response */ iq = wocky_stanza_build (WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_SET, NULL, NULL, '(', "bind", ':', WOCKY_XMPP_NS_BIND, ')', NULL); } else if (problems & BIND_PROBLEM_NONSENSE) { /* deliberately nonsensical response */ iq = wocky_stanza_build (WOCKY_STANZA_TYPE_MESSAGE, WOCKY_STANZA_SUB_TYPE_NONE, NULL, NULL, '(', "bind", ':', WOCKY_XMPP_NS_BIND, ')', NULL); } else if (problems & BIND_PROBLEM_CLASH) { iq = error_stanza ("conflict", NULL, FALSE); } else { WockyNode *ciq = wocky_stanza_get_top_node (xml); WockyNode *bind = wocky_node_get_child_ns (ciq, "bind", WOCKY_XMPP_NS_BIND); WockyNode *res = wocky_node_get_child (bind, "resource"); const gchar *uniq = NULL; gchar *jid = NULL; if (res != NULL) uniq = res->content; if (uniq == NULL) uniq = "a-made-up-resource"; if (problems & BIND_PROBLEM_NO_JID) { iq = wocky_stanza_build (WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_RESULT, NULL, NULL, '(', "bind", ':', WOCKY_XMPP_NS_BIND, ')', NULL); } else { jid = g_strdup_printf ("user@some.doma.in/%s", uniq); iq = wocky_stanza_build (WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_RESULT, NULL, NULL, '(', "bind", ':', WOCKY_XMPP_NS_BIND, '(', "jid", '$', jid, ')', ')', NULL); g_free (jid); } } server_enc_outstanding (self); wocky_xmpp_connection_send_stanza_async (conn, iq, priv->cancellable, iq_sent, self); g_object_unref (xml); g_object_unref (iq); } static void iq_set_session_XMPP_SESSION (TestConnectorServer *self, WockyStanza *xml) { TestConnectorServerPrivate *priv = self->priv; WockyXmppConnection *conn = priv->conn; WockyStanza *iq = NULL; SessionProblem problems = priv->problem.connector->session; SessionProblem sp = SESSION_PROBLEM_NONE; DEBUG (""); if ((sp = problems & SESSION_PROBLEM_FAILED) || (sp = problems & SESSION_PROBLEM_DENIED) || (sp = problems & SESSION_PROBLEM_CONFLICT) || (sp = problems & SESSION_PROBLEM_REJECTED)) { const gchar *error = NULL; const gchar *etype = NULL; switch (sp) { case SESSION_PROBLEM_FAILED: error = "internal-server-error"; etype = "wait"; break; case SESSION_PROBLEM_DENIED: error = "forbidden"; etype = "auth"; break; case SESSION_PROBLEM_CONFLICT: error = "conflict"; etype = "cancel"; break; default: error = "snaaaaake"; etype = "mushroom"; break; } iq = wocky_stanza_build (WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_ERROR, NULL, NULL, '(', "session", ':', WOCKY_XMPP_NS_SESSION, ')', '(', "error", '@', "type", etype, '(', error, ':', WOCKY_XMPP_NS_STANZAS, ')', ')', NULL); } else if (problems & SESSION_PROBLEM_NO_SESSION) { iq = error_stanza ("resource-constraint", "Out of Cheese Error", FALSE); } else if (problems & SESSION_PROBLEM_NONSENSE) { /* deliberately nonsensical response */ iq = wocky_stanza_build (WOCKY_STANZA_TYPE_MESSAGE, WOCKY_STANZA_SUB_TYPE_NONE, NULL, NULL, '(', "surstromming", ':', WOCKY_XMPP_NS_BIND, ')', NULL); } else { iq = wocky_stanza_build (WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_RESULT, NULL, NULL, '(', "session", ':', WOCKY_XMPP_NS_SESSION, ')', NULL); } server_enc_outstanding (self); wocky_xmpp_connection_send_stanza_async (conn, iq, NULL, iq_sent, self); g_object_unref (xml); g_object_unref (iq); } static void iq_sent_unregistered (GObject *source, GAsyncResult *result, gpointer data) { GError *error = NULL; TestConnectorServer *self = TEST_CONNECTOR_SERVER (data); TestConnectorServerPrivate *priv = self->priv; WockyXmppConnection *conn = priv->conn; WockyStanza *es = NULL; DEBUG(""); if (!wocky_xmpp_connection_send_stanza_finish (conn, result, &error)) { DEBUG ("send iq response failed: %s", error->message); g_error_free (error); server_dec_outstanding (self); return; } if (server_dec_outstanding (self)) return; es = error_stanza ("not-authorized", NULL, FALSE); server_enc_outstanding (self); wocky_xmpp_connection_send_stanza_async (conn, es, priv->cancellable, finished, self); g_object_unref (es); } static void iq_sent (GObject *source, GAsyncResult *result, gpointer data) { GError *error = NULL; TestConnectorServer *self = TEST_CONNECTOR_SERVER (data); TestConnectorServerPrivate *priv = self->priv; WockyXmppConnection *conn = priv->conn; DEBUG(""); if (!wocky_xmpp_connection_send_stanza_finish (conn, result, &error)) { DEBUG ("send iq response failed: %s", error->message); g_error_free (error); server_dec_outstanding (self); return; } if (server_dec_outstanding (self)) return; server_enc_outstanding (self); DEBUG ("waiting for next stanza from client"); wocky_xmpp_connection_recv_stanza_async (conn, priv->cancellable, xmpp_handler, data); } static void handle_auth (TestConnectorServer *self, WockyStanza *xml) { TestConnectorServerPrivate *priv = self->priv; GObject *sasl = G_OBJECT (priv->sasl); DEBUG (""); /* after this the sasl auth server object is in charge: control should return to us after the auth stages, at the point when we need to send our final feature stanza: the stream does not return to us */ /* this will also unref *xml when it has finished with it */ server_enc_outstanding (self); test_sasl_auth_server_auth_async (sasl, priv->conn, xml, after_auth, priv->cancellable, self); } static void handle_starttls (TestConnectorServer *self, WockyStanza *xml) { TestConnectorServerPrivate *priv = self->priv; DEBUG (""); if (!priv->tls_started) { WockyXmppConnection *conn = priv->conn; ConnectorProblem *problem = priv->problem.connector; WockyStanza *reply = NULL; GAsyncReadyCallback cb = finished; if (problem->xmpp & XMPP_PROBLEM_TLS_LOAD) { reply = error_stanza ("resource-constraint", "Load Too High", FALSE); } else if (problem->xmpp & XMPP_PROBLEM_TLS_REFUSED) { reply = wocky_stanza_new ("failure", WOCKY_XMPP_NS_TLS); } else { reply = wocky_stanza_new ("proceed", WOCKY_XMPP_NS_TLS); cb = starttls; /* set up the tls server session */ /* gnutls_global_set_log_function ((gnutls_log_func)debug_gnutls); * gnutls_global_set_log_level (10); */ if (problem->death & SERVER_DEATH_TLS_NEG) priv->tls_sess = wocky_tls_session_server_new (priv->stream, 1024, NULL, NULL); else { int x; const gchar *key = TLS_SERVER_KEY_FILE; const gchar *crt = TLS_SERVER_CRT_FILE; for (x = 0; certs[x].set != CERT_NONE; x++) { if (certs[x].set == priv->cert) { key = certs[x].key; crt = certs[x].crt; break; } } DEBUG ("cert file: %s", crt); priv->tls_sess = wocky_tls_session_server_new (priv->stream, 1024, key, crt); } } server_enc_outstanding (self); wocky_xmpp_connection_send_stanza_async (conn, reply, NULL, cb, self); g_object_unref (reply); } g_object_unref (xml); } static void finished (GObject *source, GAsyncResult *result, gpointer data) { TestConnectorServer *self = TEST_CONNECTOR_SERVER (data); TestConnectorServerPrivate *priv = self->priv; DEBUG (""); if (server_dec_outstanding (self)) return; server_enc_outstanding (self); wocky_xmpp_connection_send_close_async (priv->conn, priv->cancellable, quit, data); } static void quit (GObject *source, GAsyncResult *result, gpointer data) { TestConnectorServer *self = TEST_CONNECTOR_SERVER (data); TestConnectorServerPrivate *priv = self->priv; DEBUG (""); wocky_xmpp_connection_send_close_finish (priv->conn, result, NULL); server_dec_outstanding (self); } static void handshake_cb (GObject *source, GAsyncResult *result, gpointer user_data) { TestConnectorServer *self = TEST_CONNECTOR_SERVER (user_data); TestConnectorServerPrivate *priv = self->priv; WockyTLSConnection *tls_conn; GError *error = NULL; DEBUG ("TLS/SSL handshake finished"); tls_conn = wocky_tls_session_handshake_finish ( WOCKY_TLS_SESSION (source), result, &error); if (server_dec_outstanding (self)) goto out; if (tls_conn == NULL) { DEBUG ("SSL or TLS Server Setup failed: %s", error->message); g_io_stream_close (priv->stream, NULL, NULL); goto out; } if (priv->conn != NULL) g_object_unref (priv->conn); priv->state = SERVER_STATE_START; priv->conn = wocky_xmpp_connection_new (G_IO_STREAM (tls_conn)); g_object_unref (tls_conn); priv->tls_started = TRUE; xmpp_init (NULL,NULL,self); out: if (error != NULL) g_error_free (error); } static void starttls (GObject *source, GAsyncResult *result, gpointer data) { TestConnectorServer *self = TEST_CONNECTOR_SERVER (data); TestConnectorServerPrivate *priv = self->priv; WockyXmppConnection *conn = WOCKY_XMPP_CONNECTION (source); GError *error = NULL; DEBUG (""); if (!wocky_xmpp_connection_send_stanza_finish (conn, result, &error)) { DEBUG ("Sending starttls '' failed: %s", error->message); g_error_free (error); server_dec_outstanding (self); return; } if (server_dec_outstanding (self)) return; server_enc_outstanding (self); wocky_tls_session_handshake_async (priv->tls_sess, G_PRIORITY_DEFAULT, NULL, handshake_cb, self); } static void xmpp_handler (GObject *source, GAsyncResult *result, gpointer user_data) { TestConnectorServer *self; TestConnectorServerPrivate *priv; WockyStanza *xml = NULL; WockyXmppConnection *conn = NULL; const gchar *ns = NULL; const gchar *name = NULL; gboolean handled = FALSE; GError *error = NULL; WockyStanzaType type = WOCKY_STANZA_TYPE_NONE; WockyStanzaSubType subtype = WOCKY_STANZA_SUB_TYPE_NONE; int i; DEBUG (""); self = TEST_CONNECTOR_SERVER (user_data); priv = self->priv; conn = priv->conn; xml = wocky_xmpp_connection_recv_stanza_finish (conn, result, &error); /* A real XMPP server would need to do some error handling here, but if * we got this far, we can just exit: The client (ie the test) will * report any error that actually needs reporting - we don't need to */ if (error != NULL) { if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) { g_error_free (error); server_dec_outstanding (self); return; } g_assert_not_reached (); } if (server_dec_outstanding (self)) return; ns = wocky_node_get_ns (wocky_stanza_get_top_node (xml)); name = wocky_stanza_get_top_node (xml)->name; wocky_stanza_get_type_info (xml, &type, &subtype); /* if we find a handler, the handler is responsible for listening for the next stanza and setting up the next callback in the chain: */ if (type == WOCKY_STANZA_TYPE_IQ) for (i = 0; iq_handlers[i].payload != NULL; i++) { iq_handler *iq = &iq_handlers[i]; WockyNode *payload = wocky_node_get_child_ns (wocky_stanza_get_top_node (xml), iq->payload, iq->ns); /* namespace, stanza subtype and payload tag name must match: */ if ((payload == NULL) || (subtype != iq->subtype)) continue; DEBUG ("test_connector_server:invoking iq handler %s", iq->payload); (iq->func) (self, xml); handled = TRUE; break; } else for (i = 0; handlers[i].ns != NULL; i++) { if (!strcmp (ns, handlers[i].ns) && !strcmp (name, handlers[i].name)) { DEBUG ("test_connector_server:invoking handler %s.%s", ns, name); (handlers[i].func) (self, xml); handled = TRUE; break; } } /* no handler found: just complain and sit waiting for the next stanza */ if (!handled) { DEBUG ("<%s xmlns=\"%s\"… not handled", name, ns); server_enc_outstanding (self); wocky_xmpp_connection_recv_stanza_async (conn, priv->cancellable, xmpp_handler, self); g_object_unref (xml); } } /* ************************************************************************* */ /* resume control after the sasl auth server is done: */ static void after_auth (GObject *source, GAsyncResult *res, gpointer data) { GError *error = NULL; WockyStanza *feat = NULL; WockyNode *node = NULL; TestSaslAuthServer *tsas = TEST_SASL_AUTH_SERVER (source); TestConnectorServer *tcs = TEST_CONNECTOR_SERVER (data); TestConnectorServerPrivate *priv = tcs->priv; WockyXmppConnection *conn = priv->conn; DEBUG ("Auth finished: %d", priv->outstanding); if (!test_sasl_auth_server_auth_finish (tsas, res, &error)) { g_object_unref (priv->sasl); priv->sasl = NULL; if (server_dec_outstanding (tcs)) return; server_enc_outstanding (tcs); wocky_xmpp_connection_send_close_async (conn, priv->cancellable, xmpp_close, data); return; } priv->used_mech = g_strdup (test_sasl_auth_server_get_selected_mech (priv->sasl)); g_object_unref (priv->sasl); priv->sasl = NULL; if (server_dec_outstanding (tcs)) return; feat = wocky_stanza_build (WOCKY_STANZA_TYPE_STREAM_FEATURES, WOCKY_STANZA_SUB_TYPE_NONE, NULL, NULL, NULL); node = wocky_stanza_get_top_node (feat); if (!(priv->problem.connector->xmpp & XMPP_PROBLEM_NO_SESSION)) wocky_node_add_child_ns (node, "session", WOCKY_XMPP_NS_SESSION); if (!(priv->problem.connector->xmpp & XMPP_PROBLEM_CANNOT_BIND)) wocky_node_add_child_ns (node, "bind", WOCKY_XMPP_NS_BIND); priv->state = SERVER_STATE_FEATURES_SENT; server_enc_outstanding (tcs); wocky_xmpp_connection_send_stanza_async (conn, feat, priv->cancellable, xmpp_init, data); g_object_unref (feat); } /* ************************************************************************* */ /* initial XMPP stream setup, up to sending features stanza */ static WockyStanza * feature_stanza (TestConnectorServer *self) { TestConnectorServerPrivate *priv = self->priv; XmppProblem problem = priv->problem.connector->xmpp; const gchar *name = NULL; WockyStanza *feat = NULL; WockyNode *node = NULL; DEBUG (""); if (problem & XMPP_PROBLEM_OTHER_HOST) return error_stanza ("host-unknown", "some sort of DNS error", TRUE); name = (problem & XMPP_PROBLEM_FEATURES) ? "badger" : "features"; feat = wocky_stanza_new (name, WOCKY_XMPP_NS_STREAM); node = wocky_stanza_get_top_node (feat); DEBUG ("constructing <%s...>... stanza", name); if (priv->problem.sasl != SERVER_PROBLEM_NO_SASL) { if (priv->sasl == NULL) priv->sasl = test_sasl_auth_server_new (NULL, priv->mech, priv->user, priv->pass, NULL, priv->problem.sasl, FALSE); test_sasl_auth_server_set_mechs (G_OBJECT (priv->sasl), feat); } if (problem & XMPP_PROBLEM_OLD_AUTH_FEATURE) wocky_node_add_child_ns (node, "auth", WOCKY_JABBER_NS_AUTH_FEATURE); if (!(problem & XMPP_PROBLEM_NO_TLS) && !priv->tls_started) wocky_node_add_child_ns (node, "starttls", WOCKY_XMPP_NS_TLS); return feat; } static void xmpp_close (GObject *source, GAsyncResult *result, gpointer data) { TestConnectorServer *self = TEST_CONNECTOR_SERVER (data); TestConnectorServerPrivate *priv = self->priv; DEBUG ("Closing connection"); wocky_xmpp_connection_send_close_async (priv->conn, NULL, xmpp_closed, self); } static void xmpp_closed (GObject *source, GAsyncResult *result, gpointer data) { TestConnectorServer *self = TEST_CONNECTOR_SERVER (data); TestConnectorServerPrivate *priv = self->priv; DEBUG ("Connection closed"); wocky_xmpp_connection_send_close_finish (priv->conn, result, NULL); } static void startssl (TestConnectorServer *self) { TestConnectorServerPrivate *priv = self->priv; ConnectorProblem *problem = priv->problem.connector; g_assert (!priv->tls_started); DEBUG ("creating SSL Session [server]"); if (problem->death & SERVER_DEATH_TLS_NEG) priv->tls_sess = wocky_tls_session_server_new (priv->stream, 1024, NULL, NULL); else { int x; const gchar *key = TLS_SERVER_KEY_FILE; const gchar *crt = TLS_SERVER_CRT_FILE; for (x = 0; certs[x].set != CERT_NONE; x++) { if (certs[x].set == priv->cert) { key = certs[x].key; crt = certs[x].crt; break; } } priv->tls_sess = wocky_tls_session_server_new (priv->stream, 1024, key, crt); } DEBUG ("starting server SSL handshake"); server_enc_outstanding (self); wocky_tls_session_handshake_async (priv->tls_sess, G_PRIORITY_DEFAULT, priv->cancellable, handshake_cb, self); } static void force_closed_cb (GObject *source, GAsyncResult *result, gpointer user_data) { TestConnectorServer *self = TEST_CONNECTOR_SERVER (user_data); DEBUG ("Connection force closed"); g_assert (wocky_xmpp_connection_force_close_finish ( WOCKY_XMPP_CONNECTION (source), result, NULL)); server_dec_outstanding (self); } static void see_other_host_cb (GObject *source, GAsyncResult *result, gpointer user_data) { TestConnectorServer *self = user_data; g_assert (wocky_xmpp_connection_send_stanza_finish (self->priv->conn, result, NULL)); if (server_dec_outstanding (self)) return; server_enc_outstanding (self); wocky_xmpp_connection_force_close_async (self->priv->conn, self->priv->cancellable, force_closed_cb, self); } static void xmpp_init (GObject *source, GAsyncResult *result, gpointer data) { TestConnectorServer *self; TestConnectorServerPrivate *priv; WockyStanza *xml; WockyXmppConnection *conn; self = TEST_CONNECTOR_SERVER (data); priv = self->priv; conn = priv->conn; DEBUG ("test_connector_server:xmpp_init %d", priv->state); DEBUG ("connection: %p", conn); switch (priv->state) { /* wait for state = SERVER_STATE_CLIENT_OPENED; server_enc_outstanding (self); if (priv->problem.connector->death & SERVER_DEATH_SERVER_START) { wocky_xmpp_connection_force_close_async (conn, priv->cancellable, force_closed_cb, self); } else { wocky_xmpp_connection_recv_open_async (conn, priv->cancellable, xmpp_init, self); } break; /* send our own state = SERVER_STATE_SERVER_OPENED; wocky_xmpp_connection_recv_open_finish (conn, result, NULL, NULL, NULL, NULL, NULL, NULL); if (server_dec_outstanding (self)) return; server_enc_outstanding (self); if (priv->problem.connector->death & SERVER_DEATH_CLIENT_OPEN) { wocky_xmpp_connection_force_close_async (conn, priv->cancellable, force_closed_cb, self); } else { wocky_xmpp_connection_send_open_async (conn, NULL, "testserver", priv->version, NULL, INITIAL_STREAM_ID, priv->cancellable, xmpp_init, self); } break; /* send our feature set */ case SERVER_STATE_SERVER_OPENED: DEBUG ("SERVER_STATE_SERVER_OPENED"); priv->state = SERVER_STATE_FEATURES_SENT; wocky_xmpp_connection_send_open_finish (conn, result, NULL); if (server_dec_outstanding (self)) return; if (priv->problem.connector->death & SERVER_DEATH_SERVER_OPEN) { server_enc_outstanding (self); wocky_xmpp_connection_force_close_async (conn, priv->cancellable, force_closed_cb, self); } else if (priv->problem.connector->xmpp & XMPP_PROBLEM_OLD_SERVER) { DEBUG ("diverting to old-jabber-auth"); server_enc_outstanding (self); wocky_xmpp_connection_recv_stanza_async (priv->conn, priv->cancellable, xmpp_handler, self); } else if (priv->problem.connector->xmpp & XMPP_PROBLEM_SEE_OTHER_HOST) { WockyStanza *stanza; WockyNode *node; gchar *host_and_port; host_and_port = g_strdup_printf ("%s:%u", self->priv->other_host, self->priv->other_port); DEBUG ("Redirect to another host: %s", host_and_port); stanza = wocky_stanza_new ("error", WOCKY_XMPP_NS_STREAM); node = wocky_stanza_get_top_node (stanza); wocky_node_add_child_with_content_ns (node, "see-other-host", host_and_port, WOCKY_XMPP_NS_STREAMS); server_enc_outstanding (self); wocky_xmpp_connection_send_stanza_async (self->priv->conn, stanza, self->priv->cancellable, see_other_host_cb, self); g_object_unref (stanza); } else { xml = feature_stanza (self); server_enc_outstanding (self); wocky_xmpp_connection_send_stanza_async (conn, xml, priv->cancellable, xmpp_init, self); g_object_unref (xml); } break; /* ok, we're done with initial stream setup */ case SERVER_STATE_FEATURES_SENT: DEBUG ("SERVER_STATE_FEATURES_SENT"); wocky_xmpp_connection_send_stanza_finish (conn, result, NULL); if (server_dec_outstanding (self)) return; server_enc_outstanding (self); if (priv->problem.connector->death & SERVER_DEATH_FEATURES) { wocky_xmpp_connection_force_close_async (conn, priv->cancellable, force_closed_cb, self); } else { wocky_xmpp_connection_recv_stanza_async (conn, priv->cancellable, xmpp_handler, self); } break; default: DEBUG ("Unknown Server state. Broken code flow."); } } /* ************************************************************************* */ /* exposed methods */ TestConnectorServer * test_connector_server_new (GIOStream *stream, gchar *mech, const gchar *user, const gchar *pass, const gchar *version, ConnectorProblem *problem, ServerProblem sasl_problem, CertSet cert) { TestConnectorServer *self; TestConnectorServerPrivate *priv; DEBUG ("test_connector_server_new"); self = g_object_new (TEST_TYPE_CONNECTOR_SERVER, NULL); priv = self->priv; priv->stream = g_object_ref (stream); priv->mech = g_strdup (mech); priv->user = g_strdup (user); priv->pass = g_strdup (pass); priv->problem.sasl = sasl_problem; priv->problem.connector = problem; priv->conn = wocky_xmpp_connection_new (stream); priv->cert = cert; DEBUG ("connection: %p", priv->conn); if (problem->xmpp & XMPP_PROBLEM_OLD_SERVER) priv->version = g_strdup ((version == NULL) ? "0.9" : version); else priv->version = g_strdup ((version == NULL) ? "1.0" : version); return self; } static void server_enc_outstanding (TestConnectorServer *self) { TestConnectorServerPrivate *priv = self->priv; priv->outstanding++; DEBUG ("Upped outstanding to %d", priv->outstanding); } static gboolean server_dec_outstanding (TestConnectorServer *self) { TestConnectorServerPrivate *priv = self->priv; priv->outstanding--; g_assert (priv->outstanding >= 0); if (priv->teardown_result != NULL && priv->outstanding == 0) { GSimpleAsyncResult *r = priv->teardown_result; priv->teardown_result = NULL; DEBUG ("Tearing down, bye bye"); g_simple_async_result_complete (r); g_object_unref (r); DEBUG ("Unreffed!"); return TRUE; } DEBUG ("Outstanding: %d", priv->outstanding); return FALSE; } void test_connector_server_teardown (TestConnectorServer *self, GAsyncReadyCallback callback, gpointer user_data) { TestConnectorServerPrivate *priv = self->priv; GSimpleAsyncResult *result = g_simple_async_result_new (G_OBJECT (self), callback, user_data, test_connector_server_teardown); /* For now, we'll assert if this gets called twice */ g_assert (priv->cancellable != NULL); DEBUG ("Requested to stop: %d", priv->outstanding); g_cancellable_cancel (priv->cancellable); g_object_unref (priv->cancellable); priv->cancellable = NULL; if (priv->outstanding == 0) { g_simple_async_result_complete_in_idle (result); g_object_unref (result); } else { priv->teardown_result = result; } } gboolean test_connector_server_teardown_finish (TestConnectorServer *self, GAsyncResult *result, GError *error) { return TRUE; } void test_connector_server_start (TestConnectorServer *self) { TestConnectorServerPrivate *priv; DEBUG("test_connector_server_start"); priv = self->priv; priv->state = SERVER_STATE_START; DEBUG ("connection: %p", priv->conn); if (priv->problem.connector->xmpp & XMPP_PROBLEM_OLD_SSL) { startssl (self); } else { xmpp_init (NULL,NULL,self); } } const gchar * test_connector_server_get_used_mech (TestConnectorServer *self) { TestConnectorServerPrivate *priv = self->priv; return priv->used_mech; } void test_connector_server_set_other_host (TestConnectorServer *self, const gchar *host, guint port) { g_return_if_fail (TEST_IS_CONNECTOR_SERVER (self)); g_return_if_fail (self->priv->other_host == NULL); g_return_if_fail (self->priv->other_port == 0); self->priv->other_host = g_strdup (host); self->priv->other_port = port; } telepathy-gabble-0.18.2/lib/ext/wocky/tests/wocky-test-sasl-auth-server.h0000644000175000017500000000656412200204546026354 0ustar00smcvsmcv00000000000000/* * wocky-test-sasl-auth-server.h - Header for TestSaslAuthServer * Copyright (C) 2006 Collabora Ltd. * @author Sjoerd Simons * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 __TEST_SASL_AUTH_SERVER_H__ #define __TEST_SASL_AUTH_SERVER_H__ #include #include #include G_BEGIN_DECLS typedef enum { SERVER_PROBLEM_NO_PROBLEM, SERVER_PROBLEM_NO_SASL, SERVER_PROBLEM_NO_MECHANISMS, SERVER_PROBLEM_INVALID_USERNAME, SERVER_PROBLEM_INVALID_PASSWORD, SERVER_PROBLEM_REQUIRE_GOOGLE_JDD, SERVER_PROBLEM_DISLIKE_GOOGLE_JDD, SERVER_PROBLEM_SPACE_CHALLENGE, SERVER_PROBLEM_SLASH_CHALLENGE, /* Not actually a problem, but let the server choose to put * ``additional data with success'' in a success stanza. */ SERVER_PROBLEM_FINAL_DATA_IN_SUCCESS, } ServerProblem; typedef struct _TestSaslAuthServer TestSaslAuthServer; typedef struct _TestSaslAuthServerClass TestSaslAuthServerClass; typedef struct _TestSaslAuthServerPrivate TestSaslAuthServerPrivate; struct _TestSaslAuthServerClass { GObjectClass parent_class; }; struct _TestSaslAuthServer { GObject parent; TestSaslAuthServerPrivate *priv; }; GType test_sasl_auth_server_get_type (void); /* TYPE MACROS */ #define TEST_TYPE_SASL_AUTH_SERVER \ (test_sasl_auth_server_get_type ()) #define TEST_SASL_AUTH_SERVER(obj) \ (G_TYPE_CHECK_INSTANCE_CAST((obj), TEST_TYPE_SASL_AUTH_SERVER, \ TestSaslAuthServer)) #define TEST_SASL_AUTH_SERVER_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST((klass), TEST_TYPE_SASL_AUTH_SERVER, \ TestSaslAuthServerClass)) #define TEST_IS_SASL_AUTH_SERVER(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE((obj), TEST_TYPE_SASL_AUTH_SERVER)) #define TEST_IS_SASL_AUTH_SERVER_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE((klass), TEST_TYPE_SASL_AUTH_SERVER)) #define TEST_SASL_AUTH_SERVER_GET_CLASS(obj) \ (G_TYPE_INSTANCE_GET_CLASS ((obj), TEST_TYPE_SASL_AUTH_SERVER, \ TestSaslAuthServerClass)) void test_sasl_auth_server_auth_async (GObject *obj, WockyXmppConnection *conn, WockyStanza *auth, GAsyncReadyCallback cb, GCancellable *cancellable, gpointer data); gboolean test_sasl_auth_server_auth_finish (TestSaslAuthServer *self, GAsyncResult *res, GError **error); const gchar *test_sasl_auth_server_get_selected_mech (TestSaslAuthServer *self); TestSaslAuthServer * test_sasl_auth_server_new (GIOStream *stream, gchar *mech, const gchar *user, const gchar *password, const gchar *servername, ServerProblem problem, gboolean start); void test_sasl_auth_server_stop (TestSaslAuthServer *self); gint test_sasl_auth_server_set_mechs (GObject *obj, WockyStanza *feat); G_END_DECLS #endif /* #ifndef __TEST_SASL_AUTH_SERVER_H__*/ telepathy-gabble-0.18.2/lib/ext/wocky/tests/wocky-test-sasl-auth-server.c0000644000175000017500000007251112213345474026354 0ustar00smcvsmcv00000000000000/* * wocky-test-sasl-auth-server.c - Source for TestSaslAuthServer * Copyright (C) 2006 Collabora Ltd. * @author Sjoerd Simons * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include #include "wocky-test-sasl-auth-server.h" #include "wocky-test-helper.h" #ifdef HAVE_LIBSASL2 #include #include #define CHECK_SASL_RETURN(x) \ G_STMT_START { \ if (x < SASL_OK) { \ fprintf (stderr, "sasl error (%d): %s\n", \ ret, sasl_errdetail (priv->sasl_conn)); \ g_assert_not_reached (); \ } \ } G_STMT_END /* Apparently, we're allowed to typedef the same thing *again* if it's * the same signature, so this allows for backwards compatiblity with * older libsasl2s and also works with newer ones too. This'll only * break if libsasl2 change the type of sasl_callback_ft. I sure hope * they don't! */ typedef int (*sasl_callback_ft)(void); #else #define SASL_OK 0 #define SASL_BADAUTH -13 #define SASL_NOUSER -20 #define CHECK_SASL_RETURN(x) \ G_STMT_START { \ if (x < SASL_OK) { \ fprintf (stderr, "sasl error (%d): ???\n", ret); \ g_assert_not_reached (); \ } \ } G_STMT_END #endif G_DEFINE_TYPE(TestSaslAuthServer, test_sasl_auth_server, G_TYPE_OBJECT) #if 0 /* signal enum */ enum { LAST_SIGNAL }; static guint signals[LAST_SIGNAL] = {0}; #endif typedef enum { AUTH_STATE_STARTED, AUTH_STATE_CHALLENGE, AUTH_STATE_FINAL_CHALLENGE, AUTH_STATE_AUTHENTICATED, } AuthState; /* private structure */ struct _TestSaslAuthServerPrivate { gboolean dispose_has_run; WockyXmppConnection *conn; GIOStream *stream; #ifdef HAVE_LIBSASL2 sasl_conn_t *sasl_conn; #endif gchar *username; gchar *password; gchar *mech; gchar *selected_mech; AuthState state; ServerProblem problem; GSimpleAsyncResult *result; GCancellable *cancellable; }; static void received_stanza (GObject *source, GAsyncResult *result, gpointer user_data); static void test_sasl_auth_server_init (TestSaslAuthServer *self) { TestSaslAuthServerPrivate *priv; self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, TEST_TYPE_SASL_AUTH_SERVER, TestSaslAuthServerPrivate); priv = self->priv; priv->username = NULL; priv->password = NULL; priv->mech = NULL; priv->state = AUTH_STATE_STARTED; } static void test_sasl_auth_server_dispose (GObject *object); static void test_sasl_auth_server_finalize (GObject *object); static void test_sasl_auth_server_class_init ( TestSaslAuthServerClass *test_sasl_auth_server_class) { GObjectClass *object_class = G_OBJECT_CLASS (test_sasl_auth_server_class); g_type_class_add_private (test_sasl_auth_server_class, sizeof (TestSaslAuthServerPrivate)); object_class->dispose = test_sasl_auth_server_dispose; object_class->finalize = test_sasl_auth_server_finalize; } void test_sasl_auth_server_dispose (GObject *object) { TestSaslAuthServer *self = TEST_SASL_AUTH_SERVER (object); TestSaslAuthServerPrivate *priv = self->priv; if (priv->dispose_has_run) return; priv->dispose_has_run = TRUE; /* release any references held by the object here */ if (priv->conn != NULL) g_object_unref (priv->conn); priv->conn = NULL; if (priv->stream != NULL) g_object_unref (priv->stream); priv->stream = NULL; #ifdef HAVE_LIBSASL2 if (&priv->sasl_conn != NULL) sasl_dispose (&priv->sasl_conn); priv->sasl_conn = NULL; #endif g_warn_if_fail (priv->result == NULL); g_warn_if_fail (priv->cancellable == NULL); if (G_OBJECT_CLASS (test_sasl_auth_server_parent_class)->dispose) G_OBJECT_CLASS (test_sasl_auth_server_parent_class)->dispose (object); } void test_sasl_auth_server_finalize (GObject *object) { TestSaslAuthServer *self = TEST_SASL_AUTH_SERVER (object); TestSaslAuthServerPrivate *priv = self->priv; /* free any data held directly by the object here */ g_free (priv->username); g_free (priv->password); g_free (priv->mech); g_free (priv->selected_mech); G_OBJECT_CLASS (test_sasl_auth_server_parent_class)->finalize (object); } static void features_sent (GObject *source, GAsyncResult *res, gpointer user_data) { TestSaslAuthServer *self = TEST_SASL_AUTH_SERVER (user_data); TestSaslAuthServerPrivate *priv = self->priv; g_assert (wocky_xmpp_connection_send_stanza_finish ( WOCKY_XMPP_CONNECTION (source), res, NULL)); wocky_xmpp_connection_recv_stanza_async (WOCKY_XMPP_CONNECTION (source), priv->cancellable, received_stanza, user_data); } static void stream_open_sent (GObject *source, GAsyncResult *res, gpointer user_data) { TestSaslAuthServer *self = TEST_SASL_AUTH_SERVER(user_data); TestSaslAuthServerPrivate * priv = self->priv; WockyStanza *stanza; g_assert (wocky_xmpp_connection_send_open_finish ( WOCKY_XMPP_CONNECTION (source), res, NULL)); /* Send stream features */ stanza = wocky_stanza_new ("features", WOCKY_XMPP_NS_STREAM); test_sasl_auth_server_set_mechs (G_OBJECT (self), stanza); wocky_xmpp_connection_send_stanza_async (priv->conn, stanza, priv->cancellable, features_sent, user_data); g_object_unref (stanza); } static void stream_open_received (GObject *source, GAsyncResult *res, gpointer user_data) { TestSaslAuthServer *self = TEST_SASL_AUTH_SERVER(user_data); TestSaslAuthServerPrivate * priv = self->priv; g_assert (wocky_xmpp_connection_recv_open_finish ( WOCKY_XMPP_CONNECTION (source), res, NULL, NULL, NULL, NULL, NULL, NULL)); wocky_xmpp_connection_send_open_async (priv->conn, NULL, "testserver", "1.0", NULL, "0-HA2", NULL, stream_open_sent, self); } static void post_auth_close_sent (GObject *source, GAsyncResult *result, gpointer user_data) { g_assert (wocky_xmpp_connection_send_close_finish ( WOCKY_XMPP_CONNECTION (source), result, NULL)); } static void post_auth_recv_stanza (GObject *source, GAsyncResult *result, gpointer user_data) { TestSaslAuthServer *self = TEST_SASL_AUTH_SERVER(user_data); TestSaslAuthServerPrivate * priv = self->priv; WockyStanza *stanza; GError *error = NULL; /* ignore all stanza until close */ stanza = wocky_xmpp_connection_recv_stanza_finish ( WOCKY_XMPP_CONNECTION (source), result, &error); if (stanza != NULL) { g_object_unref (stanza); wocky_xmpp_connection_recv_stanza_async ( WOCKY_XMPP_CONNECTION (source), priv->cancellable, post_auth_recv_stanza, user_data); } else if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) { GSimpleAsyncResult *r = priv->result; priv->result = NULL; if (priv->cancellable != NULL) g_object_unref (priv->cancellable); priv->cancellable = NULL; g_simple_async_result_set_from_error (r, error); g_simple_async_result_complete (r); g_object_unref (r); g_error_free (error); } else { g_assert_error (error, WOCKY_XMPP_CONNECTION_ERROR, WOCKY_XMPP_CONNECTION_ERROR_CLOSED); wocky_xmpp_connection_send_close_async (WOCKY_XMPP_CONNECTION (source), priv->cancellable, post_auth_close_sent, user_data); g_error_free (error); } } static void post_auth_features_sent (GObject *source, GAsyncResult *result, gpointer user_data) { TestSaslAuthServer *self = TEST_SASL_AUTH_SERVER(user_data); TestSaslAuthServerPrivate * priv = self->priv; g_assert (wocky_xmpp_connection_send_stanza_finish ( WOCKY_XMPP_CONNECTION (source), result, NULL)); wocky_xmpp_connection_recv_stanza_async (WOCKY_XMPP_CONNECTION (source), priv->cancellable, post_auth_recv_stanza, user_data); } static void post_auth_open_sent (GObject *source, GAsyncResult *result, gpointer user_data) { TestSaslAuthServer *tsas = TEST_SASL_AUTH_SERVER (user_data); TestSaslAuthServerPrivate *priv = tsas->priv; g_assert (wocky_xmpp_connection_send_open_finish ( WOCKY_XMPP_CONNECTION (source), result, NULL)); /* if our caller wanted control back, hand it back here: */ if (priv->result != NULL) { GSimpleAsyncResult *r = priv->result; priv->result = NULL; if (priv->cancellable != NULL) g_object_unref (priv->cancellable); priv->cancellable = NULL; g_simple_async_result_complete (r); g_object_unref (r); } else { WockyStanza *s = wocky_stanza_new ("features", WOCKY_XMPP_NS_STREAM); wocky_xmpp_connection_send_stanza_async (WOCKY_XMPP_CONNECTION (source), s, NULL, post_auth_features_sent, user_data); g_object_unref (s); } } static void post_auth_open_received (GObject *source, GAsyncResult *result, gpointer user_data) { g_assert (wocky_xmpp_connection_recv_open_finish ( WOCKY_XMPP_CONNECTION (source), result, NULL, NULL, NULL, NULL, NULL, user_data)); wocky_xmpp_connection_send_open_async ( WOCKY_XMPP_CONNECTION (source), NULL, "testserver", "1.0", NULL, "0-HA1", NULL, post_auth_open_sent, user_data); } static void success_sent (GObject *source, GAsyncResult *result, gpointer user_data) { g_assert (wocky_xmpp_connection_send_stanza_finish ( WOCKY_XMPP_CONNECTION (source), result, NULL)); wocky_xmpp_connection_reset (WOCKY_XMPP_CONNECTION (source)); wocky_xmpp_connection_recv_open_async (WOCKY_XMPP_CONNECTION (source), NULL, post_auth_open_received, user_data); } static void auth_succeeded (TestSaslAuthServer *self, const gchar *challenge) { TestSaslAuthServerPrivate *priv = self->priv; WockyStanza *s; g_assert (priv->state < AUTH_STATE_AUTHENTICATED); priv->state = AUTH_STATE_AUTHENTICATED; s = wocky_stanza_new ("success", WOCKY_XMPP_NS_SASL_AUTH); wocky_node_set_content (wocky_stanza_get_top_node (s), challenge); wocky_xmpp_connection_send_stanza_async (priv->conn, s, NULL, success_sent, self); g_object_unref (s); } static void failure_sent (GObject *source, GAsyncResult *result, gpointer user_data) { TestSaslAuthServer *tsas = TEST_SASL_AUTH_SERVER (user_data); TestSaslAuthServerPrivate *priv = tsas->priv; GSimpleAsyncResult *r = priv->result; priv->result = NULL; g_assert (wocky_xmpp_connection_send_stanza_finish ( WOCKY_XMPP_CONNECTION (source), result, NULL)); if (r != NULL) { if (priv->cancellable != NULL) g_object_unref (priv->cancellable); priv->cancellable = NULL; g_simple_async_result_complete (r); g_object_unref (r); } } static void not_authorized (TestSaslAuthServer *self) { TestSaslAuthServerPrivate *priv = self->priv; WockyStanza *s; g_assert (priv->state < AUTH_STATE_AUTHENTICATED); priv->state = AUTH_STATE_AUTHENTICATED; s = wocky_stanza_build (WOCKY_STANZA_TYPE_FAILURE, WOCKY_STANZA_SUB_TYPE_NONE, NULL, NULL, '(', "not-authorized", ')', NULL); wocky_xmpp_connection_send_stanza_async (priv->conn, s, NULL, failure_sent, self); g_object_unref (s); } /* check if the return of the sasl function was as expected, if not FALSE is * returned and the call function should stop processing */ static gboolean check_sasl_return (TestSaslAuthServer *self, int ret) { TestSaslAuthServerPrivate * priv = self->priv; switch (ret) { case SASL_BADAUTH: /* Bad password provided */ g_assert (priv->problem == SERVER_PROBLEM_INVALID_PASSWORD); not_authorized (self); return FALSE; case SASL_NOUSER: /* Unknown user */ g_assert (priv->problem == SERVER_PROBLEM_INVALID_USERNAME); not_authorized (self); return FALSE; default: /* sasl auth should be ok */ CHECK_SASL_RETURN (ret); break; } return TRUE; } enum { BEFORE_KEY, INSIDE_KEY, AFTER_KEY, AFTER_EQ, INSIDE_VALUE, AFTER_VALUE, }; /* insert space, CRLF, TAB etc at strategic locations in the challenge * * to make sure our challenge parser is sufficently robust */ static gchar * space_challenge (const gchar *challenge, unsigned *len) { GString *spaced = g_string_new_len (challenge, (gssize) *len); gchar *c = spaced->str; gchar q = '\0'; gsize pos; gulong state = BEFORE_KEY; gchar spc[] = { ' ', '\t', '\r', '\n' }; for (pos = 0; pos < spaced->len; pos++) { c = spaced->str + pos; switch (state) { case BEFORE_KEY: if (!g_ascii_isspace (*c) && *c != '\0' && *c != '=') state = INSIDE_KEY; break; case INSIDE_KEY: if (*c != '=') break; g_string_insert_c (spaced, pos++, spc [rand () % sizeof (spc)]); state = AFTER_EQ; break; case AFTER_KEY: if (*c != '=') break; state = AFTER_EQ; break; case AFTER_EQ: if (g_ascii_isspace (*c)) break; q = *c; g_string_insert_c (spaced, pos++, spc [rand () % sizeof (spc)]); state = INSIDE_VALUE; break; case INSIDE_VALUE: if (q == '"' && *c != '"') break; if (q != '"' && !g_ascii_isspace (*c) && *c != ',') break; if (q != '"') { g_string_insert_c (spaced, pos++, spc [rand () % sizeof (spc)]); g_string_insert_c (spaced, ++pos, spc [rand () % sizeof (spc)]); } state = AFTER_VALUE; break; case AFTER_VALUE: if (*c == ',') { g_string_insert_c (spaced, pos++, spc [rand () % sizeof (spc)]); g_string_insert_c (spaced, ++pos, spc [rand () % sizeof (spc)]); } state = BEFORE_KEY; break; default: g_assert_not_reached (); } } *len = spaced->len; return g_string_free (spaced, FALSE); } /* insert a bogus parameter with a \" and a \\ sequence in it * scatter some \ characters through the " quoted challenge values */ static gchar * slash_challenge (const gchar *challenge, unsigned *len) { GString *slashed = g_string_new_len (challenge, (gssize) *len); gchar *c = slashed->str; gchar q = '\0'; gsize pos; gulong state = BEFORE_KEY; for (pos = 0; pos < slashed->len; pos++) { c = slashed->str + pos; switch (state) { case BEFORE_KEY: if (!g_ascii_isspace (*c) && *c != '\0' && *c != '=') state = INSIDE_KEY; break; case INSIDE_KEY: if (*c != '=') break; state = AFTER_EQ; break; case AFTER_EQ: if (g_ascii_isspace (*c)) break; q = *c; state = INSIDE_VALUE; break; case INSIDE_VALUE: if (q == '"' && *c != '"') { if ((rand () % 3) == 0) g_string_insert_c (slashed, pos++, '\\'); break; } if (q != '"' && !g_ascii_isspace (*c) && *c != ',') break; state = AFTER_VALUE; break; case AFTER_VALUE: state = BEFORE_KEY; break; default: g_assert_not_reached (); } } g_string_prepend (slashed, "ignore-me = \"(a slash \\\\ a quote \\\")\", "); *len = slashed->len; return g_string_free (slashed, FALSE); } static void handle_auth (TestSaslAuthServer *self, WockyStanza *stanza) { TestSaslAuthServerPrivate *priv = self->priv; guchar *response = NULL; const gchar *challenge; unsigned challenge_len; gsize response_len = 0; int ret; WockyNode *auth = wocky_stanza_get_top_node (stanza); const gchar *gjdd = NULL; g_free (priv->selected_mech); priv->selected_mech = g_strdup (wocky_node_get_attribute ( wocky_stanza_get_top_node (stanza), "mechanism")); if (wocky_stanza_get_top_node (stanza)->content != NULL) { response = g_base64_decode (wocky_stanza_get_top_node (stanza)->content, &response_len); } g_assert (priv->state == AUTH_STATE_STARTED); gjdd = wocky_node_get_attribute_ns (auth, "client-uses-full-bind-result", WOCKY_GOOGLE_NS_AUTH); switch (priv->problem) { case SERVER_PROBLEM_REQUIRE_GOOGLE_JDD: if ((gjdd == NULL) || wocky_strdiff ("true", gjdd)) { not_authorized (self); goto out; } break; case SERVER_PROBLEM_DISLIKE_GOOGLE_JDD: if (gjdd && !wocky_strdiff ("true", gjdd)) { not_authorized (self); goto out; } break; default: break; } priv->state = AUTH_STATE_CHALLENGE; if (!wocky_strdiff ("X-TEST", priv->selected_mech)) { challenge = ""; challenge_len = 0; ret = wocky_strdiff ((gchar *) response, priv->password) ? SASL_BADAUTH : SASL_OK; } else { #if HAVE_LIBSASL2 ret = sasl_server_start (priv->sasl_conn, priv->selected_mech, (gchar *) response, (unsigned) response_len, &challenge, &challenge_len); #else challenge = ""; challenge_len = 0; g_assert (!wocky_strdiff ("PLAIN", priv->selected_mech)); /* response format: ^@ u s e r ^@ p a s s */ /* require at least 1 char user and password */ if (response_len >= 4) { const gchar *user = ((gchar *) response) + 1; int ulen = strlen (user); gchar *pass = g_strndup (user + ulen + 1, response_len - ulen - 2); ret = ( wocky_strdiff (user, priv->username) ? SASL_NOUSER : wocky_strdiff (pass, priv->password) ? SASL_BADAUTH : SASL_OK ); g_free (pass); } else ret = SASL_BADAUTH; #endif } if (!check_sasl_return (self, ret)) goto out; if (challenge_len > 0) { WockyStanza *c; gchar *challenge64; if (ret == SASL_OK) { priv->state = AUTH_STATE_FINAL_CHALLENGE; } if (priv->problem == SERVER_PROBLEM_SPACE_CHALLENGE) { unsigned slen = challenge_len; gchar *spaced = space_challenge (challenge, &slen); challenge64 = g_base64_encode ((guchar *) spaced, slen); g_free (spaced); } else if (priv->problem == SERVER_PROBLEM_SLASH_CHALLENGE) { unsigned slen = challenge_len; gchar *slashc = slash_challenge (challenge, &slen); challenge64 = g_base64_encode ((guchar *) slashc, slen); g_free (slashc); } else { challenge64 = g_base64_encode ((guchar *) challenge, challenge_len); } c = wocky_stanza_new ("challenge", WOCKY_XMPP_NS_SASL_AUTH); wocky_node_set_content (wocky_stanza_get_top_node (c), challenge64); wocky_xmpp_connection_send_stanza_async (priv->conn, c, NULL, NULL, NULL); g_object_unref (c); g_free (challenge64); } else if (ret == SASL_OK) { auth_succeeded (self, NULL); } else { g_assert_not_reached (); } out: g_free (response); } static void handle_response (TestSaslAuthServer *self, WockyStanza *stanza) { TestSaslAuthServerPrivate * priv = self->priv; guchar *response = NULL; const gchar *challenge; unsigned challenge_len; gsize response_len = 0; int ret; if (priv->state == AUTH_STATE_FINAL_CHALLENGE) { g_assert (wocky_stanza_get_top_node (stanza)->content == NULL); auth_succeeded (self, NULL); return; } g_assert (priv->state == AUTH_STATE_CHALLENGE); if (wocky_stanza_get_top_node (stanza)->content != NULL) { response = g_base64_decode (wocky_stanza_get_top_node (stanza)->content, &response_len); } #ifdef HAVE_LIBSASL2 ret = sasl_server_step (priv->sasl_conn, (gchar *) response, (unsigned) response_len, &challenge, &challenge_len); #else ret = SASL_OK; challenge_len = 0; challenge = ""; #endif if (!check_sasl_return (self, ret)) goto out; if (challenge_len > 0) { WockyStanza *c; gchar *challenge64; if (ret == SASL_OK) { priv->state = AUTH_STATE_FINAL_CHALLENGE; } if (priv->problem == SERVER_PROBLEM_SPACE_CHALLENGE) { unsigned slen = challenge_len; gchar *spaced = space_challenge (challenge, &slen); challenge64 = g_base64_encode ((guchar *) spaced, slen); g_free (spaced); } else if (priv->problem == SERVER_PROBLEM_SLASH_CHALLENGE) { unsigned slen = challenge_len; gchar *slashc = slash_challenge (challenge, &slen); challenge64 = g_base64_encode ((guchar *) slashc, slen); g_free (slashc); } else { challenge64 = g_base64_encode ((guchar *) challenge, challenge_len); } if (priv->state == AUTH_STATE_FINAL_CHALLENGE && priv->problem == SERVER_PROBLEM_FINAL_DATA_IN_SUCCESS) { auth_succeeded (self, challenge64); } else { c = wocky_stanza_new ("challenge", WOCKY_XMPP_NS_SASL_AUTH); wocky_node_set_content (wocky_stanza_get_top_node (c), challenge64); wocky_xmpp_connection_send_stanza_async (priv->conn, c, NULL, NULL, NULL); g_object_unref (c); } g_free (challenge64); } else if (ret == SASL_OK) { auth_succeeded (self, NULL); } else { g_assert_not_reached (); } out: g_free (response); } #define HANDLE(x) { #x, handle_##x } static void received_stanza (GObject *source, GAsyncResult *result, gpointer user_data) { TestSaslAuthServer *self; TestSaslAuthServerPrivate *priv; int i; WockyStanza *stanza; GError *error = NULL; struct { const gchar *name; void (*func)(TestSaslAuthServer *self, WockyStanza *stanza); } handlers[] = { HANDLE(auth), HANDLE(response) }; stanza = wocky_xmpp_connection_recv_stanza_finish ( WOCKY_XMPP_CONNECTION (source), result, &error); if (stanza == NULL && (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED) || g_error_matches (error, WOCKY_XMPP_CONNECTION_ERROR, WOCKY_XMPP_CONNECTION_ERROR_EOS))) { g_error_free (error); return; } self = TEST_SASL_AUTH_SERVER (user_data); priv = self->priv; g_assert (stanza != NULL); if (wocky_strdiff (wocky_node_get_ns ( wocky_stanza_get_top_node (stanza)), WOCKY_XMPP_NS_SASL_AUTH)) { g_assert_not_reached (); } for (i = 0 ; handlers[i].name != NULL; i++) { if (!wocky_strdiff (wocky_stanza_get_top_node (stanza)->name, handlers[i].name)) { handlers[i].func (self, stanza); if (priv->state < AUTH_STATE_AUTHENTICATED) { wocky_xmpp_connection_recv_stanza_async (priv->conn, NULL, received_stanza, user_data); } g_object_unref (stanza); return; } } g_assert_not_reached (); } #ifdef HAVE_LIBSASL2 static int test_sasl_server_auth_log (void *context, int level, const gchar *message) { return SASL_OK; } static int test_sasl_server_auth_getopt (void *context, const char *plugin_name, const gchar *option, const gchar **result, guint *len) { int i; static const struct { const gchar *name; const gchar *value; } options[] = { { "auxprop_plugin", "sasldb"}, { "sasldb_path", "./sasl-test.db"}, { NULL, NULL }, }; for (i = 0; options[i].name != NULL; i++) { if (!wocky_strdiff (option, options[i].name)) { *result = options[i].value; if (len != NULL) *len = strlen (options[i].value); } } return SASL_OK; } #endif TestSaslAuthServer * test_sasl_auth_server_new (GIOStream *stream, gchar *mech, const gchar *user, const gchar *password, const gchar *servername, ServerProblem problem, gboolean start) { TestSaslAuthServer *server; TestSaslAuthServerPrivate *priv; #ifdef HAVE_LIBSASL2 static gboolean sasl_initialized = FALSE; int ret; static sasl_callback_t callbacks[] = { { SASL_CB_LOG, (sasl_callback_ft) test_sasl_server_auth_log, NULL }, { SASL_CB_GETOPT, (sasl_callback_ft) test_sasl_server_auth_getopt, NULL }, { SASL_CB_LIST_END, NULL, NULL }, }; if (!sasl_initialized) { sasl_server_init (NULL, NULL); sasl_initialized = TRUE; } #endif server = g_object_new (TEST_TYPE_SASL_AUTH_SERVER, NULL); priv = server->priv; priv->state = AUTH_STATE_STARTED; #ifdef HAVE_LIBSASL2 ret = sasl_server_new ("xmpp", servername, NULL, NULL, NULL, callbacks, SASL_SUCCESS_DATA, &(priv->sasl_conn)); CHECK_SASL_RETURN (ret); ret = sasl_setpass (priv->sasl_conn, user, password, strlen (password), NULL, 0, SASL_SET_CREATE); CHECK_SASL_RETURN (ret); #endif priv->username = g_strdup (user); priv->password = g_strdup (password); priv->mech = g_strdup (mech); priv->problem = problem; if (start) { priv->stream = g_object_ref (stream); priv->conn = wocky_xmpp_connection_new (stream); priv->cancellable = g_cancellable_new (); wocky_xmpp_connection_recv_open_async (priv->conn, priv->cancellable, stream_open_received, server); } return server; } void test_sasl_auth_server_stop (TestSaslAuthServer *self) { TestSaslAuthServerPrivate *priv = self->priv; if (priv->cancellable != NULL) { test_cancel_in_idle (priv->cancellable); g_object_unref (priv->cancellable); priv->cancellable = NULL; } if (priv->conn != NULL) g_object_unref (priv->conn); priv->conn = NULL; } gboolean test_sasl_auth_server_auth_finish (TestSaslAuthServer *self, GAsyncResult *res, GError **error) { gboolean ok = FALSE; TestSaslAuthServerPrivate *priv = self->priv; if (g_simple_async_result_propagate_error ( G_SIMPLE_ASYNC_RESULT (res), error)) return FALSE; ok = g_simple_async_result_is_valid (G_ASYNC_RESULT (res), G_OBJECT (self), test_sasl_auth_server_auth_async); g_return_val_if_fail (ok, FALSE); return (priv->state == AUTH_STATE_AUTHENTICATED); } void test_sasl_auth_server_auth_async (GObject *obj, WockyXmppConnection *conn, WockyStanza *auth, GAsyncReadyCallback cb, GCancellable *cancellable, gpointer data) { TestSaslAuthServer *self = TEST_SASL_AUTH_SERVER (obj); TestSaslAuthServerPrivate *priv = self->priv; /* We expect the server to not be started but just in case */ test_sasl_auth_server_stop (TEST_SASL_AUTH_SERVER (obj)); priv->state = AUTH_STATE_STARTED; priv->conn = g_object_ref (conn); /* save the details of the point ot which we will hand back control */ if (cb != NULL) { if (cancellable != NULL) priv->cancellable = g_object_ref (cancellable); priv->result = g_simple_async_result_new (obj, cb, data, test_sasl_auth_server_auth_async); } handle_auth (self, auth); if (priv->state < AUTH_STATE_AUTHENTICATED) { wocky_xmpp_connection_recv_stanza_async (priv->conn, priv->cancellable, received_stanza, self); } g_object_unref (auth); } gint test_sasl_auth_server_set_mechs (GObject *obj, WockyStanza *feat) { int ret = 0; TestSaslAuthServer *self = TEST_SASL_AUTH_SERVER (obj); TestSaslAuthServerPrivate *priv = self->priv; WockyNode *mechnode = NULL; if (priv->problem != SERVER_PROBLEM_NO_SASL) { mechnode = wocky_node_add_child_ns ( wocky_stanza_get_top_node (feat), "mechanisms", WOCKY_XMPP_NS_SASL_AUTH); if (priv->problem == SERVER_PROBLEM_NO_MECHANISMS) { /* lalala */ } else if (priv->mech != NULL) { wocky_node_add_child_with_content (mechnode, "mechanism", priv->mech); } else { const gchar *mechs; gchar **mechlist; gchar **tmp; #ifdef HAVE_LIBSASL2 ret = sasl_listmech (priv->sasl_conn, NULL, "","\n","", &mechs, NULL,NULL); CHECK_SASL_RETURN (ret); #else mechs = "PLAIN"; #endif mechlist = g_strsplit (mechs, "\n", -1); for (tmp = mechlist; *tmp != NULL; tmp++) { wocky_node_add_child_with_content (mechnode, "mechanism", *tmp); } g_strfreev (mechlist); } } return ret; } const gchar * test_sasl_auth_server_get_selected_mech (TestSaslAuthServer *self) { TestSaslAuthServerPrivate *priv = self->priv; return priv->selected_mech; } telepathy-gabble-0.18.2/lib/ext/wocky/tests/wocky-connector-test.c0000644000175000017500000041560012201202754025127 0ustar00smcvsmcv00000000000000#include #include #include #include #include #include #include #include #include #ifdef G_OS_WIN32 #include #include #else #include #include #include #include #include #endif #ifdef G_OS_UNIX #include #endif #include #include "wocky-test-connector-server.h" #include "test-resolver.h" #include "wocky-test-helper.h" #include "config.h" #ifdef G_LOG_DOMAIN #undef G_LOG_DOMAIN #endif #define G_LOG_DOMAIN "wocky-connector-test" #define SASL_DB_NAME "sasl-test.db" #define INVISIBLE_HOST "unreachable.host" #define VISIBLE_HOST "reachable.host" #define REACHABLE "127.0.0.1" #define UNREACHABLE "127.255.255.255" #define DUFF_H0ST "no_such_host.at.all" #define OLD_SSL TRUE #define OLD_JABBER TRUE #define XMPP_V1 FALSE #define STARTTLS FALSE #define CERT_CHECK_STRICT FALSE #define CERT_CHECK_LENIENT TRUE #define TLS_REQUIRED TRUE #define PLAINTEXT_OK FALSE #define QUIET TRUE #define NOISY FALSE #define TLS TRUE #define NOTLS FALSE #define PLAIN FALSE #define DIGEST TRUE #define PORT_XMPP 5222 #define PORT_NONE 0 #define CONNECTOR_INTERNALS_TEST "/connector/basic/internals" #define OK 0 #define CONNECTOR_OK { OK, OK, OK, OK, OK, OK } static GError *error = NULL; static GResolver *original; static GResolver *kludged; static GMainLoop *mainloop; enum { OP_CONNECT = 0, OP_REGISTER, OP_CANCEL, }; enum { S_NO_ERROR = 0, S_WOCKY_AUTH_ERROR, S_WOCKY_CONNECTOR_ERROR, S_WOCKY_XMPP_CONNECTION_ERROR, S_WOCKY_TLS_CERT_ERROR, S_WOCKY_XMPP_STREAM_ERROR, S_G_IO_ERROR, S_G_RESOLVER_ERROR, S_ANY_ERROR = 0xff }; #define MAP(x) case S_##x: return x static GQuark map_static_domain (gint domain) { switch (domain) { MAP (WOCKY_AUTH_ERROR); MAP (WOCKY_CONNECTOR_ERROR); MAP (WOCKY_XMPP_CONNECTION_ERROR); MAP (WOCKY_TLS_CERT_ERROR); MAP (WOCKY_XMPP_STREAM_ERROR); MAP (G_IO_ERROR); MAP (G_RESOLVER_ERROR); default: g_assert_not_reached (); } } #undef MAP typedef void (*test_setup) (gpointer); typedef struct _ServerParameters ServerParameters; struct _ServerParameters { struct { gboolean tls; gchar *auth_mech; gchar *version; } features; struct { ServerProblem sasl; ConnectorProblem conn; } problem; struct { gchar *user; gchar *pass; } auth; guint port; CertSet cert; /* Extra server for see-other-host problem */ ServerParameters *extra_server; /* Runtime */ TestConnectorServer *server; GIOChannel *channel; guint watch; }; typedef struct { gchar *desc; gboolean quiet; struct { int domain; int code; int fallback_code; gchar *mech; gchar *used_mech; gpointer xmpp; gchar *jid; gchar *sid; } result; ServerParameters server_parameters; struct { char *srv; guint port; char *host; char *addr; char *srvhost; } dns; struct { gboolean require_tls; struct { gchar *jid; gchar *pass; gboolean secure; gboolean tls; } auth; struct { gchar *host; guint port; gboolean jabber; gboolean ssl; gboolean lax_ssl; const gchar *ca; } options; int op; test_setup setup; } client; /* Runtime */ WockyConnector *connector; gboolean ok; } test_t; static void _set_connector_email_prop (test_t *test) { g_object_set (G_OBJECT (test->connector), "email", "foo@bar.org", NULL); } ServerParameters see_other_host_extra_server = { { TLS, NULL }, { SERVER_PROBLEM_NO_PROBLEM, CONNECTOR_OK }, { "moose", "something" }, 8222 }; test_t tests[] = { /* basic connection test, no SRV record, no host or port supplied: */ /* { "/name/of/test", SUPPRESS_STDERR, // result of test: { DOMAIN, CODE, FALLBACK_CODE, AUTH_MECH_USED, XMPP_CONNECTION_PLACEHOLDER }, // When an error is expected it should match the domain and either // the given CODE or the FALLBACK_CODE (GIO over time became more // specific about the error codes it gave in certain conditions) // Server Details: { { TLS_SUPPORT, AUTH_MECH_OR_NULL_FOR_ALL }, { SERVER_PROBLEM..., CONNECTOR_PROBLEM... }, { USERNAME, PASSWORD }, SERVER_LISTEN_PORT, SERVER_CERT }, // Fake DNS Record: // SRV_HOSTs SRV record → { HOSTNAME, PORT } // HOSTs A record → IP_ADDRESS // SRV_HOSTs A record → IP_ADDR_OF_SRV_HOST { SRV_HOST, PORT, HOSTNAME, IP_ADDRESS, IP_ADDR_OF_SRV_HOST }, // Client Details { TLS_REQUIRED, { BARE_JID, PASSWORD, MUST_BE_DIGEST_AUTH, MUST_BE_SECURE }, { XMPP_HOSTNAME_OR_NULL, XMPP_PORT_OR_ZERO, OLD_JABBER, OLD_SSL } } SERVER_PROCESS_ID }, */ /* simple connection, followed by checks on all the internal state * * and get/set property methods to make sure they work */ { CONNECTOR_INTERNALS_TEST, NOISY, { S_NO_ERROR, }, { { TLS, NULL }, { SERVER_PROBLEM_NO_PROBLEM, CONNECTOR_OK }, { "moose", "something" }, PORT_XMPP }, { NULL, 0, "weasel-juice.org", REACHABLE, NULL }, { PLAINTEXT_OK, { "moose@weasel-juice.org", "something", FALSE, NOTLS }, { NULL, 0 } } }, { "/connector/see-other-host", NOISY, { S_NO_ERROR, }, { { TLS, NULL }, { SERVER_PROBLEM_NO_PROBLEM, { XMPP_PROBLEM_SEE_OTHER_HOST, OK, OK, OK, OK } }, { "moose", "something" }, PORT_XMPP, CERT_STANDARD, &see_other_host_extra_server }, { "weasel-juice.org", PORT_XMPP, "thud.org", REACHABLE, UNREACHABLE }, { PLAINTEXT_OK, { "moose@weasel-juice.org", "something", PLAIN, NOTLS }, { NULL, 0 } } }, /* No SRV or connect host specified */ { "/connector/basic/noserv/nohost/noport", NOISY, { S_NO_ERROR, }, { { TLS, NULL }, { SERVER_PROBLEM_NO_PROBLEM, CONNECTOR_OK }, { "moose", "something" }, PORT_XMPP }, { NULL, 0, "weasel-juice.org", REACHABLE, NULL }, { PLAINTEXT_OK, { "moose@weasel-juice.org", "something", PLAIN, NOTLS }, { NULL, 0 } } }, /* No SRV or connect host specified, connect port specified */ { "/connector/basic/noserv/nohost/port", NOISY, { S_NO_ERROR, }, { { TLS, NULL }, { SERVER_PROBLEM_NO_PROBLEM, CONNECTOR_OK }, { "moose", "something" }, 8222 }, { NULL, 0, "weasel-juice.org", REACHABLE, NULL }, { PLAINTEXT_OK, { "moose@weasel-juice.org", "something", PLAIN, NOTLS }, { NULL, 8222 } } }, /* No SRV or connect host specified, bad port specified: FAIL */ { "/connector/basic/noserv/nohost/duffport", NOISY, { S_G_IO_ERROR, G_IO_ERROR_CONNECTION_REFUSED, G_IO_ERROR_FAILED }, { { TLS, NULL }, { SERVER_PROBLEM_NO_PROBLEM, CONNECTOR_OK }, { "moose", "something" }, 8222 }, { NULL, 0, "weasel-juice.org", REACHABLE, NULL }, { PLAINTEXT_OK, { "moose@weasel-juice.org", "something", PLAIN, NOTLS }, { NULL, 8221 } } }, /* No SRV record, connect host specified */ { "/connector/basic/noserv/host/noport", NOISY, { S_NO_ERROR, }, { { TLS, NULL }, { SERVER_PROBLEM_NO_PROBLEM, CONNECTOR_OK }, { "moose", "something" }, PORT_XMPP }, { NULL, 0, "schadenfreude.org", REACHABLE, NULL }, { PLAINTEXT_OK, { "moose@weasel-juice.org", "something", PLAIN, NOTLS }, { "schadenfreude.org", 0 } } }, /* No SRV record, connect host and port specified */ { "/connector/basic/noserv/host/port", NOISY, { S_NO_ERROR, }, { { TLS, NULL }, { SERVER_PROBLEM_NO_PROBLEM, CONNECTOR_OK }, { "moose", "something" }, 5555 }, { NULL, 0, "meerkats.net", REACHABLE, NULL }, { PLAINTEXT_OK, { "moose@weasel-juice.org", "something", PLAIN, NOTLS }, { "meerkats.net", 5555 } } }, /* No SRV record, connect host and bad port specified: FAIL */ { "/connector/basic/noserv/host/duffport", NOISY, { S_G_IO_ERROR, G_IO_ERROR_CONNECTION_REFUSED, G_IO_ERROR_FAILED }, { { TLS, NULL }, { SERVER_PROBLEM_NO_PROBLEM, CONNECTOR_OK }, { "moose", "something" }, 5555 }, { NULL, 0, "meerkats.net", REACHABLE, NULL }, { PLAINTEXT_OK, { "moose@weasel-juice.org", "something", PLAIN, NOTLS }, { "meerkats.net", 5554 } } }, /* No SRV record, bad connect host: FAIL */ { "/connector/basic/noserv/duffhost/noport", NOISY, { S_G_RESOLVER_ERROR, G_RESOLVER_ERROR_NOT_FOUND, -1 }, { { TLS, NULL }, { SERVER_PROBLEM_NO_PROBLEM, CONNECTOR_OK }, { NULL, NULL }, PORT_NONE }, { NULL, 0, NULL, NULL, NULL }, { PLAINTEXT_OK, { "moose@weasel-juice.org", "something", PLAIN, NOTLS }, { DUFF_H0ST, 0 } } }, /* No SRV record, bad connect host, port specified: FAIL */ { "/connector/basic/noserv/duffhost/port", NOISY, { S_G_RESOLVER_ERROR, G_RESOLVER_ERROR_NOT_FOUND, -1 }, { { TLS, NULL }, { SERVER_PROBLEM_NO_PROBLEM, CONNECTOR_OK }, { NULL, NULL }, PORT_NONE }, { NULL, 0, NULL, NULL, NULL }, { PLAINTEXT_OK, { "moose@weasel-juice.org", "something", PLAIN, NOTLS }, { "still.no_such_host.at.all", 23 } } }, /* SRV record specified */ { "/connector/basic/serv/nohost/noport", NOISY, { S_NO_ERROR, }, { { TLS, NULL }, { SERVER_PROBLEM_NO_PROBLEM, CONNECTOR_OK }, { "moose", "something" }, 5050 }, { "weasel-juice.org", 5050, "thud.org", REACHABLE, UNREACHABLE }, { PLAINTEXT_OK, { "moose@weasel-juice.org", "something", PLAIN, NOTLS }, { NULL, 0 } } }, /* SRV record specified, port specified: ignore SRV and connect */ { "/connector/basic/serv/nohost/port", NOISY, { S_NO_ERROR, }, { { TLS, NULL }, { SERVER_PROBLEM_NO_PROBLEM, CONNECTOR_OK }, { "moose", "something" }, 5051 }, { "weasel-juice.org", 5050, "thud.org", UNREACHABLE, REACHABLE }, { PLAINTEXT_OK, { "moose@weasel-juice.org", "something", PLAIN, NOTLS }, { NULL, 5051 } } }, /* SRV record specified, bad port: ignore SRV and FAIL */ { "/connector/basic/serv/nohost/duffport", NOISY, { S_G_IO_ERROR, G_IO_ERROR_CONNECTION_REFUSED, G_IO_ERROR_FAILED }, { { TLS, NULL }, { SERVER_PROBLEM_NO_PROBLEM, CONNECTOR_OK }, { "moose", "something" }, 5051 }, { "weasel-juice.org", 5050, "thud.org", UNREACHABLE, REACHABLE }, { PLAINTEXT_OK, { "moose@weasel-juice.org", "something", PLAIN, NOTLS }, { NULL, 5050 } } }, /* SRV record, connect host specified: use connect host */ { "/connector/basic/serv/host/noport", NOISY, { S_NO_ERROR, }, { { TLS, NULL }, { SERVER_PROBLEM_NO_PROBLEM, CONNECTOR_OK }, { "moose", "something" }, PORT_XMPP }, { "weasel-juice.org", 5050, "thud.org", UNREACHABLE, UNREACHABLE }, { PLAINTEXT_OK, { "moose@weasel-juice.org", "something", PLAIN, NOTLS }, { VISIBLE_HOST, 0 } } }, /* SRV, connect host and port specified: use host and port */ { "/connector/basic/serv/host/port", NOISY, { S_NO_ERROR, }, { { TLS, NULL }, { SERVER_PROBLEM_NO_PROBLEM, CONNECTOR_OK }, { "moose", "something" }, 5656 }, { "weasel-juice.org", 5050, "thud.org", UNREACHABLE, UNREACHABLE }, { PLAINTEXT_OK, { "moose@weasel-juice.org", "something", PLAIN, NOTLS }, { VISIBLE_HOST, 5656 } } }, /* SRV record, connect host, bad port: ignore SRV, FAIL */ { "/connector/basic/serv/host/duffport", NOISY, { S_G_IO_ERROR, G_IO_ERROR_CONNECTION_REFUSED, G_IO_ERROR_FAILED }, { { TLS, NULL }, { SERVER_PROBLEM_NO_PROBLEM, CONNECTOR_OK }, { "moose", "something" }, 5656 }, { "weasel-juice.org", 5050, "thud.org", UNREACHABLE, UNREACHABLE }, { PLAINTEXT_OK, { "moose@weasel-juice.org", "something", PLAIN, NOTLS }, { VISIBLE_HOST, 5655 } } }, /* SRV record, bad connect host: use bad host and FAIL */ { "/connector/basic/serv/duffhost/noport", NOISY, { S_G_RESOLVER_ERROR, G_RESOLVER_ERROR_NOT_FOUND, -1 }, { { TLS, NULL }, { SERVER_PROBLEM_NO_PROBLEM, CONNECTOR_OK }, { "moose", "something" }, PORT_XMPP }, { "weasel-juice.org", PORT_XMPP, "thud.org", REACHABLE, REACHABLE }, { PLAINTEXT_OK, { "moose@weasel-juice.org", "something", PLAIN, NOTLS }, { DUFF_H0ST, 0 } } }, /* SRV record, bad connect host, connect port: use bad host and FAIL */ { "/connector/basic/serv/duffhost/port", NOISY, { S_G_RESOLVER_ERROR, G_RESOLVER_ERROR_NOT_FOUND, -1 }, { { TLS, NULL }, { SERVER_PROBLEM_NO_PROBLEM, CONNECTOR_OK }, { "moose", "something" }, PORT_XMPP }, { "weasel-juice.org", PORT_XMPP, "thud.org", REACHABLE, REACHABLE }, { PLAINTEXT_OK, { "moose@weasel-juice.org", "something", PLAIN, NOTLS }, { DUFF_H0ST, PORT_XMPP } } }, /* Facebook Chat has a broken SRV record: you ask for * _xmpp-client._tcp.chat.facebook.com, and it gives you back a CNAME! So * g_socket_client_connect_to_service() fails. But as it happens the real * result should have just been chat.facebook.com anyway, so Wocky tries to * fall back to that. * * So this test has a fake SRV record for an unreachable server, but * expects to succeed because it's listening on the default XMPP port on * our hypothetical 'weasel-juice.org'. */ { "/connector/basic/facebook-chat-srv-workaround", NOISY, { S_NO_ERROR, }, { { TLS, NULL }, { SERVER_PROBLEM_NO_PROBLEM, CONNECTOR_OK }, { "moose", "something" }, PORT_XMPP }, { "weasel-juice.org", PORT_XMPP, "thud.org", UNREACHABLE, REACHABLE }, { PLAINTEXT_OK, { "moose@weasel-juice.org", "something", PLAIN, NOTLS }, { NULL, 0 } } }, /* Further to the above test, this one tests the case where the fallback * doesn't work either. The server isn't listening anywhere (that's the * PORT_NONE in the server_parameters sub-struct), and thud.org (the result * of the SRV lookup) is unreachable. So the connection should fail. */ { "/connector/basic/duffserv/nohost/noport", NOISY, { S_G_IO_ERROR, G_IO_ERROR_NETWORK_UNREACHABLE, G_IO_ERROR_FAILED }, { { TLS, NULL }, { SERVER_PROBLEM_NO_PROBLEM, CONNECTOR_OK }, { "moose", "something" }, PORT_NONE }, { "not.an.xmpp.server", PORT_XMPP, "thud.org", UNREACHABLE, REACHABLE }, { PLAINTEXT_OK, { "moose@not.an.xmpp.server", "something", PLAIN, NOTLS }, { NULL, 0 } } }, /* Bad SRV record, port specified, ignore SRV and connect to domain host */ { "/connector/basic/duffserv/nohost/port", NOISY, { S_NO_ERROR, }, { { TLS, NULL }, { SERVER_PROBLEM_NO_PROBLEM, CONNECTOR_OK }, { "moose", "something" }, 5050 }, { "weasel-juice.org", PORT_XMPP, "thud.org", UNREACHABLE, REACHABLE }, { PLAINTEXT_OK, { "moose@weasel-juice.org", "something", PLAIN, NOTLS }, { NULL, 5050 } } }, /* Bad SRV record, bad port specified, ignore SRV and FAIL */ { "/connector/basic/duffserv/nohost/duffport", NOISY, { S_G_IO_ERROR, G_IO_ERROR_CONNECTION_REFUSED, G_IO_ERROR_FAILED }, { { TLS, NULL }, { SERVER_PROBLEM_NO_PROBLEM, CONNECTOR_OK }, { "moose", "something" }, 5050 }, { "weasel-juice.org", PORT_XMPP, "thud.org", UNREACHABLE, REACHABLE }, { PLAINTEXT_OK, { "moose@weasel-juice.org", "something", PLAIN, NOTLS }, { NULL, 5049 } } }, /* Bad SRV record, connect host specified, ignore SRV */ { "/connector/basic/duffserv/host/noport", NOISY, { S_NO_ERROR, }, { { TLS, NULL }, { SERVER_PROBLEM_NO_PROBLEM, CONNECTOR_OK }, { "moose", "something" }, PORT_XMPP }, { "weasel-juice.org", 5050, "thud.org", UNREACHABLE, UNREACHABLE }, { PLAINTEXT_OK, { "moose@weasel-juice.org", "something", PLAIN, NOTLS }, { VISIBLE_HOST, 0 } } }, /* Bad SRV record, connect host and port given: ignore SRV */ { "/connector/basic/duffserv/host/port", NOISY, { S_NO_ERROR, }, { { TLS, NULL }, { SERVER_PROBLEM_NO_PROBLEM, CONNECTOR_OK }, { "moose", "something" }, 5151 }, { "weasel-juice.org", 5050, "thud.org", UNREACHABLE, REACHABLE }, { PLAINTEXT_OK, { "moose@weasel-juice.org", "something", PLAIN, NOTLS }, { VISIBLE_HOST, 5151 } } }, /* Bad SRV record, connect host and bad port, ignore SRV and FAIL */ { "/connector/basic/duffserv/host/duffport", NOISY, { S_G_IO_ERROR, G_IO_ERROR_CONNECTION_REFUSED, G_IO_ERROR_FAILED }, { { TLS, NULL }, { SERVER_PROBLEM_NO_PROBLEM, CONNECTOR_OK }, { "moose", "something" }, 5151 }, { "weasel-juice.org", 5050, "thud.org", UNREACHABLE, REACHABLE }, { PLAINTEXT_OK, { "moose@weasel-juice.org", "something", PLAIN, NOTLS }, { VISIBLE_HOST, 5149 } } }, /* Bad SRV record, bad host and bad port: Just FAIL */ { "/connector/basic/duffserv/duffhost/noport", NOISY, { S_G_IO_ERROR, G_IO_ERROR_NETWORK_UNREACHABLE, G_IO_ERROR_FAILED }, { { TLS, NULL }, { SERVER_PROBLEM_NO_PROBLEM, CONNECTOR_OK }, { "moose", "something" }, PORT_XMPP }, { "weasel-juice.org", 5050, "thud.org", UNREACHABLE, REACHABLE }, { PLAINTEXT_OK, { "moose@weasel-juice.org", "something", PLAIN, NOTLS }, { INVISIBLE_HOST, 0 } } }, /*Bad SRV and connect host, ignore SRV and FAIL */ { "/connector/basic/duffserv/duffhost/port", NOISY, { S_G_IO_ERROR, G_IO_ERROR_NETWORK_UNREACHABLE, G_IO_ERROR_FAILED }, { { TLS, NULL }, { SERVER_PROBLEM_NO_PROBLEM, CONNECTOR_OK }, { "moose", "something" }, 5151 }, { "weasel-juice.org", 5050, "thud.org", UNREACHABLE, REACHABLE }, { PLAINTEXT_OK, { "moose@weasel-juice.org", "something", PLAIN, NOTLS }, { INVISIBLE_HOST, 5151 } } }, /* ******************************************************************* * * that's it for the basic DNS/connection-logic tests * * now onto the post-tcp-connect stages: */ { "/connector/auth/secure/no-tlsplain/notls/nodigest", NOISY, { S_WOCKY_AUTH_ERROR, WOCKY_AUTH_ERROR_NO_SUPPORTED_MECHANISMS, -1 }, { { NOTLS, "PLAIN" }, { SERVER_PROBLEM_NO_PROBLEM, CONNECTOR_OK }, { "moose", "something" }, PORT_XMPP }, { "weasel-juice.org", PORT_XMPP, "thud.org", REACHABLE, UNREACHABLE }, { PLAINTEXT_OK, { "moose@weasel-juice.org", "something", DIGEST, TLS }, { NULL, 0 } } }, { "/connector/auth/secure/no-tlsplain/notls/digest", NOISY, { S_NO_ERROR, 0, 0, "DIGEST-MD5" }, { { NOTLS, NULL }, { SERVER_PROBLEM_NO_PROBLEM, CONNECTOR_OK }, { "moose", "something" }, PORT_XMPP }, { "weasel-juice.org", PORT_XMPP, "thud.org", REACHABLE, UNREACHABLE }, { PLAINTEXT_OK, { "moose@weasel-juice.org", "something", DIGEST, TLS }, { NULL, 0 } } }, { "/connector/auth/insecure/no-tlsplain/notls/nodigest", NOISY, { S_NO_ERROR, 0, 0, "PLAIN" }, { { NOTLS, "PLAIN" }, { SERVER_PROBLEM_NO_PROBLEM, CONNECTOR_OK }, { "moose", "something" }, PORT_XMPP }, { "weasel-juice.org", PORT_XMPP, "thud.org", REACHABLE, UNREACHABLE }, { PLAINTEXT_OK, { "moose@weasel-juice.org", "something", PLAIN, NOTLS }, { NULL, 0 } } }, { "/connector/auth/insecure/no-tlsplain/notls/digest", NOISY, { S_NO_ERROR, 0, 0, "DIGEST-MD5" }, { { NOTLS, NULL }, { SERVER_PROBLEM_NO_PROBLEM, CONNECTOR_OK }, { "moose", "something" }, PORT_XMPP }, { "weasel-juice.org", PORT_XMPP, "thud.org", REACHABLE, UNREACHABLE }, { PLAINTEXT_OK, { "moose@weasel-juice.org", "something", PLAIN, NOTLS }, { NULL, 0 } } }, /* *************************************************************** * * This block of tests will fail as we don't advertise TLS support */ { "/connector/auth/insecure/no-tlsplain/notls/any", NOISY, { S_WOCKY_CONNECTOR_ERROR, WOCKY_CONNECTOR_ERROR_TLS_UNAVAILABLE, -1 }, { { NOTLS, NULL }, { SERVER_PROBLEM_NO_PROBLEM, CONNECTOR_OK }, { "moose", "something" }, PORT_XMPP }, { "weasel-juice.org", PORT_XMPP, "thud.org", REACHABLE, UNREACHABLE }, { TLS_REQUIRED, { "moose@weasel-juice.org", "something", PLAIN, TLS }, { NULL, 0 } } }, { "/connector/auth/insecure/tlsplain/notls/any", NOISY, { S_WOCKY_CONNECTOR_ERROR, WOCKY_CONNECTOR_ERROR_TLS_UNAVAILABLE, -1 }, { { NOTLS, NULL }, { SERVER_PROBLEM_NO_PROBLEM, CONNECTOR_OK }, { "moose", "something" }, PORT_XMPP }, { "weasel-juice.org", PORT_XMPP, "thud.org", REACHABLE, UNREACHABLE }, { TLS_REQUIRED, { "moose@weasel-juice.org", "something", PLAIN, NOTLS }, { NULL, 0 } } }, { "/connector/auth/secure/no-tlsplain/notls/any", NOISY, { S_WOCKY_CONNECTOR_ERROR, WOCKY_CONNECTOR_ERROR_TLS_UNAVAILABLE, -1 }, { { NOTLS, NULL }, { SERVER_PROBLEM_NO_PROBLEM, CONNECTOR_OK }, { "moose", "something" }, PORT_XMPP }, { "weasel-juice.org", PORT_XMPP, "thud.org", REACHABLE, UNREACHABLE }, { TLS_REQUIRED, { "moose@weasel-juice.org", "something", TRUE, TLS }, { NULL, 0 } } }, { "/connector/auth/secure/tlsplain/notls/any", NOISY, { S_WOCKY_CONNECTOR_ERROR, WOCKY_CONNECTOR_ERROR_TLS_UNAVAILABLE, -1 }, { { NOTLS, NULL }, { SERVER_PROBLEM_NO_PROBLEM, CONNECTOR_OK }, { "moose", "something" }, PORT_XMPP }, { "weasel-juice.org", PORT_XMPP, "thud.org", REACHABLE, UNREACHABLE }, { TLS_REQUIRED, { "moose@weasel-juice.org", "something", TRUE, NOTLS }, { NULL, 0 } } }, /* **************************************************************** * * this will be a mix of failures and sucesses depending on whether * * we allow plain auth or not */ { "/connector/auth/secure/no-tlsplain/tls/plain", NOISY, { S_WOCKY_AUTH_ERROR, WOCKY_AUTH_ERROR_NO_SUPPORTED_MECHANISMS, -1 }, { { TLS, "PLAIN" }, { SERVER_PROBLEM_INVALID_PASSWORD, CONNECTOR_OK }, { "moose", "something" }, PORT_XMPP }, { "weasel-juice.org", PORT_XMPP, "thud.org", REACHABLE, UNREACHABLE }, { PLAINTEXT_OK, { "moose@weasel-juice.org", "something", DIGEST, TLS }, { NULL, 0 } } }, { "/connector/auth/secure/tlsplain/tls/plain", NOISY, { S_NO_ERROR, 0, 0, "PLAIN" }, { { TLS, "PLAIN" }, { SERVER_PROBLEM_NO_PROBLEM, CONNECTOR_OK }, { "moose", "something" }, PORT_XMPP }, { "weasel-juice.org", PORT_XMPP, "thud.org", REACHABLE, UNREACHABLE }, { PLAINTEXT_OK, { "moose@weasel-juice.org", "something", PLAIN, TLS }, { NULL, 0 } } }, { "/connector/auth/insecure/no-tlsplain/tls/plain", NOISY, { S_NO_ERROR, 0, 0, "PLAIN" }, { { TLS, "PLAIN" }, { SERVER_PROBLEM_NO_PROBLEM, CONNECTOR_OK }, { "moose", "something" }, PORT_XMPP }, { "weasel-juice.org", PORT_XMPP, "thud.org", REACHABLE, UNREACHABLE }, { PLAINTEXT_OK, { "moose@weasel-juice.org", "something", DIGEST, NOTLS }, { NULL, 0 } } }, { "/connector/auth/insecure/tlsplain/tls/plain", NOISY, { S_NO_ERROR, 0, 0, "PLAIN" }, { { TLS, "PLAIN" }, { SERVER_PROBLEM_NO_PROBLEM, CONNECTOR_OK }, { "moose", "something" }, PORT_XMPP }, { "weasel-juice.org", PORT_XMPP, "thud.org", REACHABLE, UNREACHABLE }, { PLAINTEXT_OK, { "moose@weasel-juice.org", "something", PLAIN, NOTLS }, { NULL, 0 } } }, { "/connector/tls+auth/secure/no-tlsplain/tls/plain", NOISY, { S_WOCKY_AUTH_ERROR, WOCKY_AUTH_ERROR_NO_SUPPORTED_MECHANISMS, -1 }, { { TLS, "PLAIN" }, { SERVER_PROBLEM_NO_PROBLEM, CONNECTOR_OK }, { "moose", "something" }, PORT_XMPP }, { "weasel-juice.org", PORT_XMPP, "thud.org", REACHABLE, UNREACHABLE }, { PLAINTEXT_OK, { "moose@weasel-juice.org", "something", DIGEST, TLS }, { NULL, 0 } } }, { "/connector/tls+auth/secure/tlsplain/tls/plain", NOISY, { S_NO_ERROR, 0, 0, "PLAIN" }, { { TLS, "PLAIN" }, { SERVER_PROBLEM_NO_PROBLEM, CONNECTOR_OK }, { "moose", "something" }, PORT_XMPP }, { "weasel-juice.org", PORT_XMPP, "thud.org", REACHABLE, UNREACHABLE }, { PLAINTEXT_OK, { "moose@weasel-juice.org", "something", PLAIN, TLS }, { NULL, 0 } } }, { "/connector/tls+auth/insecure/no-tlsplain/tls/plain", NOISY, { S_NO_ERROR, 0, 0, "PLAIN" }, { { TLS, "PLAIN" }, { SERVER_PROBLEM_NO_PROBLEM, CONNECTOR_OK }, { "moose", "something" }, PORT_XMPP }, { "weasel-juice.org", PORT_XMPP, "thud.org", REACHABLE, UNREACHABLE }, { PLAINTEXT_OK, { "moose@weasel-juice.org", "something", DIGEST, NOTLS }, { NULL, 0 } } }, { "/connector/tls+auth/insecure/tlsplain/tls/plain", NOISY, { S_NO_ERROR, 0, 0, "PLAIN" }, { { TLS, "PLAIN" }, { SERVER_PROBLEM_NO_PROBLEM, CONNECTOR_OK }, { "moose", "something" }, PORT_XMPP }, { "weasel-juice.org", PORT_XMPP, "thud.org", REACHABLE, UNREACHABLE }, { PLAINTEXT_OK, { "moose@weasel-juice.org", "something", PLAIN, NOTLS }, { NULL, 0 } } }, /* **************************************************************** * * these should all be digest auth successes */ { "/connector/auth/secure/no-tlsplain/tls/digest", NOISY, { S_NO_ERROR, 0, 0, "DIGEST-MD5" }, { { TLS, NULL }, { SERVER_PROBLEM_NO_PROBLEM, CONNECTOR_OK }, { "moose", "something" }, PORT_XMPP }, { "weasel-juice.org", PORT_XMPP, "thud.org", REACHABLE, UNREACHABLE }, { PLAINTEXT_OK, { "moose@weasel-juice.org", "something", DIGEST, TLS }, { NULL, 0 } } }, { "/connector/auth/secure/tlsplain/tls/digest", NOISY, { S_NO_ERROR, 0, 0, "DIGEST-MD5" }, { { TLS, NULL }, { SERVER_PROBLEM_NO_PROBLEM, CONNECTOR_OK }, { "moose", "something" }, PORT_XMPP }, { "weasel-juice.org", PORT_XMPP, "thud.org", REACHABLE, UNREACHABLE }, { PLAINTEXT_OK, { "moose@weasel-juice.org", "something", PLAIN, TLS }, { NULL, 0 } } }, { "/connector/auth/insecure/no-tlsplain/tls/digest", NOISY, { S_NO_ERROR, 0, 0, "DIGEST-MD5" }, { { TLS, NULL }, { SERVER_PROBLEM_NO_PROBLEM, CONNECTOR_OK }, { "moose", "something" }, PORT_XMPP }, { "weasel-juice.org", PORT_XMPP, "thud.org", REACHABLE, UNREACHABLE }, { PLAINTEXT_OK, { "moose@weasel-juice.org", "something", DIGEST, NOTLS }, { NULL, 0 } } }, { "/connector/auth/insecure/tlsplain/tls/digest", NOISY, { S_NO_ERROR, 0, 0, "DIGEST-MD5" }, { { TLS, NULL }, { SERVER_PROBLEM_NO_PROBLEM, CONNECTOR_OK }, { "moose", "something" }, PORT_XMPP }, { "weasel-juice.org", PORT_XMPP, "thud.org", REACHABLE, UNREACHABLE }, { PLAINTEXT_OK, { "moose@weasel-juice.org", "something", PLAIN, NOTLS }, { NULL, 0 } } }, { "/connector/tls+auth/secure/no-tlsplain/tls/digest", NOISY, { S_NO_ERROR, 0, 0, "DIGEST-MD5" }, { { TLS, NULL }, { SERVER_PROBLEM_NO_PROBLEM, CONNECTOR_OK }, { "moose", "something" }, PORT_XMPP }, { "weasel-juice.org", PORT_XMPP, "thud.org", REACHABLE, UNREACHABLE }, { PLAINTEXT_OK, { "moose@weasel-juice.org", "something", DIGEST, TLS }, { NULL, 0 } } }, { "/connector/tls+auth/secure/tlsplain/tls/digest", NOISY, { S_NO_ERROR, 0, 0, "DIGEST-MD5" }, { { TLS, NULL }, { SERVER_PROBLEM_NO_PROBLEM, CONNECTOR_OK }, { "moose", "something" }, PORT_XMPP }, { "weasel-juice.org", PORT_XMPP, "thud.org", REACHABLE, UNREACHABLE }, { PLAINTEXT_OK, { "moose@weasel-juice.org", "something", PLAIN, TLS }, { NULL, 0 } } }, { "/connector/tls+auth/insecure/no-tlsplain/tls/digest", NOISY, { S_NO_ERROR, 0, 0, "DIGEST-MD5" }, { { TLS, NULL }, { SERVER_PROBLEM_NO_PROBLEM, CONNECTOR_OK }, { "moose", "something" }, PORT_XMPP }, { "weasel-juice.org", PORT_XMPP, "thud.org", REACHABLE, UNREACHABLE }, { PLAINTEXT_OK, { "moose@weasel-juice.org", "something", DIGEST, NOTLS }, { NULL, 0 } } }, { "/connector/tls+auth/insecure/tlsplain/tls/digest", NOISY, { S_NO_ERROR, 0, 0, "DIGEST-MD5" }, { { TLS, NULL }, { SERVER_PROBLEM_NO_PROBLEM, CONNECTOR_OK }, { "moose", "something" }, PORT_XMPP }, { "weasel-juice.org", PORT_XMPP, "thud.org", REACHABLE, UNREACHABLE }, { PLAINTEXT_OK, { "moose@weasel-juice.org", "something", PLAIN, NOTLS }, { NULL, 0 } } }, /* ***************************************************************** * * SASL problems */ { "/connector/problem/sasl/bad-pass", NOISY, { S_WOCKY_AUTH_ERROR, WOCKY_AUTH_ERROR_FAILURE, -1 }, { { TLS, NULL }, { SERVER_PROBLEM_INVALID_PASSWORD, CONNECTOR_OK }, { "foo", "bar" }, PORT_XMPP }, { "weasel-juice.org", PORT_XMPP, "thud.org", REACHABLE, UNREACHABLE }, { PLAINTEXT_OK, { "foo@weasel-juice.org", "notbar", PLAIN, NOTLS }, { NULL, 0 } } }, { "/connector/problem/sasl/bad-user", NOISY, { S_WOCKY_AUTH_ERROR, WOCKY_AUTH_ERROR_FAILURE, -1 }, { { TLS, NULL }, { SERVER_PROBLEM_INVALID_USERNAME, CONNECTOR_OK }, { "moose", "something" }, PORT_XMPP }, { "weasel-juice.org", PORT_XMPP, "thud.org", REACHABLE, UNREACHABLE }, { PLAINTEXT_OK, { "caribou@weasel-juice.org", "something", PLAIN, NOTLS }, { NULL, 0 } } }, { "/connector/problem/sasl/no-sasl", NOISY, { S_WOCKY_AUTH_ERROR, WOCKY_AUTH_ERROR_NOT_SUPPORTED, -1 }, { { TLS, NULL }, { SERVER_PROBLEM_NO_SASL, CONNECTOR_OK }, { "moose", "something" }, PORT_XMPP }, { "weasel-juice.org", PORT_XMPP, "thud.org", REACHABLE, UNREACHABLE }, { PLAINTEXT_OK, { "moose@weasel-juice.org", "something", PLAIN, NOTLS }, { NULL, 0 } } }, { "/connector/problem/sasl/no-mechanisms", NOISY, { S_WOCKY_AUTH_ERROR, WOCKY_AUTH_ERROR_NOT_SUPPORTED, -1 }, { { TLS, NULL }, { SERVER_PROBLEM_NO_MECHANISMS, CONNECTOR_OK }, { "moose", "something" }, PORT_XMPP }, { "weasel-juice.org", PORT_XMPP, "thud.org", REACHABLE, UNREACHABLE }, { PLAINTEXT_OK, { "moose@weasel-juice.org", "something", PLAIN, NOTLS }, { NULL, 0 } } }, { "/connector/problem/sasl/bad-mechanism", NOISY, { S_WOCKY_AUTH_ERROR, WOCKY_AUTH_ERROR_NO_SUPPORTED_MECHANISMS, -1 }, { { TLS, "omg-poniez" }, { SERVER_PROBLEM_NO_PROBLEM, CONNECTOR_OK }, { "moose", "something" }, PORT_XMPP }, { "weasel-juice.org", PORT_XMPP, "thud.org", REACHABLE, UNREACHABLE }, { PLAINTEXT_OK, { "moose@weasel-juice.org", "something", PLAIN, NOTLS }, { NULL, 0 } } }, /* ********************************************************************* */ /* TLS error conditions */ { "/connector/problem/tls/refused", NOISY, { S_WOCKY_CONNECTOR_ERROR, WOCKY_CONNECTOR_ERROR_TLS_REFUSED, -1 }, { { TLS, NULL }, { SERVER_PROBLEM_NO_PROBLEM, { XMPP_PROBLEM_TLS_REFUSED, OK, OK, OK, OK} }, { "moose", "something" }, PORT_XMPP }, { "weasel-juice.org", PORT_XMPP, "thud.org", REACHABLE, UNREACHABLE }, { PLAINTEXT_OK, { "moose@weasel-juice.org", "something", PLAIN, NOTLS }, { NULL, 0 } } }, /* ********************************************************************* * * Invalid JID */ { "/connector/problem/jid/invalid", NOISY, { S_WOCKY_CONNECTOR_ERROR, WOCKY_CONNECTOR_ERROR_BAD_JID, -1 }, { { TLS, NULL }, { SERVER_PROBLEM_NO_PROBLEM, CONNECTOR_OK }, { "moose", "something" }, PORT_NONE }, { NULL, 0, "thud.org", REACHABLE }, { PLAINTEXT_OK, { "bla@h@_b&la<>h", "something", PLAIN, NOTLS }, { "weasel-juice.org", PORT_XMPP } } }, { "/connector/problem/jid/domainless", NOISY, { S_WOCKY_CONNECTOR_ERROR, WOCKY_CONNECTOR_ERROR_BAD_JID, -1 }, { { TLS, NULL }, { SERVER_PROBLEM_NO_PROBLEM, CONNECTOR_OK }, { "moose", "something" }, PORT_NONE }, { "weasel-juice.org", 5001, "thud.org", REACHABLE, REACHABLE }, { PLAINTEXT_OK, { "moose@", "something", PLAIN, NOTLS }, { "weasel-juice.org", 0 } } }, /* ********************************************************************* * * XMPP errors */ { "/connector/problem/xmpp/version/0.x", NOISY, { S_WOCKY_CONNECTOR_ERROR, WOCKY_CONNECTOR_ERROR_NON_XMPP_V1_SERVER, -1 }, { { TLS, NULL, "0.9" }, { SERVER_PROBLEM_NO_PROBLEM, CONNECTOR_OK }, { "moose", "something" }, PORT_XMPP }, { "weasel-juice.org", PORT_XMPP, "thud.org", REACHABLE, UNREACHABLE }, { PLAINTEXT_OK, { "moose@weasel-juice.org", "something", PLAIN, NOTLS }, { NULL, 0 } } }, /* we actually tolerate > 1.0 versions */ { "/connector/problem/xmpp/version/1.x", NOISY, { S_NO_ERROR, 0, 0, "DIGEST-MD5" }, { { TLS, NULL, "1.1" }, { SERVER_PROBLEM_NO_PROBLEM, CONNECTOR_OK }, { "moose", "something" }, PORT_XMPP }, { "weasel-juice.org", PORT_XMPP, "thud.org", REACHABLE, UNREACHABLE }, { PLAINTEXT_OK, { "moose@weasel-juice.org", "something", PLAIN, NOTLS }, { NULL, 0 } } }, { "/connector/problem/xmpp/error/host-unknown", NOISY, { S_WOCKY_XMPP_STREAM_ERROR, WOCKY_XMPP_STREAM_ERROR_HOST_UNKNOWN, -1 }, { { TLS, NULL }, { SERVER_PROBLEM_NO_PROBLEM, { XMPP_PROBLEM_OTHER_HOST, OK, OK, OK, OK } }, { "moose", "something" }, PORT_XMPP }, { "weasel-juice.org", PORT_XMPP, "thud.org", REACHABLE, UNREACHABLE }, { PLAINTEXT_OK, { "moose@weasel-juice.org", "something", PLAIN, NOTLS }, { NULL, 0 } } }, { "/connector/problem/xmpp/error/tls-load", NOISY, { S_WOCKY_XMPP_STREAM_ERROR, WOCKY_XMPP_STREAM_ERROR_RESOURCE_CONSTRAINT, -1 }, { { TLS, NULL }, { SERVER_PROBLEM_NO_PROBLEM, { XMPP_PROBLEM_TLS_LOAD, OK, OK, OK, OK } }, { "moose", "something" }, PORT_XMPP }, { "weasel-juice.org", PORT_XMPP, "thud.org", REACHABLE, UNREACHABLE }, { PLAINTEXT_OK, { "moose@weasel-juice.org", "something", PLAIN, NOTLS }, { NULL, 0 } } }, { "/connector/problem/xmpp/error/bind-conflict", NOISY, { S_WOCKY_XMPP_STREAM_ERROR, WOCKY_XMPP_STREAM_ERROR_CONFLICT, -1 }, { { TLS, NULL }, { SERVER_PROBLEM_NO_PROBLEM, { OK, BIND_PROBLEM_CLASH, OK, OK, OK } }, { "moose", "something" }, PORT_XMPP }, { "weasel-juice.org", PORT_XMPP, "thud.org", REACHABLE, UNREACHABLE }, { PLAINTEXT_OK, { "moose@weasel-juice.org", "something", PLAIN, NOTLS }, { NULL, 0 } } }, { "/connector/problem/xmpp/error/session-fail", NOISY, { S_WOCKY_XMPP_STREAM_ERROR, WOCKY_XMPP_STREAM_ERROR_RESOURCE_CONSTRAINT, -1 }, { { TLS, NULL }, { SERVER_PROBLEM_NO_PROBLEM, { OK, OK, SESSION_PROBLEM_NO_SESSION, OK, OK } }, { "moose", "something" }, PORT_XMPP }, { "weasel-juice.org", PORT_XMPP, "thud.org", REACHABLE, UNREACHABLE }, { PLAINTEXT_OK, { "moose@weasel-juice.org", "something", PLAIN, NOTLS }, { NULL, 0 } } }, { "/connector/problem/xmpp/features", NOISY, { S_WOCKY_CONNECTOR_ERROR, WOCKY_CONNECTOR_ERROR_BAD_FEATURES, -1 }, { { TLS, NULL }, { SERVER_PROBLEM_NO_PROBLEM, { XMPP_PROBLEM_FEATURES, OK, OK, OK, OK } }, { "moose", "something" }, PORT_XMPP }, { "weasel-juice.org", PORT_XMPP, "thud.org", REACHABLE, UNREACHABLE }, { PLAINTEXT_OK, { "moose@weasel-juice.org", "something", PLAIN, NOTLS }, { NULL, 0 } } }, /* WOCKY_CONNECTOR_ERROR_BIND_UNAVAILABLE */ { "/connector/problem/xmpp/no-bind", NOISY, { S_WOCKY_CONNECTOR_ERROR, WOCKY_CONNECTOR_ERROR_BIND_UNAVAILABLE, -1 }, { { TLS, NULL }, { SERVER_PROBLEM_NO_PROBLEM, { XMPP_PROBLEM_CANNOT_BIND, OK, OK, OK, OK } }, { "moose", "something" }, PORT_XMPP }, { "weasel-juice.org", PORT_XMPP, "thud.org", REACHABLE, UNREACHABLE }, { PLAINTEXT_OK, { "moose@weasel-juice.org", "something", PLAIN, NOTLS }, { NULL, 0 } } }, /* WOCKY_CONNECTOR_ERROR_BIND_INVALID */ { "/connector/problem/xmpp/bind/invalid", NOISY, { S_WOCKY_CONNECTOR_ERROR, WOCKY_CONNECTOR_ERROR_BIND_INVALID, -1 }, { { TLS, NULL }, { SERVER_PROBLEM_NO_PROBLEM, { OK, BIND_PROBLEM_INVALID, OK, OK, OK } }, { "moose", "something" }, PORT_XMPP }, { "weasel-juice.org", PORT_XMPP, "thud.org", REACHABLE, UNREACHABLE }, { PLAINTEXT_OK, { "moose@weasel-juice.org", "something", PLAIN, NOTLS }, { NULL, 0 } } }, /* WOCKY_CONNECTOR_ERROR_BIND_DENIED */ { "/connector/problem/xmpp/bind/denied", NOISY, { S_WOCKY_CONNECTOR_ERROR, WOCKY_CONNECTOR_ERROR_BIND_DENIED, -1 }, { { TLS, NULL }, { SERVER_PROBLEM_NO_PROBLEM, { OK, BIND_PROBLEM_DENIED, OK, OK, OK } }, { "moose", "something" }, PORT_XMPP }, { "weasel-juice.org", PORT_XMPP, "thud.org", REACHABLE, UNREACHABLE }, { PLAINTEXT_OK, { "moose@weasel-juice.org", "something", PLAIN, NOTLS }, { NULL, 0 } } }, /* WOCKY_CONNECTOR_ERROR_BIND_CONFLICT */ { "/connector/problem/xmpp/bind/conflict", NOISY, { S_WOCKY_CONNECTOR_ERROR, WOCKY_CONNECTOR_ERROR_BIND_CONFLICT, -1 }, { { TLS, NULL }, { SERVER_PROBLEM_NO_PROBLEM, { OK, BIND_PROBLEM_CONFLICT, OK, OK, OK } }, { "moose", "something" }, PORT_XMPP }, { "weasel-juice.org", PORT_XMPP, "thud.org", REACHABLE, UNREACHABLE }, { PLAINTEXT_OK, { "moose@weasel-juice.org", "something", PLAIN, NOTLS }, { NULL, 0 } } }, /* WOCKY_CONNECTOR_ERROR_BIND_REJECTED */ { "/connector/problem/xmpp/bind/rejected", NOISY, { S_WOCKY_CONNECTOR_ERROR, WOCKY_CONNECTOR_ERROR_BIND_REJECTED, -1 }, { { TLS, NULL }, { SERVER_PROBLEM_NO_PROBLEM, { OK, BIND_PROBLEM_REJECTED, OK, OK, OK } }, { "moose", "something" }, PORT_XMPP }, { "weasel-juice.org", PORT_XMPP, "thud.org", REACHABLE, UNREACHABLE }, { PLAINTEXT_OK, { "moose@weasel-juice.org", "something", PLAIN, NOTLS }, { NULL, 0 } } }, /* WOCKY_CONNECTOR_ERROR_BIND_FAILED */ { "/connector/problem/xmpp/bind/failed", NOISY, { S_WOCKY_CONNECTOR_ERROR, WOCKY_CONNECTOR_ERROR_BIND_FAILED, -1 }, { { TLS, NULL }, { SERVER_PROBLEM_NO_PROBLEM, { OK, BIND_PROBLEM_FAILED, OK, OK, OK } }, { "moose", "something" }, PORT_XMPP }, { "weasel-juice.org", PORT_XMPP, "thud.org", REACHABLE, UNREACHABLE }, { PLAINTEXT_OK, { "moose@weasel-juice.org", "something", PLAIN, NOTLS }, { NULL, 0 } } }, { "/connector/problem/xmpp/bind/nonsense", NOISY, { S_WOCKY_CONNECTOR_ERROR, WOCKY_CONNECTOR_ERROR_BIND_FAILED, -1 }, { { TLS, NULL }, { SERVER_PROBLEM_NO_PROBLEM, { OK, BIND_PROBLEM_NONSENSE, OK, OK, OK } }, { "moose", "something" }, PORT_XMPP }, { "weasel-juice.org", PORT_XMPP, "thud.org", REACHABLE, UNREACHABLE }, { PLAINTEXT_OK, { "moose@weasel-juice.org", "something", PLAIN, NOTLS }, { NULL, 0 } } }, { "/connector/problem/xmpp/bind/no-jid", NOISY, { S_NO_ERROR, 0, 0, "DIGEST-MD5" }, { { TLS, NULL }, { SERVER_PROBLEM_NO_PROBLEM, { OK, BIND_PROBLEM_NO_JID, OK, OK, OK } }, { "moose", "something" }, PORT_XMPP }, { "weasel-juice.org", PORT_XMPP, "thud.org", REACHABLE, UNREACHABLE }, { PLAINTEXT_OK, { "moose@weasel-juice.org", "something", PLAIN, NOTLS }, { NULL, 0 } } }, { "/connector/problem/xmpp/session/none", NOISY, { S_NO_ERROR, 0, 0, "DIGEST-MD5" }, { { TLS, NULL }, { SERVER_PROBLEM_NO_PROBLEM, { XMPP_PROBLEM_NO_SESSION, OK, OK, OK, OK } }, { "moose", "something" }, PORT_XMPP }, { "weasel-juice.org", PORT_XMPP, "thud.org", REACHABLE, UNREACHABLE }, { PLAINTEXT_OK, { "moose@weasel-juice.org", "something", PLAIN, NOTLS }, { NULL, 0 } } }, /* WOCKY_CONNECTOR_ERROR_SESSION_FAILED */ { "/connector/problem/xmpp/session/failed", NOISY, { S_WOCKY_CONNECTOR_ERROR, WOCKY_CONNECTOR_ERROR_SESSION_FAILED, -1 }, { { TLS, NULL }, { SERVER_PROBLEM_NO_PROBLEM, { OK, OK, SESSION_PROBLEM_FAILED, OK, OK } }, { "moose", "something" }, PORT_XMPP }, { "weasel-juice.org", PORT_XMPP, "thud.org", REACHABLE, UNREACHABLE }, { PLAINTEXT_OK, { "moose@weasel-juice.org", "something", PLAIN, NOTLS }, { NULL, 0 } } }, /* WOCKY_CONNECTOR_ERROR_SESSION_DENIED */ { "/connector/problem/xmpp/session/denied", NOISY, { S_WOCKY_CONNECTOR_ERROR, WOCKY_CONNECTOR_ERROR_SESSION_DENIED, -1 }, { { TLS, NULL }, { SERVER_PROBLEM_NO_PROBLEM, { OK, OK, SESSION_PROBLEM_DENIED, OK, OK } }, { "moose", "something" }, PORT_XMPP }, { "weasel-juice.org", PORT_XMPP, "thud.org", REACHABLE, UNREACHABLE }, { PLAINTEXT_OK, { "moose@weasel-juice.org", "something", PLAIN, NOTLS }, { NULL, 0 } } }, /* WOCKY_CONNECTOR_ERROR_SESSION_CONFLICT */ { "/connector/problem/xmpp/session/conflict", NOISY, { S_WOCKY_CONNECTOR_ERROR, WOCKY_CONNECTOR_ERROR_SESSION_CONFLICT, -1 }, { { TLS, NULL }, { SERVER_PROBLEM_NO_PROBLEM, { OK, OK, SESSION_PROBLEM_CONFLICT, OK, OK } }, { "moose", "something" }, PORT_XMPP }, { "weasel-juice.org", PORT_XMPP, "thud.org", REACHABLE, UNREACHABLE }, { PLAINTEXT_OK, { "moose@weasel-juice.org", "something", PLAIN, NOTLS }, { NULL, 0 } } }, /* WOCKY_CONNECTOR_ERROR_SESSION_REJECTED */ { "/connector/problem/xmpp/session/rejected", NOISY, { S_WOCKY_CONNECTOR_ERROR, WOCKY_CONNECTOR_ERROR_SESSION_REJECTED, -1 }, { { TLS, NULL }, { SERVER_PROBLEM_NO_PROBLEM, { OK, OK, SESSION_PROBLEM_REJECTED, OK, OK } }, { "moose", "something" }, PORT_XMPP }, { "weasel-juice.org", PORT_XMPP, "thud.org", REACHABLE, UNREACHABLE }, { PLAINTEXT_OK, { "moose@weasel-juice.org", "something", PLAIN, NOTLS }, { NULL, 0 } } }, { "/connector/problem/xmpp/session/nonsense", NOISY, { S_WOCKY_CONNECTOR_ERROR, WOCKY_CONNECTOR_ERROR_SESSION_FAILED, -1 }, { { TLS, NULL }, { SERVER_PROBLEM_NO_PROBLEM, { OK, OK, SESSION_PROBLEM_NONSENSE, OK, OK } }, { "moose", "something" }, PORT_XMPP }, { "weasel-juice.org", PORT_XMPP, "thud.org", REACHABLE, UNREACHABLE }, { PLAINTEXT_OK, { "moose@weasel-juice.org", "something", PLAIN, NOTLS }, { NULL, 0 } } }, { "/connector/econnreset/server-start", NOISY, { S_ANY_ERROR, 0 }, { { TLS, NULL }, { SERVER_PROBLEM_NO_PROBLEM, { OK, OK, OK, SERVER_DEATH_SERVER_START, OK } }, { "moose", "something" }, PORT_XMPP }, { "weasel-juice.org", PORT_XMPP, "thud.org", REACHABLE, UNREACHABLE }, { PLAINTEXT_OK, { "moose@weasel-juice.org", "something", PLAIN, NOTLS }, { NULL, 0 } } }, { "/connector/econnreset/client-open", NOISY, { S_ANY_ERROR, 0 }, { { TLS, NULL }, { SERVER_PROBLEM_NO_PROBLEM, { OK, OK, OK, SERVER_DEATH_CLIENT_OPEN, OK } }, { "moose", "something" }, PORT_XMPP }, { "weasel-juice.org", PORT_XMPP, "thud.org", REACHABLE, UNREACHABLE }, { PLAINTEXT_OK, { "moose@weasel-juice.org", "something", PLAIN, NOTLS }, { NULL, 0 } } }, { "/connector/econnreset/server-open", NOISY, { S_ANY_ERROR, 0 }, { { TLS, NULL }, { SERVER_PROBLEM_NO_PROBLEM, { OK, OK, OK, SERVER_DEATH_SERVER_OPEN, OK } }, { "moose", "something" }, PORT_XMPP }, { "weasel-juice.org", PORT_XMPP, "thud.org", REACHABLE, UNREACHABLE }, { PLAINTEXT_OK, { "moose@weasel-juice.org", "something", PLAIN, NOTLS }, { NULL, 0 } } }, { "/connector/econnreset/features", NOISY, { S_ANY_ERROR, 0 }, { { TLS, NULL }, { SERVER_PROBLEM_NO_PROBLEM, { OK, OK, OK, SERVER_DEATH_FEATURES, OK } }, { "moose", "something" }, PORT_XMPP }, { "weasel-juice.org", PORT_XMPP, "thud.org", REACHABLE, UNREACHABLE }, { PLAINTEXT_OK, { "moose@weasel-juice.org", "something", PLAIN, NOTLS }, { NULL, 0 } } }, { "/connector/econnreset/tls-negotiate", QUIET, { S_ANY_ERROR, 0 }, { { TLS, NULL }, { SERVER_PROBLEM_NO_PROBLEM, { OK, OK, OK, SERVER_DEATH_TLS_NEG, OK } }, { "moose", "something" }, PORT_XMPP }, { "weasel-juice.org", PORT_XMPP, "thud.org", REACHABLE, UNREACHABLE }, { PLAINTEXT_OK, { "moose@weasel-juice.org", "something", PLAIN, NOTLS }, { NULL, 0 } } }, /* ******************************************************************** */ /* quirks */ { "/connector/google/domain-discovery/require", QUIET, { S_NO_ERROR, 0, 0, "DIGEST-MD5" }, { { TLS, NULL }, { SERVER_PROBLEM_REQUIRE_GOOGLE_JDD, CONNECTOR_OK }, { "moose", "something" }, PORT_XMPP }, { "weasel-juice.org", PORT_XMPP, "thud.org", REACHABLE, UNREACHABLE }, { PLAINTEXT_OK, { "moose@weasel-juice.org", "something", PLAIN, NOTLS }, { NULL, 0 } } }, { "/connector/google/domain-discovery/dislike", QUIET, { S_WOCKY_AUTH_ERROR, WOCKY_AUTH_ERROR_FAILURE, -1 }, { { TLS, NULL }, { SERVER_PROBLEM_DISLIKE_GOOGLE_JDD, CONNECTOR_OK }, { "moose", "something" }, PORT_XMPP }, { "weasel-juice.org", PORT_XMPP, "thud.org", REACHABLE, UNREACHABLE }, { PLAINTEXT_OK, { "moose@weasel-juice.org", "something", PLAIN, NOTLS }, { NULL, 0 } } }, /* ******************************************************************** */ /* XEP 0077 */ { "/connector/xep77/register/ok", NOISY, { S_NO_ERROR, }, { { TLS, NULL }, { SERVER_PROBLEM_NO_PROBLEM, CONNECTOR_OK }, { "moose", "something" }, PORT_XMPP }, { NULL, 0, "weasel-juice.org", REACHABLE, NULL }, { PLAINTEXT_OK, { "moose@weasel-juice.org", "something", PLAIN, NOTLS }, { NULL, 0 }, OP_REGISTER } }, { "/connector/xep77/register/no-args", NOISY, { S_WOCKY_CONNECTOR_ERROR, WOCKY_CONNECTOR_ERROR_REGISTRATION_EMPTY, -1 }, { { TLS, NULL }, { SERVER_PROBLEM_NO_PROBLEM, { OK, OK, OK, OK, OK, XEP77_PROBLEM_NO_ARGS } }, { "moose", "something" }, PORT_XMPP }, { NULL, 0, "weasel-juice.org", REACHABLE, NULL }, { PLAINTEXT_OK, { "moose@weasel-juice.org", "something", PLAIN, NOTLS }, { NULL, 0 }, OP_REGISTER } }, { "/connector/xep77/register/email-missing", NOISY, { S_WOCKY_CONNECTOR_ERROR, WOCKY_CONNECTOR_ERROR_REGISTRATION_REJECTED, -1 }, { { TLS, NULL }, { SERVER_PROBLEM_NO_PROBLEM, { OK, OK, OK, OK, OK, XEP77_PROBLEM_EMAIL_ARG } }, { "moose", "something" }, PORT_XMPP }, { NULL, 0, "weasel-juice.org", REACHABLE, NULL }, { PLAINTEXT_OK, { "moose@weasel-juice.org", "something", PLAIN, NOTLS }, { NULL, 0 }, OP_REGISTER } }, { "/connector/xep77/register/unknown-arg", NOISY, { S_WOCKY_CONNECTOR_ERROR, WOCKY_CONNECTOR_ERROR_REGISTRATION_UNSUPPORTED, -1 }, { { TLS, NULL }, { SERVER_PROBLEM_NO_PROBLEM, { OK, OK, OK, OK, OK, XEP77_PROBLEM_STRANGE_ARG } }, { "moose", "something" }, PORT_XMPP }, { NULL, 0, "weasel-juice.org", REACHABLE, NULL }, { PLAINTEXT_OK, { "moose@weasel-juice.org", "something", PLAIN, NOTLS }, { NULL, 0 }, OP_REGISTER } }, { "/connector/xep77/register/unknown+email-args", NOISY, { S_ANY_ERROR, }, { { TLS, NULL }, { SERVER_PROBLEM_NO_PROBLEM, { OK, OK, OK, OK, OK, XEP77_PROBLEM_STRANGE_ARG|XEP77_PROBLEM_EMAIL_ARG } }, { "moose", "something" }, PORT_XMPP }, { NULL, 0, "weasel-juice.org", REACHABLE, NULL }, { PLAINTEXT_OK, { "moose@weasel-juice.org", "something", PLAIN, NOTLS }, { NULL, 0 }, OP_REGISTER } }, { "/connector/xep77/register/email-arg-ok", NOISY, { S_NO_ERROR, 0, 0, "DIGEST-MD5" }, { { TLS, NULL }, { SERVER_PROBLEM_NO_PROBLEM, { OK, OK, OK, OK, OK, XEP77_PROBLEM_EMAIL_ARG } }, { "moose", "something" }, PORT_XMPP }, { NULL, 0, "weasel-juice.org", REACHABLE, NULL }, { PLAINTEXT_OK, { "moose@weasel-juice.org", "something", PLAIN, NOTLS }, { NULL, 0 }, OP_REGISTER, (test_setup)_set_connector_email_prop } }, { "/connector/xep77/register/email-arg-ok/unknown-arg", NOISY, { S_WOCKY_CONNECTOR_ERROR, WOCKY_CONNECTOR_ERROR_REGISTRATION_UNSUPPORTED, -1 }, { { TLS, NULL }, { SERVER_PROBLEM_NO_PROBLEM, { OK, OK, OK, OK, OK, XEP77_PROBLEM_EMAIL_ARG|XEP77_PROBLEM_STRANGE_ARG } }, { "moose", "something" }, PORT_XMPP }, { NULL, 0, "weasel-juice.org", REACHABLE, NULL }, { PLAINTEXT_OK, { "moose@weasel-juice.org", "something", PLAIN, NOTLS }, { NULL, 0 }, OP_REGISTER, (test_setup)_set_connector_email_prop } }, { "/connector/xep77/register/fail/conflict", NOISY, { S_WOCKY_CONNECTOR_ERROR, WOCKY_CONNECTOR_ERROR_REGISTRATION_CONFLICT, -1 }, { { TLS, NULL }, { SERVER_PROBLEM_NO_PROBLEM, { OK, OK, OK, OK, OK, XEP77_PROBLEM_FAIL_CONFLICT } }, { "moose", "something" }, PORT_XMPP }, { NULL, 0, "weasel-juice.org", REACHABLE, NULL }, { PLAINTEXT_OK, { "moose@weasel-juice.org", "something", PLAIN, NOTLS }, { NULL, 0 }, OP_REGISTER } }, { "/connector/xep77/register/fail/other", NOISY, { S_WOCKY_CONNECTOR_ERROR, WOCKY_CONNECTOR_ERROR_REGISTRATION_REJECTED, -1 }, { { TLS, NULL }, { SERVER_PROBLEM_NO_PROBLEM, { OK, OK, OK, OK, OK, XEP77_PROBLEM_FAIL_REJECTED } }, { "moose", "something" }, PORT_XMPP }, { NULL, 0, "weasel-juice.org", REACHABLE, NULL }, { PLAINTEXT_OK, { "moose@weasel-juice.org", "something", PLAIN, NOTLS }, { NULL, 0 }, OP_REGISTER } }, { "/connector/xep77/register/nonsense", NOISY, { S_WOCKY_CONNECTOR_ERROR, WOCKY_CONNECTOR_ERROR_REGISTRATION_FAILED, -1 }, { { TLS, NULL }, { SERVER_PROBLEM_NO_PROBLEM, { OK, OK, OK, OK, OK, XEP77_PROBLEM_QUERY_NONSENSE } }, { "moose", "something" }, PORT_XMPP }, { NULL, 0, "weasel-juice.org", REACHABLE, NULL }, { PLAINTEXT_OK, { "moose@weasel-juice.org", "something", PLAIN, NOTLS }, { NULL, 0 }, OP_REGISTER } }, { "/connector/xep77/register/already/get", NOISY, { S_NO_ERROR, 0 , 0, "DIGEST-MD5" }, { { TLS, NULL }, { SERVER_PROBLEM_NO_PROBLEM, { OK, OK, OK, OK, OK, XEP77_PROBLEM_QUERY_ALREADY } }, { "moose", "something" }, PORT_XMPP }, { NULL, 0, "weasel-juice.org", REACHABLE, NULL }, { PLAINTEXT_OK, { "moose@weasel-juice.org", "something", PLAIN, NOTLS }, { NULL, 0 }, OP_REGISTER } }, { "/connector/xep77/register/already/set", NOISY, { S_NO_ERROR, 0, 0, "DIGEST-MD5" }, { { TLS, NULL }, { SERVER_PROBLEM_NO_PROBLEM, { OK, OK, OK, OK, OK, XEP77_PROBLEM_ALREADY } }, { "moose", "something" }, PORT_XMPP }, { NULL, 0, "weasel-juice.org", REACHABLE, NULL }, { PLAINTEXT_OK, { "moose@weasel-juice.org", "something", PLAIN, NOTLS }, { NULL, 0 }, OP_REGISTER } }, { "/connector/xep77/register/not-available", NOISY, { S_WOCKY_CONNECTOR_ERROR, WOCKY_CONNECTOR_ERROR_REGISTRATION_UNAVAILABLE, -1 }, { { TLS, NULL }, { SERVER_PROBLEM_NO_PROBLEM, { OK, OK, OK, OK, OK, XEP77_PROBLEM_NOT_AVAILABLE } }, { "moose", "something" }, PORT_XMPP }, { NULL, 0, "weasel-juice.org", REACHABLE, NULL }, { PLAINTEXT_OK, { "moose@weasel-juice.org", "something", PLAIN, NOTLS }, { NULL, 0 }, OP_REGISTER } }, /* ******************************************************************** */ { "/connector/xep77/cancel/ok", NOISY, { S_NO_ERROR, }, { { TLS, NULL }, { SERVER_PROBLEM_NO_PROBLEM, CONNECTOR_OK }, { "moose", "something" }, PORT_XMPP }, { NULL, 0, "weasel-juice.org", REACHABLE, NULL }, { PLAINTEXT_OK, { "moose@weasel-juice.org", "something", PLAIN, NOTLS }, { NULL, 0 }, OP_CANCEL } }, { "/connector/xep77/cancel/denied", NOISY, { S_WOCKY_CONNECTOR_ERROR, WOCKY_CONNECTOR_ERROR_UNREGISTER_DENIED, -1 }, { { TLS, NULL }, { SERVER_PROBLEM_NO_PROBLEM, { OK, OK, OK, OK, OK, XEP77_PROBLEM_CANCEL_FAILED } }, { "moose", "something" }, PORT_XMPP }, { NULL, 0, "weasel-juice.org", REACHABLE, NULL }, { PLAINTEXT_OK, { "moose@weasel-juice.org", "something", PLAIN, NOTLS }, { NULL, 0 }, OP_CANCEL } }, { "/connector/xep77/cancel/disabled", NOISY, { S_WOCKY_CONNECTOR_ERROR, WOCKY_CONNECTOR_ERROR_UNREGISTER_DENIED, -1 }, { { TLS, NULL }, { SERVER_PROBLEM_NO_PROBLEM, { OK, OK, OK, OK, OK, XEP77_PROBLEM_CANCEL_DISABLED } }, { "moose", "something" }, PORT_XMPP }, { NULL, 0, "weasel-juice.org", REACHABLE, NULL }, { PLAINTEXT_OK, { "moose@weasel-juice.org", "something", PLAIN, NOTLS }, { NULL, 0 }, OP_CANCEL } }, { "/connector/xep77/cancel/rejected", NOISY, { S_WOCKY_CONNECTOR_ERROR, WOCKY_CONNECTOR_ERROR_UNREGISTER_FAILED, -1 }, { { TLS, NULL }, { SERVER_PROBLEM_NO_PROBLEM, { OK, OK, OK, OK, OK, XEP77_PROBLEM_CANCEL_REJECTED } }, { "moose", "something" }, PORT_XMPP }, { NULL, 0, "weasel-juice.org", REACHABLE, NULL }, { PLAINTEXT_OK, { "moose@weasel-juice.org", "something", PLAIN, NOTLS }, { NULL, 0 }, OP_CANCEL } }, { "/connector/xep77/cancel/stream-closed", NOISY, { S_NO_ERROR, }, { { TLS, NULL }, { SERVER_PROBLEM_NO_PROBLEM, { OK, OK, OK, OK, OK, XEP77_PROBLEM_CANCEL_STREAM } }, { "moose", "something" }, PORT_XMPP }, { NULL, 0, "weasel-juice.org", REACHABLE, NULL }, { PLAINTEXT_OK, { "moose@weasel-juice.org", "something", PLAIN, NOTLS }, { NULL, 0 }, OP_CANCEL } }, /* ******************************************************************** */ /* old school jabber tests (pre XMPP 1.0) */ { "/connector/jabber/no-ssl/auth/digest", NOISY, { S_NO_ERROR, }, { { TLS, NULL }, { SERVER_PROBLEM_NO_PROBLEM, { XMPP_PROBLEM_OLD_SERVER, OK, OK, OK, OK } }, { "moose", "something" }, PORT_XMPP }, { NULL, 0, "weasel-juice.org", REACHABLE, NULL }, { PLAINTEXT_OK, { "moose@weasel-juice.org", "something", PLAIN, NOTLS }, { NULL, 0, OLD_JABBER } } }, { "/connector/jabber/no-ssl/auth/reject", NOISY, { S_WOCKY_AUTH_ERROR, WOCKY_AUTH_ERROR_NOT_AUTHORIZED, -1 }, { { TLS, NULL }, { SERVER_PROBLEM_NO_PROBLEM, { XMPP_PROBLEM_OLD_SERVER, OK, OK, OK, OK } }, { "moose", "blerg" }, PORT_XMPP }, { NULL, 0, "weasel-juice.org", REACHABLE, NULL }, { PLAINTEXT_OK, { "moose@weasel-juice.org", "something", PLAIN, NOTLS }, { NULL, 0, OLD_JABBER } } }, { "/connector/jabber/no-ssl/auth/unavailable", NOISY, { S_WOCKY_AUTH_ERROR, WOCKY_AUTH_ERROR_NOT_SUPPORTED, -1 }, { { TLS, NULL }, { SERVER_PROBLEM_NO_PROBLEM, { XMPP_PROBLEM_OLD_SERVER, OK, OK, OK, JABBER_PROBLEM_AUTH_NIH } }, { "moose", "blerg" }, PORT_XMPP }, { NULL, 0, "weasel-juice.org", REACHABLE, NULL }, { PLAINTEXT_OK, { "moose@weasel-juice.org", "something", PLAIN, NOTLS }, { NULL, 0, OLD_JABBER } } }, { "/connector/jabber/no-ssl/auth/bind-error", NOISY, { S_WOCKY_AUTH_ERROR, WOCKY_AUTH_ERROR_RESOURCE_CONFLICT, -1 }, { { TLS, NULL }, { SERVER_PROBLEM_NO_PROBLEM, { XMPP_PROBLEM_OLD_SERVER, OK, OK, OK, JABBER_PROBLEM_AUTH_BIND } }, { "moose", "something" }, PORT_XMPP }, { NULL, 0, "weasel-juice.org", REACHABLE, NULL }, { PLAINTEXT_OK, { "moose@weasel-juice.org", "something", PLAIN, NOTLS }, { NULL, 0, OLD_JABBER } } }, { "/connector/jabber/no-ssl/auth/incomplete", NOISY, { S_WOCKY_AUTH_ERROR, WOCKY_AUTH_ERROR_NO_CREDENTIALS, -1 }, { { TLS, NULL }, { SERVER_PROBLEM_NO_PROBLEM, { XMPP_PROBLEM_OLD_SERVER, OK, OK, OK, JABBER_PROBLEM_AUTH_PARTIAL } }, { "moose", "something" }, PORT_XMPP }, { NULL, 0, "weasel-juice.org", REACHABLE, NULL }, { PLAINTEXT_OK, { "moose@weasel-juice.org", "something", PLAIN, NOTLS }, { NULL, 0, OLD_JABBER } } }, { "/connector/jabber/no-ssl/auth/failure", NOISY, { S_WOCKY_AUTH_ERROR, WOCKY_AUTH_ERROR_FAILURE, -1 }, { { TLS, NULL }, { SERVER_PROBLEM_NO_PROBLEM, { XMPP_PROBLEM_OLD_SERVER, OK, OK, OK, JABBER_PROBLEM_AUTH_FAILED } }, { "moose", "something" }, PORT_XMPP }, { NULL, 0, "weasel-juice.org", REACHABLE, NULL }, { PLAINTEXT_OK, { "moose@weasel-juice.org", "something", PLAIN, NOTLS }, { NULL, 0, OLD_JABBER } } }, { "/connector/jabber/no-ssl/auth/bizarre", NOISY, { S_WOCKY_AUTH_ERROR, WOCKY_AUTH_ERROR_INVALID_REPLY, -1 }, { { TLS, NULL }, { SERVER_PROBLEM_NO_PROBLEM, { XMPP_PROBLEM_OLD_SERVER, OK, OK, OK, JABBER_PROBLEM_AUTH_STRANGE } }, { "moose", "something" }, PORT_XMPP }, { NULL, 0, "weasel-juice.org", REACHABLE, NULL }, { PLAINTEXT_OK, { "moose@weasel-juice.org", "something", PLAIN, NOTLS }, { NULL, 0, OLD_JABBER } } }, { "/connector/jabber/no-ssl/auth/nonsense", NOISY, { S_WOCKY_AUTH_ERROR, WOCKY_AUTH_ERROR_INVALID_REPLY, -1 }, { { TLS, NULL }, { SERVER_PROBLEM_NO_PROBLEM, { XMPP_PROBLEM_OLD_SERVER, OK, OK, OK, JABBER_PROBLEM_AUTH_NONSENSE } }, { "moose", "something" }, PORT_XMPP }, { NULL, 0, "weasel-juice.org", REACHABLE, NULL }, { PLAINTEXT_OK, { "moose@weasel-juice.org", "something", PLAIN, NOTLS }, { NULL, 0, OLD_JABBER } } }, { "/connector/jabber/no-ssl/auth/no-mechs", NOISY, { S_WOCKY_AUTH_ERROR, WOCKY_AUTH_ERROR_NO_SUPPORTED_MECHANISMS, -1 }, { { TLS, "none" }, { SERVER_PROBLEM_NO_PROBLEM, { XMPP_PROBLEM_OLD_SERVER, OK, OK, OK, OK } }, { "moose", "something" }, PORT_XMPP }, { NULL, 0, "weasel-juice.org", REACHABLE, NULL }, { PLAINTEXT_OK, { "moose@weasel-juice.org", "something", PLAIN, NOTLS }, { NULL, 0, OLD_JABBER } } }, { "/connector/jabber/no-ssl/auth/plain", NOISY, { S_NO_ERROR, }, { { TLS, "password" }, { SERVER_PROBLEM_NO_PROBLEM, { XMPP_PROBLEM_OLD_SERVER, OK, OK, OK, OK } }, { "moose", "something" }, PORT_XMPP }, { NULL, 0, "weasel-juice.org", REACHABLE, NULL }, { PLAINTEXT_OK, { "moose@weasel-juice.org", "something", PLAIN, NOTLS }, { NULL, 0, OLD_JABBER } } }, { "/connector/jabber/no-ssl/auth/plain/rejected", NOISY, { S_WOCKY_AUTH_ERROR, WOCKY_AUTH_ERROR_NOT_AUTHORIZED, -1 }, { { TLS, "password" }, { SERVER_PROBLEM_NO_PROBLEM, { XMPP_PROBLEM_OLD_SERVER, OK, OK, OK, JABBER_PROBLEM_AUTH_REJECT } }, { "moose", "something" }, PORT_XMPP }, { NULL, 0, "weasel-juice.org", REACHABLE, NULL }, { PLAINTEXT_OK, { "moose@weasel-juice.org", "something", PLAIN, NOTLS }, { NULL, 0, OLD_JABBER } } }, { "/connector/jabber/no-ssl/auth/digest/rejected", NOISY, { S_WOCKY_AUTH_ERROR, WOCKY_AUTH_ERROR_NOT_AUTHORIZED, -1 }, { { TLS, "digest" }, { SERVER_PROBLEM_NO_PROBLEM, { XMPP_PROBLEM_OLD_SERVER, OK, OK, OK, JABBER_PROBLEM_AUTH_REJECT } }, { "moose", "something" }, PORT_XMPP }, { NULL, 0, "weasel-juice.org", REACHABLE, NULL }, { PLAINTEXT_OK, { "moose@weasel-juice.org", "something", PLAIN, NOTLS }, { NULL, 0, OLD_JABBER } } }, { "/connector/jabber/no-ssl/auth/old+sasl", NOISY, { S_NO_ERROR, 0, 0, "DIGEST-MD5" }, { { TLS, NULL }, { SERVER_PROBLEM_NO_PROBLEM, { XMPP_PROBLEM_OLD_AUTH_FEATURE, OK, OK, OK, OK } }, { "moose", "something" }, PORT_XMPP }, { NULL, 0, "weasel-juice.org", REACHABLE, NULL }, { PLAINTEXT_OK, { "moose@weasel-juice.org", "something", PLAIN, NOTLS }, { NULL, 0, OLD_JABBER } } }, { "/connector/jabber/no-ssl/auth/old-sasl", NOISY, { S_NO_ERROR, }, { { TLS, NULL }, { SERVER_PROBLEM_NO_SASL, { XMPP_PROBLEM_OLD_AUTH_FEATURE, OK, OK, OK, OK } }, { "moose", "something" }, PORT_XMPP }, { NULL, 0, "weasel-juice.org", REACHABLE, NULL }, { PLAINTEXT_OK, { "moose@weasel-juice.org", "something", PLAIN, NOTLS }, { NULL, 0, OLD_JABBER } } }, /* ******************************************************************** */ /* old SSL */ { "/connector/jabber/ssl/auth/digest", NOISY, { S_NO_ERROR, }, { { TLS, NULL }, { SERVER_PROBLEM_NO_PROBLEM, { XMPP_PROBLEM_OLD_SERVER|XMPP_PROBLEM_OLD_SSL, OK, OK, OK, OK } }, { "moose", "something" }, PORT_XMPP }, { NULL, 0, "weasel-juice.org", REACHABLE, NULL }, { PLAINTEXT_OK, { "moose@weasel-juice.org", "something", PLAIN, NOTLS }, { NULL, 0, OLD_JABBER, OLD_SSL } } }, { "/connector/jabber/ssl/auth/reject", NOISY, { S_WOCKY_AUTH_ERROR, WOCKY_AUTH_ERROR_NOT_AUTHORIZED, -1 }, { { TLS, NULL }, { SERVER_PROBLEM_NO_PROBLEM, { XMPP_PROBLEM_OLD_SERVER|XMPP_PROBLEM_OLD_SSL, OK, OK, OK, OK } }, { "moose", "blerg" }, PORT_XMPP }, { NULL, 0, "weasel-juice.org", REACHABLE, NULL }, { PLAINTEXT_OK, { "moose@weasel-juice.org", "something", PLAIN, NOTLS }, { NULL, 0, OLD_JABBER, OLD_SSL } } }, { "/connector/jabber/ssl/auth/unavailable", NOISY, { S_WOCKY_AUTH_ERROR, WOCKY_AUTH_ERROR_NOT_SUPPORTED, -1 }, { { TLS, NULL }, { SERVER_PROBLEM_NO_PROBLEM, { XMPP_PROBLEM_OLD_SERVER|XMPP_PROBLEM_OLD_SSL, OK, OK, OK, JABBER_PROBLEM_AUTH_NIH } }, { "moose", "blerg" }, PORT_XMPP }, { NULL, 0, "weasel-juice.org", REACHABLE, NULL }, { PLAINTEXT_OK, { "moose@weasel-juice.org", "something", PLAIN, NOTLS }, { NULL, 0, OLD_JABBER, OLD_SSL } } }, { "/connector/jabber/ssl/auth/bind-error", NOISY, { S_WOCKY_AUTH_ERROR, WOCKY_AUTH_ERROR_RESOURCE_CONFLICT, -1 }, { { TLS, NULL }, { SERVER_PROBLEM_NO_PROBLEM, { XMPP_PROBLEM_OLD_SERVER|XMPP_PROBLEM_OLD_SSL, OK, OK, OK, JABBER_PROBLEM_AUTH_BIND } }, { "moose", "something" }, PORT_XMPP }, { NULL, 0, "weasel-juice.org", REACHABLE, NULL }, { PLAINTEXT_OK, { "moose@weasel-juice.org", "something", PLAIN, NOTLS }, { NULL, 0, OLD_JABBER, OLD_SSL } } }, { "/connector/jabber/ssl/auth/incomplete", NOISY, { S_WOCKY_AUTH_ERROR, WOCKY_AUTH_ERROR_NO_CREDENTIALS, -1 }, { { TLS, NULL }, { SERVER_PROBLEM_NO_PROBLEM, { XMPP_PROBLEM_OLD_SERVER|XMPP_PROBLEM_OLD_SSL, OK, OK, OK, JABBER_PROBLEM_AUTH_PARTIAL } }, { "moose", "something" }, PORT_XMPP }, { NULL, 0, "weasel-juice.org", REACHABLE, NULL }, { PLAINTEXT_OK, { "moose@weasel-juice.org", "something", PLAIN, NOTLS }, { NULL, 0, OLD_JABBER, OLD_SSL } } }, { "/connector/jabber/ssl/auth/failure", NOISY, { S_WOCKY_AUTH_ERROR, WOCKY_AUTH_ERROR_FAILURE, -1 }, { { TLS, NULL }, { SERVER_PROBLEM_NO_PROBLEM, { XMPP_PROBLEM_OLD_SERVER|XMPP_PROBLEM_OLD_SSL, OK, OK, OK, JABBER_PROBLEM_AUTH_FAILED } }, { "moose", "something" }, PORT_XMPP }, { NULL, 0, "weasel-juice.org", REACHABLE, NULL }, { PLAINTEXT_OK, { "moose@weasel-juice.org", "something", PLAIN, NOTLS }, { NULL, 0, OLD_JABBER, OLD_SSL } } }, { "/connector/jabber/ssl/auth/bizarre", NOISY, { S_WOCKY_AUTH_ERROR, WOCKY_AUTH_ERROR_INVALID_REPLY, -1 }, { { TLS, NULL }, { SERVER_PROBLEM_NO_PROBLEM, { XMPP_PROBLEM_OLD_SERVER|XMPP_PROBLEM_OLD_SSL, OK, OK, OK, JABBER_PROBLEM_AUTH_STRANGE } }, { "moose", "something" }, PORT_XMPP }, { NULL, 0, "weasel-juice.org", REACHABLE, NULL }, { PLAINTEXT_OK, { "moose@weasel-juice.org", "something", PLAIN, NOTLS }, { NULL, 0, OLD_JABBER, OLD_SSL } } }, { "/connector/jabber/ssl/auth/nonsense", NOISY, { S_WOCKY_AUTH_ERROR, WOCKY_AUTH_ERROR_INVALID_REPLY, -1 }, { { TLS, NULL }, { SERVER_PROBLEM_NO_PROBLEM, { XMPP_PROBLEM_OLD_SERVER|XMPP_PROBLEM_OLD_SSL, OK, OK, OK, JABBER_PROBLEM_AUTH_NONSENSE } }, { "moose", "something" }, PORT_XMPP }, { NULL, 0, "weasel-juice.org", REACHABLE, NULL }, { PLAINTEXT_OK, { "moose@weasel-juice.org", "something", PLAIN, NOTLS }, { NULL, 0, OLD_JABBER, OLD_SSL } } }, { "/connector/jabber/ssl/auth/no-mechs", NOISY, { S_WOCKY_AUTH_ERROR, WOCKY_AUTH_ERROR_NO_SUPPORTED_MECHANISMS, -1 }, { { TLS, "none" }, { SERVER_PROBLEM_NO_PROBLEM, { XMPP_PROBLEM_OLD_SERVER|XMPP_PROBLEM_OLD_SSL, OK, OK, OK, OK } }, { "moose", "something" }, PORT_XMPP }, { NULL, 0, "weasel-juice.org", REACHABLE, NULL }, { PLAINTEXT_OK, { "moose@weasel-juice.org", "something", PLAIN, NOTLS }, { NULL, 0, OLD_JABBER, OLD_SSL } } }, { "/connector/jabber/ssl/auth/plain", NOISY, { S_NO_ERROR, }, { { TLS, "password" }, { SERVER_PROBLEM_NO_PROBLEM, { XMPP_PROBLEM_OLD_SERVER|XMPP_PROBLEM_OLD_SSL, OK, OK, OK, OK } }, { "moose", "something" }, PORT_XMPP }, { NULL, 0, "weasel-juice.org", REACHABLE, NULL }, { PLAINTEXT_OK, { "moose@weasel-juice.org", "something", PLAIN, NOTLS }, { NULL, 0, OLD_JABBER, OLD_SSL } } }, { "/connector/jabber/ssl/auth/plain/rejected", NOISY, { S_WOCKY_AUTH_ERROR, WOCKY_AUTH_ERROR_NOT_AUTHORIZED, -1 }, { { TLS, "password" }, { SERVER_PROBLEM_NO_PROBLEM, { XMPP_PROBLEM_OLD_SERVER|XMPP_PROBLEM_OLD_SSL, OK, OK, OK, JABBER_PROBLEM_AUTH_REJECT } }, { "moose", "something" }, PORT_XMPP }, { NULL, 0, "weasel-juice.org", REACHABLE, NULL }, { PLAINTEXT_OK, { "moose@weasel-juice.org", "something", PLAIN, NOTLS }, { NULL, 0, OLD_JABBER, OLD_SSL } } }, { "/connector/jabber/ssl/auth/digest/rejected", NOISY, { S_WOCKY_AUTH_ERROR, WOCKY_AUTH_ERROR_NOT_AUTHORIZED, -1 }, { { TLS, "digest" }, { SERVER_PROBLEM_NO_PROBLEM, { XMPP_PROBLEM_OLD_SERVER|XMPP_PROBLEM_OLD_SSL, OK, OK, OK, JABBER_PROBLEM_AUTH_REJECT } }, { "moose", "something" }, PORT_XMPP }, { NULL, 0, "weasel-juice.org", REACHABLE, NULL }, { PLAINTEXT_OK, { "moose@weasel-juice.org", "something", PLAIN, NOTLS }, { NULL, 0, OLD_JABBER, OLD_SSL } } }, { "/connector/jabber/ssl/auth/old+sasl", NOISY, { S_NO_ERROR, 0, 0, "DIGEST-MD5" }, { { TLS, NULL }, { SERVER_PROBLEM_NO_PROBLEM, { XMPP_PROBLEM_OLD_AUTH_FEATURE|XMPP_PROBLEM_OLD_SSL, OK, OK, OK, OK } }, { "moose", "something" }, PORT_XMPP }, { NULL, 0, "weasel-juice.org", REACHABLE, NULL }, { PLAINTEXT_OK, { "moose@weasel-juice.org", "something", PLAIN, NOTLS }, { NULL, 0, OLD_JABBER, OLD_SSL } } }, { "/connector/jabber/ssl/auth/old-sasl", NOISY, { S_NO_ERROR, }, { { TLS, NULL }, { SERVER_PROBLEM_NO_SASL, { XMPP_PROBLEM_OLD_AUTH_FEATURE|XMPP_PROBLEM_OLD_SSL, OK, OK, OK, OK } }, { "moose", "something" }, PORT_XMPP }, { NULL, 0, "weasel-juice.org", REACHABLE, NULL }, { PLAINTEXT_OK, { "moose@weasel-juice.org", "something", PLAIN, NOTLS }, { NULL, 0, OLD_JABBER, OLD_SSL } } }, /* ******************************************************************* */ /* duplicate earlier blocks of tests, but with old SSL */ { "/connector+ssl/auth/secure/no-tlsplain/notls/nodigest", NOISY, { S_WOCKY_AUTH_ERROR, WOCKY_AUTH_ERROR_NO_SUPPORTED_MECHANISMS, -1 }, { { NOTLS, "PLAIN" }, { SERVER_PROBLEM_NO_PROBLEM, { XMPP_PROBLEM_OLD_SSL, OK, OK, OK, OK } }, { "moose", "something" }, PORT_XMPP }, { "weasel-juice.org", PORT_XMPP, "thud.org", REACHABLE, UNREACHABLE }, { PLAINTEXT_OK, { "moose@weasel-juice.org", "something", DIGEST, TLS }, { NULL, 0, OLD_JABBER, OLD_SSL } } }, { "/connector+ssl/auth/secure/no-tlsplain/notls/digest", NOISY, { S_NO_ERROR, 0, 0, "DIGEST-MD5" }, { { NOTLS, NULL }, { SERVER_PROBLEM_NO_PROBLEM, { XMPP_PROBLEM_OLD_SSL, OK, OK, OK, OK } }, { "moose", "something" }, PORT_XMPP }, { "weasel-juice.org", PORT_XMPP, "thud.org", REACHABLE, UNREACHABLE }, { PLAINTEXT_OK, { "moose@weasel-juice.org", "something", DIGEST, TLS }, { NULL, 0, XMPP_V1, OLD_SSL } } }, { "/connector+ssl/auth/insecure/no-tlsplain/notls/nodigest", NOISY, { S_NO_ERROR, 0, 0, "PLAIN" }, { { NOTLS, "PLAIN" }, { SERVER_PROBLEM_NO_PROBLEM, { XMPP_PROBLEM_OLD_SSL, OK, OK, OK, OK } }, { "moose", "something" }, PORT_XMPP }, { "weasel-juice.org", PORT_XMPP, "thud.org", REACHABLE, UNREACHABLE }, { PLAINTEXT_OK, { "moose@weasel-juice.org", "something", PLAIN, NOTLS }, { NULL, 0, XMPP_V1, OLD_SSL } } }, { "/connector+ssl/auth/insecure/no-tlsplain/notls/digest", NOISY, { S_NO_ERROR, 0, 0, "DIGEST-MD5" }, { { NOTLS, NULL }, { SERVER_PROBLEM_NO_PROBLEM, { XMPP_PROBLEM_OLD_SSL, OK, OK, OK, OK } }, { "moose", "something" }, PORT_XMPP }, { "weasel-juice.org", PORT_XMPP, "thud.org", REACHABLE, UNREACHABLE }, { PLAINTEXT_OK, { "moose@weasel-juice.org", "something", PLAIN, NOTLS }, { NULL, 0, XMPP_V1, OLD_SSL } } }, /* **************************************************************** * * this will be a mix of failures and sucesses depending on whether * * we allow plain auth or not */ { "/connector+ssl/auth/secure/no-tlsplain/tls/plain", NOISY, { S_WOCKY_AUTH_ERROR, WOCKY_AUTH_ERROR_NO_SUPPORTED_MECHANISMS, -1 }, { { TLS, "PLAIN" }, { SERVER_PROBLEM_INVALID_PASSWORD, { XMPP_PROBLEM_OLD_SSL, OK, OK, OK, OK } }, { "moose", "something" }, PORT_XMPP }, { "weasel-juice.org", PORT_XMPP, "thud.org", REACHABLE, UNREACHABLE }, { PLAINTEXT_OK, { "moose@weasel-juice.org", "something", DIGEST, TLS }, { NULL, 0, XMPP_V1, OLD_SSL } } }, { "/connector+ssl/auth/secure/tlsplain/tls/plain", NOISY, { S_NO_ERROR, 0, 0, "PLAIN" }, { { TLS, "PLAIN" }, { SERVER_PROBLEM_NO_PROBLEM, { XMPP_PROBLEM_OLD_SSL, OK, OK, OK, OK } }, { "moose", "something" }, PORT_XMPP }, { "weasel-juice.org", PORT_XMPP, "thud.org", REACHABLE, UNREACHABLE }, { PLAINTEXT_OK, { "moose@weasel-juice.org", "something", PLAIN, TLS }, { NULL, 0, XMPP_V1, OLD_SSL } } }, { "/connector+ssl/auth/insecure/no-tlsplain/tls/plain", NOISY, { S_NO_ERROR, 0, 0, "PLAIN" }, { { TLS, "PLAIN" }, { SERVER_PROBLEM_NO_PROBLEM, { XMPP_PROBLEM_OLD_SSL, OK, OK, OK, OK } }, { "moose", "something" }, PORT_XMPP }, { "weasel-juice.org", PORT_XMPP, "thud.org", REACHABLE, UNREACHABLE }, { PLAINTEXT_OK, { "moose@weasel-juice.org", "something", DIGEST, NOTLS }, { NULL, 0, XMPP_V1, OLD_SSL } } }, { "/connector+ssl/auth/insecure/tlsplain/tls/plain", NOISY, { S_NO_ERROR, 0, 0, "PLAIN" }, { { TLS, "PLAIN" }, { SERVER_PROBLEM_NO_PROBLEM, { XMPP_PROBLEM_OLD_SSL, OK, OK, OK, OK } }, { "moose", "something" }, PORT_XMPP }, { "weasel-juice.org", PORT_XMPP, "thud.org", REACHABLE, UNREACHABLE }, { PLAINTEXT_OK, { "moose@weasel-juice.org", "something", PLAIN, NOTLS }, { NULL, 0, XMPP_V1, OLD_SSL } } }, { "/connector+ssl/tls+auth/secure/no-tlsplain/tls/plain", NOISY, { S_WOCKY_AUTH_ERROR, WOCKY_AUTH_ERROR_NO_SUPPORTED_MECHANISMS, -1 }, { { TLS, "PLAIN" }, { SERVER_PROBLEM_NO_PROBLEM, { XMPP_PROBLEM_OLD_SSL, OK, OK, OK, OK } }, { "moose", "something" }, PORT_XMPP }, { "weasel-juice.org", PORT_XMPP, "thud.org", REACHABLE, UNREACHABLE }, { PLAINTEXT_OK, { "moose@weasel-juice.org", "something", DIGEST, TLS }, { NULL, 0, XMPP_V1, OLD_SSL } } }, { "/connector+ssl/tls+auth/secure/tlsplain/tls/plain", NOISY, { S_NO_ERROR, 0, 0, "PLAIN" }, { { TLS, "PLAIN" }, { SERVER_PROBLEM_NO_PROBLEM, { XMPP_PROBLEM_OLD_SSL, OK, OK, OK, OK } }, { "moose", "something" }, PORT_XMPP }, { "weasel-juice.org", PORT_XMPP, "thud.org", REACHABLE, UNREACHABLE }, { PLAINTEXT_OK, { "moose@weasel-juice.org", "something", PLAIN, TLS }, { NULL, 0, XMPP_V1, OLD_SSL } } }, { "/connector+ssl/tls+auth/insecure/no-tlsplain/tls/plain", NOISY, { S_NO_ERROR, 0, 0, "PLAIN" }, { { TLS, "PLAIN" }, { SERVER_PROBLEM_NO_PROBLEM, { XMPP_PROBLEM_OLD_SSL, OK, OK, OK, OK } }, { "moose", "something" }, PORT_XMPP }, { "weasel-juice.org", PORT_XMPP, "thud.org", REACHABLE, UNREACHABLE }, { PLAINTEXT_OK, { "moose@weasel-juice.org", "something", DIGEST, NOTLS }, { NULL, 0, XMPP_V1, OLD_SSL } } }, { "/connector+ssl/tls+auth/insecure/tlsplain/tls/plain", NOISY, { S_NO_ERROR, 0, 0, "PLAIN" }, { { TLS, "PLAIN" }, { SERVER_PROBLEM_NO_PROBLEM, { XMPP_PROBLEM_OLD_SSL, OK, OK, OK, OK } }, { "moose", "something" }, PORT_XMPP }, { "weasel-juice.org", PORT_XMPP, "thud.org", REACHABLE, UNREACHABLE }, { PLAINTEXT_OK, { "moose@weasel-juice.org", "something", PLAIN, NOTLS }, { NULL, 0, XMPP_V1, OLD_SSL } } }, /* **************************************************************** * * these should all be digest auth successes */ { "/connector+ssl/auth/secure/no-tlsplain/tls/digest", NOISY, { S_NO_ERROR, 0, 0, "DIGEST-MD5" }, { { TLS, NULL }, { SERVER_PROBLEM_NO_PROBLEM, { XMPP_PROBLEM_OLD_SSL, OK, OK, OK, OK } }, { "moose", "something" }, PORT_XMPP }, { "weasel-juice.org", PORT_XMPP, "thud.org", REACHABLE, UNREACHABLE }, { PLAINTEXT_OK, { "moose@weasel-juice.org", "something", DIGEST, TLS }, { NULL, 0, XMPP_V1, OLD_SSL } } }, { "/connector+ssl/auth/secure/tlsplain/tls/digest", NOISY, { S_NO_ERROR, 0, 0, "DIGEST-MD5" }, { { TLS, NULL }, { SERVER_PROBLEM_NO_PROBLEM, { XMPP_PROBLEM_OLD_SSL, OK, OK, OK, OK } }, { "moose", "something" }, PORT_XMPP }, { "weasel-juice.org", PORT_XMPP, "thud.org", REACHABLE, UNREACHABLE }, { PLAINTEXT_OK, { "moose@weasel-juice.org", "something", PLAIN, TLS }, { NULL, 0, XMPP_V1, OLD_SSL } } }, { "/connector+ssl/auth/insecure/no-tlsplain/tls/digest", NOISY, { S_NO_ERROR, 0, 0, "DIGEST-MD5" }, { { TLS, NULL }, { SERVER_PROBLEM_NO_PROBLEM, { XMPP_PROBLEM_OLD_SSL, OK, OK, OK, OK } }, { "moose", "something" }, PORT_XMPP }, { "weasel-juice.org", PORT_XMPP, "thud.org", REACHABLE, UNREACHABLE }, { PLAINTEXT_OK, { "moose@weasel-juice.org", "something", DIGEST, NOTLS }, { NULL, 0, XMPP_V1, OLD_SSL } } }, { "/connector+ssl/auth/insecure/tlsplain/tls/digest", NOISY, { S_NO_ERROR, 0, 0, "DIGEST-MD5" }, { { TLS, NULL }, { SERVER_PROBLEM_NO_PROBLEM, { XMPP_PROBLEM_OLD_SSL, OK, OK, OK, OK } }, { "moose", "something" }, PORT_XMPP }, { "weasel-juice.org", PORT_XMPP, "thud.org", REACHABLE, UNREACHABLE }, { PLAINTEXT_OK, { "moose@weasel-juice.org", "something", PLAIN, NOTLS }, { NULL, 0, XMPP_V1, OLD_SSL } } }, { "/connector+ssl/tls+auth/secure/no-tlsplain/tls/digest", NOISY, { S_NO_ERROR, 0, 0, "DIGEST-MD5" }, { { TLS, NULL }, { SERVER_PROBLEM_NO_PROBLEM, { XMPP_PROBLEM_OLD_SSL, OK, OK, OK, OK } }, { "moose", "something" }, PORT_XMPP }, { "weasel-juice.org", PORT_XMPP, "thud.org", REACHABLE, UNREACHABLE }, { PLAINTEXT_OK, { "moose@weasel-juice.org", "something", DIGEST, TLS }, { NULL, 0, XMPP_V1, OLD_SSL } } }, { "/connector+ssl/tls+auth/secure/tlsplain/tls/digest", NOISY, { S_NO_ERROR, 0, 0, "DIGEST-MD5" }, { { TLS, NULL }, { SERVER_PROBLEM_NO_PROBLEM, { XMPP_PROBLEM_OLD_SSL, OK, OK, OK, OK } }, { "moose", "something" }, PORT_XMPP }, { "weasel-juice.org", PORT_XMPP, "thud.org", REACHABLE, UNREACHABLE }, { PLAINTEXT_OK, { "moose@weasel-juice.org", "something", PLAIN, TLS }, { NULL, 0, XMPP_V1, OLD_SSL } } }, { "/connector+ssl/tls+auth/insecure/no-tlsplain/tls/digest", NOISY, { S_NO_ERROR, 0, 0, "DIGEST-MD5" }, { { TLS, NULL }, { SERVER_PROBLEM_NO_PROBLEM, { XMPP_PROBLEM_OLD_SSL, OK, OK, OK, OK } }, { "moose", "something" }, PORT_XMPP }, { "weasel-juice.org", PORT_XMPP, "thud.org", REACHABLE, UNREACHABLE }, { PLAINTEXT_OK, { "moose@weasel-juice.org", "something", DIGEST, NOTLS }, { NULL, 0, XMPP_V1, OLD_SSL } } }, { "/connector+ssl/tls+auth/insecure/tlsplain/tls/digest", NOISY, { S_NO_ERROR, 0, 0, "DIGEST-MD5" }, { { TLS, NULL }, { SERVER_PROBLEM_NO_PROBLEM, { XMPP_PROBLEM_OLD_SSL, OK, OK, OK, OK } }, { "moose", "something" }, PORT_XMPP }, { "weasel-juice.org", PORT_XMPP, "thud.org", REACHABLE, UNREACHABLE }, { PLAINTEXT_OK, { "moose@weasel-juice.org", "something", PLAIN, NOTLS }, { NULL, 0, XMPP_V1, OLD_SSL } } }, /* ***************************************************************** * * SASL problems */ { "/connector+ssl/problem/sasl/bad-pass", NOISY, { S_WOCKY_AUTH_ERROR, WOCKY_AUTH_ERROR_FAILURE, -1 }, { { TLS, NULL }, { SERVER_PROBLEM_INVALID_PASSWORD, { XMPP_PROBLEM_OLD_SSL, OK, OK, OK, OK } }, { "foo", "bar" }, PORT_XMPP }, { "weasel-juice.org", PORT_XMPP, "thud.org", REACHABLE, UNREACHABLE }, { PLAINTEXT_OK, { "foo@weasel-juice.org", "notbar", PLAIN, NOTLS }, { NULL, 0, XMPP_V1, OLD_SSL } } }, { "/connector+ssl/problem/sasl/bad-user", NOISY, { S_WOCKY_AUTH_ERROR, WOCKY_AUTH_ERROR_FAILURE, -1 }, { { TLS, NULL }, { SERVER_PROBLEM_INVALID_USERNAME, { XMPP_PROBLEM_OLD_SSL, OK, OK, OK, OK } }, { "moose", "something" }, PORT_XMPP }, { "weasel-juice.org", PORT_XMPP, "thud.org", REACHABLE, UNREACHABLE }, { PLAINTEXT_OK, { "caribou@weasel-juice.org", "something", PLAIN, NOTLS }, { NULL, 0, XMPP_V1, OLD_SSL } } }, { "/connector+ssl/problem/sasl/no-sasl", NOISY, { S_WOCKY_AUTH_ERROR, WOCKY_AUTH_ERROR_NOT_SUPPORTED, -1 }, { { TLS, NULL }, { SERVER_PROBLEM_NO_SASL, { XMPP_PROBLEM_OLD_SSL, OK, OK, OK, OK } }, { "moose", "something" }, PORT_XMPP }, { "weasel-juice.org", PORT_XMPP, "thud.org", REACHABLE, UNREACHABLE }, { PLAINTEXT_OK, { "moose@weasel-juice.org", "something", PLAIN, NOTLS }, { NULL, 0, XMPP_V1, OLD_SSL } } }, { "/connector+ssl/problem/sas/no-mechanisms", NOISY, { S_WOCKY_AUTH_ERROR, WOCKY_AUTH_ERROR_NOT_SUPPORTED, -1 }, { { TLS, NULL }, { SERVER_PROBLEM_NO_MECHANISMS, { XMPP_PROBLEM_OLD_SSL, OK, OK, OK, OK } }, { "moose", "something" }, PORT_XMPP }, { "weasel-juice.org", PORT_XMPP, "thud.org", REACHABLE, UNREACHABLE }, { PLAINTEXT_OK, { "moose@weasel-juice.org", "something", PLAIN, NOTLS }, { NULL, 0, XMPP_V1, OLD_SSL } } }, { "/connector+ssl/problem/sasl/bad-mechanism", NOISY, { S_WOCKY_AUTH_ERROR, WOCKY_AUTH_ERROR_NO_SUPPORTED_MECHANISMS, -1 }, { { TLS, "omg-poniez" }, { SERVER_PROBLEM_NO_PROBLEM, { XMPP_PROBLEM_OLD_SSL, OK, OK, OK, OK } }, { "moose", "something" }, PORT_XMPP }, { "weasel-juice.org", PORT_XMPP, "thud.org", REACHABLE, UNREACHABLE }, { PLAINTEXT_OK, { "moose@weasel-juice.org", "something", PLAIN, NOTLS }, { NULL, 0, XMPP_V1, OLD_SSL } } }, { "/connector+ssl/problem/xmpp/version/0.x", NOISY, { S_WOCKY_CONNECTOR_ERROR, WOCKY_CONNECTOR_ERROR_NON_XMPP_V1_SERVER, -1 }, { { TLS, NULL, "0.9" }, { SERVER_PROBLEM_NO_PROBLEM, { XMPP_PROBLEM_OLD_SSL, OK, OK, OK, OK } }, { "moose", "something" }, PORT_XMPP }, { "weasel-juice.org", PORT_XMPP, "thud.org", REACHABLE, UNREACHABLE }, { PLAINTEXT_OK, { "moose@weasel-juice.org", "something", PLAIN, NOTLS }, { NULL, 0, XMPP_V1, OLD_SSL } } }, /* we actually tolerate > 1.0 versions */ { "/connector+ssl/problem/xmpp/version/1.x", NOISY, { S_NO_ERROR, 0, 0, "DIGEST-MD5" }, { { TLS, NULL, "1.1" }, { SERVER_PROBLEM_NO_PROBLEM, { XMPP_PROBLEM_OLD_SSL, OK, OK, OK, OK } }, { "moose", "something" }, PORT_XMPP }, { "weasel-juice.org", PORT_XMPP, "thud.org", REACHABLE, UNREACHABLE }, { PLAINTEXT_OK, { "moose@weasel-juice.org", "something", PLAIN, NOTLS }, { NULL, 0, XMPP_V1, OLD_SSL } } }, { "/connector+ssl/problem/xmpp/error/host-unknown", NOISY, { S_WOCKY_XMPP_STREAM_ERROR, WOCKY_XMPP_STREAM_ERROR_HOST_UNKNOWN, -1 }, { { TLS, NULL }, { SERVER_PROBLEM_NO_PROBLEM, { XMPP_PROBLEM_OTHER_HOST|XMPP_PROBLEM_OLD_SSL, OK, OK, OK, OK } }, { "moose", "something" }, PORT_XMPP }, { "weasel-juice.org", PORT_XMPP, "thud.org", REACHABLE, UNREACHABLE }, { PLAINTEXT_OK, { "moose@weasel-juice.org", "something", PLAIN, NOTLS }, { NULL, 0, XMPP_V1, OLD_SSL } } }, { "/connector+ssl/problem/xmpp/error/bind-conflict", NOISY, { S_WOCKY_XMPP_STREAM_ERROR, WOCKY_XMPP_STREAM_ERROR_CONFLICT, -1 }, { { TLS, NULL }, { SERVER_PROBLEM_NO_PROBLEM, { XMPP_PROBLEM_OLD_SSL, BIND_PROBLEM_CLASH, OK, OK, OK } }, { "moose", "something" }, PORT_XMPP }, { "weasel-juice.org", PORT_XMPP, "thud.org", REACHABLE, UNREACHABLE }, { PLAINTEXT_OK, { "moose@weasel-juice.org", "something", PLAIN, NOTLS }, { NULL, 0, XMPP_V1, OLD_SSL } } }, { "/connector+ssl/problem/xmpp/error/session-fail", NOISY, { S_WOCKY_XMPP_STREAM_ERROR, WOCKY_XMPP_STREAM_ERROR_RESOURCE_CONSTRAINT, -1 }, { { TLS, NULL }, { SERVER_PROBLEM_NO_PROBLEM, { XMPP_PROBLEM_OLD_SSL, OK, SESSION_PROBLEM_NO_SESSION, OK, OK } }, { "moose", "something" }, PORT_XMPP }, { "weasel-juice.org", PORT_XMPP, "thud.org", REACHABLE, UNREACHABLE }, { PLAINTEXT_OK, { "moose@weasel-juice.org", "something", PLAIN, NOTLS }, { NULL, 0, XMPP_V1, OLD_SSL } } }, { "/connector+ssl/problem/xmpp/features", NOISY, { S_WOCKY_CONNECTOR_ERROR, WOCKY_CONNECTOR_ERROR_BAD_FEATURES, -1 }, { { TLS, NULL }, { SERVER_PROBLEM_NO_PROBLEM, { XMPP_PROBLEM_FEATURES|XMPP_PROBLEM_OLD_SSL, OK, OK, OK, OK } }, { "moose", "something" }, PORT_XMPP }, { "weasel-juice.org", PORT_XMPP, "thud.org", REACHABLE, UNREACHABLE }, { PLAINTEXT_OK, { "moose@weasel-juice.org", "something", PLAIN, NOTLS }, { NULL, 0, XMPP_V1, OLD_SSL } } }, /* WOCKY_CONNECTOR_ERROR_BIND_UNAVAILABLE */ { "/connector+ssl/problem/xmpp/no-bind", NOISY, { S_WOCKY_CONNECTOR_ERROR, WOCKY_CONNECTOR_ERROR_BIND_UNAVAILABLE, -1 }, { { TLS, NULL }, { SERVER_PROBLEM_NO_PROBLEM, { XMPP_PROBLEM_CANNOT_BIND|XMPP_PROBLEM_OLD_SSL, OK, OK, OK, OK } }, { "moose", "something" }, PORT_XMPP }, { "weasel-juice.org", PORT_XMPP, "thud.org", REACHABLE, UNREACHABLE }, { PLAINTEXT_OK, { "moose@weasel-juice.org", "something", PLAIN, NOTLS }, { NULL, 0, XMPP_V1, OLD_SSL } } }, /* WOCKY_CONNECTOR_ERROR_BIND_INVALID */ { "/connector+ssl/problem/xmpp/bind/invalid", NOISY, { S_WOCKY_CONNECTOR_ERROR, WOCKY_CONNECTOR_ERROR_BIND_INVALID, -1 }, { { TLS, NULL }, { SERVER_PROBLEM_NO_PROBLEM, { XMPP_PROBLEM_OLD_SSL, BIND_PROBLEM_INVALID, OK, OK, OK } }, { "moose", "something" }, PORT_XMPP }, { "weasel-juice.org", PORT_XMPP, "thud.org", REACHABLE, UNREACHABLE }, { PLAINTEXT_OK, { "moose@weasel-juice.org", "something", PLAIN, NOTLS }, { NULL, 0, XMPP_V1, OLD_SSL } } }, /* WOCKY_CONNECTOR_ERROR_BIND_DENIED */ { "/connector+ssl/problem/xmpp/bind/denied", NOISY, { S_WOCKY_CONNECTOR_ERROR, WOCKY_CONNECTOR_ERROR_BIND_DENIED, -1 }, { { TLS, NULL }, { SERVER_PROBLEM_NO_PROBLEM, { XMPP_PROBLEM_OLD_SSL, BIND_PROBLEM_DENIED, OK, OK, OK } }, { "moose", "something" }, PORT_XMPP }, { "weasel-juice.org", PORT_XMPP, "thud.org", REACHABLE, UNREACHABLE }, { PLAINTEXT_OK, { "moose@weasel-juice.org", "something", PLAIN, NOTLS }, { NULL, 0, XMPP_V1, OLD_SSL } } }, /* WOCKY_CONNECTOR_ERROR_BIND_CONFLICT */ { "/connector+ssl/problem/xmpp/bind/conflict", NOISY, { S_WOCKY_CONNECTOR_ERROR, WOCKY_CONNECTOR_ERROR_BIND_CONFLICT, -1 }, { { TLS, NULL }, { SERVER_PROBLEM_NO_PROBLEM, { XMPP_PROBLEM_OLD_SSL, BIND_PROBLEM_CONFLICT, OK, OK, OK } }, { "moose", "something" }, PORT_XMPP }, { "weasel-juice.org", PORT_XMPP, "thud.org", REACHABLE, UNREACHABLE }, { PLAINTEXT_OK, { "moose@weasel-juice.org", "something", PLAIN, NOTLS }, { NULL, 0, XMPP_V1, OLD_SSL } } }, /* WOCKY_CONNECTOR_ERROR_BIND_REJECTED */ { "/connector+ssl/problem/xmpp/bind/rejected", NOISY, { S_WOCKY_CONNECTOR_ERROR, WOCKY_CONNECTOR_ERROR_BIND_REJECTED, -1 }, { { TLS, NULL }, { SERVER_PROBLEM_NO_PROBLEM, { XMPP_PROBLEM_OLD_SSL, BIND_PROBLEM_REJECTED, OK, OK, OK } }, { "moose", "something" }, PORT_XMPP }, { "weasel-juice.org", PORT_XMPP, "thud.org", REACHABLE, UNREACHABLE }, { PLAINTEXT_OK, { "moose@weasel-juice.org", "something", PLAIN, NOTLS }, { NULL, 0, XMPP_V1, OLD_SSL } } }, /* WOCKY_CONNECTOR_ERROR_BIND_FAILED */ { "/connector+ssl/problem/xmpp/bind/failed", NOISY, { S_WOCKY_CONNECTOR_ERROR, WOCKY_CONNECTOR_ERROR_BIND_FAILED, -1 }, { { TLS, NULL }, { SERVER_PROBLEM_NO_PROBLEM, { XMPP_PROBLEM_OLD_SSL, BIND_PROBLEM_FAILED, OK, OK, OK } }, { "moose", "something" }, PORT_XMPP }, { "weasel-juice.org", PORT_XMPP, "thud.org", REACHABLE, UNREACHABLE }, { PLAINTEXT_OK, { "moose@weasel-juice.org", "something", PLAIN, NOTLS }, { NULL, 0, XMPP_V1, OLD_SSL } } }, { "/connector+ssl/problem/xmpp/bind/nonsense", NOISY, { S_WOCKY_CONNECTOR_ERROR, WOCKY_CONNECTOR_ERROR_BIND_FAILED, -1 }, { { TLS, NULL }, { SERVER_PROBLEM_NO_PROBLEM, { XMPP_PROBLEM_OLD_SSL, BIND_PROBLEM_NONSENSE, OK, OK, OK } }, { "moose", "something" }, PORT_XMPP }, { "weasel-juice.org", PORT_XMPP, "thud.org", REACHABLE, UNREACHABLE }, { PLAINTEXT_OK, { "moose@weasel-juice.org", "something", PLAIN, NOTLS }, { NULL, 0, XMPP_V1, OLD_SSL } } }, { "/connector+ssl/problem/xmpp/bind/no-jid", NOISY, { S_NO_ERROR, 0, 0, "DIGEST-MD5" }, { { TLS, NULL }, { SERVER_PROBLEM_NO_PROBLEM, { XMPP_PROBLEM_OLD_SSL, BIND_PROBLEM_NO_JID, OK, OK, OK } }, { "moose", "something" }, PORT_XMPP }, { "weasel-juice.org", PORT_XMPP, "thud.org", REACHABLE, UNREACHABLE }, { PLAINTEXT_OK, { "moose@weasel-juice.org", "something", PLAIN, NOTLS }, { NULL, 0, XMPP_V1, OLD_SSL } } }, { "/connector+ssl/problem/xmpp/session/none", NOISY, { S_NO_ERROR, 0, 0, "DIGEST-MD5" }, { { TLS, NULL }, { SERVER_PROBLEM_NO_PROBLEM, { XMPP_PROBLEM_NO_SESSION|XMPP_PROBLEM_OLD_SSL, OK, OK, OK, OK } }, { "moose", "something" }, PORT_XMPP }, { "weasel-juice.org", PORT_XMPP, "thud.org", REACHABLE, UNREACHABLE }, { PLAINTEXT_OK, { "moose@weasel-juice.org", "something", PLAIN, NOTLS }, { NULL, 0, XMPP_V1, OLD_SSL } } }, /* WOCKY_CONNECTOR_ERROR_SESSION_FAILED */ { "/connector+ssl/problem/xmpp/session/failed", NOISY, { S_WOCKY_CONNECTOR_ERROR, WOCKY_CONNECTOR_ERROR_SESSION_FAILED, -1 }, { { TLS, NULL }, { SERVER_PROBLEM_NO_PROBLEM, { XMPP_PROBLEM_OLD_SSL, OK, SESSION_PROBLEM_FAILED, OK, OK } }, { "moose", "something" }, PORT_XMPP }, { "weasel-juice.org", PORT_XMPP, "thud.org", REACHABLE, UNREACHABLE }, { PLAINTEXT_OK, { "moose@weasel-juice.org", "something", PLAIN, NOTLS }, { NULL, 0, XMPP_V1, OLD_SSL } } }, /* WOCKY_CONNECTOR_ERROR_SESSION_DENIED */ { "/connector+ssl/problem/xmpp/session/denied", NOISY, { S_WOCKY_CONNECTOR_ERROR, WOCKY_CONNECTOR_ERROR_SESSION_DENIED, -1 }, { { TLS, NULL }, { SERVER_PROBLEM_NO_PROBLEM, { XMPP_PROBLEM_OLD_SSL, OK, SESSION_PROBLEM_DENIED, OK, OK } }, { "moose", "something" }, PORT_XMPP }, { "weasel-juice.org", PORT_XMPP, "thud.org", REACHABLE, UNREACHABLE }, { PLAINTEXT_OK, { "moose@weasel-juice.org", "something", PLAIN, NOTLS }, { NULL, 0, XMPP_V1, OLD_SSL } } }, /* WOCKY_CONNECTOR_ERROR_SESSION_CONFLICT */ { "/connector+ssl/problem/xmpp/session/conflict", NOISY, { S_WOCKY_CONNECTOR_ERROR, WOCKY_CONNECTOR_ERROR_SESSION_CONFLICT, -1 }, { { TLS, NULL }, { SERVER_PROBLEM_NO_PROBLEM, { XMPP_PROBLEM_OLD_SSL, OK, SESSION_PROBLEM_CONFLICT, OK, OK } }, { "moose", "something" }, PORT_XMPP }, { "weasel-juice.org", PORT_XMPP, "thud.org", REACHABLE, UNREACHABLE }, { PLAINTEXT_OK, { "moose@weasel-juice.org", "something", PLAIN, NOTLS }, { NULL, 0, XMPP_V1, OLD_SSL } } }, /* WOCKY_CONNECTOR_ERROR_SESSION_REJECTED */ { "/connector+ssl/problem/xmpp/session/rejected", NOISY, { S_WOCKY_CONNECTOR_ERROR, WOCKY_CONNECTOR_ERROR_SESSION_REJECTED, -1 }, { { TLS, NULL }, { SERVER_PROBLEM_NO_PROBLEM, { XMPP_PROBLEM_OLD_SSL, OK, SESSION_PROBLEM_REJECTED, OK, OK } }, { "moose", "something" }, PORT_XMPP }, { "weasel-juice.org", PORT_XMPP, "thud.org", REACHABLE, UNREACHABLE }, { PLAINTEXT_OK, { "moose@weasel-juice.org", "something", PLAIN, NOTLS }, { NULL, 0, XMPP_V1, OLD_SSL } } }, { "/connector+ssl/problem/xmpp/session/nonsense", NOISY, { S_WOCKY_CONNECTOR_ERROR, WOCKY_CONNECTOR_ERROR_SESSION_FAILED, -1 }, { { TLS, NULL }, { SERVER_PROBLEM_NO_PROBLEM, { XMPP_PROBLEM_OLD_SSL, OK, SESSION_PROBLEM_NONSENSE, OK, OK } }, { "moose", "something" }, PORT_XMPP }, { "weasel-juice.org", PORT_XMPP, "thud.org", REACHABLE, UNREACHABLE }, { PLAINTEXT_OK, { "moose@weasel-juice.org", "something", PLAIN, NOTLS }, { NULL, 0, XMPP_V1, OLD_SSL } } }, { "/connector+ssl/econnreset/server-start", NOISY, { S_ANY_ERROR, 0 }, { { TLS, NULL }, { SERVER_PROBLEM_NO_PROBLEM, { XMPP_PROBLEM_OLD_SSL, OK, OK, SERVER_DEATH_SERVER_START, OK } }, { "moose", "something" }, PORT_XMPP }, { "weasel-juice.org", PORT_XMPP, "thud.org", REACHABLE, UNREACHABLE }, { PLAINTEXT_OK, { "moose@weasel-juice.org", "something", PLAIN, NOTLS }, { NULL, 0, XMPP_V1, OLD_SSL } } }, { "/connector+ssl/econnreset/client-open", NOISY, { S_ANY_ERROR, 0 }, { { TLS, NULL }, { SERVER_PROBLEM_NO_PROBLEM, { XMPP_PROBLEM_OLD_SSL, OK, OK, SERVER_DEATH_CLIENT_OPEN, OK } }, { "moose", "something" }, PORT_XMPP }, { "weasel-juice.org", PORT_XMPP, "thud.org", REACHABLE, UNREACHABLE }, { PLAINTEXT_OK, { "moose@weasel-juice.org", "something", PLAIN, NOTLS }, { NULL, 0, XMPP_V1, OLD_SSL } } }, { "/connector+ssl/econnreset/server-open", NOISY, { S_ANY_ERROR, 0 }, { { TLS, NULL }, { SERVER_PROBLEM_NO_PROBLEM, { XMPP_PROBLEM_OLD_SSL, OK, OK, SERVER_DEATH_SERVER_OPEN, OK } }, { "moose", "something" }, PORT_XMPP }, { "weasel-juice.org", PORT_XMPP, "thud.org", REACHABLE, UNREACHABLE }, { PLAINTEXT_OK, { "moose@weasel-juice.org", "something", PLAIN, NOTLS }, { NULL, 0, XMPP_V1, OLD_SSL } } }, { "/connector+ssl/econnreset/features", NOISY, { S_ANY_ERROR, 0 }, { { TLS, NULL }, { SERVER_PROBLEM_NO_PROBLEM, { XMPP_PROBLEM_OLD_SSL, OK, OK, SERVER_DEATH_FEATURES, OK } }, { "moose", "something" }, PORT_XMPP }, { "weasel-juice.org", PORT_XMPP, "thud.org", REACHABLE, UNREACHABLE }, { PLAINTEXT_OK, { "moose@weasel-juice.org", "something", PLAIN, NOTLS }, { NULL, 0, XMPP_V1, OLD_SSL } } }, { "/connector+ssl/econnreset/ssl-negotiate", QUIET, { S_ANY_ERROR, 0 }, { { TLS, NULL }, { SERVER_PROBLEM_NO_PROBLEM, { XMPP_PROBLEM_OLD_SSL, OK, OK, SERVER_DEATH_TLS_NEG, OK } }, { "moose", "something" }, PORT_XMPP }, { "weasel-juice.org", PORT_XMPP, "thud.org", REACHABLE, UNREACHABLE }, { PLAINTEXT_OK, { "moose@weasel-juice.org", "something", PLAIN, NOTLS }, { NULL, 0, XMPP_V1, OLD_SSL } } }, /* ********************************************************************* */ /* certificate verification tests */ { "/connector/cert-verification/tls/nohost/ok", QUIET, { S_NO_ERROR, }, { { TLS, NULL }, { SERVER_PROBLEM_NO_PROBLEM, CONNECTOR_OK }, { "moose", "something" }, PORT_XMPP }, { "weasel-juice.org", PORT_XMPP, "thud.org", REACHABLE, UNREACHABLE }, { PLAINTEXT_OK, { "moose@weasel-juice.org", "something", PLAIN, TLS }, { NULL, 0, XMPP_V1 } } }, { "/connector/multica-verification/tls/nohost/ok", QUIET, { S_NO_ERROR, }, { { TLS, NULL }, { SERVER_PROBLEM_NO_PROBLEM, CONNECTOR_OK }, { "moose", "something" }, PORT_XMPP }, { "weasel-juice.org", PORT_XMPP, "thud.org", REACHABLE, UNREACHABLE }, { PLAINTEXT_OK, { "moose@weasel-juice.org", "something", PLAIN, TLS }, { NULL, 0, XMPP_V1, STARTTLS, CERT_CHECK_STRICT, TLS_CA_DIR } } }, { "/connector/cert-verification/tls/host/ok", QUIET, { S_NO_ERROR, }, { { TLS, NULL }, { SERVER_PROBLEM_NO_PROBLEM, CONNECTOR_OK }, { "moose", "something" }, PORT_XMPP }, { NULL, 0, "thud.org", REACHABLE, NULL }, { PLAINTEXT_OK, { "moose@weasel-juice.org", "something", PLAIN, TLS }, { "thud.org", 0, XMPP_V1 } } }, { "/connector/cert-verification/tls/nohost/fail/name-mismatch", QUIET, { S_WOCKY_TLS_CERT_ERROR, WOCKY_TLS_CERT_NAME_MISMATCH, -1 }, { { TLS, NULL }, { SERVER_PROBLEM_NO_PROBLEM, CONNECTOR_OK }, { "moose", "something" }, PORT_XMPP }, { "tomato-juice.org", PORT_XMPP, "thud.org", REACHABLE, UNREACHABLE }, { PLAINTEXT_OK, { "moose@tomato-juice.org", "something", PLAIN, TLS }, { NULL, 0, XMPP_V1 } } }, { "/connector/cert-verification/tls/host/fail/name-mismatch", QUIET, { S_WOCKY_TLS_CERT_ERROR, WOCKY_TLS_CERT_NAME_MISMATCH, -1 }, { { TLS, NULL }, { SERVER_PROBLEM_NO_PROBLEM, CONNECTOR_OK }, { "moose", "something" }, PORT_XMPP }, { NULL, 0, "tomato-juice.org", REACHABLE, NULL }, { PLAINTEXT_OK, { "moose@tomato-juice.org", "something", PLAIN, TLS }, { "tomato-juice.org", 0, XMPP_V1 } } }, { "/connector/cert-verification/tls/expired/fail", QUIET, { S_WOCKY_TLS_CERT_ERROR, WOCKY_TLS_CERT_EXPIRED, -1 }, { { TLS, NULL }, { SERVER_PROBLEM_NO_PROBLEM, CONNECTOR_OK }, { "moose", "something" }, PORT_XMPP, CERT_EXPIRED }, { "weasel-juice.org", PORT_XMPP, "thud.org", REACHABLE, UNREACHABLE }, { PLAINTEXT_OK, { "moose@weasel-juice.org", "something", PLAIN, TLS }, { NULL, 0, XMPP_V1 } } }, { "/connector/cert-verification/tls/inactive/fail", QUIET, { S_WOCKY_TLS_CERT_ERROR, WOCKY_TLS_CERT_NOT_ACTIVE, -1 }, { { TLS, NULL }, { SERVER_PROBLEM_NO_PROBLEM, CONNECTOR_OK }, { "moose", "something" }, PORT_XMPP, CERT_NOT_YET }, { "weasel-juice.org", PORT_XMPP, "thud.org", REACHABLE, UNREACHABLE }, { PLAINTEXT_OK, { "moose@weasel-juice.org", "something", PLAIN, TLS }, { NULL, 0, XMPP_V1 } } }, { "/connector/cert-verification/tls/selfsigned/fail", QUIET, { S_WOCKY_TLS_CERT_ERROR, WOCKY_TLS_CERT_INVALID, -1 }, { { TLS, NULL }, { SERVER_PROBLEM_NO_PROBLEM, CONNECTOR_OK }, { "moose", "something" }, PORT_XMPP, CERT_SELFSIGN }, { "weasel-juice.org", PORT_XMPP, "thud.org", REACHABLE, UNREACHABLE }, { PLAINTEXT_OK, { "moose@weasel-juice.org", "something", PLAIN, TLS }, { NULL, 0, XMPP_V1 } } }, { "/connector/cert-verification/tls/unknown/fail", QUIET, { S_WOCKY_TLS_CERT_ERROR, WOCKY_TLS_CERT_SIGNER_UNKNOWN, -1 }, { { TLS, NULL }, { SERVER_PROBLEM_NO_PROBLEM, CONNECTOR_OK }, { "moose", "something" }, PORT_XMPP, CERT_UNKNOWN }, { "weasel-juice.org", PORT_XMPP, "thud.org", REACHABLE, UNREACHABLE }, { PLAINTEXT_OK, { "moose@weasel-juice.org", "something", PLAIN, TLS }, { NULL, 0, XMPP_V1 } } }, /* This is a combination of the above test * (/connector/cert-verification/tls/unknown/fail) and * /connector/cert-verification/tls/host/fail/name-mismatch. It checks that * Wocky considers a hostname mismatch more erroneous than the certificate * being broken. */ { "/connector/cert-verification/tls/host/fail/name-mismatch-and-unknown", QUIET, { S_WOCKY_TLS_CERT_ERROR, WOCKY_TLS_CERT_NAME_MISMATCH, -1 }, { { TLS, NULL }, { SERVER_PROBLEM_NO_PROBLEM, CONNECTOR_OK }, { "moose", "something" }, PORT_XMPP, CERT_UNKNOWN }, { "tomato-juice.org", PORT_XMPP, "thud.org", REACHABLE, UNREACHABLE }, { PLAINTEXT_OK, { "moose@tomato-juice.org", "something", PLAIN, TLS }, { NULL, 0, XMPP_V1 } } }, { "/connector/cert-verification/tls/wildcard/ok", QUIET, { S_NO_ERROR }, { { TLS, NULL }, { SERVER_PROBLEM_NO_PROBLEM, CONNECTOR_OK }, { "moose", "something" }, PORT_XMPP, CERT_WILDCARD }, { "foo.weasel-juice.org", PORT_XMPP, "thud.org", REACHABLE, UNREACHABLE }, { TLS_REQUIRED, { "moose@foo.weasel-juice.org", "something", PLAIN, TLS }, { NULL, 0, XMPP_V1, STARTTLS, CERT_CHECK_STRICT, TLS_CA_DIR } } }, { "/connector/cert-verification/tls/wildcard/level-mismatch/fail", QUIET, { S_WOCKY_TLS_CERT_ERROR, WOCKY_TLS_CERT_NAME_MISMATCH, -1 }, { { TLS, NULL }, { SERVER_PROBLEM_NO_PROBLEM, CONNECTOR_OK }, { "moose", "something" }, PORT_XMPP, CERT_WILDCARD }, { "weasel-juice.org", PORT_XMPP, "thud.org", REACHABLE, UNREACHABLE }, { PLAINTEXT_OK, { "moose@weasel-juice.org", "something", PLAIN, TLS }, { NULL, 0, XMPP_V1, STARTTLS, CERT_CHECK_STRICT, TLS_CA_DIR } } }, { "/connector/cert-verification/tls/wildcard/glob-mismatch/fail", QUIET, { S_WOCKY_TLS_CERT_ERROR, WOCKY_TLS_CERT_NAME_MISMATCH, -1 }, { { TLS, NULL }, { SERVER_PROBLEM_NO_PROBLEM, CONNECTOR_OK }, { "moose", "something" }, PORT_XMPP, CERT_WILDCARD }, { "foo.diesel-juice.org", PORT_XMPP, "thud.org", REACHABLE, UNREACHABLE }, { TLS_REQUIRED, { "moose@foo.diesel-juice.org", "something", PLAIN, TLS }, { NULL, 0, XMPP_V1, STARTTLS, CERT_CHECK_STRICT, TLS_CA_DIR } } }, { "/connector/cert-verification/tls/bad-wildcard/fail", QUIET, { S_WOCKY_TLS_CERT_ERROR, WOCKY_TLS_CERT_NAME_MISMATCH, -1 }, { { TLS, NULL }, { SERVER_PROBLEM_NO_PROBLEM, CONNECTOR_OK }, { "moose", "something" }, PORT_XMPP, CERT_BADWILD }, { "weasel-juice.org", PORT_XMPP, "thud.org", REACHABLE, UNREACHABLE }, { TLS_REQUIRED, { "moose@weasel-juice.org", "something", PLAIN, TLS }, { NULL, 0, XMPP_V1, STARTTLS, CERT_CHECK_STRICT, TLS_CA_DIR } } }, { "/connector/cert-verification/tls/revoked/fail", QUIET, { S_WOCKY_TLS_CERT_ERROR, WOCKY_TLS_CERT_REVOKED, -1 }, { { TLS, NULL }, { SERVER_PROBLEM_NO_PROBLEM, CONNECTOR_OK }, { "moose", "something" }, PORT_XMPP, CERT_REVOKED }, { "weasel-juice.org", PORT_XMPP, "thud.org", REACHABLE, UNREACHABLE }, { TLS_REQUIRED, { "moose@weasel-juice.org", "something", PLAIN, TLS }, { NULL, 0, XMPP_V1, STARTTLS, CERT_CHECK_STRICT, TLS_CA_DIR } } }, { "/connector/cert-verification/tls/revoked/lenient/fail", QUIET, { S_WOCKY_TLS_CERT_ERROR, WOCKY_TLS_CERT_REVOKED, -1 }, { { TLS, NULL }, { SERVER_PROBLEM_NO_PROBLEM, CONNECTOR_OK }, { "moose", "something" }, PORT_XMPP, CERT_REVOKED }, { "weasel-juice.org", PORT_XMPP, "thud.org", REACHABLE, UNREACHABLE }, { TLS_REQUIRED, { "moose@weasel-juice.org", "something", PLAIN, TLS }, { NULL, 0, XMPP_V1, STARTTLS, CERT_CHECK_LENIENT, TLS_CA_DIR } } }, /* ********************************************************************* */ /* as above but with legacy ssl */ { "/connector/cert-verification/ssl/nohost/ok", QUIET, { S_NO_ERROR, }, { { TLS, NULL }, { SERVER_PROBLEM_NO_PROBLEM, { XMPP_PROBLEM_OLD_SSL, OK, OK, OK, OK } }, { "moose", "something" }, PORT_XMPP }, { "weasel-juice.org", PORT_XMPP, "thud.org", REACHABLE, UNREACHABLE }, { PLAINTEXT_OK, { "moose@weasel-juice.org", "something", PLAIN, TLS }, { NULL, 0, XMPP_V1, OLD_SSL } } }, { "/connector/cert-verification/ssl/host/ok", QUIET, { S_NO_ERROR, }, { { TLS, NULL }, { SERVER_PROBLEM_NO_PROBLEM, { XMPP_PROBLEM_OLD_SSL, OK, OK, OK, OK } }, { "moose", "something" }, PORT_XMPP }, { NULL, 0, "weasel-juice.org", REACHABLE, NULL }, { PLAINTEXT_OK, { "moose@thud.org", "something", PLAIN, TLS }, { "weasel-juice.org", PORT_XMPP, XMPP_V1, OLD_SSL } } }, { "/connector/cert-verification/ssl/nohost/fail/name-mismatch", QUIET, { S_WOCKY_TLS_CERT_ERROR, WOCKY_TLS_CERT_NAME_MISMATCH, -1 }, { { TLS, NULL }, { SERVER_PROBLEM_NO_PROBLEM, { XMPP_PROBLEM_OLD_SSL, OK, OK, OK, OK } }, { "moose", "something" }, PORT_XMPP }, { "tomato-juice.org", PORT_XMPP, "thud.org", REACHABLE, UNREACHABLE }, { PLAINTEXT_OK, { "moose@tomato-juice.org", "something", PLAIN, TLS }, { NULL, 0, XMPP_V1, OLD_SSL } } }, { "/connector/cert-verification/ssl/host/fail/name-mismatch", QUIET, { S_WOCKY_TLS_CERT_ERROR, WOCKY_TLS_CERT_NAME_MISMATCH, -1 }, { { TLS, NULL }, { SERVER_PROBLEM_NO_PROBLEM, { XMPP_PROBLEM_OLD_SSL, OK, OK, OK, OK } }, { "moose", "something" }, PORT_XMPP }, { NULL, 0, "tomato-juice.org", REACHABLE, NULL }, { PLAINTEXT_OK, { "moose@tomato-juice.org", "something", PLAIN, TLS }, { "tomato-juice.org", 0, XMPP_V1, OLD_SSL } } }, { "/connector/cert-verification/ssl/expired/fail", QUIET, { S_WOCKY_TLS_CERT_ERROR, WOCKY_TLS_CERT_EXPIRED, -1 }, { { TLS, NULL }, { SERVER_PROBLEM_NO_PROBLEM, { XMPP_PROBLEM_OLD_SSL, OK, OK, OK, OK } }, { "moose", "something" }, PORT_XMPP, CERT_EXPIRED }, { "weasel-juice.org", PORT_XMPP, "thud.org", REACHABLE, UNREACHABLE }, { PLAINTEXT_OK, { "moose@weasel-juice.org", "something", PLAIN, TLS }, { NULL, 0, XMPP_V1, OLD_SSL } } }, { "/connector/cert-verification/ssl/inactive/fail", QUIET, { S_WOCKY_TLS_CERT_ERROR, WOCKY_TLS_CERT_NOT_ACTIVE, -1 }, { { TLS, NULL }, { SERVER_PROBLEM_NO_PROBLEM, { XMPP_PROBLEM_OLD_SSL, OK, OK, OK, OK } }, { "moose", "something" }, PORT_XMPP, CERT_NOT_YET }, { "weasel-juice.org", PORT_XMPP, "thud.org", REACHABLE, UNREACHABLE }, { PLAINTEXT_OK, { "moose@weasel-juice.org", "something", PLAIN, TLS }, { NULL, 0, XMPP_V1, OLD_SSL } } }, { "/connector/cert-verification/ssl/selfsigned/fail", QUIET, { S_WOCKY_TLS_CERT_ERROR, WOCKY_TLS_CERT_INVALID, -1 }, { { TLS, NULL }, { SERVER_PROBLEM_NO_PROBLEM, { XMPP_PROBLEM_OLD_SSL, OK, OK, OK, OK } }, { "moose", "something" }, PORT_XMPP, CERT_SELFSIGN }, { "weasel-juice.org", PORT_XMPP, "thud.org", REACHABLE, UNREACHABLE }, { PLAINTEXT_OK, { "moose@weasel-juice.org", "something", PLAIN, TLS }, { NULL, 0, XMPP_V1, OLD_SSL } } }, { "/connector/cert-verification/ssl/unknown/fail", QUIET, { S_WOCKY_TLS_CERT_ERROR, WOCKY_TLS_CERT_SIGNER_UNKNOWN, -1 }, { { TLS, NULL }, { SERVER_PROBLEM_NO_PROBLEM, { XMPP_PROBLEM_OLD_SSL, OK, OK, OK, OK } }, { "moose", "something" }, PORT_XMPP, CERT_UNKNOWN }, { "weasel-juice.org", PORT_XMPP, "thud.org", REACHABLE, UNREACHABLE }, { PLAINTEXT_OK, { "moose@weasel-juice.org", "something", PLAIN, TLS }, { NULL, 0, XMPP_V1, OLD_SSL } } }, { "/connector/cert-verification/ssl/wildcard/ok", QUIET, { S_NO_ERROR }, { { TLS, NULL }, { SERVER_PROBLEM_NO_PROBLEM, { XMPP_PROBLEM_OLD_SSL, OK, OK, OK, OK } }, { "moose", "something" }, PORT_XMPP, CERT_WILDCARD }, { "foo.weasel-juice.org", PORT_XMPP, "thud.org", REACHABLE, UNREACHABLE }, { TLS_REQUIRED, { "moose@foo.weasel-juice.org", "something", PLAIN, TLS }, { NULL, 0, XMPP_V1, OLD_SSL, CERT_CHECK_STRICT, TLS_CA_DIR } } }, { "/connector/cert-verification/ssl/wildcard/level-mismatch/fail", QUIET, { S_WOCKY_TLS_CERT_ERROR, WOCKY_TLS_CERT_NAME_MISMATCH, -1 }, { { TLS, NULL }, { SERVER_PROBLEM_NO_PROBLEM, { XMPP_PROBLEM_OLD_SSL, OK, OK, OK, OK } }, { "moose", "something" }, PORT_XMPP, CERT_WILDCARD }, { "weasel-juice.org", PORT_XMPP, "thud.org", REACHABLE, UNREACHABLE }, { PLAINTEXT_OK, { "moose@weasel-juice.org", "something", PLAIN, TLS }, { NULL, 0, XMPP_V1, OLD_SSL, CERT_CHECK_STRICT, TLS_CA_DIR } } }, { "/connector/cert-verification/ssl/wildcard/glob-mismatch/fail", QUIET, { S_WOCKY_TLS_CERT_ERROR, WOCKY_TLS_CERT_NAME_MISMATCH, -1 }, { { TLS, NULL }, { SERVER_PROBLEM_NO_PROBLEM, { XMPP_PROBLEM_OLD_SSL, OK, OK, OK, OK } }, { "moose", "something" }, PORT_XMPP, CERT_WILDCARD }, { "foo.diesel-juice.org", PORT_XMPP, "thud.org", REACHABLE, UNREACHABLE }, { TLS_REQUIRED, { "moose@foo.diesel-juice.org", "something", PLAIN, TLS }, { NULL, 0, XMPP_V1, OLD_SSL, CERT_CHECK_STRICT, TLS_CA_DIR } } }, { "/connector/cert-verification/ssl/bad-wildcard/fail", QUIET, { S_WOCKY_TLS_CERT_ERROR, WOCKY_TLS_CERT_NAME_MISMATCH, -1 }, { { TLS, NULL }, { SERVER_PROBLEM_NO_PROBLEM, { XMPP_PROBLEM_OLD_SSL, OK, OK, OK, OK } }, { "moose", "something" }, PORT_XMPP, CERT_BADWILD }, { "weasel-juice.org", PORT_XMPP, "thud.org", REACHABLE, UNREACHABLE }, { TLS_REQUIRED, { "moose@weasel-juice.org", "something", PLAIN, TLS }, { NULL, 0, XMPP_V1, OLD_SSL, CERT_CHECK_STRICT, TLS_CA_DIR } } }, { "/connector/cert-verification/ssl/revoked/fail", QUIET, { S_WOCKY_TLS_CERT_ERROR, WOCKY_TLS_CERT_REVOKED, -1 }, { { TLS, NULL }, { SERVER_PROBLEM_NO_PROBLEM, { XMPP_PROBLEM_OLD_SSL, OK, OK, OK, OK } }, { "moose", "something" }, PORT_XMPP, CERT_REVOKED }, { "weasel-juice.org", PORT_XMPP, "thud.org", REACHABLE, UNREACHABLE }, { TLS_REQUIRED, { "moose@weasel-juice.org", "something", PLAIN, TLS }, { NULL, 0, XMPP_V1, OLD_SSL, CERT_CHECK_STRICT, TLS_CA_DIR } } }, { "/connector/cert-verification/ssl/revoked/lenient/fail", QUIET, { S_WOCKY_TLS_CERT_ERROR, WOCKY_TLS_CERT_REVOKED, -1 }, { { TLS, NULL }, { SERVER_PROBLEM_NO_PROBLEM, { XMPP_PROBLEM_OLD_SSL, OK, OK, OK, OK } }, { "moose", "something" }, PORT_XMPP, CERT_REVOKED }, { "weasel-juice.org", PORT_XMPP, "thud.org", REACHABLE, UNREACHABLE }, { TLS_REQUIRED, { "moose@weasel-juice.org", "something", PLAIN, TLS }, { NULL, 0, XMPP_V1, OLD_SSL, CERT_CHECK_LENIENT, TLS_CA_DIR } } }, /* ********************************************************************* */ /* certificate non-verification tests */ { "/connector/cert-nonverification/tls/nohost/ok", QUIET, { S_NO_ERROR, }, { { TLS, NULL }, { SERVER_PROBLEM_NO_PROBLEM, CONNECTOR_OK }, { "moose", "something" }, PORT_XMPP }, { "weasel-juice.org", PORT_XMPP, "thud.org", REACHABLE, UNREACHABLE }, { PLAINTEXT_OK, { "moose@weasel-juice.org", "something", PLAIN, TLS }, { NULL, 0, XMPP_V1 } } }, { "/connector/cert-nonverification/tls/host/ok", QUIET, { S_NO_ERROR, }, { { TLS, NULL }, { SERVER_PROBLEM_NO_PROBLEM, CONNECTOR_OK }, { "moose", "something" }, PORT_XMPP }, { NULL, 0, "thud.org", REACHABLE, NULL }, { PLAINTEXT_OK, { "moose@weasel-juice.org", "something", PLAIN, TLS }, { "thud.org", 0, XMPP_V1, STARTTLS, CERT_CHECK_LENIENT } } }, { "/connector/cert-nonverification/tls/nohost/ok/name-mismatch", QUIET, { S_NO_ERROR, }, { { TLS, NULL }, { SERVER_PROBLEM_NO_PROBLEM, CONNECTOR_OK }, { "moose", "something" }, PORT_XMPP }, { "tomato-juice.org", PORT_XMPP, "thud.org", REACHABLE, UNREACHABLE }, { PLAINTEXT_OK, { "moose@tomato-juice.org", "something", PLAIN, TLS }, { NULL, 0, XMPP_V1, STARTTLS, CERT_CHECK_LENIENT } } }, { "/connector/cert-nonverification/tls/host/ok/name-mismatch", QUIET, { S_NO_ERROR, }, { { TLS, NULL }, { SERVER_PROBLEM_NO_PROBLEM, CONNECTOR_OK }, { "moose", "something" }, PORT_XMPP }, { NULL, 0, "tomato-juice.org", REACHABLE, NULL }, { PLAINTEXT_OK, { "moose@tomato-juice.org", "something", PLAIN, TLS }, { "tomato-juice.org", 0, XMPP_V1, STARTTLS, CERT_CHECK_LENIENT } } }, { "/connector/cert-nonverification/tls/expired/ok", QUIET, { S_NO_ERROR, }, { { TLS, NULL }, { SERVER_PROBLEM_NO_PROBLEM, CONNECTOR_OK }, { "moose", "something" }, PORT_XMPP, CERT_EXPIRED }, { "weasel-juice.org", PORT_XMPP, "thud.org", REACHABLE, UNREACHABLE }, { PLAINTEXT_OK, { "moose@weasel-juice.org", "something", PLAIN, TLS }, { NULL, 0, XMPP_V1, STARTTLS, CERT_CHECK_LENIENT } } }, { "/connector/cert-nonverification/tls/inactive/ok", QUIET, { S_NO_ERROR, }, { { TLS, NULL }, { SERVER_PROBLEM_NO_PROBLEM, CONNECTOR_OK }, { "moose", "something" }, PORT_XMPP, CERT_NOT_YET }, { "weasel-juice.org", PORT_XMPP, "thud.org", REACHABLE, UNREACHABLE }, { PLAINTEXT_OK, { "moose@weasel-juice.org", "something", PLAIN, TLS }, { NULL, 0, XMPP_V1, STARTTLS, CERT_CHECK_LENIENT } } }, { "/connector/cert-nonverification/tls/selfsigned/ok", QUIET, { S_NO_ERROR, }, { { TLS, NULL }, { SERVER_PROBLEM_NO_PROBLEM, CONNECTOR_OK }, { "moose", "something" }, PORT_XMPP, CERT_SELFSIGN }, { "weasel-juice.org", PORT_XMPP, "thud.org", REACHABLE, UNREACHABLE }, { PLAINTEXT_OK, { "moose@weasel-juice.org", "something", PLAIN, TLS }, { NULL, 0, XMPP_V1, STARTTLS, CERT_CHECK_LENIENT } } }, { "/connector/cert-nonverification/tls/unknown/ok", QUIET, { S_NO_ERROR, }, { { TLS, NULL }, { SERVER_PROBLEM_NO_PROBLEM, CONNECTOR_OK }, { "moose", "something" }, PORT_XMPP, CERT_UNKNOWN }, { "weasel-juice.org", PORT_XMPP, "thud.org", REACHABLE, UNREACHABLE }, { PLAINTEXT_OK, { "moose@weasel-juice.org", "something", PLAIN, TLS }, { NULL, 0, XMPP_V1, STARTTLS, CERT_CHECK_LENIENT } } }, /* ********************************************************************* */ /* as above but with legacy ssl */ { "/connector/cert-nonverification/ssl/nohost/ok", QUIET, { S_NO_ERROR, }, { { TLS, NULL }, { SERVER_PROBLEM_NO_PROBLEM, { XMPP_PROBLEM_OLD_SSL, OK, OK, OK, OK } }, { "moose", "something" }, PORT_XMPP }, { "weasel-juice.org", PORT_XMPP, "thud.org", REACHABLE, UNREACHABLE }, { PLAINTEXT_OK, { "moose@weasel-juice.org", "something", PLAIN, TLS }, { NULL, 0, XMPP_V1, OLD_SSL, CERT_CHECK_LENIENT } } }, { "/connector/cert-nonverification/ssl/host/ok", QUIET, { S_NO_ERROR, }, { { TLS, NULL }, { SERVER_PROBLEM_NO_PROBLEM, { XMPP_PROBLEM_OLD_SSL, OK, OK, OK, OK } }, { "moose", "something" }, PORT_XMPP }, { NULL, 0, "weasel-juice.org", REACHABLE, NULL }, { PLAINTEXT_OK, { "moose@thud.org", "something", PLAIN, TLS }, { "weasel-juice.org", PORT_XMPP, XMPP_V1, OLD_SSL, CERT_CHECK_LENIENT } } }, { "/connector/cert-nonverification/ssl/nohost/ok/name-mismatch", QUIET, { S_NO_ERROR, }, { { TLS, NULL }, { SERVER_PROBLEM_NO_PROBLEM, { XMPP_PROBLEM_OLD_SSL, OK, OK, OK, OK } }, { "moose", "something" }, PORT_XMPP }, { "tomato-juice.org", PORT_XMPP, "thud.org", REACHABLE, UNREACHABLE }, { PLAINTEXT_OK, { "moose@tomato-juice.org", "something", PLAIN, TLS }, { NULL, 0, XMPP_V1, OLD_SSL, CERT_CHECK_LENIENT } } }, { "/connector/cert-nonverification/ssl/host/ok/name-mismatch", QUIET, { S_NO_ERROR, }, { { TLS, NULL }, { SERVER_PROBLEM_NO_PROBLEM, { XMPP_PROBLEM_OLD_SSL, OK, OK, OK, OK } }, { "moose", "something" }, PORT_XMPP }, { NULL, 0, "tomato-juice.org", REACHABLE, NULL }, { PLAINTEXT_OK, { "moose@tomato-juice.org", "something", PLAIN, TLS }, { "tomato-juice.org", 0, XMPP_V1, OLD_SSL, CERT_CHECK_LENIENT } } }, { "/connector/cert-nonverification/ssl/expired/ok", QUIET, { S_NO_ERROR, }, { { TLS, NULL }, { SERVER_PROBLEM_NO_PROBLEM, { XMPP_PROBLEM_OLD_SSL, OK, OK, OK, OK } }, { "moose", "something" }, PORT_XMPP, CERT_EXPIRED }, { "weasel-juice.org", PORT_XMPP, "thud.org", REACHABLE, UNREACHABLE }, { PLAINTEXT_OK, { "moose@weasel-juice.org", "something", PLAIN, TLS }, { NULL, 0, XMPP_V1, OLD_SSL, CERT_CHECK_LENIENT } } }, { "/connector/cert-nonverification/ssl/inactive/ok", QUIET, { S_NO_ERROR, }, { { TLS, NULL }, { SERVER_PROBLEM_NO_PROBLEM, { XMPP_PROBLEM_OLD_SSL, OK, OK, OK, OK } }, { "moose", "something" }, PORT_XMPP, CERT_NOT_YET }, { "weasel-juice.org", PORT_XMPP, "thud.org", REACHABLE, UNREACHABLE }, { PLAINTEXT_OK, { "moose@weasel-juice.org", "something", PLAIN, TLS }, { NULL, 0, XMPP_V1, OLD_SSL, CERT_CHECK_LENIENT } } }, { "/connector/cert-nonverification/ssl/selfsigned/ok", QUIET, { S_NO_ERROR, }, { { TLS, NULL }, { SERVER_PROBLEM_NO_PROBLEM, { XMPP_PROBLEM_OLD_SSL, OK, OK, OK, OK } }, { "moose", "something" }, PORT_XMPP, CERT_SELFSIGN }, { "weasel-juice.org", PORT_XMPP, "thud.org", REACHABLE, UNREACHABLE }, { PLAINTEXT_OK, { "moose@weasel-juice.org", "something", PLAIN, TLS }, { NULL, 0, XMPP_V1, OLD_SSL, CERT_CHECK_LENIENT } } }, { "/connector/cert-nonverification/ssl/unknown/ok", QUIET, { S_NO_ERROR, }, { { TLS, NULL }, { SERVER_PROBLEM_NO_PROBLEM, { XMPP_PROBLEM_OLD_SSL, OK, OK, OK, OK } }, { "moose", "something" }, PORT_XMPP, CERT_UNKNOWN }, { "weasel-juice.org", PORT_XMPP, "thud.org", REACHABLE, UNREACHABLE }, { PLAINTEXT_OK, { "moose@weasel-juice.org", "something", PLAIN, TLS }, { NULL, 0, XMPP_V1, OLD_SSL, CERT_CHECK_LENIENT } } }, /* we are done, cap the list: */ { NULL } }; /* ************************************************************************* */ #define STRING_OK(x) (((x) != NULL) && (*x != '\0')) static void setup_dummy_dns_entries (const test_t *test) { TestResolver *tr = TEST_RESOLVER (kludged); guint port = test->dns.port ? test->dns.port : PORT_XMPP; const char *domain = test->dns.srv; const char *host = test->dns.host; const char *addr = test->dns.addr; const char *s_ip = test->dns.srvhost; test_resolver_reset (tr); if (STRING_OK (domain) && STRING_OK (host)) test_resolver_add_SRV (tr, "xmpp-client", "tcp", domain, host, port); if (STRING_OK (domain) && STRING_OK (s_ip)) test_resolver_add_A (tr, domain, s_ip); if (STRING_OK (host) && STRING_OK (addr)) test_resolver_add_A (tr, host, addr); test_resolver_add_A (tr, INVISIBLE_HOST, UNREACHABLE); test_resolver_add_A (tr, VISIBLE_HOST, REACHABLE); } /* ************************************************************************* */ /* Dummy XMPP server */ static void start_dummy_xmpp_server (ServerParameters *srv); static gboolean client_connected (GIOChannel *channel, GIOCondition cond, gpointer data) { ServerParameters *srv = data; struct sockaddr_in client; socklen_t clen = sizeof (client); int ssock = g_io_channel_unix_get_fd (channel); int csock = accept (ssock, (struct sockaddr *) &client, &clen); GSocket *gsock = g_socket_new_from_fd (csock, NULL); ConnectorProblem *cproblem = &srv->problem.conn; GSocketConnection *gconn; if (csock < 0) { perror ("accept() failed"); g_warning ("accept() failed on socket that should have been ready."); return TRUE; } if (!srv->features.tls) cproblem->xmpp |= XMPP_PROBLEM_NO_TLS; gconn = g_object_new (G_TYPE_SOCKET_CONNECTION, "socket", gsock, NULL); g_object_unref (gsock); srv->server = test_connector_server_new (G_IO_STREAM (gconn), srv->features.auth_mech, srv->auth.user, srv->auth.pass, srv->features.version, cproblem, srv->problem.sasl, srv->cert); g_object_unref (gconn); /* Recursively start extra servers */ if (srv->extra_server != NULL) { test_connector_server_set_other_host (srv->server, REACHABLE, srv->extra_server->port); start_dummy_xmpp_server (srv->extra_server); } test_connector_server_start (srv->server); srv->watch = 0; return FALSE; } static void start_dummy_xmpp_server (ServerParameters *srv) { int ssock; int reuse = 1; struct sockaddr_in server; int res = -1; guint port = srv->port; if (port == 0) return; memset (&server, 0, sizeof (server)); server.sin_family = AF_INET; /* mingw doesn't support aton or pton so using more portable inet_addr */ server.sin_addr.s_addr = inet_addr ((const char * ) REACHABLE); server.sin_port = htons (port); ssock = socket (AF_INET, SOCK_STREAM, IPPROTO_TCP); setsockopt (ssock, SOL_SOCKET, SO_REUSEADDR, (const char *) &reuse, sizeof (reuse)); #ifdef G_OS_UNIX setsockopt (ssock, IPPROTO_TCP, TCP_NODELAY, (const char *) &reuse, sizeof (reuse)); #endif res = bind (ssock, (struct sockaddr *) &server, sizeof (server)); if (res != 0) { int code = errno; char *err = g_strdup_printf ("bind to " REACHABLE ":%d failed", port); perror (err); g_free (err); exit (code); } res = listen (ssock, 1024); if (res != 0) { int code = errno; char *err = g_strdup_printf ("listen on " REACHABLE ":%d failed", port); perror (err); g_free (err); exit (code); } srv->channel = g_io_channel_unix_new (ssock); g_io_channel_set_flags (srv->channel, G_IO_FLAG_NONBLOCK, NULL); srv->watch = g_io_add_watch (srv->channel, G_IO_IN|G_IO_PRI, client_connected, srv); g_io_channel_set_close_on_unref (srv->channel, TRUE); return; } /* ************************************************************************* */ static void test_server_teardown_cb (GObject *source, GAsyncResult *result, gpointer user_data) { GMainLoop *loop = user_data; g_assert (test_connector_server_teardown_finish ( TEST_CONNECTOR_SERVER (source), result, NULL)); g_main_loop_quit (loop); } static void test_server_teardown (test_t *test, ServerParameters *srv) { /* Recursively teardown extra servers */ if (srv->extra_server != NULL) test_server_teardown (test, srv->extra_server); srv->extra_server = NULL; if (srv->server != NULL) { GMainLoop *loop = g_main_loop_new (NULL, FALSE); if (test->result.used_mech == NULL) { test->result.used_mech = g_strdup ( test_connector_server_get_used_mech (srv->server)); } /* Run until server is down */ test_connector_server_teardown (srv->server, test_server_teardown_cb, loop); g_main_loop_run (loop); g_clear_object (&srv->server); } if (srv->watch != 0) g_source_remove (srv->watch); srv->watch = 0; if (srv->channel != NULL) g_io_channel_unref (srv->channel); srv->channel = NULL; } static void test_done (GObject *source, GAsyncResult *res, gpointer data) { test_t *test = data; WockyConnector *wcon = WOCKY_CONNECTOR (source); WockyXmppConnection *conn = NULL; error = NULL; switch (test->client.op) { case OP_CONNECT: conn = wocky_connector_connect_finish (wcon, res, &test->result.jid, &test->result.sid, &error); test->ok = (conn != NULL); break; case OP_REGISTER: conn = wocky_connector_register_finish (wcon, res, &test->result.jid, &test->result.sid, &error); test->ok = (conn != NULL); break; case OP_CANCEL: test->ok = wocky_connector_unregister_finish (wcon, res, &error); break; } if (conn != NULL) test->result.xmpp = g_object_ref (conn); test_server_teardown (test, &test->server_parameters); g_main_loop_quit (mainloop); } typedef void (*test_func) (gconstpointer); #ifdef G_OS_UNIX static void connection_established_cb (WockyConnector *connector, GSocketConnection *conn, gpointer user_data) { GSocket *sock = g_socket_connection_get_socket (conn); gint fd, flag = 1; fd = g_socket_get_fd (sock); setsockopt (fd, IPPROTO_TCP, TCP_NODELAY, (const char *) &flag, sizeof (flag)); } #endif static gboolean start_test (gpointer data) { test_t *test = data; if (test->client.setup != NULL) (test->client.setup) (test); switch (test->client.op) { case OP_CONNECT: wocky_connector_connect_async (test->connector, NULL, test_done, data); break; case OP_REGISTER: wocky_connector_register_async (test->connector, NULL, test_done, data); break; case OP_CANCEL: wocky_connector_unregister_async (test->connector, NULL, test_done, data); break; } return FALSE; } static void run_test (gpointer data) { WockyConnector *wcon = NULL; WockyTLSHandler *handler; test_t *test = data; struct stat dummy; gchar *base; char *path; const gchar *ca; /* clean up any leftover messes from previous tests */ /* unlink the sasl db tmpfile, it will cause a deadlock */ base = g_get_current_dir (); path = g_strdup_printf ("%s/__db.%s", base, SASL_DB_NAME); g_free (base); g_assert ((g_stat (path, &dummy) != 0) || (g_unlink (path) == 0)); g_free (path); /* end of cleanup block */ start_dummy_xmpp_server (&test->server_parameters); setup_dummy_dns_entries (test); ca = test->client.options.ca ? test->client.options.ca : TLS_CA_CRT_FILE; /* insecure tls cert/etc not yet implemented */ handler = wocky_tls_handler_new (test->client.options.lax_ssl); wcon = g_object_new ( WOCKY_TYPE_CONNECTOR, "jid" , test->client.auth.jid, "password" , test->client.auth.pass, "xmpp-server" , test->client.options.host, "xmpp-port" , test->client.options.port, "tls-required" , test->client.require_tls, "encrypted-plain-auth-ok" , !test->client.auth.secure, /* this refers to PLAINTEXT vs CRYPT, not PLAIN vs DIGEST */ "plaintext-auth-allowed" , !test->client.auth.tls, "legacy" , test->client.options.jabber, "old-ssl" , test->client.options.ssl, "tls-handler" , handler, NULL); /* Make sure we only use the test CAs, not system-wide ones. */ wocky_tls_handler_forget_cas (handler); g_assert (wocky_tls_handler_get_cas (handler) == NULL); /* check if the cert paths are valid */ g_assert (g_file_test (TLS_CA_CRT_FILE, G_FILE_TEST_EXISTS)); wocky_tls_handler_add_ca (handler, ca); /* not having a CRL can expose a bug in the openssl error handling * (basically we get 'CRL not fetched' instead of 'Expired'): * The bug has been fixed, but we can keep checking for it by * dropping the CRLs when the test is for an expired cert */ if (test->server_parameters.cert != CERT_EXPIRED) wocky_tls_handler_add_crl (handler, TLS_CRL_DIR); g_object_unref (handler); test->connector = wcon; g_idle_add (start_test, test); #ifdef G_OS_UNIX /* set TCP_NODELAY as soon as possible */ g_signal_connect (test->connector, "connection-established", G_CALLBACK (connection_established_cb), NULL); #endif g_main_loop_run (mainloop); if (test->result.domain == S_NO_ERROR) { if (error != NULL) fprintf (stderr, "Error: %s.%d: %s\n", g_quark_to_string (error->domain), error->code, error->message); g_assert_no_error (error); if (test->client.op == OP_CANCEL) { g_assert (test->ok == TRUE); g_assert (test->result.xmpp == NULL); } else { g_assert (test->result.xmpp != NULL); /* make sure we selected the right auth mechanism */ if (test->result.mech != NULL) { g_assert_cmpstr (test->result.mech, ==, test->result.used_mech); } /* we got a JID back, I hope */ g_assert (test->result.jid != NULL); g_assert (*test->result.jid != '\0'); g_free (test->result.jid); /* we got a SID back, I hope */ g_assert (test->result.sid != NULL); g_assert (*test->result.sid != '\0'); g_free (test->result.sid); } /* property get/set functionality */ if (!strcmp (test->desc, CONNECTOR_INTERNALS_TEST)) { int i; gchar *identity, *session_id, *resource; WockyConnector *tmp = wocky_connector_new ("foo@bar.org", "abc", "xyz", NULL, NULL); WockyStanza *feat = NULL; gboolean jabber; gboolean oldssl; XmppProblem xproblem = test->server_parameters.problem.conn.xmpp; const gchar *prop = NULL; const gchar *str_prop[] = { "jid", "password", "xmpp-server", "email", NULL }; const gchar *str_vals[] = { "abc", "PASSWORD", "xmpp.server", "e@org", NULL }; const gchar *boolprop[] = { "plaintext-auth-allowed", "encrypted-plain-auth-ok", "tls-required", NULL }; g_object_get (wcon, "identity", &identity, "features", &feat, NULL); g_assert (identity != NULL); g_assert (*identity != '\0'); g_assert (feat != NULL); g_assert (G_OBJECT_TYPE (feat) == WOCKY_TYPE_STANZA); g_free (identity); g_object_unref (feat); identity = NULL; g_object_get (wcon, "session-id", &session_id, NULL); g_assert (session_id != NULL); g_assert (*session_id != '\0'); g_free (session_id); g_object_get (wcon, "resource", &resource, NULL); /* TODO: really? :resource gets updated to contain the actual * post-bind resource, but perhaps :resource should be updated too? */ g_assert_cmpstr (resource, ==, NULL); g_free (resource); g_object_get (wcon, "legacy", &jabber, "old-ssl", &oldssl, NULL); g_assert (jabber == (gboolean)(xproblem & XMPP_PROBLEM_OLD_SERVER)); g_assert (oldssl == (gboolean)(xproblem & XMPP_PROBLEM_OLD_SSL)); for (i = 0, prop = str_prop[0]; prop; prop = str_prop[++i]) { gchar *val = NULL; g_object_set (tmp, prop, str_vals[i], NULL); g_object_get (tmp, prop, &val, NULL); g_assert (!strcmp (val, str_vals[i])); g_assert (val != str_vals[i]); g_free (val); } for (i = 0, prop = boolprop[0]; prop; prop = boolprop[++i]) { gboolean val; g_object_set (tmp, prop, TRUE, NULL); g_object_get (tmp, prop, &val, NULL); g_assert (val); g_object_set (tmp, prop, FALSE, NULL); g_object_get (tmp, prop, &val, NULL); g_assert (!val); } g_object_set (tmp, "xmpp-port", 31415, NULL); g_object_get (tmp, "xmpp-port", &i, NULL); g_assert (i == 31415); g_object_unref (tmp); } } else { g_assert (test->result.xmpp == NULL); if (test->result.domain != S_ANY_ERROR) { /* We want the error to match either of result.code or * result.fallback_code, but don't care which. * The expected error domain is the same for either code. */ if (error->code == test->result.fallback_code) g_assert_error (error, map_static_domain (test->result.domain), test->result.fallback_code); else g_assert_error (error, map_static_domain (test->result.domain), test->result.code); } } if (wcon != NULL) g_object_unref (wcon); if (error != NULL) g_error_free (error); if (test->result.xmpp != NULL) g_object_unref (test->result.xmpp); g_free (test->result.used_mech); error = NULL; } int main (int argc, char **argv) { int i; gchar *base; gchar *path = NULL; struct stat dummy; int result; test_init (argc, argv); /* hook up the fake DNS resolver that lets us divert A and SRV queries * * into our local cache before asking the real DNS */ original = g_resolver_get_default (); kludged = g_object_new (TEST_TYPE_RESOLVER, "real-resolver", original, NULL); g_resolver_set_default (kludged); /* unlink the sasl db, we want to test against a fresh one */ base = g_get_current_dir (); path = g_strdup_printf ("%s/%s", base, SASL_DB_NAME); g_free (base); g_assert ((g_stat (path, &dummy) != 0) || (g_unlink (path) == 0)); g_free (path); mainloop = g_main_loop_new (NULL, FALSE); #ifdef HAVE_LIBSASL2 for (i = 0; tests[i].desc != NULL; i++) g_test_add_data_func (tests[i].desc, &tests[i], (test_func)run_test); #else g_message ("libsasl2 not found: skipping MD5 SASL tests"); for (i = 0; tests[i].desc != NULL; i++) { if (!wocky_strdiff (tests[i].result.mech, "DIGEST-MD5")) continue; g_test_add_data_func (tests[i].desc, &tests[i], (test_func)run_test); } #endif result = g_test_run (); test_deinit (); return result; } telepathy-gabble-0.18.2/lib/ext/wocky/tests/wocky-caps-hash-test.c0000644000175000017500000005624612200204546025013 0ustar00smcvsmcv00000000000000#ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include "wocky-test-helper.h" static gboolean check_hash (WockyStanza *stanza, const gchar *expected) { gchar *hash; hash = wocky_caps_hash_compute_from_node ( wocky_stanza_get_top_node (stanza)); g_assert_cmpstr (hash, ==, expected); g_object_unref (stanza); g_free (hash); return TRUE; } static void test_simple (void) { /* Simple example from XEP-0115 */ WockyStanza *stanza = wocky_stanza_build (WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_RESULT, NULL, NULL, '(', "identity", '@', "category", "client", '@', "name", "Exodus 0.9.1", '@', "type", "pc", ')', '(', "feature", '@', "var", "http://jabber.org/protocol/disco#info", ')', '(', "feature", '@', "var", "http://jabber.org/protocol/disco#items", ')', '(', "feature", '@', "var", "http://jabber.org/protocol/muc", ')', '(', "feature", '@', "var", "http://jabber.org/protocol/caps", ')', NULL); check_hash (stanza, "QgayPKawpkPSDYmwT/WM94uAlu0="); } static void test_complex (void) { /* Complex example from XEP-0115 */ WockyStanza *stanza = wocky_stanza_build (WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_NONE, NULL, "badger", '(', "identity", '@', "category", "client", '@', "name", "Psi 0.11", '@', "type", "pc", '#', "en", ')', '(', "identity", '@', "category", "client", '@', "name", "Ψ 0.11", '@', "type", "pc", '#', "el", ')', '(', "feature", '@', "var", "http://jabber.org/protocol/disco#info", ')', '(', "feature", '@', "var", "http://jabber.org/protocol/disco#items", ')', '(', "feature", '@', "var", "http://jabber.org/protocol/muc", ')', '(', "feature", '@', "var", "http://jabber.org/protocol/caps", ')', '(', "x", ':', "jabber:x:data", '@', "type", "result", '(', "field", '@', "var", "FORM_TYPE", '@', "type", "hidden", '(', "value", '$', "urn:xmpp:dataforms:softwareinfo", ')', ')', '(', "field", '@', "var", "ip_version", '(', "value", '$', "ipv4", ')', '(', "value", '$', "ipv6", ')', ')', '(', "field", '@', "var", "os", '(', "value", '$', "Mac", ')', ')', '(', "field", '@', "var", "os_version", '(', "value", '$', "10.5.1", ')', ')', '(', "field", '@', "var", "software", '(', "value", '$', "Psi", ')', ')', '(', "field", '@', "var", "software_version", '(', "value", '$', "0.11", ')', ')', ')', NULL); check_hash (stanza, "q07IKJEyjvHSyhy//CH0CxmKi8w="); } static void test_sorting_simple (void) { WockyStanza *stanza; gchar *one, *two; stanza = wocky_stanza_build (WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_NONE, NULL, "badger", '(', "identity", '@', "category", "client", '@', "name", "Psi 0.11", '@', "type", "pc", '#', "en", ')', '(', "identity", '@', "category", "client", '@', "name", "Ψ 0.11", '@', "type", "pc", '#', "el", ')', NULL); one = wocky_caps_hash_compute_from_node ( wocky_stanza_get_top_node (stanza)); g_object_unref (stanza); stanza = wocky_stanza_build (WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_NONE, NULL, "badger", '(', "identity", '@', "category", "client", '@', "name", "Ψ 0.11", '@', "type", "pc", '#', "el", ')', '(', "identity", '@', "category", "client", '@', "name", "Psi 0.11", '@', "type", "pc", '#', "en", ')', NULL); two = wocky_caps_hash_compute_from_node ( wocky_stanza_get_top_node (stanza)); g_object_unref (stanza); g_assert_cmpstr (one, ==, two); g_free (one); g_free (two); } static void test_sorting_complex (void) { WockyStanza *stanza; gchar *one, *two; stanza = wocky_stanza_build (WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_NONE, NULL, "badger", '(', "identity", '@', "category", "client", '@', "name", "Psi 0.11", '@', "type", "pc", '#', "en", ')', '(', "identity", '@', "category", "client", '@', "name", "Ψ 0.11", '@', "type", "pc", '#', "el", ')', '(', "feature", '@', "var", "http://jabber.org/protocol/disco#info", ')', '(', "feature", '@', "var", "http://jabber.org/protocol/disco#items", ')', '(', "feature", '@', "var", "http://jabber.org/protocol/muc", ')', '(', "feature", '@', "var", "http://jabber.org/protocol/caps", ')', '(', "x", ':', "jabber:x:data", '@', "type", "result", '(', "field", '@', "var", "FORM_TYPE", '@', "type", "hidden", '(', "value", '$', "urn:xmpp:dataforms:softwareinfo", ')', ')', '(', "field", '@', "var", "ip_version", '(', "value", '$', "ipv4", ')', '(', "value", '$', "ipv6", ')', ')', '(', "field", '@', "var", "os", '(', "value", '$', "Mac", ')', ')', '(', "field", '@', "var", "os_version", '(', "value", '$', "10.5.1", ')', ')', '(', "field", '@', "var", "software", '(', "value", '$', "Psi", ')', ')', '(', "field", '@', "var", "software_version", '(', "value", '$', "0.11", ')', ')', ')', '(', "x", ':', "jabber:x:data", '@', "type", "result", '(', "field", '@', "var", "FORM_TYPE", '@', "type", "hidden", '(', "value", '$', "urn:xmpp:dataforms:somethingelse", ')', ')', '(', "field", '@', "var", "foo", '(', "value", '$', "bananas", ')', '(', "value", '$', "cheese", ')', ')', '(', "field", '@', "var", "wheeeeee", '(', "value", '$', "I'm on a rollercoster", ')', ')', '(', "field", '@', "var", "loldongz", '@', "type", "boolean", '(', "value", '$', "1", ')', ')', ')', NULL); one = wocky_caps_hash_compute_from_node ( wocky_stanza_get_top_node (stanza)); g_object_unref (stanza); stanza = wocky_stanza_build (WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_NONE, NULL, "badger", '(', "feature", '@', "var", "http://jabber.org/protocol/disco#items", ')', '(', "x", ':', "jabber:x:data", '@', "type", "result", '(', "field", '@', "var", "FORM_TYPE", '@', "type", "hidden", '(', "value", '$', "urn:xmpp:dataforms:somethingelse", ')', ')', '(', "field", '@', "var", "wheeeeee", '(', "value", '$', "I'm on a rollercoster", ')', ')', '(', "field", '@', "var", "loldongz", '@', "type", "boolean", '(', "value", '$', "1", ')', ')', '(', "field", '@', "var", "foo", '(', "value", '$', "cheese", ')', '(', "value", '$', "bananas", ')', ')', ')', '(', "feature", '@', "var", "http://jabber.org/protocol/muc", ')', '(', "identity", '@', "category", "client", '@', "name", "Ψ 0.11", '@', "type", "pc", '#', "el", ')', '(', "feature", '@', "var", "http://jabber.org/protocol/disco#info", ')', '(', "x", ':', "jabber:x:data", '@', "type", "result", '(', "field", '@', "var", "FORM_TYPE", '@', "type", "hidden", '(', "value", '$', "urn:xmpp:dataforms:softwareinfo", ')', ')', '(', "field", '@', "var", "software", '(', "value", '$', "Psi", ')', ')', '(', "field", '@', "var", "os", '(', "value", '$', "Mac", ')', ')', '(', "field", '@', "var", "os_version", '(', "value", '$', "10.5.1", ')', ')', '(', "field", '@', "var", "software_version", '(', "value", '$', "0.11", ')', ')', '(', "field", '@', "var", "ip_version", '(', "value", '$', "ipv4", ')', '(', "value", '$', "ipv6", ')', ')', ')', '(', "identity", '@', "category", "client", '@', "name", "Psi 0.11", '@', "type", "pc", '#', "en", ')', '(', "feature", '@', "var", "http://jabber.org/protocol/caps", ')', NULL); two = wocky_caps_hash_compute_from_node ( wocky_stanza_get_top_node (stanza)); g_object_unref (stanza); g_assert_cmpstr (one, ==, two); g_free (one); g_free (two); } static void test_dataforms_invalid (void) { gchar *out; /* this stanza has a bad data form type */ WockyStanza *stanza = wocky_stanza_build (WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_NONE, NULL, "badger", '(', "identity", '@', "category", "client", '@', "name", "Psi 0.11", '@', "type", "pc", '#', "en", ')', '(', "x", ':', "jabber:x:data", '@', "type", "lol", ')', NULL); out = wocky_caps_hash_compute_from_node ( wocky_stanza_get_top_node (stanza)); g_object_unref (stanza); /* should be NULL because of type="lol" */ g_assert (out == NULL); /* now with no FORM_TYPE field but fine otherwise */ stanza = wocky_stanza_build (WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_NONE, NULL, "badger", '(', "identity", '@', "category", "client", '@', "name", "Psi 0.11", '@', "type", "pc", '#', "en", ')', '(', "x", ':', "jabber:x:data", '@', "type", "result", '(', "field", '@', "var", "ip_version", '(', "value", '$', "ipv4", ')', '(', "value", '$', "ipv6", ')', ')', ')', NULL); /* should just be ignoring the invalid data form as XEP-0115 section * "5.4 Processing Method", bullet point 3.6 tells us */ check_hash (stanza, "9LXnSGAOqGkjoewMq7WHTF4wK/U="); /* now with a FORM_TYPE but not type="hidden" */ stanza = wocky_stanza_build (WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_NONE, NULL, "badger", '(', "identity", '@', "category", "client", '@', "name", "Psi 0.11", '@', "type", "pc", '#', "en", ')', '(', "x", ':', "jabber:x:data", '@', "type", "result", '(', "field", '@', "var", "FORM_TYPE", '(', "value", '$', "urn:xmpp:dataforms:softwareinfo", ')', ')', ')', NULL); /* should just be ignoring the invalid data form as XEP-0115 section * "5.4 Processing Method", bullet point 3.6 tells us */ check_hash (stanza, "9LXnSGAOqGkjoewMq7WHTF4wK/U="); /* now with but fine otherwise; * this will fail because we have everything about the field but the * actual value */ stanza = wocky_stanza_build (WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_NONE, NULL, "badger", '(', "identity", '@', "category", "client", '@', "name", "Psi 0.11", '@', "type", "pc", '#', "en", ')', '(', "x", ':', "jabber:x:data", '@', "type", "result", '(', "field", '@', "var", "FORM_TYPE", '@', "type", "hidden", '(', "value", '$', "urn:xmpp:dataforms:softwareinfo", ')', ')', '(', "field", '@', "var", "lol_version", '(', "value", ')', ')', ')', NULL); out = wocky_caps_hash_compute_from_node ( wocky_stanza_get_top_node (stanza)); g_object_unref (stanza); g_assert (out == NULL); /* now with but fine otherwise; this will fail * because we have everything about the field but the actual * value */ stanza = wocky_stanza_build (WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_NONE, NULL, "badger", '(', "identity", '@', "category", "client", '@', "name", "Psi 0.11", '@', "type", "pc", '#', "en", ')', '(', "x", ':', "jabber:x:data", '@', "type", "result", '(', "field", '@', "var", "FORM_TYPE", '@', "type", "hidden", '(', "value", '$', "urn:xmpp:dataforms:softwareinfo", ')', ')', '(', "field", '@', "var", "lol_version", ')', ')', NULL); out = wocky_caps_hash_compute_from_node ( wocky_stanza_get_top_node (stanza)); g_object_unref (stanza); g_assert (out == NULL); /* now with but fine otherwise; the data form parser will * ignore fields with no var='' attribute so this will succeed */ stanza = wocky_stanza_build (WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_NONE, NULL, "badger", '(', "identity", '@', "category", "client", '@', "name", "Psi 0.11", '@', "type", "pc", '#', "en", ')', '(', "x", ':', "jabber:x:data", '@', "type", "result", '(', "field", '@', "var", "FORM_TYPE", '@', "type", "hidden", '(', "value", '$', "urn:xmpp:dataforms:softwareinfo", ')', ')', '(', "field", ')', ')', NULL); out = wocky_caps_hash_compute_from_node ( wocky_stanza_get_top_node (stanza)); g_object_unref (stanza); g_assert_cmpstr (out, ==, "wMFSetHbIiscGZgVgx4CZMaYIBQ="); g_free (out); } static void test_dataforms_fixed_and_no_var (void) { WockyStanza *stanza; gchar *out; g_test_bug ("61433"); /* with no var='' attribute is legal in data forms in * general, but the hashing algorithm doesn't specify what to do in this * case. We choose to make it fail; previously, we crashed. */ stanza = wocky_stanza_build (WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_NONE, NULL, "badger", '(', "identity", '@', "category", "client", '@', "name", "Psi 0.11", '@', "type", "pc", '#', "en", ')', '(', "x", ':', "jabber:x:data", '@', "type", "result", '(', "field", '@', "var", "FORM_TYPE", '@', "type", "hidden", '(', "value", '$', "urn:xmpp:dataforms:softwareinfo", ')', ')', '(', "field", '@', "type", "fixed", '(', "value", '$', "trolololol", ')', ')', ')', NULL); out = wocky_caps_hash_compute_from_node ( wocky_stanza_get_top_node (stanza)); g_object_unref (stanza); g_assert_cmpstr (out, ==, NULL); g_free (out); } static void test_dataforms_form_type_wrong_type (void) { WockyStanza *stanza; /* 6. If the response includes an extended service discovery information * form where the FORM_TYPE field is not of type "hidden" or the form * does not include a FORM_TYPE field, ignore the form but continue * processing. * * This form's FORM_TYPE is of type boolean. This used to crash the hashing * code. */ g_test_bug ("61433"); stanza = wocky_stanza_build (WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_NONE, NULL, "badger", '(', "identity", '@', "category", "client", '@', "name", "Psi 0.11", '@', "type", "pc", '#', "en", ')', '(', "x", ':', "jabber:x:data", '@', "type", "result", '(', "field", '@', "var", "FORM_TYPE", '@', "type", "boolean", '(', "value", '$', "true", ')', ')', ')', NULL); check_hash (stanza, "9LXnSGAOqGkjoewMq7WHTF4wK/U="); } static void test_dataforms_form_type_without_value (void) { WockyStanza *stanza; /* XEP-0115 says: * * 5. If the response includes more than one extended service discovery * information form with the same FORM_TYPE or the FORM_TYPE field * contains more than one element with different XML * character data, consider the entire response to be ill-formed. * 6. If the response includes an extended service discovery information * form where the FORM_TYPE field is not of type "hidden" or the form * does not include a FORM_TYPE field, ignore the form but continue * processing. * * Neither item covers the case where there is a FORM_TYPE field of type * "hidden" with no . I choose to treat it as 5 above. */ g_test_bug ("61433"); stanza = wocky_stanza_build (WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_NONE, NULL, "badger", '(', "identity", '@', "category", "client", '@', "name", "Psi 0.11", '@', "type", "pc", '#', "en", ')', '(', "x", ':', "jabber:x:data", '@', "type", "result", '(', "field", '@', "var", "FORM_TYPE", '@', "type", "hidden", ')', ')', NULL); check_hash (stanza, NULL); } static void test_dataforms_form_type_two_both_with_no_value (void) { WockyStanza *stanza; /* This ought to be equivalent to test_dataforms_form_type_without_value but * there was a crash in the form-sorting code, which of course doesn't * trigger if there's only one form. */ g_test_bug ("61433"); stanza = wocky_stanza_build (WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_NONE, NULL, "badger", '(', "identity", '@', "category", "client", '@', "name", "Psi 0.11", '@', "type", "pc", '#', "en", ')', '(', "x", ':', "jabber:x:data", '@', "type", "result", '(', "field", '@', "var", "FORM_TYPE", '@', "type", "hidden", ')', ')', '(', "x", ':', "jabber:x:data", '@', "type", "result", '(', "field", '@', "var", "FORM_TYPE", '@', "type", "hidden", ')', ')', NULL); check_hash (stanza, NULL); } static void test_dataforms_same_type (void) { gchar *out; /* stanza has two data forms both with the same FORM_TYPE value */ WockyStanza *stanza = wocky_stanza_build (WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_NONE, NULL, "badger", '(', "identity", '@', "category", "client", '@', "name", "Psi 0.11", '@', "type", "pc", '#', "en", ')', '(', "feature", '@', "var", "http://jabber.org/protocol/caps", ')', '(', "x", ':', "jabber:x:data", '@', "type", "result", '(', "field", '@', "var", "FORM_TYPE", '@', "type", "hidden", '(', "value", '$', "urn:xmpp:dataforms:softwareinfo", ')', ')', '(', "field", '@', "var", "ip_version", '(', "value", '$', "ipv4", ')', '(', "value", '$', "ipv6", ')', ')', '(', "field", '@', "var", "os", '(', "value", '$', "Mac", ')', ')', '(', "field", '@', "var", "os_version", '(', "value", '$', "10.5.1", ')', ')', '(', "field", '@', "var", "software", '(', "value", '$', "Psi", ')', ')', '(', "field", '@', "var", "software_version", '(', "value", '$', "0.11", ')', ')', ')', '(', "x", ':', "jabber:x:data", '@', "type", "result", '(', "field", '@', "var", "FORM_TYPE", '@', "type", "hidden", '(', "value", '$', "urn:xmpp:dataforms:softwareinfo", ')', ')', '(', "field", '@', "var", "ip_version", '(', "value", '$', "ipv4", ')', '(', "value", '$', "ipv6", ')', ')', '(', "field", '@', "var", "os", '(', "value", '$', "Mac", ')', ')', '(', "field", '@', "var", "os_version", '(', "value", '$', "10.5.1", ')', ')', '(', "field", '@', "var", "software", '(', "value", '$', "Psi", ')', ')', '(', "field", '@', "var", "software_version", '(', "value", '$', "0.11", ')', ')', ')', NULL); out = wocky_caps_hash_compute_from_node ( wocky_stanza_get_top_node (stanza)); g_object_unref (stanza); g_assert (out == NULL); } static void test_dataforms_boolean_values (void) { WockyStanza *stanza; gchar *one, *two; stanza = wocky_stanza_build (WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_NONE, NULL, "badger", '(', "identity", '@', "category", "client", '@', "name", "Ψ 0.11", '@', "type", "pc", '#', "el", ')', '(', "feature", '@', "var", "http://jabber.org/protocol/disco#info", ')', '(', "x", ':', "jabber:x:data", '@', "type", "result", '(', "field", '@', "var", "FORM_TYPE", '@', "type", "hidden", '(', "value", '$', "urn:xmpp:dataforms:softwareinfo", ')', ')', '(', "field", '@', "var", "software", '@', "type", "boolean", '(', "value", '$', "1", ')', ')', ')', NULL); one = wocky_caps_hash_compute_from_node ( wocky_stanza_get_top_node (stanza)); g_object_unref (stanza); stanza = wocky_stanza_build (WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_NONE, NULL, "badger", '(', "identity", '@', "category", "client", '@', "name", "Ψ 0.11", '@', "type", "pc", '#', "el", ')', '(', "feature", '@', "var", "http://jabber.org/protocol/disco#info", ')', '(', "x", ':', "jabber:x:data", '@', "type", "result", '(', "field", '@', "var", "FORM_TYPE", '@', "type", "hidden", '(', "value", '$', "urn:xmpp:dataforms:softwareinfo", ')', ')', '(', "field", '@', "var", "software", '@', "type", "boolean", '(', "value", '$', "true", ')', ')', ')', NULL); two = wocky_caps_hash_compute_from_node ( wocky_stanza_get_top_node (stanza)); g_object_unref (stanza); g_assert (one != NULL); g_assert (two != NULL); g_assert_cmpstr (one, !=, two); g_free (one); g_free (two); } int main (int argc, char **argv) { int result; test_init (argc, argv); g_test_add_func ("/caps-hash/simple", test_simple); g_test_add_func ("/caps-hash/complex", test_complex); g_test_add_func ("/caps-hash/sorting/simple", test_sorting_simple); g_test_add_func ("/caps-hash/sorting/complex", test_sorting_complex); g_test_add_func ("/caps-hash/dataforms/invalid", test_dataforms_invalid); g_test_add_func ("/caps-hash/dataforms/invalid/fixed-and-no-var", test_dataforms_fixed_and_no_var); g_test_add_func ("/caps-hash/dataforms/invalid/form_type/wrong-type", test_dataforms_form_type_wrong_type); g_test_add_func ("/caps-hash/dataforms/invalid/form_type/no-value", test_dataforms_form_type_without_value); g_test_add_func ("/caps-hash/dataforms/invalid/form_type/two-both-with-no-value", test_dataforms_form_type_two_both_with_no_value); g_test_add_func ("/caps-hash/dataforms/same-type", test_dataforms_same_type); g_test_add_func ("/caps-hash/dataforms/boolean-values", test_dataforms_boolean_values); result = g_test_run (); test_deinit (); return result; } telepathy-gabble-0.18.2/lib/ext/wocky/tests/wocky-bare-contact-test.c0000644000175000017500000002511212200204546025472 0ustar00smcvsmcv00000000000000#ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include #include "wocky-test-helper.h" static void test_instantiation (void) { WockyBareContact *a, *b; a = g_object_new (WOCKY_TYPE_BARE_CONTACT, "jid", "romeo@example.net", NULL); g_assert (WOCKY_IS_BARE_CONTACT (a)); /* WockyBareContact is a sub-class of WockyContact */ g_assert (WOCKY_IS_CONTACT (a)); b = wocky_bare_contact_new ("romeo@example.net"); g_assert (wocky_bare_contact_equal (a, b)); g_object_unref (a); g_object_unref (b); } static void test_contact_equal (void) { WockyBareContact *a, *b, *c, *d, *e, *f, *g, *h, *i; const gchar *groups[] = { "Friends", "Badger", NULL }; const gchar *groups2[] = { "Friends", "Snake", NULL }; const gchar *groups3[] = { "Badger", "Friends", NULL }; const gchar *groups4[] = { "aa", "bb", NULL }; const gchar *groups5[] = { "ab", "ba", NULL }; a = g_object_new (WOCKY_TYPE_BARE_CONTACT, "jid", "romeo@example.net", "name", "Romeo", "subscription", WOCKY_ROSTER_SUBSCRIPTION_TYPE_BOTH, "groups", groups, NULL); g_assert (wocky_bare_contact_equal (a, a)); g_assert (!wocky_bare_contact_equal (a, NULL)); g_assert (!wocky_bare_contact_equal (NULL, a)); /* Different jid */ b = g_object_new (WOCKY_TYPE_BARE_CONTACT, "jid", "romeo@example.org", "name", "Romeo", "subscription", WOCKY_ROSTER_SUBSCRIPTION_TYPE_BOTH, "groups", groups, NULL); g_assert (!wocky_bare_contact_equal (a, b)); /* Different name */ c = g_object_new (WOCKY_TYPE_BARE_CONTACT, "jid", "romeo@example.net", "name", "Juliet", "subscription", WOCKY_ROSTER_SUBSCRIPTION_TYPE_BOTH, "groups", groups, NULL); g_assert (!wocky_bare_contact_equal (a, c)); /* Different subscription */ d = g_object_new (WOCKY_TYPE_BARE_CONTACT, "jid", "romeo@example.net", "name", "Romeo", "subscription", WOCKY_ROSTER_SUBSCRIPTION_TYPE_TO, "groups", groups, NULL); g_assert (!wocky_bare_contact_equal (a, d)); /* Different groups */ e = g_object_new (WOCKY_TYPE_BARE_CONTACT, "jid", "romeo@example.net", "name", "Romeo", "subscription", WOCKY_ROSTER_SUBSCRIPTION_TYPE_BOTH, "groups", groups2, NULL); g_assert (!wocky_bare_contact_equal (a, e)); /* Same groups but in a different order */ f = g_object_new (WOCKY_TYPE_BARE_CONTACT, "jid", "romeo@example.net", "name", "Romeo", "subscription", WOCKY_ROSTER_SUBSCRIPTION_TYPE_BOTH, "groups", groups3, NULL); g_assert (wocky_bare_contact_equal (a, f)); /* No group defined */ g = g_object_new (WOCKY_TYPE_BARE_CONTACT, "jid", "romeo@example.net", "name", "Romeo", "subscription", WOCKY_ROSTER_SUBSCRIPTION_TYPE_BOTH, NULL); g_assert (wocky_bare_contact_equal (g, g)); g_assert (!wocky_bare_contact_equal (a, g)); /* regression test: used to fail with old group comparison algorithm */ h = g_object_new (WOCKY_TYPE_BARE_CONTACT, "jid", "romeo@example.net", "groups", groups4, NULL); i = g_object_new (WOCKY_TYPE_BARE_CONTACT, "jid", "romeo@example.net", "groups", groups5, NULL); g_assert (!wocky_bare_contact_equal (h, i)); g_object_unref (a); g_object_unref (b); g_object_unref (c); g_object_unref (d); g_object_unref (e); g_object_unref (f); g_object_unref (g); g_object_unref (h); g_object_unref (i); } /* test wocky_bare_contact_add_group */ static void test_add_group (void) { WockyBareContact *a, *b, *c, *d; const gchar *groups[] = { "Friends", "Badger", NULL }; const gchar *groups2[] = { "Friends", "Badger", "Snake", NULL }; const gchar *groups3[] = { "Friends", NULL }; a = g_object_new (WOCKY_TYPE_BARE_CONTACT, "jid", "romeo@example.net", "name", "Romeo", "subscription", WOCKY_ROSTER_SUBSCRIPTION_TYPE_BOTH, "groups", groups, NULL); /* same as 'a' but with one more group */ b = g_object_new (WOCKY_TYPE_BARE_CONTACT, "jid", "romeo@example.net", "name", "Romeo", "subscription", WOCKY_ROSTER_SUBSCRIPTION_TYPE_BOTH, "groups", groups2, NULL); g_assert (!wocky_bare_contact_equal (a, b)); wocky_bare_contact_add_group (a, "Snake"); g_assert (wocky_bare_contact_equal (a, b)); /* try to add an already present group is no-op */ wocky_bare_contact_add_group (a, "Snake"); g_assert (wocky_bare_contact_equal (a, b)); /* No group */ c = g_object_new (WOCKY_TYPE_BARE_CONTACT, "jid", "romeo@example.net", "name", "Romeo", "subscription", WOCKY_ROSTER_SUBSCRIPTION_TYPE_BOTH, NULL); d = g_object_new (WOCKY_TYPE_BARE_CONTACT, "jid", "romeo@example.net", "name", "Romeo", "subscription", WOCKY_ROSTER_SUBSCRIPTION_TYPE_BOTH, "groups", groups3, NULL); g_assert (!wocky_bare_contact_equal (c, d)); wocky_bare_contact_add_group (c, "Friends"); g_assert (wocky_bare_contact_equal (c, d)); g_object_unref (a); g_object_unref (b); g_object_unref (c); g_object_unref (d); } /* test wocky_bare_contact_in_group */ static void test_in_group (void) { WockyBareContact *a, *b; const gchar *groups[] = { "Friends", "Badger", NULL }; a = g_object_new (WOCKY_TYPE_BARE_CONTACT, "jid", "romeo@example.net", "name", "Romeo", "subscription", WOCKY_ROSTER_SUBSCRIPTION_TYPE_BOTH, "groups", groups, NULL); g_assert (wocky_bare_contact_in_group (a, "Friends")); g_assert (wocky_bare_contact_in_group (a, "Badger")); g_assert (!wocky_bare_contact_in_group (a, "Snake")); /* no group defined */ b = g_object_new (WOCKY_TYPE_BARE_CONTACT, "jid", "romeo@example.net", "name", "Romeo", "subscription", WOCKY_ROSTER_SUBSCRIPTION_TYPE_BOTH, NULL); g_assert (!wocky_bare_contact_in_group (b, "Snake")); g_object_unref (a); g_object_unref (b); } /* test wocky_bare_contact_remove_group */ static void test_remove_group (void) { WockyBareContact *a, *b, *c; const gchar *groups[] = { "Friends", "Badger", NULL }; const gchar *groups2[] = { "Badger", NULL }; a = g_object_new (WOCKY_TYPE_BARE_CONTACT, "jid", "romeo@example.net", "name", "Romeo", "subscription", WOCKY_ROSTER_SUBSCRIPTION_TYPE_BOTH, "groups", groups, NULL); /* same as 'a' but with one more group */ b = g_object_new (WOCKY_TYPE_BARE_CONTACT, "jid", "romeo@example.net", "name", "Romeo", "subscription", WOCKY_ROSTER_SUBSCRIPTION_TYPE_BOTH, "groups", groups2, NULL); g_assert (!wocky_bare_contact_equal (a, b)); wocky_bare_contact_remove_group (a, "Friends"); g_assert (wocky_bare_contact_equal (a, b)); /* try to remove an already not present group is no-op */ wocky_bare_contact_remove_group (a, "Friends"); g_assert (wocky_bare_contact_equal (a, b)); /* no group defined */ c = g_object_new (WOCKY_TYPE_BARE_CONTACT, "jid", "romeo@example.net", "name", "Romeo", "subscription", WOCKY_ROSTER_SUBSCRIPTION_TYPE_BOTH, NULL); g_assert (wocky_bare_contact_equal (c, c)); wocky_bare_contact_remove_group (c, "Misc"); g_assert (wocky_bare_contact_equal (c, c)); g_object_unref (a); g_object_unref (b); g_object_unref (c); } static void test_contact_copy (void) { WockyBareContact *a, *copy; const gchar *groups[] = { "Friends", "Badger", NULL }; /* Full contact */ a = g_object_new (WOCKY_TYPE_BARE_CONTACT, "jid", "romeo@example.net", "name", "Romeo", "subscription", WOCKY_ROSTER_SUBSCRIPTION_TYPE_BOTH, "groups", groups, NULL); copy = wocky_bare_contact_copy (a); g_assert (wocky_bare_contact_equal (a, copy)); g_object_unref (copy); g_object_unref (a); /* No name */ a = g_object_new (WOCKY_TYPE_BARE_CONTACT, "jid", "romeo@example.net", "subscription", WOCKY_ROSTER_SUBSCRIPTION_TYPE_BOTH, "groups", groups, NULL); copy = wocky_bare_contact_copy (a); g_assert (wocky_bare_contact_equal (a, copy)); g_object_unref (copy); g_object_unref (a); /* No subscription */ a = g_object_new (WOCKY_TYPE_BARE_CONTACT, "jid", "romeo@example.net", "groups", groups, NULL); copy = wocky_bare_contact_copy (a); g_assert (wocky_bare_contact_equal (a, copy)); g_object_unref (copy); g_object_unref (a); /* No group */ a = g_object_new (WOCKY_TYPE_BARE_CONTACT, "jid", "romeo@example.net", "subscription", WOCKY_ROSTER_SUBSCRIPTION_TYPE_BOTH, NULL); copy = wocky_bare_contact_copy (a); g_assert (wocky_bare_contact_equal (a, copy)); g_object_unref (copy); g_object_unref (a); } static void test_add_get_resource (void) { WockyBareContact *bare; WockyResourceContact *resource_1, *resource_2; GSList *resources; bare = wocky_bare_contact_new ("juliet@example.org"); resources = wocky_bare_contact_get_resources (bare); g_assert_cmpuint (g_slist_length (resources), ==, 0); /* add a ressource */ resource_1 = wocky_resource_contact_new (bare, "Resource1"); wocky_bare_contact_add_resource (bare, resource_1); resources = wocky_bare_contact_get_resources (bare); g_assert_cmpuint (g_slist_length (resources), ==, 1); g_assert (g_slist_find (resources, resource_1) != NULL); g_slist_free (resources); /* add another resource */ resource_2 = wocky_resource_contact_new (bare, "Resource2"); wocky_bare_contact_add_resource (bare, resource_2); resources = wocky_bare_contact_get_resources (bare); g_assert_cmpuint (g_slist_length (resources), ==, 2); g_assert (g_slist_find (resources, resource_1) != NULL); g_assert (g_slist_find (resources, resource_2) != NULL); g_slist_free (resources); /* first resource is disposed and so automatically removed */ g_object_unref (resource_1); resources = wocky_bare_contact_get_resources (bare); g_assert_cmpuint (g_slist_length (resources), ==, 1); g_assert (g_slist_find (resources, resource_2) != NULL); g_slist_free (resources); g_object_unref (bare); g_object_unref (resource_2); } int main (int argc, char **argv) { int result; test_init (argc, argv); g_test_add_func ("/bare-contact/instantiation", test_instantiation); g_test_add_func ("/bare-contact/contact-equal", test_contact_equal); g_test_add_func ("/bare-contact/add-group", test_add_group); g_test_add_func ("/bare-contact/in-group", test_in_group); g_test_add_func ("/bare-contact/remove-group", test_remove_group); g_test_add_func ("/bare-contact/contact-copy", test_contact_copy); g_test_add_func ("/bare-contact/add-get-resource", test_add_get_resource); result = g_test_run (); test_deinit (); return result; } telepathy-gabble-0.18.2/lib/ext/wocky/tests/wocky-test-stream.h0000644000175000017500000000661612200204546024440 0ustar00smcvsmcv00000000000000/* * wocky-test-stream.h - Header for WockyTestStream * Copyright (C) 2009 Collabora Ltd. * @author Sjoerd Simons * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 __WOCKY_TEST_STREAM_H__ #define __WOCKY_TEST_STREAM_H__ #include #include G_BEGIN_DECLS typedef struct _WockyTestStream WockyTestStream; typedef struct _WockyTestStreamClass WockyTestStreamClass; typedef struct _WockyTestStreamPrivate WockyTestStreamPrivate; struct _WockyTestStreamClass { GObjectClass parent_class; }; struct _WockyTestStream { GObject parent; GIOStream *stream0; GIOStream *stream1; GInputStream *stream0_input; GOutputStream *stream0_output; GInputStream *stream1_input; GOutputStream *stream1_output; WockyTestStreamPrivate *priv; }; GType wocky_test_stream_get_type (void); /* TYPE MACROS */ #define WOCKY_TYPE_TEST_STREAM \ (wocky_test_stream_get_type ()) #define WOCKY_TEST_STREAM(obj) \ (G_TYPE_CHECK_INSTANCE_CAST((obj), WOCKY_TYPE_TEST_STREAM, WockyTestStream)) #define WOCKY_TEST_STREAM_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST((klass), WOCKY_TYPE_TEST_STREAM, WockyTestStreamClass)) #define WOCKY_IS_TEST_STREAM(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE((obj), WOCKY_TYPE_TEST_STREAM)) #define WOCKY_IS_TEST_STREAM_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE((klass), WOCKY_TYPE_TEST_STREAM)) #define WOCKY_TEST_STREAM_GET_CLASS(obj) \ (G_TYPE_INSTANCE_GET_CLASS ((obj), WOCKY_TYPE_TEST_STREAM, WockyTestStreamClass)) void wocky_test_input_stream_set_read_error (GInputStream *stream); void wocky_test_output_stream_set_write_error (GOutputStream *stream); void wocky_test_stream_cork (GInputStream *stream, gboolean cork); typedef enum { /* one read can have data from two writes, but never has all the data * from one specific write */ WOCK_TEST_STREAM_READ_COMBINE_SLICE = 0, /* one read reads as much as is available */ WOCK_TEST_STREAM_READ_COMBINE, /* one read reads only data from one write */ WOCK_TEST_STREAM_READ_EXACT, } WockyTestStreamReadMode; typedef enum { /* all writes are only half-done, default */ WOCKY_TEST_STREAM_WRITE_INCOMPLETE = 0, /* Always succeed in writing everything */ WOCKY_TEST_STREAM_WRITE_COMPLETE = 1, } WockyTestStreamWriteMode; void wocky_test_stream_set_mode (GInputStream *stream, WockyTestStreamReadMode mode); void wocky_test_stream_set_write_mode (GOutputStream *stream, WockyTestStreamWriteMode mode); typedef void (*WockyTestStreamDirectReadCb) (const gchar *buff, gsize len, gpointer user_data); void wocky_test_stream_set_direct_read_callback (GInputStream *stream, WockyTestStreamDirectReadCb cb, gpointer user_data); G_END_DECLS #endif /* #ifndef __WOCKY_TEST_STREAM_H__*/ telepathy-gabble-0.18.2/lib/ext/wocky/tests/wocky-test-stream.c0000644000175000017500000004456512200204546024440 0ustar00smcvsmcv00000000000000/* * wocky-test-stream.c - Source for WockyTestStream * Copyright (C) 2009 Collabora Ltd. * @author Sjoerd Simons * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include "wocky-test-stream.h" G_DEFINE_TYPE (WockyTestStream, wocky_test_stream, G_TYPE_OBJECT); struct _WockyTestStreamPrivate { gboolean dispose_has_run; }; enum { PROP_IO_INPUT_STREAM = 1, PROP_IO_OUTPUT_STREAM }; static GType wocky_test_io_stream_get_type (void); static GType wocky_test_input_stream_get_type (void); static GType wocky_test_output_stream_get_type (void); typedef struct { GIOStream parent; GInputStream *input; GOutputStream *output; } WockyTestIOStream; typedef struct { GIOStreamClass parent; } WockyTestIOStreamClass; typedef struct { GOutputStream parent; GAsyncQueue *queue; WockyTestStreamWriteMode mode; GError *write_error /* no, this is not a coding style violation */; gboolean dispose_has_run; } WockyTestOutputStream; typedef struct { GOutputStreamClass parent_class; } WockyTestOutputStreamClass; typedef struct { GInputStream parent; GAsyncQueue *queue; guint offset; GArray *out_array; GSimpleAsyncResult *read_result; GCancellable *read_cancellable; gulong read_cancellable_sig_id; void *buffer; gsize count; GError *read_error /* no, this is not a coding style violation */; gboolean dispose_has_run; WockyTestStreamReadMode mode; WockyTestStreamDirectReadCb direct_read_cb; gpointer direct_read_user_data; gboolean corked; } WockyTestInputStream; typedef struct { GOutputStreamClass parent_class; } WockyTestInputStreamClass; G_DEFINE_TYPE (WockyTestIOStream, wocky_test_io_stream, G_TYPE_IO_STREAM); G_DEFINE_TYPE (WockyTestInputStream, wocky_test_input_stream, G_TYPE_INPUT_STREAM); G_DEFINE_TYPE (WockyTestOutputStream, wocky_test_output_stream, G_TYPE_OUTPUT_STREAM); #define WOCKY_TYPE_TEST_IO_STREAM (wocky_test_io_stream_get_type ()) #define WOCKY_TYPE_TEST_INPUT_STREAM (wocky_test_input_stream_get_type ()) #define WOCKY_TYPE_TEST_OUTPUT_STREAM (wocky_test_output_stream_get_type ()) #define WOCKY_TEST_IO_STREAM(inst) (G_TYPE_CHECK_INSTANCE_CAST ((inst), \ WOCKY_TYPE_TEST_IO_STREAM, \ WockyTestIOStream)) #define WOCKY_TEST_INPUT_STREAM(inst) (G_TYPE_CHECK_INSTANCE_CAST ((inst), \ WOCKY_TYPE_TEST_INPUT_STREAM, \ WockyTestInputStream)) #define WOCKY_TEST_OUTPUT_STREAM(inst) (G_TYPE_CHECK_INSTANCE_CAST ((inst), \ WOCKY_TYPE_TEST_OUTPUT_STREAM, \ WockyTestOutputStream)) static gboolean wocky_test_input_stream_try_read (WockyTestInputStream *self); static void output_data_written_cb (GOutputStream *output, WockyTestInputStream *input_stream) { wocky_test_input_stream_try_read (input_stream); } static void wocky_test_stream_init (WockyTestStream *self) { /* allocate any data required by the object here */ self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, WOCKY_TYPE_TEST_STREAM, WockyTestStreamPrivate); self->stream0_output = g_object_new (WOCKY_TYPE_TEST_OUTPUT_STREAM, NULL); self->stream1_output = g_object_new (WOCKY_TYPE_TEST_OUTPUT_STREAM, NULL); self->stream0_input = g_object_new (WOCKY_TYPE_TEST_INPUT_STREAM, NULL); WOCKY_TEST_INPUT_STREAM (self->stream0_input)->queue = g_async_queue_ref ( WOCKY_TEST_OUTPUT_STREAM (self->stream1_output)->queue); self->stream1_input = g_object_new (WOCKY_TYPE_TEST_INPUT_STREAM, NULL); WOCKY_TEST_INPUT_STREAM (self->stream1_input)->queue = g_async_queue_ref ( WOCKY_TEST_OUTPUT_STREAM (self->stream0_output)->queue); self->stream0 = g_object_new (WOCKY_TYPE_TEST_IO_STREAM, NULL); WOCKY_TEST_IO_STREAM (self->stream0)->input = self->stream0_input; WOCKY_TEST_IO_STREAM (self->stream0)->output = self->stream0_output; self->stream1 = g_object_new (WOCKY_TYPE_TEST_IO_STREAM, NULL); WOCKY_TEST_IO_STREAM (self->stream1)->input = self->stream1_input; WOCKY_TEST_IO_STREAM (self->stream1)->output = self->stream1_output; g_signal_connect (self->stream0_output, "data-written", G_CALLBACK (output_data_written_cb), self->stream1_input); g_signal_connect (self->stream1_output, "data-written", G_CALLBACK (output_data_written_cb), self->stream0_input); } static void wocky_test_stream_dispose (GObject *object); static void wocky_test_stream_class_init (WockyTestStreamClass *wocky_test_stream_class) { GObjectClass *object_class = G_OBJECT_CLASS (wocky_test_stream_class); g_type_class_add_private (wocky_test_stream_class, \ sizeof (WockyTestStreamPrivate)); object_class->dispose = wocky_test_stream_dispose; } static void wocky_test_stream_dispose (GObject *object) { WockyTestStream *self = WOCKY_TEST_STREAM (object); if (self->priv->dispose_has_run) return; self->priv->dispose_has_run = TRUE; g_object_unref (self->stream0); self->stream0 = NULL; g_object_unref (self->stream1); self->stream1 = NULL; if (G_OBJECT_CLASS (wocky_test_stream_parent_class)->dispose) G_OBJECT_CLASS (wocky_test_stream_parent_class)->dispose (object); } /* IO stream */ static void wocky_test_io_stream_init (WockyTestIOStream *self) { } static void wocky_test_io_stream_class_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { WockyTestIOStream *self = WOCKY_TEST_IO_STREAM (object); switch (property_id) { case PROP_IO_INPUT_STREAM: g_value_set_object (value, self->input); break; case PROP_IO_OUTPUT_STREAM: g_value_set_object (value, self->output); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); } } static void wocky_test_io_stream_finalize (GObject *object) { WockyTestIOStream *self = WOCKY_TEST_IO_STREAM (object); g_object_unref (self->input); g_object_unref (self->output); if (G_OBJECT_CLASS (wocky_test_io_stream_parent_class)->finalize) G_OBJECT_CLASS (wocky_test_io_stream_parent_class)->finalize (object); } static GInputStream * wocky_test_io_stream_get_input_stream (GIOStream *stream) { return WOCKY_TEST_IO_STREAM (stream)->input; } static GOutputStream * wocky_test_io_stream_get_output_stream (GIOStream *stream) { return WOCKY_TEST_IO_STREAM (stream)->output; } static void wocky_test_io_stream_class_init ( WockyTestIOStreamClass *wocky_test_io_stream_class) { GObjectClass *obj_class = G_OBJECT_CLASS (wocky_test_io_stream_class); GIOStreamClass *stream_class = G_IO_STREAM_CLASS (wocky_test_io_stream_class); obj_class->finalize = wocky_test_io_stream_finalize; obj_class->get_property = wocky_test_io_stream_class_get_property; stream_class->get_input_stream = wocky_test_io_stream_get_input_stream; stream_class->get_output_stream = wocky_test_io_stream_get_output_stream; g_object_class_install_property (obj_class, PROP_IO_INPUT_STREAM, g_param_spec_object ("input-stream", "Input stream", "the input stream", G_TYPE_INPUT_STREAM, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (obj_class, PROP_IO_OUTPUT_STREAM, g_param_spec_object ("output-stream", "Output stream", "the output stream", G_TYPE_OUTPUT_STREAM, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); } /* Input stream */ static gssize wocky_test_input_stream_read (GInputStream *stream, void *buffer, gsize count, GCancellable *cancellable, GError **error) { WockyTestInputStream *self = WOCKY_TEST_INPUT_STREAM (stream); gsize written = 0; if (self->out_array == NULL) { g_assert (self->offset == 0); self->out_array = g_async_queue_pop (self->queue); } do { gsize towrite; if (self->mode == WOCK_TEST_STREAM_READ_COMBINE_SLICE && self->offset == 0) towrite = MIN (count - written, MAX (self->out_array->len/2, 1)); else towrite = MIN (count - written, self->out_array->len - self->offset); memcpy ((guchar *) buffer + written, self->out_array->data + self->offset, towrite); self->offset += towrite; written += towrite; if (self->offset == self->out_array->len) { g_array_unref (self->out_array); self->out_array = g_async_queue_try_pop (self->queue); self->offset = 0; } else if (self->mode == WOCK_TEST_STREAM_READ_COMBINE_SLICE) { break; } } while (self->mode != WOCK_TEST_STREAM_READ_EXACT && written < count && self->out_array != NULL); if (self->direct_read_cb != NULL) self->direct_read_cb (buffer, written, self->direct_read_user_data); return written; } static void read_async_complete (WockyTestInputStream *self) { GSimpleAsyncResult *r = self->read_result; if (self->read_cancellable != NULL) { g_signal_handler_disconnect (self->read_cancellable, self->read_cancellable_sig_id); g_object_unref (self->read_cancellable); self->read_cancellable = NULL; } self->read_result = NULL; g_simple_async_result_complete_in_idle (r); g_object_unref (r); } static void read_cancelled_cb (GCancellable *cancellable, WockyTestInputStream *self) { g_simple_async_result_set_error (self->read_result, G_IO_ERROR, G_IO_ERROR_CANCELLED, "Reading cancelled"); self->buffer = NULL; read_async_complete (self); } static void wocky_test_input_stream_read_async (GInputStream *stream, void *buffer, gsize count, int io_priority, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { WockyTestInputStream *self = WOCKY_TEST_INPUT_STREAM (stream); g_assert (self->buffer == NULL); g_assert (self->read_result == NULL); g_assert (self->read_cancellable == NULL); self->buffer = buffer; self->count = count; self->read_result = g_simple_async_result_new (G_OBJECT (stream), callback, user_data, wocky_test_input_stream_read_async); if (self->read_error != NULL) { g_simple_async_result_set_from_error (self->read_result, self->read_error); g_error_free (self->read_error); self->read_error = NULL; read_async_complete (self); return; } if (cancellable != NULL) { self->read_cancellable = g_object_ref (cancellable); self->read_cancellable_sig_id = g_signal_connect (cancellable, "cancelled", G_CALLBACK (read_cancelled_cb), self); } wocky_test_input_stream_try_read (self); } static gssize wocky_test_input_stream_read_finish (GInputStream *stream, GAsyncResult *result, GError **error) { WockyTestInputStream *self = WOCKY_TEST_INPUT_STREAM (stream); gssize len = -1; if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), error)) goto out; g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (self), wocky_test_input_stream_read_async), -1); len = wocky_test_input_stream_read (stream, self->buffer, self->count, NULL, error); out: self->buffer = NULL; return len; } static gboolean wocky_test_input_stream_try_read (WockyTestInputStream *self) { if (self->read_result == NULL) /* No pending read operation */ return FALSE; if (self->out_array == NULL && g_async_queue_length (self->queue) == 0) return FALSE; if (self->corked) return FALSE; read_async_complete (self); return TRUE; } static void wocky_test_input_stream_init (WockyTestInputStream *self) { } static void wocky_test_input_stream_dispose (GObject *object) { WockyTestInputStream *self = WOCKY_TEST_INPUT_STREAM (object); if (self->dispose_has_run) return; self->dispose_has_run = TRUE; if (self->out_array != NULL) g_array_unref (self->out_array); self->out_array = NULL; if (self->queue != NULL) g_async_queue_unref (self->queue); self->queue = NULL; g_warn_if_fail (self->read_result == NULL); g_warn_if_fail (self->read_cancellable == NULL); /* release any references held by the object here */ if (G_OBJECT_CLASS (wocky_test_input_stream_parent_class)->dispose) G_OBJECT_CLASS (wocky_test_input_stream_parent_class)->dispose (object); } static void wocky_test_input_stream_class_init ( WockyTestInputStreamClass *wocky_test_input_stream_class) { GObjectClass *obj_class = G_OBJECT_CLASS (wocky_test_input_stream_class); GInputStreamClass *stream_class = G_INPUT_STREAM_CLASS (wocky_test_input_stream_class); obj_class->dispose = wocky_test_input_stream_dispose; stream_class->read_fn = wocky_test_input_stream_read; stream_class->read_async = wocky_test_input_stream_read_async; stream_class->read_finish = wocky_test_input_stream_read_finish; } /* Output stream */ enum { OUTPUT_DATA_WRITTEN, LAST_SIGNAL }; static guint output_signals[LAST_SIGNAL] = {0}; static gssize wocky_test_output_stream_write (GOutputStream *stream, const void *buffer, gsize count, GCancellable *cancellable, GError **error) { WockyTestOutputStream *self = WOCKY_TEST_OUTPUT_STREAM (stream); GArray *data; gsize written = count; if (self->mode == WOCKY_TEST_STREAM_WRITE_INCOMPLETE) written = MAX (count/2, 1); if (self->write_error != NULL) { *error = self->write_error; self->write_error = NULL; return -1; } data = g_array_sized_new (FALSE, FALSE, sizeof (guint8), written); g_array_insert_vals (data, 0, buffer, written); g_async_queue_push (self->queue, data); g_signal_emit (self, output_signals[OUTPUT_DATA_WRITTEN], 0); return written; } static void wocky_test_output_stream_write_async (GOutputStream *stream, const void *buffer, gsize count, int io_priority, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { GSimpleAsyncResult *simple; GError *error = NULL; gssize result; result = wocky_test_output_stream_write (stream, buffer, count, cancellable, &error); simple = g_simple_async_result_new (G_OBJECT (stream), callback, user_data, wocky_test_output_stream_write_async); if (result == -1) { g_simple_async_result_set_from_error (simple, error); g_error_free (error); } else { g_simple_async_result_set_op_res_gssize (simple, result); } g_simple_async_result_complete_in_idle (simple); g_object_unref (simple); } static gssize wocky_test_output_stream_write_finish (GOutputStream *stream, GAsyncResult *result, GError **error) { if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), error)) return -1; g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (stream), wocky_test_output_stream_write_async), -1); return g_simple_async_result_get_op_res_gssize ( G_SIMPLE_ASYNC_RESULT (result)); } static void wocky_test_output_stream_dispose (GObject *object) { WockyTestOutputStream *self = WOCKY_TEST_OUTPUT_STREAM (object); if (self->dispose_has_run) return; self->dispose_has_run = TRUE; g_async_queue_push (self->queue, g_array_sized_new (FALSE, FALSE, sizeof (guint8), 0)); g_async_queue_unref (self->queue); /* release any references held by the object here */ if (G_OBJECT_CLASS (wocky_test_output_stream_parent_class)->dispose) G_OBJECT_CLASS (wocky_test_output_stream_parent_class)->dispose (object); } static void queue_destroyed (gpointer data) { g_array_free ((GArray *) data, TRUE); } static void wocky_test_output_stream_init (WockyTestOutputStream *self) { self->queue = g_async_queue_new_full (queue_destroyed); } static void wocky_test_output_stream_class_init ( WockyTestOutputStreamClass *wocky_test_output_stream_class) { GObjectClass *obj_class = G_OBJECT_CLASS (wocky_test_output_stream_class); GOutputStreamClass *stream_class = G_OUTPUT_STREAM_CLASS (wocky_test_output_stream_class); obj_class->dispose = wocky_test_output_stream_dispose; stream_class->write_fn = wocky_test_output_stream_write; stream_class->write_async = wocky_test_output_stream_write_async; stream_class->write_finish = wocky_test_output_stream_write_finish; output_signals[OUTPUT_DATA_WRITTEN] = g_signal_new ("data-written", G_OBJECT_CLASS_TYPE(wocky_test_output_stream_class), G_SIGNAL_RUN_LAST, 0, NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); } void wocky_test_input_stream_set_read_error (GInputStream *stream) { WockyTestInputStream *self = WOCKY_TEST_INPUT_STREAM (stream); if (self->read_result == NULL) { /* No pending read operation. Set the error so next read will fail */ self->read_error = g_error_new_literal (G_IO_ERROR, G_IO_ERROR_FAILED, "read error"); return; } g_simple_async_result_set_error (self->read_result, G_IO_ERROR, G_IO_ERROR_FAILED, "read error"); read_async_complete (self); } void wocky_test_output_stream_set_write_error (GOutputStream *stream) { WockyTestOutputStream *self = WOCKY_TEST_OUTPUT_STREAM (stream); self->write_error = g_error_new_literal (G_IO_ERROR, G_IO_ERROR_FAILED, "write error"); } void wocky_test_stream_set_mode (GInputStream *stream, WockyTestStreamReadMode mode) { WOCKY_TEST_INPUT_STREAM (stream)->mode = mode; } void wocky_test_stream_cork (GInputStream *stream, gboolean cork) { WockyTestInputStream *tstream = WOCKY_TEST_INPUT_STREAM (stream); tstream->corked = cork; if (cork == FALSE) wocky_test_input_stream_try_read (tstream); } void wocky_test_stream_set_direct_read_callback (GInputStream *stream, WockyTestStreamDirectReadCb cb, gpointer user_data) { WockyTestInputStream *tstream = WOCKY_TEST_INPUT_STREAM (stream); tstream->direct_read_cb = cb; tstream->direct_read_user_data = user_data; } void wocky_test_stream_set_write_mode (GOutputStream *stream, WockyTestStreamWriteMode mode) { WOCKY_TEST_OUTPUT_STREAM (stream)->mode = mode; } telepathy-gabble-0.18.2/lib/ext/wocky/tests/wocky-test-helper.h0000644000175000017500000000575212201202754024424 0ustar00smcvsmcv00000000000000#ifndef __WOCKY_TEST_HELPER_H__ #define __WOCKY_TEST_HELPER_H__ G_BEGIN_DECLS #include #include "wocky-test-stream.h" typedef struct { GMainLoop *loop; gboolean parsed_stanza; GQueue *expected_stanzas; WockyXmppConnection *in; WockyXmppConnection *out; WockyPorter *sched_in; WockyPorter *sched_out; WockySession *session_in; WockySession *session_out; WockyTestStream *stream; guint outstanding; GCancellable *cancellable; gulong timeout_id; } test_data_t; test_data_t * setup_test (void); test_data_t * setup_test_with_timeout (guint timeout); test_data_t * setup_test_with_jids (const gchar *in_jid, const gchar *out_jid); void teardown_test (test_data_t *data); void test_wait_pending (test_data_t *test); gboolean test_timeout_cb (gpointer data); void test_open_connection (test_data_t *test); void test_close_connection (test_data_t *test); void test_open_both_connections (test_data_t *test); void test_close_porter (test_data_t *test); void test_expected_stanza_received (test_data_t *test, WockyStanza *stanza); void test_close_both_porters (test_data_t *test); void test_cancel_in_idle (GCancellable *cancellable); #define test_assert_nodes_equal(n1, n2) \ G_STMT_START { \ if (!wocky_node_equal ((n1), (n2))) \ g_assertion_message (G_LOG_DOMAIN, __FILE__, __LINE__, G_STRFUNC, \ g_strdup_printf ("Nodes not equal:\n%s\n\n%s", \ wocky_node_to_string (n1), \ wocky_node_to_string (n2))); \ } G_STMT_END #define test_assert_stanzas_equal(s1, s2) \ test_assert_nodes_equal (wocky_stanza_get_top_node (s1), \ wocky_stanza_get_top_node (s2)) #define test_assert_nodes_not_equal(n1, n2) \ G_STMT_START { \ if (wocky_node_equal ((n1), (n2))) \ g_assertion_message (G_LOG_DOMAIN, __FILE__, __LINE__, G_STRFUNC, \ g_strdup_printf ("Nodes unexpectedly equal:\n%s\n\n%s", \ wocky_node_to_string (n1), \ wocky_node_to_string (n2))); \ } G_STMT_END #define test_assert_stanzas_not_equal(s1, s2) \ test_assert_nodes_not_equal (wocky_stanza_get_top_node (s1), \ wocky_stanza_get_top_node (s2)) /* Slightly evil macro that tests that two stanzas are equal, except that if * one has an id and the other does not this is not considered a difference. It * modifies the stanzas because I am lazy. */ #define test_assert_stanzas_equal_no_id(s1, s2) \ G_STMT_START { \ WockyNode *n1 = wocky_stanza_get_top_node (s1); \ WockyNode *n2 = wocky_stanza_get_top_node (s2); \ const gchar *_id1 = wocky_node_get_attribute (n1, "id"); \ const gchar *_id2 = wocky_node_get_attribute (n2, "id"); \ if (_id1 == NULL && _id2 != NULL) \ wocky_node_set_attribute (n1, "id", _id2); \ else if (_id1 != NULL && _id2 == NULL) \ wocky_node_set_attribute (n2, "id", _id1); \ test_assert_stanzas_equal (s1, s2); \ } G_STMT_END void test_init (int argc, char **argv); void test_deinit (void); G_END_DECLS #endif /* #ifndef __WOCKY_TEST_HELPER_H__*/ telepathy-gabble-0.18.2/lib/ext/wocky/tests/wocky-test-helper.c0000644000175000017500000001647412201202754024422 0ustar00smcvsmcv00000000000000#ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include "wocky-test-helper.h" #include "wocky-test-stream.h" #define TIMEOUT 10 gboolean test_timeout_cb (gpointer data) { g_test_message ("Timeout reached :("); g_assert_not_reached (); return FALSE; } static test_data_t * setup_test_full (guint timeout, const gchar *in_jid, const gchar *out_jid) { test_data_t *data; data = g_new0 (test_data_t, 1); data->loop = g_main_loop_new (NULL, FALSE); data->stream = g_object_new (WOCKY_TYPE_TEST_STREAM, NULL); data->in = wocky_xmpp_connection_new (data->stream->stream0); data->out = wocky_xmpp_connection_new (data->stream->stream1); data->session_in = wocky_session_new_with_connection (data->in, in_jid); data->session_out = wocky_session_new_with_connection (data->out, out_jid); data->sched_in = wocky_session_get_porter (data->session_in); data->sched_out = wocky_session_get_porter (data->session_out); data->expected_stanzas = g_queue_new (); data->cancellable = g_cancellable_new (); data->timeout_id = g_timeout_add_seconds (timeout, test_timeout_cb, NULL); return data; } test_data_t * setup_test_with_jids (const gchar *in_jid, const gchar *out_jid) { return setup_test_full (TIMEOUT, in_jid, out_jid); } test_data_t * setup_test_with_timeout (guint timeout) { return setup_test_full (timeout, "example.com", "example.com"); } test_data_t * setup_test (void) { return setup_test_full (TIMEOUT, "example.com", "example.com"); } void teardown_test (test_data_t *data) { g_main_loop_unref (data->loop); g_object_unref (data->stream); g_object_unref (data->in); g_object_unref (data->out); if (data->session_in != NULL) g_object_unref (data->session_in); if (data->session_out != NULL) g_object_unref (data->session_out); g_object_unref (data->cancellable); g_source_remove (data->timeout_id); /* All the stanzas should have been received */ g_assert (g_queue_get_length (data->expected_stanzas) == 0); g_queue_free (data->expected_stanzas); g_free (data); } void test_wait_pending (test_data_t *test) { while (test->outstanding > 0) g_main_loop_run (test->loop); } static void send_received_open_cb (GObject *source, GAsyncResult *res, gpointer user_data) { WockyXmppConnection *conn = WOCKY_XMPP_CONNECTION (source); test_data_t *d = (test_data_t *) user_data; g_assert (wocky_xmpp_connection_recv_open_finish (conn, res, NULL, NULL, NULL, NULL, NULL, NULL)); d->outstanding--; g_main_loop_quit (d->loop); } static void send_open_cb (GObject *source, GAsyncResult *res, gpointer user_data) { test_data_t *data = (test_data_t *) user_data; WockyXmppConnection *conn; g_assert (wocky_xmpp_connection_send_open_finish ( WOCKY_XMPP_CONNECTION (source), res, NULL)); /* The other connection has to receive the opening */ if (WOCKY_XMPP_CONNECTION (source) == data->in) conn = data->out; else conn = data->in; wocky_xmpp_connection_recv_open_async (conn, NULL, send_received_open_cb, user_data); } static void open_connection (test_data_t *test, WockyXmppConnection *connection) { wocky_xmpp_connection_send_open_async (connection, NULL, NULL, NULL, NULL, NULL, NULL, send_open_cb, test); test->outstanding++; test_wait_pending (test); } /* Open XMPP the 'in' connection */ void test_open_connection (test_data_t *test) { open_connection (test, test->in); } /* Open both XMPP connections */ void test_open_both_connections (test_data_t *test) { open_connection (test, test->in); open_connection (test, test->out); } static void wait_close_cb (GObject *source, GAsyncResult *res, gpointer user_data) { test_data_t *data = (test_data_t *) user_data; WockyStanza *s; GError *error = NULL; s = wocky_xmpp_connection_recv_stanza_finish (WOCKY_XMPP_CONNECTION (source), res, &error); g_assert (s == NULL); /* connection has been disconnected */ g_assert_error (error, WOCKY_XMPP_CONNECTION_ERROR, WOCKY_XMPP_CONNECTION_ERROR_CLOSED); g_error_free (error); data->outstanding--; g_main_loop_quit (data->loop); } static void close_sent_cb (GObject *source, GAsyncResult *res, gpointer user_data) { test_data_t *data = (test_data_t *) user_data; g_assert (wocky_xmpp_connection_send_close_finish ( WOCKY_XMPP_CONNECTION (source), res, NULL)); data->outstanding--; g_main_loop_quit (data->loop); } /* Close XMPP connections on both sides */ void test_close_connection (test_data_t *test) { wocky_xmpp_connection_recv_stanza_async (test->out, NULL, wait_close_cb, test); test->outstanding++; /* Close the connection */ wocky_xmpp_connection_send_close_async ( WOCKY_XMPP_CONNECTION (test->in), NULL, close_sent_cb, test); test->outstanding++; test_wait_pending (test); } static void sched_close_cb (GObject *source, GAsyncResult *res, gpointer user_data) { test_data_t *test = (test_data_t *) user_data; GError *error = NULL; gboolean ret = wocky_porter_close_finish ( WOCKY_PORTER (source), res, &error); g_assert_no_error (error); g_assert (ret); test->outstanding--; g_main_loop_quit (test->loop); } static void wait_sched_close_cb (GObject *source, GAsyncResult *res, gpointer user_data) { test_data_t *test = (test_data_t *) user_data; WockyXmppConnection *connection = WOCKY_XMPP_CONNECTION (source); WockyStanza *s; GError *error = NULL; s = wocky_xmpp_connection_recv_stanza_finish (connection, res, &error); g_assert (s == NULL); /* connection has been disconnected */ g_assert_error (error, WOCKY_XMPP_CONNECTION_ERROR, WOCKY_XMPP_CONNECTION_ERROR_CLOSED); g_error_free (error); /* close on our side */ wocky_xmpp_connection_send_close_async (connection, NULL, close_sent_cb, test); /* Don't decrement test->outstanding as we are waiting for another * callback */ g_main_loop_quit (test->loop); } void test_close_porter (test_data_t *test) { /* close connections */ wocky_xmpp_connection_recv_stanza_async (test->in, NULL, wait_sched_close_cb, test); wocky_porter_close_async (test->sched_out, NULL, sched_close_cb, test); test->outstanding += 2; test_wait_pending (test); } void test_expected_stanza_received (test_data_t *test, WockyStanza *stanza) { WockyStanza *expected; expected = g_queue_pop_head (test->expected_stanzas); g_assert (expected != NULL); test_assert_nodes_equal (wocky_stanza_get_top_node (stanza), wocky_stanza_get_top_node (expected)); g_object_unref (expected); test->outstanding--; g_main_loop_quit (test->loop); } void test_close_both_porters (test_data_t *test) { wocky_porter_close_async (test->sched_out, NULL, sched_close_cb, test); wocky_porter_close_async (test->sched_in, NULL, sched_close_cb, test); test->outstanding += 2; test_wait_pending (test); } void test_init (int argc, char **argv) { g_test_init (&argc, &argv, NULL); g_test_bug_base ("http://bugs.freedesktop.org/show_bug.cgi?id="); g_type_init (); wocky_init (); } void test_deinit (void) { wocky_deinit (); } static gboolean cancel_in_idle_cb (gpointer cancellable) { g_cancellable_cancel (cancellable); return FALSE; } void test_cancel_in_idle (GCancellable *cancellable) { g_idle_add_full (G_PRIORITY_DEFAULT_IDLE, cancel_in_idle_cb, g_object_ref (cancellable), g_object_unref); } telepathy-gabble-0.18.2/lib/ext/wocky/tests/README0000644000175000017500000000065512200204546021542 0ustar00smcvsmcv00000000000000You can valgrind all the tests by running: % make valgrind To valgrind an individual test binary (wocky-porter-test, say), run: % make wocky-porter-test.valgrind If you want to pass arguments to the tests when valgrinding (perhaps you've narrowed the leaky test down to one test case, /xmpp-porter/cancel-iq-closing say), run: % make wocky-porter-test.valgrind \ TEST_ARGS='-p /xmpp-porter/cancel-iq-closing' telepathy-gabble-0.18.2/lib/ext/wocky/tests/Makefile.am0000644000175000017500000002454612213345474022735 0ustar00smcvsmcv00000000000000############################################################################ # x509 certificates: TEST_DIR := @abs_top_srcdir@/tests SUMMARY := $(TEST_DIR)/summarise-tests.py CERT_DIR := $(TEST_DIR)/certs CA_KEY := $(CERT_DIR)/ca-0-key.pem CA_CERT := $(CERT_DIR)/ca-0-cert.pem SS_KEY := $(CERT_DIR)/ss-key.pem SS_CERT := $(CERT_DIR)/ss-cert.pem REV_KEY := $(CERT_DIR)/rev-key.pem REV_CERT := $(CERT_DIR)/rev-cert.pem EXP_KEY := $(CERT_DIR)/exp-key.pem EXP_CERT := $(CERT_DIR)/exp-cert.pem NEW_KEY := $(CERT_DIR)/new-key.pem NEW_CERT := $(CERT_DIR)/new-cert.pem TLS_KEY := $(CERT_DIR)/tls-key.pem TLS_CERT := $(CERT_DIR)/tls-cert.pem WILD_KEY := $(CERT_DIR)/wild-key.pem WILD_CERT:= $(CERT_DIR)/wild-cert.pem BADWILD_KEY := $(CERT_DIR)/badwild-key.pem BADWILD_CERT := $(CERT_DIR)/badwild-cert.pem CA_DIR := $(CERT_DIR)/cas CRL_DIR := $(CERT_DIR)/crl UNKNOWN_KEY := $(CERT_DIR)/unknown-key.pem UNKNOWN_CERT := $(CERT_DIR)/unknown-cert.pem INCLUDES := -I$(top_builddir)/wocky TLSDEFS := -DTLS_CA_KEY_FILE='"$(CA_KEY)"' \ -DTLS_CA_CRT_FILE='"$(CA_CERT)"' \ -DTLS_SS_KEY_FILE='"$(SS_KEY)"' \ -DTLS_SS_CRT_FILE='"$(SS_CERT)"' \ -DTLS_EXP_KEY_FILE='"$(EXP_KEY)"' \ -DTLS_EXP_CRT_FILE='"$(EXP_CERT)"' \ -DTLS_NEW_KEY_FILE='"$(NEW_KEY)"' \ -DTLS_NEW_CRT_FILE='"$(NEW_CERT)"' \ -DTLS_REV_KEY_FILE='"$(REV_KEY)"' \ -DTLS_REV_CRT_FILE='"$(REV_CERT)"' \ -DTLS_UNKNOWN_KEY_FILE='"$(UNKNOWN_KEY)"' \ -DTLS_UNKNOWN_CRT_FILE='"$(UNKNOWN_CERT)"' \ -DTLS_SERVER_KEY_FILE='"$(TLS_KEY)"' \ -DTLS_SERVER_CRT_FILE='"$(TLS_CERT)"'\ -DTLS_WILD_KEY_FILE='"$(WILD_KEY)"' \ -DTLS_WILD_CRT_FILE='"$(WILD_CERT)"' \ -DTLS_BADWILD_CRT_FILE='"$(BADWILD_CERT)"' \ -DTLS_BADWILD_KEY_FILE='"$(BADWILD_KEY)"' \ -DTLS_CRL_DIR='"$(CRL_DIR)"' \ -DTLS_CA_DIR='"$(CA_DIR)"' ############################################################################ TEST_PROGS = \ wocky-bare-contact-test \ wocky-caps-hash-test \ wocky-connector-test \ wocky-contact-factory-test \ wocky-data-form-test \ wocky-jid-validation-test \ wocky-loopback-test \ wocky-node-tree-test \ wocky-pep-service-test \ wocky-ping-test \ wocky-porter-test \ wocky-pubsub-node-test \ wocky-pubsub-service-test \ wocky-resource-contact-test \ wocky-roster-test \ wocky-sasl-utils-test \ wocky-scram-sha1-test \ wocky-session-test \ wocky-stanza-test \ wocky-tls-test \ wocky-utils-test \ wocky-xmpp-connection-test \ wocky-xmpp-node-test \ wocky-xmpp-reader-test \ wocky-xmpp-readwrite-test \ $(NULL) noinst_PROGRAMS = if HAVE_LIBSASL2 TEST_PROGS += wocky-test-sasl-auth noinst_PROGRAMS += wocky-dummy-xmpp-server endif if HAVE_GIO_PROXY TEST_PROGS += wocky-http-proxy-test endif wocky_bare_contact_test_SOURCES = \ wocky-test-helper.c wocky-test-helper.h \ wocky-test-stream.c wocky-test-stream.h \ wocky-bare-contact-test.c wocky_caps_hash_test_SOURCES = wocky-caps-hash-test.c \ wocky-test-helper.c wocky-test-helper.h \ wocky-test-stream.c wocky-test-stream.h EXTRA_wocky_connector_test_DEPENDENCIES = $(CA_DIR) certs wocky_connector_test_SOURCES = \ wocky-connector-test.c \ wocky-test-sasl-auth-server.c \ wocky-test-sasl-auth-server.h \ wocky-test-connector-server.c \ wocky-test-connector-server.h \ wocky-test-helper.c wocky-test-helper.h \ wocky-test-stream.c wocky-test-stream.h \ test-resolver.c test-resolver.h wocky_connector_test_LDADD = $(LDADD) @LIBSASL2_LIBS@ wocky_connector_test_CFLAGS = $(AM_CFLAGS) @LIBSASL2_CFLAGS@ $(TLSDEFS) wocky_contact_factory_test_SOURCES = \ wocky-test-helper.c wocky-test-helper.h \ wocky-test-stream.c wocky-test-stream.h \ wocky-contact-factory-test.c wocky_data_form_test_SOURCES = \ wocky-test-helper.c wocky-test-helper.h \ wocky-test-stream.c wocky-test-stream.h \ wocky-data-form-test.c wocky_dummy_xmpp_server_SOURCES = wocky-dummy-xmpp-server.c \ wocky-test-connector-server.c wocky-test-connector-server.h \ wocky-test-helper.c wocky-test-helper.h \ wocky-test-stream.c wocky-test-stream.h \ wocky-test-sasl-auth-server.c wocky-test-sasl-auth-server.h wocky_dummy_xmpp_server_LDADD = $(LDADD) @LIBSASL2_LIBS@ wocky_dummy_xmpp_server_CFLAGS = $(AM_CFLAGS) @LIBSASL2_CFLAGS@ $(TLSDEFS) wocky_http_proxy_test_SOURCES = wocky-http-proxy-test.c \ wocky-test-helper.c wocky-test-helper.h \ wocky-test-stream.c wocky-test-stream.h wocky_jid_validation_test_SOURCES = \ wocky-jid-validation-test.c \ wocky-test-helper.c wocky-test-helper.h \ wocky-test-stream.c wocky-test-stream.h wocky_loopback_test_SOURCES = \ wocky-test-helper.c wocky-test-helper.h \ wocky-test-stream.c wocky-test-stream.h \ wocky-loopback-test.c wocky_node_tree_test_SOURCES = \ wocky-test-helper.c wocky-test-helper.h \ wocky-test-stream.c wocky-test-stream.h \ wocky-node-tree-test.c wocky_pep_service_test_SOURCES = \ wocky-test-helper.c wocky-test-helper.h \ wocky-test-stream.c wocky-test-stream.h \ wocky-pep-service-test.c wocky_ping_test_SOURCES = \ wocky-ping-test.c \ wocky-test-helper.c wocky-test-helper.h \ wocky-test-stream.c wocky-test-stream.h wocky_porter_test_SOURCES = \ wocky-porter-test.c \ wocky-test-helper.c wocky-test-helper.h \ wocky-test-stream.c wocky-test-stream.h wocky_pubsub_node_test_SOURCES = \ wocky-test-helper.c wocky-test-helper.h \ wocky-test-stream.c wocky-test-stream.h \ wocky-pubsub-test-helpers.c wocky-pubsub-test-helpers.h \ wocky-pubsub-node-test.c wocky_pubsub_service_test_SOURCES = \ wocky-test-helper.c wocky-test-helper.h \ wocky-test-stream.c wocky-test-stream.h \ wocky-pubsub-test-helpers.c wocky-pubsub-test-helpers.h \ wocky-pubsub-service-test.c wocky_resource_contact_test_SOURCES = \ wocky-test-helper.c wocky-test-helper.h \ wocky-test-stream.c wocky-test-stream.h \ wocky-resource-contact-test.c wocky_roster_test_SOURCES = \ wocky-roster-test.c \ wocky-test-helper.c wocky-test-helper.h \ wocky-test-stream.c wocky-test-stream.h wocky_sasl_utils_test_SOURCES = wocky-sasl-utils-test.c wocky_scram_sha1_test_SOURCES = wocky-scram-sha1-test.c \ wocky-test-helper.c wocky-test-helper.h \ wocky-test-stream.c wocky-test-stream.h wocky_session_test_SOURCES = \ wocky-test-helper.c wocky-test-helper.h \ wocky-test-stream.c wocky-test-stream.h \ wocky-session-test.c wocky_stanza_test_SOURCES = \ wocky-test-helper.c wocky-test-helper.h \ wocky-test-stream.c wocky-test-stream.h \ wocky-stanza-test.c wocky_test_sasl_auth_SOURCES = \ wocky-test-helper.c wocky-test-helper.h \ wocky-test-sasl-auth.c \ wocky-test-sasl-handler.c \ wocky-test-sasl-handler.h \ wocky-test-sasl-auth-server.c \ wocky-test-sasl-auth-server.h \ wocky-test-stream.c \ wocky-test-stream.h wocky_test_sasl_auth_LDADD = $(LDADD) @LIBSASL2_LIBS@ wocky_test_sasl_auth_CFLAGS = $(AM_CFLAGS) @LIBSASL2_CFLAGS@ wocky_tls_test_SOURCES = \ wocky-tls-test.c \ wocky-test-helper.c wocky-test-helper.h \ wocky-test-stream.c wocky-test-stream.h wocky_tls_test_CFLAGS = $(AM_CFLAGS) $(TLSDEFS) wocky_utils_test_SOURCES = wocky-utils-test.c wocky_xmpp_connection_test_SOURCES = \ wocky-xmpp-connection-test.c \ wocky-test-helper.c wocky-test-helper.h \ wocky-test-stream.c wocky-test-stream.h wocky_xmpp_node_test_SOURCES = \ wocky-test-helper.c wocky-test-helper.h \ wocky-test-stream.c wocky-test-stream.h \ wocky-xmpp-node-test.c wocky_xmpp_reader_test_SOURCES = \ wocky-test-helper.c wocky-test-helper.h \ wocky-test-stream.c wocky-test-stream.h \ wocky-xmpp-reader-test.c wocky_xmpp_readwrite_test_SOURCES = \ wocky-test-stream.c wocky-test-stream.h \ wocky-test-helper.c wocky-test-helper.h \ wocky-xmpp-readwrite-test.c AM_CFLAGS = $(ERROR_CFLAGS) $(GCOV_CFLAGS) @GLIB_CFLAGS@ \ @LIBXML2_CFLAGS@ @TLS_CFLAGS@ @WOCKY_CFLAGS@ AM_LDFLAGS = $(GCOV_LIBS) @GLIB_LIBS@ @LIBXML2_LIBS@ @TLS_LIBS@ LDADD = $(top_builddir)/wocky/libwocky.la check_PROGRAMS = $(TEST_PROGS) check_c_sources = $(notdir $(wildcard $(srcdir)/*.c) $(wildcard $(srcdir)/*.h)) noinst_PROGRAMS += $(TEST_PROGS) test-report: test-report.xml gtester-report $(top_builddir)/tests/$@.xml > \ $(top_builddir)/tests/$@.html test-report.xml: ${TEST_PROGS} test test: ${TEST_PROGS} gtester -o test-report.xml -k --verbose ${TEST_PROGS} @if [ -x $(which python) ] ; then \ $(SUMMARY) $@-report.xml ; \ else \ echo "No python available, not summarizing test results" ; \ fi test-%: wocky-%-test gtester -o $@-report.xml -k --verbose $< @if [ -x $(which python) ] ; then \ $(SUMMARY) $@-report.xml ; \ else \ echo "No python available, not summarizing test results" ; \ fi .PHONY: test test-report include $(top_srcdir)/tools/check-coding-style.mk check-local: test check-coding-style ############################################################################ SUPPRESSIONS = \ threadlocal.supp \ gabble.supp \ tp-glib.supp CLEANFILES = test-report.xml \ sasl-test.db # valgrind any given test by running make test.valgrind %.valgrind: % G_SLICE=always-malloc \ G_DEBUG=gc-friendly \ valgrind -q \ $(foreach s,$(SUPPRESSIONS),--suppressions=$(srcdir)/$(s)) \ --tool=memcheck --leak-check=full --trace-children=yes \ --leak-resolution=high --num-callers=20 \ ./$* $(TEST_ARGS) 2>&1 | tee "valgrind.$*.log" @if grep "==" "valgrind.$*.log" > /dev/null 2>&1; then \ exit 1; \ fi valgrind: $(TEST_PROGS) @echo "Valgrinding tests ..." @failed=0; \ for t in $(filter-out $(VALGRIND_TESTS_DISABLE),$(TEST_PROGS)); do \ $(MAKE) $$t.valgrind; \ if test "$$?" -ne 0; then \ echo "Valgrind error for test $$t"; \ failed=`expr $$failed + 1`; \ whicht="$$whicht $$t"; \ fi; \ done; \ if test "$$failed" -ne 0; then \ echo "$$failed tests had leaks or errors under valgrind:"; \ echo "$$whicht"; \ false; \ fi EXTRA_DIST = $(SUPPRESSIONS) \ README \ connector-test-plan.txt \ summarise-tests.py \ wocky-dummy-xmpp-server.c \ $(wildcard $(srcdir)/certs/*.cfg) \ $(wildcard $(srcdir)/certs/*.pem) \ $(wildcard $(srcdir)/certs/cas/*.pem) \ $(wildcard $(srcdir)/certs/cas/*.0) telepathy-gabble-0.18.2/lib/ext/wocky/tests/Makefile.in0000644000175000017500000030510312312536107022730 0ustar00smcvsmcv00000000000000# Makefile.in generated by automake 1.14.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2013 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ VPATH = @srcdir@ am__is_gnu_make = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)' am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ noinst_PROGRAMS = $(am__EXEEXT_4) $(am__EXEEXT_3) @HAVE_LIBSASL2_TRUE@am__append_1 = wocky-test-sasl-auth @HAVE_LIBSASL2_TRUE@am__append_2 = wocky-dummy-xmpp-server @HAVE_GIO_PROXY_TRUE@am__append_3 = wocky-http-proxy-test check_PROGRAMS = $(am__EXEEXT_3) DIST_COMMON = $(top_srcdir)/tools/check-coding-style.mk \ $(srcdir)/Makefile.in $(srcdir)/Makefile.am \ $(top_srcdir)/depcomp README subdir = tests ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/as-compiler-flag.m4 \ $(top_srcdir)/m4/gtk-doc.m4 $(top_srcdir)/m4/libtool.m4 \ $(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \ $(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \ $(top_srcdir)/m4/tp-compiler-flag.m4 \ $(top_srcdir)/m4/tp-compiler-warnings.m4 \ $(top_srcdir)/m4/wocky-gcov.m4 $(top_srcdir)/m4/wocky-lcov.m4 \ $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = @HAVE_LIBSASL2_TRUE@am__EXEEXT_1 = wocky-test-sasl-auth$(EXEEXT) @HAVE_GIO_PROXY_TRUE@am__EXEEXT_2 = wocky-http-proxy-test$(EXEEXT) am__EXEEXT_3 = wocky-bare-contact-test$(EXEEXT) \ wocky-caps-hash-test$(EXEEXT) wocky-connector-test$(EXEEXT) \ wocky-contact-factory-test$(EXEEXT) \ wocky-data-form-test$(EXEEXT) \ wocky-jid-validation-test$(EXEEXT) \ wocky-loopback-test$(EXEEXT) wocky-node-tree-test$(EXEEXT) \ wocky-pep-service-test$(EXEEXT) wocky-ping-test$(EXEEXT) \ wocky-porter-test$(EXEEXT) wocky-pubsub-node-test$(EXEEXT) \ wocky-pubsub-service-test$(EXEEXT) \ wocky-resource-contact-test$(EXEEXT) \ wocky-roster-test$(EXEEXT) wocky-sasl-utils-test$(EXEEXT) \ wocky-scram-sha1-test$(EXEEXT) wocky-session-test$(EXEEXT) \ wocky-stanza-test$(EXEEXT) wocky-tls-test$(EXEEXT) \ wocky-utils-test$(EXEEXT) wocky-xmpp-connection-test$(EXEEXT) \ wocky-xmpp-node-test$(EXEEXT) wocky-xmpp-reader-test$(EXEEXT) \ wocky-xmpp-readwrite-test$(EXEEXT) $(am__EXEEXT_1) \ $(am__EXEEXT_2) @HAVE_LIBSASL2_TRUE@am__EXEEXT_4 = wocky-dummy-xmpp-server$(EXEEXT) PROGRAMS = $(noinst_PROGRAMS) am_wocky_bare_contact_test_OBJECTS = wocky-test-helper.$(OBJEXT) \ wocky-test-stream.$(OBJEXT) wocky-bare-contact-test.$(OBJEXT) wocky_bare_contact_test_OBJECTS = \ $(am_wocky_bare_contact_test_OBJECTS) wocky_bare_contact_test_LDADD = $(LDADD) wocky_bare_contact_test_DEPENDENCIES = \ $(top_builddir)/wocky/libwocky.la AM_V_lt = $(am__v_lt_@AM_V@) am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) am__v_lt_0 = --silent am__v_lt_1 = am_wocky_caps_hash_test_OBJECTS = wocky-caps-hash-test.$(OBJEXT) \ wocky-test-helper.$(OBJEXT) wocky-test-stream.$(OBJEXT) wocky_caps_hash_test_OBJECTS = $(am_wocky_caps_hash_test_OBJECTS) wocky_caps_hash_test_LDADD = $(LDADD) wocky_caps_hash_test_DEPENDENCIES = $(top_builddir)/wocky/libwocky.la am_wocky_connector_test_OBJECTS = \ wocky_connector_test-wocky-connector-test.$(OBJEXT) \ wocky_connector_test-wocky-test-sasl-auth-server.$(OBJEXT) \ wocky_connector_test-wocky-test-connector-server.$(OBJEXT) \ wocky_connector_test-wocky-test-helper.$(OBJEXT) \ wocky_connector_test-wocky-test-stream.$(OBJEXT) \ wocky_connector_test-test-resolver.$(OBJEXT) wocky_connector_test_OBJECTS = $(am_wocky_connector_test_OBJECTS) wocky_connector_test_DEPENDENCIES = $(LDADD) wocky_connector_test_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ $(wocky_connector_test_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) \ $(LDFLAGS) -o $@ am_wocky_contact_factory_test_OBJECTS = wocky-test-helper.$(OBJEXT) \ wocky-test-stream.$(OBJEXT) \ wocky-contact-factory-test.$(OBJEXT) wocky_contact_factory_test_OBJECTS = \ $(am_wocky_contact_factory_test_OBJECTS) wocky_contact_factory_test_LDADD = $(LDADD) wocky_contact_factory_test_DEPENDENCIES = \ $(top_builddir)/wocky/libwocky.la am_wocky_data_form_test_OBJECTS = wocky-test-helper.$(OBJEXT) \ wocky-test-stream.$(OBJEXT) wocky-data-form-test.$(OBJEXT) wocky_data_form_test_OBJECTS = $(am_wocky_data_form_test_OBJECTS) wocky_data_form_test_LDADD = $(LDADD) wocky_data_form_test_DEPENDENCIES = $(top_builddir)/wocky/libwocky.la am_wocky_dummy_xmpp_server_OBJECTS = \ wocky_dummy_xmpp_server-wocky-dummy-xmpp-server.$(OBJEXT) \ wocky_dummy_xmpp_server-wocky-test-connector-server.$(OBJEXT) \ wocky_dummy_xmpp_server-wocky-test-helper.$(OBJEXT) \ wocky_dummy_xmpp_server-wocky-test-stream.$(OBJEXT) \ wocky_dummy_xmpp_server-wocky-test-sasl-auth-server.$(OBJEXT) wocky_dummy_xmpp_server_OBJECTS = \ $(am_wocky_dummy_xmpp_server_OBJECTS) wocky_dummy_xmpp_server_DEPENDENCIES = $(LDADD) wocky_dummy_xmpp_server_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ $(wocky_dummy_xmpp_server_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) \ $(LDFLAGS) -o $@ am_wocky_http_proxy_test_OBJECTS = wocky-http-proxy-test.$(OBJEXT) \ wocky-test-helper.$(OBJEXT) wocky-test-stream.$(OBJEXT) wocky_http_proxy_test_OBJECTS = $(am_wocky_http_proxy_test_OBJECTS) wocky_http_proxy_test_LDADD = $(LDADD) wocky_http_proxy_test_DEPENDENCIES = \ $(top_builddir)/wocky/libwocky.la am_wocky_jid_validation_test_OBJECTS = \ wocky-jid-validation-test.$(OBJEXT) \ wocky-test-helper.$(OBJEXT) wocky-test-stream.$(OBJEXT) wocky_jid_validation_test_OBJECTS = \ $(am_wocky_jid_validation_test_OBJECTS) wocky_jid_validation_test_LDADD = $(LDADD) wocky_jid_validation_test_DEPENDENCIES = \ $(top_builddir)/wocky/libwocky.la am_wocky_loopback_test_OBJECTS = wocky-test-helper.$(OBJEXT) \ wocky-test-stream.$(OBJEXT) wocky-loopback-test.$(OBJEXT) wocky_loopback_test_OBJECTS = $(am_wocky_loopback_test_OBJECTS) wocky_loopback_test_LDADD = $(LDADD) wocky_loopback_test_DEPENDENCIES = $(top_builddir)/wocky/libwocky.la am_wocky_node_tree_test_OBJECTS = wocky-test-helper.$(OBJEXT) \ wocky-test-stream.$(OBJEXT) wocky-node-tree-test.$(OBJEXT) wocky_node_tree_test_OBJECTS = $(am_wocky_node_tree_test_OBJECTS) wocky_node_tree_test_LDADD = $(LDADD) wocky_node_tree_test_DEPENDENCIES = $(top_builddir)/wocky/libwocky.la am_wocky_pep_service_test_OBJECTS = wocky-test-helper.$(OBJEXT) \ wocky-test-stream.$(OBJEXT) wocky-pep-service-test.$(OBJEXT) wocky_pep_service_test_OBJECTS = $(am_wocky_pep_service_test_OBJECTS) wocky_pep_service_test_LDADD = $(LDADD) wocky_pep_service_test_DEPENDENCIES = \ $(top_builddir)/wocky/libwocky.la am_wocky_ping_test_OBJECTS = wocky-ping-test.$(OBJEXT) \ wocky-test-helper.$(OBJEXT) wocky-test-stream.$(OBJEXT) wocky_ping_test_OBJECTS = $(am_wocky_ping_test_OBJECTS) wocky_ping_test_LDADD = $(LDADD) wocky_ping_test_DEPENDENCIES = $(top_builddir)/wocky/libwocky.la am_wocky_porter_test_OBJECTS = wocky-porter-test.$(OBJEXT) \ wocky-test-helper.$(OBJEXT) wocky-test-stream.$(OBJEXT) wocky_porter_test_OBJECTS = $(am_wocky_porter_test_OBJECTS) wocky_porter_test_LDADD = $(LDADD) wocky_porter_test_DEPENDENCIES = $(top_builddir)/wocky/libwocky.la am_wocky_pubsub_node_test_OBJECTS = wocky-test-helper.$(OBJEXT) \ wocky-test-stream.$(OBJEXT) \ wocky-pubsub-test-helpers.$(OBJEXT) \ wocky-pubsub-node-test.$(OBJEXT) wocky_pubsub_node_test_OBJECTS = $(am_wocky_pubsub_node_test_OBJECTS) wocky_pubsub_node_test_LDADD = $(LDADD) wocky_pubsub_node_test_DEPENDENCIES = \ $(top_builddir)/wocky/libwocky.la am_wocky_pubsub_service_test_OBJECTS = wocky-test-helper.$(OBJEXT) \ wocky-test-stream.$(OBJEXT) \ wocky-pubsub-test-helpers.$(OBJEXT) \ wocky-pubsub-service-test.$(OBJEXT) wocky_pubsub_service_test_OBJECTS = \ $(am_wocky_pubsub_service_test_OBJECTS) wocky_pubsub_service_test_LDADD = $(LDADD) wocky_pubsub_service_test_DEPENDENCIES = \ $(top_builddir)/wocky/libwocky.la am_wocky_resource_contact_test_OBJECTS = wocky-test-helper.$(OBJEXT) \ wocky-test-stream.$(OBJEXT) \ wocky-resource-contact-test.$(OBJEXT) wocky_resource_contact_test_OBJECTS = \ $(am_wocky_resource_contact_test_OBJECTS) wocky_resource_contact_test_LDADD = $(LDADD) wocky_resource_contact_test_DEPENDENCIES = \ $(top_builddir)/wocky/libwocky.la am_wocky_roster_test_OBJECTS = wocky-roster-test.$(OBJEXT) \ wocky-test-helper.$(OBJEXT) wocky-test-stream.$(OBJEXT) wocky_roster_test_OBJECTS = $(am_wocky_roster_test_OBJECTS) wocky_roster_test_LDADD = $(LDADD) wocky_roster_test_DEPENDENCIES = $(top_builddir)/wocky/libwocky.la am_wocky_sasl_utils_test_OBJECTS = wocky-sasl-utils-test.$(OBJEXT) wocky_sasl_utils_test_OBJECTS = $(am_wocky_sasl_utils_test_OBJECTS) wocky_sasl_utils_test_LDADD = $(LDADD) wocky_sasl_utils_test_DEPENDENCIES = \ $(top_builddir)/wocky/libwocky.la am_wocky_scram_sha1_test_OBJECTS = wocky-scram-sha1-test.$(OBJEXT) \ wocky-test-helper.$(OBJEXT) wocky-test-stream.$(OBJEXT) wocky_scram_sha1_test_OBJECTS = $(am_wocky_scram_sha1_test_OBJECTS) wocky_scram_sha1_test_LDADD = $(LDADD) wocky_scram_sha1_test_DEPENDENCIES = \ $(top_builddir)/wocky/libwocky.la am_wocky_session_test_OBJECTS = wocky-test-helper.$(OBJEXT) \ wocky-test-stream.$(OBJEXT) wocky-session-test.$(OBJEXT) wocky_session_test_OBJECTS = $(am_wocky_session_test_OBJECTS) wocky_session_test_LDADD = $(LDADD) wocky_session_test_DEPENDENCIES = $(top_builddir)/wocky/libwocky.la am_wocky_stanza_test_OBJECTS = wocky-test-helper.$(OBJEXT) \ wocky-test-stream.$(OBJEXT) wocky-stanza-test.$(OBJEXT) wocky_stanza_test_OBJECTS = $(am_wocky_stanza_test_OBJECTS) wocky_stanza_test_LDADD = $(LDADD) wocky_stanza_test_DEPENDENCIES = $(top_builddir)/wocky/libwocky.la am_wocky_test_sasl_auth_OBJECTS = \ wocky_test_sasl_auth-wocky-test-helper.$(OBJEXT) \ wocky_test_sasl_auth-wocky-test-sasl-auth.$(OBJEXT) \ wocky_test_sasl_auth-wocky-test-sasl-handler.$(OBJEXT) \ wocky_test_sasl_auth-wocky-test-sasl-auth-server.$(OBJEXT) \ wocky_test_sasl_auth-wocky-test-stream.$(OBJEXT) wocky_test_sasl_auth_OBJECTS = $(am_wocky_test_sasl_auth_OBJECTS) wocky_test_sasl_auth_DEPENDENCIES = $(LDADD) wocky_test_sasl_auth_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ $(wocky_test_sasl_auth_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) \ $(LDFLAGS) -o $@ am_wocky_tls_test_OBJECTS = wocky_tls_test-wocky-tls-test.$(OBJEXT) \ wocky_tls_test-wocky-test-helper.$(OBJEXT) \ wocky_tls_test-wocky-test-stream.$(OBJEXT) wocky_tls_test_OBJECTS = $(am_wocky_tls_test_OBJECTS) wocky_tls_test_LDADD = $(LDADD) wocky_tls_test_DEPENDENCIES = $(top_builddir)/wocky/libwocky.la wocky_tls_test_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ $(wocky_tls_test_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o \ $@ am_wocky_utils_test_OBJECTS = wocky-utils-test.$(OBJEXT) wocky_utils_test_OBJECTS = $(am_wocky_utils_test_OBJECTS) wocky_utils_test_LDADD = $(LDADD) wocky_utils_test_DEPENDENCIES = $(top_builddir)/wocky/libwocky.la am_wocky_xmpp_connection_test_OBJECTS = \ wocky-xmpp-connection-test.$(OBJEXT) \ wocky-test-helper.$(OBJEXT) wocky-test-stream.$(OBJEXT) wocky_xmpp_connection_test_OBJECTS = \ $(am_wocky_xmpp_connection_test_OBJECTS) wocky_xmpp_connection_test_LDADD = $(LDADD) wocky_xmpp_connection_test_DEPENDENCIES = \ $(top_builddir)/wocky/libwocky.la am_wocky_xmpp_node_test_OBJECTS = wocky-test-helper.$(OBJEXT) \ wocky-test-stream.$(OBJEXT) wocky-xmpp-node-test.$(OBJEXT) wocky_xmpp_node_test_OBJECTS = $(am_wocky_xmpp_node_test_OBJECTS) wocky_xmpp_node_test_LDADD = $(LDADD) wocky_xmpp_node_test_DEPENDENCIES = $(top_builddir)/wocky/libwocky.la am_wocky_xmpp_reader_test_OBJECTS = wocky-test-helper.$(OBJEXT) \ wocky-test-stream.$(OBJEXT) wocky-xmpp-reader-test.$(OBJEXT) wocky_xmpp_reader_test_OBJECTS = $(am_wocky_xmpp_reader_test_OBJECTS) wocky_xmpp_reader_test_LDADD = $(LDADD) wocky_xmpp_reader_test_DEPENDENCIES = \ $(top_builddir)/wocky/libwocky.la am_wocky_xmpp_readwrite_test_OBJECTS = wocky-test-stream.$(OBJEXT) \ wocky-test-helper.$(OBJEXT) \ wocky-xmpp-readwrite-test.$(OBJEXT) wocky_xmpp_readwrite_test_OBJECTS = \ $(am_wocky_xmpp_readwrite_test_OBJECTS) wocky_xmpp_readwrite_test_LDADD = $(LDADD) wocky_xmpp_readwrite_test_DEPENDENCIES = \ $(top_builddir)/wocky/libwocky.la AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) depcomp = $(SHELL) $(top_srcdir)/depcomp am__depfiles_maybe = depfiles am__mv = mv -f COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ $(AM_CFLAGS) $(CFLAGS) AM_V_CC = $(am__v_CC_@AM_V@) am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) am__v_CC_0 = @echo " CC " $@; am__v_CC_1 = CCLD = $(CC) LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ $(AM_LDFLAGS) $(LDFLAGS) -o $@ AM_V_CCLD = $(am__v_CCLD_@AM_V@) am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) am__v_CCLD_0 = @echo " CCLD " $@; am__v_CCLD_1 = SOURCES = $(wocky_bare_contact_test_SOURCES) \ $(wocky_caps_hash_test_SOURCES) \ $(wocky_connector_test_SOURCES) \ $(wocky_contact_factory_test_SOURCES) \ $(wocky_data_form_test_SOURCES) \ $(wocky_dummy_xmpp_server_SOURCES) \ $(wocky_http_proxy_test_SOURCES) \ $(wocky_jid_validation_test_SOURCES) \ $(wocky_loopback_test_SOURCES) $(wocky_node_tree_test_SOURCES) \ $(wocky_pep_service_test_SOURCES) $(wocky_ping_test_SOURCES) \ $(wocky_porter_test_SOURCES) $(wocky_pubsub_node_test_SOURCES) \ $(wocky_pubsub_service_test_SOURCES) \ $(wocky_resource_contact_test_SOURCES) \ $(wocky_roster_test_SOURCES) $(wocky_sasl_utils_test_SOURCES) \ $(wocky_scram_sha1_test_SOURCES) $(wocky_session_test_SOURCES) \ $(wocky_stanza_test_SOURCES) $(wocky_test_sasl_auth_SOURCES) \ $(wocky_tls_test_SOURCES) $(wocky_utils_test_SOURCES) \ $(wocky_xmpp_connection_test_SOURCES) \ $(wocky_xmpp_node_test_SOURCES) \ $(wocky_xmpp_reader_test_SOURCES) \ $(wocky_xmpp_readwrite_test_SOURCES) DIST_SOURCES = $(wocky_bare_contact_test_SOURCES) \ $(wocky_caps_hash_test_SOURCES) \ $(wocky_connector_test_SOURCES) \ $(wocky_contact_factory_test_SOURCES) \ $(wocky_data_form_test_SOURCES) \ $(wocky_dummy_xmpp_server_SOURCES) \ $(wocky_http_proxy_test_SOURCES) \ $(wocky_jid_validation_test_SOURCES) \ $(wocky_loopback_test_SOURCES) $(wocky_node_tree_test_SOURCES) \ $(wocky_pep_service_test_SOURCES) $(wocky_ping_test_SOURCES) \ $(wocky_porter_test_SOURCES) $(wocky_pubsub_node_test_SOURCES) \ $(wocky_pubsub_service_test_SOURCES) \ $(wocky_resource_contact_test_SOURCES) \ $(wocky_roster_test_SOURCES) $(wocky_sasl_utils_test_SOURCES) \ $(wocky_scram_sha1_test_SOURCES) $(wocky_session_test_SOURCES) \ $(wocky_stanza_test_SOURCES) $(wocky_test_sasl_auth_SOURCES) \ $(wocky_tls_test_SOURCES) $(wocky_utils_test_SOURCES) \ $(wocky_xmpp_connection_test_SOURCES) \ $(wocky_xmpp_node_test_SOURCES) \ $(wocky_xmpp_reader_test_SOURCES) \ $(wocky_xmpp_readwrite_test_SOURCES) am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) # Read a list of newline-separated strings from the standard input, # and print each of them once, without duplicates. Input order is # *not* preserved. am__uniquify_input = $(AWK) '\ BEGIN { nonempty = 0; } \ { items[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in items) print i; }; } \ ' # Make sure the list of sources is unique. This is necessary because, # e.g., the same source file might be shared among _SOURCES variables # for different programs/libraries. am__define_uniq_tagged_files = \ list='$(am__tagged_files)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | $(am__uniquify_input)` ETAGS = etags CTAGS = ctags DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ AR = @AR@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CC = @CC@ CCAS = @CCAS@ CCASDEPMODE = @CCASDEPMODE@ CCASFLAGS = @CCASFLAGS@ CCDEPMODE = @CCDEPMODE@ CFLAGS = @CFLAGS@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CXXFLAGS = @CXXFLAGS@ CYGPATH_W = @CYGPATH_W@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DLLTOOL = @DLLTOOL@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ ENABLE_CODING_STYLE_CHECKS = @ENABLE_CODING_STYLE_CHECKS@ ERROR_CFLAGS = @ERROR_CFLAGS@ EXEEXT = @EXEEXT@ FFLAGS = @FFLAGS@ FGREP = @FGREP@ GCOV = @GCOV@ GCOV_CFLAGS = @GCOV_CFLAGS@ GCOV_LIBS = @GCOV_LIBS@ GLIB_CFLAGS = @GLIB_CFLAGS@ GLIB_GENMARSHAL = @GLIB_GENMARSHAL@ GLIB_LIBS = @GLIB_LIBS@ GNUTLS_FOR_STREAM_CIPHERS_CFLAGS = @GNUTLS_FOR_STREAM_CIPHERS_CFLAGS@ GNUTLS_FOR_STREAM_CIPHERS_LIBS = @GNUTLS_FOR_STREAM_CIPHERS_LIBS@ GREP = @GREP@ GTKDOC_CHECK = @GTKDOC_CHECK@ GTKDOC_CHECK_PATH = @GTKDOC_CHECK_PATH@ GTKDOC_DEPS_CFLAGS = @GTKDOC_DEPS_CFLAGS@ GTKDOC_DEPS_LIBS = @GTKDOC_DEPS_LIBS@ GTKDOC_MKPDF = @GTKDOC_MKPDF@ GTKDOC_REBASE = @GTKDOC_REBASE@ HEADER_DIR = @HEADER_DIR@ HTML_DIR = @HTML_DIR@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ LCOV_PATH = @LCOV_PATH@ LD = @LD@ LDFLAGS = @LDFLAGS@ LIBIPHB_CFLAGS = @LIBIPHB_CFLAGS@ LIBIPHB_LIBS = @LIBIPHB_LIBS@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBSASL2_CFLAGS = @LIBSASL2_CFLAGS@ LIBSASL2_LIBS = @LIBSASL2_LIBS@ LIBTOOL = @LIBTOOL@ LIBXML2_CFLAGS = @LIBXML2_CFLAGS@ LIBXML2_LIBS = @LIBXML2_LIBS@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBOBJS = @LTLIBOBJS@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ MOSTLYCLEANFILES = @MOSTLYCLEANFILES@ NM = @NM@ NMEDIT = @NMEDIT@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ RANLIB = @RANLIB@ SED = @SED@ SET_MAKE = @SET_MAKE@ SHARED_SUFFIX = @SHARED_SUFFIX@ SHELL = @SHELL@ SOUP_CFLAGS = @SOUP_CFLAGS@ SOUP_LIBS = @SOUP_LIBS@ SQLITE_CFLAGS = @SQLITE_CFLAGS@ SQLITE_LIBS = @SQLITE_LIBS@ STRIP = @STRIP@ TLS_CFLAGS = @TLS_CFLAGS@ TLS_LIBS = @TLS_LIBS@ VERSION = @VERSION@ WOCKY_CFLAGS = @WOCKY_CFLAGS@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ have_gcov = @have_gcov@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ mandir = @mandir@ mkdir_p = @mkdir_p@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ srcdir = @srcdir@ sysconfdir = @sysconfdir@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ ############################################################################ # x509 certificates: TEST_DIR := @abs_top_srcdir@/tests SUMMARY := $(TEST_DIR)/summarise-tests.py CERT_DIR := $(TEST_DIR)/certs CA_KEY := $(CERT_DIR)/ca-0-key.pem CA_CERT := $(CERT_DIR)/ca-0-cert.pem SS_KEY := $(CERT_DIR)/ss-key.pem SS_CERT := $(CERT_DIR)/ss-cert.pem REV_KEY := $(CERT_DIR)/rev-key.pem REV_CERT := $(CERT_DIR)/rev-cert.pem EXP_KEY := $(CERT_DIR)/exp-key.pem EXP_CERT := $(CERT_DIR)/exp-cert.pem NEW_KEY := $(CERT_DIR)/new-key.pem NEW_CERT := $(CERT_DIR)/new-cert.pem TLS_KEY := $(CERT_DIR)/tls-key.pem TLS_CERT := $(CERT_DIR)/tls-cert.pem WILD_KEY := $(CERT_DIR)/wild-key.pem WILD_CERT := $(CERT_DIR)/wild-cert.pem BADWILD_KEY := $(CERT_DIR)/badwild-key.pem BADWILD_CERT := $(CERT_DIR)/badwild-cert.pem CA_DIR := $(CERT_DIR)/cas CRL_DIR := $(CERT_DIR)/crl UNKNOWN_KEY := $(CERT_DIR)/unknown-key.pem UNKNOWN_CERT := $(CERT_DIR)/unknown-cert.pem INCLUDES := -I$(top_builddir)/wocky TLSDEFS := -DTLS_CA_KEY_FILE='"$(CA_KEY)"' \ -DTLS_CA_CRT_FILE='"$(CA_CERT)"' \ -DTLS_SS_KEY_FILE='"$(SS_KEY)"' \ -DTLS_SS_CRT_FILE='"$(SS_CERT)"' \ -DTLS_EXP_KEY_FILE='"$(EXP_KEY)"' \ -DTLS_EXP_CRT_FILE='"$(EXP_CERT)"' \ -DTLS_NEW_KEY_FILE='"$(NEW_KEY)"' \ -DTLS_NEW_CRT_FILE='"$(NEW_CERT)"' \ -DTLS_REV_KEY_FILE='"$(REV_KEY)"' \ -DTLS_REV_CRT_FILE='"$(REV_CERT)"' \ -DTLS_UNKNOWN_KEY_FILE='"$(UNKNOWN_KEY)"' \ -DTLS_UNKNOWN_CRT_FILE='"$(UNKNOWN_CERT)"' \ -DTLS_SERVER_KEY_FILE='"$(TLS_KEY)"' \ -DTLS_SERVER_CRT_FILE='"$(TLS_CERT)"'\ -DTLS_WILD_KEY_FILE='"$(WILD_KEY)"' \ -DTLS_WILD_CRT_FILE='"$(WILD_CERT)"' \ -DTLS_BADWILD_CRT_FILE='"$(BADWILD_CERT)"' \ -DTLS_BADWILD_KEY_FILE='"$(BADWILD_KEY)"' \ -DTLS_CRL_DIR='"$(CRL_DIR)"' \ -DTLS_CA_DIR='"$(CA_DIR)"' ############################################################################ TEST_PROGS = wocky-bare-contact-test wocky-caps-hash-test \ wocky-connector-test wocky-contact-factory-test \ wocky-data-form-test wocky-jid-validation-test \ wocky-loopback-test wocky-node-tree-test \ wocky-pep-service-test wocky-ping-test wocky-porter-test \ wocky-pubsub-node-test wocky-pubsub-service-test \ wocky-resource-contact-test wocky-roster-test \ wocky-sasl-utils-test wocky-scram-sha1-test wocky-session-test \ wocky-stanza-test wocky-tls-test wocky-utils-test \ wocky-xmpp-connection-test wocky-xmpp-node-test \ wocky-xmpp-reader-test wocky-xmpp-readwrite-test $(NULL) \ $(am__append_1) $(am__append_3) wocky_bare_contact_test_SOURCES = \ wocky-test-helper.c wocky-test-helper.h \ wocky-test-stream.c wocky-test-stream.h \ wocky-bare-contact-test.c wocky_caps_hash_test_SOURCES = wocky-caps-hash-test.c \ wocky-test-helper.c wocky-test-helper.h \ wocky-test-stream.c wocky-test-stream.h EXTRA_wocky_connector_test_DEPENDENCIES = $(CA_DIR) certs wocky_connector_test_SOURCES = \ wocky-connector-test.c \ wocky-test-sasl-auth-server.c \ wocky-test-sasl-auth-server.h \ wocky-test-connector-server.c \ wocky-test-connector-server.h \ wocky-test-helper.c wocky-test-helper.h \ wocky-test-stream.c wocky-test-stream.h \ test-resolver.c test-resolver.h wocky_connector_test_LDADD = $(LDADD) @LIBSASL2_LIBS@ wocky_connector_test_CFLAGS = $(AM_CFLAGS) @LIBSASL2_CFLAGS@ $(TLSDEFS) wocky_contact_factory_test_SOURCES = \ wocky-test-helper.c wocky-test-helper.h \ wocky-test-stream.c wocky-test-stream.h \ wocky-contact-factory-test.c wocky_data_form_test_SOURCES = \ wocky-test-helper.c wocky-test-helper.h \ wocky-test-stream.c wocky-test-stream.h \ wocky-data-form-test.c wocky_dummy_xmpp_server_SOURCES = wocky-dummy-xmpp-server.c \ wocky-test-connector-server.c wocky-test-connector-server.h \ wocky-test-helper.c wocky-test-helper.h \ wocky-test-stream.c wocky-test-stream.h \ wocky-test-sasl-auth-server.c wocky-test-sasl-auth-server.h wocky_dummy_xmpp_server_LDADD = $(LDADD) @LIBSASL2_LIBS@ wocky_dummy_xmpp_server_CFLAGS = $(AM_CFLAGS) @LIBSASL2_CFLAGS@ $(TLSDEFS) wocky_http_proxy_test_SOURCES = wocky-http-proxy-test.c \ wocky-test-helper.c wocky-test-helper.h \ wocky-test-stream.c wocky-test-stream.h wocky_jid_validation_test_SOURCES = \ wocky-jid-validation-test.c \ wocky-test-helper.c wocky-test-helper.h \ wocky-test-stream.c wocky-test-stream.h wocky_loopback_test_SOURCES = \ wocky-test-helper.c wocky-test-helper.h \ wocky-test-stream.c wocky-test-stream.h \ wocky-loopback-test.c wocky_node_tree_test_SOURCES = \ wocky-test-helper.c wocky-test-helper.h \ wocky-test-stream.c wocky-test-stream.h \ wocky-node-tree-test.c wocky_pep_service_test_SOURCES = \ wocky-test-helper.c wocky-test-helper.h \ wocky-test-stream.c wocky-test-stream.h \ wocky-pep-service-test.c wocky_ping_test_SOURCES = \ wocky-ping-test.c \ wocky-test-helper.c wocky-test-helper.h \ wocky-test-stream.c wocky-test-stream.h wocky_porter_test_SOURCES = \ wocky-porter-test.c \ wocky-test-helper.c wocky-test-helper.h \ wocky-test-stream.c wocky-test-stream.h wocky_pubsub_node_test_SOURCES = \ wocky-test-helper.c wocky-test-helper.h \ wocky-test-stream.c wocky-test-stream.h \ wocky-pubsub-test-helpers.c wocky-pubsub-test-helpers.h \ wocky-pubsub-node-test.c wocky_pubsub_service_test_SOURCES = \ wocky-test-helper.c wocky-test-helper.h \ wocky-test-stream.c wocky-test-stream.h \ wocky-pubsub-test-helpers.c wocky-pubsub-test-helpers.h \ wocky-pubsub-service-test.c wocky_resource_contact_test_SOURCES = \ wocky-test-helper.c wocky-test-helper.h \ wocky-test-stream.c wocky-test-stream.h \ wocky-resource-contact-test.c wocky_roster_test_SOURCES = \ wocky-roster-test.c \ wocky-test-helper.c wocky-test-helper.h \ wocky-test-stream.c wocky-test-stream.h wocky_sasl_utils_test_SOURCES = wocky-sasl-utils-test.c wocky_scram_sha1_test_SOURCES = wocky-scram-sha1-test.c \ wocky-test-helper.c wocky-test-helper.h \ wocky-test-stream.c wocky-test-stream.h wocky_session_test_SOURCES = \ wocky-test-helper.c wocky-test-helper.h \ wocky-test-stream.c wocky-test-stream.h \ wocky-session-test.c wocky_stanza_test_SOURCES = \ wocky-test-helper.c wocky-test-helper.h \ wocky-test-stream.c wocky-test-stream.h \ wocky-stanza-test.c wocky_test_sasl_auth_SOURCES = \ wocky-test-helper.c wocky-test-helper.h \ wocky-test-sasl-auth.c \ wocky-test-sasl-handler.c \ wocky-test-sasl-handler.h \ wocky-test-sasl-auth-server.c \ wocky-test-sasl-auth-server.h \ wocky-test-stream.c \ wocky-test-stream.h wocky_test_sasl_auth_LDADD = $(LDADD) @LIBSASL2_LIBS@ wocky_test_sasl_auth_CFLAGS = $(AM_CFLAGS) @LIBSASL2_CFLAGS@ wocky_tls_test_SOURCES = \ wocky-tls-test.c \ wocky-test-helper.c wocky-test-helper.h \ wocky-test-stream.c wocky-test-stream.h wocky_tls_test_CFLAGS = $(AM_CFLAGS) $(TLSDEFS) wocky_utils_test_SOURCES = wocky-utils-test.c wocky_xmpp_connection_test_SOURCES = \ wocky-xmpp-connection-test.c \ wocky-test-helper.c wocky-test-helper.h \ wocky-test-stream.c wocky-test-stream.h wocky_xmpp_node_test_SOURCES = \ wocky-test-helper.c wocky-test-helper.h \ wocky-test-stream.c wocky-test-stream.h \ wocky-xmpp-node-test.c wocky_xmpp_reader_test_SOURCES = \ wocky-test-helper.c wocky-test-helper.h \ wocky-test-stream.c wocky-test-stream.h \ wocky-xmpp-reader-test.c wocky_xmpp_readwrite_test_SOURCES = \ wocky-test-stream.c wocky-test-stream.h \ wocky-test-helper.c wocky-test-helper.h \ wocky-xmpp-readwrite-test.c AM_CFLAGS = $(ERROR_CFLAGS) $(GCOV_CFLAGS) @GLIB_CFLAGS@ \ @LIBXML2_CFLAGS@ @TLS_CFLAGS@ @WOCKY_CFLAGS@ AM_LDFLAGS = $(GCOV_LIBS) @GLIB_LIBS@ @LIBXML2_LIBS@ @TLS_LIBS@ LDADD = $(top_builddir)/wocky/libwocky.la check_c_sources = $(notdir $(wildcard $(srcdir)/*.c) $(wildcard $(srcdir)/*.h)) ############################################################################ SUPPRESSIONS = \ threadlocal.supp \ gabble.supp \ tp-glib.supp CLEANFILES = test-report.xml \ sasl-test.db EXTRA_DIST = $(SUPPRESSIONS) \ README \ connector-test-plan.txt \ summarise-tests.py \ wocky-dummy-xmpp-server.c \ $(wildcard $(srcdir)/certs/*.cfg) \ $(wildcard $(srcdir)/certs/*.pem) \ $(wildcard $(srcdir)/certs/cas/*.pem) \ $(wildcard $(srcdir)/certs/cas/*.0) all: all-am .SUFFIXES: .SUFFIXES: .c .lo .o .obj $(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(top_srcdir)/tools/check-coding-style.mk $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu tests/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --gnu tests/Makefile .PRECIOUS: Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ esac; $(top_srcdir)/tools/check-coding-style.mk: $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): clean-checkPROGRAMS: @list='$(check_PROGRAMS)'; test -n "$$list" || exit 0; \ echo " rm -f" $$list; \ rm -f $$list || exit $$?; \ test -n "$(EXEEXT)" || exit 0; \ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \ echo " rm -f" $$list; \ rm -f $$list clean-noinstPROGRAMS: @list='$(noinst_PROGRAMS)'; test -n "$$list" || exit 0; \ echo " rm -f" $$list; \ rm -f $$list || exit $$?; \ test -n "$(EXEEXT)" || exit 0; \ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \ echo " rm -f" $$list; \ rm -f $$list wocky-bare-contact-test$(EXEEXT): $(wocky_bare_contact_test_OBJECTS) $(wocky_bare_contact_test_DEPENDENCIES) $(EXTRA_wocky_bare_contact_test_DEPENDENCIES) @rm -f wocky-bare-contact-test$(EXEEXT) $(AM_V_CCLD)$(LINK) $(wocky_bare_contact_test_OBJECTS) $(wocky_bare_contact_test_LDADD) $(LIBS) wocky-caps-hash-test$(EXEEXT): $(wocky_caps_hash_test_OBJECTS) $(wocky_caps_hash_test_DEPENDENCIES) $(EXTRA_wocky_caps_hash_test_DEPENDENCIES) @rm -f wocky-caps-hash-test$(EXEEXT) $(AM_V_CCLD)$(LINK) $(wocky_caps_hash_test_OBJECTS) $(wocky_caps_hash_test_LDADD) $(LIBS) wocky-connector-test$(EXEEXT): $(wocky_connector_test_OBJECTS) $(wocky_connector_test_DEPENDENCIES) $(EXTRA_wocky_connector_test_DEPENDENCIES) @rm -f wocky-connector-test$(EXEEXT) $(AM_V_CCLD)$(wocky_connector_test_LINK) $(wocky_connector_test_OBJECTS) $(wocky_connector_test_LDADD) $(LIBS) wocky-contact-factory-test$(EXEEXT): $(wocky_contact_factory_test_OBJECTS) $(wocky_contact_factory_test_DEPENDENCIES) $(EXTRA_wocky_contact_factory_test_DEPENDENCIES) @rm -f wocky-contact-factory-test$(EXEEXT) $(AM_V_CCLD)$(LINK) $(wocky_contact_factory_test_OBJECTS) $(wocky_contact_factory_test_LDADD) $(LIBS) wocky-data-form-test$(EXEEXT): $(wocky_data_form_test_OBJECTS) $(wocky_data_form_test_DEPENDENCIES) $(EXTRA_wocky_data_form_test_DEPENDENCIES) @rm -f wocky-data-form-test$(EXEEXT) $(AM_V_CCLD)$(LINK) $(wocky_data_form_test_OBJECTS) $(wocky_data_form_test_LDADD) $(LIBS) wocky-dummy-xmpp-server$(EXEEXT): $(wocky_dummy_xmpp_server_OBJECTS) $(wocky_dummy_xmpp_server_DEPENDENCIES) $(EXTRA_wocky_dummy_xmpp_server_DEPENDENCIES) @rm -f wocky-dummy-xmpp-server$(EXEEXT) $(AM_V_CCLD)$(wocky_dummy_xmpp_server_LINK) $(wocky_dummy_xmpp_server_OBJECTS) $(wocky_dummy_xmpp_server_LDADD) $(LIBS) wocky-http-proxy-test$(EXEEXT): $(wocky_http_proxy_test_OBJECTS) $(wocky_http_proxy_test_DEPENDENCIES) $(EXTRA_wocky_http_proxy_test_DEPENDENCIES) @rm -f wocky-http-proxy-test$(EXEEXT) $(AM_V_CCLD)$(LINK) $(wocky_http_proxy_test_OBJECTS) $(wocky_http_proxy_test_LDADD) $(LIBS) wocky-jid-validation-test$(EXEEXT): $(wocky_jid_validation_test_OBJECTS) $(wocky_jid_validation_test_DEPENDENCIES) $(EXTRA_wocky_jid_validation_test_DEPENDENCIES) @rm -f wocky-jid-validation-test$(EXEEXT) $(AM_V_CCLD)$(LINK) $(wocky_jid_validation_test_OBJECTS) $(wocky_jid_validation_test_LDADD) $(LIBS) wocky-loopback-test$(EXEEXT): $(wocky_loopback_test_OBJECTS) $(wocky_loopback_test_DEPENDENCIES) $(EXTRA_wocky_loopback_test_DEPENDENCIES) @rm -f wocky-loopback-test$(EXEEXT) $(AM_V_CCLD)$(LINK) $(wocky_loopback_test_OBJECTS) $(wocky_loopback_test_LDADD) $(LIBS) wocky-node-tree-test$(EXEEXT): $(wocky_node_tree_test_OBJECTS) $(wocky_node_tree_test_DEPENDENCIES) $(EXTRA_wocky_node_tree_test_DEPENDENCIES) @rm -f wocky-node-tree-test$(EXEEXT) $(AM_V_CCLD)$(LINK) $(wocky_node_tree_test_OBJECTS) $(wocky_node_tree_test_LDADD) $(LIBS) wocky-pep-service-test$(EXEEXT): $(wocky_pep_service_test_OBJECTS) $(wocky_pep_service_test_DEPENDENCIES) $(EXTRA_wocky_pep_service_test_DEPENDENCIES) @rm -f wocky-pep-service-test$(EXEEXT) $(AM_V_CCLD)$(LINK) $(wocky_pep_service_test_OBJECTS) $(wocky_pep_service_test_LDADD) $(LIBS) wocky-ping-test$(EXEEXT): $(wocky_ping_test_OBJECTS) $(wocky_ping_test_DEPENDENCIES) $(EXTRA_wocky_ping_test_DEPENDENCIES) @rm -f wocky-ping-test$(EXEEXT) $(AM_V_CCLD)$(LINK) $(wocky_ping_test_OBJECTS) $(wocky_ping_test_LDADD) $(LIBS) wocky-porter-test$(EXEEXT): $(wocky_porter_test_OBJECTS) $(wocky_porter_test_DEPENDENCIES) $(EXTRA_wocky_porter_test_DEPENDENCIES) @rm -f wocky-porter-test$(EXEEXT) $(AM_V_CCLD)$(LINK) $(wocky_porter_test_OBJECTS) $(wocky_porter_test_LDADD) $(LIBS) wocky-pubsub-node-test$(EXEEXT): $(wocky_pubsub_node_test_OBJECTS) $(wocky_pubsub_node_test_DEPENDENCIES) $(EXTRA_wocky_pubsub_node_test_DEPENDENCIES) @rm -f wocky-pubsub-node-test$(EXEEXT) $(AM_V_CCLD)$(LINK) $(wocky_pubsub_node_test_OBJECTS) $(wocky_pubsub_node_test_LDADD) $(LIBS) wocky-pubsub-service-test$(EXEEXT): $(wocky_pubsub_service_test_OBJECTS) $(wocky_pubsub_service_test_DEPENDENCIES) $(EXTRA_wocky_pubsub_service_test_DEPENDENCIES) @rm -f wocky-pubsub-service-test$(EXEEXT) $(AM_V_CCLD)$(LINK) $(wocky_pubsub_service_test_OBJECTS) $(wocky_pubsub_service_test_LDADD) $(LIBS) wocky-resource-contact-test$(EXEEXT): $(wocky_resource_contact_test_OBJECTS) $(wocky_resource_contact_test_DEPENDENCIES) $(EXTRA_wocky_resource_contact_test_DEPENDENCIES) @rm -f wocky-resource-contact-test$(EXEEXT) $(AM_V_CCLD)$(LINK) $(wocky_resource_contact_test_OBJECTS) $(wocky_resource_contact_test_LDADD) $(LIBS) wocky-roster-test$(EXEEXT): $(wocky_roster_test_OBJECTS) $(wocky_roster_test_DEPENDENCIES) $(EXTRA_wocky_roster_test_DEPENDENCIES) @rm -f wocky-roster-test$(EXEEXT) $(AM_V_CCLD)$(LINK) $(wocky_roster_test_OBJECTS) $(wocky_roster_test_LDADD) $(LIBS) wocky-sasl-utils-test$(EXEEXT): $(wocky_sasl_utils_test_OBJECTS) $(wocky_sasl_utils_test_DEPENDENCIES) $(EXTRA_wocky_sasl_utils_test_DEPENDENCIES) @rm -f wocky-sasl-utils-test$(EXEEXT) $(AM_V_CCLD)$(LINK) $(wocky_sasl_utils_test_OBJECTS) $(wocky_sasl_utils_test_LDADD) $(LIBS) wocky-scram-sha1-test$(EXEEXT): $(wocky_scram_sha1_test_OBJECTS) $(wocky_scram_sha1_test_DEPENDENCIES) $(EXTRA_wocky_scram_sha1_test_DEPENDENCIES) @rm -f wocky-scram-sha1-test$(EXEEXT) $(AM_V_CCLD)$(LINK) $(wocky_scram_sha1_test_OBJECTS) $(wocky_scram_sha1_test_LDADD) $(LIBS) wocky-session-test$(EXEEXT): $(wocky_session_test_OBJECTS) $(wocky_session_test_DEPENDENCIES) $(EXTRA_wocky_session_test_DEPENDENCIES) @rm -f wocky-session-test$(EXEEXT) $(AM_V_CCLD)$(LINK) $(wocky_session_test_OBJECTS) $(wocky_session_test_LDADD) $(LIBS) wocky-stanza-test$(EXEEXT): $(wocky_stanza_test_OBJECTS) $(wocky_stanza_test_DEPENDENCIES) $(EXTRA_wocky_stanza_test_DEPENDENCIES) @rm -f wocky-stanza-test$(EXEEXT) $(AM_V_CCLD)$(LINK) $(wocky_stanza_test_OBJECTS) $(wocky_stanza_test_LDADD) $(LIBS) wocky-test-sasl-auth$(EXEEXT): $(wocky_test_sasl_auth_OBJECTS) $(wocky_test_sasl_auth_DEPENDENCIES) $(EXTRA_wocky_test_sasl_auth_DEPENDENCIES) @rm -f wocky-test-sasl-auth$(EXEEXT) $(AM_V_CCLD)$(wocky_test_sasl_auth_LINK) $(wocky_test_sasl_auth_OBJECTS) $(wocky_test_sasl_auth_LDADD) $(LIBS) wocky-tls-test$(EXEEXT): $(wocky_tls_test_OBJECTS) $(wocky_tls_test_DEPENDENCIES) $(EXTRA_wocky_tls_test_DEPENDENCIES) @rm -f wocky-tls-test$(EXEEXT) $(AM_V_CCLD)$(wocky_tls_test_LINK) $(wocky_tls_test_OBJECTS) $(wocky_tls_test_LDADD) $(LIBS) wocky-utils-test$(EXEEXT): $(wocky_utils_test_OBJECTS) $(wocky_utils_test_DEPENDENCIES) $(EXTRA_wocky_utils_test_DEPENDENCIES) @rm -f wocky-utils-test$(EXEEXT) $(AM_V_CCLD)$(LINK) $(wocky_utils_test_OBJECTS) $(wocky_utils_test_LDADD) $(LIBS) wocky-xmpp-connection-test$(EXEEXT): $(wocky_xmpp_connection_test_OBJECTS) $(wocky_xmpp_connection_test_DEPENDENCIES) $(EXTRA_wocky_xmpp_connection_test_DEPENDENCIES) @rm -f wocky-xmpp-connection-test$(EXEEXT) $(AM_V_CCLD)$(LINK) $(wocky_xmpp_connection_test_OBJECTS) $(wocky_xmpp_connection_test_LDADD) $(LIBS) wocky-xmpp-node-test$(EXEEXT): $(wocky_xmpp_node_test_OBJECTS) $(wocky_xmpp_node_test_DEPENDENCIES) $(EXTRA_wocky_xmpp_node_test_DEPENDENCIES) @rm -f wocky-xmpp-node-test$(EXEEXT) $(AM_V_CCLD)$(LINK) $(wocky_xmpp_node_test_OBJECTS) $(wocky_xmpp_node_test_LDADD) $(LIBS) wocky-xmpp-reader-test$(EXEEXT): $(wocky_xmpp_reader_test_OBJECTS) $(wocky_xmpp_reader_test_DEPENDENCIES) $(EXTRA_wocky_xmpp_reader_test_DEPENDENCIES) @rm -f wocky-xmpp-reader-test$(EXEEXT) $(AM_V_CCLD)$(LINK) $(wocky_xmpp_reader_test_OBJECTS) $(wocky_xmpp_reader_test_LDADD) $(LIBS) wocky-xmpp-readwrite-test$(EXEEXT): $(wocky_xmpp_readwrite_test_OBJECTS) $(wocky_xmpp_readwrite_test_DEPENDENCIES) $(EXTRA_wocky_xmpp_readwrite_test_DEPENDENCIES) @rm -f wocky-xmpp-readwrite-test$(EXEEXT) $(AM_V_CCLD)$(LINK) $(wocky_xmpp_readwrite_test_OBJECTS) $(wocky_xmpp_readwrite_test_LDADD) $(LIBS) mostlyclean-compile: -rm -f *.$(OBJEXT) distclean-compile: -rm -f *.tab.c @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/wocky-bare-contact-test.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/wocky-caps-hash-test.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/wocky-contact-factory-test.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/wocky-data-form-test.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/wocky-http-proxy-test.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/wocky-jid-validation-test.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/wocky-loopback-test.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/wocky-node-tree-test.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/wocky-pep-service-test.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/wocky-ping-test.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/wocky-porter-test.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/wocky-pubsub-node-test.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/wocky-pubsub-service-test.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/wocky-pubsub-test-helpers.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/wocky-resource-contact-test.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/wocky-roster-test.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/wocky-sasl-utils-test.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/wocky-scram-sha1-test.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/wocky-session-test.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/wocky-stanza-test.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/wocky-test-helper.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/wocky-test-stream.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/wocky-utils-test.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/wocky-xmpp-connection-test.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/wocky-xmpp-node-test.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/wocky-xmpp-reader-test.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/wocky-xmpp-readwrite-test.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/wocky_connector_test-test-resolver.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/wocky_connector_test-wocky-connector-test.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/wocky_connector_test-wocky-test-connector-server.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/wocky_connector_test-wocky-test-helper.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/wocky_connector_test-wocky-test-sasl-auth-server.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/wocky_connector_test-wocky-test-stream.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/wocky_dummy_xmpp_server-wocky-dummy-xmpp-server.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/wocky_dummy_xmpp_server-wocky-test-connector-server.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/wocky_dummy_xmpp_server-wocky-test-helper.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/wocky_dummy_xmpp_server-wocky-test-sasl-auth-server.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/wocky_dummy_xmpp_server-wocky-test-stream.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/wocky_test_sasl_auth-wocky-test-helper.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/wocky_test_sasl_auth-wocky-test-sasl-auth-server.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/wocky_test_sasl_auth-wocky-test-sasl-auth.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/wocky_test_sasl_auth-wocky-test-sasl-handler.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/wocky_test_sasl_auth-wocky-test-stream.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/wocky_tls_test-wocky-test-helper.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/wocky_tls_test-wocky-test-stream.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/wocky_tls_test-wocky-tls-test.Po@am__quote@ .c.o: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< .c.obj: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` .c.lo: @am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< wocky_connector_test-wocky-connector-test.o: wocky-connector-test.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(wocky_connector_test_CFLAGS) $(CFLAGS) -MT wocky_connector_test-wocky-connector-test.o -MD -MP -MF $(DEPDIR)/wocky_connector_test-wocky-connector-test.Tpo -c -o wocky_connector_test-wocky-connector-test.o `test -f 'wocky-connector-test.c' || echo '$(srcdir)/'`wocky-connector-test.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/wocky_connector_test-wocky-connector-test.Tpo $(DEPDIR)/wocky_connector_test-wocky-connector-test.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='wocky-connector-test.c' object='wocky_connector_test-wocky-connector-test.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(wocky_connector_test_CFLAGS) $(CFLAGS) -c -o wocky_connector_test-wocky-connector-test.o `test -f 'wocky-connector-test.c' || echo '$(srcdir)/'`wocky-connector-test.c wocky_connector_test-wocky-connector-test.obj: wocky-connector-test.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(wocky_connector_test_CFLAGS) $(CFLAGS) -MT wocky_connector_test-wocky-connector-test.obj -MD -MP -MF $(DEPDIR)/wocky_connector_test-wocky-connector-test.Tpo -c -o wocky_connector_test-wocky-connector-test.obj `if test -f 'wocky-connector-test.c'; then $(CYGPATH_W) 'wocky-connector-test.c'; else $(CYGPATH_W) '$(srcdir)/wocky-connector-test.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/wocky_connector_test-wocky-connector-test.Tpo $(DEPDIR)/wocky_connector_test-wocky-connector-test.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='wocky-connector-test.c' object='wocky_connector_test-wocky-connector-test.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(wocky_connector_test_CFLAGS) $(CFLAGS) -c -o wocky_connector_test-wocky-connector-test.obj `if test -f 'wocky-connector-test.c'; then $(CYGPATH_W) 'wocky-connector-test.c'; else $(CYGPATH_W) '$(srcdir)/wocky-connector-test.c'; fi` wocky_connector_test-wocky-test-sasl-auth-server.o: wocky-test-sasl-auth-server.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(wocky_connector_test_CFLAGS) $(CFLAGS) -MT wocky_connector_test-wocky-test-sasl-auth-server.o -MD -MP -MF $(DEPDIR)/wocky_connector_test-wocky-test-sasl-auth-server.Tpo -c -o wocky_connector_test-wocky-test-sasl-auth-server.o `test -f 'wocky-test-sasl-auth-server.c' || echo '$(srcdir)/'`wocky-test-sasl-auth-server.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/wocky_connector_test-wocky-test-sasl-auth-server.Tpo $(DEPDIR)/wocky_connector_test-wocky-test-sasl-auth-server.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='wocky-test-sasl-auth-server.c' object='wocky_connector_test-wocky-test-sasl-auth-server.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(wocky_connector_test_CFLAGS) $(CFLAGS) -c -o wocky_connector_test-wocky-test-sasl-auth-server.o `test -f 'wocky-test-sasl-auth-server.c' || echo '$(srcdir)/'`wocky-test-sasl-auth-server.c wocky_connector_test-wocky-test-sasl-auth-server.obj: wocky-test-sasl-auth-server.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(wocky_connector_test_CFLAGS) $(CFLAGS) -MT wocky_connector_test-wocky-test-sasl-auth-server.obj -MD -MP -MF $(DEPDIR)/wocky_connector_test-wocky-test-sasl-auth-server.Tpo -c -o wocky_connector_test-wocky-test-sasl-auth-server.obj `if test -f 'wocky-test-sasl-auth-server.c'; then $(CYGPATH_W) 'wocky-test-sasl-auth-server.c'; else $(CYGPATH_W) '$(srcdir)/wocky-test-sasl-auth-server.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/wocky_connector_test-wocky-test-sasl-auth-server.Tpo $(DEPDIR)/wocky_connector_test-wocky-test-sasl-auth-server.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='wocky-test-sasl-auth-server.c' object='wocky_connector_test-wocky-test-sasl-auth-server.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(wocky_connector_test_CFLAGS) $(CFLAGS) -c -o wocky_connector_test-wocky-test-sasl-auth-server.obj `if test -f 'wocky-test-sasl-auth-server.c'; then $(CYGPATH_W) 'wocky-test-sasl-auth-server.c'; else $(CYGPATH_W) '$(srcdir)/wocky-test-sasl-auth-server.c'; fi` wocky_connector_test-wocky-test-connector-server.o: wocky-test-connector-server.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(wocky_connector_test_CFLAGS) $(CFLAGS) -MT wocky_connector_test-wocky-test-connector-server.o -MD -MP -MF $(DEPDIR)/wocky_connector_test-wocky-test-connector-server.Tpo -c -o wocky_connector_test-wocky-test-connector-server.o `test -f 'wocky-test-connector-server.c' || echo '$(srcdir)/'`wocky-test-connector-server.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/wocky_connector_test-wocky-test-connector-server.Tpo $(DEPDIR)/wocky_connector_test-wocky-test-connector-server.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='wocky-test-connector-server.c' object='wocky_connector_test-wocky-test-connector-server.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(wocky_connector_test_CFLAGS) $(CFLAGS) -c -o wocky_connector_test-wocky-test-connector-server.o `test -f 'wocky-test-connector-server.c' || echo '$(srcdir)/'`wocky-test-connector-server.c wocky_connector_test-wocky-test-connector-server.obj: wocky-test-connector-server.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(wocky_connector_test_CFLAGS) $(CFLAGS) -MT wocky_connector_test-wocky-test-connector-server.obj -MD -MP -MF $(DEPDIR)/wocky_connector_test-wocky-test-connector-server.Tpo -c -o wocky_connector_test-wocky-test-connector-server.obj `if test -f 'wocky-test-connector-server.c'; then $(CYGPATH_W) 'wocky-test-connector-server.c'; else $(CYGPATH_W) '$(srcdir)/wocky-test-connector-server.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/wocky_connector_test-wocky-test-connector-server.Tpo $(DEPDIR)/wocky_connector_test-wocky-test-connector-server.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='wocky-test-connector-server.c' object='wocky_connector_test-wocky-test-connector-server.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(wocky_connector_test_CFLAGS) $(CFLAGS) -c -o wocky_connector_test-wocky-test-connector-server.obj `if test -f 'wocky-test-connector-server.c'; then $(CYGPATH_W) 'wocky-test-connector-server.c'; else $(CYGPATH_W) '$(srcdir)/wocky-test-connector-server.c'; fi` wocky_connector_test-wocky-test-helper.o: wocky-test-helper.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(wocky_connector_test_CFLAGS) $(CFLAGS) -MT wocky_connector_test-wocky-test-helper.o -MD -MP -MF $(DEPDIR)/wocky_connector_test-wocky-test-helper.Tpo -c -o wocky_connector_test-wocky-test-helper.o `test -f 'wocky-test-helper.c' || echo '$(srcdir)/'`wocky-test-helper.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/wocky_connector_test-wocky-test-helper.Tpo $(DEPDIR)/wocky_connector_test-wocky-test-helper.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='wocky-test-helper.c' object='wocky_connector_test-wocky-test-helper.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(wocky_connector_test_CFLAGS) $(CFLAGS) -c -o wocky_connector_test-wocky-test-helper.o `test -f 'wocky-test-helper.c' || echo '$(srcdir)/'`wocky-test-helper.c wocky_connector_test-wocky-test-helper.obj: wocky-test-helper.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(wocky_connector_test_CFLAGS) $(CFLAGS) -MT wocky_connector_test-wocky-test-helper.obj -MD -MP -MF $(DEPDIR)/wocky_connector_test-wocky-test-helper.Tpo -c -o wocky_connector_test-wocky-test-helper.obj `if test -f 'wocky-test-helper.c'; then $(CYGPATH_W) 'wocky-test-helper.c'; else $(CYGPATH_W) '$(srcdir)/wocky-test-helper.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/wocky_connector_test-wocky-test-helper.Tpo $(DEPDIR)/wocky_connector_test-wocky-test-helper.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='wocky-test-helper.c' object='wocky_connector_test-wocky-test-helper.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(wocky_connector_test_CFLAGS) $(CFLAGS) -c -o wocky_connector_test-wocky-test-helper.obj `if test -f 'wocky-test-helper.c'; then $(CYGPATH_W) 'wocky-test-helper.c'; else $(CYGPATH_W) '$(srcdir)/wocky-test-helper.c'; fi` wocky_connector_test-wocky-test-stream.o: wocky-test-stream.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(wocky_connector_test_CFLAGS) $(CFLAGS) -MT wocky_connector_test-wocky-test-stream.o -MD -MP -MF $(DEPDIR)/wocky_connector_test-wocky-test-stream.Tpo -c -o wocky_connector_test-wocky-test-stream.o `test -f 'wocky-test-stream.c' || echo '$(srcdir)/'`wocky-test-stream.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/wocky_connector_test-wocky-test-stream.Tpo $(DEPDIR)/wocky_connector_test-wocky-test-stream.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='wocky-test-stream.c' object='wocky_connector_test-wocky-test-stream.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(wocky_connector_test_CFLAGS) $(CFLAGS) -c -o wocky_connector_test-wocky-test-stream.o `test -f 'wocky-test-stream.c' || echo '$(srcdir)/'`wocky-test-stream.c wocky_connector_test-wocky-test-stream.obj: wocky-test-stream.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(wocky_connector_test_CFLAGS) $(CFLAGS) -MT wocky_connector_test-wocky-test-stream.obj -MD -MP -MF $(DEPDIR)/wocky_connector_test-wocky-test-stream.Tpo -c -o wocky_connector_test-wocky-test-stream.obj `if test -f 'wocky-test-stream.c'; then $(CYGPATH_W) 'wocky-test-stream.c'; else $(CYGPATH_W) '$(srcdir)/wocky-test-stream.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/wocky_connector_test-wocky-test-stream.Tpo $(DEPDIR)/wocky_connector_test-wocky-test-stream.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='wocky-test-stream.c' object='wocky_connector_test-wocky-test-stream.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(wocky_connector_test_CFLAGS) $(CFLAGS) -c -o wocky_connector_test-wocky-test-stream.obj `if test -f 'wocky-test-stream.c'; then $(CYGPATH_W) 'wocky-test-stream.c'; else $(CYGPATH_W) '$(srcdir)/wocky-test-stream.c'; fi` wocky_connector_test-test-resolver.o: test-resolver.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(wocky_connector_test_CFLAGS) $(CFLAGS) -MT wocky_connector_test-test-resolver.o -MD -MP -MF $(DEPDIR)/wocky_connector_test-test-resolver.Tpo -c -o wocky_connector_test-test-resolver.o `test -f 'test-resolver.c' || echo '$(srcdir)/'`test-resolver.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/wocky_connector_test-test-resolver.Tpo $(DEPDIR)/wocky_connector_test-test-resolver.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-resolver.c' object='wocky_connector_test-test-resolver.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(wocky_connector_test_CFLAGS) $(CFLAGS) -c -o wocky_connector_test-test-resolver.o `test -f 'test-resolver.c' || echo '$(srcdir)/'`test-resolver.c wocky_connector_test-test-resolver.obj: test-resolver.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(wocky_connector_test_CFLAGS) $(CFLAGS) -MT wocky_connector_test-test-resolver.obj -MD -MP -MF $(DEPDIR)/wocky_connector_test-test-resolver.Tpo -c -o wocky_connector_test-test-resolver.obj `if test -f 'test-resolver.c'; then $(CYGPATH_W) 'test-resolver.c'; else $(CYGPATH_W) '$(srcdir)/test-resolver.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/wocky_connector_test-test-resolver.Tpo $(DEPDIR)/wocky_connector_test-test-resolver.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-resolver.c' object='wocky_connector_test-test-resolver.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(wocky_connector_test_CFLAGS) $(CFLAGS) -c -o wocky_connector_test-test-resolver.obj `if test -f 'test-resolver.c'; then $(CYGPATH_W) 'test-resolver.c'; else $(CYGPATH_W) '$(srcdir)/test-resolver.c'; fi` wocky_dummy_xmpp_server-wocky-dummy-xmpp-server.o: wocky-dummy-xmpp-server.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(wocky_dummy_xmpp_server_CFLAGS) $(CFLAGS) -MT wocky_dummy_xmpp_server-wocky-dummy-xmpp-server.o -MD -MP -MF $(DEPDIR)/wocky_dummy_xmpp_server-wocky-dummy-xmpp-server.Tpo -c -o wocky_dummy_xmpp_server-wocky-dummy-xmpp-server.o `test -f 'wocky-dummy-xmpp-server.c' || echo '$(srcdir)/'`wocky-dummy-xmpp-server.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/wocky_dummy_xmpp_server-wocky-dummy-xmpp-server.Tpo $(DEPDIR)/wocky_dummy_xmpp_server-wocky-dummy-xmpp-server.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='wocky-dummy-xmpp-server.c' object='wocky_dummy_xmpp_server-wocky-dummy-xmpp-server.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(wocky_dummy_xmpp_server_CFLAGS) $(CFLAGS) -c -o wocky_dummy_xmpp_server-wocky-dummy-xmpp-server.o `test -f 'wocky-dummy-xmpp-server.c' || echo '$(srcdir)/'`wocky-dummy-xmpp-server.c wocky_dummy_xmpp_server-wocky-dummy-xmpp-server.obj: wocky-dummy-xmpp-server.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(wocky_dummy_xmpp_server_CFLAGS) $(CFLAGS) -MT wocky_dummy_xmpp_server-wocky-dummy-xmpp-server.obj -MD -MP -MF $(DEPDIR)/wocky_dummy_xmpp_server-wocky-dummy-xmpp-server.Tpo -c -o wocky_dummy_xmpp_server-wocky-dummy-xmpp-server.obj `if test -f 'wocky-dummy-xmpp-server.c'; then $(CYGPATH_W) 'wocky-dummy-xmpp-server.c'; else $(CYGPATH_W) '$(srcdir)/wocky-dummy-xmpp-server.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/wocky_dummy_xmpp_server-wocky-dummy-xmpp-server.Tpo $(DEPDIR)/wocky_dummy_xmpp_server-wocky-dummy-xmpp-server.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='wocky-dummy-xmpp-server.c' object='wocky_dummy_xmpp_server-wocky-dummy-xmpp-server.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(wocky_dummy_xmpp_server_CFLAGS) $(CFLAGS) -c -o wocky_dummy_xmpp_server-wocky-dummy-xmpp-server.obj `if test -f 'wocky-dummy-xmpp-server.c'; then $(CYGPATH_W) 'wocky-dummy-xmpp-server.c'; else $(CYGPATH_W) '$(srcdir)/wocky-dummy-xmpp-server.c'; fi` wocky_dummy_xmpp_server-wocky-test-connector-server.o: wocky-test-connector-server.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(wocky_dummy_xmpp_server_CFLAGS) $(CFLAGS) -MT wocky_dummy_xmpp_server-wocky-test-connector-server.o -MD -MP -MF $(DEPDIR)/wocky_dummy_xmpp_server-wocky-test-connector-server.Tpo -c -o wocky_dummy_xmpp_server-wocky-test-connector-server.o `test -f 'wocky-test-connector-server.c' || echo '$(srcdir)/'`wocky-test-connector-server.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/wocky_dummy_xmpp_server-wocky-test-connector-server.Tpo $(DEPDIR)/wocky_dummy_xmpp_server-wocky-test-connector-server.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='wocky-test-connector-server.c' object='wocky_dummy_xmpp_server-wocky-test-connector-server.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(wocky_dummy_xmpp_server_CFLAGS) $(CFLAGS) -c -o wocky_dummy_xmpp_server-wocky-test-connector-server.o `test -f 'wocky-test-connector-server.c' || echo '$(srcdir)/'`wocky-test-connector-server.c wocky_dummy_xmpp_server-wocky-test-connector-server.obj: wocky-test-connector-server.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(wocky_dummy_xmpp_server_CFLAGS) $(CFLAGS) -MT wocky_dummy_xmpp_server-wocky-test-connector-server.obj -MD -MP -MF $(DEPDIR)/wocky_dummy_xmpp_server-wocky-test-connector-server.Tpo -c -o wocky_dummy_xmpp_server-wocky-test-connector-server.obj `if test -f 'wocky-test-connector-server.c'; then $(CYGPATH_W) 'wocky-test-connector-server.c'; else $(CYGPATH_W) '$(srcdir)/wocky-test-connector-server.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/wocky_dummy_xmpp_server-wocky-test-connector-server.Tpo $(DEPDIR)/wocky_dummy_xmpp_server-wocky-test-connector-server.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='wocky-test-connector-server.c' object='wocky_dummy_xmpp_server-wocky-test-connector-server.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(wocky_dummy_xmpp_server_CFLAGS) $(CFLAGS) -c -o wocky_dummy_xmpp_server-wocky-test-connector-server.obj `if test -f 'wocky-test-connector-server.c'; then $(CYGPATH_W) 'wocky-test-connector-server.c'; else $(CYGPATH_W) '$(srcdir)/wocky-test-connector-server.c'; fi` wocky_dummy_xmpp_server-wocky-test-helper.o: wocky-test-helper.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(wocky_dummy_xmpp_server_CFLAGS) $(CFLAGS) -MT wocky_dummy_xmpp_server-wocky-test-helper.o -MD -MP -MF $(DEPDIR)/wocky_dummy_xmpp_server-wocky-test-helper.Tpo -c -o wocky_dummy_xmpp_server-wocky-test-helper.o `test -f 'wocky-test-helper.c' || echo '$(srcdir)/'`wocky-test-helper.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/wocky_dummy_xmpp_server-wocky-test-helper.Tpo $(DEPDIR)/wocky_dummy_xmpp_server-wocky-test-helper.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='wocky-test-helper.c' object='wocky_dummy_xmpp_server-wocky-test-helper.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(wocky_dummy_xmpp_server_CFLAGS) $(CFLAGS) -c -o wocky_dummy_xmpp_server-wocky-test-helper.o `test -f 'wocky-test-helper.c' || echo '$(srcdir)/'`wocky-test-helper.c wocky_dummy_xmpp_server-wocky-test-helper.obj: wocky-test-helper.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(wocky_dummy_xmpp_server_CFLAGS) $(CFLAGS) -MT wocky_dummy_xmpp_server-wocky-test-helper.obj -MD -MP -MF $(DEPDIR)/wocky_dummy_xmpp_server-wocky-test-helper.Tpo -c -o wocky_dummy_xmpp_server-wocky-test-helper.obj `if test -f 'wocky-test-helper.c'; then $(CYGPATH_W) 'wocky-test-helper.c'; else $(CYGPATH_W) '$(srcdir)/wocky-test-helper.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/wocky_dummy_xmpp_server-wocky-test-helper.Tpo $(DEPDIR)/wocky_dummy_xmpp_server-wocky-test-helper.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='wocky-test-helper.c' object='wocky_dummy_xmpp_server-wocky-test-helper.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(wocky_dummy_xmpp_server_CFLAGS) $(CFLAGS) -c -o wocky_dummy_xmpp_server-wocky-test-helper.obj `if test -f 'wocky-test-helper.c'; then $(CYGPATH_W) 'wocky-test-helper.c'; else $(CYGPATH_W) '$(srcdir)/wocky-test-helper.c'; fi` wocky_dummy_xmpp_server-wocky-test-stream.o: wocky-test-stream.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(wocky_dummy_xmpp_server_CFLAGS) $(CFLAGS) -MT wocky_dummy_xmpp_server-wocky-test-stream.o -MD -MP -MF $(DEPDIR)/wocky_dummy_xmpp_server-wocky-test-stream.Tpo -c -o wocky_dummy_xmpp_server-wocky-test-stream.o `test -f 'wocky-test-stream.c' || echo '$(srcdir)/'`wocky-test-stream.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/wocky_dummy_xmpp_server-wocky-test-stream.Tpo $(DEPDIR)/wocky_dummy_xmpp_server-wocky-test-stream.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='wocky-test-stream.c' object='wocky_dummy_xmpp_server-wocky-test-stream.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(wocky_dummy_xmpp_server_CFLAGS) $(CFLAGS) -c -o wocky_dummy_xmpp_server-wocky-test-stream.o `test -f 'wocky-test-stream.c' || echo '$(srcdir)/'`wocky-test-stream.c wocky_dummy_xmpp_server-wocky-test-stream.obj: wocky-test-stream.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(wocky_dummy_xmpp_server_CFLAGS) $(CFLAGS) -MT wocky_dummy_xmpp_server-wocky-test-stream.obj -MD -MP -MF $(DEPDIR)/wocky_dummy_xmpp_server-wocky-test-stream.Tpo -c -o wocky_dummy_xmpp_server-wocky-test-stream.obj `if test -f 'wocky-test-stream.c'; then $(CYGPATH_W) 'wocky-test-stream.c'; else $(CYGPATH_W) '$(srcdir)/wocky-test-stream.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/wocky_dummy_xmpp_server-wocky-test-stream.Tpo $(DEPDIR)/wocky_dummy_xmpp_server-wocky-test-stream.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='wocky-test-stream.c' object='wocky_dummy_xmpp_server-wocky-test-stream.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(wocky_dummy_xmpp_server_CFLAGS) $(CFLAGS) -c -o wocky_dummy_xmpp_server-wocky-test-stream.obj `if test -f 'wocky-test-stream.c'; then $(CYGPATH_W) 'wocky-test-stream.c'; else $(CYGPATH_W) '$(srcdir)/wocky-test-stream.c'; fi` wocky_dummy_xmpp_server-wocky-test-sasl-auth-server.o: wocky-test-sasl-auth-server.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(wocky_dummy_xmpp_server_CFLAGS) $(CFLAGS) -MT wocky_dummy_xmpp_server-wocky-test-sasl-auth-server.o -MD -MP -MF $(DEPDIR)/wocky_dummy_xmpp_server-wocky-test-sasl-auth-server.Tpo -c -o wocky_dummy_xmpp_server-wocky-test-sasl-auth-server.o `test -f 'wocky-test-sasl-auth-server.c' || echo '$(srcdir)/'`wocky-test-sasl-auth-server.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/wocky_dummy_xmpp_server-wocky-test-sasl-auth-server.Tpo $(DEPDIR)/wocky_dummy_xmpp_server-wocky-test-sasl-auth-server.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='wocky-test-sasl-auth-server.c' object='wocky_dummy_xmpp_server-wocky-test-sasl-auth-server.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(wocky_dummy_xmpp_server_CFLAGS) $(CFLAGS) -c -o wocky_dummy_xmpp_server-wocky-test-sasl-auth-server.o `test -f 'wocky-test-sasl-auth-server.c' || echo '$(srcdir)/'`wocky-test-sasl-auth-server.c wocky_dummy_xmpp_server-wocky-test-sasl-auth-server.obj: wocky-test-sasl-auth-server.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(wocky_dummy_xmpp_server_CFLAGS) $(CFLAGS) -MT wocky_dummy_xmpp_server-wocky-test-sasl-auth-server.obj -MD -MP -MF $(DEPDIR)/wocky_dummy_xmpp_server-wocky-test-sasl-auth-server.Tpo -c -o wocky_dummy_xmpp_server-wocky-test-sasl-auth-server.obj `if test -f 'wocky-test-sasl-auth-server.c'; then $(CYGPATH_W) 'wocky-test-sasl-auth-server.c'; else $(CYGPATH_W) '$(srcdir)/wocky-test-sasl-auth-server.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/wocky_dummy_xmpp_server-wocky-test-sasl-auth-server.Tpo $(DEPDIR)/wocky_dummy_xmpp_server-wocky-test-sasl-auth-server.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='wocky-test-sasl-auth-server.c' object='wocky_dummy_xmpp_server-wocky-test-sasl-auth-server.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(wocky_dummy_xmpp_server_CFLAGS) $(CFLAGS) -c -o wocky_dummy_xmpp_server-wocky-test-sasl-auth-server.obj `if test -f 'wocky-test-sasl-auth-server.c'; then $(CYGPATH_W) 'wocky-test-sasl-auth-server.c'; else $(CYGPATH_W) '$(srcdir)/wocky-test-sasl-auth-server.c'; fi` wocky_test_sasl_auth-wocky-test-helper.o: wocky-test-helper.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(wocky_test_sasl_auth_CFLAGS) $(CFLAGS) -MT wocky_test_sasl_auth-wocky-test-helper.o -MD -MP -MF $(DEPDIR)/wocky_test_sasl_auth-wocky-test-helper.Tpo -c -o wocky_test_sasl_auth-wocky-test-helper.o `test -f 'wocky-test-helper.c' || echo '$(srcdir)/'`wocky-test-helper.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/wocky_test_sasl_auth-wocky-test-helper.Tpo $(DEPDIR)/wocky_test_sasl_auth-wocky-test-helper.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='wocky-test-helper.c' object='wocky_test_sasl_auth-wocky-test-helper.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(wocky_test_sasl_auth_CFLAGS) $(CFLAGS) -c -o wocky_test_sasl_auth-wocky-test-helper.o `test -f 'wocky-test-helper.c' || echo '$(srcdir)/'`wocky-test-helper.c wocky_test_sasl_auth-wocky-test-helper.obj: wocky-test-helper.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(wocky_test_sasl_auth_CFLAGS) $(CFLAGS) -MT wocky_test_sasl_auth-wocky-test-helper.obj -MD -MP -MF $(DEPDIR)/wocky_test_sasl_auth-wocky-test-helper.Tpo -c -o wocky_test_sasl_auth-wocky-test-helper.obj `if test -f 'wocky-test-helper.c'; then $(CYGPATH_W) 'wocky-test-helper.c'; else $(CYGPATH_W) '$(srcdir)/wocky-test-helper.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/wocky_test_sasl_auth-wocky-test-helper.Tpo $(DEPDIR)/wocky_test_sasl_auth-wocky-test-helper.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='wocky-test-helper.c' object='wocky_test_sasl_auth-wocky-test-helper.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(wocky_test_sasl_auth_CFLAGS) $(CFLAGS) -c -o wocky_test_sasl_auth-wocky-test-helper.obj `if test -f 'wocky-test-helper.c'; then $(CYGPATH_W) 'wocky-test-helper.c'; else $(CYGPATH_W) '$(srcdir)/wocky-test-helper.c'; fi` wocky_test_sasl_auth-wocky-test-sasl-auth.o: wocky-test-sasl-auth.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(wocky_test_sasl_auth_CFLAGS) $(CFLAGS) -MT wocky_test_sasl_auth-wocky-test-sasl-auth.o -MD -MP -MF $(DEPDIR)/wocky_test_sasl_auth-wocky-test-sasl-auth.Tpo -c -o wocky_test_sasl_auth-wocky-test-sasl-auth.o `test -f 'wocky-test-sasl-auth.c' || echo '$(srcdir)/'`wocky-test-sasl-auth.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/wocky_test_sasl_auth-wocky-test-sasl-auth.Tpo $(DEPDIR)/wocky_test_sasl_auth-wocky-test-sasl-auth.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='wocky-test-sasl-auth.c' object='wocky_test_sasl_auth-wocky-test-sasl-auth.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(wocky_test_sasl_auth_CFLAGS) $(CFLAGS) -c -o wocky_test_sasl_auth-wocky-test-sasl-auth.o `test -f 'wocky-test-sasl-auth.c' || echo '$(srcdir)/'`wocky-test-sasl-auth.c wocky_test_sasl_auth-wocky-test-sasl-auth.obj: wocky-test-sasl-auth.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(wocky_test_sasl_auth_CFLAGS) $(CFLAGS) -MT wocky_test_sasl_auth-wocky-test-sasl-auth.obj -MD -MP -MF $(DEPDIR)/wocky_test_sasl_auth-wocky-test-sasl-auth.Tpo -c -o wocky_test_sasl_auth-wocky-test-sasl-auth.obj `if test -f 'wocky-test-sasl-auth.c'; then $(CYGPATH_W) 'wocky-test-sasl-auth.c'; else $(CYGPATH_W) '$(srcdir)/wocky-test-sasl-auth.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/wocky_test_sasl_auth-wocky-test-sasl-auth.Tpo $(DEPDIR)/wocky_test_sasl_auth-wocky-test-sasl-auth.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='wocky-test-sasl-auth.c' object='wocky_test_sasl_auth-wocky-test-sasl-auth.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(wocky_test_sasl_auth_CFLAGS) $(CFLAGS) -c -o wocky_test_sasl_auth-wocky-test-sasl-auth.obj `if test -f 'wocky-test-sasl-auth.c'; then $(CYGPATH_W) 'wocky-test-sasl-auth.c'; else $(CYGPATH_W) '$(srcdir)/wocky-test-sasl-auth.c'; fi` wocky_test_sasl_auth-wocky-test-sasl-handler.o: wocky-test-sasl-handler.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(wocky_test_sasl_auth_CFLAGS) $(CFLAGS) -MT wocky_test_sasl_auth-wocky-test-sasl-handler.o -MD -MP -MF $(DEPDIR)/wocky_test_sasl_auth-wocky-test-sasl-handler.Tpo -c -o wocky_test_sasl_auth-wocky-test-sasl-handler.o `test -f 'wocky-test-sasl-handler.c' || echo '$(srcdir)/'`wocky-test-sasl-handler.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/wocky_test_sasl_auth-wocky-test-sasl-handler.Tpo $(DEPDIR)/wocky_test_sasl_auth-wocky-test-sasl-handler.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='wocky-test-sasl-handler.c' object='wocky_test_sasl_auth-wocky-test-sasl-handler.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(wocky_test_sasl_auth_CFLAGS) $(CFLAGS) -c -o wocky_test_sasl_auth-wocky-test-sasl-handler.o `test -f 'wocky-test-sasl-handler.c' || echo '$(srcdir)/'`wocky-test-sasl-handler.c wocky_test_sasl_auth-wocky-test-sasl-handler.obj: wocky-test-sasl-handler.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(wocky_test_sasl_auth_CFLAGS) $(CFLAGS) -MT wocky_test_sasl_auth-wocky-test-sasl-handler.obj -MD -MP -MF $(DEPDIR)/wocky_test_sasl_auth-wocky-test-sasl-handler.Tpo -c -o wocky_test_sasl_auth-wocky-test-sasl-handler.obj `if test -f 'wocky-test-sasl-handler.c'; then $(CYGPATH_W) 'wocky-test-sasl-handler.c'; else $(CYGPATH_W) '$(srcdir)/wocky-test-sasl-handler.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/wocky_test_sasl_auth-wocky-test-sasl-handler.Tpo $(DEPDIR)/wocky_test_sasl_auth-wocky-test-sasl-handler.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='wocky-test-sasl-handler.c' object='wocky_test_sasl_auth-wocky-test-sasl-handler.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(wocky_test_sasl_auth_CFLAGS) $(CFLAGS) -c -o wocky_test_sasl_auth-wocky-test-sasl-handler.obj `if test -f 'wocky-test-sasl-handler.c'; then $(CYGPATH_W) 'wocky-test-sasl-handler.c'; else $(CYGPATH_W) '$(srcdir)/wocky-test-sasl-handler.c'; fi` wocky_test_sasl_auth-wocky-test-sasl-auth-server.o: wocky-test-sasl-auth-server.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(wocky_test_sasl_auth_CFLAGS) $(CFLAGS) -MT wocky_test_sasl_auth-wocky-test-sasl-auth-server.o -MD -MP -MF $(DEPDIR)/wocky_test_sasl_auth-wocky-test-sasl-auth-server.Tpo -c -o wocky_test_sasl_auth-wocky-test-sasl-auth-server.o `test -f 'wocky-test-sasl-auth-server.c' || echo '$(srcdir)/'`wocky-test-sasl-auth-server.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/wocky_test_sasl_auth-wocky-test-sasl-auth-server.Tpo $(DEPDIR)/wocky_test_sasl_auth-wocky-test-sasl-auth-server.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='wocky-test-sasl-auth-server.c' object='wocky_test_sasl_auth-wocky-test-sasl-auth-server.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(wocky_test_sasl_auth_CFLAGS) $(CFLAGS) -c -o wocky_test_sasl_auth-wocky-test-sasl-auth-server.o `test -f 'wocky-test-sasl-auth-server.c' || echo '$(srcdir)/'`wocky-test-sasl-auth-server.c wocky_test_sasl_auth-wocky-test-sasl-auth-server.obj: wocky-test-sasl-auth-server.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(wocky_test_sasl_auth_CFLAGS) $(CFLAGS) -MT wocky_test_sasl_auth-wocky-test-sasl-auth-server.obj -MD -MP -MF $(DEPDIR)/wocky_test_sasl_auth-wocky-test-sasl-auth-server.Tpo -c -o wocky_test_sasl_auth-wocky-test-sasl-auth-server.obj `if test -f 'wocky-test-sasl-auth-server.c'; then $(CYGPATH_W) 'wocky-test-sasl-auth-server.c'; else $(CYGPATH_W) '$(srcdir)/wocky-test-sasl-auth-server.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/wocky_test_sasl_auth-wocky-test-sasl-auth-server.Tpo $(DEPDIR)/wocky_test_sasl_auth-wocky-test-sasl-auth-server.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='wocky-test-sasl-auth-server.c' object='wocky_test_sasl_auth-wocky-test-sasl-auth-server.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(wocky_test_sasl_auth_CFLAGS) $(CFLAGS) -c -o wocky_test_sasl_auth-wocky-test-sasl-auth-server.obj `if test -f 'wocky-test-sasl-auth-server.c'; then $(CYGPATH_W) 'wocky-test-sasl-auth-server.c'; else $(CYGPATH_W) '$(srcdir)/wocky-test-sasl-auth-server.c'; fi` wocky_test_sasl_auth-wocky-test-stream.o: wocky-test-stream.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(wocky_test_sasl_auth_CFLAGS) $(CFLAGS) -MT wocky_test_sasl_auth-wocky-test-stream.o -MD -MP -MF $(DEPDIR)/wocky_test_sasl_auth-wocky-test-stream.Tpo -c -o wocky_test_sasl_auth-wocky-test-stream.o `test -f 'wocky-test-stream.c' || echo '$(srcdir)/'`wocky-test-stream.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/wocky_test_sasl_auth-wocky-test-stream.Tpo $(DEPDIR)/wocky_test_sasl_auth-wocky-test-stream.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='wocky-test-stream.c' object='wocky_test_sasl_auth-wocky-test-stream.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(wocky_test_sasl_auth_CFLAGS) $(CFLAGS) -c -o wocky_test_sasl_auth-wocky-test-stream.o `test -f 'wocky-test-stream.c' || echo '$(srcdir)/'`wocky-test-stream.c wocky_test_sasl_auth-wocky-test-stream.obj: wocky-test-stream.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(wocky_test_sasl_auth_CFLAGS) $(CFLAGS) -MT wocky_test_sasl_auth-wocky-test-stream.obj -MD -MP -MF $(DEPDIR)/wocky_test_sasl_auth-wocky-test-stream.Tpo -c -o wocky_test_sasl_auth-wocky-test-stream.obj `if test -f 'wocky-test-stream.c'; then $(CYGPATH_W) 'wocky-test-stream.c'; else $(CYGPATH_W) '$(srcdir)/wocky-test-stream.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/wocky_test_sasl_auth-wocky-test-stream.Tpo $(DEPDIR)/wocky_test_sasl_auth-wocky-test-stream.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='wocky-test-stream.c' object='wocky_test_sasl_auth-wocky-test-stream.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(wocky_test_sasl_auth_CFLAGS) $(CFLAGS) -c -o wocky_test_sasl_auth-wocky-test-stream.obj `if test -f 'wocky-test-stream.c'; then $(CYGPATH_W) 'wocky-test-stream.c'; else $(CYGPATH_W) '$(srcdir)/wocky-test-stream.c'; fi` wocky_tls_test-wocky-tls-test.o: wocky-tls-test.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(wocky_tls_test_CFLAGS) $(CFLAGS) -MT wocky_tls_test-wocky-tls-test.o -MD -MP -MF $(DEPDIR)/wocky_tls_test-wocky-tls-test.Tpo -c -o wocky_tls_test-wocky-tls-test.o `test -f 'wocky-tls-test.c' || echo '$(srcdir)/'`wocky-tls-test.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/wocky_tls_test-wocky-tls-test.Tpo $(DEPDIR)/wocky_tls_test-wocky-tls-test.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='wocky-tls-test.c' object='wocky_tls_test-wocky-tls-test.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(wocky_tls_test_CFLAGS) $(CFLAGS) -c -o wocky_tls_test-wocky-tls-test.o `test -f 'wocky-tls-test.c' || echo '$(srcdir)/'`wocky-tls-test.c wocky_tls_test-wocky-tls-test.obj: wocky-tls-test.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(wocky_tls_test_CFLAGS) $(CFLAGS) -MT wocky_tls_test-wocky-tls-test.obj -MD -MP -MF $(DEPDIR)/wocky_tls_test-wocky-tls-test.Tpo -c -o wocky_tls_test-wocky-tls-test.obj `if test -f 'wocky-tls-test.c'; then $(CYGPATH_W) 'wocky-tls-test.c'; else $(CYGPATH_W) '$(srcdir)/wocky-tls-test.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/wocky_tls_test-wocky-tls-test.Tpo $(DEPDIR)/wocky_tls_test-wocky-tls-test.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='wocky-tls-test.c' object='wocky_tls_test-wocky-tls-test.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(wocky_tls_test_CFLAGS) $(CFLAGS) -c -o wocky_tls_test-wocky-tls-test.obj `if test -f 'wocky-tls-test.c'; then $(CYGPATH_W) 'wocky-tls-test.c'; else $(CYGPATH_W) '$(srcdir)/wocky-tls-test.c'; fi` wocky_tls_test-wocky-test-helper.o: wocky-test-helper.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(wocky_tls_test_CFLAGS) $(CFLAGS) -MT wocky_tls_test-wocky-test-helper.o -MD -MP -MF $(DEPDIR)/wocky_tls_test-wocky-test-helper.Tpo -c -o wocky_tls_test-wocky-test-helper.o `test -f 'wocky-test-helper.c' || echo '$(srcdir)/'`wocky-test-helper.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/wocky_tls_test-wocky-test-helper.Tpo $(DEPDIR)/wocky_tls_test-wocky-test-helper.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='wocky-test-helper.c' object='wocky_tls_test-wocky-test-helper.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(wocky_tls_test_CFLAGS) $(CFLAGS) -c -o wocky_tls_test-wocky-test-helper.o `test -f 'wocky-test-helper.c' || echo '$(srcdir)/'`wocky-test-helper.c wocky_tls_test-wocky-test-helper.obj: wocky-test-helper.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(wocky_tls_test_CFLAGS) $(CFLAGS) -MT wocky_tls_test-wocky-test-helper.obj -MD -MP -MF $(DEPDIR)/wocky_tls_test-wocky-test-helper.Tpo -c -o wocky_tls_test-wocky-test-helper.obj `if test -f 'wocky-test-helper.c'; then $(CYGPATH_W) 'wocky-test-helper.c'; else $(CYGPATH_W) '$(srcdir)/wocky-test-helper.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/wocky_tls_test-wocky-test-helper.Tpo $(DEPDIR)/wocky_tls_test-wocky-test-helper.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='wocky-test-helper.c' object='wocky_tls_test-wocky-test-helper.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(wocky_tls_test_CFLAGS) $(CFLAGS) -c -o wocky_tls_test-wocky-test-helper.obj `if test -f 'wocky-test-helper.c'; then $(CYGPATH_W) 'wocky-test-helper.c'; else $(CYGPATH_W) '$(srcdir)/wocky-test-helper.c'; fi` wocky_tls_test-wocky-test-stream.o: wocky-test-stream.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(wocky_tls_test_CFLAGS) $(CFLAGS) -MT wocky_tls_test-wocky-test-stream.o -MD -MP -MF $(DEPDIR)/wocky_tls_test-wocky-test-stream.Tpo -c -o wocky_tls_test-wocky-test-stream.o `test -f 'wocky-test-stream.c' || echo '$(srcdir)/'`wocky-test-stream.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/wocky_tls_test-wocky-test-stream.Tpo $(DEPDIR)/wocky_tls_test-wocky-test-stream.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='wocky-test-stream.c' object='wocky_tls_test-wocky-test-stream.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(wocky_tls_test_CFLAGS) $(CFLAGS) -c -o wocky_tls_test-wocky-test-stream.o `test -f 'wocky-test-stream.c' || echo '$(srcdir)/'`wocky-test-stream.c wocky_tls_test-wocky-test-stream.obj: wocky-test-stream.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(wocky_tls_test_CFLAGS) $(CFLAGS) -MT wocky_tls_test-wocky-test-stream.obj -MD -MP -MF $(DEPDIR)/wocky_tls_test-wocky-test-stream.Tpo -c -o wocky_tls_test-wocky-test-stream.obj `if test -f 'wocky-test-stream.c'; then $(CYGPATH_W) 'wocky-test-stream.c'; else $(CYGPATH_W) '$(srcdir)/wocky-test-stream.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/wocky_tls_test-wocky-test-stream.Tpo $(DEPDIR)/wocky_tls_test-wocky-test-stream.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='wocky-test-stream.c' object='wocky_tls_test-wocky-test-stream.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(wocky_tls_test_CFLAGS) $(CFLAGS) -c -o wocky_tls_test-wocky-test-stream.obj `if test -f 'wocky-test-stream.c'; then $(CYGPATH_W) 'wocky-test-stream.c'; else $(CYGPATH_W) '$(srcdir)/wocky-test-stream.c'; fi` mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs ID: $(am__tagged_files) $(am__define_uniq_tagged_files); mkid -fID $$unique tags: tags-am TAGS: tags tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) set x; \ here=`pwd`; \ $(am__define_uniq_tagged_files); \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ if test $$# -gt 0; then \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ "$$@" $$unique; \ else \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ $$unique; \ fi; \ fi ctags: ctags-am CTAGS: ctags ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) $(am__define_uniq_tagged_files); \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" cscopelist: cscopelist-am cscopelist-am: $(am__tagged_files) list='$(am__tagged_files)'; \ case "$(srcdir)" in \ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ *) sdir=$(subdir)/$(srcdir) ;; \ esac; \ for i in $$list; do \ if test -f "$$i"; then \ echo "$(subdir)/$$i"; \ else \ echo "$$sdir/$$i"; \ fi; \ done >> $(top_builddir)/cscope.files distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags distdir: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done check-am: all-am $(MAKE) $(AM_MAKEFLAGS) $(check_PROGRAMS) $(MAKE) $(AM_MAKEFLAGS) check-local check: check-am all-am: Makefile $(PROGRAMS) installdirs: install: install-am install-exec: install-exec-am install-data: install-data-am uninstall: uninstall-am install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-am install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: -test -z "$(MOSTLYCLEANFILES)" || rm -f $(MOSTLYCLEANFILES) clean-generic: -test -z "$(CLEANFILES)" || rm -f $(CLEANFILES) distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." clean: clean-am clean-am: clean-checkPROGRAMS clean-generic clean-libtool \ clean-noinstPROGRAMS mostlyclean-am distclean: distclean-am -rm -rf ./$(DEPDIR) -rm -f Makefile distclean-am: clean-am distclean-compile distclean-generic \ distclean-tags dvi: dvi-am dvi-am: html: html-am html-am: info: info-am info-am: install-data-am: install-dvi: install-dvi-am install-dvi-am: install-exec-am: install-html: install-html-am install-html-am: install-info: install-info-am install-info-am: install-man: install-pdf: install-pdf-am install-pdf-am: install-ps: install-ps-am install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-am -rm -rf ./$(DEPDIR) -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-am mostlyclean-am: mostlyclean-compile mostlyclean-generic \ mostlyclean-libtool pdf: pdf-am pdf-am: ps: ps-am ps-am: uninstall-am: .MAKE: check-am install-am install-strip .PHONY: CTAGS GTAGS TAGS all all-am check check-am check-local clean \ clean-checkPROGRAMS clean-generic clean-libtool \ clean-noinstPROGRAMS cscopelist-am ctags ctags-am distclean \ distclean-compile distclean-generic distclean-libtool \ distclean-tags distdir dvi dvi-am html html-am info info-am \ install install-am install-data install-data-am install-dvi \ install-dvi-am install-exec install-exec-am install-html \ install-html-am install-info install-info-am install-man \ install-pdf install-pdf-am install-ps install-ps-am \ install-strip installcheck installcheck-am installdirs \ maintainer-clean maintainer-clean-generic mostlyclean \ mostlyclean-compile mostlyclean-generic mostlyclean-libtool \ pdf pdf-am ps ps-am tags tags-am uninstall uninstall-am test-report: test-report.xml gtester-report $(top_builddir)/tests/$@.xml > \ $(top_builddir)/tests/$@.html test-report.xml: ${TEST_PROGS} test test: ${TEST_PROGS} gtester -o test-report.xml -k --verbose ${TEST_PROGS} @if [ -x $(which python) ] ; then \ $(SUMMARY) $@-report.xml ; \ else \ echo "No python available, not summarizing test results" ; \ fi test-%: wocky-%-test gtester -o $@-report.xml -k --verbose $< @if [ -x $(which python) ] ; then \ $(SUMMARY) $@-report.xml ; \ else \ echo "No python available, not summarizing test results" ; \ fi .PHONY: test test-report check-coding-style: @fail=0; \ if test -n "$(check_misc_sources)"; then \ tools_dir=$(top_srcdir)/tools \ sh $(top_srcdir)/tools/check-misc.sh \ $(addprefix $(srcdir)/,$(check_misc_sources)) || fail=1; \ fi; \ if test -n "$(check_c_sources)"; then \ tools_dir=$(top_srcdir)/tools \ sh $(top_srcdir)/tools/check-c-style.sh \ $(addprefix $(srcdir)/,$(check_c_sources)) || fail=1; \ fi;\ if test yes = "$(ENABLE_CODING_STYLE_CHECKS)"; then \ exit "$$fail";\ else \ exit 0;\ fi check-local: test check-coding-style # valgrind any given test by running make test.valgrind %.valgrind: % G_SLICE=always-malloc \ G_DEBUG=gc-friendly \ valgrind -q \ $(foreach s,$(SUPPRESSIONS),--suppressions=$(srcdir)/$(s)) \ --tool=memcheck --leak-check=full --trace-children=yes \ --leak-resolution=high --num-callers=20 \ ./$* $(TEST_ARGS) 2>&1 | tee "valgrind.$*.log" @if grep "==" "valgrind.$*.log" > /dev/null 2>&1; then \ exit 1; \ fi valgrind: $(TEST_PROGS) @echo "Valgrinding tests ..." @failed=0; \ for t in $(filter-out $(VALGRIND_TESTS_DISABLE),$(TEST_PROGS)); do \ $(MAKE) $$t.valgrind; \ if test "$$?" -ne 0; then \ echo "Valgrind error for test $$t"; \ failed=`expr $$failed + 1`; \ whicht="$$whicht $$t"; \ fi; \ done; \ if test "$$failed" -ne 0; then \ echo "$$failed tests had leaks or errors under valgrind:"; \ echo "$$whicht"; \ false; \ fi # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: telepathy-gabble-0.18.2/lib/ext/wocky/tests/certs/0000755000175000017500000000000012312537050021777 5ustar00smcvsmcv00000000000000telepathy-gabble-0.18.2/lib/ext/wocky/tests/certs/wild-key.pem0000644000175000017500000001460312200204546024230 0ustar00smcvsmcv00000000000000Public Key Info: Public Key Algorithm: RSA Key Security Level: Normal modulus: 00:e2:d3:86:c4:99:5f:f3:a8:9a:c9:66:85:f6:cb: 43:84:0b:fa:b2:d7:7c:05:4d:ff:5e:c9:65:1e:16: 45:85:9e:5f:a2:59:50:73:52:31:82:76:07:df:09: 66:d5:3f:69:11:dd:35:5e:3a:a2:be:b2:fb:45:03: d4:01:42:e7:8d:f0:a7:d2:9e:b5:c2:db:61:a5:f6: 2e:cc:5c:ef:4b:28:06:d7:db:e8:bd:36:4d:06:da: 31:fd:aa:f3:8f:d8:5b:1c:0e:e7:10:84:1b:5d:0a: 18:b4:6b:80:06:f1:aa:d3:2f:60:87:46:5e:cb:47: f5:30:c2:af:a2:da:e8:8a:9e:a2:ae:d1:ff:87:41: 80:5d:f4:85:54:ec:3a:76:a2:ed:6f:98:42:e3:d3: d7:c4:25:5f:53:bb:1d:d6:9f:7d:70:c6:64:f0:bf: fb:43:d8:cc:f7:6b:da:f1:e5:4b:38:9f:34:e9:ed: b4:f6:6e:a3:80:0b:04:4d:e1:85:fe:31:9f:18:79: df:5c:1d:55:08:6c:4b:a5:93:5f:94:61:15:4f:68: c7:85:ac:1b:b4:37:cb:9b:50:25:0e:f6:01:13:d6: a9:56:62:2b:55:89:20:3b:84:56:5a:56:3a:eb:44: 77:53:1c:23:71:95:32:e6:6d:f3:43:d9:68:90:b7: b8:9b:3b:7f:a9:79:5e:ec:7d:c9:4a:c4:e7:3b:73: 83:7e:fc:c3:91:e2:e9:1b:77:0b:4a:cb:8c:7c:e7: ef:c3:22:0b:a0:c6:95:c1:b9:28:c3:81:f8:ee:30: 80:5c:c7:09:27: public exponent: 01:00:01: private exponent: 55:09:ac:8a:e4:5d:7e:c2:05:55:e0:63:f5:04:7b: 89:73:dc:47:54:56:20:be:ff:30:90:1c:a2:cd:02: bf:77:82:af:21:00:f2:5b:2c:48:96:eb:98:88:b3: ce:da:f9:0f:43:79:90:9c:37:0b:7b:9a:8c:63:45: 06:3c:09:07:c3:e3:87:29:0d:47:c2:6b:b7:86:b6: 40:d3:ce:ba:c4:84:dc:44:8b:da:f4:12:a8:b1:00: f0:f3:38:61:03:62:15:00:d9:4b:ed:db:3c:64:5e: a2:b5:72:c8:27:ae:3d:82:93:93:e3:a4:02:5e:35: e4:53:f8:f8:fb:4d:17:3b:26:0e:98:98:4e:23:16: 77:23:07:21:b1:76:b1:a7:9f:90:a8:ea:da:f3:3e: ff:3f:b6:be:19:7e:15:b0:3c:7d:75:f6:7d:58:bc: cd:da:ba:4a:ac:b5:ab:91:a9:71:b7:bf:db:0c:ca: ff:7a:72:0e:98:64:78:af:26:fc:07:df:8a:2e:9f: 51:3f:b3:d7:14:9f:1e:5a:0d:73:58:3c:37:67:5e: 4e:6c:f4:bd:e4:58:ae:aa:bd:eb:68:e3:ed:9c:78: af:de:80:9b:6a:93:01:ce:8e:b9:1b:ea:c4:cc:a8: df:55:8c:fb:27:6b:c1:64:9b:ed:48:c8:80:86:e3: 2d:a9:67:0a:7e:be:83:c4:aa:33:24:a1:c4:b4:2d: 17:72:6e:45:9f:81:b3:61:34:03:38:a2:6e:48:84: c3:aa:4b:9b:ba:0f:af:c2:2d:e0:b8:55:bb:01:3b: 6e:bb:77:81: prime1: 00:e6:06:0c:d6:72:1a:a9:3e:91:2f:5c:ca:a4:36: 2a:0e:1c:b8:e8:86:e8:c8:8d:f3:01:da:58:1c:5a: fb:71:23:84:66:0f:ec:00:f7:0a:ce:f4:0b:bb:93: 29:fe:50:ce:39:03:ed:0f:67:3e:a2:2d:3e:fa:c3: 1b:bd:cd:59:22:b8:57:35:f3:14:e9:4a:a6:62:22: 99:9b:a3:1a:b4:2b:d3:be:d0:7b:0b:8a:fb:5c:3c: 6e:05:45:58:c4:13:e0:96:33:b7:da:b0:14:dc:fa: 07:57:68:33:ab:a8:ab:1f:26:f6:96:ea:f2:5c:02: ee:99:28:ec:2a:65:f6:6f:6f:4e:5e:65:87:95:b3: 65:70:05:f2:15:37:6f:f7:94:67:19:98:b9:94:f1: 68:d5:47: prime2: 00:fc:71:0a:87:55:5d:b1:46:2a:95:0b:c4:38:ea: 38:71:bc:ce:68:8c:4b:0a:34:cc:50:df:50:d9:84: 1d:e3:9a:29:45:90:36:54:a0:f8:ce:69:82:06:5c: c9:db:e3:2f:90:86:ec:2f:bc:b6:ed:95:72:1c:b8: 1c:58:de:f5:ee:24:99:3c:e5:ab:8b:89:c0:94:c2: 63:a2:c9:2c:b1:7d:ad:f5:f5:fa:e7:04:84:e5:08: a6:c7:8a:46:92:5d:45:0f:b0:e9:b5:9f:a0:6c:43: 72:14:eb:00:e1:9a:af:9d:5e:2f:be:a3:d6:82:b2: c2:db:c9:3d:5a:42:31:a8:8b:26:6e:ee:50:96:48: 57:fc:96:2a:6c:ce:dd:d6:1c:59:22:06:0f:63:c8: 51:9d:21: coefficient: 00:8b:25:6f:8d:58:ca:17:d7:66:73:7d:c1:22:b3: fa:f6:09:b7:2c:b6:95:47:66:d5:17:97:51:cd:21: df:aa:d6:29:41:6d:c2:22:7e:dc:28:8a:a8:ab:fa: ba:c8:84:d3:45:be:9b:1f:8d:b6:c2:c6:17:88:65: 39:19:f4:1f:fc:98:a3:27:ae:4b:c1:3b:b3:b4:d8: 76:89:ec:51:c2:56:bc:53:e7:6c:1f:39:ab:37:97: 03:32:67:c8:15:0c:67:9f:6f:7c:92:3f:b3:12:b1: ec:ef:98:11:65:fd:96:20:5a:27:5d:fc:3b:cb:f0: 7a:c8:be:dd:02:c0:52:36:da:7e:9a:99:32:3f:44: 8e:fe:84:29:5b:44:8c:ea:42:22:18:4b:a9:00:e7: 63:e1:3a: exp1: 00:ae:50:cd:6f:c1:de:e4:7d:2e:c7:46:b9:a6:82: 09:92:d2:3f:6a:af:ba:3f:2a:1b:8b:3e:ed:60:e7: ad:ff:0a:5c:6e:80:08:68:9e:ce:89:11:36:c6:fa: 7a:6b:68:cf:2f:34:03:75:95:d7:48:4d:a3:99:a3: 3c:25:b5:35:a4:73:30:5c:09:0f:c2:cf:b8:91:4c: 09:3e:81:f7:5d:ac:8c:f5:e0:c6:2e:74:2f:92:b1: 94:ea:d7:a7:b2:48:21:fd:91:c9:f5:a7:54:d8:35: 7e:54:c5:f9:ca:4f:b3:06:93:9f:71:b5:30:df:7b: b5:57:4a:44:d6:c8:98:5d:d0:6b:02:6c:4c:b8:ac: cf:e5:7a:0b:ff:fa:18:16:f6:56:bf:76:16:c5:81: c0:e1:47: exp2: 39:1a:b5:20:02:0e:8c:b0:6b:a7:85:9e:e0:a5:13: 19:9f:75:2d:af:36:b6:5b:55:30:a2:23:9e:e0:c5: 06:1c:74:63:99:08:c1:42:1c:a6:4c:b5:ae:0c:c5: 58:e9:5e:74:1a:21:49:77:2e:06:36:7d:36:c3:eb: 1d:ab:6a:04:71:e0:fc:26:94:14:9f:97:a1:cf:0b: 4c:e2:a7:2a:8b:5c:93:bc:0e:02:be:41:f5:b1:6c: 50:cd:15:c3:bc:37:88:22:23:ec:02:ec:fe:30:8f: 98:ed:c4:28:44:9e:9f:41:94:19:52:e4:8e:72:33: a4:16:34:bb:bb:27:92:91:cf:a2:de:63:fa:6a:e9: 04:69:4f:b5:87:62:25:32:0a:73:a8:dd:48:5c:23: 22:21: Public Key ID: 76:93:58:59:EE:CC:7C:85:34:97:25:10:BE:DB:72:40:CC:57:4D:EC -----BEGIN RSA PRIVATE KEY----- MIIFfAIBAAKCATEA4tOGxJlf86iayWaF9stDhAv6std8BU3/XsllHhZFhZ5follQ c1IxgnYH3wlm1T9pEd01XjqivrL7RQPUAULnjfCn0p61wtthpfYuzFzvSygG19vo vTZNBtox/arzj9hbHA7nEIQbXQoYtGuABvGq0y9gh0Zey0f1MMKvotroip6irtH/ h0GAXfSFVOw6dqLtb5hC49PXxCVfU7sd1p99cMZk8L/7Q9jM92va8eVLOJ806e20 9m6jgAsETeGF/jGfGHnfXB1VCGxLpZNflGEVT2jHhawbtDfLm1AlDvYBE9apVmIr VYkgO4RWWlY660R3UxwjcZUy5m3zQ9lokLe4mzt/qXle7H3JSsTnO3ODfvzDkeLp G3cLSsuMfOfvwyILoMaVwbkow4H47jCAXMcJJwIDAQABAoIBMFUJrIrkXX7CBVXg Y/UEe4lz3EdUViC+/zCQHKLNAr93gq8hAPJbLEiW65iIs87a+Q9DeZCcNwt7moxj RQY8CQfD44cpDUfCa7eGtkDTzrrEhNxEi9r0EqixAPDzOGEDYhUA2Uvt2zxkXqK1 csgnrj2Ck5PjpAJeNeRT+Pj7TRc7Jg6YmE4jFncjByGxdrGnn5Co6trzPv8/tr4Z fhWwPH119n1YvM3aukqstauRqXG3v9sMyv96cg6YZHivJvwH34oun1E/s9cUnx5a DXNYPDdnXk5s9L3kWK6qveto4+2ceK/egJtqkwHOjrkb6sTMqN9VjPsna8Fkm+1I yICG4y2pZwp+voPEqjMkocS0LRdybkWfgbNhNAM4om5IhMOqS5u6D6/CLeC4VbsB O267d4ECgZkA5gYM1nIaqT6RL1zKpDYqDhy46IboyI3zAdpYHFr7cSOEZg/sAPcK zvQLu5Mp/lDOOQPtD2c+oi0++sMbvc1ZIrhXNfMU6UqmYiKZm6MatCvTvtB7C4r7 XDxuBUVYxBPgljO32rAU3PoHV2gzq6irHyb2luryXALumSjsKmX2b29OXmWHlbNl cAXyFTdv95RnGZi5lPFo1UcCgZkA/HEKh1VdsUYqlQvEOOo4cbzOaIxLCjTMUN9Q 2YQd45opRZA2VKD4zmmCBlzJ2+MvkIbsL7y27ZVyHLgcWN717iSZPOWri4nAlMJj oskssX2t9fX65wSE5Qimx4pGkl1FD7DptZ+gbENyFOsA4ZqvnV4vvqPWgrLC28k9 WkIxqIsmbu5QlkhX/JYqbM7d1hxZIgYPY8hRnSECgZkArlDNb8He5H0ux0a5poIJ ktI/aq+6Pyobiz7tYOet/wpcboAIaJ7OiRE2xvp6a2jPLzQDdZXXSE2jmaM8JbU1 pHMwXAkPws+4kUwJPoH3XayM9eDGLnQvkrGU6tenskgh/ZHJ9adU2DV+VMX5yk+z BpOfcbUw33u1V0pE1siYXdBrAmxMuKzP5XoL//oYFvZWv3YWxYHA4UcCgZg5GrUg Ag6MsGunhZ7gpRMZn3Utrza2W1UwoiOe4MUGHHRjmQjBQhymTLWuDMVY6V50GiFJ dy4GNn02w+sdq2oEceD8JpQUn5ehzwtM4qcqi1yTvA4CvkH1sWxQzRXDvDeIIiPs Auz+MI+Y7cQoRJ6fQZQZUuSOcjOkFjS7uyeSkc+i3mP6aukEaU+1h2IlMgpzqN1I XCMiIQKBmQCLJW+NWMoX12ZzfcEis/r2CbcstpVHZtUXl1HNId+q1ilBbcIiftwo iqir+rrIhNNFvpsfjbbCxheIZTkZ9B/8mKMnrkvBO7O02HaJ7FHCVrxT52wfOas3 lwMyZ8gVDGefb3ySP7MSsezvmBFl/ZYgWidd/DvL8HrIvt0CwFI22n6amTI/RI7+ hClbRIzqQiIYS6kA52PhOg== -----END RSA PRIVATE KEY----- telepathy-gabble-0.18.2/lib/ext/wocky/tests/certs/wild-cert.pem0000644000175000017500000000303212200204546024367 0ustar00smcvsmcv00000000000000-----BEGIN CERTIFICATE----- MIIEVTCCAz2gAwIBAgIBAjANBgkqhkiG9w0BAQsFADBsMQswCQYDVQQGEwJVSzES MBAGA1UEChMJQ29sbGFib3JhMRkwFwYDVQQLExBXb2NreSBUZXN0IFN1aXRlMREw DwYDVQQIEwhDb25mdXNlZDEbMBkGA1UEAxMSV29ja3kgWE1QUCBMaWJyYXJ5MB4X DTEyMDUwODE3MjMxNFoXDTQwMDUwMTE3MjMxNFowaTELMAkGA1UEBhMCVUsxEjAQ BgNVBAoTCUNvbGxhYm9yYTEZMBcGA1UECxMQV29ja3kgVGVzdCBTdWl0ZTEOMAwG A1UECBMFRGF6ZWQxGzAZBgNVBAMTEldvY2t5IFhNUFAgTGlicmFyeTCCAVIwDQYJ KoZIhvcNAQEBBQADggE/ADCCAToCggExAOLThsSZX/OomslmhfbLQ4QL+rLXfAVN /17JZR4WRYWeX6JZUHNSMYJ2B98JZtU/aRHdNV46or6y+0UD1AFC543wp9KetcLb YaX2Lsxc70soBtfb6L02TQbaMf2q84/YWxwO5xCEG10KGLRrgAbxqtMvYIdGXstH 9TDCr6La6Iqeoq7R/4dBgF30hVTsOnai7W+YQuPT18QlX1O7HdaffXDGZPC/+0PY zPdr2vHlSzifNOnttPZuo4ALBE3hhf4xnxh531wdVQhsS6WTX5RhFU9ox4WsG7Q3 y5tQJQ72ARPWqVZiK1WJIDuEVlpWOutEd1McI3GVMuZt80PZaJC3uJs7f6l5Xux9 yUrE5ztzg378w5Hi6Rt3C0rLjHzn78MiC6DGlcG5KMOB+O4wgFzHCScCAwEAAaOB 1DCB0TAMBgNVHRMBAf8EAjAAMB0GA1UdJQQWMBQGCCsGAQUFBwMCBggrBgEFBQcD ATAjBgNVHREEHDAaghIqLndlYXNlbC1qdWljZS5vcmeHBH8AAAEwDwYDVR0PAQH/ BAUDAwcgADAdBgNVHQ4EFgQUdpNYWe7MfIU0lyUQvttyQMxXTewwHwYDVR0jBBgw FoAUSTAmCIya1mnNi8DMDlwCjkofpowwLAYDVR0fBCUwIzAhoB+gHYYbZmlsZTov Ly90bXAvd29ja3ktdGVzdHMvY3JsMA0GCSqGSIb3DQEBCwUAA4IBAQBd0wScKbZR +VhPij0/MWoQCwo7SpXyOCc1oTH5Cj2FKg70Al2fyfNXeQ94QybK2IFUuhuj6QuC 2kr11a8yUaYj5GfbMP0JHjJn8EKeRlxsv7q37nGkFcN4hweR3UfrY4dhEh195ZlM f3odQaKfyE4ST/SmVzGrzxwVupcuHcxCD4VowYMjO2xl2XeMjTMmkRhuW/kTcVHb xdhkLekyD5AEI3XndFKDRufFXg5nGmV7t5A/i1gKxGi/Co2YeTYd1XDspK0GSyX7 hl6RoZrGPkYF3mUmv6E/SiL8inCMfvt56aSQttX9zqB7zXOTXFnFOho+EYb6ZxvL liKZJQZ39OX7 -----END CERTIFICATE----- telepathy-gabble-0.18.2/lib/ext/wocky/tests/certs/unknown-key.pem0000644000175000017500000000321712200204546024767 0ustar00smcvsmcv00000000000000-----BEGIN RSA PRIVATE KEY----- MIIEpQIBAAKCAQEAvpcNcJNSz2GrNEEWNMaXDqNFZaEcBJ0Tx4QTVA66HaKPXSm7 sS3oYikt9bzeLp1lJqtJqJ8bJiRD6rH3vKMJjunw1BphOPsIAOtNcu0slWR5WKGK DfZfj0+KF7QSbKJ5cx03tV3IFMOg8SCW/HnIDT+yt0OWTo2qtF0BpnFbBUGUgCgO 15Mwo1tqRA02NUJ/Hr87oppVF1/o8qxDf1r5u/icW7C+czLvLOaYCrYwJFjno7IS 5RlkqjhSvNt3whaZfGCS8qriWb+mdTzKEguBawbn0lyFp/R7RfkyDRHKjrl5b2jE L430TvQx8epfGzoQpyoQWQk2IcMZTukCkJIZKQIDAQABAoIBABKIZDNr8/u7+tam NJdVr0cqW7tt7uhI8O0v++Qq2XC7x7aOshRgyy3OZMx2POGqBvW7Zrp5nD15fcwy 2YzziqClwXgSh50vld/nAZ05EGKHhmA2txK0ACdSm/JBnaAILYii0PG3KJdSTZJc 8amj2u7k4Ic/EHzbfDu1Jo2tjKUGSEhECOKsl3I+yrnjlRl8rla7ck8KFY7OJmkp +N4+byV+o+9fz5rxWw9A3yZYa5V3vOMc0tAIQGh0TcDqkRsMONJesFf418W9fqE+ ZZos8qw7Si6sXS1Xge7I/+3SbdzxpI8Wf3HmyEpKIuqAFFtCFd7mvF8UjgNNW8hF Ga8Qkr0CgYEA2Sq4lADOwJt6p1g03Gy4AbS+4hcEKjJ4fEoJuQs6OauDgc1Sp2OM Ee1560XaJK95ccZyFrO6uNO0A9cwCtTTqUkmYFzwtUXzMGa4eQcUjS2aYWIS/yw+ HwCEkQKuzWy0mDeDgWvAjEczgj9ZzVDJoucA51PlmBBuv4Hn0OWz2lUCgYEA4Ku4 XTblxlWzJfnNXUkWSsguUuroH2O8V6WOds0igxuLDMwFLBxROsL1EcpZod8XoBcM MATwJJ0smK65DXnOibMzqUYXOPRfyy360TnIMOaqgKKo6t9ZZMQJiNWq+liZ6gli n43QOxDibojbeWIjLLRGooPFcVfOAWER3eoL/4UCgYEAmKIsJOoEZt9gb6nRTXOk SJ6u4t1mcfoTMNggyUwY2luwDFUxWLDgy+DrJe9siQhEBTGDEm20OR+ikGh4PcSk F4FwVZ49GEtXx5cK5RWS3j9FGFkEHRENgGCuy/Tkw0E6FRo/dYE0TePlUhAQn/u+ HJ7bxbrzzdMO4878DZjEthkCgYEAuCusPyVQaCw6ZggHU9Ak6Pp+FtSfoZOlggeh 6Dsybl02uC/KkaVI5S2JraK9pRb3C/G4uOhjXvBE7YhVA/n02CZBVCKzyKlymWDA uaBvPb7ox/gPFjIu2o5k7nmQ5hWZXiOBaB/dMX4ez9N6bl+HTA8f0/rG8Km0oqbK +anxWe0CgYEAvTT09MGFC8jELxU7vD95euAvdeC4wCOhmNTk99kx2QLvOLqFo6GN wtzu2j1bTO29uX7DQ8iVcqQTuVO9nz6APhSwnkXvtN7tHHUiAKSJewXrBIgsVvY7 E0Ilhz/ILcmuigxe9tB682x9V6NuUhMZnhPvyZFxSM+JCTAfkwmPbj8= -----END RSA PRIVATE KEY----- telepathy-gabble-0.18.2/lib/ext/wocky/tests/certs/unknown-cert.pem0000644000175000017500000000272512200204546025137 0ustar00smcvsmcv00000000000000-----BEGIN CERTIFICATE----- MIIEITCCAwugAwIBAgIBAjALBgkqhkiG9w0BAQUwcTELMAkGA1UEBhMCVUsxEjAQ BgNVBAoTCUNvbGxhYm9yYTEZMBcGA1UECxMQV29ja3kgVGVzdCBTdWl0ZTEWMBQG A1UECBMNTG9zdC1pbi1TcGFjZTEbMBkGA1UEAxMSV29ja3kgWE1QUCBMaWJyYXJ5 MB4XDTA5MDkxODEyNTQxM1oXDTM3MDkxMTEyNTQxM1owaTELMAkGA1UEBhMCVUsx EjAQBgNVBAoTCUNvbGxhYm9yYTEZMBcGA1UECxMQV29ja3kgVGVzdCBTdWl0ZTEO MAwGA1UECBMFRGF6ZWQxGzAZBgNVBAMTEldvY2t5IFhNUFAgTGlicmFyeTCCAR8w CwYJKoZIhvcNAQEBA4IBDgAwggEJAoIBAL6XDXCTUs9hqzRBFjTGlw6jRWWhHASd E8eEE1QOuh2ij10pu7Et6GIpLfW83i6dZSarSaifGyYkQ+qx97yjCY7p8NQaYTj7 CADrTXLtLJVkeVihig32X49Pihe0EmyieXMdN7VdyBTDoPEglvx5yA0/srdDlk6N qrRdAaZxWwVBlIAoDteTMKNbakQNNjVCfx6/O6KaVRdf6PKsQ39a+bv4nFuwvnMy 7yzmmAq2MCRY56OyEuUZZKo4Urzbd8IWmXxgkvKq4lm/pnU8yhILgWsG59Jchaf0 e0X5Mg0Ryo65eW9oxC+N9E70MfHqXxs6EKcqEFkJNiHDGU7pApCSGSkCAwEAAaOB 0jCBzzAMBgNVHRMBAf8EAjAAMB0GA1UdJQQWMBQGCCsGAQUFBwMCBggrBgEFBQcD ATAhBgNVHREEGjAYghB3ZWFzZWwtanVpY2Uub3JnhwR/AAABMA8GA1UdDwEB/wQF AwMHIAAwHQYDVR0OBBYEFKH0v7AWPAzbSGYbG+h0kTcRaf+RMB8GA1UdIwQYMBaA FK5MjvmEOvzqUkos/bUV40sy5ngKMCwGA1UdHwQlMCMwIaAfoB2GG2ZpbGU6Ly8v dG1wL3dvY2t5LXRlc3RzL2NybDALBgkqhkiG9w0BAQUDggEBAAH4rfEh9rrTpzz7 I4rLAmKyKpp9FHiPkA/ZSH/U4ffbILrych0SvpHxusNWRo3rjcUOBAq5Xt+2Oqvl 0gVqcFF8GuGhiXYSV3k40bQHxv3MO5HanPbCIw0BDjERC0Ii+8BkDEMXRdQdyYmP q+D1PKjev0bQtjai2L8/R6WOWTJtZeQAMwvtEqdwSLXDxyQSEJc4NIvWWijqq3hP Ld+wozE/+Ajx4A4qeab9Vyj1Ul/zGYF/JDPgPLwOoBXeBJxJ+cB5HP4OOt/dpTWu L1ratvrGEubystJ05fPewqHhwyBDuqnCI/gIyRXEcbHA6sIJ5RpaKFWbOF1SQO4P 30nZOag= -----END CERTIFICATE----- telepathy-gabble-0.18.2/lib/ext/wocky/tests/certs/unknown-ca-key.pem0000644000175000017500000000321312200204546025344 0ustar00smcvsmcv00000000000000-----BEGIN RSA PRIVATE KEY----- MIIEowIBAAKCAQEA3OnZj1DlQPIDzJ15thqOY8IAIUCAL7zKbCwC1b60WGQasDF4 5DVD/WGBFfRLQlWRIlfj57bCdmc+1SrXjJCJGFGzr0QLZsqoOEi8V8goW57Eqdzu HW7e37oY9zd5zScnnftUnt6+2fl/lKR6Vit3AhNsCSZg8v5glccBuq27XlzSDc9K tSBeleUV71tF1K+n9RC8oKrixlB9KLgY6ppfBn7N4m5Awoqozhk+85snrlWZZELb S6ov5MEVJ8BoVsCejZWCPxHIG8fX8zccgZ/Ktll6Qx2RUTsPlsETXteYBEh7zWoc DZi6Qnr7qqHZoq8nlgi8Q+LeT9z00uItD4RH0QIDAQABAoIBAArnr83Jb2Gw+LNK cEJHJhGCV32MuK8ylXb4xrSMrwwgd+lL7eTtBLPOsv+1kRv/i5QrjMRo0wljgBhn fp+xmFTnHpS3qgzrUtnBbGu0ZZqdOCXO8kGpcg7O+dHFTynLhaMAr70Ob5Mkw/Gf W2sixuOHOCls97LzD+tA2ULg9dt658QqtSO4UhJjQz7aV4oz625NBlV6C0GYSo+5 vtxIey2jO4mzg0uTdDMbk+x0D0+Y5KfVGTZfKv4c3Ekk56m0IZfDA6hi3/16DY7X Th2f6Y6gmrxcyz6BvaV05LMXeYgcxSnp6np3PoH7cD9PSmlBYWuW0yx7KfQjFdr/ pdJl2EkCgYEA5OHifkfJa6toFbSF82E5u/l8u0JsjLYlVGJMdpD62tFPMRlpE4mQ HN45JF6uHbkCd00xqZ+z0WBw6gpk84BKW8Qk5luU0PPc2giLn6WlD0GBCi7TV5Cw lhf5CL/Dz9tsfnoJ2SRfr948h2Gc7c/XmeTub8BnN2F7cqwkmfLKcwkCgYEA9xZD zI+5s5qv/ZRkqs697kKVEqX51FsVHmNDp/dVq4vkMjrqfWfD5PwRwZ2kPyvwXfb/ s9NR58IJkkqxwSvSTAOfZtlJmWMVxP/6GStOcuPI6MHcwjXQmkKCgx6z00VLB1TG OIjNqU8hSTpd9Mqecj7Y5U/aTGQTbRNxLXbc+IkCgYAeQFTFSAuxNWh+ZevGbTVK SQFZZsAeIPzyGEPu796YCU4kOS2QLYBksGJxEiqFNyzCQ+uqdbc4lzyVUmZhYLVz R4tw/u2+aFzqXHLH2Qbl60x44hOA4VjVnbmGlIH2+RNv7AVdM9eJ0R/C7P3m2K+w YXe6QX3i7fl5AJOd3V8wsQKBgFGdAhH8WztO2dipfkLI/QVI6tJ5DjctSqF56iv9 z0dVcq+AtFltv8/PztGkD09qZP+0iStNnp0tg1nV4DbNEa5X9/vRw7StfNBQOLy9 iDD8uizfc9qWSevaEh/bMUyUc77dKogb78p/v5/fbo9fqcxUo/HCWXmoCJ6bsGNz AnP5AoGBAKVK8otu7a2z4uxhVySOhaJ6GvWZZ6TkM8SZUFxW8H2EPLyxaIeSnMZF G217tfER+hiP+nviCwGYxU88mo5Pn2OXlhWTAEJv/TccM6G4eWo06SDz3cqWYfNh B7rT3jiBfNwbpjokwcbIbzvyx7jjFjsas1b9Fdk+XdFV2pyXB9JA -----END RSA PRIVATE KEY----- telepathy-gabble-0.18.2/lib/ext/wocky/tests/certs/tls-key.pem0000644000175000017500000000321312200204546024066 0ustar00smcvsmcv00000000000000-----BEGIN RSA PRIVATE KEY----- MIIEowIBAAKCAQEAwt9AF1py3GpF66t6xyH8vtLu8zQJnUWMPPDCAO8KWWeC8TBz KruGJ58srKETSPcG5Qj5CdvV8gZoXy3PRpp55eqYc39OJ/cfd2fLfSR558Kqic3b cX7h7AFmv/th3t7jcVjC/nVMvzgeYmsz3gsCrasJoee6CxSx5Bfg4Ya8HlJKEAw5 yf9cU9Gx5nbJUPapzCMYYw1KGfU5AJPvD6bKB2z3JX6+srZwoxnyzqAPb1noT6JE w70D0Jl3OdryBTafAN4dOY3qdwwWM5BvliXCUTYcsQtHcxasqxSVtlzLQrNKdXD2 h4F1TwnuInFy2OPHoVWhbwUdNit41UO1pHrc8QIDAQABAoIBAC0rgHipT4yF2bU5 51i3KRW2YQrgmgXpdAtAJ0f+IKD+nFx5xYg3NW6Dt+A/6e90yxVV0hwV5+6Uy6ac QLp13iGMElBbRut+nb2YwpM8XEF7XvpYTDBvn8CLxpxjkZkOgxvn3jMLT4HXaTuY 68nhNXq59Z6gzv/4iQ989XRxPbOtKWI1m96ZTFstkG8TJjaO9AnDxR2sEGHQ+rhP IRMbHLagY1qA0OxMhHCHMTwq/oVBR7xtgraLzeHnSg+Q4Z+hY+TSNmlmJxZINHTK c4kxVhAv2iGJaLDC7XviyKs/AMmoVfcSuG+BGDDOxhpOjx/G4ZzKmp2VVwNOEYk1 E2z7JH0CgYEAy+zMzpdgNIYIcKRAYqQITInsNnfGY+y2tOhUeNA558ZVoqt7Gf1Z TWegqyxtP71ZYkLqidNlJasgX2cU6aAkAHfO1EnpZiaX6WKSXGIfukLhH9Jwc6MW Xnnv0dZ9i83IL5sLIkAVb0TqqIIBvbxZjEYFSQVwpfG8BCvxNwQJ2U8CgYEA9KKh /DkoULrC8SIQ1TroOFcqLZPUFcCPHEfxHC+urWkYpRCrQ43jiZMCegPnKuUEQ9Nu 7UZqOrP0WYyEqQ3P+jC4I4xBozJigGttMTBnC2rm40PCPZcUz28Ja/Tos4oiEN6q qVG5/IlfcjuZZGsFIKbTCxM2uXwykmJDeAC91b8CgYAvbIWAsfF8pYMG9xvGFNGd QyH81MP9bwpabgFfC0W8IgK+TtTVCXcgKi5SQIWzogxMbrVukgvew7pGlYlmf4h/ 11zxP7MYv3bqnrLc6zDnty/1n5HpQo8sL31XNmOCBLw+Xfcr4u1ZMBTGVV2kS04j 8hC+l5ZH8TzBV5rEKZtEvwKBgQCtltmqyEQ7RMsfoDShmfM+R1u+i69q4ACs6L/G aG9izbiXKITeoshazt5rBmn6nhewqU+FPvoSPa+d+4AHFa4Gspt3XgcVbqNGzPPm e5ojF/BOQ76JRbOWngvpdxfIjrQtlFM1YrC+6hu4S2JFR0uUJ8yJh1DFvcOE7AVE GgKasQKBgET77FdaEiiajroe0CE8Ulmge4mpok7QQ81ag6gV6X3R7Ru20pg9iqGo U9OS2VgpqhoWz9PSoI/ZZghRYq0oQz9XDNLtZeGswFJgl4A/TrVs6gWcRxbVfAtg 9EmaJucDIxXNHqoD2yXj7XJPqDkNuD7rV7IOnrsg4La1N7SQpQk2 -----END RSA PRIVATE KEY----- telepathy-gabble-0.18.2/lib/ext/wocky/tests/certs/tls-cert.pem0000644000175000017500000000271412200204546024240 0ustar00smcvsmcv00000000000000-----BEGIN CERTIFICATE----- MIIEHDCCAwagAwIBAgIBAjALBgkqhkiG9w0BAQUwbDELMAkGA1UEBhMCVUsxEjAQ BgNVBAoTCUNvbGxhYm9yYTEZMBcGA1UECxMQV29ja3kgVGVzdCBTdWl0ZTERMA8G A1UECBMIQ29uZnVzZWQxGzAZBgNVBAMTEldvY2t5IFhNUFAgTGlicmFyeTAeFw0w OTA5MTgxMjU0MDdaFw0zNzA5MTExMjU0MDdaMGkxCzAJBgNVBAYTAlVLMRIwEAYD VQQKEwlDb2xsYWJvcmExGTAXBgNVBAsTEFdvY2t5IFRlc3QgU3VpdGUxDjAMBgNV BAgTBURhemVkMRswGQYDVQQDExJXb2NreSBYTVBQIExpYnJhcnkwggEfMAsGCSqG SIb3DQEBAQOCAQ4AMIIBCQKCAQDC30AXWnLcakXrq3rHIfy+0u7zNAmdRYw88MIA 7wpZZ4LxMHMqu4YnnyysoRNI9wblCPkJ29XyBmhfLc9Gmnnl6phzf04n9x93Z8t9 JHnnwqqJzdtxfuHsAWa/+2He3uNxWML+dUy/OB5iazPeCwKtqwmh57oLFLHkF+Dh hrweUkoQDDnJ/1xT0bHmdslQ9qnMIxhjDUoZ9TkAk+8PpsoHbPclfr6ytnCjGfLO oA9vWehPokTDvQPQmXc52vIFNp8A3h05jep3DBYzkG+WJcJRNhyxC0dzFqyrFJW2 XMtCs0p1cPaHgXVPCe4icXLY48ehVaFvBR02K3jVQ7WketzxAgMBAAGjgdIwgc8w DAYDVR0TAQH/BAIwADAdBgNVHSUEFjAUBggrBgEFBQcDAgYIKwYBBQUHAwEwIQYD VR0RBBowGIIQd2Vhc2VsLWp1aWNlLm9yZ4cEfwAAATAPBgNVHQ8BAf8EBQMDByAA MB0GA1UdDgQWBBRDAfw/7QRZO5a0qmJ75Oeo3hA01zAfBgNVHSMEGDAWgBRJMCYI jJrWac2LwMwOXAKOSh+mjDAsBgNVHR8EJTAjMCGgH6AdhhtmaWxlOi8vL3RtcC93 b2NreS10ZXN0cy9jcmwwCwYJKoZIhvcNAQEFA4IBAQAIcwQ8FN7lnnQPm4al6y5v zrGzVSxkUuN+I8457E9ZAoFpItMGqWWKjjbOgjS3d95yJWmEW2eBVC3/LMEAvv4z Q6HkTRhafkiLWmXNa8DtbUq1cZ2hNrR1lNTOL4zXwg9JQbtFw0EAM7LfSgqHhTzs xO0AbXaO0TlbYkn9/amPCNQcFjg6Dgdm3x0T3g/tLQjtzjro/hdgYZqPng0MYBpG AUj99FwahI5D8cAPoUjtpxZOlsexz4r8UVGNRvL0Wqg57w8KKF7GVr15b2ZAeQwo pNJkMSXAyPpKW24Q06zwYFnC+Cp32udf2wIB9FEC3zNQugUbtFitHzPjzhum13iR -----END CERTIFICATE----- telepathy-gabble-0.18.2/lib/ext/wocky/tests/certs/ss-key.pem0000644000175000017500000000321312200204546023711 0ustar00smcvsmcv00000000000000-----BEGIN RSA PRIVATE KEY----- MIIEogIBAAKCAQEAuEDq6imjA3Glai3qXE7nmiHpj18fQthP+JShwyyIcTFfGh7+ DWE5RH63nasAQYYwfYxSKfB1TNdFLTOlwvm6qkZxfpCuzQQpu159+bzHuwvpns1S Q7ZXVbCLzi0OfYkIyovXr8oIFGfsaIYlyd0YSP4/5dal5boZfsAI9mNMv8LtP85B wUUZmkVopQhGGtaFXZ0ew+kbpvPiTJy4HyKjkGT9d83l0snZHqba7phGxZtH19Gc TXGkz3wOq1pViIZNAE676B6F+0YdlpztcLb3uEIB4LEOQhuquzT8FKLd3ThwjV5I CKNj0P6tOHmNw0mpJqZsWZcKA2XuvMekq8mAeQIDAQABAoIBAFnOUnG1v52jG9Pn 803wq5QFqXhXDb6f/kKT91BQ+WPqg4cQyhUtaSNIfCieO260gBgBd963NAUy+6Wv gcDJxcDOuuzMilalC84mnVJHQDab9M+RFeKpEmJSvaHNdj2buCw5AvTMjSmdCa5c jDiaygZx3iUhfRS+o0STRwsIlVT1UZDaDD0+FP7zUYr6D/zXSUBM8i9euOo8Od0b T908CGTJTO2M2DlOqzUUv7wcs2mb7kBTOwH8v6k56/It1F/rSif8tyGBDshna50c 42CUSZdVDEBOJFV+JkhqG5Da63is9bqT5teAb18CrqeNqKhKG5kcvaWG8QdrHfOQ MQlpHS0CgYEAwzyFSud8B71QMcnaJw0R5wB7cKNZx2X+nn+YrSGqCYhvfJFfzmx9 zV28u5k1ZtipXmpKgHRQR1+uo8lymnG9+d9pmd8M5j/rjnOYZpZCLfWSMXGeHih2 UiM1zFwDQJsu5JlCmGP1NInHep4rhAbU5mE2BBassJAb2bfqX8GTjXcCgYEA8ZlW 4mNQ2o5saBn9v1cOb/bmXuM0DWA2bZnDWhg7SZXSd6cgigClFJRMpsB6PXlFrZt0 JwCWKOtcoZhYDfFNbNojVrxMcDxNQ2fXGx8oQil5BiBvTBtjnWI98DdjsF4C/N7R MrScTkxBlC7ES4SK2VyaBPJDV+7Jx1K62eEhHY8CgYBpwWLCjFnXJ2lFTv3ooV/N Lamv/gwnwswFt0BQqCefOlSJuYWYH5SVpe0SAY+3wi0cg58PrfG+d8n11q1Jk8tA ixB81SH7pyxF4b/v8fyvQJKMTetEks5k98WwiTcJzAW+tnYobhzo9KkldoBD6B9z G71SwaWRjr5HVSST8hunSQKBgDa0+i+ZPZ2/0lxgRk0lcWd5CQFDgW3l820t/EZS ZlprSpU9iui07KyUSCcaPpPc+iItqUeLonTxlrAgxw+hLF8Rph7l1Ik1nmk9AkBp 9bvFmFoCzjD1osDolg3m/PPa1eJcshJBQ4OXUOI1FM3k3WwKw/WKxiULNTWlTho4 GD+RAoGAdhDUfixx/mOlPKNz08EaiqvH5abdbvDNTvi4UgCBAxfZAYnyL/fFB53H thKpHO2vKS1WtPZIiGBKffYVg1fJjmEeRof2lFhC7aoyBzcfyjjp9OmXII7mdFQn x3b6EUM92+LliP8D/joXjFOHABuJQBMcCb1Cr2t59MQHtlLWUrc= -----END RSA PRIVATE KEY----- telepathy-gabble-0.18.2/lib/ext/wocky/tests/certs/ss-cert.pem0000644000175000017500000000266412200204546024067 0ustar00smcvsmcv00000000000000-----BEGIN CERTIFICATE----- MIIECDCCAvCgAwIBAgIBATANBgkqhkiG9w0BAQsFADBsMQswCQYDVQQGEwJVSzES MBAGA1UEChMJQ29sbGFib3JhMRkwFwYDVQQLExBXb2NreSBUZXN0IFN1aXRlMREw DwYDVQQIEwhDb25mdXNlZDEbMBkGA1UEAxMSV29ja3kgWE1QUCBMaWJyYXJ5MB4X DTEyMDUwOTIxMTkwNFoXDTQwMDUwMjIxMTkwNFowbDELMAkGA1UEBhMCVUsxEjAQ BgNVBAoTCUNvbGxhYm9yYTEZMBcGA1UECxMQV29ja3kgVGVzdCBTdWl0ZTERMA8G A1UECBMIQ29uZnVzZWQxGzAZBgNVBAMTEldvY2t5IFhNUFAgTGlicmFyeTCCASIw DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALhA6uopowNxpWot6lxO55oh6Y9f H0LYT/iUocMsiHExXxoe/g1hOUR+t52rAEGGMH2MUinwdUzXRS0zpcL5uqpGcX6Q rs0EKbteffm8x7sL6Z7NUkO2V1Wwi84tDn2JCMqL16/KCBRn7GiGJcndGEj+P+XW peW6GX7ACPZjTL/C7T/OQcFFGZpFaKUIRhrWhV2dHsPpG6bz4kycuB8io5Bk/XfN 5dLJ2R6m2u6YRsWbR9fRnE1xpM98DqtaVYiGTQBOu+gehftGHZac7XC297hCAeCx DkIbqrs0/BSi3d04cI1eSAijY9D+rTh5jcNJqSambFmXCgNl7rzHpKvJgHkCAwEA AaOBtDCBsTAPBgNVHRMBAf8EBTADAQH/MB0GA1UdJQQWMBQGCCsGAQUFBwMCBggr BgEFBQcDATAhBgNVHREEGjAYghB3ZWFzZWwtanVpY2Uub3JnhwR/AAABMA8GA1Ud DwEB/wQFAwMHpgAwHQYDVR0OBBYEFJSBitgIFe+DDoAc7lAGk5+gbReHMCwGA1Ud HwQlMCMwIaAfoB2GG2ZpbGU6Ly8vdG1wL3dvY2t5LXRlc3RzL2NybDANBgkqhkiG 9w0BAQsFAAOCAQEArMZFCHoZHZGpT7XrsWHJALVIixkwtWePte9iiznKbl4ywrlZ fRAOoPUl+ZrspDU0lA5299V67M2WMIMs9uAjW5jgCPswylkO1RaJBTHcHmiGm2Ev R/8h4PCDo/LJvQjQs3B7Wev5zQEE25h8DUxvqA499wRWPxVmiLt4n/yFuVMNUDAF eQvCDv2YQu8EZ/A++kQjbjujTHT3okSXoPEcOZj+C1Eqp8cWXRrrh8xkE9ptRWIS Q+MhjvgVPrniGBBK1Mh0DbQhlriLD++XYI3bDwlg/Hnc0o9bWv0sfyvtOI45g5QG SDeBQ8L0KFUfu03ZBwATpd9pHUngpWbVXGqLhg== -----END CERTIFICATE----- telepathy-gabble-0.18.2/lib/ext/wocky/tests/certs/rev-key.pem0000644000175000017500000000321312200204546024060 0ustar00smcvsmcv00000000000000-----BEGIN RSA PRIVATE KEY----- MIIEowIBAAKCAQEAp8maSrexTHpBTvTk8yGc0D081R9xQgY4aFYgYUrQvaFnAha+ vkRzaYt1FBvsrYJcuwpWq93M/g97Tylln3jKNOPPeYevwdcFAb7aYzWcVbXNsr6J inKAslmslp7rnE0W9cGwAdPrIbLgShwQ+C9lrO6B5J9MJlXdB2KGsq11ATJkLou2 OZGIdC2gfoHMZrv0aIUsLwb6UrpDpfDjpAep4B36CbV90q43UxyAmNv1Gtnzsb/p 4eoTsuccu32qWDBTbXbzQpYn6i7yu1a4plajcbYkmSjRk1aEzwVmoKPjVdmmzWI0 rLRpFFoauGSODZpMg3B03gLX8f9eNlVz6hrtCwIDAQABAoIBAAH0S9hu8fZiviao nOS6vCmDbRVLu8qkaT0d3lwDvmasXezHowpy9afXDf1hTeJqJaEFankEqQHp4D/N yvc5NZxkGDiMJ18/R+RQpW6JDfxu6lRt+mAjG/NNLsDFB8LW9bDVxAe2wNha+Fh5 9zX3fLGvnX0Hi0rZroIK2GggXmqDIj+Ce3k6wcJLhZ7h6bext18lPKXigvTAFqVZ eJ7sucBrn+ww0qhBACpZ4RT2aBk991/Vrberw9OnJg+ipvjKnn+c+MoyftMj9Ih4 YMh87gRkdObukX5AiEYyYf0VGi7ejDMIygmQzpuup3jUYjNbpwwZZdoz4mwKqmvm Tvr7xdECgYEAzGxPHANCPkl4typwmEYEDRFKPtcoYKr9DJAQL9zqtqb3wEzbbSmN x5HzQqeX/hHND93OdCLKCzcHoF6KgglAmMIwu0ihagHT5Vul1VCoGItGWorL4XIW GTm8kKZXcDCgb6UWSG48fDj5eO5y+ZAxYjof97OmX6D9eleptKfZPe8CgYEA0h8A Wl1b7Qcsx26bLOGih5UuedKsda7EWUIbg5M86zy2UfJC3Rl6n17+gS55ffjuRrek eYrB2bomAnjhvPPaR6us7j/z87BsJ2PbZSsS/AkoCGqn6yjYR1eUWA65zbSEbWd2 RHB9rhcY7DDW2hwzkSkxbWzaXCuHNme0ZStsHqUCgYBNIbDXyQL9rYs20XSagBCX Pabd/yDlGJ9SVilFZf3J3UYt8NH6ZmtwmclHpSTcYKsXCYIUn+vMP39JK2LnncYZ 3Wu1vrno3beuGt5/lmdWm3Z4Q1aaQgnEitxzV9A2LKLcfVXjDnUUCgtXbeFD+Pwp K/VT6R0liAEcYGdQGuUFUQKBgHhCYAxYJJXLpnhaOJv+Y+xfmKMGzcfpB93iNScg LRyhueO62UP8Ii92ygkblVNhFtcIoi0iGoLHxJtjnidsFcExY59UoQYXMj91KouD lLEFeJEgogy4atiiKngfrX4rVCPdtFXFYFk0RQJhjYZ135m0TuLHC073ZsLfpeLF cHM1AoGBAKru0XcmVES6ieGAPtKm4IBgT4kXoBdAfYcans9DCRqJqqqcnALgHyo2 P2iP+/5u20XOb56bu15ko0fGkq7ibsdrsl9tZbXVyo3/Q0WesN4lOfVEB3tCmS2r D7alla/Fs5HrQEiCYUQGHisEuLqxt6LoHMv8vOqR8jGHiwX98cbe -----END RSA PRIVATE KEY----- telepathy-gabble-0.18.2/lib/ext/wocky/tests/certs/rev-cert.pem0000644000175000017500000000271412200204546024232 0ustar00smcvsmcv00000000000000-----BEGIN CERTIFICATE----- MIIEHDCCAwagAwIBAgIBCzALBgkqhkiG9w0BAQUwbDELMAkGA1UEBhMCVUsxEjAQ BgNVBAoTCUNvbGxhYm9yYTEZMBcGA1UECxMQV29ja3kgVGVzdCBTdWl0ZTERMA8G A1UECBMIQ29uZnVzZWQxGzAZBgNVBAMTEldvY2t5IFhNUFAgTGlicmFyeTAeFw0w OTA5MTgxMjU0MTJaFw0zNzA5MTExMjU0MTJaMGkxCzAJBgNVBAYTAlVLMRIwEAYD VQQKEwlDb2xsYWJvcmExGTAXBgNVBAsTEFdvY2t5IFRlc3QgU3VpdGUxDjAMBgNV BAgTBURhemVkMRswGQYDVQQDExJXb2NreSBYTVBQIExpYnJhcnkwggEfMAsGCSqG SIb3DQEBAQOCAQ4AMIIBCQKCAQCnyZpKt7FMekFO9OTzIZzQPTzVH3FCBjhoViBh StC9oWcCFr6+RHNpi3UUG+ytgly7Clar3cz+D3tPKWWfeMo04895h6/B1wUBvtpj NZxVtc2yvomKcoCyWayWnuucTRb1wbAB0+shsuBKHBD4L2Ws7oHkn0wmVd0HYoay rXUBMmQui7Y5kYh0LaB+gcxmu/RohSwvBvpSukOl8OOkB6ngHfoJtX3SrjdTHICY 2/Ua2fOxv+nh6hOy5xy7fapYMFNtdvNClifqLvK7VrimVqNxtiSZKNGTVoTPBWag o+NV2abNYjSstGkUWhq4ZI4NmkyDcHTeAtfx/142VXPqGu0LAgMBAAGjgdIwgc8w DAYDVR0TAQH/BAIwADAdBgNVHSUEFjAUBggrBgEFBQcDAgYIKwYBBQUHAwEwIQYD VR0RBBowGIIQd2Vhc2VsLWp1aWNlLm9yZ4cEfwAAATAPBgNVHQ8BAf8EBQMDByAA MB0GA1UdDgQWBBR2DyOQu6Qw1sh+lkkmX7HGJ1scGjAfBgNVHSMEGDAWgBRJMCYI jJrWac2LwMwOXAKOSh+mjDAsBgNVHR8EJTAjMCGgH6AdhhtmaWxlOi8vL3RtcC93 b2NreS10ZXN0cy9jcmwwCwYJKoZIhvcNAQEFA4IBAQDAvd60gplt/Yl/On9Ucht/ rhYfyUzW4pioSLVmiRKhKbDo8ujmhT3vgxdj2nW+uhPMxMbUsgMqj7tOBxIFqXaW Silo+A9iTr3Xxp/oPT+6u0FurGpu50/vPf/TMNhm+ucwYOfvpXVCJgu52c3WXp8R N4jvBOn8+MSlcetBUkmwRoUApNIEp+BglxDDdOB9IWhEZPvgbNokzAPrsoKflpXp pwkUFBuvn6/ffBBHb2I6rF4a4DC6yoK6DCYONzfHUjKMec7ex7CCWKubN31CogvH sb7ugiIFom/dMqjtGtWzG4ilVAQjjV5HJN+RscVp/6c/VY4WEYPfegUReGR0E47h -----END CERTIFICATE----- telepathy-gabble-0.18.2/lib/ext/wocky/tests/certs/new-key.pem0000644000175000017500000000321312200204546024055 0ustar00smcvsmcv00000000000000-----BEGIN RSA PRIVATE KEY----- MIIEogIBAAKCAQEAuZZqCSGB8MHUsUeeN/B/VEJSH2kqfE/4q8UBtbQw6o5L9+x+ SVCMBiGF8UbjOJNbQ2I/pxCAo+3S8xRmOjQh6zj0iC78UX72EOCM+1UlE5x40pK5 UOiC82VnaK5NiiqQBOjtHwCfpocuH8LY5yIVw1GqSMqDp4nnuEmXxOy2cfGYyDuo Yb+CQkXijc9bG+847lZeV4Ia/tS2DuzpopgXo5uC8TSD7baZJGYduksq/lLRNVIT eTo4optqynXpGH90uVoopbTsP91hQ9SiZoMi8uVy4Q/OzOfsCfuwKpb+KGwIGCiT 4N63MuVF/9YkY0+/wBm1pQYtO3ucymvlQNcWRwIDAQABAoIBAEy55UuyGS0GjZ7f S4e3+4RDyPzodyKe02F8WMkc3m5SdxDgKpO347PzLxOBqZXtwfjZSRSSK4TaYXCS mfLtM6qySNJ2jmVI9oUiuNZ9rI7vjToNY+URw3XKEhcCnNcG4rTM8Z90HAz2bTi/ Tvo7qsCt5hci9owQ5QZZEpBdBs6TDXYdbHlFEBVOFs8tHQHZ7AhsP46k8QJ+XwC3 nBh01M6aV4jjJw6nJLCJ+Ow2y2xXlySMHwi7XG/r/L8vHTsBDDtTtumtj4u4fpg9 NkM0SrkjiaSigAdGbO7DYSxZtPaw0+PmVXSosHy65lnlxjf8su9pAwi+PyHUerz2 Q3mDRlkCgYEAzSbdglm8Pfo1ONtb43wkzcn8mtheyiGlG4tpOa9PQl60vKOUEe2V Ou3h095kFArZV+l9f1YFQFqMWYCKD8rFm8fcNKgXyeOZaz/xZlPk6tNohGWOv2jv uC6ihAMiiWPRxxP3T2ls9nW+BNq23WKkrxBwdWoqGMRRJavV2FT+sokCgYEA55Ys J/sGi/VaIL/RCbOiDlYgfzzTBGAAFmjwjvN9A6De8zWZACAXqUiZd61kDZTo8kIZ ys/pI9huaiKhoeM3UyjjeCMemVY94ffaKNFRzk5gxcvDJ6pkyow6pmU/p8pVwcb1 u/+eQ1JIjfxzRwE+z4w8HOGJVi7mDa4ztRAGjk8CgYBdRvyUVgS3EVq9nU9sYXfC ccJ/DT4cHawamAqiBcm93Z3D34RlmmatwpdZY9aRHlWwGSPMj+oXVpRV5ZHgmEu9 BuRitMMMMoYBM7Oo2SBOfQ0alaqP8UB9uAaAi4Raf55ULAnYe5Dlhqd9QYy/oChM yOa5HCpD1I8o17aFhC/CMQKBgCy0XYO+Pm5IB4To4kZYKcFQuOc265kdmwa4bS97 KzfHFyKlxwoyJ78i55UloHnKtAkH34i6B8xGnFHaq47fmK9x5i8rwF1jO4DiYnec qIkFskKcaei/SMOcvDmgs+AE+/bzHD6VQozGVoEqKWqcf+56qKP+mY9McFZVuV+L X2ibAoGALs+PgMHw1LkzwfA4n9jB4L48FN5TdpdL5P6VckujqCZaZyjoJ6cVq9gw lFf5lCpHmqIOzxDTzR2Mj8b7mT1q+a7az1BqDwpXG8rwNmhyx22RdyJSY4hiUHAx 0QL3ZcIXOyqneMXU+a2Qoj3aMAr2ZUS2OEpWEtmPfXcxf5DHaB4= -----END RSA PRIVATE KEY----- telepathy-gabble-0.18.2/lib/ext/wocky/tests/certs/new-cert.pem0000644000175000017500000000271412200204546024227 0ustar00smcvsmcv00000000000000-----BEGIN CERTIFICATE----- MIIEHDCCAwagAwIBAgIBAzALBgkqhkiG9w0BAQUwbDELMAkGA1UEBhMCVUsxEjAQ BgNVBAoTCUNvbGxhYm9yYTEZMBcGA1UECxMQV29ja3kgVGVzdCBTdWl0ZTERMA8G A1UECBMIQ29uZnVzZWQxGzAZBgNVBAMTEldvY2t5IFhNUFAgTGlicmFyeTAeFw0z NzA5MTExMjU0MDBaFw0zNzA5MTgxMjU0MDBaMGkxCzAJBgNVBAYTAlVLMRIwEAYD VQQKEwlDb2xsYWJvcmExGTAXBgNVBAsTEFdvY2t5IFRlc3QgU3VpdGUxDjAMBgNV BAgTBURhemVkMRswGQYDVQQDExJXb2NreSBYTVBQIExpYnJhcnkwggEfMAsGCSqG SIb3DQEBAQOCAQ4AMIIBCQKCAQC5lmoJIYHwwdSxR5438H9UQlIfaSp8T/irxQG1 tDDqjkv37H5JUIwGIYXxRuM4k1tDYj+nEICj7dLzFGY6NCHrOPSILvxRfvYQ4Iz7 VSUTnHjSkrlQ6ILzZWdork2KKpAE6O0fAJ+mhy4fwtjnIhXDUapIyoOniee4SZfE 7LZx8ZjIO6hhv4JCReKNz1sb7zjuVl5Xghr+1LYO7OmimBejm4LxNIPttpkkZh26 Syr+UtE1UhN5Ojiim2rKdekYf3S5WiiltOw/3WFD1KJmgyLy5XLhD87M5+wJ+7Aq lv4obAgYKJPg3rcy5UX/1iRjT7/AGbWlBi07e5zKa+VA1xZHAgMBAAGjgdIwgc8w DAYDVR0TAQH/BAIwADAdBgNVHSUEFjAUBggrBgEFBQcDAgYIKwYBBQUHAwEwIQYD VR0RBBowGIIQd2Vhc2VsLWp1aWNlLm9yZ4cEfwAAATAPBgNVHQ8BAf8EBQMDByAA MB0GA1UdDgQWBBTQlqg773FSODizSAmYG7bIV06GuzAfBgNVHSMEGDAWgBRJMCYI jJrWac2LwMwOXAKOSh+mjDAsBgNVHR8EJTAjMCGgH6AdhhtmaWxlOi8vL3RtcC93 b2NreS10ZXN0cy9jcmwwCwYJKoZIhvcNAQEFA4IBAQBTok7jC9jwZeiPmGgiktNe kkAwKJuJpa9O9lgeiZywlaR3vWIEuQpi7P/VpkWca7pjmu0kVJk7AupW6s6RfeW6 OALA7Zn9SC0w1zj2aSDmjI0DWosvkvoO+jkNS6HIozkMkWa6IX7veL7sTUP6cZ7f twMPExDhdNrmyOOLjxiW7Bvz6HVfAvcgCah6JjNSxL4QAdFiz0VJOInWXtp7/2rc YMeBsg4YFkV51YBVts7gCL4lDLxE79Rkj7YwhP6f84tTy+NjFmjBYKS9tyKJViOd 9L82PfXhHFUcYP/8wqhk+nE5PF4mLhroUq9DZpGU8ICEIzGburysoQi64IoPDCGM -----END CERTIFICATE----- telepathy-gabble-0.18.2/lib/ext/wocky/tests/certs/exp-key.pem0000644000175000017500000000321712200204546024064 0ustar00smcvsmcv00000000000000-----BEGIN RSA PRIVATE KEY----- MIIEpAIBAAKCAQEAnlBf47+ldQRlzW/nqYpggSx0alV6TkLnf7gt0WIBv4XGoDOJ FKmrwMkEiGvNznPS9NUmN/EDRDW7OGHSwR9Br8aOEnNwP1/CGadt5YOpksMWc1qE Tvw9IOW8QwJAl2PoYfnneB53x6PmtSf21Z8jcHerLGoEH8gl19IQpg+LDZOAxZlq 99KBFTEaOY2k3EaMl3+QE7gWvA9G5bsDmqnHGqLBS0upugdHPKOjjrk+0jPvVb8x tQVPk87bWayiZi4dr6yldPfiJ4sQxTH3plhlAh39fN8Z/Um9W5TcozPvmYE7PfHW yV0rc8WCzDZHiRWba6DIusM4LdSm33vV63GgoQIDAQABAoIBAEe1Vkm9w/8l98q/ FmrIAABil3SWtSh/y4Yhcbd2sh4DRT3JOh0+4UoYg1IbqoQKGJLHfWE3bOhXgi7+ uEy7kLBCupWG7l/2St/945xjL8sHTMMBTA7O2A1vof+kJIeuPFMss/jHrC+kgNqr eHW4eH/35HPgrW+L5ABG6T2eqriB0fxRLHH1QwsOqIoIG2YRe04PdEsjcNgtSA7d pqcUQU21zbfk2ELUTE8SrlMeDv+ybQTrUJeM44i50R7S6nX5z/KceLO69Hgbgy0Z CXLsmwCgQl1s7Sg0Ur5TSgSKmqVbyyVtJxln3LBMYpO4JxM6LTMT2klxtpZcGbkW rRbLtRUCgYEAwX1LrXcQWm1m6mkni5NVxXxM70hySKuchcDustLxjlnuGuE/IkXI 937fFmXoI8XMGdsBna/WA+CfngKOZK+w0LhOp5aZHGl74r0v/fkS2fz+qzctekQ1 XZ0/QY1hzquZWiczlxkT/lbe0hD4t0GnQom9tJdmLfqxU/ovqKItQO8CgYEA0XXc vljS32koN2oUWWkeBijO7tXHYze2MyiqA447zTaVqSaP69j3LjVa8tHMbADCe2Qx 4CCPuizbGzf8T++hA0e4jGKcnwd96HUHbfP4Q9qNdzg88kUIT0QrDXccMF3MAKRI zd/YzmMDPr+mTWxkDiMQ3kvyixDaJAyTbzzTF28CgYEAgF220lt2bve72nJu+OuD cOR5Shp/L3Ui/52y/tJxzWYeUJj1QLCZlpEGQh7Ttr/oG5MvbEUWsDXaz4KUo3nn zWEpVYfVBoN43EF1UIJpHlP5RO/zbPVJjlTffYblx64SrDvrvye1GvzZEPaPe0zE QaGOSPxUntZ9xN/rtG37peMCgYAfBwKH/8hc58rZSpFxHS0hZHIi1vAONnZ65Y8p r6wUHf3VbKztFtqmsaijR4ishwBIHvN0a95eib57LbPmc2y6e6lOwlzJAecYxB0x oG4qPvmtq1r72FX+x+5ItFgsofhSMAPI21vWVrxoUyNjXKcFoRQimcV37CskI+jD FZN/aQKBgQCQ0pGix/pOA5SJ6ZgwsovK97h/F2VvVx2NhzR2hKkp1ifFzZYmYCLf sm7bzuzcb322ahb+bCoVTUpD6U1QXpeuJnqCx+VBI6J3WfBMerKSzxQQ6ksGfANS 3OOYYAtZOMN72utKg6UPd7zVtTx8gC1peRogQEfELDT8INPOv/RG9g== -----END RSA PRIVATE KEY----- telepathy-gabble-0.18.2/lib/ext/wocky/tests/certs/exp-cert.pem0000644000175000017500000000271412200204546024232 0ustar00smcvsmcv00000000000000-----BEGIN CERTIFICATE----- MIIEHDCCAwagAwIBAgIBAjALBgkqhkiG9w0BAQUwbDELMAkGA1UEBhMCVUsxEjAQ BgNVBAoTCUNvbGxhYm9yYTEZMBcGA1UECxMQV29ja3kgVGVzdCBTdWl0ZTERMA8G A1UECBMIQ29uZnVzZWQxGzAZBgNVBAMTEldvY2t5IFhNUFAgTGlicmFyeTAeFw0w OTA5MTExMjU0MDBaFw0wOTA5MTIxMjU0MDBaMGkxCzAJBgNVBAYTAlVLMRIwEAYD VQQKEwlDb2xsYWJvcmExGTAXBgNVBAsTEFdvY2t5IFRlc3QgU3VpdGUxDjAMBgNV BAgTBURhemVkMRswGQYDVQQDExJXb2NreSBYTVBQIExpYnJhcnkwggEfMAsGCSqG SIb3DQEBAQOCAQ4AMIIBCQKCAQCeUF/jv6V1BGXNb+epimCBLHRqVXpOQud/uC3R YgG/hcagM4kUqavAyQSIa83Oc9L01SY38QNENbs4YdLBH0Gvxo4Sc3A/X8IZp23l g6mSwxZzWoRO/D0g5bxDAkCXY+hh+ed4HnfHo+a1J/bVnyNwd6ssagQfyCXX0hCm D4sNk4DFmWr30oEVMRo5jaTcRoyXf5ATuBa8D0bluwOaqccaosFLS6m6B0c8o6OO uT7SM+9VvzG1BU+TzttZrKJmLh2vrKV09+InixDFMfemWGUCHf183xn9Sb1blNyj M++ZgTs98dbJXStzxYLMNkeJFZtroMi6wzgt1Kbfe9XrcaChAgMBAAGjgdIwgc8w DAYDVR0TAQH/BAIwADAdBgNVHSUEFjAUBggrBgEFBQcDAgYIKwYBBQUHAwEwIQYD VR0RBBowGIIQd2Vhc2VsLWp1aWNlLm9yZ4cEfwAAATAPBgNVHQ8BAf8EBQMDByAA MB0GA1UdDgQWBBSp54s9nx3wWAR/8iKl1XGPyKC/VTAfBgNVHSMEGDAWgBRJMCYI jJrWac2LwMwOXAKOSh+mjDAsBgNVHR8EJTAjMCGgH6AdhhtmaWxlOi8vL3RtcC93 b2NreS10ZXN0cy9jcmwwCwYJKoZIhvcNAQEFA4IBAQCzzxwwXOB5xnaKoDerB6fq QbUmyi5Xj/c1kNgN5WnICUJc8bgXWlhLtvXyEE+/2Gshv6rOwpeH7pO4wtACaiHH NmR78Z1OPKlf4+Bxo5/wQhx228gCOsRpFK+qab5s5/OhmJe4r4AyWoF4CPYfyE0v Z5PkOeFiYc5VSJdI7y360zPV8FBYcASU90aCjREDAi+FK0r+Bp2OdxiTRL9N+mEM blgInz/adg74oDjPmrwENkEZbaKyVwL9WBUD7OABIA42exK8nvCwPRiu1//8ALSR wXTAhJ3lOIG4FOoOKBu9sIY/iRL/mJa39YJHG+gVFx+taJKB95tHthGuPraGJjch -----END CERTIFICATE----- telepathy-gabble-0.18.2/lib/ext/wocky/tests/certs/ca-2-key.pem0000644000175000017500000000321712200204546024012 0ustar00smcvsmcv00000000000000-----BEGIN RSA PRIVATE KEY----- MIIEpQIBAAKCAQEAvmAEI3lGwJZ574UOIpkGy+GlPLCYzPzO4IJudTXV3r91sLzn Qw1PC4L2iiKDEMkGkuVwLeEP4QHh2SVtDcd82VvV1oqph5arFC3UCr8SM/cZ7y0U e+IB3XTF6rDwh4nkjb15uHJ5zTyMJljYgSKOTXSP03vrTZhqs6TuLdRvY2JYRjYX Nr1CKQ6OGztr1qnkcqp0JawMpH2bEnI68mbTAQ0nPcWXYyZSbdp2y5UCVVIItWxM Gsz90lhIo32UyoZ159G74RRlEhLLB2+QlFa+iC0c1ibQFaf7rGAGkC2S4TIXAPeG LxQdHZVZbbHbb8AT4nRAuDTDyXIrTf97NX0GzwIDAQABAoIBACH44mQuLSkK8UGD 3ezgn+zcHG+DGBFkf8pinDGAZOT2B5t9akt14YgtW690zyy8otat6OHXCs1dV583 ZYmS8r2a/MLnRa9zfJ4CO5ebVzU8wD0FL2WYBDsvrm3eG14khkug50q56vBsAJqL Oj95GinKRiw4LdpP+6KJKRO2mBYE1Mkm+Ma2YD36/40zuDGKyp47oFNbZOHgwI/l U5Oi10/HWFXZ7J8qFWX06prQVymWW/eZdgEtjrVqzRXCdea85UdlbPYZ2DoIjO2e TFJ2ApjVY8uCZtd1PdTAmIK+RWHPbRY3e1fGji+1W5PT6FkVzS9DoYUTsA8GFche /bqvDUECgYEAz5N9iaAGISlGjSU+iW2WHD/+WIru1u6hjXjKyI1kAW42h6C9rLnS U+fRl34p+chR9pI+RM8lQjqa1Pfl+kQEu4hQertgSbbdy8FJ6UK1nV+ianl+rdYB HXup2a7EDZGYsWMKLE0hSZ/Y6aVrO4mrrvGX//aDHsB5qSGEIAnzP6ECgYEA6slF yXbGYxOK2qvioUhrDYAMCcPEOy1Gn9pcyurV4Vetk+uFjZs80pcO1fu6GXkc7mR3 pveFUx0f7C7D5NYq9PckK/JPj3CkVDlm/uIHSZeRDYjkPDaAB38Lphkl7DDTlP54 XD22XhziV+WIIvly4t7vziBtkJMR1s+sq+JucG8CgYEAxpJLl6qT8okvyrqEVqeU 4DV/tWhDDy19Mn8JTk2kC5JJa/mjOWcSA95SLGAu+5pcfkpscxrOg8rYbz8uq/kB pfzDMIWrmRjsmIyxjgmY/5GLJ9xOCTIeIvw4AdwkBO7xaVBbXQH0BCB8OxdZu7z5 lgPb6qsByBtMxzUe1h9uqcECgYEAxiI4nH15id7SDRuE02qTvP7UKeFlVjMtKHVU XYG0IMinGO9m80uQFn+X64jVUe3dNmjeeK7lenBXoi8M7a627acqhVOlRH1gkBsp CeuhDr+zj2J4iT/M54aVRARw9lN2GoRs+hqyLdXeRSLUsf8krsRIRwEitSEHquny 49LgNE0CgYEAhATwToe64ujjjADzacdQ17Jxlxjp6Ko8afuKMRixY+gIwhLhRv/i FUHq3uOxVuPK4P2m2Euc0UUQyWhXsgOzSFcwwlnfLbTBoe1TNsBYPgf/Lm/MqgYi GG/QIM1GkaheDwCrn4QIAgzvHLFNJC8ouwA3Uj9dkaYvXjoW65ftwcE= -----END RSA PRIVATE KEY----- telepathy-gabble-0.18.2/lib/ext/wocky/tests/certs/ca-2-cert.pem0000644000175000017500000000260312200204546024155 0ustar00smcvsmcv00000000000000-----BEGIN CERTIFICATE----- MIID5jCCAtCgAwIBAgIBATALBgkqhkiG9w0BAQUwbDELMAkGA1UEBhMCVUsxEjAQ BgNVBAoTCUNvbGxhYm9yYTEZMBcGA1UECxMQV29ja3kgVGVzdCBTdWl0ZTERMA8G A1UECBMIU3BpbmRsZWQxGzAZBgNVBAMTEldvY2t5IFhNUFAgTGlicmFyeTAeFw0w OTA5MTgxMjU1MDVaFw0zNzA5MTExMjU1MDVaMGwxCzAJBgNVBAYTAlVLMRIwEAYD VQQKEwlDb2xsYWJvcmExGTAXBgNVBAsTEFdvY2t5IFRlc3QgU3VpdGUxETAPBgNV BAgTCFNwaW5kbGVkMRswGQYDVQQDExJXb2NreSBYTVBQIExpYnJhcnkwggEfMAsG CSqGSIb3DQEBAQOCAQ4AMIIBCQKCAQC+YAQjeUbAlnnvhQ4imQbL4aU8sJjM/M7g gm51NdXev3WwvOdDDU8LgvaKIoMQyQaS5XAt4Q/hAeHZJW0Nx3zZW9XWiqmHlqsU LdQKvxIz9xnvLRR74gHddMXqsPCHieSNvXm4cnnNPIwmWNiBIo5NdI/Te+tNmGqz pO4t1G9jYlhGNhc2vUIpDo4bO2vWqeRyqnQlrAykfZsScjryZtMBDSc9xZdjJlJt 2nbLlQJVUgi1bEwazP3SWEijfZTKhnXn0bvhFGUSEssHb5CUVr6ILRzWJtAVp/us YAaQLZLhMhcA94YvFB0dlVltsdtvwBPidEC4NMPJcitN/3s1fQbPAgMBAAGjgZkw gZYwDwYDVR0TAQH/BAUwAwEB/zAlBgNVHREEHjAcgRpwb3N0bWFzdGVyQGNvbGxh Ym9yYS5jby51azAPBgNVHQ8BAf8EBQMDBwYAMB0GA1UdDgQWBBTeKryla1ofuyCr Gg3ugeTEymWLhzAsBgNVHR8EJTAjMCGgH6AdhhtmaWxlOi8vL3RtcC93b2NreS10 ZXN0cy9jcmwwCwYJKoZIhvcNAQEFA4IBAQC2RqDYK3xPnMGnlzJBVJI4cPH/iRLi 9ZUWmBzpjHpo0d/in6lAF0UirmjpgllXpCUhJMfP5fxOsZYk1eE/leqTyTory7Xj K6zdHmkFGPU3b5ibFPX65iGFpqZdjCAplFZ9HI7au1b0SpbsMKesE7ZXzBF0+ki3 GrKgQF+e7cMktJf07I4xoVVYimSpADFOHo9Pk1sceK/zSKGzob7TziD4Tpnc8Rhq dOj7J8EbzhqjBVciPYzXvlLjyAqdYRg5RHV1xSIvYKeK0PwVzmNY9Xu30+VShJHc 3Hf/VLl2T1ll8tXUj3hc1Jd2+ZMthTF1PIX3bunTnNUm2PENoxa8pDG3 -----END CERTIFICATE----- telepathy-gabble-0.18.2/lib/ext/wocky/tests/certs/ca-1-key.pem0000644000175000017500000000321312200204546024005 0ustar00smcvsmcv00000000000000-----BEGIN RSA PRIVATE KEY----- MIIEogIBAAKCAQEA1GRhu+pZ2Vr7M0ux+1didoAPRqANs2XvTLjLE2NXUOuJmc2C OBYzxkiBR28XH6wDzAyhEUEuddZh/Mj58dgVqT4mrKy9CapOJ9eMo6xDxB2FveFz bPXRfumhtEr6jETtVcMoqnmi7h/fDHWa9JOMHWhOvmjZmcDkr90sIzs6YnYL9WPz PqXodXjV6w8RgRm/7B8//3PfGY2E0dxFYh6KkR0NkN9dzSfbZJ4ScZJmtIkM/0Jl olYBwtSEPk4SH4Hc/+B870oMA3w+JxIUVePZfS3yKdybPl+zomiyAQWjjQP9HjFQ hTnrYcRj/i44zo3accz4wqCIMcRSH2zeUuZIHwIDAQABAoIBADF4hC9CAdWX1IT5 L6asjvmtEGHR6/8KUjfbnymP7QmjIzTY4mjv+vVHdB1QaeFtrqC7nFSpiwnVepNn uJg1Ta5TFK0JuypiKbwr/80r1cj7W2iD99+8TiSyhIC7Kiq771AXmgRDOskeUVTU m9g1+d/rqNO/FlgyztGLwbkfFZVTHpQ6QP3j5zaUcQzu0JS4RDCusuBZKcIMaXll u0xs9kwk8+yD7BNEoZqXsOHA36AzftdRTgxSmXGYN66pHNEU/f+QjcksOdO7L2ya sphrEweSALSH15okw8f+O1cMwPV/BVPkzhhS5ERvRbqVq/YWPS12gy8YXjSwNE17 HQlRfckCgYEA1I39mbambVPX4TaYzZygbDzLbfMCdmtLMht+LaHNbe7FQnALsxR8 jt0LIHfAo8pztDlJfe/Zppfsz+JBlpRFRUUB+S/NXEk59EX8HN+tAL93L1nZVmjU qOSdwPHxkGlfuuQG7gP0zjSTwQBGbMmdC+S0PXa1SubbOSCw1AXiDEUCgYEA/83i 7uthjW/AQXCgxP+tlb6KP3Dp6009URa+V9hT4jRO523FnsLJXtKEFCV+Wxq7dO8X QNrXWy5dj+tnzl6dJ76maWxORBiPArFFeUvTAUfzogR6Z0wrtBwPEvsNkVUMK8Xp NxndLY5DxNqHrTugIwdTDFZxAwO66YGwAddhUxMCgYB+t9QL8t9PaF/YbXM1iX3+ aVQiTXEXZinjSo6z57WQJ3xEeoYPElSb214J0jrvqv/F3y1YPkj0z7gny0ys1+Jg RJ7Dj2MP9LvvTFXcZOFAA+WrPhabNE4sDneaEuOf46JlyhHzjjpBQQkhU+eobZ4J /CQUTJQSfoUNjta84HD+PQKBgCzXClCXMiJ41FqkQ4pEx0jYfaNhR5/XikgMlJER eqLMSIiI1Xte6a2VeVGOwPd3WCTHRGw58EYrensf6LJkI2g0IzeXpKCLLYZrTKJW iEZRNoPQBSTWVVLdGEdbeqVfIyMJLjhacErsBTUcmWvkZ828GvKutGCy5rDH6vJn rsfBAoGAOGcl22yaS2ngXM1/l0tNTuZSdjiF7mtD73Gmv/VAae0sDqLgwr64F1at f0ppTACSM8uVA0d2EgvcmpBGNMi/ApvqVnpcDd2HZ6BFJAW+hk6QBB8GGpKPUSt+ F0Q3LJmmMz6Ct9spGu4Kach6k0DapGKBFUkaor3xOj1Lgi34yV0= -----END RSA PRIVATE KEY----- telepathy-gabble-0.18.2/lib/ext/wocky/tests/certs/ca-1-cert.pem0000644000175000017500000000257712200204546024166 0ustar00smcvsmcv00000000000000-----BEGIN CERTIFICATE----- MIID4jCCAsygAwIBAgIBATALBgkqhkiG9w0BAQUwajELMAkGA1UEBhMCVUsxEjAQ BgNVBAoTCUNvbGxhYm9yYTEZMBcGA1UECxMQV29ja3kgVGVzdCBTdWl0ZTEPMA0G A1UECBMGRm9sZGVkMRswGQYDVQQDExJXb2NreSBYTVBQIExpYnJhcnkwHhcNMDkw OTE4MTI1NTAzWhcNMzcwOTExMTI1NTAzWjBqMQswCQYDVQQGEwJVSzESMBAGA1UE ChMJQ29sbGFib3JhMRkwFwYDVQQLExBXb2NreSBUZXN0IFN1aXRlMQ8wDQYDVQQI EwZGb2xkZWQxGzAZBgNVBAMTEldvY2t5IFhNUFAgTGlicmFyeTCCAR8wCwYJKoZI hvcNAQEBA4IBDgAwggEJAoIBANRkYbvqWdla+zNLsftXYnaAD0agDbNl70y4yxNj V1DriZnNgjgWM8ZIgUdvFx+sA8wMoRFBLnXWYfzI+fHYFak+JqysvQmqTifXjKOs Q8Qdhb3hc2z10X7pobRK+oxE7VXDKKp5ou4f3wx1mvSTjB1oTr5o2ZnA5K/dLCM7 OmJ2C/Vj8z6l6HV41esPEYEZv+wfP/9z3xmNhNHcRWIeipEdDZDfXc0n22SeEnGS ZrSJDP9CZaJWAcLUhD5OEh+B3P/gfO9KDAN8PicSFFXj2X0t8incmz5fs6JosgEF o40D/R4xUIU562HEY/4uOM6N2nHM+MKgiDHEUh9s3lLmSB8CAwEAAaOBmTCBljAP BgNVHRMBAf8EBTADAQH/MCUGA1UdEQQeMByBGnBvc3RtYXN0ZXJAY29sbGFib3Jh LmNvLnVrMA8GA1UdDwEB/wQFAwMHBgAwHQYDVR0OBBYEFJdQenuGG7TS4L2av7+3 HHWC+uO7MCwGA1UdHwQlMCMwIaAfoB2GG2ZpbGU6Ly8vdG1wL3dvY2t5LXRlc3Rz L2NybDALBgkqhkiG9w0BAQUDggEBAI2E2XcrxGbK+e9CdIxnrVWsh8HgcvBB/F4M 9jRUSjc3+w3E1zxlyfqGgSrzCJIpZbq7TQ7tTd1AV8j9hP4bP1r3BX6lhiXRtRKC thobgl1gXQtHkFhRpZrWe4vECqucHlznel9nhtLeY50ykt8ajvqxhthe/uabKNlo wC87Aq1ykigR1imLAY8UdvdXfvnfS1d5bF/k3TSw2YOZE4u8KToQf1LVPIG12OPi uStuIX9UYzSe8qOurrbjX+qehoubmrgLPGRKyNfIHsp7Arr/yUnjK5WtojTjcF85 nuP8FceVT5cvKYYPcBP6+YpAeeBJBiK95kzZGqgVewoLpKJht+0= -----END CERTIFICATE----- telepathy-gabble-0.18.2/lib/ext/wocky/tests/certs/ca-0-key.pem0000644000175000017500000000321312200204546024004 0ustar00smcvsmcv00000000000000-----BEGIN RSA PRIVATE KEY----- MIIEowIBAAKCAQEA35q0WVGuFFgy4JhzUVGlyBMIcEkEREkxdX1nfjT+cTDuuThN CQJs5OK3Tx/MYrm2t1p1VbwPp6Ked5Qu3pk0zFc1i1iyRFd39tcTXhwkVsZJswMA BfsfcvcxleJkYDFpNuoNAtHZmmDyC3E3ImEcI72sBHXe/M9PcAUhnWMdWGvjK/QQ ZdrU4Q+HJYleg1XZcIZ77u3mpq0Py3Xkv5+Gfl/DGFvhuPWvG76h76aezEvKSMBD H4JM37hviv/vgr8+Vnzn5w2B9d2oU5LO5NEQdeq7dalJ7xeuq82CW86iUnOJm4XD ibhVEV3omSmNn+/FtW+kUU7xDSEnZxiiT8DMWQIDAQABAoIBACYUQcXbPd7LlWB/ vdW5ppafvSZoHOCnKVPVlyAcVL+AgosK7j2MSvCgtnB0XRlsAJSRhF1lITJeZIe/ NcslQqNQ77arFHLEhZLFFlCG0N5xNsBUULb0COtKcwiQLSFfkMgwDCawh5L9kwLF VUsWyBTDGo4I58Bcaq4MRaj4pdj2dxNlpsdf1Nd/FlDVYksD/bBRHBXW//eUYrEc oCYGh65JQKOeCBXL3O8imgcq5nbC0On5O1t2YfqIdmk67iFubn3IkLLm2/hvnSNl m4JvcDzq+uJA4T760s6CSayTd4tHfHS23IXQyluhqsRPd2mZaDkM45oNKBXMNIaN /EU2500CgYEA7QVO6McEq9uWlx/QrWIpMWTdHH0EUqwFEd7wDoWskdyUPX/QDYJb E088O9c1TrNpVcAIQgWy/2EEOMYmUhjrM9q5jiX2m9P745g+ij+cta4GhW+B2sfd ODiY0b7y2Z2bzUuew7UtZ3H5BAuzWcMGz558xh2J8x8DLM0YHtjgNMUCgYEA8YJf cY8ZUPz8tlYXB/NPYRBlqGMwt6x//nxnElYfquXXOMIWf7WsXNjMHcRsksF1IA3i jTLi7s+N6lBR0QO4b/ohXzbSGcxPwJF/OIcQ1+WYpdywO84Y531whKrkn6jopYlK XXChkEZAqNjCoYx19ORi1vpv3KSixK/K2u6Z+oUCgYEAl64R9X2HS8PW9xdxH8P1 wJpftd97aLyU8f7NeT2qVh3m8ARfTIXjatQGI/VxM9A01J9zqWykRDH91adY1qkD u8d5f7Jjww1B4UCIpUhYLRwVKEdXvn6w30243mFoYEMwdYyzKWNEjzKsvR4PxJ2+ OheemdpFccSi74TArtzzflECgYA4dYsMiDCZ71rA4aVmtWnUPjoVwIKOHeSaQMjz z3B9ylejLDSITJdr6T7mopnjQRi5kqVo7wGAZMM/iOFnpKAEhYwxryY047+tPBZq x6CXaFRkShBlGsxVqcyZJz5lWN5yfIMHDyKmENZi4rG1hJm6032L1mY6Rb7LjnE0 91zF5QKBgBFQqqhH1I/2hrJ9knPTYjK+e4iN/zLsE3PliL9IxFrrnc6dZ/qkQ3qL l70zC2GKJf9lVoG6T3uoS2BKz504onusYvAKTKgF/J7giEmzJE2/Eol2+ffZ1uKR jsDx84YYvFZhiPNBfSbsB6IRiH+EEl7SSasTmL/l3TgE1gfaw5S7 -----END RSA PRIVATE KEY----- telepathy-gabble-0.18.2/lib/ext/wocky/tests/certs/ca-0-crl.pem0000644000175000017500000000134712200204546024002 0ustar00smcvsmcv00000000000000-----BEGIN X509 CRL----- MIIB/DCB5QIBATANBgkqhkiG9w0BAQsFADBsMQswCQYDVQQGEwJVSzESMBAGA1UE ChMJQ29sbGFib3JhMRkwFwYDVQQLExBXb2NreSBUZXN0IFN1aXRlMREwDwYDVQQI EwhDb25mdXNlZDEbMBkGA1UEAxMSV29ja3kgWE1QUCBMaWJyYXJ5Fw0xMjA1MTAx NjQzNTBaFw0xMzA1MTAxNjQzNTBaMBQwEgIBCxcNMTIwNTEwMTY0MzUwWqAvMC0w HwYDVR0jBBgwFoAUSTAmCIya1mnNi8DMDlwCjkofpowwCgYDVR0UBAMCAQAwDQYJ KoZIhvcNAQELBQADggEBACFaj/M6g+fP0RQEiB7kvoocdM7XGUemkl9Ns/chc9zH yLgq1891jIO5GoKoCuMGEFfYat/VZutNOLFHkJ0AeqrvOSPVZ8atcZTJR/lgjR6I PN/UMFpHMEVa7cUtLPx47UvGDolrOo1d4ciLVUUPoZMRGxTitVz8KtEk+O9s6NjS W25uTGoNT58OQS51dXq4N97gNMSeggWGN1Y9swv0s992G/Y93t/uQvsRsSEMe7kj ddChE3Gb4I+7TkjL+e64RlYsAtvMePM3k3+Zk95wFGWqlwRow46Nv3F02C8Af5JV zp+tsq2foM0lIADnOTjUs2XgNGqx0Gm/hTAfBMsIgkM= -----END X509 CRL----- telepathy-gabble-0.18.2/lib/ext/wocky/tests/certs/ca-0-cert.pem0000644000175000017500000000260312200204546024153 0ustar00smcvsmcv00000000000000-----BEGIN CERTIFICATE----- MIID5jCCAtCgAwIBAgIBATALBgkqhkiG9w0BAQUwbDELMAkGA1UEBhMCVUsxEjAQ BgNVBAoTCUNvbGxhYm9yYTEZMBcGA1UECxMQV29ja3kgVGVzdCBTdWl0ZTERMA8G A1UECBMIQ29uZnVzZWQxGzAZBgNVBAMTEldvY2t5IFhNUFAgTGlicmFyeTAeFw0w OTA5MTgxMjE3MDZaFw0zNzA5MTExMjE3MDZaMGwxCzAJBgNVBAYTAlVLMRIwEAYD VQQKEwlDb2xsYWJvcmExGTAXBgNVBAsTEFdvY2t5IFRlc3QgU3VpdGUxETAPBgNV BAgTCENvbmZ1c2VkMRswGQYDVQQDExJXb2NreSBYTVBQIExpYnJhcnkwggEfMAsG CSqGSIb3DQEBAQOCAQ4AMIIBCQKCAQDfmrRZUa4UWDLgmHNRUaXIEwhwSQRESTF1 fWd+NP5xMO65OE0JAmzk4rdPH8xiuba3WnVVvA+nop53lC7emTTMVzWLWLJEV3f2 1xNeHCRWxkmzAwAF+x9y9zGV4mRgMWk26g0C0dmaYPILcTciYRwjvawEdd78z09w BSGdYx1Ya+Mr9BBl2tThD4cliV6DVdlwhnvu7eamrQ/LdeS/n4Z+X8MYW+G49a8b vqHvpp7MS8pIwEMfgkzfuG+K/++Cvz5WfOfnDYH13ahTks7k0RB16rt1qUnvF66r zYJbzqJSc4mbhcOJuFURXeiZKY2f78W1b6RRTvENISdnGKJPwMxZAgMBAAGjgZkw gZYwDwYDVR0TAQH/BAUwAwEB/zAlBgNVHREEHjAcgRpwb3N0bWFzdGVyQGNvbGxh Ym9yYS5jby51azAPBgNVHQ8BAf8EBQMDBwYAMB0GA1UdDgQWBBRJMCYIjJrWac2L wMwOXAKOSh+mjDAsBgNVHR8EJTAjMCGgH6AdhhtmaWxlOi8vL3RtcC93b2NreS10 ZXN0cy9jcmwwCwYJKoZIhvcNAQEFA4IBAQCL+FK+IzN9Z88ePw/MU2Y1Yd2oigeb /02d7DrzQQXpSN1y3t0LKpi3MM7QbGDVUmC6WI7Tpe9wf0G6ZTfZ6jMeS/4OpeG+ QNRWxAWtAv1+QDEtHf9rVFKjl9SUbKhbsLHePggS52LV/YTwD1PxbCsl75jpaCvU W22tTJL0K1lFBYLM0IxiOkn+vrWNsOYfVpXZbjB4ExLeNp1li2tetXJLluMgeGr5 bKYmWfYyze6FOcp1zQh13e6I0DqIh7F6hb/nKaWcJKSNsq8JsgLWTba8iDF24X5C 2l+SJE0iNtg08tC6XdZ9v1DQUHKIKSX7L5MEVgHbLiOKroriqVeQu8zK -----END CERTIFICATE----- telepathy-gabble-0.18.2/lib/ext/wocky/tests/certs/badwild-key.pem0000644000175000017500000001457112200204546024703 0ustar00smcvsmcv00000000000000Public Key Info: Public Key Algorithm: RSA Key Security Level: Normal modulus: 00:aa:ae:81:ce:91:0e:8d:b7:5d:fe:24:ed:ce:99: 71:60:1c:7a:79:d8:a5:9e:7a:0c:de:28:9e:ca:af: 2f:b7:ee:e6:8a:a8:ac:c4:96:b9:99:c7:07:a6:15: ef:8a:be:16:ed:14:0c:b5:d2:f5:31:38:f9:5b:e7: dd:e2:6b:12:1e:15:91:c2:8b:aa:81:6c:81:f9:46: 85:04:b8:7a:e9:78:1b:52:44:44:f4:26:bb:1d:c9: 93:24:4d:ae:c2:44:09:39:dc:ca:a5:b9:03:cd:98: a4:58:7d:49:a1:d6:6a:27:f1:6f:a6:36:e0:6f:6e: fe:4c:5f:dc:6f:10:76:a2:a3:5c:d2:37:24:7f:1b: 4c:ac:c0:25:67:ec:fa:54:98:39:aa:f0:ce:41:8f: 9e:73:99:08:a5:39:ac:a5:e2:28:ec:8f:fb:4b:46: d4:a9:c7:1b:d1:47:ff:87:97:d4:97:e6:35:3e:15: ca:80:30:16:35:53:07:0d:76:db:cc:f6:6e:7c:ad: fc:c4:85:09:59:db:9d:87:0d:53:7f:35:ad:81:63: ab:a3:bd:6f:37:fd:09:f8:25:6f:9f:00:6f:02:67: 28:8a:1a:0a:7d:80:cd:ab:e9:31:76:26:12:3b:7d: be:99:59:dc:34:05:05:c0:b9:ff:2b:14:59:6d:b6: 0b:61:e3:a9:2c:f4:05:c4:34:6b:7f:aa:a6:a9:76: 81:da:fe:b4:a0:00:69:cb:58:ef:20:b5:1c:8d:3d: e5:e3:0b:47:13:0b:37:96:9e:74:89:9f:01:6f:28: fd:a5:4e:06:c5: public exponent: 01:00:01: private exponent: 30:f2:e9:01:3f:b2:87:cb:fd:a5:c4:1b:7b:33:a0: 43:2b:07:a8:e8:0b:df:5e:2b:50:8f:1f:b3:0a:f8: c9:6c:37:2b:ae:e2:15:63:10:89:5c:7e:02:10:aa: 69:04:c8:f8:66:d6:9a:52:8c:c4:f8:0c:f3:61:94: cd:b9:5d:31:c9:87:ca:de:59:20:d2:e8:97:7f:b4: e8:3c:ab:eb:46:e9:b6:f7:23:3b:4d:dc:3d:d7:b0: 5e:29:f5:23:7b:75:95:e5:8f:2c:65:da:04:5d:71: 4c:7b:69:e1:82:e7:60:db:29:e4:e6:3a:09:5b:30: 8c:e0:b5:0c:19:11:9c:e4:f2:da:ca:bb:a9:c4:3f: 82:0f:7c:ea:c8:18:a6:a8:1d:69:bb:b3:bb:33:a0: 02:90:79:e1:78:fc:56:ca:a6:fa:fc:42:59:2f:43: c2:d6:66:88:d7:9f:83:01:60:25:bb:31:f3:67:90: 51:6f:8f:9b:54:e6:54:a3:6a:b9:e7:15:21:8f:03: bb:31:b4:63:77:c8:b6:88:b6:f2:ee:88:c7:c8:e6: 7a:3e:05:25:5f:fe:6a:54:11:0f:44:57:18:4c:34: a4:fe:01:07:65:68:bb:6c:b2:f4:df:a3:74:94:65: ba:84:37:26:6a:75:4f:38:81:46:43:51:43:21:3e: 1e:1b:8c:cd:28:41:d9:23:8b:c3:7b:b1:31:21:8e: 51:cc:7d:a7:4b:bd:ec:f7:34:45:df:e3:b1:4b:5e: 86:4d:03:3e:ea:b5:21:4c:7b:e2:08:a2:6e:94:df: 6c:c8:0d:65: prime1: 00:c9:35:da:a7:66:35:95:86:65:2d:87:e9:c4:34: c8:af:6d:8b:d7:2a:ab:cd:f5:db:52:27:f6:3b:07: 15:2c:f1:ce:e9:01:87:69:c7:50:7d:48:fc:86:ac: ac:c6:27:f3:e4:94:fd:03:dd:1a:10:dd:c4:9c:28: d6:eb:52:42:3a:32:d2:83:14:73:1a:75:a8:a5:ac: 86:76:1b:4f:84:67:4f:a7:9a:4b:d2:11:fc:0d:8f: 45:46:e1:cc:ce:a7:01:b8:eb:c5:0b:f0:90:56:0d: b1:04:a6:3f:06:a0:d8:03:be:df:7c:51:6f:14:f1: dc:3e:08:0d:a5:6c:cc:6b:68:ec:c6:d3:d2:35:cf: 28:c5:d7:36:f6:7e:71:6c:7e:1e:12:a3:e3:64:8b: 1e:94:83: prime2: 00:d9:28:88:32:c2:21:fd:24:1e:10:5c:ae:c9:f4: 85:8f:f6:a1:a9:23:39:f9:8a:c6:17:f0:a1:db:fa: f0:51:a0:66:e7:db:3b:39:c7:2f:c1:62:7d:e3:bc: 8a:27:5d:99:0c:11:69:a0:f0:8e:62:22:56:a9:c5: d5:9e:ce:6f:c0:47:9c:c8:90:86:ad:b0:2e:56:64: 35:2c:b6:1d:f1:71:bf:0e:e5:bf:24:1d:40:02:34: ba:37:00:d3:f5:19:65:3c:78:89:20:f8:72:b7:50: 0f:c9:e7:bd:5c:10:fe:b5:d0:07:66:7b:3d:e6:e0: f3:81:b9:7a:19:48:ce:fe:5e:e7:d8:65:f3:d7:9b: f9:95:98:f7:bc:44:0d:a2:24:50:3b:8e:99:f3:5b: 7d:65:17: coefficient: 24:63:2a:2a:91:68:87:30:89:7f:df:a4:22:1f:b2: 22:db:e9:4f:c3:00:87:da:7c:62:d3:ff:19:cb:6d: 01:a9:71:9e:03:4f:66:a2:92:ea:5b:e4:d6:6e:d9: 71:a9:25:49:41:a9:aa:e3:1b:ef:7f:1b:57:83:c4: 78:cd:39:47:d0:af:79:3a:a5:e1:fb:48:4a:f4:d9: 02:db:96:f0:3a:d1:3b:7b:b6:f8:28:1e:2c:1d:a2: 4c:f9:19:29:52:6c:6f:70:19:49:37:59:9d:fd:6c: 36:3e:c8:e0:50:6b:49:ff:2b:d9:31:72:c7:38:67: cc:30:49:40:06:ba:70:63:36:bd:11:19:1a:f2:d1: 2b:f3:09:cc:ed:b6:23:11:4c:9d:9d:d5:56:25:06: bc:9b: exp1: 2d:cb:78:0c:89:92:c0:89:6a:15:c6:cd:49:be:c9: be:43:ac:84:38:4e:09:fe:5f:00:7f:df:e6:e7:61: c6:6c:f2:ae:cd:8e:48:60:f8:cc:cb:03:ce:93:16: 6f:b3:40:2b:52:4d:93:c7:8a:db:33:de:3f:bc:7b: cd:eb:56:ef:70:09:c1:93:b1:ee:df:c0:96:94:81: b5:f7:d9:4d:45:46:37:db:42:4e:79:91:68:74:a7: 4b:ce:b0:92:88:28:38:32:51:12:ba:99:df:3c:16: 81:fc:64:73:d9:bf:d0:7a:6c:db:1a:9d:b1:a2:aa: 81:e3:cb:57:4f:3f:e7:62:66:21:8a:ac:59:5a:e0: e8:c6:66:d8:0b:47:3d:6e:26:e2:0f:32:fd:fb:3b: e6:89: exp2: 77:5c:ef:18:2f:5b:2d:60:87:e1:e8:7f:ee:e4:27: f2:0c:d7:a9:37:82:ab:66:9b:22:17:93:70:6e:0b: 60:62:b1:8d:aa:14:70:da:ca:a6:1a:74:26:14:c1: 3f:88:14:12:ed:13:49:72:50:61:22:8c:ce:3e:be: ff:ce:6a:e0:9e:bc:50:06:18:f1:29:91:1e:cd:6f: e9:06:a5:88:cc:43:ff:75:4b:4e:17:81:d2:74:97: 12:9a:b2:e6:db:31:a9:3e:7b:e9:92:86:c3:ba:0f: 23:a4:ec:91:c9:89:a7:f8:13:c9:41:de:b1:a4:5e: 54:a4:d5:b7:46:2c:f8:e6:c3:bb:4f:b6:eb:81:81: 7b:b8:1c:1b:f0:b0:29:ba:9a:1b:52:73:ce:af:30: 4a:bb: Public Key ID: 03:AC:54:81:78:E7:4A:E9:85:69:30:7A:AC:51:F1:B9:D1:68:19:E6 -----BEGIN RSA PRIVATE KEY----- MIIFegIBAAKCATEAqq6BzpEOjbdd/iTtzplxYBx6edilnnoM3iieyq8vt+7miqis xJa5mccHphXvir4W7RQMtdL1MTj5W+fd4msSHhWRwouqgWyB+UaFBLh66XgbUkRE 9Ca7HcmTJE2uwkQJOdzKpbkDzZikWH1JodZqJ/Fvpjbgb27+TF/cbxB2oqNc0jck fxtMrMAlZ+z6VJg5qvDOQY+ec5kIpTmspeIo7I/7S0bUqccb0Uf/h5fUl+Y1PhXK gDAWNVMHDXbbzPZufK38xIUJWdudhw1TfzWtgWOro71vN/0J+CVvnwBvAmcoihoK fYDNq+kxdiYSO32+mVncNAUFwLn/KxRZbbYLYeOpLPQFxDRrf6qmqXaB2v60oABp y1jvILUcjT3l4wtHEws3lp50iZ8Bbyj9pU4GxQIDAQABAoIBMDDy6QE/sofL/aXE G3szoEMrB6joC99eK1CPH7MK+MlsNyuu4hVjEIlcfgIQqmkEyPhm1ppSjMT4DPNh lM25XTHJh8reWSDS6Jd/tOg8q+tG6bb3IztN3D3XsF4p9SN7dZXljyxl2gRdcUx7 aeGC52DbKeTmOglbMIzgtQwZEZzk8trKu6nEP4IPfOrIGKaoHWm7s7szoAKQeeF4 /FbKpvr8QlkvQ8LWZojXn4MBYCW7MfNnkFFvj5tU5lSjarnnFSGPA7sxtGN3yLaI tvLuiMfI5no+BSVf/mpUEQ9EVxhMNKT+AQdlaLtssvTfo3SUZbqENyZqdU84gUZD UUMhPh4bjM0oQdkji8N7sTEhjlHMfadLvez3NEXf47FLXoZNAz7qtSFMe+IIom6U 32zIDWUCgZkAyTXap2Y1lYZlLYfpxDTIr22L1yqrzfXbUif2OwcVLPHO6QGHacdQ fUj8hqysxifz5JT9A90aEN3EnCjW61JCOjLSgxRzGnWopayGdhtPhGdPp5pL0hH8 DY9FRuHMzqcBuOvFC/CQVg2xBKY/BqDYA77ffFFvFPHcPggNpWzMa2jsxtPSNc8o xdc29n5xbH4eEqPjZIselIMCgZkA2SiIMsIh/SQeEFyuyfSFj/ahqSM5+YrGF/Ch 2/rwUaBm59s7OccvwWJ947yKJ12ZDBFpoPCOYiJWqcXVns5vwEecyJCGrbAuVmQ1 LLYd8XG/DuW/JB1AAjS6NwDT9RllPHiJIPhyt1APyee9XBD+tdAHZns95uDzgbl6 GUjO/l7n2GXz15v5lZj3vEQNoiRQO46Z81t9ZRcCgZgty3gMiZLAiWoVxs1Jvsm+ Q6yEOE4J/l8Af9/m52HGbPKuzY5IYPjMywPOkxZvs0ArUk2Tx4rbM94/vHvN61bv cAnBk7Hu38CWlIG199lNRUY320JOeZFodKdLzrCSiCg4MlESupnfPBaB/GRz2b/Q emzbGp2xoqqB48tXTz/nYmYhiqxZWuDoxmbYC0c9bibiDzL9+zvmiQKBmHdc7xgv Wy1gh+Hof+7kJ/IM16k3gqtmmyIXk3BuC2BisY2qFHDayqYadCYUwT+IFBLtE0ly UGEijM4+vv/OauCevFAGGPEpkR7Nb+kGpYjMQ/91S04XgdJ0lxKasubbMak+e+mS hsO6DyOk7JHJiaf4E8lB3rGkXlSk1bdGLPjmw7tPtuuBgXu4HBvwsCm6mhtSc86v MEq7AoGYJGMqKpFohzCJf9+kIh+yItvpT8MAh9p8YtP/GcttAalxngNPZqKS6lvk 1m7ZcaklSUGpquMb738bV4PEeM05R9CveTql4ftISvTZAtuW8DrRO3u2+CgeLB2i TPkZKVJsb3AZSTdZnf1sNj7I4FBrSf8r2TFyxzhnzDBJQAa6cGM2vREZGvLRK/MJ zO22IxFMnZ3VViUGvJs= -----END RSA PRIVATE KEY----- telepathy-gabble-0.18.2/lib/ext/wocky/tests/certs/badwild-cert.pem0000644000175000017500000000303212200204546025036 0ustar00smcvsmcv00000000000000-----BEGIN CERTIFICATE----- MIIEUzCCAzugAwIBAgIBAjANBgkqhkiG9w0BAQsFADBsMQswCQYDVQQGEwJVSzES MBAGA1UEChMJQ29sbGFib3JhMRkwFwYDVQQLExBXb2NreSBUZXN0IFN1aXRlMREw DwYDVQQIEwhDb25mdXNlZDEbMBkGA1UEAxMSV29ja3kgWE1QUCBMaWJyYXJ5MB4X DTEyMDUwODE3MjMxNFoXDTQwMDUwMTE3MjMxNFowaTELMAkGA1UEBhMCVUsxEjAQ BgNVBAoTCUNvbGxhYm9yYTEZMBcGA1UECxMQV29ja3kgVGVzdCBTdWl0ZTEOMAwG A1UECBMFRGF6ZWQxGzAZBgNVBAMTEldvY2t5IFhNUFAgTGlicmFyeTCCAVIwDQYJ KoZIhvcNAQEBBQADggE/ADCCAToCggExAKqugc6RDo23Xf4k7c6ZcWAcennYpZ56 DN4onsqvL7fu5oqorMSWuZnHB6YV74q+Fu0UDLXS9TE4+Vvn3eJrEh4VkcKLqoFs gflGhQS4eul4G1JERPQmux3JkyRNrsJECTncyqW5A82YpFh9SaHWaifxb6Y24G9u /kxf3G8QdqKjXNI3JH8bTKzAJWfs+lSYOarwzkGPnnOZCKU5rKXiKOyP+0tG1KnH G9FH/4eX1JfmNT4VyoAwFjVTBw1228z2bnyt/MSFCVnbnYcNU381rYFjq6O9bzf9 Cfglb58AbwJnKIoaCn2AzavpMXYmEjt9vplZ3DQFBcC5/ysUWW22C2HjqSz0BcQ0 a3+qpql2gdr+tKAAactY7yC1HI095eMLRxMLN5aedImfAW8o/aVOBsUCAwEAAaOB 0jCBzzAMBgNVHRMBAf8EAjAAMB0GA1UdJQQWMBQGCCsGAQUFBwMCBggrBgEFBQcD ATAhBgNVHREEGjAYghAqZWFzZWwtanVpY2Uub3JnhwR/AAABMA8GA1UdDwEB/wQF AwMHIAAwHQYDVR0OBBYEFAOsVIF450rphWkweqxR8bnRaBnmMB8GA1UdIwQYMBaA FEkwJgiMmtZpzYvAzA5cAo5KH6aMMCwGA1UdHwQlMCMwIaAfoB2GG2ZpbGU6Ly8v dG1wL3dvY2t5LXRlc3RzL2NybDANBgkqhkiG9w0BAQsFAAOCAQEAeJIQ1SZS3UQZ 1aBh3901WZhYozH38i7I7JclEQO0Kla2et289IAc2IiocEoeMjwE3bO+1xO4EF2N zEgGzodaIsVGol9zlrRm9CXeYgRBPkgorebFWfEigIsLY3VjrhBhV3UnDTY7Hebr tqdOX7Xa0DLud1AqGzNia2aon28qY05qu5RPgxfvpRUYnJJlT5tidSkbXJIuvbb0 roFd+es4qgQU+ZiAc6DHpREcShxd+20JJVR9zvqeM7egP3tZPiFf8u6n3CsYKQ0p be3NygY35lwc1VYxX/PMecslbsHQoa+ZWgd2+lcZ/81QD2JrmRo7nG3clsCbXodj 1c8cuflfhw== -----END CERTIFICATE----- telepathy-gabble-0.18.2/lib/ext/wocky/tests/certs/wild-cert.cfg0000644000175000017500000000415412200204546024353 0ustar00smcvsmcv00000000000000# X.509 Certificate options # # DN options # The organization of the subject. organization = "Collabora" # The organizational unit of the subject. unit = "Wocky Test Suite" # The locality of the subject. # locality = # The state of the certificate owner. state = "Dazed" # The country of the subject. Two letter code. country = UK # The common name of the certificate owner. cn = "Wocky XMPP Library" # A user id of the certificate owner. #uid = "clauper" # If the supported DN OIDs are not adequate you can set # any OID here. # For example set the X.520 Title and the X.520 Pseudonym # by using OID and string pairs. #dn_oid = "2.5.4.12" "Dr." "2.5.4.65" "jackal" # This is deprecated and should not be used in new # certificates. # pkcs9_email = "none@none.org" # The serial number of the certificate serial = 002 # In how many days, counting from today, this certificate will expire. expiration_days = 10220 # X.509 v3 extensions # A dnsname in case of a WWW server. dns_name = "*.weasel-juice.org" #dns_name = "www.morethanone.org" # An IP address in case of a server. ip_address = "127.0.0.1" # An email in case of a person #email = "postmaster@collabora.co.uk" # An URL that has CRLs (certificate revocation lists) # available. Needed in CA certificates. #crl_dist_points = "file:///tmp/wocky-tests/crl" # Whether this is a CA certificate or not #ca # Whether this certificate will be used for a TLS client tls_www_client # Whether this certificate will be used for a TLS server tls_www_server # Whether this certificate will be used to sign data (needed # in TLS DHE ciphersuites). #signing_key # Whether this certificate will be used to encrypt data (needed # in TLS RSA ciphersuites). Note that it is prefered to use different # keys for encryption and signing. encryption_key # Whether this key will be used to sign other certificates. #cert_signing_key # Whether this key will be used to sign CRLs. #crl_signing_key # Whether this key will be used to sign code. #code_signing_key # Whether this key will be used to sign OCSP data. #ocsp_signing_key # Whether this key will be used for time stamping. time_stamping_key telepathy-gabble-0.18.2/lib/ext/wocky/tests/certs/unknown-cert.cfg0000644000175000017500000000415212200204546025111 0ustar00smcvsmcv00000000000000# X.509 Certificate options # # DN options # The organization of the subject. organization = "Collabora" # The organizational unit of the subject. unit = "Wocky Test Suite" # The locality of the subject. # locality = # The state of the certificate owner. state = "Dazed" # The country of the subject. Two letter code. country = UK # The common name of the certificate owner. cn = "Wocky XMPP Library" # A user id of the certificate owner. #uid = "clauper" # If the supported DN OIDs are not adequate you can set # any OID here. # For example set the X.520 Title and the X.520 Pseudonym # by using OID and string pairs. #dn_oid = "2.5.4.12" "Dr." "2.5.4.65" "jackal" # This is deprecated and should not be used in new # certificates. # pkcs9_email = "none@none.org" # The serial number of the certificate serial = 002 # In how many days, counting from today, this certificate will expire. expiration_days = 10220 # X.509 v3 extensions # A dnsname in case of a WWW server. dns_name = "weasel-juice.org" #dns_name = "www.morethanone.org" # An IP address in case of a server. ip_address = "127.0.0.1" # An email in case of a person #email = "postmaster@collabora.co.uk" # An URL that has CRLs (certificate revocation lists) # available. Needed in CA certificates. #crl_dist_points = "file:///tmp/wocky-tests/crl" # Whether this is a CA certificate or not #ca # Whether this certificate will be used for a TLS client tls_www_client # Whether this certificate will be used for a TLS server tls_www_server # Whether this certificate will be used to sign data (needed # in TLS DHE ciphersuites). #signing_key # Whether this certificate will be used to encrypt data (needed # in TLS RSA ciphersuites). Note that it is prefered to use different # keys for encryption and signing. encryption_key # Whether this key will be used to sign other certificates. #cert_signing_key # Whether this key will be used to sign CRLs. #crl_signing_key # Whether this key will be used to sign code. #code_signing_key # Whether this key will be used to sign OCSP data. #ocsp_signing_key # Whether this key will be used for time stamping. time_stamping_key telepathy-gabble-0.18.2/lib/ext/wocky/tests/certs/unknown-ca-cert.cfg0000644000175000017500000000416012200204546025471 0ustar00smcvsmcv00000000000000# X.509 Certificate options # # DN options # The organization of the subject. organization = "Collabora" # The organizational unit of the subject. unit = "Wocky Test Suite" # The locality of the subject. # locality = # The state of the certificate owner. state = "Lost-in-Space" # The country of the subject. Two letter code. country = UK # The common name of the certificate owner. cn = "Wocky XMPP Library" # A user id of the certificate owner. #uid = "clauper" # If the supported DN OIDs are not adequate you can set # any OID here. # For example set the X.520 Title and the X.520 Pseudonym # by using OID and string pairs. #dn_oid = "2.5.4.12" "Dr." "2.5.4.65" "jackal" # This is deprecated and should not be used in new # certificates. # pkcs9_email = "none@none.org" # The serial number of the certificate serial = 001 # In how many days, counting from today, this certificate will expire. expiration_days = 10220 # X.509 v3 extensions # A dnsname in case of a WWW server. #dns_name = "www.none.org" #dns_name = "www.morethanone.org" # An IP address in case of a server. #ip_address = "192.168.1.1" # An email in case of a person email = "postmaster@collabora.co.uk" # An URL that has CRLs (certificate revocation lists) # available. Needed in CA certificates. crl_dist_points = "file:///tmp/wocky-tests/crl" # Whether this is a CA certificate or not ca # Whether this certificate will be used for a TLS client #tls_www_client # Whether this certificate will be used for a TLS server #tls_www_server # Whether this certificate will be used to sign data (needed # in TLS DHE ciphersuites). signing_key # Whether this certificate will be used to encrypt data (needed # in TLS RSA ciphersuites). Note that it is prefered to use different # keys for encryption and signing. #encryption_key # Whether this key will be used to sign other certificates. cert_signing_key # Whether this key will be used to sign CRLs. crl_signing_key # Whether this key will be used to sign code. #code_signing_key # Whether this key will be used to sign OCSP data. #ocsp_signing_key # Whether this key will be used for time stamping. #time_stamping_key telepathy-gabble-0.18.2/lib/ext/wocky/tests/certs/tls-cert.cfg0000644000175000017500000000415212200204546024214 0ustar00smcvsmcv00000000000000# X.509 Certificate options # # DN options # The organization of the subject. organization = "Collabora" # The organizational unit of the subject. unit = "Wocky Test Suite" # The locality of the subject. # locality = # The state of the certificate owner. state = "Dazed" # The country of the subject. Two letter code. country = UK # The common name of the certificate owner. cn = "Wocky XMPP Library" # A user id of the certificate owner. #uid = "clauper" # If the supported DN OIDs are not adequate you can set # any OID here. # For example set the X.520 Title and the X.520 Pseudonym # by using OID and string pairs. #dn_oid = "2.5.4.12" "Dr." "2.5.4.65" "jackal" # This is deprecated and should not be used in new # certificates. # pkcs9_email = "none@none.org" # The serial number of the certificate serial = 002 # In how many days, counting from today, this certificate will expire. expiration_days = 10220 # X.509 v3 extensions # A dnsname in case of a WWW server. dns_name = "weasel-juice.org" #dns_name = "www.morethanone.org" # An IP address in case of a server. ip_address = "127.0.0.1" # An email in case of a person #email = "postmaster@collabora.co.uk" # An URL that has CRLs (certificate revocation lists) # available. Needed in CA certificates. #crl_dist_points = "file:///tmp/wocky-tests/crl" # Whether this is a CA certificate or not #ca # Whether this certificate will be used for a TLS client tls_www_client # Whether this certificate will be used for a TLS server tls_www_server # Whether this certificate will be used to sign data (needed # in TLS DHE ciphersuites). #signing_key # Whether this certificate will be used to encrypt data (needed # in TLS RSA ciphersuites). Note that it is prefered to use different # keys for encryption and signing. encryption_key # Whether this key will be used to sign other certificates. #cert_signing_key # Whether this key will be used to sign CRLs. #crl_signing_key # Whether this key will be used to sign code. #code_signing_key # Whether this key will be used to sign OCSP data. #ocsp_signing_key # Whether this key will be used for time stamping. time_stamping_key telepathy-gabble-0.18.2/lib/ext/wocky/tests/certs/ss-cert.cfg0000644000175000017500000000415012200204546024035 0ustar00smcvsmcv00000000000000# X.509 Certificate options # # DN options # The organization of the subject. organization = "Collabora" # The organizational unit of the subject. unit = "Wocky Test Suite" # The locality of the subject. # locality = # The state of the certificate owner. state = "Confused" # The country of the subject. Two letter code. country = UK # The common name of the certificate owner. cn = "Wocky XMPP Library" # A user id of the certificate owner. #uid = "clauper" # If the supported DN OIDs are not adequate you can set # any OID here. # For example set the X.520 Title and the X.520 Pseudonym # by using OID and string pairs. #dn_oid = "2.5.4.12" "Dr." "2.5.4.65" "jackal" # This is deprecated and should not be used in new # certificates. # pkcs9_email = "none@none.org" # The serial number of the certificate serial = 001 # In how many days, counting from today, this certificate will expire. expiration_days = 10220 # X.509 v3 extensions # A dnsname in case of a WWW server. dns_name = "weasel-juice.org" #dns_name = "www.morethanone.org" # An IP address in case of a server. ip_address = "127.0.0.1" # An email in case of a person email = "postmaster@collabora.co.uk" # An URL that has CRLs (certificate revocation lists) # available. Needed in CA certificates. crl_dist_points = "file:///tmp/wocky-tests/crl" # Whether this is a CA certificate or not ca # Whether this certificate will be used for a TLS client tls_www_client # Whether this certificate will be used for a TLS server tls_www_server # Whether this certificate will be used to sign data (needed # in TLS DHE ciphersuites). signing_key # Whether this certificate will be used to encrypt data (needed # in TLS RSA ciphersuites). Note that it is prefered to use different # keys for encryption and signing. encryption_key # Whether this key will be used to sign other certificates. cert_signing_key # Whether this key will be used to sign CRLs. crl_signing_key # Whether this key will be used to sign code. #code_signing_key # Whether this key will be used to sign OCSP data. #ocsp_signing_key # Whether this key will be used for time stamping. #time_stamping_key telepathy-gabble-0.18.2/lib/ext/wocky/tests/certs/rev-cert.cfg0000644000175000017500000000415212200204546024206 0ustar00smcvsmcv00000000000000# X.509 Certificate options # # DN options # The organization of the subject. organization = "Collabora" # The organizational unit of the subject. unit = "Wocky Test Suite" # The locality of the subject. # locality = # The state of the certificate owner. state = "Dazed" # The country of the subject. Two letter code. country = UK # The common name of the certificate owner. cn = "Wocky XMPP Library" # A user id of the certificate owner. #uid = "clauper" # If the supported DN OIDs are not adequate you can set # any OID here. # For example set the X.520 Title and the X.520 Pseudonym # by using OID and string pairs. #dn_oid = "2.5.4.12" "Dr." "2.5.4.65" "jackal" # This is deprecated and should not be used in new # certificates. # pkcs9_email = "none@none.org" # The serial number of the certificate serial = 013 # In how many days, counting from today, this certificate will expire. expiration_days = 10220 # X.509 v3 extensions # A dnsname in case of a WWW server. dns_name = "weasel-juice.org" #dns_name = "www.morethanone.org" # An IP address in case of a server. ip_address = "127.0.0.1" # An email in case of a person #email = "postmaster@collabora.co.uk" # An URL that has CRLs (certificate revocation lists) # available. Needed in CA certificates. #crl_dist_points = "file:///tmp/wocky-tests/crl" # Whether this is a CA certificate or not #ca # Whether this certificate will be used for a TLS client tls_www_client # Whether this certificate will be used for a TLS server tls_www_server # Whether this certificate will be used to sign data (needed # in TLS DHE ciphersuites). #signing_key # Whether this certificate will be used to encrypt data (needed # in TLS RSA ciphersuites). Note that it is prefered to use different # keys for encryption and signing. encryption_key # Whether this key will be used to sign other certificates. #cert_signing_key # Whether this key will be used to sign CRLs. #crl_signing_key # Whether this key will be used to sign code. #code_signing_key # Whether this key will be used to sign OCSP data. #ocsp_signing_key # Whether this key will be used for time stamping. time_stamping_key telepathy-gabble-0.18.2/lib/ext/wocky/tests/certs/new-cert.cfg0000644000175000017500000000414612200204546024206 0ustar00smcvsmcv00000000000000# X.509 Certificate options # # DN options # The organization of the subject. organization = "Collabora" # The organizational unit of the subject. unit = "Wocky Test Suite" # The locality of the subject. # locality = # The state of the certificate owner. state = "Dazed" # The country of the subject. Two letter code. country = UK # The common name of the certificate owner. cn = "Wocky XMPP Library" # A user id of the certificate owner. #uid = "clauper" # If the supported DN OIDs are not adequate you can set # any OID here. # For example set the X.520 Title and the X.520 Pseudonym # by using OID and string pairs. #dn_oid = "2.5.4.12" "Dr." "2.5.4.65" "jackal" # This is deprecated and should not be used in new # certificates. # pkcs9_email = "none@none.org" # The serial number of the certificate serial = 003 # In how many days, counting from today, this certificate will expire. expiration_days = 7 # X.509 v3 extensions # A dnsname in case of a WWW server. dns_name = "weasel-juice.org" #dns_name = "www.morethanone.org" # An IP address in case of a server. ip_address = "127.0.0.1" # An email in case of a person #email = "postmaster@collabora.co.uk" # An URL that has CRLs (certificate revocation lists) # available. Needed in CA certificates. #crl_dist_points = "file:///tmp/wocky-tests/crl" # Whether this is a CA certificate or not #ca # Whether this certificate will be used for a TLS client tls_www_client # Whether this certificate will be used for a TLS server tls_www_server # Whether this certificate will be used to sign data (needed # in TLS DHE ciphersuites). #signing_key # Whether this certificate will be used to encrypt data (needed # in TLS RSA ciphersuites). Note that it is prefered to use different # keys for encryption and signing. encryption_key # Whether this key will be used to sign other certificates. #cert_signing_key # Whether this key will be used to sign CRLs. #crl_signing_key # Whether this key will be used to sign code. #code_signing_key # Whether this key will be used to sign OCSP data. #ocsp_signing_key # Whether this key will be used for time stamping. time_stamping_key telepathy-gabble-0.18.2/lib/ext/wocky/tests/certs/exp-cert.cfg0000644000175000017500000000414612200204546024211 0ustar00smcvsmcv00000000000000# X.509 Certificate options # # DN options # The organization of the subject. organization = "Collabora" # The organizational unit of the subject. unit = "Wocky Test Suite" # The locality of the subject. # locality = # The state of the certificate owner. state = "Dazed" # The country of the subject. Two letter code. country = UK # The common name of the certificate owner. cn = "Wocky XMPP Library" # A user id of the certificate owner. #uid = "clauper" # If the supported DN OIDs are not adequate you can set # any OID here. # For example set the X.520 Title and the X.520 Pseudonym # by using OID and string pairs. #dn_oid = "2.5.4.12" "Dr." "2.5.4.65" "jackal" # This is deprecated and should not be used in new # certificates. # pkcs9_email = "none@none.org" # The serial number of the certificate serial = 002 # In how many days, counting from today, this certificate will expire. expiration_days = 1 # X.509 v3 extensions # A dnsname in case of a WWW server. dns_name = "weasel-juice.org" #dns_name = "www.morethanone.org" # An IP address in case of a server. ip_address = "127.0.0.1" # An email in case of a person #email = "postmaster@collabora.co.uk" # An URL that has CRLs (certificate revocation lists) # available. Needed in CA certificates. #crl_dist_points = "file:///tmp/wocky-tests/crl" # Whether this is a CA certificate or not #ca # Whether this certificate will be used for a TLS client tls_www_client # Whether this certificate will be used for a TLS server tls_www_server # Whether this certificate will be used to sign data (needed # in TLS DHE ciphersuites). #signing_key # Whether this certificate will be used to encrypt data (needed # in TLS RSA ciphersuites). Note that it is prefered to use different # keys for encryption and signing. encryption_key # Whether this key will be used to sign other certificates. #cert_signing_key # Whether this key will be used to sign CRLs. #crl_signing_key # Whether this key will be used to sign code. #code_signing_key # Whether this key will be used to sign OCSP data. #ocsp_signing_key # Whether this key will be used for time stamping. time_stamping_key telepathy-gabble-0.18.2/lib/ext/wocky/tests/certs/ca-2-cert.cfg0000644000175000017500000000415312200204546024135 0ustar00smcvsmcv00000000000000# X.509 Certificate options # # DN options # The organization of the subject. organization = "Collabora" # The organizational unit of the subject. unit = "Wocky Test Suite" # The locality of the subject. # locality = # The state of the certificate owner. state = "Spindled" # The country of the subject. Two letter code. country = UK # The common name of the certificate owner. cn = "Wocky XMPP Library" # A user id of the certificate owner. #uid = "clauper" # If the supported DN OIDs are not adequate you can set # any OID here. # For example set the X.520 Title and the X.520 Pseudonym # by using OID and string pairs. #dn_oid = "2.5.4.12" "Dr." "2.5.4.65" "jackal" # This is deprecated and should not be used in new # certificates. # pkcs9_email = "none@none.org" # The serial number of the certificate serial = 001 # In how many days, counting from today, this certificate will expire. expiration_days = 10220 # X.509 v3 extensions # A dnsname in case of a WWW server. #dns_name = "www.none.org" #dns_name = "www.morethanone.org" # An IP address in case of a server. #ip_address = "192.168.1.1" # An email in case of a person email = "postmaster@collabora.co.uk" # An URL that has CRLs (certificate revocation lists) # available. Needed in CA certificates. crl_dist_points = "file:///tmp/wocky-tests/crl" # Whether this is a CA certificate or not ca # Whether this certificate will be used for a TLS client #tls_www_client # Whether this certificate will be used for a TLS server #tls_www_server # Whether this certificate will be used to sign data (needed # in TLS DHE ciphersuites). signing_key # Whether this certificate will be used to encrypt data (needed # in TLS RSA ciphersuites). Note that it is prefered to use different # keys for encryption and signing. #encryption_key # Whether this key will be used to sign other certificates. cert_signing_key # Whether this key will be used to sign CRLs. crl_signing_key # Whether this key will be used to sign code. #code_signing_key # Whether this key will be used to sign OCSP data. #ocsp_signing_key # Whether this key will be used for time stamping. #time_stamping_key telepathy-gabble-0.18.2/lib/ext/wocky/tests/certs/ca-1-cert.cfg0000644000175000017500000000415112200204546024132 0ustar00smcvsmcv00000000000000# X.509 Certificate options # # DN options # The organization of the subject. organization = "Collabora" # The organizational unit of the subject. unit = "Wocky Test Suite" # The locality of the subject. # locality = # The state of the certificate owner. state = "Folded" # The country of the subject. Two letter code. country = UK # The common name of the certificate owner. cn = "Wocky XMPP Library" # A user id of the certificate owner. #uid = "clauper" # If the supported DN OIDs are not adequate you can set # any OID here. # For example set the X.520 Title and the X.520 Pseudonym # by using OID and string pairs. #dn_oid = "2.5.4.12" "Dr." "2.5.4.65" "jackal" # This is deprecated and should not be used in new # certificates. # pkcs9_email = "none@none.org" # The serial number of the certificate serial = 001 # In how many days, counting from today, this certificate will expire. expiration_days = 10220 # X.509 v3 extensions # A dnsname in case of a WWW server. #dns_name = "www.none.org" #dns_name = "www.morethanone.org" # An IP address in case of a server. #ip_address = "192.168.1.1" # An email in case of a person email = "postmaster@collabora.co.uk" # An URL that has CRLs (certificate revocation lists) # available. Needed in CA certificates. crl_dist_points = "file:///tmp/wocky-tests/crl" # Whether this is a CA certificate or not ca # Whether this certificate will be used for a TLS client #tls_www_client # Whether this certificate will be used for a TLS server #tls_www_server # Whether this certificate will be used to sign data (needed # in TLS DHE ciphersuites). signing_key # Whether this certificate will be used to encrypt data (needed # in TLS RSA ciphersuites). Note that it is prefered to use different # keys for encryption and signing. #encryption_key # Whether this key will be used to sign other certificates. cert_signing_key # Whether this key will be used to sign CRLs. crl_signing_key # Whether this key will be used to sign code. #code_signing_key # Whether this key will be used to sign OCSP data. #ocsp_signing_key # Whether this key will be used for time stamping. #time_stamping_key telepathy-gabble-0.18.2/lib/ext/wocky/tests/certs/ca-0-crl.cfg0000644000175000017500000000415412200204546023757 0ustar00smcvsmcv00000000000000# X.509 Certificate options # # DN options # The organization of the subject. organization = "Collabora" # The organizational unit of the subject. unit = "Wocky Test Suite" # The locality of the subject. # locality = # The state of the certificate owner. state = "Confused" # The country of the subject. Two letter code. country = UK # The common name of the certificate owner. cn = "Wocky XMPP Library" # A user id of the certificate owner. #uid = "clauper" # If the supported DN OIDs are not adequate you can set # any OID here. # For example set the X.520 Title and the X.520 Pseudonym # by using OID and string pairs. #dn_oid = "2.5.4.12" "Dr." "2.5.4.65" "jackal" # This is deprecated and should not be used in new # certificates. # pkcs9_email = "none@none.org" # The serial number of the certificate serial = 001 # In how many days, counting from today, this certificate will expire. expiration_days = 10220 # X.509 v3 extensions # A dnsname in case of a WWW server. #dns_name = "www.none.org" #dns_name = "www.morethanone.org" # An IP address in case of a server. #ip_address = "192.168.1.1" # An email in case of a person email = "postmaster@collabora.co.uk" # An URL that has CRLs (certificate revocation lists) # available. Needed in CA certificates. crl_dist_points = "file:///tmp/wocky-tests/crl" # Whether this is a CA certificate or not #ca # Whether this certificate will be used for a TLS client #tls_www_client # Whether this certificate will be used for a TLS server #tls_www_server # Whether this certificate will be used to sign data (needed # in TLS DHE ciphersuites). signing_key # Whether this certificate will be used to encrypt data (needed # in TLS RSA ciphersuites). Note that it is prefered to use different # keys for encryption and signing. #encryption_key # Whether this key will be used to sign other certificates. cert_signing_key # Whether this key will be used to sign CRLs. crl_signing_key # Whether this key will be used to sign code. #code_signing_key # Whether this key will be used to sign OCSP data. #ocsp_signing_key # Whether this key will be used for time stamping. #time_stamping_key telepathy-gabble-0.18.2/lib/ext/wocky/tests/certs/ca-0-cert.cfg0000644000175000017500000000415312200204546024133 0ustar00smcvsmcv00000000000000# X.509 Certificate options # # DN options # The organization of the subject. organization = "Collabora" # The organizational unit of the subject. unit = "Wocky Test Suite" # The locality of the subject. # locality = # The state of the certificate owner. state = "Confused" # The country of the subject. Two letter code. country = UK # The common name of the certificate owner. cn = "Wocky XMPP Library" # A user id of the certificate owner. #uid = "clauper" # If the supported DN OIDs are not adequate you can set # any OID here. # For example set the X.520 Title and the X.520 Pseudonym # by using OID and string pairs. #dn_oid = "2.5.4.12" "Dr." "2.5.4.65" "jackal" # This is deprecated and should not be used in new # certificates. # pkcs9_email = "none@none.org" # The serial number of the certificate serial = 001 # In how many days, counting from today, this certificate will expire. expiration_days = 10220 # X.509 v3 extensions # A dnsname in case of a WWW server. #dns_name = "www.none.org" #dns_name = "www.morethanone.org" # An IP address in case of a server. #ip_address = "192.168.1.1" # An email in case of a person email = "postmaster@collabora.co.uk" # An URL that has CRLs (certificate revocation lists) # available. Needed in CA certificates. crl_dist_points = "file:///tmp/wocky-tests/crl" # Whether this is a CA certificate or not ca # Whether this certificate will be used for a TLS client #tls_www_client # Whether this certificate will be used for a TLS server #tls_www_server # Whether this certificate will be used to sign data (needed # in TLS DHE ciphersuites). signing_key # Whether this certificate will be used to encrypt data (needed # in TLS RSA ciphersuites). Note that it is prefered to use different # keys for encryption and signing. #encryption_key # Whether this key will be used to sign other certificates. cert_signing_key # Whether this key will be used to sign CRLs. crl_signing_key # Whether this key will be used to sign code. #code_signing_key # Whether this key will be used to sign OCSP data. #ocsp_signing_key # Whether this key will be used for time stamping. #time_stamping_key telepathy-gabble-0.18.2/lib/ext/wocky/tests/certs/badwild-cert.cfg0000644000175000017500000000415212200204546025020 0ustar00smcvsmcv00000000000000# X.509 Certificate options # # DN options # The organization of the subject. organization = "Collabora" # The organizational unit of the subject. unit = "Wocky Test Suite" # The locality of the subject. # locality = # The state of the certificate owner. state = "Dazed" # The country of the subject. Two letter code. country = UK # The common name of the certificate owner. cn = "Wocky XMPP Library" # A user id of the certificate owner. #uid = "clauper" # If the supported DN OIDs are not adequate you can set # any OID here. # For example set the X.520 Title and the X.520 Pseudonym # by using OID and string pairs. #dn_oid = "2.5.4.12" "Dr." "2.5.4.65" "jackal" # This is deprecated and should not be used in new # certificates. # pkcs9_email = "none@none.org" # The serial number of the certificate serial = 002 # In how many days, counting from today, this certificate will expire. expiration_days = 10220 # X.509 v3 extensions # A dnsname in case of a WWW server. dns_name = "*easel-juice.org" #dns_name = "www.morethanone.org" # An IP address in case of a server. ip_address = "127.0.0.1" # An email in case of a person #email = "postmaster@collabora.co.uk" # An URL that has CRLs (certificate revocation lists) # available. Needed in CA certificates. #crl_dist_points = "file:///tmp/wocky-tests/crl" # Whether this is a CA certificate or not #ca # Whether this certificate will be used for a TLS client tls_www_client # Whether this certificate will be used for a TLS server tls_www_server # Whether this certificate will be used to sign data (needed # in TLS DHE ciphersuites). #signing_key # Whether this certificate will be used to encrypt data (needed # in TLS RSA ciphersuites). Note that it is prefered to use different # keys for encryption and signing. encryption_key # Whether this key will be used to sign other certificates. #cert_signing_key # Whether this key will be used to sign CRLs. #crl_signing_key # Whether this key will be used to sign code. #code_signing_key # Whether this key will be used to sign OCSP data. #ocsp_signing_key # Whether this key will be used for time stamping. time_stamping_key telepathy-gabble-0.18.2/lib/ext/wocky/tests/certs/cas/0000755000175000017500000000000012312537050022545 5ustar00smcvsmcv00000000000000telepathy-gabble-0.18.2/lib/ext/wocky/tests/certs/cas/e7df1717.00000644000175000017500000000260312200204546023771 0ustar00smcvsmcv00000000000000-----BEGIN CERTIFICATE----- MIID5jCCAtCgAwIBAgIBATALBgkqhkiG9w0BAQUwbDELMAkGA1UEBhMCVUsxEjAQ BgNVBAoTCUNvbGxhYm9yYTEZMBcGA1UECxMQV29ja3kgVGVzdCBTdWl0ZTERMA8G A1UECBMIQ29uZnVzZWQxGzAZBgNVBAMTEldvY2t5IFhNUFAgTGlicmFyeTAeFw0w OTA5MTgxMjE3MDZaFw0zNzA5MTExMjE3MDZaMGwxCzAJBgNVBAYTAlVLMRIwEAYD VQQKEwlDb2xsYWJvcmExGTAXBgNVBAsTEFdvY2t5IFRlc3QgU3VpdGUxETAPBgNV BAgTCENvbmZ1c2VkMRswGQYDVQQDExJXb2NreSBYTVBQIExpYnJhcnkwggEfMAsG CSqGSIb3DQEBAQOCAQ4AMIIBCQKCAQDfmrRZUa4UWDLgmHNRUaXIEwhwSQRESTF1 fWd+NP5xMO65OE0JAmzk4rdPH8xiuba3WnVVvA+nop53lC7emTTMVzWLWLJEV3f2 1xNeHCRWxkmzAwAF+x9y9zGV4mRgMWk26g0C0dmaYPILcTciYRwjvawEdd78z09w BSGdYx1Ya+Mr9BBl2tThD4cliV6DVdlwhnvu7eamrQ/LdeS/n4Z+X8MYW+G49a8b vqHvpp7MS8pIwEMfgkzfuG+K/++Cvz5WfOfnDYH13ahTks7k0RB16rt1qUnvF66r zYJbzqJSc4mbhcOJuFURXeiZKY2f78W1b6RRTvENISdnGKJPwMxZAgMBAAGjgZkw gZYwDwYDVR0TAQH/BAUwAwEB/zAlBgNVHREEHjAcgRpwb3N0bWFzdGVyQGNvbGxh Ym9yYS5jby51azAPBgNVHQ8BAf8EBQMDBwYAMB0GA1UdDgQWBBRJMCYIjJrWac2L wMwOXAKOSh+mjDAsBgNVHR8EJTAjMCGgH6AdhhtmaWxlOi8vL3RtcC93b2NreS10 ZXN0cy9jcmwwCwYJKoZIhvcNAQEFA4IBAQCL+FK+IzN9Z88ePw/MU2Y1Yd2oigeb /02d7DrzQQXpSN1y3t0LKpi3MM7QbGDVUmC6WI7Tpe9wf0G6ZTfZ6jMeS/4OpeG+ QNRWxAWtAv1+QDEtHf9rVFKjl9SUbKhbsLHePggS52LV/YTwD1PxbCsl75jpaCvU W22tTJL0K1lFBYLM0IxiOkn+vrWNsOYfVpXZbjB4ExLeNp1li2tetXJLluMgeGr5 bKYmWfYyze6FOcp1zQh13e6I0DqIh7F6hb/nKaWcJKSNsq8JsgLWTba8iDF24X5C 2l+SJE0iNtg08tC6XdZ9v1DQUHKIKSX7L5MEVgHbLiOKroriqVeQu8zK -----END CERTIFICATE----- telepathy-gabble-0.18.2/lib/ext/wocky/tests/certs/cas/c5d5c0da.00000644000175000017500000000260312200204546024114 0ustar00smcvsmcv00000000000000-----BEGIN CERTIFICATE----- MIID5jCCAtCgAwIBAgIBATALBgkqhkiG9w0BAQUwbDELMAkGA1UEBhMCVUsxEjAQ BgNVBAoTCUNvbGxhYm9yYTEZMBcGA1UECxMQV29ja3kgVGVzdCBTdWl0ZTERMA8G A1UECBMIQ29uZnVzZWQxGzAZBgNVBAMTEldvY2t5IFhNUFAgTGlicmFyeTAeFw0w OTA5MTgxMjE3MDZaFw0zNzA5MTExMjE3MDZaMGwxCzAJBgNVBAYTAlVLMRIwEAYD VQQKEwlDb2xsYWJvcmExGTAXBgNVBAsTEFdvY2t5IFRlc3QgU3VpdGUxETAPBgNV BAgTCENvbmZ1c2VkMRswGQYDVQQDExJXb2NreSBYTVBQIExpYnJhcnkwggEfMAsG CSqGSIb3DQEBAQOCAQ4AMIIBCQKCAQDfmrRZUa4UWDLgmHNRUaXIEwhwSQRESTF1 fWd+NP5xMO65OE0JAmzk4rdPH8xiuba3WnVVvA+nop53lC7emTTMVzWLWLJEV3f2 1xNeHCRWxkmzAwAF+x9y9zGV4mRgMWk26g0C0dmaYPILcTciYRwjvawEdd78z09w BSGdYx1Ya+Mr9BBl2tThD4cliV6DVdlwhnvu7eamrQ/LdeS/n4Z+X8MYW+G49a8b vqHvpp7MS8pIwEMfgkzfuG+K/++Cvz5WfOfnDYH13ahTks7k0RB16rt1qUnvF66r zYJbzqJSc4mbhcOJuFURXeiZKY2f78W1b6RRTvENISdnGKJPwMxZAgMBAAGjgZkw gZYwDwYDVR0TAQH/BAUwAwEB/zAlBgNVHREEHjAcgRpwb3N0bWFzdGVyQGNvbGxh Ym9yYS5jby51azAPBgNVHQ8BAf8EBQMDBwYAMB0GA1UdDgQWBBRJMCYIjJrWac2L wMwOXAKOSh+mjDAsBgNVHR8EJTAjMCGgH6AdhhtmaWxlOi8vL3RtcC93b2NreS10 ZXN0cy9jcmwwCwYJKoZIhvcNAQEFA4IBAQCL+FK+IzN9Z88ePw/MU2Y1Yd2oigeb /02d7DrzQQXpSN1y3t0LKpi3MM7QbGDVUmC6WI7Tpe9wf0G6ZTfZ6jMeS/4OpeG+ QNRWxAWtAv1+QDEtHf9rVFKjl9SUbKhbsLHePggS52LV/YTwD1PxbCsl75jpaCvU W22tTJL0K1lFBYLM0IxiOkn+vrWNsOYfVpXZbjB4ExLeNp1li2tetXJLluMgeGr5 bKYmWfYyze6FOcp1zQh13e6I0DqIh7F6hb/nKaWcJKSNsq8JsgLWTba8iDF24X5C 2l+SJE0iNtg08tC6XdZ9v1DQUHKIKSX7L5MEVgHbLiOKroriqVeQu8zK -----END CERTIFICATE----- telepathy-gabble-0.18.2/lib/ext/wocky/tests/certs/cas/bb7d74ae.00000644000175000017500000000257712200204546024135 0ustar00smcvsmcv00000000000000-----BEGIN CERTIFICATE----- MIID4jCCAsygAwIBAgIBATALBgkqhkiG9w0BAQUwajELMAkGA1UEBhMCVUsxEjAQ BgNVBAoTCUNvbGxhYm9yYTEZMBcGA1UECxMQV29ja3kgVGVzdCBTdWl0ZTEPMA0G A1UECBMGRm9sZGVkMRswGQYDVQQDExJXb2NreSBYTVBQIExpYnJhcnkwHhcNMDkw OTE4MTI1NTAzWhcNMzcwOTExMTI1NTAzWjBqMQswCQYDVQQGEwJVSzESMBAGA1UE ChMJQ29sbGFib3JhMRkwFwYDVQQLExBXb2NreSBUZXN0IFN1aXRlMQ8wDQYDVQQI EwZGb2xkZWQxGzAZBgNVBAMTEldvY2t5IFhNUFAgTGlicmFyeTCCAR8wCwYJKoZI hvcNAQEBA4IBDgAwggEJAoIBANRkYbvqWdla+zNLsftXYnaAD0agDbNl70y4yxNj V1DriZnNgjgWM8ZIgUdvFx+sA8wMoRFBLnXWYfzI+fHYFak+JqysvQmqTifXjKOs Q8Qdhb3hc2z10X7pobRK+oxE7VXDKKp5ou4f3wx1mvSTjB1oTr5o2ZnA5K/dLCM7 OmJ2C/Vj8z6l6HV41esPEYEZv+wfP/9z3xmNhNHcRWIeipEdDZDfXc0n22SeEnGS ZrSJDP9CZaJWAcLUhD5OEh+B3P/gfO9KDAN8PicSFFXj2X0t8incmz5fs6JosgEF o40D/R4xUIU562HEY/4uOM6N2nHM+MKgiDHEUh9s3lLmSB8CAwEAAaOBmTCBljAP BgNVHRMBAf8EBTADAQH/MCUGA1UdEQQeMByBGnBvc3RtYXN0ZXJAY29sbGFib3Jh LmNvLnVrMA8GA1UdDwEB/wQFAwMHBgAwHQYDVR0OBBYEFJdQenuGG7TS4L2av7+3 HHWC+uO7MCwGA1UdHwQlMCMwIaAfoB2GG2ZpbGU6Ly8vdG1wL3dvY2t5LXRlc3Rz L2NybDALBgkqhkiG9w0BAQUDggEBAI2E2XcrxGbK+e9CdIxnrVWsh8HgcvBB/F4M 9jRUSjc3+w3E1zxlyfqGgSrzCJIpZbq7TQ7tTd1AV8j9hP4bP1r3BX6lhiXRtRKC thobgl1gXQtHkFhRpZrWe4vECqucHlznel9nhtLeY50ykt8ajvqxhthe/uabKNlo wC87Aq1ykigR1imLAY8UdvdXfvnfS1d5bF/k3TSw2YOZE4u8KToQf1LVPIG12OPi uStuIX9UYzSe8qOurrbjX+qehoubmrgLPGRKyNfIHsp7Arr/yUnjK5WtojTjcF85 nuP8FceVT5cvKYYPcBP6+YpAeeBJBiK95kzZGqgVewoLpKJht+0= -----END CERTIFICATE----- telepathy-gabble-0.18.2/lib/ext/wocky/tests/certs/cas/ae5bb84e.00000644000175000017500000000260312200204546024123 0ustar00smcvsmcv00000000000000-----BEGIN CERTIFICATE----- MIID5jCCAtCgAwIBAgIBATALBgkqhkiG9w0BAQUwbDELMAkGA1UEBhMCVUsxEjAQ BgNVBAoTCUNvbGxhYm9yYTEZMBcGA1UECxMQV29ja3kgVGVzdCBTdWl0ZTERMA8G A1UECBMIU3BpbmRsZWQxGzAZBgNVBAMTEldvY2t5IFhNUFAgTGlicmFyeTAeFw0w OTA5MTgxMjU1MDVaFw0zNzA5MTExMjU1MDVaMGwxCzAJBgNVBAYTAlVLMRIwEAYD VQQKEwlDb2xsYWJvcmExGTAXBgNVBAsTEFdvY2t5IFRlc3QgU3VpdGUxETAPBgNV BAgTCFNwaW5kbGVkMRswGQYDVQQDExJXb2NreSBYTVBQIExpYnJhcnkwggEfMAsG CSqGSIb3DQEBAQOCAQ4AMIIBCQKCAQC+YAQjeUbAlnnvhQ4imQbL4aU8sJjM/M7g gm51NdXev3WwvOdDDU8LgvaKIoMQyQaS5XAt4Q/hAeHZJW0Nx3zZW9XWiqmHlqsU LdQKvxIz9xnvLRR74gHddMXqsPCHieSNvXm4cnnNPIwmWNiBIo5NdI/Te+tNmGqz pO4t1G9jYlhGNhc2vUIpDo4bO2vWqeRyqnQlrAykfZsScjryZtMBDSc9xZdjJlJt 2nbLlQJVUgi1bEwazP3SWEijfZTKhnXn0bvhFGUSEssHb5CUVr6ILRzWJtAVp/us YAaQLZLhMhcA94YvFB0dlVltsdtvwBPidEC4NMPJcitN/3s1fQbPAgMBAAGjgZkw gZYwDwYDVR0TAQH/BAUwAwEB/zAlBgNVHREEHjAcgRpwb3N0bWFzdGVyQGNvbGxh Ym9yYS5jby51azAPBgNVHQ8BAf8EBQMDBwYAMB0GA1UdDgQWBBTeKryla1ofuyCr Gg3ugeTEymWLhzAsBgNVHR8EJTAjMCGgH6AdhhtmaWxlOi8vL3RtcC93b2NreS10 ZXN0cy9jcmwwCwYJKoZIhvcNAQEFA4IBAQC2RqDYK3xPnMGnlzJBVJI4cPH/iRLi 9ZUWmBzpjHpo0d/in6lAF0UirmjpgllXpCUhJMfP5fxOsZYk1eE/leqTyTory7Xj K6zdHmkFGPU3b5ibFPX65iGFpqZdjCAplFZ9HI7au1b0SpbsMKesE7ZXzBF0+ki3 GrKgQF+e7cMktJf07I4xoVVYimSpADFOHo9Pk1sceK/zSKGzob7TziD4Tpnc8Rhq dOj7J8EbzhqjBVciPYzXvlLjyAqdYRg5RHV1xSIvYKeK0PwVzmNY9Xu30+VShJHc 3Hf/VLl2T1ll8tXUj3hc1Jd2+ZMthTF1PIX3bunTnNUm2PENoxa8pDG3 -----END CERTIFICATE----- telepathy-gabble-0.18.2/lib/ext/wocky/tests/certs/cas/a7481a9e.00000644000175000017500000000260312200204546023767 0ustar00smcvsmcv00000000000000-----BEGIN CERTIFICATE----- MIID5jCCAtCgAwIBAgIBATALBgkqhkiG9w0BAQUwbDELMAkGA1UEBhMCVUsxEjAQ BgNVBAoTCUNvbGxhYm9yYTEZMBcGA1UECxMQV29ja3kgVGVzdCBTdWl0ZTERMA8G A1UECBMIU3BpbmRsZWQxGzAZBgNVBAMTEldvY2t5IFhNUFAgTGlicmFyeTAeFw0w OTA5MTgxMjU1MDVaFw0zNzA5MTExMjU1MDVaMGwxCzAJBgNVBAYTAlVLMRIwEAYD VQQKEwlDb2xsYWJvcmExGTAXBgNVBAsTEFdvY2t5IFRlc3QgU3VpdGUxETAPBgNV BAgTCFNwaW5kbGVkMRswGQYDVQQDExJXb2NreSBYTVBQIExpYnJhcnkwggEfMAsG CSqGSIb3DQEBAQOCAQ4AMIIBCQKCAQC+YAQjeUbAlnnvhQ4imQbL4aU8sJjM/M7g gm51NdXev3WwvOdDDU8LgvaKIoMQyQaS5XAt4Q/hAeHZJW0Nx3zZW9XWiqmHlqsU LdQKvxIz9xnvLRR74gHddMXqsPCHieSNvXm4cnnNPIwmWNiBIo5NdI/Te+tNmGqz pO4t1G9jYlhGNhc2vUIpDo4bO2vWqeRyqnQlrAykfZsScjryZtMBDSc9xZdjJlJt 2nbLlQJVUgi1bEwazP3SWEijfZTKhnXn0bvhFGUSEssHb5CUVr6ILRzWJtAVp/us YAaQLZLhMhcA94YvFB0dlVltsdtvwBPidEC4NMPJcitN/3s1fQbPAgMBAAGjgZkw gZYwDwYDVR0TAQH/BAUwAwEB/zAlBgNVHREEHjAcgRpwb3N0bWFzdGVyQGNvbGxh Ym9yYS5jby51azAPBgNVHQ8BAf8EBQMDBwYAMB0GA1UdDgQWBBTeKryla1ofuyCr Gg3ugeTEymWLhzAsBgNVHR8EJTAjMCGgH6AdhhtmaWxlOi8vL3RtcC93b2NreS10 ZXN0cy9jcmwwCwYJKoZIhvcNAQEFA4IBAQC2RqDYK3xPnMGnlzJBVJI4cPH/iRLi 9ZUWmBzpjHpo0d/in6lAF0UirmjpgllXpCUhJMfP5fxOsZYk1eE/leqTyTory7Xj K6zdHmkFGPU3b5ibFPX65iGFpqZdjCAplFZ9HI7au1b0SpbsMKesE7ZXzBF0+ki3 GrKgQF+e7cMktJf07I4xoVVYimSpADFOHo9Pk1sceK/zSKGzob7TziD4Tpnc8Rhq dOj7J8EbzhqjBVciPYzXvlLjyAqdYRg5RHV1xSIvYKeK0PwVzmNY9Xu30+VShJHc 3Hf/VLl2T1ll8tXUj3hc1Jd2+ZMthTF1PIX3bunTnNUm2PENoxa8pDG3 -----END CERTIFICATE----- telepathy-gabble-0.18.2/lib/ext/wocky/tests/certs/cas/8a76ade9.00000644000175000017500000000257712200204546024066 0ustar00smcvsmcv00000000000000-----BEGIN CERTIFICATE----- MIID4jCCAsygAwIBAgIBATALBgkqhkiG9w0BAQUwajELMAkGA1UEBhMCVUsxEjAQ BgNVBAoTCUNvbGxhYm9yYTEZMBcGA1UECxMQV29ja3kgVGVzdCBTdWl0ZTEPMA0G A1UECBMGRm9sZGVkMRswGQYDVQQDExJXb2NreSBYTVBQIExpYnJhcnkwHhcNMDkw OTE4MTI1NTAzWhcNMzcwOTExMTI1NTAzWjBqMQswCQYDVQQGEwJVSzESMBAGA1UE ChMJQ29sbGFib3JhMRkwFwYDVQQLExBXb2NreSBUZXN0IFN1aXRlMQ8wDQYDVQQI EwZGb2xkZWQxGzAZBgNVBAMTEldvY2t5IFhNUFAgTGlicmFyeTCCAR8wCwYJKoZI hvcNAQEBA4IBDgAwggEJAoIBANRkYbvqWdla+zNLsftXYnaAD0agDbNl70y4yxNj V1DriZnNgjgWM8ZIgUdvFx+sA8wMoRFBLnXWYfzI+fHYFak+JqysvQmqTifXjKOs Q8Qdhb3hc2z10X7pobRK+oxE7VXDKKp5ou4f3wx1mvSTjB1oTr5o2ZnA5K/dLCM7 OmJ2C/Vj8z6l6HV41esPEYEZv+wfP/9z3xmNhNHcRWIeipEdDZDfXc0n22SeEnGS ZrSJDP9CZaJWAcLUhD5OEh+B3P/gfO9KDAN8PicSFFXj2X0t8incmz5fs6JosgEF o40D/R4xUIU562HEY/4uOM6N2nHM+MKgiDHEUh9s3lLmSB8CAwEAAaOBmTCBljAP BgNVHRMBAf8EBTADAQH/MCUGA1UdEQQeMByBGnBvc3RtYXN0ZXJAY29sbGFib3Jh LmNvLnVrMA8GA1UdDwEB/wQFAwMHBgAwHQYDVR0OBBYEFJdQenuGG7TS4L2av7+3 HHWC+uO7MCwGA1UdHwQlMCMwIaAfoB2GG2ZpbGU6Ly8vdG1wL3dvY2t5LXRlc3Rz L2NybDALBgkqhkiG9w0BAQUDggEBAI2E2XcrxGbK+e9CdIxnrVWsh8HgcvBB/F4M 9jRUSjc3+w3E1zxlyfqGgSrzCJIpZbq7TQ7tTd1AV8j9hP4bP1r3BX6lhiXRtRKC thobgl1gXQtHkFhRpZrWe4vECqucHlznel9nhtLeY50ykt8ajvqxhthe/uabKNlo wC87Aq1ykigR1imLAY8UdvdXfvnfS1d5bF/k3TSw2YOZE4u8KToQf1LVPIG12OPi uStuIX9UYzSe8qOurrbjX+qehoubmrgLPGRKyNfIHsp7Arr/yUnjK5WtojTjcF85 nuP8FceVT5cvKYYPcBP6+YpAeeBJBiK95kzZGqgVewoLpKJht+0= -----END CERTIFICATE----- telepathy-gabble-0.18.2/lib/ext/wocky/tests/certs/cas/ca-2-cert.pem0000644000175000017500000000260312200204546024723 0ustar00smcvsmcv00000000000000-----BEGIN CERTIFICATE----- MIID5jCCAtCgAwIBAgIBATALBgkqhkiG9w0BAQUwbDELMAkGA1UEBhMCVUsxEjAQ BgNVBAoTCUNvbGxhYm9yYTEZMBcGA1UECxMQV29ja3kgVGVzdCBTdWl0ZTERMA8G A1UECBMIU3BpbmRsZWQxGzAZBgNVBAMTEldvY2t5IFhNUFAgTGlicmFyeTAeFw0w OTA5MTgxMjU1MDVaFw0zNzA5MTExMjU1MDVaMGwxCzAJBgNVBAYTAlVLMRIwEAYD VQQKEwlDb2xsYWJvcmExGTAXBgNVBAsTEFdvY2t5IFRlc3QgU3VpdGUxETAPBgNV BAgTCFNwaW5kbGVkMRswGQYDVQQDExJXb2NreSBYTVBQIExpYnJhcnkwggEfMAsG CSqGSIb3DQEBAQOCAQ4AMIIBCQKCAQC+YAQjeUbAlnnvhQ4imQbL4aU8sJjM/M7g gm51NdXev3WwvOdDDU8LgvaKIoMQyQaS5XAt4Q/hAeHZJW0Nx3zZW9XWiqmHlqsU LdQKvxIz9xnvLRR74gHddMXqsPCHieSNvXm4cnnNPIwmWNiBIo5NdI/Te+tNmGqz pO4t1G9jYlhGNhc2vUIpDo4bO2vWqeRyqnQlrAykfZsScjryZtMBDSc9xZdjJlJt 2nbLlQJVUgi1bEwazP3SWEijfZTKhnXn0bvhFGUSEssHb5CUVr6ILRzWJtAVp/us YAaQLZLhMhcA94YvFB0dlVltsdtvwBPidEC4NMPJcitN/3s1fQbPAgMBAAGjgZkw gZYwDwYDVR0TAQH/BAUwAwEB/zAlBgNVHREEHjAcgRpwb3N0bWFzdGVyQGNvbGxh Ym9yYS5jby51azAPBgNVHQ8BAf8EBQMDBwYAMB0GA1UdDgQWBBTeKryla1ofuyCr Gg3ugeTEymWLhzAsBgNVHR8EJTAjMCGgH6AdhhtmaWxlOi8vL3RtcC93b2NreS10 ZXN0cy9jcmwwCwYJKoZIhvcNAQEFA4IBAQC2RqDYK3xPnMGnlzJBVJI4cPH/iRLi 9ZUWmBzpjHpo0d/in6lAF0UirmjpgllXpCUhJMfP5fxOsZYk1eE/leqTyTory7Xj K6zdHmkFGPU3b5ibFPX65iGFpqZdjCAplFZ9HI7au1b0SpbsMKesE7ZXzBF0+ki3 GrKgQF+e7cMktJf07I4xoVVYimSpADFOHo9Pk1sceK/zSKGzob7TziD4Tpnc8Rhq dOj7J8EbzhqjBVciPYzXvlLjyAqdYRg5RHV1xSIvYKeK0PwVzmNY9Xu30+VShJHc 3Hf/VLl2T1ll8tXUj3hc1Jd2+ZMthTF1PIX3bunTnNUm2PENoxa8pDG3 -----END CERTIFICATE----- telepathy-gabble-0.18.2/lib/ext/wocky/tests/certs/cas/ca-1-cert.pem0000644000175000017500000000257712200204546024734 0ustar00smcvsmcv00000000000000-----BEGIN CERTIFICATE----- MIID4jCCAsygAwIBAgIBATALBgkqhkiG9w0BAQUwajELMAkGA1UEBhMCVUsxEjAQ BgNVBAoTCUNvbGxhYm9yYTEZMBcGA1UECxMQV29ja3kgVGVzdCBTdWl0ZTEPMA0G A1UECBMGRm9sZGVkMRswGQYDVQQDExJXb2NreSBYTVBQIExpYnJhcnkwHhcNMDkw OTE4MTI1NTAzWhcNMzcwOTExMTI1NTAzWjBqMQswCQYDVQQGEwJVSzESMBAGA1UE ChMJQ29sbGFib3JhMRkwFwYDVQQLExBXb2NreSBUZXN0IFN1aXRlMQ8wDQYDVQQI EwZGb2xkZWQxGzAZBgNVBAMTEldvY2t5IFhNUFAgTGlicmFyeTCCAR8wCwYJKoZI hvcNAQEBA4IBDgAwggEJAoIBANRkYbvqWdla+zNLsftXYnaAD0agDbNl70y4yxNj V1DriZnNgjgWM8ZIgUdvFx+sA8wMoRFBLnXWYfzI+fHYFak+JqysvQmqTifXjKOs Q8Qdhb3hc2z10X7pobRK+oxE7VXDKKp5ou4f3wx1mvSTjB1oTr5o2ZnA5K/dLCM7 OmJ2C/Vj8z6l6HV41esPEYEZv+wfP/9z3xmNhNHcRWIeipEdDZDfXc0n22SeEnGS ZrSJDP9CZaJWAcLUhD5OEh+B3P/gfO9KDAN8PicSFFXj2X0t8incmz5fs6JosgEF o40D/R4xUIU562HEY/4uOM6N2nHM+MKgiDHEUh9s3lLmSB8CAwEAAaOBmTCBljAP BgNVHRMBAf8EBTADAQH/MCUGA1UdEQQeMByBGnBvc3RtYXN0ZXJAY29sbGFib3Jh LmNvLnVrMA8GA1UdDwEB/wQFAwMHBgAwHQYDVR0OBBYEFJdQenuGG7TS4L2av7+3 HHWC+uO7MCwGA1UdHwQlMCMwIaAfoB2GG2ZpbGU6Ly8vdG1wL3dvY2t5LXRlc3Rz L2NybDALBgkqhkiG9w0BAQUDggEBAI2E2XcrxGbK+e9CdIxnrVWsh8HgcvBB/F4M 9jRUSjc3+w3E1zxlyfqGgSrzCJIpZbq7TQ7tTd1AV8j9hP4bP1r3BX6lhiXRtRKC thobgl1gXQtHkFhRpZrWe4vECqucHlznel9nhtLeY50ykt8ajvqxhthe/uabKNlo wC87Aq1ykigR1imLAY8UdvdXfvnfS1d5bF/k3TSw2YOZE4u8KToQf1LVPIG12OPi uStuIX9UYzSe8qOurrbjX+qehoubmrgLPGRKyNfIHsp7Arr/yUnjK5WtojTjcF85 nuP8FceVT5cvKYYPcBP6+YpAeeBJBiK95kzZGqgVewoLpKJht+0= -----END CERTIFICATE----- telepathy-gabble-0.18.2/lib/ext/wocky/tests/certs/cas/ca-0-cert.pem0000644000175000017500000000260312200204546024721 0ustar00smcvsmcv00000000000000-----BEGIN CERTIFICATE----- MIID5jCCAtCgAwIBAgIBATALBgkqhkiG9w0BAQUwbDELMAkGA1UEBhMCVUsxEjAQ BgNVBAoTCUNvbGxhYm9yYTEZMBcGA1UECxMQV29ja3kgVGVzdCBTdWl0ZTERMA8G A1UECBMIQ29uZnVzZWQxGzAZBgNVBAMTEldvY2t5IFhNUFAgTGlicmFyeTAeFw0w OTA5MTgxMjE3MDZaFw0zNzA5MTExMjE3MDZaMGwxCzAJBgNVBAYTAlVLMRIwEAYD VQQKEwlDb2xsYWJvcmExGTAXBgNVBAsTEFdvY2t5IFRlc3QgU3VpdGUxETAPBgNV BAgTCENvbmZ1c2VkMRswGQYDVQQDExJXb2NreSBYTVBQIExpYnJhcnkwggEfMAsG CSqGSIb3DQEBAQOCAQ4AMIIBCQKCAQDfmrRZUa4UWDLgmHNRUaXIEwhwSQRESTF1 fWd+NP5xMO65OE0JAmzk4rdPH8xiuba3WnVVvA+nop53lC7emTTMVzWLWLJEV3f2 1xNeHCRWxkmzAwAF+x9y9zGV4mRgMWk26g0C0dmaYPILcTciYRwjvawEdd78z09w BSGdYx1Ya+Mr9BBl2tThD4cliV6DVdlwhnvu7eamrQ/LdeS/n4Z+X8MYW+G49a8b vqHvpp7MS8pIwEMfgkzfuG+K/++Cvz5WfOfnDYH13ahTks7k0RB16rt1qUnvF66r zYJbzqJSc4mbhcOJuFURXeiZKY2f78W1b6RRTvENISdnGKJPwMxZAgMBAAGjgZkw gZYwDwYDVR0TAQH/BAUwAwEB/zAlBgNVHREEHjAcgRpwb3N0bWFzdGVyQGNvbGxh Ym9yYS5jby51azAPBgNVHQ8BAf8EBQMDBwYAMB0GA1UdDgQWBBRJMCYIjJrWac2L wMwOXAKOSh+mjDAsBgNVHR8EJTAjMCGgH6AdhhtmaWxlOi8vL3RtcC93b2NreS10 ZXN0cy9jcmwwCwYJKoZIhvcNAQEFA4IBAQCL+FK+IzN9Z88ePw/MU2Y1Yd2oigeb /02d7DrzQQXpSN1y3t0LKpi3MM7QbGDVUmC6WI7Tpe9wf0G6ZTfZ6jMeS/4OpeG+ QNRWxAWtAv1+QDEtHf9rVFKjl9SUbKhbsLHePggS52LV/YTwD1PxbCsl75jpaCvU W22tTJL0K1lFBYLM0IxiOkn+vrWNsOYfVpXZbjB4ExLeNp1li2tetXJLluMgeGr5 bKYmWfYyze6FOcp1zQh13e6I0DqIh7F6hb/nKaWcJKSNsq8JsgLWTba8iDF24X5C 2l+SJE0iNtg08tC6XdZ9v1DQUHKIKSX7L5MEVgHbLiOKroriqVeQu8zK -----END CERTIFICATE----- telepathy-gabble-0.18.2/lib/ext/wocky/examples/0000755000175000017500000000000012312537050021333 5ustar00smcvsmcv00000000000000telepathy-gabble-0.18.2/lib/ext/wocky/examples/unregister.c0000644000175000017500000000313612200204546023666 0ustar00smcvsmcv00000000000000#ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include #include GMainLoop *mainloop; int rval = -1; static void unregister_callback (GObject *source, GAsyncResult *res, gpointer user_data) { GError *error = NULL; WockyConnector *wcon = WOCKY_CONNECTOR (source); gboolean done = wocky_connector_unregister_finish (wcon, res, &error); if (done) { printf ("Unregistered!\n"); } else { if (error) g_warning ("Unregistration error: %s: %d: %s\n", g_quark_to_string (error->domain), error->code, error->message); else g_warning ("Unregister failed: No error supplied\n"); } g_main_loop_quit (mainloop); } int main (int argc, char **argv) { gchar *jid = NULL; gchar *user = NULL; gchar *host = NULL; gchar *pass = NULL; WockyConnector *wcon = NULL; g_type_init (); if ((argc < 3) || (argc > 4)) { printf ("Usage: %s [host]\n", argv[0]); return -1; } jid = argv[1]; pass = argv[2]; wocky_decode_jid (jid, &user, NULL, NULL); if (argc == 4) host = argv[3]; mainloop = g_main_loop_new (NULL, FALSE); if ((host != NULL) && (*host != '\0')) wcon = wocky_connector_new (jid, pass, NULL, NULL, NULL); else wcon = g_object_new (WOCKY_TYPE_CONNECTOR, "jid" , jid , "password" , pass, "xmpp-server", host, NULL); wocky_connector_unregister_async (wcon, NULL, unregister_callback, NULL); g_main_loop_run (mainloop); return rval; } telepathy-gabble-0.18.2/lib/ext/wocky/examples/send-message.c0000644000175000017500000000734212200204546024055 0ustar00smcvsmcv00000000000000#ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include #include GMainLoop *mainloop; char *recipient; char *message; static void closed_cb ( GObject *source, GAsyncResult *res, gpointer user_data) { WockyPorter *porter = WOCKY_PORTER (source); WockySession *session = WOCKY_SESSION (user_data); GError *error = NULL; if (wocky_porter_close_finish (porter, res, &error)) { g_print ("Signed out\n"); } else { g_warning ("Couldn't sign out cleanly: %s\n", error->message); g_clear_error (&error); } /* Either way, we're done. */ g_object_unref (session); g_main_loop_quit (mainloop); } static void message_sent_cb ( GObject *source, GAsyncResult *res, gpointer user_data) { WockyPorter *porter = WOCKY_PORTER (source); WockySession *session = WOCKY_SESSION (user_data); GError *error = NULL; if (wocky_porter_send_finish (porter, res, &error)) { g_print ("Sent '%s' to %s\n", message, recipient); } else { g_warning ("Couldn't send message: %s\n", error->message); g_clear_error (&error); } /* Sign out. */ wocky_porter_close_async (porter, NULL, closed_cb, session); } static void connected_cb ( GObject *source, GAsyncResult *res, gpointer user_data) { WockyConnector *connector = WOCKY_CONNECTOR (source); WockyXmppConnection *connection; gchar *jid = NULL; GError *error = NULL; connection = wocky_connector_connect_finish (connector, res, &jid, NULL, &error); if (connection == NULL) { g_warning ("Couldn't connect: %s", error->message); g_clear_error (&error); g_main_loop_quit (mainloop); } else { WockySession *session = wocky_session_new_with_connection (connection, jid); WockyPorter *porter = wocky_session_get_porter (session); WockyStanza *stanza = wocky_stanza_build (WOCKY_STANZA_TYPE_MESSAGE, WOCKY_STANZA_SUB_TYPE_NONE, NULL, recipient, '(', "body", '$', message, ')', NULL); g_print ("Connected as %s\n", jid); wocky_porter_start (porter); wocky_porter_send_async (porter, stanza, NULL, message_sent_cb, session); g_object_unref (stanza); g_free (jid); } } static gboolean ignore_ssl_errors = FALSE; static GOptionEntry entries[] = { { "ignore-ssl-errors", 0, 0, G_OPTION_ARG_NONE, &ignore_ssl_errors, "Continue connecting, even if the server provides an invalid SSL certificate", NULL }, { NULL } }; int main (int argc, char **argv) { GError *error = NULL; GOptionContext *context; char *jid, *password; WockyTLSHandler *tls_handler = NULL; WockyConnector *connector; g_type_init (); wocky_init (); context = g_option_context_new (" - signs in as and sends to "); g_option_context_add_main_entries (context, entries, NULL); if (!g_option_context_parse (context, &argc, &argv, &error) || argc != 5) { if (error != NULL) printf ("option parsing failed: %s\n", error->message); printf ("%s", g_option_context_get_help (context, FALSE, NULL)); return -1; } jid = argv[1]; password = argv[2]; recipient = argv[3]; message = argv[4]; if (ignore_ssl_errors) tls_handler = wocky_tls_handler_new (TRUE); mainloop = g_main_loop_new (NULL, FALSE); connector = wocky_connector_new (jid, password, NULL, NULL, tls_handler); wocky_connector_connect_async (connector, NULL, connected_cb, NULL); g_main_loop_run (mainloop); g_object_unref (connector); g_clear_object (&tls_handler); g_main_loop_unref (mainloop); return 0; } telepathy-gabble-0.18.2/lib/ext/wocky/examples/register.c0000644000175000017500000000334412200204546023324 0ustar00smcvsmcv00000000000000#ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include #include GMainLoop *mainloop; static void connector_callback (GObject *source, GAsyncResult *res, gpointer user_data) { GError *error = NULL; gchar *jid = NULL; gchar *sid = NULL; WockyConnector *wcon = WOCKY_CONNECTOR (source); WockyXmppConnection *connection = wocky_connector_register_finish (wcon, res, &jid, &sid, &error); if (connection != NULL) { printf ("connected (%s) [%s]!\n", jid, sid); g_free (sid); g_free (jid); } else { if (error) g_warning ("%s: %d: %s\n", g_quark_to_string (error->domain), error->code, error->message); } g_main_loop_quit (mainloop); } int main (int argc, char **argv) { gchar *jid = NULL; gchar *user = NULL; gchar *host = NULL; gchar *pass = NULL; gchar *email = NULL; WockyConnector *wcon = NULL; g_type_init (); if ((argc < 4) || (argc > 5)) { printf ("Usage: %s [host]\n", argv[0]); return -1; } jid = argv[1]; pass = argv[2]; email = argv[3]; wocky_decode_jid (jid, &user, NULL, NULL); if (argc == 5) host = argv[4]; mainloop = g_main_loop_new (NULL, FALSE); if ((host != NULL) && (*host != '\0')) wcon = wocky_connector_new (jid, pass, NULL, NULL, NULL); else wcon = g_object_new (WOCKY_TYPE_CONNECTOR, "jid" , jid , "password" , pass, "xmpp-server", host, NULL); g_object_set (G_OBJECT (wcon), "email", email, NULL); wocky_connector_register_async (wcon, NULL, connector_callback, NULL); g_main_loop_run (mainloop); return 0; } telepathy-gabble-0.18.2/lib/ext/wocky/examples/receive-messages.c0000644000175000017500000001375212200204546024733 0ustar00smcvsmcv00000000000000#ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include GMainLoop *mainloop; static void porter_closed_cb ( GObject *source, GAsyncResult *res, gpointer user_data) { WockyPorter *porter = WOCKY_PORTER (source); WockySession *session = WOCKY_SESSION (user_data); GError *error = NULL; if (wocky_porter_close_finish (porter, res, &error)) { g_print ("Signed out\n"); } else { g_warning ("Couldn't sign out cleanly: %s\n", error->message); g_clear_error (&error); } /* Either way, we're done. */ g_object_unref (session); g_main_loop_quit (mainloop); } static gboolean message_received_cb (WockyPorter *porter, WockyStanza *stanza, gpointer user_data) { WockyNode *body_node = wocky_node_get_child ( wocky_stanza_get_top_node (stanza), "body"); const gchar *from = wocky_stanza_get_from (stanza); const gchar *message; /* We told the porter only to give us messages which contained a * element. */ g_assert (body_node != NULL); message = body_node->content; if (message == NULL) message = ""; g_print ("Message received from %s: “%s”\n", from, message); if (g_ascii_strcasecmp (message, "sign out") == 0) { WockyStanza *reply = wocky_stanza_build (WOCKY_STANZA_TYPE_MESSAGE, WOCKY_STANZA_SUB_TYPE_NORMAL, NULL, from, '(', "body", '$', "Say please! Didn’t your parents teach you any manners‽", ')', NULL); wocky_porter_send (porter, reply); g_object_unref (reply); } /* Tell the porter that we've handled this stanza; if there were any * lower-priority handlers, they would not be called for this stanza. */ return TRUE; } static gboolean sign_out_message_received_cb (WockyPorter *porter, WockyStanza *stanza, gpointer user_data) { WockySession *session = WOCKY_SESSION (user_data); const gchar *from = wocky_stanza_get_from (stanza); g_print ("%s asked us nicely to sign out\n", from); wocky_porter_close_async (porter, NULL, porter_closed_cb, session); /* Returning FALSE tells the porter that other, lower-priority handlers that * match the stanza should be invoked — in this example, that means * message_received_cb(). */ return FALSE; } static void connected_cb ( GObject *source, GAsyncResult *res, gpointer user_data) { WockyConnector *connector = WOCKY_CONNECTOR (source); WockyXmppConnection *connection; gchar *jid = NULL; GError *error = NULL; connection = wocky_connector_connect_finish (connector, res, &jid, NULL, &error); if (connection == NULL) { g_warning ("Couldn't connect: %s", error->message); g_clear_error (&error); g_main_loop_quit (mainloop); } else { WockySession *session = wocky_session_new_with_connection (connection, jid); WockyPorter *porter = wocky_session_get_porter (session); WockyStanza *stanza; g_print ("Connected as %s\n", jid); /* Register a callback for incoming stanzas from * anyone, but only if it contains a element (the element that * actually contains the text of IMs). */ wocky_porter_register_handler_from_anyone (porter, WOCKY_STANZA_TYPE_MESSAGE, WOCKY_STANZA_SUB_TYPE_CHAT, WOCKY_PORTER_HANDLER_PRIORITY_NORMAL, message_received_cb, session, '(', "body", ')', NULL); /* Register a higher-priority handler for incoming * stanzas which contain a element containing the text * "please sign out". */ wocky_porter_register_handler_from_anyone (porter, WOCKY_STANZA_TYPE_MESSAGE, WOCKY_STANZA_SUB_TYPE_CHAT, WOCKY_PORTER_HANDLER_PRIORITY_NORMAL, sign_out_message_received_cb, session, '(', "body", '$', "please sign out", ')', NULL); wocky_porter_start (porter); /* Broadcast presence for ourself, so our contacts can see us online, * with a status message. */ stanza = wocky_stanza_build (WOCKY_STANZA_TYPE_PRESENCE, WOCKY_STANZA_SUB_TYPE_NONE, NULL, NULL, '(', "show", '$', "chat", ')', '(', "status", '$', "talk to me! (or tell me to “sign out”)", ')', NULL); wocky_porter_send (porter, stanza); g_object_unref (stanza); g_free (jid); } } static gboolean ignore_ssl_errors = FALSE; static gchar *resource = NULL; static GOptionEntry entries[] = { { "ignore-ssl-errors", 0, 0, G_OPTION_ARG_NONE, &ignore_ssl_errors, "Continue connecting, even if the server provides an invalid SSL certificate", NULL }, { "resource", 'r', 0, G_OPTION_ARG_STRING, &resource, "Connect as a specific resource (default: let the server decide)", "RESOURCE" }, { NULL } }; int main (int argc, char **argv) { GError *error = NULL; GOptionContext *context; char *jid, *password; WockyTLSHandler *tls_handler = NULL; WockyConnector *connector; g_type_init (); wocky_init (); context = g_option_context_new (" - signs in as and prints incoming messages"); g_option_context_add_main_entries (context, entries, NULL); if (!g_option_context_parse (context, &argc, &argv, &error) || argc != 3) { if (error != NULL) printf ("option parsing failed: %s\n", error->message); printf ("%s", g_option_context_get_help (context, FALSE, NULL)); return -1; } jid = argv[1]; password = argv[2]; if (ignore_ssl_errors) tls_handler = wocky_tls_handler_new (TRUE); mainloop = g_main_loop_new (NULL, FALSE); connector = wocky_connector_new (jid, password, resource, NULL, tls_handler); wocky_connector_connect_async (connector, NULL, connected_cb, NULL); g_main_loop_run (mainloop); g_object_unref (connector); g_clear_object (&tls_handler); g_main_loop_unref (mainloop); return 0; } telepathy-gabble-0.18.2/lib/ext/wocky/examples/dump-certificates.c0000644000175000017500000001105712213345474025122 0ustar00smcvsmcv00000000000000/* * dump-certificates.c - Dump all Certificates from TLS Handshake * Copyright (C) 2011 Collabora Ltd. * @author Stef Walter * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 "config.h" #include #include #include #include #include #include #include static GMainLoop *mainloop; typedef struct { WockyTLSHandler parent; } DumpTLSHandler; typedef struct { WockyTLSHandlerClass parent_class; } DumpTLSHandlerClass; GType dump_tls_handler_get_type (void); G_DEFINE_TYPE (DumpTLSHandler, dump_tls_handler, WOCKY_TYPE_TLS_HANDLER) static void dump_tls_handler_init (DumpTLSHandler *self) { } static void dump_tls_handler_verify_async (WockyTLSHandler *self, WockyTLSSession *tls_session, const gchar *peername, GStrv extra_identities, GAsyncReadyCallback callback, gpointer user_data) { GSimpleAsyncResult *res; GPtrArray *chain; gnutls_x509_crt_t cert; gnutls_datum_t datum; gchar buffer[1024 * 20]; gsize length; guint i; chain = wocky_tls_session_get_peers_certificate (tls_session, NULL); for (i = 0; i < chain->len; i++) { GArray *cert_data = g_ptr_array_index (chain, i); datum.data = (gpointer)cert_data->data; datum.size = cert_data->len; if (gnutls_x509_crt_init (&cert) < 0) g_assert_not_reached (); if (gnutls_x509_crt_import (cert, &datum, GNUTLS_X509_FMT_DER) < 0) { g_warning ("couldn't parse certificate %u in chain", i); gnutls_x509_crt_deinit (cert); continue; } length = sizeof (buffer); gnutls_x509_crt_get_dn (cert, buffer, &length); g_print ("Subject: %.*s\n", (gint) length, buffer); length = sizeof (buffer); gnutls_x509_crt_get_issuer_dn (cert, buffer, &length); g_print ("Issuer: %.*s\n", (gint) length, buffer); length = sizeof (buffer); if (gnutls_x509_crt_export (cert, GNUTLS_X509_FMT_PEM, buffer, &length) < 0) { g_warning ("couldn't export certificate %u in chain", i); gnutls_x509_crt_deinit (cert); continue; } g_print ("%.*s\n", (gint) length, buffer); gnutls_x509_crt_deinit (cert); } g_ptr_array_unref (chain); res = g_simple_async_result_new (G_OBJECT (self), callback, user_data, dump_tls_handler_verify_async); g_simple_async_result_complete_in_idle (res); g_object_unref (res); } static gboolean dump_tls_handler_verify_finish (WockyTLSHandler *self, GAsyncResult *result, GError **error) { return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), error); } static void dump_tls_handler_class_init (DumpTLSHandlerClass *klass) { WockyTLSHandlerClass *handler_class = WOCKY_TLS_HANDLER_CLASS (klass); handler_class->verify_async_func = dump_tls_handler_verify_async; handler_class->verify_finish_func = dump_tls_handler_verify_finish; } static void connected_cb ( GObject *source, GAsyncResult *res, gpointer user_data) { g_main_loop_quit (mainloop); } int main (int argc, char **argv) { char *jid, *password; WockyConnector *connector; WockyTLSHandler *handler; g_type_init (); wocky_init (); if (argc != 2) { g_printerr ("Usage: %s \n", argv[0]); return -1; } jid = argv[1]; /* This example doesn't use your real password because it does not actually * validate certificates: it just dumps them then declares them valid. */ password = "not a chance"; mainloop = g_main_loop_new (NULL, FALSE); handler = g_object_new (dump_tls_handler_get_type (), NULL); connector = wocky_connector_new (jid, password, NULL, NULL, handler); wocky_connector_connect_async (connector, NULL, connected_cb, NULL); g_main_loop_run (mainloop); g_object_unref (connector); g_main_loop_unref (mainloop); return 0; } telepathy-gabble-0.18.2/lib/ext/wocky/examples/Makefile.am0000644000175000017500000000201612201202754023363 0ustar00smcvsmcv00000000000000EXAMPLES = if ! USING_OPENSSL EXAMPLES += wocky-dump-certificates endif EXAMPLES += wocky-send-message EXAMPLES += wocky-receive-messages EXAMPLES += wocky-register EXAMPLES += wocky-unregister INCLUDES := -I$(top_builddir)/wocky wocky_dump_certificates_SOURCES = dump-certificates.c wocky_dump_certificates_CFLAGS = $(TLS_CFLAGS) $(AM_CFLAGS) wocky_dump_certificates_LDADD = $(TLS_LIBS) $(LDADD) wocky_send_message_SOURCES = send-message.c wocky_receive_messages_SOURCES = receive-messages.c wocky_register_SOURCES = register.c wocky_unregister_SOURCES = unregister.c LDADD = \ @GLIB_LIBS@ \ $(top_builddir)/wocky/libwocky.la AM_CFLAGS = \ $(WOCKY_CFLAGS) \ $(ERROR_CFLAGS) \ @GLIB_CFLAGS@ check_c_sources = \ $(wocky_dump_certificates_SOURCES) \ $(wocky_send_message_SOURCES) \ $(wocky_receive_messages_SOURCES) \ $(wocky_register_SOURCES) \ $(wocky_unregister_SOURCES) include $(top_srcdir)/tools/check-coding-style.mk check-local: check-coding-style noinst_PROGRAMS = $(EXAMPLES) telepathy-gabble-0.18.2/lib/ext/wocky/examples/Makefile.in0000644000175000017500000006255312312536107023415 0ustar00smcvsmcv00000000000000# Makefile.in generated by automake 1.14.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2013 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ VPATH = @srcdir@ am__is_gnu_make = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)' am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ @USING_OPENSSL_FALSE@am__append_1 = wocky-dump-certificates DIST_COMMON = $(top_srcdir)/tools/check-coding-style.mk \ $(srcdir)/Makefile.in $(srcdir)/Makefile.am \ $(top_srcdir)/depcomp noinst_PROGRAMS = $(am__EXEEXT_2) subdir = examples ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/as-compiler-flag.m4 \ $(top_srcdir)/m4/gtk-doc.m4 $(top_srcdir)/m4/libtool.m4 \ $(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \ $(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \ $(top_srcdir)/m4/tp-compiler-flag.m4 \ $(top_srcdir)/m4/tp-compiler-warnings.m4 \ $(top_srcdir)/m4/wocky-gcov.m4 $(top_srcdir)/m4/wocky-lcov.m4 \ $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = @USING_OPENSSL_FALSE@am__EXEEXT_1 = wocky-dump-certificates$(EXEEXT) am__EXEEXT_2 = $(am__EXEEXT_1) wocky-send-message$(EXEEXT) \ wocky-receive-messages$(EXEEXT) wocky-register$(EXEEXT) \ wocky-unregister$(EXEEXT) PROGRAMS = $(noinst_PROGRAMS) am_wocky_dump_certificates_OBJECTS = \ wocky_dump_certificates-dump-certificates.$(OBJEXT) wocky_dump_certificates_OBJECTS = \ $(am_wocky_dump_certificates_OBJECTS) am__DEPENDENCIES_1 = am__DEPENDENCIES_2 = $(top_builddir)/wocky/libwocky.la wocky_dump_certificates_DEPENDENCIES = $(am__DEPENDENCIES_1) \ $(am__DEPENDENCIES_2) AM_V_lt = $(am__v_lt_@AM_V@) am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) am__v_lt_0 = --silent am__v_lt_1 = wocky_dump_certificates_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ $(wocky_dump_certificates_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) \ $(LDFLAGS) -o $@ am_wocky_receive_messages_OBJECTS = receive-messages.$(OBJEXT) wocky_receive_messages_OBJECTS = $(am_wocky_receive_messages_OBJECTS) wocky_receive_messages_LDADD = $(LDADD) wocky_receive_messages_DEPENDENCIES = \ $(top_builddir)/wocky/libwocky.la am_wocky_register_OBJECTS = register.$(OBJEXT) wocky_register_OBJECTS = $(am_wocky_register_OBJECTS) wocky_register_LDADD = $(LDADD) wocky_register_DEPENDENCIES = $(top_builddir)/wocky/libwocky.la am_wocky_send_message_OBJECTS = send-message.$(OBJEXT) wocky_send_message_OBJECTS = $(am_wocky_send_message_OBJECTS) wocky_send_message_LDADD = $(LDADD) wocky_send_message_DEPENDENCIES = $(top_builddir)/wocky/libwocky.la am_wocky_unregister_OBJECTS = unregister.$(OBJEXT) wocky_unregister_OBJECTS = $(am_wocky_unregister_OBJECTS) wocky_unregister_LDADD = $(LDADD) wocky_unregister_DEPENDENCIES = $(top_builddir)/wocky/libwocky.la AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) depcomp = $(SHELL) $(top_srcdir)/depcomp am__depfiles_maybe = depfiles am__mv = mv -f COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ $(AM_CFLAGS) $(CFLAGS) AM_V_CC = $(am__v_CC_@AM_V@) am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) am__v_CC_0 = @echo " CC " $@; am__v_CC_1 = CCLD = $(CC) LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ $(AM_LDFLAGS) $(LDFLAGS) -o $@ AM_V_CCLD = $(am__v_CCLD_@AM_V@) am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) am__v_CCLD_0 = @echo " CCLD " $@; am__v_CCLD_1 = SOURCES = $(wocky_dump_certificates_SOURCES) \ $(wocky_receive_messages_SOURCES) $(wocky_register_SOURCES) \ $(wocky_send_message_SOURCES) $(wocky_unregister_SOURCES) DIST_SOURCES = $(wocky_dump_certificates_SOURCES) \ $(wocky_receive_messages_SOURCES) $(wocky_register_SOURCES) \ $(wocky_send_message_SOURCES) $(wocky_unregister_SOURCES) am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) # Read a list of newline-separated strings from the standard input, # and print each of them once, without duplicates. Input order is # *not* preserved. am__uniquify_input = $(AWK) '\ BEGIN { nonempty = 0; } \ { items[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in items) print i; }; } \ ' # Make sure the list of sources is unique. This is necessary because, # e.g., the same source file might be shared among _SOURCES variables # for different programs/libraries. am__define_uniq_tagged_files = \ list='$(am__tagged_files)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | $(am__uniquify_input)` ETAGS = etags CTAGS = ctags DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ AR = @AR@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CC = @CC@ CCAS = @CCAS@ CCASDEPMODE = @CCASDEPMODE@ CCASFLAGS = @CCASFLAGS@ CCDEPMODE = @CCDEPMODE@ CFLAGS = @CFLAGS@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CXXFLAGS = @CXXFLAGS@ CYGPATH_W = @CYGPATH_W@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DLLTOOL = @DLLTOOL@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ ENABLE_CODING_STYLE_CHECKS = @ENABLE_CODING_STYLE_CHECKS@ ERROR_CFLAGS = @ERROR_CFLAGS@ EXEEXT = @EXEEXT@ FFLAGS = @FFLAGS@ FGREP = @FGREP@ GCOV = @GCOV@ GCOV_CFLAGS = @GCOV_CFLAGS@ GCOV_LIBS = @GCOV_LIBS@ GLIB_CFLAGS = @GLIB_CFLAGS@ GLIB_GENMARSHAL = @GLIB_GENMARSHAL@ GLIB_LIBS = @GLIB_LIBS@ GNUTLS_FOR_STREAM_CIPHERS_CFLAGS = @GNUTLS_FOR_STREAM_CIPHERS_CFLAGS@ GNUTLS_FOR_STREAM_CIPHERS_LIBS = @GNUTLS_FOR_STREAM_CIPHERS_LIBS@ GREP = @GREP@ GTKDOC_CHECK = @GTKDOC_CHECK@ GTKDOC_CHECK_PATH = @GTKDOC_CHECK_PATH@ GTKDOC_DEPS_CFLAGS = @GTKDOC_DEPS_CFLAGS@ GTKDOC_DEPS_LIBS = @GTKDOC_DEPS_LIBS@ GTKDOC_MKPDF = @GTKDOC_MKPDF@ GTKDOC_REBASE = @GTKDOC_REBASE@ HEADER_DIR = @HEADER_DIR@ HTML_DIR = @HTML_DIR@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ LCOV_PATH = @LCOV_PATH@ LD = @LD@ LDFLAGS = @LDFLAGS@ LIBIPHB_CFLAGS = @LIBIPHB_CFLAGS@ LIBIPHB_LIBS = @LIBIPHB_LIBS@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBSASL2_CFLAGS = @LIBSASL2_CFLAGS@ LIBSASL2_LIBS = @LIBSASL2_LIBS@ LIBTOOL = @LIBTOOL@ LIBXML2_CFLAGS = @LIBXML2_CFLAGS@ LIBXML2_LIBS = @LIBXML2_LIBS@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBOBJS = @LTLIBOBJS@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ MOSTLYCLEANFILES = @MOSTLYCLEANFILES@ NM = @NM@ NMEDIT = @NMEDIT@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ RANLIB = @RANLIB@ SED = @SED@ SET_MAKE = @SET_MAKE@ SHARED_SUFFIX = @SHARED_SUFFIX@ SHELL = @SHELL@ SOUP_CFLAGS = @SOUP_CFLAGS@ SOUP_LIBS = @SOUP_LIBS@ SQLITE_CFLAGS = @SQLITE_CFLAGS@ SQLITE_LIBS = @SQLITE_LIBS@ STRIP = @STRIP@ TLS_CFLAGS = @TLS_CFLAGS@ TLS_LIBS = @TLS_LIBS@ VERSION = @VERSION@ WOCKY_CFLAGS = @WOCKY_CFLAGS@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ have_gcov = @have_gcov@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ mandir = @mandir@ mkdir_p = @mkdir_p@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ srcdir = @srcdir@ sysconfdir = @sysconfdir@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ EXAMPLES = $(am__append_1) wocky-send-message wocky-receive-messages \ wocky-register wocky-unregister INCLUDES := -I$(top_builddir)/wocky wocky_dump_certificates_SOURCES = dump-certificates.c wocky_dump_certificates_CFLAGS = $(TLS_CFLAGS) $(AM_CFLAGS) wocky_dump_certificates_LDADD = $(TLS_LIBS) $(LDADD) wocky_send_message_SOURCES = send-message.c wocky_receive_messages_SOURCES = receive-messages.c wocky_register_SOURCES = register.c wocky_unregister_SOURCES = unregister.c LDADD = \ @GLIB_LIBS@ \ $(top_builddir)/wocky/libwocky.la AM_CFLAGS = \ $(WOCKY_CFLAGS) \ $(ERROR_CFLAGS) \ @GLIB_CFLAGS@ check_c_sources = \ $(wocky_dump_certificates_SOURCES) \ $(wocky_send_message_SOURCES) \ $(wocky_receive_messages_SOURCES) \ $(wocky_register_SOURCES) \ $(wocky_unregister_SOURCES) all: all-am .SUFFIXES: .SUFFIXES: .c .lo .o .obj $(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(top_srcdir)/tools/check-coding-style.mk $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu examples/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --gnu examples/Makefile .PRECIOUS: Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ esac; $(top_srcdir)/tools/check-coding-style.mk: $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): clean-noinstPROGRAMS: @list='$(noinst_PROGRAMS)'; test -n "$$list" || exit 0; \ echo " rm -f" $$list; \ rm -f $$list || exit $$?; \ test -n "$(EXEEXT)" || exit 0; \ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \ echo " rm -f" $$list; \ rm -f $$list wocky-dump-certificates$(EXEEXT): $(wocky_dump_certificates_OBJECTS) $(wocky_dump_certificates_DEPENDENCIES) $(EXTRA_wocky_dump_certificates_DEPENDENCIES) @rm -f wocky-dump-certificates$(EXEEXT) $(AM_V_CCLD)$(wocky_dump_certificates_LINK) $(wocky_dump_certificates_OBJECTS) $(wocky_dump_certificates_LDADD) $(LIBS) wocky-receive-messages$(EXEEXT): $(wocky_receive_messages_OBJECTS) $(wocky_receive_messages_DEPENDENCIES) $(EXTRA_wocky_receive_messages_DEPENDENCIES) @rm -f wocky-receive-messages$(EXEEXT) $(AM_V_CCLD)$(LINK) $(wocky_receive_messages_OBJECTS) $(wocky_receive_messages_LDADD) $(LIBS) wocky-register$(EXEEXT): $(wocky_register_OBJECTS) $(wocky_register_DEPENDENCIES) $(EXTRA_wocky_register_DEPENDENCIES) @rm -f wocky-register$(EXEEXT) $(AM_V_CCLD)$(LINK) $(wocky_register_OBJECTS) $(wocky_register_LDADD) $(LIBS) wocky-send-message$(EXEEXT): $(wocky_send_message_OBJECTS) $(wocky_send_message_DEPENDENCIES) $(EXTRA_wocky_send_message_DEPENDENCIES) @rm -f wocky-send-message$(EXEEXT) $(AM_V_CCLD)$(LINK) $(wocky_send_message_OBJECTS) $(wocky_send_message_LDADD) $(LIBS) wocky-unregister$(EXEEXT): $(wocky_unregister_OBJECTS) $(wocky_unregister_DEPENDENCIES) $(EXTRA_wocky_unregister_DEPENDENCIES) @rm -f wocky-unregister$(EXEEXT) $(AM_V_CCLD)$(LINK) $(wocky_unregister_OBJECTS) $(wocky_unregister_LDADD) $(LIBS) mostlyclean-compile: -rm -f *.$(OBJEXT) distclean-compile: -rm -f *.tab.c @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/receive-messages.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/register.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/send-message.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/unregister.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/wocky_dump_certificates-dump-certificates.Po@am__quote@ .c.o: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< .c.obj: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` .c.lo: @am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< wocky_dump_certificates-dump-certificates.o: dump-certificates.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(wocky_dump_certificates_CFLAGS) $(CFLAGS) -MT wocky_dump_certificates-dump-certificates.o -MD -MP -MF $(DEPDIR)/wocky_dump_certificates-dump-certificates.Tpo -c -o wocky_dump_certificates-dump-certificates.o `test -f 'dump-certificates.c' || echo '$(srcdir)/'`dump-certificates.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/wocky_dump_certificates-dump-certificates.Tpo $(DEPDIR)/wocky_dump_certificates-dump-certificates.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='dump-certificates.c' object='wocky_dump_certificates-dump-certificates.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(wocky_dump_certificates_CFLAGS) $(CFLAGS) -c -o wocky_dump_certificates-dump-certificates.o `test -f 'dump-certificates.c' || echo '$(srcdir)/'`dump-certificates.c wocky_dump_certificates-dump-certificates.obj: dump-certificates.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(wocky_dump_certificates_CFLAGS) $(CFLAGS) -MT wocky_dump_certificates-dump-certificates.obj -MD -MP -MF $(DEPDIR)/wocky_dump_certificates-dump-certificates.Tpo -c -o wocky_dump_certificates-dump-certificates.obj `if test -f 'dump-certificates.c'; then $(CYGPATH_W) 'dump-certificates.c'; else $(CYGPATH_W) '$(srcdir)/dump-certificates.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/wocky_dump_certificates-dump-certificates.Tpo $(DEPDIR)/wocky_dump_certificates-dump-certificates.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='dump-certificates.c' object='wocky_dump_certificates-dump-certificates.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(wocky_dump_certificates_CFLAGS) $(CFLAGS) -c -o wocky_dump_certificates-dump-certificates.obj `if test -f 'dump-certificates.c'; then $(CYGPATH_W) 'dump-certificates.c'; else $(CYGPATH_W) '$(srcdir)/dump-certificates.c'; fi` mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs ID: $(am__tagged_files) $(am__define_uniq_tagged_files); mkid -fID $$unique tags: tags-am TAGS: tags tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) set x; \ here=`pwd`; \ $(am__define_uniq_tagged_files); \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ if test $$# -gt 0; then \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ "$$@" $$unique; \ else \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ $$unique; \ fi; \ fi ctags: ctags-am CTAGS: ctags ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) $(am__define_uniq_tagged_files); \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" cscopelist: cscopelist-am cscopelist-am: $(am__tagged_files) list='$(am__tagged_files)'; \ case "$(srcdir)" in \ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ *) sdir=$(subdir)/$(srcdir) ;; \ esac; \ for i in $$list; do \ if test -f "$$i"; then \ echo "$(subdir)/$$i"; \ else \ echo "$$sdir/$$i"; \ fi; \ done >> $(top_builddir)/cscope.files distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags distdir: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done check-am: all-am $(MAKE) $(AM_MAKEFLAGS) check-local check: check-am all-am: Makefile $(PROGRAMS) installdirs: install: install-am install-exec: install-exec-am install-data: install-data-am uninstall: uninstall-am install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-am install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: -test -z "$(MOSTLYCLEANFILES)" || rm -f $(MOSTLYCLEANFILES) clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." clean: clean-am clean-am: clean-generic clean-libtool clean-noinstPROGRAMS \ mostlyclean-am distclean: distclean-am -rm -rf ./$(DEPDIR) -rm -f Makefile distclean-am: clean-am distclean-compile distclean-generic \ distclean-tags dvi: dvi-am dvi-am: html: html-am html-am: info: info-am info-am: install-data-am: install-dvi: install-dvi-am install-dvi-am: install-exec-am: install-html: install-html-am install-html-am: install-info: install-info-am install-info-am: install-man: install-pdf: install-pdf-am install-pdf-am: install-ps: install-ps-am install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-am -rm -rf ./$(DEPDIR) -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-am mostlyclean-am: mostlyclean-compile mostlyclean-generic \ mostlyclean-libtool pdf: pdf-am pdf-am: ps: ps-am ps-am: uninstall-am: .MAKE: check-am install-am install-strip .PHONY: CTAGS GTAGS TAGS all all-am check check-am check-local clean \ clean-generic clean-libtool clean-noinstPROGRAMS cscopelist-am \ ctags ctags-am distclean distclean-compile distclean-generic \ distclean-libtool distclean-tags distdir dvi dvi-am html \ html-am info info-am install install-am install-data \ install-data-am install-dvi install-dvi-am install-exec \ install-exec-am install-html install-html-am install-info \ install-info-am install-man install-pdf install-pdf-am \ install-ps install-ps-am install-strip installcheck \ installcheck-am installdirs maintainer-clean \ maintainer-clean-generic mostlyclean mostlyclean-compile \ mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ tags tags-am uninstall uninstall-am check-coding-style: @fail=0; \ if test -n "$(check_misc_sources)"; then \ tools_dir=$(top_srcdir)/tools \ sh $(top_srcdir)/tools/check-misc.sh \ $(addprefix $(srcdir)/,$(check_misc_sources)) || fail=1; \ fi; \ if test -n "$(check_c_sources)"; then \ tools_dir=$(top_srcdir)/tools \ sh $(top_srcdir)/tools/check-c-style.sh \ $(addprefix $(srcdir)/,$(check_c_sources)) || fail=1; \ fi;\ if test yes = "$(ENABLE_CODING_STYLE_CHECKS)"; then \ exit "$$fail";\ else \ exit 0;\ fi check-local: check-coding-style # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: telepathy-gabble-0.18.2/lib/ext/wocky/tools/0000755000175000017500000000000012312537050020655 5ustar00smcvsmcv00000000000000telepathy-gabble-0.18.2/lib/ext/wocky/tools/check-whitespace.sh0000644000175000017500000000032312200204546024413 0ustar00smcvsmcv00000000000000#!/bin/sh fail=0 if grep -n ' $' "$@" then echo "^^^ The above files contain unwanted trailing spaces" fail=1 fi if grep -n ' ' "$@" then echo "^^^ The above files contain tabs" fail=1 fi exit $fail telepathy-gabble-0.18.2/lib/ext/wocky/tools/check-misc.sh0000644000175000017500000000036012200204546023213 0ustar00smcvsmcv00000000000000#!/bin/sh fail=0 ( . "${tools_dir}"/check-whitespace.sh ) || fail=$? if egrep '(Free\s*Software\s*Foundation.*02139|02111-1307)' "$@" then echo "^^^ The above files contain the FSF's old address in GPL headers" fail=1 fi exit $fail telepathy-gabble-0.18.2/lib/ext/wocky/tools/check-c-style.sh0000644000175000017500000000376412200204546023653 0ustar00smcvsmcv00000000000000#!/bin/sh fail=0 ( . "${tools_dir}"/check-misc.sh ) || fail=$? if grep -n '^ *GError *\*[[:alpha:]_][[:alnum:]_]* *;' "$@" then echo "^^^ The above files contain uninitialized GError*s - they should be" echo " initialized to NULL" fail=1 fi # The first regex finds function calls like foo() (as opposed to foo ()). # It attempts to ignore string constants (may cause false negatives). # The second and third ignore block comments (gtkdoc uses foo() as markup). # The fourth ignores cpp so you can # #define foo(bar) (_real_foo (__FUNC__, bar)) (cpp insists on foo() style). if grep -n '^[^"]*[[:lower:]](' "$@" \ | grep -v '^[-[:alnum:]_./]*:[[:digit:]]*: *\*' \ | grep -v '^[-[:alnum:]_./]*:[[:digit:]]*: */\*' \ | grep -v '^[-[:alnum:]_./]*:[[:digit:]]*: *#' then echo "^^^ Our coding style is to use function calls like foo (), not foo()" fail=1 fi if grep -En '[(][[:alnum:]_]+ ?\*[)][(]?[[:alpha:]_]' "$@"; then echo "^^^ Our coding style is to have a space between a cast and the " echo " thing being cast" fail=1 fi # this only spots casts if grep -En '[(][[:alnum:]_]+\*+[)]' "$@"; then echo "^^^ Our coding style is to have a space before the * of pointer types" echo " (regex 1)" fail=1 fi # ... and this only spots variable declarations and function return types if grep -En '^ *(static |const |)* *[[:alnum:]_]+\*+([[:alnum:]_]|;|$)' \ "$@"; then echo "^^^ Our coding style is to have a space before the * of pointer types" echo " (regex 2)" fail=1 fi if grep -n 'g_hash_table_destroy' "$@"; then echo "^^^ Our coding style is to use g_hash_table_unref" fail=1 fi for p in "" "ptr_" "byte_"; do if grep -En "g_${p}array_free \(([^ ,]+), TRUE\)" "$@"; then echo "^^^ Our coding style is to use g_${p}array_unref in the case " echo " the underlying C array is not used" fail=1 fi done if test -n "$CHECK_FOR_LONG_LINES" then if egrep -n '.{80,}' "$@" then echo "^^^ The above files contain long lines" fail=1 fi fi exit $fail telepathy-gabble-0.18.2/lib/ext/wocky/tools/Makefile.am0000644000175000017500000000017712200204546022713 0ustar00smcvsmcv00000000000000EXTRA_DIST = \ check-coding-style.mk \ check-c-style.sh \ check-misc.sh \ check-whitespace.sh \ flymake.mk telepathy-gabble-0.18.2/lib/ext/wocky/tools/Makefile.in0000644000175000017500000003212612312536107022730 0ustar00smcvsmcv00000000000000# Makefile.in generated by automake 1.14.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2013 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ VPATH = @srcdir@ am__is_gnu_make = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)' am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ subdir = tools DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/as-compiler-flag.m4 \ $(top_srcdir)/m4/gtk-doc.m4 $(top_srcdir)/m4/libtool.m4 \ $(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \ $(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \ $(top_srcdir)/m4/tp-compiler-flag.m4 \ $(top_srcdir)/m4/tp-compiler-warnings.m4 \ $(top_srcdir)/m4/wocky-gcov.m4 $(top_srcdir)/m4/wocky-lcov.m4 \ $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = SOURCES = DIST_SOURCES = am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ AR = @AR@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CC = @CC@ CCAS = @CCAS@ CCASDEPMODE = @CCASDEPMODE@ CCASFLAGS = @CCASFLAGS@ CCDEPMODE = @CCDEPMODE@ CFLAGS = @CFLAGS@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CXXFLAGS = @CXXFLAGS@ CYGPATH_W = @CYGPATH_W@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DLLTOOL = @DLLTOOL@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ ENABLE_CODING_STYLE_CHECKS = @ENABLE_CODING_STYLE_CHECKS@ ERROR_CFLAGS = @ERROR_CFLAGS@ EXEEXT = @EXEEXT@ FFLAGS = @FFLAGS@ FGREP = @FGREP@ GCOV = @GCOV@ GCOV_CFLAGS = @GCOV_CFLAGS@ GCOV_LIBS = @GCOV_LIBS@ GLIB_CFLAGS = @GLIB_CFLAGS@ GLIB_GENMARSHAL = @GLIB_GENMARSHAL@ GLIB_LIBS = @GLIB_LIBS@ GNUTLS_FOR_STREAM_CIPHERS_CFLAGS = @GNUTLS_FOR_STREAM_CIPHERS_CFLAGS@ GNUTLS_FOR_STREAM_CIPHERS_LIBS = @GNUTLS_FOR_STREAM_CIPHERS_LIBS@ GREP = @GREP@ GTKDOC_CHECK = @GTKDOC_CHECK@ GTKDOC_CHECK_PATH = @GTKDOC_CHECK_PATH@ GTKDOC_DEPS_CFLAGS = @GTKDOC_DEPS_CFLAGS@ GTKDOC_DEPS_LIBS = @GTKDOC_DEPS_LIBS@ GTKDOC_MKPDF = @GTKDOC_MKPDF@ GTKDOC_REBASE = @GTKDOC_REBASE@ HEADER_DIR = @HEADER_DIR@ HTML_DIR = @HTML_DIR@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ LCOV_PATH = @LCOV_PATH@ LD = @LD@ LDFLAGS = @LDFLAGS@ LIBIPHB_CFLAGS = @LIBIPHB_CFLAGS@ LIBIPHB_LIBS = @LIBIPHB_LIBS@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBSASL2_CFLAGS = @LIBSASL2_CFLAGS@ LIBSASL2_LIBS = @LIBSASL2_LIBS@ LIBTOOL = @LIBTOOL@ LIBXML2_CFLAGS = @LIBXML2_CFLAGS@ LIBXML2_LIBS = @LIBXML2_LIBS@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBOBJS = @LTLIBOBJS@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ MOSTLYCLEANFILES = @MOSTLYCLEANFILES@ NM = @NM@ NMEDIT = @NMEDIT@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ RANLIB = @RANLIB@ SED = @SED@ SET_MAKE = @SET_MAKE@ SHARED_SUFFIX = @SHARED_SUFFIX@ SHELL = @SHELL@ SOUP_CFLAGS = @SOUP_CFLAGS@ SOUP_LIBS = @SOUP_LIBS@ SQLITE_CFLAGS = @SQLITE_CFLAGS@ SQLITE_LIBS = @SQLITE_LIBS@ STRIP = @STRIP@ TLS_CFLAGS = @TLS_CFLAGS@ TLS_LIBS = @TLS_LIBS@ VERSION = @VERSION@ WOCKY_CFLAGS = @WOCKY_CFLAGS@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ have_gcov = @have_gcov@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ mandir = @mandir@ mkdir_p = @mkdir_p@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ srcdir = @srcdir@ sysconfdir = @sysconfdir@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ EXTRA_DIST = \ check-coding-style.mk \ check-c-style.sh \ check-misc.sh \ check-whitespace.sh \ flymake.mk all: all-am .SUFFIXES: $(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu tools/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --gnu tools/Makefile .PRECIOUS: Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs tags TAGS: ctags CTAGS: cscope cscopelist: distdir: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done check-am: all-am check: check-am all-am: Makefile installdirs: install: install-am install-exec: install-exec-am install-data: install-data-am uninstall: uninstall-am install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-am install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: -test -z "$(MOSTLYCLEANFILES)" || rm -f $(MOSTLYCLEANFILES) clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." clean: clean-am clean-am: clean-generic clean-libtool mostlyclean-am distclean: distclean-am -rm -f Makefile distclean-am: clean-am distclean-generic dvi: dvi-am dvi-am: html: html-am html-am: info: info-am info-am: install-data-am: install-dvi: install-dvi-am install-dvi-am: install-exec-am: install-html: install-html-am install-html-am: install-info: install-info-am install-info-am: install-man: install-pdf: install-pdf-am install-pdf-am: install-ps: install-ps-am install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-am -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-am mostlyclean-am: mostlyclean-generic mostlyclean-libtool pdf: pdf-am pdf-am: ps: ps-am ps-am: uninstall-am: .MAKE: install-am install-strip .PHONY: all all-am check check-am clean clean-generic clean-libtool \ cscopelist-am ctags-am distclean distclean-generic \ distclean-libtool distdir dvi dvi-am html html-am info info-am \ install install-am install-data install-data-am install-dvi \ install-dvi-am install-exec install-exec-am install-html \ install-html-am install-info install-info-am install-man \ install-pdf install-pdf-am install-ps install-ps-am \ install-strip installcheck installcheck-am installdirs \ maintainer-clean maintainer-clean-generic mostlyclean \ mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ tags-am uninstall uninstall-am # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: telepathy-gabble-0.18.2/lib/ext/wocky/tools/check-coding-style.mk0000644000175000017500000000076412200204546024666 0ustar00smcvsmcv00000000000000check-coding-style: @fail=0; \ if test -n "$(check_misc_sources)"; then \ tools_dir=$(top_srcdir)/tools \ sh $(top_srcdir)/tools/check-misc.sh \ $(addprefix $(srcdir)/,$(check_misc_sources)) || fail=1; \ fi; \ if test -n "$(check_c_sources)"; then \ tools_dir=$(top_srcdir)/tools \ sh $(top_srcdir)/tools/check-c-style.sh \ $(addprefix $(srcdir)/,$(check_c_sources)) || fail=1; \ fi;\ if test yes = "$(ENABLE_CODING_STYLE_CHECKS)"; then \ exit "$$fail";\ else \ exit 0;\ fi telepathy-gabble-0.18.2/lib/ext/wocky/tools/flymake.mk0000644000175000017500000000014412200204546022632 0ustar00smcvsmcv00000000000000check-syntax: $(CC) $(AM_CPPFLAGS) $(AM_CFLAGS) -fsyntax-only $(CHK_SOURCES) .PHONY: check-syntax telepathy-gabble-0.18.2/lib/ext/wocky/wocky/0000755000175000017500000000000012312537050020651 5ustar00smcvsmcv00000000000000telepathy-gabble-0.18.2/lib/ext/wocky/wocky/wocky-http-proxy.c0000644000175000017500000002506312200204546024310 0ustar00smcvsmcv00000000000000 /* wocky-http-proxy.c: Source for WockyHttpProxy * * Copyright (C) 2010 Collabora, Ltd. * @author Nicolas Dufresne * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 "config.h" #include "wocky-http-proxy.h" #include #include struct _WockyHttpProxy { GObject parent; }; struct _WockyHttpProxyClass { GObjectClass parent_class; }; static void wocky_http_proxy_iface_init (GProxyInterface *proxy_iface); #define wocky_http_proxy_get_type _wocky_http_proxy_get_type G_DEFINE_TYPE_WITH_CODE (WockyHttpProxy, wocky_http_proxy, G_TYPE_OBJECT, G_IMPLEMENT_INTERFACE (G_TYPE_PROXY, wocky_http_proxy_iface_init) g_io_extension_point_set_required_type ( g_io_extension_point_register (G_PROXY_EXTENSION_POINT_NAME), G_TYPE_PROXY); g_io_extension_point_implement (G_PROXY_EXTENSION_POINT_NAME, g_define_type_id, "http", 0)) static void wocky_http_proxy_init (WockyHttpProxy *proxy) { } #define HTTP_END_MARKER "\r\n\r\n" static gchar * create_request (GProxyAddress *proxy_address, gboolean *has_cred) { const gchar *hostname; gint port; const gchar *username; const gchar *password; GString *request; gchar *ascii_hostname; if (has_cred) *has_cred = FALSE; hostname = g_proxy_address_get_destination_hostname (proxy_address); port = g_proxy_address_get_destination_port (proxy_address); username = g_proxy_address_get_username (proxy_address); password = g_proxy_address_get_password (proxy_address); request = g_string_new (NULL); ascii_hostname = g_hostname_to_ascii (hostname); g_string_append_printf (request, "CONNECT %s:%i HTTP/1.0\r\n" "Host: %s:%i\r\n" "Proxy-Connection: keep-alive\r\n" "User-Agent: GLib/%i.%i\r\n", ascii_hostname, port, ascii_hostname, port, GLIB_MAJOR_VERSION, GLIB_MINOR_VERSION); g_free (ascii_hostname); if (username != NULL && password != NULL) { gchar *cred; gchar *base64_cred; if (has_cred) *has_cred = TRUE; cred = g_strdup_printf ("%s:%s", username, password); base64_cred = g_base64_encode ((guchar *) cred, strlen (cred)); g_free (cred); g_string_append_printf (request, "Proxy-Authorization: %s\r\n", base64_cred); g_free (base64_cred); } g_string_append (request, "\r\n"); return g_string_free (request, FALSE); } static gboolean check_reply (const gchar *buffer, gboolean has_cred, GError **error) { gint err_code; const gchar *ptr = buffer + 7; if (strncmp (buffer, "HTTP/1.", 7) != 0 || (*ptr != '0' && *ptr != '1')) { g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_PROXY_FAILED, "Bad HTTP proxy reply"); return FALSE; } ptr++; while (*ptr == ' ') ptr++; err_code = atoi (ptr); if (err_code < 200 || err_code >= 300) { const gchar *msg_start; gchar *msg; while (g_ascii_isdigit (*ptr)) ptr++; while (*ptr == ' ') ptr++; msg_start = ptr; ptr = strchr (msg_start, '\r'); if (ptr == NULL) ptr = strchr (msg_start, '\0'); msg = g_strndup (msg_start, ptr - msg_start); if (err_code == 407) { if (has_cred) g_set_error (error, G_IO_ERROR, G_IO_ERROR_PROXY_AUTH_FAILED, "HTTP proxy authentication failed"); else g_set_error (error, G_IO_ERROR, G_IO_ERROR_PROXY_NEED_AUTH, "HTTP proxy authentication required"); } else if (msg[0] == '\0') g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_PROXY_FAILED, "Connection failed due to broken HTTP reply"); else g_set_error (error, G_IO_ERROR, G_IO_ERROR_PROXY_FAILED, "HTTP proxy connection failed: %i %s", err_code, msg); g_free (msg); return FALSE; } return TRUE; } static GIOStream * wocky_http_proxy_connect (GProxy *proxy, GIOStream *io_stream, GProxyAddress *proxy_address, GCancellable *cancellable, GError **error) { GInputStream *in; GOutputStream *out; GDataInputStream *data_in; gchar *buffer; gboolean has_cred; in = g_io_stream_get_input_stream (io_stream); out = g_io_stream_get_output_stream (io_stream); data_in = g_data_input_stream_new (in); g_filter_input_stream_set_close_base_stream (G_FILTER_INPUT_STREAM (data_in), FALSE); buffer = create_request (proxy_address, &has_cred); if (!g_output_stream_write_all (out, buffer, strlen (buffer), NULL, cancellable, error)) goto error; g_free (buffer); buffer = g_data_input_stream_read_until (data_in, HTTP_END_MARKER, NULL, cancellable, error); g_object_unref (data_in); data_in = NULL; if (buffer == NULL) { if (error && (*error == NULL)) g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_PROXY_FAILED, "HTTP proxy server closed connection unexpectedly."); goto error; } if (!check_reply (buffer, has_cred, error)) goto error; g_free (buffer); return g_object_ref (io_stream); error: if (data_in != NULL) g_object_unref (data_in); g_free (buffer); return NULL; } typedef struct { GSimpleAsyncResult *simple; GIOStream *io_stream; gchar *buffer; gssize length; gssize offset; GDataInputStream *data_in; gboolean has_cred; GCancellable *cancellable; } ConnectAsyncData; static void request_write_cb (GObject *source, GAsyncResult *res, gpointer user_data); static void reply_read_cb (GObject *source, GAsyncResult *res, gpointer user_data); static void free_connect_data (ConnectAsyncData *data) { if (data->io_stream != NULL) g_object_unref (data->io_stream); g_free (data->buffer); if (data->data_in != NULL) g_object_unref (data->data_in); if (data->cancellable != NULL) g_object_unref (data->cancellable); g_slice_free (ConnectAsyncData, data); } static void complete_async_from_error (ConnectAsyncData *data, GError *error) { GSimpleAsyncResult *simple = data->simple; if (error == NULL) g_set_error_literal (&error, G_IO_ERROR, G_IO_ERROR_PROXY_FAILED, "HTTP proxy server closed connection unexpectedly."); g_simple_async_result_set_from_error (data->simple, error); g_error_free (error); g_simple_async_result_set_op_res_gpointer (simple, NULL, NULL); g_simple_async_result_complete (simple); g_object_unref (simple); } static void do_write (GAsyncReadyCallback callback, ConnectAsyncData *data) { GOutputStream *out; out = g_io_stream_get_output_stream (data->io_stream); g_output_stream_write_async (out, data->buffer + data->offset, data->length - data->offset, G_PRIORITY_DEFAULT, data->cancellable, callback, data); } static void wocky_http_proxy_connect_async (GProxy *proxy, GIOStream *io_stream, GProxyAddress *proxy_address, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { GSimpleAsyncResult *simple; ConnectAsyncData *data; GInputStream *in; simple = g_simple_async_result_new (G_OBJECT (proxy), callback, user_data, wocky_http_proxy_connect_async); data = g_slice_new0 (ConnectAsyncData); data->simple = simple; data->io_stream = g_object_ref (io_stream); if (cancellable != NULL) data->cancellable = g_object_ref (cancellable); in = g_io_stream_get_input_stream (io_stream); data->data_in = g_data_input_stream_new (in); g_filter_input_stream_set_close_base_stream (G_FILTER_INPUT_STREAM (data->data_in), FALSE); g_simple_async_result_set_op_res_gpointer (simple, data, (GDestroyNotify) free_connect_data); data->buffer = create_request (proxy_address, &data->has_cred); data->length = strlen (data->buffer); data->offset = 0; do_write (request_write_cb, data); } static void request_write_cb (GObject *source, GAsyncResult *res, gpointer user_data) { GError *error = NULL; ConnectAsyncData *data = user_data; gssize written; written = g_output_stream_write_finish (G_OUTPUT_STREAM (source), res, &error); if (written < 0) { complete_async_from_error (data, error); return; } data->offset += written; if (data->offset == data->length) { g_free (data->buffer); data->buffer = NULL; g_data_input_stream_read_until_async (data->data_in, HTTP_END_MARKER, G_PRIORITY_DEFAULT, data->cancellable, reply_read_cb, data); } else { do_write (request_write_cb, data); } } static void reply_read_cb (GObject *source, GAsyncResult *res, gpointer user_data) { GError *error = NULL; ConnectAsyncData *data = user_data; data->buffer = g_data_input_stream_read_until_finish (data->data_in, res, NULL, &error); if (data->buffer == NULL) { complete_async_from_error (data, error); return; } if (!check_reply (data->buffer, data->has_cred, &error)) { complete_async_from_error (data, error); return; } g_simple_async_result_complete (data->simple); g_object_unref (data->simple); } static GIOStream * wocky_http_proxy_connect_finish (GProxy *proxy, GAsyncResult *result, GError **error) { GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (result); ConnectAsyncData *data = g_simple_async_result_get_op_res_gpointer (simple); if (g_simple_async_result_propagate_error (simple, error)) return NULL; return g_object_ref (data->io_stream); } static gboolean wocky_http_proxy_supports_hostname (GProxy *proxy) { return TRUE; } static void wocky_http_proxy_class_init (WockyHttpProxyClass *class) { } static void wocky_http_proxy_iface_init (GProxyInterface *proxy_iface) { proxy_iface->connect = wocky_http_proxy_connect; proxy_iface->connect_async = wocky_http_proxy_connect_async; proxy_iface->connect_finish = wocky_http_proxy_connect_finish; proxy_iface->supports_hostname = wocky_http_proxy_supports_hostname; } telepathy-gabble-0.18.2/lib/ext/wocky/wocky/wocky-http-proxy.h0000644000175000017500000000353012200204546024310 0ustar00smcvsmcv00000000000000 /* wocky-http-proxy.h: Header for WockyHttpProxy * * Copyright (C) 2010 Collabora, Ltd. * @author Nicolas Dufresne * * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 */ #if !defined (WOCKY_COMPILATION) # error "This is an internal header." #endif #ifndef _WOCKY_HTTP_PROXY_H_ #define _WOCKY_HTTP_PROXY_H_ #include G_BEGIN_DECLS #define WOCKY_TYPE_HTTP_PROXY (_wocky_http_proxy_get_type ()) #define WOCKY_HTTP_PROXY(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), WOCKY_TYPE_HTTP_PROXY, WockyHttpProxy)) #define WOCKY_HTTP_PROXY_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), WOCKY_TYPE_HTTP_PROXY, WockyHttpProxyClass)) #define WOCKY_IS_HTTP_PROXY(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), WOCKY_TYPE_HTTP_PROXY)) #define WOCKY_IS_HTTP_PROXY_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), WOCKY_TYPE_HTTP_PROXY)) #define WOCKY_HTTP_PROXY_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), WOCKY_TYPE_HTTP_PROXY, WockyHttpProxyClass)) typedef struct _WockyHttpProxy WockyHttpProxy; typedef struct _WockyHttpProxyClass WockyHttpProxyClass; GType _wocky_http_proxy_get_type (void); G_END_DECLS #endif /* _WOCKY_HTTP_PROXY_H_ */ telepathy-gabble-0.18.2/lib/ext/wocky/wocky/wocky-tls.c0000644000175000017500000016043312200204546022755 0ustar00smcvsmcv00000000000000/* * Wocky TLS integration - GNUTLS implementation * (just named -tls for historical reasons) * * Copyright © 2008 Christian Kellner, Samuel Cormier-Iijima * Copyright © 2008-2009 Codethink Limited * Copyright © 2009 Collabora Limited * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; either version 2 of the licence or (at * your option) any later version. * * Authors: Ryan Lortie * Christian Kellner * Samuel Cormier-Iijima * Vivek Dasmohapatra * * Upstream: git://git.gnome.org/gnio * Branched at: 42b00d143fcf644880456d06d3a20b6e990a7fa3 * "toss out everything that moved to glib" * * This file follows the original coding style from upstream, not house * collabora style: It is a copy of unmerged gnio TLS support with the * 'g' prefixes changes to 'wocky' and server-side TLS support added. */ /** * SECTION: wocky-tls * @title: Wocky GnuTLS TLS * @short_description: Establish TLS sessions * * The WOCKY_TLS_DEBUG_LEVEL environment variable can be used to print debug * output from GNU TLS. To enable it, set it to a value from 1 to 9. * Higher values will print more information. See the documentation of * gnutls_global_set_log_level for more details. * * Increasing the value past certain thresholds will also trigger increased * debugging output from within wocky-tls.c as well. * * The WOCKY_GNUTLS_OPTIONS environment variable can be set to a gnutls * priority string [See gnutls-cli(1) or the gnutls_priority_init docs] * to control most tls protocol details. An empty or unset value is roughly * equivalent to a priority string of "SECURE:+COMP-DEFLATE". */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "wocky-tls.h" #include #include #include #include #include #include #ifdef ENABLE_PREFER_STREAM_CIPHERS #define DEFAULT_TLS_OPTIONS \ /* start with nothing enabled by default */ \ "NONE:" \ /* enable all the normal algorithms */ \ "+VERS-TLS-ALL:+SIGN-ALL:+MAC-ALL:+CTYPE-ALL:+RSA:" \ /* prefer deflate compression, but fall back to null compression */ \ "+COMP-DEFLATE:+COMP-NULL:" \ /* our preferred stream ciphers */ \ "+ARCFOUR-128:+ARCFOUR-40:" \ /* all the other ciphers */ \ "+AES-128-CBC:+AES-256-CBC:+3DES-CBC:+DES-CBC:+RC2-40:" \ "+CAMELLIA-256-CBC:+CAMELLIA-128-CBC" #else #define DEFAULT_TLS_OPTIONS \ "NORMAL:" /* all secure algorithms */ \ "-COMP-NULL:" /* remove null compression */ \ "+COMP-DEFLATE:" /* prefer deflate */ \ "+COMP-NULL" /* fall back to null */ #endif #define WOCKY_DEBUG_FLAG WOCKY_DEBUG_TLS #define DEBUG_HANDSHAKE_LEVEL 5 #define DEBUG_ASYNC_DETAIL_LEVEL 6 #define VERIFY_STRICT GNUTLS_VERIFY_DO_NOT_ALLOW_SAME #define VERIFY_NORMAL GNUTLS_VERIFY_ALLOW_X509_V1_CA_CRT #define VERIFY_LENIENT ( GNUTLS_VERIFY_ALLOW_X509_V1_CA_CRT | \ GNUTLS_VERIFY_ALLOW_ANY_X509_V1_CA_CRT | \ GNUTLS_VERIFY_ALLOW_SIGN_RSA_MD2 | \ GNUTLS_VERIFY_ALLOW_SIGN_RSA_MD5 | \ GNUTLS_VERIFY_DISABLE_TIME_CHECKS | \ GNUTLS_VERIFY_DISABLE_CA_SIGN ) #include "wocky-debug-internal.h" #include "wocky-utils.h" #include #include #include #include #include enum { PROP_S_NONE, PROP_S_STREAM, PROP_S_SERVER, PROP_S_DHBITS, PROP_S_KEYFILE, PROP_S_CERTFILE, }; enum { PROP_C_NONE, PROP_C_SESSION, }; enum { PROP_O_NONE, PROP_O_SESSION }; enum { PROP_I_NONE, PROP_I_SESSION }; typedef struct { gboolean active; gint io_priority; GCancellable *cancellable; GObject *source_object; GAsyncReadyCallback callback; gpointer user_data; gpointer source_tag; GError *error; } WockyTLSJob; typedef enum { WOCKY_TLS_OP_READ, WOCKY_TLS_OP_WRITE } WockyTLSOperation; typedef enum { WOCKY_TLS_OP_STATE_IDLE, WOCKY_TLS_OP_STATE_ACTIVE, WOCKY_TLS_OP_STATE_DONE } WockyTLSOpState; typedef struct { WockyTLSJob job; } WockyTLSJobHandshake; typedef struct { WockyTLSJob job; gconstpointer buffer; gsize count; } WockyTLSJobWrite; typedef struct { WockyTLSJob job; gpointer buffer; gsize count; } WockyTLSJobRead; typedef struct { WockyTLSOpState state; guint8 *buffer; gssize requested; gssize result; GError *error; } WockyTLSOp; typedef GIOStreamClass WockyTLSConnectionClass; typedef GObjectClass WockyTLSSessionClass; typedef GInputStreamClass WockyTLSInputStreamClass; typedef GOutputStreamClass WockyTLSOutputStreamClass; static gnutls_dh_params_t dh_0768 = NULL; static gnutls_dh_params_t dh_1024 = NULL; static gnutls_dh_params_t dh_2048 = NULL; static gnutls_dh_params_t dh_3072 = NULL; static gnutls_dh_params_t dh_4096 = NULL; struct _WockyTLSSession { GObject parent; GIOStream *stream; GCancellable *cancellable; GError *error; gboolean async; /* tls server support */ gboolean server; gnutls_dh_params_t dh_params; guint dh_bits; gchar *key_file; gchar *cert_file; /* frontend jobs */ WockyTLSJobHandshake handshake_job; WockyTLSJobRead read_job; WockyTLSJobWrite write_job; /* backend jobs */ WockyTLSOp read_op; WockyTLSOp write_op; gnutls_session_t session; gnutls_certificate_credentials gnutls_cert_cred; }; typedef struct { GInputStream parent; WockyTLSSession *session; } WockyTLSInputStream; typedef struct { GOutputStream parent; WockyTLSSession *session; } WockyTLSOutputStream; struct _WockyTLSConnection { GIOStream parent; WockyTLSSession *session; WockyTLSInputStream *input; WockyTLSOutputStream *output; }; static guint tls_debug_level = 0; static GType wocky_tls_input_stream_get_type (void); static GType wocky_tls_output_stream_get_type (void); G_DEFINE_TYPE (WockyTLSConnection, wocky_tls_connection, G_TYPE_IO_STREAM); G_DEFINE_TYPE (WockyTLSSession, wocky_tls_session, G_TYPE_OBJECT); G_DEFINE_TYPE (WockyTLSInputStream, wocky_tls_input_stream, G_TYPE_INPUT_STREAM); G_DEFINE_TYPE (WockyTLSOutputStream, wocky_tls_output_stream, G_TYPE_OUTPUT_STREAM); #define WOCKY_TYPE_TLS_INPUT_STREAM (wocky_tls_input_stream_get_type ()) #define WOCKY_TYPE_TLS_OUTPUT_STREAM (wocky_tls_output_stream_get_type ()) #define WOCKY_TLS_INPUT_STREAM(inst) (G_TYPE_CHECK_INSTANCE_CAST ((inst), \ WOCKY_TYPE_TLS_INPUT_STREAM, \ WockyTLSInputStream)) #define WOCKY_TLS_OUTPUT_STREAM(inst) (G_TYPE_CHECK_INSTANCE_CAST ((inst), \ WOCKY_TYPE_TLS_OUTPUT_STREAM, \ WockyTLSOutputStream)) static const gchar *hdesc_to_string (long desc) { #define HDESC(x) case GNUTLS_HANDSHAKE_##x: return #x; break; switch (desc) { HDESC (HELLO_REQUEST); HDESC (CLIENT_HELLO); HDESC (SERVER_HELLO); HDESC (CERTIFICATE_PKT); HDESC (SERVER_KEY_EXCHANGE); HDESC (CERTIFICATE_REQUEST); HDESC (SERVER_HELLO_DONE); HDESC (CERTIFICATE_VERIFY); HDESC (CLIENT_KEY_EXCHANGE); HDESC (FINISHED); HDESC (SUPPLEMENTAL); } return "Unknown State"; } static const gchar *error_to_string (long error) { const gchar *result; result = gnutls_strerror_name (error); if (result != NULL) return result; return "Unknown Error"; } static gboolean wocky_tls_set_error (GError **error, gssize result) { int code = (int) result; if (result < 0) g_set_error (error, WOCKY_TLS_ERROR, 0, "%d: %s", code, error_to_string (code)); return result < 0; } static GSimpleAsyncResult * wocky_tls_job_make_result (WockyTLSJob *job, gssize result) { if (result != GNUTLS_E_AGAIN) { GSimpleAsyncResult *simple; GError *error = NULL; simple = g_simple_async_result_new (job->source_object, job->callback, job->user_data, job->source_tag); if (job->error != NULL) { #ifdef WOCKY_TLS_STRICT_ERROR_ASSERTIONS g_assert (result == GNUTLS_E_PUSH_ERROR || result == GNUTLS_E_PULL_ERROR); #endif g_simple_async_result_set_from_error (simple, job->error); g_error_free (job->error); } else if (wocky_tls_set_error (&error, result)) { g_simple_async_result_set_from_error (simple, error); g_error_free (error); } if (job->cancellable != NULL) g_object_unref (job->cancellable); job->cancellable = NULL; g_object_unref (job->source_object); job->source_object = NULL; job->active = FALSE; return simple; } else { g_assert (job->active); return NULL; } } static void wocky_tls_job_result_gssize (WockyTLSJob *job, gssize result) { GSimpleAsyncResult *simple; if ((simple = wocky_tls_job_make_result (job, result))) { if (result >= 0) g_simple_async_result_set_op_res_gssize (simple, result); g_simple_async_result_complete (simple); g_object_unref (simple); } } static void wocky_tls_job_result_boolean (WockyTLSJob *job, gint result) { GSimpleAsyncResult *simple; if ((simple = wocky_tls_job_make_result (job, result))) { g_simple_async_result_complete (simple); g_object_unref (simple); } } static void wocky_tls_session_try_operation (WockyTLSSession *session, WockyTLSOperation operation) { if (session->handshake_job.job.active) { gint result; DEBUG ("session %p: async job handshake", session); session->async = TRUE; result = gnutls_handshake (session->session); g_assert (result != GNUTLS_E_INTERRUPTED); if (tls_debug_level >= DEBUG_HANDSHAKE_LEVEL) { gnutls_handshake_description_t i; gnutls_handshake_description_t o; DEBUG ("session %p: async job handshake: %d %s", session, result, error_to_string(result)); i = gnutls_handshake_get_last_in (session->session); o = gnutls_handshake_get_last_out (session->session); DEBUG ("session %p: async job handshake: { in: %s; out: %s }", session, hdesc_to_string (i), hdesc_to_string (o)); } session->async = FALSE; wocky_tls_job_result_boolean (&session->handshake_job.job, result); } else if (operation == WOCKY_TLS_OP_READ) { gssize result = 0; if (tls_debug_level >= DEBUG_ASYNC_DETAIL_LEVEL) DEBUG ("async job OP_READ"); g_assert (session->read_job.job.active); /* If the read result is 0, the remote end disconnected us, no need to * pull data through gnutls_record_recv in that case */ if (session->read_op.result != 0) { session->async = TRUE; result = gnutls_record_recv (session->session, session->read_job.buffer, session->read_job.count); g_assert (result != GNUTLS_E_INTERRUPTED); session->async = FALSE; } wocky_tls_job_result_gssize (&session->read_job.job, result); } else { gssize result; if (tls_debug_level >= DEBUG_ASYNC_DETAIL_LEVEL) DEBUG ("async job OP_WRITE: %"G_GSIZE_FORMAT, session->write_job.count); g_assert (operation == WOCKY_TLS_OP_WRITE); g_assert (session->write_job.job.active); session->async = TRUE; result = gnutls_record_send (session->session, session->write_job.buffer, session->write_job.count); g_assert (result != GNUTLS_E_INTERRUPTED); session->async = FALSE; wocky_tls_job_result_gssize (&session->write_job.job, result); } } static void wocky_tls_job_start (WockyTLSJob *job, gpointer source_object, gint io_priority, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data, gpointer source_tag) { g_assert (job->active == FALSE); g_assert (job->cancellable == NULL); /* this is always a circular reference, so it will keep the * session alive for as long as the job is running. */ job->source_object = g_object_ref (source_object); job->io_priority = io_priority; if (cancellable != NULL) job->cancellable = g_object_ref (cancellable); job->callback = callback; job->user_data = user_data; job->source_tag = source_tag; job->error = NULL; job->active = TRUE; } WockyTLSConnection * wocky_tls_session_handshake (WockyTLSSession *session, GCancellable *cancellable, GError **error) { gint result; DEBUG ("sync job handshake"); session->error = NULL; session->cancellable = cancellable; result = gnutls_handshake (session->session); g_assert (result != GNUTLS_E_INTERRUPTED); g_assert (result != GNUTLS_E_AGAIN); session->cancellable = NULL; if (tls_debug_level >= DEBUG_HANDSHAKE_LEVEL) DEBUG ("sync job handshake: %d %s", result, error_to_string (result)); if (session->error != NULL) { g_assert (result == GNUTLS_E_PULL_ERROR || result == GNUTLS_E_PUSH_ERROR); g_propagate_error (error, session->error); return NULL; } else if (wocky_tls_set_error (error, result)) return NULL; return g_object_new (WOCKY_TYPE_TLS_CONNECTION, "session", session, NULL); } /* ************************************************************************* */ /* adding CA certificates lists for peer certificate verification */ void wocky_tls_session_add_ca (WockyTLSSession *session, const gchar *ca_path) { int n = 0; struct stat target; DEBUG ("adding CA CERT path '%s'", (gchar *) ca_path); if (stat (ca_path, &target) != 0) { DEBUG ("CA file '%s': stat failed)", ca_path); return; } if (S_ISDIR (target.st_mode)) { DIR *dir; struct dirent *entry; if ((dir = opendir (ca_path)) == NULL) return; for (entry = readdir (dir); entry != NULL; entry = readdir (dir)) { struct stat file; gchar *path = g_build_path ("/", ca_path, entry->d_name, NULL); if ((stat (path, &file) == 0) && S_ISREG (file.st_mode)) n += gnutls_certificate_set_x509_trust_file ( session->gnutls_cert_cred, path, GNUTLS_X509_FMT_PEM); g_free (path); } DEBUG ("+ %s: %d certs from dir", ca_path, n); closedir (dir); } else if (S_ISREG (target.st_mode)) { n = gnutls_certificate_set_x509_trust_file (session->gnutls_cert_cred, ca_path, GNUTLS_X509_FMT_PEM); DEBUG ("+ %s: %d certs from file", ca_path, n); } } void wocky_tls_session_add_crl (WockyTLSSession *session, const gchar *crl_path) { int n = 0; struct stat target; DEBUG ("adding CRL CERT path '%s'", (gchar *) crl_path); if (stat (crl_path, &target) != 0) { DEBUG ("CRL file '%s': stat failed)", crl_path); return; } if (S_ISDIR (target.st_mode)) { DIR *dir; struct dirent *entry; if ((dir = opendir (crl_path)) == NULL) return; for (entry = readdir (dir); entry != NULL; entry = readdir (dir)) { struct stat file; gchar *path = g_build_path ("/", crl_path, entry->d_name, NULL); if ((stat (path, &file) == 0) && S_ISREG (file.st_mode)) { int x = gnutls_certificate_set_x509_crl_file ( session->gnutls_cert_cred, path, GNUTLS_X509_FMT_PEM); if (x < 0) DEBUG ("Error loading %s: %d %s", path, x, gnutls_strerror (x)); else n += x; } g_free (path); } DEBUG ("+ %s: %d certs from dir", crl_path, n); closedir (dir); } else if (S_ISREG (target.st_mode)) { n = gnutls_certificate_set_x509_trust_file (session->gnutls_cert_cred, crl_path, GNUTLS_X509_FMT_PEM); if (n < 0) DEBUG ("Error loading '%s': %d %s", crl_path, n, gnutls_strerror(n)); else DEBUG ("+ %s: %d certs from file", crl_path, n); } } /* ************************************************************************* */ void wocky_tls_session_handshake_async (WockyTLSSession *session, gint io_priority, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { wocky_tls_job_start (&session->handshake_job.job, session, io_priority, cancellable, callback, user_data, wocky_tls_session_handshake_async); wocky_tls_session_try_operation (session, 0); } WockyTLSConnection * wocky_tls_session_handshake_finish (WockyTLSSession *session, GAsyncResult *result, GError **error) { GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (result); { GObject *source_object; source_object = g_async_result_get_source_object (result); g_object_unref (source_object); g_return_val_if_fail (G_OBJECT (session) == source_object, NULL); } g_return_val_if_fail (wocky_tls_session_handshake_async == g_simple_async_result_get_source_tag (simple), NULL); if (g_simple_async_result_propagate_error (simple, error)) return NULL; DEBUG ("connection OK"); return g_object_new (WOCKY_TYPE_TLS_CONNECTION, "session", session, NULL); } GPtrArray * wocky_tls_session_get_peers_certificate (WockyTLSSession *session, WockyTLSCertType *type) { guint idx; guint n_peers; const gnutls_datum_t *peers = NULL; GPtrArray *certificates; peers = gnutls_certificate_get_peers (session->session, &n_peers); if (peers == NULL) return NULL; certificates = g_ptr_array_new_with_free_func ((GDestroyNotify) g_array_unref); for (idx = 0; idx < n_peers; idx++) { GArray *cert = g_array_sized_new (TRUE, TRUE, sizeof (guchar), peers[idx].size); g_array_append_vals (cert, peers[idx].data, peers[idx].size); g_ptr_array_add (certificates, cert); } if (type != NULL) { switch (gnutls_certificate_type_get (session->session)) { case GNUTLS_CRT_X509: *type = WOCKY_TLS_CERT_TYPE_X509; break; case GNUTLS_CRT_OPENPGP: *type = WOCKY_TLS_CERT_TYPE_OPENPGP; break; default: *type = WOCKY_TLS_CERT_TYPE_NONE; break; } } return certificates; } static inline gboolean contains_illegal_wildcard (const char *name, int size) { if (name[0] == '*' && name[1] == '.') { name += 2; size -= 2; } if (memchr (name, '*', size) != NULL) return TRUE; return FALSE; } #define OID_X520_COMMON_NAME "2.5.4.3" static gboolean cert_names_are_valid (gnutls_x509_crt_t cert) { char name[256]; size_t size; gboolean found = FALSE; int type = 0; int i = 0; /* GNUTLS allows wildcards anywhere within the certificate name, but XMPP only * permits a single leading "*.". */ for (i = 0; type >= 0; i++) { size = sizeof (name); type = gnutls_x509_crt_get_subject_alt_name (cert, i, name, &size, NULL); switch (type) { case GNUTLS_SAN_DNSNAME: case GNUTLS_SAN_IPADDRESS: found = TRUE; if (contains_illegal_wildcard (name, size)) return FALSE; break; default: break; } } if (!found) { size = sizeof (name); /* cert has no names at all? bizarro! */ if (gnutls_x509_crt_get_dn_by_oid (cert, OID_X520_COMMON_NAME, 0, 0, name, &size) < 0) return FALSE; found = TRUE; if (contains_illegal_wildcard (name, size)) return FALSE; } /* found a name, wasn't a duff wildcard */ return found; } int wocky_tls_session_verify_peer (WockyTLSSession *session, const gchar *peername, GStrv extra_identities, WockyTLSVerificationLevel level, WockyTLSCertStatus *status) { int rval = -1; guint peer_cert_status = 0; gboolean peer_name_ok = TRUE; gnutls_certificate_verify_flags check; /* list gnutls cert error conditions in descending order of noteworthiness * * and map them to wocky cert error conditions */ static const struct { gnutls_certificate_status_t gnutls; WockyTLSCertStatus wocky; } status_map[] = { { GNUTLS_CERT_REVOKED, WOCKY_TLS_CERT_REVOKED }, { GNUTLS_CERT_NOT_ACTIVATED, WOCKY_TLS_CERT_NOT_ACTIVE }, { GNUTLS_CERT_EXPIRED, WOCKY_TLS_CERT_EXPIRED }, { GNUTLS_CERT_SIGNER_NOT_FOUND, WOCKY_TLS_CERT_SIGNER_UNKNOWN }, { GNUTLS_CERT_SIGNER_NOT_CA, WOCKY_TLS_CERT_SIGNER_UNAUTHORISED }, { GNUTLS_CERT_INSECURE_ALGORITHM, WOCKY_TLS_CERT_INSECURE }, { GNUTLS_CERT_INVALID, WOCKY_TLS_CERT_INVALID }, { ~((long) 0), WOCKY_TLS_CERT_UNKNOWN_ERROR }, { 0, WOCKY_TLS_CERT_OK } }; g_assert (status != NULL); *status = WOCKY_TLS_CERT_OK; switch (level) { case WOCKY_TLS_VERIFY_STRICT: check = VERIFY_STRICT; break; case WOCKY_TLS_VERIFY_NORMAL: check = VERIFY_NORMAL; break; case WOCKY_TLS_VERIFY_LENIENT: check = VERIFY_LENIENT; break; default: g_warn_if_reached (); check = VERIFY_STRICT; break; } DEBUG ("setting gnutls verify flags level to: %s", wocky_enum_to_nick (WOCKY_TYPE_TLS_VERIFICATION_LEVEL, level)); gnutls_certificate_set_verify_flags (session->gnutls_cert_cred, check); rval = gnutls_certificate_verify_peers2 (session->session, &peer_cert_status); if (rval != GNUTLS_E_SUCCESS) { switch (rval) { case GNUTLS_E_NO_CERTIFICATE_FOUND: case GNUTLS_E_INVALID_REQUEST: *status = WOCKY_TLS_CERT_NO_CERTIFICATE; break; case GNUTLS_E_INSUFFICIENT_CREDENTIALS: *status = WOCKY_TLS_CERT_INSECURE; break; case GNUTLS_E_CONSTRAINT_ERROR: *status = WOCKY_TLS_CERT_MAYBE_DOS; break; case GNUTLS_E_MEMORY_ERROR: *status = WOCKY_TLS_CERT_INTERNAL_ERROR; break; default: *status = WOCKY_TLS_CERT_UNKNOWN_ERROR; } return rval; } /* if we get this far, we have a structurally valid certificate * * signed by _someone_: check the hostname matches the peername */ if (peername != NULL || extra_identities != NULL) { const gnutls_datum_t *peers; guint n_peers; gnutls_x509_crt_t x509; gnutls_openpgp_crt_t opgp; /* we know these ops must succeed, or verify_peers2 would have * * failed before we got here: We just need to duplicate a bit * * of what it does: */ peers = gnutls_certificate_get_peers (session->session, &n_peers); switch (gnutls_certificate_type_get (session->session)) { case GNUTLS_CRT_X509: DEBUG ("checking X509 cert"); if ((rval = gnutls_x509_crt_init (&x509)) == GNUTLS_E_SUCCESS) { gnutls_x509_crt_import (x509, &peers[0], GNUTLS_X509_FMT_DER); if (peername != NULL) { if (!cert_names_are_valid (x509)) { rval = 0; } else { rval = gnutls_x509_crt_check_hostname (x509, peername); DEBUG ("gnutls_x509_crt_check_hostname: %s -> %d", peername, rval); } } else { rval = 0; } if (rval == 0 && extra_identities != NULL) { if (!cert_names_are_valid (x509)) { rval = 0; } else { gint i; for (i = 0; extra_identities[i] != NULL; i++) { rval = gnutls_x509_crt_check_hostname (x509, extra_identities[i]); DEBUG ("gnutls_x509_crt_check_hostname: %s -> %d", extra_identities[i], rval); if (rval != 0) break; } } } rval = (rval == 0) ? -1 : GNUTLS_E_SUCCESS; gnutls_x509_crt_deinit (x509); } break; case GNUTLS_CRT_OPENPGP: DEBUG ("checking PGP cert"); if ((rval = gnutls_openpgp_crt_init (&opgp)) == GNUTLS_E_SUCCESS) { gnutls_openpgp_crt_import (opgp, &peers[0], GNUTLS_OPENPGP_FMT_RAW); rval = gnutls_openpgp_crt_check_hostname (opgp, peername); DEBUG ("gnutls_openpgp_crt_check_hostname: %s -> %d", peername, rval); if (peername != NULL) { rval = gnutls_openpgp_crt_check_hostname (opgp, peername); DEBUG ("gnutls_openpgp_crt_check_hostname: %s -> %d", peername, rval); } else { rval = 0; } if (rval == 0 && extra_identities != NULL) { gint i; for (i = 0; extra_identities[i] != NULL; i++) { rval = gnutls_openpgp_crt_check_hostname (opgp, extra_identities[i]); DEBUG ("gnutls_openpgp_crt_check_hostname: %s -> %d", extra_identities[i], rval); if (rval != 0) break; } } rval = (rval == 0) ? -1 : GNUTLS_E_SUCCESS; gnutls_openpgp_crt_deinit (opgp); } break; default: /* theoretically, this can't happen if ...verify_peers2 is working: */ DEBUG ("unknown cert type!"); rval = GNUTLS_E_INVALID_REQUEST; } peer_name_ok = (rval == GNUTLS_E_SUCCESS); } DEBUG ("peer_name_ok: %d", peer_name_ok ); /* if the hostname didn't match, we can just bail out with an error here * * otherwise we need to figure out which error (if any) our verification * * call failed with: */ if (!peer_name_ok) *status = WOCKY_TLS_CERT_NAME_MISMATCH; else { /* Gnutls cert checking can return multiple errors bitwise &ed together * * but we are realy only interested in the "most important" error: */ int x; *status = WOCKY_TLS_CERT_OK; for (x = 0; status_map[x].gnutls != 0; x++) { DEBUG ("checking gnutls error %d", status_map[x].gnutls); if (peer_cert_status & status_map[x].gnutls) { DEBUG ("gnutls error %d set", status_map[x].gnutls); *status = status_map[x].wocky; rval = GNUTLS_E_CERTIFICATE_ERROR; break; } } } return rval; } static gssize wocky_tls_input_stream_read (GInputStream *stream, void *buffer, gsize count, GCancellable *cancellable, GError **error) { WockyTLSSession *session = WOCKY_TLS_INPUT_STREAM (stream)->session; gssize result; session->cancellable = cancellable; result = gnutls_record_recv (session->session, buffer, count); g_assert (result != GNUTLS_E_INTERRUPTED); g_assert (result != GNUTLS_E_AGAIN); session->cancellable = NULL; if (session->error != NULL) { g_assert (result == GNUTLS_E_PULL_ERROR); g_propagate_error (error, session->error); return -1; } else if (wocky_tls_set_error (error, result)) return -1; return result; } static void wocky_tls_input_stream_read_async (GInputStream *stream, void *buffer, gsize count, gint io_priority, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { WockyTLSSession *session = WOCKY_TLS_INPUT_STREAM (stream)->session; wocky_tls_job_start (&session->read_job.job, stream, io_priority, cancellable, callback, user_data, wocky_tls_input_stream_read_async); session->read_job.buffer = buffer; session->read_job.count = count; wocky_tls_session_try_operation (session, WOCKY_TLS_OP_READ); } static gssize wocky_tls_input_stream_read_finish (GInputStream *stream, GAsyncResult *result, GError **error) { GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (result); { GObject *source_object; source_object = g_async_result_get_source_object (result); g_object_unref (source_object); g_return_val_if_fail (G_OBJECT (stream) == source_object, -1); } g_return_val_if_fail (wocky_tls_input_stream_read_async == g_simple_async_result_get_source_tag (simple), -1); if (g_simple_async_result_propagate_error (simple, error)) return -1; return g_simple_async_result_get_op_res_gssize (simple); } static gssize wocky_tls_output_stream_write (GOutputStream *stream, const void *buffer, gsize count, GCancellable *cancellable, GError **error) { WockyTLSSession *session = WOCKY_TLS_OUTPUT_STREAM (stream)->session; gssize result; session->cancellable = cancellable; result = gnutls_record_send (session->session, buffer, count); g_assert (result != GNUTLS_E_INTERRUPTED); g_assert (result != GNUTLS_E_AGAIN); session->cancellable = NULL; if (session->error != NULL) { g_assert (result == GNUTLS_E_PUSH_ERROR); g_propagate_error (error, session->error); return -1; } else if (wocky_tls_set_error (error, result)) return -1; return result; } static void wocky_tls_output_stream_write_async (GOutputStream *stream, const void *buffer, gsize count, gint io_priority, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { WockyTLSSession *session = WOCKY_TLS_OUTPUT_STREAM (stream)->session; wocky_tls_job_start (&session->write_job.job, stream, io_priority, cancellable, callback, user_data, wocky_tls_output_stream_write_async); session->write_job.buffer = buffer; session->write_job.count = count; wocky_tls_session_try_operation (session, WOCKY_TLS_OP_WRITE); } static gssize wocky_tls_output_stream_write_finish (GOutputStream *stream, GAsyncResult *result, GError **error) { GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (result); { GObject *source_object; source_object = g_async_result_get_source_object (result); g_object_unref (source_object); g_return_val_if_fail (G_OBJECT (stream) == source_object, -1); } g_return_val_if_fail (wocky_tls_output_stream_write_async == g_simple_async_result_get_source_tag (simple), -1); if (g_simple_async_result_propagate_error (simple, error)) return -1; return g_simple_async_result_get_op_res_gssize (simple); } static void wocky_tls_output_stream_init (WockyTLSOutputStream *stream) { } static void wocky_tls_input_stream_init (WockyTLSInputStream *stream) { } static void wocky_tls_output_stream_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { WockyTLSOutputStream *stream = WOCKY_TLS_OUTPUT_STREAM (object); switch (prop_id) { case PROP_C_SESSION: stream->session = g_value_dup_object (value); break; default: g_assert_not_reached (); } } static void wocky_tls_output_stream_constructed (GObject *object) { WockyTLSOutputStream *stream = WOCKY_TLS_OUTPUT_STREAM (object); g_assert (stream->session); } static void wocky_tls_output_stream_finalize (GObject *object) { WockyTLSOutputStream *stream = WOCKY_TLS_OUTPUT_STREAM (object); g_object_unref (stream->session); G_OBJECT_CLASS (wocky_tls_output_stream_parent_class) ->finalize (object); } static void wocky_tls_output_stream_class_init (GOutputStreamClass *class) { GObjectClass *obj_class = G_OBJECT_CLASS (class); class->write_fn = wocky_tls_output_stream_write; class->write_async = wocky_tls_output_stream_write_async; class->write_finish = wocky_tls_output_stream_write_finish; obj_class->set_property = wocky_tls_output_stream_set_property; obj_class->constructed = wocky_tls_output_stream_constructed; obj_class->finalize = wocky_tls_output_stream_finalize; g_object_class_install_property (obj_class, PROP_O_SESSION, g_param_spec_object ("session", "TLS session", "the TLS session object for this stream", WOCKY_TYPE_TLS_SESSION, G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB)); } static void wocky_tls_input_stream_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { WockyTLSInputStream *stream = WOCKY_TLS_INPUT_STREAM (object); switch (prop_id) { case PROP_C_SESSION: stream->session = g_value_dup_object (value); break; default: g_assert_not_reached (); } } static void wocky_tls_input_stream_constructed (GObject *object) { WockyTLSInputStream *stream = WOCKY_TLS_INPUT_STREAM (object); g_assert (stream->session); } static void wocky_tls_input_stream_finalize (GObject *object) { WockyTLSInputStream *stream = WOCKY_TLS_INPUT_STREAM (object); g_object_unref (stream->session); G_OBJECT_CLASS (wocky_tls_input_stream_parent_class) ->finalize (object); } static void wocky_tls_input_stream_class_init (GInputStreamClass *class) { GObjectClass *obj_class = G_OBJECT_CLASS (class); class->read_fn = wocky_tls_input_stream_read; class->read_async = wocky_tls_input_stream_read_async; class->read_finish = wocky_tls_input_stream_read_finish; obj_class->set_property = wocky_tls_input_stream_set_property; obj_class->constructed = wocky_tls_input_stream_constructed; obj_class->finalize = wocky_tls_input_stream_finalize; g_object_class_install_property (obj_class, PROP_I_SESSION, g_param_spec_object ("session", "TLS session", "the TLS session object for this stream", WOCKY_TYPE_TLS_SESSION, G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB)); } static void wocky_tls_connection_init (WockyTLSConnection *connection) { } static void wocky_tls_session_read_ready (GObject *object, GAsyncResult *result, gpointer user_data) { WockyTLSSession *session = WOCKY_TLS_SESSION (user_data); g_assert (session->read_op.state == WOCKY_TLS_OP_STATE_ACTIVE); session->read_op.result = g_input_stream_read_finish (G_INPUT_STREAM (object), result, &session->read_op.error); session->read_op.state = WOCKY_TLS_OP_STATE_DONE; /* don't recurse if the async handler is already running */ if (!session->async) wocky_tls_session_try_operation (session, WOCKY_TLS_OP_READ); } static void wocky_tls_session_write_ready (GObject *object, GAsyncResult *result, gpointer user_data) { WockyTLSSession *session = WOCKY_TLS_SESSION (user_data); gssize ret; g_assert (session->write_op.state == WOCKY_TLS_OP_STATE_ACTIVE); ret = g_output_stream_write_finish (G_OUTPUT_STREAM (object), result, &session->write_op.error); if (ret > 0) { session->write_op.result += ret; if (session->write_op.result < session->write_op.requested) { GOutputStream *stream; WockyTLSJob *active_job; stream = g_io_stream_get_output_stream (session->stream); if (session->handshake_job.job.active) active_job = &session->handshake_job.job; else active_job = &session->write_job.job; g_output_stream_write_async (stream, session->write_op.buffer + session->write_op.result, session->write_op.requested - session->write_op.result, active_job->io_priority, active_job->cancellable, wocky_tls_session_write_ready, session); return; } } else { /* Error or EOF, we're done */ session->write_op.result = ret; } session->write_op.state = WOCKY_TLS_OP_STATE_DONE; /* don't recurse if the async handler is already running */ if (!session->async) wocky_tls_session_try_operation (session, WOCKY_TLS_OP_WRITE); } static ssize_t wocky_tls_session_push_func (gpointer user_data, const void *buffer, size_t count) { WockyTLSSession *session = WOCKY_TLS_SESSION (user_data); GOutputStream *stream; stream = g_io_stream_get_output_stream (session->stream); if (session->async) { WockyTLSJob *active_job; g_assert (session->handshake_job.job.active || session->write_job.job.active); if (session->handshake_job.job.active) active_job = &session->handshake_job.job; else active_job = &session->write_job.job; g_assert (active_job->active); if (session->write_op.state == WOCKY_TLS_OP_STATE_IDLE) { session->write_op.state = WOCKY_TLS_OP_STATE_ACTIVE; session->write_op.buffer = g_memdup (buffer, count); session->write_op.requested = count; session->write_op.error = NULL; session->write_op.result = 0; g_output_stream_write_async (stream, session->write_op.buffer, session->write_op.requested, active_job->io_priority, active_job->cancellable, wocky_tls_session_write_ready, session); if G_UNLIKELY (session->write_op.state != WOCKY_TLS_OP_STATE_ACTIVE) g_warning ("The underlying stream '%s' used by the WockyTLSSession " "called the GAsyncResultCallback recursively. This " "is an error in the underlying implementation: in " "some cases it may lead to unbounded recursion. " "Result callbacks should always be dispatched from " "the mainloop.", G_OBJECT_TYPE_NAME (stream)); } g_assert (session->write_op.state != WOCKY_TLS_OP_STATE_IDLE); g_assert_cmpint (session->write_op.requested, ==, count); g_assert (memcmp (session->write_op.buffer, buffer, count) == 0); if (session->write_op.state == WOCKY_TLS_OP_STATE_DONE) { session->write_op.state = WOCKY_TLS_OP_STATE_IDLE; g_free (session->write_op.buffer); if (session->write_op.result < 0) { active_job->error = session->write_op.error; gnutls_transport_set_errno (session->session, EIO); return -1; } else { g_assert_cmpint (session->write_op.result, <=, count); return session->write_op.result; } } gnutls_transport_set_errno (session->session, EAGAIN); return -1; } else { gssize result; result = g_output_stream_write (stream, buffer, count, session->cancellable, &session->error); if (result < 0) gnutls_transport_set_errno (session->session, EIO); return result; } } static ssize_t wocky_tls_session_pull_func (gpointer user_data, void *buffer, size_t count) { WockyTLSSession *session = WOCKY_TLS_SESSION (user_data); GInputStream *stream; stream = g_io_stream_get_input_stream (session->stream); if (session->async) { WockyTLSJob *active_job; g_assert (session->handshake_job.job.active || session->read_job.job.active); if (session->handshake_job.job.active) active_job = &session->handshake_job.job; else active_job = &session->read_job.job; g_assert (active_job->active); if (session->read_op.state == WOCKY_TLS_OP_STATE_IDLE) { session->read_op.state = WOCKY_TLS_OP_STATE_ACTIVE; session->read_op.buffer = g_malloc (count); session->read_op.requested = count; session->read_op.error = NULL; g_input_stream_read_async (stream, session->read_op.buffer, session->read_op.requested, active_job->io_priority, active_job->cancellable, wocky_tls_session_read_ready, session); if G_UNLIKELY (session->read_op.state != WOCKY_TLS_OP_STATE_ACTIVE) g_warning ("The underlying stream '%s' used by the WockyTLSSession " "called the GAsyncResultCallback recursively. This " "is an error in the underlying implementation: in " "some cases it may lead to unbounded recursion. " "Result callbacks should always be dispatched from " "the mainloop.", G_OBJECT_TYPE_NAME (stream)); } g_assert (session->read_op.state != WOCKY_TLS_OP_STATE_IDLE); g_assert_cmpint (session->read_op.requested, ==, count); if (session->read_op.state == WOCKY_TLS_OP_STATE_DONE) { session->read_op.state = WOCKY_TLS_OP_STATE_IDLE; if (session->read_op.result < 0) { g_free (session->read_op.buffer); session->read_op.buffer = NULL; active_job->error = session->read_op.error; gnutls_transport_set_errno (session->session, EIO); return -1; } else { g_assert_cmpint (session->read_op.result, <=, count); memcpy (buffer, session->read_op.buffer, session->read_op.result); g_free (session->read_op.buffer); session->read_op.buffer = NULL; return session->read_op.result; } } gnutls_transport_set_errno (session->session, EAGAIN); return -1; } else { gssize result; result = g_input_stream_read (stream, buffer, count, session->cancellable, &session->error); if (result < 0) gnutls_transport_set_errno (session->session, EIO); return result; } } static void tls_debug (int level, const char *msg) { DEBUG ("[%d] [%02d] %s", getpid(), level, msg); } static void wocky_tls_session_init (WockyTLSSession *session) { const char *level; guint64 lvl = 0; static gsize initialised; if G_UNLIKELY (g_once_init_enter (&initialised)) { gnutls_global_init (); gnutls_global_set_log_function (tls_debug); g_once_init_leave (&initialised, 1); } if ((level = g_getenv ("WOCKY_TLS_DEBUG_LEVEL")) != NULL) lvl = g_ascii_strtoull (level, NULL, 10); tls_debug_level = lvl; gnutls_global_set_log_level (lvl); } static void wocky_tls_session_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { WockyTLSSession *session = WOCKY_TLS_SESSION (object); switch (prop_id) { case PROP_S_STREAM: session->stream = g_value_dup_object (value); break; case PROP_S_SERVER: session->server = g_value_get_boolean (value); break; case PROP_S_DHBITS: session->dh_bits = g_value_get_uint (value); break; case PROP_S_KEYFILE: session->key_file = g_value_dup_string (value); break; case PROP_S_CERTFILE: session->cert_file = g_value_dup_string (value); break; default: g_assert_not_reached (); } } static const char * tls_options (void) { const char *options = g_getenv ("WOCKY_GNUTLS_OPTIONS"); return (options != NULL && *options != '\0') ? options : DEFAULT_TLS_OPTIONS; } static void wocky_tls_session_constructed (GObject *object) { WockyTLSSession *session = WOCKY_TLS_SESSION (object); gboolean server = session->server; gint code; const gchar *opt = tls_options (); const gchar *pos = NULL; /* gnutls_handshake_set_private_extensions (session->session, 1); */ gnutls_certificate_allocate_credentials (&(session->gnutls_cert_cred)); /* I think this all needs to be done per connection: conceivably the DH parameters could be moved to the global section above, but IANA cryptographer */ if (server) { gnutls_dh_params_t *dhp; if ((session->key_file != NULL) && (session->cert_file != NULL)) { DEBUG ("cert/key pair: %s/%s", session->cert_file, session->key_file); gnutls_certificate_set_x509_key_file (session->gnutls_cert_cred, session->cert_file, session->key_file, GNUTLS_X509_FMT_PEM); } switch (session->dh_bits) { case 768: dhp = &dh_0768; break; case 1024: dhp = &dh_1024; break; case 2048: dhp = &dh_2048; break; case 3072: dhp = &dh_3072; break; case 4096: dhp = &dh_4096; break; default: dhp = &dh_1024; break; } if (*dhp == NULL) { DEBUG ("Initialising DH parameters (%d bits)", session->dh_bits); gnutls_dh_params_init (dhp); gnutls_dh_params_generate2 (*dhp, session->dh_bits); } session->dh_params = *dhp; gnutls_certificate_set_dh_params (session->gnutls_cert_cred, *dhp); gnutls_init (&session->session, GNUTLS_SERVER); } else gnutls_init (&session->session, GNUTLS_CLIENT); code = gnutls_priority_set_direct (session->session, opt, &pos); if (code != GNUTLS_E_SUCCESS) { DEBUG ("could not set priority string: %s", error_to_string (code)); DEBUG (" '%s'", opt); if (pos >= opt) DEBUG (" %*s^", (int) (pos - opt), ""); } else { DEBUG ("priority set to: '%s'", opt); } code = gnutls_credentials_set (session->session, GNUTLS_CRD_CERTIFICATE, session->gnutls_cert_cred); if (code != GNUTLS_E_SUCCESS) DEBUG ("could not set credentials: %s", error_to_string (code)); gnutls_transport_set_push_function (session->session, wocky_tls_session_push_func); gnutls_transport_set_pull_function (session->session, wocky_tls_session_pull_func); gnutls_transport_set_ptr (session->session, session); g_assert (session->stream); } static void wocky_tls_session_finalize (GObject *object) { WockyTLSSession *session = WOCKY_TLS_SESSION (object); gnutls_deinit (session->session); gnutls_certificate_free_credentials (session->gnutls_cert_cred); g_object_unref (session->stream); G_OBJECT_CLASS (wocky_tls_session_parent_class) ->finalize (object); } static void wocky_tls_session_dispose (GObject *object) { WockyTLSSession *session = WOCKY_TLS_SESSION (object); g_free (session->key_file); session->key_file = NULL; g_free (session->cert_file); session->cert_file = NULL; g_free (session->read_op.buffer); session->read_op.buffer = NULL; G_OBJECT_CLASS (wocky_tls_session_parent_class)->dispose (object); } static void wocky_tls_session_class_init (GObjectClass *class) { class->set_property = wocky_tls_session_set_property; class->constructed = wocky_tls_session_constructed; class->finalize = wocky_tls_session_finalize; class->dispose = wocky_tls_session_dispose; g_object_class_install_property (class, PROP_S_STREAM, g_param_spec_object ("base-stream", "base stream", "the stream that TLS communicates over", G_TYPE_IO_STREAM, G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB)); g_object_class_install_property (class, PROP_S_SERVER, g_param_spec_boolean ("server", "server", "whether this is a server", FALSE, G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB)); g_object_class_install_property (class, PROP_S_DHBITS, g_param_spec_uint ("dh-bits", "Diffie-Hellman bits", "Diffie-Hellmann bits: 768, 1024, 2048, 3072 0r 4096", 768, 4096, 1024, G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB)); g_object_class_install_property (class, PROP_S_KEYFILE, g_param_spec_string ("x509-key", "x509 key", "x509 PEM key file", NULL, G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB)); g_object_class_install_property (class, PROP_S_CERTFILE, g_param_spec_string ("x509-cert", "x509 certificate", "x509 PEM certificate file", NULL, G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB)); } static void wocky_tls_connection_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { WockyTLSConnection *connection = WOCKY_TLS_CONNECTION (object); switch (prop_id) { case PROP_C_SESSION: connection->session = g_value_dup_object (value); break; default: g_assert_not_reached (); } } static gboolean wocky_tls_connection_close (GIOStream *stream, GCancellable *cancellable, GError **error) { WockyTLSConnection *connection = WOCKY_TLS_CONNECTION (stream); return g_io_stream_close (connection->session->stream, cancellable, error); } static GInputStream * wocky_tls_connection_get_input_stream (GIOStream *io_stream) { WockyTLSConnection *connection = WOCKY_TLS_CONNECTION (io_stream); if (connection->input == NULL) connection->input = g_object_new (WOCKY_TYPE_TLS_INPUT_STREAM, "session", connection->session, NULL); return (GInputStream *)connection->input; } static GOutputStream * wocky_tls_connection_get_output_stream (GIOStream *io_stream) { WockyTLSConnection *connection = WOCKY_TLS_CONNECTION (io_stream); if (connection->output == NULL) connection->output = g_object_new (WOCKY_TYPE_TLS_OUTPUT_STREAM, "session", connection->session, NULL); return (GOutputStream *)connection->output; } static void wocky_tls_connection_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { switch (prop_id) { default: g_assert_not_reached (); } } static void wocky_tls_connection_constructed (GObject *object) { WockyTLSConnection *connection = WOCKY_TLS_CONNECTION (object); g_assert (connection->session); } static void wocky_tls_connection_finalize (GObject *object) { WockyTLSConnection *connection = WOCKY_TLS_CONNECTION (object); g_object_unref (connection->session); if (connection->input != NULL) g_object_unref (connection->input); if (connection->output != NULL) g_object_unref (connection->output); G_OBJECT_CLASS (wocky_tls_connection_parent_class) ->finalize (object); } static void wocky_tls_connection_class_init (WockyTLSConnectionClass *class) { GObjectClass *gobject_class = G_OBJECT_CLASS (class); GIOStreamClass *stream_class = G_IO_STREAM_CLASS (class); gobject_class->get_property = wocky_tls_connection_get_property; gobject_class->set_property = wocky_tls_connection_set_property; gobject_class->constructed = wocky_tls_connection_constructed; gobject_class->finalize = wocky_tls_connection_finalize; g_object_class_install_property (gobject_class, PROP_C_SESSION, g_param_spec_object ("session", "TLS session", "the TLS session object for this connection", WOCKY_TYPE_TLS_SESSION, G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); stream_class->get_input_stream = wocky_tls_connection_get_input_stream; stream_class->get_output_stream = wocky_tls_connection_get_output_stream; stream_class->close_fn = wocky_tls_connection_close; } WockyTLSSession * wocky_tls_session_new (GIOStream *stream) { return g_object_new (WOCKY_TYPE_TLS_SESSION, "base-stream", stream, "server", FALSE, NULL); } /** * wocky_tls_session_server_new: * @stream: a GIOStream on which we expect to receive the client TLS handshake * @dhbits: size of the DH parameters (see gnutls for valid settings) * @key: the path to the X509 PEM key file * @cert: the path to the X509 PEM certificate * * Create a new TLS server session * * Returns: a #WockyTLSSession object */ WockyTLSSession * wocky_tls_session_server_new (GIOStream *stream, guint dhbits, const gchar* key, const gchar* cert) { if (dhbits == 0) dhbits = 1024; return g_object_new (WOCKY_TYPE_TLS_SESSION, "base-stream", stream, "dh-bits", dhbits, "x509-key", key, "x509-cert", cert, "server", TRUE, NULL); } /* this file is "borrowed" from an unmerged gnio feature: */ /* Local Variables: */ /* c-file-style: "gnu" */ /* End: */ telepathy-gabble-0.18.2/lib/ext/wocky/wocky/wocky-openssl-dh4096.c0000644000175000017500000000613512200204546024550 0ustar00smcvsmcv00000000000000#ifdef HAVE_CONFIG_H #include "config.h" #endif #ifndef HEADER_DH_H #include #endif DH *get_dh4096(void); DH *get_dh4096(void) { static unsigned char dh4096_p[]={ 0xFA,0x14,0x72,0x52,0xC1,0x4D,0xE1,0x5A,0x49,0xD4,0xEF,0x09, 0x2D,0xC0,0xA8,0xFD,0x55,0xAB,0xD7,0xD9,0x37,0x04,0x28,0x09, 0xE2,0xE9,0x3E,0x77,0xE2,0xA1,0x7A,0x18,0xDD,0x46,0xA3,0x43, 0x37,0x23,0x90,0x97,0xF3,0x0E,0xC9,0x03,0x50,0x7D,0x65,0xCF, 0x78,0x62,0xA6,0x3A,0x62,0x22,0x83,0xA1,0x2F,0xFE,0x79,0xBA, 0x35,0xFF,0x59,0xD8,0x1D,0x61,0xDD,0x1E,0x21,0x13,0x17,0xFE, 0xCD,0x38,0x87,0x9E,0xF5,0x4F,0x79,0x10,0x61,0x8D,0xD4,0x22, 0xF3,0x5A,0xED,0x5D,0xEA,0x21,0xE9,0x33,0x6B,0x48,0x12,0x0A, 0x20,0x77,0xD4,0x25,0x60,0x61,0xDE,0xF6,0xB4,0x4F,0x1C,0x63, 0x40,0x8B,0x3A,0x21,0x93,0x8B,0x79,0x53,0x51,0x2C,0xCA,0xB3, 0x7B,0x29,0x56,0xA8,0xC7,0xF8,0xF4,0x7B,0x08,0x5E,0xA6,0xDC, 0xA2,0x45,0x12,0x56,0xDD,0x41,0x92,0xF2,0xDD,0x5B,0x8F,0x23, 0xF0,0xF3,0xEF,0xE4,0x3B,0x0A,0x44,0xDD,0xED,0x96,0x84,0xF1, 0xA8,0x32,0x46,0xA3,0xDB,0x4A,0xBE,0x3D,0x45,0xBA,0x4E,0xF8, 0x03,0xE5,0xDD,0x6B,0x59,0x0D,0x84,0x1E,0xCA,0x16,0x5A,0x8C, 0xC8,0xDF,0x7C,0x54,0x44,0xC4,0x27,0xA7,0x3B,0x2A,0x97,0xCE, 0xA3,0x7D,0x26,0x9C,0xAD,0xF4,0xC2,0xAC,0x37,0x4B,0xC3,0xAD, 0x68,0x84,0x7F,0x99,0xA6,0x17,0xEF,0x6B,0x46,0x3A,0x7A,0x36, 0x7A,0x11,0x43,0x92,0xAD,0xE9,0x9C,0xFB,0x44,0x6C,0x3D,0x82, 0x49,0xCC,0x5C,0x6A,0x52,0x42,0xF8,0x42,0xFB,0x44,0xF9,0x39, 0x73,0xFB,0x60,0x79,0x3B,0xC2,0x9E,0x0B,0xDC,0xD4,0xA6,0x67, 0xF7,0x66,0x3F,0xFC,0x42,0x3B,0x1B,0xDB,0x4F,0x66,0xDC,0xA5, 0x8F,0x66,0xF9,0xEA,0xC1,0xED,0x31,0xFB,0x48,0xA1,0x82,0x7D, 0xF8,0xE0,0xCC,0xB1,0xC7,0x03,0xE4,0xF8,0xB3,0xFE,0xB7,0xA3, 0x13,0x73,0xA6,0x7B,0xC1,0x0E,0x39,0xC7,0x94,0x48,0x26,0x00, 0x85,0x79,0xFC,0x6F,0x7A,0xAF,0xC5,0x52,0x35,0x75,0xD7,0x75, 0xA4,0x40,0xFA,0x14,0x74,0x61,0x16,0xF2,0xEB,0x67,0x11,0x6F, 0x04,0x43,0x3D,0x11,0x14,0x4C,0xA7,0x94,0x2A,0x39,0xA1,0xC9, 0x90,0xCF,0x83,0xC6,0xFF,0x02,0x8F,0xA3,0x2A,0xAC,0x26,0xDF, 0x0B,0x8B,0xBE,0x64,0x4A,0xF1,0xA1,0xDC,0xEE,0xBA,0xC8,0x03, 0x82,0xF6,0x62,0x2C,0x5D,0xB6,0xBB,0x13,0x19,0x6E,0x86,0xC5, 0x5B,0x2B,0x5E,0x3A,0xF3,0xB3,0x28,0x6B,0x70,0x71,0x3A,0x8E, 0xFF,0x5C,0x15,0xE6,0x02,0xA4,0xCE,0xED,0x59,0x56,0xCC,0x15, 0x51,0x07,0x79,0x1A,0x0F,0x25,0x26,0x27,0x30,0xA9,0x15,0xB2, 0xC8,0xD4,0x5C,0xCC,0x30,0xE8,0x1B,0xD8,0xD5,0x0F,0x19,0xA8, 0x80,0xA4,0xC7,0x01,0xAA,0x8B,0xBA,0x53,0xBB,0x47,0xC2,0x1F, 0x6B,0x54,0xB0,0x17,0x60,0xED,0x79,0x21,0x95,0xB6,0x05,0x84, 0x37,0xC8,0x03,0xA4,0xDD,0xD1,0x06,0x69,0x8F,0x4C,0x39,0xE0, 0xC8,0x5D,0x83,0x1D,0xBE,0x6A,0x9A,0x99,0xF3,0x9F,0x0B,0x45, 0x29,0xD4,0xCB,0x29,0x66,0xEE,0x1E,0x7E,0x3D,0xD7,0x13,0x4E, 0xDB,0x90,0x90,0x58,0xCB,0x5E,0x9B,0xCD,0x2E,0x2B,0x0F,0xA9, 0x4E,0x78,0xAC,0x05,0x11,0x7F,0xE3,0x9E,0x27,0xD4,0x99,0xE1, 0xB9,0xBD,0x78,0xE1,0x84,0x41,0xA0,0xDF, }; static unsigned char dh4096_g[]={ 0x02, }; DH *dh; if ((dh=DH_new()) == NULL) return(NULL); dh->p=BN_bin2bn(dh4096_p,sizeof(dh4096_p),NULL); dh->g=BN_bin2bn(dh4096_g,sizeof(dh4096_g),NULL); if ((dh->p == NULL) || (dh->g == NULL)) { DH_free(dh); return(NULL); } return(dh); } telepathy-gabble-0.18.2/lib/ext/wocky/wocky/wocky-openssl-dh2048.c0000644000175000017500000000343612200204546024544 0ustar00smcvsmcv00000000000000#ifdef HAVE_CONFIG_H #include "config.h" #endif #ifndef HEADER_DH_H #include #endif DH *get_dh2048(void); DH *get_dh2048(void) { static unsigned char dh2048_p[]={ 0xF6,0x42,0x57,0xB7,0x08,0x7F,0x08,0x17,0x72,0xA2,0xBA,0xD6, 0xA9,0x42,0xF3,0x05,0xE8,0xF9,0x53,0x11,0x39,0x4F,0xB6,0xF1, 0x6E,0xB9,0x4B,0x38,0x20,0xDA,0x01,0xA7,0x56,0xA3,0x14,0xE9, 0x8F,0x40,0x55,0xF3,0xD0,0x07,0xC6,0xCB,0x43,0xA9,0x94,0xAD, 0xF7,0x4C,0x64,0x86,0x49,0xF8,0x0C,0x83,0xBD,0x65,0xE9,0x17, 0xD4,0xA1,0xD3,0x50,0xF8,0xF5,0x59,0x5F,0xDC,0x76,0x52,0x4F, 0x3D,0x3D,0x8D,0xDB,0xCE,0x99,0xE1,0x57,0x92,0x59,0xCD,0xFD, 0xB8,0xAE,0x74,0x4F,0xC5,0xFC,0x76,0xBC,0x83,0xC5,0x47,0x30, 0x61,0xCE,0x7C,0xC9,0x66,0xFF,0x15,0xF9,0xBB,0xFD,0x91,0x5E, 0xC7,0x01,0xAA,0xD3,0x5B,0x9E,0x8D,0xA0,0xA5,0x72,0x3A,0xD4, 0x1A,0xF0,0xBF,0x46,0x00,0x58,0x2B,0xE5,0xF4,0x88,0xFD,0x58, 0x4E,0x49,0xDB,0xCD,0x20,0xB4,0x9D,0xE4,0x91,0x07,0x36,0x6B, 0x33,0x6C,0x38,0x0D,0x45,0x1D,0x0F,0x7C,0x88,0xB3,0x1C,0x7C, 0x5B,0x2D,0x8E,0xF6,0xF3,0xC9,0x23,0xC0,0x43,0xF0,0xA5,0x5B, 0x18,0x8D,0x8E,0xBB,0x55,0x8C,0xB8,0x5D,0x38,0xD3,0x34,0xFD, 0x7C,0x17,0x57,0x43,0xA3,0x1D,0x18,0x6C,0xDE,0x33,0x21,0x2C, 0xB5,0x2A,0xFF,0x3C,0xE1,0xB1,0x29,0x40,0x18,0x11,0x8D,0x7C, 0x84,0xA7,0x0A,0x72,0xD6,0x86,0xC4,0x03,0x19,0xC8,0x07,0x29, 0x7A,0xCA,0x95,0x0C,0xD9,0x96,0x9F,0xAB,0xD0,0x0A,0x50,0x9B, 0x02,0x46,0xD3,0x08,0x3D,0x66,0xA4,0x5D,0x41,0x9F,0x9C,0x7C, 0xBD,0x89,0x4B,0x22,0x19,0x26,0xBA,0xAB,0xA2,0x5E,0xC3,0x55, 0xE9,0x32,0x0B,0x3B, }; static unsigned char dh2048_g[]={ 0x02, }; DH *dh; if ((dh=DH_new()) == NULL) return(NULL); dh->p=BN_bin2bn(dh2048_p,sizeof(dh2048_p),NULL); dh->g=BN_bin2bn(dh2048_g,sizeof(dh2048_g),NULL); if ((dh->p == NULL) || (dh->g == NULL)) { DH_free(dh); return(NULL); } return(dh); } telepathy-gabble-0.18.2/lib/ext/wocky/wocky/wocky-openssl-dh1024.c0000644000175000017500000000217512200204546024534 0ustar00smcvsmcv00000000000000#ifdef HAVE_CONFIG_H #include "config.h" #endif #ifndef HEADER_DH_H #include #endif DH *get_dh1024(void); DH *get_dh1024(void) { static unsigned char dh1024_p[]={ 0xF4,0x88,0xFD,0x58,0x4E,0x49,0xDB,0xCD,0x20,0xB4,0x9D,0xE4, 0x91,0x07,0x36,0x6B,0x33,0x6C,0x38,0x0D,0x45,0x1D,0x0F,0x7C, 0x88,0xB3,0x1C,0x7C,0x5B,0x2D,0x8E,0xF6,0xF3,0xC9,0x23,0xC0, 0x43,0xF0,0xA5,0x5B,0x18,0x8D,0x8E,0xBB,0x55,0x8C,0xB8,0x5D, 0x38,0xD3,0x34,0xFD,0x7C,0x17,0x57,0x43,0xA3,0x1D,0x18,0x6C, 0xDE,0x33,0x21,0x2C,0xB5,0x2A,0xFF,0x3C,0xE1,0xB1,0x29,0x40, 0x18,0x11,0x8D,0x7C,0x84,0xA7,0x0A,0x72,0xD6,0x86,0xC4,0x03, 0x19,0xC8,0x07,0x29,0x7A,0xCA,0x95,0x0C,0xD9,0x96,0x9F,0xAB, 0xD0,0x0A,0x50,0x9B,0x02,0x46,0xD3,0x08,0x3D,0x66,0xA4,0x5D, 0x41,0x9F,0x9C,0x7C,0xBD,0x89,0x4B,0x22,0x19,0x26,0xBA,0xAB, 0xA2,0x5E,0xC3,0x55,0xE9,0x2F,0x78,0xC7, }; static unsigned char dh1024_g[]={ 0x02, }; DH *dh; if ((dh=DH_new()) == NULL) return(NULL); dh->p=BN_bin2bn(dh1024_p,sizeof(dh1024_p),NULL); dh->g=BN_bin2bn(dh1024_g,sizeof(dh1024_g),NULL); if ((dh->p == NULL) || (dh->g == NULL)) { DH_free(dh); return(NULL); } return(dh); } telepathy-gabble-0.18.2/lib/ext/wocky/wocky/wocky-openssl-dh512.c0000644000175000017500000000144612200204546024455 0ustar00smcvsmcv00000000000000#ifdef HAVE_CONFIG_H #include "config.h" #endif #ifndef HEADER_DH_H #include #endif DH *get_dh512(void); DH *get_dh512(void) { static unsigned char dh512_p[]={ 0xF5,0x2A,0xFF,0x3C,0xE1,0xB1,0x29,0x40,0x18,0x11,0x8D,0x7C, 0x84,0xA7,0x0A,0x72,0xD6,0x86,0xC4,0x03,0x19,0xC8,0x07,0x29, 0x7A,0xCA,0x95,0x0C,0xD9,0x96,0x9F,0xAB,0xD0,0x0A,0x50,0x9B, 0x02,0x46,0xD3,0x08,0x3D,0x66,0xA4,0x5D,0x41,0x9F,0x9C,0x7C, 0xBD,0x89,0x4B,0x22,0x19,0x26,0xBA,0xAB,0xA2,0x5E,0xC3,0x55, 0xE9,0x2A,0x05,0x5F, }; static unsigned char dh512_g[]={ 0x02, }; DH *dh; if ((dh=DH_new()) == NULL) return(NULL); dh->p=BN_bin2bn(dh512_p,sizeof(dh512_p),NULL); dh->g=BN_bin2bn(dh512_g,sizeof(dh512_g),NULL); if ((dh->p == NULL) || (dh->g == NULL)) { DH_free(dh); return(NULL); } return(dh); } telepathy-gabble-0.18.2/lib/ext/wocky/wocky/wocky-openssl.c0000644000175000017500000020010712200204546023627 0ustar00smcvsmcv00000000000000/* * Copyright © 2008 Christian Kellner, Samuel Cormier-Iijima * Copyright © 2008-2009 Codethink Limited * Copyright © 2009-2010 Collabora Limited * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; either version 2 of the licence or (at * your option) any later version. * * Authors: Vivek Dasmohapatra * Ryan Lortie * Christian Kellner * Samuel Cormier-Iijima * * Based on wocky-tls.c, which was in turn based on an unmerged gnio feature. * See wocky-tls.c for details. * * This file follows the original coding style from upstream, not collabora * house style. See wocky-tls.c for details. */ /** * SECTION: wocky-tls * @title: Wocky OpenSSL TLS * @short_description: Establish TLS sessions * * The WOCKY_TLS_DEBUG_LEVEL environment variable can be used to print debug * output from OpenSSL. To enable it, set it to a value from 1 to 9. * Higher values will print more information. * * Increasing the value past certain thresholds will also trigger increased * debugging output from within wocky-openssl.c as well. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "wocky-tls.h" /* Apparently an implicit requirement of OpenSSL's headers... */ #ifdef G_OS_WIN32 #include #endif #include #include #include #ifdef HAVE_UNISTD_H # include #endif #include #include #include #define WOCKY_DEBUG_FLAG WOCKY_DEBUG_TLS #define DEBUG_HANDSHAKE_LEVEL 5 #define DEBUG_ASYNC_DETAIL_LEVEL 6 #include "wocky-debug-internal.h" #include "wocky-utils.h" #include #include #include #include #include #include /* SSL_CTX_set_cipher_list() allows to restrict/alter the list of supported * ciphers; see ciphers(1) for documentation on the format. * Usually the normal ciphers are ok, but on mobile phones we prefer RC4 as * it decreases the size of packets. The bandwidth difference is tiny, but * the difference in power consumption between small and very small packets * can be significant on 3G. */ #ifdef ENABLE_PREFER_STREAM_CIPHERS #define CIPHER_LIST \ "RC4-SHA:" \ "RC4-MD5:" \ "ECDHE-RSA-RC4-SHA:" \ "ECDHE-ECDSA-RC4-SHA:" \ "ECDH-RSA-RC4-SHA:" \ "ECDH-ECDSA-RC4-SHA:" \ "PSK-RC4-SHA:" \ "ALL" /* fall-back to all the other algorithms */ #endif enum { PROP_S_NONE, PROP_S_STREAM, PROP_S_SERVER, PROP_S_DHBITS, PROP_S_KEYFILE, PROP_S_CERTFILE, }; enum { PROP_C_NONE, PROP_C_SESSION, }; enum { PROP_O_NONE, PROP_O_SESSION }; enum { PROP_I_NONE, PROP_I_SESSION }; typedef enum { WOCKY_TLS_OP_HANDSHAKE, WOCKY_TLS_OP_READ, WOCKY_TLS_OP_WRITE } WockyTLSOperation; /* from openssl docs: not clear if this is exported as a constant by openssl */ #define MAX_SSLV3_BLOCK_SIZE 0x4000 typedef struct { gboolean active; gint io_priority; GCancellable *cancellable; GObject *source_object; GAsyncReadyCallback callback; gpointer user_data; gpointer source_tag; GError *error; gboolean sync_complete; gchar *buffer; gsize count; gchar rbuf[MAX_SSLV3_BLOCK_SIZE]; } WockyTLSJob; typedef struct { WockyTLSJob job; gulong state; gboolean done; } WockyTLSHandshake; typedef GIOStreamClass WockyTLSConnectionClass; typedef GObjectClass WockyTLSSessionClass; typedef GInputStreamClass WockyTLSInputStreamClass; typedef GOutputStreamClass WockyTLSOutputStreamClass; struct _WockyTLSSession { GObject parent; GIOStream *stream; GCancellable *cancellable; GError *error; gboolean async; /* tls server support */ gboolean server; guint dh_bits; gchar *key_file; gchar *cert_file; /* frontend jobs */ struct { WockyTLSHandshake handshake; WockyTLSJob read; WockyTLSJob write; } job; /* openssl structures */ BIO *rbio; BIO *wbio; SSL_METHOD *method; SSL_CTX *ctx; SSL *ssl; }; typedef struct { GInputStream parent; WockyTLSSession *session; } WockyTLSInputStream; typedef struct { GOutputStream parent; WockyTLSSession *session; } WockyTLSOutputStream; struct _WockyTLSConnection { GIOStream parent; WockyTLSSession *session; WockyTLSInputStream *input; WockyTLSOutputStream *output; }; DH * get_dh4096 (void); DH * get_dh2048 (void); DH * get_dh1024 (void); DH * get_dh512 (void); static guint tls_debug_level = 0; static GType wocky_tls_input_stream_get_type (void); static GType wocky_tls_output_stream_get_type (void); G_DEFINE_TYPE (WockyTLSConnection, wocky_tls_connection, G_TYPE_IO_STREAM); G_DEFINE_TYPE (WockyTLSSession, wocky_tls_session, G_TYPE_OBJECT); G_DEFINE_TYPE (WockyTLSInputStream, wocky_tls_input_stream, G_TYPE_INPUT_STREAM); G_DEFINE_TYPE (WockyTLSOutputStream, wocky_tls_output_stream, G_TYPE_OUTPUT_STREAM); #define WOCKY_TYPE_TLS_INPUT_STREAM (wocky_tls_input_stream_get_type ()) #define WOCKY_TYPE_TLS_OUTPUT_STREAM (wocky_tls_output_stream_get_type ()) #define WOCKY_TLS_INPUT_STREAM(inst) (G_TYPE_CHECK_INSTANCE_CAST ((inst), \ WOCKY_TYPE_TLS_INPUT_STREAM, \ WockyTLSInputStream)) #define WOCKY_TLS_OUTPUT_STREAM(inst) (G_TYPE_CHECK_INSTANCE_CAST ((inst), \ WOCKY_TYPE_TLS_OUTPUT_STREAM, \ WockyTLSOutputStream)) /* Ok: This function tries to retrieve the error that caused a problem from * * bottom of the openssl error stack: The errnum argument is the error code * * returned by the last openssl operation which MAY NOT have come from the * * openssl error stack (cf SSL_get_error) and which MAY be SSL_ERROR_NONE: * * it's not supposed to be SSL_ERROR_NONE if a problem occurred, but this is * * not actually guaranteed anywhere so we have to check for it here: */ static const gchar *error_to_string (long error) { static gchar ssl_error[256]; int e; int x; /* SSL_ERROR_NONE from ERR_get_error means we have emptied the stack, * * in which case we should back up and use the last error we saw: */ for (e = x = error; x != SSL_ERROR_NONE; x = ERR_get_error ()) e = x; /* we found an error in the stack, or were passed one in errnum: */ if (e != SSL_ERROR_NONE) { ERR_error_string_n ((gulong) e, ssl_error, sizeof (ssl_error)); return ssl_error; } /* No useful/informative/relevant error found */ return NULL; } static GSimpleAsyncResult * wocky_tls_job_make_result (WockyTLSJob *job, gssize result) { GSimpleAsyncResult *simple; simple = g_simple_async_result_new (job->source_object, job->callback, job->user_data, job->source_tag); if (job->error != NULL) { DEBUG ("setting error from job '%s'", job->error->message); g_simple_async_result_set_from_error (simple, job->error); g_error_free (job->error); job->error = NULL; } if (job->source_object != NULL) g_object_unref (job->source_object); job->source_object = NULL; if (job->cancellable != NULL) g_object_unref (job->cancellable); job->cancellable = NULL; job->active = FALSE; return simple; } static void wocky_tls_job_result_gssize (WockyTLSJob *job, gssize result) { GSimpleAsyncResult *simple; if ((simple = wocky_tls_job_make_result (job, result))) { if (result >= 0) g_simple_async_result_set_op_res_gssize (simple, result); g_simple_async_result_complete (simple); g_object_unref (simple); } } /* only used for handshake results: read + write use result_gssize */ static void wocky_tls_job_result_boolean (WockyTLSJob *job, gint result) { GSimpleAsyncResult *simple; if ((simple = wocky_tls_job_make_result (job, result))) { g_simple_async_result_complete (simple); g_object_unref (simple); } } /* ************************************************************************* */ static void wocky_tls_session_try_operation (WockyTLSSession *session, WockyTLSOperation operation); static void wocky_tls_session_write_ready (GObject *object, GAsyncResult *result, gpointer user_data); static void wocky_tls_session_read_ready (GObject *object, GAsyncResult *result, gpointer user_data); /* writes to the internal BIO should always succeed, so we should never * receive SSL_ERROR_WANT_WRITE: reads, on the other hand, obviously * depend on how much data we have buffered, so SSL_ERROR_WANT_READ can * clearly happen */ static void handshake_write (WockyTLSSession *session) { gchar *wbuf; WockyTLSJob *handshake = &(session->job.handshake.job); GCancellable *cancel = handshake->cancellable; gint prio = handshake->io_priority; GOutputStream *output = g_io_stream_get_output_stream (session->stream); long wsize = BIO_get_mem_data (session->wbio, &wbuf); if (tls_debug_level >= DEBUG_ASYNC_DETAIL_LEVEL) DEBUG (""); g_output_stream_write_async (output, wbuf, wsize, prio, cancel, wocky_tls_session_write_ready, session); } static void handshake_read (WockyTLSSession *session) { GInputStream *input = g_io_stream_get_input_stream (session->stream); WockyTLSJob *handshake = (WockyTLSJob *) &session->job.handshake.job; if (tls_debug_level >= DEBUG_ASYNC_DETAIL_LEVEL) DEBUG (""); g_input_stream_read_async (input, &(handshake->rbuf), MAX_SSLV3_BLOCK_SIZE, handshake->io_priority, handshake->cancellable, wocky_tls_session_read_ready, session); } static int ssl_handshake (WockyTLSSession *session) { gint result = 1; gulong errnum = SSL_ERROR_NONE; gboolean want_read = FALSE; gboolean want_write = FALSE; const gchar *errstr = NULL; gboolean done = session->job.handshake.done; gboolean fatal = FALSE; if (tls_debug_level >= DEBUG_ASYNC_DETAIL_LEVEL) DEBUG (""); if (!done) { const gchar *method; if (session->server) { method = "SSL_accept"; result = SSL_accept (session->ssl); } else { method = "SSL_connect"; result = SSL_connect (session->ssl); } errnum = SSL_get_error (session->ssl, result); done = (result == 1); errstr = error_to_string (errnum); fatal = (errnum != SSL_ERROR_WANT_READ && errnum != SSL_ERROR_WANT_WRITE && errnum != SSL_ERROR_NONE); DEBUG ("%s - result: %d; error: %ld", method, result, errnum); DEBUG ("%s : %s", method, errstr); } /* buffered write data means we need to write */ want_write = BIO_pending (session->wbio) > 0; /* check to see if there's data waiting to go out: * * since writes to a BIO should always succeed, it is possible to * * have buffered write data after a successful return, but not * * possible to be waiting on a read, since SSL_connect should not * * return success if waiting for data to come in */ if (done) { session->job.handshake.done = TRUE; if (!want_write) { DEBUG ("Handshake completed"); errnum = session->job.handshake.state = SSL_ERROR_NONE; } else { DEBUG ("Handshake completed (IO incomplete)"); g_assert (errnum != SSL_ERROR_WANT_READ); errnum = SSL_ERROR_WANT_WRITE; } } else { DEBUG ("Handshake state: %ld", errnum); session->job.handshake.state = errnum; want_read = (errnum == SSL_ERROR_WANT_READ); } /* sif we want both a write (buffered data in the BIO) AND a read * * (SSL_ERROR_WANT_READ) then this will happen when the handshake_write * * invokes wocky_tls_session_write_ready which will in turn call * * wocky_tls_session_try_operation which will re-enter handshake * * and then proceed to fall back through to this block of code */ if (!fatal) { DEBUG ("want write: %d; want read: %d;", want_write, want_read); if (want_write) handshake_write (session); else if (want_read) handshake_read (session); else wocky_tls_session_try_operation (session, WOCKY_TLS_OP_HANDSHAKE); } else { DEBUG ("Handshake failed: [%d:%ld] %s", result, errnum, errstr); if (session->job.handshake.job.error != NULL) { g_error_free (session->job.handshake.job.error); session->job.handshake.job.error = NULL; } g_set_error (&(session->job.handshake.job.error), WOCKY_TLS_ERROR, result, "Handshake failed: %s", errstr); wocky_tls_session_try_operation (session, WOCKY_TLS_OP_HANDSHAKE); } return errnum; } static void ssl_fill (WockyTLSSession *session) { GInputStream *input = g_io_stream_get_input_stream (session->stream); gchar *rbuf = session->job.read.rbuf; gint prio = session->job.read.io_priority; GCancellable *cancel = session->job.read.cancellable; if (tls_debug_level >= DEBUG_ASYNC_DETAIL_LEVEL) DEBUG (""); g_input_stream_read_async (input, rbuf, MAX_SSLV3_BLOCK_SIZE, prio, cancel, wocky_tls_session_read_ready, session); } static void ssl_flush (WockyTLSSession *session) { long wsize; gchar *wbuf; gint prio = session->job.read.io_priority; GOutputStream *output = g_io_stream_get_output_stream (session->stream); GCancellable *cancel = session->job.read.cancellable; if (tls_debug_level >= DEBUG_ASYNC_DETAIL_LEVEL) DEBUG (""); wsize = BIO_get_mem_data (session->wbio, &wbuf); if (wsize > 0) g_output_stream_write_async (output, wbuf, wsize, prio, cancel, wocky_tls_session_write_ready, session); } /* FALSE indicates we should go round again and try to get more data */ static gboolean ssl_read_is_complete (WockyTLSSession *session, gint result) { /* if the job error is set, we should bail out now, we have failed * * otherwise: * * a -ve return with an SSL error of WANT_READ implies an incomplete * * crypto record: we need to go round again and get more data * * or: * * a 0 return means the SSL connection was shut down cleanly */ if ((session->job.read.error == NULL) && (result <= 0)) { int err = SSL_get_error (session->ssl, result); switch (err) { case SSL_ERROR_WANT_READ: DEBUG ("Incomplete SSL record, read again"); return FALSE; case SSL_ERROR_WANT_WRITE: g_warning ("read caused write: unsupported TLS re-negotiation?"); /* deliberately falling through to the default case, having logged a * more specific warning. */ default: g_set_error (&session->job.read.error, WOCKY_TLS_ERROR, err, "OpenSSL read: protocol error %d", err); } } return TRUE; } static void wocky_tls_session_try_operation (WockyTLSSession *session, WockyTLSOperation operation) { WockyTLSJob *handshake = &(session->job.handshake.job); if (handshake->active || operation == WOCKY_TLS_OP_HANDSHAKE) { gint result = session->job.handshake.state; DEBUG ("async job handshake"); if (tls_debug_level >= DEBUG_HANDSHAKE_LEVEL) DEBUG ("async job handshake: %d", result); switch (result) { case SSL_ERROR_WANT_READ: case SSL_ERROR_WANT_WRITE: DEBUG ("Handshake incomplete..."); ssl_handshake (session); break; case SSL_ERROR_NONE: DEBUG ("Handshake complete (success): %d", result); wocky_tls_job_result_boolean (handshake, result); break; default: DEBUG ("Handshake complete (failure): %d", result); if (handshake->error == NULL) handshake->error = g_error_new (WOCKY_TLS_ERROR, result, "Handshake Error"); wocky_tls_job_result_boolean (handshake, result); } } else if (operation == WOCKY_TLS_OP_READ) { gssize result = 0; gulong pending = 0; gsize wanted = 0; if (tls_debug_level >= DEBUG_ASYNC_DETAIL_LEVEL) DEBUG ("async job OP_READ"); /* cipherbytes in the BIO != clearbytes after SSL_read */ wanted = session->job.read.count; pending = (gulong)BIO_pending (session->rbio); result = SSL_read (session->ssl, session->job.read.buffer, wanted); DEBUG ("read %" G_GSSIZE_FORMAT " clearbytes (from %ld cipherbytes)", result, pending); if (ssl_read_is_complete (session, result)) wocky_tls_job_result_gssize (&session->job.read, result); else ssl_fill (session); } else { /* we have no useful way of mapping SSL cipherbytes to raw * * clearbytes: it should always be a complete write unless * * there's been a network error, in which case the utility * * of a byte count is debatable anyway */ gssize result = session->job.write.count; if (tls_debug_level >= DEBUG_ASYNC_DETAIL_LEVEL) DEBUG ("async job OP_WRITE"); g_assert (operation == WOCKY_TLS_OP_WRITE); DEBUG ("wrote %" G_GSSIZE_FORMAT " clearbytes", result); wocky_tls_job_result_gssize (&session->job.write, result); } } static void wocky_tls_job_start (WockyTLSJob *job, gpointer source_object, gint io_priority, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data, gpointer source_tag) { g_assert (job->active == FALSE); g_assert (job->cancellable == NULL); /* this is always a circular reference, so it will keep the * session alive for as long as the job is running. */ job->source_object = g_object_ref (source_object); job->io_priority = io_priority; if (cancellable != NULL) job->cancellable = g_object_ref (cancellable); job->callback = callback; job->user_data = user_data; job->source_tag = source_tag; job->error = NULL; job->active = TRUE; } typedef gint (*ssl_handler) (SSL *ssl); WockyTLSConnection * wocky_tls_session_handshake (WockyTLSSession *session, GCancellable *cancellable, GError **error) { gint result = -1; gboolean go = TRUE; gboolean done = FALSE; ssl_handler handler = session->server ? SSL_accept : SSL_connect; gboolean want_write = FALSE; gboolean want_read = FALSE; gint errnum = SSL_ERROR_NONE; const gchar *errstr = NULL; while (go) { DEBUG ("sync SSL handshake loop"); if (!done) { result = handler (session->ssl); errnum = SSL_get_error (session->ssl, result); done = (result == 1); DEBUG ("SSL_%s: %d:%d", (handler == SSL_accept) ? "accept" : "connect", result, errnum); if (errnum != SSL_ERROR_NONE && errnum != SSL_ERROR_WANT_READ && errnum != SSL_ERROR_WANT_WRITE) { errstr = error_to_string (errnum); DEBUG ("SSL handshake error: [%d:%d] %s", result, errnum, errstr); } } want_write = BIO_pending (session->wbio) > 0; want_read = (errnum == SSL_ERROR_WANT_READ); if (want_write) { gchar *wbuf; GOutputStream *out = g_io_stream_get_output_stream (session->stream); long wsize = BIO_get_mem_data (session->wbio, &wbuf); gssize sent = 0; DEBUG ("sending %ld cipherbytes", wsize); if (wsize > 0) sent = g_output_stream_write (out, wbuf, wsize, NULL, error); DEBUG ("sent %" G_GSSIZE_FORMAT " cipherbytes", sent); (void) BIO_reset (session->wbio); } if (want_read) { char rbuf[MAX_SSLV3_BLOCK_SIZE]; GInputStream *in = g_io_stream_get_input_stream (session->stream); gssize bytes = g_input_stream_read (in, &rbuf, sizeof(rbuf), NULL, error); DEBUG ("read %" G_GSSIZE_FORMAT " cipherbytes", bytes); BIO_write (session->rbio, &rbuf, bytes); } switch (errnum) { case SSL_ERROR_WANT_WRITE: /* WANT_WRITE is theoretically impossible, but what the hell */ case SSL_ERROR_WANT_READ: break; case SSL_ERROR_NONE: DEBUG ("handshake complete, all IO done"); go = FALSE; break; default: DEBUG ("SSL handshake error: [%d:%d] %s", result, errnum, errstr); *error = g_error_new (WOCKY_TLS_ERROR, errnum, "Handshake: %s", errstr); go = FALSE; } } if (done) return g_object_new (WOCKY_TYPE_TLS_CONNECTION, "session", session, NULL); return NULL; } /* ************************************************************************* */ /* adding CA certificates & CRL lists for peer certificate verification */ void wocky_tls_session_add_ca (WockyTLSSession *session, const gchar *path) { gboolean ok = FALSE; if (!g_file_test (path, G_FILE_TEST_EXISTS)) { DEBUG ("CA file or path '%s' not accessible", path); return; } if (g_file_test (path, G_FILE_TEST_IS_DIR)) { DEBUG ("Loading CA directory"); ok = SSL_CTX_load_verify_locations (session->ctx, NULL, path); } if (g_file_test (path, G_FILE_TEST_IS_REGULAR)) { DEBUG ("Loading CA file"); ok = SSL_CTX_load_verify_locations (session->ctx, path, NULL); } if (!ok) { gulong e, f; for (f = e = ERR_get_error (); e != 0; e = ERR_get_error ()) f = e; DEBUG ("CA '%s' failed: %s", path, ERR_error_string (f, NULL)); } else DEBUG ("CA '%s' loaded", path); } void wocky_tls_session_add_crl (WockyTLSSession *session, const gchar *path) { gboolean ok = FALSE; if (!g_file_test (path, G_FILE_TEST_EXISTS)) { DEBUG ("CRL file or path '%s' not accessible", path); return; } if (g_file_test (path, G_FILE_TEST_IS_DIR)) { X509_STORE *store = SSL_CTX_get_cert_store (session->ctx); X509_LOOKUP_METHOD *method = X509_LOOKUP_hash_dir (); X509_LOOKUP *lookup = X509_STORE_add_lookup (store, method); DEBUG ("Loading CRL directory"); ok = X509_LOOKUP_add_dir (lookup, path, X509_FILETYPE_PEM) == 1; } if (g_file_test (path, G_FILE_TEST_IS_REGULAR)) { X509_STORE *store = SSL_CTX_get_cert_store (session->ctx); X509_LOOKUP_METHOD *method = X509_LOOKUP_file (); X509_LOOKUP *lookup = X509_STORE_add_lookup (store, method); DEBUG ("Loading CRL file"); ok = X509_LOOKUP_load_file (lookup, path, X509_FILETYPE_PEM) == 1; } if (!ok) { gulong e, f; for (f = e = ERR_get_error (); e != 0; e = ERR_get_error ()) f = e; DEBUG ("'%s' failed: %s\n", path, ERR_error_string (f, NULL)); } else DEBUG ("'%s' loaded\n", path); } /* ************************************************************************* */ void wocky_tls_session_handshake_async (WockyTLSSession *session, gint io_priority, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { DEBUG (""); wocky_tls_job_start (&session->job.handshake.job, session, io_priority, cancellable, callback, user_data, wocky_tls_session_handshake_async); ssl_handshake (session); } WockyTLSConnection * wocky_tls_session_handshake_finish (WockyTLSSession *session, GAsyncResult *result, GError **error) { GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (result); DEBUG (""); { GObject *source_object; source_object = g_async_result_get_source_object (result); g_object_unref (source_object); g_return_val_if_fail (G_OBJECT (session) == source_object, NULL); } g_return_val_if_fail (wocky_tls_session_handshake_async == g_simple_async_result_get_source_tag (simple), NULL); if (g_simple_async_result_propagate_error (simple, error)) return NULL; DEBUG ("connection OK"); return g_object_new (WOCKY_TYPE_TLS_CONNECTION, "session", session, NULL); } static gboolean compare_wildcarded_hostname (const char *hostname, const char *certname) { DEBUG ("%s ~ %s", hostname, certname); if (g_ascii_strcasecmp (hostname, certname) == 0) return TRUE; /* We only allow leading '*.' wildcards. See the final bullet point of XMPP * Core §13.7.1.2.1 * : * * DNS domain names in server certificates MAY contain the wildcard * character '*' as the complete left-most label within the identifier. */ if (g_str_has_prefix (certname, "*.")) { const gchar *certname_tail = certname + 2; const gchar *hostname_tail = index (hostname, '.'); if (hostname_tail == NULL) return FALSE; hostname_tail++; DEBUG ("%s ~ %s", hostname_tail, certname_tail); return g_ascii_strcasecmp (hostname_tail, certname_tail) == 0; } return FALSE; } static gboolean check_peer_name (const char *target, X509 *cert) { int i; gboolean rval = FALSE; X509_NAME *subject = X509_get_subject_name (cert); X509_CINF *ci = cert->cert_info; static const long nid[] = { NID_commonName, NID_subject_alt_name, NID_undef }; /* first, see if the x509 name contains the info we want: */ for (i = 0; nid[i] != NID_undef; i++) { gssize len = X509_NAME_get_text_by_NID (subject, nid[i], NULL, -1); if (len > 0) { char *cname = g_new0 (gchar, len + 1); X509_NAME_get_text_by_NID (subject, nid[i], cname, len + 1); DEBUG ("got cname '%s' from x509 name, nid #%u", cname, i); rval = compare_wildcarded_hostname (target, cname); g_free (cname); } } /* ok, if that failed, we need to dive into the guts of the x509 structure * * and extract the subject_alt_name from the x509 v3 extensions: if that * * extension is present, and a string, use that. If it is present, and * * a multi-value stack, trawl it for the "DNS" entry and use that */ if (!rval && (ci->extensions != NULL)) for (i = 0; i < sk_X509_EXTENSION_num(ci->extensions) && !rval; i++) { X509_EXTENSION *ext = sk_X509_EXTENSION_value (ci->extensions, i); ASN1_OBJECT *obj = X509_EXTENSION_get_object (ext); X509V3_EXT_METHOD *convert = NULL; long ni = OBJ_obj2nid (obj); const guchar *p; char *value = NULL; int len = ext->value->length; void *ext_str = NULL; if (ni != NID_subject_alt_name) continue; /* OpenSSL >= 1.0 returns a const here, but we need to be also * * compatible with older versions that return a non-const value, * * hence the cast */ if ((convert = (X509V3_EXT_METHOD *) X509V3_EXT_get (ext)) == NULL) continue; p = ext->value->data; ext_str = ((convert->it != NULL) ? ASN1_item_d2i (NULL, &p, len, ASN1_ITEM_ptr(convert->it)) : convert->d2i (NULL, &p, len) ); if (ext_str == NULL) continue; if (convert->i2s != NULL) { value = convert->i2s (convert, ext_str); DEBUG ("got cname '%s' from subject_alt_name, which is a string", value); rval = compare_wildcarded_hostname (target, value); OPENSSL_free (value); } else if (convert->i2v != NULL) { int j; STACK_OF(CONF_VALUE) *nval = convert->i2v(convert, ext_str, NULL); for (j = 0; j < sk_CONF_VALUE_num (nval); j++) { CONF_VALUE *v = sk_CONF_VALUE_value(nval, j); if (!wocky_strdiff (v->name, "DNS")) { DEBUG ("Got cname '%s' from subject_alt_name, which is a " "multi-value stack with a 'DNS' entry", v->value); rval = compare_wildcarded_hostname (target, v->value); } } sk_CONF_VALUE_pop_free(nval, X509V3_conf_free); } if (convert->it) ASN1_item_free (ext_str, ASN1_ITEM_ptr (convert->it)); else convert->ext_free (ext_str); } return rval; } static gboolean check_peer_names (const char *peer_name, GStrv extra_identities, X509 *cert) { gboolean tried = FALSE; if (peer_name != NULL) { if (check_peer_name (peer_name, cert)) return TRUE; tried = TRUE; } if (extra_identities != NULL) { gint i; for (i = 0; extra_identities[i] != NULL; i++) { if (wocky_strdiff (extra_identities[i], peer_name)) { if (check_peer_name (extra_identities[i], cert)) return TRUE; tried = TRUE; } } } /* If no peer names were passed it means we didn't want to check the * certificate against anything. * If some attempts were made then it means the check failed. */ return !tried; } GPtrArray * wocky_tls_session_get_peers_certificate (WockyTLSSession *session, WockyTLSCertType *type) { STACK_OF(X509) *cert_chain = NULL; guint cls = 0; GPtrArray *certificates; certificates = g_ptr_array_new_with_free_func ((GDestroyNotify) g_array_unref); cert_chain = SSL_get_peer_cert_chain (session->ssl); if (cert_chain == NULL) return NULL; if (type != NULL) *type = WOCKY_TLS_CERT_TYPE_X509; cls = sk_X509_num (cert_chain); for (guint i = 0; i < cls; i++) { GArray *certificate; X509 *peer; gint peer_len; guchar *peer_buffer; peer = sk_X509_value (cert_chain, i); peer_len = i2d_X509 (peer, NULL); certificate = g_array_sized_new (TRUE, TRUE, sizeof (guchar), peer_len); peer_buffer = g_malloc (peer_len); i2d_X509 (peer, &peer_buffer); peer_buffer -= peer_len; g_array_append_vals (certificate, peer_buffer, peer_len); g_ptr_array_add (certificates, certificate); g_free (peer_buffer); } return certificates; } static WockyTLSCertStatus _cert_status (WockyTLSSession *session, int ssl_code, WockyTLSVerificationLevel level, int old_code) { switch (ssl_code) { case X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN: case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT: case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY: case X509_V_ERR_SUBJECT_ISSUER_MISMATCH: return WOCKY_TLS_CERT_SIGNER_UNKNOWN; break; case X509_V_ERR_UNABLE_TO_DECRYPT_CERT_SIGNATURE: case X509_V_ERR_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY: case X509_V_ERR_CERT_SIGNATURE_FAILURE: case X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE: case X509_V_ERR_INVALID_PURPOSE: case X509_V_ERR_CERT_REJECTED: case X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT: return WOCKY_TLS_CERT_INVALID; break; case X509_V_ERR_CERT_REVOKED: return WOCKY_TLS_CERT_REVOKED; break; case X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD: case X509_V_ERR_CERT_NOT_YET_VALID: return WOCKY_TLS_CERT_NOT_ACTIVE; break; case X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD: case X509_V_ERR_CERT_HAS_EXPIRED: return WOCKY_TLS_CERT_EXPIRED; break; case X509_V_ERR_OUT_OF_MEM: return WOCKY_TLS_CERT_INTERNAL_ERROR; break; case X509_V_ERR_INVALID_CA: case X509_V_ERR_CERT_UNTRUSTED: case X509_V_ERR_AKID_SKID_MISMATCH: case X509_V_ERR_AKID_ISSUER_SERIAL_MISMATCH: case X509_V_ERR_KEYUSAGE_NO_CERTSIGN: return WOCKY_TLS_CERT_SIGNER_UNAUTHORISED; break; case X509_V_ERR_PATH_LENGTH_EXCEEDED: return WOCKY_TLS_CERT_MAYBE_DOS; break; case X509_V_ERR_UNABLE_TO_GET_CRL: /* if we are in STRICT mode, being unable to see the CRL is a * terminal condition: in NORMAL or LENIENT we can live with it. * Also, if we re-tried and got the same error, we're just going * to loop indefinitely, so bail out with the original error. * NOTE: 'unable to fetch' a CRL is not the same as CRL invalidated * the certificate, or we'd just turn the CRL checks off when in * NORMAL or LENIENT mode */ if (level == WOCKY_TLS_VERIFY_STRICT || old_code == X509_V_ERR_UNABLE_TO_GET_CRL) { return WOCKY_TLS_CERT_INSECURE; } else { WockyTLSCertStatus status = WOCKY_TLS_CERT_OK; X509_STORE_CTX *xctx = X509_STORE_CTX_new(); X509_STORE *store = SSL_CTX_get_cert_store(session->ctx); X509 *cert = SSL_get_peer_certificate (session->ssl); STACK_OF(X509) *chain = SSL_get_peer_cert_chain (session->ssl); long old_flags = store->param->flags; long new_flags = old_flags; DEBUG("No CRL available, but not in strict mode - re-verifying"); new_flags &= ~(X509_V_FLAG_CRL_CHECK|X509_V_FLAG_CRL_CHECK_ALL); store->param->flags = new_flags; X509_STORE_CTX_init (xctx, store, cert, chain); X509_STORE_CTX_set_flags (xctx, new_flags); if( X509_verify_cert (xctx) < 1 ) { int new_code = X509_STORE_CTX_get_error (xctx); status = _cert_status (session, new_code, level, ssl_code); } store->param->flags = old_flags; X509_STORE_CTX_free (xctx); X509_free (cert); return status; } break; default: return WOCKY_TLS_CERT_UNKNOWN_ERROR; } } int wocky_tls_session_verify_peer (WockyTLSSession *session, const gchar *peername, GStrv extra_identities, WockyTLSVerificationLevel level, WockyTLSCertStatus *status) { int rval = -1; X509 *cert; gboolean lenient = (level == WOCKY_TLS_VERIFY_LENIENT); DEBUG (""); g_assert (status != NULL); *status = WOCKY_TLS_CERT_OK; switch (level) { case WOCKY_TLS_VERIFY_STRICT: case WOCKY_TLS_VERIFY_NORMAL: case WOCKY_TLS_VERIFY_LENIENT: break; default: g_warn_if_reached (); level = WOCKY_TLS_VERIFY_STRICT; } DEBUG ("setting ssl verify flags level to: %s", wocky_enum_to_nick (WOCKY_TYPE_TLS_VERIFICATION_LEVEL, level)); cert = SSL_get_peer_certificate (session->ssl); rval = SSL_get_verify_result (session->ssl); DEBUG ("X509 cert: %p; verified: %d", cert, rval); /* If no certificate is presented, SSL_get_verify_result() always returns * X509_V_OK. This is listed as a bug in `man 3 SSL_get_verify_result`. To * future-proof against that bug being fixed, we don't assume that behaviour. */ if (cert == NULL) { if (lenient) { *status = WOCKY_TLS_CERT_OK; return X509_V_OK; } else if (rval == X509_V_OK) { DEBUG ("Anonymous SSL handshake"); rval = X509_V_ERR_CERT_UNTRUSTED; } } else if (!check_peer_names (peername, extra_identities, cert)) { /* Irrespective of whether the certificate is valid, if it's for the * wrong host that's arguably a more useful error condition to report. */ *status = WOCKY_TLS_CERT_NAME_MISMATCH; return X509_V_ERR_APPLICATION_VERIFICATION; } if (rval != X509_V_OK) { DEBUG ("cert verification error: %d", rval); *status = _cert_status (session, rval, level, X509_V_OK); /* some conditions are to be ignored when lenient, others still matter */ if (lenient) switch (*status) { case WOCKY_TLS_CERT_INTERNAL_ERROR: case WOCKY_TLS_CERT_REVOKED: case WOCKY_TLS_CERT_MAYBE_DOS: DEBUG ("this error matters, even though we're in lenient mode"); break; default: DEBUG ("ignoring errors: we're in lenient mode"); rval = X509_V_OK; *status = WOCKY_TLS_CERT_OK; } } return rval; } static gssize wocky_tls_input_stream_read (GInputStream *stream, void *buffer, gsize count, GCancellable *cancellable, GError **error) { /* WockyTLSSession *session = WOCKY_TLS_INPUT_STREAM (stream)->session; */ DEBUG ("sync read - not implmented"); g_assert_not_reached (); return 0; } static void wocky_tls_input_stream_read_async (GInputStream *stream, void *buffer, gsize count, gint io_priority, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { WockyTLSSession *session = WOCKY_TLS_INPUT_STREAM (stream)->session; int ret; if (tls_debug_level >= DEBUG_ASYNC_DETAIL_LEVEL) DEBUG (""); g_assert (session->job.read.active == FALSE); /* It is possible for a complete SSL record to be present in the read BIO * * already as a result of a previous read, since SSL_read may extract * * just the first complete record, or some or all of them: * * as a result, we may not want to issue an actual read request as the * * data we are expecting may already have been read, causing us to wait * * until the next block of data arrives over the network (which may not * * ever happen): short-circuit the actual read if this is the case: */ ret = SSL_read (session->ssl, buffer, count); if (ssl_read_is_complete (session, ret)) { GSimpleAsyncResult *r; if (tls_debug_level >= DEBUG_ASYNC_DETAIL_LEVEL) DEBUG ("already have %d clearbytes buffered", ret); r = g_simple_async_result_new (G_OBJECT (stream), callback, user_data, wocky_tls_input_stream_read_async); if (session->job.read.error == NULL) g_simple_async_result_set_op_res_gssize (r, ret); else g_simple_async_result_set_from_error (r, session->job.read.error); g_simple_async_result_complete_in_idle (r); g_object_unref (r); return; } wocky_tls_job_start (&session->job.read, stream, io_priority, cancellable, callback, user_data, wocky_tls_input_stream_read_async); session->job.read.buffer = buffer; session->job.read.count = count; ssl_fill (session); } static gssize wocky_tls_input_stream_read_finish (GInputStream *stream, GAsyncResult *result, GError **error) { GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (result); if (tls_debug_level >= DEBUG_ASYNC_DETAIL_LEVEL) DEBUG (""); g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (stream), wocky_tls_input_stream_read_async), -1); if (g_simple_async_result_propagate_error (simple, error)) return -1; return g_simple_async_result_get_op_res_gssize (simple); } static gssize wocky_tls_output_stream_write (GOutputStream *stream, const void *buffer, gsize count, GCancellable *cancellable, GError **error) { /* WockyTLSSession *session = WOCKY_TLS_OUTPUT_STREAM (stream)->session; */ DEBUG ("sync write - not implemented"); g_assert_not_reached (); return 0; } static void wocky_tls_output_stream_write_async (GOutputStream *stream, const void *buffer, gsize count, gint io_priority, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { int code; WockyTLSSession *session = WOCKY_TLS_OUTPUT_STREAM (stream)->session; DEBUG ("%" G_GSIZE_FORMAT " clearbytes to send", count); wocky_tls_job_start (&session->job.write, stream, io_priority, cancellable, callback, user_data, wocky_tls_output_stream_write_async); session->job.write.count = count; code = SSL_write (session->ssl, buffer, count); if (code < 0) { int error = SSL_get_error (session->ssl, code); switch (error) { case SSL_ERROR_WANT_WRITE: DEBUG ("Incomplete SSL write to BIO (theoretically impossible)"); ssl_flush (session); return; case SSL_ERROR_WANT_READ: g_warning ("write caused read: unsupported TLS re-negotiation?"); /* deliberately falling through to the default case, having logged a * more specific warning. */ default: DEBUG ("SSL write failed, setting error %d", error); /* if we haven't already generated an error, set one here: */ if(session->job.write.error == NULL) session->job.write.error = g_error_new (WOCKY_TLS_ERROR, error, "OpenSSL write: protocol error %d", error); wocky_tls_session_try_operation (session, WOCKY_TLS_OP_WRITE); return; } } ssl_flush (session); } static gssize wocky_tls_output_stream_write_finish (GOutputStream *stream, GAsyncResult *result, GError **error) { GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (result); if (tls_debug_level >= DEBUG_ASYNC_DETAIL_LEVEL) DEBUG (""); { GObject *source_object; source_object = g_async_result_get_source_object (result); g_object_unref (source_object); g_return_val_if_fail (G_OBJECT (stream) == source_object, -1); } g_return_val_if_fail (wocky_tls_output_stream_write_async == g_simple_async_result_get_source_tag (simple), -1); if (g_simple_async_result_propagate_error (simple, error)) return -1; return g_simple_async_result_get_op_res_gssize (simple); } static void wocky_tls_output_stream_init (WockyTLSOutputStream *stream) { } static void wocky_tls_input_stream_init (WockyTLSInputStream *stream) { } static void wocky_tls_output_stream_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { WockyTLSOutputStream *stream = WOCKY_TLS_OUTPUT_STREAM (object); switch (prop_id) { case PROP_C_SESSION: stream->session = g_value_dup_object (value); break; default: g_assert_not_reached (); } } static void wocky_tls_output_stream_constructed (GObject *object) { WockyTLSOutputStream *stream = WOCKY_TLS_OUTPUT_STREAM (object); g_assert (stream->session); } static void wocky_tls_output_stream_finalize (GObject *object) { WockyTLSOutputStream *stream = WOCKY_TLS_OUTPUT_STREAM (object); g_object_unref (stream->session); G_OBJECT_CLASS (wocky_tls_output_stream_parent_class) ->finalize (object); } static void wocky_tls_output_stream_class_init (GOutputStreamClass *class) { GObjectClass *obj_class = G_OBJECT_CLASS (class); class->write_fn = wocky_tls_output_stream_write; class->write_async = wocky_tls_output_stream_write_async; class->write_finish = wocky_tls_output_stream_write_finish; obj_class->set_property = wocky_tls_output_stream_set_property; obj_class->constructed = wocky_tls_output_stream_constructed; obj_class->finalize = wocky_tls_output_stream_finalize; g_object_class_install_property (obj_class, PROP_O_SESSION, g_param_spec_object ("session", "TLS session", "the TLS session object for this stream", WOCKY_TYPE_TLS_SESSION, G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB)); } static void wocky_tls_input_stream_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { WockyTLSInputStream *stream = WOCKY_TLS_INPUT_STREAM (object); switch (prop_id) { case PROP_C_SESSION: stream->session = g_value_dup_object (value); break; default: g_assert_not_reached (); } } static void wocky_tls_input_stream_constructed (GObject *object) { WockyTLSInputStream *stream = WOCKY_TLS_INPUT_STREAM (object); g_assert (stream->session); } static void wocky_tls_input_stream_finalize (GObject *object) { WockyTLSInputStream *stream = WOCKY_TLS_INPUT_STREAM (object); g_object_unref (stream->session); G_OBJECT_CLASS (wocky_tls_input_stream_parent_class) ->finalize (object); } static void wocky_tls_input_stream_class_init (GInputStreamClass *class) { GObjectClass *obj_class = G_OBJECT_CLASS (class); class->read_fn = wocky_tls_input_stream_read; class->read_async = wocky_tls_input_stream_read_async; class->read_finish = wocky_tls_input_stream_read_finish; obj_class->set_property = wocky_tls_input_stream_set_property; obj_class->constructed = wocky_tls_input_stream_constructed; obj_class->finalize = wocky_tls_input_stream_finalize; g_object_class_install_property (obj_class, PROP_I_SESSION, g_param_spec_object ("session", "TLS session", "the TLS session object for this stream", WOCKY_TYPE_TLS_SESSION, G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB)); } static void wocky_tls_connection_init (WockyTLSConnection *connection) { } static void wocky_tls_session_read_ready (GObject *object, GAsyncResult *result, gpointer user_data) { WockyTLSSession *session = WOCKY_TLS_SESSION (user_data); GInputStream *input = G_INPUT_STREAM (object); GError **error = &(session->job.read.error); gssize rsize = 0; gchar *buf = session->job.handshake.job.active ? session->job.handshake.job.rbuf : session->job.read.rbuf; if (tls_debug_level >= DEBUG_ASYNC_DETAIL_LEVEL) DEBUG (""); rsize = g_input_stream_read_finish (input, result, error); if (rsize > 0) { int x; int y; DEBUG ("received %" G_GSSIZE_FORMAT " cipherbytes, filling SSL BIO", rsize); BIO_write (session->rbio, buf, rsize); if (tls_debug_level > DEBUG_ASYNC_DETAIL_LEVEL + 1) for (x = 0; x < rsize; x += 16) { for (y = 0; y < 16 && x + y < rsize; y++) { char c = *(buf + x + y); char d = (g_ascii_isprint (c) && g_ascii_isgraph (c)) ? c : '.'; fprintf (stderr, "%02x %c ", c & 0xff, d); } fprintf (stderr, "\n"); } } /* note that we never issue a read of 0, so this _must_ be EOF (0) * * or a fatal error (-ve rval) */ else if (session->job.handshake.job.active) { if (tls_debug_level >= DEBUG_ASYNC_DETAIL_LEVEL) DEBUG("read SSL cipherbytes (handshake) failed: %" G_GSSIZE_FORMAT, rsize); session->job.handshake.state = SSL_ERROR_SSL; } else { DEBUG ("read of SSL cipherbytes failed: %" G_GSSIZE_FORMAT, rsize); if ((*error != NULL) && ((*error)->domain == g_io_error_quark ())) { /* if there were any errors we could ignore, we'd do it like this: * * g_error_free (*error); *error = NULL; */ DEBUG ("failed op: [%d] %s", (*error)->code, (*error)->message); } /* in order for non-handshake reads to return an error properly * * we need to make sure the error in the job is set */ else if (*error == NULL) { *error = g_error_new (WOCKY_TLS_ERROR, SSL_ERROR_SSL, "unknown error"); } } wocky_tls_session_try_operation (session, WOCKY_TLS_OP_READ); } static void wocky_tls_session_write_ready (GObject *object, GAsyncResult *result, gpointer user_data) { WockyTLSSession *session = WOCKY_TLS_SESSION (user_data); gint buffered = BIO_pending (session->wbio); gssize written; if (tls_debug_level >= DEBUG_ASYNC_DETAIL_LEVEL) DEBUG (""); written = g_output_stream_write_finish (G_OUTPUT_STREAM (object), result, &(session->job.write.error)); if (written == buffered) { DEBUG ("%d bytes written, clearing write BIO", buffered); (void) BIO_reset (session->wbio); wocky_tls_session_try_operation (session, WOCKY_TLS_OP_WRITE); } else { gchar *buffer; long bsize = BIO_get_mem_data (session->wbio, &buffer); long psize = bsize - written; /* scrub the data we did manage to write from our buffer */ if (written > 0) { gchar *pending = g_memdup (buffer + written, psize); (void) BIO_reset (session->wbio); (void) BIO_write (session->wbio, pending, psize); g_free (pending); } if (session->job.write.error != NULL) { if (tls_debug_level >= DEBUG_ASYNC_DETAIL_LEVEL) DEBUG ("Incomplete async write [%" G_GSSIZE_FORMAT "/%d bytes]: " "%s:%u %s", written, buffered, g_quark_to_string (session->job.write.error->domain), session->job.write.error->code, session->job.write.error->message); /* if we have a non-fatal error, erase it try again */ if (g_error_matches (session->job.write.error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK)) g_clear_error (&(session->job.write.error)); } /* no error here means retry the operation; otherwise bail out */ if (session->job.write.error == NULL) ssl_flush (session); else wocky_tls_session_try_operation (session, WOCKY_TLS_OP_WRITE); } } static void wocky_tls_session_init (WockyTLSSession *session) { const char *level; guint lvl = 0; static gsize initialised; if G_UNLIKELY (g_once_init_enter (&initialised)) { gint malloc_init_succeeded; DEBUG ("initialising SSL library and error strings"); malloc_init_succeeded = CRYPTO_malloc_init (); g_warn_if_fail (malloc_init_succeeded); SSL_library_init (); SSL_load_error_strings (); OpenSSL_add_all_algorithms(); ENGINE_load_builtin_engines (); g_once_init_leave (&initialised, 1); } if ((level = getenv ("WOCKY_TLS_DEBUG_LEVEL")) != NULL) lvl = atoi (level); tls_debug_level = lvl; } static void wocky_tls_session_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { WockyTLSSession *session = WOCKY_TLS_SESSION (object); switch (prop_id) { case PROP_S_STREAM: session->stream = g_value_dup_object (value); break; case PROP_S_SERVER: session->server = g_value_get_boolean (value); break; case PROP_S_DHBITS: session->dh_bits = g_value_get_uint (value); break; case PROP_S_KEYFILE: session->key_file = g_value_dup_string (value); break; case PROP_S_CERTFILE: session->cert_file = g_value_dup_string (value); break; default: g_assert_not_reached (); } } static void set_dh_parameters (WockyTLSSession *session) { DH *dh; switch (session->dh_bits) { case 4096: DEBUG ("get_dh4096"); dh = get_dh4096 (); break; case 2048: DEBUG ("get_dh2048"); dh = get_dh2048 (); break; case 1024: DEBUG ("get_dh1024"); dh = get_dh1024 (); break; case 512: DEBUG ("get_dh512"); dh = get_dh512 (); break; default: DEBUG ("Bad dh-bits setting: %d, reset to 1024", session->dh_bits); dh = get_dh1024 (); } SSL_CTX_set_tmp_dh (session->ctx, dh); DH_free (dh); } static void set_ecdh_key (WockyTLSSession *session) { EC_KEY *ecdh = EC_KEY_new_by_curve_name (NID_sect163r2); if (ecdh == NULL) { DEBUG ("unable to create elliptical crypto key for sect163r2 curve"); return; } SSL_CTX_set_tmp_ecdh (session->ctx,ecdh); EC_KEY_free (ecdh); } static void wocky_tls_session_constructed (GObject *object) { WockyTLSSession *session = WOCKY_TLS_SESSION (object); if (session->server) { DEBUG ("I'm a server; using TLSv1_server_method"); /* OpenSSL >= 1.0 returns a const here, but we need to be also * * compatible with older versions that return a non-const value, * * hence the cast */ session->method = (SSL_METHOD *) TLSv1_server_method (); } else { DEBUG ("I'm a client; using TLSv1_client_method"); session->method = (SSL_METHOD *) TLSv1_client_method (); } session->ctx = SSL_CTX_new (session->method); if (!SSL_CTX_set_default_verify_paths (session->ctx)) g_warning ("SSL_CTX_set_default_verify_paths() failed"); /* verification will be done manually after the handshake: */ SSL_CTX_set_verify (session->ctx, SSL_VERIFY_NONE, NULL); SSL_CTX_set_options (session->ctx, SSL_OP_CIPHER_SERVER_PREFERENCE | /* It is usually safe to use SSL_OP_ALL to enable the bug workaround * options if compatibility with somewhat broken implementations is * desired. */ SSL_OP_ALL | /* Set the NO_TICKET option on the context to be kind to the Google Talk * server, which seems unwilling to handle empty session tickets due to a * bug in Java. * * See http://twistedmatrix.com/trac/ticket/3463 and * http://loudmouth.lighthouseapp.com/projects/17276/tickets/28. */ SSL_OP_NO_TICKET | /* SSLv2 is excessively quaint. We shouldn't be using it anyway, since * we're using TLSv1 methods, but... */ SSL_OP_NO_SSLv2); X509_STORE_set_flags (SSL_CTX_get_cert_store (session->ctx), X509_V_FLAG_CRL_CHECK|X509_V_FLAG_CRL_CHECK_ALL); #ifdef CIPHER_LIST SSL_CTX_set_cipher_list (session->ctx, CIPHER_LIST); #endif if (session->server) { set_dh_parameters (session); set_ecdh_key (session); } if ((session->key_file != NULL) && (session->cert_file != NULL)) { long errnum; DEBUG ("cert: %s", session->cert_file); DEBUG ("key : %s", session->key_file); SSL_CTX_use_certificate_file (session->ctx, session->cert_file, SSL_FILETYPE_PEM); SSL_CTX_use_PrivateKey_file (session->ctx, session->key_file, SSL_FILETYPE_PEM); if (!SSL_CTX_check_private_key (session->ctx)) { errnum = ERR_get_error (); DEBUG ("cert/key check: %ld %s", errnum, error_to_string (errnum)); } else DEBUG ("certificate loaded"); } session->ssl = SSL_new (session->ctx); session->rbio = BIO_new (BIO_s_mem ()); session->wbio = BIO_new (BIO_s_mem ()); if (session->rbio == NULL) g_error ("Could not allocate memory BIO for SSL reads"); if (session->wbio == NULL) g_error ("Could not allocate memory BIO for SSL writes"); if (tls_debug_level >= DEBUG_ASYNC_DETAIL_LEVEL) { int x = 0; const char *c = SSL_get_cipher_list (session->ssl, x); for (; c != NULL; c = SSL_get_cipher_list (session->ssl, ++x)) DEBUG ("%03d: %s", x, c); } if (tls_debug_level >= DEBUG_ASYNC_DETAIL_LEVEL) { BIO_set_callback (session->rbio, BIO_debug_callback); BIO_set_callback (session->wbio, BIO_debug_callback); } BIO_set_mem_eof_return (session->rbio, -1); SSL_set_bio (session->ssl, session->rbio, session->wbio); DEBUG ("done"); } static void wocky_tls_session_finalize (GObject *object) { WockyTLSSession *session = WOCKY_TLS_SESSION (object); /* the BIOs are freed by this call */ SSL_free (session->ssl); /* free (session->method); handled by SSL_CTX_free */ session->method = NULL; SSL_CTX_free (session->ctx); session->ctx = NULL; g_object_unref (session->stream); G_OBJECT_CLASS (wocky_tls_session_parent_class)->finalize (object); } static void wocky_tls_session_dispose (GObject *object) { WockyTLSSession *session = WOCKY_TLS_SESSION (object); g_free (session->key_file); session->key_file = NULL; g_free (session->cert_file); session->cert_file = NULL; G_OBJECT_CLASS (wocky_tls_session_parent_class)->dispose (object); } static void wocky_tls_session_class_init (GObjectClass *class) { class->set_property = wocky_tls_session_set_property; class->constructed = wocky_tls_session_constructed; class->finalize = wocky_tls_session_finalize; class->dispose = wocky_tls_session_dispose; g_object_class_install_property (class, PROP_S_STREAM, g_param_spec_object ("base-stream", "base stream", "the stream that TLS communicates over", G_TYPE_IO_STREAM, G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB)); g_object_class_install_property (class, PROP_S_SERVER, g_param_spec_boolean ("server", "server", "whether this is a server", FALSE, G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB)); g_object_class_install_property (class, PROP_S_DHBITS, g_param_spec_uint ("dh-bits", "Diffie-Hellman bits", "Diffie-Hellmann bits: 512, 1024, 2048, or 4096", 512, 4096, 1024, G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB)); g_object_class_install_property (class, PROP_S_KEYFILE, g_param_spec_string ("x509-key", "x509 key", "x509 PEM key file", NULL, G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB)); g_object_class_install_property (class, PROP_S_CERTFILE, g_param_spec_string ("x509-cert", "x509 certificate", "x509 PEM certificate file", NULL, G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB)); } static void wocky_tls_connection_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { WockyTLSConnection *connection = WOCKY_TLS_CONNECTION (object); switch (prop_id) { case PROP_C_SESSION: connection->session = g_value_dup_object (value); break; default: g_assert_not_reached (); } } static gboolean wocky_tls_connection_close (GIOStream *stream, GCancellable *cancellable, GError **error) { WockyTLSConnection *connection = WOCKY_TLS_CONNECTION (stream); return g_io_stream_close (connection->session->stream, cancellable, error); } static GInputStream * wocky_tls_connection_get_input_stream (GIOStream *io_stream) { WockyTLSConnection *connection = WOCKY_TLS_CONNECTION (io_stream); if (connection->input == NULL) connection->input = g_object_new (WOCKY_TYPE_TLS_INPUT_STREAM, "session", connection->session, NULL); return (GInputStream *)connection->input; } static GOutputStream * wocky_tls_connection_get_output_stream (GIOStream *io_stream) { WockyTLSConnection *connection = WOCKY_TLS_CONNECTION (io_stream); if (connection->output == NULL) connection->output = g_object_new (WOCKY_TYPE_TLS_OUTPUT_STREAM, "session", connection->session, NULL); return (GOutputStream *)connection->output; } static void wocky_tls_connection_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { switch (prop_id) { default: g_assert_not_reached (); } } static void wocky_tls_connection_constructed (GObject *object) { WockyTLSConnection *connection = WOCKY_TLS_CONNECTION (object); g_assert (connection->session); } static void wocky_tls_connection_finalize (GObject *object) { WockyTLSConnection *connection = WOCKY_TLS_CONNECTION (object); g_object_unref (connection->session); if (connection->input != NULL) g_object_unref (connection->input); if (connection->output != NULL) g_object_unref (connection->output); G_OBJECT_CLASS (wocky_tls_connection_parent_class) ->finalize (object); } static void wocky_tls_connection_class_init (WockyTLSConnectionClass *class) { GObjectClass *gobject_class = G_OBJECT_CLASS (class); GIOStreamClass *stream_class = G_IO_STREAM_CLASS (class); gobject_class->get_property = wocky_tls_connection_get_property; gobject_class->set_property = wocky_tls_connection_set_property; gobject_class->constructed = wocky_tls_connection_constructed; gobject_class->finalize = wocky_tls_connection_finalize; g_object_class_install_property (gobject_class, PROP_C_SESSION, g_param_spec_object ("session", "TLS session", "the TLS session object for this connection", WOCKY_TYPE_TLS_SESSION, G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); stream_class->get_input_stream = wocky_tls_connection_get_input_stream; stream_class->get_output_stream = wocky_tls_connection_get_output_stream; stream_class->close_fn = wocky_tls_connection_close; } WockyTLSSession * wocky_tls_session_new (GIOStream *stream) { return g_object_new (WOCKY_TYPE_TLS_SESSION, "base-stream", stream, "server", FALSE, NULL); } /** * wocky_tls_session_server_new: * @stream: a GIOStream on which we expect to receive the client TLS handshake * @dhbits: size of the DH parameters * @key: the path to the X509 PEM key file * @cert: the path to the X509 PEM certificate * * Create a new TLS server session * * Returns: a #WockyTLSSession object */ WockyTLSSession * wocky_tls_session_server_new (GIOStream *stream, guint dhbits, const gchar* key, const gchar* cert) { if (dhbits == 0) dhbits = 1024; return g_object_new (WOCKY_TYPE_TLS_SESSION, "base-stream", stream, "dh-bits", dhbits, "x509-key", key, "x509-cert", cert, "server", TRUE, NULL); } /* this file is "borrowed" from an unmerged gnio feature: */ /* Local Variables: */ /* c-file-style: "gnu" */ /* End: */ telepathy-gabble-0.18.2/lib/ext/wocky/wocky/wocky-xmpp-writer.c0000644000175000017500000003427012200204546024450 0ustar00smcvsmcv00000000000000/* * wocky-xmpp-writer.c - Source for WockyXmppWriter * Copyright (C) 2006-2009 Collabora Ltd. * @author Sjoerd Simons * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 */ /** * SECTION: wocky-xmpp-writer * @title: WockyXmppWriter * @short_description: Xmpp stanza to XML serializer * * The #WockyXmppWriter serializes #WockyStanzas and XMPP stream opening * and closing to raw XML. The various functions provide a pointer to an * internal buffer, which remains valid until the next call to the writer. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include "wocky-xmpp-writer.h" G_DEFINE_TYPE (WockyXmppWriter, wocky_xmpp_writer, G_TYPE_OBJECT) #define WOCKY_DEBUG_FLAG WOCKY_DEBUG_XMPP_WRITER #include "wocky-debug-internal.h" /* properties */ enum { PROP_STREAMING_MODE = 1, }; /* private structure */ struct _WockyXmppWriterPrivate { gboolean dispose_has_run; xmlTextWriterPtr xmlwriter; GQuark current_ns; GQuark stream_ns; gboolean stream_mode; xmlBufferPtr buffer; }; static void wocky_xmpp_writer_init (WockyXmppWriter *self) { WockyXmppWriterPrivate *priv; self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, WOCKY_TYPE_XMPP_WRITER, WockyXmppWriterPrivate); priv = self->priv; priv->current_ns = 0; priv->stream_ns = 0; priv->buffer = xmlBufferCreate (); priv->xmlwriter = xmlNewTextWriterMemory (priv->buffer, 0); priv->stream_mode = TRUE; /* xmlTextWriterSetIndent (priv->xmlwriter, 1); */ } static void wocky_xmpp_writer_dispose (GObject *object); static void wocky_xmpp_writer_finalize (GObject *object); static void wocky_xmpp_write_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec); static void wocky_xmpp_write_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec); static void wocky_xmpp_writer_class_init (WockyXmppWriterClass *wocky_xmpp_writer_class) { GObjectClass *object_class = G_OBJECT_CLASS (wocky_xmpp_writer_class); GParamSpec *param_spec; g_type_class_add_private (wocky_xmpp_writer_class, sizeof (WockyXmppWriterPrivate)); object_class->dispose = wocky_xmpp_writer_dispose; object_class->finalize = wocky_xmpp_writer_finalize; object_class->set_property = wocky_xmpp_write_set_property; object_class->get_property = wocky_xmpp_write_get_property; param_spec = g_param_spec_boolean ("streaming-mode", "streaming-mode", "Whether the xml to be written is one big stream or separate documents", TRUE, G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, PROP_STREAMING_MODE, param_spec); } void wocky_xmpp_writer_dispose (GObject *object) { WockyXmppWriter *self = WOCKY_XMPP_WRITER (object); WockyXmppWriterPrivate *priv = self->priv; if (priv->dispose_has_run) return; priv->dispose_has_run = TRUE; /* release any references held by the object here */ if (G_OBJECT_CLASS (wocky_xmpp_writer_parent_class)->dispose) G_OBJECT_CLASS (wocky_xmpp_writer_parent_class)->dispose (object); } void wocky_xmpp_writer_finalize (GObject *object) { WockyXmppWriter *self = WOCKY_XMPP_WRITER (object); WockyXmppWriterPrivate *priv = self->priv; /* free any data held directly by the object here */ xmlFreeTextWriter (priv->xmlwriter); xmlBufferFree (priv->buffer); G_OBJECT_CLASS (wocky_xmpp_writer_parent_class)->finalize (object); } static void wocky_xmpp_write_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { WockyXmppWriter *writer = WOCKY_XMPP_WRITER (object); WockyXmppWriterPrivate *priv = writer->priv; switch (property_id) { case PROP_STREAMING_MODE: priv->stream_mode = g_value_get_boolean (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void wocky_xmpp_write_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { WockyXmppWriter *writer = WOCKY_XMPP_WRITER (object); WockyXmppWriterPrivate *priv = writer->priv; switch (property_id) { case PROP_STREAMING_MODE: g_value_set_boolean (value, priv->stream_mode); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } /** * wocky_xmpp_writer_new * * Convenience function to create a new #WockyXmppWriter. * * Returns: a new #WockyXmppWriter */ WockyXmppWriter * wocky_xmpp_writer_new (void) { return g_object_new (WOCKY_TYPE_XMPP_WRITER, NULL); } /** * wocky_xmpp_writer_new_no_stream * * Convenience function to create a new #WockyXmppWriter that has streaming * mode disabled. * * Returns: a new #WockyXmppWriter in non-streaming mode */ WockyXmppWriter * wocky_xmpp_writer_new_no_stream (void) { return g_object_new (WOCKY_TYPE_XMPP_WRITER, "streaming-mode", FALSE, NULL); } /** * wocky_xmpp_writer_stream_open: * @writer: a WockyXmppWriter * @to: the target of the stream opening (usually the xmpp server name) * @from: the sender of the stream opening (usually the jid of the client) * @version: XMPP version * @lang: default XMPP stream language * @id: XMPP Stream ID, if any, or NULL * @data: location to store a pointer to the data buffer * @length: length of the data buffer * * Create the XML opening header of an XMPP stream. The result is available in * the @data buffer. The buffer is only valid until the next call to a function * the writer. * * This function can only be called in streaming mode. */ void wocky_xmpp_writer_stream_open (WockyXmppWriter *writer, const gchar *to, const gchar *from, const gchar *version, const gchar *lang, const gchar *id, const guint8 **data, gsize *length) { WockyXmppWriterPrivate *priv = writer->priv; g_assert (priv->stream_mode); xmlBufferEmpty (priv->buffer); xmlTextWriterWriteString (priv->xmlwriter, (xmlChar *) "\n" \ "xmlwriter, (xmlChar *)" to=\""); xmlTextWriterFlush (priv->xmlwriter); xmlAttrSerializeTxtContent (priv->buffer, NULL, NULL, (xmlChar *) to); xmlTextWriterWriteString (priv->xmlwriter, (xmlChar *)"\""); } if (from != NULL) { xmlTextWriterWriteString (priv->xmlwriter, (xmlChar *)" from=\""); xmlTextWriterFlush (priv->xmlwriter); xmlAttrSerializeTxtContent (priv->buffer, NULL, NULL, (xmlChar *) from); xmlTextWriterWriteString (priv->xmlwriter, (xmlChar *)"\""); } if (version != NULL) { xmlTextWriterWriteString (priv->xmlwriter, (xmlChar *)" version=\""); xmlTextWriterFlush (priv->xmlwriter); xmlAttrSerializeTxtContent (priv->buffer, NULL, NULL, (xmlChar *) version); xmlTextWriterWriteString (priv->xmlwriter, (xmlChar *)"\""); } if (lang != NULL) { xmlTextWriterWriteString (priv->xmlwriter, (xmlChar *)" xml:lang=\""); xmlTextWriterFlush (priv->xmlwriter); xmlAttrSerializeTxtContent (priv->buffer, NULL, NULL, (xmlChar *) lang); xmlTextWriterWriteString (priv->xmlwriter, (xmlChar *)"\""); } if (id != NULL) { xmlTextWriterWriteString (priv->xmlwriter, (xmlChar *)" id=\""); xmlTextWriterFlush (priv->xmlwriter); xmlAttrSerializeTxtContent (priv->buffer, NULL, NULL, (xmlChar *) id); xmlTextWriterWriteString (priv->xmlwriter, (xmlChar *)"\""); } xmlTextWriterWriteString (priv->xmlwriter, (xmlChar *) ">\n"); xmlTextWriterFlush (priv->xmlwriter); *data = (const guint8 *)priv->buffer->content; *length = priv->buffer->use; /* Set the magic known namespaces */ priv->current_ns = g_quark_from_string ("jabber:client"); priv->stream_ns = g_quark_from_string ("http://etherx.jabber.org/streams"); DEBUG ("Writing stream opening: %.*s", (int) *length, *data); } /** * wocky_xmpp_writer_stream_close: * @writer: a WockyXmppWriter * @data: location to store a pointer to the data buffer * @length: length of the data buffer * * Create the XML closing footer of an XMPP stream . The result is available * in the @data buffer. The buffer is only valid until the next call to a * function * * This function can only be called in streaming mode. */ void wocky_xmpp_writer_stream_close (WockyXmppWriter *writer, const guint8 **data, gsize *length) { WockyXmppWriterPrivate *priv = writer->priv; static const guint8 *close = (const guint8 *)"
\n"; g_assert (priv->stream_mode); *data = close; *length = strlen ((gchar *) close); DEBUG ("Writing stream close: %.*s", (int) *length, *data); } static void _xml_write_node (WockyXmppWriter *writer, WockyNode *node); static gboolean _write_attr (const gchar *key, const gchar *value, const gchar *prefix, const gchar *ns, gpointer user_data) { WockyXmppWriter *self = WOCKY_XMPP_WRITER (user_data); WockyXmppWriterPrivate *priv = self->priv; GQuark attrns = 0; if (ns != NULL) { attrns = g_quark_from_string (ns); } if (attrns == 0 || attrns == priv->current_ns) { xmlTextWriterWriteAttribute (priv->xmlwriter, (const xmlChar *)key, (const xmlChar *)value); } else if (attrns == priv->stream_ns) { xmlTextWriterWriteAttributeNS (priv->xmlwriter, (const xmlChar *)"stream", (const xmlChar *)key, (const xmlChar *)NULL, (const xmlChar *)value); } else { xmlTextWriterWriteAttributeNS (priv->xmlwriter, (const xmlChar *)prefix, (const xmlChar *)key, (const xmlChar *)ns, (const xmlChar *)value); } return TRUE; } static gboolean _write_child (WockyNode *node, gpointer user_data) { _xml_write_node (WOCKY_XMPP_WRITER (user_data), node); return TRUE; } static void _xml_write_node (WockyXmppWriter *writer, WockyNode *node) { const gchar *l; GQuark oldns; WockyXmppWriterPrivate *priv = writer->priv; oldns = priv->current_ns; if (node->ns == 0 || oldns == node->ns) { /* Another element in the current namespace */ xmlTextWriterStartElement (priv->xmlwriter, (const xmlChar*) node->name); } else if (node->ns == priv->stream_ns) { xmlTextWriterStartElementNS(priv->xmlwriter, (const xmlChar *) "stream", (const xmlChar *) node->name, NULL); } else { priv->current_ns = node->ns; xmlTextWriterStartElementNS (priv->xmlwriter, NULL, (const xmlChar *) node->name, (const xmlChar *) wocky_node_get_ns (node)); } wocky_node_each_attribute (node, _write_attr, writer); l = wocky_node_get_language (node); if (l != NULL) { xmlTextWriterWriteAttributeNS(priv->xmlwriter, (const xmlChar *)"xml", (const xmlChar *)"lang", NULL, (const xmlChar *)l); } wocky_node_each_child (node, _write_child, writer); if (node->content != NULL) { xmlTextWriterWriteString (priv->xmlwriter, (const xmlChar*)node->content); } xmlTextWriterEndElement (priv->xmlwriter); priv->current_ns = oldns; } static void _write_node_tree (WockyXmppWriter *writer, WockyNodeTree *tree, const guint8 **data, gsize *length) { WockyXmppWriterPrivate *priv = writer->priv; xmlBufferEmpty (priv->buffer); DEBUG_NODE_TREE (tree, "Serializing tree:"); if (!priv->stream_mode) { xmlTextWriterStartDocument (priv->xmlwriter, "1.0", "utf-8", NULL); } _xml_write_node (writer, wocky_node_tree_get_top_node (tree)); if (!priv->stream_mode) { xmlTextWriterEndDocument (priv->xmlwriter); } xmlTextWriterFlush (priv->xmlwriter); *data = (const guint8 *)priv->buffer->content; *length = priv->buffer->use; #ifdef ENABLE_DEBUG wocky_debug (WOCKY_DEBUG_NET, "Writing xml: %.*s", (int)*length, *data); #endif } /** * wocky_xmpp_writer_write_stanza: * @writer: a WockyXmppWriter * @stanza: the stanza to serialize * @data: location to store a pointer to the data buffer * @length: length of the data buffer * * Serialize the @stanza to XML. The result is available in the * @data buffer. The buffer is only valid until the next call to a function */ void wocky_xmpp_writer_write_stanza (WockyXmppWriter *writer, WockyStanza *stanza, const guint8 **data, gsize *length) { _write_node_tree (writer, WOCKY_NODE_TREE (stanza), data, length); } /** * wocky_xmpp_writer_write_node_tree: * @writer: a WockyXmppWriter * @tree: the node tree to serialize * @data: location to store a pointer to the data buffer * @length: length of the data buffer * * Serialize the @tree to XML. The result is available in the * @data buffer. The buffer is only valid until the next call to a function. * This function may only be called in non-streaming mode. */ void wocky_xmpp_writer_write_node_tree (WockyXmppWriter *writer, WockyNodeTree *tree, const guint8 **data, gsize *length) { *data = NULL; *length = 0; g_return_if_fail (!writer->priv->stream_mode); _write_node_tree (writer, tree, data, length); } /** * wocky_xmpp_writer_flush: * @writer: a WockyXmppWriter * * Flushes and frees the internal data buffer */ void wocky_xmpp_writer_flush (WockyXmppWriter *writer) { WockyXmppWriterPrivate *priv = writer->priv; xmlBufferFree (priv->buffer); priv->buffer = xmlBufferCreate (); } telepathy-gabble-0.18.2/lib/ext/wocky/wocky/wocky-xmpp-reader.c0000644000175000017500000005451012200204546024375 0ustar00smcvsmcv00000000000000/* * wocky-xmpp-reader.c - Source for WockyXmppReader * Copyright (C) 2006,2009 Collabora Ltd. * @author Sjoerd Simons * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 */ /** * SECTION: wocky-xmpp-reader * @title: WockyXmppReader * @short_description: Xmpp XML to stanza deserializer * * The #WockyXmppReader deserializes XML to #WockyStanzas, * misc, other */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include "wocky-xmpp-reader.h" #include "wocky-signals-marshal.h" #include "wocky-utils.h" #include "wocky-namespaces.h" #include "wocky-stanza.h" #define WOCKY_DEBUG_FLAG WOCKY_DEBUG_XMPP_READER #include "wocky-debug-internal.h" /* properties */ enum { PROP_STREAMING_MODE = 1, PROP_DEFAULT_NAMESPACE, PROP_TO, PROP_FROM, PROP_VERSION, PROP_LANG, PROP_ID, }; G_DEFINE_TYPE (WockyXmppReader, wocky_xmpp_reader, G_TYPE_OBJECT) /* Parser prototypes */ static void _start_element_ns (void *user_data, const xmlChar *localname, const xmlChar *prefix, const xmlChar *uri, int nb_namespaces, const xmlChar **namespaces, int nb_attributes, int nb_defaulted, const xmlChar **attributes); static void _end_element_ns (void *user_data, const xmlChar *localname, const xmlChar *prefix, const xmlChar *URI); static void _characters (void *user_data, const xmlChar *ch, int len); static void _error (void *user_data, xmlErrorPtr error); static xmlSAXHandler parser_handler = { /* internalSubset */ NULL, /* isStandalone */ NULL, /* hasInternalSubset */ NULL, /* hasExternalSubset */ NULL, /* resolveEntity */ NULL, /* getEntity */ NULL, /* entityDecl */ NULL, /* notationDecl */ NULL, /* attributeDecl */ NULL, /* elementDecl */ NULL, /* unparsedEntityDecl */ NULL, /* setDocumentLocator */ NULL, /* startDocument */ NULL, /* endDocument */ NULL, /* startElement */ NULL, /* endElement */ NULL, /* reference */ NULL, /* characters */ _characters, /* ignorableWhitespace */ NULL, /* processingInstruction */ NULL, /* comment */ NULL, /* warning */ NULL, /* error */ NULL, /* fatalError */ NULL, /* getParameterEntity */ NULL, /* cdataBlock */ NULL, /* externalSubset */ NULL, /* initialized */ XML_SAX2_MAGIC, /* _private */ NULL, /* startElementNs */ _start_element_ns, /* endElementNs */ _end_element_ns, /* serror */ _error }; /* private structure */ struct _WockyXmppReaderPrivate { xmlParserCtxtPtr parser; guint depth; WockyStanza *stanza; WockyNode *node; GQueue *nodes; gchar *to; gchar *from; gchar *version; gchar *lang; gchar *id; gboolean dispose_has_run; GError *error /* defeat the coding style checker... */; gboolean stream_mode; gchar *default_namespace; GQueue *stanzas; WockyXmppReaderState state; }; /** * wocky_xmpp_reader_error_quark * * Get the error quark used by the reader. * * Returns: the quark for reader errors. */ GQuark wocky_xmpp_reader_error_quark (void) { static GQuark quark = 0; if (quark == 0) quark = g_quark_from_static_string ("wocky-xmpp-reader-error"); return quark; } /* clear parser state */ static void wocky_xmpp_reader_clear_parser_state (WockyXmppReader *self) { WockyXmppReaderPrivate *priv = self->priv; while (!g_queue_is_empty (priv->stanzas)) { gpointer stanza; stanza = g_queue_pop_head (priv->stanzas); if (stanza != NULL) g_object_unref (stanza); } if (priv->stanza != NULL) g_object_unref (priv->stanza); priv->stanza = NULL; g_queue_clear (priv->nodes); priv->node = NULL; priv->depth = 0; g_free (priv->to); priv->to = NULL; g_free (priv->from); priv->from = NULL; g_free (priv->lang); priv->lang = NULL; g_free (priv->version); priv->version = NULL; g_free (priv->id); priv->id = NULL; if (priv->error != NULL) g_error_free (priv->error); priv->error = NULL; if (priv->parser != NULL) xmlFreeParserCtxt (priv->parser); priv->parser = NULL; priv->state = WOCKY_XMPP_READER_STATE_CLOSED; } static void wocky_init_xml_parser (WockyXmppReader *obj) { WockyXmppReaderPrivate *priv = obj->priv; priv->parser = xmlCreatePushParserCtxt (&parser_handler, obj, NULL, 0, NULL); xmlCtxtUseOptions (priv->parser, XML_PARSE_NOENT); priv->state = priv->stream_mode ? WOCKY_XMPP_READER_STATE_INITIAL : WOCKY_XMPP_READER_STATE_OPENED; } static void wocky_xmpp_reader_constructed (GObject *obj) { wocky_init_xml_parser (WOCKY_XMPP_READER (obj)); } static void wocky_xmpp_reader_init (WockyXmppReader *self) { WockyXmppReaderPrivate *priv; self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, WOCKY_TYPE_XMPP_READER, WockyXmppReaderPrivate); priv = self->priv; priv->nodes = g_queue_new (); priv->stanzas = g_queue_new (); } static void wocky_xmpp_reader_dispose (GObject *object); static void wocky_xmpp_reader_finalize (GObject *object); static void wocky_xmpp_reader_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec); static void wocky_xmpp_reader_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec); static void wocky_xmpp_reader_class_init (WockyXmppReaderClass *wocky_xmpp_reader_class) { GObjectClass *object_class = G_OBJECT_CLASS (wocky_xmpp_reader_class); GParamSpec *param_spec; g_type_class_add_private (wocky_xmpp_reader_class, sizeof (WockyXmppReaderPrivate)); wocky_xmpp_reader_class->stream_element_name = "stream"; wocky_xmpp_reader_class->stream_element_ns = WOCKY_XMPP_NS_STREAM; object_class->constructed = wocky_xmpp_reader_constructed; object_class->dispose = wocky_xmpp_reader_dispose; object_class->finalize = wocky_xmpp_reader_finalize; object_class->set_property = wocky_xmpp_reader_set_property; object_class->get_property = wocky_xmpp_reader_get_property; param_spec = g_param_spec_boolean ("streaming-mode", "streaming-mode", "Whether the xml to be read is one big stream or separate documents", TRUE, G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, PROP_STREAMING_MODE, param_spec); param_spec = g_param_spec_string ("default-namespace", "default namespace", "The default namespace for the root element of the document. " "Only meaningful if streaming-mode is FALSE.", "", G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, PROP_DEFAULT_NAMESPACE, param_spec); param_spec = g_param_spec_string ("to", "to", "to attribute in the xml stream opening", NULL, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, PROP_TO, param_spec); param_spec = g_param_spec_string ("from", "from", "from attribute in the xml stream opening", NULL, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, PROP_FROM, param_spec); param_spec = g_param_spec_string ("version", "version", "version attribute in the xml stream opening", NULL, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, PROP_VERSION, param_spec); param_spec = g_param_spec_string ("lang", "lang", "xml:lang attribute in the xml stream opening", NULL, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, PROP_LANG, param_spec); param_spec = g_param_spec_string ("id", "ID", "id attribute in the xml stream opening", NULL, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, PROP_ID, param_spec); } void wocky_xmpp_reader_dispose (GObject *object) { WockyXmppReader *self = WOCKY_XMPP_READER (object); WockyXmppReaderPrivate *priv = self->priv; if (priv->dispose_has_run) return; priv->dispose_has_run = TRUE; /* release any references held by the object here */ wocky_xmpp_reader_clear_parser_state (self); if (G_OBJECT_CLASS (wocky_xmpp_reader_parent_class)->dispose) G_OBJECT_CLASS (wocky_xmpp_reader_parent_class)->dispose (object); } void wocky_xmpp_reader_finalize (GObject *object) { WockyXmppReader *self = WOCKY_XMPP_READER (object); WockyXmppReaderPrivate *priv = self->priv; /* free any data held directly by the object here */ g_queue_free (priv->stanzas); g_queue_free (priv->nodes); if (priv->error != NULL) g_error_free (priv->error); G_OBJECT_CLASS (wocky_xmpp_reader_parent_class)->finalize (object); } static void wocky_xmpp_reader_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { WockyXmppReader *reader = WOCKY_XMPP_READER (object); WockyXmppReaderPrivate *priv = reader->priv; switch (property_id) { case PROP_STREAMING_MODE: priv->stream_mode = g_value_get_boolean (value); break; case PROP_DEFAULT_NAMESPACE: g_free (priv->default_namespace); priv->default_namespace = g_value_dup_string (value); if (priv->default_namespace == NULL) priv->default_namespace = g_strdup (""); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void wocky_xmpp_reader_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { WockyXmppReader *reader = WOCKY_XMPP_READER (object); WockyXmppReaderPrivate *priv = reader->priv; switch (property_id) { case PROP_STREAMING_MODE: g_value_set_boolean (value, priv->stream_mode); break; case PROP_DEFAULT_NAMESPACE: g_value_set_string (value, priv->default_namespace); break; case PROP_FROM: g_value_set_string (value, priv->from); break; case PROP_TO: g_value_set_string (value, priv->to); break; case PROP_LANG: g_value_set_string (value, priv->lang); break; case PROP_VERSION: g_value_set_string (value, priv->version); break; case PROP_ID: g_value_set_string (value, priv->id); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } /** * wocky_xmpp_reader_new: * * Convenience function to create a new #WockyXmppReader. * * Returns: a new #WockyXmppReader */ WockyXmppReader * wocky_xmpp_reader_new (void) { return g_object_new (WOCKY_TYPE_XMPP_READER, NULL); } /** * wocky_xmpp_reader_new_no_stream: * * Convenience function to create a new #WockyXmppReader that has streaming * mode disabled. * * Returns: a new #WockyXmppReader in non-streaming mode */ WockyXmppReader * wocky_xmpp_reader_new_no_stream (void) { return g_object_new (WOCKY_TYPE_XMPP_READER, "streaming-mode", FALSE, NULL); } /** * wocky_xmpp_reader_new_no_stream_ns: * @default_namespace: default XML namespace to apply to the top-level element * * Create a new #WockyXmppReader, with #WockyXmppReader:streaming-mode disabled * and the specified #WockyXmppReader:default-namespace. * * Returns: (transfer full): a new #WockyXmppReader in non-streaming mode. */ WockyXmppReader * wocky_xmpp_reader_new_no_stream_ns ( const gchar *default_namespace) { return g_object_new (WOCKY_TYPE_XMPP_READER, "streaming-mode", FALSE, "default-namespace", default_namespace, NULL); } static void handle_stream_open ( WockyXmppReader *self, const gchar *localname, const gchar *uri, const gchar *prefix, int nb_attributes, const xmlChar **attributes) { WockyXmppReaderClass *klass = WOCKY_XMPP_READER_GET_CLASS (self); WockyXmppReaderPrivate *priv = self->priv; int i; if (wocky_strdiff (klass->stream_element_name, localname) || wocky_strdiff (klass->stream_element_ns, uri)) { priv->error = g_error_new (WOCKY_XMPP_READER_ERROR, WOCKY_XMPP_READER_ERROR_INVALID_STREAM_START, "Invalid start of the XMPP stream " "(expected <%s xmlns=%s>, got <%s xmlns=%s>)", klass->stream_element_name, klass->stream_element_ns, localname, uri); g_queue_push_tail (priv->stanzas, NULL); return; } DEBUG ("Received stream opening: %s, prefix: %s, uri: %s", localname, prefix != NULL ? prefix : "", uri != NULL ? uri : ""); priv->state = WOCKY_XMPP_READER_STATE_OPENED; for (i = 0; i < nb_attributes * 5; i+=5) { /* attr_name and attr_value are guaranteed non-NULL; attr_prefix and * attr_uri may be NULL. */ const gchar *attr_name = (const gchar *) attributes[i]; const gchar *attr_prefix = (const gchar *) attributes[i+1]; const gchar *attr_uri = (const gchar *) attributes[i+2]; gsize value_len = attributes[i+4] - attributes[i+3]; gchar *attr_value = g_strndup ( (const gchar *) attributes[i+3], value_len); DEBUG ("Stream opening attribute: %s = '%s' (prefix: %s, uri: %s)", attr_name, attr_value, attr_prefix != NULL ? attr_prefix : "", attr_uri != NULL ? attr_uri : ""); if (!strcmp (attr_name, "to")) { g_free (priv->to); priv->to = attr_value; } else if (!strcmp (attr_name, "from")) { g_free (priv->from); priv->from = attr_value; } else if (!strcmp (attr_name, "version")) { g_free (priv->version); priv->version = attr_value; } else if (!strcmp (attr_name, "lang") && !wocky_strdiff (attr_prefix, "xml")) { g_free (priv->lang); priv->lang = attr_value; } else if (!strcmp (attr_name, "id")) { g_free (priv->id); priv->id = attr_value; } else { g_free (attr_value); } } priv->depth++; } static void handle_regular_element ( WockyXmppReader *self, const gchar *localname, const gchar *uri, int nb_attributes, const xmlChar **attributes) { WockyXmppReaderPrivate *priv = self->priv; int i; if (priv->stanza == NULL) { if (uri != NULL) { priv->stanza = wocky_stanza_new (localname, uri); } else { /* This can only happy in non-streaming mode when the top node * of the document doesn't have a namespace. */ DEBUG ("Stanza without a namespace, using default namespace '%s'", priv->default_namespace); priv->stanza = wocky_stanza_new (localname, priv->default_namespace); } priv->node = wocky_stanza_get_top_node (priv->stanza); } else { g_queue_push_tail (priv->nodes, priv->node); priv->node = wocky_node_add_child_ns (priv->node, localname, uri); } for (i = 0; i < nb_attributes * 5; i+=5) { /* attr_name and attr_value are guaranteed non-NULL; attr_prefix and * attr_uri may be NULL. */ const gchar *attr_name = (const gchar *) attributes[i]; const gchar *attr_prefix = (const gchar *) attributes[i+1]; const gchar *attr_uri = (const gchar *) attributes[i+2]; /* Not NULL-terminated! */ const gchar *attr_value = (const gchar *) attributes[i+3]; gsize value_len = attributes[i+4] - attributes[i+3]; if (!wocky_strdiff (attr_prefix, "xml") && !wocky_strdiff (attr_name, "lang")) { wocky_node_set_language_n (priv->node, attr_value, value_len); } else { /* preserve the prefix, if any was received */ if (attr_prefix != NULL) { GQuark ns = g_quark_from_string (attr_uri); wocky_node_attribute_ns_set_prefix (ns, attr_prefix); } wocky_node_set_attribute_n_ns (priv->node, attr_name, attr_value, value_len, attr_uri); } } priv->depth++; } static void _start_element_ns (void *user_data, const xmlChar *localname, const xmlChar *prefix, const xmlChar *ns_uri, int nb_namespaces, const xmlChar **namespaces, int nb_attributes, int nb_defaulted, const xmlChar **attributes) { WockyXmppReader *self = WOCKY_XMPP_READER (user_data); WockyXmppReaderPrivate *priv = self->priv; gchar *uri = NULL; if (ns_uri != NULL) uri = g_strstrip (g_strdup ((const gchar *) ns_uri)); if (priv->stream_mode && G_UNLIKELY (priv->depth == 0)) handle_stream_open (self, (const gchar *) localname, uri, (const gchar *) prefix, nb_attributes, attributes); else handle_regular_element (self, (const gchar *) localname, uri, nb_attributes, attributes); g_free (uri); } static void _characters (void *user_data, const xmlChar *ch, int len) { WockyXmppReader *self = WOCKY_XMPP_READER (user_data); WockyXmppReaderPrivate *priv = self->priv; if (priv->node != NULL) { wocky_node_append_content_n (priv->node, (const gchar *)ch, (gsize)len); } } static void _end_element_ns (void *user_data, const xmlChar *localname, const xmlChar *prefix, const xmlChar *uri) { WockyXmppReader *self = WOCKY_XMPP_READER (user_data); WockyXmppReaderPrivate *priv = self->priv; priv->depth--; if (priv->stream_mode && priv->depth == 0) { DEBUG ("Stream ended"); g_queue_push_tail (priv->stanzas, NULL); } else if (priv->depth == (priv->stream_mode ? 1 : 0)) { g_assert (g_queue_get_length (priv->nodes) == 0); DEBUG_STANZA (priv->stanza, "Received stanza"); g_queue_push_tail (priv->stanzas, priv->stanza); priv->stanza = NULL; priv->node = NULL; } else { priv->node = (WockyNode *) g_queue_pop_tail (priv->nodes); } } static void _error (void *user_data, xmlErrorPtr error) { WockyXmppReader *self = WOCKY_XMPP_READER (user_data); WockyXmppReaderPrivate *priv = self->priv; if (error->level < XML_ERR_FATAL) { DEBUG ("Ignoring parser %s: %s", error->level == XML_ERR_WARNING ? "warning" : "recoverable error", error->message); return; } priv->error = g_error_new_literal (WOCKY_XMPP_READER_ERROR, WOCKY_XMPP_READER_ERROR_PARSE_ERROR, error->message); DEBUG ("Parsing failed %s", error->message); g_queue_push_tail (priv->stanzas, NULL); } /** * wocky_xmpp_reader_get_state: * @reader: a #WockyXmppReader * * Returns: The current state of the reader */ WockyXmppReaderState wocky_xmpp_reader_get_state (WockyXmppReader *reader) { WockyXmppReaderPrivate *priv = reader->priv; return priv->state; } /* When the end of stream is reached the parser puts a NULL entry on the * queue. When that's the only entry left, go into either closed or ready state * (depending if an error was hit) */ static void wocky_xmpp_reader_check_eos (WockyXmppReader *reader) { WockyXmppReaderPrivate *priv = reader->priv; if (!g_queue_is_empty (priv->stanzas) && g_queue_peek_head (priv->stanzas) == NULL) { priv->state = priv->error ? WOCKY_XMPP_READER_STATE_ERROR : WOCKY_XMPP_READER_STATE_CLOSED; } } /** * wocky_xmpp_reader_push: * @reader: a WockyXmppReader * @data: Data to read * @length: Size of @data * * Push an amount of data to parse. */ void wocky_xmpp_reader_push (WockyXmppReader *reader, const guint8 *data, gsize length) { WockyXmppReaderPrivate *priv = reader->priv; xmlParserCtxtPtr parser; g_return_if_fail (priv->state < WOCKY_XMPP_READER_STATE_CLOSED); #ifdef ENABLE_DEBUG wocky_debug (WOCKY_DEBUG_NET, "Parsing chunk: %.*s", (int)length, data); #endif parser = priv->parser; xmlParseChunk (parser, (const char*)data, length, FALSE); wocky_xmpp_reader_check_eos (reader); } /** * wocky_xmpp_reader_peek_stanza: * @reader: a #WockyXmppReader * * Returns the first #WockyStanza available from reader or NULL * if there are no available stanzas. The stanza is not removed from the * readers queue * * Returns: One #WockyStanza or NULL if there are no available stanzas. The * stanza is owned by the #WockyXmppReader */ WockyStanza * wocky_xmpp_reader_peek_stanza (WockyXmppReader *reader) { WockyXmppReaderPrivate *priv = reader->priv; return g_queue_peek_head (priv->stanzas); } /** * wocky_xmpp_reader_pop_stanza: * @reader: a #WockyXmppReader * * Gets one #WockyStanza out of the reader or NULL if there are no * available stanzas. * * Returns: One #WockyStanza or NULL if there are no available stanzas. * Caller owns the returned stanza. */ WockyStanza * wocky_xmpp_reader_pop_stanza (WockyXmppReader *reader) { WockyXmppReaderPrivate *priv = reader->priv; WockyStanza *s; if (g_queue_is_empty (priv->stanzas)) return NULL; s = g_queue_pop_head (priv->stanzas); wocky_xmpp_reader_check_eos (reader); if (!priv->stream_mode) { priv->state = WOCKY_XMPP_READER_STATE_CLOSED; } return s; } /** * wocky_xmpp_reader_get_error: * @reader: a #WockyXmppReader * * Get the error from the reader * * Returns: A copy of the error as encountered by the reader or NULL if there * was no error. Free after use. */ GError * wocky_xmpp_reader_get_error (WockyXmppReader *reader) { WockyXmppReaderPrivate *priv = reader->priv; return priv->error == NULL ? NULL : g_error_copy (priv->error); } /** * wocky_xmpp_reader_reset: * @reader: a #WockyXmppReader * * Reset the xml parser. * */ void wocky_xmpp_reader_reset (WockyXmppReader *reader) { DEBUG ("Resetting the xmpp reader"); wocky_xmpp_reader_clear_parser_state (reader); wocky_init_xml_parser (reader); } telepathy-gabble-0.18.2/lib/ext/wocky/wocky/wocky-xmpp-error.c0000644000175000017500000005202512200204546024263 0ustar00smcvsmcv00000000000000/* * wocky-xmpp-error.c - Source for Wocky's XMPP error handling API * Copyright (C) 2006-2009 Collabora Ltd. * Copyright (C) 2006 Nokia Corporation * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "wocky-xmpp-error.h" #include #include #include "wocky-namespaces.h" #include "wocky-utils.h" /* Definitions of XMPP core stanza errors, as per RFC 3920 §9.3; plus the * corresponding legacy error codes as described by XEP-0086. */ #define MAX_LEGACY_ERRORS 3 typedef struct { const gchar *description; WockyXmppErrorType type; const guint16 legacy_errors[MAX_LEGACY_ERRORS]; } XmppErrorSpec; static const XmppErrorSpec xmpp_errors[NUM_WOCKY_XMPP_ERRORS] = { /* undefined-condition */ { "application-specific condition", WOCKY_XMPP_ERROR_TYPE_CANCEL, { 500, 0, }, }, /* redirect */ { "the recipient or server is redirecting requests for this information " "to another entity", WOCKY_XMPP_ERROR_TYPE_MODIFY, { 302, 0, }, }, /* gone */ { "the recipient or server can no longer be contacted at this address", WOCKY_XMPP_ERROR_TYPE_MODIFY, { 302, 0, }, }, /* bad-request */ { "the sender has sent XML that is malformed or that cannot be processed", WOCKY_XMPP_ERROR_TYPE_MODIFY, { 400, 0, }, }, /* unexpected-request */ { "the recipient or server understood the request but was not expecting " "it at this time", WOCKY_XMPP_ERROR_TYPE_WAIT, { 400, 0, }, }, /* jid-malformed */ { "the sending entity has provided or communicated an XMPP address or " "aspect thereof (e.g., a resource identifier) that does not adhere " "to the syntax defined in Addressing Scheme (Section 3)", WOCKY_XMPP_ERROR_TYPE_MODIFY, { 400, 0, }, }, /* not-authorized */ { "the sender must provide proper credentials before being allowed to " "perform the action, or has provided improper credentials", WOCKY_XMPP_ERROR_TYPE_AUTH, { 401, 0, }, }, /* payment-required */ { "the requesting entity is not authorized to access the requested " "service because payment is required", WOCKY_XMPP_ERROR_TYPE_AUTH, { 402, 0, }, }, /* forbidden */ { "the requesting entity does not possess the required permissions to " "perform the action", WOCKY_XMPP_ERROR_TYPE_AUTH, { 403, 0, }, }, /* item-not-found */ { "the addressed JID or item requested cannot be found", WOCKY_XMPP_ERROR_TYPE_CANCEL, { 404, 0, }, }, /* recipient-unavailable */ { "the intended recipient is temporarily unavailable", WOCKY_XMPP_ERROR_TYPE_WAIT, { 404, 0, }, }, /* remote-server-not-found */ { "a remote server or service specified as part or all of the JID of the " "intended recipient (or required to fulfill a request) could not be " "contacted within a reasonable amount of time", WOCKY_XMPP_ERROR_TYPE_CANCEL, { 404, 0, }, }, /* not-allowed */ { "the recipient or server does not allow any entity to perform the action", WOCKY_XMPP_ERROR_TYPE_CANCEL, { 405, 0, }, }, /* not-acceptable */ { "the recipient or server understands the request but is refusing to " "process it because it does not meet criteria defined by the recipient " "or server (e.g., a local policy regarding acceptable words in messages)", WOCKY_XMPP_ERROR_TYPE_MODIFY, { 406, 0, }, }, /* registration-required */ { "the requesting entity is not authorized to access the requested service " "because registration is required", WOCKY_XMPP_ERROR_TYPE_AUTH, { 407, 0, }, }, /* subscription-required */ { "the requesting entity is not authorized to access the requested service " "because a subscription is required", WOCKY_XMPP_ERROR_TYPE_AUTH, { 407, 0, }, }, /* remote-server-timeout */ { "a remote server or service specified as part or all of the JID of the " "intended recipient (or required to fulfill a request) could not be " "contacted within a reasonable amount of time", WOCKY_XMPP_ERROR_TYPE_WAIT, { 504, 408, 0, }, }, /* conflict */ { "access cannot be granted because an existing resource or session exists " "with the same name or address", WOCKY_XMPP_ERROR_TYPE_CANCEL, { 409, 0, }, }, /* internal-server-error */ { "the server could not process the stanza because of a misconfiguration " "or an otherwise-undefined internal server error", WOCKY_XMPP_ERROR_TYPE_WAIT, { 500, 0, }, }, /* resource-constraint */ { "the server or recipient lacks the system resources necessary to service " "the request", WOCKY_XMPP_ERROR_TYPE_WAIT, { 500, 0, }, }, /* feature-not-implemented */ { "the feature requested is not implemented by the recipient or server and " "therefore cannot be processed", WOCKY_XMPP_ERROR_TYPE_CANCEL, { 501, 0, }, }, /* service-unavailable */ { "the server or recipient does not currently provide the requested " "service", WOCKY_XMPP_ERROR_TYPE_CANCEL, { 503, 502, 510, }, }, /* policy-violation */ { "the entity has violated some local service policy (e.g., a message " "contains words that are prohibited by the service)", /* TODO: should support either MODIFY or WAIT depending on the policy * being violated */ WOCKY_XMPP_ERROR_TYPE_MODIFY, { 406, 0, }, }, }; GQuark wocky_xmpp_error_quark (void) { static GQuark quark = 0; if (quark == 0) quark = g_quark_from_static_string (WOCKY_XMPP_NS_STANZAS); return quark; } /** * wocky_xmpp_error_string: * @error: a core stanza error * * * * Returns: the name of the tag corresponding to @error */ const gchar * wocky_xmpp_error_string (WockyXmppError error) { return wocky_enum_to_nick (WOCKY_TYPE_XMPP_ERROR, error); } /** * wocky_xmpp_error_description: * @error: a core stanza error * * * * Returns: a description of the error, in English, as specified in XMPP Core */ const gchar * wocky_xmpp_error_description (WockyXmppError error) { if (error < NUM_WOCKY_XMPP_ERRORS) return xmpp_errors[error].description; else return NULL; } static GList *error_domains = NULL; /** * wocky_xmpp_error_register_domain * @domain: a description of the error domain * * Registers a new set of application-specific stanza errors. This allows * GErrors in that domain to be passed to wocky_stanza_error_to_node(), and to * be recognized and returned by wocky_xmpp_error_extract() (and * wocky_stanza_extract_errors(), by extension). */ void wocky_xmpp_error_register_domain (WockyXmppErrorDomain *domain) { error_domains = g_list_prepend (error_domains, domain); } static WockyXmppErrorDomain * xmpp_error_find_domain (GQuark domain) { GList *l; for (l = error_domains; l != NULL; l = l->next) { WockyXmppErrorDomain *d = l->data; if (d->domain == domain) return d; } return NULL; } /** * wocky_xmpp_stanza_error_to_string: * @error: an error in the domain %WOCKY_XMPP_ERROR, or another domain * registered with wocky_xmpp_error_register_domain() (such as * %WOCKY_JINGLE_ERROR). * * Returns the name of the XMPP stanza error element represented by @error. * This is intended for use in debugging messages, with %GErrors returned by * wocky_stanza_extract_errors(). * * Returns: the error code as a string, or %NULL if * error->domain is not known to Wocky. */ const gchar * wocky_xmpp_stanza_error_to_string (GError *error) { g_return_val_if_fail (error != NULL, NULL); if (error->domain == WOCKY_XMPP_ERROR) { return wocky_enum_to_nick (WOCKY_TYPE_XMPP_ERROR, error->code); } else { WockyXmppErrorDomain *domain = xmpp_error_find_domain (error->domain); if (domain != NULL) return wocky_enum_to_nick (domain->enum_type, error->code); else return NULL; } } /* Static, but bears documenting. * * xmpp_error_from_node_for_ns: * @node: a node believed to contain an error child * @ns: the namespace for errors corresponding to @enum_type * @enum_type: a GEnum of error codes * @code: location at which to store an error code * * Scans @node's children for nodes in @ns whose name corresponds to a nickname * of a value of @enum_type, storing the value in @code if found. * * Returns: %TRUE if an error code was retrieved. */ static gboolean xmpp_error_from_node_for_ns ( WockyNode *node, GQuark ns, GType enum_type, gint *code) { GSList *l; for (l = node->children; l != NULL; l = l->next) { WockyNode *child = l->data; if (wocky_node_has_ns_q (child, ns) && wocky_enum_from_nick (enum_type, child->name, code)) return TRUE; } return FALSE; } /* Attempts to divine a WockyXmppError from a legacy numeric code='' attribute */ static WockyXmppError xmpp_error_from_code (WockyNode *error_node, WockyXmppErrorType *type) { const gchar *code = wocky_node_get_attribute (error_node, "code"); gint error_code, i, j; if (code == NULL) goto out; error_code = atoi (code); /* skip UNDEFINED_CONDITION, we want code 500 to be translated * to INTERNAL_SERVER_ERROR */ for (i = 1; i < NUM_WOCKY_XMPP_ERRORS; i++) { const XmppErrorSpec *spec = &xmpp_errors[i]; for (j = 0; j < MAX_LEGACY_ERRORS; j++) { gint cur_code = spec->legacy_errors[j]; if (cur_code == 0) break; if (cur_code == error_code) { if (type != NULL) *type = spec->type; return i; } } } out: if (type != NULL) *type = WOCKY_XMPP_ERROR_TYPE_CANCEL; return WOCKY_XMPP_ERROR_UNDEFINED_CONDITION; } /** * wocky_xmpp_error_extract: * @error: the <error/> child of a stanza with type='error' * @type: location at which to store the error type * @core: location at which to store an error in the domain #WOCKY_XMPP_ERROR * @specialized: location at which to store an error in an application-specific * domain, if one is found * @specialized_node: location at which to store the node representing an * application-specific error, if one is found * * Given an <error/> node, breaks it down into values describing the error. * @type and @core are guaranteed to be set; @specialized and @specialized_node * will be set if a recognised application-specific error is found, and the * latter will be set to %NULL if no application-specific error is found. * * Any or all of the out parameters may be %NULL to ignore the value. The * value stored in @specialized_node is borrowed from @stanza, and is only * valid as long as the latter is alive. */ void wocky_xmpp_error_extract (WockyNode *error, WockyXmppErrorType *type, GError **core, GError **specialized, WockyNode **specialized_node) { gboolean found_core_error = FALSE; gint core_code = WOCKY_XMPP_ERROR_UNDEFINED_CONDITION; GQuark specialized_domain = 0; gint specialized_code; gboolean have_specialized = FALSE; WockyNode *specialized_node_tmp = NULL; const gchar *message = NULL; GSList *l; g_return_if_fail (!wocky_strdiff (error->name, "error")); /* The type='' attributes being present and one of the defined five is a * MUST; if the other party is getting XMPP *that* wrong, 'cancel' seems like * a sensible default. (If the other party only uses legacy error codes, the * call to xmpp_error_from_code() below will try to improve on that default.) */ if (type != NULL) { const gchar *type_attr = wocky_node_get_attribute (error, "type"); gint type_i; if (type_attr != NULL && wocky_enum_from_nick (WOCKY_TYPE_XMPP_ERROR_TYPE, type_attr, &type_i)) { *type = type_i; /* Don't let the xmpp_error_from_code() path below clobber the valid * type we found. */ type = NULL; } else { *type = WOCKY_XMPP_ERROR_TYPE_CANCEL; } } for (l = error->children; l != NULL; l = g_slist_next (l)) { WockyNode *child = l->data; if (child->ns == WOCKY_XMPP_ERROR) { if (!wocky_strdiff (child->name, "text")) { message = child->content; } else if (!found_core_error) { /* See if the element is a XMPP Core stanza error we know about, * given that we haven't found one yet. */ found_core_error = wocky_enum_from_nick (WOCKY_TYPE_XMPP_ERROR, child->name, &core_code); } } else if (specialized_node_tmp == NULL) { WockyXmppErrorDomain *domain; specialized_node_tmp = child; /* This could be a specialized error; let's check if it's in a * namespace we know about, and if so that it's an element name we * know. */ domain = xmpp_error_find_domain (child->ns); if (domain != NULL) { specialized_domain = child->ns; if (wocky_enum_from_nick (domain->enum_type, child->name, &specialized_code)) { have_specialized = TRUE; } } } } /* If we don't have an XMPP Core stanza error yet, maybe the peer uses Þe * Olde Numeric Error Codes. */ if (!found_core_error) core_code = xmpp_error_from_code (error, type); /* okay, time to make some errors */ if (message == NULL) message = ""; g_set_error_literal (core, WOCKY_XMPP_ERROR, core_code, message); if (have_specialized) g_set_error_literal (specialized, specialized_domain, specialized_code, message); if (specialized_node != NULL) *specialized_node = specialized_node_tmp; } /** * wocky_g_error_to_node: * @error: an error in the domain #WOCKY_XMPP_ERROR, or in an * application-specific domain registered with * wocky_xmpp_error_register_domain() * @parent_node: the node to which to add an error (such as an IQ error) * * Adds an <error/> node to a stanza corresponding * to the error described by @error. If @error is in a domain other * than #WOCKY_XMPP_ERROR, both the application-specific error name * and the error from #WOCKY_XMPP_ERROR will be created. See RFC 3902 * (XMPP Core) §9.3, “Stanza Errors”. * * There is currently no way to override the type='' of an XMPP Core stanza * error without creating an application-specific error code which does so. * * Returns: the newly-created node */ WockyNode * wocky_stanza_error_to_node (const GError *error, WockyNode *parent_node) { WockyNode *error_node; WockyXmppErrorDomain *domain = NULL; WockyXmppError core_error; const XmppErrorSpec *spec; WockyXmppErrorType type; gchar str[6]; g_return_val_if_fail (parent_node != NULL, NULL); error_node = wocky_node_add_child (parent_node, "error"); g_return_val_if_fail (error != NULL, error_node); if (error->domain == WOCKY_XMPP_ERROR) { core_error = error->code; spec = &(xmpp_errors[core_error]); type = spec->type; } else { WockyXmppErrorSpecialization *s; domain = xmpp_error_find_domain (error->domain); g_return_val_if_fail (domain != NULL, error_node); /* This will crash if you mess up and pass a code that's not in the * domain. */ s = &(domain->codes[error->code]); core_error = s->specializes; spec = &(xmpp_errors[core_error]); if (s->override_type) type = s->type; else type = spec->type; } sprintf (str, "%d", spec->legacy_errors[0]); wocky_node_set_attribute (error_node, "code", str); wocky_node_set_attribute (error_node, "type", wocky_enum_to_nick (WOCKY_TYPE_XMPP_ERROR_TYPE, type)); wocky_node_add_child_ns (error_node, wocky_xmpp_error_string (core_error), WOCKY_XMPP_NS_STANZAS); if (domain != NULL) { const gchar *name = wocky_enum_to_nick (domain->enum_type, error->code); wocky_node_add_child_ns_q (error_node, name, domain->domain); } if (error->message != NULL && *error->message != '\0') wocky_node_add_child_with_content_ns (error_node, "text", error->message, WOCKY_XMPP_NS_STANZAS); return error_node; } /** * wocky_xmpp_stream_error_quark * * Get the error quark used for stream errors * * Returns: the quark for stream errors. */ GQuark wocky_xmpp_stream_error_quark (void) { static GQuark quark = 0; if (quark == 0) quark = g_quark_from_static_string (WOCKY_XMPP_NS_STREAMS); return quark; } /** * wocky_xmpp_stream_error_from_node: * @error: the root node of a #WOCKY_STANZA_TYPE_STREAM_ERROR stanza * * Returns: a GError in the #WOCKY_XMPP_STREAM_ERROR domain. */ GError * wocky_xmpp_stream_error_from_node (WockyNode *error) { gint code = WOCKY_XMPP_STREAM_ERROR_UNKNOWN; const gchar *message = NULL; /* Ignore the return value; we have a default. */ xmpp_error_from_node_for_ns (error, WOCKY_XMPP_STREAM_ERROR, WOCKY_TYPE_XMPP_STREAM_ERROR, &code); message = wocky_node_get_content_from_child_ns (error, "text", WOCKY_XMPP_NS_STREAMS); if (message == NULL) message = ""; return g_error_new_literal (WOCKY_XMPP_STREAM_ERROR, code, message); } /* Built-in specialized error domains */ GQuark wocky_jingle_error_quark (void) { static GQuark quark = 0; if (quark == 0) quark = g_quark_from_static_string (WOCKY_XMPP_NS_JINGLE_ERRORS); return quark; } static WockyXmppErrorDomain * jingle_error_get_domain (void) { static WockyXmppErrorSpecialization codes[] = { /* out-of-order */ { "The request cannot occur at this point in the state machine (e.g., " "session-initiate after session-accept).", WOCKY_XMPP_ERROR_UNEXPECTED_REQUEST, FALSE }, /* tie-break */ { "The request is rejected because it was sent while the initiator was " "awaiting a reply on a similar request.", WOCKY_XMPP_ERROR_CONFLICT, FALSE }, /* unknown-session */ { "The 'sid' attribute specifies a session that is unknown to the " "recipient (e.g., no longer live according to the recipient's state " "machine because the recipient previously terminated the session).", WOCKY_XMPP_ERROR_ITEM_NOT_FOUND, FALSE }, /* unsupported-info */ { "The recipient does not support the informational payload of a " "session-info action.", WOCKY_XMPP_ERROR_FEATURE_NOT_IMPLEMENTED, FALSE } }; static WockyXmppErrorDomain jingle_errors = { 0, }; if (G_UNLIKELY (jingle_errors.domain == 0)) { jingle_errors.domain = WOCKY_JINGLE_ERROR; jingle_errors.enum_type = WOCKY_TYPE_JINGLE_ERROR; jingle_errors.codes = codes; } return &jingle_errors; } GQuark wocky_si_error_quark (void) { static GQuark quark = 0; if (quark == 0) quark = g_quark_from_static_string (WOCKY_XMPP_NS_SI); return quark; } static WockyXmppErrorDomain * si_error_get_domain (void) { static WockyXmppErrorSpecialization codes[] = { /* no-valid-streams */ { "None of the available streams are acceptable.", WOCKY_XMPP_ERROR_BAD_REQUEST, TRUE, WOCKY_XMPP_ERROR_TYPE_CANCEL }, /* bad-profile */ { "The profile is not understood or invalid. The profile MAY supply a " "profile-specific error condition.", WOCKY_XMPP_ERROR_BAD_REQUEST, TRUE, WOCKY_XMPP_ERROR_TYPE_MODIFY } }; static WockyXmppErrorDomain si_errors = { 0, }; if (G_UNLIKELY (si_errors.domain == 0)) { si_errors.domain = WOCKY_SI_ERROR; si_errors.enum_type = WOCKY_TYPE_SI_ERROR; si_errors.codes = codes; } return &si_errors; } void wocky_xmpp_error_init () { if (error_domains == NULL) { /* Register standard domains */ wocky_xmpp_error_register_domain (jingle_error_get_domain ()); wocky_xmpp_error_register_domain (si_error_get_domain ()); } } void wocky_xmpp_error_deinit () { g_list_free (error_domains); error_domains = NULL; } telepathy-gabble-0.18.2/lib/ext/wocky/wocky/wocky-xmpp-connection.c0000644000175000017500000010144012200204546025265 0ustar00smcvsmcv00000000000000/* * wocky-xmpp-connection.c - Source for WockyXmppConnection * Copyright (C) 2006-2009 Collabora Ltd. * @author Sjoerd Simons * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 */ /** * SECTION: wocky-xmpp-connection * @title: WockyXmppConnection * @short_description: Low-level XMPP connection. * * Sends and receives #WockyStanzas from an underlying #GIOStream. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "wocky-xmpp-connection.h" #include #include #include #ifdef HAVE_UNISTD_H # include #endif #include #include "wocky-signals-marshal.h" #include "wocky-xmpp-reader.h" #include "wocky-xmpp-writer.h" #include "wocky-stanza.h" #include "wocky-utils.h" #define BUFFER_SIZE 1024 static void _xmpp_connection_received_data (GObject *source, GAsyncResult *result, gpointer user_data); static void wocky_xmpp_connection_do_write (WockyXmppConnection *self); G_DEFINE_TYPE(WockyXmppConnection, wocky_xmpp_connection, G_TYPE_OBJECT) /* properties */ enum { PROP_BASE_STREAM = 1, }; /* private structure */ struct _WockyXmppConnectionPrivate { gboolean dispose_has_run; WockyXmppReader *reader; WockyXmppWriter *writer; GIOStream *stream; /* received open from the input stream */ gboolean input_open; GSimpleAsyncResult *input_result; GCancellable *input_cancellable; /* sent open to the output stream */ gboolean output_open; /* sent close to the output stream */ gboolean output_closed; GSimpleAsyncResult *output_result; GCancellable *output_cancellable; guint8 input_buffer[BUFFER_SIZE]; const guint8 *output_buffer; gsize offset; gsize length; GSimpleAsyncResult *force_close_result; guint last_id; }; /** * wocky_xmpp_connection_error_quark * * Get the error quark used by the connection. * * Returns: the quark for connection errors. */ GQuark wocky_xmpp_connection_error_quark (void) { static GQuark quark = 0; if (quark == 0) quark = g_quark_from_static_string ("wocky-xmpp-connection-error"); return quark; } static void wocky_xmpp_connection_init (WockyXmppConnection *self) { WockyXmppConnectionPrivate *priv; self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, WOCKY_TYPE_XMPP_CONNECTION, WockyXmppConnectionPrivate); priv = self->priv; priv->writer = wocky_xmpp_writer_new (); priv->reader = wocky_xmpp_reader_new (); } static void wocky_xmpp_connection_dispose (GObject *object); static void wocky_xmpp_connection_finalize (GObject *object); static void wocky_xmpp_connection_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { WockyXmppConnection *connection = WOCKY_XMPP_CONNECTION (object); WockyXmppConnectionPrivate *priv = connection->priv; switch (property_id) { case PROP_BASE_STREAM: g_assert (priv->stream == NULL); priv->stream = g_value_dup_object (value); g_assert (priv->stream != NULL); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void wocky_xmpp_connection_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { WockyXmppConnection *connection = WOCKY_XMPP_CONNECTION (object); WockyXmppConnectionPrivate *priv = connection->priv; switch (property_id) { case PROP_BASE_STREAM: g_value_set_object (value, priv->stream); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void wocky_xmpp_connection_class_init ( WockyXmppConnectionClass *wocky_xmpp_connection_class) { GObjectClass *object_class = G_OBJECT_CLASS (wocky_xmpp_connection_class); GParamSpec *spec; g_type_class_add_private (wocky_xmpp_connection_class, sizeof (WockyXmppConnectionPrivate)); object_class->set_property = wocky_xmpp_connection_set_property; object_class->get_property = wocky_xmpp_connection_get_property; object_class->dispose = wocky_xmpp_connection_dispose; object_class->finalize = wocky_xmpp_connection_finalize; spec = g_param_spec_object ("base-stream", "base stream", "the stream that the XMPP connection communicates over", G_TYPE_IO_STREAM, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, PROP_BASE_STREAM, spec); } void wocky_xmpp_connection_dispose (GObject *object) { WockyXmppConnection *self = WOCKY_XMPP_CONNECTION (object); WockyXmppConnectionPrivate *priv = self->priv; if (priv->dispose_has_run) return; g_warn_if_fail (priv->input_result == NULL); g_warn_if_fail (priv->output_result == NULL); priv->dispose_has_run = TRUE; if (priv->stream != NULL) { g_object_unref (priv->stream); priv->stream = NULL; } if (priv->reader != NULL) { g_object_unref (priv->reader); priv->reader = NULL; } if (priv->writer != NULL) { g_object_unref (priv->writer); priv->writer = NULL; } if (priv->output_result != NULL) { g_object_unref (priv->output_result); priv->output_result = NULL; } if (priv->output_cancellable != NULL) { g_object_unref (priv->output_cancellable); priv->output_cancellable = NULL; } if (priv->input_result != NULL) { g_object_unref (priv->input_result); priv->input_result = NULL; } if (priv->input_cancellable != NULL) { g_object_unref (priv->input_cancellable); priv->input_cancellable = NULL; } /* release any references held by the object here */ if (G_OBJECT_CLASS (wocky_xmpp_connection_parent_class)->dispose) G_OBJECT_CLASS (wocky_xmpp_connection_parent_class)->dispose (object); } void wocky_xmpp_connection_finalize (GObject *object) { G_OBJECT_CLASS (wocky_xmpp_connection_parent_class)->finalize (object); } /** * wocky_xmpp_connection_new: * @stream: GIOStream over wich all the data will be sent/received. * * Convenience function to create a new #WockyXmppConnection. * * Returns: a new #WockyXmppConnection. */ WockyXmppConnection * wocky_xmpp_connection_new (GIOStream *stream) { WockyXmppConnection * result; result = g_object_new (WOCKY_TYPE_XMPP_CONNECTION, "base-stream", stream, NULL); return result; } static void wocky_xmpp_connection_write_cb (GObject *source, GAsyncResult *res, gpointer user_data) { WockyXmppConnection *self = WOCKY_XMPP_CONNECTION (user_data); WockyXmppConnectionPrivate *priv = self->priv; gssize written; GError *error = NULL; written = g_output_stream_write_finish (G_OUTPUT_STREAM (source), res, &error); if (G_UNLIKELY (written < 0)) { g_simple_async_result_set_from_error (priv->output_result, error); g_error_free (error); goto finished; } if (G_UNLIKELY (written == 0)) { g_simple_async_result_set_error (priv->output_result, WOCKY_XMPP_CONNECTION_ERROR, WOCKY_XMPP_CONNECTION_ERROR_EOS, "Connection got disconnected" ); goto finished; } priv->offset += written; if (priv->offset == priv->length) { /* Done ! */ goto finished; } wocky_xmpp_connection_do_write (self); return; finished: { GSimpleAsyncResult *r = priv->output_result; if (priv->output_cancellable != NULL) g_object_unref (priv->output_cancellable); priv->output_cancellable = NULL; priv->output_result = NULL; g_simple_async_result_complete (r); g_object_unref (r); } } static void wocky_xmpp_connection_do_write (WockyXmppConnection *self) { WockyXmppConnectionPrivate *priv = self->priv; GOutputStream *output = g_io_stream_get_output_stream (priv->stream); g_assert (priv->length != priv->offset); g_output_stream_write_async (output, priv->output_buffer + priv->offset, priv->length - priv->offset, G_PRIORITY_DEFAULT, priv->output_cancellable, wocky_xmpp_connection_write_cb, self); } /** * wocky_xmpp_connection_send_open_async: * @connection: a #WockyXmppConnection. * @to: destination in the XMPP opening (can be NULL). * @from: sender in the XMPP opening (can be NULL). * @version: XMPP version sent (can be NULL). * @lang: language sent (can be NULL). * @id: XMPP Stream ID, if any, or NULL * @cancellable: optional GCancellable object, NULL to ignore. * @callback: callback to call when the request is satisfied. * @user_data: the data to pass to callback function. * * Request asynchronous sending of an XMPP stream opening over the stream. When * the operation is finished @callback will be called. You can then call * wocky_xmpp_connection_send_open_finish() to get the result of the operation. * */ void wocky_xmpp_connection_send_open_async (WockyXmppConnection *connection, const gchar *to, const gchar *from, const gchar *version, const gchar *lang, const gchar *id, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { WockyXmppConnectionPrivate *priv = connection->priv; if (G_UNLIKELY (priv->output_result != NULL)) goto pending; if (G_UNLIKELY (priv->output_closed)) goto is_closed; if (G_UNLIKELY (priv->output_open)) goto is_open; g_assert (priv->output_result == NULL); g_assert (priv->output_cancellable == NULL); priv->output_result = g_simple_async_result_new (G_OBJECT (connection), callback, user_data, wocky_xmpp_connection_send_open_async); if (cancellable != NULL) priv->output_cancellable = g_object_ref (cancellable); priv->offset = 0; priv->length = 0; wocky_xmpp_writer_stream_open (priv->writer, to, from, version, lang, id, &priv->output_buffer, &priv->length); wocky_xmpp_connection_do_write (connection); return; pending: g_simple_async_report_error_in_idle (G_OBJECT (connection), callback, user_data, G_IO_ERROR, G_IO_ERROR_PENDING, "Another send operation is pending"); return; is_open: g_simple_async_report_error_in_idle (G_OBJECT (connection), callback, user_data, WOCKY_XMPP_CONNECTION_ERROR, WOCKY_XMPP_CONNECTION_ERROR_IS_OPEN, "Connection is already open"); return; is_closed: g_simple_async_report_error_in_idle (G_OBJECT (connection), callback, user_data, WOCKY_XMPP_CONNECTION_ERROR, WOCKY_XMPP_CONNECTION_ERROR_IS_CLOSED, "Connection is closed for sending"); return; } /** * wocky_xmpp_connection_send_open_finish: * @connection: a #WockyXmppConnection. * @result: a GAsyncResult. * @error: a GError location to store the error occuring, or NULL to ignore. * * Finishes sending a stream opening. * * Returns: TRUE if the opening was succesfully sent, FALSE on error. */ gboolean wocky_xmpp_connection_send_open_finish (WockyXmppConnection *connection, GAsyncResult *result, GError **error) { WockyXmppConnectionPrivate *priv = connection->priv; if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), error)) return FALSE; g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (connection), wocky_xmpp_connection_send_open_async), FALSE); priv->output_open = TRUE; return TRUE; } static void wocky_xmpp_connection_do_read (WockyXmppConnection *self) { WockyXmppConnectionPrivate *priv = self->priv; GInputStream *input = g_io_stream_get_input_stream (priv->stream); g_input_stream_read_async (input, priv->input_buffer, BUFFER_SIZE, G_PRIORITY_DEFAULT, priv->input_cancellable, _xmpp_connection_received_data, self); } static gboolean input_is_closed (WockyXmppConnection *self) { WockyXmppConnectionPrivate *priv = self->priv; return wocky_xmpp_reader_get_state (priv->reader) > WOCKY_XMPP_READER_STATE_OPENED; } static void _xmpp_connection_received_data (GObject *source, GAsyncResult *result, gpointer user_data) { WockyXmppConnection *self = WOCKY_XMPP_CONNECTION (user_data); WockyXmppConnectionPrivate *priv = self->priv; gssize size; GError *error = NULL; size = g_input_stream_read_finish (G_INPUT_STREAM (source), result, &error); if (G_UNLIKELY (size < 0)) { g_simple_async_result_set_from_error (priv->input_result, error); g_error_free (error); goto finished; } if (G_UNLIKELY (size == 0)) { g_simple_async_result_set_error (priv->input_result, WOCKY_XMPP_CONNECTION_ERROR, WOCKY_XMPP_CONNECTION_ERROR_EOS, "Connection got disconnected" ); goto finished; } wocky_xmpp_reader_push (priv->reader, priv->input_buffer, size); if (!priv->input_open && (wocky_xmpp_reader_get_state (priv->reader) == WOCKY_XMPP_READER_STATE_OPENED)) { /* stream was opened, can only be as a result of calling recv_open */ priv->input_open = TRUE; goto finished; } if (wocky_xmpp_reader_peek_stanza (priv->reader) != NULL) goto finished; switch (wocky_xmpp_reader_get_state (priv->reader)) { case WOCKY_XMPP_READER_STATE_CLOSED: case WOCKY_XMPP_READER_STATE_ERROR: goto finished; default: /* Need more data */ break; } wocky_xmpp_connection_do_read (self); return; finished: { GSimpleAsyncResult *r = priv->input_result; if (priv->input_cancellable != NULL) g_object_unref (priv->input_cancellable); priv->input_cancellable = NULL; priv->input_result = NULL; g_simple_async_result_complete (r); g_object_unref (r); } } /** * wocky_xmpp_connection_recv_open_async: * @connection: a #WockyXmppConnection. * @cancellable: optional GCancellable object, NULL to ignore. * @callback: callback to call when the request is satisfied. * @user_data: the data to pass to callback function. * * Request asynchronous receiving of an XMPP stream opening over the stream. * When the operation is finished @callback will be called. You can then call * wocky_xmpp_connection_recv_open_finish() to get the result of the operation. * */ void wocky_xmpp_connection_recv_open_async (WockyXmppConnection *connection, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { WockyXmppConnectionPrivate *priv = connection->priv; if (G_UNLIKELY (priv->input_result != NULL)) goto pending; if (G_UNLIKELY (input_is_closed (connection))) goto is_closed; if (G_UNLIKELY (priv->input_open)) goto is_open; g_assert (priv->input_result == NULL); g_assert (priv->input_cancellable == NULL); priv->input_result = g_simple_async_result_new (G_OBJECT (connection), callback, user_data, wocky_xmpp_connection_recv_open_async); if (cancellable != NULL) priv->input_cancellable = g_object_ref (cancellable); wocky_xmpp_connection_do_read (connection); return; pending: g_simple_async_report_error_in_idle (G_OBJECT (connection), callback, user_data, G_IO_ERROR, G_IO_ERROR_PENDING, "Another receive operation is pending"); return; is_closed: g_simple_async_report_error_in_idle (G_OBJECT (connection), callback, user_data, WOCKY_XMPP_CONNECTION_ERROR, WOCKY_XMPP_CONNECTION_ERROR_IS_CLOSED, "Connection is closed for receiving"); return; is_open: g_simple_async_report_error_in_idle (G_OBJECT (connection), callback, user_data, WOCKY_XMPP_CONNECTION_ERROR, WOCKY_XMPP_CONNECTION_ERROR_IS_OPEN, "Connection has already received open"); return; } /** * wocky_xmpp_connection_recv_open_finish: * @connection: a #WockyXmppConnection. * @result: a GAsyncResult. * @to: Optional location to store the to attribute in the XMPP open stanza * will be stored (free after usage). * @from: Optional location to store the from attribute in the XMPP open stanza * will be stored (free after usage). * @version: Optional location to store the version attribute in the XMPP open * stanza will be stored (free after usage). * @lang: Optional location to store the lang attribute in the XMPP open * stanza will be stored (free after usage). * @id: Optional location to store the Session ID of the XMPP stream * (free after usage) * @error: a GError location to store the error occuring, or NULL to ignore. * * Finishes receiving a stream opening. * * Returns: TRUE if the opening was succesfully received, FALSE on error. */ gboolean wocky_xmpp_connection_recv_open_finish (WockyXmppConnection *connection, GAsyncResult *result, gchar **to, gchar **from, gchar **version, gchar **lang, gchar **id, GError **error) { WockyXmppConnectionPrivate *priv; if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), error)) return FALSE; g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (connection), wocky_xmpp_connection_recv_open_async), FALSE); priv = connection->priv; priv->input_open = TRUE; if (to != NULL) g_object_get (priv->reader, "to", to, NULL); if (from != NULL) g_object_get (priv->reader, "from", from, NULL); if (version != NULL) g_object_get (priv->reader, "version", version, NULL); if (lang != NULL) g_object_get (priv->reader, "lang", lang, NULL); if (id != NULL) g_object_get (priv->reader, "id", id, NULL); return TRUE; } /** * wocky_xmpp_connection_send_stanza_async: * @connection: a #WockyXmppConnection * @stanza: #WockyStanza to send. * @cancellable: optional GCancellable object, NULL to ignore. * @callback: callback to call when the request is satisfied. * @user_data: the data to pass to callback function. * * Request asynchronous sending of a #WockyStanza. When the operation is * finished @callback will be called. You can then call * wocky_xmpp_connection_send_stanza_finish() to get the result of * the operation. * * Can only be called after wocky_xmpp_connection_send_open_async has finished * its operation. * */ void wocky_xmpp_connection_send_stanza_async (WockyXmppConnection *connection, WockyStanza *stanza, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { WockyXmppConnectionPrivate *priv = connection->priv; if (G_UNLIKELY (priv->output_result != NULL)) goto pending; if (G_UNLIKELY (!priv->output_open)) goto not_open; if (G_UNLIKELY (priv->output_closed)) goto is_closed; g_assert (!priv->output_closed); g_assert (priv->output_result == NULL); g_assert (priv->output_cancellable == NULL); priv->output_result = g_simple_async_result_new (G_OBJECT (connection), callback, user_data, wocky_xmpp_connection_send_stanza_async); if (cancellable != NULL) priv->output_cancellable = g_object_ref (cancellable); priv->offset = 0; priv->length = 0; wocky_xmpp_writer_write_stanza (priv->writer, stanza, &priv->output_buffer, &priv->length); wocky_xmpp_connection_do_write (connection); return; pending: g_simple_async_report_error_in_idle (G_OBJECT (connection), callback, user_data, G_IO_ERROR, G_IO_ERROR_PENDING, "Another send operation is pending"); return; not_open: g_simple_async_report_error_in_idle (G_OBJECT (connection), callback, user_data, WOCKY_XMPP_CONNECTION_ERROR, WOCKY_XMPP_CONNECTION_ERROR_NOT_OPEN, "Connections hasn't been opened for sending"); return; is_closed: g_simple_async_report_error_in_idle (G_OBJECT (connection), callback, user_data, WOCKY_XMPP_CONNECTION_ERROR, WOCKY_XMPP_CONNECTION_ERROR_IS_CLOSED, "Connections has been closed for sending"); return; } /** * wocky_xmpp_connection_send_stanza_finish: * @connection: a #WockyXmppConnection. * @result: a GAsyncResult. * @error: a GError location to store the error occuring, or NULL to ignore. * * Finishes sending a stanza. * * Returns: TRUE if the stanza was succesfully sent, FALSE on error. */ gboolean wocky_xmpp_connection_send_stanza_finish ( WockyXmppConnection *connection, GAsyncResult *result, GError **error) { if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), error)) return FALSE; g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (connection), wocky_xmpp_connection_send_stanza_async), FALSE); return TRUE; } /** * wocky_xmpp_connection_recv_stanza_async: * @connection: a #WockyXmppConnection * @cancellable: optional GCancellable object, NULL to ignore. * @callback: callback to call when the request is satisfied. * @user_data: the data to pass to callback function. * * Asynchronous receive a #WockyStanza. When the operation is * finished @callback will be called. You can then call * wocky_xmpp_connection_recv_stanza_finish() to get the result of * the operation. * * Can only be called after wocky_xmpp_connection_recv_open_async has finished * its operation. */ void wocky_xmpp_connection_recv_stanza_async (WockyXmppConnection *connection, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { WockyXmppConnectionPrivate *priv = connection->priv; if (G_UNLIKELY (priv->input_result != NULL)) goto pending; if (G_UNLIKELY (!priv->input_open)) goto not_open; if (G_UNLIKELY (input_is_closed (connection))) goto is_closed; g_assert (priv->input_result == NULL); g_assert (priv->input_cancellable == NULL); priv->input_result = g_simple_async_result_new (G_OBJECT (connection), callback, user_data, wocky_xmpp_connection_recv_stanza_async); /* There is already a stanza waiting, no need to read */ if (wocky_xmpp_reader_peek_stanza (priv->reader) != NULL) { GSimpleAsyncResult *r = priv->input_result; priv->input_result = NULL; g_simple_async_result_complete_in_idle (r); g_object_unref (r); return; } if (cancellable != NULL) priv->input_cancellable = g_object_ref (cancellable); wocky_xmpp_connection_do_read (connection); return; pending: g_simple_async_report_error_in_idle (G_OBJECT (connection), callback, user_data, G_IO_ERROR, G_IO_ERROR_PENDING, "Another receive operation is pending"); return; not_open: g_simple_async_report_error_in_idle (G_OBJECT (connection), callback, user_data, WOCKY_XMPP_CONNECTION_ERROR, WOCKY_XMPP_CONNECTION_ERROR_NOT_OPEN, "Connection hasn't been opened for reading stanzas"); return; is_closed: g_simple_async_report_error_in_idle (G_OBJECT (connection), callback, user_data, WOCKY_XMPP_CONNECTION_ERROR, WOCKY_XMPP_CONNECTION_ERROR_IS_CLOSED, "Connection has been closed for reading stanzas"); return; } /** * wocky_xmpp_connection_recv_stanza_finish: * @connection: a #WockyXmppConnection. * @result: a GAsyncResult. * @error: a GError location to store the error occuring, or NULL to ignore. * * Finishes receiving a stanza * * Returns: A #WockyStanza or NULL on error (unref after usage) */ WockyStanza * wocky_xmpp_connection_recv_stanza_finish (WockyXmppConnection *connection, GAsyncResult *result, GError **error) { WockyXmppConnectionPrivate *priv; WockyStanza *stanza = NULL; if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), error)) return NULL; g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (connection), wocky_xmpp_connection_recv_stanza_async), NULL); priv = connection->priv; switch (wocky_xmpp_reader_get_state (priv->reader)) { case WOCKY_XMPP_READER_STATE_INITIAL: g_assert_not_reached (); break; case WOCKY_XMPP_READER_STATE_OPENED: stanza = wocky_xmpp_reader_pop_stanza (priv->reader); break; case WOCKY_XMPP_READER_STATE_CLOSED: g_set_error_literal (error, WOCKY_XMPP_CONNECTION_ERROR, WOCKY_XMPP_CONNECTION_ERROR_CLOSED, "Stream closed"); break; case WOCKY_XMPP_READER_STATE_ERROR: { GError *e /* default coding style checker */; e = wocky_xmpp_reader_get_error (priv->reader); g_assert (e != NULL); g_propagate_error (error, e); break; } } return stanza; } /** * wocky_xmpp_connection_send_close_async: * @connection: a #WockyXmppConnection. * @cancellable: optional GCancellable object, NULL to ignore. * @callback: callback to call when the request is satisfied. * @user_data: the data to pass to callback function. * * Request asynchronous sending of an XMPP stream close. When * the operation is finished @callback will be called. You can then call * wocky_xmpp_connection_send_close_finish() to get the result of the * operation. * * Can only be called after wocky_xmpp_connection_send_open_async has finished * its operation. * */ void wocky_xmpp_connection_send_close_async (WockyXmppConnection *connection, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { WockyXmppConnectionPrivate *priv = connection->priv; if (G_UNLIKELY (priv->output_result != NULL)) goto pending; if (G_UNLIKELY (priv->output_closed)) goto is_closed; if (G_UNLIKELY (!priv->output_open)) goto not_open; g_assert (priv->output_result == NULL); g_assert (priv->output_cancellable == NULL); priv->output_result = g_simple_async_result_new (G_OBJECT (connection), callback, user_data, wocky_xmpp_connection_send_close_async); if (cancellable != NULL) priv->output_cancellable = g_object_ref (cancellable); priv->offset = 0; priv->length = 0; wocky_xmpp_writer_stream_close (priv->writer, &priv->output_buffer, &priv->length); wocky_xmpp_connection_do_write (connection); return; pending: g_simple_async_report_error_in_idle (G_OBJECT (connection), callback, user_data, G_IO_ERROR, G_IO_ERROR_PENDING, "Another send operation is pending"); return; not_open: g_simple_async_report_error_in_idle (G_OBJECT (connection), callback, user_data, WOCKY_XMPP_CONNECTION_ERROR, WOCKY_XMPP_CONNECTION_ERROR_NOT_OPEN, "Connections hasn't been opened for sending"); return; is_closed: g_simple_async_report_error_in_idle (G_OBJECT (connection), callback, user_data, WOCKY_XMPP_CONNECTION_ERROR, WOCKY_XMPP_CONNECTION_ERROR_IS_CLOSED, "Connections has been closed sending"); return; } /** * wocky_xmpp_connection_send_close_finish: * @connection: a #WockyXmppConnection. * @result: a GAsyncResult. * @error: a GError location to store the error occuring, or NULL to ignore. * * Finishes send the xmpp stream close. * * Returns: TRUE on success or FALSE on error. */ gboolean wocky_xmpp_connection_send_close_finish (WockyXmppConnection *connection, GAsyncResult *result, GError **error) { WockyXmppConnectionPrivate *priv = connection->priv; if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), error)) return FALSE; g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (connection), wocky_xmpp_connection_send_close_async), FALSE); priv->output_closed = TRUE; return TRUE; } /** * wocky_xmpp_connection_send_whitespace_ping_async: * @connection: a #WockyXmppConnection * @cancellable: optional GCancellable object, NULL to ignore. * @callback: callback to call when the request is satisfied. * @user_data: the data to pass to callback function. * * Request asynchronous sending of a whitespace ping. When the operation is * finished @callback will be called. You can then call * wocky_xmpp_connection_send_whitespace_ping_finish() to get the result of * the operation. * * Can only be called after wocky_xmpp_connection_send_open_async has finished * its operation. */ void wocky_xmpp_connection_send_whitespace_ping_async (WockyXmppConnection *connection, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { WockyXmppConnectionPrivate *priv = connection->priv; if (G_UNLIKELY (priv->output_result != NULL)) goto pending; if (G_UNLIKELY (!priv->output_open)) goto not_open; if (G_UNLIKELY (priv->output_closed)) goto is_closed; g_assert (!priv->output_closed); g_assert (priv->output_result == NULL); g_assert (priv->output_cancellable == NULL); priv->output_result = g_simple_async_result_new (G_OBJECT (connection), callback, user_data, wocky_xmpp_connection_send_whitespace_ping_async); if (cancellable != NULL) priv->output_cancellable = g_object_ref (cancellable); priv->output_buffer = (guint8 *) " "; priv->length = 1; priv->offset = 0; wocky_xmpp_connection_do_write (connection); return; pending: g_simple_async_report_error_in_idle (G_OBJECT (connection), callback, user_data, G_IO_ERROR, G_IO_ERROR_PENDING, "Another send operation is pending"); return; not_open: g_simple_async_report_error_in_idle (G_OBJECT (connection), callback, user_data, WOCKY_XMPP_CONNECTION_ERROR, WOCKY_XMPP_CONNECTION_ERROR_NOT_OPEN, "Connections hasn't been opened for sending"); return; is_closed: g_simple_async_report_error_in_idle (G_OBJECT (connection), callback, user_data, WOCKY_XMPP_CONNECTION_ERROR, WOCKY_XMPP_CONNECTION_ERROR_IS_CLOSED, "Connections has been closed for sending"); return; } /** * wocky_xmpp_connection_send_whitespace_ping_finish: * @connection: a #WockyXmppConnection. * @result: a GAsyncResult. * @error: a GError location to store the error occuring, or NULL to ignore. * * Finishes sending a whitespace ping. * * Returns: TRUE if the ping was succesfully sent, FALSE on error. */ gboolean wocky_xmpp_connection_send_whitespace_ping_finish ( WockyXmppConnection *connection, GAsyncResult *result, GError **error) { wocky_implement_finish_void (connection, wocky_xmpp_connection_send_whitespace_ping_async); } /** * wocky_xmpp_connection_reset: * @connection: a #WockyXmppConnection. * * Reset the XMPP Connection. After the reset the connection is back in its * initial state (as if wocky_xmpp_connection_send_open_async() and * wocky_xmpp_connection_recv_open_async() were never called). */ void wocky_xmpp_connection_reset (WockyXmppConnection *connection) { WockyXmppConnectionPrivate *priv = connection->priv; /* There can't be any pending operations */ g_assert (priv->input_result == NULL); g_assert (priv->output_result == NULL); priv->input_open = FALSE; priv->output_open = FALSE; priv->output_closed = FALSE; wocky_xmpp_reader_reset (priv->reader); } /** * wocky_xmpp_connection_new_id: * @self: a #WockyXmppConnection. * * Returns: A short unique string for usage as the id attribute on a stanza * (free after usage). */ gchar * wocky_xmpp_connection_new_id (WockyXmppConnection *self) { WockyXmppConnectionPrivate *priv = self->priv; GTimeVal tv; glong val; g_get_current_time (&tv); val = (tv.tv_sec & tv.tv_usec) + priv->last_id++; return g_strdup_printf ("%ld%ld", val, tv.tv_usec); } static void stream_close_cb (GObject *source, GAsyncResult *res, gpointer user_data) { WockyXmppConnection *connection = WOCKY_XMPP_CONNECTION (user_data); WockyXmppConnectionPrivate *priv = connection->priv; GError *error = NULL; GSimpleAsyncResult *r = priv->force_close_result; if (!g_io_stream_close_finish (G_IO_STREAM (source), res, &error)) { g_simple_async_result_set_from_error (priv->force_close_result, error); g_error_free (error); } priv->force_close_result = NULL; g_simple_async_result_complete (r); g_object_unref (r); } void wocky_xmpp_connection_force_close_async (WockyXmppConnection *connection, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { WockyXmppConnectionPrivate *priv = connection->priv; if (G_UNLIKELY (priv->force_close_result != NULL)) { g_simple_async_report_error_in_idle (G_OBJECT (connection), callback, user_data, G_IO_ERROR, G_IO_ERROR_PENDING, "Another close operation is pending"); return; } priv->force_close_result = g_simple_async_result_new (G_OBJECT (connection), callback, user_data, wocky_xmpp_connection_force_close_async); g_io_stream_close_async (priv->stream, G_PRIORITY_HIGH, cancellable, stream_close_cb, connection); } gboolean wocky_xmpp_connection_force_close_finish ( WockyXmppConnection *connection, GAsyncResult *result, GError **error) { if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), error)) return FALSE; g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (connection), wocky_xmpp_connection_force_close_async), FALSE); return TRUE; } telepathy-gabble-0.18.2/lib/ext/wocky/wocky/wocky-xep-0115-capabilities.c0000644000175000017500000000434412200204546025760 0ustar00smcvsmcv00000000000000/* * wocky-xep-0115-capabilities.c - interface for holding capabilities * of contacts * * Copyright (C) 2011-2012 Collabora Ltd. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 "config.h" #include "wocky-xep-0115-capabilities.h" #include "wocky-contact.h" G_DEFINE_INTERFACE (WockyXep0115Capabilities, wocky_xep_0115_capabilities, G_TYPE_OBJECT); static void wocky_xep_0115_capabilities_default_init ( WockyXep0115CapabilitiesInterface *interface) { GType iface_type = G_TYPE_FROM_INTERFACE (interface); static gsize initialization_value = 0; if (g_once_init_enter (&initialization_value)) { g_signal_new ("capabilities-changed", iface_type, G_SIGNAL_RUN_LAST, 0, NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); g_once_init_leave (&initialization_value, 1); } } const GPtrArray * wocky_xep_0115_capabilities_get_data_forms ( WockyXep0115Capabilities *contact) { WockyXep0115CapabilitiesInterface *iface = WOCKY_XEP_0115_CAPABILITIES_GET_INTERFACE (contact); WockyXep0115CapabilitiesGetDataFormsFunc method = iface->get_data_forms; if (method != NULL) return method (contact); return NULL; } gboolean wocky_xep_0115_capabilities_has_feature ( WockyXep0115Capabilities *contact, const gchar *feature) { WockyXep0115CapabilitiesInterface *iface = WOCKY_XEP_0115_CAPABILITIES_GET_INTERFACE (contact); WockyXep0115CapabilitiesHasFeatureFunc method = iface->has_feature; if (method != NULL) return method (contact, feature); return FALSE; } telepathy-gabble-0.18.2/lib/ext/wocky/wocky/wocky-tls-connector.c0000644000175000017500000003134712200204546024746 0ustar00smcvsmcv00000000000000/* * wocky-tls-connector.h - Header for WockyTLSConnector * Copyright (C) 2010 Collabora Ltd. * @author Cosimo Cecchi * @author Vivek Dasmohapatra * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 #include #include "wocky-tls-connector.h" #define WOCKY_DEBUG_FLAG WOCKY_DEBUG_TLS #include "wocky-debug-internal.h" #include "wocky-namespaces.h" #include "wocky-connector.h" #include "wocky-tls.h" #include "wocky-tls-handler.h" #include "wocky-utils.h" #include "wocky-xmpp-connection.h" struct _WockyTLSConnectorPrivate { gboolean legacy_ssl; gchar *peername; GStrv extra_identities; WockyTLSHandler *handler; WockyTLSSession *session; WockyXmppConnection *connection; WockyXmppConnection *tls_connection; GSimpleAsyncResult *secure_result; GCancellable *cancellable; }; enum { PROP_HANDLER = 1, LAST_PROPERTY, }; static void session_handshake_cb (GObject *source, GAsyncResult *res, gpointer user_data); G_DEFINE_TYPE (WockyTLSConnector, wocky_tls_connector, G_TYPE_OBJECT); static void wocky_tls_connector_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { WockyTLSConnector *self = WOCKY_TLS_CONNECTOR (object); switch (property_id) { case PROP_HANDLER: g_value_set_object (value, self->priv->handler); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void wocky_tls_connector_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { WockyTLSConnector *self = WOCKY_TLS_CONNECTOR (object); switch (property_id) { case PROP_HANDLER: if (g_value_get_object (value) == NULL) self->priv->handler = wocky_tls_handler_new (FALSE); else self->priv->handler = g_value_dup_object (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void wocky_tls_connector_finalize (GObject *object) { WockyTLSConnector *self = WOCKY_TLS_CONNECTOR (object); g_free (self->priv->peername); g_strfreev (self->priv->extra_identities); if (self->priv->session != NULL) { g_object_unref (self->priv->session); self->priv->session = NULL; } if (self->priv->handler != NULL) { g_object_unref (self->priv->handler); self->priv->handler = NULL; } if (self->priv->tls_connection != NULL) { g_object_unref (self->priv->tls_connection); self->priv->tls_connection = NULL; } G_OBJECT_CLASS (wocky_tls_connector_parent_class)->finalize (object); } static void wocky_tls_connector_class_init (WockyTLSConnectorClass *klass) { GObjectClass *oclass = G_OBJECT_CLASS (klass); GParamSpec *pspec; g_type_class_add_private (klass, sizeof (WockyTLSConnectorPrivate)); oclass->get_property = wocky_tls_connector_get_property; oclass->set_property = wocky_tls_connector_set_property; oclass->finalize = wocky_tls_connector_finalize; /** * WockyTLSConnector:tls-handler: * * The #WockyTLSHandler object used for the TLS handshake. */ pspec = g_param_spec_object ("tls-handler", "TLS Handler", "Handler for the TLS handshake", WOCKY_TYPE_TLS_HANDLER, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); g_object_class_install_property (oclass, PROP_HANDLER, pspec); } static void wocky_tls_connector_init (WockyTLSConnector *self) { self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, WOCKY_TYPE_TLS_CONNECTOR, WockyTLSConnectorPrivate); self->priv->secure_result = NULL; } static void add_ca (gpointer data, gpointer user_data) { WockyTLSSession *session = user_data; const gchar *path = data; wocky_tls_session_add_ca (session, path); } static void add_crl (gpointer data, gpointer user_data) { WockyTLSSession *session = user_data; const gchar *path = data; wocky_tls_session_add_crl (session, path); } static void prepare_session (WockyTLSConnector *self) { GSList *cas; GSList *crl; cas = wocky_tls_handler_get_cas (self->priv->handler); crl = wocky_tls_handler_get_crl (self->priv->handler); g_slist_foreach (cas, add_ca, self->priv->session); g_slist_foreach (crl, add_crl, self->priv->session); } static void report_error_in_idle (WockyTLSConnector *self, gint error_code, const gchar *format, ...) { GError *error = NULL; va_list args; va_start (args, format); error = g_error_new_valist (WOCKY_CONNECTOR_ERROR, error_code, format, args); va_end (args); DEBUG ("%s", error->message); g_simple_async_result_set_from_error (self->priv->secure_result, error); g_error_free (error); g_simple_async_result_complete_in_idle (self->priv->secure_result); g_object_unref (self->priv->secure_result); if (self->priv->cancellable != NULL) { g_object_unref (self->priv->cancellable); self->priv->cancellable = NULL; } } static void report_error_in_idle_gerror (WockyTLSConnector *self, const GError *error) { DEBUG ("Reporting error %s", error->message); g_simple_async_result_set_from_error (self->priv->secure_result, error); g_simple_async_result_complete_in_idle (self->priv->secure_result); g_object_unref (self->priv->secure_result); if (self->priv->cancellable != NULL) { g_object_unref (self->priv->cancellable); self->priv->cancellable = NULL; } } static void do_handshake (WockyTLSConnector *self) { GIOStream *base_stream = NULL; g_object_get (self->priv->connection, "base-stream", &base_stream, NULL); g_assert (base_stream != NULL); self->priv->session = wocky_tls_session_new (base_stream); g_object_unref (base_stream); if (self->priv->session == NULL) { report_error_in_idle (self, WOCKY_CONNECTOR_ERROR_TLS_SESSION_FAILED, "%s", "SSL session failed"); return; } prepare_session (self); wocky_tls_session_handshake_async (self->priv->session, G_PRIORITY_DEFAULT, self->priv->cancellable, session_handshake_cb, self); } static void tls_handler_verify_async_cb (GObject *source, GAsyncResult *res, gpointer user_data) { WockyTLSConnector *self = user_data; WockyTLSHandler *handler = WOCKY_TLS_HANDLER (source); GError *error = NULL; wocky_tls_handler_verify_finish (handler, res, &error); if (error != NULL) { /* forward the GError as we got it in this case */ report_error_in_idle_gerror (self, error); g_error_free (error); return; } g_simple_async_result_set_op_res_gpointer (self->priv->secure_result, self->priv->tls_connection, (GDestroyNotify) g_object_unref); self->priv->tls_connection = NULL; g_simple_async_result_complete_in_idle (self->priv->secure_result); g_object_unref (self->priv->secure_result); if (self->priv->cancellable != NULL) { g_object_unref (self->priv->cancellable); self->priv->cancellable = NULL; } } static void session_handshake_cb (GObject *source, GAsyncResult *res, gpointer user_data) { GError *error = NULL; WockyTLSConnection *tls_conn; WockyTLSConnector *self = user_data; const gchar *tls_type; tls_type = self->priv->legacy_ssl ? "SSL" : "TLS"; tls_conn = wocky_tls_session_handshake_finish (self->priv->session, res, &error); if (tls_conn == NULL) { report_error_in_idle (self, WOCKY_CONNECTOR_ERROR_TLS_SESSION_FAILED, "%s handshake error: %s", tls_type, error->message); g_error_free (error); return; } DEBUG ("Completed %s handshake", tls_type); self->priv->tls_connection = wocky_xmpp_connection_new ( G_IO_STREAM (tls_conn)); g_object_unref (tls_conn); wocky_tls_handler_verify_async (self->priv->handler, self->priv->session, self->priv->peername, self->priv->extra_identities, tls_handler_verify_async_cb, self); } static void starttls_recv_cb (GObject *source, GAsyncResult *result, gpointer user_data) { WockyTLSConnector *self = user_data; WockyStanza *stanza; GError *error = NULL; WockyNode *node; stanza = wocky_xmpp_connection_recv_stanza_finish ( WOCKY_XMPP_CONNECTION (self->priv->connection), result, &error); if (stanza == NULL) { report_error_in_idle (self, WOCKY_CONNECTOR_ERROR_TLS_SESSION_FAILED, "STARTTLS reply not received: %s", error->message); g_error_free (error); goto out; } if (wocky_stanza_extract_stream_error (stanza, &error)) { /* forward the GError as we got it in this case */ report_error_in_idle_gerror (self, error); g_error_free (error); goto out; } DEBUG ("Received STARTTLS response"); node = wocky_stanza_get_top_node (stanza); if (!wocky_node_matches (node, "proceed", WOCKY_XMPP_NS_TLS)) { report_error_in_idle (self, WOCKY_CONNECTOR_ERROR_TLS_REFUSED, "%s", "STARTTLS refused by the server"); goto out; } else { GIOStream *base_stream = NULL; g_object_get (self->priv->connection, "base-stream", &base_stream, NULL); g_assert (base_stream != NULL); self->priv->session = wocky_tls_session_new (base_stream); g_object_unref (base_stream); if (self->priv->session == NULL) { report_error_in_idle (self, WOCKY_CONNECTOR_ERROR_TLS_SESSION_FAILED, "%s", "Unable to create a TLS session"); goto out; } prepare_session (self); DEBUG ("Starting client TLS handshake %p", self->priv->session); wocky_tls_session_handshake_async (self->priv->session, G_PRIORITY_HIGH, self->priv->cancellable, session_handshake_cb, self); } out: if (stanza != NULL) g_object_unref (stanza); } static void starttls_sent_cb (GObject *source, GAsyncResult *result, gpointer user_data) { WockyTLSConnector *self = user_data; GError *error = NULL; if (!wocky_xmpp_connection_send_stanza_finish ( WOCKY_XMPP_CONNECTION (self->priv->connection), result, &error)) { report_error_in_idle (self, WOCKY_CONNECTOR_ERROR_TLS_SESSION_FAILED, "Failed to send STARTTLS stanza: %s", error->message); g_error_free (error); return; } DEBUG ("Sent STARTTLS stanza"); wocky_xmpp_connection_recv_stanza_async ( WOCKY_XMPP_CONNECTION (self->priv->connection), self->priv->cancellable, starttls_recv_cb, self); } static void do_starttls (WockyTLSConnector *self) { WockyStanza *starttls; starttls = wocky_stanza_new ("starttls", WOCKY_XMPP_NS_TLS); DEBUG ("Sending STARTTLS stanza"); wocky_xmpp_connection_send_stanza_async ( WOCKY_XMPP_CONNECTION (self->priv->connection), starttls, self->priv->cancellable, starttls_sent_cb, self); g_object_unref (starttls); } WockyTLSConnector * wocky_tls_connector_new (WockyTLSHandler *handler) { return g_object_new (WOCKY_TYPE_TLS_CONNECTOR, "tls-handler", handler, NULL); } void wocky_tls_connector_secure_async (WockyTLSConnector *self, WockyXmppConnection *connection, gboolean old_style_ssl, const gchar *peername, GStrv extra_identities, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { GSimpleAsyncResult *async_result; g_assert (self->priv->secure_result == NULL); g_assert (self->priv->cancellable == NULL); async_result = g_simple_async_result_new (G_OBJECT (self), callback, user_data, wocky_tls_connector_secure_async); if (cancellable != NULL) self->priv->cancellable = g_object_ref (cancellable); self->priv->connection = connection; self->priv->secure_result = async_result; self->priv->legacy_ssl = old_style_ssl; self->priv->peername = g_strdup (peername); self->priv->extra_identities = g_strdupv (extra_identities); if (old_style_ssl) do_handshake (self); else do_starttls (self); } WockyXmppConnection * wocky_tls_connector_secure_finish (WockyTLSConnector *self, GAsyncResult *result, GError **error) { wocky_implement_finish_return_copy_pointer (self, wocky_tls_connector_secure_async, g_object_ref); } telepathy-gabble-0.18.2/lib/ext/wocky/wocky/wocky-tls-handler.c0000644000175000017500000002704412200204546024370 0ustar00smcvsmcv00000000000000/* * wocky-tls-handler.c - Source for WockyTLSHandler * Copyright (C) 2010 Collabora Ltd. * @author Cosimo Cecchi * @author Vivek Dasmohapatra * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 #include "wocky-tls-handler.h" #include "wocky-utils.h" #define WOCKY_DEBUG_FLAG WOCKY_DEBUG_TLS #include "wocky-debug-internal.h" static void real_verify_async (WockyTLSHandler *self, WockyTLSSession *tls_session, const gchar *peername, GStrv extra_identities, GAsyncReadyCallback callback, gpointer user_data); static gboolean real_verify_finish (WockyTLSHandler *self, GAsyncResult *result, GError **error); G_DEFINE_TYPE (WockyTLSHandler, wocky_tls_handler, G_TYPE_OBJECT) enum { PROP_TLS_INSECURE_OK = 1, }; struct _WockyTLSHandlerPrivate { gboolean ignore_ssl_errors; GSList *cas; GSList *crl; }; static void wocky_tls_handler_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { WockyTLSHandler *self = WOCKY_TLS_HANDLER (object); switch (property_id) { case PROP_TLS_INSECURE_OK: g_value_set_boolean (value, self->priv->ignore_ssl_errors); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void wocky_tls_handler_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { WockyTLSHandler *self = WOCKY_TLS_HANDLER (object); switch (property_id) { case PROP_TLS_INSECURE_OK: self->priv->ignore_ssl_errors = g_value_get_boolean (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void wocky_tls_handler_finalize (GObject *object) { WockyTLSHandler *self = WOCKY_TLS_HANDLER (object); if (self->priv->cas != NULL) { g_slist_foreach (self->priv->cas, (GFunc) g_free, NULL); g_slist_free (self->priv->cas); } if (self->priv->crl != NULL) { g_slist_foreach (self->priv->crl, (GFunc) g_free, NULL); g_slist_free (self->priv->crl); } G_OBJECT_CLASS (wocky_tls_handler_parent_class)->finalize (object); } static void wocky_tls_handler_class_init (WockyTLSHandlerClass *klass) { GObjectClass *oclass = G_OBJECT_CLASS (klass); GParamSpec *pspec; g_type_class_add_private (klass, sizeof (WockyTLSHandlerPrivate)); klass->verify_async_func = real_verify_async; klass->verify_finish_func = real_verify_finish; oclass->get_property = wocky_tls_handler_get_property; oclass->set_property = wocky_tls_handler_set_property; oclass->finalize = wocky_tls_handler_finalize; /** * WockyTLSHandler:ignore-ssl-errors: * * Whether to ignore recoverable SSL errors (certificate * insecurity/expiry etc). */ pspec = g_param_spec_boolean ("ignore-ssl-errors", "ignore-ssl-errors", "Whether recoverable TLS errors should be ignored", FALSE, (G_PARAM_CONSTRUCT | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (oclass, PROP_TLS_INSECURE_OK, pspec); } static void wocky_tls_handler_init (WockyTLSHandler *self) { self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, WOCKY_TYPE_TLS_HANDLER, WockyTLSHandlerPrivate); #ifdef GTLS_SYSTEM_CA_CERTIFICATES wocky_tls_handler_add_ca (self, GTLS_SYSTEM_CA_CERTIFICATES); #endif } static void real_verify_async (WockyTLSHandler *self, WockyTLSSession *tls_session, const gchar *peername, GStrv extra_identities, GAsyncReadyCallback callback, gpointer user_data) { GSimpleAsyncResult *result; glong flags = WOCKY_TLS_VERIFY_NORMAL; WockyTLSCertStatus status = WOCKY_TLS_CERT_UNKNOWN_ERROR; const gchar *verify_peername = NULL; GStrv verify_extra_identities = NULL; result = g_simple_async_result_new (G_OBJECT (self), callback, user_data, wocky_tls_handler_verify_async); /* When ignore_ssl_errors is true, don't check the peername. Otherwise: * - Under legacy SSL, the connect hostname is the preferred peername; * - Under STARTTLS, we check the domain regardless of the connect server. */ if (self->priv->ignore_ssl_errors) { flags = WOCKY_TLS_VERIFY_LENIENT; } else { verify_peername = peername; verify_extra_identities = extra_identities; } DEBUG ("Verifying certificate (peername: %s)", (verify_peername == NULL) ? "-" : verify_peername); wocky_tls_session_verify_peer (tls_session, verify_peername, verify_extra_identities, flags, &status); if (status != WOCKY_TLS_CERT_OK) { gboolean ok_when_lenient = FALSE; const gchar *msg = NULL; switch (status) { case WOCKY_TLS_CERT_NAME_MISMATCH: msg = "SSL Certificate does not match name '%s'"; break; case WOCKY_TLS_CERT_REVOKED: msg = "SSL Certificate for %s has been revoked"; break; case WOCKY_TLS_CERT_SIGNER_UNKNOWN: ok_when_lenient = TRUE; msg = "SSL Certificate for %s is insecure (unknown signer)"; break; case WOCKY_TLS_CERT_SIGNER_UNAUTHORISED: msg = "SSL Certificate for %s is insecure (unauthorised signer)"; break; case WOCKY_TLS_CERT_INSECURE: msg = "SSL Certificate for %s is insecure (weak crypto)"; break; case WOCKY_TLS_CERT_NOT_ACTIVE: msg = "SSL Certificate for %s not active yet"; break; case WOCKY_TLS_CERT_EXPIRED: msg = "SSL Certificate for %s expired"; break; case WOCKY_TLS_CERT_INVALID: msg = "SSL Certificate for %s invalid"; ok_when_lenient = TRUE; break; /* Handle UNKNOWN_ERROR and any other unexpected values equivalently */ case WOCKY_TLS_CERT_UNKNOWN_ERROR: default: msg = "SSL Certificate Verification Error for %s"; } if (!(self->priv->ignore_ssl_errors && ok_when_lenient)) { GError *cert_error = NULL; cert_error = g_error_new (WOCKY_TLS_CERT_ERROR, status, msg, peername); g_simple_async_result_set_from_error (result, cert_error); g_error_free (cert_error); g_simple_async_result_complete_in_idle (result); g_object_unref (result); return; } else { gchar *err; err = g_strdup_printf (msg, peername); DEBUG ("Cert error: '%s', but ignore-ssl-errors is set", err); g_free (err); } } g_simple_async_result_complete_in_idle (result); g_object_unref (result); } static gboolean real_verify_finish (WockyTLSHandler *self, GAsyncResult *result, GError **error) { wocky_implement_finish_void (self, wocky_tls_handler_verify_async); } void wocky_tls_handler_verify_async (WockyTLSHandler *self, WockyTLSSession *session, const gchar *peername, GStrv extra_identities, GAsyncReadyCallback callback, gpointer user_data) { WockyTLSHandlerClass *klass = WOCKY_TLS_HANDLER_GET_CLASS (self); klass->verify_async_func (self, session, peername, extra_identities, callback, user_data); } gboolean wocky_tls_handler_verify_finish (WockyTLSHandler *self, GAsyncResult *res, GError **error) { WockyTLSHandlerClass *klass = WOCKY_TLS_HANDLER_GET_CLASS (self); return klass->verify_finish_func (self, res, error); } WockyTLSHandler * wocky_tls_handler_new (gboolean ignore_ssl_errors) { return g_object_new (WOCKY_TYPE_TLS_HANDLER, "ignore-ssl-errors", ignore_ssl_errors, NULL); } /** * wocky_tls_handler_add_ca: * @self: a #WockyTLSHandler instance * @path: a path to a directory or file containing PEM encoded CA certificates * * Adds a single CA certificate, or directory full of CA certificates, to the * set used to check certificates. By default, Wocky will check the system-wide * certificate directory (as determined at compile time), so you need only add * additional CA paths if you want to trust additional CAs. * * Returns: %TRUE if @path could be resolved to an absolute path. Note that * this does not indicate that there was actually a file or directory there or * that any CAs were actually found. The CAs won't actually be loaded until * just before the TLS session setup is attempted. */ gboolean wocky_tls_handler_add_ca (WockyTLSHandler *self, const gchar *path) { gchar *abspath = wocky_absolutize_path (path); if (abspath != NULL) self->priv->cas = g_slist_prepend (self->priv->cas, abspath); return abspath != NULL; } /** * wocky_tls_handler_forget_cas: * @self: a #WockyTLSHandler instance * * Removes all known locations for CA certificates, including the system-wide * certificate directory and any paths added by previous calls to * wocky_tls_handler_add_ca(). This is only useful if you want Wocky to * distrust your system CAs for some reason. */ void wocky_tls_handler_forget_cas (WockyTLSHandler *self) { g_slist_free_full (self->priv->cas, g_free); self->priv->cas = NULL; } /** * wocky_tls_handler_add_crl: * @self: a #WockyTLSHandler instance * @path: a path to a directory or file containing PEM encoded CRL certificates * * Adds a single certificate revocation list file, or a directory of CRLs, to * the set used to check certificates. Unlike for CA certificates, there is * typically no good default path, so no CRLs are used by default. The path to * use depends on the CRL-management software you use; `dirmngr` * (for example) will cache CRLs in `/var/cache/dirmngr/crls.d`. * * Returns: %TRUE if @path could be resolved to an absolute path. Note that * this does not indicate that there was actually a file or directory there or * that any CRLs were actually found. The CRLs won't actually be loaded until * just before the TLS session setup is attempted. */ gboolean wocky_tls_handler_add_crl (WockyTLSHandler *self, const gchar *path) { gchar *abspath = wocky_absolutize_path (path); if (abspath != NULL) self->priv->crl = g_slist_prepend (self->priv->crl, abspath); return abspath != NULL; } /** * wocky_tls_handler_get_cas: * @self: a #WockyTLSHandler instance * * Gets the CA certificate search path, including any extra paths added with * wocky_tls_handler_add_ca(). * * Returns: (transfer none) (element-type utf8): the paths to search for CA certificates. */ GSList * wocky_tls_handler_get_cas (WockyTLSHandler *self) { g_assert (WOCKY_IS_TLS_HANDLER (self)); return self->priv->cas; } /** * wocky_tls_handler_get_crl: * @self: a #WockyTLSHandler instance * * Gets the CRL search path, consisting of all paths added with * wocky_tls_handler_add_crl(). * * Returns: (transfer none) (element-type utf8): the CRL search path. */ GSList * wocky_tls_handler_get_crl (WockyTLSHandler *self) { g_assert (WOCKY_IS_TLS_HANDLER (self)); return self->priv->crl; } telepathy-gabble-0.18.2/lib/ext/wocky/wocky/wocky-tls-common.c0000644000175000017500000000272112200204546024236 0ustar00smcvsmcv00000000000000/* * Wocky TLS integration - common stuff for GNUTLS and OpenSSL backends * (This would be called wocky-tls.c, but that's the GNUTLS backend for * historical reasons.) * * Copyright © 2008 Christian Kellner, Samuel Cormier-Iijima * Copyright © 2008-2009 Codethink Limited * Copyright © 2009-2012 Collabora Limited * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; either version 2 of the licence or (at * your option) any later version. * * Authors: Vivek Dasmohapatra * Ryan Lortie * Christian Kellner * Samuel Cormier-Iijima * * Based on an unmerged gnio feature. See wocky-tls.c for details. */ #include "config.h" #include "wocky-tls.h" GQuark wocky_tls_cert_error_quark (void) { static GQuark quark = 0; if (quark == 0) quark = g_quark_from_static_string ("wocky-tls-cert-error"); return quark; } GQuark wocky_tls_error_quark (void) { static GQuark quark = 0; if (quark == 0) quark = g_quark_from_static_string ("wocky-tls-error"); return quark; } /* this file is "borrowed" from an unmerged gnio feature: */ /* Local Variables: */ /* c-file-style: "gnu" */ /* End: */ telepathy-gabble-0.18.2/lib/ext/wocky/wocky/wocky-utils.c0000644000175000017500000004476512200204546023324 0ustar00smcvsmcv00000000000000/* * wocky-utils.c - Code for Wocky utility functions * Copyright © 2007–2010 Collabora Ltd. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 * * wocky_g_value_slice_* functions have been copied from telepathy-glib's * util.c file: * Copyright (C) 2006-2007 Collabora Ltd. * Copyright (C) 2006-2007 Nokia Corporation */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "wocky-utils.h" #include #include /** * wocky_strdiff: * @left: The first string to compare (may be NULL) * @right: The second string to compare (may be NULL) * * Return %TRUE if the given strings are different. Unlike #strcmp this * function will handle null pointers, treating them as distinct from any * string. * * Returns: %FALSE if @left and @right are both %NULL, or if * neither is %NULL and both have the same contents; %TRUE otherwise */ gboolean wocky_strdiff (const gchar *left, const gchar *right) { return g_strcmp0 (left, right) != 0; } static gboolean validate_jid_node (const gchar *node) { /* See RFC 3920 §3.3. */ const gchar *c; for (c = node; *c; c++) if (strchr ("\"&'/:<>@", *c)) /* RFC 3920 §A.5 */ return FALSE; return TRUE; } static gboolean validate_jid_domain (const gchar *domain) { /* XXX: This doesn't do proper validation: it checks the character * range for ASCII characters, but lets through any non-ASCII characters. See * the ifdef-d out tests in wocky-jid-validation-test.c for examples of * erroneously accepted JIDs. In theory, we check that the domain is a * well-formed IDN or an IPv4/IPv6 address literal. * * See RFC 3920 §3.2. */ const gchar *c; for (c = domain; *c; c++) { if ((unsigned char) *c >= 0x7F) continue; if (!g_ascii_isalnum (*c) && !strchr (":-.", *c)) return FALSE; } return TRUE; } /** * wocky_decode_jid: * @jid: a JID * @node: (allow-none): address to store the normalised localpart of the JID * @domain: (allow-none): address to store the normalised domainpart of the JID * @resource: address to store the resourcepart of the JID * * If @jid is valid, returns %TRUE and sets the caller's @node, @domain and * @resource pointers. @node and @resource will be set to %NULL if the * respective part is not present in @jid. If @jid is invalid, sets @node, * @domain and @resource to %NULL and returns %FALSE. * * In theory, the returned parts will be normalised as specified in RFC 6122 (XMPP Address * Format); in practice, Wocky does not fully implement the * normalisation and validation algorithms. FIXME: Do nodeprep/resourceprep and * length checking. * * Returns: %TRUE if the JID is valid */ gboolean wocky_decode_jid (const gchar *jid, gchar **node, gchar **domain, gchar **resource) { char *tmp_jid, *tmp_node, *tmp_domain, *tmp_resource; g_assert (jid != NULL); if (node != NULL) *node = NULL; if (domain != NULL) *domain = NULL; if (resource != NULL) *resource = NULL; /* Take a local copy so we don't modify the caller's string. */ tmp_jid = g_strdup (jid); /* If there's a slash in tmp_jid, split it in two and take the second part as * the resource. */ tmp_resource = strchr (tmp_jid, '/'); if (tmp_resource != NULL) { *tmp_resource = '\0'; tmp_resource++; } else { tmp_resource = NULL; } /* If there's an at sign in tmp_jid, split it in two and set tmp_node and * tmp_domain appropriately. Otherwise, tmp_node is NULL and the domain is * the whole string. */ tmp_domain = strchr (tmp_jid, '@'); if (tmp_domain != NULL) { *tmp_domain = '\0'; tmp_domain++; tmp_node = tmp_jid; } else { tmp_domain = tmp_jid; tmp_node = NULL; } /* Domain must be non-empty and not contain invalid characters. If the node * or the resource exist, they must be non-empty and the node must not * contain invalid characters. */ if (*tmp_domain == '\0' || !validate_jid_domain (tmp_domain) || (tmp_node != NULL && (*tmp_node == '\0' || !validate_jid_node (tmp_node))) || (tmp_resource != NULL && *tmp_resource == '\0')) { g_free (tmp_jid); return FALSE; } /* the server must be stored after we find the resource, in case we * truncated a resource from it */ if (domain != NULL) *domain = g_utf8_strdown (tmp_domain, -1); /* store the username if the user provided a pointer */ if (tmp_node != NULL && node != NULL) *node = g_utf8_strdown (tmp_node, -1); /* store the resource if the user provided a pointer */ if (tmp_resource != NULL && resource != NULL) *resource = g_strdup (tmp_resource); /* free our working copy */ g_free (tmp_jid); return TRUE; } /** * wocky_normalise_jid: * @jid: a JID * * Returns: a normalised JID, using the same rules as wocky_decode_jid(), * or %NULL if the JID could not be sensibly decoded. * This value should be freed when you are done with it. */ gchar * wocky_normalise_jid (const gchar *jid) { gchar *node = NULL; gchar *domain = NULL; gchar *resource = NULL; gchar *rval = NULL; if (jid == NULL) return NULL; if (!wocky_decode_jid (jid, &node, &domain, &resource)) return NULL; rval = wocky_compose_jid (node, domain, resource); g_free (node); g_free (domain); g_free (resource); return rval; } static inline gsize strlen0 (const gchar *s) { return (s == NULL ? 0 : strlen (s)); } /** * wocky_compose_jid: * @node: (allow-none): the node part of a JID, possibly empty or %NULL * @domain: the non-%NULL domain part of a JID * @resource: (allow-none): the resource part of a JID, possibly empty or %NULL * * Composes a JID from its parts. If @node is empty or %NULL, the '@' * separator is also omitted; if @resource is empty or %NULL, the '/' separator * is also omitted. @node and @domain are assumed to have already been * normalised. * * Returns: a JID constructed from @node, @domain and @resource */ gchar * wocky_compose_jid (const gchar *node, const gchar *domain, const gchar *resource) { GString *normal = NULL; normal = g_string_sized_new (strlen0 (node) + strlen0 (domain) + strlen0 (resource) + 2); if (node != NULL && *node != '\0') g_string_printf (normal, "%s@%s", node, domain); else g_string_printf (normal, "%s", domain); if (resource != NULL && *resource != '\0' && normal->len > 0) g_string_append_printf (normal, "/%s", resource); return g_string_free (normal, FALSE); } /** * wocky_g_value_slice_new: * @type: The type desired for the new GValue * * Slice-allocate an empty #GValue. wocky_g_value_slice_new_boolean() and similar * functions are likely to be more convenient to use for the types supported. * * Returns: a newly allocated, newly initialized #GValue, to be freed with * wocky_g_value_slice_free() or g_slice_free(). * Since: 0.5.14 */ GValue * wocky_g_value_slice_new (GType type) { GValue *ret = g_slice_new0 (GValue); g_value_init (ret, type); return ret; } /** * wocky_g_value_slice_new_boolean: * @b: a boolean value * * Slice-allocate and initialize a #GValue. This function is convenient to * use when constructing hash tables from string to #GValue, for example. * * Returns: a #GValue of type %G_TYPE_BOOLEAN with value @b, to be freed with * wocky_g_value_slice_free() or g_slice_free() * * Since: 0.7.27 */ GValue * wocky_g_value_slice_new_boolean (gboolean b) { GValue *v = wocky_g_value_slice_new (G_TYPE_BOOLEAN); g_value_set_boolean (v, b); return v; } /** * wocky_g_value_slice_new_int: * @n: an integer * * Slice-allocate and initialize a #GValue. This function is convenient to * use when constructing hash tables from string to #GValue, for example. * * Returns: a #GValue of type %G_TYPE_INT with value @n, to be freed with * wocky_g_value_slice_free() or g_slice_free() * * Since: 0.7.27 */ GValue * wocky_g_value_slice_new_int (gint n) { GValue *v = wocky_g_value_slice_new (G_TYPE_INT); g_value_set_int (v, n); return v; } /** * wocky_g_value_slice_new_int64: * @n: a 64-bit integer * * Slice-allocate and initialize a #GValue. This function is convenient to * use when constructing hash tables from string to #GValue, for example. * * Returns: a #GValue of type %G_TYPE_INT64 with value @n, to be freed with * wocky_g_value_slice_free() or g_slice_free() * * Since: 0.7.27 */ GValue * wocky_g_value_slice_new_int64 (gint64 n) { GValue *v = wocky_g_value_slice_new (G_TYPE_INT64); g_value_set_int64 (v, n); return v; } /** * wocky_g_value_slice_new_uint: * @n: an unsigned integer * * Slice-allocate and initialize a #GValue. This function is convenient to * use when constructing hash tables from string to #GValue, for example. * * Returns: a #GValue of type %G_TYPE_UINT with value @n, to be freed with * wocky_g_value_slice_free() or g_slice_free() * * Since: 0.7.27 */ GValue * wocky_g_value_slice_new_uint (guint n) { GValue *v = wocky_g_value_slice_new (G_TYPE_UINT); g_value_set_uint (v, n); return v; } /** * wocky_g_value_slice_new_uint64: * @n: a 64-bit unsigned integer * * Slice-allocate and initialize a #GValue. This function is convenient to * use when constructing hash tables from string to #GValue, for example. * * Returns: a #GValue of type %G_TYPE_UINT64 with value @n, to be freed with * wocky_g_value_slice_free() or g_slice_free() * * Since: 0.7.27 */ GValue * wocky_g_value_slice_new_uint64 (guint64 n) { GValue *v = wocky_g_value_slice_new (G_TYPE_UINT64); g_value_set_uint64 (v, n); return v; } /** * wocky_g_value_slice_new_double: * @d: a number * * Slice-allocate and initialize a #GValue. This function is convenient to * use when constructing hash tables from string to #GValue, for example. * * Returns: a #GValue of type %G_TYPE_DOUBLE with value @n, to be freed with * wocky_g_value_slice_free() or g_slice_free() * * Since: 0.7.27 */ GValue * wocky_g_value_slice_new_double (double n) { GValue *v = wocky_g_value_slice_new (G_TYPE_DOUBLE); g_value_set_double (v, n); return v; } /** * wocky_g_value_slice_new_string: * @string: a string to be copied into the value * * Slice-allocate and initialize a #GValue. This function is convenient to * use when constructing hash tables from string to #GValue, for example. * * Returns: a #GValue of type %G_TYPE_STRING whose value is a copy of @string, * to be freed with wocky_g_value_slice_free() or g_slice_free() * * Since: 0.7.27 */ GValue * wocky_g_value_slice_new_string (const gchar *string) { GValue *v = wocky_g_value_slice_new (G_TYPE_STRING); g_value_set_string (v, string); return v; } /** * wocky_g_value_slice_new_static_string: * @string: a static string which must remain valid forever, to be pointed to * by the value * * Slice-allocate and initialize a #GValue. This function is convenient to * use when constructing hash tables from string to #GValue, for example. * * Returns: a #GValue of type %G_TYPE_STRING whose value is @string, * to be freed with wocky_g_value_slice_free() or g_slice_free() * * Since: 0.7.27 */ GValue * wocky_g_value_slice_new_static_string (const gchar *string) { GValue *v = wocky_g_value_slice_new (G_TYPE_STRING); g_value_set_static_string (v, string); return v; } /** * wocky_g_value_slice_new_take_string: * @string: a string which will be freed with g_free() by the returned #GValue * (the caller must own it before calling this function, but no longer owns * it after this function returns) * * Slice-allocate and initialize a #GValue. This function is convenient to * use when constructing hash tables from string to #GValue, for example. * * Returns: a #GValue of type %G_TYPE_STRING whose value is @string, * to be freed with wocky_g_value_slice_free() or g_slice_free() * * Since: 0.7.27 */ GValue * wocky_g_value_slice_new_take_string (gchar *string) { GValue *v = wocky_g_value_slice_new (G_TYPE_STRING); g_value_take_string (v, string); return v; } /** * wocky_g_value_slice_new_boxed: * @type: a boxed type * @p: a pointer of type @type, which will be copied * * Slice-allocate and initialize a #GValue. This function is convenient to * use when constructing hash tables from string to #GValue, for example. * * Returns: a #GValue of type @type whose value is a copy of @p, * to be freed with wocky_g_value_slice_free() or g_slice_free() * * Since: 0.7.27 */ GValue * wocky_g_value_slice_new_boxed (GType type, gconstpointer p) { GValue *v; g_return_val_if_fail (G_TYPE_FUNDAMENTAL (type) == G_TYPE_BOXED, NULL); v = wocky_g_value_slice_new (type); g_value_set_boxed (v, p); return v; } /** * wocky_g_value_slice_new_static_boxed: * @type: a boxed type * @p: a pointer of type @type, which must remain valid forever * * Slice-allocate and initialize a #GValue. This function is convenient to * use when constructing hash tables from string to #GValue, for example. * * Returns: a #GValue of type @type whose value is @p, * to be freed with wocky_g_value_slice_free() or g_slice_free() * * Since: 0.7.27 */ GValue * wocky_g_value_slice_new_static_boxed (GType type, gconstpointer p) { GValue *v; g_return_val_if_fail (G_TYPE_FUNDAMENTAL (type) == G_TYPE_BOXED, NULL); v = wocky_g_value_slice_new (type); g_value_set_static_boxed (v, p); return v; } /** * wocky_g_value_slice_new_take_boxed: * @type: a boxed type * @p: a pointer of type @type which will be freed with g_boxed_free() by the * returned #GValue (the caller must own it before calling this function, but * no longer owns it after this function returns) * * Slice-allocate and initialize a #GValue. This function is convenient to * use when constructing hash tables from string to #GValue, for example. * * Returns: a #GValue of type @type whose value is @p, * to be freed with wocky_g_value_slice_free() or g_slice_free() * * Since: 0.7.27 */ GValue * wocky_g_value_slice_new_take_boxed (GType type, gpointer p) { GValue *v; g_return_val_if_fail (G_TYPE_FUNDAMENTAL (type) == G_TYPE_BOXED, NULL); v = wocky_g_value_slice_new (type); g_value_take_boxed (v, p); return v; } /** * wocky_g_value_slice_free: * @value: A GValue which was allocated with the g_slice API * * Unset and free a slice-allocated GValue. * * (GDestroyNotify) wocky_g_value_slice_free can be used * as a destructor for values in a #GHashTable, for example. */ void wocky_g_value_slice_free (GValue *value) { g_value_unset (value); g_slice_free (GValue, value); } /** * wocky_g_value_slice_dup: * @value: A GValue * * * * Returns: a newly allocated copy of @value, to be freed with * wocky_g_value_slice_free() or g_slice_free(). * Since: 0.5.14 */ GValue * wocky_g_value_slice_dup (const GValue *value) { GValue *ret = wocky_g_value_slice_new (G_VALUE_TYPE (value)); g_value_copy (value, ret); return ret; } /** * wocky_enum_from_nick: * @enum_type: the GType of a subtype of GEnum * @nick: a non-%NULL string purporting to be the nickname of a value of * @enum_type * @value: the address at which to store the value of @enum_type corresponding * to @nick if this functions returns %TRUE; if this function returns * %FALSE, this variable will be left untouched. * * * * Returns: %TRUE if @nick is a member of @enum_type, or %FALSE otherwise */ gboolean wocky_enum_from_nick ( GType enum_type, const gchar *nick, gint *value) { GEnumClass *klass = g_type_class_ref (enum_type); GEnumValue *enum_value; g_return_val_if_fail (klass != NULL, FALSE); g_return_val_if_fail (value != NULL, FALSE); enum_value = g_enum_get_value_by_nick (klass, nick); g_type_class_unref (klass); if (enum_value != NULL) { *value = enum_value->value; return TRUE; } else { return FALSE; } } /** * wocky_enum_to_nick: * @enum_type: the GType of a subtype of GEnum * @value: a value of @enum_type * * * * Returns: the nickname of @value, or %NULL if it is not, in fact, a value of * @enum_type */ const gchar * wocky_enum_to_nick ( GType enum_type, gint value) { GEnumClass *klass = g_type_class_ref (enum_type); GEnumValue *enum_value; g_return_val_if_fail (klass != NULL, NULL); enum_value = g_enum_get_value (klass, value); g_type_class_unref (klass); if (enum_value != NULL) return enum_value->value_nick; else return NULL; } /** * wocky_absolutize_path: * @path: an absolute or relative path * * Return an absolute form of @path. This cleans up duplicate slashes, "." or * ".." path segments, etc., and prepends g_get_current_dir() if necessary, but * does not necessarily resolve symlinks. * * Returns: an absolute path which must be freed with g_free(), or possibly * %NULL for invalid filenames */ gchar * wocky_absolutize_path (const gchar *path) { GFile *cwd, *absolute; gchar *cwd_str, *ret; cwd_str = g_get_current_dir (); cwd = g_file_new_for_path (cwd_str); g_free (cwd_str); if (cwd == NULL) return NULL; absolute = g_file_resolve_relative_path (cwd, path); if (absolute == NULL) { g_object_unref (cwd); return NULL; } ret = g_file_get_path (absolute); /* possibly NULL */ g_object_unref (cwd); g_object_unref (absolute); return ret; } GList * wocky_list_deep_copy (GBoxedCopyFunc copy, GList *items) { GList *ret = NULL; GList *l; g_return_val_if_fail (copy != NULL, NULL); for (l = items; l != NULL; l = l->next) ret = g_list_prepend (ret, copy (l->data)); return g_list_reverse (ret); } GString * wocky_g_string_dup (const GString *str) { if (str == NULL) return NULL; return g_string_new_len (str->str, str->len); } void wocky_g_string_free (GString *str) { if (str != NULL) g_string_free (str, TRUE); } telepathy-gabble-0.18.2/lib/ext/wocky/wocky/wocky-stanza.c0000644000175000017500000005373112200204546023455 0ustar00smcvsmcv00000000000000/* * wocky-stanza.c - Source for WockyStanza * Copyright (C) 2006-2010 Collabora Ltd. * @author Sjoerd Simons * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include "wocky-stanza.h" #include "wocky-xmpp-error.h" #include "wocky-namespaces.h" #include "wocky-debug-internal.h" #include "wocky-node-private.h" G_DEFINE_TYPE(WockyStanza, wocky_stanza, WOCKY_TYPE_NODE_TREE) /* private structure */ struct _WockyStanzaPrivate { WockyContact *from_contact; WockyContact *to_contact; gboolean dispose_has_run; }; typedef struct { WockyStanzaType type; const gchar *name; const gchar *ns; GQuark ns_q; } StanzaTypeName; static StanzaTypeName type_names[NUM_WOCKY_STANZA_TYPE] = { { WOCKY_STANZA_TYPE_NONE, NULL, WOCKY_XMPP_NS_JABBER_CLIENT }, { WOCKY_STANZA_TYPE_MESSAGE, "message", WOCKY_XMPP_NS_JABBER_CLIENT }, { WOCKY_STANZA_TYPE_PRESENCE, "presence", WOCKY_XMPP_NS_JABBER_CLIENT }, { WOCKY_STANZA_TYPE_IQ, "iq", WOCKY_XMPP_NS_JABBER_CLIENT }, { WOCKY_STANZA_TYPE_STREAM, "stream", WOCKY_XMPP_NS_STREAM }, { WOCKY_STANZA_TYPE_STREAM_FEATURES, "features", WOCKY_XMPP_NS_STREAM }, { WOCKY_STANZA_TYPE_AUTH, "auth", WOCKY_XMPP_NS_SASL_AUTH }, { WOCKY_STANZA_TYPE_CHALLENGE, "challenge", WOCKY_XMPP_NS_SASL_AUTH }, { WOCKY_STANZA_TYPE_RESPONSE, "response", WOCKY_XMPP_NS_SASL_AUTH }, { WOCKY_STANZA_TYPE_SUCCESS, "success", WOCKY_XMPP_NS_SASL_AUTH }, { WOCKY_STANZA_TYPE_FAILURE, "failure", WOCKY_XMPP_NS_SASL_AUTH }, { WOCKY_STANZA_TYPE_STREAM_ERROR, "error", WOCKY_XMPP_NS_STREAM }, { WOCKY_STANZA_TYPE_UNKNOWN, NULL, NULL }, }; static void fill_in_namespace_quarks (void) { int i; /* We skip the first entry as it's NONE */ for (i = 1; type_names[i].type != WOCKY_STANZA_TYPE_UNKNOWN; i++) type_names[i].ns_q = g_quark_from_static_string (type_names[i].ns); } typedef struct { WockyStanzaSubType sub_type; const gchar *name; WockyStanzaType type; } StanzaSubTypeName; static const StanzaSubTypeName sub_type_names[NUM_WOCKY_STANZA_SUB_TYPE] = { { WOCKY_STANZA_SUB_TYPE_NONE, NULL, WOCKY_STANZA_TYPE_NONE }, { WOCKY_STANZA_SUB_TYPE_AVAILABLE, NULL, WOCKY_STANZA_TYPE_PRESENCE }, { WOCKY_STANZA_SUB_TYPE_NORMAL, "normal", WOCKY_STANZA_TYPE_NONE }, { WOCKY_STANZA_SUB_TYPE_CHAT, "chat", WOCKY_STANZA_TYPE_MESSAGE }, { WOCKY_STANZA_SUB_TYPE_GROUPCHAT, "groupchat", WOCKY_STANZA_TYPE_MESSAGE }, { WOCKY_STANZA_SUB_TYPE_HEADLINE, "headline", WOCKY_STANZA_TYPE_MESSAGE }, { WOCKY_STANZA_SUB_TYPE_UNAVAILABLE, "unavailable", WOCKY_STANZA_TYPE_PRESENCE }, { WOCKY_STANZA_SUB_TYPE_PROBE, "probe", WOCKY_STANZA_TYPE_PRESENCE }, { WOCKY_STANZA_SUB_TYPE_SUBSCRIBE, "subscribe", WOCKY_STANZA_TYPE_PRESENCE }, { WOCKY_STANZA_SUB_TYPE_UNSUBSCRIBE, "unsubscribe", WOCKY_STANZA_TYPE_PRESENCE }, { WOCKY_STANZA_SUB_TYPE_SUBSCRIBED, "subscribed", WOCKY_STANZA_TYPE_PRESENCE }, { WOCKY_STANZA_SUB_TYPE_UNSUBSCRIBED, "unsubscribed", WOCKY_STANZA_TYPE_PRESENCE }, { WOCKY_STANZA_SUB_TYPE_GET, "get", WOCKY_STANZA_TYPE_IQ }, { WOCKY_STANZA_SUB_TYPE_SET, "set", WOCKY_STANZA_TYPE_IQ }, { WOCKY_STANZA_SUB_TYPE_RESULT, "result", WOCKY_STANZA_TYPE_IQ }, { WOCKY_STANZA_SUB_TYPE_ERROR, "error", WOCKY_STANZA_TYPE_NONE }, { WOCKY_STANZA_SUB_TYPE_UNKNOWN, NULL, WOCKY_STANZA_TYPE_UNKNOWN }, }; static void wocky_stanza_init (WockyStanza *self) { self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, WOCKY_TYPE_STANZA, WockyStanzaPrivate); self->priv->from_contact = NULL; self->priv->to_contact = NULL; } static void wocky_stanza_dispose (GObject *object); static void wocky_stanza_finalize (GObject *object); static void wocky_stanza_class_init (WockyStanzaClass *wocky_stanza_class) { GObjectClass *object_class = G_OBJECT_CLASS (wocky_stanza_class); g_type_class_add_private (wocky_stanza_class, sizeof (WockyStanzaPrivate)); object_class->dispose = wocky_stanza_dispose; object_class->finalize = wocky_stanza_finalize; fill_in_namespace_quarks (); } static void wocky_stanza_dispose (GObject *object) { WockyStanza *self = WOCKY_STANZA (object); WockyStanzaPrivate *priv = self->priv; if (priv->dispose_has_run) return; priv->dispose_has_run = TRUE; /* release any references held by the object here */ if (G_OBJECT_CLASS (wocky_stanza_parent_class)->dispose) G_OBJECT_CLASS (wocky_stanza_parent_class)->dispose (object); } static void wocky_stanza_finalize (GObject *object) { WockyStanza *self = WOCKY_STANZA (object); if (self->priv->from_contact != NULL) { g_object_unref (self->priv->from_contact); self->priv->from_contact = NULL; } if (self->priv->to_contact != NULL) { g_object_unref (self->priv->to_contact); self->priv->to_contact = NULL; } G_OBJECT_CLASS (wocky_stanza_parent_class)->finalize (object); } WockyStanza * wocky_stanza_new (const gchar *name, const gchar *ns) { WockyStanza *result; result = WOCKY_STANZA (g_object_new (WOCKY_TYPE_STANZA, "top-node", wocky_node_new (name, ns), NULL)); return result; } WockyStanza * wocky_stanza_copy (WockyStanza *old) { WockyNode *top; top = _wocky_node_copy (wocky_stanza_get_top_node (old)); return g_object_new (WOCKY_TYPE_STANZA, "top-node", top, NULL); } static const gchar * get_type_name (WockyStanzaType type) { if (type <= WOCKY_STANZA_TYPE_NONE || type >= NUM_WOCKY_STANZA_TYPE) return NULL; g_assert (type_names[type].type == type); return type_names[type].name; } static const gchar * get_type_ns (WockyStanzaType type) { if (type <= WOCKY_STANZA_TYPE_NONE || type >= NUM_WOCKY_STANZA_TYPE) return NULL; g_assert (type_names[type].type == type); return type_names[type].ns; } static const gchar * get_sub_type_name (WockyStanzaSubType sub_type) { if (sub_type <= WOCKY_STANZA_SUB_TYPE_NONE || sub_type >= NUM_WOCKY_STANZA_SUB_TYPE) return NULL; g_assert (sub_type_names[sub_type].sub_type == sub_type); return sub_type_names[sub_type].name; } static gboolean check_sub_type (WockyStanzaType type, WockyStanzaSubType sub_type) { WockyStanzaType expected_type; g_return_val_if_fail (type > WOCKY_STANZA_TYPE_NONE && type < NUM_WOCKY_STANZA_TYPE, FALSE); g_return_val_if_fail (sub_type < NUM_WOCKY_STANZA_SUB_TYPE, FALSE); g_assert (sub_type_names[sub_type].sub_type == sub_type); expected_type = sub_type_names[sub_type].type; if (expected_type != WOCKY_STANZA_TYPE_NONE && expected_type != type) { g_critical ("Stanza sub-type '%s' may only be used with stanzas of " "type '%s', not of type '%s'", sub_type_names[sub_type].name, type_names[expected_type].name, type_names[type].name); g_return_val_if_reached (FALSE); } return TRUE; } static WockyStanza * wocky_stanza_new_with_sub_type (WockyStanzaType type, WockyStanzaSubType sub_type) { WockyStanza *stanza = NULL; const gchar *sub_type_name; if (!check_sub_type (type, sub_type)) return NULL; stanza = wocky_stanza_new (get_type_name (type), get_type_ns (type)); sub_type_name = get_sub_type_name (sub_type); if (sub_type_name != NULL) wocky_node_set_attribute (wocky_stanza_get_top_node (stanza), "type", sub_type_name); return stanza; } /** * wocky_stanza_build: * @type: The type of stanza to build * @sub_type: The stanza's subtype; valid values depend on @type. (For instance, * #WOCKY_STANZA_TYPE_IQ can use #WOCKY_STANZA_SUB_TYPE_GET, but not * #WOCKY_STANZA_SUB_TYPE_SUBSCRIBED.) * @from: The sender's JID, or %NULL to leave it unspecified. * @to: The target's JID, or %NULL to leave it unspecified. * @...: the description of the stanza to build, * terminated with %NULL * * Build a XMPP stanza from a list of arguments. For example, the following invocation: * * |[ * wocky_stanza_build ( * WOCKY_STANZA_TYPE_MESSAGE, WOCKY_STANZA_SUB_TYPE_NONE, * "alice@collabora.co.uk", "bob@collabora.co.uk", * WOCKY_NODE_START, "html", * WOCKY_NODE_XMLNS, "http://www.w3.org/1999/xhtml", * WOCKY_NODE, "body", * WOCKY_NODE_ATTRIBUTE, "textcolor", "red", * WOCKY_NODE_TEXT, "Telepathy rocks!", * WOCKY_NODE_END, * WOCKY_NODE_END, * NULL); * ]| * * produces this stanza: * * |[ * <message from='alice@collabora.co.uk' to='bob@collabora.co.uk'> * <html xmlns='http://www.w3.org/1999/xhtml'> * <body textcolor='red'> * Telepathy rocks! * </body> * </html> * </message> * ]| * * You may optionally use mnemonic ASCII characters in place of the build tags, * to better reflect the structure of the stanza in C source. For example, the * above stanza could be written as: * * |[ * wocky_stanza_build ( * WOCKY_STANZA_TYPE_MESSAGE, WOCKY_STANZA_SUB_TYPE_NONE, * "alice@collabora.co.uk", "bob@collabora.co.uk", * '(', "html", ':', "http://www.w3.org/1999/xhtml", * '(', "body", '@', "textcolor", "red", * '$', "Telepathy rocks!", * ')', * ')' * NULL); * ]| * * Returns: a new stanza object */ WockyStanza * wocky_stanza_build (WockyStanzaType type, WockyStanzaSubType sub_type, const gchar *from, const gchar *to, ...) { WockyStanza *stanza; va_list ap; va_start (ap, to); stanza = wocky_stanza_build_va (type, sub_type, from, to, ap); va_end (ap); return stanza; } WockyStanza * wocky_stanza_build_to_contact (WockyStanzaType type, WockyStanzaSubType sub_type, const gchar *from, WockyContact *to, ...) { WockyStanza *stanza; va_list ap; gchar *to_jid = NULL; if (to != NULL) to_jid = wocky_contact_dup_jid (to); va_start (ap, to); stanza = wocky_stanza_build_va (type, sub_type, from, to_jid, ap); va_end (ap); g_free (to_jid); stanza->priv->to_contact = g_object_ref (to); return stanza; } WockyStanza * wocky_stanza_build_va (WockyStanzaType type, WockyStanzaSubType sub_type, const gchar *from, const gchar *to, va_list ap) { WockyStanza *stanza; g_return_val_if_fail (type < NUM_WOCKY_STANZA_TYPE, NULL); g_return_val_if_fail (sub_type < NUM_WOCKY_STANZA_SUB_TYPE, NULL); stanza = wocky_stanza_new_with_sub_type (type, sub_type); if (stanza == NULL) return NULL; if (from != NULL) wocky_node_set_attribute (wocky_stanza_get_top_node (stanza), "from", from); if (to != NULL) wocky_node_set_attribute (wocky_stanza_get_top_node (stanza), "to", to); wocky_node_add_build_va (wocky_stanza_get_top_node (stanza), ap); return stanza; } static WockyStanzaType get_type_from_node (WockyNode *node) { const gchar *name = node->name; GQuark ns = node->ns; guint i; if (name == NULL) return WOCKY_STANZA_TYPE_NONE; /* We skip the first entry as it's NONE */ for (i = 1; i < WOCKY_STANZA_TYPE_UNKNOWN; i++) { if (type_names[i].name != NULL && ns == type_names[i].ns_q && strcmp (name, type_names[i].name) == 0) { return type_names[i].type; } } return WOCKY_STANZA_TYPE_UNKNOWN; } static WockyStanzaSubType get_sub_type_from_name (const gchar *name) { guint i; if (name == NULL) return WOCKY_STANZA_SUB_TYPE_NONE; /* We skip the first entry as it's NONE */ for (i = 1; i < WOCKY_STANZA_SUB_TYPE_UNKNOWN; i++) { if (sub_type_names[i].name != NULL && strcmp (name, sub_type_names[i].name) == 0) { return sub_type_names[i].sub_type; } } return WOCKY_STANZA_SUB_TYPE_UNKNOWN; } void wocky_stanza_get_type_info (WockyStanza *stanza, WockyStanzaType *type, WockyStanzaSubType *sub_type) { WockyNode *top_node; g_return_if_fail (stanza != NULL); top_node = wocky_stanza_get_top_node (stanza); g_assert (top_node != NULL); if (type != NULL) *type = get_type_from_node (top_node); if (sub_type != NULL) *sub_type = get_sub_type_from_name ( wocky_node_get_attribute (top_node, "type")); } gboolean wocky_stanza_has_type (WockyStanza *stanza, WockyStanzaType expected_type) { WockyStanzaType actual_type; g_return_val_if_fail (WOCKY_IS_STANZA (stanza), FALSE); wocky_stanza_get_type_info (stanza, &actual_type, NULL); return expected_type == actual_type; } static WockyStanza * create_iq_reply (WockyStanza *iq, WockyStanzaSubType sub_type_reply, va_list ap) { WockyStanza *reply; WockyStanzaType type; WockyNode *node; WockyStanzaSubType sub_type; const gchar *from, *to, *id; WockyContact *contact; g_return_val_if_fail (iq != NULL, NULL); wocky_stanza_get_type_info (iq, &type, &sub_type); g_return_val_if_fail (type == WOCKY_STANZA_TYPE_IQ, NULL); g_return_val_if_fail (sub_type == WOCKY_STANZA_SUB_TYPE_GET || sub_type == WOCKY_STANZA_SUB_TYPE_SET, NULL); node = wocky_stanza_get_top_node (iq); from = wocky_node_get_attribute (node, "from"); to = wocky_node_get_attribute (node, "to"); id = wocky_node_get_attribute (node, "id"); if (id == NULL) return NULL; reply = wocky_stanza_build_va (WOCKY_STANZA_TYPE_IQ, sub_type_reply, to, from, ap); wocky_node_set_attribute (wocky_stanza_get_top_node (reply), "id", id); contact = wocky_stanza_get_from_contact (iq); if (contact != NULL) wocky_stanza_set_to_contact (reply, contact); return reply; } WockyStanza * wocky_stanza_build_iq_result (WockyStanza *iq, ...) { WockyStanza *reply; va_list ap; va_start (ap, iq); reply = create_iq_reply (iq, WOCKY_STANZA_SUB_TYPE_RESULT, ap); va_end (ap); return reply; } WockyStanza * wocky_stanza_build_iq_result_va ( WockyStanza *iq, va_list ap) { return create_iq_reply (iq, WOCKY_STANZA_SUB_TYPE_RESULT, ap); } /** * wocky_stanza_build_iq_error: * @iq: a stanza of type #WOCKY_STANZA_TYPE_IQ and sub-type either * #WOCKY_STANZA_SUB_TYPE_SET or #WOCKY_STANZA_SUB_TYPE_GET * @...: a wocky_stanza_build() specification * * Builds an error reply to @iq containing the given body. This function also * adds the child element of @iq to the reply, as recommended by RFC3920 §9.2.3 * ‘IQ Semantics’. * * No <error/> element is added to the reply. To add a * standard stanza error, plus message, consider using * wocky_stanza_error_to_node(). To add a more complicated error with an * application-specific condition, specify it when calling this function. For * example: * * |[ * WockyStanza *reply = wocky_stanza_build_iq_error (iq, * '(', "error", * '@', "type", "cancel", * '(', "feature-not-implemented", ':', WOCKY_XMPP_NS_STANZAS, ')', * '(', "unsupported", ':', WOCKY_XMPP_NS_PUBSUB_ERRORS, * '@', "feature", "subscribe", * ')', * ')', NULL); * ]| * * Returns: an error reply for @iq */ WockyStanza * wocky_stanza_build_iq_error (WockyStanza *iq, ...) { WockyStanza *reply; WockyNode *query; va_list ap; va_start (ap, iq); reply = create_iq_reply (iq, WOCKY_STANZA_SUB_TYPE_ERROR, ap); va_end (ap); /* RFC3920 §9.2.3 dictates: * 5. An IQ stanza of type "get" or "set" MUST contain one and only one * child element that specifies the semantics of the particular request * or response. * … * 7. An IQ stanza of type "error" SHOULD include the child element * contained in the associated "get" or "set" and MUST include an * child; for details, see Stanza Errors. * * So here we take the first child out of the stanza we're replying to, and * include it in the error reply. If @iq has more than one child, it * was illegal, so we're within our rights to ignore everything after the * first. If @iq has no children, it was also illegal, so there's no way we * can comply with the SHOULD. */ query = wocky_node_get_first_child (wocky_stanza_get_top_node (iq)); if (reply != NULL && query != NULL) { WockyNodeTree *query_tree = wocky_node_tree_new_from_node (query); wocky_node_prepend_node_tree (wocky_stanza_get_top_node (reply), query_tree); g_object_unref (query_tree); } return reply; } WockyStanza * wocky_stanza_build_iq_error_va ( WockyStanza *iq, va_list ap) { return create_iq_reply (iq, WOCKY_STANZA_SUB_TYPE_ERROR, ap); } /** * wocky_stanza_extract_errors: * @stanza: a message/iq/presence stanza * @type: location at which to store the error type * @core: location at which to store an error in the domain #WOCKY_XMPP_ERROR * @specialized: location at which to store an error in an application-specific * domain, if one is found * @specialized_node: location at which to store the node representing an * application-specific error, if one is found * * Given a message, iq or presence stanza with type='error', breaks it down * into values describing the error. @type and @core are guaranteed to be set; * @specialized and @specialized_node will be set if a recognised * application-specific error is found, and the latter will be set to %NULL if * no application-specific error is found. * * Any or all of the out parameters may be %NULL to ignore the value. The * value stored in @specialized_node is borrowed from @stanza, and is only * valid as long as the latter is alive. * * Returns: %TRUE if the stanza had type='error'; %FALSE otherwise */ gboolean wocky_stanza_extract_errors (WockyStanza *stanza, WockyXmppErrorType *type, GError **core, GError **specialized, WockyNode **specialized_node) { WockyStanzaSubType sub_type; WockyNode *error; wocky_stanza_get_type_info (stanza, NULL, &sub_type); if (sub_type != WOCKY_STANZA_SUB_TYPE_ERROR) return FALSE; error = wocky_node_get_child (wocky_stanza_get_top_node (stanza), "error"); if (error == NULL) { if (type != NULL) *type = WOCKY_XMPP_ERROR_TYPE_CANCEL; g_set_error (core, WOCKY_XMPP_ERROR, WOCKY_XMPP_ERROR_UNDEFINED_CONDITION, "stanza had type='error' but no node"); if (specialized_node != NULL) *specialized_node = NULL; } else { wocky_xmpp_error_extract (error, type, core, specialized, specialized_node); } return TRUE; } /** * wocky_stanza_extract_stream_error: * @stanza: a stanza * @stream_error: location at which to store an error in domain * #WOCKY_XMPP_STREAM_ERROR, if one is found. * * Returns: %TRUE and sets @stream_error if the stanza was indeed a stream * error. */ gboolean wocky_stanza_extract_stream_error (WockyStanza *stanza, GError **stream_error) { if (!wocky_stanza_has_type (stanza, WOCKY_STANZA_TYPE_STREAM_ERROR)) return FALSE; g_propagate_error (stream_error, wocky_xmpp_stream_error_from_node (wocky_stanza_get_top_node (stanza))); return TRUE; } /** * wocky_stanza_get_top_node: * @self: a stanza * * Returns: A pointer to the topmost node of the stanza */ WockyNode * wocky_stanza_get_top_node (WockyStanza *self) { return wocky_node_tree_get_top_node (WOCKY_NODE_TREE (self)); } /** * wocky_stanza_get_from: * @self: a stanza * * * * Returns: The sender of @self, or %NULL if no sender was specified. */ const gchar * wocky_stanza_get_from (WockyStanza *self) { g_return_val_if_fail (self != NULL, NULL); g_return_val_if_fail (WOCKY_IS_STANZA (self), NULL); return wocky_node_get_attribute (wocky_stanza_get_top_node (self), "from"); } /** * wocky_stanza_get_to: * @self: a stanza * * * * Returns: The recipient of @self, or %NULL if no recipient was specified. */ const gchar * wocky_stanza_get_to (WockyStanza *self) { g_return_val_if_fail (self != NULL, NULL); g_return_val_if_fail (WOCKY_IS_STANZA (self), NULL); return wocky_node_get_attribute (wocky_stanza_get_top_node (self), "to"); } WockyContact * wocky_stanza_get_to_contact (WockyStanza *self) { g_return_val_if_fail (self != NULL, NULL); g_return_val_if_fail (WOCKY_IS_STANZA (self), NULL); return self->priv->to_contact; } WockyContact * wocky_stanza_get_from_contact (WockyStanza *self) { g_return_val_if_fail (self != NULL, NULL); g_return_val_if_fail (WOCKY_IS_STANZA (self), NULL); return self->priv->from_contact; } void wocky_stanza_set_to_contact (WockyStanza *self, WockyContact *contact) { g_return_if_fail (self != NULL); g_return_if_fail (WOCKY_IS_STANZA (self)); g_return_if_fail (WOCKY_IS_CONTACT (contact)); if (self->priv->to_contact != NULL) g_object_unref (self->priv->to_contact); self->priv->to_contact = g_object_ref (contact); } void wocky_stanza_set_from_contact (WockyStanza *self, WockyContact *contact) { g_return_if_fail (self != NULL); g_return_if_fail (WOCKY_IS_STANZA (self)); g_return_if_fail (WOCKY_IS_CONTACT (contact)); if (self->priv->from_contact != NULL) g_object_unref (self->priv->from_contact); self->priv->from_contact = g_object_ref (contact); } telepathy-gabble-0.18.2/lib/ext/wocky/wocky/wocky-session.c0000644000175000017500000001636212200204546023637 0ustar00smcvsmcv00000000000000/* * wocky-session.c - Source for WockySession * Copyright (C) 2009 Collabora Ltd. * @author Guillaume Desmottes * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 */ /** * SECTION: wocky-session * @title: WockySession * @short_description: * @include: wocky/wocky-session.h * */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "wocky-session.h" #include #include #include #ifdef HAVE_UNISTD_H # include #endif #include "wocky-signals-marshal.h" #include "wocky-utils.h" #include "wocky-c2s-porter.h" #include "wocky-meta-porter.h" G_DEFINE_TYPE (WockySession, wocky_session, G_TYPE_OBJECT) /* properties */ enum { PROP_CONNECTION = 1, PROP_PORTER, PROP_CONTACT_FACTORY, PROP_FULL_JID, }; /* signal enum */ enum { LAST_SIGNAL, }; /* static guint signals[LAST_SIGNAL] = {0}; */ /* private structure */ struct _WockySessionPrivate { gboolean dispose_has_run; gchar *full_jid; WockyXmppConnection *connection; WockyPorter *porter; WockyContactFactory *contact_factory; }; static void wocky_session_init (WockySession *self) { self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, WOCKY_TYPE_SESSION, WockySessionPrivate); self->priv->contact_factory = wocky_contact_factory_new (); } static void wocky_session_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { WockySession *self = WOCKY_SESSION (object); WockySessionPrivate *priv = self->priv; switch (property_id) { case PROP_CONNECTION: priv->connection = g_value_dup_object (value); break; case PROP_FULL_JID: priv->full_jid = g_value_dup_string (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void wocky_session_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { WockySession *self = WOCKY_SESSION (object); WockySessionPrivate *priv = self->priv; switch (property_id) { case PROP_CONNECTION: g_value_set_object (value, priv->connection); break; case PROP_PORTER: g_value_set_object (value, priv->porter); break; case PROP_CONTACT_FACTORY: g_value_set_object (value, priv->contact_factory); break; case PROP_FULL_JID: g_value_set_string (value, priv->full_jid); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void wocky_session_constructed (GObject *object) { WockySession *self = WOCKY_SESSION (object); WockySessionPrivate *priv = self->priv; if (priv->connection != NULL) priv->porter = wocky_c2s_porter_new (priv->connection, priv->full_jid); else priv->porter = wocky_meta_porter_new (priv->full_jid, priv->contact_factory); } static void wocky_session_dispose (GObject *object) { WockySession *self = WOCKY_SESSION (object); WockySessionPrivate *priv = self->priv; if (priv->dispose_has_run) return; priv->dispose_has_run = TRUE; if (priv->connection != NULL) { g_object_unref (priv->connection); priv->connection = NULL; } g_object_unref (priv->porter); g_object_unref (priv->contact_factory); if (G_OBJECT_CLASS (wocky_session_parent_class)->dispose) G_OBJECT_CLASS (wocky_session_parent_class)->dispose (object); } static void wocky_session_finalize (GObject *object) { WockySession *self = WOCKY_SESSION (object); g_free (self->priv->full_jid); G_OBJECT_CLASS (wocky_session_parent_class)->finalize (object); } static void wocky_session_class_init (WockySessionClass *wocky_session_class) { GObjectClass *object_class = G_OBJECT_CLASS (wocky_session_class); GParamSpec *spec; g_type_class_add_private (wocky_session_class, sizeof (WockySessionPrivate)); object_class->constructed = wocky_session_constructed; object_class->set_property = wocky_session_set_property; object_class->get_property = wocky_session_get_property; object_class->dispose = wocky_session_dispose; object_class->finalize = wocky_session_finalize; spec = g_param_spec_object ("connection", "Connection", "The WockyXmppConnection associated with this session", WOCKY_TYPE_XMPP_CONNECTION, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, PROP_CONNECTION, spec); spec = g_param_spec_object ("porter", "Porter", "The WockyPorter associated with this session", WOCKY_TYPE_PORTER, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, PROP_PORTER, spec); spec = g_param_spec_object ("contact-factory", "Contact factory", "The WockyContactFactory associated with this session", WOCKY_TYPE_CONTACT_FACTORY, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, PROP_CONTACT_FACTORY, spec); spec = g_param_spec_string ("full-jid", "Full JID", "The user's JID in this session", NULL, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, PROP_FULL_JID, spec); } WockySession * wocky_session_new_with_connection (WockyXmppConnection *conn, const gchar *full_jid) { g_return_val_if_fail (WOCKY_IS_XMPP_CONNECTION (conn), NULL); g_return_val_if_fail (full_jid != NULL, NULL); return g_object_new (WOCKY_TYPE_SESSION, "connection", conn, "full-jid", full_jid, NULL); } WockySession * wocky_session_new_ll (const gchar *full_jid) { return g_object_new (WOCKY_TYPE_SESSION, "full-jid", full_jid, NULL); } void wocky_session_start (WockySession *self) { WockySessionPrivate *priv = self->priv; wocky_porter_start (priv->porter); } WockyPorter * wocky_session_get_porter (WockySession *self) { WockySessionPrivate *priv = self->priv; return priv->porter; } WockyContactFactory * wocky_session_get_contact_factory (WockySession *self) { WockySessionPrivate *priv = self->priv; return priv->contact_factory; } void wocky_session_set_jid (WockySession *self, const gchar *jid) { WockySessionPrivate *priv = self->priv; g_free (priv->full_jid); priv->full_jid = g_strdup (jid); if (WOCKY_IS_META_PORTER (priv->porter)) { wocky_meta_porter_set_jid (WOCKY_META_PORTER (priv->porter), priv->full_jid); } } const gchar * wocky_session_get_jid (WockySession *self) { WockySessionPrivate *priv = self->priv; return priv->full_jid; } telepathy-gabble-0.18.2/lib/ext/wocky/wocky/wocky-sasl-plain.c0000644000175000017500000001053312200204546024211 0ustar00smcvsmcv00000000000000#ifdef HAVE_CONFIG_H #include "config.h" #endif #include "wocky-sasl-plain.h" #include "wocky-auth-registry.h" #define WOCKY_DEBUG_FLAG WOCKY_DEBUG_AUTH #include "wocky-debug-internal.h" static void auth_handler_iface_init (gpointer g_iface); G_DEFINE_TYPE_WITH_CODE (WockySaslPlain, wocky_sasl_plain, G_TYPE_OBJECT, G_IMPLEMENT_INTERFACE (WOCKY_TYPE_AUTH_HANDLER, auth_handler_iface_init)) enum { PROP_USERNAME = 1, PROP_PASSWORD }; struct _WockySaslPlainPrivate { gchar *username; gchar *password; }; static void wocky_sasl_plain_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { WockySaslPlain *self = WOCKY_SASL_PLAIN (object); WockySaslPlainPrivate *priv = self->priv; switch (property_id) { case PROP_USERNAME: g_value_set_string (value, priv->username); break; case PROP_PASSWORD: g_value_set_string (value, priv->password); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); } } static void wocky_sasl_plain_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { WockySaslPlain *self = WOCKY_SASL_PLAIN (object); WockySaslPlainPrivate *priv = self->priv; switch (property_id) { case PROP_USERNAME: g_free (priv->username); priv->username = 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 (object, property_id, pspec); } } static void wocky_sasl_plain_dispose (GObject *object) { WockySaslPlain *self = WOCKY_SASL_PLAIN (object); WockySaslPlainPrivate *priv = self->priv; g_free (priv->username); g_free (priv->password); G_OBJECT_CLASS (wocky_sasl_plain_parent_class)->dispose (object); } static void wocky_sasl_plain_class_init (WockySaslPlainClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); g_type_class_add_private (klass, sizeof (WockySaslPlainPrivate)); object_class->get_property = wocky_sasl_plain_get_property; object_class->set_property = wocky_sasl_plain_set_property; object_class->dispose = wocky_sasl_plain_dispose; g_object_class_install_property (object_class, PROP_USERNAME, g_param_spec_string ("username", "username", "The username to authenticate with", NULL, G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (object_class, PROP_PASSWORD, g_param_spec_string ("password", "password", "The password to authenticate with", NULL, G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS)); } static gboolean plain_initial_response (WockyAuthHandler *handler, GString **initial_data, GError **error); static void auth_handler_iface_init (gpointer g_iface) { WockyAuthHandlerIface *iface = g_iface; iface->mechanism = WOCKY_AUTH_MECH_SASL_PLAIN; iface->plain = TRUE; iface->initial_response_func = plain_initial_response; } static void wocky_sasl_plain_init (WockySaslPlain *self) { self->priv = G_TYPE_INSTANCE_GET_PRIVATE ( self, WOCKY_TYPE_SASL_PLAIN, WockySaslPlainPrivate); } WockySaslPlain * wocky_sasl_plain_new (const gchar *username, const gchar *password) { return g_object_new (WOCKY_TYPE_SASL_PLAIN, "username", username, "password", password, NULL); } static GString * plain_generate_initial_response (const gchar *username, const gchar *password) { GString *str = g_string_new (""); g_string_append_c (str, '\0'); g_string_append (str, username); g_string_append_c (str, '\0'); g_string_append (str, password); return str; } static gboolean plain_initial_response (WockyAuthHandler *handler, GString **initial_data, GError **error) { WockySaslPlain *self = WOCKY_SASL_PLAIN (handler); WockySaslPlainPrivate *priv = self->priv; if (priv->username == NULL || priv->password == NULL) { g_set_error (error, WOCKY_AUTH_ERROR, WOCKY_AUTH_ERROR_NO_CREDENTIALS, "No username or password provided"); return FALSE; } DEBUG ("Got username and password"); *initial_data = plain_generate_initial_response (priv->username, priv->password); return TRUE; } telepathy-gabble-0.18.2/lib/ext/wocky/wocky/wocky-sasl-utils.c0000644000175000017500000000625212200204546024251 0ustar00smcvsmcv00000000000000/* * wocky-sasl-utils.c - Some sasl helper functions * Copyright (C) 2006-2010 Collabora Ltd. * @author Sjoerd Simons * Copyright (C) 2010 Sjoerd Simons * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include "wocky-sasl-utils.h" /* Generate a good random nonce encoded with base64 such that it falls in the * allowable alphabet of various crypto mechanism. */ gchar * sasl_generate_base64_nonce (void) { /* RFC 2831 recommends the the nonce to be either hexadecimal or base64 with * at least 64 bits of entropy */ #define NR 8 guint32 n[NR]; int i; for (i = 0; i < NR; i++) n[i] = g_random_int (); return g_base64_encode ((guchar *) n, sizeof (n)); } GByteArray * sasl_calculate_hmac_sha1 (guint8 *key, gsize key_len, guint8 *text, gsize text_len) { /* Calculate the HMAC keyed hash algorithm as defined in RFC2104, using * SHA-1 as the hash algorithm */ GChecksum *checksum; guint8 k_ipad[WOCKY_SHA1_BLOCK_SIZE]; guint8 k_opad[WOCKY_SHA1_BLOCK_SIZE]; guint8 inner_checksum[WOCKY_SHA1_DIGEST_SIZE]; GByteArray *result; gsize len = WOCKY_SHA1_DIGEST_SIZE, i; memset (k_ipad, 0x36, WOCKY_SHA1_BLOCK_SIZE); memset (k_opad, 0x5c, WOCKY_SHA1_BLOCK_SIZE); if (key_len > WOCKY_SHA1_BLOCK_SIZE) { guchar k[WOCKY_SHA1_DIGEST_SIZE]; checksum = g_checksum_new (G_CHECKSUM_SHA1); g_checksum_update (checksum, key, key_len); g_checksum_get_digest (checksum, k, &len); g_checksum_free (checksum); for (i = 0; i < WOCKY_SHA1_DIGEST_SIZE; i++) { k_ipad[i] ^= k[i]; k_opad[i] ^= k[i]; } } else { for (i = 0; i < key_len; i++) { k_ipad[i] ^= key[i]; k_opad[i] ^= key[i]; } } /* inner checksum */ checksum = g_checksum_new (G_CHECKSUM_SHA1); g_checksum_update (checksum, k_ipad, WOCKY_SHA1_BLOCK_SIZE); g_checksum_update (checksum, text, text_len); g_checksum_get_digest (checksum, inner_checksum, &len); g_checksum_free (checksum); /* outer checksum */ result = g_byte_array_new (); g_byte_array_set_size (result, WOCKY_SHA1_DIGEST_SIZE); checksum = g_checksum_new (G_CHECKSUM_SHA1); g_checksum_update (checksum, k_opad, WOCKY_SHA1_BLOCK_SIZE); g_checksum_update (checksum, inner_checksum, WOCKY_SHA1_DIGEST_SIZE); g_checksum_get_digest (checksum, result->data, &len); g_checksum_free (checksum); return result; } telepathy-gabble-0.18.2/lib/ext/wocky/wocky/wocky-sasl-scram.c0000644000175000017500000003742212200204546024221 0ustar00smcvsmcv00000000000000/* * wocky-sasl-scram.c - SCRAM-SHA1 implementation (to be RFC 5802) * Copyright (C) 2010 Sjoerd Simons * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "wocky-sasl-scram.h" #include "wocky-sasl-auth.h" #include "wocky-sasl-utils.h" #include "wocky-utils.h" #include #define WOCKY_DEBUG_FLAG WOCKY_DEBUG_AUTH #include "wocky-debug-internal.h" typedef enum { WOCKY_SASL_SCRAM_STATE_STARTED, WOCKY_SASL_SCRAM_STATE_SERVER_FIRST_MESSAGE, WOCKY_SASL_SCRAM_STATE_SERVER_FINAL_MESSAGE, WOCKY_SASL_SCRAM_STATE_FINISHED, } WockySaslScramState; static void sasl_handler_iface_init (gpointer g_iface); G_DEFINE_TYPE_WITH_CODE (WockySaslScram, wocky_sasl_scram, G_TYPE_OBJECT, G_IMPLEMENT_INTERFACE ( WOCKY_TYPE_AUTH_HANDLER, sasl_handler_iface_init)) enum { PROP_SERVER = 1, PROP_USERNAME, PROP_PASSWORD }; struct _WockySaslScramPrivate { WockySaslScramState state; gchar *username; gchar *password; gchar *server; gchar *client_nonce; gchar *nonce; gchar *salt; gchar *client_first_bare; gchar *server_first_bare; gchar *auth_message; guint64 iterations; GByteArray *salted_password; }; static void wocky_sasl_scram_get_property ( GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { WockySaslScram *self = (WockySaslScram *) object; WockySaslScramPrivate *priv = self->priv; switch (property_id) { case PROP_USERNAME: g_value_set_string (value, priv->username); break; case PROP_PASSWORD: g_value_set_string (value, priv->password); break; case PROP_SERVER: g_value_set_string (value, priv->server); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); } } static void wocky_sasl_scram_set_property ( GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { WockySaslScram *self = (WockySaslScram *) object; WockySaslScramPrivate *priv = self->priv; switch (property_id) { case PROP_SERVER: g_free (priv->server); priv->server = g_value_dup_string (value); break; case PROP_USERNAME: g_free (priv->username); priv->username = 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 (object, property_id, pspec); } } static void wocky_sasl_scram_dispose (GObject *object) { WockySaslScram *self = (WockySaslScram *) object; WockySaslScramPrivate *priv = self->priv; g_free (priv->server); g_free (priv->username); g_free (priv->password); g_free (priv->client_nonce); g_free (priv->nonce); g_free (priv->salt); g_free (priv->client_first_bare); g_free (priv->server_first_bare); g_free (priv->auth_message); if (priv->salted_password != NULL) g_byte_array_unref (priv->salted_password); G_OBJECT_CLASS (wocky_sasl_scram_parent_class)->dispose (object); } static void wocky_sasl_scram_class_init ( WockySaslScramClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); g_type_class_add_private (klass, sizeof (WockySaslScramPrivate)); object_class->dispose = wocky_sasl_scram_dispose; object_class->set_property = wocky_sasl_scram_set_property; object_class->get_property = wocky_sasl_scram_get_property; g_object_class_install_property (object_class, PROP_SERVER, g_param_spec_string ("server", "server", "The name of the server we're authenticating to", NULL, G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (object_class, PROP_USERNAME, g_param_spec_string ("username", "username", "The username to authenticate with", NULL, G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (object_class, PROP_PASSWORD, g_param_spec_string ("password", "password", "The password to authenticate with", NULL, G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS)); } static gboolean scram_initial_response (WockyAuthHandler *handler, GString **response, GError **error); static gboolean scram_handle_auth_data (WockyAuthHandler *handler, const GString *data, GString **response, GError **error); static gboolean scram_handle_success (WockyAuthHandler *handler, GError **error); static void sasl_handler_iface_init (gpointer g_iface) { WockyAuthHandlerIface *iface = g_iface; iface->mechanism = "SCRAM-SHA-1"; iface->plain = FALSE; iface->initial_response_func = scram_initial_response; iface->auth_data_func = scram_handle_auth_data; iface->success_func = scram_handle_success; } static void wocky_sasl_scram_init (WockySaslScram *self) { self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, WOCKY_TYPE_SASL_SCRAM, WockySaslScramPrivate); self->priv->state = WOCKY_SASL_SCRAM_STATE_STARTED; } WockySaslScram * wocky_sasl_scram_new ( const gchar *server, const gchar *username, const gchar *password) { return g_object_new (WOCKY_TYPE_SASL_SCRAM, "server", server, "username", username, "password", password, NULL); } static gboolean scram_initial_response (WockyAuthHandler *handler, GString **response, GError **error) { WockySaslScram *self = WOCKY_SASL_SCRAM (handler); WockySaslScramPrivate *priv = self->priv; if (priv->username == NULL || priv->password == NULL) { g_set_error (error, WOCKY_AUTH_ERROR, WOCKY_AUTH_ERROR_NO_CREDENTIALS, "No username or password"); return FALSE; } g_assert (priv->client_nonce == NULL); priv->client_nonce = sasl_generate_base64_nonce (); priv->client_first_bare = g_strdup_printf ("n,,n=%s,r=%s", priv->username, priv->client_nonce); *response = g_string_new (priv->client_first_bare); priv->state = WOCKY_SASL_SCRAM_STATE_SERVER_FIRST_MESSAGE; return TRUE; } static gboolean scram_get_next_attr_value (gchar **message, gchar *attr, gchar **value) { gchar *end; end = *message; /* Need to not be at the end of the string, have a = as the first character * after the attribute and a valid value character as the next one */ if (end[0] == '\0' || end[1] != '=' || end[2] == '\0') return FALSE; *attr = end[0]; end += 2; *value = end; for (; *end != '\0' && *end != ','; end++) /* pass */ ; if (*end != '\0') *message = end + 1; /* Set the end to \0 so we can just point value to the start of it */ *end = '\0'; return TRUE; } /* xor the result array with the in array */ static void scram_xor_array (GByteArray *result, GByteArray *in) { gsize i; g_assert (result->len == in->len); for (i = 0; i < result->len ; i++) result->data[i] ^= in->data[i]; } static void scram_calculate_salted_password (WockySaslScram *self) { WockySaslScramPrivate *priv = self->priv; guint64 i; GByteArray *result, *prev, *salt; guint8 one[] = {0,0,0,1}; gint state = 0; guint save = 0; gsize len; gsize pass_len = strlen (priv->password); /* salt for U1 */ salt = g_byte_array_new (); /* Make sure we have enough data for the decoding base 64 and add 4 extra * bytes for the calculation */ g_byte_array_set_size (salt, (strlen (priv->salt)/4 * 3) + 3 + 4 ); len = g_base64_decode_step (priv->salt, strlen (priv->salt), salt->data, &state, &save); g_byte_array_set_size (salt, len); g_byte_array_append (salt, one, sizeof (one)); /* Calculate U1 */ result = sasl_calculate_hmac_sha1 ((guint8 *) priv->password, pass_len, salt->data, salt->len); prev = g_byte_array_sized_new (result->len); g_byte_array_append (prev, result->data, result->len); /* Calculate U2 and onwards, while keeping a rolling result */ for (i = 1; i < priv->iterations; i++) { GByteArray *U = sasl_calculate_hmac_sha1 ((guint8 *) priv->password, pass_len, prev->data, prev->len); g_byte_array_unref (prev); prev = U; scram_xor_array (result, U); } g_byte_array_unref (prev); g_byte_array_unref (salt); priv->salted_password = result; } static gchar * scram_make_client_proof (WockySaslScram *self) { /* As per RFC * ClientProof := ClientKey XOR ClientSignature * ClientSignature := HMAC(StoredKey, AuthMessage) * StoredKey := H(ClientKey) * ClientKey := HMAC(SaltedPassword, "Client Key") * SaltedPassword := Hi(Normalize(password), salt, i) */ #define CLIENT_KEY_STR "Client Key" WockySaslScramPrivate *priv = self->priv; gchar *proof = NULL; GByteArray *client_key, *client_signature; gsize len = WOCKY_SHA1_DIGEST_SIZE; guint8 stored_key[WOCKY_SHA1_DIGEST_SIZE]; GChecksum *checksum; /* Calculate the salted password and save it for later as we need it to * verify the servers reply */ scram_calculate_salted_password (self); client_key = sasl_calculate_hmac_sha1 (priv->salted_password->data, priv->salted_password->len, (guint8 *) CLIENT_KEY_STR, strlen (CLIENT_KEY_STR)); checksum = g_checksum_new (G_CHECKSUM_SHA1); g_checksum_update (checksum, client_key->data, client_key->len); g_checksum_get_digest (checksum, stored_key, &len); g_checksum_free (checksum); DEBUG ("auth message: %s", priv->auth_message); client_signature = sasl_calculate_hmac_sha1 (stored_key, WOCKY_SHA1_DIGEST_SIZE, (guint8 *) priv->auth_message, strlen (priv->auth_message)); /* xor signature and key, overwriting key */ scram_xor_array (client_key, client_signature); proof = g_base64_encode (client_key->data, client_key->len); g_byte_array_unref (client_key); g_byte_array_unref (client_signature); return proof; #undef CLIENT_KEY_STR } static gboolean scram_handle_server_first_message (WockySaslScram *self, gchar *message, GString **reply, GError **error) { WockySaslScramPrivate *priv = self->priv; gchar attr, *value = NULL; gchar *proof = NULL; GString *client_reply; if (!scram_get_next_attr_value (&message, &attr, &value)) goto invalid; /* Fail when getting an unknown mandatory extension (we don't know any) */ if (attr == 'm') goto unknown_extension; /* get the nonce */ if (attr != 'r') goto invalid; priv->nonce = g_strdup (value); if (strncmp (priv->client_nonce, priv->nonce, strlen (priv->client_nonce)) != 0) goto invalid_nonce; /* get the salt */ if (!scram_get_next_attr_value (&message, &attr, &value) || attr != 's') goto invalid; priv->salt = g_strdup (value); /* get the number of iterations */ if (!scram_get_next_attr_value (&message, &attr, &value) || attr != 'i') goto invalid; priv->iterations = g_ascii_strtoull (value, NULL, 10); if (priv->iterations == 0) goto invalid_iterations; /* We got everything we needed for our response without proof * base64("n,,") => biws */ client_reply = g_string_new (NULL); g_string_append_printf (client_reply, "c=biws,r=%s", priv->nonce); /* So we can make the auth message */ priv->auth_message = g_strdup_printf ("%s,%s,%s", priv->client_first_bare + 3, priv->server_first_bare, client_reply->str); /* and prepare our proof */ proof = scram_make_client_proof (self); g_string_append_printf (client_reply, ",p=%s", proof); g_free (proof); DEBUG ("Client reply: %s", client_reply->str); *reply = client_reply; return TRUE; invalid_iterations: g_set_error (error, WOCKY_AUTH_ERROR, WOCKY_AUTH_ERROR_INVALID_REPLY, "Server sent an invalid interation count"); return FALSE; invalid_nonce: g_set_error (error, WOCKY_AUTH_ERROR, WOCKY_AUTH_ERROR_INVALID_REPLY, "Server sent an invalid invalid nonce value"); return FALSE; invalid: g_set_error (error, WOCKY_AUTH_ERROR, WOCKY_AUTH_ERROR_INVALID_REPLY, "Server sent an invalid first reply"); return FALSE; unknown_extension: g_set_error (error, WOCKY_AUTH_ERROR, WOCKY_AUTH_ERROR_INVALID_REPLY, "Server sent an unknown mandatory extension"); return FALSE; } static gboolean scram_check_server_verification (WockySaslScram *self, gchar *verification) { /* * ServerSignature := HMAC(ServerKey, AuthMessage) * ServerKey := HMAC(SaltedPassword, "Server Key") */ #define SERVER_KEY_STR "Server Key" WockySaslScramPrivate *priv = self->priv; GByteArray *server_key; GByteArray *server_signature; gchar *v; gboolean ret; server_key = sasl_calculate_hmac_sha1 ( priv->salted_password->data, priv->salted_password->len, (guint8 *) SERVER_KEY_STR, strlen (SERVER_KEY_STR)); server_signature = sasl_calculate_hmac_sha1 (server_key->data, server_key->len, (guint8 *) priv->auth_message, strlen (priv->auth_message)); v = g_base64_encode (server_signature->data, server_signature->len); ret = !wocky_strdiff (v, verification); if (!ret) DEBUG ("Unexpected verification: got %s, expected %s", verification, v); g_byte_array_unref (server_key); g_byte_array_unref (server_signature); g_free (v); return ret; #undef SERVER_KEY_STR } static gboolean scram_handle_server_final_message (WockySaslScram *self, gchar *message, GError **error) { gchar attr, *value = NULL; if (!scram_get_next_attr_value (&message, &attr, &value) || attr != 'v') goto invalid; if (!scram_check_server_verification (self, value)) goto server_not_verified; return TRUE; server_not_verified: g_set_error (error, WOCKY_AUTH_ERROR, WOCKY_AUTH_ERROR_INVALID_REPLY, "Server sent an incorrect final reply"); return FALSE; invalid: g_set_error (error, WOCKY_AUTH_ERROR, WOCKY_AUTH_ERROR_INVALID_REPLY, "Server sent an invalid final reply"); return FALSE; } static gboolean scram_handle_auth_data (WockyAuthHandler *handler, const GString *data, GString **response, GError **error) { WockySaslScram *self = WOCKY_SASL_SCRAM (handler); WockySaslScramPrivate *priv = self->priv; gboolean ret = FALSE; DEBUG ("Got server message: %s", data->str); switch (priv->state) { case WOCKY_SASL_SCRAM_STATE_SERVER_FIRST_MESSAGE: priv->server_first_bare = g_strdup (data->str); if (!(ret = scram_handle_server_first_message (self, data->str, response, error))) goto out; priv->state = WOCKY_SASL_SCRAM_STATE_SERVER_FINAL_MESSAGE; break; case WOCKY_SASL_SCRAM_STATE_SERVER_FINAL_MESSAGE: if (!(ret = scram_handle_server_final_message (self, data->str, error))) goto out; priv->state = WOCKY_SASL_SCRAM_STATE_FINISHED; break; default: g_set_error (error, WOCKY_AUTH_ERROR, WOCKY_AUTH_ERROR_INVALID_REPLY, "Server sent an unexpected reply"); goto out; } out: return ret; } static gboolean scram_handle_success (WockyAuthHandler *handler, GError **error) { WockySaslScram *self = WOCKY_SASL_SCRAM (handler); WockySaslScramPrivate *priv = self->priv; if (priv->state == WOCKY_SASL_SCRAM_STATE_FINISHED) return TRUE; g_set_error (error, WOCKY_AUTH_ERROR, WOCKY_AUTH_ERROR_INVALID_REPLY, "Server sent success before finishing authentication"); return FALSE; } telepathy-gabble-0.18.2/lib/ext/wocky/wocky/wocky-sasl-digest-md5.c0000644000175000017500000003220412200204546025047 0ustar00smcvsmcv00000000000000#ifdef HAVE_CONFIG_H #include "config.h" #endif #include "wocky-sasl-digest-md5.h" #include "wocky-auth-registry.h" #include "wocky-sasl-utils.h" #include #define WOCKY_DEBUG_FLAG WOCKY_DEBUG_AUTH #include "wocky-debug-internal.h" typedef enum { WOCKY_SASL_DIGEST_MD5_STATE_STARTED, WOCKY_SASL_DIGEST_MD5_STATE_SENT_AUTH_RESPONSE, WOCKY_SASL_DIGEST_MD5_STATE_SENT_FINAL_RESPONSE, } WockySaslDigestMd5State; static void auth_handler_iface_init (gpointer g_iface); G_DEFINE_TYPE_WITH_CODE (WockySaslDigestMd5, wocky_sasl_digest_md5, G_TYPE_OBJECT, G_IMPLEMENT_INTERFACE ( WOCKY_TYPE_AUTH_HANDLER, auth_handler_iface_init)) enum { PROP_SERVER = 1, PROP_USERNAME, PROP_PASSWORD }; struct _WockySaslDigestMd5Private { WockySaslDigestMd5State state; gchar *username; gchar *password; gchar *server; gchar *digest_md5_rspauth; }; static void wocky_sasl_digest_md5_get_property ( GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { WockySaslDigestMd5 *self = (WockySaslDigestMd5 *) object; WockySaslDigestMd5Private *priv = self->priv; switch (property_id) { case PROP_USERNAME: g_value_set_string (value, priv->username); break; case PROP_PASSWORD: g_value_set_string (value, priv->password); break; case PROP_SERVER: g_value_set_string (value, priv->server); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); } } static void wocky_sasl_digest_md5_set_property ( GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { WockySaslDigestMd5 *self = (WockySaslDigestMd5 *) object; WockySaslDigestMd5Private *priv = self->priv; switch (property_id) { case PROP_SERVER: g_free (priv->server); priv->server = g_value_dup_string (value); break; case PROP_USERNAME: g_free (priv->username); priv->username = 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 (object, property_id, pspec); } } static void wocky_sasl_digest_md5_dispose (GObject *object) { WockySaslDigestMd5 *self = (WockySaslDigestMd5 *) object; WockySaslDigestMd5Private *priv = self->priv; g_free (priv->server); g_free (priv->username); g_free (priv->password); g_free (priv->digest_md5_rspauth); G_OBJECT_CLASS (wocky_sasl_digest_md5_parent_class)->dispose (object); } static void wocky_sasl_digest_md5_class_init ( WockySaslDigestMd5Class *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); g_type_class_add_private (klass, sizeof (WockySaslDigestMd5Private)); object_class->dispose = wocky_sasl_digest_md5_dispose; object_class->set_property = wocky_sasl_digest_md5_set_property; object_class->get_property = wocky_sasl_digest_md5_get_property; g_object_class_install_property (object_class, PROP_SERVER, g_param_spec_string ("server", "server", "The name of the server we're authenticating to", NULL, G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (object_class, PROP_USERNAME, g_param_spec_string ("username", "username", "The username to authenticate with", NULL, G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (object_class, PROP_PASSWORD, g_param_spec_string ("password", "password", "The password to authenticate with", NULL, G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS)); } static gboolean digest_md5_handle_auth_data (WockyAuthHandler *handler, const GString *data, GString **response, GError **error); static gboolean digest_md5_handle_success (WockyAuthHandler *handler, GError **error); static void auth_handler_iface_init (gpointer g_iface) { WockyAuthHandlerIface *iface = g_iface; iface->mechanism = WOCKY_AUTH_MECH_SASL_DIGEST_MD5; iface->plain = FALSE; iface->auth_data_func = digest_md5_handle_auth_data; iface->success_func = digest_md5_handle_success; } static void wocky_sasl_digest_md5_init (WockySaslDigestMd5 *self) { self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, WOCKY_TYPE_SASL_DIGEST_MD5, WockySaslDigestMd5Private); self->priv->state = WOCKY_SASL_DIGEST_MD5_STATE_STARTED; } WockySaslDigestMd5 * wocky_sasl_digest_md5_new ( const gchar *server, const gchar *username, const gchar *password) { return g_object_new (WOCKY_TYPE_SASL_DIGEST_MD5, "server", server, "username", username, "password", password, NULL); } static gchar * strndup_unescaped (const gchar *str, gsize len) { const gchar *s; gchar *d, *ret; ret = g_malloc0 (len + 1); for (s = str, d = ret ; s < (str + len) ; s++, d++) { if (*s == '\\') s++; *d = *s; } return ret; } static GHashTable * digest_md5_challenge_to_hash (const GString * challenge) { GHashTable *result = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); const gchar *keystart, *keyend, *valstart; const gchar *c = challenge->str; gchar *key, *val; do { keystart = c; for (; *c != '\0' && *c != '='; c++) ; if (*c == '\0' || c == keystart) goto error; keyend = c; c++; /* eat any whitespace between the '=' and the '"' */ for (; g_ascii_isspace (*c); c++) ; if (*c == '"') { gboolean esc = FALSE; c++; valstart = c; /* " terminates a quoted value _unless_ we are in a \ escape */ /* \0 always terminates (end of string encountered) */ for (; *c != '\0' && (esc || *c != '"'); c++) { if (esc) esc = FALSE; /* we are in a \ char escape, finish it */ else esc = *c == '\\'; /* is this char \ (ie starting an escape) */ } if (*c == '\0' || c == valstart) goto error; val = strndup_unescaped (valstart, c - valstart); c++; } else { valstart = c; for (; *c != '\0' && *c != ','; c++) ; if (c == valstart) goto error; val = g_strndup (valstart, c - valstart); } /* the key is unguarded by '"' delimiters so any whitespace * * at either end should be discarded as irrelevant */ key = g_strndup (keystart, keyend - keystart); key = g_strstrip (key); DEBUG ("challenge '%s' = '%s'", key, val); g_hash_table_insert (result, key, val); /* eat any whitespace between the '"' and the next ',' */ for (; g_ascii_isspace (*c); c++) ; if (*c == ',') c++; } while (*c != '\0'); return result; error: DEBUG ("Failed to parse challenge: %s", challenge->str); g_hash_table_unref (result); return NULL; } static guint8 * md5_hash (gchar *value) { GChecksum *checksum; guint8 *result; gsize len; len = g_checksum_type_get_length (G_CHECKSUM_MD5); g_assert (len == 16); result = g_malloc (len); checksum = g_checksum_new (G_CHECKSUM_MD5); g_checksum_update (checksum, (guchar *) value, -1); g_checksum_get_digest (checksum, result, &len); g_checksum_free (checksum); g_assert (len == 16); return result; } static gchar * md5_hex_hash (gchar *value, gsize length) { return g_compute_checksum_for_string (G_CHECKSUM_MD5, value, length); } static GString * md5_prepare_response (WockySaslDigestMd5Private *priv, GHashTable *challenge, GError **error) { GString *response = g_string_new (""); const gchar *realm, *nonce; gchar *a1, *a1h, *a2, *a2h, *kd, *kdh; gchar *cnonce = NULL; gchar *tmp; guint8 *digest_md5; gsize len; if (priv->username == NULL || priv->password == NULL) { g_set_error (error, WOCKY_AUTH_ERROR, WOCKY_AUTH_ERROR_NO_CREDENTIALS, "No username or password provided"); goto error; } DEBUG ("Got username and password"); nonce = g_hash_table_lookup (challenge, "nonce"); if (nonce == NULL || nonce == '\0') { g_set_error (error, WOCKY_AUTH_ERROR, WOCKY_AUTH_ERROR_INVALID_REPLY, "Server didn't provide a nonce in the challenge"); goto error; } cnonce = sasl_generate_base64_nonce (); /* FIXME challenge can contain multiple realms */ realm = g_hash_table_lookup (challenge, "realm"); if (realm == NULL) { realm = priv->server; } /* FIXME properly escape values */ g_string_append_printf (response, "username=\"%s\"", priv->username); g_string_append_printf (response, ",realm=\"%s\"", realm); g_string_append_printf (response, ",digest-uri=\"xmpp/%s\"", priv->server); g_string_append_printf (response, ",nonce=\"%s\",nc=00000001", nonce); g_string_append_printf (response, ",cnonce=\"%s\"", cnonce); /* FIXME should check if auth is in the cop challenge val */ g_string_append_printf (response, ",qop=auth,charset=utf-8"); tmp = g_strdup_printf ("%s:%s:%s", priv->username, realm, priv->password); digest_md5 = md5_hash (tmp); g_free (tmp); a1 = g_strdup_printf ("0123456789012345:%s:%s", nonce, cnonce); len = strlen (a1); /* MD5 hash is 16 bytes */ memcpy (a1, digest_md5, 16); a1h = md5_hex_hash (a1, len); g_free (digest_md5); a2 = g_strdup_printf ("AUTHENTICATE:xmpp/%s", priv->server); a2h = md5_hex_hash (a2, -1); kd = g_strdup_printf ("%s:%s:00000001:%s:auth:%s", a1h, nonce, cnonce, a2h); kdh = md5_hex_hash (kd, -1); g_string_append_printf (response, ",response=%s", kdh); g_free (kd); g_free (kdh); g_free (a2); g_free (a2h); /* Calculate the response we expect from the server */ a2 = g_strdup_printf (":xmpp/%s", priv->server); a2h = md5_hex_hash (a2, -1); kd = g_strdup_printf ("%s:%s:00000001:%s:auth:%s", a1h, nonce, cnonce, a2h); g_free (priv->digest_md5_rspauth); priv->digest_md5_rspauth = md5_hex_hash (kd, -1); g_free (a1); g_free (a1h); g_free (a2); g_free (a2h); g_free (kd); out: g_free (cnonce); return response; error: g_string_free (response, TRUE); response = NULL; goto out; } static gboolean digest_md5_make_initial_response ( WockySaslDigestMd5Private *priv, GHashTable *challenge, GString **response, GError **error) { g_return_val_if_fail (response != NULL, FALSE); *response = md5_prepare_response (priv, challenge, error); if (*response == NULL) return FALSE; DEBUG ("Prepared response: %s", (*response)->str); priv->state = WOCKY_SASL_DIGEST_MD5_STATE_SENT_AUTH_RESPONSE; return TRUE; } static gboolean digest_md5_check_server_response ( WockySaslDigestMd5Private *priv, GHashTable *challenge, GError **error) { const gchar *rspauth; rspauth = g_hash_table_lookup (challenge, "rspauth"); if (rspauth == NULL) { g_set_error (error, WOCKY_AUTH_ERROR, WOCKY_AUTH_ERROR_INVALID_REPLY, "Server sent an invalid reply (no rspauth)"); return FALSE; } if (strcmp (priv->digest_md5_rspauth, rspauth)) { g_set_error (error, WOCKY_AUTH_ERROR, WOCKY_AUTH_ERROR_INVALID_REPLY, "Server sent an invalid reply (rspauth not matching)"); return FALSE; } priv->state = WOCKY_SASL_DIGEST_MD5_STATE_SENT_FINAL_RESPONSE; return TRUE; } static GHashTable * auth_data_to_hash (const GString *challenge, GError **error) { GHashTable *h = NULL; DEBUG ("Got digest-md5 challenge: %s", challenge->str); h = digest_md5_challenge_to_hash (challenge); if (h == NULL) g_set_error (error, WOCKY_AUTH_ERROR, WOCKY_AUTH_ERROR_INVALID_REPLY, "Server sent invalid auth data"); return h; } static gboolean digest_md5_handle_auth_data (WockyAuthHandler *handler, const GString *data, GString **response, GError **error) { WockySaslDigestMd5 *self = WOCKY_SASL_DIGEST_MD5 (handler); WockySaslDigestMd5Private *priv = self->priv; GHashTable *h; gboolean ret = FALSE; if (data == NULL) { DEBUG ("Expected auth data but didn't get any!"); g_set_error (error, WOCKY_AUTH_ERROR, WOCKY_AUTH_ERROR_INVALID_REPLY, "Expected auth data from the server, but didn't get any"); return FALSE; } h = auth_data_to_hash (data, error); if (h == NULL) return FALSE; switch (priv->state) { case WOCKY_SASL_DIGEST_MD5_STATE_STARTED: ret = digest_md5_make_initial_response (priv, h, response, error); break; case WOCKY_SASL_DIGEST_MD5_STATE_SENT_AUTH_RESPONSE: ret = digest_md5_check_server_response (priv, h, error); break; default: g_set_error (error, WOCKY_AUTH_ERROR, WOCKY_AUTH_ERROR_INVALID_REPLY, "Server sent unexpected auth data"); } g_hash_table_unref (h); return ret; } static gboolean digest_md5_handle_success (WockyAuthHandler *handler, GError **error) { WockySaslDigestMd5 *self = WOCKY_SASL_DIGEST_MD5 (handler); WockySaslDigestMd5Private *priv = self->priv; if (priv->state == WOCKY_SASL_DIGEST_MD5_STATE_SENT_FINAL_RESPONSE) return TRUE; g_set_error (error, WOCKY_AUTH_ERROR, WOCKY_AUTH_ERROR_INVALID_REPLY, "Server sent success before finishing authentication"); return FALSE; } telepathy-gabble-0.18.2/lib/ext/wocky/wocky/wocky-sasl-auth.c0000644000175000017500000004545712200204546024064 0ustar00smcvsmcv00000000000000/* * wocky-sasl-auth.c - Source for WockySaslAuth * Copyright (C) 2006-2009 Collabora Ltd. * @author Sjoerd Simons * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include "wocky-sasl-auth.h" #include "wocky-signals-marshal.h" #include "wocky-namespaces.h" #include "wocky-utils.h" #define WOCKY_DEBUG_FLAG WOCKY_DEBUG_AUTH #include "wocky-debug-internal.h" G_DEFINE_TYPE(WockySaslAuth, wocky_sasl_auth, G_TYPE_OBJECT) enum { PROP_SERVER = 1, PROP_USERNAME, PROP_PASSWORD, PROP_CONNECTION, PROP_AUTH_REGISTRY, }; /* private structure */ struct _WockySaslAuthPrivate { gboolean dispose_has_run; WockyXmppConnection *connection; gchar *username; gchar *password; gchar *server; GCancellable *cancel; GSimpleAsyncResult *result; WockyAuthRegistry *auth_registry; }; static void sasl_auth_stanza_received (GObject *source, GAsyncResult *res, gpointer user_data); static void wocky_sasl_auth_init (WockySaslAuth *self) { self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, WOCKY_TYPE_SASL_AUTH, WockySaslAuthPrivate); } static void wocky_sasl_auth_dispose (GObject *object); static void wocky_sasl_auth_finalize (GObject *object); static void wocky_sasl_auth_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { WockySaslAuth *sasl = WOCKY_SASL_AUTH (object); WockySaslAuthPrivate *priv = sasl->priv; switch (property_id) { case PROP_SERVER: g_free (priv->server); priv->server = g_value_dup_string (value); break; case PROP_USERNAME: g_free (priv->username); priv->username = g_value_dup_string (value); break; case PROP_PASSWORD: g_free (priv->password); priv->password = g_value_dup_string (value); break; case PROP_CONNECTION: priv->connection = g_value_dup_object (value); break; case PROP_AUTH_REGISTRY: if (g_value_get_object (value) == NULL) priv->auth_registry = wocky_auth_registry_new (); else priv->auth_registry = g_value_dup_object (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void wocky_sasl_auth_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { WockySaslAuth *sasl = WOCKY_SASL_AUTH (object); WockySaslAuthPrivate *priv = sasl->priv; switch (property_id) { case PROP_SERVER: g_value_set_string (value, priv->server); break; case PROP_CONNECTION: g_value_set_object (value, priv->connection); break; case PROP_AUTH_REGISTRY: g_value_set_object (value, priv->auth_registry); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void wocky_sasl_auth_class_init (WockySaslAuthClass *wocky_sasl_auth_class) { GObjectClass *object_class = G_OBJECT_CLASS (wocky_sasl_auth_class); GParamSpec *spec; g_type_class_add_private (wocky_sasl_auth_class, sizeof (WockySaslAuthPrivate)); object_class->set_property = wocky_sasl_auth_set_property; object_class->get_property = wocky_sasl_auth_get_property; spec = g_param_spec_string ("server", "server", "The name of the server", NULL, G_PARAM_READWRITE|G_PARAM_CONSTRUCT); g_object_class_install_property (object_class, PROP_SERVER, spec); spec = g_param_spec_string ("username", "username", "The username to authenticate with", NULL, G_PARAM_WRITABLE|G_PARAM_CONSTRUCT); g_object_class_install_property (object_class, PROP_USERNAME, spec); spec = g_param_spec_string ("password", "password", "The password to authenticate with", NULL, G_PARAM_WRITABLE|G_PARAM_CONSTRUCT); g_object_class_install_property (object_class, PROP_PASSWORD, spec); spec = g_param_spec_object ("connection", "connection", "The Xmpp connection to user", WOCKY_TYPE_XMPP_CONNECTION, G_PARAM_READWRITE|G_PARAM_CONSTRUCT_ONLY); g_object_class_install_property (object_class, PROP_CONNECTION, spec); spec = g_param_spec_object ("auth-registry", "Authentication Registry", "Authentication Registry", WOCKY_TYPE_AUTH_REGISTRY, G_PARAM_READWRITE|G_PARAM_CONSTRUCT_ONLY); g_object_class_install_property (object_class, PROP_AUTH_REGISTRY, spec); object_class->dispose = wocky_sasl_auth_dispose; object_class->finalize = wocky_sasl_auth_finalize; } void wocky_sasl_auth_dispose (GObject *object) { WockySaslAuth *self = WOCKY_SASL_AUTH (object); WockySaslAuthPrivate *priv = self->priv; if (priv->connection != NULL) g_object_unref (priv->connection); if (priv->auth_registry != NULL) g_object_unref (priv->auth_registry); if (G_OBJECT_CLASS (wocky_sasl_auth_parent_class)->dispose) G_OBJECT_CLASS (wocky_sasl_auth_parent_class)->dispose (object); } void wocky_sasl_auth_finalize (GObject *object) { WockySaslAuth *self = WOCKY_SASL_AUTH (object); WockySaslAuthPrivate *priv = self->priv; /* free any data held directly by the object here */ g_free (priv->server); g_free (priv->username); g_free (priv->password); G_OBJECT_CLASS (wocky_sasl_auth_parent_class)->finalize (object); } static void auth_reset (WockySaslAuth *sasl) { WockySaslAuthPrivate *priv = sasl->priv; g_free (priv->server); priv->server = NULL; if (priv->connection != NULL) { g_object_unref (priv->connection); priv->connection = NULL; } if (priv->cancel != NULL) { g_object_unref (priv->cancel); priv->cancel = NULL; } } static void auth_succeeded (WockySaslAuth *sasl) { WockySaslAuthPrivate *priv = sasl->priv; GSimpleAsyncResult *r; DEBUG ("Authentication succeeded"); auth_reset (sasl); r = priv->result; priv->result = NULL; g_simple_async_result_complete (r); g_object_unref (r); } static void auth_failed (WockySaslAuth *sasl, gint code, const gchar *format, ...) { gchar *message; va_list args; GSimpleAsyncResult *r; GError *error = NULL; WockySaslAuthPrivate *priv = sasl->priv; auth_reset (sasl); va_start (args, format); message = g_strdup_vprintf (format, args); va_end (args); DEBUG ("Authentication failed!: %s", message); r = priv->result; priv->result = NULL; error = g_error_new_literal (WOCKY_AUTH_ERROR, code, message); g_simple_async_result_set_from_error (r, error); wocky_auth_registry_failure (priv->auth_registry, error); g_simple_async_result_complete (r); g_object_unref (r); g_error_free (error); g_free (message); } static gboolean stream_error (WockySaslAuth *sasl, WockyStanza *stanza) { WockySaslAuthPrivate *priv = sasl->priv; GError *error = NULL; if (stanza == NULL) { auth_failed (sasl, WOCKY_AUTH_ERROR_CONNRESET, "Disconnected"); return TRUE; } if (wocky_stanza_extract_stream_error (stanza, &error)) { auth_failed (sasl, WOCKY_AUTH_ERROR_STREAM, "%s: %s", wocky_enum_to_nick (WOCKY_TYPE_XMPP_STREAM_ERROR, error->code), error->message); g_error_free (error); return TRUE; } if (g_cancellable_is_cancelled (priv->cancel)) { /* We got disconnected but we still had this stanza to process. Don't * bother with it. */ auth_failed (sasl, WOCKY_AUTH_ERROR_CONNRESET, "Disconnected"); return TRUE; } return FALSE; } WockySaslAuth * wocky_sasl_auth_new (const gchar *server, const gchar *username, const gchar *password, WockyXmppConnection *connection, WockyAuthRegistry *auth_registry) { return g_object_new (WOCKY_TYPE_SASL_AUTH, "server", server, "username", username, "password", password, "connection", connection, "auth-registry", auth_registry, NULL); } static GSList * wocky_sasl_auth_mechanisms_to_list (WockyNode *mechanisms) { GSList *result = NULL; WockyNode *mechanism; WockyNodeIter iter; if (mechanisms == NULL) return NULL; wocky_node_iter_init (&iter, mechanisms, "mechanism", NULL); while (wocky_node_iter_next (&iter, &mechanism)) result = g_slist_append (result, g_strdup (mechanism->content)); return result; } static void sasl_auth_got_failure (WockySaslAuth *sasl, WockyStanza *stanza, GError **error) { WockyNode *reason = NULL; if (wocky_stanza_get_top_node (stanza)->children != NULL) { /* TODO add a wocky xmpp node utility to either get the first child or * iterate the children list */ reason = (WockyNode *) wocky_stanza_get_top_node (stanza)->children->data; } /* TODO Handle the different error cases in a different way. i.e. * make it clear for the user if it's credentials were wrong, if the server * just has a temporary error or if the authentication procedure itself was * at fault (too weak, invalid mech etc) */ g_set_error (error, WOCKY_AUTH_ERROR, WOCKY_AUTH_ERROR_FAILURE, "Authentication failed: %s", reason == NULL ? "Unknown reason" : reason->name); } static GString * wocky_sasl_auth_decode_challenge (const gchar *challenge) { gchar *challenge_str; GString *challenge_data; gsize len; if (challenge == NULL) return g_string_new_len ("", 0); challenge_str = (gchar *) g_base64_decode (challenge, &len); challenge_data = g_string_new_len (challenge_str, len); g_free (challenge_str); return challenge_data; } static gchar * wocky_sasl_auth_encode_response (const GString *response_data) { if (response_data != NULL && response_data->len > 0) return g_base64_encode ((guchar *) response_data->str, response_data->len); return NULL; } static void wocky_sasl_auth_success_cb (GObject *source_object, GAsyncResult *res, gpointer user_data) { WockySaslAuth *self = (WockySaslAuth *) user_data; WockySaslAuthPrivate *priv = self->priv; GError *error = NULL; if (!wocky_auth_registry_success_finish (priv->auth_registry, res, &error)) { auth_failed (self, error->code, error->message); g_error_free (error); } else { auth_succeeded (self); } } static void wocky_sasl_auth_response_cb (GObject *source_object, GAsyncResult *res, gpointer user_data) { WockySaslAuth *self = (WockySaslAuth *) user_data; WockySaslAuthPrivate *priv = self->priv; WockyStanza *response_stanza; GString *response_data = NULL; gchar *response; GError *error = NULL; if (!wocky_auth_registry_challenge_finish (priv->auth_registry, res, &response_data, &error)) { auth_failed (self, error->code, error->message); g_error_free (error); return; } response = wocky_sasl_auth_encode_response (response_data); response_stanza = wocky_stanza_new ("response", WOCKY_XMPP_NS_SASL_AUTH); wocky_node_set_content (wocky_stanza_get_top_node (response_stanza), response); /* FIXME handle send error */ wocky_xmpp_connection_send_stanza_async ( priv->connection, response_stanza, NULL, NULL, NULL); wocky_xmpp_connection_recv_stanza_async (priv->connection, NULL, sasl_auth_stanza_received, self); if (response_data != NULL) g_string_free (response_data, TRUE); g_free (response); g_object_unref (response_stanza); } static void wocky_sasl_auth_success_response_cb (GObject *source_object, GAsyncResult *res, gpointer user_data) { WockySaslAuth *self = (WockySaslAuth *) user_data; WockySaslAuthPrivate *priv = self->priv; GError *error = NULL; GString *response_data; if (!wocky_auth_registry_challenge_finish (priv->auth_registry, res, &response_data, &error)) { auth_failed (self, error->code, error->message); g_error_free (error); return; } if (response_data != NULL) { auth_failed (self, WOCKY_AUTH_ERROR_INVALID_REPLY, "Got success from the server while we still had more data to " "send"); g_string_free (response_data, TRUE); return; } wocky_auth_registry_success_async (priv->auth_registry, wocky_sasl_auth_success_cb, self); } static void sasl_auth_stanza_received (GObject *source, GAsyncResult *res, gpointer user_data) { WockySaslAuth *sasl = WOCKY_SASL_AUTH (user_data); WockySaslAuthPrivate *priv = sasl->priv; WockyStanza *stanza; GError *error = NULL; stanza = wocky_xmpp_connection_recv_stanza_finish ( WOCKY_XMPP_CONNECTION (priv->connection), res, NULL); if (stream_error (sasl, stanza)) return; if (wocky_strdiff ( wocky_node_get_ns (wocky_stanza_get_top_node (stanza)), WOCKY_XMPP_NS_SASL_AUTH)) { auth_failed (sasl, WOCKY_AUTH_ERROR_INVALID_REPLY, "Server sent a reply not in the %s namespace", WOCKY_XMPP_NS_SASL_AUTH); return; } /* If the SASL async result is _complete()d in the handler, the SASL object * * will be unref'd, which means the ref count could fall to zero while we * * are still using it. grab aref to it and drop it after we are sure that * * we don't need it anymore: */ g_object_ref (sasl); if (!wocky_strdiff (wocky_stanza_get_top_node (stanza)->name, "challenge")) { GString *challenge; challenge = wocky_sasl_auth_decode_challenge ( wocky_stanza_get_top_node (stanza)->content); wocky_auth_registry_challenge_async (priv->auth_registry, challenge, wocky_sasl_auth_response_cb, sasl); g_string_free (challenge, TRUE); } else if (!wocky_strdiff (wocky_stanza_get_top_node (stanza)->name, "success")) { if (wocky_stanza_get_top_node (stanza)->content != NULL) { GString *challenge; challenge = wocky_sasl_auth_decode_challenge ( wocky_stanza_get_top_node (stanza)->content); wocky_auth_registry_challenge_async (priv->auth_registry, challenge, wocky_sasl_auth_success_response_cb, sasl); g_string_free (challenge, TRUE); } else { wocky_auth_registry_success_async (priv->auth_registry, wocky_sasl_auth_success_cb, sasl); } } else if (!wocky_strdiff (wocky_stanza_get_top_node (stanza)->name, "failure")) { sasl_auth_got_failure (sasl, stanza, &error); g_assert (error != NULL); auth_failed (sasl, error->code, error->message); g_error_free (error); } else { auth_failed (sasl, WOCKY_AUTH_ERROR_INVALID_REPLY, "Server sent an invalid reply (%s)", wocky_stanza_get_top_node (stanza)->name); } g_object_unref (sasl); g_object_unref (stanza); return; } static void sasl_auth_stanza_sent (GObject *source, GAsyncResult *res, gpointer user_data) { GError *error = NULL; WockyXmppConnection *connection = WOCKY_XMPP_CONNECTION (source); WockySaslAuth *self = user_data; WockySaslAuthPrivate *priv = self->priv; if (!wocky_xmpp_connection_send_stanza_finish (connection, res, &error)) { auth_failed (self, error->code, error->message); g_error_free (error); return; } wocky_xmpp_connection_recv_stanza_async (priv->connection, priv->cancel, sasl_auth_stanza_received, self); } gboolean wocky_sasl_auth_authenticate_finish (WockySaslAuth *sasl, GAsyncResult *result, GError **error) { wocky_implement_finish_void (sasl, wocky_sasl_auth_authenticate_async); } static void wocky_sasl_auth_start_cb (GObject *source_object, GAsyncResult *res, gpointer user_data) { WockySaslAuth *self = (WockySaslAuth *) user_data; WockySaslAuthPrivate *priv = self->priv; WockyStanza *stanza; GError *error = NULL; WockyAuthRegistryStartData *start_data = NULL; if (!wocky_auth_registry_start_auth_finish (priv->auth_registry, res, &start_data, &error)) { auth_failed (self, error->code, error->message); g_error_free (error); return; } stanza = wocky_stanza_new ("auth", WOCKY_XMPP_NS_SASL_AUTH); /* google JID domain discovery - client sets a namespaced attribute */ wocky_node_set_attribute_ns (wocky_stanza_get_top_node (stanza), "client-uses-full-bind-result", "true", WOCKY_GOOGLE_NS_AUTH); if (start_data->initial_response != NULL) { gchar *initial_response_str = wocky_sasl_auth_encode_response ( start_data->initial_response); wocky_node_set_content ( wocky_stanza_get_top_node (stanza), initial_response_str); g_free (initial_response_str); } wocky_node_set_attribute (wocky_stanza_get_top_node (stanza), "mechanism", start_data->mechanism); wocky_xmpp_connection_send_stanza_async (priv->connection, stanza, priv->cancel, sasl_auth_stanza_sent, self); wocky_auth_registry_start_data_free (start_data); g_object_unref (stanza); } /* Initiate sasl auth. features should contain the stream features stanza as * receiver from the server */ void wocky_sasl_auth_authenticate_async (WockySaslAuth *sasl, WockyStanza *features, gboolean allow_plain, gboolean is_secure, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { WockySaslAuthPrivate *priv = sasl->priv; WockyNode *mech_node; GSList *mechanisms, *t; g_assert (sasl != NULL); g_assert (features != NULL); mech_node = wocky_node_get_child_ns ( wocky_stanza_get_top_node (features), "mechanisms", WOCKY_XMPP_NS_SASL_AUTH); mechanisms = wocky_sasl_auth_mechanisms_to_list (mech_node); if (G_UNLIKELY (mechanisms == NULL)) { g_simple_async_report_error_in_idle (G_OBJECT (sasl), callback, user_data, WOCKY_AUTH_ERROR, WOCKY_AUTH_ERROR_NOT_SUPPORTED, "Server doesn't have any sasl mechanisms"); goto out; } priv->result = g_simple_async_result_new (G_OBJECT (sasl), callback, user_data, wocky_sasl_auth_authenticate_async); if (cancellable != NULL) priv->cancel = g_object_ref (cancellable); wocky_auth_registry_start_auth_async (priv->auth_registry, mechanisms, allow_plain, is_secure, priv->username, priv->password, priv->server, NULL, wocky_sasl_auth_start_cb, sasl); out: for (t = mechanisms ; t != NULL; t = g_slist_next (t)) { g_free (t->data); } g_slist_free (mechanisms); } telepathy-gabble-0.18.2/lib/ext/wocky/wocky/wocky-roster.c0000644000175000017500000011402512200204546023465 0ustar00smcvsmcv00000000000000/* * wocky-roster.c - Source for WockyRoster * Copyright (C) 2009 Collabora Ltd. * @author Jonny Lamb * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 */ /** * SECTION: wocky-roster * @title: WockyRoster * @short_description: TODO * * TODO */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include "wocky-roster.h" #include "wocky-bare-contact.h" #include "wocky-c2s-porter.h" #include "wocky-namespaces.h" #include "wocky-stanza.h" #include "wocky-utils.h" #include "wocky-signals-marshal.h" #include "wocky-contact-factory.h" #include "wocky-porter.h" #include "wocky-session.h" #define WOCKY_DEBUG_FLAG WOCKY_DEBUG_ROSTER #include "wocky-debug-internal.h" #define GOOGLE_ROSTER_VERSION "2" G_DEFINE_TYPE (WockyRoster, wocky_roster, G_TYPE_OBJECT) typedef struct { WockyRoster *self; /* The result of the already-sent operation */ /* List of GSimpleAsyncResult to which an IQ has been sent to the server and * that will be completed once we receive the reply of this IQ. */ GSList *flying_operations; gchar *jid; /* The future name of the contact, or NULL to not change it */ gchar *new_name; /* (const gchar *) => TRUE * Each key is a group to add to the contact */ GHashTable *groups_to_add; /* owned (gchar *) => TRUE * Each key is a group to remove from the contact */ GHashTable *groups_to_remove; /* TRUE if a 'add' operation is waiting */ gboolean add_contact; /* TRUE if a 'remove' operation is waiting */ gboolean remove_contact; /* List of GSimpleAsyncResult that will be completed once the changes stored * in this PendingOperation structure will have be completed */ GSList *waiting_operations; } PendingOperation; static PendingOperation * pending_operation_new (WockyRoster *self, GSimpleAsyncResult *result, const gchar *jid) { PendingOperation *pending = g_slice_new0 (PendingOperation); g_assert (self != NULL); g_assert (result != NULL); g_assert (jid != NULL); pending->self = g_object_ref (self); pending->flying_operations = g_slist_append (pending->flying_operations, result); pending->jid = g_strdup (jid); pending->groups_to_add = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); pending->groups_to_remove = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); return pending; } static void pending_operation_free (PendingOperation *pending) { g_object_unref (pending->self); g_free (pending->new_name); g_free (pending->jid); g_slist_foreach (pending->flying_operations, (GFunc) g_object_unref, NULL); g_slist_free (pending->flying_operations); g_slist_foreach (pending->waiting_operations, (GFunc) g_object_unref, NULL); g_slist_free (pending->waiting_operations); g_hash_table_unref (pending->groups_to_add); g_hash_table_unref (pending->groups_to_remove); g_slice_free (PendingOperation, pending); } static void pending_operation_set_new_name (PendingOperation *pending, const gchar *name) { g_free (pending->new_name); pending->new_name = g_strdup (name); } static void pending_operation_set_groups (PendingOperation *pending, GStrv groups) { guint i; g_hash_table_remove_all (pending->groups_to_add); g_hash_table_remove_all (pending->groups_to_remove); for (i = 0; groups[i] != NULL; i++) g_hash_table_insert (pending->groups_to_add, g_strdup (groups[i]), GUINT_TO_POINTER (TRUE)); } static void pending_operation_add_group (PendingOperation *pending, const gchar *group) { g_hash_table_insert (pending->groups_to_add, g_strdup (group), GUINT_TO_POINTER (TRUE)); g_hash_table_remove (pending->groups_to_remove, group); } static void pending_operation_remove_group (PendingOperation *pending, const gchar *group) { g_hash_table_insert (pending->groups_to_remove, g_strdup (group), GUINT_TO_POINTER (TRUE)); g_hash_table_remove (pending->groups_to_add, group); } static void pending_operation_set_add (PendingOperation *pending) { pending->add_contact = TRUE; pending->remove_contact = FALSE; } static void pending_operation_set_remove (PendingOperation *pending) { pending->add_contact = FALSE; pending->remove_contact = TRUE; } static void pending_operation_add_waiting_operation (PendingOperation *pending, GSimpleAsyncResult *result) { pending->waiting_operations = g_slist_append (pending->waiting_operations, result); } static gboolean pending_operation_has_changes (PendingOperation *pending) { if (pending->new_name != NULL) return TRUE; if (g_hash_table_size (pending->groups_to_add) > 0) return TRUE; if (g_hash_table_size (pending->groups_to_remove) > 0) return TRUE; if (pending->add_contact) return TRUE; if (pending->remove_contact) return TRUE; return FALSE; } /* Called when the flying operations have been completed and the IQ to complete * the waiting operations has been sent. */ static void pending_operation_reset (PendingOperation *pending) { /* Free old flying operations */ g_slist_foreach (pending->flying_operations, (GFunc) g_object_unref, NULL); g_slist_free (pending->flying_operations); /* Previously waiting operations are now flying */ pending->flying_operations = pending->waiting_operations; pending->waiting_operations = NULL; } /* properties */ enum { PROP_SESSION = 1, }; /* signal enum */ enum { ADDED, REMOVED, LAST_SIGNAL, }; static guint signals[LAST_SIGNAL] = {0}; /* private structure */ struct _WockyRosterPrivate { WockySession *session; WockyPorter *porter; WockyContactFactory *contact_factory; /* owned (gchar *) => reffed (WockyBareContact *) */ GHashTable *items; guint iq_cb; /* owned (gchar *) => owned (PendingOperation *) * When an edit attempt is "in-flight", we store a PendingOperation * in * this hash table which will be used to store required edits until the one * we already sent is acknowledged. This prevents some race conditions. */ GHashTable *pending_operations; GSimpleAsyncResult *fetch_result; gboolean dispose_has_run; }; /** * wocky_roster_error_quark * * Get the error quark used by the roster. * * Returns: the quark for roster errors. */ GQuark wocky_roster_error_quark (void) { static GQuark quark = 0; if (quark == 0) quark = g_quark_from_static_string ("wocky-roster-error"); return quark; } static void change_roster_iq_cb (GObject *source_object, GAsyncResult *send_iq_res, gpointer user_data); static void wocky_roster_init (WockyRoster *self) { self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, WOCKY_TYPE_ROSTER, WockyRosterPrivate); } static void wocky_roster_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { WockyRoster *self = WOCKY_ROSTER (object); WockyRosterPrivate *priv = self->priv; switch (property_id) { case PROP_SESSION: /* Don't keep a ref on the session at the session keeps a ref on the * roster */ priv->session = g_value_get_object (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void wocky_roster_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { WockyRoster *self = WOCKY_ROSTER (object); WockyRosterPrivate *priv = self->priv; switch (property_id) { case PROP_SESSION: g_value_set_object (value, priv->session); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } const gchar * wocky_roster_subscription_to_string (WockyRosterSubscriptionFlags subscription) { switch (subscription) { case WOCKY_ROSTER_SUBSCRIPTION_TYPE_NONE: return "none"; case WOCKY_ROSTER_SUBSCRIPTION_TYPE_TO: return "to"; case WOCKY_ROSTER_SUBSCRIPTION_TYPE_FROM: return "from"; case WOCKY_ROSTER_SUBSCRIPTION_TYPE_BOTH: return "both"; default: g_assert_not_reached (); return NULL; } } static void remove_item (WockyRoster *self, const gchar *jid) { WockyRosterPrivate *priv = self->priv; WockyBareContact *contact; contact = g_hash_table_lookup (priv->items, jid); if (contact == NULL) { DEBUG ("%s is not in the roster; can't remove it", jid); return; } /* Removing the contact from the hash table will unref it. Keep it a ref * while firing the 'removed' signal. */ g_object_ref (contact); g_hash_table_remove (priv->items, jid); g_signal_emit (self, signals[REMOVED], 0, contact); g_object_unref (contact); } static gboolean roster_update (WockyRoster *self, WockyStanza *stanza, gboolean fire_signals, GError **error) { WockyRosterPrivate *priv = self->priv; WockyNode *query_node; GSList *j; /* Check stanza contains query node. */ query_node = wocky_node_get_child_ns ( wocky_stanza_get_top_node (stanza), "query", WOCKY_XMPP_NS_ROSTER); if (query_node == NULL) { g_set_error_literal (error, WOCKY_ROSTER_ERROR, WOCKY_ROSTER_ERROR_INVALID_STANZA, "IQ does not have query node"); return FALSE; } /* Iterate through item nodes. */ for (j = query_node->children; j; j = j->next) { const gchar *jid; WockyNode *n = (WockyNode *) j->data; WockyBareContact *contact = NULL; const gchar *subscription; WockyRosterSubscriptionFlags subscription_type; GPtrArray *groups_arr; GStrv groups = { NULL }; GSList *l; if (wocky_strdiff (n->name, "item")) { DEBUG ("Node %s is not item, skipping", n->name); continue; } jid = wocky_node_get_attribute (n, "jid"); if (jid == NULL) { DEBUG ("Node %s has no jid attribute, skipping", n->name); continue; } if (strchr (jid, '/') != NULL) { DEBUG ("Item node has resource in jid, skipping"); continue; } /* Parse item. */ subscription = wocky_node_get_attribute (n, "subscription"); if (!wocky_strdiff (subscription, "to")) subscription_type = WOCKY_ROSTER_SUBSCRIPTION_TYPE_TO; else if (!wocky_strdiff (subscription, "from")) subscription_type = WOCKY_ROSTER_SUBSCRIPTION_TYPE_FROM; else if (!wocky_strdiff (subscription, "both")) subscription_type = WOCKY_ROSTER_SUBSCRIPTION_TYPE_BOTH; else if (!wocky_strdiff (subscription, "none")) subscription_type = WOCKY_ROSTER_SUBSCRIPTION_TYPE_NONE; else if (!wocky_strdiff (subscription, "remove")) { remove_item (self, jid); continue; } else { DEBUG ("Unknown subscription: %s; ignoring", subscription); continue; } groups_arr = g_ptr_array_new (); /* Look for "group" nodes */ for (l = n->children; l != NULL; l = g_slist_next (l)) { WockyNode *node = (WockyNode *) l->data; if (wocky_strdiff (node->name, "group")) continue; g_ptr_array_add (groups_arr, g_strdup (node->content)); } /* Add trailing NULL */ g_ptr_array_add (groups_arr, NULL); groups = (GStrv) g_ptr_array_free (groups_arr, FALSE); contact = g_hash_table_lookup (priv->items, jid); if (contact != NULL) { /* Contact already exists; update. */ wocky_bare_contact_set_name (contact, wocky_node_get_attribute (n, "name")); wocky_bare_contact_set_subscription (contact, subscription_type); wocky_bare_contact_set_groups (contact, groups); } else { /* Create a new contact. */ contact = wocky_contact_factory_ensure_bare_contact ( priv->contact_factory, jid); g_object_set (contact, "name", wocky_node_get_attribute (n, "name"), "subscription", subscription_type, "groups", groups, NULL); g_hash_table_insert (priv->items, g_strdup (jid), contact); DEBUG ("New contact added:"); wocky_bare_contact_debug_print (contact); if (fire_signals) g_signal_emit (self, signals[ADDED], 0, contact); } g_strfreev (groups); } return TRUE; } static gboolean roster_iq_handler_set_cb (WockyPorter *porter, WockyStanza *stanza, gpointer user_data) { WockyRoster *self = WOCKY_ROSTER (user_data); GError *error = NULL; WockyStanza *reply; if (!roster_update (self, stanza, TRUE, &error)) { DEBUG ("Failed to update roster: %s", error ? error->message : "no message"); g_error_free (error); reply = wocky_stanza_build_iq_error (stanza, NULL); } else { /* ack */ reply = wocky_stanza_build_iq_result (stanza, NULL); } if (reply != NULL) { wocky_porter_send (porter, reply); g_object_unref (reply); } return TRUE; } static void wocky_roster_constructed (GObject *object) { WockyRoster *self = WOCKY_ROSTER (object); WockyRosterPrivate *priv = self->priv; priv->items = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref); priv->pending_operations = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify) pending_operation_free); g_assert (priv->session != NULL); priv->porter = wocky_session_get_porter (priv->session); g_assert (priv->porter != NULL); g_object_ref (priv->porter); priv->iq_cb = wocky_c2s_porter_register_handler_from_server ( WOCKY_C2S_PORTER (priv->porter), WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_SET, WOCKY_PORTER_HANDLER_PRIORITY_NORMAL, roster_iq_handler_set_cb, self, '(', "query", ':', WOCKY_XMPP_NS_ROSTER, ')', NULL); priv->contact_factory = wocky_session_get_contact_factory (priv->session); g_assert (priv->contact_factory != NULL); g_object_ref (priv->contact_factory); } static void wocky_roster_dispose (GObject *object) { WockyRoster *self = WOCKY_ROSTER (object); WockyRosterPrivate *priv = self->priv; if (priv->dispose_has_run) return; priv->dispose_has_run = TRUE; if (priv->iq_cb != 0) { wocky_porter_unregister_handler (priv->porter, priv->iq_cb); priv->iq_cb = 0; } g_object_unref (priv->porter); g_object_unref (priv->contact_factory); if (G_OBJECT_CLASS (wocky_roster_parent_class)->dispose) G_OBJECT_CLASS (wocky_roster_parent_class)->dispose (object); } static void wocky_roster_finalize (GObject *object) { WockyRoster *self = WOCKY_ROSTER (object); WockyRosterPrivate *priv = self->priv; g_hash_table_unref (priv->items); g_hash_table_unref (priv->pending_operations); G_OBJECT_CLASS (wocky_roster_parent_class)->finalize (object); } static void wocky_roster_class_init (WockyRosterClass *wocky_roster_class) { GObjectClass *object_class = G_OBJECT_CLASS (wocky_roster_class); GParamSpec *spec; g_type_class_add_private (wocky_roster_class, sizeof (WockyRosterPrivate)); object_class->constructed = wocky_roster_constructed; object_class->set_property = wocky_roster_set_property; object_class->get_property = wocky_roster_get_property; object_class->dispose = wocky_roster_dispose; object_class->finalize = wocky_roster_finalize; spec = g_param_spec_object ("session", "Wocky session", "the wocky session used by this roster", WOCKY_TYPE_SESSION, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, PROP_SESSION, spec); signals[ADDED] = g_signal_new ("added", G_OBJECT_CLASS_TYPE (wocky_roster_class), G_SIGNAL_RUN_LAST, 0, NULL, NULL, _wocky_signals_marshal_VOID__OBJECT, G_TYPE_NONE, 1, G_TYPE_OBJECT); signals[REMOVED] = g_signal_new ("removed", G_OBJECT_CLASS_TYPE (wocky_roster_class), G_SIGNAL_RUN_LAST, 0, NULL, NULL, _wocky_signals_marshal_VOID__OBJECT, G_TYPE_NONE, 1, G_TYPE_OBJECT); } WockyRoster * wocky_roster_new (WockySession *session) { g_return_val_if_fail (WOCKY_IS_SESSION (session), NULL); return g_object_new (WOCKY_TYPE_ROSTER, "session", session, NULL); } static void roster_fetch_roster_cb (GObject *source_object, GAsyncResult *res, gpointer user_data) { GError *error = NULL; WockyStanza *iq; WockyRoster *self = WOCKY_ROSTER (user_data); WockyRosterPrivate *priv = self->priv; iq = wocky_porter_send_iq_finish (WOCKY_PORTER (source_object), res, &error); if (iq == NULL) goto out; if (!roster_update (self, iq, FALSE, &error)) goto out; out: if (error != NULL) { g_simple_async_result_set_from_error (priv->fetch_result, error); g_error_free (error); } if (iq != NULL) g_object_unref (iq); g_simple_async_result_complete (priv->fetch_result); g_object_unref (priv->fetch_result); priv->fetch_result = NULL; } void wocky_roster_fetch_roster_async (WockyRoster *self, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { WockyRosterPrivate *priv; WockyStanza *iq; g_return_if_fail (WOCKY_IS_ROSTER (self)); priv = self->priv; if (priv->fetch_result != NULL) { g_simple_async_report_error_in_idle (G_OBJECT (self), callback, user_data, G_IO_ERROR, G_IO_ERROR_PENDING, "Another fetch operation is pending"); return; } iq = wocky_stanza_build (WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_GET, NULL, NULL, '(', "query", ':', WOCKY_XMPP_NS_ROSTER, ')', NULL); priv->fetch_result = g_simple_async_result_new (G_OBJECT (self), callback, user_data, wocky_roster_fetch_roster_async); wocky_porter_send_iq_async (priv->porter, iq, cancellable, roster_fetch_roster_cb, self); g_object_unref (iq); } gboolean wocky_roster_fetch_roster_finish (WockyRoster *self, GAsyncResult *result, GError **error) { if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), error)) return FALSE; g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (self), wocky_roster_fetch_roster_async), FALSE); return TRUE; } WockyBareContact * wocky_roster_get_contact (WockyRoster *self, const gchar *jid) { WockyRosterPrivate *priv = self->priv; return g_hash_table_lookup (priv->items, jid); } GSList * wocky_roster_get_all_contacts (WockyRoster *self) { WockyRosterPrivate *priv = self->priv; GSList *result = NULL; GHashTableIter iter; gpointer value; g_hash_table_iter_init (&iter, priv->items); while (g_hash_table_iter_next (&iter, NULL, &value)) { result = g_slist_prepend (result, g_object_ref (value)); } return result; } /* Build an IQ set stanza containing the current state of the contact. * If not NULL, item_node will contain a pointer on the "item" node */ static WockyStanza * build_iq_for_contact (WockyBareContact *contact, WockyNode **item_node) { WockyStanza *iq; WockyNode *item = NULL; const gchar *jid, *name; const gchar * const *groups; guint i; WockyRosterSubscriptionFlags subscription; jid = wocky_bare_contact_get_jid (contact); g_return_val_if_fail (jid != NULL, NULL); iq = wocky_stanza_build (WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_SET, NULL, NULL, '(', "query", ':', WOCKY_XMPP_NS_ROSTER, '(', "item", '*', &item, '@', "jid", jid, ')', ')', NULL); g_assert (item != NULL); name = wocky_bare_contact_get_name (contact); if (name != NULL) { wocky_node_set_attribute (item, "name", name); } subscription = wocky_bare_contact_get_subscription (contact); if (subscription != WOCKY_ROSTER_SUBSCRIPTION_TYPE_NONE) { wocky_node_set_attribute (item, "subscription", wocky_roster_subscription_to_string (subscription)); } groups = wocky_bare_contact_get_groups (contact); for (i = 0; groups != NULL && groups[i] != NULL; i++) { WockyNode *group; group = wocky_node_add_child (item, "group"); wocky_node_set_content (group, groups[i]); } if (item_node != NULL) *item_node = item; return iq; } static WockyStanza * build_remove_contact_iq (WockyBareContact *contact) { return wocky_stanza_build (WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_SET, NULL, NULL, '(', "query", ':', WOCKY_XMPP_NS_ROSTER, '(', "item", '@', "jid", wocky_bare_contact_get_jid (contact), '@', "subscription", "remove", ')', ')', NULL); } static WockyStanza * build_iq_for_pending (WockyRoster *self, PendingOperation *pending) { WockyRosterPrivate *priv = self->priv; WockyBareContact *contact, *tmp; WockyStanza *iq; GHashTableIter iter; gpointer group; contact = g_hash_table_lookup (priv->items, pending->jid); if (!pending_operation_has_changes (pending)) { /* Nothing to change */ return NULL; } /* There is no sense to try to add and remove a contact at the same time */ g_assert (!pending->add_contact || !pending->remove_contact); if (contact == NULL) { if (pending->remove_contact) { DEBUG ("Contact %s was already removed", pending->jid); return NULL; } else if (!pending->add_contact) { GSList *l; DEBUG ("contact is not in the roster any more"); for (l = pending->waiting_operations; l != NULL; l = g_slist_next (l)) { GSimpleAsyncResult *result = (GSimpleAsyncResult *) l->data; g_simple_async_result_set_error (result, WOCKY_ROSTER_ERROR, WOCKY_ROSTER_ERROR_NOT_IN_ROSTER, "Contact %s is not in the roster any more", pending->jid); } return NULL; } tmp = g_object_new (WOCKY_TYPE_BARE_CONTACT, "jid", pending->jid, NULL); } else { if (pending->remove_contact) { DEBUG ("Remove contact %s", pending->jid); return build_remove_contact_iq (contact); } tmp = wocky_bare_contact_copy (contact); } if (pending->new_name != NULL) wocky_bare_contact_set_name (tmp, pending->new_name); g_hash_table_iter_init (&iter, pending->groups_to_add); while (g_hash_table_iter_next (&iter, &group, NULL)) { wocky_bare_contact_add_group (tmp, (const gchar *) group); } g_hash_table_iter_init (&iter, pending->groups_to_remove); while (g_hash_table_iter_next (&iter, &group, NULL)) { wocky_bare_contact_remove_group (tmp, (const gchar *) group); } if (wocky_bare_contact_equal (contact, tmp)) { DEBUG ("No change needed"); g_object_unref (tmp); return NULL; } iq = build_iq_for_contact (tmp, NULL); g_object_unref (tmp); return iq; } static void waiting_operations_completed (WockyRoster *self, PendingOperation *pending) { GSList *l; for (l = pending->waiting_operations; l != NULL; l = g_slist_next (l)) { GSimpleAsyncResult *result = (GSimpleAsyncResult *) l->data; g_simple_async_result_complete (result); } } static void flying_operation_completed (PendingOperation *pending, GError *error) { WockyRoster *self = pending->self; WockyRosterPrivate *priv = self->priv; WockyStanza *iq; GSList *l; /* Flying operations are completed */ for (l = pending->flying_operations; l != NULL; l = g_slist_next (l)) { GSimpleAsyncResult *result = (GSimpleAsyncResult *) l->data; if (error != NULL) g_simple_async_result_set_from_error (result, error); g_simple_async_result_complete (result); } if (g_slist_length (pending->waiting_operations) == 0) { /* No waiting operation, we are done */ DEBUG ("No waiting operations"); g_hash_table_remove (priv->pending_operations, pending->jid); return; } iq = build_iq_for_pending (self, pending); if (iq == NULL) { /* No need to send an IQ; complete waiting operations right now */ DEBUG ("No need to send an IQ; complete waiting operations"); waiting_operations_completed (self, pending); g_hash_table_remove (priv->pending_operations, pending->jid); return; } pending_operation_reset (pending); wocky_porter_send_iq_async (priv->porter, iq, NULL, change_roster_iq_cb, pending); g_object_unref (iq); } static void change_roster_iq_cb (GObject *source_object, GAsyncResult *send_iq_res, gpointer user_data) { PendingOperation *pending = (PendingOperation *) user_data; WockyStanza *reply; GError *error = NULL; reply = wocky_porter_send_iq_finish (WOCKY_PORTER (source_object), send_iq_res, &error); if (reply == NULL) goto out; wocky_stanza_extract_errors (reply, NULL, &error, NULL, NULL); /* According to the XMPP RFC, the server has to send a roster upgrade to * each client (including the one which requested the change) before * replying to the 'set' stanza. We upgraded our list of contacts when this * notification has been received. * We can't really check that this upgrade has been actually receive as we * could receive other notifications from the server due to other clients * actions. So we just have to trust the server on this. */ out: if (reply != NULL) g_object_unref (reply); flying_operation_completed (pending, error); if (error != NULL) g_error_free (error); } static PendingOperation * get_pending_operation (WockyRoster *self, const gchar *jid) { WockyRosterPrivate *priv = self->priv; DEBUG ("Look for pending operation with contact %s", jid); return g_hash_table_lookup (priv->pending_operations, jid); } /* Creates a new PendingOperation structure and associates it with the given * jid. This function is used when starting a edit operation while there is no * flying operation with this contact atm. The PendingOperation won't contain * any information as this operation is going to be sent right away and so * doesn't have to be queued. */ static PendingOperation * add_pending_operation (WockyRoster *self, const gchar *jid, GSimpleAsyncResult *result) { WockyRosterPrivate *priv = self->priv; PendingOperation *pending = pending_operation_new (self, result, jid); DEBUG ("Add pending operation for %s", jid); g_hash_table_insert (priv->pending_operations, g_strdup (jid), pending); return pending; } void wocky_roster_add_contact_async (WockyRoster *self, const gchar *jid, const gchar *name, const gchar * const * groups, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { WockyRosterPrivate *priv = self->priv; WockyStanza *iq; GSimpleAsyncResult *result; WockyBareContact *contact, *existing_contact; PendingOperation *pending; g_return_if_fail (jid != NULL); result = g_simple_async_result_new (G_OBJECT (self), callback, user_data, wocky_roster_add_contact_async); pending = get_pending_operation (self, jid); if (pending != NULL) { DEBUG ("Another operation is pending for contact %s; queuing this one", jid); pending_operation_set_new_name (pending, name); pending_operation_set_groups (pending, (GStrv) groups); pending_operation_add_waiting_operation (pending, result); pending_operation_set_add (pending); return; } contact = g_object_new (WOCKY_TYPE_BARE_CONTACT, "jid", jid, NULL); if (name != NULL) wocky_bare_contact_set_name (contact, name); if (groups != NULL) wocky_bare_contact_set_groups (contact, (gchar **) groups); existing_contact = g_hash_table_lookup (priv->items, jid); if (existing_contact != NULL) { /* contact is already in the roster. Check if we need to change him. */ if (wocky_bare_contact_equal (contact, existing_contact)) { DEBUG ("Contact %s is already present in the roster; " "no need to change him", jid); g_simple_async_result_complete_in_idle (result); g_object_unref (contact); g_object_unref (result); return; } } iq = build_iq_for_contact (contact, NULL); pending = add_pending_operation (self, jid, result); wocky_porter_send_iq_async (priv->porter, iq, cancellable, change_roster_iq_cb, pending); /* A new contact object will be created and added when we'll receive the * server push notification. */ g_object_unref (contact); g_object_unref (iq); } gboolean wocky_roster_add_contact_finish (WockyRoster *self, GAsyncResult *result, GError **error) { if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), error)) return FALSE; g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (self), wocky_roster_add_contact_async), FALSE); return TRUE; } static gboolean is_contact (gpointer key, gpointer value, gpointer contact) { return value == contact; } static gboolean contact_in_roster (WockyRoster *self, WockyBareContact *contact) { WockyRosterPrivate *priv = self->priv; return g_hash_table_find (priv->items, is_contact, contact) != NULL; } void wocky_roster_remove_contact_async (WockyRoster *self, WockyBareContact *contact, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { WockyRosterPrivate *priv = self->priv; WockyStanza *iq; GSimpleAsyncResult *result; PendingOperation *pending; const gchar *jid; g_return_if_fail (contact != NULL); jid = wocky_bare_contact_get_jid (contact); result = g_simple_async_result_new (G_OBJECT (self), callback, user_data, wocky_roster_remove_contact_async); pending = get_pending_operation (self, jid); if (pending != NULL) { DEBUG ("Another operation is pending for contact %s; queuing this one", jid); pending_operation_set_remove (pending); pending_operation_add_waiting_operation (pending, result); return; } if (!contact_in_roster (self, contact)) { DEBUG ("Contact %s is not in the roster", wocky_bare_contact_get_jid ( contact)); g_simple_async_result_complete_in_idle (result); g_object_unref (result); return; } pending = add_pending_operation (self, jid, result); iq = build_remove_contact_iq (contact); wocky_porter_send_iq_async (priv->porter, iq, cancellable, change_roster_iq_cb, pending); g_object_unref (iq); } gboolean wocky_roster_remove_contact_finish (WockyRoster *self, GAsyncResult *result, GError **error) { if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), error)) return FALSE; g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (self), wocky_roster_remove_contact_async), FALSE); return TRUE; } void wocky_roster_change_contact_name_async (WockyRoster *self, WockyBareContact *contact, const gchar *name, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { WockyRosterPrivate *priv = self->priv; WockyStanza *iq; WockyNode *item; GSimpleAsyncResult *result; PendingOperation *pending; const gchar *jid; g_return_if_fail (contact != NULL); jid = wocky_bare_contact_get_jid (contact); result = g_simple_async_result_new (G_OBJECT (self), callback, user_data, wocky_roster_change_contact_name_async); pending = get_pending_operation (self, jid); if (pending != NULL) { DEBUG ("Another operation is pending for contact %s; queuing this one", jid); pending_operation_set_new_name (pending, name); pending_operation_add_waiting_operation (pending, result); return; } if (!contact_in_roster (self, contact)) { g_simple_async_report_error_in_idle (G_OBJECT (self), callback, user_data, WOCKY_ROSTER_ERROR, WOCKY_ROSTER_ERROR_NOT_IN_ROSTER, "Contact %s is not in the roster", wocky_bare_contact_get_jid (contact)); g_object_unref (result); return; } if (!wocky_strdiff (wocky_bare_contact_get_name (contact), name)) { DEBUG ("No need to change name; complete immediately"); g_simple_async_result_complete_in_idle (result); g_object_unref (result); return; } pending = add_pending_operation (self, jid, result); iq = build_iq_for_contact (contact, &item); /* set new name */ wocky_node_set_attribute (item, "name", name); wocky_porter_send_iq_async (priv->porter, iq, cancellable, change_roster_iq_cb, pending); g_object_unref (iq); } gboolean wocky_roster_change_contact_name_finish (WockyRoster *self, GAsyncResult *result, GError **error) { if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), error)) return FALSE; g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (self), wocky_roster_change_contact_name_async), FALSE); return TRUE; } void wocky_roster_contact_add_group_async (WockyRoster *self, WockyBareContact *contact, const gchar *group, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { WockyRosterPrivate *priv = self->priv; WockyStanza *iq; WockyNode *item, *group_node; GSimpleAsyncResult *result; PendingOperation *pending; const gchar *jid; g_return_if_fail (contact != NULL); jid = wocky_bare_contact_get_jid (contact); result = g_simple_async_result_new (G_OBJECT (self), callback, user_data, wocky_roster_contact_add_group_async); pending = get_pending_operation (self, jid); if (pending != NULL) { DEBUG ("Another operation is pending for contact %s; queuing this one", jid); pending_operation_add_group (pending, group); pending_operation_add_waiting_operation (pending, result); return; } if (!contact_in_roster (self, contact)) { g_simple_async_report_error_in_idle (G_OBJECT (self), callback, user_data, WOCKY_ROSTER_ERROR, WOCKY_ROSTER_ERROR_NOT_IN_ROSTER, "Contact %s is not in the roster", jid); g_object_unref (result); return; } if (wocky_bare_contact_in_group (contact, group)) { DEBUG ("Contact %s in already in group %s; complete immediately", wocky_bare_contact_get_jid (contact), group); g_simple_async_result_complete_in_idle (result); g_object_unref (result); return; } pending = add_pending_operation (self, jid, result); iq = build_iq_for_contact (contact, &item); /* add new group */ group_node = wocky_node_add_child (item, "group"); wocky_node_set_content (group_node, group); wocky_porter_send_iq_async (priv->porter, iq, cancellable, change_roster_iq_cb, pending); g_object_unref (iq); } gboolean wocky_roster_contact_add_group_finish (WockyRoster *self, GAsyncResult *result, GError **error) { if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), error)) return FALSE; g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (self), wocky_roster_contact_add_group_async), FALSE); return TRUE; } void wocky_roster_contact_remove_group_async (WockyRoster *self, WockyBareContact *contact, const gchar *group, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { WockyRosterPrivate *priv = self->priv; WockyStanza *iq; WockyNode *item; GSimpleAsyncResult *result; GSList *l; PendingOperation *pending; const gchar *jid; g_return_if_fail (contact != NULL); jid = wocky_bare_contact_get_jid (contact); result = g_simple_async_result_new (G_OBJECT (self), callback, user_data, wocky_roster_contact_remove_group_async); pending = get_pending_operation (self, jid); if (pending != NULL) { DEBUG ("Another operation is pending for contact %s; queuing this one", jid); pending_operation_remove_group (pending, group); pending_operation_add_waiting_operation (pending, result); return; } if (!contact_in_roster (self, contact)) { g_simple_async_report_error_in_idle (G_OBJECT (self), callback, user_data, WOCKY_ROSTER_ERROR, WOCKY_ROSTER_ERROR_NOT_IN_ROSTER, "Contact %s is not in the roster", jid); g_object_unref (result); return; } if (!wocky_bare_contact_in_group (contact, group)) { DEBUG ("Contact %s is not in group %s; complete immediately", jid, group); g_simple_async_result_complete_in_idle (result); g_object_unref (result); return; } pending = add_pending_operation (self, jid, result); iq = build_iq_for_contact (contact, &item); /* remove the group */ /* FIXME: should we add a wocky_node_remove_child () ? */ for (l = item->children; l != NULL; l = g_slist_next (l)) { WockyNode *group_node = (WockyNode *) l->data; if (wocky_strdiff (group_node->name, "group")) continue; if (!wocky_strdiff (group_node->content, group)) { wocky_node_free (group_node); item->children = g_slist_delete_link (item->children, l); break; } } wocky_porter_send_iq_async (priv->porter, iq, cancellable, change_roster_iq_cb, pending); g_object_unref (iq); } gboolean wocky_roster_contact_remove_group_finish (WockyRoster *self, GAsyncResult *result, GError **error) { if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), error)) return FALSE; g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (self), wocky_roster_contact_remove_group_async), FALSE); return TRUE; } telepathy-gabble-0.18.2/lib/ext/wocky/wocky/wocky-resource-contact.c0000644000175000017500000001545312200204546025434 0ustar00smcvsmcv00000000000000/* * wocky-resource-contact.c - Source for WockyResourceContact * Copyright (C) 2009 Collabora Ltd. * @author Guillaume Desmottes * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 */ /** * SECTION: wocky-resource-contact * @title: WockyResourceContact * @short_description: * @include: wocky/wocky-resource-contact.h * */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "wocky-resource-contact.h" #include #include #include #ifdef HAVE_UNISTD_H # include #endif #include #include "wocky-signals-marshal.h" #include "wocky-utils.h" #define WOCKY_DEBUG_FLAG WOCKY_DEBUG_ROSTER #include "wocky-debug-internal.h" G_DEFINE_TYPE (WockyResourceContact, wocky_resource_contact, WOCKY_TYPE_CONTACT) /* properties */ enum { PROP_RESOURCE = 1, PROP_BARE_CONTACT, }; /* signal enum */ enum { LAST_SIGNAL, }; /* static guint signals[LAST_SIGNAL] = {0}; */ /* private structure */ struct _WockyResourceContactPrivate { gboolean dispose_has_run; gchar *resource; WockyBareContact *bare_contact; }; static void wocky_resource_contact_init (WockyResourceContact *self) { self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, WOCKY_TYPE_RESOURCE_CONTACT, WockyResourceContactPrivate); } static void wocky_resource_contact_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { WockyResourceContact *self = WOCKY_RESOURCE_CONTACT (object); WockyResourceContactPrivate *priv = self->priv; switch (property_id) { case PROP_RESOURCE: priv->resource = g_value_dup_string (value); break; case PROP_BARE_CONTACT: priv->bare_contact = g_value_dup_object (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void wocky_resource_contact_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { WockyResourceContact *self = WOCKY_RESOURCE_CONTACT (object); WockyResourceContactPrivate *priv = self->priv; switch (property_id) { case PROP_RESOURCE: g_value_set_string (value, priv->resource); break; case PROP_BARE_CONTACT: g_value_set_object (value, priv->bare_contact); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void wocky_resource_contact_constructed (GObject *object) { WockyResourceContact *self = WOCKY_RESOURCE_CONTACT (object); WockyResourceContactPrivate *priv = self->priv; g_assert (priv->resource != NULL); g_assert (priv->bare_contact != NULL); } static void wocky_resource_contact_dispose (GObject *object) { WockyResourceContact *self = WOCKY_RESOURCE_CONTACT (object); WockyResourceContactPrivate *priv = self->priv; if (priv->dispose_has_run) return; priv->dispose_has_run = TRUE; g_object_unref (priv->bare_contact); if (G_OBJECT_CLASS (wocky_resource_contact_parent_class)->dispose) G_OBJECT_CLASS (wocky_resource_contact_parent_class)->dispose (object); } static void wocky_resource_contact_finalize (GObject *object) { WockyResourceContact *self = WOCKY_RESOURCE_CONTACT (object); WockyResourceContactPrivate *priv = self->priv; g_free (priv->resource); G_OBJECT_CLASS (wocky_resource_contact_parent_class)->finalize (object); } static gchar * wocky_resource_contact_dup_jid (WockyContact *contact) { WockyResourceContact *self = WOCKY_RESOURCE_CONTACT (contact); const gchar *bare = wocky_bare_contact_get_jid (self->priv->bare_contact); return g_strdup_printf ("%s/%s", bare, self->priv->resource); } static void wocky_resource_contact_class_init ( WockyResourceContactClass *wocky_resource_contact_class) { GObjectClass *object_class = G_OBJECT_CLASS (wocky_resource_contact_class); WockyContactClass *contact_class = WOCKY_CONTACT_CLASS (wocky_resource_contact_class); GParamSpec *spec; g_type_class_add_private (wocky_resource_contact_class, sizeof (WockyResourceContactPrivate)); object_class->constructed = wocky_resource_contact_constructed; object_class->set_property = wocky_resource_contact_set_property; object_class->get_property = wocky_resource_contact_get_property; object_class->dispose = wocky_resource_contact_dispose; object_class->finalize = wocky_resource_contact_finalize; contact_class->dup_jid = wocky_resource_contact_dup_jid; /** * WockyResourceContact:resource: * * The resource of the contact. */ spec = g_param_spec_string ("resource", "Contact resource", "Contact resource", NULL, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, PROP_RESOURCE, spec); /** * WockyResourceContact:bare-contact: * * The #WockyBareContact associated with this #WockyResourceContact */ spec = g_param_spec_object ("bare-contact", "Bare contact", "the WockyBareContact associated with this WockyResourceContact", WOCKY_TYPE_BARE_CONTACT, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, PROP_BARE_CONTACT, spec); } WockyResourceContact * wocky_resource_contact_new (WockyBareContact *bare, const gchar *resource) { return g_object_new (WOCKY_TYPE_RESOURCE_CONTACT, "bare-contact", bare, "resource", resource, NULL); } const gchar * wocky_resource_contact_get_resource (WockyResourceContact *self) { WockyResourceContactPrivate *priv = self->priv; return priv->resource; } WockyBareContact * wocky_resource_contact_get_bare_contact (WockyResourceContact *self) { WockyResourceContactPrivate *priv = self->priv; return priv->bare_contact; } gboolean wocky_resource_contact_equal (WockyResourceContact *a, WockyResourceContact *b) { if (a == NULL || b == NULL) return FALSE; if (wocky_strdiff (wocky_resource_contact_get_resource (a), wocky_resource_contact_get_resource (b))) return FALSE; return wocky_bare_contact_equal (wocky_resource_contact_get_bare_contact (a), wocky_resource_contact_get_bare_contact (b)); } telepathy-gabble-0.18.2/lib/ext/wocky/wocky/wocky-pubsub-service.c0000644000175000017500000007447412200204546025122 0ustar00smcvsmcv00000000000000/* * wocky-pubsub-service.c - WockyPubsubService * Copyright (C) 2009 Collabora Ltd. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "wocky-pubsub-service.h" #include "wocky-pubsub-service-protected.h" #include "wocky-porter.h" #include "wocky-utils.h" #include "wocky-pubsub-helpers.h" #include "wocky-pubsub-node.h" #include "wocky-pubsub-node-protected.h" #include "wocky-pubsub-node-internal.h" #include "wocky-namespaces.h" #include "wocky-signals-marshal.h" #define WOCKY_DEBUG_FLAG WOCKY_DEBUG_PUBSUB #include "wocky-debug-internal.h" static gboolean pubsub_service_propagate_event (WockyPorter *porter, WockyStanza *event_stanza, gpointer user_data); G_DEFINE_TYPE (WockyPubsubService, wocky_pubsub_service, G_TYPE_OBJECT) /* signal enum */ enum { SIG_EVENT_RECEIVED, SIG_SUB_STATE_CHANGED, SIG_NODE_DELETED, LAST_SIGNAL }; static guint signals[LAST_SIGNAL] = {0}; enum { PROP_SESSION = 1, PROP_JID, }; /* private structure */ typedef struct _EventTrampoline EventTrampoline; struct _EventTrampoline { const WockyPubsubNodeEventMapping *mapping; WockyPubsubService *self; guint handler_id; }; struct _WockyPubsubServicePrivate { WockySession *session; WockyPorter *porter; gchar *jid; /* owned (gchar *) => weak reffed (WockyPubsubNode *) */ GHashTable *nodes; /* slice-allocated (EventTrampoline *) s, used for handlers */ GPtrArray *trampolines; gboolean dispose_has_run; }; GQuark wocky_pubsub_service_error_quark (void) { static GQuark quark = 0; if (quark == 0) quark = g_quark_from_static_string ("wocky-pubsub-service-error"); return quark; } static void wocky_pubsub_service_init (WockyPubsubService *self) { self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, WOCKY_TYPE_PUBSUB_SERVICE, WockyPubsubServicePrivate); self->priv->nodes = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); } static void wocky_pubsub_service_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { WockyPubsubService *self = WOCKY_PUBSUB_SERVICE (object); WockyPubsubServicePrivate *priv = self->priv; switch (property_id) { case PROP_SESSION: priv->session = g_value_get_object (value); break; case PROP_JID: priv->jid = g_value_dup_string (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void wocky_pubsub_service_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { WockyPubsubService *self = WOCKY_PUBSUB_SERVICE (object); WockyPubsubServicePrivate *priv = self->priv; switch (property_id) { case PROP_SESSION: g_value_set_object (value, priv->session); break; case PROP_JID: g_value_set_string (value, priv->jid); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void wocky_pubsub_service_dispose (GObject *object) { WockyPubsubService *self = WOCKY_PUBSUB_SERVICE (object); WockyPubsubServicePrivate *priv = self->priv; if (priv->dispose_has_run) return; priv->dispose_has_run = TRUE; if (priv->porter != NULL) { guint i; for (i = 0; i < priv->trampolines->len; i++) { EventTrampoline *t = g_ptr_array_index (priv->trampolines, i); wocky_porter_unregister_handler (priv->porter, t->handler_id); g_slice_free (EventTrampoline, t); } g_ptr_array_unref (priv->trampolines); priv->trampolines = NULL; g_object_unref (priv->porter); priv->porter = NULL; } if (G_OBJECT_CLASS (wocky_pubsub_service_parent_class)->dispose) G_OBJECT_CLASS (wocky_pubsub_service_parent_class)->dispose (object); } static void wocky_pubsub_service_finalize (GObject *object) { WockyPubsubService *self = WOCKY_PUBSUB_SERVICE (object); WockyPubsubServicePrivate *priv = self->priv; g_free (priv->jid); g_hash_table_unref (priv->nodes); G_OBJECT_CLASS (wocky_pubsub_service_parent_class)->finalize (object); } static void wocky_pubsub_service_constructed (GObject *object) { WockyPubsubService *self = WOCKY_PUBSUB_SERVICE (object); WockyPubsubServicePrivate *priv = self->priv; const WockyPubsubNodeEventMapping *m; guint n_mappings; g_assert (priv->session != NULL); g_assert (priv->jid != NULL); priv->porter = wocky_session_get_porter (priv->session); g_object_ref (priv->porter); m = _wocky_pubsub_node_get_event_mappings (&n_mappings); priv->trampolines = g_ptr_array_sized_new (n_mappings); for (; m->action != NULL; m++) { EventTrampoline *t = g_slice_new (EventTrampoline); t->mapping = m; t->self = self; t->handler_id = wocky_porter_register_handler_from (priv->porter, WOCKY_STANZA_TYPE_MESSAGE, WOCKY_STANZA_SUB_TYPE_NONE, priv->jid, WOCKY_PORTER_HANDLER_PRIORITY_NORMAL, pubsub_service_propagate_event, t, '(', "event", ':', WOCKY_XMPP_NS_PUBSUB_EVENT, '(', m->action, ')', ')', NULL); g_ptr_array_add (priv->trampolines, t); } } static void wocky_pubsub_service_class_init ( WockyPubsubServiceClass *wocky_pubsub_service_class) { GObjectClass *object_class = G_OBJECT_CLASS (wocky_pubsub_service_class); GType ctype = G_OBJECT_CLASS_TYPE (wocky_pubsub_service_class); GParamSpec *param_spec; g_type_class_add_private (wocky_pubsub_service_class, sizeof (WockyPubsubServicePrivate)); object_class->set_property = wocky_pubsub_service_set_property; object_class->get_property = wocky_pubsub_service_get_property; object_class->dispose = wocky_pubsub_service_dispose; object_class->finalize = wocky_pubsub_service_finalize; object_class->constructed = wocky_pubsub_service_constructed; param_spec = g_param_spec_object ("session", "session", "the Wocky Session associated with this pubsub service", WOCKY_TYPE_SESSION, G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, PROP_SESSION, param_spec); param_spec = g_param_spec_string ("jid", "jid", "The jid of the pubsub service", NULL, G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, PROP_JID, param_spec); /** * WockyPubsubService::event-received: * @service: a pubsub service * @node: the node on @service for which an event has been received * wire * @event_stanza: the message/event stanza in its entirity * @event_node: the event node from the stanza * @items_node: the items node from the stanza * @items: a list of WockyNode *s for each item child of @items_node * * Emitted when an event is received for a node. */ signals[SIG_EVENT_RECEIVED] = g_signal_new ("event-received", ctype, 0, 0, NULL, NULL, _wocky_signals_marshal_VOID__OBJECT_OBJECT_POINTER_POINTER_POINTER, G_TYPE_NONE, 5, WOCKY_TYPE_PUBSUB_NODE, WOCKY_TYPE_STANZA, G_TYPE_POINTER, G_TYPE_POINTER, G_TYPE_POINTER); /** * WockyPubsubService::subscription-state-changed: * @service: a pubsub service * @node: a pubsub node for which the subscription state has changed * @stanza: the message/event stanza in its entirety * @event_node: the event node from @stanza * @subscription_node: the subscription node from @stanza * @subscription: subscription information parsed from @subscription_node */ signals[SIG_SUB_STATE_CHANGED] = g_signal_new ("subscription-state-changed", ctype, G_SIGNAL_RUN_LAST, 0, NULL, NULL, _wocky_signals_marshal_VOID__OBJECT_OBJECT_POINTER_POINTER_BOXED, G_TYPE_NONE, 5, WOCKY_TYPE_PUBSUB_NODE, WOCKY_TYPE_STANZA, G_TYPE_POINTER, G_TYPE_POINTER, WOCKY_TYPE_PUBSUB_SUBSCRIPTION); /** * WockyPubsubService::node-deleted * @node: a pubsub node * @stanza: the message/event stanza in its entirety * @event_node: the event node from @stanza * @delete_node: the delete node from @stanza * * Emitted when a notification of a node's deletion is received from the * server. */ signals[SIG_NODE_DELETED] = g_signal_new ("node-deleted", ctype, G_SIGNAL_RUN_LAST, 0, NULL, NULL, _wocky_signals_marshal_VOID__OBJECT_OBJECT_POINTER_POINTER, G_TYPE_NONE, 4, WOCKY_TYPE_PUBSUB_NODE, WOCKY_TYPE_STANZA, G_TYPE_POINTER, G_TYPE_POINTER); wocky_pubsub_service_class->node_object_type = WOCKY_TYPE_PUBSUB_NODE; } WockyPubsubService * wocky_pubsub_service_new (WockySession *session, const gchar *jid) { return g_object_new (WOCKY_TYPE_PUBSUB_SERVICE, "session", session, "jid", jid, NULL); } static gboolean remove_node (gpointer key, gpointer value, gpointer node) { return value == node; } /* Called when a WockyPubsubNode has been disposed so we can remove it from * the hash table. */ static void node_disposed_cb (gpointer user_data, GObject *node) { WockyPubsubService *self = WOCKY_PUBSUB_SERVICE (user_data); WockyPubsubServicePrivate *priv = self->priv; g_hash_table_foreach_remove (priv->nodes, remove_node, node); } static void pubsub_service_node_event_received_cb ( WockyPubsubNode *node, WockyStanza *event_stanza, WockyNode *event_node, WockyNode *items_node, GList *items, gpointer user_data) { WockyPubsubService *self = WOCKY_PUBSUB_SERVICE (user_data); g_signal_emit (self, signals[SIG_EVENT_RECEIVED], 0, node, event_stanza, event_node, items_node, items); } static void pubsub_service_node_subscription_state_changed_cb ( WockyPubsubNode *node, WockyStanza *stanza, WockyNode *event_node, WockyNode *subscription_node, WockyPubsubSubscription *subscription, gpointer user_data) { WockyPubsubService *self = WOCKY_PUBSUB_SERVICE (user_data); g_signal_emit (self, signals[SIG_SUB_STATE_CHANGED], 0, node, stanza, event_node, subscription_node, subscription); } static void pubsub_service_node_deleted_cb ( WockyPubsubNode *node, WockyStanza *stanza, WockyNode *event_node, WockyNode *delete_node, gpointer user_data) { WockyPubsubService *self = WOCKY_PUBSUB_SERVICE (user_data); g_signal_emit (self, signals[SIG_NODE_DELETED], 0, node, stanza, event_node, delete_node); } static WockyPubsubNode * pubsub_service_create_node (WockyPubsubService *self, const gchar *name) { WockyPubsubServicePrivate *priv = self->priv; WockyPubsubServiceClass *class = WOCKY_PUBSUB_SERVICE_GET_CLASS (self); WockyPubsubNode *node; g_return_val_if_fail ( g_type_is_a (class->node_object_type, WOCKY_TYPE_PUBSUB_NODE), NULL); node = g_object_new (class->node_object_type, "service", self, "name", name, NULL); g_object_weak_ref (G_OBJECT (node), node_disposed_cb, self); g_hash_table_insert (priv->nodes, g_strdup (name), node); /* It's safe to never explicitly disconnect these handlers: the node holds a * reference to the service, so the service will always outlive the node. */ g_signal_connect (node, "event-received", (GCallback) pubsub_service_node_event_received_cb, self); g_signal_connect (node, "subscription-state-changed", (GCallback) pubsub_service_node_subscription_state_changed_cb, self); g_signal_connect (node, "deleted", (GCallback) pubsub_service_node_deleted_cb, self); return node; } /** * wocky_pubsub_service_ensure_node: * @self: a pubsub service * @name: the name of a node on @self * * Fetches or creates an object representing a node on the pubsub service. Note * that this does not ensure that a node exists on the server; it merely * ensures a local representation. * * Returns: a new reference to an object representing a node named @name on * @self */ WockyPubsubNode * wocky_pubsub_service_ensure_node (WockyPubsubService *self, const gchar *name) { WockyPubsubServicePrivate *priv = self->priv; WockyPubsubNode *node; node = g_hash_table_lookup (priv->nodes, name); if (node != NULL) return g_object_ref (node); else return pubsub_service_create_node (self, name); } /** * wocky_pubsub_service_lookup_node: * @self: a pubsub service * @name: the name of a node on @self * * Fetches an object representing a node on a pubsub service, if the object * already exists; if not, returns %NULL. Note that this does not check whether * a node exists on the server; it only checks for a local representation. * * Returns: a borrowed reference to a node, or %NULL */ WockyPubsubNode * wocky_pubsub_service_lookup_node (WockyPubsubService *self, const gchar *name) { WockyPubsubServicePrivate *priv = self->priv; return g_hash_table_lookup (priv->nodes, name); } static gboolean pubsub_service_propagate_event (WockyPorter *porter, WockyStanza *event_stanza, gpointer user_data) { EventTrampoline *trampoline = user_data; WockyPubsubService *self = trampoline->self; WockyNode *event_node, *action_node; const gchar *node_name; WockyPubsubNode *node; g_assert (WOCKY_IS_PUBSUB_SERVICE (self)); event_node = wocky_node_get_child_ns ( wocky_stanza_get_top_node (event_stanza), "event", WOCKY_XMPP_NS_PUBSUB_EVENT); g_return_val_if_fail (event_node != NULL, FALSE); action_node = wocky_node_get_child (event_node, trampoline->mapping->action); g_return_val_if_fail (action_node != NULL, FALSE); node_name = wocky_node_get_attribute (action_node, "node"); if (node_name == NULL) { DEBUG_STANZA (event_stanza, "no node='' attribute on <%s/>", trampoline->mapping->action); return FALSE; } node = wocky_pubsub_service_ensure_node (self, node_name); trampoline->mapping->method (node, event_stanza, event_node, action_node); g_object_unref (node); return TRUE; } static void default_configuration_iq_cb (GObject *source, GAsyncResult *res, gpointer user_data) { GSimpleAsyncResult *result = G_SIMPLE_ASYNC_RESULT (user_data); GError *error = NULL; WockyNodeTree *default_tree; WockyDataForm *form; if (wocky_pubsub_distill_iq_reply (source, res, WOCKY_XMPP_NS_PUBSUB_OWNER, "default", &default_tree, &error)) { form = wocky_data_form_new_from_form ( wocky_node_tree_get_top_node (default_tree), &error); if (form != NULL) g_simple_async_result_set_op_res_gpointer (result, form, NULL); g_object_unref (default_tree); } if (error != NULL) { g_simple_async_result_set_from_error (result, error); g_clear_error (&error); } g_simple_async_result_complete (result); g_object_unref (result); } void wocky_pubsub_service_get_default_node_configuration_async ( WockyPubsubService *self, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { WockyPubsubServicePrivate *priv = self->priv; WockyStanza *stanza; GSimpleAsyncResult *result; stanza = wocky_pubsub_make_stanza (priv->jid, WOCKY_STANZA_SUB_TYPE_GET, WOCKY_XMPP_NS_PUBSUB_OWNER, "default", NULL, NULL); result = g_simple_async_result_new (G_OBJECT (self), callback, user_data, wocky_pubsub_service_get_default_node_configuration_async); wocky_porter_send_iq_async (priv->porter, stanza, NULL, default_configuration_iq_cb, result); g_object_unref (stanza); } WockyDataForm * wocky_pubsub_service_get_default_node_configuration_finish ( WockyPubsubService *self, GAsyncResult *result, GError **error) { if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), error)) return NULL; g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (self), wocky_pubsub_service_get_default_node_configuration_async), NULL); return g_simple_async_result_get_op_res_gpointer ( G_SIMPLE_ASYNC_RESULT (result)); } WockyPubsubSubscription * wocky_pubsub_service_parse_subscription (WockyPubsubService *self, WockyNode *subscription_node, const gchar *parent_node_attr, GError **error) { const gchar *node; const gchar *jid = wocky_node_get_attribute (subscription_node, "jid"); const gchar *subscription = wocky_node_get_attribute (subscription_node, "subscription"); const gchar *subid = wocky_node_get_attribute (subscription_node, "subid"); WockyPubsubNode *node_obj; gint state; WockyPubsubSubscription *sub; if (parent_node_attr != NULL) node = parent_node_attr; else node = wocky_node_get_attribute (subscription_node, "node"); #define FAIL_IF_NULL(attr) \ if (attr == NULL) \ { \ g_set_error (error, WOCKY_PUBSUB_SERVICE_ERROR, \ WOCKY_PUBSUB_SERVICE_ERROR_WRONG_REPLY, \ " missing " #attr "='' attribute"); \ return NULL; \ } FAIL_IF_NULL (node); FAIL_IF_NULL (jid); FAIL_IF_NULL (subscription); /* subid is technically a MUST if the service supports it, but... */ #undef FAIL_IF_NULL if (!wocky_enum_from_nick (WOCKY_TYPE_PUBSUB_SUBSCRIPTION_STATE, subscription, &state)) { g_set_error (error, WOCKY_PUBSUB_SERVICE_ERROR, WOCKY_PUBSUB_SERVICE_ERROR_WRONG_REPLY, "subscription='%s' is not a valid state", subscription); return NULL; } node_obj = wocky_pubsub_service_ensure_node (self, node); sub = wocky_pubsub_subscription_new (node_obj, jid, state, subid); g_object_unref (node_obj); return sub; } GList * wocky_pubsub_service_parse_subscriptions (WockyPubsubService *self, WockyNode *subscriptions_node, GList **subscription_nodes) { const gchar *parent_node_attr = wocky_node_get_attribute ( subscriptions_node, "node"); GQueue subs = G_QUEUE_INIT; GQueue sub_nodes = G_QUEUE_INIT; WockyNode *n; WockyNodeIter i; wocky_node_iter_init (&i, subscriptions_node, "subscription", NULL); while (wocky_node_iter_next (&i, &n)) { GError *error = NULL; WockyPubsubSubscription *sub = wocky_pubsub_service_parse_subscription ( self, n, parent_node_attr, &error); if (sub != NULL) { g_queue_push_tail (&subs, sub); g_queue_push_tail (&sub_nodes, n); } else { DEBUG ("%s", error->message); g_clear_error (&error); } } if (subscription_nodes == NULL) g_queue_clear (&sub_nodes); else *subscription_nodes = sub_nodes.head; return subs.head; } static void receive_subscriptions_cb (GObject *source, GAsyncResult *res, gpointer user_data) { GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (user_data); WockyPubsubService *self = WOCKY_PUBSUB_SERVICE ( g_async_result_get_source_object (user_data)); WockyNodeTree *subs_tree; GError *error = NULL; if (wocky_pubsub_distill_iq_reply (source, res, WOCKY_XMPP_NS_PUBSUB, "subscriptions", &subs_tree, &error)) { GList *subs = wocky_pubsub_service_parse_subscriptions (self, wocky_node_tree_get_top_node (subs_tree), NULL); g_simple_async_result_set_op_res_gpointer (simple, subs, (GDestroyNotify) wocky_pubsub_subscription_list_free); g_object_unref (subs_tree); } else { g_simple_async_result_set_from_error (simple, error); g_clear_error (&error); } g_simple_async_result_complete (simple); g_object_unref (simple); g_object_unref (self); } WockyStanza * wocky_pubsub_service_create_retrieve_subscriptions_stanza ( WockyPubsubService *self, WockyPubsubNode *node, WockyNode **pubsub_node, WockyNode **subscriptions_node) { WockyPubsubServicePrivate *priv = self->priv; WockyStanza *stanza; WockyNode *subscriptions; stanza = wocky_pubsub_make_stanza (priv->jid, WOCKY_STANZA_SUB_TYPE_GET, WOCKY_XMPP_NS_PUBSUB, "subscriptions", pubsub_node, &subscriptions); if (node != NULL) wocky_node_set_attribute (subscriptions, "node", wocky_pubsub_node_get_name (node)); if (subscriptions_node != NULL) *subscriptions_node = subscriptions; return stanza; } void wocky_pubsub_service_retrieve_subscriptions_async ( WockyPubsubService *self, WockyPubsubNode *node, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { WockyPubsubServicePrivate *priv = self->priv; GSimpleAsyncResult *simple = g_simple_async_result_new (G_OBJECT (self), callback, user_data, wocky_pubsub_service_retrieve_subscriptions_async); WockyStanza *stanza; stanza = wocky_pubsub_service_create_retrieve_subscriptions_stanza (self, node, NULL, NULL); wocky_porter_send_iq_async (priv->porter, stanza, cancellable, receive_subscriptions_cb, simple); g_object_unref (stanza); } gboolean wocky_pubsub_service_retrieve_subscriptions_finish ( WockyPubsubService *self, GAsyncResult *result, GList **subscriptions, GError **error) { wocky_implement_finish_copy_pointer (self, wocky_pubsub_service_retrieve_subscriptions_async, wocky_pubsub_subscription_list_copy, subscriptions); } /** * wocky_pubsub_service_handle_create_node_reply: * @self: a pubsub service * @create_tree: the <create/> tree from the reply to an attempt to * create a node, or %NULL if none was present in the reply. * @requested_name: the name we asked the server to use for the node, or %NULL * if we requested an instant node * @error: location at which to store an error * * Handles the body of a reply to a create node request. This is * ever-so-slightly involved, because the server is allowed to omit the body of * the reply if you specified a node name and it created a node with that name, * but it may also tell you "hey, you asked for 'ringo', but I gave you * 'george'". Good times. * * Returns: a pubsub node if the reply made sense, or %NULL with @error set if * not. */ WockyPubsubNode * wocky_pubsub_service_handle_create_node_reply ( WockyPubsubService *self, WockyNodeTree *create_tree, const gchar *requested_name, GError **error) { WockyPubsubNode *node = NULL; const gchar *name = NULL; if (create_tree != NULL) { /* If the reply contained , it'd better contain the * nodeID. */ name = wocky_node_get_attribute ( wocky_node_tree_get_top_node (create_tree), "node"); if (name == NULL) g_set_error (error, WOCKY_PUBSUB_SERVICE_ERROR, WOCKY_PUBSUB_SERVICE_ERROR_WRONG_REPLY, "reply doesn't contain node='' attribute"); } else if (requested_name == NULL) { g_set_error (error, WOCKY_PUBSUB_SERVICE_ERROR, WOCKY_PUBSUB_SERVICE_ERROR_WRONG_REPLY, "requested an instant node, but the server did not report the " "newly-created node's name"); } else { name = requested_name; } if (name != NULL) { node = wocky_pubsub_service_ensure_node (self, name); DEBUG ("node %s created\n", name); } return node; } static void create_node_iq_cb (GObject *source, GAsyncResult *res, gpointer user_data) { GSimpleAsyncResult *result = G_SIMPLE_ASYNC_RESULT (user_data); WockyPubsubService *self; WockyPubsubNode *node = NULL; const gchar *requested_name; WockyNodeTree *create_tree; GError *error = NULL; self = WOCKY_PUBSUB_SERVICE (g_async_result_get_source_object (user_data)); requested_name = g_object_get_data ((GObject *) result, "requested-name"); if (wocky_pubsub_distill_ambivalent_iq_reply (source, res, WOCKY_XMPP_NS_PUBSUB, "create", &create_tree, &error)) { node = wocky_pubsub_service_handle_create_node_reply (self, create_tree, requested_name, &error); if (create_tree != NULL) g_object_unref (create_tree); } if (node != NULL) { /* 'result' steals our reference to 'node' */ g_simple_async_result_set_op_res_gpointer (result, node, g_object_unref); } else { g_assert (error != NULL); g_simple_async_result_set_from_error (result, error); g_clear_error (&error); } g_simple_async_result_complete (result); g_object_unref (result); g_object_unref (self); } WockyStanza * wocky_pubsub_service_create_create_node_stanza ( WockyPubsubService *self, const gchar *name, WockyDataForm *config, WockyNode **pubsub_node, WockyNode **create_node) { WockyPubsubServicePrivate *priv = self->priv; WockyStanza *stanza; WockyNode *pubsub, *create; stanza = wocky_pubsub_make_stanza (priv->jid, WOCKY_STANZA_SUB_TYPE_SET, WOCKY_XMPP_NS_PUBSUB, "create", &pubsub, &create); if (name != NULL) wocky_node_set_attribute (create, "node", name); if (config != NULL) wocky_data_form_submit (config, wocky_node_add_child (pubsub, "configure")); if (pubsub_node != NULL) *pubsub_node = pubsub; if (create_node != NULL) *create_node = create; return stanza; } void wocky_pubsub_service_create_node_async (WockyPubsubService *self, const gchar *name, WockyDataForm *config, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { WockyPubsubServicePrivate *priv = self->priv; WockyStanza *stanza = wocky_pubsub_service_create_create_node_stanza ( self, name, config, NULL, NULL); GSimpleAsyncResult *result = g_simple_async_result_new (G_OBJECT (self), callback, user_data, wocky_pubsub_service_create_node_async); g_object_set_data_full ((GObject *) result, "requested-name", g_strdup (name), g_free); wocky_porter_send_iq_async (priv->porter, stanza, NULL, create_node_iq_cb, result); g_object_unref (stanza); } WockyPubsubNode * wocky_pubsub_service_create_node_finish (WockyPubsubService *self, GAsyncResult *result, GError **error) { GSimpleAsyncResult *simple; WockyPubsubNode *node; g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (self), wocky_pubsub_service_create_node_async), NULL); simple = (GSimpleAsyncResult *) result; if (g_simple_async_result_propagate_error (simple, error)) return NULL; node = WOCKY_PUBSUB_NODE (g_simple_async_result_get_op_res_gpointer (simple)); return g_object_ref (node); } WockyPorter * wocky_pubsub_service_get_porter (WockyPubsubService *self) { WockyPubsubServicePrivate *priv = self->priv; return priv->porter; } WockyPubsubSubscription * wocky_pubsub_subscription_new ( WockyPubsubNode *node, const gchar *jid, WockyPubsubSubscriptionState state, const gchar *subid) { WockyPubsubSubscription *sub = g_slice_new (WockyPubsubSubscription); sub->node = g_object_ref (node); sub->jid = g_strdup (jid); sub->state = state; sub->subid = g_strdup (subid); return sub; } WockyPubsubSubscription * wocky_pubsub_subscription_copy (WockyPubsubSubscription *sub) { g_return_val_if_fail (sub != NULL, NULL); return wocky_pubsub_subscription_new (sub->node, sub->jid, sub->state, sub->subid); } void wocky_pubsub_subscription_free (WockyPubsubSubscription *sub) { g_return_if_fail (sub != NULL); g_object_unref (sub->node); g_free (sub->jid); g_free (sub->subid); g_slice_free (WockyPubsubSubscription, sub); } GList * wocky_pubsub_subscription_list_copy (GList *subs) { return wocky_list_deep_copy ((GBoxedCopyFunc) wocky_pubsub_subscription_copy, subs); } void wocky_pubsub_subscription_list_free (GList *subs) { g_list_foreach (subs, (GFunc) wocky_pubsub_subscription_free, NULL); g_list_free (subs); } GType wocky_pubsub_subscription_get_type (void) { static GType t = 0; if (G_UNLIKELY (t == 0)) t = g_boxed_type_register_static ("WockyPubsubSubscription", (GBoxedCopyFunc) wocky_pubsub_subscription_copy, (GBoxedFreeFunc) wocky_pubsub_subscription_free); return t; } /** * WockyPubsubServiceClass: * @parent_class: parent * @node_object_type: the subtype of #WOCKY_TYPE_PUBSUB_NODE to be created by * wocky_pubsub_service_ensure_node() * * The class structure for the #WockyPubsubService type. */ /** * WockyPubsubSubscription: * @node: a PubSub node * @jid: the JID which is subscribed to @node. This may be a bare JID, or a * full JID with a resource, depending on which was specified when * subscribing to @node. See XEP-0060 §6.1 Subscribe to a Node * @state: the state of this subscription * @subid: a unique identifier for this subscription, if a JID is subscribed to * a node multiple times, or %NULL if there is no such identifier. See * XEP-0060 §6.1.6 “Multiple Subscriptions” * * Represents a subscription to a node on a pubsub service, as seen when * listing your own subscriptions on a service with * wocky_pubsub_service_retrieve_subscriptions_async() or subscribing to a node * with wocky_pubsub_node_subscribe_async(). */ /** * WockyPubsubSubscriptionState: * @WOCKY_PUBSUB_SUBSCRIPTION_NONE: The node MUST NOT send event notifications * or payloads to the Entity. * @WOCKY_PUBSUB_SUBSCRIPTION_PENDING: An entity has requested to subscribe to * a node and the request has not yet been approved by a node owner. The node * MUST NOT send event notifications or payloads to the entity while it is in * this state. * @WOCKY_PUBSUB_SUBSCRIPTION_SUBSCRIBED: An entity has subscribed but its * subscription options have not yet been configured. The node MAY send event * notifications or payloads to the entity while it is in this state. The * service MAY timeout unconfigured subscriptions. * @WOCKY_PUBSUB_SUBSCRIPTION_UNCONFIGURED: An entity is subscribed to a node. * The node MUST send all event notifications (and, if configured, payloads) * to the entity while it is in this state (subject to subscriber * configuration and content filtering). * * Describes the state of a subscription to a node. Definitions are taken from * XEP-0060 * §4.2. */ telepathy-gabble-0.18.2/lib/ext/wocky/wocky/wocky-pubsub-node-internal.h0000644000175000017500000000302412200204546026205 0ustar00smcvsmcv00000000000000/* * wocky-pubsub-node-internal.h - internal methods on WockyPubsubNode * used by WockyPubsubService * Copyright © 2010 Collabora Ltd. * Copyright © 2010 Nokia Corporation * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 */ #if !defined (WOCKY_COMPILATION) # error "This is an internal header." #endif #ifndef WOCKY_PUBSUB_NODE_INTERNAL_H #define WOCKY_PUBSUB_NODE_INTERNAL_H #include "wocky-pubsub-node.h" typedef void (*WockyPubsubNodeEventHandler) ( WockyPubsubNode *self, WockyStanza *event_stanza, WockyNode *event_node, WockyNode *action_node); typedef struct { const gchar *action; WockyPubsubNodeEventHandler method; } WockyPubsubNodeEventMapping; const WockyPubsubNodeEventMapping *_wocky_pubsub_node_get_event_mappings ( guint *n_mappings); #endif /* WOCKY_PUBSUB_NODE_INTERNAL_H */ telepathy-gabble-0.18.2/lib/ext/wocky/wocky/wocky-pubsub-node.c0000644000175000017500000010747012200204546024400 0ustar00smcvsmcv00000000000000/* * wocky-pubsub-node.c - WockyPubsubNode * Copyright (C) 2009 Collabora Ltd. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "wocky-pubsub-node.h" #include "wocky-pubsub-node-protected.h" #include "wocky-pubsub-node-internal.h" #include "wocky-namespaces.h" #include "wocky-porter.h" #include "wocky-pubsub-helpers.h" #include "wocky-pubsub-service-protected.h" #include "wocky-signals-marshal.h" #include "wocky-utils.h" #define WOCKY_DEBUG_FLAG WOCKY_DEBUG_PUBSUB #include "wocky-debug-internal.h" G_DEFINE_TYPE (WockyPubsubNode, wocky_pubsub_node, G_TYPE_OBJECT) enum { SIG_EVENT_RECEIVED, SIG_SUB_STATE_CHANGED, SIG_DELETED, LAST_SIGNAL, }; static guint signals[LAST_SIGNAL] = {0}; enum { PROP_SERVICE = 1, PROP_NAME, }; /* private structure */ struct _WockyPubsubNodePrivate { WockyPubsubService *service; WockyPorter *porter; gchar *service_jid; gchar *name; gboolean dispose_has_run; }; static void wocky_pubsub_node_init (WockyPubsubNode *self) { self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, WOCKY_TYPE_PUBSUB_NODE, WockyPubsubNodePrivate); } static void wocky_pubsub_node_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { WockyPubsubNode *self = WOCKY_PUBSUB_NODE (object); WockyPubsubNodePrivate *priv = self->priv; switch (property_id) { case PROP_SERVICE: priv->service = g_value_dup_object (value); break; case PROP_NAME: priv->name = g_value_dup_string (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void wocky_pubsub_node_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { WockyPubsubNode *self = WOCKY_PUBSUB_NODE (object); WockyPubsubNodePrivate *priv = self->priv; switch (property_id) { case PROP_SERVICE: g_value_set_object (value, priv->service); break; case PROP_NAME: g_value_set_string (value, priv->name); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void wocky_pubsub_node_dispose (GObject *object) { WockyPubsubNode *self = WOCKY_PUBSUB_NODE (object); WockyPubsubNodePrivate *priv = self->priv; if (priv->dispose_has_run) return; priv->dispose_has_run = TRUE; g_object_unref (priv->service); g_object_unref (priv->porter); if (G_OBJECT_CLASS (wocky_pubsub_node_parent_class)->dispose) G_OBJECT_CLASS (wocky_pubsub_node_parent_class)->dispose (object); } static void wocky_pubsub_node_finalize (GObject *object) { WockyPubsubNode *self = WOCKY_PUBSUB_NODE (object); WockyPubsubNodePrivate *priv = self->priv; g_free (priv->name); g_free (priv->service_jid); G_OBJECT_CLASS (wocky_pubsub_node_parent_class)->finalize (object); } static void wocky_pubsub_node_constructed (GObject *object) { WockyPubsubNode *self = WOCKY_PUBSUB_NODE (object); WockyPubsubNodePrivate *priv = self->priv; WockySession *session; g_assert (priv->service != NULL); g_assert (priv->name != NULL); g_object_get (priv->service, "jid", &(priv->service_jid), "session", &session, NULL); g_assert (priv->service_jid != NULL); g_assert (session != NULL); priv->porter = wocky_session_get_porter (session); g_object_ref (priv->porter); g_object_unref (session); } static void wocky_pubsub_node_emit_event_received ( WockyPubsubNode *self, WockyStanza *event_stanza, WockyNode *event_node, WockyNode *items_node, GList *items) { g_signal_emit (self, signals[SIG_EVENT_RECEIVED], 0, event_stanza, event_node, items_node, items); } static void wocky_pubsub_node_emit_subscription_state_changed ( WockyPubsubNode *self, WockyStanza *stanza, WockyNode *event_node, WockyNode *subscription_node, WockyPubsubSubscription *subscription) { g_signal_emit (self, signals[SIG_SUB_STATE_CHANGED], 0, stanza, event_node, subscription_node, subscription); } static void wocky_pubsub_node_emit_deleted ( WockyPubsubNode *self, WockyStanza *stanza, WockyNode *event_node, WockyNode *delete_node) { g_signal_emit (self, signals[SIG_DELETED], 0, stanza, event_node, delete_node); } static void wocky_pubsub_node_class_init ( WockyPubsubNodeClass *wocky_pubsub_node_class) { GObjectClass *object_class = G_OBJECT_CLASS (wocky_pubsub_node_class); GType ctype = G_OBJECT_CLASS_TYPE (wocky_pubsub_node_class); GParamSpec *param_spec; g_type_class_add_private (wocky_pubsub_node_class, sizeof (WockyPubsubNodePrivate)); object_class->set_property = wocky_pubsub_node_set_property; object_class->get_property = wocky_pubsub_node_get_property; object_class->dispose = wocky_pubsub_node_dispose; object_class->finalize = wocky_pubsub_node_finalize; object_class->constructed = wocky_pubsub_node_constructed; param_spec = g_param_spec_object ("service", "service", "the Wocky Pubsub service associated with this pubsub node", WOCKY_TYPE_PUBSUB_SERVICE, G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, PROP_SERVICE, param_spec); param_spec = g_param_spec_string ("name", "name", "The name of the pubsub node", NULL, G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, PROP_NAME, param_spec); /** * WockyPubsubNode::event-received: * @node: a pubsub node * @event_stanza: the message/event stanza in its entirity * @event_node: the event node from the stanza * @items_node: the items node from the stanza * @items: a list of WockyNode *s for each item child of @items_node */ signals[SIG_EVENT_RECEIVED] = g_signal_new ("event-received", ctype, 0, 0, NULL, NULL, _wocky_signals_marshal_VOID__OBJECT_POINTER_POINTER_POINTER, G_TYPE_NONE, 4, WOCKY_TYPE_STANZA, G_TYPE_POINTER, G_TYPE_POINTER, G_TYPE_POINTER); /** * WockyPubsubNode::subscription-state-changed: * @node: a pubsub node * @stanza: the message/event stanza in its entirety * @event_node: the event node from @stanza * @subscription_node: the subscription node from @stanza * @subscription: subscription information parsed from @subscription_node */ signals[SIG_SUB_STATE_CHANGED] = g_signal_new ("subscription-state-changed", ctype, G_SIGNAL_RUN_LAST, 0, NULL, NULL, _wocky_signals_marshal_VOID__OBJECT_POINTER_POINTER_BOXED, G_TYPE_NONE, 4, WOCKY_TYPE_STANZA, G_TYPE_POINTER, G_TYPE_POINTER, WOCKY_TYPE_PUBSUB_SUBSCRIPTION); /** * WockyPubsubNode::deleted * @node: a pubsub node * @stanza: the message/event stanza in its entirety * @event_node: the event node from @stanza * @delete_node: the delete node from @stanza * * Emitted when a notification of this node's deletion is received from the * server. */ signals[SIG_DELETED] = g_signal_new ("deleted", ctype, G_SIGNAL_RUN_LAST, 0, NULL, NULL, _wocky_signals_marshal_VOID__OBJECT_POINTER_POINTER, G_TYPE_NONE, 3, WOCKY_TYPE_STANZA, G_TYPE_POINTER, G_TYPE_POINTER); } static void pubsub_node_handle_items_event (WockyPubsubNode *self, WockyStanza *event_stanza, WockyNode *event_node, WockyNode *items_node) { WockyNode *item_node; GQueue items = G_QUEUE_INIT; WockyNodeIter iter; wocky_node_iter_init (&iter, items_node, "item", NULL); while (wocky_node_iter_next (&iter, &item_node)) g_queue_push_tail (&items, item_node); DEBUG_STANZA (event_stanza, "extracted %u items", items.length); wocky_pubsub_node_emit_event_received (self, event_stanza, event_node, items_node, items.head); g_queue_clear (&items); } static void pubsub_node_handle_subscription_event (WockyPubsubNode *self, WockyStanza *event_stanza, WockyNode *event_node, WockyNode *subscription_node) { WockyPubsubNodePrivate *priv = self->priv; WockyPubsubSubscription *sub; GError *error = NULL; sub = wocky_pubsub_service_parse_subscription (priv->service, subscription_node, NULL, &error); if (sub == NULL) { DEBUG ("received unparseable subscription state change notification: %s", error->message); g_clear_error (&error); } else { wocky_pubsub_node_emit_subscription_state_changed (self, event_stanza, event_node, subscription_node, sub); wocky_pubsub_subscription_free (sub); } } static const WockyPubsubNodeEventMapping mappings[] = { { "items", pubsub_node_handle_items_event, }, { "subscription", pubsub_node_handle_subscription_event, }, { "delete", wocky_pubsub_node_emit_deleted, }, { NULL, } }; const WockyPubsubNodeEventMapping * _wocky_pubsub_node_get_event_mappings (guint *n_mappings) { if (n_mappings != NULL) *n_mappings = G_N_ELEMENTS (mappings) - 1; return mappings; } const gchar * wocky_pubsub_node_get_name (WockyPubsubNode *self) { WockyPubsubNodePrivate *priv = self->priv; return priv->name; } WockyStanza * wocky_pubsub_node_make_publish_stanza (WockyPubsubNode *self, WockyNode **pubsub_out, WockyNode **publish_out, WockyNode **item_out) { WockyPubsubNodePrivate *priv = self->priv; return wocky_pubsub_make_publish_stanza (priv->service_jid, priv->name, pubsub_out, publish_out, item_out); } static WockyStanza * pubsub_node_make_action_stanza (WockyPubsubNode *self, WockyStanzaSubType sub_type, const gchar *pubsub_ns, const gchar *action_name, const gchar *jid, WockyNode **pubsub_node, WockyNode **action_node) { WockyPubsubNodePrivate *priv = self->priv; WockyStanza *stanza; WockyNode *action; g_assert (pubsub_ns != NULL); g_assert (action_name != NULL); stanza = wocky_pubsub_make_stanza (priv->service_jid, sub_type, pubsub_ns, action_name, pubsub_node, &action); wocky_node_set_attribute (action, "node", priv->name); if (jid != NULL) wocky_node_set_attribute (action, "jid", jid); if (action_node != NULL) *action_node = action; return stanza; } WockyStanza * wocky_pubsub_node_make_subscribe_stanza (WockyPubsubNode *self, const gchar *jid, WockyNode **pubsub_node, WockyNode **subscribe_node) { /* TODO: when the connection/porter/session/something knows our own JID, we * should provide an easy way to say “my bare JID” or “my full JID”. Could be * really evil and use 0x1 and 0x3 or something on the assumption that those * will never be strings.... */ g_return_val_if_fail (jid != NULL, NULL); return pubsub_node_make_action_stanza (self, WOCKY_STANZA_SUB_TYPE_SET, WOCKY_XMPP_NS_PUBSUB, "subscribe", jid, pubsub_node, subscribe_node); } static void subscribe_cb (GObject *source, GAsyncResult *res, gpointer user_data) { GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (user_data); WockyPubsubNode *self = WOCKY_PUBSUB_NODE ( g_async_result_get_source_object (user_data)); WockyPubsubNodePrivate *priv = self->priv; WockyNodeTree *sub_tree; WockyPubsubSubscription *sub = NULL; GError *error = NULL; if (wocky_pubsub_distill_iq_reply (source, res, WOCKY_XMPP_NS_PUBSUB, "subscription", &sub_tree, &error)) { WockyNode *subscription_node = wocky_node_tree_get_top_node (sub_tree); sub = wocky_pubsub_service_parse_subscription (priv->service, subscription_node, NULL, &error); g_object_unref (sub_tree); } if (sub != NULL) { g_simple_async_result_set_op_res_gpointer (simple, sub, (GDestroyNotify) wocky_pubsub_subscription_free); } else { g_simple_async_result_set_from_error (simple, error); g_clear_error (&error); } g_simple_async_result_complete (simple); g_object_unref (simple); g_object_unref (self); } /** * wocky_pubsub_node_subscribe_async: * @self: a pubsub node * @jid: the JID to use as the subscribed JID (usually the connection's bare or * full JID); may not be %NULL * @cancellable: optional GCancellable object, %NULL to ignore * @callback: a callback to call when the request is completed * @user_data: data to pass to @callback * * Attempts to subscribe to @self. */ void wocky_pubsub_node_subscribe_async (WockyPubsubNode *self, const gchar *jid, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { WockyPubsubNodePrivate *priv = self->priv; GSimpleAsyncResult *simple = g_simple_async_result_new (G_OBJECT (self), callback, user_data, wocky_pubsub_node_subscribe_async); WockyStanza *stanza; g_return_if_fail (jid != NULL); stanza = wocky_pubsub_node_make_subscribe_stanza (self, jid, NULL, NULL); wocky_porter_send_iq_async (priv->porter, stanza, cancellable, subscribe_cb, simple); g_object_unref (stanza); } WockyPubsubSubscription * wocky_pubsub_node_subscribe_finish (WockyPubsubNode *self, GAsyncResult *result, GError **error) { GSimpleAsyncResult *simple; g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (self), wocky_pubsub_node_subscribe_async), NULL); simple = (GSimpleAsyncResult *) result; if (g_simple_async_result_propagate_error (simple, error)) return NULL; else return wocky_pubsub_subscription_copy ( g_simple_async_result_get_op_res_gpointer (simple)); } WockyStanza * wocky_pubsub_node_make_unsubscribe_stanza (WockyPubsubNode *self, const gchar *jid, const gchar *subid, WockyNode **pubsub_node, WockyNode **unsubscribe_node) { WockyStanza *stanza; WockyNode *unsubscribe; /* TODO: when the connection/porter/session/something knows our own JID, we * should provide an easy way to say “my bare JID” or “my full JID”. Could be * really evil and use 0x1 and 0x3 or something on the assumption that those * will never be strings.... */ g_return_val_if_fail (jid != NULL, NULL); stanza = pubsub_node_make_action_stanza (self, WOCKY_STANZA_SUB_TYPE_SET, WOCKY_XMPP_NS_PUBSUB, "unsubscribe", jid, pubsub_node, &unsubscribe); if (subid != NULL) wocky_node_set_attribute (unsubscribe, "subid", subid); if (unsubscribe_node != NULL) *unsubscribe_node = unsubscribe; return stanza; } static void pubsub_node_void_iq_cb (GObject *source, GAsyncResult *res, gpointer user_data) { GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (user_data); GError *error = NULL; if (!wocky_pubsub_distill_void_iq_reply (source, res, &error)) { g_simple_async_result_set_from_error (simple, error); g_clear_error (&error); } g_simple_async_result_complete (simple); g_object_unref (simple); } /** * wocky_pubsub_node_unsubscribe_async: * @self: a pubsub node * @jid: the JID subscribed to @self (usually the connection's bare or * full JID); may not be %NULL * @subid: the identifier associated with the subscription * @cancellable: optional GCancellable object, %NULL to ignore * @callback: a callback to call when the request is completed * @user_data: data to pass to @callback * * Attempts to unsubscribe from @self. */ void wocky_pubsub_node_unsubscribe_async (WockyPubsubNode *self, const gchar *jid, const gchar *subid, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { WockyPubsubNodePrivate *priv = self->priv; GSimpleAsyncResult *simple = g_simple_async_result_new (G_OBJECT (self), callback, user_data, wocky_pubsub_node_unsubscribe_async); WockyStanza *stanza; g_return_if_fail (jid != NULL); stanza = wocky_pubsub_node_make_unsubscribe_stanza (self, jid, subid, NULL, NULL); wocky_porter_send_iq_async (priv->porter, stanza, cancellable, pubsub_node_void_iq_cb, simple); g_object_unref (stanza); } gboolean wocky_pubsub_node_unsubscribe_finish (WockyPubsubNode *self, GAsyncResult *result, GError **error) { wocky_implement_finish_void (self, wocky_pubsub_node_unsubscribe_async); } WockyStanza * wocky_pubsub_node_make_delete_stanza ( WockyPubsubNode *self, WockyNode **pubsub_node, WockyNode **delete_node) { return pubsub_node_make_action_stanza (self, WOCKY_STANZA_SUB_TYPE_SET, WOCKY_XMPP_NS_PUBSUB_OWNER, "delete", NULL, pubsub_node, delete_node); } void wocky_pubsub_node_delete_async (WockyPubsubNode *self, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { WockyPubsubNodePrivate *priv = self->priv; WockyStanza *stanza; GSimpleAsyncResult *result; stanza = wocky_pubsub_node_make_delete_stanza (self, NULL, NULL); result = g_simple_async_result_new (G_OBJECT (self), callback, user_data, wocky_pubsub_node_delete_async); wocky_porter_send_iq_async (priv->porter, stanza, NULL, pubsub_node_void_iq_cb, result); g_object_unref (stanza); } gboolean wocky_pubsub_node_delete_finish (WockyPubsubNode *self, GAsyncResult *result, GError **error) { wocky_implement_finish_void (self, wocky_pubsub_node_delete_async); } WockyStanza * wocky_pubsub_node_make_list_subscribers_stanza ( WockyPubsubNode *self, WockyNode **pubsub_node, WockyNode **subscriptions_node) { return pubsub_node_make_action_stanza (self, WOCKY_STANZA_SUB_TYPE_GET, WOCKY_XMPP_NS_PUBSUB_OWNER, "subscriptions", NULL, pubsub_node, subscriptions_node); } static void list_subscribers_cb (GObject *source, GAsyncResult *res, gpointer user_data) { GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (user_data); WockyPubsubNode *self = WOCKY_PUBSUB_NODE ( g_async_result_get_source_object (user_data)); WockyPubsubNodePrivate *priv = self->priv; WockyNodeTree *subs_tree; GError *error = NULL; if (wocky_pubsub_distill_iq_reply (source, res, WOCKY_XMPP_NS_PUBSUB_OWNER, "subscriptions", &subs_tree, &error)) { GList *subs = wocky_pubsub_service_parse_subscriptions (priv->service, wocky_node_tree_get_top_node (subs_tree), NULL); g_simple_async_result_set_op_res_gpointer (simple, subs, (GDestroyNotify) wocky_pubsub_subscription_list_free); g_object_unref (subs_tree); } else { g_simple_async_result_set_from_error (simple, error); g_clear_error (&error); } g_simple_async_result_complete (simple); g_object_unref (simple); g_object_unref (self); } /** * wocky_pubsub_node_list_subscribers_async: * @self: a pubsub node * @cancellable: optional #GCancellable object * @callback: function to call when the subscribers have been retrieved or an * error has occured * @user_data: data to pass to @callback. * * Retrieves the list of subscriptions to a node you own. @callback may * complete the call using wocky_pubsub_node_list_subscribers_finish(). * * (A note on naming: this is §8.8.1 — Retrieve Subscriptions List — in * XEP-0060, not to be confused with §5.6 — Retrieve Subscriptions. The * different terminology in Wocky is intended to help disambiguate!) */ void wocky_pubsub_node_list_subscribers_async ( WockyPubsubNode *self, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { WockyPubsubNodePrivate *priv = self->priv; GSimpleAsyncResult *simple = g_simple_async_result_new (G_OBJECT (self), callback, user_data, wocky_pubsub_node_list_subscribers_async); WockyStanza *stanza; stanza = wocky_pubsub_node_make_list_subscribers_stanza (self, NULL, NULL); wocky_porter_send_iq_async (priv->porter, stanza, cancellable, list_subscribers_cb, simple); g_object_unref (stanza); } /** * wocky_pubsub_node_list_subscribers_finish: * @self: a pubsub node * @result: the result passed to a callback * @subscribers: location at which to store a list of #WockyPubsubSubscription * pointers, or %NULL * @error: location at which to store an error, or %NULL * * Completes a call to wocky_pubsub_node_list_subscribers_async(). The list * returned in @subscribers should be freed with * wocky_pubsub_subscription_list_free() when it is no longer needed. * * Returns: %TRUE if the list of subscribers was successfully retrieved; %FALSE * and sets @error if an error occured. */ gboolean wocky_pubsub_node_list_subscribers_finish ( WockyPubsubNode *self, GAsyncResult *result, GList **subscribers, GError **error) { wocky_implement_finish_copy_pointer (self, wocky_pubsub_node_list_subscribers_async, wocky_pubsub_subscription_list_copy, subscribers); } WockyStanza * wocky_pubsub_node_make_list_affiliates_stanza ( WockyPubsubNode *self, WockyNode **pubsub_node, WockyNode **affiliations_node) { return pubsub_node_make_action_stanza (self, WOCKY_STANZA_SUB_TYPE_GET, WOCKY_XMPP_NS_PUBSUB_OWNER, "affiliations", NULL, pubsub_node, affiliations_node); } GList * wocky_pubsub_node_parse_affiliations ( WockyPubsubNode *self, WockyNode *affiliations_node) { GQueue affs = G_QUEUE_INIT; WockyNodeIter i; WockyNode *n; wocky_node_iter_init (&i, affiliations_node, "affiliation", NULL); while (wocky_node_iter_next (&i, &n)) { const gchar *jid = wocky_node_get_attribute (n, "jid"); const gchar *affiliation = wocky_node_get_attribute (n, "affiliation"); gint state; if (jid == NULL) { DEBUG (" missing jid=''; skipping"); continue; } if (affiliation == NULL) { DEBUG (" missing affiliation=''; skipping"); continue; } if (!wocky_enum_from_nick (WOCKY_TYPE_PUBSUB_AFFILIATION_STATE, affiliation, &state)) { DEBUG ("unknown affiliation '%s'; skipping", affiliation); continue; } g_queue_push_tail (&affs, wocky_pubsub_affiliation_new (self, jid, state)); } return affs.head; } static void list_affiliates_cb (GObject *source, GAsyncResult *res, gpointer user_data) { GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (user_data); WockyPubsubNode *self = WOCKY_PUBSUB_NODE ( g_async_result_get_source_object (user_data)); WockyNodeTree *affs_tree; GError *error = NULL; if (wocky_pubsub_distill_iq_reply (source, res, WOCKY_XMPP_NS_PUBSUB_OWNER, "affiliations", &affs_tree, &error)) { WockyNode *affiliations_node = wocky_node_tree_get_top_node (affs_tree); g_simple_async_result_set_op_res_gpointer (simple, wocky_pubsub_node_parse_affiliations (self, affiliations_node), (GDestroyNotify) wocky_pubsub_affiliation_list_free); g_object_unref (affs_tree); } else { g_simple_async_result_set_from_error (simple, error); g_clear_error (&error); } g_simple_async_result_complete (simple); g_object_unref (simple); g_object_unref (self); } /** * wocky_pubsub_node_list_affiliates_async: * @self: a pubsub node * @cancellable: optional #GCancellable object * @callback: function to call when the affiliates have been retrieved or an * error has occured * @user_data: data to pass to @callback. * * Retrieves the list of entities affilied to a node you own. @callback may * complete the call using wocky_pubsub_node_list_affiliates_finish(). * * (A note on naming: this is §8.9.1 — Retrieve Affiliations List — in * XEP-0060, not to be confused with §5.7 — Retrieve Affiliations. The * slightly different terminology in Wocky is intended to help disambiguate!) */ void wocky_pubsub_node_list_affiliates_async ( WockyPubsubNode *self, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { WockyPubsubNodePrivate *priv = self->priv; GSimpleAsyncResult *simple = g_simple_async_result_new (G_OBJECT (self), callback, user_data, wocky_pubsub_node_list_affiliates_async); WockyStanza *stanza; stanza = wocky_pubsub_node_make_list_affiliates_stanza (self, NULL, NULL); wocky_porter_send_iq_async (priv->porter, stanza, cancellable, list_affiliates_cb, simple); g_object_unref (stanza); } /** * wocky_pubsub_node_list_affiliates_finish: * @self: a pubsub node * @result: the result passed to a callback * @affiliates: location at which to store a list of #WockyPubsubAffiliation * pointers, or %NULL * @error: location at which to store an error, or %NULL * * Completes a call to wocky_pubsub_node_list_affiliates_async(). The list * returned in @affiliates should be freed with * wocky_pubsub_affiliation_list_free() when it is no longer needed. * * Returns: %TRUE if the list of subscribers was successfully retrieved; %FALSE * and sets @error if an error occured. */ gboolean wocky_pubsub_node_list_affiliates_finish ( WockyPubsubNode *self, GAsyncResult *result, GList **affiliates, GError **error) { wocky_implement_finish_copy_pointer (self, wocky_pubsub_node_list_affiliates_async, wocky_pubsub_affiliation_list_copy, affiliates); } /** * wocky_pubsub_node_make_modify_affiliates_stanza: * @self: a pubsub node * @affiliates: a list of #WockyPubsubAffiliation structures, describing only * the affiliations which should be changed. * @pubsub_node: location at which to store a pointer to the <pubsub/> * node, or %NULL * @affiliations_node: location at which to store a pointer to the * <affiliations/> node, or %NULL * * Returns: an IQ stanza to modify the entities affiliated to a node that you * own. */ WockyStanza * wocky_pubsub_node_make_modify_affiliates_stanza ( WockyPubsubNode *self, GList *affiliates, WockyNode **pubsub_node, WockyNode **affiliations_node) { WockyStanza *stanza; WockyNode *affiliations; GList *l; stanza = pubsub_node_make_action_stanza (self, WOCKY_STANZA_SUB_TYPE_SET, WOCKY_XMPP_NS_PUBSUB_OWNER, "affiliations", NULL, pubsub_node, &affiliations); for (l = affiliates; l != NULL; l = l->next) { const WockyPubsubAffiliation *aff = l->data; WockyNode *affiliation = wocky_node_add_child (affiliations, "affiliation"); const gchar *state = wocky_enum_to_nick ( WOCKY_TYPE_PUBSUB_AFFILIATION_STATE, aff->state); if (aff->jid == NULL) { g_warning ("Affiliate JID may not be NULL"); continue; } if (state == NULL) { g_warning ("Invalid WockyPubsubAffiliationState %u", aff->state); continue; } /* Let's allow the API user to leave node as NULL in each element in the * list of updates, given that we know which node they want to update. * But if they *do* specify it, it'd better be this node. */ if (aff->node != NULL && aff->node != self) { g_warning ("Tried to update affiliates for %s, passing a " "WockyPubsubAffiliation for %s", wocky_pubsub_node_get_name (self), wocky_pubsub_node_get_name (aff->node)); continue; } wocky_node_set_attribute (affiliation, "jid", aff->jid); wocky_node_set_attribute (affiliation, "affiliation", state); } if (affiliations_node != NULL) *affiliations_node = affiliations; return stanza; } /** * wocky_pubsub_node_modify_affiliates_async: * @self: a pubsub node * @affiliates: a list of #WockyPubsubAffiliation structures, describing only * the affiliations which should be changed. * @cancellable: optional GCancellable object, %NULL to ignore * @callback: a callback to call when the request is completed * @user_data: data to pass to @callback * * Modifies the entities affiliated to a node that you own. */ void wocky_pubsub_node_modify_affiliates_async ( WockyPubsubNode *self, GList *affiliates, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { WockyPubsubNodePrivate *priv = self->priv; GSimpleAsyncResult *simple = g_simple_async_result_new (G_OBJECT (self), callback, user_data, wocky_pubsub_node_modify_affiliates_async); WockyStanza *stanza; stanza = wocky_pubsub_node_make_modify_affiliates_stanza ( self, affiliates, NULL, NULL); wocky_porter_send_iq_async (priv->porter, stanza, cancellable, pubsub_node_void_iq_cb, simple); g_object_unref (stanza); } /** * wocky_pubsub_node_modify_affiliates_finish: * @self: a node * @result: the result * @error: location at which to store an error, if one occurred. * * Complete a call to wocky_pubsub_node_modify_affiliates_async(). * * Returns: %TRUE if the affiliates were successfully modified; %FALSE and sets * @error otherwise. */ gboolean wocky_pubsub_node_modify_affiliates_finish ( WockyPubsubNode *self, GAsyncResult *result, GError **error) { wocky_implement_finish_void (self, wocky_pubsub_node_modify_affiliates_async); } /** * wocky_pubsub_node_make_get_configuration_stanza: * @self: a pubsub node * @pubsub_node: location at which to store a pointer to the <pubsub/> * node, or %NULL * @configure_node: location at which to store a pointer to the * <configure/> node, or %NULL * * Returns: an IQ stanza to retrieve the configuration of @self */ WockyStanza * wocky_pubsub_node_make_get_configuration_stanza ( WockyPubsubNode *self, WockyNode **pubsub_node, WockyNode **configure_node) { return pubsub_node_make_action_stanza (self, WOCKY_STANZA_SUB_TYPE_GET, WOCKY_XMPP_NS_PUBSUB_OWNER, "configure", NULL, pubsub_node, configure_node); } static void get_configuration_iq_cb (GObject *source, GAsyncResult *result, gpointer user_data) { GSimpleAsyncResult *simple = user_data; WockyNodeTree *conf_tree; WockyDataForm *form = NULL; GError *error = NULL; if (wocky_pubsub_distill_iq_reply (source, result, WOCKY_XMPP_NS_PUBSUB_OWNER, "configure", &conf_tree, &error)) { form = wocky_data_form_new_from_form ( wocky_node_tree_get_top_node (conf_tree), &error); g_object_unref (conf_tree); } if (form != NULL) { g_simple_async_result_set_op_res_gpointer (simple, form, g_object_unref); } else { g_simple_async_result_set_from_error (simple, error); g_clear_error (&error); } g_simple_async_result_complete (simple); g_object_unref (simple); } /** * wocky_pubsub_node_get_configuration_async: * @self: a node * @cancellable: optional GCancellable object, %NULL to ignore * @callback: a callback to call when the request is completed * @user_data: data to pass to @callback * * Retrieves the current configuration for a node owned by the user. */ void wocky_pubsub_node_get_configuration_async ( WockyPubsubNode *self, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { WockyPubsubNodePrivate *priv = self->priv; GSimpleAsyncResult *simple = g_simple_async_result_new (G_OBJECT (self), callback, user_data, wocky_pubsub_node_get_configuration_async); WockyStanza *stanza; stanza = wocky_pubsub_node_make_get_configuration_stanza ( self, NULL, NULL); wocky_porter_send_iq_async (priv->porter, stanza, cancellable, get_configuration_iq_cb, simple); g_object_unref (stanza); } /** * wocky_pubsub_node_get_configuration_finish: * @self: a node * @result: the result * @error: location at which to store an error, if one occurred. * * Complete a call to wocky_pubsub_node_get_configuration_async(). * * Returns: a form representing the node configuration on success; %NULL and * sets @error otherwise */ WockyDataForm * wocky_pubsub_node_get_configuration_finish ( WockyPubsubNode *self, GAsyncResult *result, GError **error) { wocky_implement_finish_return_copy_pointer (self, wocky_pubsub_node_get_configuration_async, g_object_ref); } WockyPorter * wocky_pubsub_node_get_porter (WockyPubsubNode *self) { WockyPubsubNodePrivate *priv = self->priv; return priv->porter; } /* WockyPubsubAffiliation boilerplate */ /** * WockyPubsubAffiliation: * @node: the node to which this affiliation relates * @jid: the bare JID affiliated to @node * @state: the state of @jid's affiliation to @node * * Represents an affiliation to a node, as returned by * wocky_pubsub_node_list_affiliates_finish(). */ /** * WockyPubsubAffiliationState: * @WOCKY_PUBSUB_AFFILIATION_OWNER: Owner * @WOCKY_PUBSUB_AFFILIATION_PUBLISHER: Publisher * @WOCKY_PUBSUB_AFFILIATION_PUBLISH_ONLY: Publish-Only * @WOCKY_PUBSUB_AFFILIATION_MEMBER: Member * @WOCKY_PUBSUB_AFFILIATION_NONE: None * @WOCKY_PUBSUB_AFFILIATION_OUTCAST: Outcast * * Possible affiliations to a PubSub node, which determine privileges an entity * has. See XEP-0060 * §4.1 for the details. */ GType wocky_pubsub_affiliation_get_type (void) { static GType t = 0; if (G_UNLIKELY (t == 0)) t = g_boxed_type_register_static ("WockyPubsubAffiliation", (GBoxedCopyFunc) wocky_pubsub_affiliation_copy, (GBoxedFreeFunc) wocky_pubsub_affiliation_free); return t; } /** * wocky_pubsub_affiliation_new: * @node: a node * @jid: the JID affiliated to @node * @state: the state of @jid's affiliation to @node * * * * Returns: a new structure representing an affiliation, which should * ultimately be freed with wocky_pubsub_affiliation_free() */ WockyPubsubAffiliation * wocky_pubsub_affiliation_new ( WockyPubsubNode *node, const gchar *jid, WockyPubsubAffiliationState state) { WockyPubsubAffiliation aff = { NULL, g_strdup (jid), state }; g_return_val_if_fail (node != NULL, NULL); aff.node = g_object_ref (node); return g_slice_dup (WockyPubsubAffiliation, &aff); } /** * wocky_pubsub_affiliation_copy: * @aff: an existing affiliation structure * * * * Returns: a duplicate of @aff; the duplicate should ultimately be freed * with wocky_pubsub_affiliation_free() */ WockyPubsubAffiliation * wocky_pubsub_affiliation_copy ( WockyPubsubAffiliation *aff) { g_return_val_if_fail (aff != NULL, NULL); return wocky_pubsub_affiliation_new (aff->node, aff->jid, aff->state); } /** * wocky_pubsub_affiliation_free: * @aff: an affiliation * * Frees an affiliation, previously allocated with * wocky_pubsub_affiliation_new() or wocky_pubsub_affiliation_copy() */ void wocky_pubsub_affiliation_free (WockyPubsubAffiliation *aff) { g_return_if_fail (aff != NULL); g_object_unref (aff->node); g_free (aff->jid); g_slice_free (WockyPubsubAffiliation, aff); } /** * wocky_pubsub_affiliation_list_copy: * @affs: a list of #WockyPubsubAffiliation * * Shorthand for manually copying @affs, duplicating each element with * wocky_pubsub_affiliation_copy(). * * Returns: a deep copy of @affs, which should ultimately be freed with * wocky_pubsub_affiliation_list_free(). */ GList * wocky_pubsub_affiliation_list_copy (GList *affs) { return wocky_list_deep_copy ( (GBoxedCopyFunc) wocky_pubsub_affiliation_copy, affs); } /** * wocky_pubsub_affiliation_list_free: * @affs: a list of #WockyPubsubAffiliation * * Frees a list of WockyPubsubAffiliation structures, as shorthand for calling * wocky_pubsub_affiliation_free() for each element, followed by g_list_free(). */ void wocky_pubsub_affiliation_list_free (GList *affs) { g_list_foreach (affs, (GFunc) wocky_pubsub_affiliation_free, NULL); g_list_free (affs); } telepathy-gabble-0.18.2/lib/ext/wocky/wocky/wocky-pubsub-helpers.c0000644000175000017500000003523712200204546025116 0ustar00smcvsmcv00000000000000/* * wocky-pubsub-helpers.c — PubSub helper functions * Copyright © 2009–2012 Collabora Ltd. * Copyright © 2010 Nokia Corporation * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "wocky-pubsub-helpers.h" #include "wocky-namespaces.h" #include "wocky-pubsub-service.h" #include "wocky-session.h" #include "wocky-xep-0115-capabilities.h" /** * wocky_pubsub_make_event_stanza: * @node: the the name of the pubsub node; may not be %NULL * @from: a JID to use as the 'from' attribute, or %NULL * @item_out: a location to store the item #WockyNode, or %NULL * * Generates a new message stanza to send to other contacts about an * updated PEP node. * * Note that this should only be used in link-local * connections. Regular pubsub consists of making a publish stanza * with wocky_pubsub_make_publish_stanza() and sending it to your own * server. The server will then send the event stanza on to your * contacts who have the appropriate capability. * * Returns: a new #WockyStanza pubsub event stanza; free with g_object_unref() */ WockyStanza * wocky_pubsub_make_event_stanza (const gchar *node, const gchar *from, WockyNode **item_out) { WockyStanza *stanza; WockyNode *message, *event, *items, *item; g_return_val_if_fail (node != NULL, NULL); stanza = wocky_stanza_build (WOCKY_STANZA_TYPE_MESSAGE, WOCKY_STANZA_SUB_TYPE_HEADLINE, from, NULL, '(', "event", ':', WOCKY_XMPP_NS_PUBSUB_EVENT, '(', "items", '@', "node", node, '(', "item", ')', ')', ')', NULL); message = wocky_stanza_get_top_node (stanza); event = wocky_node_get_first_child (message); items = wocky_node_get_first_child (event); item = wocky_node_get_first_child (items); if (item_out != NULL) *item_out = item; return stanza; } /** * wocky_pubsub_make_publish_stanza: * @service: the JID of a PubSub service, or %NULL * @node: the name of a node on @service; may not be %NULL * @pubsub_out: address at which to store a pointer to the <pubsub/> node * @publish_out: address at which to store a pointer to the <publish/> * node * @item_out: address at which to store a pointer to the <item/> node * * * * Returns: a new iq[type='set']/pubsub/publish/item stanza */ WockyStanza * wocky_pubsub_make_publish_stanza ( const gchar *service, const gchar *node, WockyNode **pubsub_out, WockyNode **publish_out, WockyNode **item_out) { WockyStanza *stanza; WockyNode *publish, *item; g_return_val_if_fail (node != NULL, NULL); stanza = wocky_pubsub_make_stanza (service, WOCKY_STANZA_SUB_TYPE_SET, WOCKY_XMPP_NS_PUBSUB, "publish", pubsub_out, &publish); wocky_node_set_attribute (publish, "node", node); item = wocky_node_add_child (publish, "item"); if (publish_out != NULL) *publish_out = publish; if (item_out != NULL) *item_out = item; return stanza; } /** * wocky_pubsub_make_stanza: * @service: the JID of a PubSub service, or %NULL * @sub_type: #WOCKY_STANZA_SUB_TYPE_SET or #WOCKY_STANZA_SUB_TYPE_GET, as you wish * @pubsub_ns: the namespace for the <pubsub/> node of the stanza * @action_name: the action node to add to <pubsub/> * @pubsub_node: address at which to store a pointer to the <pubsub/> node * @action_node: address at wihch to store a pointer to the <@action/> * node * * * * Returns: a new iq[type=@sub_type]/pubsub/@action stanza */ WockyStanza * wocky_pubsub_make_stanza ( const gchar *service, WockyStanzaSubType sub_type, const gchar *pubsub_ns, const gchar *action_name, WockyNode **pubsub_node, WockyNode **action_node) { WockyStanza *stanza; WockyNode *pubsub, *action; g_assert (pubsub_ns != NULL); g_assert (action_name != NULL); stanza = wocky_stanza_build ( WOCKY_STANZA_TYPE_IQ, sub_type, NULL, service, '(', "pubsub", ':', pubsub_ns, '*', &pubsub, '(', action_name, '*', &action, ')', ')', NULL); if (pubsub_node != NULL) *pubsub_node = pubsub; if (action_node != NULL) *action_node = action; return stanza; } static void send_stanza_to_contact (WockyPorter *porter, WockyContact *contact, WockyStanza *stanza) { WockyStanza *to_send = wocky_stanza_copy (stanza); wocky_stanza_set_to_contact (to_send, contact); wocky_porter_send (porter, to_send); g_object_unref (to_send); } /** * wocky_send_ll_pep_event: * @session: the WockySession to send on * @stanza: the PEP event stanza to send * * Send a PEP event to all link-local contacts interested in receiving it. */ void wocky_send_ll_pep_event (WockySession *session, WockyStanza *stanza) { WockyContactFactory *contact_factory; WockyPorter *porter; WockyLLContact *self_contact; GList *contacts, *l; WockyNode *message, *event, *items; const gchar *pep_node; gchar *node; g_return_if_fail (WOCKY_IS_SESSION (session)); g_return_if_fail (WOCKY_IS_STANZA (stanza)); message = wocky_stanza_get_top_node (stanza); event = wocky_node_get_first_child (message); items = wocky_node_get_first_child (event); pep_node = wocky_node_get_attribute (items, "node"); if (pep_node == NULL) return; node = g_strdup_printf ("%s+notify", pep_node); contact_factory = wocky_session_get_contact_factory (session); porter = wocky_session_get_porter (session); contacts = wocky_contact_factory_get_ll_contacts (contact_factory); for (l = contacts; l != NULL; l = l->next) { WockyXep0115Capabilities *contact; if (!WOCKY_IS_XEP_0115_CAPABILITIES (l->data)) continue; contact = l->data; if (wocky_xep_0115_capabilities_has_feature (contact, node)) send_stanza_to_contact (porter, WOCKY_CONTACT (contact), stanza); } /* now send to self */ self_contact = wocky_contact_factory_ensure_ll_contact (contact_factory, wocky_porter_get_full_jid (porter)); send_stanza_to_contact (porter, WOCKY_CONTACT (self_contact), stanza); g_object_unref (self_contact); g_list_free (contacts); g_free (node); } static gboolean get_pubsub_child_node (WockyStanza *reply, const gchar *pubsub_ns, const gchar *child_name, WockyNodeTree **child_out, GError **error) { WockyNode *n; g_return_val_if_fail (reply != NULL, FALSE); n = wocky_node_get_child_ns ( wocky_stanza_get_top_node (reply), "pubsub", pubsub_ns); if (n == NULL) { g_set_error (error, WOCKY_PUBSUB_SERVICE_ERROR, WOCKY_PUBSUB_SERVICE_ERROR_WRONG_REPLY, "Reply doesn't contain <pubsub/> node"); return FALSE; } n = wocky_node_get_child (n, child_name); if (n == NULL) { g_set_error (error, WOCKY_PUBSUB_SERVICE_ERROR, WOCKY_PUBSUB_SERVICE_ERROR_WRONG_REPLY, "Reply doesn't contain <%s/> node", child_name); return FALSE; } if (child_out != NULL) *child_out = wocky_node_tree_new_from_node (n); return TRUE; } static gboolean wocky_pubsub_distill_iq_reply_internal (GObject *source, GAsyncResult *res, const gchar *pubsub_ns, const gchar *child_name, gboolean body_optional, WockyNodeTree **child_out, GError **error) { WockyStanza *reply; gboolean ret = FALSE; if (child_out != NULL) *child_out = NULL; /* Superlative news out of the 00:04 to Cambridge: earlier today, an * asynchronous method call announced its plans to bring a node to The * People's Republic of Wocky. */ reply = wocky_porter_send_iq_finish (WOCKY_PORTER (source), res, error); if (reply != NULL) { if (!wocky_stanza_extract_errors (reply, NULL, error, NULL, NULL)) { if (pubsub_ns == NULL) ret = TRUE; else ret = wocky_pubsub_distill_stanza (reply, pubsub_ns, child_name, body_optional, child_out, error); } g_object_unref (reply); } return ret; } /** * wocky_pubsub_distill_stanza: * @result: an iq type='result' * @pubsub_ns: the namespace of the <pubsub/> node expected in this reply * (such as #WOCKY_XMPP_NS_PUBSUB) * @child_name: the name of the child of <pubsub/> expected in this reply * (such as "subscriptions") * @body_optional: If %TRUE, the child being absent is not considered an error * @child_out: location at which to store a reference to the node tree at * @child_name, if it is found, or to be set to %NULL if it is not. * @error: location at which to store an error if the child node is not found * and @body_optional is %FALSE * * Helper function to extract a particular pubsub child node from a reply, if * it is present. If @body_optional is %FALSE, the * <pubsub><@child_name/></pubsub> tree being absent is not * considered an error: @child_out is set to %NULL and the function returns * %TRUE. * * If you are happy to delegate calling wocky_porter_send_iq_finish() and * extracting stanza errors, you would probably be better served by one of * wocky_pubsub_distill_iq_reply() or * wocky_pubsub_distill_ambivalent_iq_reply(). * * Returns: %TRUE if the child was found or was optional; %FALSE with @error * set otherwise. */ gboolean wocky_pubsub_distill_stanza (WockyStanza *result, const gchar *pubsub_ns, const gchar *child_name, gboolean body_optional, WockyNodeTree **child_out, GError **error) { g_return_val_if_fail (pubsub_ns != NULL, FALSE); g_return_val_if_fail (child_name != NULL, FALSE); if (child_out != NULL) *child_out = NULL; /* A force of a thousand function calls will anchor the node to * a resplendent out parameter modeled on the Dear Leader's hand. */ if (get_pubsub_child_node (result, pubsub_ns, child_name, child_out, error)) { /* The People's Great and Harmonious Node Pointer of Peter * Saint-Andre will conclude the most astonishing stanza breakdown * ever witnessed by man. */ return TRUE; } else if (body_optional) { /* “The stanza is perfect. We have already succeeded.” */ g_clear_error (error); return TRUE; } else { /* Meanwhile, the American president today revealed himself to be a * lizard. */ return FALSE; } } /** * wocky_pubsub_distill_iq_reply: * @source: a #WockyPorter instance * @res: a result passed to the callback for wocky_porter_send_iq_async() * @pubsub_ns: the namespace of the <pubsub/> node expected in this reply * (such as #WOCKY_XMPP_NS_PUBSUB), or %NULL if one is not expected * @child_name: the name of the child of <pubsub/> expected in this reply * (such as "subscriptions"); ignored if @pubsub_ns is %NULL * @child_out: location at which to store a reference to the node tree at * @child_name, or %NULL if you don't need it. * @error: location at which to store an error if the call to * wocky_porter_send_iq_async() returned an error, or if the reply was * an error * * Helper function to finish a wocky_porter_send_iq_async() operation * and extract a particular pubsub child from the resulting reply, if needed. * * Returns: %TRUE if the desired pubsub child was found; %FALSE if * sending the IQ failed, the reply had type='error', or the * pubsub child was not found, with @error set appropriately. */ gboolean wocky_pubsub_distill_iq_reply (GObject *source, GAsyncResult *res, const gchar *pubsub_ns, const gchar *child_name, WockyNodeTree **child_out, GError **error) { return wocky_pubsub_distill_iq_reply_internal (source, res, pubsub_ns, child_name, FALSE, child_out, error); } /** * wocky_pubsub_distill_void_iq_reply: * @source: a #WockyPorter instance * @res: a result passed to the callback for wocky_porter_send_iq_async() * @error: location at which to store an error if the call to * wocky_porter_send_iq_async() returned an error, or if the reply was * an error * * Helper function to finish a wocky_porter_send_iq_async() operation where no * pubsub child is expected in the resulting reply. * * Returns: %TRUE if the IQ was a success; %FALSE if * sending the IQ failed or the reply had type='error', * with @error set appropriately. */ gboolean wocky_pubsub_distill_void_iq_reply (GObject *source, GAsyncResult *res, GError **error) { return wocky_pubsub_distill_iq_reply_internal (source, res, NULL, NULL, TRUE, NULL, error); } /** * wocky_pubsub_distill_ambivalent_iq_reply: * @source: a #WockyPorter instance * @res: a result passed to the callback for wocky_porter_send_iq_async() * @pubsub_ns: the namespace of the <pubsub/> node accepted in this reply * (such as #WOCKY_XMPP_NS_PUBSUB) * @child_name: the name of the child of <pubsub/> accepted in this reply * (such as "subscriptions") * @child_out: location at which to store a reference to the node tree at * @child_name, if it is found, or to be set to %NULL if it is not * found * @error: location at which to store an error if the call to * wocky_porter_send_iq_async() returned an error, or if the reply was * an error * * Helper function to finish a wocky_porter_send_iq_async() operation * and extract a particular pubsub child from the resulting reply, if it is * present. This is like wocky_pubsub_distill_iq_reply(), but is ambivalent as * to whether the <pubsub/> structure has to be included. * * Returns: %TRUE if the IQ was a success; %FALSE if * sending the IQ failed or the reply had type='error', * with @error set appropriately. */ gboolean wocky_pubsub_distill_ambivalent_iq_reply (GObject *source, GAsyncResult *res, const gchar *pubsub_ns, const gchar *child_name, WockyNodeTree **child_out, GError **error) { return wocky_pubsub_distill_iq_reply_internal (source, res, pubsub_ns, child_name, TRUE, child_out, error); } telepathy-gabble-0.18.2/lib/ext/wocky/wocky/wocky-porter.c0000644000175000017500000007673212200204546023476 0ustar00smcvsmcv00000000000000/* * wocky-porter.c - Source for WockyPorter * Copyright (C) 2009-2011 Collabora Ltd. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "wocky-porter.h" #include "wocky-signals-marshal.h" #include "wocky-xmpp-connection.h" G_DEFINE_INTERFACE (WockyPorter, wocky_porter, G_TYPE_OBJECT) static void wocky_porter_default_init (WockyPorterInterface *iface) { GType iface_type = G_TYPE_FROM_INTERFACE (iface); static gsize initialization_value = 0; GParamSpec *spec; if (g_once_init_enter (&initialization_value)) { /** * WockyPorter:connection: * * The underlying #WockyXmppConnection wrapped by the #WockyPorter */ spec = g_param_spec_object ("connection", "XMPP connection", "the XMPP connection used by this porter", WOCKY_TYPE_XMPP_CONNECTION, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); g_object_interface_install_property (iface, spec); /** * WockyPorter:full-jid: * * The user's full JID (node@domain/resource). */ spec = g_param_spec_string ("full-jid", "Full JID", "The user's own full JID (node@domain/resource)", NULL, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); g_object_interface_install_property (iface, spec); /** * WockyPorter:bare-jid: * * The user's bare JID (node@domain). */ spec = g_param_spec_string ("bare-jid", "Bare JID", "The user's own bare JID (node@domain)", NULL, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); g_object_interface_install_property (iface, spec); /** * WockyPorter:resource: * * The resource part of the user's full JID, or %NULL if their full JID does * not contain a resource at all. */ spec = g_param_spec_string ("resource", "Resource", "The user's resource", NULL, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); g_object_interface_install_property (iface, spec); /** * WockyPorter::remote-closed: * @porter: the object on which the signal is emitted * * The ::remote-closed signal is emitted when the other side closed the XMPP * stream. */ g_signal_new ("remote-closed", iface_type, G_SIGNAL_RUN_LAST, 0, NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); /** * WockyPorter::remote-error: * @porter: the object on which the signal is emitted * @domain: error domain (a #GQuark) * @code: error code * @message: human-readable informative error message * * The ::remote-error signal is emitted when an error has been detected * on the XMPP stream. */ g_signal_new ("remote-error", iface_type, G_SIGNAL_RUN_LAST, 0, NULL, NULL, _wocky_signals_marshal_VOID__UINT_INT_STRING, G_TYPE_NONE, 3, G_TYPE_UINT, G_TYPE_INT, G_TYPE_STRING); /** * WockyPorter::closing: * @porter: the object on which the signal is emitted * * The ::closing signal is emitted when the #WockyPorter starts to close its * XMPP connection. Once this signal has been emitted, the #WockyPorter * can't be used to send stanzas any more. */ g_signal_new ("closing", iface_type, G_SIGNAL_RUN_LAST, 0, NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); /** * WockyPorter::sending: * @porter: the object on which the signal is emitted * @stanza: the #WockyStanza being sent, or %NULL if @porter is just * sending whitespace * * The ::sending signal is emitted whenever #WockyPorter sends data * on the XMPP connection. */ g_signal_new ("sending", iface_type, G_SIGNAL_RUN_LAST, 0, NULL, NULL, g_cclosure_marshal_VOID__OBJECT, G_TYPE_NONE, 1, WOCKY_TYPE_STANZA); g_once_init_leave (&initialization_value, 1); } } /** * wocky_porter_error_quark: * * Get the error quark used by the porter. * * Returns: the quark for porter errors. */ GQuark wocky_porter_error_quark (void) { static GQuark quark = 0; if (quark == 0) quark = g_quark_from_static_string ("wocky-porter-error"); return quark; } /** * wocky_porter_get_full_jid: (skip) * @self: a porter * * * * Returns: (transfer none): the value of #WockyPorter:full-jid */ const gchar * wocky_porter_get_full_jid (WockyPorter *self) { WockyPorterInterface *iface; g_return_val_if_fail (WOCKY_IS_PORTER (self), NULL); iface = WOCKY_PORTER_GET_INTERFACE (self); g_assert (iface->get_full_jid != NULL); return iface->get_full_jid (self); } /** * wocky_porter_get_bare_jid: (skip) * @self: a porter * * * * Returns: (transfer none): the value of #WockyPorter:bare-jid */ const gchar * wocky_porter_get_bare_jid (WockyPorter *self) { WockyPorterInterface *iface; g_return_val_if_fail (WOCKY_IS_PORTER (self), NULL); iface = WOCKY_PORTER_GET_INTERFACE (self); g_assert (iface->get_bare_jid != NULL); return iface->get_bare_jid (self); } /** * wocky_porter_get_resource: (skip) * @self: a porter * * * * Returns: (transfer none): the value of #WockyPorter:resource */ const gchar * wocky_porter_get_resource (WockyPorter *self) { WockyPorterInterface *iface; g_return_val_if_fail (WOCKY_IS_PORTER (self), NULL); iface = WOCKY_PORTER_GET_INTERFACE (self); g_assert (iface->get_resource != NULL); return iface->get_resource (self); } /** * wocky_porter_start: * @porter: a #WockyPorter * * Start a #WockyPorter to make it read and dispatch incoming stanzas. */ void wocky_porter_start (WockyPorter *self) { WockyPorterInterface *iface; g_return_if_fail (WOCKY_IS_PORTER (self)); iface = WOCKY_PORTER_GET_INTERFACE (self); g_assert (iface->start != NULL); iface->start (self); } /** * wocky_porter_send_async: * @porter: a #WockyPorter * @stanza: the #WockyStanza to send * @cancellable: optional #GCancellable object, %NULL to ignore * @callback: callback to call when the request is satisfied * @user_data: the data to pass to callback function * * Request asynchronous sending of a #WockyStanza. * When the stanza has been sent callback will be called. * You can then call wocky_porter_send_finish() to get the result * of the operation. */ void wocky_porter_send_async (WockyPorter *self, WockyStanza *stanza, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { WockyPorterInterface *iface; g_return_if_fail (WOCKY_IS_PORTER (self)); iface = WOCKY_PORTER_GET_INTERFACE (self); g_assert (iface->send_async != NULL); iface->send_async (self, stanza, cancellable, callback, user_data); } /** * wocky_porter_send_finish: * @porter: a #WockyPorter * @result: a #GAsyncResult * @error: a #GError location to store the error occuring, or %NULL to * ignore. * * Finishes sending a #WockyStanza. * * Returns: %TRUE on success or %FALSE on error. */ gboolean wocky_porter_send_finish (WockyPorter *self, GAsyncResult *result, GError **error) { WockyPorterInterface *iface; g_return_val_if_fail (WOCKY_IS_PORTER (self), FALSE); iface = WOCKY_PORTER_GET_INTERFACE (self); g_assert (iface->send_finish != NULL); return iface->send_finish (self, result, error); } /** * wocky_porter_send: * @porter: a #WockyPorter * @stanza: the #WockyStanza to send * * Send a #WockyStanza. This is a convenient function to not have to * call wocky_porter_send_async() with lot of %NULL arguments if you * don't care to know when the stanza has been actually sent. */ void wocky_porter_send (WockyPorter *porter, WockyStanza *stanza) { wocky_porter_send_async (porter, stanza, NULL, NULL, NULL); } /** * wocky_porter_register_handler_from_va: * @self: A #WockyPorter instance (passed to @callback). * @type: The type of stanza to be handled, or WOCKY_STANZA_TYPE_NONE to match * any type of stanza. * @sub_type: The subtype of stanza to be handled, or * WOCKY_STANZA_SUB_TYPE_NONE to match any type of stanza. * @from: the JID whose messages this handler is intended for (may not be * %NULL) * @priority: a priority between %WOCKY_PORTER_HANDLER_PRIORITY_MIN and * %WOCKY_PORTER_HANDLER_PRIORITY_MAX (often * %WOCKY_PORTER_HANDLER_PRIORITY_NORMAL). Handlers with a higher priority * (larger number) are called first. * @callback: A #WockyPorterHandlerFunc, which should return %FALSE to decline * the stanza (Wocky will continue to the next handler, if any), or %TRUE to * stop further processing. * @user_data: Passed to @callback. * @ap: a wocky_stanza_build() specification. The handler * will match a stanza only if the stanza received is a superset of the one * passed to this function, as per wocky_node_is_superset(). * * A va_list version of wocky_porter_register_handler_from(); see * that function for more details. * * Returns: a non-zero ID for use with wocky_porter_unregister_handler(). */ guint wocky_porter_register_handler_from_va (WockyPorter *self, WockyStanzaType type, WockyStanzaSubType sub_type, const gchar *from, guint priority, WockyPorterHandlerFunc callback, gpointer user_data, va_list ap) { guint ret; WockyStanza *stanza; g_return_val_if_fail (WOCKY_IS_PORTER (self), 0); g_return_val_if_fail (from != NULL, 0); if (type == WOCKY_STANZA_TYPE_NONE) { stanza = NULL; g_return_val_if_fail ( (va_arg (ap, WockyNodeBuildTag) == 0) && "Pattern-matching is not supported when matching stanzas " "of any type", 0); } else { stanza = wocky_stanza_build_va (type, WOCKY_STANZA_SUB_TYPE_NONE, NULL, NULL, ap); g_assert (stanza != NULL); } ret = wocky_porter_register_handler_from_by_stanza (self, type, sub_type, from, priority, callback, user_data, stanza); if (stanza != NULL) g_object_unref (stanza); return ret; } /** * wocky_porter_register_handler_from_by_stanza: * @self: A #WockyPorter instance (passed to @callback). * @type: The type of stanza to be handled, or WOCKY_STANZA_TYPE_NONE to match * any type of stanza. * @sub_type: The subtype of stanza to be handled, or * WOCKY_STANZA_SUB_TYPE_NONE to match any type of stanza. * @from: the JID whose messages this handler is intended for (may not be * %NULL) * @priority: a priority between %WOCKY_PORTER_HANDLER_PRIORITY_MIN and * %WOCKY_PORTER_HANDLER_PRIORITY_MAX (often * %WOCKY_PORTER_HANDLER_PRIORITY_NORMAL). Handlers with a higher priority * (larger number) are called first. * @callback: A #WockyPorterHandlerFunc, which should return %FALSE to decline * the stanza (Wocky will continue to the next handler, if any), or %TRUE to * stop further processing. * @user_data: Passed to @callback. * @stanza: a #WockyStanza. The handler will match a stanza only if * the stanza received is a superset of the one passed to this * function, as per wocky_node_is_superset(). * * A #WockyStanza version of wocky_porter_register_handler_from(); see * that function for more details. * * Returns: a non-zero ID for use with wocky_porter_unregister_handler(). */ guint wocky_porter_register_handler_from_by_stanza (WockyPorter *self, WockyStanzaType type, WockyStanzaSubType sub_type, const gchar *from, guint priority, WockyPorterHandlerFunc callback, gpointer user_data, WockyStanza *stanza) { WockyPorterInterface *iface; g_return_val_if_fail (WOCKY_IS_PORTER (self), 0); g_return_val_if_fail (from != NULL, 0); if (type == WOCKY_STANZA_TYPE_NONE) g_return_val_if_fail (stanza == NULL, 0); else g_return_val_if_fail (WOCKY_IS_STANZA (stanza), 0); iface = WOCKY_PORTER_GET_INTERFACE (self); g_assert (iface->register_handler_from_by_stanza != NULL); return iface->register_handler_from_by_stanza (self, type, sub_type, from, priority, callback, user_data, stanza); } /** * wocky_porter_register_handler_from: * @self: A #WockyPorter instance (passed to @callback). * @type: The type of stanza to be handled, or WOCKY_STANZA_TYPE_NONE to match * any type of stanza. * @sub_type: The subtype of stanza to be handled, or * WOCKY_STANZA_SUB_TYPE_NONE to match any type of stanza. * @from: the JID whose messages this handler is intended for (may not be * %NULL) * @priority: a priority between %WOCKY_PORTER_HANDLER_PRIORITY_MIN and * %WOCKY_PORTER_HANDLER_PRIORITY_MAX (often * %WOCKY_PORTER_HANDLER_PRIORITY_NORMAL). Handlers with a higher priority * (larger number) are called first. * @callback: A #WockyPorterHandlerFunc, which should return %FALSE to decline * the stanza (Wocky will continue to the next handler, if any), or %TRUE to * stop further processing. * @user_data: Passed to @callback. * @...: a wocky_stanza_build() specification. The handler * will match a stanza only if the stanza received is a superset of the one * passed to this function, as per wocky_node_is_superset(). * * Register a new stanza handler. * Stanza handlers are called when the Porter receives a new stanza matching * the rules of the handler. Matching handlers are sorted by priority and are * called until one claims to have handled the stanza (by returning %TRUE). * * If @from is a bare JID, then the resource of the JID in the from attribute * will be ignored: In other words, a handler registered against a bare JID * will match all stanzas from a JID with the same node * and domain: * "foo@bar.org" will match * "foo@bar.org", * "foo@bar.org/moose" and so forth. * * To register an IQ handler from Juliet for all the Jingle stanzas related * to one Jingle session: * * |[ * id = wocky_porter_register_handler_from (porter, * WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_NONE, * "juliet@example.com/Balcony", * WOCKY_PORTER_HANDLER_PRIORITY_NORMAL, * jingle_cb, * '(', "jingle", * ':', "urn:xmpp:jingle:1", * '@', "sid", "my_sid", * ')', NULL); * ]| * * To match stanzas from any sender, see * wocky_porter_register_handler_from_anyone(). If the porter is a * #WockyC2SPorter, one can match stanzas sent by the server; see * wocky_c2s_porter_register_handler_from_server(). * * Returns: a non-zero ID for use with wocky_porter_unregister_handler(). */ guint wocky_porter_register_handler_from (WockyPorter *self, WockyStanzaType type, WockyStanzaSubType sub_type, const gchar *from, guint priority, WockyPorterHandlerFunc callback, gpointer user_data, ...) { va_list ap; guint ret; g_return_val_if_fail (WOCKY_IS_PORTER (self), 0); g_return_val_if_fail (from != NULL, 0); va_start (ap, user_data); ret = wocky_porter_register_handler_from_va (self, type, sub_type, from, priority, callback, user_data, ap); va_end (ap); return ret; } /** * wocky_porter_register_handler_from_anyone_va: * @self: A #WockyPorter instance (passed to @callback). * @type: The type of stanza to be handled, or WOCKY_STANZA_TYPE_NONE to match * any type of stanza. * @sub_type: The subtype of stanza to be handled, or * WOCKY_STANZA_SUB_TYPE_NONE to match any type of stanza. * @priority: a priority between %WOCKY_PORTER_HANDLER_PRIORITY_MIN and * %WOCKY_PORTER_HANDLER_PRIORITY_MAX (often * %WOCKY_PORTER_HANDLER_PRIORITY_NORMAL). Handlers with a higher priority * (larger number) are called first. * @callback: A #WockyPorterHandlerFunc, which should return %FALSE to decline * the stanza (Wocky will continue to the next handler, if any), or %TRUE to * stop further processing. * @user_data: Passed to @callback. * @ap: a wocky_stanza_build() specification. The handler * will match a stanza only if the stanza received is a superset of the one * passed to this function, as per wocky_node_is_superset(). * * A va_list version of * wocky_porter_register_handler_from_anyone(); see that function for more * details. * * Returns: a non-zero ID for use with wocky_porter_unregister_handler(). */ guint wocky_porter_register_handler_from_anyone_va ( WockyPorter *self, WockyStanzaType type, WockyStanzaSubType sub_type, guint priority, WockyPorterHandlerFunc callback, gpointer user_data, va_list ap) { guint ret; WockyStanza *stanza; g_return_val_if_fail (WOCKY_IS_PORTER (self), 0); if (type == WOCKY_STANZA_TYPE_NONE) { stanza = NULL; g_return_val_if_fail ( (va_arg (ap, WockyNodeBuildTag) == 0) && "Pattern-matching is not supported when matching stanzas " "of any type", 0); } else { stanza = wocky_stanza_build_va (type, WOCKY_STANZA_SUB_TYPE_NONE, NULL, NULL, ap); g_assert (stanza != NULL); } ret = wocky_porter_register_handler_from_anyone_by_stanza (self, type, sub_type, priority, callback, user_data, stanza); if (stanza != NULL) g_object_unref (stanza); return ret; } /** * wocky_porter_register_handler_from_anyone_by_stanza: * @self: A #WockyPorter instance (passed to @callback). * @type: The type of stanza to be handled, or WOCKY_STANZA_TYPE_NONE to match * any type of stanza. * @sub_type: The subtype of stanza to be handled, or * WOCKY_STANZA_SUB_TYPE_NONE to match any type of stanza. * @priority: a priority between %WOCKY_PORTER_HANDLER_PRIORITY_MIN and * %WOCKY_PORTER_HANDLER_PRIORITY_MAX (often * %WOCKY_PORTER_HANDLER_PRIORITY_NORMAL). Handlers with a higher priority * (larger number) are called first. * @callback: A #WockyPorterHandlerFunc, which should return %FALSE to decline * the stanza (Wocky will continue to the next handler, if any), or %TRUE to * stop further processing. * @user_data: Passed to @callback. * @stanza: a #WockyStanza. The handler will match a stanza only if * the stanza received is a superset of the one passed to this * function, as per wocky_node_is_superset(). * * A #WockyStanza version of * wocky_porter_register_handler_from_anyone(); see that function for * more details. * * Returns: a non-zero ID for use with wocky_porter_unregister_handler(). */ guint wocky_porter_register_handler_from_anyone_by_stanza ( WockyPorter *self, WockyStanzaType type, WockyStanzaSubType sub_type, guint priority, WockyPorterHandlerFunc callback, gpointer user_data, WockyStanza *stanza) { WockyPorterInterface *iface; g_return_val_if_fail (WOCKY_IS_PORTER (self), 0); if (type == WOCKY_STANZA_TYPE_NONE) g_return_val_if_fail (stanza == NULL, 0); else g_return_val_if_fail (WOCKY_IS_STANZA (stanza), 0); iface = WOCKY_PORTER_GET_INTERFACE (self); g_assert (iface->register_handler_from_anyone_by_stanza != NULL); return iface->register_handler_from_anyone_by_stanza (self, type, sub_type, priority, callback, user_data, stanza); } /** * wocky_porter_register_handler_from_anyone: * @self: A #WockyPorter instance (passed to @callback). * @type: The type of stanza to be handled, or WOCKY_STANZA_TYPE_NONE to match * any type of stanza. * @sub_type: The subtype of stanza to be handled, or * WOCKY_STANZA_SUB_TYPE_NONE to match any type of stanza. * @priority: a priority between %WOCKY_PORTER_HANDLER_PRIORITY_MIN and * %WOCKY_PORTER_HANDLER_PRIORITY_MAX (often * %WOCKY_PORTER_HANDLER_PRIORITY_NORMAL). Handlers with a higher priority * (larger number) are called first. * @callback: A #WockyPorterHandlerFunc, which should return %FALSE to decline * the stanza (Wocky will continue to the next handler, if any), or %TRUE to * stop further processing. * @user_data: Passed to @callback. * @...: a wocky_stanza_build() specification. The handler * will match a stanza only if the stanza received is a superset of the one * passed to this function, as per wocky_node_is_superset(). * * Registers a handler for incoming stanzas from anyone, including those where * the from attribute is missing. * * For example, to register a handler matching all message stanzas received * from anyone, call: * * |[ * id = wocky_porter_register_handler (porter, * WOCKY_STANZA_TYPE_MESSAGE, WOCKY_STANZA_SUB_TYPE_NONE, NULL, * WOCKY_PORTER_HANDLER_PRIORITY_NORMAL, message_received_cb, NULL, * NULL); * ]| * * As a more interesting example, the following matches incoming PEP * notifications for contacts' geolocation information: * * |[ * id = wocky_porter_register_handler_from_anyone (porter, * WOCKY_STANZA_TYPE_MESSAGE, WOCKY_STANZA_SUB_TYPE_NONE, * WOCKY_PORTER_HANDLER_PRIORITY_MAX, * msg_event_cb, self, * '(', "event", * ':', WOCKY_XMPP_NS_PUBSUB_EVENT, * '(', "items", * '@', "node", "http://jabber.org/protocol/geoloc", * ')', * ')', * NULL); * ]| * * Returns: a non-zero ID for use with wocky_porter_unregister_handler(). */ guint wocky_porter_register_handler_from_anyone ( WockyPorter *self, WockyStanzaType type, WockyStanzaSubType sub_type, guint priority, WockyPorterHandlerFunc callback, gpointer user_data, ...) { va_list ap; guint ret; g_return_val_if_fail (WOCKY_IS_PORTER (self), 0); va_start (ap, user_data); ret = wocky_porter_register_handler_from_anyone_va (self, type, sub_type, priority, callback, user_data, ap); va_end (ap); return ret; } /** * wocky_porter_unregister_handler: * @porter: a #WockyPorter * @id: the id of the handler to unregister * * Unregister a registered handler. This handler won't be called when * receiving stanzas anymore. */ void wocky_porter_unregister_handler (WockyPorter *self, guint id) { WockyPorterInterface *iface; g_return_if_fail (WOCKY_IS_PORTER (self)); iface = WOCKY_PORTER_GET_INTERFACE (self); g_assert (iface->unregister_handler != NULL); iface->unregister_handler (self, id); } /** * wocky_porter_close_async: * @porter: a #WockyPorter * @cancellable: optional #GCancellable object, %NULL to ignore * @callback: callback to call when the request is satisfied * @user_data: the data to pass to callback function * * Request asynchronous closing of a #WockyPorter. This fires the * WockyPorter::closing signal, flushes the sending queue, closes the XMPP * stream and waits that the other side closes the XMPP stream as well. * When this is done, @callback is called. * You can then call wocky_porter_close_finish() to get the result of * the operation. */ void wocky_porter_close_async (WockyPorter *self, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { WockyPorterInterface *iface; g_return_if_fail (WOCKY_IS_PORTER (self)); iface = WOCKY_PORTER_GET_INTERFACE (self); g_assert (iface->close_async != NULL); iface->close_async (self, cancellable, callback, user_data); } /** * wocky_porter_close_finish: * @porter: a #WockyPorter * @result: a #GAsyncResult * @error: a #GError location to store the error occuring, or %NULL to ignore. * * Finishes a close operation. * * Returns: %TRUE on success or %FALSE on error. */ gboolean wocky_porter_close_finish (WockyPorter *self, GAsyncResult *result, GError **error) { WockyPorterInterface *iface; g_return_val_if_fail (WOCKY_IS_PORTER (self), FALSE); iface = WOCKY_PORTER_GET_INTERFACE (self); g_assert (iface->close_finish != NULL); return iface->close_finish (self, result, error); } /** * wocky_porter_send_iq_async: * @porter: a #WockyPorter * @stanza: the #WockyStanza to send * @cancellable: optional #GCancellable object, %NULL to ignore * @callback: callback to call when the request is satisfied * @user_data: the data to pass to callback function * * Request asynchronous sending of a #WockyStanza of type * %WOCKY_STANZA_TYPE_IQ and sub-type %WOCKY_STANZA_SUB_TYPE_GET or * %WOCKY_STANZA_SUB_TYPE_SET. * When the reply to this IQ has been received callback will be called. * You can then call #wocky_porter_send_iq_finish to get the reply stanza. */ void wocky_porter_send_iq_async (WockyPorter *self, WockyStanza *stanza, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { WockyPorterInterface *iface; g_return_if_fail (WOCKY_IS_PORTER (self)); iface = WOCKY_PORTER_GET_INTERFACE (self); g_assert (iface->send_iq_async != NULL); iface->send_iq_async (self, stanza, cancellable, callback, user_data); } /** * wocky_porter_send_iq_finish: * @porter: a #WockyPorter * @result: a #GAsyncResult * @error: a #GError location to store the error occuring, or %NULL to ignore. * * Get the reply of an IQ query. * * Returns: a reffed #WockyStanza on success, %NULL on error */ WockyStanza * wocky_porter_send_iq_finish (WockyPorter *self, GAsyncResult *result, GError **error) { WockyPorterInterface *iface; g_return_val_if_fail (WOCKY_IS_PORTER (self), FALSE); iface = WOCKY_PORTER_GET_INTERFACE (self); g_assert (iface->send_iq_finish != NULL); return iface->send_iq_finish (self, result, error); } /** * wocky_porter_acknowledge_iq: * @porter: a #WockyPorter * @stanza: a stanza of type #WOCKY_STANZA_TYPE_IQ and sub-type either * #WOCKY_STANZA_SUB_TYPE_SET or #WOCKY_STANZA_SUB_TYPE_GET * @...: a wocky_stanza_build() specification; pass %NULL to include no * body in the reply. * * Sends an acknowledgement for @stanza back to the sender, as a shorthand for * calling wocky_stanza_build_iq_result() and wocky_porter_send(). */ void wocky_porter_acknowledge_iq ( WockyPorter *porter, WockyStanza *stanza, ...) { WockyStanzaType type; WockyStanzaSubType sub_type; WockyStanza *result; va_list ap; g_return_if_fail (WOCKY_IS_PORTER (porter)); g_return_if_fail (WOCKY_IS_STANZA (stanza)); wocky_stanza_get_type_info (stanza, &type, &sub_type); g_return_if_fail (type == WOCKY_STANZA_TYPE_IQ); g_return_if_fail (sub_type == WOCKY_STANZA_SUB_TYPE_GET || sub_type == WOCKY_STANZA_SUB_TYPE_SET); va_start (ap, stanza); result = wocky_stanza_build_iq_result_va (stanza, ap); va_end (ap); if (result != NULL) { wocky_porter_send (porter, result); g_object_unref (result); } } /** * wocky_porter_send_iq_error: * @porter: the porter whence @stanza came * @stanza: a stanza of type %WOCKY_STANZA_TYPE_IQ and sub-type either * #WOCKY_STANZA_SUB_TYPE_SET or #WOCKY_STANZA_SUB_TYPE_GET * @error_code: an XMPP Core stanza error code * @message: (allow-none): an optional error message to include with the reply. * * Sends an error reply for @stanza back to its sender, with the given * @error_code and @message, and including the child element from the original * stanza. * * To send error replies with more detailed error elements, see * wocky_porter_send_iq_gerror(), or use wocky_stanza_build_iq_error() and * wocky_porter_send() directly, possibly using wocky_stanza_error_to_node() to * construct the error element. */ void wocky_porter_send_iq_error ( WockyPorter *porter, WockyStanza *stanza, WockyXmppError error_code, const gchar *message) { WockyStanzaType type; WockyStanzaSubType sub_type; GError *error = NULL; g_return_if_fail (WOCKY_IS_PORTER (porter)); g_return_if_fail (WOCKY_IS_STANZA (stanza)); wocky_stanza_get_type_info (stanza, &type, &sub_type); g_return_if_fail (type == WOCKY_STANZA_TYPE_IQ); g_return_if_fail (sub_type == WOCKY_STANZA_SUB_TYPE_GET || sub_type == WOCKY_STANZA_SUB_TYPE_SET); g_return_if_fail (error_code < NUM_WOCKY_XMPP_ERRORS); error = g_error_new_literal (WOCKY_XMPP_ERROR, error_code, message != NULL ? message : ""); wocky_porter_send_iq_gerror (porter, stanza, error); g_clear_error (&error); } /** * wocky_porter_send_iq_gerror: * @porter: the porter whence @stanza came * @stanza: a stanza of type %WOCKY_STANZA_TYPE_IQ and sub-type either * #WOCKY_STANZA_SUB_TYPE_SET or #WOCKY_STANZA_SUB_TYPE_GET * @error: an error whose domain is either %WOCKY_XMPP_ERROR, some other stanza * error domain supplied with Wocky (such as %WOCKY_JINGLE_ERROR or * %WOCKY_SI_ERROR), or a custom domain registered with * wocky_xmpp_error_register_domain() * * Sends an error reply for @stanza back to its sender, building the * <error/> element from the given @error. To send error * replies with simple XMPP Core stanza errors in the %WOCKY_XMPP_ERROR domain, * wocky_porter_send_iq_error() may be more convenient to use. */ void wocky_porter_send_iq_gerror ( WockyPorter *porter, WockyStanza *stanza, const GError *error) { WockyStanzaType type; WockyStanzaSubType sub_type; WockyStanza *result; WockyNode *result_node; g_return_if_fail (WOCKY_IS_PORTER (porter)); g_return_if_fail (WOCKY_IS_STANZA (stanza)); g_return_if_fail (error != NULL); wocky_stanza_get_type_info (stanza, &type, &sub_type); g_return_if_fail (type == WOCKY_STANZA_TYPE_IQ); g_return_if_fail (sub_type == WOCKY_STANZA_SUB_TYPE_GET || sub_type == WOCKY_STANZA_SUB_TYPE_SET); result = wocky_stanza_build_iq_error (stanza, '*', &result_node, NULL); if (result != NULL) { /* RFC3920 §9.2.3 dictates: * An IQ stanza of type "error" … MUST include an child. */ wocky_stanza_error_to_node (error, result_node); wocky_porter_send (porter, result); g_object_unref (result); } } /** * wocky_porter_force_close_async: * @porter: a #WockyPorter * @cancellable: optional #GCancellable object, %NULL to ignore * @callback: callback to call when the request is satisfied * @user_data: the data to pass to callback function * * Force the #WockyPorter to close the TCP connection of the underlying * #WockyXmppConnection. * If a close operation is pending, it will be completed with the * %WOCKY_PORTER_ERROR_FORCIBLY_CLOSED error. * When the connection has been closed, @callback will be called. * You can then call wocky_porter_force_close_finish() to get the result of * the operation. */ void wocky_porter_force_close_async (WockyPorter *self, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { WockyPorterInterface *iface; g_return_if_fail (WOCKY_IS_PORTER (self)); iface = WOCKY_PORTER_GET_INTERFACE (self); g_assert (iface->force_close_async != NULL); iface->force_close_async (self, cancellable, callback, user_data); } /** * wocky_porter_force_close_finish: * @porter: a #WockyPorter * @result: a #GAsyncResult * @error: a #GError location to store the error occuring, or %NULL to ignore. * * Finishes a force close operation. * * Returns: %TRUE on success or %FALSE on error. */ gboolean wocky_porter_force_close_finish ( WockyPorter *self, GAsyncResult *result, GError **error) { WockyPorterInterface *iface; g_return_val_if_fail (WOCKY_IS_PORTER (self), FALSE); iface = WOCKY_PORTER_GET_INTERFACE (self); g_assert (iface->force_close_finish != NULL); return iface->force_close_finish (self, result, error); } telepathy-gabble-0.18.2/lib/ext/wocky/wocky/wocky-ping.c0000644000175000017500000001445412200204546023111 0ustar00smcvsmcv00000000000000/* * wocky-ping.c - Source for WockyPing * Copyright (C) 2010 Collabora Ltd. * @author Senko Rasic * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 */ /** * SECTION: wocky-ping * @title: WockyPing * @short_description: support for pings/keepalives * * Support for XEP-0199 pings. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "wocky-ping.h" #include "wocky-heartbeat-source.h" #include "wocky-namespaces.h" #include "wocky-stanza.h" #define WOCKY_DEBUG_FLAG WOCKY_DEBUG_PING #include "wocky-debug-internal.h" G_DEFINE_TYPE (WockyPing, wocky_ping, G_TYPE_OBJECT) /* properties */ enum { PROP_PORTER = 1, PROP_PING_INTERVAL, }; /* private structure */ struct _WockyPingPrivate { WockyC2SPorter *porter; guint ping_interval; GSource *heartbeat; gulong ping_iq_cb; gboolean dispose_has_run; }; static void send_ping (WockyPing *self); static gboolean ping_iq_cb (WockyPorter *porter, WockyStanza *stanza, gpointer data); static void wocky_ping_init (WockyPing *self) { self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, WOCKY_TYPE_PING, WockyPingPrivate); } static void wocky_ping_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { WockyPing *self = WOCKY_PING (object); WockyPingPrivate *priv = self->priv; switch (property_id) { case PROP_PORTER: priv->porter = g_value_dup_object (value); break; case PROP_PING_INTERVAL: priv->ping_interval = g_value_get_uint (value); DEBUG ("updated ping interval to %u", priv->ping_interval); if (priv->heartbeat != NULL) wocky_heartbeat_source_update_interval (priv->heartbeat, priv->ping_interval); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void wocky_ping_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { WockyPing *self = WOCKY_PING (object); WockyPingPrivate *priv = self->priv; switch (property_id) { case PROP_PORTER: g_value_set_object (value, priv->porter); break; case PROP_PING_INTERVAL: g_value_set_uint (value, priv->ping_interval); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void wocky_ping_constructed (GObject *object) { WockyPing *self = WOCKY_PING (object); WockyPingPrivate *priv = self->priv; g_assert (priv->porter != NULL); priv->ping_iq_cb = wocky_porter_register_handler_from_anyone ( WOCKY_PORTER (priv->porter), WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_GET, WOCKY_PORTER_HANDLER_PRIORITY_NORMAL, ping_iq_cb, self, '(', "ping", ':', WOCKY_XMPP_NS_PING, ')', NULL); priv->heartbeat = wocky_heartbeat_source_new (priv->ping_interval); g_source_set_callback (priv->heartbeat, (GSourceFunc) send_ping, self, NULL); g_source_attach (priv->heartbeat, NULL); } static void wocky_ping_dispose (GObject *object) { WockyPing *self = WOCKY_PING (object); WockyPingPrivate *priv = self->priv; if (priv->dispose_has_run) return; priv->dispose_has_run = TRUE; if (priv->ping_iq_cb != 0) { wocky_porter_unregister_handler (WOCKY_PORTER (priv->porter), priv->ping_iq_cb); priv->ping_iq_cb = 0; } g_object_unref (priv->porter); priv->porter = NULL; g_source_destroy (self->priv->heartbeat); g_source_unref (self->priv->heartbeat); self->priv->heartbeat = NULL; if (G_OBJECT_CLASS (wocky_ping_parent_class)->dispose) G_OBJECT_CLASS (wocky_ping_parent_class)->dispose (object); } static void wocky_ping_class_init (WockyPingClass *wocky_ping_class) { GObjectClass *object_class = G_OBJECT_CLASS (wocky_ping_class); GParamSpec *spec; g_type_class_add_private (wocky_ping_class, sizeof (WockyPingPrivate)); object_class->constructed = wocky_ping_constructed; object_class->set_property = wocky_ping_set_property; object_class->get_property = wocky_ping_get_property; object_class->dispose = wocky_ping_dispose; spec = g_param_spec_object ("porter", "Wocky C2S porter", "the wocky porter to set up keepalive pings on", WOCKY_TYPE_C2S_PORTER, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, PROP_PORTER, spec); spec = g_param_spec_uint ("ping-interval", "Ping interval", "keepalive ping interval in seconds, or 0 to disable", 0, G_MAXUINT, 0, G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, PROP_PING_INTERVAL, spec); } WockyPing * wocky_ping_new (WockyC2SPorter *porter, guint interval) { g_return_val_if_fail (WOCKY_IS_C2S_PORTER (porter), NULL); return g_object_new (WOCKY_TYPE_PING, "porter", porter, "ping-interval", interval, NULL); } static void send_ping (WockyPing *self) { g_return_if_fail (WOCKY_IS_PING (self)); /* We send a whitespace ping and not a XMPP one to save bandwidth. * As much as it can sound a stupidly small gain, it can be useful when * sending pings on an idle cellular connection; very small packets can * be sent using a low power 3G channel. */ DEBUG ("pinging"); wocky_c2s_porter_send_whitespace_ping_async (self->priv->porter, NULL, NULL, NULL); } static gboolean ping_iq_cb (WockyPorter *porter, WockyStanza *stanza, gpointer data) { const gchar *from = wocky_stanza_get_from (stanza); DEBUG ("replying to ping from %s", from ? from : ""); wocky_porter_acknowledge_iq (porter, stanza, NULL); return TRUE; } telepathy-gabble-0.18.2/lib/ext/wocky/wocky/wocky-pep-service.c0000644000175000017500000003142412200204546024372 0ustar00smcvsmcv00000000000000/* * wocky-pep-service.c - WockyPepService * Copyright © 2009,2012 Collabora Ltd. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 */ /** * SECTION: wocky-pep-service * @title: WockyPepService * @short_description: Object to represent a single PEP service * @include: wocky/wocky-pep-service.h * * Object to aid with looking up PEP nodes and listening for changes. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "wocky-pep-service.h" #include "wocky-pubsub-helpers.h" #include "wocky-porter.h" #include "wocky-utils.h" #include "wocky-namespaces.h" #include "wocky-signals-marshal.h" #define WOCKY_DEBUG_FLAG WOCKY_DEBUG_PUBSUB #include "wocky-debug-internal.h" G_DEFINE_TYPE (WockyPepService, wocky_pep_service, G_TYPE_OBJECT) /* signal enum */ enum { CHANGED, LAST_SIGNAL, }; static guint signals[LAST_SIGNAL] = {0}; enum { PROP_NODE = 1, PROP_SUBSCRIBE, }; /* private structure */ struct _WockyPepServicePrivate { WockySession *session; WockyPorter *porter; WockyContactFactory *contact_factory; gchar *node; gboolean subscribe; guint handler_id; gboolean dispose_has_run; }; static void wocky_pep_service_init (WockyPepService *self) { self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, WOCKY_TYPE_PEP_SERVICE, WockyPepServicePrivate); } static void wocky_pep_service_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { WockyPepService *self = WOCKY_PEP_SERVICE (object); WockyPepServicePrivate *priv = self->priv; switch (property_id) { case PROP_NODE: priv->node = g_value_dup_string (value); break; case PROP_SUBSCRIBE: priv->subscribe = g_value_get_boolean (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void wocky_pep_service_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { WockyPepService *self = WOCKY_PEP_SERVICE (object); WockyPepServicePrivate *priv = self->priv; switch (property_id) { case PROP_NODE: g_value_set_string (value, priv->node); break; case PROP_SUBSCRIBE: g_value_set_boolean (value, priv->subscribe); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void wocky_pep_service_dispose (GObject *object) { WockyPepService *self = WOCKY_PEP_SERVICE (object); WockyPepServicePrivate *priv = self->priv; if (priv->dispose_has_run) return; priv->dispose_has_run = TRUE; if (priv->porter != NULL) { g_assert (priv->handler_id != 0); wocky_porter_unregister_handler (priv->porter, priv->handler_id); g_object_unref (priv->porter); } if (priv->contact_factory != NULL) g_object_unref (priv->contact_factory); if (G_OBJECT_CLASS (wocky_pep_service_parent_class)->dispose) G_OBJECT_CLASS (wocky_pep_service_parent_class)->dispose (object); } static void wocky_pep_service_finalize (GObject *object) { WockyPepService *self = WOCKY_PEP_SERVICE (object); WockyPepServicePrivate *priv = self->priv; g_free (priv->node); G_OBJECT_CLASS (wocky_pep_service_parent_class)->finalize (object); } static void wocky_pep_service_constructed (GObject *object) { WockyPepService *self = WOCKY_PEP_SERVICE (object); WockyPepServicePrivate *priv = self->priv; g_assert (priv->node != NULL); } static void wocky_pep_service_class_init (WockyPepServiceClass *wocky_pep_service_class) { GObjectClass *object_class = G_OBJECT_CLASS (wocky_pep_service_class); GParamSpec *param_spec; g_type_class_add_private (wocky_pep_service_class, sizeof (WockyPepServicePrivate)); object_class->set_property = wocky_pep_service_set_property; object_class->get_property = wocky_pep_service_get_property; object_class->dispose = wocky_pep_service_dispose; object_class->finalize = wocky_pep_service_finalize; object_class->constructed = wocky_pep_service_constructed; /** * WockyPepService:node: * * Namespace of the PEP node. */ param_spec = g_param_spec_string ("node", "node", "namespace of the pep node", NULL, G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, PROP_NODE, param_spec); /** * WockyPepService:subscribe: * * %TRUE if Wocky is to subscribe to the notifications of the node. */ param_spec = g_param_spec_boolean ("subscribe", "subscribe", "if TRUE, Wocky will subscribe to the notifications of the node", FALSE, G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, PROP_SUBSCRIBE, param_spec); /** * WockyPepService::changed: * @self: a #WockyPepService object * @contact: the #WockyBareContact who changed the node * @stanza: the #WockyStanza * @item: the first—and typically only—<item> element in @stanza, or * %NULL if there is none. * * Emitted when the node value changes. */ signals[CHANGED] = g_signal_new ("changed", G_OBJECT_CLASS_TYPE (wocky_pep_service_class), G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED, 0, NULL, NULL, _wocky_signals_marshal_VOID__OBJECT_OBJECT_POINTER, G_TYPE_NONE, 3, WOCKY_TYPE_BARE_CONTACT, WOCKY_TYPE_STANZA, G_TYPE_POINTER); } /** * wocky_pep_service_new: * @node: the namespace of the PEP node * @subscribe: %TRUE if Wocky is to subscribe to the notifications of * the node, otherwise %FALSE * * A convenience function to create a new #WockyPepService object. * * Returns: a new #WockyPepService */ WockyPepService * wocky_pep_service_new (const gchar *node, gboolean subscribe) { return g_object_new (WOCKY_TYPE_PEP_SERVICE, "node", node, "subscribe", subscribe, NULL); } static gboolean msg_event_cb (WockyPorter *porter, WockyStanza *stanza, gpointer user_data) { WockyPepService *self = WOCKY_PEP_SERVICE (user_data); WockyPepServicePrivate *priv = self->priv; const gchar *from; WockyBareContact *contact; WockyStanzaSubType sub_type; WockyNode *event, *items, *item; from = wocky_stanza_get_from (stanza); if (from == NULL) { DEBUG ("No 'from' attribute; ignoring event"); return FALSE; } wocky_stanza_get_type_info (stanza, NULL, &sub_type); /* type of the message is supposed to be 'headline' but old ejabberd * omits it */ if (sub_type != WOCKY_STANZA_SUB_TYPE_NONE && sub_type != WOCKY_STANZA_SUB_TYPE_HEADLINE) { return FALSE; } event = wocky_node_get_child_ns (wocky_stanza_get_top_node (stanza), "event", WOCKY_XMPP_NS_PUBSUB_EVENT); g_return_val_if_fail (event != NULL, FALSE); items = wocky_node_get_child (event, "items"); g_return_val_if_fail (items != NULL, FALSE); item = wocky_node_get_child (items, "item"); contact = wocky_contact_factory_ensure_bare_contact ( priv->contact_factory, from); g_signal_emit (G_OBJECT (self), signals[CHANGED], 0, contact, stanza, item); g_object_unref (contact); return TRUE; } /** * wocky_pep_service_start: * @self: a #WockyPepService object * @session: a #WockySession object * * Start listening to the PEP node @node and signal changes by using * #WockyPepService::changed. */ void wocky_pep_service_start (WockyPepService *self, WockySession *session) { WockyPepServicePrivate *priv = self->priv; g_assert (priv->session == NULL); priv->session = session; priv->porter = wocky_session_get_porter (priv->session); g_object_ref (priv->porter); priv->contact_factory = wocky_session_get_contact_factory (priv->session); g_object_ref (priv->contact_factory); /* Register event handler */ priv->handler_id = wocky_porter_register_handler_from_anyone (priv->porter, WOCKY_STANZA_TYPE_MESSAGE, WOCKY_STANZA_SUB_TYPE_NONE, WOCKY_PORTER_HANDLER_PRIORITY_MAX, msg_event_cb, self, '(', "event", ':', WOCKY_XMPP_NS_PUBSUB_EVENT, '(', "items", '@', "node", priv->node, ')', ')', NULL); /* TODO: subscribe to node if needed */ } static void send_query_cb (GObject *source, GAsyncResult *res, gpointer user_data) { GSimpleAsyncResult *result = G_SIMPLE_ASYNC_RESULT (user_data); GError *error = NULL; WockyStanza *reply; reply = wocky_porter_send_iq_finish (WOCKY_PORTER (source), res, &error); if (reply == NULL) { g_simple_async_result_set_from_error (result, error); g_error_free (error); } else { g_simple_async_result_set_op_res_gpointer (result, reply, g_object_unref); } g_simple_async_result_complete (result); g_object_unref (result); } /** * wocky_pep_service_get_async: * @self: a #WockyPepService object * @contact: a #WockyBareContact object * @cancellable: an optional #GCancellable object, or %NULL * @callback: a function to call when the node is retrieved * @user_data: user data for @callback * * Starts an asynchronous operation to get the PEP node, * #WockyPepService:node. * * When the operation is complete, @callback will be called and the * function should call wocky_pep_service_get_finish(). */ void wocky_pep_service_get_async (WockyPepService *self, WockyBareContact *contact, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { WockyPepServicePrivate *priv = self->priv; WockyStanza *msg; GSimpleAsyncResult *result; const gchar *jid; if (priv->porter == NULL) { g_simple_async_report_error_in_idle (G_OBJECT (self), callback, user_data, WOCKY_PORTER_ERROR, WOCKY_PORTER_ERROR_NOT_STARTED, "Service has not been started"); return; } jid = wocky_bare_contact_get_jid (contact); msg = wocky_stanza_build ( WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_GET, NULL, jid, '(', "pubsub", ':', WOCKY_XMPP_NS_PUBSUB, '(', "items", '@', "node", priv->node, ')', ')', NULL); result = g_simple_async_result_new (G_OBJECT (self), callback, user_data, wocky_pep_service_get_async); wocky_porter_send_iq_async (priv->porter, msg, cancellable, send_query_cb, result); g_object_unref (msg); } /** * wocky_pep_service_get_finish: * @self: a #WockyPepService object * @result: a #GAsyncResult * @item: (out) (allow-none): on success, the first <item> element * in the result, or %NULL if @self has no published items. * @error: a location to store a #GError if an error occurs * * Finishes an asynchronous operation to get the PEP node, * #WockyPepService:node. For more details, see * wocky_pep_service_get_async(). * * Returns: the #WockyStanza retrieved from getting the PEP node. */ WockyStanza * wocky_pep_service_get_finish (WockyPepService *self, GAsyncResult *result, WockyNode **item, GError **error) { GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (result); WockyStanza *reply; if (g_simple_async_result_propagate_error (simple, error)) return NULL; g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (self), wocky_pep_service_get_async), NULL); reply = WOCKY_STANZA (g_simple_async_result_get_op_res_gpointer (simple)); if (item != NULL) { WockyNode *pubsub_node = wocky_node_get_child_ns ( wocky_stanza_get_top_node (reply), "pubsub", WOCKY_XMPP_NS_PUBSUB); WockyNode *items_node = NULL; if (pubsub_node != NULL) items_node = wocky_node_get_child (pubsub_node, "items"); if (items_node != NULL) *item = wocky_node_get_child (items_node, "item"); else *item = NULL; } return g_object_ref (reply); } /** * wocky_pep_service_make_publish_stanza: * @self: a #WockyPepService * @item: a location to store the item #WockyNode, or %NULL * * Generates a new IQ type='set' PEP publish stanza. * * Returns: a new #WockyStanza PEP publish stanza; free with g_object_unref() */ WockyStanza * wocky_pep_service_make_publish_stanza (WockyPepService *self, WockyNode **item) { WockyPepServicePrivate *priv = self->priv; return wocky_pubsub_make_publish_stanza (NULL, priv->node, NULL, NULL, item); } telepathy-gabble-0.18.2/lib/ext/wocky/wocky/wocky-node-tree.c0000644000175000017500000001240212200204546024025 0ustar00smcvsmcv00000000000000/* * wocky-node-tree.c - Source for WockyNodeTree * Copyright (C) 2006-2010 Collabora Ltd. * @author Sjoerd Simons * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include "wocky-node-tree.h" #include "wocky-node-private.h" G_DEFINE_TYPE(WockyNodeTree, wocky_node_tree, G_TYPE_OBJECT) /* properties */ enum { PROP_TOP_NODE = 1, }; struct _WockyNodeTreePrivate { gboolean dispose_has_run; WockyNode *node; }; static void wocky_node_tree_init (WockyNodeTree *self) { self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, WOCKY_TYPE_NODE_TREE, WockyNodeTreePrivate); } static void wocky_node_tree_dispose (GObject *object); static void wocky_node_tree_finalize (GObject *object); static void wocky_node_tree_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { WockyNodeTree *self = WOCKY_NODE_TREE (object); switch (property_id) { case PROP_TOP_NODE: self->priv->node = g_value_get_pointer (value); g_warn_if_fail (self->priv->node != NULL); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void wocky_node_tree_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { WockyNodeTree *self = WOCKY_NODE_TREE (object); switch (property_id) { case PROP_TOP_NODE: g_value_set_pointer (value, self->priv->node); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void wocky_node_tree_class_init (WockyNodeTreeClass *wocky_node_tree_class) { GObjectClass *object_class = G_OBJECT_CLASS (wocky_node_tree_class); GParamSpec *param_spec; g_type_class_add_private (wocky_node_tree_class, sizeof (WockyNodeTreePrivate)); object_class->dispose = wocky_node_tree_dispose; object_class->finalize = wocky_node_tree_finalize; object_class->set_property = wocky_node_tree_set_property; object_class->get_property = wocky_node_tree_get_property; param_spec = g_param_spec_pointer ("top-node", "top-node", "The topmost node of the node-tree", G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, PROP_TOP_NODE, param_spec); } void wocky_node_tree_dispose (GObject *object) { WockyNodeTree *self = WOCKY_NODE_TREE (object); WockyNodeTreePrivate *priv = self->priv; if (priv->dispose_has_run) return; priv->dispose_has_run = TRUE; /* release any references held by the object here */ if (G_OBJECT_CLASS (wocky_node_tree_parent_class)->dispose) G_OBJECT_CLASS (wocky_node_tree_parent_class)->dispose (object); } void wocky_node_tree_finalize (GObject *object) { WockyNodeTree *self = WOCKY_NODE_TREE (object); /* free any data held directly by the object here */ wocky_node_free (self->priv->node); G_OBJECT_CLASS (wocky_node_tree_parent_class)->finalize (object); } /** * wocky_node_tree_new: * @name: The name of the toplevel node * @ns: The namespace of the toplevel node * @...: the description of the node tree to build, * terminated with %NULL * * Build a node-tree from a list of arguments. * Example: * * * wocky_node_tree_new ("html", "http://www.w3.org/1999/xhtml", * '(', "body", '@', "textcolor", "red", * '$', "Wocky wooo", * ')', * NULL); * * * Returns: a new node-tree object */ WockyNodeTree * wocky_node_tree_new (const gchar *name, const gchar *ns, ...) { WockyNodeTree *tree; va_list va; va_start (va, ns); tree = wocky_node_tree_new_va (name, ns, va); va_end (va); return tree; } WockyNodeTree * wocky_node_tree_new_va (const gchar *name, const char *ns, va_list va) { WockyNode *top; g_return_val_if_fail (name != NULL, NULL); g_return_val_if_fail (ns != NULL, NULL); top = wocky_node_new (name, ns); wocky_node_add_build_va (top, va); return g_object_new (WOCKY_TYPE_NODE_TREE, "top-node", top, NULL); } /** * wocky_node_tree_new_from_node: * @node: The node to copy * * Build a new WockyNodeTree that contains a copy of the given node. * * Returns: a new node-tree object */ WockyNodeTree * wocky_node_tree_new_from_node (WockyNode *node) { WockyNode *top = _wocky_node_copy (node); return g_object_new (WOCKY_TYPE_NODE_TREE, "top-node", top, NULL); } WockyNode * wocky_node_tree_get_top_node (WockyNodeTree *self) { return self->priv->node; } telepathy-gabble-0.18.2/lib/ext/wocky/wocky/wocky-node-private.h0000644000175000017500000000231512200204546024547 0ustar00smcvsmcv00000000000000/* * wocky-node-private.h - Private header for dealing with Wocky xmpp nodes * Copyright (C) 2010 Collabora Ltd. * @author Sjoerd Simons * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 */ #if !defined (WOCKY_COMPILATION) # error "This is an internal header." #endif #ifndef __WOCKY__NODE_PRIVATE_H__ #define __WOCKY__NODE_PRIVATE_H__ #include #include G_BEGIN_DECLS WockyNode *_wocky_node_copy (WockyNode *node); G_END_DECLS #endif /* #ifndef __WOCKY_NODE__PRIVATE_H__*/ telepathy-gabble-0.18.2/lib/ext/wocky/wocky/wocky-node.c0000644000175000017500000011621012200204546023072 0ustar00smcvsmcv00000000000000/* * wocky-node.c - Code for Wocky xmpp nodes * Copyright (C) 2006-2010 Collabora Ltd. * @author Sjoerd Simons * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include "wocky-node.h" #include "wocky-node-private.h" #include "wocky-node-tree.h" #include "wocky-utils.h" #include "wocky-namespaces.h" /** * SECTION: wocky-node * @title: WockyNode * @short_description: representation of a XMPP node * @include: wocky/wocky-node.h * * Low level representation of a XMPP node. Provides ways to set various * parameters on the node, such as content, language, namespaces and prefixes. * It also offers methods to lookup children of a node. */ typedef struct { gchar *key; gchar *value; gchar *prefix; GQuark ns; } Attribute; typedef struct { const gchar *key; GQuark ns; } Tuple; typedef struct { const gchar *ns_urn; gchar *prefix; GQuark ns; } NSPrefix; static NSPrefix default_attr_ns_prefixes[] = { { WOCKY_GOOGLE_NS_AUTH, "ga" }, { NULL, NULL } }; static GHashTable *user_ns_prefixes = NULL; static GHashTable *default_ns_prefixes = NULL; /* Do a strndup operation, but at the same time replace all characters that * aren't valid according to g_utf8_validate by � */ static gchar * strndup_make_valid (const gchar *str, gssize len) { const gchar *remainder = str; GString *result; const gchar *endp; gssize left = len; /* Simplify things by always keeping track of the string lenght */ if (left < 0) left = strlen (remainder); result = g_string_sized_new (len); while (!g_utf8_validate (remainder, left, &endp)) { g_string_append_len (result, remainder, endp - remainder); /* append U+FFFD REPLACEMENT CHARACTER */ g_string_append (result, "\357\277\275"); /* left, minus the valid part of the string */ left -= (endp - remainder); remainder = g_utf8_find_next_char (endp, endp + left); /* left, minus the skipped part, if there is no next utf8 character, * nothing is left */ if (remainder == NULL) left = 0; else if (left > 0) left -= (remainder - endp); } g_string_append_len (result, remainder, left); return g_string_free (result, FALSE); } static gchar * strndup_validated (const gchar *str, gssize len) { if (str == NULL) return NULL; /* Fast path, string happily validates, simple copy */ if (G_LIKELY (g_utf8_validate (str, len, NULL))) { if (len < 0) return g_strdup (str); else return g_strndup (str, len); } /* slow path, string doesn't validate.. */ return strndup_make_valid (str, len); } static gchar * concat_validated (const gchar *s1, const gchar *s2, gssize s2_size) { gchar *result; gssize s1_size; /* data to be freed after use if needed */ const gchar *to_free = NULL; /* concatting to nothing, iotw, strndup :) */ if (s1 == NULL) return strndup_validated (s2, s2_size); s1_size = strlen (s1); if (s2_size < 0) s2_size = strlen (s2); if (G_UNLIKELY (!g_utf8_validate (s2, s2_size, NULL))) { /* Make a validated copy we will free later on. Making a copy to just * concat and then free isn't the most efficient way, but at this point * we're out of the fast-path anyway */ to_free = s2 = strndup_make_valid (s2, s2_size); s2_size = strlen (s2); } result = g_malloc0 (s1_size + s2_size + 1); memcpy (result, s1, s1_size); memcpy (result + s1_size, s2, s2_size); g_free ((gchar *) to_free); return result; } static WockyNode * new_node (const char *name, GQuark ns) { WockyNode *result; g_return_val_if_fail (name != NULL, NULL); g_return_val_if_fail (ns != 0, NULL); result = g_slice_new0 (WockyNode); result->name = strndup_validated (name, -1); result->ns = ns; return result; } /** * wocky_node_new: * @name: the node's name (may not be NULL) * @ns: the nodes namespace (may not be NULL) * * Convenience function which creates a #WockyNode and sets its * name to @name. * * Returns: a newly allocated #WockyNode. */ WockyNode * wocky_node_new (const char *name, const gchar *ns) { g_return_val_if_fail (ns != NULL, NULL); return new_node (name, g_quark_from_string (ns)); } static void attribute_free (Attribute *a) { g_free (a->key); g_free (a->value); g_free (a->prefix); g_slice_free (Attribute, a); } /** * wocky_node_free: * @node: a #WockyNode. * * Convenience function that frees the passed in #WockyNode and * all of its children. */ void wocky_node_free (WockyNode *node) { GSList *l; if (node == NULL) { return ; } g_free (node->name); g_free (node->content); g_free (node->language); for (l = node->children; l != NULL ; l = l->next) { wocky_node_free ((WockyNode *) l->data); } g_slist_free (node->children); for (l = node->attributes; l != NULL ; l = l->next) { Attribute *a = (Attribute *) l->data; attribute_free (a); } g_slist_free (node->attributes); g_slice_free (WockyNode, node); } /** * wocky_node_each_attribute: * @node: a #WockyNode * @func: the function to be called on each node's attribute * @user_data: user data to pass to the function * * Calls a function for each attribute of a #WockyNode. */ void wocky_node_each_attribute (WockyNode *node, wocky_node_each_attr_func func, gpointer user_data) { GSList *l; for (l = node->attributes; l != NULL ; l = l->next) { Attribute *a = (Attribute *) l->data; const gchar *ns = g_quark_to_string (a->ns); if (!func (a->key, a->value, a->prefix, ns, user_data)) { return; } } } /** * wocky_node_each_child: * @node: a #WockyNode * @func: the function to be called on each node's child * @user_data: user data to pass to the function * * Calls a function for each child of a #WockyNode. */ void wocky_node_each_child (WockyNode *node, wocky_node_each_child_func func, gpointer user_data) { GSList *l; for (l = node->children; l != NULL ; l = l->next) { WockyNode *n = (WockyNode *) l->data; if (!func (n, user_data)) { return; } } } static gint attribute_compare (gconstpointer a, gconstpointer b) { const Attribute *attr = (const Attribute *)a; const Tuple *target = (const Tuple *)b; if (target->ns != 0 && attr->ns != target->ns) { return 1; } return strcmp (attr->key, target->key); } /** * wocky_node_get_attribute_ns: * @node: a #WockyNode * @key: the attribute name * @ns: the namespace to search within, or %NULL * * Returns the value of an attribute in a #WockyNode, limiting the search * within a specific namespace. If the namespace is %NULL, this is equivalent * to wocky_node_get_attribute(). * * Returns: the value of the attribute @key, or %NULL if @node doesn't * have such attribute in @ns. */ const gchar * wocky_node_get_attribute_ns (WockyNode *node, const gchar *key, const gchar *ns) { GSList *link; Tuple search; search.key = (gchar *) key; search.ns = (ns != NULL ? g_quark_from_string (ns) : 0); link = g_slist_find_custom (node->attributes, &search, attribute_compare); return (link == NULL) ? NULL : ((Attribute *) (link->data))->value; } /** * wocky_node_get_attribute: * @node: a #WockyNode * @key: the attribute name * * Returns the value of an attribute in a #WockyNode. * * Returns: the value of the attribute @key, or %NULL if @node doesn't * have such attribute. */ const gchar * wocky_node_get_attribute (WockyNode *node, const gchar *key) { return wocky_node_get_attribute_ns (node, key, NULL); } /** * wocky_node_set_attribute: * @node: a #WockyNode * @key: the attribute name to set * @value: the value to set * * Sets an attribute in a #WockyNode to a specific value. */ void wocky_node_set_attribute (WockyNode *node, const gchar *key, const gchar *value) { g_assert (value != NULL); wocky_node_set_attribute_n_ns (node, key, value, strlen (value), NULL); } /** * wocky_node_set_attributes: * @node: a #WockyNode * @key: the attribute name to set * @...: pairs of keys and values, terminated by %NULL * * Sets attributes in a #WockyNode to specific values. */ void wocky_node_set_attributes (WockyNode *node, const gchar *key, ...) { va_list args; g_return_if_fail (key != NULL); va_start (args, key); while (key != NULL) { const gchar *value; value = (const gchar *) va_arg (args, gchar *); wocky_node_set_attribute (node, key, value); key = (const gchar *) va_arg (args, gchar *); } va_end (args); } /** * wocky_node_set_attribute_ns: * @node: a #WockyNode * @key: the attribute name to set * @value: the value to set * @ns: a namespace, or %NULL * * Sets an attribute in a #WockyNode, within a specific namespace. * If the namespace is %NULL, this is equivalent to * wocky_node_set_attribute(). */ void wocky_node_set_attribute_ns (WockyNode *node, const gchar *key, const gchar *value, const gchar *ns) { wocky_node_set_attribute_n_ns (node, key, value, strlen (value), ns); } static NSPrefix * ns_prefix_new (const gchar *urn, GQuark ns, const gchar *prefix) { NSPrefix *nsp = g_slice_new0 (NSPrefix); nsp->ns_urn = urn; nsp->prefix = strndup_validated (prefix, -1); nsp->ns = ns; return nsp; } static void ns_prefix_free (NSPrefix *nsp) { g_free (nsp->prefix); g_slice_free (NSPrefix, nsp); } static GHashTable * _init_prefix_table (void) { /* do NOT use the astonishingly poorly named g_int_hash here */ /* it most emphatically does NOT do what it says on the tin */ return g_hash_table_new_full (g_direct_hash, /* quarks are uint32s */ g_direct_equal, /* ibid */ NULL, /* cannot free quarks */ (GDestroyNotify)ns_prefix_free); } /* convert the NS URN Quark to a base-26 number represented as a * * lowercase a-z digit string (aa, ab, ac, ad ... etc) * * then prepend a string ("wocky-") to make the attr ns prefix */ static gchar * _generate_ns_prefix (GQuark ns) { GString *prefix = g_string_new ("wocky-"); int p = ns; /* actually, I think we might end up with the digits in le order * * having re-read the code, but that doesn't actually matter. */ while (p > 0) { guchar x = (p % 26); p -= x; p /= 26; x += 'a'; g_string_append_c (prefix, x); } return g_string_free (prefix, FALSE); } static void _init_user_prefix_table (void) { if (user_ns_prefixes == NULL) user_ns_prefixes = _init_prefix_table (); } static const NSPrefix * _add_prefix_to_table (GHashTable *table, GQuark ns, const gchar *urn, const gchar *prefix) { NSPrefix *nsp = ns_prefix_new (urn, ns, prefix); g_hash_table_insert (table, GINT_TO_POINTER (ns), nsp); return nsp; } static void _init_default_prefix_table (void) { int i; if (default_ns_prefixes != NULL) return; default_ns_prefixes = _init_prefix_table (); for (i = 0; default_attr_ns_prefixes[i].ns_urn != NULL; i++) { const gchar *urn = default_attr_ns_prefixes[i].ns_urn; GQuark ns = g_quark_from_string (urn); gchar *prefix = _generate_ns_prefix (ns); _add_prefix_to_table (default_ns_prefixes, ns, urn, prefix); g_free (prefix); } } static const gchar * _attribute_ns_get_prefix (GQuark ns, const gchar *urn) { const NSPrefix *nsp = NULL; gchar *prefix; /* check user-registered explicit prefixes for this namespace */ nsp = g_hash_table_lookup (user_ns_prefixes, GINT_TO_POINTER (ns)); if (nsp != NULL) return nsp->prefix; /* check any built-in explicit prefixes for this namespace */ nsp = g_hash_table_lookup (default_ns_prefixes, GINT_TO_POINTER (ns)); if (nsp != NULL) return nsp->prefix; /* ok, there was no registered prefix - generate and register a prefix */ /* initialise the user prefix table here if we need to */ prefix = _generate_ns_prefix (ns); nsp = _add_prefix_to_table (user_ns_prefixes, ns, urn, prefix); g_free (prefix); return nsp->prefix; } /** * wocky_node_attribute_ns_get_prefix_from_quark: * @ns: a quark corresponding to an XML namespace URN * * Gets the prefix of the namespace identified by the quark. * * Returns: a string containing the prefix of the namespace @ns. */ const gchar * wocky_node_attribute_ns_get_prefix_from_quark (GQuark ns) { const gchar *urn; if (ns == 0) return NULL; urn = g_quark_to_string (ns); /* fetch an existing prefix, a default prefix, or a newly allocated one * * in that order of preference */ return _attribute_ns_get_prefix (ns, urn); } /** * wocky_node_attribute_ns_get_prefix_from_urn: * @urn: a string containing an URN * * Gets the prefix of the namespace identified by the URN. * * Returns: a string containing the prefix of the namespace @urn. */ const gchar * wocky_node_attribute_ns_get_prefix_from_urn (const gchar *urn) { GQuark ns; if ((urn == NULL) || (*urn == '\0')) return NULL; ns = g_quark_from_string (urn); /* fetch an existing prefix, a default prefix, or a newly allocated one * * in that order of preference */ return _attribute_ns_get_prefix (ns, urn); } /** * wocky_node_attribute_ns_set_prefix: * @ns: a #GQuark * @prefix: a string containing the desired prefix * * Sets a desired prefix for a namespace. */ void wocky_node_attribute_ns_set_prefix (GQuark ns, const gchar *prefix) { const gchar *urn = g_quark_to_string (ns); /* add/replace user prefix table entry */ _add_prefix_to_table (user_ns_prefixes, ns, urn, prefix); } /** * wocky_node_set_attribute_n_ns: * @node: a #WockyNode * @key: the attribute to set * @value: the value to set * @value_size: the number of bytes of @value to set as a value * @ns: a namespace, or %NULL * * Sets a new attribute to a #WockyNode, with the supplied values. * If the namespace is %NULL, this is equivalent to * wocky_node_set_attribute_n(). */ void wocky_node_set_attribute_n_ns (WockyNode *node, const gchar *key, const gchar *value, gsize value_size, const gchar *ns) { Attribute *a = g_slice_new0 (Attribute); GSList *link; Tuple search; a->key = strndup_validated (key, -1); a->value = strndup_validated (value, value_size); a->prefix = g_strdup (wocky_node_attribute_ns_get_prefix_from_urn (ns)); a->ns = (ns != NULL) ? g_quark_from_string (ns) : 0; /* Remove the old attribute if needed */ search.key = a->key; search.ns = a->ns; link = g_slist_find_custom (node->attributes, &search, attribute_compare); if (link != NULL) { Attribute *old = (Attribute *) link->data; attribute_free (old); node->attributes = g_slist_delete_link (node->attributes, link); } node->attributes = g_slist_append (node->attributes, a); } /** * wocky_node_set_attribute_n: * @node: a #WockyNode * @key: the attribute to set * @value: the value to set * @value_size: the number of bytes of @value to set as a value * * Sets a new attribute to a #WockyNode, with the supplied values. */ void wocky_node_set_attribute_n (WockyNode *node, const gchar *key, const gchar *value, gsize value_size) { wocky_node_set_attribute_n_ns (node, key, value, value_size, NULL); } static gint node_compare_child (gconstpointer a, gconstpointer b) { const WockyNode *node = (const WockyNode *)a; Tuple *target = (Tuple *) b; if (target->ns != 0 && target->ns != node->ns) return 1; if (target->key == NULL) return 0; return strcmp (node->name, target->key); } /** * wocky_node_get_child_ns: * @node: a #WockyNode * @name: the name of the child to get * @ns: the namespace of the child to get, or %NULL * * Gets the child of a node, searching by name and limiting the search * to the specified namespace. * If the namespace is %NULL, this is equivalent to wocky_node_get_child() * * Returns: a #WockyNode. */ WockyNode * wocky_node_get_child_ns (WockyNode *node, const gchar *name, const gchar *ns) { GSList *link; Tuple t; /* This secretly works just fine if @name is %NULL, but don't tell anyone! * wocky_node_get_first_child_ns() is what people should be using. * */ t.key = name; t.ns = (ns != NULL ? g_quark_from_string (ns) : 0); link = g_slist_find_custom (node->children, &t, node_compare_child); return (link == NULL) ? NULL : (WockyNode *) (link->data); } /** * wocky_node_get_child: * @node: a #WockyNode * @name: the name of the child to get * * Gets a child of a node, searching by name. * * Returns: a #WockyNode. */ WockyNode * wocky_node_get_child (WockyNode *node, const gchar *name) { return wocky_node_get_child_ns (node, name, NULL); } /** * wocky_node_get_first_child: * @node: a #WockyNode * * Convenience function to return the first child of a #WockyNode. * * Returns: a #WockyNode, or %NULL if @node has no children. */ WockyNode * wocky_node_get_first_child (WockyNode *node) { g_return_val_if_fail (node != NULL, NULL); if (node->children == NULL) return NULL; return (WockyNode *) node->children->data; } /** * wocky_node_get_first_child_ns: * @node: a #WockyNode * @ns: the namespace of the child node you seek. * * Returns the first child of @node whose namespace is @ns, saving you the * bother of faffing around with a #WockyNodeIter. * * Returns: the first child of @node whose namespace is @ns, or %NULL if none * is found. */ WockyNode * wocky_node_get_first_child_ns (WockyNode *node, const gchar *ns) { g_return_val_if_fail (node != NULL, NULL); g_return_val_if_fail (ns != NULL, NULL); return wocky_node_get_child_ns (node, NULL, ns); } /** * wocky_node_get_content_from_child: * @node: a #WockyNode * @name: the name of the child whose content to retrieve * * Retrieves the content from a child of a node, if it exists. * * Returns: the content of the child of @node named @name, or %NULL if @node * has no such child. */ const gchar * wocky_node_get_content_from_child (WockyNode *node, const gchar *name) { return wocky_node_get_content_from_child_ns (node, name, NULL); } /** * wocky_node_get_content_from_child_ns: * @node: a #WockyNode * @name: the name of the child whose content to retrieve * @ns: the namespace of the child whose content to retrieve * * Retrieves the content from a child of a node, if it exists. * * Returns: the content of the child of @node named @name in @ns, or %NULL if * @node has no such child. */ const gchar *wocky_node_get_content_from_child_ns (WockyNode *node, const gchar *name, const gchar *ns) { WockyNode *child = wocky_node_get_child_ns (node, name, ns); if (child == NULL) return NULL; else return child->content; } /** * wocky_node_add_child: * @node: a #WockyNode * @name: the name of the child to add * * Adds a #WockyNode with the specified name to an already existing node. * * Returns: the newly added #WockyNode. */ WockyNode * wocky_node_add_child (WockyNode *node, const gchar *name) { return wocky_node_add_child_with_content_ns_q (node, name, NULL, 0); } /** * wocky_node_add_child_ns: * @node: a #WockyNode * @name: the name of the child to add * @ns: a namespace * * Adds a #WockyNode with the specified name to an already existing node, * under the specified namespace. If the namespace is %NULL, this is equivalent * to wocky_node_add_child(). * * Returns: the newly added #WockyNode. */ WockyNode * wocky_node_add_child_ns (WockyNode *node, const gchar *name, const gchar *ns) { return wocky_node_add_child_with_content_ns (node, name, NULL, ns); } /** * wocky_node_add_child_ns_q: * @node: a #WockyNode * @name: the name of the child to add * @ns: a namespace * * Adds a #WockyNode with the specified name to an already existing node, * under the specified namespace. If the namespace is 0, this is equivalent * to wocky_node_add_child(). * * Returns: the newly added #WockyNode. */ WockyNode * wocky_node_add_child_ns_q (WockyNode *node, const gchar *name, GQuark ns) { return wocky_node_add_child_with_content_ns_q (node, name, NULL, ns); } /** * wocky_node_add_child_with_content: * @node: a #WockyNode * @name: the name of the child to add * @content: the content of the child to add * * Adds a #WockyNode with the specified name and containing the * specified content to an already existing node. * * Returns: the newly added #WockyNode. */ WockyNode * wocky_node_add_child_with_content (WockyNode *node, const gchar *name, const char *content) { return wocky_node_add_child_with_content_ns_q (node, name, content, 0); } /** * wocky_node_add_child_with_content_ns: * @node: a #WockyNode * @name: the name of the child to add * @content: the content of the child to add * @ns: a namespace * * Adds a #WockyNode with the specified name and the specified content * to an already existing node, under the specified namespace. * If the namespace is %NULL, this is equivalent to * wocky_node_add_child_with_content(). * * Returns: the newly added #WockyNode. */ WockyNode * wocky_node_add_child_with_content_ns (WockyNode *node, const gchar *name, const gchar *content, const gchar *ns) { return wocky_node_add_child_with_content_ns_q (node, name, content, ns != NULL ? g_quark_from_string (ns) : 0); } /** * wocky_node_add_child_with_content_ns_q: * @node: a #WockyNode * @name: the name of the child to add * @content: the content of the child to add * @ns: a namespace * * Adds a #WockyNode with the specified name and the specified content * to an already existing node, under the specified namespace. * If the namespace is 0, this is equivalent to * wocky_node_add_child_with_content(). * * Returns: the newly added #WockyNode. */ WockyNode * wocky_node_add_child_with_content_ns_q (WockyNode *node, const gchar *name, const gchar *content, GQuark ns) { WockyNode *result = new_node (name, ns != 0 ? ns : node->ns); wocky_node_set_content (result, content); node->children = g_slist_append (node->children, result); return result; } /** * wocky_node_get_ns: * @node: a #WockyNode * * Gets the namespace of a #WockyNode * * Returns: a string containing the namespace of the node. */ const gchar * wocky_node_get_ns (WockyNode *node) { return g_quark_to_string (node->ns); } gboolean wocky_node_has_ns (WockyNode *node, const gchar *ns) { return wocky_node_has_ns_q (node, g_quark_try_string (ns)); } gboolean wocky_node_has_ns_q (WockyNode *node, GQuark ns) { return node->ns == ns; } /** * wocky_node_matches_q: * @node: a #WockyNode * @name: the expected element name, which may not be %NULL * @ns: the expected element namespace, which may not be 0 * * Checks whether a node has a particular name and namespace. * * Returns: %TRUE if @node is named @name, in namespace @ns. */ gboolean wocky_node_matches_q ( WockyNode *node, const gchar *name, GQuark ns) { g_return_val_if_fail (node != NULL, FALSE); g_return_val_if_fail (name != NULL, FALSE); g_return_val_if_fail (ns != 0, FALSE); if (wocky_strdiff (node->name, name)) return FALSE; return wocky_node_has_ns_q (node, ns); } /** * wocky_node_matches: * @node: a #WockyNode * @name: the expected element name, which may not be %NULL * @ns: the expected element namespace, which may not be %NULL * * Checks whether a node has a particular name and namespace. * * Returns: %TRUE if @node is named @name, in namespace @ns. */ gboolean wocky_node_matches ( WockyNode *node, const gchar *name, const gchar *ns) { g_return_val_if_fail (node != NULL, FALSE); g_return_val_if_fail (name != NULL, FALSE); g_return_val_if_fail (ns != NULL, FALSE); return wocky_node_matches_q (node, name, g_quark_try_string (ns)); } /** * wocky_node_get_language: * @node: a #WockyNode * * Gets the language of a #WockyNode * * Returns: a string containing the language of the node. */ const gchar * wocky_node_get_language (WockyNode *node) { g_return_val_if_fail (node != NULL, NULL); return node->language; } /** * wocky_node_set_language_n: * @node: a #WockyNode * @lang: a language * @lang_size: the length of @lang, in bytes. * * Sets the language of a #WockyNode. */ void wocky_node_set_language_n (WockyNode *node, const gchar *lang, gsize lang_size) { g_free (node->language); node->language = strndup_validated (lang, lang_size); } /** * wocky_node_set_language: * @node: a #WockyNode * @lang: a %NULL-terminated string containing the language * * Sets the language of a #WockyNode. */ void wocky_node_set_language (WockyNode *node, const gchar *lang) { gsize lang_size = 0; if (lang != NULL) { lang_size = strlen (lang); } wocky_node_set_language_n (node, lang, lang_size); } /** * wocky_node_set_content: * @node: a #WockyNode * @content: the content to set to the node * * Sets the content of a #WockyNode. */ void wocky_node_set_content (WockyNode *node, const gchar *content) { g_free (node->content); node->content = strndup_validated (content, -1); } /** * wocky_node_append_content: * @node: a #WockyNode * @content: the content to append to the node * * Appends some content to the content of a #WockyNode. */ void wocky_node_append_content (WockyNode *node, const gchar *content) { gchar *t = node->content; node->content = concat_validated (t, content, -1); g_free (t); } /** * wocky_node_append_content_n: * @node: a #WockyNode * @content: the content to append to the node * @size: the size of the content to append * * Appends a specified number of content bytes to the content of a * #WockyNode. */ void wocky_node_append_content_n (WockyNode *node, const gchar *content, gsize size) { gchar *t = node->content; node->content = concat_validated (t, content, size); g_free (t); } static gboolean attribute_to_string (const gchar *key, const gchar *value, const gchar *prefix, const gchar *ns, gpointer user_data) { GString *str = user_data; g_string_append_c (str, ' '); if (ns != NULL) g_string_append_printf (str, "xmlns:%s='%s' ", prefix, ns); if (prefix != NULL) { g_string_append (str, prefix); g_string_append_c (str, ':'); } g_string_append_printf (str, "%s='%s'", key, value); return TRUE; } static gboolean node_to_string (WockyNode *node, GQuark parent_ns, const gchar *prefix, GString *str) { GSList *l; gchar *nprefix; g_string_append_printf (str, "%s* %s", prefix, node->name); if (parent_ns != node->ns) { const gchar *ns = wocky_node_get_ns (node); g_string_append_printf (str, " xmlns='%s'", ns); } wocky_node_each_attribute (node, attribute_to_string, str); g_string_append_c (str, '\n'); nprefix = g_strdup_printf ("%s ", prefix); if (node->content != NULL && *node->content != '\0') g_string_append_printf (str, "%s\"%s\"\n", nprefix, node->content); for (l = node->children ; l != NULL; l = g_slist_next (l)) node_to_string (l->data, node->ns, nprefix, str); g_free (nprefix); return TRUE; } /** * wocky_node_to_string: * @node: a #WockyNode * * Obtains a string representation of a #WockyNode. * * Returns: a newly allocated string containing a serialization of * the node. */ gchar * wocky_node_to_string (WockyNode *node) { GString *str; gchar *result; str = g_string_new (""); node_to_string (node, 0, "", str); g_string_truncate (str, str->len - 1); result = str->str; g_string_free (str, FALSE); return result; } /** * wocky_node_equal: * @node0: a #WockyNode * @node1: a #WockyNode to compare to @node0 * * Compares two #WockyNodes for equality. * * Returns: %TRUE if the two nodes are equal. */ gboolean wocky_node_equal (WockyNode *node0, WockyNode *node1) { GSList *l0, *l1; if (wocky_strdiff (node0->name, node1->name)) return FALSE; if (wocky_strdiff (node0->content, node1->content)) return FALSE; if (wocky_strdiff (node0->language, node1->language)) return FALSE; if (node0->ns != node1->ns) return FALSE; if (g_slist_length (node0->attributes) != g_slist_length (node1->attributes)) return FALSE; /* Compare attributes */ for (l0 = node0->attributes ; l0 != NULL; l0 = g_slist_next (l0)) { Attribute *a = (Attribute *) l0->data; const gchar *c; c = wocky_node_get_attribute_ns (node1, a->key, a->ns == 0 ? NULL : g_quark_to_string (a->ns)); if (wocky_strdiff (a->value, c)) return FALSE; } /* Recursively compare children, order matters */ for (l0 = node0->children, l1 = node1->children ; l0 != NULL && l1 != NULL; l0 = g_slist_next (l0), l1 = g_slist_next (l1)) { WockyNode *c0 = (WockyNode *) l0->data; WockyNode *c1 = (WockyNode *) l1->data; if (!wocky_node_equal (c0, c1)) return FALSE; } if (l0 != NULL || l1 != NULL) return FALSE; return TRUE; } /** * wocky_node_is_superset: * @node: the #WockyNode to test * @subset: the supposed subset * * Returns: %TRUE if @node is a superset of @subset. */ gboolean wocky_node_is_superset (WockyNode *node, WockyNode *subset) { GSList *l; if (subset == NULL) /* We are always a superset of nothing */ return TRUE; if (node == NULL) /* subset is not NULL so we are not a superset */ return FALSE; if (wocky_strdiff (node->name, subset->name)) /* Node name doesn't match */ return FALSE; if (subset->ns != 0 && node->ns != subset->ns) /* Namespace doesn't match */ return FALSE; if (subset->content != NULL && wocky_strdiff (node->content, subset->content)) /* Content doesn't match */ return FALSE; /* Check attributes */ for (l = subset->attributes; l != NULL; l = g_slist_next (l)) { Attribute *a = (Attribute *) l->data; const gchar *c; c = wocky_node_get_attribute_ns (node, a->key, a->ns == 0 ? NULL : g_quark_to_string (a->ns)); if (wocky_strdiff (a->value, c)) return FALSE; } /* Recursively check children; order doesn't matter */ for (l = subset->children; l != NULL; l = g_slist_next (l)) { WockyNode *pattern_child = (WockyNode *) l->data; WockyNode *node_child; node_child = wocky_node_get_child_ns (node, pattern_child->name, wocky_node_get_ns (pattern_child)); if (!wocky_node_is_superset (node_child, pattern_child)) return FALSE; } return TRUE; } /** * wocky_node_iter_init: * @iter: unitialized iterator * @node: Node whose children to iterate over * @name: Name to filter on or %NULL * @ns: namespace to filter on or %NULL * * Initializes an iterator that can be used to iterate over the children of * @node, filtered by @name and @ns * |[ * WockyNodeIter iter; * WockyNode *child; * * wocky_node_iter_init (&iter, wocky_stanza_get_top_node (stanza), * "payload-type", * WOCKY_XMPP_NS_JINGLE_RTP); * while (wocky_node_iter_next (iter, &child)) * { * /* do something with the child */ * } * ]| */ void wocky_node_iter_init (WockyNodeIter *iter, WockyNode *node, const gchar *name, const gchar *ns) { g_return_if_fail (iter != NULL); g_return_if_fail (node != NULL); iter->node = node; iter->pending = node->children; iter->current = NULL; iter->name = name; iter->ns = g_quark_from_string (ns); } /** * wocky_node_iter_next: * @iter: an initialized WockyNodeIter * @next: a location to store the next child * * Advances iter to the next child that matches its filter. if %FALSE is * returned next is not set and the iterator becomes invalid * * Returns: %FALSE if the last child has been reached * */ gboolean wocky_node_iter_next (WockyNodeIter *iter, WockyNode **next) { while (iter->pending != NULL) { WockyNode *ln = (WockyNode *) iter->pending->data; iter->current = iter->pending; iter->pending = g_slist_next (iter->pending); if (iter->name != NULL && wocky_strdiff (ln->name, iter->name)) continue; if (iter->ns != 0 && iter->ns != ln->ns) continue; if (next != NULL) *next = ln; return TRUE; } iter->current = NULL; return FALSE; } /** * wocky_node_iter_remove: * @iter: an initialized #WockyNodeIter * * Removes and frees the node returned by the last call to * wocky_node_iter_next() from its parent. Can only be called after * wocky_node_iter_next() returned %TRUE, and cannot be called more than once * per successful call to wocky_node_iter_next(). */ void wocky_node_iter_remove (WockyNodeIter *iter) { g_return_if_fail (iter->node != NULL); g_return_if_fail (iter->current != NULL); g_assert (iter->current->data != NULL); wocky_node_free (iter->current->data); iter->node->children = g_slist_delete_link (iter->node->children, iter->current); iter->current = NULL; } /** * wocky_node_add_build: * @node: The node under which to add a new subtree * @...: the description of the stanza to build, * terminated with %NULL * * Add a node subtree to an existing parent node. * * wocky_node_add_build (node, * '(', "body", * '$', "Telepathy rocks!", * ')', * NULL); * * * The above examples adds the following subtree under the given node: * * <body> * Telepathy rocks! * </body> * * */ void wocky_node_add_build (WockyNode *node, ...) { va_list ap; va_start (ap, node); wocky_node_add_build_va (node, ap); va_end (ap); } void wocky_node_add_build_va (WockyNode *node, va_list ap) { GSList *stack = NULL; WockyNodeBuildTag arg; stack = g_slist_prepend (stack, node); while ((arg = va_arg (ap, WockyNodeBuildTag)) != 0) { switch (arg) { case WOCKY_NODE_ATTRIBUTE: { gchar *key = va_arg (ap, gchar *); gchar *value = va_arg (ap, gchar *); g_assert (key != NULL); g_assert (value != NULL); g_assert (stack != NULL); wocky_node_set_attribute (stack->data, key, value); } break; case WOCKY_NODE_START: { gchar *name = va_arg (ap, gchar *); WockyNode *child; g_assert (name != NULL); g_assert (stack != NULL); child = wocky_node_add_child (stack->data, name); stack = g_slist_prepend (stack, child); } break; case WOCKY_NODE_TEXT: { gchar *txt = va_arg (ap, gchar *); g_assert (stack != NULL); wocky_node_set_content (stack->data, txt); } break; case WOCKY_NODE_XMLNS: { gchar *ns = va_arg (ap, gchar *); g_assert (ns != NULL); g_assert (stack != NULL); ((WockyNode *) stack->data)->ns = g_quark_from_string (ns); } break; case WOCKY_NODE_LANGUAGE: { gchar *lang = va_arg (ap, gchar *); g_assert (lang != NULL); wocky_node_set_language ((WockyNode *) stack->data, lang); } break; case WOCKY_NODE_END: { /* delete the top of the stack */ stack = g_slist_delete_link (stack, stack); /* If you put too many ')'s at the end of your build spec, we just * warn; if you actually try to do anything else having fallen off * the end, we'll assert in the relevant branch. */ g_warn_if_fail (stack != NULL); } break; case WOCKY_NODE_ASSIGN_TO: { WockyNode **dest = va_arg (ap, WockyNode **); g_assert (dest != NULL); g_assert (stack != NULL); *dest = stack->data; } break; default: g_critical ("unknown build tag %c", arg); g_assert_not_reached (); } } if (G_UNLIKELY (stack != NULL && stack->data != node)) { GString *still_open = g_string_new (""); while (stack != NULL && stack->data != node) { WockyNode *unclosed = stack->data; g_string_append_printf (still_open, " ", unclosed->name); stack = stack->next; } g_warning ("improperly nested build spec! unclosed: %s", still_open->str); g_string_free (still_open, TRUE); } g_slist_free (stack); } WockyNode * _wocky_node_copy (WockyNode *node) { WockyNode *result = new_node (node->name, node->ns); GSList *l; result->content = g_strdup (node->content); result->language = g_strdup (node->language); for (l = node->attributes ; l != NULL; l = g_slist_next (l)) { Attribute *a = l->data; Attribute *b = g_slice_new0 (Attribute); b->key = g_strdup (a->key); b->value = g_strdup (a->value); b->prefix = g_strdup (a->prefix); b->ns = a->ns; result->attributes = g_slist_append (result->attributes, b); } for (l = node->children ; l != NULL; l = g_slist_next (l)) result->children = g_slist_append (result->children, _wocky_node_copy ((WockyNode *) l->data)); return result; } /** * wocky_node_add_node_tree: * @node: A node * @tree: The node tree to add * * Copies the nodes from @tree, and appends them to @node's children. * * Returns: the root of the copy of @tree added to @node. */ WockyNode * wocky_node_add_node_tree (WockyNode *node, WockyNodeTree *tree) { WockyNode *copy; g_return_val_if_fail (node != NULL, NULL); g_return_val_if_fail (tree != NULL, NULL); copy = _wocky_node_copy (wocky_node_tree_get_top_node (tree)); node->children = g_slist_append (node->children, copy); return copy; } /** * wocky_node_prepend_node_tree: * @node: a node * @tree: the node tree to prepend to @node's children * * Copies the nodes from @tree, and inserts them as the first child of @node, * before any existing children. * * Returns: the root of the copy of @tree added to @node. */ WockyNode * wocky_node_prepend_node_tree ( WockyNode *node, WockyNodeTree *tree) { WockyNode *copy; g_return_val_if_fail (node != NULL, NULL); g_return_val_if_fail (tree != NULL, NULL); copy = _wocky_node_copy (wocky_node_tree_get_top_node (tree)); node->children = g_slist_prepend (node->children, copy); return copy; } /** * wocky_node_init: * * Initializes the caches used by #WockyNode. * This should be always called before using #WockyNode structs. */ void wocky_node_init () { _init_user_prefix_table (); _init_default_prefix_table (); } /** * wocky_node_deinit: * * Releases all the resources used by the #WockyNode caches. */ void wocky_node_deinit () { g_hash_table_unref (user_ns_prefixes); g_hash_table_unref (default_ns_prefixes); } telepathy-gabble-0.18.2/lib/ext/wocky/wocky/wocky-muc.c0000644000175000017500000013502612200204546022737 0ustar00smcvsmcv00000000000000/* * wocky-muc.c - Source for WockyMuc * Copyright © 2009 Collabora Ltd. * @author Vivek Dasmohapatra * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 */ /** * SECTION: wocky-muc * @title: WockyMuc * @short_description: multi-user chat rooms * @include: wocky/wocky.h * * Represents a multi-user chat room. Because the MUC protocol is so terrible, * you will find yourself consulting XEP-0045 and shedding * more than a few tears while using this class. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #define WOCKY_DEBUG_FLAG WOCKY_DEBUG_MUC_CONNECTION #include "wocky-debug-internal.h" #include "wocky-muc.h" #include "wocky-namespaces.h" #include "wocky-utils.h" #include "wocky-signals-marshal.h" #include "wocky-xmpp-error.h" typedef enum { SIG_NICK_CHANGE, SIG_PERM_CHANGE, SIG_PRESENCE, SIG_OWN_PRESENCE, SIG_PRESENCE_ERROR, SIG_JOINED, SIG_PARTED, SIG_LEFT, SIG_MSG, SIG_MSG_ERR, SIG_FILL_PRESENCE, SIG_NULL } WockyMucSig; static guint signals[SIG_NULL] = { 0 }; typedef struct { const gchar *ns; WockyMucFeature flag; } feature; static const feature feature_map[] = { { WOCKY_NS_MUC, WOCKY_MUC_MODERN }, { WOCKY_NS_MUC "#register", WOCKY_MUC_FORM_REGISTER }, { WOCKY_NS_MUC "#roomconfig", WOCKY_MUC_FORM_ROOMCONFIG }, { WOCKY_NS_MUC "#roominfo", WOCKY_MUC_FORM_ROOMINFO }, { "muc_hidden", WOCKY_MUC_HIDDEN }, { "muc_membersonly", WOCKY_MUC_MEMBERSONLY }, { "muc_moderated", WOCKY_MUC_MODERATED }, { "muc_nonanonymous", WOCKY_MUC_NONANONYMOUS }, { "muc_open", WOCKY_MUC_OPEN }, { "muc_passwordprotected", WOCKY_MUC_PASSWORDPROTECTED }, { "muc_persistent", WOCKY_MUC_PERSISTENT }, { "muc_public", WOCKY_MUC_PUBLIC }, { "muc_rooms", WOCKY_MUC_ROOMS }, { "muc_semianonymous", WOCKY_MUC_SEMIANONYMOUS }, { "muc_temporary", WOCKY_MUC_TEMPORARY }, { "muc_unmoderated", WOCKY_MUC_UNMODERATED }, { "muc_unsecured", WOCKY_MUC_UNSECURED }, { "gc-1.0", WOCKY_MUC_OBSOLETE }, { NULL, 0 } }; static void wocky_muc_class_init (WockyMucClass *klass); /* create MUC object */ G_DEFINE_TYPE (WockyMuc, wocky_muc, G_TYPE_OBJECT); /* private methods */ static void wocky_muc_dispose (GObject *object); static void wocky_muc_finalize (GObject *object); static void wocky_muc_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec); static void wocky_muc_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec); /* private functions */ static gboolean handle_presence (WockyPorter *porter, WockyStanza *stanza, gpointer data); static gboolean handle_message (WockyPorter *porter, WockyStanza *stanza, gpointer data); enum { PROP_JID = 1, PROP_USER, PROP_PORTER, PROP_SERVICE, PROP_ROOM, PROP_DESC, PROP_NICK, PROP_RNICK, PROP_PASS, PROP_STATUS, PROP_ROOM_TYPE, PROP_ID_CATEGORY, PROP_ID_TYPE, PROP_ID_NAME, PROP_ROLE, PROP_AFFILIATION, }; struct _WockyMucPrivate { /* properties */ WockyPorter *porter; gchar *user; /* full JID of user */ gchar *jid; /* room@service/nick */ gchar *service; /* service */ gchar *room; /* room */ gchar *rjid; /* room@service */ gchar *nick; /* nick */ gchar *rnick; /* reserved nick, if any */ gchar *id_category; /* eg "conference" */ gchar *id_type; /* eg "text" */ gchar *id_name; gchar *desc; /* long room description */ gchar *pass; /* password or NULL */ gchar *status; /* status message */ guint room_type; /* ORed WockyMucFeature flags */ /* not props */ gboolean dispose_has_run; GHashTable *members; WockyMucState state; WockyMucRole role; WockyMucAffiliation affiliation; guint pres_handler; guint mesg_handler; GSimpleAsyncResult *join_cb; }; static void free_member (gpointer data) { WockyMucMember *member = data; if (member->presence_stanza != NULL) g_object_unref (member->presence_stanza); g_free (member->from); g_free (member->jid); g_free (member->nick); g_free (member->status); g_slice_free (WockyMucMember, member); } static gpointer alloc_member (void) { return g_slice_new0 (WockyMucMember); } static void wocky_muc_init (WockyMuc *muc) { muc->priv = G_TYPE_INSTANCE_GET_PRIVATE (muc, WOCKY_TYPE_MUC, WockyMucPrivate); muc->priv->members = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, free_member); } static void wocky_muc_dispose (GObject *object) { WockyMuc *muc = WOCKY_MUC (object); WockyMucPrivate *priv = muc->priv; if (priv->dispose_has_run) return; priv->dispose_has_run = TRUE; if (priv->pres_handler != 0) wocky_porter_unregister_handler (priv->porter, priv->pres_handler); priv->pres_handler = 0; if (priv->mesg_handler != 0) wocky_porter_unregister_handler (priv->porter, priv->mesg_handler); priv->mesg_handler = 0; if (priv->porter != NULL) g_object_unref (priv->porter); priv->porter = NULL; if (priv->members != NULL) g_hash_table_unref (priv->members); priv->members = NULL; if (G_OBJECT_CLASS (wocky_muc_parent_class )->dispose) G_OBJECT_CLASS (wocky_muc_parent_class)->dispose (object); } #define GFREE_AND_FORGET(x) g_free (x); x = NULL; static void wocky_muc_finalize (GObject *object) { WockyMuc *muc = WOCKY_MUC (object); WockyMucPrivate *priv = muc->priv; GFREE_AND_FORGET (priv->user); GFREE_AND_FORGET (priv->jid); GFREE_AND_FORGET (priv->service); GFREE_AND_FORGET (priv->room); GFREE_AND_FORGET (priv->rjid); GFREE_AND_FORGET (priv->nick); GFREE_AND_FORGET (priv->rnick); GFREE_AND_FORGET (priv->id_category); GFREE_AND_FORGET (priv->id_type); GFREE_AND_FORGET (priv->id_name); G_OBJECT_CLASS (wocky_muc_parent_class)->finalize (object); } static void wocky_muc_class_init (WockyMucClass *klass) { GObjectClass *oclass = G_OBJECT_CLASS (klass); GType ctype = G_OBJECT_CLASS_TYPE (klass); GParamSpec *spec; g_type_class_add_private (klass, sizeof (WockyMucPrivate)); oclass->get_property = wocky_muc_get_property; oclass->set_property = wocky_muc_set_property; oclass->dispose = wocky_muc_dispose; oclass->finalize = wocky_muc_finalize; spec = g_param_spec_string ("jid", "jid", "Full room@service/nick JID of the MUC room", NULL, G_PARAM_READWRITE|G_PARAM_STATIC_STRINGS); g_object_class_install_property (oclass, PROP_JID, spec); spec = g_param_spec_string ("user", "user", "Full JID of the user (node@domain/resource) who is connecting", NULL, (G_PARAM_CONSTRUCT_ONLY|G_PARAM_READWRITE|G_PARAM_STATIC_STRINGS)); g_object_class_install_property (oclass, PROP_USER, spec); spec = g_param_spec_object ("porter", "porter", "The WockyPorter instance doing all the actual XMPP interaction", WOCKY_TYPE_PORTER, (G_PARAM_CONSTRUCT_ONLY|G_PARAM_READWRITE|G_PARAM_STATIC_STRINGS)); g_object_class_install_property (oclass, PROP_PORTER, spec); spec = g_param_spec_string ("service", "service", "The service (domain) part of the MUC JID", NULL, (G_PARAM_READABLE|G_PARAM_STATIC_STRINGS)); g_object_class_install_property (oclass, PROP_SERVICE, spec); spec = g_param_spec_string ("room", "room", "The node part of the MUC room JID", NULL, (G_PARAM_READABLE|G_PARAM_STATIC_STRINGS)); g_object_class_install_property (oclass, PROP_ROOM, spec); spec = g_param_spec_string ("description", "desc", "The long description oof the room", NULL, (G_PARAM_READABLE|G_PARAM_STATIC_STRINGS)); g_object_class_install_property (oclass, PROP_DESC, spec); spec = g_param_spec_string ("nickname", "nick", "The user's in-room nickname", NULL, (G_PARAM_READABLE|G_PARAM_STATIC_STRINGS)); g_object_class_install_property (oclass, PROP_NICK, spec); spec = g_param_spec_string ("reserved-nick", "reserved-nick", "The user's reserved in-room nickname, if any", NULL, (G_PARAM_READABLE|G_PARAM_STATIC_STRINGS)); g_object_class_install_property (oclass, PROP_RNICK, spec); spec = g_param_spec_string ("password", "password", "User's MUC room password", NULL, (G_PARAM_READWRITE|G_PARAM_STATIC_STRINGS)); g_object_class_install_property (oclass, PROP_PASS, spec); spec = g_param_spec_string ("status-message", "status", "User's MUC status message", NULL, (G_PARAM_READABLE|G_PARAM_STATIC_STRINGS)); g_object_class_install_property (oclass, PROP_STATUS, spec); spec = g_param_spec_ulong ("muc-flags", "muc-flags", "ORed set of WockyMucFeature MUC property flags", 0, G_MAXULONG, 0, (G_PARAM_READABLE|G_PARAM_STATIC_STRINGS)); g_object_class_install_property (oclass, PROP_ROOM_TYPE, spec); spec = g_param_spec_string ("category", "category", "Category of the MUC, usually \"conference\"", NULL, (G_PARAM_READABLE|G_PARAM_STATIC_STRINGS)); g_object_class_install_property (oclass, PROP_ID_CATEGORY, spec); spec = g_param_spec_string ("type", "type", "Type of the MUC, eg \"text\"", NULL, (G_PARAM_READABLE|G_PARAM_STATIC_STRINGS)); g_object_class_install_property (oclass, PROP_ID_TYPE, spec); spec = g_param_spec_string ("name", "name", "The human-readable name of the room (usually a short label)", NULL, (G_PARAM_READABLE|G_PARAM_STATIC_STRINGS)); g_object_class_install_property (oclass, PROP_ID_NAME, spec); spec = g_param_spec_uint ("role", "role", "The role (WockyMucRole) of the user in the MUC room", WOCKY_MUC_ROLE_NONE, WOCKY_MUC_ROLE_MODERATOR, WOCKY_MUC_ROLE_NONE, (G_PARAM_READABLE|G_PARAM_STATIC_STRINGS)); g_object_class_install_property (oclass, PROP_ROLE, spec); spec = g_param_spec_enum ("affiliation", "affiliation", "The affiliation of the user with the MUC room", WOCKY_TYPE_MUC_AFFILIATION, WOCKY_MUC_AFFILIATION_NONE, (G_PARAM_READABLE|G_PARAM_STATIC_STRINGS)); g_object_class_install_property (oclass, PROP_AFFILIATION, spec); signals[SIG_NICK_CHANGE] = g_signal_new ("nick-change", ctype, G_SIGNAL_RUN_LAST, 0, NULL, NULL, _wocky_signals_marshal_VOID__POINTER_UINT, G_TYPE_NONE, 2, WOCKY_TYPE_STANZA, G_TYPE_UINT); signals[SIG_PRESENCE] = g_signal_new ("presence", ctype, G_SIGNAL_RUN_LAST, 0, NULL, NULL, _wocky_signals_marshal_VOID__OBJECT_UINT_POINTER, G_TYPE_NONE, 3, WOCKY_TYPE_STANZA, G_TYPE_UINT, G_TYPE_POINTER); signals[SIG_OWN_PRESENCE] = g_signal_new ("own-presence", ctype, G_SIGNAL_RUN_LAST, 0, NULL, NULL, _wocky_signals_marshal_VOID__OBJECT_UINT, G_TYPE_NONE, 2, WOCKY_TYPE_STANZA, G_TYPE_UINT); /** * WockyMuc::joined: * @muc: the MUC * @stanza: the presence stanza * @codes: bitwise OR of %WockyMucStatusCode flags with miscellaneous * information about the MUC * * Emitted when the local user successfully joins @muc. */ signals[SIG_JOINED] = g_signal_new ("joined", ctype, G_SIGNAL_RUN_LAST, 0, NULL, NULL, _wocky_signals_marshal_VOID__POINTER_UINT, G_TYPE_NONE, 2, WOCKY_TYPE_STANZA, G_TYPE_UINT); /** * WockyMuc::error: * @muc: the MUC * @stanza: the presence stanza * @error_type: the type of error * @error: an error in domain #WOCKY_XMPP_ERROR, whose message (if not %NULL) * is a human-readable message from the server * * Emitted when a presence error is received from the MUC, which is generally * in response to trying to join the MUC. */ signals[SIG_PRESENCE_ERROR] = g_signal_new ("error", ctype, G_SIGNAL_RUN_LAST, 0, NULL, NULL, _wocky_signals_marshal_VOID__OBJECT_ENUM_BOXED, G_TYPE_NONE, 3, WOCKY_TYPE_STANZA, WOCKY_TYPE_XMPP_ERROR_TYPE, G_TYPE_ERROR); /** * WockyMuc::permissions: * @muc: the muc * @stanza: the presence stanza heralding the change * @codes: bitwise OR of %WockyMucStatusCode flags * @actor_jid: the JID of the user who changed our permissions, or %NULL * @reason: a human-readable reason for the change, or %NULL * * Emitted when our permissions within the MUC are changed. */ signals[SIG_PERM_CHANGE] = g_signal_new ("permissions", ctype, G_SIGNAL_RUN_LAST, 0, NULL, NULL, _wocky_signals_marshal_VOID__POINTER_UINT_POINTER_POINTER, G_TYPE_NONE, 4, WOCKY_TYPE_STANZA, G_TYPE_UINT, G_TYPE_STRING, G_TYPE_STRING); /** * WockyMuc::parted: * @muc: the MUC * @stanza: the presence stanza * @codes: bitwise OR of %WockyMucStatusCode flags describing why the user * left the MUC * @actor: if the user was removed from the MUC by another participant, that * participant's JID * @reason: if the user was removed from the MUC by another participant, a * human-readable reason given by that participant * @message: a parting message we provided to other participants, or %NULL * * Emitted when the local user leaves the MUC, whether by choice or by force. */ signals[SIG_PARTED] = g_signal_new ("parted", ctype, G_SIGNAL_RUN_LAST, 0, NULL, NULL, _wocky_signals_marshal_VOID__OBJECT_UINT_STRING_STRING_STRING, G_TYPE_NONE, 5, WOCKY_TYPE_STANZA, G_TYPE_UINT, G_TYPE_STRING, /* actor jid */ G_TYPE_STRING, /* reason */ G_TYPE_STRING); /* message: usually none, but allowed by spec */ /** * WockyMuc::left: * @muc: the MUC * @stanza: the presence stanza * @codes: bitwise OR of %WockyMucStatusCode flags describing why @member * left the MUC * @member: the (now ex-)member of the MUC who left * @actor: if @member was removed from the MUC by another participant, that * participant's JID * @reason: if @member was removed from the MUC by another participant, a * human-readable reason given by that participant * @message: a parting message provided by @member, or %NULL * * Emitted when another participant leaves, or is kicked from, the MUC */ signals[SIG_LEFT] = g_signal_new ("left", ctype, G_SIGNAL_RUN_LAST, 0, NULL, NULL, _wocky_signals_marshal_VOID__OBJECT_UINT_POINTER_STRING_STRING_STRING, G_TYPE_NONE, 6, WOCKY_TYPE_STANZA, G_TYPE_UINT, G_TYPE_POINTER, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING); /** * WockyMuc::message: * @muc: the MUC * @stanza: the incoming message stanza * @message_type: the message's type * @id: the stanza's identifier (which may be %NULL if neither the sender nor * the MUC specified one) * @timestamp: for messages received as scrollback when joining the MUC, the * time the message was sent; %NULL for messages received while in the MUC * @sender: a %WockyMucMember struct describing the sender of the message * @body: the body of the message, or %NULL * @subject: the new subject for the MUC, or %NULL * @state: whether @sender is currently typing. * * Emitted when a non-error message stanza is received. This may indicate: * * * if @body is not %NULL, a message sent by @sender to the * MUC; * or, if @subject is not %NULL, @sender changed the subject of the * MUC; * additionally, that @sender is typing, or maybe stopped typing, * depending on @state. * */ signals[SIG_MSG] = g_signal_new ("message", ctype, G_SIGNAL_RUN_LAST, 0, NULL, NULL, _wocky_signals_marshal_VOID__OBJECT_ENUM_STRING_LONG_POINTER_STRING_STRING_ENUM, G_TYPE_NONE, 8, WOCKY_TYPE_STANZA, WOCKY_TYPE_MUC_MSG_TYPE, G_TYPE_STRING, G_TYPE_DATE_TIME, G_TYPE_POINTER, G_TYPE_STRING, G_TYPE_STRING, WOCKY_TYPE_MUC_MSG_STATE); /** * WockyMuc::message-error: * @muc: the MUC * @stanza: the incoming %WOCKY_STANZA_SUB_TYPE_ERROR message * @message_type: the type of the message which was rejected * @id: the identifier for the original message and this error (which may be * %NULL) * @timestamp: the timestamp attached to the original message, which is * probably %NULL because timestamps are only attached to scrollback messages * @member: a %WockyMucMember struct describing the sender of the original * message (which is, we presume, us) * @body: the body of the message which failed to send * @error_type: the type of error * @error: an error in domain %WOCKY_XMPP_ERROR, whose message (if not %NULL) * is a human-readable message from the server * * Emitted when we receive an error from the MUC in response to sending a * message stanza to the MUC. */ signals[SIG_MSG_ERR] = g_signal_new ("message-error", ctype, G_SIGNAL_RUN_LAST, 0, NULL, NULL, _wocky_signals_marshal_VOID__OBJECT_ENUM_STRING_LONG_POINTER_STRING_ENUM_BOXED, G_TYPE_NONE, 8, WOCKY_TYPE_STANZA, WOCKY_TYPE_MUC_MSG_TYPE, G_TYPE_STRING, G_TYPE_DATE_TIME, G_TYPE_POINTER, G_TYPE_STRING, WOCKY_TYPE_XMPP_ERROR_TYPE, G_TYPE_ERROR); signals[SIG_FILL_PRESENCE] = g_signal_new ("fill-presence", ctype, G_SIGNAL_RUN_LAST, 0, NULL, NULL, _wocky_signals_marshal_VOID__OBJECT, G_TYPE_NONE, 1, WOCKY_TYPE_STANZA); } static void wocky_muc_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { WockyMuc *muc = WOCKY_MUC (object); WockyMucPrivate *priv = muc->priv; switch (property_id) { case PROP_PORTER: priv->porter = g_value_dup_object (value); break; case PROP_JID: g_free (priv->jid); g_free (priv->service); g_free (priv->room); g_free (priv->nick); g_free (priv->rjid); priv->jid = g_value_dup_string (value); wocky_decode_jid (priv->jid, &(priv->room), &(priv->service), &(priv->nick)); priv->rjid = g_strdup_printf ("%s@%s", priv->room, priv->service); break; case PROP_NICK: g_free (priv->nick); priv->nick = g_value_dup_string (value); if (priv->jid != NULL && priv->nick != NULL) { g_free (priv->jid); priv->jid = g_strdup_printf ("%s@%s/%s", priv->room, priv->service, priv->nick); } break; case PROP_RNICK: g_free (priv->rnick); priv->rnick = g_value_dup_string (value); break; case PROP_PASS: g_free (priv->pass); priv->pass = g_value_dup_string (value); break; case PROP_USER: g_free (priv->user); priv->user = g_value_dup_string (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void wocky_muc_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { WockyMuc *muc = WOCKY_MUC (object); WockyMucPrivate *priv = muc->priv; switch (property_id) { case PROP_PORTER: g_value_set_object (value, priv->porter); break; case PROP_JID: g_value_set_string (value, priv->jid); break; case PROP_SERVICE: g_value_set_string (value, priv->service); break; case PROP_ROOM: g_value_set_string (value, priv->room); break; case PROP_DESC: g_value_set_string (value, priv->desc); break; case PROP_NICK: g_value_set_string (value, priv->nick); break; case PROP_PASS: g_value_set_string (value, priv->pass); break; case PROP_STATUS: g_value_set_string (value, priv->status); break; case PROP_RNICK: g_value_set_string (value, priv->rnick); break; case PROP_USER: g_value_set_string (value, priv->user); break; case PROP_ROOM_TYPE: g_value_set_uint (value, priv->room_type); break; case PROP_ID_CATEGORY: g_value_set_string (value, priv->id_category); break; case PROP_ID_TYPE: g_value_set_string (value, priv->id_type); break; case PROP_ID_NAME: g_value_set_string (value, priv->id_name); break; case PROP_ROLE: g_value_set_uint (value, priv->role); break; case PROP_AFFILIATION: g_value_set_enum (value, priv->affiliation); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static guint status_code_to_muc_flag (guint64 code) { switch (code) { case 100: return WOCKY_MUC_CODE_ONYMOUS; case 101: return WOCKY_MUC_CODE_AF_CHANGE_OOB; case 102: return WOCKY_MUC_CODE_CFG_SHOW_UNAVAILABLE; case 103: return WOCKY_MUC_CODE_CFG_HIDE_UNAVAILABLE; case 104: return WOCKY_MUC_CODE_CFG_NONPRIVACY; case 110: return WOCKY_MUC_CODE_OWN_PRESENCE; case 170: return WOCKY_MUC_CODE_CFG_LOGGING_ENABLED; case 171: return WOCKY_MUC_CODE_CFG_LOGGING_DISABLED; case 172: return WOCKY_MUC_CODE_CFG_ONYMOUS; case 173: return WOCKY_MUC_CODE_CFG_SEMIONYMOUS; case 174: return WOCKY_MUC_CODE_CFG_ANONYMOUS; case 201: return WOCKY_MUC_CODE_NEW_ROOM; case 210: return WOCKY_MUC_CODE_NICK_CHANGE_FORCED; case 301: return WOCKY_MUC_CODE_BANNED; case 303: return WOCKY_MUC_CODE_NICK_CHANGE_USER; case 307: return WOCKY_MUC_CODE_KICKED; case 321: return WOCKY_MUC_CODE_KICKED_AFFILIATION; case 322: return WOCKY_MUC_CODE_KICKED_ROOM_PRIVATISED; case 332: return WOCKY_MUC_CODE_KICKED_SHUTDOWN; } return WOCKY_MUC_CODE_UNKNOWN; } static gboolean store_muc_disco_info_x (WockyNode *field, gpointer data) { WockyMucPrivate *priv = data; const gchar *var = NULL; if (wocky_strdiff (field->name, "field")) return TRUE; var = wocky_node_get_attribute (field, "var"); if (wocky_strdiff (var, "muc#roominfo_description")) return TRUE; priv->desc = g_strdup ( wocky_node_get_content_from_child (field, "value")); return TRUE; } static gboolean store_muc_disco_info (WockyNode *feat, gpointer data) { WockyMucPrivate *priv = data; if (!wocky_strdiff (feat->name, "feature")) { guint i; const gchar *thing = wocky_node_get_attribute (feat, "var"); if (thing == NULL) return TRUE; for (i = 0; feature_map[i].ns != NULL; i++) if (!wocky_strdiff (thing, feature_map[i].ns)) { priv->room_type |= feature_map[i].flag; break; } return TRUE; } if (!wocky_strdiff (feat->name, "x")) wocky_node_each_child (feat, store_muc_disco_info_x, priv); return TRUE; } static void muc_disco_info (GObject *source, GAsyncResult *res, gpointer data) { WockyMuc *muc; WockyMucPrivate *priv; GError *error = NULL; WockyStanza *iq; WockyStanzaType type; WockyStanzaSubType sub; GSimpleAsyncResult *result = G_SIMPLE_ASYNC_RESULT (data); muc = WOCKY_MUC (g_async_result_get_source_object (G_ASYNC_RESULT (result))); priv = muc->priv; iq = wocky_porter_send_iq_finish (priv->porter, res, &error); priv->room_type = 0; g_free (priv->id_name); g_free (priv->id_type); g_free (priv->id_category); priv->id_name = NULL; priv->id_type = NULL; priv->id_category = NULL; if (error != NULL) goto out; if (iq == NULL) goto out; wocky_stanza_get_type_info (iq, &type, &sub); if (type != WOCKY_STANZA_TYPE_IQ) { error = g_error_new (WOCKY_XMPP_ERROR, WOCKY_XMPP_ERROR_UNDEFINED_CONDITION, "Bizarre response: Not an IQ"); goto out; } switch (sub) { WockyNode *query; WockyNode *node; case WOCKY_STANZA_SUB_TYPE_RESULT: query = wocky_node_get_child_ns ( wocky_stanza_get_top_node (iq), "query", WOCKY_NS_DISCO_INFO); if (query == NULL) { error = g_error_new (WOCKY_XMPP_ERROR, WOCKY_XMPP_ERROR_UNDEFINED_CONDITION, "Malformed IQ reply"); goto out; } node = wocky_node_get_child (query, "identity"); if (node == NULL) { error = g_error_new (WOCKY_XMPP_ERROR, WOCKY_XMPP_ERROR_UNDEFINED_CONDITION, "Malformed IQ reply: No Identity"); goto out; } else { const gchar *attr; attr = wocky_node_get_attribute (node, "category"); g_free (priv->id_category); priv->id_category = g_strdup (attr); attr = wocky_node_get_attribute (node, "name"); g_free (priv->id_name); priv->id_name = g_strdup (attr); attr = wocky_node_get_attribute (node, "type"); g_free (priv->id_type); priv->id_type = g_strdup (attr); } wocky_node_each_child (query, store_muc_disco_info, priv); if (priv->state < WOCKY_MUC_INITIATED) priv->state = WOCKY_MUC_INITIATED; break; case WOCKY_STANZA_SUB_TYPE_ERROR: wocky_stanza_extract_errors (iq, NULL, &error, NULL, NULL); break; default: break; } out: if (error != NULL) { g_simple_async_result_set_from_error (result, error); g_error_free (error); } g_simple_async_result_complete (result); g_object_unref (result); g_object_unref (muc); if (iq != NULL) g_object_unref (iq); } gboolean wocky_muc_disco_info_finish (WockyMuc *muc, GAsyncResult *res, GError **error) { GSimpleAsyncResult *result = G_SIMPLE_ASYNC_RESULT (res); if (g_simple_async_result_propagate_error (result, error)) return FALSE; return TRUE; } void wocky_muc_disco_info_async (WockyMuc *muc, GAsyncReadyCallback callback, GCancellable *cancel, gpointer data) { WockyMucPrivate *priv = muc->priv; GSimpleAsyncResult *result; WockyStanza *iq = wocky_stanza_build (WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_GET, priv->user, priv->jid, '(', "query", ':', WOCKY_NS_DISCO_INFO, ')', NULL); result = g_simple_async_result_new (G_OBJECT (muc), callback, data, wocky_muc_disco_info_async); wocky_porter_send_iq_async (priv->porter, iq, cancel, muc_disco_info, result); } /* ask for MUC member list */ WockyStanza * wocky_muc_create_presence (WockyMuc *muc, WockyStanzaSubType type, const gchar *status) { WockyMucPrivate *priv = muc->priv; WockyStanza *stanza = wocky_stanza_build (WOCKY_STANZA_TYPE_PRESENCE, type, priv->user, priv->jid, NULL); WockyNode *presence = wocky_stanza_get_top_node (stanza); /* There should be separate API to leave a room, but atm there isn't... so * only allow the status to be set directly when making a presence to leave * the muc */ g_assert (status == NULL || type == WOCKY_STANZA_SUB_TYPE_UNAVAILABLE); if (status != NULL) { wocky_node_add_child_with_content (presence, "status", status); } else { g_signal_emit (muc, signals[SIG_FILL_PRESENCE], 0, stanza); } return stanza; } static void register_presence_handler (WockyMuc *muc) { WockyMucPrivate *priv = muc->priv; if (priv->pres_handler == 0) priv->pres_handler = wocky_porter_register_handler_from (priv->porter, WOCKY_STANZA_TYPE_PRESENCE, WOCKY_STANZA_SUB_TYPE_NONE, priv->rjid, WOCKY_PORTER_HANDLER_PRIORITY_NORMAL, handle_presence, muc, NULL); } static void register_message_handler (WockyMuc *muc) { WockyMucPrivate *priv = muc->priv; if (priv->mesg_handler == 0) priv->mesg_handler = wocky_porter_register_handler_from (priv->porter, WOCKY_STANZA_TYPE_MESSAGE, WOCKY_STANZA_SUB_TYPE_NONE, priv->rjid, WOCKY_PORTER_HANDLER_PRIORITY_NORMAL, handle_message, muc, NULL); } static guint extract_status_codes (WockyNode *x) { guint codes = 0; WockyNodeIter iter; WockyNode *node; wocky_node_iter_init (&iter, x, "status", NULL); while (wocky_node_iter_next (&iter, &node)) { const gchar *code; WockyMucStatusCode cnum; code = wocky_node_get_attribute (node, "code"); if (code == NULL) continue; cnum = status_code_to_muc_flag (g_ascii_strtoull (code, NULL, 10)); codes |= cnum; /* OWN_PRESENCE is a SHOULD * * CHANGE_FORCED is a MUST which * * implies OWN_PRESENCE */ /* 201 (NEW_ROOM) also implies OWN_PRESENCE */ if (cnum == WOCKY_MUC_CODE_NICK_CHANGE_FORCED) codes |= WOCKY_MUC_CODE_OWN_PRESENCE; if (cnum == WOCKY_MUC_CODE_NEW_ROOM) codes |= WOCKY_MUC_CODE_OWN_PRESENCE; } return codes; } static void presence_features ( WockyMucPrivate *priv, guint codes) { if ((codes & WOCKY_MUC_CODE_CFG_ONYMOUS) != 0) { priv->room_type |= WOCKY_MUC_NONANONYMOUS; priv->room_type &= ~WOCKY_MUC_SEMIANONYMOUS; } else if ((codes & WOCKY_MUC_CODE_CFG_SEMIONYMOUS) != 0) { priv->room_type |= WOCKY_MUC_SEMIANONYMOUS; priv->room_type &= ~WOCKY_MUC_NONANONYMOUS; } else if ((codes & WOCKY_MUC_CODE_CFG_ANONYMOUS) != 0) { priv->room_type &= ~(WOCKY_MUC_NONANONYMOUS|WOCKY_MUC_SEMIANONYMOUS); } } #define REPLACE_STR(place,val) \ if (wocky_strdiff (place, val)) \ { \ g_free (place); \ place = g_strdup (val); \ } static void handle_self_presence (WockyMuc *muc, WockyStanza *stanza, const gchar *nick, WockyMucRole role, WockyMucAffiliation aff, const gchar *actor, const gchar *why, const gchar *status, guint codes) { gboolean nick_update = FALSE; gboolean permission_update = FALSE; WockyMucPrivate *priv = muc->priv; DEBUG ("Received our own presence"); if (wocky_strdiff (priv->nick, nick)) { nick_update = TRUE; g_free (priv->nick); priv->nick = g_strdup (nick); } /* we already know if we changed our own status, so no signal for that */ REPLACE_STR (priv->status, status); permission_update = ((priv->role != role) || (priv->affiliation != aff)); priv->role = role; priv->affiliation = aff; presence_features (priv, codes); if (nick_update) { gchar *new_jid = g_strdup_printf ("%s@%s/%s", priv->room, priv->service, priv->nick); g_free (priv->jid); priv->jid = new_jid; g_signal_emit (muc, signals[SIG_NICK_CHANGE], 0, stanza, codes); } if (permission_update) g_signal_emit (muc, signals[SIG_PERM_CHANGE], 0, stanza, codes, actor, why); } static gboolean handle_user_presence (WockyMuc *muc, WockyStanza *stanza, const gchar *from, const gchar *jid, const gchar *nick, WockyMucRole role, WockyMucAffiliation aff, const gchar *actor, const gchar *why, const gchar *status, guint codes) { WockyMucPrivate *priv = muc->priv; WockyMucMember *member = NULL; if (nick == NULL) return FALSE; member = g_hash_table_lookup (priv->members, from); if (member == NULL) { DEBUG ("New presence from %s, %s (state: %d)", from, nick, priv->state); member = alloc_member (); g_hash_table_insert (priv->members, g_strdup (from), member); } else { } REPLACE_STR (member->from, from); REPLACE_STR (member->jid, jid); REPLACE_STR (member->nick, nick); REPLACE_STR (member->status, status); member->role = role; member->affiliation = aff; if (member->presence_stanza != NULL) g_object_unref (member->presence_stanza); member->presence_stanza = g_object_ref (stanza); if (priv->state >= WOCKY_MUC_JOINED) g_signal_emit (muc, signals[SIG_PRESENCE], 0, stanza, codes, member); return TRUE; } static WockyMucRole string_to_role (const gchar *role) { if (!wocky_strdiff (role, "visitor")) return WOCKY_MUC_ROLE_VISITOR; if (!wocky_strdiff (role, "participant")) return WOCKY_MUC_ROLE_PARTICIPANT; if (!wocky_strdiff (role, "moderator")) return WOCKY_MUC_ROLE_MODERATOR; return WOCKY_MUC_ROLE_NONE; } static WockyMucAffiliation string_to_aff (const gchar *aff) { if (!wocky_strdiff (aff, "outcast")) return WOCKY_MUC_AFFILIATION_OUTCAST; if (!wocky_strdiff (aff, "member")) return WOCKY_MUC_AFFILIATION_MEMBER; if (!wocky_strdiff (aff, "admin")) return WOCKY_MUC_AFFILIATION_ADMIN; if (!wocky_strdiff (aff, "owner")) return WOCKY_MUC_AFFILIATION_OWNER; return WOCKY_MUC_AFFILIATION_NONE; } static gboolean handle_presence_standard (WockyMuc *muc, WockyStanza *stanza, WockyStanzaSubType type, const gchar *resource) { WockyNode *node = wocky_stanza_get_top_node (stanza); WockyNode *x = wocky_node_get_child_ns (node, "x", WOCKY_NS_MUC_USER); WockyNode *item = NULL; const gchar *from = wocky_stanza_get_from (stanza); const gchar *pjid = NULL; const gchar *pnic = NULL; const gchar *role = NULL; const gchar *aff = NULL; guint codes = 0; const gchar *ajid = NULL; const gchar *why = NULL; WockyMucPrivate *priv = muc->priv; WockyMucRole r = WOCKY_MUC_ROLE_NONE; WockyMucAffiliation a = WOCKY_MUC_AFFILIATION_NONE; gboolean self_presence = FALSE; const gchar *msg = NULL; msg = wocky_node_get_content_from_child (node, "status"); if (x == NULL) return FALSE; item = wocky_node_get_child (x, "item"); if (item != NULL) { WockyNode *actor = NULL; WockyNode *cause = NULL; pjid = wocky_node_get_attribute (item, "jid"); pnic = wocky_node_get_attribute (item, "nick"); role = wocky_node_get_attribute (item, "role"); aff = wocky_node_get_attribute (item, "affiliation"); actor = wocky_node_get_child (item, "actor"); cause = wocky_node_get_child (item, "reason"); r = string_to_role (role); a = string_to_aff (aff); if (actor != NULL) ajid = wocky_node_get_attribute (actor, "jid"); if (cause != NULL) why = cause->content; } /* if this was not in the item, set it from the envelope: */ if (pnic == NULL) pnic = resource; codes = extract_status_codes (x); /* belt and braces: it is possible OWN_PRESENCE is not set, as it is * * only a SHOULD in the RFC: check the 'from' stanza attribute and the * * jid item node attribute against the MUC jid and the users full jid * * respectively to see if this is our own presence */ if (!wocky_strdiff (priv->jid, from) || !wocky_strdiff (priv->user, pjid) ) codes |= WOCKY_MUC_CODE_OWN_PRESENCE; self_presence = (codes & WOCKY_MUC_CODE_OWN_PRESENCE) != 0; /* ok, we've extracted all the presence stanza data we should need: * * if this was a presence notification, deal with it: */ if (type == WOCKY_STANZA_SUB_TYPE_NONE) { /* if this was the first time we got our own presence it also means * * we successfully joined the channel, so update our internal state * * and emit the channel-joined signal */ if (self_presence) { handle_self_presence (muc, stanza, pnic, r, a, ajid, why, msg, codes); if (priv->state < WOCKY_MUC_JOINED) { priv->state = WOCKY_MUC_JOINED; if (priv->join_cb != NULL) { g_simple_async_result_complete (priv->join_cb); g_object_unref (priv->join_cb); priv->join_cb = NULL; } g_signal_emit (muc, signals[SIG_JOINED], 0, stanza, codes); } else g_signal_emit (muc, signals[SIG_OWN_PRESENCE], 0, stanza, codes); /* Allow other handlers to run for this stanza. */ return FALSE; } /* if this is someone else's presence, update internal member list */ else { return handle_user_presence (muc, stanza, from, /* room@service/nick */ pjid, /* jid attr from item */ pnic, /* nick attr from item or /res from envelope 'from' */ r, a, ajid, why, msg, codes); } } else if (type == WOCKY_STANZA_SUB_TYPE_UNAVAILABLE) { if (self_presence) { priv->state = WOCKY_MUC_ENDED; priv->role = WOCKY_MUC_ROLE_NONE; g_signal_emit (muc, signals[SIG_PARTED], 0, stanza, codes, ajid, why, msg); return TRUE; } else { WockyMucMember *member = g_hash_table_lookup (priv->members, from); if (member == NULL) { DEBUG ("Someone not in the muc left!?"); return FALSE; } g_signal_emit (muc, signals[SIG_LEFT], 0, stanza, codes, member, ajid, why, msg); g_hash_table_remove (priv->members, from); return TRUE; } } return FALSE; } static gboolean handle_presence_error (WockyMuc *muc, WockyStanza *stanza) { gboolean ok = FALSE; WockyMucPrivate *priv = muc->priv; WockyXmppErrorType type; GError *error = NULL; wocky_stanza_extract_errors (stanza, &type, &error, NULL, NULL); if (priv->state >= WOCKY_MUC_JOINED) { DEBUG ("presence error after joining; not handled"); DEBUG (" %s: %s", wocky_xmpp_error_string (error->code), error->message); } g_signal_emit (muc, signals[SIG_PRESENCE_ERROR], 0, stanza, type, error); g_clear_error (&error); return ok; } static gboolean handle_presence (WockyPorter *porter, WockyStanza *stanza, gpointer data) { WockyMuc *muc = WOCKY_MUC (data); WockyStanzaSubType subtype; gboolean handled = FALSE; wocky_stanza_get_type_info (stanza, NULL, &subtype); switch (subtype) { case WOCKY_STANZA_SUB_TYPE_NONE: case WOCKY_STANZA_SUB_TYPE_UNAVAILABLE: { gchar *resource; /* If the JID is unparseable, discard the stanza. The porter shouldn't * even give us such stanzas. */ if (!wocky_decode_jid (wocky_stanza_get_from (stanza), NULL, NULL, &resource)) return TRUE; handled = handle_presence_standard (muc, stanza, subtype, resource); g_free (resource); break; } case WOCKY_STANZA_SUB_TYPE_ERROR: handled = handle_presence_error (muc, stanza); break; default: DEBUG ("unexpected stanza sub-type: %d", subtype); break; } return handled; } /* Looks up the sender of a message. If they're not currently a MUC member, * then a temporary structure is created, and @member_is_temporary is set to * %TRUE; the caller needs to free the returned value when they're done with * it. */ static WockyMucMember * get_message_sender (WockyMuc *muc, const gchar *from, gboolean *member_is_temporary) { WockyMucPrivate *priv = muc->priv; WockyMucMember *who = g_hash_table_lookup (priv->members, from); if (who != NULL) { *member_is_temporary = FALSE; return who; } /* Okay, it's from someone not currently in the MUC. We'll have to * fake up a structure. */ *member_is_temporary = TRUE; who = alloc_member (); who->from = wocky_normalise_jid (from); if (!wocky_strdiff (who->from, priv->jid)) { /* It's from us! */ who->jid = g_strdup (priv->user); who->nick = g_strdup (priv->nick); who->role = priv->role; who->affiliation = priv->affiliation; } /* else, we don't know anything more about the sender. * * FIXME: actually, if the server uses XEP-0203 Delayed Delivery * rather than XEP-0091 Legacy Delayed Delivery, the from='' * attribute of the element says who the original JID * actually was. Unfortunately, XEP-0091 said that from='' should be * the bare JID of the MUC, so it's completely useless. * * FIXME: also: we assume here that a delayed message from resource * /blah was sent by the user currently called /blah, but that ain't * necessarily so. */ return who; } /* * Parse timestamp of delayed messages. For non-delayed, it's 0. */ static GDateTime * extract_timestamp (WockyNode *msg) { WockyNode *x = wocky_node_get_child_ns (msg, "x", WOCKY_XMPP_NS_DELAY); GDateTime *stamp = NULL; if (x != NULL) { const gchar *tm = wocky_node_get_attribute (x, "stamp"); /* These timestamps do not contain a timezone, but are understood to be * in GMT. They're in the format yyyymmddThhmmss, so if we append 'Z' * we'll get (one of the many valid syntaxes for) an ISO-8601 timestamp. */ if (tm != NULL) { GTimeVal timeval = { 0, 0 }; gchar *tm_dup = g_strdup_printf ("%sZ", tm); /* FIXME: GTimeVal should go away */ if (!g_time_val_from_iso8601 (tm_dup, &timeval)) DEBUG ("Malformed date string '%s' for " WOCKY_XMPP_NS_DELAY, tm); else stamp = g_date_time_new_from_timeval_local (&timeval); g_free (tm_dup); } } return stamp; } /* Messages starting with /me are ACTION messages, and the /me should be * removed. type="chat" messages are NORMAL. Everything else is * something that doesn't necessarily expect a reply or ongoing * conversation ("normal") or has been auto-sent, so we make it NOTICE in * all other cases. */ static WockyMucMsgType determine_message_type (const gchar **body, WockyStanzaSubType sub_type) { WockyMucMsgType mtype = WOCKY_MUC_MSG_NOTICE; if (*body != NULL) { if (g_str_has_prefix (*body, "/me ")) { mtype = WOCKY_MUC_MSG_ACTION; *body += 4; } else if (g_str_equal (body, "/me")) { mtype = WOCKY_MUC_MSG_ACTION; *body = ""; } else if ((sub_type == WOCKY_STANZA_SUB_TYPE_GROUPCHAT) || (sub_type == WOCKY_STANZA_SUB_TYPE_CHAT)) { mtype = WOCKY_MUC_MSG_NORMAL; } } return mtype; } static WockyMucMsgState extract_chat_state (WockyNode *msg) { WockyNode *child = wocky_node_get_first_child_ns (msg, WOCKY_NS_CHATSTATE); WockyMucMsgState mstate; if (child == NULL || !wocky_enum_from_nick (WOCKY_TYPE_MUC_MSG_STATE, child->name, &mstate)) mstate = WOCKY_MUC_MSG_NONE; return mstate; } static gboolean handle_message (WockyPorter *porter, WockyStanza *stanza, gpointer data) { WockyMuc *muc = WOCKY_MUC (data); WockyNode *msg = wocky_stanza_get_top_node (stanza); const gchar *id = wocky_node_get_attribute (msg, "id"); const gchar *from = wocky_node_get_attribute (msg, "from"); const gchar *body = wocky_node_get_content_from_child (msg, "body"); const gchar *subj = wocky_node_get_content_from_child (msg, "subject"); GDateTime *datetime = extract_timestamp (msg); WockyStanzaSubType sub_type; WockyMucMsgType mtype; WockyMucMember *who = NULL; gboolean member_is_temporary = FALSE; wocky_stanza_get_type_info (stanza, NULL, &sub_type); /* if the message purports to be from a MUC member, treat as such: */ if (strchr (from, '/') != NULL) { who = get_message_sender (muc, from, &member_is_temporary); /* If it's a message from a member (as opposed to the MUC itself), and * it's not type='groupchat', then it's a non-MUC message relayed by the * MUC and therefore not our responsibility. */ if (sub_type != WOCKY_STANZA_SUB_TYPE_GROUPCHAT) { DEBUG ("Non groupchat message from MUC member %s: ignored.", from); return FALSE; } } mtype = determine_message_type (&body, sub_type); if (sub_type == WOCKY_STANZA_SUB_TYPE_ERROR) { WockyXmppErrorType etype; GError *error = NULL; wocky_stanza_extract_errors (stanza, &etype, &error, NULL, NULL); g_signal_emit (muc, signals[SIG_MSG_ERR], 0, stanza, mtype, id, datetime, who, body, etype, error); g_clear_error (&error); } else { WockyMucMsgState mstate = extract_chat_state (msg); g_signal_emit (muc, signals[SIG_MSG], 0, stanza, mtype, id, datetime, who, body, subj, mstate); } if (member_is_temporary) free_member (who); if (datetime != NULL) g_date_time_unref (datetime); return TRUE; } void wocky_muc_join (WockyMuc *muc, GCancellable *cancel) { WockyMucPrivate *priv = muc->priv; WockyStanza *presence = wocky_muc_create_presence (muc, WOCKY_STANZA_SUB_TYPE_NONE, NULL); WockyNode *x = wocky_node_add_child_ns (wocky_stanza_get_top_node (presence), "x", WOCKY_NS_MUC); if (priv->pass != NULL) wocky_node_add_child_with_content (x, "password", priv->pass); if (priv->state < WOCKY_MUC_INITIATED) { register_presence_handler (muc); register_message_handler (muc); } priv->state = WOCKY_MUC_INITIATED; wocky_porter_send (priv->porter, presence); g_object_unref (presence); } /* misc meta data */ const gchar * wocky_muc_jid (WockyMuc *muc) { WockyMucPrivate *priv = muc->priv; return priv->jid; } WockyMucRole wocky_muc_role (WockyMuc *muc) { WockyMucPrivate *priv = muc->priv; return priv->role; } WockyMucAffiliation wocky_muc_affiliation (WockyMuc *muc) { WockyMucPrivate *priv = muc->priv; return priv->affiliation; } const gchar * wocky_muc_user (WockyMuc *muc) { WockyMucPrivate *priv = muc->priv; return priv->user; } GHashTable * wocky_muc_members (WockyMuc *muc) { WockyMucPrivate *priv = muc->priv; if (priv->members != NULL) return g_hash_table_ref (priv->members); return NULL; } WockyMucState wocky_muc_get_state (WockyMuc *muc) { WockyMucPrivate *priv = muc->priv; return priv->state; } /* send message to muc */ /* send message to participant */ telepathy-gabble-0.18.2/lib/ext/wocky/wocky/wocky-meta-porter.c0000644000175000017500000014024712200204546024413 0ustar00smcvsmcv00000000000000/* * wocky-meta-porter.c - Source for WockyMetaPorter * Copyright (C) 2011 Collabora Ltd. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the tubesplied 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 */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "wocky-meta-porter.h" #include #include #include #ifdef G_OS_WIN32 #include #include #include typedef uint32_t u_int32_t; typedef uint16_t u_int16_t; #else #include #include #endif #include "wocky-ll-connection-factory.h" #include "wocky-contact-factory.h" #include "wocky-c2s-porter.h" #include "wocky-utils.h" #include "wocky-ll-contact.h" #include "wocky-ll-connector.h" #include "wocky-loopback-stream.h" #define WOCKY_DEBUG_FLAG WOCKY_DEBUG_PORTER #include "wocky-debug-internal.h" static void wocky_porter_iface_init (gpointer g_iface, gpointer iface_data); G_DEFINE_TYPE_WITH_CODE (WockyMetaPorter, wocky_meta_porter, G_TYPE_OBJECT, G_IMPLEMENT_INTERFACE (WOCKY_TYPE_PORTER, wocky_porter_iface_init)); /* properties */ enum { PROP_JID = 1, PROP_CONTACT_FACTORY, PROP_CONNECTION, PROP_RESOURCE, }; #define PORTER_JID_QUARK \ (g_quark_from_static_string ("wocky-meta-porter-c2s-jid")) /* private structure */ struct _WockyMetaPorterPrivate { gchar *jid; WockyContactFactory *contact_factory; WockyLLConnectionFactory *connection_factory; /* owned (gchar *) jid => owned (PorterData *) */ GHashTable *porters; /* guint handler id => owned (StanzaHandler *) */ GHashTable *handlers; GSocketService *listener; guint16 port; guint next_handler_id; }; typedef struct { WockyMetaPorter *self; WockyContact *contact; /* owned */ WockyPorter *porter; /* also owned, for convenience */ gchar *jid; guint refcount; guint timeout_id; } PorterData; typedef struct { WockyMetaPorter *self; WockyContact *contact; /* weak reffed WockyPorter* => handler ID */ GHashTable *porters; WockyStanzaType type; WockyStanzaSubType sub_type; guint priority; WockyPorterHandlerFunc callback; gpointer user_data; WockyStanza *stanza; } StanzaHandler; GQuark wocky_meta_porter_error_quark (void) { static GQuark quark = 0; if (!quark) quark = g_quark_from_static_string ( "wocky_meta_porter_error"); return quark; } static void register_porter_handlers (WockyMetaPorter *self, WockyPorter *porter, WockyContact *contact); static void disconnect_porter_signal_handlers (WockyPorter *porter, PorterData *data); static void porter_data_free (gpointer data) { PorterData *p = data; if (p->porter != NULL) { /* We have to make sure we disconnect the handlers or ::closing * will be fired by close_async, then the callback will unref * p->porter before we have a chance to do it outselves. */ disconnect_porter_signal_handlers (p->porter, p); wocky_porter_close_async (p->porter, NULL, NULL, NULL); g_object_unref (p->porter); } if (p->timeout_id > 0) g_source_remove (p->timeout_id); g_free (p->jid); g_slice_free (PorterData, data); } static void porter_closed_cb (GObject *source_object, GAsyncResult *result, gpointer user_data) { WockyPorter *porter = WOCKY_PORTER (source_object); GError *error = NULL; PorterData *data = user_data; if (!wocky_porter_close_finish (porter, result, &error)) { DEBUG ("Failed to close porter to '%s': %s", data->jid, error->message); g_clear_error (&error); } else { DEBUG ("Closed porter to '%s'", data->jid); } porter_data_free (data); } static gboolean porter_timeout_cb (gpointer d) { PorterData *data = d; WockyMetaPorterPrivate *priv = data->self->priv; data->timeout_id = 0; g_hash_table_steal (priv->porters, data->contact); /* we need to unref this ourselves as we just stole it from the hash * table */ g_object_unref (data->contact); if (data->porter != NULL) wocky_porter_close_async (data->porter, NULL, porter_closed_cb, data); else porter_data_free (data); return FALSE; } static void porter_closing_cb (WockyPorter *porter, PorterData *data); static void porter_remote_closed_cb (WockyPorter *porter, PorterData *data); static void porter_remote_error_cb (WockyPorter *porter, GQuark domain, gint code, const gchar *msg, PorterData *data); static void porter_sending_cb ( WockyC2SPorter *child_porter, WockyStanza *stanza, PorterData *data); static void disconnect_porter_signal_handlers (WockyPorter *porter, PorterData *data) { g_signal_handlers_disconnect_by_func (porter, porter_remote_closed_cb, data); g_signal_handlers_disconnect_by_func (porter, porter_closing_cb, data); g_signal_handlers_disconnect_by_func (porter, porter_remote_error_cb, data); g_signal_handlers_disconnect_by_func (porter, porter_sending_cb, data); } static void porter_closing_cb (WockyPorter *porter, PorterData *data) { DEBUG ("porter to '%s' closing, remove it from our records", data->jid); /* Don't stop the porter timeout here because that means if a * connection is never opened to the contact again the PorterData * struct will stick around until the meta porter is disposed. */ disconnect_porter_signal_handlers (porter, data); if (data->porter != NULL) g_object_unref (data->porter); data->porter = NULL; } static void porter_remote_closed_cb (WockyPorter *porter, PorterData *data) { DEBUG ("porter closed by remote, remove it from our records"); porter_closing_cb (porter, data); } static void porter_remote_error_cb (WockyPorter *porter, GQuark domain, gint code, const gchar *msg, PorterData *data) { DEBUG ("remote error in porter, close it"); wocky_porter_force_close_async (porter, NULL, NULL, NULL); porter_closing_cb (porter, data); } static void porter_sending_cb ( WockyC2SPorter *child_porter, WockyStanza *stanza, PorterData *data) { g_signal_emit_by_name (data->self, "sending", stanza); } static void maybe_start_timeout (PorterData *data) { if (data->refcount == 0) { /* if we've already got a timeout going let's cancel it and get * a new one going instead of having two. */ if (data->timeout_id > 0) g_source_remove (data->timeout_id); DEBUG ("Started porter timeout..."); data->timeout_id = g_timeout_add_seconds (5, porter_timeout_cb, data); } } static WockyPorter * create_porter (WockyMetaPorter *self, WockyXmppConnection *connection, WockyContact *contact) { WockyMetaPorterPrivate *priv = self->priv; PorterData *data; data = g_hash_table_lookup (priv->porters, contact); if (data != NULL) { if (data->porter != NULL) { /* close the new one; this function is meant to have stolen * a reference to the connection so we don't need to unref * it. It will ref itself for the duration of this close * call. */ wocky_xmpp_connection_send_close_async (connection, NULL, NULL, NULL); return data->porter; } else { data->porter = wocky_c2s_porter_new (connection, priv->jid); } } else { data = g_slice_new0 (PorterData); data->self = self; data->contact = contact; /* already will be reffed as the key */ data->jid = wocky_contact_dup_jid (contact); data->porter = wocky_c2s_porter_new (connection, priv->jid); data->refcount = 0; data->timeout_id = 0; g_hash_table_insert (priv->porters, g_object_ref (contact), data); } /* we need to set this so when we get a stanza in from a porter with * no from attribute we can find the real originating contact. The * StanzaHandler struct doesn't reference the PorterData struct, so * we simply store its jid here now. */ g_object_set_qdata_full (G_OBJECT (data->porter), PORTER_JID_QUARK, g_strdup (data->jid), g_free); g_signal_connect (data->porter, "closing", G_CALLBACK (porter_closing_cb), data); g_signal_connect (data->porter, "remote-closed", G_CALLBACK (porter_remote_closed_cb), data); g_signal_connect (data->porter, "remote-error", G_CALLBACK (porter_remote_error_cb), data); g_signal_connect (data->porter, "sending", G_CALLBACK (porter_sending_cb), data); register_porter_handlers (self, data->porter, contact); wocky_porter_start (data->porter); /* maybe start the timeout */ maybe_start_timeout (data); return data->porter; } /** * wocky_meta_porter_hold: * @porter: a #WockyMetaPorter * @contact: a #WockyContact * * Increases the hold count of the porter to @contact by * one. This means that if there is a connection open to @contact then * it will not disconnected after a timeout. Note that calling this * function does not mean a connection will be opened. The hold * count on a contact survives across connections. * * To decrement the hold count of the porter to @contact, one * must call wocky_meta_porter_unhold(). */ void wocky_meta_porter_hold (WockyMetaPorter *self, WockyContact *contact) { WockyMetaPorterPrivate *priv = self->priv; PorterData *data; g_return_if_fail (WOCKY_IS_META_PORTER (self)); data = g_hash_table_lookup (priv->porters, contact); if (data == NULL) { data = g_slice_new0 (PorterData); data->self = self; data->contact = contact; data->jid = wocky_contact_dup_jid (contact); data->porter = NULL; data->refcount = 0; data->timeout_id = 0; g_hash_table_insert (priv->porters, g_object_ref (contact), data); } DEBUG ("Porter to '%s' refcount %u --> %u", data->jid, data->refcount, data->refcount + 1); data->refcount++; if (data->timeout_id > 0) { g_source_remove (data->timeout_id); data->timeout_id = 0; } } /** * wocky_meta_porter_unhold: * @porter: a #WockyMetaPorter * @contact: a #WockyContact * * Decreases the hold count of the porter to @contact by * one. This means that if there is a connection open to @contact and * the hold count is zero, a connection timeout will be * started. */ void wocky_meta_porter_unhold (WockyMetaPorter *self, WockyContact *contact) { WockyMetaPorterPrivate *priv; PorterData *data; g_return_if_fail (WOCKY_IS_META_PORTER (self)); priv = self->priv; data = g_hash_table_lookup (priv->porters, contact); if (data == NULL) return; DEBUG ("Porter to '%s' refcount %u --> %u", data->jid, data->refcount, data->refcount - 1); data->refcount--; maybe_start_timeout (data); } static void wocky_meta_porter_init (WockyMetaPorter *self) { WockyMetaPorterPrivate *priv = G_TYPE_INSTANCE_GET_PRIVATE (self, WOCKY_TYPE_META_PORTER, WockyMetaPorterPrivate); self->priv = priv; } /* FIXME: these two functions are a hack until we get the * normalization of v6-in-v4 addresses in GLib. See bgo#646082 */ union BigSockAddr { struct sockaddr_in s4; struct sockaddr_in6 s6; struct sockaddr_storage storage; }; static void normalize_sockaddr (union BigSockAddr *addr) { if (addr->s6.sin6_family == AF_INET6 && IN6_IS_ADDR_V4MAPPED (&(addr->s6.sin6_addr))) { /* Normalize to ipv4 address */ u_int32_t addr_big_endian; u_int16_t port; memcpy (&addr_big_endian, addr->s6.sin6_addr.s6_addr + 12, 4); port = addr->s6.sin6_port; addr->s4.sin_family = AF_INET; addr->s4.sin_addr.s_addr = addr_big_endian; addr->s4.sin_port = port; } } static GSocketAddress * normalize_address (GSocketAddress *addr) { union BigSockAddr ss; if (g_socket_address_get_family (addr) != G_SOCKET_FAMILY_IPV6) return addr; if (!g_socket_address_to_native (addr, &(ss.storage), sizeof (ss.storage), NULL)) return addr; g_object_unref (addr); normalize_sockaddr (&ss); return g_socket_address_new_from_native (&(ss.storage), sizeof (ss.storage)); } static void new_connection_connect_cb (GObject *source, GAsyncResult *result, gpointer user_data) { WockyLLConnector *connector = WOCKY_LL_CONNECTOR (source); WockyXmppConnection *connection; GError *error = NULL; WockyMetaPorter *self = user_data; WockyMetaPorterPrivate *priv = self->priv; GList *contacts, *l; WockyLLContact *contact = NULL; gchar *from; connection = wocky_ll_connector_finish (connector, result, &from, &error); if (connection == NULL) { DEBUG ("connection error: %s", error->message); g_clear_error (&error); goto out; } if (from != NULL) { contact = wocky_contact_factory_ensure_ll_contact (priv->contact_factory, from); } if (contact == NULL) { GSocketConnection *socket_connection; GSocketAddress *socket_address; GInetAddress *addr; /* we didn't get a from attribute in the stream open */ g_object_get (connection, "base-stream", &socket_connection, NULL); socket_address = g_socket_connection_get_remote_address ( socket_connection, NULL); socket_address = normalize_address (socket_address); addr = g_inet_socket_address_get_address ( G_INET_SOCKET_ADDRESS (socket_address)); contacts = wocky_contact_factory_get_ll_contacts (priv->contact_factory); for (l = contacts; l != NULL; l = l->next) { WockyLLContact *c = l->data; if (wocky_ll_contact_has_address (c, addr)) { contact = g_object_ref (c); break; } } g_list_free (contacts); g_object_unref (socket_address); g_object_unref (socket_connection); } if (contact != NULL) { create_porter (self, connection, WOCKY_CONTACT (contact)); } else { DEBUG ("Failed to find contact for new connection, let it close"); } g_object_unref (connection); out: g_object_unref (self); } static gboolean _new_connection (GSocketService *service, GSocketConnection *socket_connection, GObject *source_object, gpointer user_data) { WockyMetaPorter *self = user_data; GSocketAddress *addr; GInetAddress *inet_address; gchar *str; GError *error = NULL; addr = g_socket_connection_get_remote_address ( socket_connection, &error); if (addr == NULL) { DEBUG ("New connection, but failed to get remote address " "so ignoring: %s", error->message); g_clear_error (&error); return FALSE; } addr = normalize_address (addr); inet_address = g_inet_socket_address_get_address ( G_INET_SOCKET_ADDRESS (addr)); str = g_inet_address_to_string (inet_address); DEBUG ("New connection from %s!", str); wocky_ll_connector_incoming_async (G_IO_STREAM (socket_connection), NULL, new_connection_connect_cb, g_object_ref (self)); g_free (str); g_object_unref (addr); return TRUE; } static void stanza_handler_porter_disposed_cb (gpointer data, GObject *porter); static void free_handler (gpointer data) { StanzaHandler *handler = data; GHashTableIter iter; gpointer key, value; g_hash_table_iter_init (&iter, handler->porters); while (g_hash_table_iter_next (&iter, &key, &value)) { WockyPorter *porter = key; guint id = GPOINTER_TO_UINT (value); wocky_porter_unregister_handler (porter, id); g_object_weak_unref (G_OBJECT (porter), stanza_handler_porter_disposed_cb, handler); } g_hash_table_unref (handler->porters); if (handler->contact != NULL) g_object_unref (handler->contact); if (handler->stanza != NULL) g_object_unref (handler->stanza); g_slice_free (StanzaHandler, handler); } static void loopback_recv_open_cb (GObject *source_object, GAsyncResult *result, gpointer user_data) { WockyXmppConnection *connection = WOCKY_XMPP_CONNECTION (source_object); WockyMetaPorter *self = user_data; WockyMetaPorterPrivate *priv = self->priv; WockyLLContact *contact; GError *error = NULL; if (!wocky_xmpp_connection_recv_open_finish (connection, result, NULL, NULL, NULL, NULL, NULL, &error)) { DEBUG ("Failed to receive stream open from loopback stream: %s", error->message); g_clear_error (&error); g_object_unref (connection); return; } contact = wocky_contact_factory_ensure_ll_contact ( priv->contact_factory, priv->jid); /* the ref, the porter and the connection will all be freed when the * meta porter is freed */ create_porter (self, connection, WOCKY_CONTACT (contact)); wocky_meta_porter_hold (self, WOCKY_CONTACT (contact)); g_object_unref (contact); g_object_unref (connection); } static void loopback_sent_open_cb (GObject *source_object, GAsyncResult *result, gpointer user_data) { WockyXmppConnection *connection = WOCKY_XMPP_CONNECTION (source_object); WockyMetaPorter *self = user_data; GError *error = NULL; if (!wocky_xmpp_connection_send_open_finish (connection, result, &error)) { DEBUG ("Failed to send stream open to loopback stream: %s", error->message); g_clear_error (&error); g_object_unref (connection); return; } wocky_xmpp_connection_recv_open_async (connection, NULL, loopback_recv_open_cb, self); } static void create_loopback_porter (WockyMetaPorter *self) { WockyMetaPorterPrivate *priv = self->priv; GIOStream *stream; WockyXmppConnection *connection; if (priv->jid == NULL) return; stream = wocky_loopback_stream_new (); connection = wocky_xmpp_connection_new (stream); /* really simple connector */ wocky_xmpp_connection_send_open_async (connection, NULL, NULL, NULL, NULL, NULL, NULL, loopback_sent_open_cb, self); g_object_unref (stream); } static void wocky_meta_porter_constructed (GObject *obj) { WockyMetaPorter *self = WOCKY_META_PORTER (obj); WockyMetaPorterPrivate *priv = self->priv; if (G_OBJECT_CLASS (wocky_meta_porter_parent_class)->constructed) G_OBJECT_CLASS (wocky_meta_porter_parent_class)->constructed (obj); priv->listener = g_socket_service_new (); g_signal_connect (priv->listener, "incoming", G_CALLBACK (_new_connection), self); priv->next_handler_id = 1; priv->connection_factory = wocky_ll_connection_factory_new (); priv->porters = g_hash_table_new_full (g_direct_hash, g_direct_equal, g_object_unref, porter_data_free); priv->handlers = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, free_handler); /* Create the loopback porter */ if (priv->jid != NULL) create_loopback_porter (self); } static void wocky_meta_porter_finalize (GObject *object) { WockyMetaPorter *self = WOCKY_META_PORTER (object); WockyMetaPorterPrivate *priv = self->priv; g_free (priv->jid); priv->jid = NULL; if (G_OBJECT_CLASS (wocky_meta_porter_parent_class)->finalize) G_OBJECT_CLASS (wocky_meta_porter_parent_class)->finalize (object); } static void wocky_meta_porter_dispose (GObject *object) { WockyMetaPorter *self = WOCKY_META_PORTER (object); WockyMetaPorterPrivate *priv = self->priv; g_object_unref (priv->contact_factory); g_object_unref (priv->connection_factory); g_socket_service_stop (priv->listener); g_object_unref (priv->listener); g_hash_table_unref (priv->porters); g_hash_table_unref (priv->handlers); if (G_OBJECT_CLASS (wocky_meta_porter_parent_class)->dispose) G_OBJECT_CLASS (wocky_meta_porter_parent_class)->dispose (object); } static void wocky_meta_porter_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { WockyMetaPorter *self = WOCKY_META_PORTER (object); WockyMetaPorterPrivate *priv = self->priv; switch (property_id) { case PROP_JID: g_value_set_string (value, priv->jid); break; case PROP_CONTACT_FACTORY: g_value_set_object (value, priv->contact_factory); break; case PROP_CONNECTION: /* nothing; just here to implement WockyPorter */ g_value_set_object (value, NULL); break; case PROP_RESOURCE: /* nothing; just here to implement WockyPorter */ g_value_set_string (value, NULL); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void wocky_meta_porter_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { WockyMetaPorter *self = WOCKY_META_PORTER (object); WockyMetaPorterPrivate *priv = self->priv; switch (property_id) { case PROP_JID: priv->jid = g_value_dup_string (value); break; case PROP_CONTACT_FACTORY: priv->contact_factory = g_value_dup_object (value); break; case PROP_CONNECTION: /* nothing; just here to implement WockyPorter */ break; case PROP_RESOURCE: /* nothing; just here to implement WockyPorter */ break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void wocky_meta_porter_class_init ( WockyMetaPorterClass *wocky_meta_porter_class) { GObjectClass *object_class = G_OBJECT_CLASS (wocky_meta_porter_class); GParamSpec *param_spec; g_type_class_add_private (wocky_meta_porter_class, sizeof (WockyMetaPorterPrivate)); object_class->dispose = wocky_meta_porter_dispose; object_class->finalize = wocky_meta_porter_finalize; object_class->constructed = wocky_meta_porter_constructed; object_class->get_property = wocky_meta_porter_get_property; object_class->set_property = wocky_meta_porter_set_property; /** * WockyMetaPorter:contact-factory: * * The #WockyContactFactory object in use by this meta porter. */ param_spec = g_param_spec_object ("contact-factory", "Contact factory", "WockyContactFactory object in use", WOCKY_TYPE_CONTACT_FACTORY, G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, PROP_CONTACT_FACTORY, param_spec); g_object_class_override_property (object_class, PROP_CONNECTION, "connection"); g_object_class_override_property (object_class, PROP_JID, "full-jid"); g_object_class_override_property (object_class, PROP_JID, "bare-jid"); g_object_class_override_property (object_class, PROP_RESOURCE, "resource"); } /** * wocky_meta_porter_new: * @jid: the JID of the local user, or %NULL * @contact_factory: a #WockyContactFactory object * * Convenience function to create a new #WockyMetaPorter object. The * JID can be set later by using wocky_meta_porter_set_jid(). * * Returns: a new #WockyMetaPorter */ WockyPorter * wocky_meta_porter_new (const gchar *jid, WockyContactFactory *contact_factory) { g_return_val_if_fail (WOCKY_IS_CONTACT_FACTORY (contact_factory), NULL); return g_object_new (WOCKY_TYPE_META_PORTER, "full-jid", jid, "contact-factory", contact_factory, NULL); } static const gchar * wocky_meta_porter_get_jid (WockyPorter *porter) { WockyMetaPorter *self; g_return_val_if_fail (WOCKY_IS_META_PORTER (porter), NULL); self = (WockyMetaPorter *) porter; return self->priv->jid; } static const gchar * wocky_meta_porter_get_resource (WockyPorter *porter) { return NULL; } typedef void (*OpenPorterIfNecessaryFunc) (WockyMetaPorter *self, WockyPorter *porter, GCancellable *cancellable, const GError *error, GSimpleAsyncResult *simple, gpointer user_data); typedef struct { WockyMetaPorter *self; WockyLLContact *contact; OpenPorterIfNecessaryFunc callback; GCancellable *cancellable; GSimpleAsyncResult *simple; gpointer user_data; } OpenPorterData; static void made_connection_connect_cb (GObject *source_object, GAsyncResult *result, gpointer user_data) { WockyLLConnector *connector = WOCKY_LL_CONNECTOR (source_object); WockyXmppConnection *connection; GError *error = NULL; OpenPorterData *data = user_data; WockyPorter *porter; connection = wocky_ll_connector_finish (connector, result, NULL, &error); if (connection == NULL) { DEBUG ("failed to connect: %s", error->message); data->callback (data->self, NULL, NULL, error, data->simple, data->user_data); g_clear_error (&error); goto out; } DEBUG ("connected"); porter = create_porter (data->self, connection, WOCKY_CONTACT (data->contact)); data->callback (data->self, porter, data->cancellable, NULL, data->simple, data->user_data); g_object_unref (connection); out: g_object_unref (data->contact); g_slice_free (OpenPorterData, data); } static void make_connection_cb (GObject *source_object, GAsyncResult *result, gpointer user_data) { WockyLLConnectionFactory *factory = WOCKY_LL_CONNECTION_FACTORY (source_object); WockyXmppConnection *connection; GError *error = NULL; OpenPorterData *data = user_data; WockyMetaPorterPrivate *priv = data->self->priv; gchar *jid; connection = wocky_ll_connection_factory_make_connection_finish (factory, result, &error); if (connection == NULL) { DEBUG ("making connection failed: %s", error->message); data->callback (data->self, NULL, NULL, error, data->simple, data->user_data); g_clear_error (&error); g_object_unref (data->contact); g_slice_free (OpenPorterData, data); return; } jid = wocky_contact_dup_jid (WOCKY_CONTACT (data->contact)); wocky_ll_connector_outgoing_async (connection, priv->jid, jid, data->cancellable, made_connection_connect_cb, data); g_free (jid); } /* Convenience function to call @callback with a porter and do all the * handling the creating a porter if necessary. */ static void open_porter_if_necessary (WockyMetaPorter *self, WockyLLContact *contact, GCancellable *cancellable, OpenPorterIfNecessaryFunc callback, GSimpleAsyncResult *simple, gpointer user_data) { WockyMetaPorterPrivate *priv = self->priv; PorterData *porter_data = g_hash_table_lookup (priv->porters, contact); OpenPorterData *data; if (porter_data != NULL && porter_data->porter != NULL) { callback (self, porter_data->porter, cancellable, NULL, simple, user_data); return; } data = g_slice_new0 (OpenPorterData); data->self = self; data->contact = g_object_ref (contact); data->callback = callback; data->cancellable = cancellable; data->simple = simple; data->user_data = user_data; wocky_ll_connection_factory_make_connection_async (priv->connection_factory, contact, cancellable, make_connection_cb, data); } static void meta_porter_send_cb (GObject *source_object, GAsyncResult *result, gpointer user_data) { GSimpleAsyncResult *simple = user_data; GError *error = NULL; if (!wocky_porter_send_finish (WOCKY_PORTER (source_object), result, &error)) { g_simple_async_result_set_from_error (simple, error); g_clear_error (&error); } g_simple_async_result_complete (simple); g_object_unref (simple); } static void meta_porter_send_got_porter_cb (WockyMetaPorter *self, WockyPorter *porter, GCancellable *cancellable, const GError *error, GSimpleAsyncResult *simple, gpointer user_data) { WockyStanza *stanza = user_data; if (error != NULL) { g_simple_async_result_set_from_error (simple, error); g_simple_async_result_complete (simple); g_object_unref (simple); } else { wocky_porter_send_async (porter, stanza, cancellable, meta_porter_send_cb, simple); } g_object_unref (stanza); } static void wocky_meta_porter_send_async (WockyPorter *porter, WockyStanza *stanza, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { WockyMetaPorter *self = WOCKY_META_PORTER (porter); WockyMetaPorterPrivate *priv = self->priv; GSimpleAsyncResult *simple; WockyContact *to; simple = g_simple_async_result_new (G_OBJECT (self), callback, user_data, wocky_meta_porter_send_async); to = wocky_stanza_get_to_contact (stanza); g_return_if_fail (WOCKY_IS_LL_CONTACT (to)); /* stamp on from if there is none */ if (wocky_stanza_get_from (stanza) == NULL) { wocky_node_set_attribute (wocky_stanza_get_top_node (stanza), "from", priv->jid); } open_porter_if_necessary (self, WOCKY_LL_CONTACT (to), cancellable, meta_porter_send_got_porter_cb, simple, g_object_ref (stanza)); } static gboolean wocky_meta_porter_send_finish (WockyPorter *self, GAsyncResult *result, GError **error) { g_return_val_if_fail (WOCKY_IS_META_PORTER (self), FALSE); wocky_implement_finish_void (self, wocky_meta_porter_send_async); } static guint16 wocky_meta_porter_listen (WockyMetaPorter *self, GError **error) { WockyMetaPorterPrivate *priv = self->priv; guint16 port; /* The port 5298 is preferred to remain compatible with old versions of * iChat. Try a few close to it, and if those fail, use a random port. */ for (port = 5298; port < 5300; port++) { GError *e = NULL; if (g_socket_listener_add_inet_port (G_SOCKET_LISTENER (priv->listener), port, NULL, &e)) break; if (!g_error_matches (e, G_IO_ERROR, G_IO_ERROR_ADDRESS_IN_USE)) { g_propagate_error (error, e); return 0; } g_clear_error (&e); } if (port < 5300) return port; return g_socket_listener_add_any_inet_port (G_SOCKET_LISTENER (priv->listener), NULL, error); } static void wocky_meta_porter_start (WockyPorter *porter) { WockyMetaPorter *self = WOCKY_META_PORTER (porter); WockyMetaPorterPrivate *priv = self->priv; GError *error = NULL; guint16 port; port = wocky_meta_porter_listen (self, &error); if (error != NULL) { DEBUG ("Failed to listen: %s", error->message); g_clear_error (&error); return; } DEBUG ("listening on port %u", port); g_socket_service_start (G_SOCKET_SERVICE (priv->listener)); priv->port = port; } static gboolean porter_handler_cb (WockyPorter *porter, WockyStanza *stanza, gpointer user_data) { StanzaHandler *handler = user_data; WockyMetaPorter *self = handler->self; WockyMetaPorterPrivate *priv = self->priv; WockyLLContact *contact; const gchar *from; /* prefer the from attribute over ignoring it and using the porter * JID */ from = wocky_stanza_get_from (stanza); if (from == NULL) from = g_object_get_qdata (G_OBJECT (porter), PORTER_JID_QUARK); contact = wocky_contact_factory_ensure_ll_contact ( priv->contact_factory, from); wocky_stanza_set_from_contact (stanza, WOCKY_CONTACT (contact)); g_object_unref (contact); return handler->callback (WOCKY_PORTER (handler->self), stanza, handler->user_data); } static void stanza_handler_porter_disposed_cb (gpointer data, GObject *porter) { StanzaHandler *handler = data; g_hash_table_remove (handler->porters, porter); } static void register_porter_handler (StanzaHandler *handler, WockyPorter *porter) { guint id; g_assert (g_hash_table_lookup (handler->porters, porter) == NULL); /* If handler->contact is not NULL, we know that this c2s porter is a * connection to them, so we still don't need to tell it to match the sender. */ id = wocky_porter_register_handler_from_anyone_by_stanza (porter, handler->type, handler->sub_type, handler->priority, porter_handler_cb, handler, handler->stanza); g_hash_table_insert (handler->porters, porter, GUINT_TO_POINTER (id)); g_object_weak_ref (G_OBJECT (porter), stanza_handler_porter_disposed_cb, handler); } static void register_porter_handlers (WockyMetaPorter *self, WockyPorter *porter, WockyContact *contact) { WockyMetaPorterPrivate *priv = self->priv; GList *handlers, *l; handlers = g_hash_table_get_values (priv->handlers); for (l = handlers; l != NULL; l = l->next) { StanzaHandler *handler = l->data; if (contact == handler->contact || handler->contact == NULL) register_porter_handler (handler, porter); } g_list_free (handlers); } static StanzaHandler * stanza_handler_new (WockyMetaPorter *self, WockyLLContact *contact, WockyStanzaType type, WockyStanzaSubType sub_type, guint priority, WockyPorterHandlerFunc callback, gpointer user_data, WockyStanza *stanza) { StanzaHandler *out = g_slice_new0 (StanzaHandler); out->self = self; out->porters = g_hash_table_new (NULL, NULL); if (contact != NULL) out->contact = g_object_ref (contact); out->type = type; out->sub_type = sub_type; out->priority = priority; out->callback = callback; out->user_data = user_data; if (stanza != NULL) out->stanza = g_object_ref (stanza); return out; } static guint wocky_meta_porter_register_handler_from_by_stanza (WockyPorter *porter, WockyStanzaType type, WockyStanzaSubType sub_type, const gchar *jid, guint priority, WockyPorterHandlerFunc callback, gpointer user_data, WockyStanza *stanza) { WockyMetaPorter *self = WOCKY_META_PORTER (porter); WockyMetaPorterPrivate *priv = self->priv; PorterData *porter_data; guint id; StanzaHandler *handler; WockyLLContact *from; g_return_val_if_fail (jid != NULL, 0); from = wocky_contact_factory_lookup_ll_contact ( priv->contact_factory, jid); g_return_val_if_fail (WOCKY_IS_LL_CONTACT (from), 0); handler = stanza_handler_new (self, from, type, sub_type, priority, callback, user_data, stanza); id = priv->next_handler_id++; porter_data = g_hash_table_lookup (priv->porters, from); if (porter_data != NULL && porter_data->porter != NULL) register_porter_handler (handler, porter_data->porter); g_hash_table_insert (priv->handlers, GUINT_TO_POINTER (id), handler); return id; } static guint wocky_meta_porter_register_handler_from_anyone_by_stanza (WockyPorter *porter, WockyStanzaType type, WockyStanzaSubType sub_type, guint priority, WockyPorterHandlerFunc callback, gpointer user_data, WockyStanza *stanza) { WockyMetaPorter *self = WOCKY_META_PORTER (porter); WockyMetaPorterPrivate *priv = self->priv; PorterData *porter_data; guint id; StanzaHandler *handler; GList *porters, *l; handler = stanza_handler_new (self, NULL, type, sub_type, priority, callback, user_data, stanza); id = priv->next_handler_id++; /* register on all porters */ porters = g_hash_table_get_values (priv->porters); for (l = porters; l != NULL; l = l->next) { porter_data = l->data; if (porter_data->porter != NULL) register_porter_handler (handler, porter_data->porter); } g_list_free (porters); g_hash_table_insert (priv->handlers, GUINT_TO_POINTER (id), handler); return id; } static void wocky_meta_porter_unregister_handler (WockyPorter *porter, guint id) { WockyMetaPorter *self = WOCKY_META_PORTER (porter); WockyMetaPorterPrivate *priv = self->priv; g_hash_table_remove (priv->handlers, GUINT_TO_POINTER (id)); } typedef gboolean (* ClosePorterFinishFunc) (WockyPorter *, GAsyncResult *, GError **); typedef void (* ClosePorterAsyncFunc) (WockyPorter *, GCancellable *, GAsyncReadyCallback, gpointer); typedef struct { GSimpleAsyncResult *simple; guint remaining; gboolean failed; ClosePorterFinishFunc close_finish; } ClosePorterData; static void porter_close_cb (GObject *source_object, GAsyncResult *result, gpointer user_data) { WockyPorter *porter = WOCKY_PORTER (source_object); GError *error = NULL; ClosePorterData *data = user_data; if (!data->close_finish (porter, result, &error)) { DEBUG ("Failed to close porter: %s", error->message); g_clear_error (&error); data->failed = TRUE; } data->remaining--; if (data->remaining > 0) return; /* all porters have now replied */ if (data->failed) { g_simple_async_result_set_error (data->simple, WOCKY_META_PORTER_ERROR, WOCKY_META_PORTER_ERROR_FAILED_TO_CLOSE, "Failed to close at least one porter"); } g_simple_async_result_complete (data->simple); g_object_unref (data->simple); g_slice_free (ClosePorterData, data); } static void close_all_porters (WockyMetaPorter *self, ClosePorterAsyncFunc close_async_func, ClosePorterFinishFunc close_finish_func, gpointer source_tag, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { WockyMetaPorterPrivate *priv = self->priv; GSimpleAsyncResult *simple; GList *porters, *l; gboolean close_called = FALSE; porters = g_hash_table_get_values (priv->porters); simple = g_simple_async_result_new (G_OBJECT (self), callback, user_data, source_tag); g_signal_emit_by_name (self, "closing"); if (porters != NULL) { ClosePorterData *data = g_slice_new0 (ClosePorterData); data->close_finish = close_finish_func; data->remaining = 0; data->simple = simple; for (l = porters; l != NULL; l = l->next) { PorterData *porter_data = l->data; /* NULL if there's a refcount but no porter */ if (porter_data->porter == NULL) continue; data->remaining++; close_called = TRUE; close_async_func (porter_data->porter, cancellable, porter_close_cb, data); } /* Actually, none of the PorterData structs had C2S porters */ if (!close_called) g_slice_free (ClosePorterData, data); } if (!close_called) { /* there were no porters to close anyway */ g_simple_async_result_complete (simple); g_object_unref (simple); } g_list_free (porters); } static void wocky_meta_porter_close_async (WockyPorter *porter, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { WockyMetaPorter *self = WOCKY_META_PORTER (porter); close_all_porters (self, wocky_porter_close_async, wocky_porter_close_finish, wocky_meta_porter_close_async, cancellable, callback, user_data); } static gboolean wocky_meta_porter_close_finish (WockyPorter *self, GAsyncResult *result, GError **error) { wocky_implement_finish_void (self, wocky_meta_porter_close_async); } typedef struct { WockyMetaPorter *self; /* already reffed by simple */ GSimpleAsyncResult *simple; WockyContact *contact; } SendIQData; static void meta_porter_send_iq_cb (GObject *source_object, GAsyncResult *result, gpointer user_data) { SendIQData *data = user_data; GSimpleAsyncResult *simple = data->simple; GError *error = NULL; WockyStanza *stanza; stanza = wocky_porter_send_iq_finish (WOCKY_PORTER (source_object), result, &error); if (stanza == NULL) { g_simple_async_result_set_from_error (simple, error); g_clear_error (&error); } else { wocky_stanza_set_from_contact (stanza, data->contact); g_simple_async_result_set_op_res_gpointer (simple, stanza, g_object_unref); } g_simple_async_result_complete (simple); wocky_meta_porter_unhold (data->self, data->contact); /* unref simple here as we depend on it holding potentially the last * ref on self */ g_object_unref (data->simple); g_object_unref (data->contact); g_slice_free (SendIQData, data); } static void meta_porter_send_iq_got_porter_cb (WockyMetaPorter *self, WockyPorter *porter, GCancellable *cancellable, const GError *error, GSimpleAsyncResult *simple, gpointer user_data) { WockyStanza *stanza = user_data; WockyContact *contact; contact = wocky_stanza_get_to_contact (stanza); if (error != NULL) { g_simple_async_result_set_from_error (simple, error); g_simple_async_result_complete (simple); wocky_meta_porter_unhold (self, contact); /* unref simple here as we depend on it potentially holding the * last ref to self */ g_object_unref (simple); } else { SendIQData *data = g_slice_new0 (SendIQData); data->self = self; data->simple = simple; data->contact = g_object_ref (contact); wocky_porter_send_iq_async (porter, stanza, cancellable, meta_porter_send_iq_cb, data); } g_object_unref (stanza); } static void wocky_meta_porter_send_iq_async (WockyPorter *porter, WockyStanza *stanza, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { WockyMetaPorter *self = WOCKY_META_PORTER (porter); WockyMetaPorterPrivate *priv = self->priv; GSimpleAsyncResult *simple; WockyContact *to; to = wocky_stanza_get_to_contact (stanza); g_return_if_fail (WOCKY_IS_LL_CONTACT (to)); simple = g_simple_async_result_new (G_OBJECT (self), callback, user_data, wocky_meta_porter_send_iq_async); wocky_meta_porter_hold (self, to); /* stamp on from if there is none */ if (wocky_node_get_attribute (wocky_stanza_get_top_node (stanza), "from") == NULL) { wocky_node_set_attribute (wocky_stanza_get_top_node (stanza), "from", priv->jid); } open_porter_if_necessary (self, WOCKY_LL_CONTACT (to), cancellable, meta_porter_send_iq_got_porter_cb, simple, g_object_ref (stanza)); } static WockyStanza * wocky_meta_porter_send_iq_finish (WockyPorter *self, GAsyncResult *result, GError **error) { wocky_implement_finish_return_copy_pointer (self, wocky_meta_porter_send_iq_async, g_object_ref); } static void wocky_meta_porter_force_close_async (WockyPorter *porter, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { WockyMetaPorter *self = WOCKY_META_PORTER (porter); close_all_porters (self, wocky_porter_force_close_async, wocky_porter_force_close_finish, wocky_meta_porter_force_close_async, cancellable, callback, user_data); } static gboolean wocky_meta_porter_force_close_finish (WockyPorter *self, GAsyncResult *result, GError **error) { wocky_implement_finish_void (self, wocky_meta_porter_force_close_async); } /** * wocky_meta_porter_get_port: * @porter: a #WockyMetaPorter * * Returns the port @porter is listening in on for new incoming XMPP * connections, or 0 if it has not been started yet with * wocky_porter_start(). * * Returns: the port @porter is listening in on for new incoming XMPP * connections, or 0 if it has not been started. */ guint16 wocky_meta_porter_get_port (WockyMetaPorter *self) { g_return_val_if_fail (WOCKY_IS_META_PORTER (self), 0); return self->priv->port; } /** * wocky_meta_porter_set_jid: * @porter: a #WockyMetaPorter * @jid: a new JID * * Changes the local JID according to @porter. Note that this function * can only be called once, and only if %NULL was passed to * wocky_meta_porter_new() when creating @porter. Calling it again * will be a no-op. */ void wocky_meta_porter_set_jid (WockyMetaPorter *self, const gchar *jid) { WockyMetaPorterPrivate *priv; g_return_if_fail (WOCKY_IS_META_PORTER (self)); priv = self->priv; /* You cannot set the meta porter JID again */ g_return_if_fail (priv->jid == NULL); /* don't try and change existing porter's JIDs */ priv->jid = g_strdup (jid); /* now we can do this */ create_loopback_porter (self); } static void meta_porter_open_got_porter_cb (WockyMetaPorter *self, WockyPorter *porter, GCancellable *cancellable, const GError *error, GSimpleAsyncResult *simple, gpointer user_data) { WockyContact *contact = user_data; if (error != NULL) { g_simple_async_result_set_from_error (simple, error); wocky_meta_porter_unhold (self, contact); } g_simple_async_result_complete (simple); g_object_unref (contact); g_object_unref (simple); } /** * wocky_meta_porter_open_async: * @porter: a #WockyMetaPorter * @contact: the #WockyLLContact * @cancellable: an optional #GCancellable, or %NULL * @callback: a callback to be called * @user_data: data for @callback * * Make an asynchronous request to open a connection to @contact if * one is not already open. The hold count of the porter to * @contact will be incrememented and so after completion * wocky_meta_porter_unhold() should be called on contact to release * the hold. * * When the request is complete, @callback will be called and the user * should call wocky_meta_porter_open_finish() to finish the request. */ void wocky_meta_porter_open_async (WockyMetaPorter *self, WockyLLContact *contact, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { GSimpleAsyncResult *simple; g_return_if_fail (WOCKY_IS_META_PORTER (self)); g_return_if_fail (WOCKY_IS_LL_CONTACT (contact)); g_return_if_fail (callback != NULL); simple = g_simple_async_result_new (G_OBJECT (self), callback, user_data, wocky_meta_porter_open_async); wocky_meta_porter_hold (self, WOCKY_CONTACT (contact)); open_porter_if_necessary (self, contact, cancellable, meta_porter_open_got_porter_cb, simple, g_object_ref (contact)); } /** * wocky_meta_porter_open_finish: * @porter: a #WockyMetaPorter * @result: the #GAsyncResult * @error: an optional #GError location to store an error message * * Finishes an asynchronous request to open a connection if one is not * already open. See wocky_meta_porter_open_async() for more details. * * Returns: %TRUE if the operation was a success, otherwise %FALSE */ gboolean wocky_meta_porter_open_finish (WockyMetaPorter *self, GAsyncResult *result, GError **error) { wocky_implement_finish_void (self, wocky_meta_porter_open_async); } /** * wocky_meta_porter_borrow_connection: * @porter: a #WockyMetaPorter * @contact: the #WockyContact * * Borrow the #GSocketConnection of the porter to @contact, if one * exists, otherwise %NULL will be returned. * Note that the connection returned should be reffed using * g_object_ref() if it needs to be kept. However, it will still be * operated on by the underlying #WockyXmppConnection object so can * close spontaneously unless wocky_meta_porter_hold() is called with * @contact. * * Returns: the #GSocketConnection or %NULL if no connection is open */ GSocketConnection * wocky_meta_porter_borrow_connection (WockyMetaPorter *self, WockyLLContact *contact) { WockyMetaPorterPrivate *priv; PorterData *porter_data; GSocketConnection *socket_conn; WockyXmppConnection *xmpp_conn; g_return_val_if_fail (WOCKY_IS_META_PORTER (self), NULL); g_return_val_if_fail (WOCKY_IS_LL_CONTACT (contact), NULL); priv = self->priv; porter_data = g_hash_table_lookup (priv->porters, contact); if (porter_data == NULL || porter_data->porter == NULL) return NULL; /* splendid, the connection is already open */ g_object_get (porter_data->porter, "connection", &xmpp_conn, NULL); /* will give it a new ref */ g_object_get (xmpp_conn, "base-stream", &socket_conn, NULL); /* we take back the ref */ g_object_unref (socket_conn); g_object_unref (xmpp_conn); /* but this will still be alive */ return socket_conn; } static void wocky_porter_iface_init (gpointer g_iface, gpointer iface_data) { WockyPorterInterface *iface = g_iface; iface->get_full_jid = wocky_meta_porter_get_jid; iface->get_bare_jid = wocky_meta_porter_get_jid; /* a dummy implementation to return NULL so if someone calls it on * us it won't assert */ iface->get_resource = wocky_meta_porter_get_resource; iface->start = wocky_meta_porter_start; iface->send_async = wocky_meta_porter_send_async; iface->send_finish = wocky_meta_porter_send_finish; iface->register_handler_from_by_stanza = wocky_meta_porter_register_handler_from_by_stanza; iface->register_handler_from_anyone_by_stanza = wocky_meta_porter_register_handler_from_anyone_by_stanza; iface->unregister_handler = wocky_meta_porter_unregister_handler; iface->close_async = wocky_meta_porter_close_async; iface->close_finish = wocky_meta_porter_close_finish; iface->send_iq_async = wocky_meta_porter_send_iq_async; iface->send_iq_finish = wocky_meta_porter_send_iq_finish; iface->force_close_async = wocky_meta_porter_force_close_async; iface->force_close_finish = wocky_meta_porter_force_close_finish; } telepathy-gabble-0.18.2/lib/ext/wocky/wocky/wocky-loopback-stream.c0000644000175000017500000003532512200204546025237 0ustar00smcvsmcv00000000000000/* * wocky-loopback-stream.c - Source for WockyLoopbackStream * Copyright (C) 2009-2011 Collabora Ltd. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include "wocky-loopback-stream.h" enum { PROP_IO_INPUT_STREAM = 1, PROP_IO_OUTPUT_STREAM }; static GType wocky_loopback_input_stream_get_type (void); static GType wocky_loopback_output_stream_get_type (void); struct _WockyLoopbackStreamPrivate { GInputStream *input; GOutputStream *output; }; typedef struct { GOutputStream parent; GAsyncQueue *queue; GError *write_error /* no, this is not a coding style violation */; gboolean dispose_has_run; } WockyLoopbackOutputStream; typedef struct { GOutputStreamClass parent_class; } WockyLoopbackOutputStreamClass; typedef struct { GInputStream parent; GAsyncQueue *queue; guint offset; GArray *out_array; GSimpleAsyncResult *read_result; GCancellable *read_cancellable; gulong read_cancellable_sig_id; void *buffer; gsize count; GError *read_error /* no, this is not a coding style violation */; gboolean dispose_has_run; } WockyLoopbackInputStream; typedef struct { GOutputStreamClass parent_class; } WockyLoopbackInputStreamClass; G_DEFINE_TYPE (WockyLoopbackStream, wocky_loopback_stream, G_TYPE_IO_STREAM); G_DEFINE_TYPE (WockyLoopbackInputStream, wocky_loopback_input_stream, G_TYPE_INPUT_STREAM); G_DEFINE_TYPE (WockyLoopbackOutputStream, wocky_loopback_output_stream, G_TYPE_OUTPUT_STREAM); #define WOCKY_TYPE_LOOPBACK_INPUT_STREAM (wocky_loopback_input_stream_get_type ()) #define WOCKY_TYPE_LOOPBACK_OUTPUT_STREAM (wocky_loopback_output_stream_get_type ()) #define WOCKY_LOOPBACK_INPUT_STREAM(inst) (G_TYPE_CHECK_INSTANCE_CAST ((inst), \ WOCKY_TYPE_LOOPBACK_INPUT_STREAM, \ WockyLoopbackInputStream)) #define WOCKY_LOOPBACK_OUTPUT_STREAM(inst) (G_TYPE_CHECK_INSTANCE_CAST ((inst), \ WOCKY_TYPE_LOOPBACK_OUTPUT_STREAM, \ WockyLoopbackOutputStream)) static gboolean wocky_loopback_input_stream_try_read (WockyLoopbackInputStream *self); static void output_data_written_cb (GOutputStream *output, WockyLoopbackInputStream *input_stream) { wocky_loopback_input_stream_try_read (input_stream); } /* connection */ static void wocky_loopback_stream_init (WockyLoopbackStream *self) { WockyLoopbackStreamPrivate *priv; self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, WOCKY_TYPE_LOOPBACK_STREAM, WockyLoopbackStreamPrivate); priv = self->priv; priv->output = g_object_new (WOCKY_TYPE_LOOPBACK_OUTPUT_STREAM, NULL); priv->input = g_object_new (WOCKY_TYPE_LOOPBACK_INPUT_STREAM, NULL); WOCKY_LOOPBACK_INPUT_STREAM (priv->input)->queue = g_async_queue_ref ( WOCKY_LOOPBACK_OUTPUT_STREAM (priv->output)->queue); g_signal_connect (priv->output, "data-written", G_CALLBACK (output_data_written_cb), priv->input); } static void wocky_loopback_stream_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { WockyLoopbackStream *self = WOCKY_LOOPBACK_STREAM (object); WockyLoopbackStreamPrivate *priv = self->priv; switch (property_id) { case PROP_IO_INPUT_STREAM: g_value_set_object (value, priv->input); break; case PROP_IO_OUTPUT_STREAM: g_value_set_object (value, priv->output); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void wocky_loopback_stream_dispose (GObject *object) { WockyLoopbackStream *self = WOCKY_LOOPBACK_STREAM (object); WockyLoopbackStreamPrivate *priv = self->priv; if (G_OBJECT_CLASS (wocky_loopback_stream_parent_class)->dispose) G_OBJECT_CLASS (wocky_loopback_stream_parent_class)->dispose (object); g_object_unref (priv->input); g_object_unref (priv->output); } static GInputStream * wocky_loopback_stream_get_input_stream (GIOStream *stream) { return WOCKY_LOOPBACK_STREAM (stream)->priv->input; } static GOutputStream * wocky_loopback_stream_get_output_stream (GIOStream *stream) { return WOCKY_LOOPBACK_STREAM (stream)->priv->output; } static void wocky_loopback_stream_class_init ( WockyLoopbackStreamClass *wocky_loopback_stream_class) { GObjectClass *obj_class = G_OBJECT_CLASS (wocky_loopback_stream_class); GIOStreamClass *stream_class = G_IO_STREAM_CLASS ( wocky_loopback_stream_class); g_type_class_add_private (wocky_loopback_stream_class, sizeof (WockyLoopbackStreamPrivate)); obj_class->dispose = wocky_loopback_stream_dispose; obj_class->get_property = wocky_loopback_stream_get_property; stream_class->get_input_stream = wocky_loopback_stream_get_input_stream; stream_class->get_output_stream = wocky_loopback_stream_get_output_stream; g_object_class_install_property (obj_class, PROP_IO_INPUT_STREAM, g_param_spec_object ("input-stream", "Input stream", "the input stream", G_TYPE_INPUT_STREAM, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (obj_class, PROP_IO_OUTPUT_STREAM, g_param_spec_object ("output-stream", "Output stream", "the output stream", G_TYPE_OUTPUT_STREAM, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); } GIOStream * wocky_loopback_stream_new (void) { return g_object_new (WOCKY_TYPE_LOOPBACK_STREAM, NULL); } /* Input stream */ static gssize wocky_loopback_input_stream_read (GInputStream *stream, void *buffer, gsize count, GCancellable *cancellable, GError **error) { WockyLoopbackInputStream *self = WOCKY_LOOPBACK_INPUT_STREAM (stream); gsize written = 0; if (self->out_array == NULL) { g_assert (self->offset == 0); self->out_array = g_async_queue_pop (self->queue); } do { gsize towrite; if (self->offset == 0) { towrite = MIN (count - written, MAX (self->out_array->len/2, 1)); } else { towrite = MIN (count - written, self->out_array->len - self->offset); } memcpy ((guchar *) buffer + written, self->out_array->data + self->offset, towrite); self->offset += towrite; written += towrite; if (self->offset == self->out_array->len) { g_array_unref (self->out_array); self->out_array = g_async_queue_try_pop (self->queue); self->offset = 0; } else { break; } } while (written < count && self->out_array != NULL); return written; } static void read_async_complete (WockyLoopbackInputStream *self) { GSimpleAsyncResult *r = self->read_result; if (self->read_cancellable != NULL) { g_signal_handler_disconnect (self->read_cancellable, self->read_cancellable_sig_id); g_object_unref (self->read_cancellable); self->read_cancellable = NULL; } self->read_result = NULL; g_simple_async_result_complete_in_idle (r); g_object_unref (r); } static void read_cancelled_cb (GCancellable *cancellable, WockyLoopbackInputStream *self) { g_simple_async_result_set_error (self->read_result, G_IO_ERROR, G_IO_ERROR_CANCELLED, "Reading cancelled"); self->buffer = NULL; read_async_complete (self); } static void wocky_loopback_input_stream_read_async (GInputStream *stream, void *buffer, gsize count, int io_priority, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { WockyLoopbackInputStream *self = WOCKY_LOOPBACK_INPUT_STREAM (stream); g_assert (self->buffer == NULL); g_assert (self->read_result == NULL); g_assert (self->read_cancellable == NULL); self->buffer = buffer; self->count = count; self->read_result = g_simple_async_result_new (G_OBJECT (stream), callback, user_data, wocky_loopback_input_stream_read_async); if (self->read_error != NULL) { g_simple_async_result_set_from_error (self->read_result, self->read_error); g_error_free (self->read_error); self->read_error = NULL; read_async_complete (self); return; } if (cancellable != NULL) { self->read_cancellable = g_object_ref (cancellable); self->read_cancellable_sig_id = g_signal_connect (cancellable, "cancelled", G_CALLBACK (read_cancelled_cb), self); } wocky_loopback_input_stream_try_read (self); } static gssize wocky_loopback_input_stream_read_finish (GInputStream *stream, GAsyncResult *result, GError **error) { WockyLoopbackInputStream *self = WOCKY_LOOPBACK_INPUT_STREAM (stream); gssize len = -1; if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), error)) goto out; g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (self), wocky_loopback_input_stream_read_async), -1); len = wocky_loopback_input_stream_read (stream, self->buffer, self->count, NULL, error); out: self->buffer = NULL; return len; } static gboolean wocky_loopback_input_stream_try_read (WockyLoopbackInputStream *self) { if (self->read_result == NULL) /* No pending read operation */ return FALSE; if (self->out_array == NULL && g_async_queue_length (self->queue) == 0) return FALSE; read_async_complete (self); return TRUE; } static void wocky_loopback_input_stream_init (WockyLoopbackInputStream *self) { } static void wocky_loopback_input_stream_dispose (GObject *object) { WockyLoopbackInputStream *self = WOCKY_LOOPBACK_INPUT_STREAM (object); if (self->dispose_has_run) return; self->dispose_has_run = TRUE; if (self->out_array != NULL) g_array_unref (self->out_array); self->out_array = NULL; if (self->queue != NULL) g_async_queue_unref (self->queue); self->queue = NULL; g_warn_if_fail (self->read_result == NULL); g_warn_if_fail (self->read_cancellable == NULL); /* release any references held by the object here */ if (G_OBJECT_CLASS (wocky_loopback_input_stream_parent_class)->dispose) G_OBJECT_CLASS (wocky_loopback_input_stream_parent_class)->dispose (object); } static void wocky_loopback_input_stream_class_init ( WockyLoopbackInputStreamClass *wocky_loopback_input_stream_class) { GObjectClass *obj_class = G_OBJECT_CLASS (wocky_loopback_input_stream_class); GInputStreamClass *stream_class = G_INPUT_STREAM_CLASS (wocky_loopback_input_stream_class); obj_class->dispose = wocky_loopback_input_stream_dispose; stream_class->read_fn = wocky_loopback_input_stream_read; stream_class->read_async = wocky_loopback_input_stream_read_async; stream_class->read_finish = wocky_loopback_input_stream_read_finish; } /* Output stream */ enum { OUTPUT_DATA_WRITTEN, LAST_SIGNAL }; static guint output_signals[LAST_SIGNAL] = {0}; static gssize wocky_loopback_output_stream_write (GOutputStream *stream, const void *buffer, gsize count, GCancellable *cancellable, GError **error) { WockyLoopbackOutputStream *self = WOCKY_LOOPBACK_OUTPUT_STREAM (stream); GArray *data; data = g_array_sized_new (FALSE, FALSE, sizeof (guint8), count); g_array_insert_vals (data, 0, buffer, count); g_async_queue_push (self->queue, data); g_signal_emit (self, output_signals[OUTPUT_DATA_WRITTEN], 0); return count; } static void wocky_loopback_output_stream_write_async (GOutputStream *stream, const void *buffer, gsize count, int io_priority, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { GSimpleAsyncResult *simple; GError *error = NULL; gssize result; result = wocky_loopback_output_stream_write (stream, buffer, count, cancellable, &error); simple = g_simple_async_result_new (G_OBJECT (stream), callback, user_data, wocky_loopback_output_stream_write_async); if (result == -1) { g_simple_async_result_set_from_error (simple, error); g_error_free (error); } else { g_simple_async_result_set_op_res_gssize (simple, result); } g_simple_async_result_complete_in_idle (simple); g_object_unref (simple); } static gssize wocky_loopback_output_stream_write_finish (GOutputStream *stream, GAsyncResult *result, GError **error) { if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), error)) return -1; g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (stream), wocky_loopback_output_stream_write_async), -1); return g_simple_async_result_get_op_res_gssize ( G_SIMPLE_ASYNC_RESULT (result)); } static void wocky_loopback_output_stream_dispose (GObject *object) { WockyLoopbackOutputStream *self = WOCKY_LOOPBACK_OUTPUT_STREAM (object); if (self->dispose_has_run) return; self->dispose_has_run = TRUE; g_async_queue_push (self->queue, g_array_sized_new (FALSE, FALSE, sizeof (guint8), 0)); g_async_queue_unref (self->queue); /* release any references held by the object here */ if (G_OBJECT_CLASS (wocky_loopback_output_stream_parent_class)->dispose) G_OBJECT_CLASS (wocky_loopback_output_stream_parent_class)->dispose (object); } static void queue_destroyed (gpointer data) { g_array_free ((GArray *) data, TRUE); } static void wocky_loopback_output_stream_init (WockyLoopbackOutputStream *self) { self->queue = g_async_queue_new_full (queue_destroyed); } static void wocky_loopback_output_stream_class_init ( WockyLoopbackOutputStreamClass *wocky_loopback_output_stream_class) { GObjectClass *obj_class = G_OBJECT_CLASS (wocky_loopback_output_stream_class); GOutputStreamClass *stream_class = G_OUTPUT_STREAM_CLASS (wocky_loopback_output_stream_class); obj_class->dispose = wocky_loopback_output_stream_dispose; stream_class->write_fn = wocky_loopback_output_stream_write; stream_class->write_async = wocky_loopback_output_stream_write_async; stream_class->write_finish = wocky_loopback_output_stream_write_finish; output_signals[OUTPUT_DATA_WRITTEN] = g_signal_new ("data-written", G_OBJECT_CLASS_TYPE(wocky_loopback_output_stream_class), G_SIGNAL_RUN_LAST, 0, NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); } telepathy-gabble-0.18.2/lib/ext/wocky/wocky/wocky-ll-contact.c0000644000175000017500000001531612200204546024212 0ustar00smcvsmcv00000000000000/* * wocky-ll-contact.c - Source for WockyLLContact * Copyright (C) 2011 Collabora Ltd. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 */ /** * SECTION: wocky-ll-contact * @title: WockyLLContact * @short_description: Wrapper around a link-local contact. * @include: wocky/wocky-ll-contact.h * */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "wocky-ll-contact.h" #include #include "wocky-utils.h" G_DEFINE_TYPE (WockyLLContact, wocky_ll_contact, WOCKY_TYPE_CONTACT) /* properties */ enum { PROP_JID = 1, }; /* signal enum */ enum { LAST_SIGNAL, }; /* private structure */ struct _WockyLLContactPrivate { gboolean dispose_has_run; gchar *jid; }; static void wocky_ll_contact_init (WockyLLContact *self) { self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, WOCKY_TYPE_LL_CONTACT, WockyLLContactPrivate); } static void wocky_ll_contact_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { WockyLLContact *self = WOCKY_LL_CONTACT (object); WockyLLContactPrivate *priv = self->priv; switch (property_id) { case PROP_JID: priv->jid = g_value_dup_string (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void wocky_ll_contact_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { WockyLLContact *self = WOCKY_LL_CONTACT (object); WockyLLContactPrivate *priv = self->priv; switch (property_id) { case PROP_JID: g_value_set_string (value, priv->jid); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void wocky_ll_contact_constructed (GObject *object) { WockyLLContact *self = WOCKY_LL_CONTACT (object); g_assert (self->priv->jid != NULL); } static void wocky_ll_contact_finalize (GObject *object) { WockyLLContact *self = WOCKY_LL_CONTACT (object); WockyLLContactPrivate *priv = self->priv; if (priv->jid != NULL) g_free (priv->jid); G_OBJECT_CLASS (wocky_ll_contact_parent_class)->finalize (object); } static gchar * ll_contact_dup_jid (WockyContact *contact) { return g_strdup (wocky_ll_contact_get_jid (WOCKY_LL_CONTACT (contact))); } static void wocky_ll_contact_class_init (WockyLLContactClass *wocky_ll_contact_class) { GObjectClass *object_class = G_OBJECT_CLASS (wocky_ll_contact_class); WockyContactClass *contact_class = WOCKY_CONTACT_CLASS (wocky_ll_contact_class); GParamSpec *spec; g_type_class_add_private (wocky_ll_contact_class, sizeof (WockyLLContactPrivate)); object_class->constructed = wocky_ll_contact_constructed; object_class->set_property = wocky_ll_contact_set_property; object_class->get_property = wocky_ll_contact_get_property; object_class->finalize = wocky_ll_contact_finalize; contact_class->dup_jid = ll_contact_dup_jid; /** * WockyLLContact:jid: * * The contact's link-local JID. */ spec = g_param_spec_string ("jid", "Contact JID", "Contact JID", "", G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, PROP_JID, spec); } /** * wocky_ll_contact_new: * @jid: the JID of the contact to create * * Creates a new #WockyLLContact for a given JID. * * Returns: a newly constructed #WockyLLContact */ WockyLLContact * wocky_ll_contact_new (const gchar *jid) { return g_object_new (WOCKY_TYPE_LL_CONTACT, "jid", jid, NULL); } /** * wocky_ll_contact_get_jid: * @contact: a #WockyLLContact instance * * Returns the JID of the contact wrapped by @contact. * * Returns: @contact's JID. */ const gchar * wocky_ll_contact_get_jid (WockyLLContact *contact) { WockyLLContactPrivate *priv; g_return_val_if_fail (WOCKY_IS_LL_CONTACT (contact), NULL); priv = contact->priv; return priv->jid; } /** * wocky_ll_contact_equal: * @a: a #WockyLLContact instance * @b: a #WockyLLContact instance to compare with @a * * Compares whether two #WockyLLContact instances refer to the same * link-local contact. * * Returns: #TRUE if the two contacts match. */ gboolean wocky_ll_contact_equal (WockyLLContact *a, WockyLLContact *b) { if (a == NULL || b == NULL) return FALSE; if (wocky_strdiff (wocky_ll_contact_get_jid (a), wocky_ll_contact_get_jid (b))) return FALSE; return TRUE; } /** * wocky_ll_contact_get_addresses: * @self: a #WockyLLContact * * Returns a #GList of #GInetSocketAddresses which are * advertised by the contact @self as addresses to connect on. Note * that the #GInetSocketAddresses should be unreffed by calling * g_object_unref() on each list member and the list freed using * g_list_free() when the caller is finished. * * Returns: (element-type GInetSocketAddress) (transfer full): a new * #GList of #GInetSocketAddresses. */ GList * wocky_ll_contact_get_addresses (WockyLLContact *self) { WockyLLContactClass *cls; g_return_val_if_fail (WOCKY_IS_LL_CONTACT (self), NULL); cls = WOCKY_LL_CONTACT_GET_CLASS (self); if (cls->get_addresses != NULL) return cls->get_addresses (self); return NULL; } /** * wocky_ll_contact_has_address: * @self: a #WockyLLContact * @address: a #GInetAddress * * Checks whether @address relates to the contact @self. * * Returns: %TRUE if @address relates to the contact @self, otherwise * %FALSE */ gboolean wocky_ll_contact_has_address (WockyLLContact *self, GInetAddress *address) { gchar *s = g_inet_address_to_string (address); gboolean ret = FALSE; GList *l, *addresses = wocky_ll_contact_get_addresses (self); for (l = addresses; l != NULL; l = l->next) { GInetAddress *a = g_inet_socket_address_get_address ( G_INET_SOCKET_ADDRESS (l->data)); gchar *tmp = g_inet_address_to_string (a); if (!wocky_strdiff (tmp, s)) ret = TRUE; g_free (tmp); if (ret) break; } g_list_foreach (addresses, (GFunc) g_object_unref, NULL); g_list_free (addresses); g_free (s); return ret; } telepathy-gabble-0.18.2/lib/ext/wocky/wocky/wocky-ll-connector.c0000644000175000017500000003536712200204546024561 0ustar00smcvsmcv00000000000000/* * wocky-ll-connector.c - Source for WockyLLConnector * Copyright (C) 2011 Collabora Ltd. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include "wocky-ll-connector.h" #include "wocky-utils.h" #include "wocky-namespaces.h" #define WOCKY_DEBUG_FLAG WOCKY_DEBUG_CONNECTOR #include "wocky-debug-internal.h" static void initable_iface_init (gpointer, gpointer); G_DEFINE_TYPE_WITH_CODE (WockyLLConnector, wocky_ll_connector, G_TYPE_OBJECT, G_IMPLEMENT_INTERFACE (G_TYPE_ASYNC_INITABLE, initable_iface_init)) enum { PROP_STREAM = 1, PROP_CONNECTION, PROP_LOCAL_JID, PROP_REMOTE_JID, PROP_INCOMING, }; /* private structure */ struct _WockyLLConnectorPrivate { GIOStream *stream; WockyXmppConnection *connection; gchar *local_jid; gchar *remote_jid; gboolean incoming; gchar *from; GSimpleAsyncResult *simple; GCancellable *cancellable; }; GQuark wocky_ll_connector_error_quark (void) { static GQuark quark = 0; if (!quark) quark = g_quark_from_static_string ( "wocky_ll_connector_error"); return quark; } static void wocky_ll_connector_init (WockyLLConnector *self) { self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, WOCKY_TYPE_LL_CONNECTOR, WockyLLConnectorPrivate); } static void wocky_ll_connector_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { WockyLLConnector *connector = WOCKY_LL_CONNECTOR (object); WockyLLConnectorPrivate *priv = connector->priv; switch (property_id) { case PROP_STREAM: priv->stream = g_value_get_object (value); break; case PROP_CONNECTION: priv->connection = g_value_get_object (value); break; case PROP_LOCAL_JID: priv->local_jid = g_value_dup_string (value); break; case PROP_REMOTE_JID: priv->remote_jid = g_value_dup_string (value); break; case PROP_INCOMING: priv->incoming = g_value_get_boolean (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void wocky_ll_connector_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { WockyLLConnector *connector = WOCKY_LL_CONNECTOR (object); WockyLLConnectorPrivate *priv = connector->priv; switch (property_id) { case PROP_STREAM: g_value_set_object (value, priv->stream); break; case PROP_CONNECTION: g_value_set_object (value, priv->connection); break; case PROP_LOCAL_JID: g_value_set_string (value, priv->local_jid); break; case PROP_REMOTE_JID: g_value_set_string (value, priv->remote_jid); break; case PROP_INCOMING: g_value_set_boolean (value, priv->incoming); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void wocky_ll_connector_dispose (GObject *object) { WockyLLConnector *self = WOCKY_LL_CONNECTOR (object); WockyLLConnectorPrivate *priv = self->priv; DEBUG ("dispose called"); g_object_unref (priv->connection); priv->connection = NULL; g_free (priv->local_jid); priv->local_jid = NULL; g_free (priv->remote_jid); priv->remote_jid = NULL; g_free (priv->from); priv->from = NULL; if (priv->cancellable != NULL) { g_object_unref (priv->cancellable); priv->cancellable = NULL; } if (G_OBJECT_CLASS (wocky_ll_connector_parent_class)->dispose) G_OBJECT_CLASS (wocky_ll_connector_parent_class)->dispose (object); } static void wocky_ll_connector_constructed (GObject *object) { WockyLLConnector *self = WOCKY_LL_CONNECTOR (object); WockyLLConnectorPrivate *priv = self->priv; if (G_OBJECT_CLASS (wocky_ll_connector_parent_class)->constructed) G_OBJECT_CLASS (wocky_ll_connector_parent_class)->constructed (object); if (priv->connection == NULL) priv->connection = wocky_xmpp_connection_new (priv->stream); } static void wocky_ll_connector_class_init ( WockyLLConnectorClass *wocky_ll_connector_class) { GObjectClass *object_class = G_OBJECT_CLASS (wocky_ll_connector_class); GParamSpec *spec; object_class->get_property = wocky_ll_connector_get_property; object_class->set_property = wocky_ll_connector_set_property; object_class->dispose = wocky_ll_connector_dispose; object_class->constructed = wocky_ll_connector_constructed; spec = g_param_spec_object ("stream", "XMPP stream", "The XMPP stream", G_TYPE_IO_STREAM, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, PROP_STREAM, spec); spec = g_param_spec_object ("connection", "XMPP connection", "The XMPP connection", WOCKY_TYPE_XMPP_CONNECTION, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, PROP_CONNECTION, spec); spec = g_param_spec_string ("local-jid", "User's JID", "Local user's XMPP JID", "", G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, PROP_LOCAL_JID, spec); spec = g_param_spec_string ("remote-jid", "Contact's JID", "Remote contact's XMPP JID", "", G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, PROP_REMOTE_JID, spec); spec = g_param_spec_boolean ("incoming", "Incoming", "Whether the connection is incoming", FALSE, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, PROP_INCOMING, spec); g_type_class_add_private (wocky_ll_connector_class, sizeof (WockyLLConnectorPrivate)); } /** * wocky_ll_connector_incoming_async: * @stream: a #GIOStream * @cancellable: an optional #GCancellable, or %NULL * @callback: a function to call when the operation is complete * @user_data: data to pass to @callback * * Start an asychronous connect operation with an incoming link-local * connection by negotiating the stream open stanzas and sending * stream features. * * The ownership of @stream is taken by the connector. * * When the operation is complete, @callback will be called and it * should call wocky_ll_connector_finish(). */ void wocky_ll_connector_incoming_async ( GIOStream *stream, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { g_async_initable_new_async (WOCKY_TYPE_LL_CONNECTOR, G_PRIORITY_DEFAULT, cancellable, callback, user_data, "stream", stream, "incoming", TRUE, NULL); } /** * wocky_ll_connector_outgoing_async: * @connection: a #WockyXmppConnection * @local_jid: the JID of the local user * @remote_jid: the JID of the remote contact * @cancellable: an optional #GCancellable, or %NULL * @callback: a function to call when the operation is complete * @user_data: data to pass to @callback * * Start an asychronous connect operation with an outgoing link-local * connection by negotiating the stream open stanzas and sending * stream features. * * The ownership of @connection is taken by the connector. * * When the operation is complete, @callback will be called and it * should call wocky_ll_connector_finish(). */ void wocky_ll_connector_outgoing_async ( WockyXmppConnection *connection, const gchar *local_jid, const gchar *remote_jid, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { g_async_initable_new_async (WOCKY_TYPE_LL_CONNECTOR, G_PRIORITY_DEFAULT, cancellable, callback, user_data, "connection", connection, "local-jid", local_jid, "remote-jid", remote_jid, "incoming", FALSE, NULL); } static void features_sent_cb (GObject *source_object, GAsyncResult *result, gpointer user_data) { WockyXmppConnection *connection = WOCKY_XMPP_CONNECTION (source_object); WockyLLConnector *self = user_data; WockyLLConnectorPrivate *priv = self->priv; GError *error = NULL; if (!wocky_xmpp_connection_send_stanza_finish (connection, result, &error)) { DEBUG ("Failed to send stream features: %s", error->message); g_simple_async_result_set_error (priv->simple, WOCKY_LL_CONNECTOR_ERROR, WOCKY_LL_CONNECTOR_ERROR_FAILED_TO_SEND_STANZA, "Failed to send stream features: %s", error->message); g_clear_error (&error); } g_simple_async_result_complete (priv->simple); g_object_unref (priv->simple); priv->simple = NULL; g_object_unref (self); } static void send_open_cb (GObject *source_object, GAsyncResult *result, gpointer user_data); static void recv_open_cb (GObject *source_object, GAsyncResult *result, gpointer user_data) { WockyXmppConnection *connection = WOCKY_XMPP_CONNECTION (source_object); GError *error = NULL; WockyLLConnector *self = user_data; WockyLLConnectorPrivate *priv = self->priv; gchar *from = NULL; if (!wocky_xmpp_connection_recv_open_finish (connection, result, NULL, &from, NULL, NULL, NULL, &error)) { DEBUG ("Failed to receive stream open: %s", error->message); g_simple_async_result_set_error (priv->simple, WOCKY_LL_CONNECTOR_ERROR, WOCKY_LL_CONNECTOR_ERROR_FAILED_TO_RECEIVE_STANZA, "Failed to receive stream open: %s", error->message); g_clear_error (&error); g_simple_async_result_complete (priv->simple); g_object_unref (priv->simple); priv->simple = NULL; return; } if (!priv->incoming) { WockyStanza *features; DEBUG ("connected, sending stream features but not " "expecting anything back"); features = wocky_stanza_new ("features", WOCKY_XMPP_NS_STREAM); wocky_xmpp_connection_send_stanza_async (connection, features, NULL, features_sent_cb, self); g_object_unref (features); } else { DEBUG ("stream opened from %s, sending open back", from != NULL ? from : ""); wocky_xmpp_connection_send_open_async (connection, from, priv->local_jid, "1.0", NULL, NULL, priv->cancellable, send_open_cb, self); } priv->from = from; } static void send_open_cb (GObject *source_object, GAsyncResult *result, gpointer user_data) { WockyXmppConnection *connection = WOCKY_XMPP_CONNECTION (source_object); GError *error = NULL; WockyLLConnector *self = user_data; WockyLLConnectorPrivate *priv = self->priv; if (!wocky_xmpp_connection_send_open_finish (connection, result, &error)) { DEBUG ("Failed to send stream open: %s", error->message); g_simple_async_result_set_error (priv->simple, WOCKY_LL_CONNECTOR_ERROR, WOCKY_LL_CONNECTOR_ERROR_FAILED_TO_SEND_STANZA, "Failed to send stream open: %s", error->message); g_clear_error (&error); g_simple_async_result_complete (priv->simple); g_object_unref (priv->simple); priv->simple = NULL; return; } if (!priv->incoming) { DEBUG ("successfully sent stream open, now waiting for other side to too"); wocky_xmpp_connection_recv_open_async (connection, priv->cancellable, recv_open_cb, self); } else { WockyStanza *features; DEBUG ("connected, sending stream features but not " "expecting anything back"); features = wocky_stanza_new ("features", WOCKY_XMPP_NS_STREAM); wocky_xmpp_connection_send_stanza_async (connection, features, NULL, features_sent_cb, self); g_object_unref (features); } } /** * wocky_ll_connector_finish: * @connector: a #WockyLLConnector * @result: a #GAsyncResult * @from: a location to store the remote user's JID, or %NULL * @error: a location to save errors to, or %NULL to ignore * * Gets the result of the asynchronous connect request. * * Returns: the connected #WockyXmppConnection which should be freed * using g_object_unref(), or %NULL on error */ WockyXmppConnection * wocky_ll_connector_finish (WockyLLConnector *self, GAsyncResult *result, gchar **from, GError **error) { WockyLLConnectorPrivate *priv = self->priv; if (g_async_initable_new_finish (G_ASYNC_INITABLE (self), result, error) == NULL) return NULL; if (from != NULL) *from = g_strdup (priv->from); return g_object_ref (priv->connection); } static void wocky_ll_connector_init_async (GAsyncInitable *initable, int io_priority, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { WockyLLConnector *self = WOCKY_LL_CONNECTOR (initable); WockyLLConnectorPrivate *priv = self->priv; g_return_if_fail (priv->simple == NULL); priv->simple = g_simple_async_result_new (G_OBJECT (self), callback, user_data, wocky_ll_connector_init_async); if (cancellable != NULL) priv->cancellable = g_object_ref (cancellable); if (priv->incoming) { /* we need to wait for stream open first */ wocky_xmpp_connection_recv_open_async (priv->connection, priv->cancellable, recv_open_cb, self); } else { /* we need to send stream open first */ wocky_xmpp_connection_send_open_async (priv->connection, priv->remote_jid, priv->local_jid, "1.0", NULL, NULL, priv->cancellable, send_open_cb, self); } } static gboolean wocky_ll_connector_init_finish (GAsyncInitable *initable, GAsyncResult *result, GError **error) { WockyLLConnector *self = WOCKY_LL_CONNECTOR (initable); GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (result); WockyLLConnectorPrivate *priv = self->priv; g_return_val_if_fail (priv->simple == simple, FALSE); if (g_simple_async_result_propagate_error (simple, error)) return FALSE; g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (self), wocky_ll_connector_init_async), FALSE); return TRUE; } static void initable_iface_init (gpointer g_iface, gpointer data) { GAsyncInitableIface *iface = g_iface; iface->init_async = wocky_ll_connector_init_async; iface->init_finish = wocky_ll_connector_init_finish; } telepathy-gabble-0.18.2/lib/ext/wocky/wocky/wocky-jingle-transport-rawudp.c0000644000175000017500000003014112200204546026745 0ustar00smcvsmcv00000000000000/* * wocky-jingle-transport-rawudp.c - Source for WockyJingleTransportRawUdp * * Copyright (C) 2008 Collabora Ltd. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 "config.h" #include "wocky-jingle-transport-rawudp.h" #include #include #include #include #define WOCKY_DEBUG_FLAG WOCKY_DEBUG_JINGLE #include "wocky-debug-internal.h" #include "wocky-jingle-content.h" #include "wocky-jingle-factory.h" #include "wocky-jingle-session.h" #include "wocky-namespaces.h" static void transport_iface_init (gpointer g_iface, gpointer iface_data); G_DEFINE_TYPE_WITH_CODE (WockyJingleTransportRawUdp, wocky_jingle_transport_rawudp, G_TYPE_OBJECT, G_IMPLEMENT_INTERFACE (WOCKY_TYPE_JINGLE_TRANSPORT_IFACE, transport_iface_init)); /* signal enum */ enum { NEW_CANDIDATES, LAST_SIGNAL }; static guint signals[LAST_SIGNAL] = {0}; /* properties */ enum { PROP_CONTENT = 1, PROP_TRANSPORT_NS, PROP_STATE, LAST_PROPERTY }; struct _WockyJingleTransportRawUdpPrivate { WockyJingleContent *content; WockyJingleTransportState state; gchar *transport_ns; GList *local_candidates; GList *remote_candidates; gboolean dispose_has_run; }; static void wocky_jingle_transport_rawudp_init (WockyJingleTransportRawUdp *obj) { WockyJingleTransportRawUdpPrivate *priv = G_TYPE_INSTANCE_GET_PRIVATE (obj, WOCKY_TYPE_JINGLE_TRANSPORT_RAWUDP, WockyJingleTransportRawUdpPrivate); obj->priv = priv; priv->dispose_has_run = FALSE; } static void wocky_jingle_transport_rawudp_dispose (GObject *object) { WockyJingleTransportRawUdp *trans = WOCKY_JINGLE_TRANSPORT_RAWUDP (object); WockyJingleTransportRawUdpPrivate *priv = trans->priv; if (priv->dispose_has_run) return; DEBUG ("dispose called"); priv->dispose_has_run = TRUE; jingle_transport_free_candidates (priv->remote_candidates); priv->remote_candidates = NULL; jingle_transport_free_candidates (priv->local_candidates); priv->local_candidates = NULL; g_free (priv->transport_ns); priv->transport_ns = NULL; if (G_OBJECT_CLASS (wocky_jingle_transport_rawudp_parent_class)->dispose) G_OBJECT_CLASS (wocky_jingle_transport_rawudp_parent_class)->dispose (object); } static void wocky_jingle_transport_rawudp_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { WockyJingleTransportRawUdp *trans = WOCKY_JINGLE_TRANSPORT_RAWUDP (object); WockyJingleTransportRawUdpPrivate *priv = trans->priv; switch (property_id) { case PROP_CONTENT: g_value_set_object (value, priv->content); break; case PROP_TRANSPORT_NS: g_value_set_string (value, priv->transport_ns); break; case PROP_STATE: g_value_set_uint (value, priv->state); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void wocky_jingle_transport_rawudp_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { WockyJingleTransportRawUdp *trans = WOCKY_JINGLE_TRANSPORT_RAWUDP (object); WockyJingleTransportRawUdpPrivate *priv = trans->priv; switch (property_id) { case PROP_CONTENT: priv->content = g_value_get_object (value); break; case PROP_TRANSPORT_NS: g_free (priv->transport_ns); priv->transport_ns = g_value_dup_string (value); break; case PROP_STATE: priv->state = g_value_get_uint (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void wocky_jingle_transport_rawudp_class_init (WockyJingleTransportRawUdpClass *cls) { GObjectClass *object_class = G_OBJECT_CLASS (cls); GParamSpec *param_spec; g_type_class_add_private (cls, sizeof (WockyJingleTransportRawUdpPrivate)); object_class->get_property = wocky_jingle_transport_rawudp_get_property; object_class->set_property = wocky_jingle_transport_rawudp_set_property; object_class->dispose = wocky_jingle_transport_rawudp_dispose; /* property definitions */ param_spec = g_param_spec_object ("content", "WockyJingleContent object", "Jingle content object using this transport.", WOCKY_TYPE_JINGLE_CONTENT, G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB); g_object_class_install_property (object_class, PROP_CONTENT, param_spec); param_spec = g_param_spec_string ("transport-ns", "Transport namespace", "Namespace identifying the transport type.", NULL, G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB); g_object_class_install_property (object_class, PROP_TRANSPORT_NS, param_spec); param_spec = g_param_spec_uint ("state", "Connection state for the transport.", "Enum specifying the connection state of the transport.", WOCKY_JINGLE_TRANSPORT_STATE_DISCONNECTED, WOCKY_JINGLE_TRANSPORT_STATE_CONNECTED, WOCKY_JINGLE_TRANSPORT_STATE_DISCONNECTED, G_PARAM_READWRITE | G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB); g_object_class_install_property (object_class, PROP_STATE, param_spec); /* signal definitions */ signals[NEW_CANDIDATES] = g_signal_new ( "new-candidates", G_TYPE_FROM_CLASS (cls), G_SIGNAL_RUN_LAST, 0, NULL, NULL, g_cclosure_marshal_VOID__POINTER, G_TYPE_NONE, 1, G_TYPE_POINTER); } static void parse_candidates (WockyJingleTransportIface *obj, WockyNode *transport_node, GError **error) { WockyJingleTransportRawUdp *t = WOCKY_JINGLE_TRANSPORT_RAWUDP (obj); WockyJingleTransportRawUdpPrivate *priv = t->priv; GList *candidates = NULL; WockyNodeIter i; WockyNode *node; DEBUG ("called"); if (priv->remote_candidates != NULL) { DEBUG ("already have raw udp candidates, ignoring extra ones"); return; } wocky_node_iter_init (&i, transport_node, "candidate", NULL); while (wocky_node_iter_next (&i, &node)) { const gchar *id, *ip, *str; guint port, gen, component = 1; WockyJingleCandidate *c; str = wocky_node_get_attribute (node, "component"); if (str != NULL) component = atoi (str); if ((component != 1) && (component != 2)) { DEBUG ("Ignoring non-RTP/RTCP component %d", component); continue; } id = wocky_node_get_attribute (node, "id"); if (id == NULL) break; ip = wocky_node_get_attribute (node, "ip"); if (ip == NULL) break; str = wocky_node_get_attribute (node, "port"); if (str == NULL) break; port = atoi (str); str = wocky_node_get_attribute (node, "generation"); if (str == NULL) break; gen = atoi (str); c = wocky_jingle_candidate_new (WOCKY_JINGLE_TRANSPORT_PROTOCOL_UDP, WOCKY_JINGLE_CANDIDATE_TYPE_LOCAL, id, component, ip, port, gen, 1.0, NULL, NULL, 0); candidates = g_list_append (candidates, c); } if (wocky_node_iter_next (&i, NULL)) { DEBUG ("not all nodes were processed, reporting error"); /* rollback these */ jingle_transport_free_candidates (candidates); g_set_error (error, WOCKY_XMPP_ERROR, WOCKY_XMPP_ERROR_BAD_REQUEST, "invalid candidate"); return; } DEBUG ("emitting %d new remote candidates", g_list_length (candidates)); g_signal_emit (obj, signals[NEW_CANDIDATES], 0, candidates); priv->remote_candidates = candidates; } static void inject_candidates (WockyJingleTransportIface *obj, WockyNode *transport_node) { WockyJingleTransportRawUdp *self = WOCKY_JINGLE_TRANSPORT_RAWUDP (obj); WockyJingleTransportRawUdpPrivate *priv = self->priv; WockyJingleCandidate *c; GList *li; gchar port_str[16], comp_str[16]; WockyNode *cnode; /* If we don't have the local candidates yet, we should've waited with * the session initiation, or can_accept would have returned FALSE. */ g_assert (priv->local_candidates != NULL); for (li = priv->local_candidates; li != NULL; li = li->next) { c = (WockyJingleCandidate *) li->data; sprintf (port_str, "%d", c->port); sprintf (comp_str, "%d", c->component); cnode = wocky_node_add_child (transport_node, "candidate"); wocky_node_set_attributes (cnode, "ip", c->address, "port", port_str, "generation", "0", "id", c->id, "component", comp_str, NULL); } } /* Takes in a list of slice-allocated WockyJingleCandidate structs */ static void new_local_candidates (WockyJingleTransportIface *obj, GList *new_candidates) { WockyJingleTransportRawUdp *transport = WOCKY_JINGLE_TRANSPORT_RAWUDP (obj); WockyJingleTransportRawUdpPrivate *priv = transport->priv; if (priv->local_candidates != NULL) { DEBUG ("ignoring new local candidates for RAW UDP"); jingle_transport_free_candidates (new_candidates); return; } priv->local_candidates = new_candidates; } static gboolean can_accept (WockyJingleTransportIface *iface) { WockyJingleTransportRawUdp *self = WOCKY_JINGLE_TRANSPORT_RAWUDP (iface); return (self->priv->local_candidates != NULL); } static GList * get_local_candidates (WockyJingleTransportIface *iface) { WockyJingleTransportRawUdp *transport = WOCKY_JINGLE_TRANSPORT_RAWUDP (iface); WockyJingleTransportRawUdpPrivate *priv = transport->priv; return priv->local_candidates; } static GList * get_remote_candidates (WockyJingleTransportIface *iface) { WockyJingleTransportRawUdp *transport = WOCKY_JINGLE_TRANSPORT_RAWUDP (iface); WockyJingleTransportRawUdpPrivate *priv = transport->priv; return priv->remote_candidates; } static WockyJingleTransportType get_transport_type (void) { DEBUG ("called"); return JINGLE_TRANSPORT_RAW_UDP; } static void transport_iface_init (gpointer g_iface, gpointer iface_data) { WockyJingleTransportIfaceClass *klass = (WockyJingleTransportIfaceClass *) g_iface; klass->parse_candidates = parse_candidates; klass->new_local_candidates = new_local_candidates; klass->inject_candidates = inject_candidates; /* Not implementing _send: XEP-0177 says that the candidates live in * content-{add,accept}, not in transport-info. */ klass->can_accept = can_accept; klass->get_remote_candidates = get_remote_candidates; klass->get_local_candidates = get_local_candidates; klass->get_transport_type = get_transport_type; } void jingle_transport_rawudp_register (WockyJingleFactory *factory) { wocky_jingle_factory_register_transport (factory, WOCKY_XMPP_NS_JINGLE_TRANSPORT_RAWUDP, WOCKY_TYPE_JINGLE_TRANSPORT_RAWUDP); } telepathy-gabble-0.18.2/lib/ext/wocky/wocky/wocky-jingle-transport-iface.c0000644000175000017500000002001612213345474026524 0ustar00smcvsmcv00000000000000/* * wocky-jingle-transport-iface.c - Source for WockyJingleTransportIface * Copyright (C) 2007-2008 Collabora Ltd. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 "config.h" #include "wocky-jingle-transport-iface.h" #include #include "wocky-jingle-content.h" #include "wocky-jingle-session.h" WockyJingleTransportIface * wocky_jingle_transport_iface_new (GType type, WockyJingleContent *content, const gchar *transport_ns) { g_return_val_if_fail (g_type_is_a (type, WOCKY_TYPE_JINGLE_TRANSPORT_IFACE), NULL); return g_object_new (type, "content", content, "transport-ns", transport_ns, NULL); } void wocky_jingle_transport_iface_parse_candidates (WockyJingleTransportIface *self, WockyNode *node, GError **error) { void (*virtual_method)(WockyJingleTransportIface *, WockyNode *, GError **) = WOCKY_JINGLE_TRANSPORT_IFACE_GET_CLASS (self)->parse_candidates; g_assert (virtual_method != NULL); return virtual_method (self, node, error); } /* Takes in a list of slice-allocated WockyJingleCandidate structs */ void wocky_jingle_transport_iface_new_local_candidates (WockyJingleTransportIface *self, GList *candidates) { void (*virtual_method)(WockyJingleTransportIface *, GList *) = WOCKY_JINGLE_TRANSPORT_IFACE_GET_CLASS (self)->new_local_candidates; g_assert (virtual_method != NULL); virtual_method (self, candidates); } /* Inserts candidates into the given node, or equivalent, of a * session-initiate, session-accept, content-add or content-accept action. */ void wocky_jingle_transport_iface_inject_candidates ( WockyJingleTransportIface *self, WockyNode *transport_node) { void (*virtual_method)(WockyJingleTransportIface *, WockyNode *) = WOCKY_JINGLE_TRANSPORT_IFACE_GET_CLASS (self)->inject_candidates; if (virtual_method != NULL) virtual_method (self, transport_node); } /* Transmits outstanding or all candidates (if applicable and @all is set). */ void wocky_jingle_transport_iface_send_candidates ( WockyJingleTransportIface *self, gboolean all) { void (*virtual_method) (WockyJingleTransportIface *, gboolean) = WOCKY_JINGLE_TRANSPORT_IFACE_GET_CLASS (self)->send_candidates; if (virtual_method != NULL) virtual_method (self, all); } /* Returns TRUE if and only if @self has enough candidates to inject into a * {session,content}-accept, and is connected. */ gboolean wocky_jingle_transport_iface_can_accept (WockyJingleTransportIface *self) { WockyJingleTransportState state; gboolean (*m) (WockyJingleTransportIface *) = WOCKY_JINGLE_TRANSPORT_IFACE_GET_CLASS (self)->can_accept; g_object_get (self, "state", &state, NULL); if (state != WOCKY_JINGLE_TRANSPORT_STATE_CONNECTED) return FALSE; /* Only Raw UDP *needs* candidates in order to accept. */ if (m != NULL) return m (self); else return TRUE; } GList * wocky_jingle_transport_iface_get_remote_candidates ( WockyJingleTransportIface *self) { GList * (*virtual_method)(WockyJingleTransportIface *) = WOCKY_JINGLE_TRANSPORT_IFACE_GET_CLASS (self)->get_remote_candidates; g_assert (virtual_method != NULL); return virtual_method (self); } GList * wocky_jingle_transport_iface_get_local_candidates ( WockyJingleTransportIface *self) { GList * (*virtual_method)(WockyJingleTransportIface *) = WOCKY_JINGLE_TRANSPORT_IFACE_GET_CLASS (self)->get_local_candidates; g_assert (virtual_method != NULL); return virtual_method (self); } gboolean jingle_transport_get_credentials (WockyJingleTransportIface *self, gchar **ufrag, gchar **pwd) { WockyJingleTransportIfaceClass *klass = WOCKY_JINGLE_TRANSPORT_IFACE_GET_CLASS (self); if (klass->get_credentials) return klass->get_credentials (self, ufrag, pwd); else return FALSE; } WockyJingleTransportType wocky_jingle_transport_iface_get_transport_type (WockyJingleTransportIface *self) { WockyJingleTransportType (*virtual_method)(void) = WOCKY_JINGLE_TRANSPORT_IFACE_GET_CLASS (self)->get_transport_type; g_assert (virtual_method != NULL); return virtual_method (); } static void wocky_jingle_transport_iface_base_init (gpointer klass) { static gboolean initialized = FALSE; if (!initialized) { GParamSpec *param_spec; param_spec = g_param_spec_object ( "content", "WockyJingleContent object", "Jingle content that's using this jingle transport object.", WOCKY_TYPE_JINGLE_CONTENT, G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB); g_object_interface_install_property (klass, param_spec); param_spec = g_param_spec_string ( "transport-ns", "Transport namespace", "Namespace identifying the transport type.", NULL, G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB); g_object_interface_install_property (klass, param_spec); param_spec = g_param_spec_uint ( "state", "Connection state for the transport.", "Enum specifying the connection state of the transport.", WOCKY_JINGLE_TRANSPORT_STATE_DISCONNECTED, WOCKY_JINGLE_TRANSPORT_STATE_CONNECTED, WOCKY_JINGLE_TRANSPORT_STATE_DISCONNECTED, G_PARAM_READWRITE | G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB); g_object_interface_install_property (klass, param_spec); initialized = TRUE; } } GType wocky_jingle_transport_iface_get_type (void) { static GType type = 0; if (type == 0) { static const GTypeInfo info = { sizeof (WockyJingleTransportIfaceClass), wocky_jingle_transport_iface_base_init, /* base_init */ NULL, /* base_finalize */ NULL, /* class_init */ NULL, /* class_finalize */ NULL, /* class_data */ 0, 0, /* n_preallocs */ NULL /* instance_init */ }; type = g_type_register_static (G_TYPE_INTERFACE, "WockyJingleTransportIface", &info, 0); } return type; } WockyJingleCandidate * wocky_jingle_candidate_new (WockyJingleTransportProtocol protocol, WockyJingleCandidateType type, const gchar *id, int component, const gchar *address, int port, int generation, int preference, const gchar *username, const gchar *password, int network) { WockyJingleCandidate *c = g_slice_new0 (WockyJingleCandidate); c->protocol = protocol; c->type = type; c->id = g_strdup (id); c->address = g_strdup (address); c->component = component; c->port = port; c->generation = generation; c->preference = preference; c->username = g_strdup (username); c->password = g_strdup (password); c->network = network; return c; } void wocky_jingle_candidate_free (WockyJingleCandidate *c) { g_free (c->id); g_free (c->address); g_free (c->username); g_free (c->password); g_slice_free (WockyJingleCandidate, c); } void jingle_transport_free_candidates (GList *candidates) { while (candidates != NULL) { WockyJingleCandidate *c = (WockyJingleCandidate *) candidates->data; wocky_jingle_candidate_free (c); candidates = g_list_remove (candidates, c); } } telepathy-gabble-0.18.2/lib/ext/wocky/wocky/wocky-jingle-transport-iceudp.c0000644000175000017500000004354312200204546026726 0ustar00smcvsmcv00000000000000/* * wocky-jingle-transport-iceudp.c - Source for WockyJingleTransportIceUdp * * Copyright (C) 2008 Collabora Ltd. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 "config.h" #include "wocky-jingle-transport-iceudp.h" #include #include #include #include #define WOCKY_DEBUG_FLAG WOCKY_DEBUG_JINGLE #include "wocky-debug-internal.h" #include "wocky-jingle-content.h" #include "wocky-jingle-factory.h" #include "wocky-jingle-session.h" #include "wocky-namespaces.h" #include "wocky-utils.h" static void transport_iface_init (gpointer g_iface, gpointer iface_data); G_DEFINE_TYPE_WITH_CODE (WockyJingleTransportIceUdp, wocky_jingle_transport_iceudp, G_TYPE_OBJECT, G_IMPLEMENT_INTERFACE (WOCKY_TYPE_JINGLE_TRANSPORT_IFACE, transport_iface_init)); /* signal enum */ enum { NEW_CANDIDATES, LAST_SIGNAL }; static guint signals[LAST_SIGNAL] = {0}; /* properties */ enum { PROP_CONTENT = 1, PROP_TRANSPORT_NS, PROP_STATE, LAST_PROPERTY }; struct _WockyJingleTransportIceUdpPrivate { WockyJingleContent *content; WockyJingleTransportState state; gchar *transport_ns; GList *local_candidates; /* A pointer into "local_candidates" list to mark the * candidates that are still not transmitted, or NULL * if all of them are transmitted. */ GList *pending_candidates; GList *remote_candidates; gchar *ufrag; gchar *pwd; /* next ID to send with a candidate */ int id_sequence; gboolean dispose_has_run; }; static void wocky_jingle_transport_iceudp_init (WockyJingleTransportIceUdp *obj) { WockyJingleTransportIceUdpPrivate *priv = G_TYPE_INSTANCE_GET_PRIVATE (obj, WOCKY_TYPE_JINGLE_TRANSPORT_ICEUDP, WockyJingleTransportIceUdpPrivate); obj->priv = priv; priv->id_sequence = 1; priv->dispose_has_run = FALSE; } static void wocky_jingle_transport_iceudp_dispose (GObject *object) { WockyJingleTransportIceUdp *trans = WOCKY_JINGLE_TRANSPORT_ICEUDP (object); WockyJingleTransportIceUdpPrivate *priv = trans->priv; if (priv->dispose_has_run) return; DEBUG ("dispose called"); priv->dispose_has_run = TRUE; jingle_transport_free_candidates (priv->remote_candidates); priv->remote_candidates = NULL; jingle_transport_free_candidates (priv->local_candidates); priv->local_candidates = NULL; g_free (priv->transport_ns); priv->transport_ns = NULL; g_free (priv->ufrag); priv->ufrag = NULL; g_free (priv->pwd); priv->pwd = NULL; if (G_OBJECT_CLASS (wocky_jingle_transport_iceudp_parent_class)->dispose) G_OBJECT_CLASS (wocky_jingle_transport_iceudp_parent_class)->dispose (object); } static void wocky_jingle_transport_iceudp_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { WockyJingleTransportIceUdp *trans = WOCKY_JINGLE_TRANSPORT_ICEUDP (object); WockyJingleTransportIceUdpPrivate *priv = trans->priv; switch (property_id) { case PROP_CONTENT: g_value_set_object (value, priv->content); break; case PROP_TRANSPORT_NS: g_value_set_string (value, priv->transport_ns); break; case PROP_STATE: g_value_set_uint (value, priv->state); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void wocky_jingle_transport_iceudp_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { WockyJingleTransportIceUdp *trans = WOCKY_JINGLE_TRANSPORT_ICEUDP (object); WockyJingleTransportIceUdpPrivate *priv = trans->priv; switch (property_id) { case PROP_CONTENT: priv->content = g_value_get_object (value); break; case PROP_TRANSPORT_NS: g_free (priv->transport_ns); priv->transport_ns = g_value_dup_string (value); break; case PROP_STATE: priv->state = g_value_get_uint (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void wocky_jingle_transport_iceudp_class_init (WockyJingleTransportIceUdpClass *cls) { GObjectClass *object_class = G_OBJECT_CLASS (cls); GParamSpec *param_spec; g_type_class_add_private (cls, sizeof (WockyJingleTransportIceUdpPrivate)); object_class->get_property = wocky_jingle_transport_iceudp_get_property; object_class->set_property = wocky_jingle_transport_iceudp_set_property; object_class->dispose = wocky_jingle_transport_iceudp_dispose; /* property definitions */ param_spec = g_param_spec_object ("content", "WockyJingleContent object", "Jingle content object using this transport.", WOCKY_TYPE_JINGLE_CONTENT, G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB); g_object_class_install_property (object_class, PROP_CONTENT, param_spec); param_spec = g_param_spec_string ("transport-ns", "Transport namespace", "Namespace identifying the transport type.", NULL, G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB); g_object_class_install_property (object_class, PROP_TRANSPORT_NS, param_spec); param_spec = g_param_spec_uint ("state", "Connection state for the transport.", "Enum specifying the connection state of the transport.", WOCKY_JINGLE_TRANSPORT_STATE_DISCONNECTED, WOCKY_JINGLE_TRANSPORT_STATE_CONNECTED, WOCKY_JINGLE_TRANSPORT_STATE_DISCONNECTED, G_PARAM_READWRITE | G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB); g_object_class_install_property (object_class, PROP_STATE, param_spec); /* signal definitions */ signals[NEW_CANDIDATES] = g_signal_new ( "new-candidates", G_TYPE_FROM_CLASS (cls), G_SIGNAL_RUN_LAST, 0, NULL, NULL, g_cclosure_marshal_VOID__POINTER, G_TYPE_NONE, 1, G_TYPE_POINTER); } static void parse_candidates (WockyJingleTransportIface *obj, WockyNode *transport_node, GError **error) { WockyJingleTransportIceUdp *t = WOCKY_JINGLE_TRANSPORT_ICEUDP (obj); WockyJingleTransportIceUdpPrivate *priv = t->priv; gboolean node_contains_a_candidate = FALSE; GList *candidates = NULL; WockyNodeIter i; WockyNode *node; DEBUG ("called"); wocky_node_iter_init (&i, transport_node, "candidate", NULL); while (wocky_node_iter_next (&i, &node)) { const gchar *id, *address, *user, *pass, *str; guint port, net, gen, component = 1; gdouble pref; WockyJingleTransportProtocol proto; WockyJingleCandidateType ctype; WockyJingleCandidate *c; node_contains_a_candidate = TRUE; id = wocky_node_get_attribute (node, "foundation"); if (id == NULL) { DEBUG ("candidate doesn't contain foundation"); continue; } address = wocky_node_get_attribute (node, "ip"); if (address == NULL) { DEBUG ("candidate doesn't contain ip"); continue; } str = wocky_node_get_attribute (node, "port"); if (str == NULL) { DEBUG ("candidate doesn't contain port"); continue; } port = atoi (str); str = wocky_node_get_attribute (node, "protocol"); if (str == NULL) { DEBUG ("candidate doesn't contain protocol"); continue; } if (!wocky_strdiff (str, "udp")) { proto = WOCKY_JINGLE_TRANSPORT_PROTOCOL_UDP; } else { /* unknown protocol */ DEBUG ("unknown protocol: %s", str); continue; } str = wocky_node_get_attribute (node, "priority"); if (str == NULL) { DEBUG ("candidate doesn't contain priority"); continue; } pref = g_ascii_strtod (str, NULL); str = wocky_node_get_attribute (node, "type"); if (str == NULL) { DEBUG ("candidate doesn't contain type"); continue; } if (!wocky_strdiff (str, "host")) { ctype = WOCKY_JINGLE_CANDIDATE_TYPE_LOCAL; } else if (!wocky_strdiff (str, "srflx") || !wocky_strdiff (str, "prflx")) { /* FIXME Strictly speaking a prflx candidate should be a different * type, but the TP spec has now way to distinguish and it doesn't * matter much anyway.. */ ctype = WOCKY_JINGLE_CANDIDATE_TYPE_STUN; } else if (!wocky_strdiff (str, "relay")) { ctype = WOCKY_JINGLE_CANDIDATE_TYPE_RELAY; } else { /* unknown candidate type */ DEBUG ("unknown candidate type: %s", str); continue; } user = wocky_node_get_attribute (transport_node, "ufrag"); if (user == NULL) { DEBUG ("transport doesn't contain ufrag"); continue; } pass = wocky_node_get_attribute (transport_node, "pwd"); if (pass == NULL) { DEBUG ("transport doesn't contain pwd"); continue; } str = wocky_node_get_attribute (node, "network"); if (str == NULL) { DEBUG ("candidate doesn't contain network"); continue; } net = atoi (str); str = wocky_node_get_attribute (node, "generation"); if (str == NULL) { DEBUG ("candidate doesn't contain generation"); continue; } gen = atoi (str); str = wocky_node_get_attribute (node, "component"); if (str == NULL) { DEBUG ("candidate doesn't contain component"); continue; } component = atoi (str); if (priv->ufrag == NULL || strcmp (priv->ufrag, user)) { g_free (priv->ufrag); priv->ufrag = g_strdup (user); } if (priv->pwd == NULL || strcmp (priv->pwd, pass)) { g_free (priv->pwd); priv->pwd = g_strdup (pass); } c = wocky_jingle_candidate_new (proto, ctype, id, component, address, port, gen, pref, user, pass, net); candidates = g_list_append (candidates, c); } if (candidates == NULL) { if (node_contains_a_candidate) { DEBUG_NODE (transport_node, "couldn't parse any of the given candidates"); g_set_error (error, WOCKY_XMPP_ERROR, WOCKY_XMPP_ERROR_BAD_REQUEST, "could not parse any of the given candidates"); } else { DEBUG ("no candidates in this stanza"); } } else { DEBUG ("emitting %d new remote candidates", g_list_length (candidates)); g_signal_emit (obj, signals[NEW_CANDIDATES], 0, candidates); priv->remote_candidates = g_list_concat (priv->remote_candidates, candidates); } } static void inject_candidates (WockyJingleTransportIface *obj, WockyNode *transport_node) { WockyJingleTransportIceUdp *self = WOCKY_JINGLE_TRANSPORT_ICEUDP (obj); WockyJingleTransportIceUdpPrivate *priv = self->priv; const gchar *username = NULL; for (; priv->pending_candidates != NULL; priv->pending_candidates = priv->pending_candidates->next) { WockyJingleCandidate *c = (WockyJingleCandidate *) priv->pending_candidates->data; gchar port_str[16], pref_str[16], comp_str[16], id_str[16], *type_str, *proto_str; WockyNode *cnode; if (username == NULL) { username = c->username; } else if (wocky_strdiff (username, c->username)) { DEBUG ("found a candidate with a different username (%s not %s); " "will send in a separate batch", c->username, username); break; } sprintf (pref_str, "%d", c->preference); sprintf (port_str, "%d", c->port); sprintf (comp_str, "%d", c->component); sprintf (id_str, "%d", priv->id_sequence++); switch (c->type) { case WOCKY_JINGLE_CANDIDATE_TYPE_LOCAL: type_str = "host"; break; case WOCKY_JINGLE_CANDIDATE_TYPE_STUN: type_str = "srflx"; break; case WOCKY_JINGLE_CANDIDATE_TYPE_RELAY: type_str = "relay"; break; default: DEBUG ("skipping candidate with unknown type %u", c->type); continue; } switch (c->protocol) { case WOCKY_JINGLE_TRANSPORT_PROTOCOL_UDP: proto_str = "udp"; break; case WOCKY_JINGLE_TRANSPORT_PROTOCOL_TCP: DEBUG ("ignoring TCP candidate"); continue; default: DEBUG ("skipping candidate with unknown protocol %u", c->protocol); continue; } wocky_node_set_attributes (transport_node, "ufrag", c->username, "pwd", c->password, NULL); cnode = wocky_node_add_child (transport_node, "candidate"); wocky_node_set_attributes (cnode, "ip", c->address, "port", port_str, "priority", pref_str, "protocol", proto_str, "type", type_str, "component", comp_str, "foundation", c->id, "id", id_str, "network", "0", "generation", "0", NULL); } } /* We never have to retransmit candidates we've already sent, so we ignore * @all. */ static void send_candidates (WockyJingleTransportIface *iface, gboolean all G_GNUC_UNUSED) { WockyJingleTransportIceUdp *self = WOCKY_JINGLE_TRANSPORT_ICEUDP (iface); WockyJingleTransportIceUdpPrivate *priv = self->priv; while (priv->pending_candidates != NULL) { WockyNode *trans_node, *sess_node; WockyStanza *msg; msg = wocky_jingle_session_new_message (priv->content->session, WOCKY_JINGLE_ACTION_TRANSPORT_INFO, &sess_node); wocky_jingle_content_produce_node (priv->content, sess_node, FALSE, TRUE, &trans_node); inject_candidates (iface, trans_node); wocky_porter_send_iq_async ( wocky_jingle_session_get_porter (priv->content->session), msg, NULL, NULL, NULL); g_object_unref (msg); } DEBUG ("sent all pending candidates"); } /* Takes in a list of slice-allocated WockyJingleCandidate structs */ static void new_local_candidates (WockyJingleTransportIface *obj, GList *new_candidates) { WockyJingleTransportIceUdp *transport = WOCKY_JINGLE_TRANSPORT_ICEUDP (obj); WockyJingleTransportIceUdpPrivate *priv = transport->priv; priv->local_candidates = g_list_concat (priv->local_candidates, new_candidates); /* If all previous candidates have been signalled, set the new * ones as pending. If there are existing pending candidates, * the new ones will just be appended to that list. */ if (priv->pending_candidates == NULL) priv->pending_candidates = new_candidates; } static GList * get_remote_candidates (WockyJingleTransportIface *iface) { WockyJingleTransportIceUdp *transport = WOCKY_JINGLE_TRANSPORT_ICEUDP (iface); WockyJingleTransportIceUdpPrivate *priv = transport->priv; return priv->remote_candidates; } static GList * get_local_candidates (WockyJingleTransportIface *iface) { WockyJingleTransportIceUdp *transport = WOCKY_JINGLE_TRANSPORT_ICEUDP (iface); WockyJingleTransportIceUdpPrivate *priv = transport->priv; return priv->local_candidates; } static WockyJingleTransportType get_transport_type (void) { return JINGLE_TRANSPORT_ICE_UDP; } static gboolean get_credentials (WockyJingleTransportIface *iface, gchar **ufrag, gchar **pwd) { WockyJingleTransportIceUdp *transport = WOCKY_JINGLE_TRANSPORT_ICEUDP (iface); WockyJingleTransportIceUdpPrivate *priv = transport->priv; if (!priv->ufrag || !priv->pwd) return FALSE; if (ufrag) *ufrag = priv->ufrag; if (pwd) *pwd = priv->pwd; return TRUE; } static void transport_iface_init (gpointer g_iface, gpointer iface_data) { WockyJingleTransportIfaceClass *klass = (WockyJingleTransportIfaceClass *) g_iface; klass->parse_candidates = parse_candidates; klass->new_local_candidates = new_local_candidates; klass->inject_candidates = inject_candidates; klass->send_candidates = send_candidates; klass->get_remote_candidates = get_remote_candidates; klass->get_local_candidates = get_local_candidates; klass->get_transport_type = get_transport_type; klass->get_credentials = get_credentials; } void jingle_transport_iceudp_register (WockyJingleFactory *factory) { wocky_jingle_factory_register_transport (factory, WOCKY_XMPP_NS_JINGLE_TRANSPORT_ICEUDP, WOCKY_TYPE_JINGLE_TRANSPORT_ICEUDP); } telepathy-gabble-0.18.2/lib/ext/wocky/wocky/wocky-jingle-transport-google.c0000644000175000017500000004570112200204546026727 0ustar00smcvsmcv00000000000000/* * wocky-jingle-transport-google.c - Source for WockyJingleTransportGoogle * * Copyright (C) 2008 Collabora Ltd. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 "config.h" #include "wocky-jingle-transport-google.h" #include #include #include #include #define WOCKY_DEBUG_FLAG WOCKY_DEBUG_JINGLE #include "wocky-debug-internal.h" #include "wocky-jingle-content.h" #include "wocky-jingle-factory.h" #include "wocky-jingle-session.h" #include "wocky-namespaces.h" #include "wocky-utils.h" static void transport_iface_init (gpointer g_iface, gpointer iface_data); G_DEFINE_TYPE_WITH_CODE (WockyJingleTransportGoogle, wocky_jingle_transport_google, G_TYPE_OBJECT, G_IMPLEMENT_INTERFACE (WOCKY_TYPE_JINGLE_TRANSPORT_IFACE, transport_iface_init)); /* signal enum */ enum { NEW_CANDIDATES, LAST_SIGNAL }; static guint signals[LAST_SIGNAL] = {0}; /* properties */ enum { PROP_CONTENT = 1, PROP_TRANSPORT_NS, PROP_STATE, LAST_PROPERTY }; struct _WockyJingleTransportGooglePrivate { WockyJingleContent *content; WockyJingleTransportState state; gchar *transport_ns; /* Component names or jingle-share transport 'channels' g_strdup'd component name => GINT_TO_POINTER (component id) */ GHashTable *component_names; GList *local_candidates; /* A pointer into "local_candidates" list to mark the * candidates that are still not transmitted, or NULL * if all of them are transmitted. */ GList *pending_candidates; GList *remote_candidates; gboolean dispose_has_run; }; static void wocky_jingle_transport_google_init (WockyJingleTransportGoogle *obj) { WockyJingleTransportGooglePrivate *priv = G_TYPE_INSTANCE_GET_PRIVATE (obj, WOCKY_TYPE_JINGLE_TRANSPORT_GOOGLE, WockyJingleTransportGooglePrivate); obj->priv = priv; priv->component_names = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); priv->dispose_has_run = FALSE; } static void wocky_jingle_transport_google_dispose (GObject *object) { WockyJingleTransportGoogle *trans = WOCKY_JINGLE_TRANSPORT_GOOGLE (object); WockyJingleTransportGooglePrivate *priv = trans->priv; if (priv->dispose_has_run) return; DEBUG ("dispose called"); priv->dispose_has_run = TRUE; g_hash_table_unref (priv->component_names); priv->component_names = NULL; jingle_transport_free_candidates (priv->remote_candidates); priv->remote_candidates = NULL; jingle_transport_free_candidates (priv->local_candidates); priv->local_candidates = NULL; g_free (priv->transport_ns); priv->transport_ns = NULL; if (G_OBJECT_CLASS (wocky_jingle_transport_google_parent_class)->dispose) G_OBJECT_CLASS (wocky_jingle_transport_google_parent_class)->dispose (object); } static void wocky_jingle_transport_google_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { WockyJingleTransportGoogle *trans = WOCKY_JINGLE_TRANSPORT_GOOGLE (object); WockyJingleTransportGooglePrivate *priv = trans->priv; switch (property_id) { case PROP_CONTENT: g_value_set_object (value, priv->content); break; case PROP_TRANSPORT_NS: g_value_set_string (value, priv->transport_ns); break; case PROP_STATE: g_value_set_uint (value, priv->state); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void wocky_jingle_transport_google_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { WockyJingleTransportGoogle *trans = WOCKY_JINGLE_TRANSPORT_GOOGLE (object); WockyJingleTransportGooglePrivate *priv = trans->priv; switch (property_id) { case PROP_CONTENT: priv->content = g_value_get_object (value); break; case PROP_TRANSPORT_NS: g_free (priv->transport_ns); priv->transport_ns = g_value_dup_string (value); break; case PROP_STATE: priv->state = g_value_get_uint (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void wocky_jingle_transport_google_class_init (WockyJingleTransportGoogleClass *cls) { GObjectClass *object_class = G_OBJECT_CLASS (cls); GParamSpec *param_spec; g_type_class_add_private (cls, sizeof (WockyJingleTransportGooglePrivate)); object_class->get_property = wocky_jingle_transport_google_get_property; object_class->set_property = wocky_jingle_transport_google_set_property; object_class->dispose = wocky_jingle_transport_google_dispose; /* property definitions */ param_spec = g_param_spec_object ("content", "WockyJingleContent object", "Jingle content object using this transport.", WOCKY_TYPE_JINGLE_CONTENT, G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB); g_object_class_install_property (object_class, PROP_CONTENT, param_spec); param_spec = g_param_spec_string ("transport-ns", "Transport namespace", "Namespace identifying the transport type.", NULL, G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB); g_object_class_install_property (object_class, PROP_TRANSPORT_NS, param_spec); param_spec = g_param_spec_uint ("state", "Connection state for the transport.", "Enum specifying the connection state of the transport.", WOCKY_JINGLE_TRANSPORT_STATE_DISCONNECTED, WOCKY_JINGLE_TRANSPORT_STATE_CONNECTED, WOCKY_JINGLE_TRANSPORT_STATE_DISCONNECTED, G_PARAM_READWRITE | G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB); g_object_class_install_property (object_class, PROP_STATE, param_spec); /* signal definitions */ signals[NEW_CANDIDATES] = g_signal_new ( "new-candidates", G_TYPE_FROM_CLASS (cls), G_SIGNAL_RUN_LAST, 0, NULL, NULL, g_cclosure_marshal_VOID__POINTER, G_TYPE_NONE, 1, G_TYPE_POINTER); } static void parse_candidates (WockyJingleTransportIface *obj, WockyNode *transport_node, GError **error) { WockyJingleTransportGoogle *t = WOCKY_JINGLE_TRANSPORT_GOOGLE (obj); WockyJingleTransportGooglePrivate *priv = t->priv; GList *candidates = NULL; WockyNodeIter i; WockyNode *node; wocky_node_iter_init (&i, transport_node, "candidate", NULL); while (wocky_node_iter_next (&i, &node)) { const gchar *name, *address, *user, *pass, *str; guint port, net, gen, component; int pref; WockyJingleTransportProtocol proto; WockyJingleCandidateType ctype; WockyJingleCandidate *c; name = wocky_node_get_attribute (node, "name"); if (name == NULL) break; if (!g_hash_table_lookup_extended (priv->component_names, name, NULL, NULL)) { DEBUG ("component name %s unknown to this transport", name); continue; } component = GPOINTER_TO_INT (g_hash_table_lookup (priv->component_names, name)); address = wocky_node_get_attribute (node, "address"); if (address == NULL) break; str = wocky_node_get_attribute (node, "port"); if (str == NULL) break; port = atoi (str); str = wocky_node_get_attribute (node, "protocol"); if (str == NULL) break; if (!wocky_strdiff (str, "udp")) { proto = WOCKY_JINGLE_TRANSPORT_PROTOCOL_UDP; } else if (!wocky_strdiff (str, "tcp")) { /* candiates on port 443 must be "ssltcp" */ if (port == 443) break; proto = WOCKY_JINGLE_TRANSPORT_PROTOCOL_TCP; } else if (!wocky_strdiff (str, "ssltcp")) { /* "ssltcp" must use port 443 */ if (port != 443) break; /* we really don't care about "ssltcp" otherwise */ proto = WOCKY_JINGLE_TRANSPORT_PROTOCOL_TCP; } else { /* unknown protocol */ DEBUG ("unknown protocol: %s", str); break; } str = wocky_node_get_attribute (node, "preference"); if (str == NULL) break; pref = g_ascii_strtod (str, NULL) * 65536; str = wocky_node_get_attribute (node, "type"); if (str == NULL) break; if (!wocky_strdiff (str, "local")) { ctype = WOCKY_JINGLE_CANDIDATE_TYPE_LOCAL; } else if (!wocky_strdiff (str, "stun")) { ctype = WOCKY_JINGLE_CANDIDATE_TYPE_STUN; } else if (!wocky_strdiff (str, "relay")) { ctype = WOCKY_JINGLE_CANDIDATE_TYPE_RELAY; } else { /* unknown candidate type */ DEBUG ("unknown candidate type: %s", str); break; } user = wocky_node_get_attribute (node, "username"); if (user == NULL) break; pass = wocky_node_get_attribute (node, "password"); if (pass == NULL) break; str = wocky_node_get_attribute (node, "network"); if (str == NULL) break; net = atoi (str); str = wocky_node_get_attribute (node, "generation"); if (str == NULL) break; gen = atoi (str); str = wocky_node_get_attribute (node, "component"); if (str != NULL) component = atoi (str); c = wocky_jingle_candidate_new (proto, ctype, NULL, component, address, port, gen, pref, user, pass, net); candidates = g_list_append (candidates, c); } if (wocky_node_iter_next (&i, NULL)) { DEBUG ("not all nodes were processed, reporting error"); /* rollback these */ jingle_transport_free_candidates (candidates); g_set_error (error, WOCKY_XMPP_ERROR, WOCKY_XMPP_ERROR_BAD_REQUEST, "invalid candidate"); return; } DEBUG ("emitting %d new remote candidates", g_list_length (candidates)); g_signal_emit (obj, signals[NEW_CANDIDATES], 0, candidates); /* append them to the known remote candidates */ priv->remote_candidates = g_list_concat (priv->remote_candidates, candidates); } static void transmit_candidates (WockyJingleTransportGoogle *transport, const gchar *name, GList *candidates) { WockyJingleTransportGooglePrivate *priv = transport->priv; GList *li; WockyStanza *msg; WockyNode *trans_node, *sess_node; if (candidates == NULL) return; msg = wocky_jingle_session_new_message (priv->content->session, WOCKY_JINGLE_ACTION_TRANSPORT_INFO, &sess_node); wocky_jingle_content_produce_node (priv->content, sess_node, FALSE, TRUE, &trans_node); for (li = candidates; li; li = li->next) { WockyJingleCandidate *c = (WockyJingleCandidate *) li->data; gchar port_str[16], pref_str[16], comp_str[16], *type_str, *proto_str; WockyNode *cnode; sprintf (port_str, "%d", c->port); sprintf (pref_str, "%lf", c->preference / 65536.0); sprintf (comp_str, "%d", c->component); switch (c->type) { case WOCKY_JINGLE_CANDIDATE_TYPE_LOCAL: type_str = "local"; break; case WOCKY_JINGLE_CANDIDATE_TYPE_STUN: type_str = "stun"; break; case WOCKY_JINGLE_CANDIDATE_TYPE_RELAY: type_str = "relay"; break; default: g_assert_not_reached (); } switch (c->protocol) { case WOCKY_JINGLE_TRANSPORT_PROTOCOL_UDP: proto_str = "udp"; break; case WOCKY_JINGLE_TRANSPORT_PROTOCOL_TCP: if ((c->port == 443) && (c->type == WOCKY_JINGLE_CANDIDATE_TYPE_RELAY)) proto_str = "ssltcp"; else proto_str = "tcp"; break; default: g_assert_not_reached (); } cnode = wocky_node_add_child (trans_node, "candidate"); wocky_node_set_attributes (cnode, "address", c->address, "port", port_str, "username", c->username, "password", c->password != NULL ? c->password : "", "preference", pref_str, "protocol", proto_str, "type", type_str, "component", comp_str, "network", "0", "generation", "0", NULL); wocky_node_set_attribute (cnode, "name", name); } wocky_porter_send_iq_async ( wocky_jingle_session_get_porter (priv->content->session), msg, NULL, NULL, NULL); g_object_unref (msg); } /* Groups @candidates into rtp and rtcp and sends each group in its own * transport-info. This works around old Gabble, which rejected transport-info * stanzas containing non-rtp candidates. */ static void group_and_transmit_candidates (WockyJingleTransportGoogle *transport, GList *candidates) { WockyJingleTransportGooglePrivate *priv = transport->priv; GList *all_candidates = NULL; GList *li; GList *cands; for (li = candidates; li != NULL; li = g_list_next (li)) { WockyJingleCandidate *c = li->data; for (cands = all_candidates; cands != NULL; cands = g_list_next (cands)) { WockyJingleCandidate *c2 = ((GList *) cands->data)->data; if (c->component == c2->component) { break; } } if (cands == NULL) { all_candidates = g_list_prepend (all_candidates, NULL); cands = all_candidates; } cands->data = g_list_prepend (cands->data, c); } for (cands = all_candidates; cands != NULL; cands = g_list_next (cands)) { GHashTableIter iter; gpointer key, value; gchar *name = NULL; WockyJingleCandidate *c = ((GList *) cands->data)->data; g_hash_table_iter_init (&iter, priv->component_names); while (g_hash_table_iter_next (&iter, &key, &value)) { if (GPOINTER_TO_INT (value) == c->component) { name = key; break; } } if (name) { transmit_candidates (transport, name, cands->data); } else { DEBUG ("Ignoring unknown component %d", c->component); } g_list_free (cands->data); } g_list_free (all_candidates); } /* Takes in a list of slice-allocated WockyJingleCandidate structs */ static void new_local_candidates (WockyJingleTransportIface *obj, GList *new_candidates) { WockyJingleTransportGoogle *transport = WOCKY_JINGLE_TRANSPORT_GOOGLE (obj); WockyJingleTransportGooglePrivate *priv = transport->priv; priv->local_candidates = g_list_concat (priv->local_candidates, new_candidates); /* If all previous candidates have been signalled, set the new * ones as pending. If there are existing pending candidates, * the new ones will just be appended to that list. */ if (priv->pending_candidates == NULL) priv->pending_candidates = new_candidates; } static void send_candidates (WockyJingleTransportIface *obj, gboolean all) { WockyJingleTransportGoogle *transport = WOCKY_JINGLE_TRANSPORT_GOOGLE (obj); WockyJingleTransportGooglePrivate *priv = transport->priv; if (all) { /* for gtalk3, we might have to retransmit everything */ group_and_transmit_candidates (transport, priv->local_candidates); priv->pending_candidates = NULL; } else { /* If the content became ready after we wanted to transmit * these originally, we are called to transmit when it them */ if (priv->pending_candidates != NULL) { group_and_transmit_candidates (transport, priv->pending_candidates); priv->pending_candidates = NULL; } } } static GList * get_local_candidates (WockyJingleTransportIface *iface) { WockyJingleTransportGoogle *transport = WOCKY_JINGLE_TRANSPORT_GOOGLE (iface); WockyJingleTransportGooglePrivate *priv = transport->priv; return priv->local_candidates; } static GList * get_remote_candidates (WockyJingleTransportIface *iface) { WockyJingleTransportGoogle *transport = WOCKY_JINGLE_TRANSPORT_GOOGLE (iface); WockyJingleTransportGooglePrivate *priv = transport->priv; return priv->remote_candidates; } static WockyJingleTransportType get_transport_type (void) { return JINGLE_TRANSPORT_GOOGLE_P2P; } static void transport_iface_init (gpointer g_iface, gpointer iface_data) { WockyJingleTransportIfaceClass *klass = (WockyJingleTransportIfaceClass *) g_iface; klass->parse_candidates = parse_candidates; klass->new_local_candidates = new_local_candidates; /* Not implementing inject_candidates: gtalk-p2p candidates are always sent * in transport-info or equivalent. */ klass->send_candidates = send_candidates; klass->get_remote_candidates = get_remote_candidates; klass->get_local_candidates = get_local_candidates; klass->get_transport_type = get_transport_type; } /* Returns FALSE if the component name already exists */ gboolean jingle_transport_google_set_component_name ( WockyJingleTransportGoogle *transport, const gchar *name, guint component_id) { WockyJingleTransportGooglePrivate *priv = transport->priv; if (g_hash_table_lookup_extended (priv->component_names, name, NULL, NULL)) return FALSE; g_hash_table_insert (priv->component_names, g_strdup (name), GINT_TO_POINTER (component_id)); return TRUE; } void jingle_transport_google_register (WockyJingleFactory *factory) { /* GTalk libjingle0.3 dialect */ wocky_jingle_factory_register_transport (factory, "", WOCKY_TYPE_JINGLE_TRANSPORT_GOOGLE); /* GTalk libjingle0.4 dialect */ wocky_jingle_factory_register_transport (factory, WOCKY_XMPP_NS_GOOGLE_TRANSPORT_P2P, WOCKY_TYPE_JINGLE_TRANSPORT_GOOGLE); } telepathy-gabble-0.18.2/lib/ext/wocky/wocky/wocky-jingle-session.c0000644000175000017500000023262212200204546025104 0ustar00smcvsmcv00000000000000/* * wocky-jingle-session.c - Source for WockyJingleSession * Copyright (C) 2008 Collabora Ltd. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 "config.h" #include "wocky-jingle-session.h" #include #include #include #include #define WOCKY_DEBUG_FLAG WOCKY_DEBUG_JINGLE #include "wocky-debug-internal.h" #include "wocky-signals-marshal.h" #include "wocky-enumtypes.h" #include "wocky-jingle-content.h" #include "wocky-jingle-factory.h" /* FIXME: the RTP-specific bits of this file should be separated from the * generic Jingle code. */ #include "wocky-jingle-media-rtp.h" #include "wocky-namespaces.h" #include "wocky-node-private.h" #include "wocky-resource-contact.h" #include "wocky-utils.h" G_DEFINE_TYPE(WockyJingleSession, wocky_jingle_session, G_TYPE_OBJECT); /* signal enum */ enum { NEW_CONTENT, REMOTE_STATE_CHANGED, TERMINATED, CONTENT_REJECTED, QUERY_CAP, ABOUT_TO_INITIATE, LAST_SIGNAL }; static guint signals[LAST_SIGNAL] = {0}; /* properties */ enum { PROP_JINGLE_FACTORY = 1, PROP_PORTER, PROP_SESSION_ID, PROP_PEER_CONTACT, PROP_LOCAL_INITIATOR, PROP_STATE, PROP_DIALECT, PROP_LOCAL_HOLD, PROP_REMOTE_HOLD, PROP_REMOTE_RINGING, LAST_PROPERTY }; struct _WockyJingleSessionPrivate { /* Borrowed; the factory owns us. */ WockyJingleFactory *jingle_factory; WockyPorter *porter; WockyContact *peer_contact; /* Borrowed from peer_contact if it's a WockyResourceContact. */ const gchar *peer_resource; gchar *peer_jid; /* Either borrowed from 'porter' or equal to peer_jid. */ const gchar *initiator; gboolean local_initiator; /* WockyJingleContent objects keyed by content name. * Table owns references to these objects. */ GHashTable *initiator_contents; GHashTable *responder_contents; WockyJingleDialect dialect; WockyJingleState state; gchar *sid; gboolean locally_accepted; gboolean locally_terminated; gboolean local_hold; gboolean remote_hold; gboolean remote_ringing; gboolean dispose_has_run; }; typedef struct { WockyJingleState state; WockyJingleAction *actions; } WockyJingleStateActions; /* gcc should be able to figure this out from the table below, but.. */ #define MAX_ACTIONS_PER_STATE 12 /* NB: WOCKY_JINGLE_ACTION_UNKNOWN is used as a terminator here. */ static WockyJingleAction allowed_actions[WOCKY_N_JINGLE_STATES][MAX_ACTIONS_PER_STATE] = { /* WOCKY_JINGLE_STATE_PENDING_CREATED */ { WOCKY_JINGLE_ACTION_SESSION_INITIATE, WOCKY_JINGLE_ACTION_UNKNOWN }, /* WOCKY_JINGLE_STATE_PENDING_INITIATE_SENT */ { WOCKY_JINGLE_ACTION_SESSION_TERMINATE, WOCKY_JINGLE_ACTION_SESSION_ACCEPT, WOCKY_JINGLE_ACTION_TRANSPORT_ACCEPT, /* required for GTalk4 */ WOCKY_JINGLE_ACTION_DESCRIPTION_INFO, WOCKY_JINGLE_ACTION_SESSION_INFO, WOCKY_JINGLE_ACTION_TRANSPORT_INFO, WOCKY_JINGLE_ACTION_INFO, WOCKY_JINGLE_ACTION_UNKNOWN }, /* WOCKY_JINGLE_STATE_PENDING_INITIATED */ { WOCKY_JINGLE_ACTION_SESSION_ACCEPT, WOCKY_JINGLE_ACTION_SESSION_TERMINATE, WOCKY_JINGLE_ACTION_TRANSPORT_INFO, WOCKY_JINGLE_ACTION_CONTENT_REJECT, WOCKY_JINGLE_ACTION_CONTENT_MODIFY, WOCKY_JINGLE_ACTION_CONTENT_ACCEPT, WOCKY_JINGLE_ACTION_CONTENT_REMOVE, WOCKY_JINGLE_ACTION_DESCRIPTION_INFO, WOCKY_JINGLE_ACTION_TRANSPORT_ACCEPT, WOCKY_JINGLE_ACTION_SESSION_INFO, WOCKY_JINGLE_ACTION_INFO, WOCKY_JINGLE_ACTION_UNKNOWN }, /* WOCKY_JINGLE_STATE_PENDING_ACCEPT_SENT */ { WOCKY_JINGLE_ACTION_TRANSPORT_INFO, WOCKY_JINGLE_ACTION_DESCRIPTION_INFO, WOCKY_JINGLE_ACTION_SESSION_TERMINATE, WOCKY_JINGLE_ACTION_SESSION_INFO, WOCKY_JINGLE_ACTION_INFO, WOCKY_JINGLE_ACTION_UNKNOWN }, /* WOCKY_JINGLE_STATE_ACTIVE */ { WOCKY_JINGLE_ACTION_CONTENT_MODIFY, WOCKY_JINGLE_ACTION_CONTENT_ADD, WOCKY_JINGLE_ACTION_CONTENT_REMOVE, WOCKY_JINGLE_ACTION_CONTENT_REPLACE, WOCKY_JINGLE_ACTION_CONTENT_ACCEPT, WOCKY_JINGLE_ACTION_CONTENT_REJECT, WOCKY_JINGLE_ACTION_SESSION_INFO, WOCKY_JINGLE_ACTION_TRANSPORT_INFO, WOCKY_JINGLE_ACTION_DESCRIPTION_INFO, WOCKY_JINGLE_ACTION_INFO, WOCKY_JINGLE_ACTION_SESSION_TERMINATE, WOCKY_JINGLE_ACTION_UNKNOWN }, /* WOCKY_JINGLE_STATE_ENDED */ { WOCKY_JINGLE_ACTION_UNKNOWN } }; gboolean wocky_jingle_session_defines_action (WockyJingleSession *sess, WockyJingleAction a) { WockyJingleDialect d = sess->priv->dialect; if (a == WOCKY_JINGLE_ACTION_UNKNOWN) return FALSE; switch (d) { case WOCKY_JINGLE_DIALECT_V032: return TRUE; case WOCKY_JINGLE_DIALECT_V015: return (a != WOCKY_JINGLE_ACTION_DESCRIPTION_INFO && a != WOCKY_JINGLE_ACTION_SESSION_INFO); case WOCKY_JINGLE_DIALECT_GTALK4: if (a == WOCKY_JINGLE_ACTION_TRANSPORT_ACCEPT || a == WOCKY_JINGLE_ACTION_INFO ) return TRUE; case WOCKY_JINGLE_DIALECT_GTALK3: return (a == WOCKY_JINGLE_ACTION_SESSION_ACCEPT || a == WOCKY_JINGLE_ACTION_SESSION_INITIATE || a == WOCKY_JINGLE_ACTION_SESSION_TERMINATE || a == WOCKY_JINGLE_ACTION_TRANSPORT_INFO || a == WOCKY_JINGLE_ACTION_INFO); default: return FALSE; } } static void wocky_jingle_session_send_held (WockyJingleSession *sess); static void content_ready_cb (WockyJingleContent *c, gpointer user_data); static void content_removed_cb (WockyJingleContent *c, gpointer user_data); static void wocky_jingle_session_init (WockyJingleSession *obj) { WockyJingleSessionPrivate *priv = G_TYPE_INSTANCE_GET_PRIVATE (obj, WOCKY_TYPE_JINGLE_SESSION, WockyJingleSessionPrivate); obj->priv = priv; DEBUG ("Initializing the jingle session %p", obj); priv->initiator_contents = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref); priv->responder_contents = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref); priv->state = WOCKY_JINGLE_STATE_PENDING_CREATED; priv->locally_accepted = FALSE; priv->locally_terminated = FALSE; priv->dispose_has_run = FALSE; } static void dispose_content_hash ( WockyJingleSession *sess, GHashTable **contents) { GHashTableIter iter; gpointer content; g_hash_table_iter_init (&iter, *contents); while (g_hash_table_iter_next (&iter, NULL, &content)) { g_signal_handlers_disconnect_by_func (content, content_ready_cb, sess); g_signal_handlers_disconnect_by_func (content, content_removed_cb, sess); g_hash_table_iter_remove (&iter); } g_hash_table_unref (*contents); *contents = NULL; } static void wocky_jingle_session_dispose (GObject *object) { WockyJingleSession *sess = WOCKY_JINGLE_SESSION (object); WockyJingleSessionPrivate *priv = sess->priv; if (priv->dispose_has_run) return; DEBUG ("called"); priv->dispose_has_run = TRUE; g_assert ((priv->state == WOCKY_JINGLE_STATE_PENDING_CREATED) || (priv->state == WOCKY_JINGLE_STATE_ENDED)); dispose_content_hash (sess, &priv->initiator_contents); dispose_content_hash (sess, &priv->responder_contents); g_clear_object (&priv->peer_contact); g_clear_object (&priv->porter); g_free (priv->sid); priv->sid = NULL; g_free (priv->peer_jid); priv->peer_jid = NULL; if (G_OBJECT_CLASS (wocky_jingle_session_parent_class)->dispose) G_OBJECT_CLASS (wocky_jingle_session_parent_class)->dispose (object); } static void wocky_jingle_session_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { WockyJingleSession *sess = WOCKY_JINGLE_SESSION (object); WockyJingleSessionPrivate *priv = sess->priv; switch (property_id) { case PROP_JINGLE_FACTORY: g_value_set_object (value, priv->jingle_factory); break; case PROP_PORTER: g_value_set_object (value, priv->porter); break; case PROP_SESSION_ID: g_value_set_string (value, priv->sid); break; case PROP_LOCAL_INITIATOR: g_value_set_boolean (value, priv->local_initiator); break; case PROP_PEER_CONTACT: g_value_set_object (value, priv->peer_contact); break; case PROP_STATE: g_value_set_uint (value, priv->state); break; case PROP_DIALECT: g_value_set_uint (value, priv->dialect); break; case PROP_LOCAL_HOLD: g_value_set_boolean (value, priv->local_hold); break; case PROP_REMOTE_HOLD: g_value_set_boolean (value, priv->remote_hold); break; case PROP_REMOTE_RINGING: g_value_set_boolean (value, priv->remote_ringing); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void wocky_jingle_session_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { WockyJingleSession *sess = WOCKY_JINGLE_SESSION (object); WockyJingleSessionPrivate *priv = sess->priv; switch (property_id) { case PROP_JINGLE_FACTORY: priv->jingle_factory = g_value_get_object (value); g_assert (priv->jingle_factory != NULL); break; case PROP_PORTER: priv->porter = g_value_dup_object (value); g_assert (priv->porter != NULL); break; case PROP_SESSION_ID: g_free (priv->sid); priv->sid = g_value_dup_string (value); break; case PROP_LOCAL_INITIATOR: priv->local_initiator = g_value_get_boolean (value); break; case PROP_DIALECT: priv->dialect = g_value_get_uint (value); break; case PROP_PEER_CONTACT: priv->peer_contact = g_value_dup_object (value); break; case PROP_LOCAL_HOLD: { gboolean local_hold = g_value_get_boolean (value); if (priv->local_hold != local_hold) { priv->local_hold = local_hold; if (priv->state >= WOCKY_JINGLE_STATE_PENDING_INITIATED && priv->state < WOCKY_JINGLE_STATE_ENDED) wocky_jingle_session_send_held (sess); /* else, we'll send this in set_state when we move to PENDING_INITIATED or * better. */ } break; } default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); g_assert_not_reached (); break; } } static void wocky_jingle_session_constructed (GObject *object) { void (*chain_up) (GObject *) = G_OBJECT_CLASS (wocky_jingle_session_parent_class)->constructed; WockyJingleSession *self = WOCKY_JINGLE_SESSION (object); WockyJingleSessionPrivate *priv = self->priv; if (chain_up != NULL) chain_up (object); g_assert (priv->jingle_factory != NULL); g_assert (priv->porter != NULL); g_assert (priv->peer_contact != NULL); g_assert (priv->sid != NULL); priv->peer_jid = wocky_contact_dup_jid (priv->peer_contact); if (priv->local_initiator) priv->initiator = wocky_porter_get_full_jid (priv->porter); else priv->initiator = priv->peer_jid; if (WOCKY_IS_RESOURCE_CONTACT (priv->peer_contact)) priv->peer_resource = wocky_resource_contact_get_resource ( WOCKY_RESOURCE_CONTACT (priv->peer_contact)); } WockyJingleSession * wocky_jingle_session_new ( WockyJingleFactory *factory, WockyPorter *porter, const gchar *session_id, gboolean local_initiator, WockyContact *peer, WockyJingleDialect dialect, gboolean local_hold) { return g_object_new (WOCKY_TYPE_JINGLE_SESSION, "session-id", session_id, "jingle-factory", factory, "porter", porter, "local-initiator", local_initiator, "peer-contact", peer, "dialect", dialect, "local-hold", local_hold, NULL); } static void wocky_jingle_session_class_init (WockyJingleSessionClass *cls) { GObjectClass *object_class = G_OBJECT_CLASS (cls); GParamSpec *param_spec; g_type_class_add_private (cls, sizeof (WockyJingleSessionPrivate)); object_class->constructed = wocky_jingle_session_constructed; object_class->get_property = wocky_jingle_session_get_property; object_class->set_property = wocky_jingle_session_set_property; object_class->dispose = wocky_jingle_session_dispose; /* property definitions */ param_spec = g_param_spec_object ("jingle-factory", "WockyJingleFactory object", "The Jingle factory which created this session", WOCKY_TYPE_JINGLE_FACTORY, G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, PROP_JINGLE_FACTORY, param_spec); param_spec = g_param_spec_object ("porter", "WockyPorter", "The WockyPorter for the current connection", WOCKY_TYPE_PORTER, G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, PROP_PORTER, param_spec); param_spec = g_param_spec_string ("session-id", "Session ID", "A unique session identifier used throughout all communication.", NULL, G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, PROP_SESSION_ID, param_spec); param_spec = g_param_spec_boolean ("local-initiator", "Session initiator", "Specifies if local end initiated the session.", TRUE, G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, PROP_LOCAL_INITIATOR, param_spec); /** * WockyJingleSession:peer-contact: * * The #WockyContact representing the other party in the session. Note that * if this is a #WockyBareContact (as opposed to a #WockyResourceContact) the * session is with the contact's bare JID. */ param_spec = g_param_spec_object ("peer-contact", "Session peer", "The WockyContact representing the other party in the session.", WOCKY_TYPE_CONTACT, G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, PROP_PEER_CONTACT, param_spec); param_spec = g_param_spec_uint ("state", "Session state", "The current state that the session is in.", 0, G_MAXUINT32, WOCKY_JINGLE_STATE_PENDING_CREATED, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, PROP_STATE, param_spec); param_spec = g_param_spec_uint ("dialect", "Jingle dialect", "Jingle dialect used for this session.", 0, G_MAXUINT32, WOCKY_JINGLE_DIALECT_ERROR, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, PROP_DIALECT, param_spec); param_spec = g_param_spec_boolean ("local-hold", "Local hold", "TRUE if we've placed the peer on hold", FALSE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, PROP_LOCAL_HOLD, param_spec); param_spec = g_param_spec_boolean ("remote-hold", "Remote hold", "TRUE if the peer has placed us on hold", FALSE, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, PROP_REMOTE_HOLD, param_spec); param_spec = g_param_spec_boolean ("remote-ringing", "Remote ringing", "TRUE if the peer's client is ringing", FALSE, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, PROP_REMOTE_RINGING, param_spec); /* signal definitions */ signals[NEW_CONTENT] = g_signal_new ("new-content", G_TYPE_FROM_CLASS (cls), G_SIGNAL_RUN_LAST, 0, NULL, NULL, g_cclosure_marshal_VOID__OBJECT, G_TYPE_NONE, 1, G_TYPE_OBJECT); /** * WockyJingleSession::terminated: * @session: the session * @locally_terminated: %TRUE if the session ended due to a call to * wocky_jingle_session_terminate(); %FALSE if the peer ended the session. * @reason: a #WockyJingleReason describing why the session terminated * @text: a possibly-%NULL human-readable string describing why the session * terminated * * Emitted when the session ends, just after #WockyJingleSession:state moves * to #WOCKY_JINGLE_STATE_ENDED. */ signals[TERMINATED] = g_signal_new ("terminated", G_TYPE_FROM_CLASS (cls), G_SIGNAL_RUN_LAST, 0, NULL, NULL, _wocky_signals_marshal_VOID__BOOLEAN_UINT_STRING, G_TYPE_NONE, 3, G_TYPE_BOOLEAN, G_TYPE_UINT, G_TYPE_STRING); signals[REMOTE_STATE_CHANGED] = g_signal_new ("remote-state-changed", G_TYPE_FROM_CLASS (cls), G_SIGNAL_RUN_LAST, 0, NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); signals[CONTENT_REJECTED] = g_signal_new ("content-rejected", G_TYPE_FROM_CLASS (cls), G_SIGNAL_RUN_LAST, 0, NULL, NULL, _wocky_signals_marshal_VOID__OBJECT_UINT_STRING, G_TYPE_NONE, 3, G_TYPE_OBJECT, G_TYPE_UINT, G_TYPE_STRING); /* * @contact: this call's peer (the artist commonly known as * wocky_jingle_session_get_peer_contact()) * @cap: the XEP-0115 feature string the session is interested in. * * Emitted when the session wants to check whether the peer has a particular * capability. The handler should return %TRUE if @contact has @cap. */ signals[QUERY_CAP] = g_signal_new ("query-cap", G_TYPE_FROM_CLASS (cls), G_SIGNAL_RUN_LAST, 0, g_signal_accumulator_first_wins, NULL, _wocky_signals_marshal_BOOLEAN__OBJECT_STRING, G_TYPE_BOOLEAN, 2, WOCKY_TYPE_CONTACT, G_TYPE_STRING); signals[ABOUT_TO_INITIATE] = g_signal_new ("about-to-initiate", G_TYPE_FROM_CLASS (cls), G_SIGNAL_RUN_LAST, 0, NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); } typedef void (*HandlerFunc)(WockyJingleSession *sess, WockyNode *node, GError **error); typedef void (*ContentHandlerFunc)(WockyJingleSession *sess, WockyJingleContent *c, WockyNode *content_node, gpointer user_data, GError **error); static gboolean extract_reason (WockyNode *node, WockyJingleReason *reason, gchar **message) { WockyJingleReason _reason = WOCKY_JINGLE_REASON_UNKNOWN; WockyNode *child; WockyNodeIter iter; g_return_val_if_fail (node != NULL, FALSE); if (message != NULL) *message = g_strdup (wocky_node_get_content_from_child (node, "text")); wocky_node_iter_init (&iter, node, NULL, NULL); while (wocky_node_iter_next (&iter, &child)) { if (wocky_enum_from_nick ( wocky_jingle_reason_get_type (), child->name, (gint *) &_reason)) { if (reason != NULL) *reason = _reason; return TRUE; } } return FALSE; } static WockyJingleAction parse_action (const gchar *txt) { if (txt == NULL) return WOCKY_JINGLE_ACTION_UNKNOWN; /* synonyms, best deal with them right now */ if (!wocky_strdiff (txt, "initiate") || !wocky_strdiff (txt, "session-initiate")) return WOCKY_JINGLE_ACTION_SESSION_INITIATE; else if (!wocky_strdiff (txt, "terminate") || !wocky_strdiff (txt, "session-terminate") || !wocky_strdiff (txt, "reject")) return WOCKY_JINGLE_ACTION_SESSION_TERMINATE; else if (!wocky_strdiff (txt, "accept") || !wocky_strdiff (txt, "session-accept")) return WOCKY_JINGLE_ACTION_SESSION_ACCEPT; else if (!wocky_strdiff (txt, "candidates") || !wocky_strdiff (txt, "transport-info")) return WOCKY_JINGLE_ACTION_TRANSPORT_INFO; else if (!wocky_strdiff (txt, "content-accept")) return WOCKY_JINGLE_ACTION_CONTENT_ACCEPT; else if (!wocky_strdiff (txt, "content-add")) return WOCKY_JINGLE_ACTION_CONTENT_ADD; else if (!wocky_strdiff (txt, "content-modify")) return WOCKY_JINGLE_ACTION_CONTENT_MODIFY; else if (!wocky_strdiff (txt, "content-replace")) return WOCKY_JINGLE_ACTION_CONTENT_REPLACE; else if (!wocky_strdiff (txt, "content-reject")) return WOCKY_JINGLE_ACTION_CONTENT_REJECT; else if (!wocky_strdiff (txt, "content-remove")) return WOCKY_JINGLE_ACTION_CONTENT_REMOVE; else if (!wocky_strdiff (txt, "session-info")) return WOCKY_JINGLE_ACTION_SESSION_INFO; else if (!wocky_strdiff (txt, "transport-accept")) return WOCKY_JINGLE_ACTION_TRANSPORT_ACCEPT; else if (!wocky_strdiff (txt, "description-info")) return WOCKY_JINGLE_ACTION_DESCRIPTION_INFO; else if (!wocky_strdiff (txt, "info")) return WOCKY_JINGLE_ACTION_INFO; return WOCKY_JINGLE_ACTION_UNKNOWN; } static const gchar * produce_action (WockyJingleAction action, WockyJingleDialect dialect) { gboolean gmode = (dialect == WOCKY_JINGLE_DIALECT_GTALK3) || (dialect == WOCKY_JINGLE_DIALECT_GTALK4); g_return_val_if_fail (action != WOCKY_JINGLE_ACTION_UNKNOWN, NULL); switch (action) { case WOCKY_JINGLE_ACTION_SESSION_INITIATE: return (gmode) ? "initiate" : "session-initiate"; case WOCKY_JINGLE_ACTION_SESSION_TERMINATE: return (gmode) ? "terminate" : "session-terminate"; case WOCKY_JINGLE_ACTION_SESSION_ACCEPT: return (gmode) ? "accept" : "session-accept"; case WOCKY_JINGLE_ACTION_TRANSPORT_INFO: return (dialect == WOCKY_JINGLE_DIALECT_GTALK3) ? "candidates" : "transport-info"; case WOCKY_JINGLE_ACTION_CONTENT_ACCEPT: return "content-accept"; case WOCKY_JINGLE_ACTION_CONTENT_ADD: return "content-add"; case WOCKY_JINGLE_ACTION_CONTENT_MODIFY: return "content-modify"; case WOCKY_JINGLE_ACTION_CONTENT_REMOVE: return "content-remove"; case WOCKY_JINGLE_ACTION_CONTENT_REPLACE: return "content-replace"; case WOCKY_JINGLE_ACTION_CONTENT_REJECT: return "content-reject"; case WOCKY_JINGLE_ACTION_SESSION_INFO: return "session-info"; case WOCKY_JINGLE_ACTION_TRANSPORT_ACCEPT: return "transport-accept"; case WOCKY_JINGLE_ACTION_DESCRIPTION_INFO: return "description-info"; case WOCKY_JINGLE_ACTION_INFO: return "info"; default: /* only reached if g_return_val_if_fail is disabled */ DEBUG ("unknown action %u", action); return NULL; } } static gboolean action_is_allowed (WockyJingleAction action, WockyJingleState state) { guint i; for (i = 0; allowed_actions[state][i] != WOCKY_JINGLE_ACTION_UNKNOWN; i++) { if (allowed_actions[state][i] == action) return TRUE; } return FALSE; } static void wocky_jingle_session_send_rtp_info (WockyJingleSession *sess, const gchar *name); static void set_state (WockyJingleSession *sess, WockyJingleState state, WockyJingleReason termination_reason, const gchar *text); static WockyJingleContent *_get_any_content (WockyJingleSession *session); gboolean wocky_jingle_session_peer_has_cap ( WockyJingleSession *self, const gchar *cap_or_quirk) { gboolean ret; g_return_val_if_fail (WOCKY_IS_JINGLE_SESSION (self), FALSE); g_return_val_if_fail (cap_or_quirk != NULL, FALSE); g_signal_emit (self, signals[QUERY_CAP], 0, self->priv->peer_contact, cap_or_quirk, &ret); return ret; } static gboolean lookup_content (WockyJingleSession *sess, const gchar *name, const gchar *creator, gboolean fail_if_missing, WockyJingleContent **c, GError **error) { WockyJingleSessionPrivate *priv = sess->priv; if (name == NULL) { g_set_error (error, WOCKY_XMPP_ERROR, WOCKY_XMPP_ERROR_BAD_REQUEST, "'name' attribute unset"); return FALSE; } if (WOCKY_JINGLE_DIALECT_IS_GOOGLE (priv->dialect)) { /* Only the initiator can create contents on GTalk. */ *c = g_hash_table_lookup (priv->initiator_contents, name); } else { /* Versions of Gabble between 0.7.16 and 0.7.28 (inclusive) omitted the * 'creator' attribute from transport-info (and possibly other) stanzas. * We try to detect contacts using such a version of Gabble from their * caps; if 'creator' is missing and the peer has that caps flag, we look * up the content in both hashes. * * While this doesn't deal with the case where the content is found in * both hashes, this isn't a problem in practice: the versions of Gabble * we're working around didn't allow this to happen (they'd either reject * the second stream, or let it replace the first, depending on the phase * of the moon, and get kind of confused in the process), and we try to * pick globally-unique content names. */ if (creator == NULL && wocky_jingle_session_peer_has_cap (sess, WOCKY_QUIRK_OMITS_CONTENT_CREATORS)) { DEBUG ("working around missing 'creator' attribute"); *c = g_hash_table_lookup (priv->initiator_contents, name); if (*c == NULL) *c = g_hash_table_lookup (priv->responder_contents, name); } else if (!wocky_strdiff (creator, "initiator")) { *c = g_hash_table_lookup (priv->initiator_contents, name); } else if (!wocky_strdiff (creator, "responder")) { *c = g_hash_table_lookup (priv->responder_contents, name); } else { g_set_error (error, WOCKY_XMPP_ERROR, WOCKY_XMPP_ERROR_BAD_REQUEST, "'creator' attribute %s", (creator == NULL ? "missing" : "invalid")); return FALSE; } } if (fail_if_missing && *c == NULL) { g_set_error (error, WOCKY_XMPP_ERROR, WOCKY_XMPP_ERROR_BAD_REQUEST, "Content '%s' (created by %s) does not exist", name, creator); return FALSE; } return TRUE; } static void _foreach_content (WockyJingleSession *sess, WockyNode *node, gboolean fail_if_missing, ContentHandlerFunc func, gpointer user_data, GError **error) { WockyJingleContent *c; WockyNode *content_node; WockyNodeIter iter; wocky_node_iter_init (&iter, node, "content", NULL); while (wocky_node_iter_next (&iter, &content_node)) { if (!lookup_content (sess, wocky_node_get_attribute (content_node, "name"), wocky_node_get_attribute (content_node, "creator"), fail_if_missing, &c, error)) return; func (sess, c, content_node, user_data, error); if (*error != NULL) return; } } struct idle_content_reject_ctx { WockyJingleSession *session; WockyStanza *msg; }; static gboolean idle_content_reject (gpointer data) { struct idle_content_reject_ctx *ctx = data; wocky_jingle_session_send (ctx->session, ctx->msg); g_object_unref (ctx->session); g_free (ctx); return FALSE; } static void fire_idle_content_reject (WockyJingleSession *sess, const gchar *name, const gchar *creator) { struct idle_content_reject_ctx *ctx = g_new0 (struct idle_content_reject_ctx, 1); WockyNode *sess_node, *node; if (creator == NULL) creator = ""; ctx->session = g_object_ref (sess); ctx->msg = wocky_jingle_session_new_message (ctx->session, WOCKY_JINGLE_ACTION_CONTENT_REJECT, &sess_node); g_debug ("name = %s, initiator = %s", name, creator); node = wocky_node_add_child (sess_node, "content"); wocky_node_set_attributes (node, "name", name, "creator", creator, NULL); /* FIXME: add API for ordering IQs rather than using g_idle_add. */ g_idle_add (idle_content_reject, ctx); } static WockyJingleContent * create_content (WockyJingleSession *sess, GType content_type, WockyJingleMediaType type, WockyJingleContentSenders senders, const gchar *content_ns, const gchar *transport_ns, const gchar *name, WockyNode *content_node, GError **error) { WockyJingleSessionPrivate *priv = sess->priv; WockyJingleContent *c; GHashTable *contents; DEBUG ("session creating new content name %s, type %d", name, type); /* FIXME: media-type is introduced by WockyJingleMediaRTP, not by the * superclass, so this call is unsafe in the general case */ c = g_object_new (content_type, "session", sess, "content-ns", content_ns, "transport-ns", transport_ns, "media-type", type, "name", name, "disposition", "session", "senders", senders, NULL); g_signal_connect (c, "ready", (GCallback) content_ready_cb, sess); g_signal_connect (c, "removed", (GCallback) content_removed_cb, sess); /* if we are called by parser, parse content add */ if (content_node != NULL) { wocky_jingle_content_parse_add (c, content_node, WOCKY_JINGLE_DIALECT_IS_GOOGLE (priv->dialect), error); if (*error != NULL) { g_object_unref (c); return NULL; } /* gtalk streams don't have name, so use whatever Content came up with */ if (name == NULL) name = wocky_jingle_content_get_name (c); } if (priv->local_initiator == wocky_jingle_content_is_created_by_us (c)) { DEBUG ("inserting content %s into initiator_contents", name); contents = priv->initiator_contents; } else { DEBUG ("inserting content %s into responder_contents", name); contents = priv->responder_contents; } /* If the content already existed, either we shouldn't have picked the name * we did (if we're creating it) or _each_content_add should have already * said no. */ g_assert (g_hash_table_lookup (contents, name) == NULL); g_hash_table_insert (contents, g_strdup (name), c); g_signal_emit (sess, signals[NEW_CONTENT], 0, c); return c; } static void _each_content_add (WockyJingleSession *sess, WockyJingleContent *c, WockyNode *content_node, gpointer user_data, GError **error) { WockyJingleSessionPrivate *priv = sess->priv; const gchar *name = wocky_node_get_attribute (content_node, "name"); WockyNode *desc_node = wocky_node_get_child (content_node, "description"); GType content_type = 0; const gchar *content_ns = NULL; if (desc_node != NULL) { content_ns = wocky_node_get_ns (desc_node); DEBUG ("namespace: %s", content_ns); content_type = wocky_jingle_factory_lookup_content_type ( wocky_jingle_session_get_factory (sess), content_ns); } if (content_type == 0) { /* if this is session-initiate, we should return error, otherwise, * we should respond with content-reject */ if (priv->state < WOCKY_JINGLE_STATE_PENDING_INITIATED) g_set_error (error, WOCKY_XMPP_ERROR, WOCKY_XMPP_ERROR_BAD_REQUEST, "unsupported content type with ns %s", content_ns); else fire_idle_content_reject (sess, name, wocky_node_get_attribute (content_node, "creator")); return; } if (c != NULL) { g_set_error (error, WOCKY_XMPP_ERROR, WOCKY_XMPP_ERROR_BAD_REQUEST, "content '%s' already exists", name); return; } create_content (sess, content_type, WOCKY_JINGLE_MEDIA_TYPE_NONE, WOCKY_JINGLE_CONTENT_SENDERS_BOTH, content_ns, NULL, NULL, content_node, error); } static void _each_content_remove (WockyJingleSession *sess, WockyJingleContent *c, WockyNode *content_node, gpointer user_data, GError **error) { g_assert (c != NULL); wocky_jingle_content_remove (c, FALSE); } static void _each_content_rejected (WockyJingleSession *sess, WockyJingleContent *c, WockyNode *content_node, gpointer user_data, GError **error) { WockyJingleReason reason = GPOINTER_TO_UINT (user_data); g_assert (c != NULL); g_signal_emit (sess, signals[CONTENT_REJECTED], 0, c, reason, ""); wocky_jingle_content_remove (c, FALSE); } static void _each_content_modify (WockyJingleSession *sess, WockyJingleContent *c, WockyNode *content_node, gpointer user_data, GError **error) { g_assert (c != NULL); wocky_jingle_content_update_senders (c, content_node, error); if (*error != NULL) return; } static void _each_content_replace (WockyJingleSession *sess, WockyJingleContent *c, WockyNode *content_node, gpointer user_data, GError **error) { _each_content_remove (sess, c, content_node, NULL, error); if (*error != NULL) return; _each_content_add (sess, c, content_node, NULL, error); } static void _each_content_accept (WockyJingleSession *sess, WockyJingleContent *c, WockyNode *content_node, gpointer user_data, GError **error) { WockyJingleSessionPrivate *priv = sess->priv; WockyJingleContentState state; g_assert (c != NULL); g_object_get (c, "state", &state, NULL); if (state != WOCKY_JINGLE_CONTENT_STATE_SENT) { #ifdef ENABLE_DEBUG const gchar *name = wocky_node_get_attribute (content_node, "name"); DEBUG ("ignoring content \"%s\"s acceptance for content not in SENT state", name); #endif return; } wocky_jingle_content_parse_accept (c, content_node, WOCKY_JINGLE_DIALECT_IS_GOOGLE (priv->dialect), error); } static void _each_description_info (WockyJingleSession *sess, WockyJingleContent *c, WockyNode *content_node, gpointer user_data, GError **error) { wocky_jingle_content_parse_description_info (c, content_node, error); } static void on_session_initiate (WockyJingleSession *sess, WockyNode *node, GError **error) { WockyJingleSessionPrivate *priv = sess->priv; /* we can't call ourselves at the moment */ if (priv->local_initiator) { /* We ignore initiate from us, and terminate the session immediately * afterwards */ wocky_jingle_session_terminate (sess, WOCKY_JINGLE_REASON_BUSY, NULL, NULL); return; } if ((priv->dialect == WOCKY_JINGLE_DIALECT_GTALK3)) { const gchar *content_ns = NULL; WockyNode *desc_node = wocky_node_get_child (node, "description"); content_ns = wocky_node_get_ns (desc_node); if (!wocky_strdiff (content_ns, WOCKY_XMPP_NS_GOOGLE_SESSION_VIDEO)) { WockyJingleFactory *factory = wocky_jingle_session_get_factory (sess); GType content_type = 0; DEBUG ("GTalk v3 session with audio and video"); /* audio and video content */ content_type = wocky_jingle_factory_lookup_content_type ( factory, content_ns); create_content (sess, content_type, WOCKY_JINGLE_MEDIA_TYPE_VIDEO, WOCKY_JINGLE_CONTENT_SENDERS_BOTH, WOCKY_XMPP_NS_GOOGLE_SESSION_VIDEO, NULL, "video", node, error); content_type = wocky_jingle_factory_lookup_content_type ( factory, WOCKY_XMPP_NS_GOOGLE_SESSION_PHONE); create_content (sess, content_type, WOCKY_JINGLE_MEDIA_TYPE_AUDIO, WOCKY_JINGLE_CONTENT_SENDERS_BOTH, WOCKY_XMPP_NS_GOOGLE_SESSION_PHONE, NULL, "audio", node, error); } else { _each_content_add (sess, NULL, node, NULL, error); } } else if (priv->dialect == WOCKY_JINGLE_DIALECT_GTALK4) { /* in this case we implicitly have just one content */ _each_content_add (sess, NULL, node, NULL, error); } else { _foreach_content (sess, node, FALSE, _each_content_add, NULL, error); } if (*error == NULL) { /* FIXME: contents defined here should always have "session" content * disposition; resolve this as soon as the proper procedure is defined * in XEP-0166. */ set_state (sess, WOCKY_JINGLE_STATE_PENDING_INITIATED, WOCKY_JINGLE_REASON_UNKNOWN, NULL); wocky_jingle_session_send_rtp_info (sess, "ringing"); } } static void on_content_add (WockyJingleSession *sess, WockyNode *node, GError **error) { _foreach_content (sess, node, FALSE, _each_content_add, NULL, error); } static void on_content_modify (WockyJingleSession *sess, WockyNode *node, GError **error) { _foreach_content (sess, node, TRUE, _each_content_modify, NULL, error); } static void on_content_remove (WockyJingleSession *sess, WockyNode *node, GError **error) { _foreach_content (sess, node, TRUE, _each_content_remove, NULL, error); } static void on_content_replace (WockyJingleSession *sess, WockyNode *node, GError **error) { _foreach_content (sess, node, TRUE, _each_content_replace, NULL, error); } static void on_content_reject (WockyJingleSession *sess, WockyNode *node, GError **error) { WockyNode *n = wocky_node_get_child (node, "reason"); WockyJingleReason reason = WOCKY_JINGLE_REASON_UNKNOWN; DEBUG (" "); if (n != NULL) extract_reason (n, &reason, NULL); if (reason == WOCKY_JINGLE_REASON_UNKNOWN) reason = WOCKY_JINGLE_REASON_GENERAL_ERROR; _foreach_content (sess, node, TRUE, _each_content_rejected, GUINT_TO_POINTER (reason), error); } static void on_content_accept (WockyJingleSession *sess, WockyNode *node, GError **error) { _foreach_content (sess, node, TRUE, _each_content_accept, NULL, error); } static void on_session_accept (WockyJingleSession *sess, WockyNode *node, GError **error) { WockyJingleSessionPrivate *priv = sess->priv; DEBUG ("called"); if ((priv->dialect == WOCKY_JINGLE_DIALECT_GTALK3) || (priv->dialect == WOCKY_JINGLE_DIALECT_GTALK4)) { /* Google Talk calls don't have contents per se; they just have * s in different namespaces for audio and video, in the * same stanza. So we need to feed the whole stanza to each * content in turn. */ GList *cs = wocky_jingle_session_get_contents (sess); GList *l; for (l = cs; l != NULL; l = l->next) _each_content_accept (sess, l->data, node, NULL, error); g_list_free (cs); } else { _foreach_content (sess, node, TRUE, _each_content_accept, NULL, error); } if (*error != NULL) return; set_state (sess, WOCKY_JINGLE_STATE_ACTIVE, WOCKY_JINGLE_REASON_UNKNOWN, NULL); /* Make sure each content knows the session is active */ g_list_foreach (wocky_jingle_session_get_contents (sess), (GFunc) g_object_notify, "state"); if (priv->dialect != WOCKY_JINGLE_DIALECT_V032) { /* If this is a dialect that doesn't support , we treat * session-accept as the cue to remove the ringing flag. */ priv->remote_ringing = FALSE; g_signal_emit (sess, signals[REMOTE_STATE_CHANGED], 0); } } static void mute_all_foreach (gpointer key, gpointer value, gpointer mute) { if (G_OBJECT_TYPE (value) == WOCKY_TYPE_JINGLE_MEDIA_RTP) g_object_set (value, "remote-mute", GPOINTER_TO_INT (mute), NULL); } static void mute_all (WockyJingleSession *sess, gboolean mute) { g_hash_table_foreach (sess->priv->initiator_contents, mute_all_foreach, GINT_TO_POINTER (mute)); g_hash_table_foreach (sess->priv->responder_contents, mute_all_foreach, GINT_TO_POINTER (mute)); } static gboolean set_mute (WockyJingleSession *sess, const gchar *name, const gchar *creator, gboolean mute, GError **error) { WockyJingleContent *c; if (name == NULL) { mute_all (sess, mute); return TRUE; } if (!lookup_content (sess, name, creator, TRUE /* fail if missing */, &c, error)) return FALSE; if (G_OBJECT_TYPE (c) != WOCKY_TYPE_JINGLE_MEDIA_RTP) { g_set_error (error, WOCKY_XMPP_ERROR, WOCKY_XMPP_ERROR_BAD_REQUEST, "content '%s' isn't an RTP session", name); return FALSE; } g_object_set (c, "remote-mute", mute, NULL); return TRUE; } static void set_hold (WockyJingleSession *sess, gboolean hold) { sess->priv->remote_hold = hold; } static void set_ringing (WockyJingleSession *sess, gboolean ringing) { sess->priv->remote_ringing = ringing; } static gboolean handle_payload (WockyJingleSession *sess, WockyNode *payload, gboolean *handled, GError **error) { const gchar *ns = wocky_node_get_ns (payload); const gchar *elt = payload->name; const gchar *name = wocky_node_get_attribute (payload, "name"); const gchar *creator = wocky_node_get_attribute (payload, "creator"); if (wocky_strdiff (ns, WOCKY_XMPP_NS_JINGLE_RTP_INFO)) { *handled = FALSE; return TRUE; } *handled = TRUE; if (!wocky_strdiff (elt, "active")) { /* Clear all states, we're active */ mute_all (sess, FALSE); set_ringing (sess, FALSE); set_hold (sess, FALSE); } else if (!wocky_strdiff (elt, "ringing")) { set_ringing (sess, TRUE); } else if (!wocky_strdiff (elt, "hold")) { set_hold (sess, TRUE); } else if (!wocky_strdiff (elt, "unhold")) { set_hold (sess, FALSE); } /* XEP-0178 says that only and can have a name='' * attribute. */ else if (!wocky_strdiff (elt, "mute")) { return set_mute (sess, name, creator, TRUE, error); } else if (!wocky_strdiff (elt, "unmute")) { return set_mute (sess, name, creator, FALSE, error); } else { g_set_error (error, WOCKY_JINGLE_ERROR, WOCKY_JINGLE_ERROR_UNSUPPORTED_INFO, "<%s> is not known in namespace %s", elt, ns); return FALSE; } return TRUE; } static void on_session_info (WockyJingleSession *sess, WockyNode *node, GError **error) { gboolean understood_a_payload = FALSE; gboolean hit_an_error = FALSE; WockyNodeIter i; WockyNode *n; /* if this is a ping, just ack it. */ if (wocky_node_get_first_child (node) == NULL) return; wocky_node_iter_init (&i, node, NULL, NULL); while (wocky_node_iter_next (&i, &n)) { gboolean handled; GError *e = NULL; if (handle_payload (sess, n, &handled, &e)) { understood_a_payload = understood_a_payload || handled; } else if (hit_an_error) { DEBUG ("already got another error; ignoring %s", e->message); g_error_free (e); } else { DEBUG ("hit an error: %s", e->message); hit_an_error = TRUE; g_propagate_error (error, e); } } /* If we understood something, the remote state (may have) changed. Else, * return an error to the peer. */ if (understood_a_payload) g_signal_emit (sess, signals[REMOTE_STATE_CHANGED], 0); else if (!hit_an_error) g_set_error (error, WOCKY_JINGLE_ERROR, WOCKY_JINGLE_ERROR_UNSUPPORTED_INFO, "no recognized session-info payloads"); } static void on_session_terminate (WockyJingleSession *sess, WockyNode *node, GError **error) { gchar *text = NULL; WockyNode *n = wocky_node_get_child (node, "reason"); WockyJingleReason wocky_jingle_reason = WOCKY_JINGLE_REASON_UNKNOWN; if (n != NULL) extract_reason (n, &wocky_jingle_reason, &text); DEBUG ("remote end terminated the session with reason %s and text '%s'", wocky_jingle_session_get_reason_name (wocky_jingle_reason), (text != NULL ? text : "(none)")); set_state (sess, WOCKY_JINGLE_STATE_ENDED, wocky_jingle_reason, text); g_free (text); } static void on_transport_info (WockyJingleSession *sess, WockyNode *node, GError **error) { WockyJingleSessionPrivate *priv = sess->priv; WockyJingleContent *c = NULL; if (WOCKY_JINGLE_DIALECT_IS_GOOGLE (priv->dialect)) { GHashTableIter iter; gpointer value; if (priv->dialect == WOCKY_JINGLE_DIALECT_GTALK4) { if (!wocky_strdiff (wocky_node_get_attribute (node, "type"), "candidates")) { GList *contents = wocky_jingle_session_get_contents (sess); GList *l; DEBUG ("switching to gtalk3 dialect and retransmiting our candidates"); priv->dialect = WOCKY_JINGLE_DIALECT_GTALK3; for (l = contents; l != NULL; l = l->next) wocky_jingle_content_retransmit_candidates (l->data, TRUE); g_list_free (contents); } else { node = wocky_node_get_child (node, "transport"); if (node == NULL) { g_set_error (error, WOCKY_XMPP_ERROR, WOCKY_XMPP_ERROR_BAD_REQUEST, "transport-info stanza without a "); return; } } } g_hash_table_iter_init (&iter, priv->initiator_contents); while (g_hash_table_iter_next (&iter, NULL, &value)) { c = value; wocky_jingle_content_parse_transport_info (c, node, error); if (error != NULL && *error != NULL) break; } } else { WockyNodeIter i; WockyNode *content_node; GError *e = NULL; wocky_node_iter_init (&i, node, "content", NULL); while (wocky_node_iter_next (&i, &content_node)) { WockyNode *transport_node; if (lookup_content (sess, wocky_node_get_attribute (content_node, "name"), wocky_node_get_attribute (content_node, "creator"), TRUE /* fail_if_missing */, &c, &e)) { /* we need transport child of content node */ transport_node = wocky_node_get_child ( content_node, "transport"); wocky_jingle_content_parse_transport_info (c, transport_node, &e); } /* Save the first error we encounter, but go through all remaining * contents anyway to try and recover as much info as we can */ if (e != NULL && error != NULL && *error == NULL) { *error = e; e = NULL; } g_clear_error (&e); } } } static void on_transport_accept (WockyJingleSession *sess, WockyNode *node, GError **error) { DEBUG ("Ignoring 'transport-accept' action from peer"); } static void on_description_info (WockyJingleSession *sess, WockyNode *node, GError **error) { _foreach_content (sess, node, TRUE, _each_description_info, NULL, error); } static void on_info (WockyJingleSession *sess, WockyNode *node, GError **error) { WockyJingleSessionPrivate *priv = sess->priv; WockyJingleContent *c = NULL; DEBUG ("received info "); if (WOCKY_JINGLE_DIALECT_IS_GOOGLE (priv->dialect)) { GHashTableIter iter; g_hash_table_iter_init (&iter, priv->initiator_contents); while (g_hash_table_iter_next (&iter, NULL, (gpointer) &c)) { wocky_jingle_content_parse_info (c, node, error); if (error != NULL && *error != NULL) break; } } } static HandlerFunc handlers[] = { NULL, /* for unknown action */ on_content_accept, on_content_add, on_content_modify, on_content_remove, on_content_replace, on_content_reject, on_session_accept, /* jingle_on_session_accept */ on_session_info, on_session_initiate, on_session_terminate, /* jingle_on_session_terminate */ on_transport_info, /* jingle_on_transport_info */ on_transport_accept, on_description_info, on_info }; static void wocky_jingle_state_machine_dance (WockyJingleSession *sess, WockyJingleAction action, WockyNode *node, GError **error) { WockyJingleSessionPrivate *priv = sess->priv; /* parser should've checked this already */ g_assert (action_is_allowed (action, priv->state)); g_assert (handlers[action] != NULL); handlers[action] (sess, node, error); } static WockyJingleDialect detect_google_dialect (WockyNode *session_node) { /* The GTALK3 dialect is the only one that supports video at this time */ if (wocky_node_get_child_ns (session_node, "description", WOCKY_XMPP_NS_GOOGLE_SESSION_VIDEO) != NULL) return WOCKY_JINGLE_DIALECT_GTALK3; /* GTalk4 has a transport item, GTalk3 doesn't */ if (wocky_node_get_child_ns (session_node, "transport", WOCKY_XMPP_NS_GOOGLE_TRANSPORT_P2P) == NULL) return WOCKY_JINGLE_DIALECT_GTALK3; return WOCKY_JINGLE_DIALECT_GTALK4; } static const gchar * wocky_jingle_session_detect_internal ( WockyStanza *stanza, WockyJingleAction *action, WockyJingleDialect *dialect, WockyNode **session_node_out) { const gchar *actxt, *sid; WockyNode *iq_node, *session_node; WockyStanzaSubType sub_type; gboolean google_mode = FALSE; /* all jingle actions are sets */ wocky_stanza_get_type_info (stanza, NULL, &sub_type); if (sub_type != WOCKY_STANZA_SUB_TYPE_SET) return NULL; iq_node = wocky_stanza_get_top_node (stanza); if ((NULL == wocky_stanza_get_from (stanza)) || (NULL == wocky_stanza_get_to (stanza))) return NULL; /* first, we try standard jingle */ session_node = wocky_node_get_child_ns (iq_node, "jingle", WOCKY_XMPP_NS_JINGLE); if (session_node != NULL) { if (dialect != NULL) *dialect = WOCKY_JINGLE_DIALECT_V032; } else { /* then, we try a bit older jingle version */ session_node = wocky_node_get_child_ns (iq_node, "jingle", WOCKY_XMPP_NS_JINGLE015); if (session_node != NULL) { if (dialect != NULL) *dialect = WOCKY_JINGLE_DIALECT_V015; } else { /* next, we try googletalk */ session_node = wocky_node_get_child_ns (iq_node, "session", WOCKY_XMPP_NS_GOOGLE_SESSION); if (session_node != NULL) { if (dialect != NULL) *dialect = detect_google_dialect (session_node); google_mode = TRUE; } else { return NULL; } } } if (google_mode) { actxt = wocky_node_get_attribute (session_node, "type"); sid = wocky_node_get_attribute (session_node, "id"); } else { actxt = wocky_node_get_attribute (session_node, "action"); sid = wocky_node_get_attribute (session_node, "sid"); } if (session_node_out != NULL) *session_node_out = session_node; if (action != NULL) *action = parse_action (actxt); return sid; } const gchar * wocky_jingle_session_detect ( WockyStanza *stanza, WockyJingleAction *action, WockyJingleDialect *dialect) { g_return_val_if_fail (WOCKY_IS_STANZA (stanza), NULL); return wocky_jingle_session_detect_internal (stanza, action, dialect, NULL); } gboolean wocky_jingle_session_parse ( WockyJingleSession *sess, WockyJingleAction action, WockyStanza *stanza, GError **error) { WockyJingleSessionPrivate *priv; WockyNode *iq_node, *session_node; const gchar *from, *action_name; g_return_val_if_fail (WOCKY_IS_JINGLE_SESSION (sess), FALSE); g_return_val_if_fail (WOCKY_IS_STANZA (stanza), FALSE); g_return_val_if_fail (error == NULL || *error == NULL, FALSE); priv = sess->priv; /* IQ from/to can come in handy */ from = wocky_stanza_get_from (stanza); iq_node = wocky_stanza_get_top_node (stanza); if (action == WOCKY_JINGLE_ACTION_UNKNOWN) { g_set_error (error, WOCKY_XMPP_ERROR, WOCKY_XMPP_ERROR_BAD_REQUEST, "unknown session action"); return FALSE; } action_name = produce_action (action, priv->dialect); DEBUG ("jingle action '%s' from '%s' in session '%s' dialect %u state %u", action_name, from, priv->sid, priv->dialect, priv->state); switch (priv->dialect) { case WOCKY_JINGLE_DIALECT_V032: session_node = wocky_node_get_child_ns (iq_node, "jingle", WOCKY_XMPP_NS_JINGLE); break; case WOCKY_JINGLE_DIALECT_V015: session_node = wocky_node_get_child_ns (iq_node, "jingle", WOCKY_XMPP_NS_JINGLE015); break; case WOCKY_JINGLE_DIALECT_GTALK3: case WOCKY_JINGLE_DIALECT_GTALK4: session_node = wocky_node_get_child_ns (iq_node, "session", WOCKY_XMPP_NS_GOOGLE_SESSION); break; default: /* just to make gcc happy about dealing with default case */ session_node = NULL; } if (session_node == NULL) { g_set_error (error, WOCKY_XMPP_ERROR, WOCKY_XMPP_ERROR_BAD_REQUEST, "malformed jingle stanza"); return FALSE; } if (!wocky_jingle_session_defines_action (sess, action)) { g_set_error (error, WOCKY_XMPP_ERROR, WOCKY_XMPP_ERROR_BAD_REQUEST, "action '%s' unknown (using dialect %u)", action_name, priv->dialect); return FALSE; } if (!action_is_allowed (action, priv->state)) { g_set_error (error, WOCKY_JINGLE_ERROR, WOCKY_JINGLE_ERROR_OUT_OF_ORDER, "action '%s' not allowed in current state", action_name); return FALSE; } wocky_jingle_state_machine_dance (sess, action, session_node, error); if (*error != NULL) return FALSE; return TRUE; } WockyStanza * wocky_jingle_session_new_message (WockyJingleSession *sess, WockyJingleAction action, WockyNode **sess_node) { WockyJingleSessionPrivate *priv = sess->priv; WockyStanza *stanza; WockyNode *session_node; gchar *el = NULL, *ns = NULL; gboolean gtalk_mode = FALSE; g_return_val_if_fail (WOCKY_IS_JINGLE_SESSION (sess), NULL); g_return_val_if_fail (action != WOCKY_JINGLE_ACTION_UNKNOWN, NULL); g_assert ((action == WOCKY_JINGLE_ACTION_SESSION_INITIATE) || (priv->state > WOCKY_JINGLE_STATE_PENDING_CREATED)); switch (priv->dialect) { case WOCKY_JINGLE_DIALECT_V032: el = "jingle"; ns = WOCKY_XMPP_NS_JINGLE; break; case WOCKY_JINGLE_DIALECT_V015: el = "jingle"; ns = WOCKY_XMPP_NS_JINGLE015; break; case WOCKY_JINGLE_DIALECT_GTALK3: case WOCKY_JINGLE_DIALECT_GTALK4: el = "session"; ns = WOCKY_XMPP_NS_GOOGLE_SESSION; gtalk_mode = TRUE; break; case WOCKY_JINGLE_DIALECT_ERROR: g_assert_not_reached (); } stanza = wocky_stanza_build ( WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_SET, NULL, priv->peer_jid, '(', el, ':', ns, '*', &session_node, ')', NULL); wocky_node_set_attributes (session_node, "initiator", priv->initiator, (gtalk_mode) ? "id" : "sid", priv->sid, (gtalk_mode) ? "type" : "action", produce_action (action, priv->dialect), NULL); if (sess_node != NULL) *sess_node = session_node; return stanza; } typedef void (*ContentMapperFunc) (WockyJingleSession *sess, WockyJingleContent *c, gpointer user_data); static void _map_initial_contents (WockyJingleSession *sess, ContentMapperFunc mapper, gpointer user_data) { GList *li; GList *contents = wocky_jingle_session_get_contents (sess); for (li = contents; li; li = li->next) { WockyJingleContent *c = WOCKY_JINGLE_CONTENT (li->data); const gchar *disposition = wocky_jingle_content_get_disposition (c); if (!wocky_strdiff (disposition, "session")) mapper (sess, c, user_data); } g_list_free (contents); } static void _check_content_ready (WockyJingleSession *sess, WockyJingleContent *c, gpointer user_data) { gboolean *ready = (gboolean *) user_data; if (!wocky_jingle_content_is_ready (c)) { *ready = FALSE; } } static void _transmit_candidates (WockyJingleSession *sess, WockyJingleContent *c, gpointer user_data) { wocky_jingle_content_retransmit_candidates (c, FALSE); } static void _fill_content (WockyJingleSession *sess, WockyJingleContent *c, gpointer user_data) { WockyNode *sess_node = user_data; WockyNode *transport_node; WockyJingleContentState state; wocky_jingle_content_produce_node (c, sess_node, TRUE, TRUE, &transport_node); wocky_jingle_content_inject_candidates (c, transport_node); g_object_get (c, "state", &state, NULL); if (state == WOCKY_JINGLE_CONTENT_STATE_EMPTY) { g_object_set (c, "state", WOCKY_JINGLE_CONTENT_STATE_SENT, NULL); } else if (state == WOCKY_JINGLE_CONTENT_STATE_NEW) { g_object_set (c, "state", WOCKY_JINGLE_CONTENT_STATE_ACKNOWLEDGED, NULL); } else { DEBUG ("content %p is in state %u", c, state); g_assert_not_reached (); } } /** * wocky_jingle_session_send: * @sess: a session * @stanza: (transfer full): a stanza, of which this function will take ownership * * A shorthand for sending a Jingle IQ without waiting for the reply. */ void wocky_jingle_session_send (WockyJingleSession *sess, WockyStanza *stanza) { g_return_if_fail (WOCKY_IS_JINGLE_SESSION (sess)); g_return_if_fail (WOCKY_IS_STANZA (stanza)); wocky_porter_send_iq_async (sess->priv->porter, stanza, NULL, NULL, NULL); g_object_unref (stanza); } static void _on_initiate_reply ( GObject *source, GAsyncResult *result, gpointer user_data) { WockyPorter *porter = WOCKY_PORTER (source); WockyJingleSession *sess = WOCKY_JINGLE_SESSION (user_data); WockyJingleSessionPrivate *priv = sess->priv; WockyStanza *reply; if (priv->state != WOCKY_JINGLE_STATE_PENDING_INITIATE_SENT) { DEBUG ("Ignoring session-initiate reply; session %p is in state %u.", sess, priv->state); g_object_unref (sess); return; } reply = wocky_porter_send_iq_finish (porter, result, NULL); if (reply != NULL && !wocky_stanza_extract_errors (reply, NULL, NULL, NULL, NULL)) { set_state (sess, WOCKY_JINGLE_STATE_PENDING_INITIATED, 0, NULL); if (priv->dialect != WOCKY_JINGLE_DIALECT_V032) { /* If this is a dialect that doesn't support , we treat the * session-initiate being acked as the cue to say we're ringing. */ priv->remote_ringing = TRUE; g_signal_emit (sess, signals[REMOTE_STATE_CHANGED], 0); } } else { set_state (sess, WOCKY_JINGLE_STATE_ENDED, WOCKY_JINGLE_REASON_UNKNOWN, NULL); } g_clear_object (&reply); g_object_unref (sess); } static void _on_accept_reply ( GObject *source, GAsyncResult *result, gpointer user_data) { WockyPorter *porter = WOCKY_PORTER (source); WockyJingleSession *sess = WOCKY_JINGLE_SESSION (user_data); WockyJingleSessionPrivate *priv = sess->priv; WockyStanza *reply; if (priv->state != WOCKY_JINGLE_STATE_PENDING_ACCEPT_SENT) { DEBUG ("Ignoring session-accept reply; session %p is in state %u.", sess, priv->state); g_object_unref (sess); return; } reply = wocky_porter_send_iq_finish (porter, result, NULL); if (reply != NULL && !wocky_stanza_extract_errors (reply, NULL, NULL, NULL, NULL)) { set_state (sess, WOCKY_JINGLE_STATE_ACTIVE, 0, NULL); wocky_jingle_session_send_rtp_info (sess, "active"); } else { set_state (sess, WOCKY_JINGLE_STATE_ENDED, WOCKY_JINGLE_REASON_UNKNOWN, NULL); } g_clear_object (&reply); g_object_unref (sess); } static void try_session_initiate_or_accept (WockyJingleSession *sess) { WockyJingleSessionPrivate *priv = sess->priv; WockyStanza *msg; WockyNode *sess_node; gboolean contents_ready = TRUE; WockyJingleAction action; WockyJingleState new_state; GAsyncReadyCallback handler; DEBUG ("Trying initiate or accept"); /* If there are no contents yet, we shouldn't have been called at all. */ g_assert (g_hash_table_size (priv->initiator_contents) + g_hash_table_size (priv->responder_contents) > 0); if (priv->local_initiator) { if (priv->state != WOCKY_JINGLE_STATE_PENDING_CREATED) { DEBUG ("session is in state %u, won't try to initiate", priv->state); return; } if (!priv->locally_accepted) { DEBUG ("session not locally accepted yet, not initiating"); return; } action = WOCKY_JINGLE_ACTION_SESSION_INITIATE; new_state = WOCKY_JINGLE_STATE_PENDING_INITIATE_SENT; handler = _on_initiate_reply; } else { if (priv->state != WOCKY_JINGLE_STATE_PENDING_INITIATED) { DEBUG ("session is in state %u, won't try to accept", priv->state); return; } if (!priv->locally_accepted) { DEBUG ("session not locally accepted yet, not accepting"); return; } action = WOCKY_JINGLE_ACTION_SESSION_ACCEPT; new_state = WOCKY_JINGLE_STATE_PENDING_ACCEPT_SENT; handler = _on_accept_reply; } _map_initial_contents (sess, _check_content_ready, &contents_ready); DEBUG ("Contents are ready: %s", contents_ready ? "yes" : "no"); if (!contents_ready) { DEBUG ("Contents not yet ready, not initiating/accepting now.."); return; } if (action == WOCKY_JINGLE_ACTION_SESSION_INITIATE) g_signal_emit (sess, signals[ABOUT_TO_INITIATE], 0); msg = wocky_jingle_session_new_message (sess, action, &sess_node); if (priv->dialect == WOCKY_JINGLE_DIALECT_GTALK3) { gboolean has_video = FALSE; gboolean has_audio = FALSE; GHashTableIter iter; gpointer value; g_hash_table_iter_init (&iter, priv->initiator_contents); while (g_hash_table_iter_next (&iter, NULL, &value)) { WockyJingleMediaType type; g_object_get (value, "media-type", &type, NULL); if (type == WOCKY_JINGLE_MEDIA_TYPE_VIDEO) { has_video = TRUE; } else if (type == WOCKY_JINGLE_MEDIA_TYPE_AUDIO) { has_audio = TRUE; } } if (has_video || has_audio) { sess_node = wocky_node_add_child_ns_q (sess_node, "description", g_quark_from_static_string (has_video ? WOCKY_XMPP_NS_GOOGLE_SESSION_VIDEO : WOCKY_XMPP_NS_GOOGLE_SESSION_PHONE)); } } _map_initial_contents (sess, _fill_content, sess_node); wocky_porter_send_iq_async (priv->porter, msg, NULL, handler, g_object_ref (sess)); g_object_unref (msg); set_state (sess, new_state, 0, NULL); /* now all initial contents can transmit their candidates */ _map_initial_contents (sess, _transmit_candidates, NULL); } /** * set_state: * @sess: a jingle session * @state: the new state for the session * @termination_reason: if @state is WOCKY_JINGLE_STATE_ENDED, the reason the session * ended. Otherwise, must be WOCKY_JINGLE_REASON_UNKNOWN. * @text: if @state is WOCKY_JINGLE_STATE_ENDED, the human-readable reason the session * ended. */ static void set_state (WockyJingleSession *sess, WockyJingleState state, WockyJingleReason termination_reason, const gchar *text) { WockyJingleSessionPrivate *priv = sess->priv; if (state <= priv->state) { DEBUG ("ignoring request to set state from %u back to %u", priv->state, state); return; } if (state != WOCKY_JINGLE_STATE_ENDED) g_assert (termination_reason == WOCKY_JINGLE_REASON_UNKNOWN); DEBUG ("Setting state of JingleSession: %p (priv = %p) from %u to %u", sess, priv, priv->state, state); priv->state = state; g_object_notify (G_OBJECT (sess), "state"); /* If we have an outstanding "you're on hold notification", send it */ if (priv->local_hold && state >= WOCKY_JINGLE_STATE_PENDING_INITIATED && state < WOCKY_JINGLE_STATE_ENDED) wocky_jingle_session_send_held (sess); if (state == WOCKY_JINGLE_STATE_ENDED) g_signal_emit (sess, signals[TERMINATED], 0, priv->locally_terminated, termination_reason, text); } /** * wocky_jingle_session_accept: * @sess: the session. * * For incoming calls, accepts the call. For outgoing calls, indicates that the * initial contents for the call have been created and the offer can be sent to * the peer. * * The acceptance or offer will only be signalled to the peer once all contents * are ready (as returned by wocky_jingle_content_is_ready()). For an RTP * session with #WockyJingleMediaRtp contents, this translates to a media * description and transport candidates having been provided to all contents. */ void wocky_jingle_session_accept (WockyJingleSession *sess) { g_return_if_fail (WOCKY_IS_JINGLE_SESSION (sess)); sess->priv->locally_accepted = TRUE; try_session_initiate_or_accept (sess); } const gchar * wocky_jingle_session_get_reason_name (WockyJingleReason reason) { GEnumClass *klass = g_type_class_ref (wocky_jingle_reason_get_type ()); GEnumValue *enum_value = g_enum_get_value (klass, (gint) reason); g_return_val_if_fail (enum_value != NULL, NULL); return enum_value->value_nick; } /** * wocky_jingle_session_terminate: * @sess: the session * @reason: the reason the session should be terminated * @text: (allow-none): human-readable information about why the session * terminated * @error: Unused, because this function never fails. * * Ends a session. * * If called for an outgoing session which has not yet been signalled to the * peer (perhaps because wocky_jingle_session_accept() has not been called, or * codecs or candidates have not been provided), the session will quietly * terminate without the peer hearing anything about it. * * If called for an already-terminated session, this is a no-op. * * Returns: %TRUE. */ gboolean wocky_jingle_session_terminate (WockyJingleSession *sess, WockyJingleReason reason, const gchar *text, GError **error G_GNUC_UNUSED) { WockyJingleSessionPrivate *priv; const gchar *reason_elt; g_return_val_if_fail (WOCKY_IS_JINGLE_SESSION (sess), FALSE); priv = sess->priv; if (priv->state == WOCKY_JINGLE_STATE_ENDED) { DEBUG ("session already terminated, ignoring terminate request"); return TRUE; } if (reason == WOCKY_JINGLE_REASON_UNKNOWN) reason = (priv->state == WOCKY_JINGLE_STATE_ACTIVE) ? WOCKY_JINGLE_REASON_SUCCESS : WOCKY_JINGLE_REASON_CANCEL; reason_elt = wocky_jingle_session_get_reason_name (reason); if (priv->state != WOCKY_JINGLE_STATE_PENDING_CREATED) { WockyNode *session_node; WockyStanza *msg = wocky_jingle_session_new_message (sess, WOCKY_JINGLE_ACTION_SESSION_TERMINATE, &session_node); if (priv->dialect == WOCKY_JINGLE_DIALECT_V032 && reason_elt != NULL) { WockyNode *r = wocky_node_add_child_with_content (session_node, "reason", NULL); wocky_node_add_child (r, reason_elt); if (text != NULL && *text != '\0') wocky_node_add_child_with_content (r, "text", text); } wocky_jingle_session_send (sess, msg); } /* NOTE: on "terminated", jingle factory and media channel will unref * it, bringing refcount to 0, so dispose will be called, and it * takes care of cleanup */ DEBUG ("we are terminating this session"); priv->locally_terminated = TRUE; set_state (sess, WOCKY_JINGLE_STATE_ENDED, reason, text); return TRUE; } static void _foreach_count_active_contents (gpointer key, gpointer value, gpointer user_data) { WockyJingleContent *c = value; guint *n_contents = user_data; WockyJingleContentState state; g_object_get (c, "state", &state, NULL); if ((state >= WOCKY_JINGLE_CONTENT_STATE_NEW) && (state < WOCKY_JINGLE_CONTENT_STATE_REMOVING)) { *n_contents = *n_contents + 1; } } static gboolean count_active_contents (WockyJingleSession *sess) { WockyJingleSessionPrivate *priv = sess->priv; guint n_contents = 0; g_hash_table_foreach (priv->initiator_contents, _foreach_count_active_contents, &n_contents); g_hash_table_foreach (priv->responder_contents, _foreach_count_active_contents, &n_contents); return n_contents; } static void content_removed_cb (WockyJingleContent *c, gpointer user_data) { WockyJingleSession *sess = WOCKY_JINGLE_SESSION (user_data); WockyJingleSessionPrivate *priv = sess->priv; const gchar *name = wocky_jingle_content_get_name (c); if (wocky_jingle_content_creator_is_initiator (c)) g_hash_table_remove (priv->initiator_contents, name); else g_hash_table_remove (priv->responder_contents, name); if (priv->state == WOCKY_JINGLE_STATE_ENDED) return; if (count_active_contents (sess) == 0) { wocky_jingle_session_terminate (sess, WOCKY_JINGLE_REASON_UNKNOWN, NULL, NULL); } else { /* It's possible the content now removed was * blocking us from creating or accepting the * session, so we might as well try now. */ try_session_initiate_or_accept (sess); } } void wocky_jingle_session_remove_content (WockyJingleSession *sess, WockyJingleContent *c) { g_return_if_fail (WOCKY_IS_JINGLE_SESSION (sess)); g_return_if_fail (WOCKY_IS_JINGLE_CONTENT (c)); if (count_active_contents (sess) > 1) { wocky_jingle_content_remove (c, TRUE); } else { /* session will be terminated when the content gets marked as removed */ DEBUG ("called for last active content, doing session-terminate instead"); wocky_jingle_content_remove (c, FALSE); } } /** * wocky_jingle_session_add_content: * @sess: the session * @mtype: what kind of media will be exchanged on the content * @senders: which directions media should initially flow in. * @name: (allow-none): a descriptive name to use for the content; this is * typically not shown to users * @content_ns: the namespace to use for the content's description * @transport_ns: the namespace of the media transport to use for the call * * Adds a content to the session. Once it has its codecs and transport * candidates filled in, it will be signalled to the peer (either as part of * the session-initiate, if it has not been sent yet, or as a content-add if * @sess has already been initiated). * * Legal values for @content_ns and @transport_ns depend on the Jingle dialect * in use for this session (and in some cases on @mtype); sensible values * depend on the peer's capabilities. * * Returns: (transfer none): the new content, which is guaranteed not to be %NULL. */ WockyJingleContent * wocky_jingle_session_add_content (WockyJingleSession *sess, WockyJingleMediaType mtype, WockyJingleContentSenders senders, const gchar *name, const gchar *content_ns, const gchar *transport_ns) { WockyJingleSessionPrivate *priv; WockyJingleContent *c; GType content_type; GHashTable *contents; guint id; gchar *cname = NULL; g_return_val_if_fail (WOCKY_IS_JINGLE_SESSION (sess), NULL); priv = sess->priv; contents = priv->local_initiator ? priv->initiator_contents : priv->responder_contents; id = g_hash_table_size (contents) + 1; if (name == NULL || *name == '\0') name = (mtype == WOCKY_JINGLE_MEDIA_TYPE_AUDIO ? "Audio" : "Video"); cname = g_strdup (name); while (g_hash_table_lookup (priv->initiator_contents, cname) != NULL || g_hash_table_lookup (priv->responder_contents, cname) != NULL) { g_free (cname); cname = g_strdup_printf ("%s_%d", name, id++); } content_type = wocky_jingle_factory_lookup_content_type ( wocky_jingle_session_get_factory (sess), content_ns); g_assert (content_type != 0); c = create_content (sess, content_type, mtype, senders, content_ns, transport_ns, cname, NULL, NULL); /* The new content better have ended up in the set we thought it would... */ g_assert (g_hash_table_lookup (contents, cname) != NULL); g_free (cname); return c; } /* Get any content. Either we're in google mode (so we only have one content * anyways), or we just need any content type to figure out what use case * we're in (media, ft, etc). */ static WockyJingleContent * _get_any_content (WockyJingleSession *session) { WockyJingleContent *c; GList *li = wocky_jingle_session_get_contents (session); if (li == NULL) return NULL; c = li->data; g_list_free (li); return c; } /* Note: if there are multiple content types, not guaranteed which one will * be returned. Typically, the same GType will know how to handle related * contents found in a session (e.g. media-rtp for audio/video), so that * should not be a problem. Returns 0 if there are no contents yet. */ GType wocky_jingle_session_get_content_type (WockyJingleSession *sess) { WockyJingleContent *c; g_return_val_if_fail (WOCKY_IS_JINGLE_SESSION (sess), G_TYPE_INVALID); c = _get_any_content (sess); if (c == NULL) return 0; return G_OBJECT_TYPE (c); } /* FIXME: probably should make this into a property */ GList * wocky_jingle_session_get_contents (WockyJingleSession *sess) { WockyJingleSessionPrivate *priv; g_return_val_if_fail (WOCKY_IS_JINGLE_SESSION (sess), NULL); priv = sess->priv; return g_list_concat (g_hash_table_get_values (priv->initiator_contents), g_hash_table_get_values (priv->responder_contents)); } const gchar * wocky_jingle_session_get_peer_resource (WockyJingleSession *sess) { g_return_val_if_fail (WOCKY_IS_JINGLE_SESSION (sess), NULL); return sess->priv->peer_resource; } const gchar * wocky_jingle_session_get_initiator (WockyJingleSession *sess) { g_return_val_if_fail (WOCKY_IS_JINGLE_SESSION (sess), NULL); return sess->priv->initiator; } const gchar * wocky_jingle_session_get_sid (WockyJingleSession *sess) { g_return_val_if_fail (WOCKY_IS_JINGLE_SESSION (sess), NULL); return sess->priv->sid; } static void content_ready_cb (WockyJingleContent *c, gpointer user_data) { WockyJingleSession *sess = WOCKY_JINGLE_SESSION (user_data); const gchar *disposition; DEBUG ("called"); disposition = wocky_jingle_content_get_disposition (c); /* This assertion is actually safe, because 'ready' is only emitted by * contents with disposition "session". But this is crazy. */ g_assert (!wocky_strdiff (disposition, "session")); try_session_initiate_or_accept (sess); } static void wocky_jingle_session_send_rtp_info (WockyJingleSession *sess, const gchar *name) { WockyStanza *message; WockyNode *jingle; if (!wocky_jingle_session_defines_action (sess, WOCKY_JINGLE_ACTION_SESSION_INFO)) { DEBUG ("Not sending <%s/>; not using modern Jingle", name); return; } message = wocky_jingle_session_new_message (sess, WOCKY_JINGLE_ACTION_SESSION_INFO, &jingle); wocky_node_add_child_ns_q (jingle, name, g_quark_from_static_string (WOCKY_XMPP_NS_JINGLE_RTP_INFO)); /* This is just informational, so ignoring the reply. */ wocky_jingle_session_send (sess, message); } static void wocky_jingle_session_send_held (WockyJingleSession *sess) { const gchar *s = (sess->priv->local_hold ? "hold" : "unhold"); wocky_jingle_session_send_rtp_info (sess, s); } void wocky_jingle_session_set_local_hold (WockyJingleSession *sess, gboolean held) { g_return_if_fail (WOCKY_IS_JINGLE_SESSION (sess)); g_object_set (sess, "local-hold", held, NULL); } gboolean wocky_jingle_session_get_remote_hold (WockyJingleSession *sess) { g_return_val_if_fail (WOCKY_IS_JINGLE_SESSION (sess), FALSE); return sess->priv->remote_hold; } gboolean wocky_jingle_session_get_remote_ringing (WockyJingleSession *sess) { g_return_val_if_fail (WOCKY_IS_JINGLE_SESSION (sess), FALSE); return sess->priv->remote_ringing; } gboolean wocky_jingle_session_can_modify_contents (WockyJingleSession *sess) { g_return_val_if_fail (WOCKY_IS_JINGLE_SESSION (sess), FALSE); return !WOCKY_JINGLE_DIALECT_IS_GOOGLE (sess->priv->dialect) && !wocky_jingle_session_peer_has_cap (sess, WOCKY_QUIRK_GOOGLE_WEBMAIL_CLIENT); } WockyJingleDialect wocky_jingle_session_get_dialect (WockyJingleSession *sess) { g_return_val_if_fail (WOCKY_IS_JINGLE_SESSION (sess), WOCKY_JINGLE_DIALECT_ERROR); return sess->priv->dialect; } WockyContact * wocky_jingle_session_get_peer_contact (WockyJingleSession *self) { g_return_val_if_fail (WOCKY_IS_JINGLE_SESSION (self), NULL); return self->priv->peer_contact; } /* * wocky_jingle_session_get_peer_jid: * @sess: a jingle session * * Returns: the full JID of the remote contact. */ const gchar * wocky_jingle_session_get_peer_jid (WockyJingleSession *sess) { g_return_val_if_fail (WOCKY_IS_JINGLE_SESSION (sess), NULL); return sess->priv->peer_jid; } WockyJingleFactory * wocky_jingle_session_get_factory (WockyJingleSession *self) { g_return_val_if_fail (WOCKY_IS_JINGLE_SESSION (self), NULL); return self->priv->jingle_factory; } WockyPorter * wocky_jingle_session_get_porter (WockyJingleSession *self) { g_return_val_if_fail (WOCKY_IS_JINGLE_SESSION (self), NULL); return self->priv->porter; } void wocky_jingle_session_acknowledge_iq (WockyJingleSession *self, WockyStanza *stanza) { g_return_if_fail (WOCKY_IS_JINGLE_SESSION (self)); g_return_if_fail (WOCKY_IS_STANZA (stanza)); if (wocky_jingle_session_peer_has_cap (self, WOCKY_QUIRK_GOOGLE_WEBMAIL_CLIENT)) { WockyJingleAction action = WOCKY_JINGLE_ACTION_UNKNOWN; WockyNode *used_node = NULL; /* As of 2013-05-29, the Google webmail client sends a session-initiate * IQ with two child nodes (which is not valid XMPP Core but never mind) * and replies to session-initiate by echoing the child for the dialect * it chose. We have to do the same echoing, otherwise it can't call us. * * It doesn't seem to reply to our other IQs at all; we still reply * here (we'd be violating XMPP Core if we didn't), but we don't * bother putting content in the IQ, to reduce bandwidth. */ if (wocky_jingle_session_detect_internal (stanza, &action, NULL, &used_node) != NULL && action == WOCKY_JINGLE_ACTION_SESSION_INITIATE) { WockyStanza *reply = wocky_stanza_build_iq_result (stanza, NULL); if (reply != NULL) { WockyNode *reply_node = wocky_stanza_get_top_node (reply); reply_node->children = g_slist_append (reply_node->children, _wocky_node_copy (used_node)); wocky_porter_send (self->priv->porter, reply); g_object_unref (reply); return; } } } /* normal Jingle just says "OK" without echoing */ wocky_porter_acknowledge_iq (self->priv->porter, stanza, NULL); } telepathy-gabble-0.18.2/lib/ext/wocky/wocky/wocky-jingle-media-rtp.c0000644000175000017500000013201412200204546025275 0ustar00smcvsmcv00000000000000/* * wocky-jingle-media-rtp.c - Source for WockyJingleMediaRtp * * Copyright (C) 2008 Collabora Ltd. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 */ /* Media/RTP content type deals with audio/video content, ie. jingle calls. It * supports standard Jingle drafts (v0.15, v0.26) and Google's jingle variants * (libjingle 0.3/0.4). */ #include "config.h" #include "wocky-jingle-media-rtp.h" #include #include #include #include #define WOCKY_DEBUG_FLAG WOCKY_DEBUG_JINGLE #include "wocky-debug-internal.h" #include "wocky-jingle-content.h" #include "wocky-jingle-factory.h" #include "wocky-jingle-session.h" #include "wocky-namespaces.h" #include "wocky-jingle-transport-google.h" #include "wocky-utils.h" G_DEFINE_TYPE (WockyJingleMediaRtp, wocky_jingle_media_rtp, WOCKY_TYPE_JINGLE_CONTENT); /* signal enum */ enum { REMOTE_MEDIA_DESCRIPTION, LAST_SIGNAL }; static guint signals[LAST_SIGNAL] = {0}; /* properties */ enum { PROP_MEDIA_TYPE = 1, PROP_REMOTE_MUTE, LAST_PROPERTY }; typedef enum { WOCKY_JINGLE_MEDIA_PROFILE_RTP_AVP, } WockyJingleMediaProfile; struct _WockyJingleMediaRtpPrivate { WockyJingleMediaDescription *local_media_description; /* Holds (WockyJingleCodec *)'s borrowed from local_media_description, * namely codecs which have changed from local_media_description's * previous value. Since the contents are borrowed, this must be * freed with g_list_free, not jingle_media_rtp_free_codecs(). */ GList *local_codec_updates; WockyJingleMediaDescription *remote_media_description; WockyJingleMediaType media_type; gboolean remote_mute; gboolean has_rtcp_fb; gboolean has_rtp_hdrext; gboolean dispose_has_run; }; static void wocky_jingle_media_rtp_init (WockyJingleMediaRtp *obj) { WockyJingleMediaRtpPrivate *priv = G_TYPE_INSTANCE_GET_PRIVATE (obj, WOCKY_TYPE_JINGLE_MEDIA_RTP, WockyJingleMediaRtpPrivate); obj->priv = priv; priv->dispose_has_run = FALSE; } /** * jingle_media_rtp_codec_new: * @id: a codec ID, as specified in tables 4 and 5 of RFC 3551. * @name: (allow-none): the codec's name. This is optional if @id is one of the * statically-defined codec identifiers, and required if @id is in the range * 96–127. (This is not enforced by this library.) * @clockrate: the clock rate for this codec, or 0 to not specify a clock rate. * @channels: the number of channels, or 0 to leave this unspecified (which the * peer should interpret as the default value, 1). * @params: (element-type utf8 utf8) (transfer none) (allow-none): parameters * for this codec. This is referenced, not copied, so you should avoid * modifying this parameter after calling this function. * * Creates a new structure describing a codec, suitable for including in a * #WockyJingleMediaDescription. * * Returns: (transfer full): the codec description. */ WockyJingleCodec * jingle_media_rtp_codec_new ( guint id, const gchar *name, guint clockrate, guint channels, GHashTable *params) { WockyJingleCodec *p = g_slice_new0 (WockyJingleCodec); p->id = id; p->name = g_strdup (name); p->clockrate = clockrate; p->channels = channels; p->trr_int = G_MAXUINT; if (params != NULL) { g_hash_table_ref (params); p->params = params; } else { p->params = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); } return p; } static GList * wocky_jingle_feedback_message_list_copy (GList *fbs) { GQueue new = G_QUEUE_INIT; GList *li; for (li = fbs; li; li = li->next) { WockyJingleFeedbackMessage *fb = li->data; g_queue_push_tail (&new, wocky_jingle_feedback_message_new (fb->type, fb->subtype)); } return new.head; } static void wocky_jingle_feedback_message_list_free (GList *fbs) { while (fbs != NULL) { wocky_jingle_feedback_message_free (fbs->data); fbs = g_list_delete_link (fbs, fbs); } } void jingle_media_rtp_codec_free (WockyJingleCodec *p) { g_hash_table_unref (p->params); g_free (p->name); wocky_jingle_feedback_message_list_free (p->feedback_msgs); g_slice_free (WockyJingleCodec, p); } static void add_codec_to_table (WockyJingleCodec *codec, GHashTable *table) { g_hash_table_insert (table, GUINT_TO_POINTER ((guint) codec->id), codec); } static GHashTable * build_codec_table (GList *codecs) { GHashTable *table = g_hash_table_new (NULL, NULL); g_list_foreach (codecs, (GFunc) add_codec_to_table, table); return table; } GList * jingle_media_rtp_copy_codecs (GList *codecs) { GList *ret = NULL, *l; for (l = codecs; l != NULL; l = g_list_next (l)) { WockyJingleCodec *c = l->data; WockyJingleCodec *newc = jingle_media_rtp_codec_new (c->id, c->name, c->clockrate, c->channels, c->params); newc->trr_int = c->trr_int; ret = g_list_append (ret, newc); } return ret; } void jingle_media_rtp_free_codecs (GList *codecs) { while (codecs != NULL) { jingle_media_rtp_codec_free (codecs->data); codecs = g_list_delete_link (codecs, codecs); } } static void wocky_jingle_media_rtp_dispose (GObject *object) { WockyJingleMediaRtp *trans = WOCKY_JINGLE_MEDIA_RTP (object); WockyJingleMediaRtpPrivate *priv = trans->priv; if (priv->dispose_has_run) return; DEBUG ("dispose called"); priv->dispose_has_run = TRUE; if (priv->remote_media_description != NULL) wocky_jingle_media_description_free (priv->remote_media_description); priv->remote_media_description = NULL; if (priv->local_media_description != NULL) wocky_jingle_media_description_free (priv->local_media_description); priv->local_media_description = NULL; if (priv->local_codec_updates != NULL) { DEBUG ("We have an unsent codec parameter update! Weird."); g_list_free (priv->local_codec_updates); priv->local_codec_updates = NULL; } if (G_OBJECT_CLASS (wocky_jingle_media_rtp_parent_class)->dispose) G_OBJECT_CLASS (wocky_jingle_media_rtp_parent_class)->dispose (object); } static void wocky_jingle_media_rtp_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { WockyJingleMediaRtp *trans = WOCKY_JINGLE_MEDIA_RTP (object); WockyJingleMediaRtpPrivate *priv = trans->priv; switch (property_id) { case PROP_MEDIA_TYPE: g_value_set_uint (value, priv->media_type); break; case PROP_REMOTE_MUTE: g_value_set_boolean (value, priv->remote_mute); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void wocky_jingle_media_rtp_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { WockyJingleMediaRtp *trans = WOCKY_JINGLE_MEDIA_RTP (object); WockyJingleMediaRtpPrivate *priv = trans->priv; switch (property_id) { case PROP_MEDIA_TYPE: priv->media_type = g_value_get_uint (value); break; case PROP_REMOTE_MUTE: priv->remote_mute = g_value_get_boolean (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void parse_description (WockyJingleContent *content, WockyNode *desc_node, GError **error); static void produce_description (WockyJingleContent *obj, WockyNode *content_node); static void transport_created (WockyJingleContent *obj, WockyJingleTransportIface *transport); static void wocky_jingle_media_rtp_class_init (WockyJingleMediaRtpClass *cls) { GObjectClass *object_class = G_OBJECT_CLASS (cls); WockyJingleContentClass *content_class = WOCKY_JINGLE_CONTENT_CLASS (cls); GParamSpec *param_spec; g_type_class_add_private (cls, sizeof (WockyJingleMediaRtpPrivate)); object_class->get_property = wocky_jingle_media_rtp_get_property; object_class->set_property = wocky_jingle_media_rtp_set_property; object_class->dispose = wocky_jingle_media_rtp_dispose; content_class->parse_description = parse_description; content_class->produce_description = produce_description; content_class->transport_created = transport_created; param_spec = g_param_spec_uint ("media-type", "RTP media type", "Media type.", WOCKY_JINGLE_MEDIA_TYPE_NONE, G_MAXUINT32, WOCKY_JINGLE_MEDIA_TYPE_NONE, G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, PROP_MEDIA_TYPE, param_spec); param_spec = g_param_spec_boolean ("remote-mute", "Remote mute", "TRUE if the peer has muted this stream", FALSE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, PROP_REMOTE_MUTE, param_spec); /* signal definitions */ /** * WockyJingleMediaRtp::remote-media-description: * @content: the RTP content * @md: a #WockyJingleMediaDescription * * Emitted when the remote media description is received or subsequently updated. */ signals[REMOTE_MEDIA_DESCRIPTION] = g_signal_new ("remote-media-description", G_TYPE_FROM_CLASS (cls), G_SIGNAL_RUN_LAST, 0, NULL, NULL, g_cclosure_marshal_VOID__POINTER, G_TYPE_NONE, 1, G_TYPE_POINTER); } static void transport_created (WockyJingleContent *content, WockyJingleTransportIface *transport) { WockyJingleMediaRtp *self = WOCKY_JINGLE_MEDIA_RTP (content); WockyJingleMediaRtpPrivate *priv = self->priv; WockyJingleTransportGoogle *gtrans = NULL; WockyJingleDialect dialect; if (WOCKY_IS_JINGLE_TRANSPORT_GOOGLE (transport)) { gtrans = WOCKY_JINGLE_TRANSPORT_GOOGLE (transport); dialect = wocky_jingle_session_get_dialect (content->session); if (priv->media_type == WOCKY_JINGLE_MEDIA_TYPE_VIDEO && (WOCKY_JINGLE_DIALECT_IS_GOOGLE (dialect) || wocky_jingle_session_peer_has_cap (content->session, WOCKY_QUIRK_GOOGLE_WEBMAIL_CLIENT) || wocky_jingle_session_peer_has_cap (content->session, WOCKY_QUIRK_ANDROID_GTALK_CLIENT))) { jingle_transport_google_set_component_name (gtrans, "video_rtp", 1); jingle_transport_google_set_component_name (gtrans, "video_rtcp", 2); } else { jingle_transport_google_set_component_name (gtrans, "rtp", 1); jingle_transport_google_set_component_name (gtrans, "rtcp", 2); } } } static WockyJingleMediaType extract_media_type (WockyNode *desc_node, GError **error) { if (wocky_node_has_ns (desc_node, WOCKY_XMPP_NS_JINGLE_RTP)) { const gchar *type = wocky_node_get_attribute (desc_node, "media"); if (type == NULL) { g_set_error (error, WOCKY_XMPP_ERROR, WOCKY_XMPP_ERROR_BAD_REQUEST, "missing required media type attribute"); return WOCKY_JINGLE_MEDIA_TYPE_NONE; } if (!wocky_strdiff (type, "audio")) return WOCKY_JINGLE_MEDIA_TYPE_AUDIO; if (!wocky_strdiff (type, "video")) return WOCKY_JINGLE_MEDIA_TYPE_VIDEO; g_set_error (error, WOCKY_XMPP_ERROR, WOCKY_XMPP_ERROR_BAD_REQUEST, "unknown media type %s", type); return WOCKY_JINGLE_MEDIA_TYPE_NONE; } if (wocky_node_has_ns (desc_node, WOCKY_XMPP_NS_JINGLE_DESCRIPTION_AUDIO)) return WOCKY_JINGLE_MEDIA_TYPE_AUDIO; if (wocky_node_has_ns (desc_node, WOCKY_XMPP_NS_JINGLE_DESCRIPTION_VIDEO)) return WOCKY_JINGLE_MEDIA_TYPE_VIDEO; if (wocky_node_has_ns (desc_node, WOCKY_XMPP_NS_GOOGLE_SESSION_PHONE)) return WOCKY_JINGLE_MEDIA_TYPE_AUDIO; if (wocky_node_has_ns (desc_node, WOCKY_XMPP_NS_GOOGLE_SESSION_VIDEO)) return WOCKY_JINGLE_MEDIA_TYPE_VIDEO; /* If we get here, namespace in use is not one of namespaces we signed up * with, so obviously a bug somewhere. */ g_assert_not_reached (); } static WockyJingleFeedbackMessage * parse_rtcp_fb (WockyJingleContent *content, WockyNode *node) { const gchar *pt_ns = wocky_node_get_ns (node); const gchar *type; const gchar *subtype; if (wocky_strdiff (pt_ns, WOCKY_XMPP_NS_JINGLE_RTCP_FB)) return NULL; type = wocky_node_get_attribute (node, "type"); if (type == NULL) return NULL; subtype = wocky_node_get_attribute (node, "subtype"); /* This is optional, defaults to "" */ if (subtype == NULL) subtype = ""; return wocky_jingle_feedback_message_new (type, subtype); } /* * Returns G_MAXUINT on error */ static guint parse_rtcp_fb_trr_int (WockyJingleContent *content, WockyNode *node) { const gchar *pt_ns = wocky_node_get_ns (node); const gchar *txt; guint trr_int; gchar *endptr = NULL; if (wocky_strdiff (pt_ns, WOCKY_XMPP_NS_JINGLE_RTCP_FB)) return G_MAXUINT; txt = wocky_node_get_attribute (node, "value"); if (txt == NULL) return G_MAXUINT; trr_int = strtol (txt, &endptr, 10); if (endptr == NULL || endptr == txt) return G_MAXUINT; return trr_int; } /** * parse_payload_type: * @node: a node. * * Returns: a newly-allocated WockyJingleCodec if parsing succeeds, or %NULL * otherwise. */ static WockyJingleCodec * parse_payload_type (WockyJingleContent *content, WockyNode *node) { WockyJingleMediaRtp *self = WOCKY_JINGLE_MEDIA_RTP (content); WockyJingleMediaRtpPrivate *priv = self->priv; WockyJingleCodec *p; const char *txt; guint8 id; const gchar *name; guint clockrate = 0; guint channels = 0; WockyNode *param; WockyNodeIter i; txt = wocky_node_get_attribute (node, "id"); if (txt == NULL) return NULL; id = atoi (txt); name = wocky_node_get_attribute (node, "name"); if (name == NULL) name = ""; /* xep-0167 v0.22, gtalk libjingle 0.3/0.4 use "clockrate" */ txt = wocky_node_get_attribute (node, "clockrate"); /* older jingle rtp used "rate" ? */ if (txt == NULL) txt = wocky_node_get_attribute (node, "rate"); if (txt != NULL) clockrate = atoi (txt); txt = wocky_node_get_attribute (node, "channels"); if (txt != NULL) channels = atoi (txt); p = jingle_media_rtp_codec_new (id, name, clockrate, channels, NULL); wocky_node_iter_init (&i, node, NULL, NULL); while (wocky_node_iter_next (&i, ¶m)) { if (!wocky_strdiff (param->name, "parameter")) { const gchar *param_name, *param_value; param_name = wocky_node_get_attribute (param, "name"); param_value = wocky_node_get_attribute (param, "value"); if (param_name == NULL || param_value == NULL) continue; g_hash_table_insert (p->params, g_strdup (param_name), g_strdup (param_value)); } else if (!wocky_strdiff (param->name, "rtcp-fb")) { WockyJingleFeedbackMessage *fb = parse_rtcp_fb (content, param); if (fb != NULL) { p->feedback_msgs = g_list_append (p->feedback_msgs, fb); priv->has_rtcp_fb = TRUE; } } else if (!wocky_strdiff (param->name, "rtcp-fb-trr-int")) { guint trr_int = parse_rtcp_fb_trr_int (content, param); if (trr_int != G_MAXUINT) { p->trr_int = trr_int; priv->has_rtcp_fb = TRUE; } } } DEBUG ("new remote codec: id = %u, name = %s, clockrate = %u, channels = %u", p->id, p->name, p->clockrate, p->channels); return p; } static WockyJingleRtpHeaderExtension * parse_rtp_header_extension (WockyNode *node) { guint id; WockyJingleContentSenders senders; const gchar *uri; const char *txt; txt = wocky_node_get_attribute (node, "id"); if (txt == NULL) return NULL; id = atoi (txt); /* Only valid ranges are 1-256 and 4096-4351 */ if ((id < 1 || id > 256) && (id < 4096 || id > 4351)) return NULL; txt = wocky_node_get_attribute (node, "senders"); if (txt == NULL || !g_ascii_strcasecmp (txt, "both")) senders = WOCKY_JINGLE_CONTENT_SENDERS_BOTH; else if (!g_ascii_strcasecmp (txt, "initiator")) senders = WOCKY_JINGLE_CONTENT_SENDERS_INITIATOR; else if (!g_ascii_strcasecmp (txt, "responder")) senders = WOCKY_JINGLE_CONTENT_SENDERS_RESPONDER; else return NULL; uri = wocky_node_get_attribute (node, "uri"); if (uri == NULL) return NULL; return wocky_jingle_rtp_header_extension_new (id, senders, uri); } /** * codec_update_coherent: * @old_c: this content's old cache of the codec, or %NULL if it hasn't heard * of it. * @new_c: the proposed update, whose id must equal that of @old_c if the * latter is non-NULL. * @domain: the error domain to set @e to if necessary * @code: the error code to set @e to if necessary * @e: location to hold an error * * Compares @old_c and @new_c, which are assumed to have the same id, to check * that the name, clockrate and number of channels hasn't changed. If they * have, returns %FALSE and sets @e. */ static gboolean codec_update_coherent (const WockyJingleCodec *old_c, const WockyJingleCodec *new_c, GError **e) { const GQuark domain = WOCKY_XMPP_ERROR; const gint code = WOCKY_XMPP_ERROR_BAD_REQUEST; if (old_c == NULL) { g_set_error (e, domain, code, "Codec with id %u ('%s') unknown", new_c->id, new_c->name); return FALSE; } if (g_ascii_strcasecmp (new_c->name, old_c->name)) { g_set_error (e, domain, code, "tried to change codec %u's name from %s to %s", new_c->id, old_c->name, new_c->name); return FALSE; } if (new_c->clockrate != old_c->clockrate) { g_set_error (e, domain, code, "tried to change codec %u (%s)'s clockrate from %u to %u", new_c->id, new_c->name, old_c->clockrate, new_c->clockrate); return FALSE; } if (old_c->channels != 0 && new_c->channels != old_c->channels) { g_set_error (e, domain, code, "tried to change codec %u (%s)'s channels from %u to %u", new_c->id, new_c->name, new_c->channels, old_c->channels); return FALSE; } return TRUE; } static void update_remote_media_description (WockyJingleMediaRtp *self, WockyJingleMediaDescription *new_media_description, GError **error) { WockyJingleMediaRtpPrivate *priv = self->priv; GHashTable *rc = NULL; WockyJingleCodec *old_c, *new_c; GList *l; GError *e = NULL; if (priv->remote_media_description == NULL) { priv->remote_media_description = new_media_description; new_media_description = NULL; goto out; } rc = build_codec_table (priv->remote_media_description->codecs); /* We already know some remote codecs, so this is just the other end updating * some parameters. */ for (l = new_media_description->codecs; l != NULL; l = l->next) { new_c = l->data; old_c = g_hash_table_lookup (rc, GUINT_TO_POINTER ((guint) new_c->id)); if (!codec_update_coherent (old_c, new_c, &e)) goto out; } /* Okay, all the updates are cool. Let's switch the parameters around. */ for (l = new_media_description->codecs; l != NULL; l = l->next) { GHashTable *params; new_c = l->data; old_c = g_hash_table_lookup (rc, GUINT_TO_POINTER ((guint) new_c->id)); params = old_c->params; old_c->params = new_c->params; new_c->params = params; } out: if (new_media_description != NULL) wocky_jingle_media_description_free (new_media_description); if (rc != NULL) g_hash_table_unref (rc); if (e != NULL) { DEBUG ("Rejecting codec update: %s", e->message); g_propagate_error (error, e); } else { DEBUG ("Emitting remote-media-description signal"); g_signal_emit (self, signals[REMOTE_MEDIA_DESCRIPTION], 0, priv->remote_media_description); } } static void parse_description (WockyJingleContent *content, WockyNode *desc_node, GError **error) { WockyJingleMediaRtp *self = WOCKY_JINGLE_MEDIA_RTP (content); WockyJingleMediaRtpPrivate *priv = self->priv; WockyJingleMediaType mtype; WockyJingleMediaDescription *md; WockyJingleCodec *p; WockyJingleDialect dialect = wocky_jingle_session_get_dialect (content->session); gboolean video_session = FALSE; WockyNodeIter i; WockyNode *node; gboolean description_error = FALSE; gboolean is_avpf = FALSE; DEBUG ("node: %s", desc_node->name); if (priv->media_type == WOCKY_JINGLE_MEDIA_TYPE_NONE) mtype = extract_media_type (desc_node, error); else mtype = priv->media_type; if (mtype == WOCKY_JINGLE_MEDIA_TYPE_NONE) return; DEBUG ("detected media type %u", mtype); if (dialect == WOCKY_JINGLE_DIALECT_GTALK3) { const gchar *desc_ns = wocky_node_get_ns (desc_node); video_session = !wocky_strdiff (desc_ns, WOCKY_XMPP_NS_GOOGLE_SESSION_VIDEO); } md = wocky_jingle_media_description_new (); wocky_node_iter_init (&i, desc_node, NULL, NULL); while (wocky_node_iter_next (&i, &node) && !description_error) { if (!wocky_strdiff (node->name, "payload-type")) { if (dialect == WOCKY_JINGLE_DIALECT_GTALK3) { const gchar *pt_ns = wocky_node_get_ns (node); if (priv->media_type == WOCKY_JINGLE_MEDIA_TYPE_AUDIO) { if (video_session && wocky_strdiff (pt_ns, WOCKY_XMPP_NS_GOOGLE_SESSION_PHONE)) continue; } else if (priv->media_type == WOCKY_JINGLE_MEDIA_TYPE_VIDEO) { if (!(video_session && pt_ns == NULL) && wocky_strdiff (pt_ns, WOCKY_XMPP_NS_GOOGLE_SESSION_VIDEO)) continue; } } p = parse_payload_type (content, node); if (p == NULL) { description_error = TRUE; } else { md->codecs = g_list_append (md->codecs, p); if (p->trr_int != G_MAXUINT || p->feedback_msgs) is_avpf = TRUE; } } else if (!wocky_strdiff (node->name, "rtp-hdrext")) { const gchar *pt_ns = wocky_node_get_ns (node); WockyJingleRtpHeaderExtension *hdrext; if (wocky_strdiff (pt_ns, WOCKY_XMPP_NS_JINGLE_RTP_HDREXT)) continue; hdrext = parse_rtp_header_extension (node); if (hdrext == NULL) { description_error = TRUE; } else { md->hdrexts = g_list_append (md->hdrexts, hdrext); priv->has_rtp_hdrext = TRUE; } } else if (!wocky_strdiff (node->name, "rtcp-fb")) { WockyJingleFeedbackMessage *fb = parse_rtcp_fb (content, node); if (fb == NULL) { description_error = TRUE; } else { md->feedback_msgs = g_list_append (md->feedback_msgs, fb); is_avpf = TRUE; priv->has_rtcp_fb = TRUE; } } else if (!wocky_strdiff (node->name, "rtcp-fb-trr-int")) { guint trr_int = parse_rtcp_fb_trr_int (content, node); if (trr_int == G_MAXUINT) { description_error = TRUE; } else { md->trr_int = trr_int; is_avpf = TRUE; priv->has_rtcp_fb = TRUE; } } } if (description_error) { /* rollback these */ wocky_jingle_media_description_free (md); g_set_error (error, WOCKY_XMPP_ERROR, WOCKY_XMPP_ERROR_BAD_REQUEST, "invalid description"); return; } /* If the profile is AVPF, the trr-int default to 0 */ if (is_avpf && md->trr_int == G_MAXUINT) md->trr_int = 0; priv->media_type = mtype; update_remote_media_description (self, md, error); } /* The Google Talk desktop client is picky about the case of codec names, even * though SDP defines them to be case-insensitive. The particular case that was * causing problems was ILBC vs iLBC, but it seems safer to special-case the * lot. This list is taken from the initiate sent by the desktop client on * 2009-07-01. */ static const gchar * const codec_cases[] = { "CN", "EG711A", "EG711U", "G723", "IPCMWB", "ISAC", "PCMA", "PCMU", "iLBC", "speex", "telephone-event", NULL }; static const gchar * gtalk_case (const gchar *codec) { const gchar * const *ret = codec_cases; for (; *ret != NULL; ret++) if (g_ascii_strcasecmp (*ret, codec) == 0) return *ret; return codec; } static void _produce_extra_param (gpointer key, gpointer value, gpointer user_data) { WockyNode *pt_node = user_data; WockyNode *param; gchar *param_name = key; gchar *param_value = value; param = wocky_node_add_child (pt_node, "parameter"); wocky_node_set_attribute (param, "name", param_name); wocky_node_set_attribute (param, "value", param_value); } static void produce_rtcp_fb_trr_int (WockyNode *node, guint trr_int) { WockyNode *trr_int_node; gchar tmp[10]; if (trr_int == G_MAXUINT || trr_int == 0) return; trr_int_node = wocky_node_add_child_ns (node, "rtcp-fb-trr-int", WOCKY_XMPP_NS_JINGLE_RTCP_FB); snprintf (tmp, 9, "%d", trr_int); wocky_node_set_attribute (trr_int_node, "value", tmp); } static void produce_rtcp_fb (WockyJingleFeedbackMessage *fb, WockyNode *node) { WockyNode *fb_node; fb_node = wocky_node_add_child (node, "rtcp-fb"); wocky_node_set_attribute (fb_node, "xmlns", WOCKY_XMPP_NS_JINGLE_RTCP_FB); wocky_node_set_attribute (fb_node, "type", fb->type); if (fb->subtype != NULL && fb->subtype[0] != 0) wocky_node_set_attribute (fb_node, "subtype", fb->subtype); } static void produce_payload_type (WockyJingleContent *content, WockyNode *desc_node, WockyJingleMediaType type, WockyJingleCodec *p, WockyJingleDialect dialect) { WockyJingleMediaRtp *self = WOCKY_JINGLE_MEDIA_RTP (content); WockyJingleMediaRtpPrivate *priv = self->priv; WockyNode *pt_node; gchar buf[16]; pt_node = wocky_node_add_child (desc_node, "payload-type"); /* id: required */ sprintf (buf, "%d", p->id); wocky_node_set_attribute (pt_node, "id", buf); if (dialect == WOCKY_JINGLE_DIALECT_GTALK3) { if (type == WOCKY_JINGLE_MEDIA_TYPE_AUDIO) { /* Gtalk 03 has either an audio or a video session, in case of a * video session the audio codecs need to set their namespace to * WOCKY_XMPP_NS_GOOGLE_SESSION_PHONE. In the case of an audio session it * doesn't matter, so just always set the namespace on audio * payloads. */ pt_node->ns = g_quark_from_static_string ( WOCKY_XMPP_NS_GOOGLE_SESSION_PHONE); } else { /* If width, height and framerate aren't set the google server ignore * our initiate.. These are a recv parameters, to it doesn't matter * for what we're sending, just for what we're getting.. 320x240 * seems a sane enough default */ wocky_node_set_attributes (pt_node, "width", "320", "height", "240", "framerate", "30", NULL); } } /* name: optional */ if (*p->name != '\0') { if (WOCKY_JINGLE_DIALECT_IS_GOOGLE (dialect)) wocky_node_set_attribute (pt_node, "name", gtalk_case (p->name)); else wocky_node_set_attribute (pt_node, "name", p->name); } /* clock rate: optional */ if (p->clockrate != 0) { const gchar *attname = "clockrate"; if (dialect == WOCKY_JINGLE_DIALECT_V015) attname = "rate"; sprintf (buf, "%u", p->clockrate); wocky_node_set_attribute (pt_node, attname, buf); } if (p->channels != 0) { sprintf (buf, "%u", p->channels); wocky_node_set_attribute (pt_node, "channels", buf); } if (p->params != NULL) g_hash_table_foreach (p->params, _produce_extra_param, pt_node); if (priv->has_rtcp_fb) { g_list_foreach (p->feedback_msgs, (GFunc) produce_rtcp_fb, pt_node); produce_rtcp_fb_trr_int (pt_node, p->trr_int); } } static WockyNode * produce_description_node (WockyJingleDialect dialect, WockyJingleMediaType media_type, WockyNode *content_node) { WockyNode *desc_node; const gchar *xmlns = NULL, *media_attr = NULL; if (dialect == WOCKY_JINGLE_DIALECT_GTALK3) return NULL; switch (dialect) { case WOCKY_JINGLE_DIALECT_GTALK4: g_assert (media_type == WOCKY_JINGLE_MEDIA_TYPE_AUDIO); xmlns = WOCKY_XMPP_NS_GOOGLE_SESSION_PHONE; break; case WOCKY_JINGLE_DIALECT_V015: if (media_type == WOCKY_JINGLE_MEDIA_TYPE_AUDIO) xmlns = WOCKY_XMPP_NS_JINGLE_DESCRIPTION_AUDIO; else if (media_type == WOCKY_JINGLE_MEDIA_TYPE_VIDEO) xmlns = WOCKY_XMPP_NS_JINGLE_DESCRIPTION_VIDEO; else { DEBUG ("unknown media type %u", media_type); xmlns = ""; } break; default: xmlns = WOCKY_XMPP_NS_JINGLE_RTP; if (media_type == WOCKY_JINGLE_MEDIA_TYPE_AUDIO) media_attr = "audio"; else if (media_type == WOCKY_JINGLE_MEDIA_TYPE_VIDEO) media_attr = "video"; else g_assert_not_reached (); break; } desc_node = wocky_node_add_child_ns (content_node, "description", xmlns); if (media_attr != NULL) wocky_node_set_attribute (desc_node, "media", media_attr); return desc_node; } static void produce_hdrext (gpointer data, gpointer user_data) { WockyJingleRtpHeaderExtension *hdrext = data; WockyNode *desc_node = user_data; WockyNode *hdrext_node; gchar buf[16]; hdrext_node = wocky_node_add_child (desc_node, "rtp-hdrext"); /* id: required */ sprintf (buf, "%d", hdrext->id); wocky_node_set_attribute (hdrext_node, "id", buf); wocky_node_set_attribute (hdrext_node, "uri", hdrext->uri); if (hdrext->senders == WOCKY_JINGLE_CONTENT_SENDERS_INITIATOR) wocky_node_set_attribute (hdrext_node, "senders", "initiator"); else if (hdrext->senders == WOCKY_JINGLE_CONTENT_SENDERS_RESPONDER) wocky_node_set_attribute (hdrext_node, "senders", "responder"); wocky_node_set_attribute (hdrext_node, "xmlns", WOCKY_XMPP_NS_JINGLE_RTP_HDREXT); } static void produce_description (WockyJingleContent *content, WockyNode *content_node) { WockyJingleMediaRtp *self = WOCKY_JINGLE_MEDIA_RTP (content); WockyJingleMediaRtpPrivate *priv = self->priv; GList *li; WockyJingleDialect dialect = wocky_jingle_session_get_dialect (content->session); WockyNode *desc_node; if (wocky_jingle_session_peer_has_cap (content->session, WOCKY_XMPP_NS_JINGLE_RTCP_FB)) priv->has_rtcp_fb = TRUE; if (wocky_jingle_session_peer_has_cap (content->session, WOCKY_XMPP_NS_JINGLE_RTP_HDREXT)) priv->has_rtp_hdrext = TRUE; desc_node = produce_description_node (dialect, priv->media_type, content_node); /* For GTalk3 the description is added by the session */ if (desc_node == NULL) desc_node = content_node; /* If we're only updating our codec parameters, only generate payload-types * for those. */ if (priv->local_codec_updates != NULL) li = priv->local_codec_updates; else li = priv->local_media_description->codecs; for (; li != NULL; li = li->next) produce_payload_type (content, desc_node, priv->media_type, li->data, dialect); if (priv->has_rtp_hdrext && priv->local_media_description->hdrexts) g_list_foreach (priv->local_media_description->hdrexts, produce_hdrext, desc_node); if (priv->has_rtcp_fb) { g_list_foreach (priv->local_media_description->feedback_msgs, (GFunc) produce_rtcp_fb, desc_node); produce_rtcp_fb_trr_int (desc_node, priv->local_media_description->trr_int); } } /** * string_string_maps_equal: * * Returns: TRUE iff @a and @b contain exactly the same keys and values when * compared as strings. */ static gboolean string_string_maps_equal (GHashTable *a, GHashTable *b) { GHashTableIter iter; gpointer a_key, a_value, b_value; if (g_hash_table_size (a) != g_hash_table_size (b)) return FALSE; g_hash_table_iter_init (&iter, a); while (g_hash_table_iter_next (&iter, &a_key, &a_value)) { if (!g_hash_table_lookup_extended (b, a_key, NULL, &b_value)) return FALSE; if (wocky_strdiff (a_value, b_value)) return FALSE; } return TRUE; } /** * compare_codecs: * @old: previous local codecs * @new: new local codecs supplied by streaming implementation * @changed: location at which to store the changed codecs * @error: location at which to store an error if the update was invalid * * Returns: %TRUE if the update made sense, %FALSE with @error set otherwise */ gboolean jingle_media_rtp_compare_codecs (GList *old, GList *new, GList **changed, GError **e) { gboolean ret = FALSE; GHashTable *old_table = build_codec_table (old); GList *l; WockyJingleCodec *old_c, *new_c; g_assert (changed != NULL && *changed == NULL); for (l = new; l != NULL; l = l->next) { new_c = l->data; old_c = g_hash_table_lookup (old_table, GUINT_TO_POINTER ( (guint) new_c->id)); if (!codec_update_coherent (old_c, new_c, e)) goto out; if (!string_string_maps_equal (old_c->params, new_c->params)) *changed = g_list_prepend (*changed, new_c); } ret = TRUE; out: if (!ret) { g_list_free (*changed); *changed = NULL; } g_hash_table_unref (old_table); return ret; } /** * jingle_media_rtp_set_local_media_description: * @self: a content in an RTP session * @md: (transfer full): new media description for this content * @ready: whether the codecs can regarded as ready to sent from now on * @error: used to return a %WOCKY_XMPP_ERROR if the codec update is illegal. * * Sets or updates the media description (codecs, feedback messages, etc) for * @self. * * Returns: %TRUE if no description was previously set, or if the update is * compatible with the existing description; %FALSE if the update is illegal * (due to adding previously-unknown codecs or renaming an existing codec, for * example) */ gboolean jingle_media_rtp_set_local_media_description (WockyJingleMediaRtp *self, WockyJingleMediaDescription *md, gboolean ready, GError **error) { WockyJingleMediaRtpPrivate *priv = self->priv; DEBUG ("setting new local media description"); if (priv->local_media_description != NULL) { GList *changed = NULL; GError *err = NULL; g_assert (priv->local_codec_updates == NULL); if (!jingle_media_rtp_compare_codecs ( priv->local_media_description->codecs, md->codecs, &changed, &err)) { DEBUG ("codec update was illegal: %s", err->message); wocky_jingle_media_description_free (md); g_propagate_error (error, err); return FALSE; } if (changed == NULL) { DEBUG ("codec update changed nothing!"); wocky_jingle_media_description_free (md); goto out; } DEBUG ("%u codecs changed", g_list_length (changed)); priv->local_codec_updates = changed; wocky_jingle_media_description_free (priv->local_media_description); } priv->local_media_description = md; /* Codecs have changed, sending a fresh description might be necessary */ wocky_jingle_content_maybe_send_description (WOCKY_JINGLE_CONTENT (self)); /* Update done if any, free the changed codecs if any */ g_list_free (priv->local_codec_updates); priv->local_codec_updates = NULL; out: if (ready) _wocky_jingle_content_set_media_ready (WOCKY_JINGLE_CONTENT (self)); return TRUE; } void jingle_media_rtp_register (WockyJingleFactory *factory) { /* Current (v0.25) Jingle draft URI */ wocky_jingle_factory_register_content_type (factory, WOCKY_XMPP_NS_JINGLE_RTP, WOCKY_TYPE_JINGLE_MEDIA_RTP); /* Old Jingle audio/video namespaces */ wocky_jingle_factory_register_content_type (factory, WOCKY_XMPP_NS_JINGLE_DESCRIPTION_AUDIO, WOCKY_TYPE_JINGLE_MEDIA_RTP); wocky_jingle_factory_register_content_type (factory, WOCKY_XMPP_NS_JINGLE_DESCRIPTION_VIDEO, WOCKY_TYPE_JINGLE_MEDIA_RTP); /* GTalk audio call namespace */ wocky_jingle_factory_register_content_type (factory, WOCKY_XMPP_NS_GOOGLE_SESSION_PHONE, WOCKY_TYPE_JINGLE_MEDIA_RTP); /* GTalk video call namespace */ wocky_jingle_factory_register_content_type (factory, WOCKY_XMPP_NS_GOOGLE_SESSION_VIDEO, WOCKY_TYPE_JINGLE_MEDIA_RTP); } /* We can't get remote media description when they're signalled, because * the signal is emitted immediately upon JingleContent creation, * and parsing, which is before a corresponding MediaStream is * created. */ /** * wocky_jingle_media_rtp_get_remote_media_description: * @self : the RTP content * * Gets the current remote media description, if known. The * #WockyJingleMediaRtp:remote-media-description signal is emitted when this * value changes. * * Returns: (transfer none): the current remote media description, which may be * %NULL for outgoing calls until it is first received. */ WockyJingleMediaDescription * wocky_jingle_media_rtp_get_remote_media_description ( WockyJingleMediaRtp *self) { WockyJingleMediaRtpPrivate *priv = self->priv; return priv->remote_media_description; } /** * WockyJingleMediaDescription: * @codecs: a list of #WockyJingleCodecs, allocated with * jingle_media_rtp_codec_new() * @hdrexts: a list of #WockyJingleRtpHeaderExtensions, allocated with * wocky_jingle_rtp_header_extension_new() * @trr_int: number of milliseconds between regular RTCP reports * @feedback_msgs: a list of #WockyJingleFeedbackMessages, allocated * with wocky_jingle_feedback_message_new() * * Media description for a #WockyJingleMediaRtp content. */ /** * wocky_jingle_media_description_new: * * Allocates a new media description. You should fill in all the fields yourself. * * Returns: a new, empty, media description */ WockyJingleMediaDescription * wocky_jingle_media_description_new (void) { WockyJingleMediaDescription *md = g_slice_new0 (WockyJingleMediaDescription); md->trr_int = G_MAXUINT; return md; } void wocky_jingle_media_description_free (WockyJingleMediaDescription *md) { jingle_media_rtp_free_codecs (md->codecs); while (md->hdrexts != NULL) { wocky_jingle_rtp_header_extension_free (md->hdrexts->data); md->hdrexts = g_list_delete_link (md->hdrexts, md->hdrexts); } g_slice_free (WockyJingleMediaDescription, md); } /** * wocky_jingle_media_description_copy: * @md: a media description * * Performs a deep copy of a media description. * * Returns: (transfer full): a deep copy of @md */ WockyJingleMediaDescription * wocky_jingle_media_description_copy (WockyJingleMediaDescription *md) { WockyJingleMediaDescription *newmd = g_slice_new0 (WockyJingleMediaDescription); GList *li; newmd->codecs = jingle_media_rtp_copy_codecs (md->codecs); newmd->feedback_msgs = wocky_jingle_feedback_message_list_copy (md->feedback_msgs); newmd->trr_int = md->trr_int; for (li = md->hdrexts; li; li = li->next) { WockyJingleRtpHeaderExtension *h = li->data; newmd->hdrexts = g_list_append (newmd->hdrexts, wocky_jingle_rtp_header_extension_new (h->id, h->senders, h->uri)); } return newmd; } WockyJingleRtpHeaderExtension * wocky_jingle_rtp_header_extension_new (guint id, WockyJingleContentSenders senders, const gchar *uri) { WockyJingleRtpHeaderExtension *hdrext = g_slice_new (WockyJingleRtpHeaderExtension); hdrext->id = id; hdrext->senders = senders; hdrext->uri = g_strdup (uri); return hdrext; } void wocky_jingle_rtp_header_extension_free (WockyJingleRtpHeaderExtension *hdrext) { g_free (hdrext->uri); g_slice_free (WockyJingleRtpHeaderExtension, hdrext); } WockyJingleFeedbackMessage * wocky_jingle_feedback_message_new (const gchar *type, const gchar *subtype) { WockyJingleFeedbackMessage *fb = g_slice_new0 (WockyJingleFeedbackMessage); fb->type = g_strdup (type); fb->subtype = g_strdup (subtype); return fb; } void wocky_jingle_feedback_message_free (WockyJingleFeedbackMessage *fb) { g_free (fb->type); g_free (fb->subtype); g_slice_free (WockyJingleFeedbackMessage, fb); } static gint wocky_jingle_feedback_message_compare (const WockyJingleFeedbackMessage *fb1, const WockyJingleFeedbackMessage *fb2) { if (!g_ascii_strcasecmp (fb1->type, fb2->type) && !g_ascii_strcasecmp (fb1->subtype, fb2->subtype)) return 0; else return 1; } /** * wocky_jingle_media_description_simplify: * @md: a description to simplify * * Removes duplicated Feedback message and put them in the global structure * * This function will iterate over every codec in a description and look for * feedback messages that are exactly the same in every codec and will instead * put the in the list in the description and remove them from the childs. * This limits the amount of duplication in the resulting XML. */ void wocky_jingle_media_description_simplify (WockyJingleMediaDescription *md) { GList *item; guint trr_int = 0; gboolean trr_int_all_same = TRUE; gboolean init = FALSE; GList *identical_fbs = NULL; for (item = md->codecs; item; item = item->next) { WockyJingleCodec *c = item->data; if (!init) { /* For the first codec, it stores the trr_int and the list * of feedback messages */ trr_int = c->trr_int; identical_fbs = g_list_copy (c->feedback_msgs); init = TRUE; } else { GList *item2; /* For every subsequent codec, we check if the trr_int is the same */ if (trr_int != c->trr_int) trr_int_all_same = FALSE; /* We also intersect the remembered list of feedback messages with * the list for that codec and remove any feedback message that isn't * in both */ for (item2 = identical_fbs; item2;) { WockyJingleFeedbackMessage *fb = identical_fbs->data; GList *next = item2->next; if (!g_list_find_custom (c->feedback_msgs, fb, (GCompareFunc) wocky_jingle_feedback_message_compare)) identical_fbs = g_list_delete_link (identical_fbs, item2); item2 = next; } /* If the trr_int is not the same everywhere and there are not common * feedback messages, then stop */ if (!trr_int_all_same && identical_fbs == NULL) break; } } if (trr_int_all_same && trr_int == G_MAXUINT) trr_int_all_same = FALSE; /* if the trr_int is the same everywhere, lets set it globally */ if (trr_int_all_same) md->trr_int = trr_int; /* If there are feedback messages that are in every codec, put a copy of them * in the global structure */ if (identical_fbs) { md->feedback_msgs = wocky_jingle_feedback_message_list_copy (identical_fbs); g_list_free (identical_fbs); } if (trr_int_all_same || md->feedback_msgs != NULL) for (item = md->codecs; item; item = item->next) { WockyJingleCodec *c = item->data; GList *item2; /* If the trr_int is the same everywhere, lets put the default on * each codec, we have it in the main structure */ if (trr_int_all_same) c->trr_int = G_MAXUINT; /* Find the feedback messages that were put in the main structure and * remove them from each codec */ for (item2 = md->feedback_msgs; item2; item2 = item2->next) { GList *duplicated; WockyJingleFeedbackMessage *fb = item2->data; while ((duplicated = g_list_find_custom (c->feedback_msgs, fb, (GCompareFunc) wocky_jingle_feedback_message_compare)) != NULL) { wocky_jingle_feedback_message_free (duplicated->data); c->feedback_msgs = g_list_delete_link (c->feedback_msgs, duplicated); } } } } telepathy-gabble-0.18.2/lib/ext/wocky/wocky/wocky-jingle-info.c0000644000175000017500000004626012200204546024355 0ustar00smcvsmcv00000000000000/* * wocky-jingle-info.c - exciting times with Google's jingleinfo extension * Copyright © 2008–2012 Collabora Ltd. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 "config.h" #include "wocky-jingle-info.h" #include "wocky-jingle-info-internal.h" #include #define WOCKY_DEBUG_FLAG WOCKY_DEBUG_JINGLE #include "wocky-debug-internal.h" #include "wocky-google-relay.h" #include "wocky-enumtypes.h" #include "wocky-signals-marshal.h" #include "wocky-namespaces.h" #include "wocky-utils.h" #include "wocky-c2s-porter.h" static gboolean jingle_info_cb ( WockyPorter *porter, WockyStanza *stanza, gpointer user_data); struct _WockyJingleInfoPrivate { WockyPorter *porter; guint jingle_info_handler_id; gchar *jid_domain; WockyGoogleRelayResolver *google_resolver; WockyStunServer *stun_server; WockyStunServer *fallback_stun_server; gchar *relay_token; /* TRUE if the user has not explicitly specified a STUN server, and hence * we should ask the XMPP server for one; FALSE if not. */ gboolean get_stun_from_jingle; gchar *relay_server; guint16 relay_http_port; guint16 relay_udp; guint16 relay_tcp; guint16 relay_ssltcp; }; enum { PROP_PORTER = 1, }; enum { STUN_SERVER_CHANGED = 0, N_SIGNALS }; static guint signals[N_SIGNALS]; static gboolean test_mode = FALSE; void wocky_jingle_info_set_test_mode (void) { test_mode = TRUE; } static WockyStunServer * wocky_stun_server_new ( gchar *address, guint16 port) { WockyStunServer stun_server = { address, port }; return g_slice_dup (WockyStunServer, &stun_server); } static void wocky_stun_server_free (WockyStunServer *stun_server) { if (stun_server != NULL) { g_free (stun_server->address); g_slice_free (WockyStunServer, stun_server); } } G_DEFINE_TYPE (WockyJingleInfo, wocky_jingle_info, G_TYPE_OBJECT) static void wocky_jingle_info_init (WockyJingleInfo *self) { self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, WOCKY_TYPE_JINGLE_INFO, WockyJingleInfoPrivate); self->priv->relay_http_port = 80; self->priv->get_stun_from_jingle = TRUE; } static void wocky_jingle_info_get_property ( GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { WockyJingleInfo *self = WOCKY_JINGLE_INFO (object); WockyJingleInfoPrivate *priv = self->priv; switch (property_id) { case PROP_PORTER: g_value_set_object (value, priv->porter); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); } } static void wocky_jingle_info_set_property ( GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { WockyJingleInfo *self = WOCKY_JINGLE_INFO (object); WockyJingleInfoPrivate *priv = self->priv; switch (property_id) { case PROP_PORTER: g_assert (priv->porter == NULL); priv->porter = g_value_dup_object (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); } } static void wocky_jingle_info_constructed (GObject *object) { WockyJingleInfo *self = WOCKY_JINGLE_INFO (object); WockyJingleInfoPrivate *priv = self->priv; GObjectClass *parent_class = wocky_jingle_info_parent_class; if (parent_class->constructed != NULL) parent_class->constructed (object); g_assert (priv->porter != NULL); if (!wocky_decode_jid (wocky_porter_get_bare_jid (priv->porter), NULL, &priv->jid_domain, NULL)) g_assert_not_reached (); } static void wocky_jingle_info_dispose (GObject *object) { WockyJingleInfo *self = WOCKY_JINGLE_INFO (object); WockyJingleInfoPrivate *priv = self->priv; GObjectClass *parent_class = wocky_jingle_info_parent_class; if (priv->porter != NULL) { if (priv->jingle_info_handler_id != 0) wocky_porter_unregister_handler (priv->porter, priv->jingle_info_handler_id); g_clear_object (&priv->porter); } if (priv->google_resolver != NULL) { wocky_google_relay_resolver_destroy (priv->google_resolver); priv->google_resolver = NULL; } g_free (priv->jid_domain); priv->jid_domain = NULL; wocky_stun_server_free (priv->stun_server); priv->stun_server = NULL; wocky_stun_server_free (priv->fallback_stun_server); priv->fallback_stun_server = NULL; g_free (priv->relay_token); priv->relay_token = NULL; g_free (priv->relay_server); priv->relay_server = NULL; if (parent_class->dispose != NULL) parent_class->dispose (object); } static void wocky_jingle_info_class_init (WockyJingleInfoClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); GParamSpec *param_spec; object_class->get_property = wocky_jingle_info_get_property; object_class->set_property = wocky_jingle_info_set_property; object_class->constructed = wocky_jingle_info_constructed; object_class->dispose = wocky_jingle_info_dispose; g_type_class_add_private (klass, sizeof (WockyJingleInfoPrivate)); param_spec = g_param_spec_object ("porter", "WockyC2SPorter", "Porter for the current connection", WOCKY_TYPE_C2S_PORTER, G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, PROP_PORTER, param_spec); signals[STUN_SERVER_CHANGED] = g_signal_new ("stun-server-changed", G_OBJECT_CLASS_TYPE (object_class), G_SIGNAL_RUN_LAST, 0, NULL, NULL, _wocky_signals_marshal_VOID__STRING_UINT, G_TYPE_NONE, 2, G_TYPE_STRING, G_TYPE_UINT); } WockyJingleInfo * wocky_jingle_info_new ( WockyPorter *porter) { return g_object_new (WOCKY_TYPE_JINGLE_INFO, "porter", porter, NULL); } typedef struct { WockyJingleInfo *factory; gchar *stun_server; guint16 stun_port; WockyStunServerSource source; GCancellable *cancellable; } PendingStunServer; static void pending_stun_server_free (gpointer p) { PendingStunServer *data = p; if (data->factory != NULL) g_object_remove_weak_pointer (G_OBJECT (data->factory), (gpointer)&data->factory); g_object_unref (data->cancellable); g_free (data->stun_server); g_slice_free (PendingStunServer, p); } static void stun_server_resolved_cb (GObject *resolver, GAsyncResult *result, gpointer user_data) { PendingStunServer *data = user_data; WockyJingleInfo *self = data->factory; WockyJingleInfoPrivate *priv = self->priv; GError *e = NULL; WockyStunServer *stun_server; gchar *address; GList *entries; if (self != NULL) g_object_weak_unref (G_OBJECT (self), (GWeakNotify)g_cancellable_cancel, data->cancellable); entries = g_resolver_lookup_by_name_finish ( G_RESOLVER (resolver), result, &e); if (entries == NULL) { DEBUG ("Failed to resolve STUN server %s:%u: %s", data->stun_server, data->stun_port, e->message); g_error_free (e); goto out; } address = g_inet_address_to_string (entries->data); g_resolver_free_addresses (entries); DEBUG ("Resolved STUN server %s:%u to %s:%u", data->stun_server, data->stun_port, address, data->stun_port); if (self == NULL) { g_free (address); goto out; } stun_server = wocky_stun_server_new (address, data->stun_port); if (data->source == WOCKY_STUN_SERVER_FALLBACK) { wocky_stun_server_free (priv->fallback_stun_server); priv->fallback_stun_server = stun_server; } else { wocky_stun_server_free (priv->stun_server); priv->stun_server = stun_server; g_signal_emit (self, signals[STUN_SERVER_CHANGED], 0, stun_server, data->stun_port); } out: pending_stun_server_free (data); g_object_unref (resolver); } static void wocky_jingle_info_take_stun_server_internal ( WockyJingleInfo *self, gchar *stun_server, guint16 stun_port, WockyStunServerSource source) { GResolver *resolver; PendingStunServer *data; if (stun_server == NULL) return; if (source == WOCKY_STUN_SERVER_USER_SPECIFIED) self->priv->get_stun_from_jingle = FALSE; resolver = g_resolver_get_default (); data = g_slice_new0 (PendingStunServer); DEBUG ("Resolving %s STUN server %s:%u", wocky_enum_to_nick (WOCKY_TYPE_STUN_SERVER_SOURCE, data->source), stun_server, stun_port); data->factory = self; g_object_add_weak_pointer (G_OBJECT (self), (gpointer *) &data->factory); data->stun_server = stun_server; data->stun_port = stun_port; data->source = source; data->cancellable = g_cancellable_new (); g_object_weak_ref (G_OBJECT (self), (GWeakNotify)g_cancellable_cancel, data->cancellable); g_resolver_lookup_by_name_async (resolver, stun_server, data->cancellable, stun_server_resolved_cb, data); } /* * wocky_jingle_info_take_stun_server: * @self: a #WockyJingleInfo object * @stun_server: (transfer full): the STUN server's address * @stun_port: the STUN server's port * @is_fallback: %TRUE if this is a last resort; %FALSE if this STUN server was * provided by the user (whether by explicitly setting one, or by asking the * user's XMPP server). */ void wocky_jingle_info_take_stun_server ( WockyJingleInfo *self, gchar *stun_server, guint16 stun_port, gboolean is_fallback) { WockyStunServerSource source = is_fallback ? WOCKY_STUN_SERVER_FALLBACK : WOCKY_STUN_SERVER_USER_SPECIFIED; wocky_jingle_info_take_stun_server_internal (self, stun_server, stun_port, source); } static void got_jingle_info_stanza ( WockyJingleInfo *self, WockyStanza *stanza) { WockyNode *node, *query_node; query_node = wocky_node_get_child_ns ( wocky_stanza_get_top_node (stanza), "query", WOCKY_XMPP_NS_GOOGLE_JINGLE_INFO); if (query_node == NULL) return; if (self->priv->get_stun_from_jingle) node = wocky_node_get_child (query_node, "stun"); else node = NULL; if (node != NULL) { WockyNodeIter iter; /* TODO: use more than just the first stun server returned. */ wocky_node_iter_init (&iter, node, "server", NULL); if (wocky_node_iter_next (&iter, &node)) { const gchar *server; const gchar *port_attr; guint port = 0; server = wocky_node_get_attribute (node, "host"); port_attr = wocky_node_get_attribute (node, "udp"); if (port_attr != NULL) port = atoi (port_attr); if (server != NULL && port_attr != NULL && port > 0 && port <= G_MAXUINT16) { DEBUG ("jingle info: got stun server %s, port %u", server, port); wocky_jingle_info_take_stun_server_internal (self, g_strdup (server), port, WOCKY_STUN_SERVER_DISCOVERED); } } } #ifdef ENABLE_GOOGLE_RELAY node = wocky_node_get_child (query_node, "relay"); if (node != NULL) { WockyNode *subnode = wocky_node_get_child (node, "token"); if (subnode != NULL) { const gchar *token = subnode->content; if (token != NULL) { DEBUG ("jingle info: got Google relay token %s", token); g_free (self->priv->relay_token); self->priv->relay_token = g_strdup (token); } } subnode = wocky_node_get_child (node, "server"); if (subnode != NULL) { const gchar *server; const gchar *port; server = wocky_node_get_attribute (subnode, "host"); if (server != NULL) { DEBUG ("jingle info: got relay server %s", server); g_free (self->priv->relay_server); self->priv->relay_server = g_strdup (server); } if (test_mode) { /* this is not part of the real protocol, but we can't listen on * port 80 in an unprivileged regression test */ port = wocky_node_get_attribute (subnode, "gabble-test-http-port"); if (port != NULL) { DEBUG ("jingle info: diverting 'Google' HTTP requests to " "port %s", port); self->priv->relay_http_port = atoi (port); } } /* FIXME: these are not really actually used anywhere at * the moment, because we get the same info when creating * relay session. */ port = wocky_node_get_attribute (subnode, "udp"); if (port != NULL) { DEBUG ("jingle info: got relay udp port %s", port); self->priv->relay_udp = atoi (port); } port = wocky_node_get_attribute (subnode, "tcp"); if (port != NULL) { DEBUG ("jingle info: got relay tcp port %s", port); self->priv->relay_tcp = atoi (port); } port = wocky_node_get_attribute (subnode, "tcpssl"); if (port != NULL) { DEBUG ("jingle info: got relay tcpssl port %s", port); self->priv->relay_ssltcp = atoi (port); } } } #endif /* ENABLE_GOOGLE_RELAY */ } static gboolean jingle_info_cb ( WockyPorter *porter, WockyStanza *stanza, gpointer user_data) { WockyJingleInfo *self = WOCKY_JINGLE_INFO (user_data); got_jingle_info_stanza (self, stanza); wocky_porter_acknowledge_iq (porter, stanza, NULL); return TRUE; } static void jingle_info_reply_cb ( GObject *source, GAsyncResult *result, gpointer user_data) { WockyPorter *porter = WOCKY_PORTER (source); WockyJingleInfo *self = WOCKY_JINGLE_INFO (user_data); WockyStanza *reply = NULL; GError *error = NULL; reply = wocky_porter_send_iq_finish (porter, result, &error); if (reply != NULL && !wocky_stanza_extract_errors (reply, NULL, &error, NULL, NULL)) { got_jingle_info_stanza (self, reply); } else { DEBUG ("jingle info request failed: %s", error->message); g_clear_error (&error); } g_clear_object (&reply); g_object_unref (self); } static void wocky_jingle_info_send_google_request ( WockyJingleInfo *self) { WockyJingleInfoPrivate *priv = self->priv; WockyStanza *stanza = wocky_stanza_build ( WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_GET, NULL, wocky_porter_get_bare_jid (priv->porter), '(', "query", ':', WOCKY_XMPP_NS_GOOGLE_JINGLE_INFO, ')', NULL); wocky_porter_send_iq_async (priv->porter, stanza, NULL, jingle_info_reply_cb, g_object_ref (self)); g_object_unref (stanza); priv->jingle_info_handler_id = wocky_c2s_porter_register_handler_from_server ( WOCKY_C2S_PORTER (priv->porter), WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_SET, WOCKY_PORTER_HANDLER_PRIORITY_NORMAL, jingle_info_cb, self, '(', "query", ':', WOCKY_XMPP_NS_GOOGLE_JINGLE_INFO, ')', NULL); } static void discover_stun_servers_cb (GObject *resolver, GAsyncResult *result, gpointer user_data) { WockyJingleInfo *self = WOCKY_JINGLE_INFO (user_data); WockyJingleInfoPrivate *priv = self->priv; GError *error = NULL; GList *targets; targets = g_resolver_lookup_service_finish (G_RESOLVER (resolver), result, &error); if (error != NULL) { DEBUG ("Failed to discover STUN servers on %s: %s", priv->jid_domain, error->message); g_clear_error (&error); } else { DEBUG ("Discovered %d STUN servers on %s", g_list_length (targets), priv->jid_domain); /* TODO: use more than just the first. */ if (targets != NULL) { GSrvTarget *target = targets->data; const gchar *hostname = g_srv_target_get_hostname (target); guint16 port = g_srv_target_get_port (target); DEBUG ("Found STUN server: %s:%d", hostname, port); wocky_jingle_info_take_stun_server (self, g_strdup (hostname), port, FALSE); } g_resolver_free_targets (targets); } g_object_unref (resolver); g_object_unref (self); } static void wocky_jingle_info_lookup_srv ( WockyJingleInfo *self) { WockyJingleInfoPrivate *priv = self->priv; GResolver *resolver; g_assert (priv->jid_domain != NULL); DEBUG ("Discovering STUN servers on %s", priv->jid_domain); resolver = g_resolver_get_default (); g_resolver_lookup_service_async (resolver, "stun", "udp", priv->jid_domain, NULL, discover_stun_servers_cb, g_object_ref (self)); } void wocky_jingle_info_send_request ( WockyJingleInfo *self, gboolean google_jingleinfo_supported) { /* FIXME: we probably don't want to send either query if the user specified a * stun server (that is, get_stun_from_jingle is FALSE). */ if (google_jingleinfo_supported) wocky_jingle_info_send_google_request (self); else wocky_jingle_info_lookup_srv (self); } /* * wocky_jingle_info_get_stun_servers: * * Grabs the currently known and resolved stun servers. * * Returns: (transfer container): a list of WockyJingleInfo structs */ GList * wocky_jingle_info_get_stun_servers ( WockyJingleInfo *self) { WockyJingleInfoPrivate *priv = self->priv; GQueue stun_servers = G_QUEUE_INIT; if (priv->stun_server != NULL) g_queue_push_head (&stun_servers, priv->stun_server); /* Only add the fallback server as a last resort. */ if (stun_servers.length == 0 && priv->fallback_stun_server != NULL) g_queue_push_tail (&stun_servers, priv->fallback_stun_server); return stun_servers.head; } const gchar * wocky_jingle_info_get_google_relay_token ( WockyJingleInfo *self) { return self->priv->relay_token; } WockyJingleRelay * wocky_jingle_relay_new ( WockyJingleRelayType type, const gchar *ip, guint port, const gchar *username, const gchar *password, guint component) { WockyJingleRelay ret = { type, g_strdup (ip), port, g_strdup (username), g_strdup (password), component }; return g_slice_dup (WockyJingleRelay, &ret); } void wocky_jingle_relay_free (WockyJingleRelay *relay) { g_free (relay->ip); g_free (relay->username); g_free (relay->password); g_slice_free (WockyJingleRelay, relay); } void wocky_jingle_info_create_google_relay_session ( WockyJingleInfo *self, guint components, WockyJingleInfoRelaySessionCb callback, gpointer user_data) { WockyJingleInfoPrivate *priv = self->priv; g_return_if_fail (callback != NULL); if (priv->google_resolver == NULL) { priv->google_resolver = wocky_google_relay_resolver_new (); } wocky_google_relay_resolver_resolve (priv->google_resolver, components, priv->relay_server, priv->relay_http_port, priv->relay_token, callback, user_data); } telepathy-gabble-0.18.2/lib/ext/wocky/wocky/wocky-jingle-factory.c0000644000175000017500000004277512200204546025100 0ustar00smcvsmcv00000000000000/* * wocky-jingle-factory.c - Support for XEP-0166 (Jingle) * * Copyright (C) 2006-2008 Collabora Ltd. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 "config.h" #include "wocky-jingle-factory.h" #include #include #include #define WOCKY_DEBUG_FLAG WOCKY_DEBUG_JINGLE #include "wocky-debug-internal.h" #include "wocky-signals-marshal.h" #include "wocky-jingle-media-rtp.h" #include "wocky-jingle-session.h" #include "wocky-jingle-transport-google.h" #include "wocky-jingle-transport-rawudp.h" #include "wocky-jingle-transport-iceudp.h" #include "wocky-namespaces.h" #include "wocky-session.h" #include "wocky-utils.h" #include "wocky-google-relay.h" G_DEFINE_TYPE(WockyJingleFactory, wocky_jingle_factory, G_TYPE_OBJECT); /* signal enum */ enum { NEW_SESSION, QUERY_CAP, LAST_SIGNAL }; static guint signals[LAST_SIGNAL] = {0}; /* properties */ enum { PROP_SESSION = 1, LAST_PROPERTY }; struct _WockyJingleFactoryPrivate { WockySession *session; WockyPorter *porter; guint jingle_handler_id; GHashTable *content_types; GHashTable *transports; /* instances of SESSION_MAP_KEY_FORMAT => WockyJingleSession. */ GHashTable *sessions; WockyJingleInfo *jingle_info; gboolean dispose_has_run; }; static gboolean jingle_cb ( WockyPorter *porter, WockyStanza *msg, gpointer user_data); static WockyJingleSession *create_session (WockyJingleFactory *fac, const gchar *sid, const gchar *jid, WockyJingleDialect dialect, gboolean local_hold); static gboolean session_query_cap_cb ( WockyJingleSession *session, WockyContact *contact, const gchar *cap_or_quirk, gpointer user_data); static void session_terminated_cb (WockyJingleSession *sess, gboolean local_terminator, WockyJingleReason reason, const gchar *text, WockyJingleFactory *fac); static void attach_to_wocky_session (WockyJingleFactory *self); static void wocky_jingle_factory_init (WockyJingleFactory *obj) { WockyJingleFactoryPrivate *priv = G_TYPE_INSTANCE_GET_PRIVATE (obj, WOCKY_TYPE_JINGLE_FACTORY, WockyJingleFactoryPrivate); obj->priv = priv; priv->sessions = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref); priv->transports = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, NULL); priv->content_types = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, NULL); priv->dispose_has_run = FALSE; } static void wocky_jingle_factory_dispose (GObject *object) { WockyJingleFactory *fac = WOCKY_JINGLE_FACTORY (object); WockyJingleFactoryPrivate *priv = fac->priv; GHashTableIter iter; gpointer val; if (priv->dispose_has_run) return; DEBUG ("dispose called"); priv->dispose_has_run = TRUE; wocky_jingle_factory_stop (fac); g_clear_object (&priv->session); g_clear_object (&priv->porter); g_hash_table_iter_init (&iter, priv->sessions); while (g_hash_table_iter_next (&iter, NULL, &val)) g_signal_handlers_disconnect_by_func (val, session_query_cap_cb, fac); g_hash_table_unref (priv->sessions); priv->sessions = NULL; g_hash_table_unref (priv->content_types); priv->content_types = NULL; g_hash_table_unref (priv->transports); priv->transports = NULL; g_clear_object (&priv->jingle_info); if (G_OBJECT_CLASS (wocky_jingle_factory_parent_class)->dispose) G_OBJECT_CLASS (wocky_jingle_factory_parent_class)->dispose (object); } static void wocky_jingle_factory_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { WockyJingleFactory *chan = WOCKY_JINGLE_FACTORY (object); WockyJingleFactoryPrivate *priv = chan->priv; switch (property_id) { case PROP_SESSION: g_value_set_object (value, priv->session); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void wocky_jingle_factory_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { WockyJingleFactory *chan = WOCKY_JINGLE_FACTORY (object); WockyJingleFactoryPrivate *priv = chan->priv; switch (property_id) { case PROP_SESSION: priv->session = g_value_dup_object (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void wocky_jingle_factory_constructed (GObject *obj) { WockyJingleFactory *self = WOCKY_JINGLE_FACTORY (obj); GObjectClass *parent = G_OBJECT_CLASS (wocky_jingle_factory_parent_class); if (parent->constructed != NULL) parent->constructed (obj); attach_to_wocky_session (self); jingle_media_rtp_register (self); jingle_transport_google_register (self); jingle_transport_rawudp_register (self); jingle_transport_iceudp_register (self); } static void wocky_jingle_factory_class_init (WockyJingleFactoryClass *cls) { GObjectClass *object_class = G_OBJECT_CLASS (cls); GParamSpec *param_spec; g_type_class_add_private (cls, sizeof (WockyJingleFactoryPrivate)); object_class->constructed = wocky_jingle_factory_constructed; object_class->get_property = wocky_jingle_factory_get_property; object_class->set_property = wocky_jingle_factory_set_property; object_class->dispose = wocky_jingle_factory_dispose; param_spec = g_param_spec_object ("session", "WockySession object", "WockySession to listen for Jingle sessions on", WOCKY_TYPE_SESSION, G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, PROP_SESSION, param_spec); /* signal definitions */ /* * @session: a fresh new Jingle session for your listening pleasure * @initiated_locally: %TRUE if this is a new outgoing session; %FALSE if it * is a new incoming session */ signals[NEW_SESSION] = g_signal_new ("new-session", G_TYPE_FROM_CLASS (cls), G_SIGNAL_RUN_LAST, 0, NULL, NULL, _wocky_signals_marshal_VOID__OBJECT_BOOL, G_TYPE_NONE, 2, WOCKY_TYPE_JINGLE_SESSION, G_TYPE_BOOLEAN); /* * @contact: the peer in a call * @cap: the XEP-0115 feature string the session is interested in. * * Emitted when a Jingle session wants to check whether the peer has a * particular capability. The handler should return %TRUE if @contact has * @cap. */ signals[QUERY_CAP] = g_signal_new ("query-cap", G_TYPE_FROM_CLASS (cls), G_SIGNAL_RUN_LAST, 0, g_signal_accumulator_first_wins, NULL, _wocky_signals_marshal_BOOLEAN__OBJECT_STRING, G_TYPE_BOOLEAN, 2, WOCKY_TYPE_CONTACT, G_TYPE_STRING); } WockyJingleFactory * wocky_jingle_factory_new ( WockySession *session) { return g_object_new (WOCKY_TYPE_JINGLE_FACTORY, "session", session, NULL); } static void attach_to_wocky_session (WockyJingleFactory *self) { WockyJingleFactoryPrivate *priv = self->priv; g_assert (priv->session != NULL); g_assert (priv->porter == NULL); priv->porter = g_object_ref (wocky_session_get_porter (priv->session)); /* TODO: we could match different dialects here maybe? */ priv->jingle_handler_id = wocky_porter_register_handler_from_anyone ( priv->porter, WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_SET, WOCKY_PORTER_HANDLER_PRIORITY_NORMAL, jingle_cb, self, NULL); priv->jingle_info = wocky_jingle_info_new (priv->porter); } void wocky_jingle_factory_stop (WockyJingleFactory *self) { WockyJingleFactoryPrivate *priv = self->priv; if (priv->porter != NULL && priv->jingle_handler_id != 0) { wocky_porter_unregister_handler (priv->porter, priv->jingle_handler_id); priv->jingle_handler_id = 0; } } /* The 'session' map is keyed by: * "\n" */ #define SESSION_MAP_KEY_FORMAT "%s\n%s" static gchar * make_session_map_key ( const gchar *jid, const gchar *sid) { return g_strdup_printf (SESSION_MAP_KEY_FORMAT, jid, sid); } static gchar * get_unique_sid_for (WockyJingleFactory *factory, const gchar *jid, gchar **key) { guint32 val; gchar *sid = NULL; gchar *key_ = NULL; do { val = g_random_int_range (1000000, G_MAXINT); g_free (sid); g_free (key_); sid = g_strdup_printf ("%u", val); key_ = make_session_map_key (jid, sid); } while (g_hash_table_lookup (factory->priv->sessions, key_) != NULL); *key = key_; return sid; } static WockyJingleSession * ensure_session (WockyJingleFactory *self, const gchar *sid, const gchar *from, WockyJingleAction action, WockyJingleDialect dialect, gboolean *new_session, GError **error) { WockyJingleFactoryPrivate *priv = self->priv; gchar *key; WockyJingleSession *sess; if (!wocky_decode_jid (from, NULL, NULL, NULL)) { g_prefix_error (error, "Couldn't parse sender '%s': ", from); return NULL; } /* If we can ensure the handle, we can decode the jid */ key = make_session_map_key (from, sid); sess = g_hash_table_lookup (priv->sessions, key); g_free (key); if (sess == NULL) { if (action == WOCKY_JINGLE_ACTION_SESSION_INITIATE) { sess = create_session (self, sid, from, dialect, FALSE); *new_session = TRUE; } else { g_set_error (error, WOCKY_JINGLE_ERROR, WOCKY_JINGLE_ERROR_UNKNOWN_SESSION, "session %s is unknown", sid); return NULL; } } else { *new_session = FALSE; } return sess; } static gboolean jingle_cb ( WockyPorter *porter, WockyStanza *msg, gpointer user_data) { WockyJingleFactory *self = WOCKY_JINGLE_FACTORY (user_data); GError *error = NULL; const gchar *sid, *from; WockyJingleSession *sess; gboolean new_session = FALSE; WockyJingleAction action; WockyJingleDialect dialect; /* see if it's a jingle message and detect dialect */ sid = wocky_jingle_session_detect (msg, &action, &dialect); from = wocky_stanza_get_from (msg); if (sid == NULL || from == NULL) return FALSE; sess = ensure_session (self, sid, from, action, dialect, &new_session, &error); if (sess == NULL) goto REQUEST_ERROR; else /* One of the possible outcomes of wocky_jingle_session_parse() is that * the Jingle session is terminated, which removes it from our * hash table, which could release its last ref. */ g_object_ref (sess); /* now act on the message */ if (!wocky_jingle_session_parse (sess, action, msg, &error)) goto REQUEST_ERROR; /* This has to be after the call to parse(), not inside create_session(): * until the session has parsed the session-initiate stanza, it does not know * about its own contents, and we don't even know if the content types are * something we understand. So it's essentially half-alive and useless to * signal listeners. */ if (new_session) g_signal_emit (self, signals[NEW_SESSION], 0, sess, FALSE); /* all went well, we can acknowledge the IQ */ wocky_jingle_session_acknowledge_iq (sess, msg); g_object_unref (sess); return TRUE; REQUEST_ERROR: g_assert (error != NULL); DEBUG ("NAKing with error: %s", error->message); wocky_porter_send_iq_gerror (porter, msg, error); g_error_free (error); if (sess != NULL) { if (new_session) wocky_jingle_session_terminate (sess, WOCKY_JINGLE_REASON_UNKNOWN, NULL, NULL); g_object_unref (sess); } return TRUE; } static gboolean session_query_cap_cb ( WockyJingleSession *session, WockyContact *contact, const gchar *cap_or_quirk, gpointer user_data) { WockyJingleFactory *self = WOCKY_JINGLE_FACTORY (user_data); gboolean ret; /* Propagate the query out to the application. We can't depend on the * application connecting to ::query-cap on the session because caps queries * may happen while parsing the session-initiate stanza, which must happen * before the session is announced to the application. */ g_signal_emit (self, signals[QUERY_CAP], 0, contact, cap_or_quirk, &ret); return ret; } /* * If sid is set to NULL a unique sid is generated and * the "local-initiator" property of the newly created * WockyJingleSession is set to true. */ static WockyJingleSession * create_session (WockyJingleFactory *fac, const gchar *sid, const gchar *jid, WockyJingleDialect dialect, gboolean local_hold) { WockyJingleFactoryPrivate *priv = fac->priv; WockyJingleSession *sess; gboolean local_initiator; gchar *sid_, *key; gpointer contact; WockyContactFactory *factory; factory = wocky_session_get_contact_factory (priv->session); g_assert (jid != NULL); if (strchr (jid, '/') != NULL) contact = wocky_contact_factory_ensure_resource_contact (factory, jid); else contact = wocky_contact_factory_ensure_bare_contact (factory, jid); g_return_val_if_fail (contact != NULL, NULL); g_return_val_if_fail (WOCKY_IS_CONTACT (contact), NULL); if (sid != NULL) { key = make_session_map_key (jid, sid); sid_ = g_strdup (sid); local_initiator = FALSE; } else { sid_ = get_unique_sid_for (fac, jid, &key); local_initiator = TRUE; } /* Either we should have found the existing session when the IQ arrived, or * get_unique_sid_for should have ensured the key is fresh. */ g_assert (NULL == g_hash_table_lookup (priv->sessions, key)); sess = wocky_jingle_session_new ( fac, priv->porter, sid_, local_initiator, contact, dialect, local_hold); g_signal_connect (sess, "terminated", (GCallback) session_terminated_cb, fac); /* Takes ownership of key and sess */ g_hash_table_insert (priv->sessions, key, sess); DEBUG ("new session (%s, %s) @ %p", jid, sid_, sess); g_free (sid_); g_object_unref (contact); g_signal_connect (sess, "query-cap", (GCallback) session_query_cap_cb, (GObject *) fac); return sess; } /** * wocky_jingle_factory_create_session: * @fac: the factory * @jid: the full JID (typically including a resource) to establish a session * with * @dialect: the variant of the Jingle protocol to use * @local_hold: whether the call should start out on hold; if in doubt, pass %FALSE * * Creates a new #WockyJingleSession to the specified contact. Note that the * session will not be initiated until at least one content is added with * wocky_jingle_session_add_content(), and those contents are ready. * * You would typically determine which @dialect to use from the peer's * capabilities. * * Returns: (transfer none): the new session, which will not be %NULL */ WockyJingleSession * wocky_jingle_factory_create_session (WockyJingleFactory *fac, const gchar *jid, WockyJingleDialect dialect, gboolean local_hold) { WockyJingleSession *session = create_session (fac, NULL, jid, dialect, local_hold); g_signal_emit (fac, signals[NEW_SESSION], 0, session, TRUE); return session; } void wocky_jingle_factory_register_transport (WockyJingleFactory *self, gchar *xmlns, GType transport_type) { g_return_if_fail (g_type_is_a (transport_type, WOCKY_TYPE_JINGLE_TRANSPORT_IFACE)); g_hash_table_insert (self->priv->transports, xmlns, GSIZE_TO_POINTER (transport_type)); } GType wocky_jingle_factory_lookup_transport (WockyJingleFactory *self, const gchar *xmlns) { return GPOINTER_TO_SIZE (g_hash_table_lookup (self->priv->transports, xmlns)); } void wocky_jingle_factory_register_content_type (WockyJingleFactory *self, gchar *xmlns, GType content_type) { g_return_if_fail (g_type_is_a (content_type, WOCKY_TYPE_JINGLE_CONTENT)); g_hash_table_insert (self->priv->content_types, xmlns, GSIZE_TO_POINTER (content_type)); } GType wocky_jingle_factory_lookup_content_type (WockyJingleFactory *self, const gchar *xmlns) { return GPOINTER_TO_SIZE (g_hash_table_lookup (self->priv->content_types, xmlns)); } static void session_terminated_cb (WockyJingleSession *session, gboolean local_terminator G_GNUC_UNUSED, WockyJingleReason reason G_GNUC_UNUSED, const gchar *text G_GNUC_UNUSED, WockyJingleFactory *factory) { gchar *key = make_session_map_key ( wocky_jingle_session_get_peer_jid (session), wocky_jingle_session_get_sid (session)); DEBUG ("removing terminated session with key %s", key); g_signal_handlers_disconnect_by_func (session, session_query_cap_cb, factory); g_warn_if_fail (g_hash_table_remove (factory->priv->sessions, key)); g_free (key); } WockyJingleInfo * wocky_jingle_factory_get_jingle_info ( WockyJingleFactory *self) { return self->priv->jingle_info; } telepathy-gabble-0.18.2/lib/ext/wocky/wocky/wocky-jingle-content.c0000644000175000017500000011747412312321734025105 0ustar00smcvsmcv00000000000000/* * wocky-jingle-content.c - Source for WockyJingleContent * Copyright (C) 2008 Collabora Ltd. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 "config.h" #include "wocky-jingle-content.h" #include #include #include #include #define WOCKY_DEBUG_FLAG WOCKY_DEBUG_JINGLE #include "wocky-debug-internal.h" #include "wocky-jingle-factory.h" #include "wocky-jingle-session.h" #include "wocky-jingle-transport-iface.h" #include "wocky-jingle-transport-google.h" #include "wocky-jingle-media-rtp.h" #include "wocky-namespaces.h" #include "wocky-signals-marshal.h" #include "wocky-utils.h" /* signal enum */ enum { READY, NEW_CANDIDATES, REMOVED, NEW_SHARE_CHANNEL, COMPLETED, LAST_SIGNAL }; static guint signals[LAST_SIGNAL] = {0}; /* properties */ enum { PROP_SESSION = 1, PROP_CONTENT_NS, PROP_TRANSPORT_NS, PROP_NAME, PROP_SENDERS, PROP_STATE, PROP_DISPOSITION, PROP_LOCALLY_CREATED, LAST_PROPERTY }; struct _WockyJingleContentPrivate { gchar *name; gchar *creator; gboolean created_by_us; WockyJingleContentState state; WockyJingleContentSenders senders; gchar *content_ns; gchar *transport_ns; gchar *disposition; WockyJingleTransportIface *transport; /* Whether we've got the codecs (intersection) ready. */ gboolean media_ready; /* Whether we have at least one local candidate. */ gboolean have_local_candidates; guint gtalk4_event_id; guint last_share_channel_component_id; gboolean dispose_has_run; }; #define DEFAULT_CONTENT_TIMEOUT 60000 /* lookup tables */ G_DEFINE_TYPE(WockyJingleContent, wocky_jingle_content, G_TYPE_OBJECT); static void new_transport_candidates_cb (WockyJingleTransportIface *trans, GList *candidates, WockyJingleContent *content); static void _maybe_ready (WockyJingleContent *self); static void transport_created (WockyJingleContent *c); static void wocky_jingle_content_init (WockyJingleContent *obj) { WockyJingleContentPrivate *priv = G_TYPE_INSTANCE_GET_PRIVATE (obj, WOCKY_TYPE_JINGLE_CONTENT, WockyJingleContentPrivate); obj->priv = priv; DEBUG ("%p", obj); priv->state = WOCKY_JINGLE_CONTENT_STATE_EMPTY; priv->created_by_us = TRUE; priv->media_ready = FALSE; priv->have_local_candidates = FALSE; priv->gtalk4_event_id = 0; priv->dispose_has_run = FALSE; obj->session = NULL; } static void wocky_jingle_content_dispose (GObject *object) { WockyJingleContent *content = WOCKY_JINGLE_CONTENT (object); WockyJingleContentPrivate *priv = content->priv; if (priv->dispose_has_run) return; DEBUG ("%p", object); priv->dispose_has_run = TRUE; if (priv->gtalk4_event_id != 0) { g_source_remove (priv->gtalk4_event_id); priv->gtalk4_event_id = 0; } g_free (priv->name); priv->name = NULL; g_free (priv->creator); priv->creator = NULL; g_free (priv->content_ns); priv->content_ns = NULL; g_free (priv->transport_ns); priv->transport_ns = NULL; g_free (priv->disposition); priv->disposition = NULL; if (G_OBJECT_CLASS (wocky_jingle_content_parent_class)->dispose) G_OBJECT_CLASS (wocky_jingle_content_parent_class)->dispose (object); } static void wocky_jingle_content_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { WockyJingleContent *self = WOCKY_JINGLE_CONTENT (object); WockyJingleContentPrivate *priv = self->priv; switch (property_id) { case PROP_SESSION: g_value_set_object (value, self->session); break; case PROP_NAME: g_value_set_string (value, priv->name); break; case PROP_SENDERS: g_value_set_uint (value, priv->senders); break; case PROP_STATE: g_value_set_uint (value, priv->state); break; case PROP_CONTENT_NS: g_value_set_string (value, priv->content_ns); break; case PROP_TRANSPORT_NS: g_value_set_string (value, priv->transport_ns); break; case PROP_DISPOSITION: g_value_set_string (value, priv->disposition); break; case PROP_LOCALLY_CREATED: g_value_set_boolean (value, priv->created_by_us); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void wocky_jingle_content_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { WockyJingleContent *self = WOCKY_JINGLE_CONTENT (object); WockyJingleContentPrivate *priv = self->priv; switch (property_id) { case PROP_SESSION: self->session = g_value_get_object (value); break; case PROP_CONTENT_NS: g_free (priv->content_ns); priv->content_ns = g_value_dup_string (value); break; case PROP_TRANSPORT_NS: g_free (priv->transport_ns); priv->transport_ns = g_value_dup_string (value); /* We can't switch transports. */ g_assert (priv->transport == NULL); if (priv->transport_ns != NULL) { GType transport_type = wocky_jingle_factory_lookup_transport ( wocky_jingle_session_get_factory (self->session), priv->transport_ns); g_assert (transport_type != 0); priv->transport = wocky_jingle_transport_iface_new (transport_type, self, priv->transport_ns); g_signal_connect (priv->transport, "new-candidates", (GCallback) new_transport_candidates_cb, self); transport_created (self); } break; case PROP_NAME: /* can't rename */ g_assert (priv->name == NULL); priv->name = g_value_dup_string (value); break; case PROP_SENDERS: priv->senders = g_value_get_uint (value); break; case PROP_STATE: priv->state = g_value_get_uint (value); break; case PROP_DISPOSITION: g_assert (priv->disposition == NULL); priv->disposition = g_value_dup_string (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static WockyJingleContentSenders get_default_senders_real (WockyJingleContent *c) { return WOCKY_JINGLE_CONTENT_SENDERS_BOTH; } static void wocky_jingle_content_class_init (WockyJingleContentClass *cls) { GParamSpec *param_spec; GObjectClass *object_class = G_OBJECT_CLASS (cls); g_type_class_add_private (cls, sizeof (WockyJingleContentPrivate)); object_class->get_property = wocky_jingle_content_get_property; object_class->set_property = wocky_jingle_content_set_property; object_class->dispose = wocky_jingle_content_dispose; cls->get_default_senders = get_default_senders_real; /* property definitions */ param_spec = g_param_spec_object ("session", "WockyJingleSession object", "Jingle session object that owns this content.", WOCKY_TYPE_JINGLE_SESSION, G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, PROP_SESSION, param_spec); param_spec = g_param_spec_string ("name", "Content name", "A unique content name in the session.", NULL, G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, PROP_NAME, param_spec); param_spec = g_param_spec_string ("content-ns", "Content namespace", "Namespace identifying the content type.", NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, PROP_CONTENT_NS, param_spec); param_spec = g_param_spec_string ("transport-ns", "Transport namespace", "Namespace identifying the transport type.", NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, PROP_TRANSPORT_NS, param_spec); param_spec = g_param_spec_uint ("senders", "Stream senders", "Valid senders for the stream.", 0, G_MAXUINT32, WOCKY_JINGLE_CONTENT_SENDERS_NONE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, PROP_SENDERS, param_spec); param_spec = g_param_spec_uint ("state", "Content state", "The current state that the content is in.", 0, G_MAXUINT32, WOCKY_JINGLE_CONTENT_STATE_EMPTY, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, PROP_STATE, param_spec); param_spec = g_param_spec_string ("disposition", "Content disposition", "Distinguishes between 'session' and other contents.", NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, PROP_DISPOSITION, param_spec); param_spec = g_param_spec_boolean ("locally-created", "Locally created", "True if the content was created by the local client.", FALSE, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, PROP_LOCALLY_CREATED, param_spec); /* signal definitions */ signals[READY] = g_signal_new ("ready", G_OBJECT_CLASS_TYPE (cls), G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED, 0, NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); /** * WockyJingleContent::new-candidates: * @content: the content * @candidates: (type GList) (element-type WockyJingleCandidate): a #GList of new candidates * * Emitted when new candidates are received from the peer. */ signals[NEW_CANDIDATES] = g_signal_new ( "new-candidates", G_TYPE_FROM_CLASS (cls), G_SIGNAL_RUN_LAST, 0, NULL, NULL, g_cclosure_marshal_VOID__POINTER, G_TYPE_NONE, 1, G_TYPE_POINTER); signals[NEW_SHARE_CHANNEL] = g_signal_new ( "new-share-channel", G_TYPE_FROM_CLASS (cls), G_SIGNAL_RUN_LAST, 0, NULL, NULL, _wocky_signals_marshal_VOID__STRING_UINT, G_TYPE_NONE, 2, G_TYPE_STRING, G_TYPE_UINT); signals[COMPLETED] = g_signal_new ( "completed", G_TYPE_FROM_CLASS (cls), G_SIGNAL_RUN_LAST, 0, NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); /* This signal serves as notification that the WockyJingleContent is now * meaningless; everything holding a reference should drop it after receiving * 'removed'. */ signals[REMOVED] = g_signal_new ("removed", G_OBJECT_CLASS_TYPE (cls), G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED, 0, NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); } static WockyJingleContentSenders get_default_senders (WockyJingleContent *c) { WockyJingleContentSenders (*virtual_method)(WockyJingleContent *) = \ WOCKY_JINGLE_CONTENT_GET_CLASS (c)->get_default_senders; g_assert (virtual_method != NULL); return virtual_method (c); } static WockyJingleContentSenders parse_senders (const gchar *txt) { if (txt == NULL) return WOCKY_JINGLE_CONTENT_SENDERS_NONE; if (!wocky_strdiff (txt, "initiator")) return WOCKY_JINGLE_CONTENT_SENDERS_INITIATOR; else if (!wocky_strdiff (txt, "responder")) return WOCKY_JINGLE_CONTENT_SENDERS_RESPONDER; else if (!wocky_strdiff (txt, "both")) return WOCKY_JINGLE_CONTENT_SENDERS_BOTH; return WOCKY_JINGLE_CONTENT_SENDERS_NONE; } static const gchar * produce_senders (WockyJingleContentSenders senders) { switch (senders) { case WOCKY_JINGLE_CONTENT_SENDERS_INITIATOR: return "initiator"; case WOCKY_JINGLE_CONTENT_SENDERS_RESPONDER: return "responder"; case WOCKY_JINGLE_CONTENT_SENDERS_BOTH: return "both"; default: DEBUG ("invalid content senders %u", senders); g_assert_not_reached (); } /* to make gcc not complain */ return NULL; } #define SET_BAD_REQ(txt) \ g_set_error (error, WOCKY_XMPP_ERROR, WOCKY_XMPP_ERROR_BAD_REQUEST, txt) static void new_transport_candidates_cb (WockyJingleTransportIface *trans, GList *candidates, WockyJingleContent *content) { /* just pass the signal on */ g_signal_emit (content, signals[NEW_CANDIDATES], 0, candidates); } static void transport_created (WockyJingleContent *c) { void (*virtual_method)(WockyJingleContent *, WockyJingleTransportIface *) = \ WOCKY_JINGLE_CONTENT_GET_CLASS (c)->transport_created; if (virtual_method != NULL) virtual_method (c, c->priv->transport); } static void parse_description (WockyJingleContent *c, WockyNode *desc_node, GError **error) { void (*virtual_method)(WockyJingleContent *, WockyNode *, GError **) = WOCKY_JINGLE_CONTENT_GET_CLASS (c)->parse_description; g_assert (virtual_method != NULL); virtual_method (c, desc_node, error); } static gboolean send_gtalk4_transport_accept (gpointer user_data) { WockyJingleContent *c = WOCKY_JINGLE_CONTENT (user_data); WockyJingleContentPrivate *priv = c->priv; WockyNode *sess_node; WockyStanza *msg = wocky_jingle_session_new_message (c->session, WOCKY_JINGLE_ACTION_TRANSPORT_ACCEPT, &sess_node); DEBUG ("Sending Gtalk4 'transport-accept' message to peer"); wocky_node_add_child_ns (sess_node, "transport", priv->transport_ns); wocky_jingle_session_send (c->session, msg); priv->gtalk4_event_id = 0; return FALSE; } void wocky_jingle_content_parse_add (WockyJingleContent *c, WockyNode *content_node, gboolean google_mode, GError **error) { WockyJingleContentPrivate *priv = c->priv; const gchar *name, *creator, *senders, *disposition; WockyNode *trans_node, *desc_node; GType transport_type = 0; WockyJingleTransportIface *trans = NULL; WockyJingleDialect dialect = wocky_jingle_session_get_dialect (c->session); priv->created_by_us = FALSE; desc_node = wocky_node_get_child (content_node, "description"); trans_node = wocky_node_get_child (content_node, "transport"); creator = wocky_node_get_attribute (content_node, "creator"); name = wocky_node_get_attribute (content_node, "name"); senders = wocky_node_get_attribute (content_node, "senders"); g_assert (priv->transport_ns == NULL); if (google_mode) { if (creator == NULL) creator = "initiator"; /* the google protocols don't give the contents names, so put in a dummy * value if none was set by the session*/ if (priv->name == NULL) name = priv->name = g_strdup ("gtalk"); else name = priv->name; if (trans_node == NULL) { /* gtalk lj0.3 assumes google-p2p transport */ DEBUG ("detected GTalk3 dialect"); dialect = WOCKY_JINGLE_DIALECT_GTALK3; g_object_set (c->session, "dialect", WOCKY_JINGLE_DIALECT_GTALK3, NULL); transport_type = wocky_jingle_factory_lookup_transport ( wocky_jingle_session_get_factory (c->session), ""); /* in practice we do support gtalk-p2p, so this can't happen */ if (G_UNLIKELY (transport_type == 0)) { SET_BAD_REQ ("gtalk-p2p transport unsupported"); return; } priv->transport_ns = g_strdup (""); } } else { if (creator == NULL && wocky_jingle_session_peer_has_cap (c->session, WOCKY_QUIRK_GOOGLE_WEBMAIL_CLIENT)) { if (wocky_jingle_content_creator_is_initiator (c)) creator = "initiator"; else creator = "responder"; DEBUG ("Working around GMail omitting creator=''; assuming '%s'", creator); } if ((trans_node == NULL) || (creator == NULL) || (name == NULL)) { SET_BAD_REQ ("missing required content attributes or elements"); return; } /* In proper protocols the name comes from the stanza */ g_assert (priv->name == NULL); priv->name = g_strdup (name); } /* if we didn't set it to google-p2p implicitly already, detect it */ if (transport_type == 0) { const gchar *ns = wocky_node_get_ns (trans_node); transport_type = wocky_jingle_factory_lookup_transport ( wocky_jingle_session_get_factory (c->session), ns); if (transport_type == 0) { SET_BAD_REQ ("unsupported content transport"); return; } priv->transport_ns = g_strdup (ns); } if (senders == NULL) priv->senders = get_default_senders (c); else priv->senders = parse_senders (senders); if (priv->senders == WOCKY_JINGLE_CONTENT_SENDERS_NONE) { SET_BAD_REQ ("invalid content senders"); return; } parse_description (c, desc_node, error); if (*error != NULL) return; disposition = wocky_node_get_attribute (content_node, "disposition"); if (disposition == NULL) disposition = "session"; if (wocky_strdiff (disposition, priv->disposition)) { g_free (priv->disposition); priv->disposition = g_strdup (disposition); } DEBUG ("content creating new transport type %s", g_type_name (transport_type)); trans = wocky_jingle_transport_iface_new (transport_type, c, priv->transport_ns); g_signal_connect (trans, "new-candidates", (GCallback) new_transport_candidates_cb, c); /* Depending on transport, there may be initial candidates specified here */ if (trans_node != NULL) { wocky_jingle_transport_iface_parse_candidates (trans, trans_node, error); if (*error) { g_object_unref (trans); return; } } g_assert (priv->transport == NULL); priv->transport = trans; transport_created (c); g_assert (priv->creator == NULL); priv->creator = g_strdup (creator); priv->state = WOCKY_JINGLE_CONTENT_STATE_NEW; /* GTalk4 seems to require "transport-accept" for acknowledging * the transport type. wjt confirms that this is apparently necessary for * incoming calls to work. */ if (dialect == WOCKY_JINGLE_DIALECT_GTALK4) priv->gtalk4_event_id = g_idle_add (send_gtalk4_transport_accept, c); return; } static guint new_share_channel (WockyJingleContent *c, const gchar *name) { WockyJingleContentPrivate *priv = c->priv; WockyJingleTransportGoogle *gtrans = NULL; if (priv->transport && WOCKY_IS_JINGLE_TRANSPORT_GOOGLE (priv->transport)) { guint id = priv->last_share_channel_component_id + 1; gtrans = WOCKY_JINGLE_TRANSPORT_GOOGLE (priv->transport); if (!jingle_transport_google_set_component_name (gtrans, name, id)) return 0; priv->last_share_channel_component_id++; DEBUG ("New Share channel '%s' with id : %d", name, id); g_signal_emit (c, signals[NEW_SHARE_CHANNEL], 0, name, id); return priv->last_share_channel_component_id; } return 0; } guint wocky_jingle_content_create_share_channel (WockyJingleContent *self, const gchar *name) { WockyJingleContentPrivate *priv = self->priv; WockyNode *sess_node, *channel_node; WockyStanza *msg = NULL; /* Send the info action before creating the channel, in case candidates need to be sent on the signal emit. It doesn't matter if the channel already exists anyways... */ msg = wocky_jingle_session_new_message (self->session, WOCKY_JINGLE_ACTION_INFO, &sess_node); DEBUG ("Sending 'info' message to peer : channel %s", name); channel_node = wocky_node_add_child_ns (sess_node, "channel", priv->content_ns); wocky_node_set_attribute (channel_node, "name", name); wocky_jingle_session_send (self->session, msg); return new_share_channel (self, name); } void wocky_jingle_content_send_complete (WockyJingleContent *self) { WockyJingleContentPrivate *priv = self->priv; WockyNode *sess_node; WockyStanza *msg = NULL; msg = wocky_jingle_session_new_message (self->session, WOCKY_JINGLE_ACTION_INFO, &sess_node); DEBUG ("Sending 'info' message to peer : complete"); wocky_node_add_child_ns (sess_node, "complete", priv->content_ns); wocky_jingle_session_send (self->session, msg); } void wocky_jingle_content_parse_info (WockyJingleContent *c, WockyNode *content_node, GError **error) { WockyNode *channel_node; WockyNode *complete_node; channel_node = wocky_node_get_child (content_node, "channel"); complete_node = wocky_node_get_child (content_node, "complete"); DEBUG ("parsing info message : %p - %p", channel_node, complete_node); if (channel_node) { const gchar *name; name = wocky_node_get_attribute (channel_node, "name"); if (name != NULL) new_share_channel (c, name); } else if (complete_node) { g_signal_emit (c, signals[COMPLETED], 0); } } void wocky_jingle_content_parse_accept (WockyJingleContent *c, WockyNode *content_node, gboolean google_mode, GError **error) { WockyJingleContentPrivate *priv = c->priv; const gchar *senders; WockyNode *trans_node, *desc_node; WockyJingleDialect dialect = wocky_jingle_session_get_dialect (c->session); WockyJingleContentSenders newsenders; desc_node = wocky_node_get_child (content_node, "description"); trans_node = wocky_node_get_child (content_node, "transport"); senders = wocky_node_get_attribute (content_node, "senders"); if (WOCKY_IS_JINGLE_MEDIA_RTP (c) && WOCKY_JINGLE_DIALECT_IS_GOOGLE (dialect) && trans_node == NULL) { DEBUG ("no transport node, assuming GTalk3 dialect"); /* gtalk lj0.3 assumes google-p2p transport */ g_object_set (c->session, "dialect", WOCKY_JINGLE_DIALECT_GTALK3, NULL); } if (senders == NULL) newsenders = get_default_senders (c); else newsenders = parse_senders (senders); if (newsenders == WOCKY_JINGLE_CONTENT_SENDERS_NONE) { SET_BAD_REQ ("invalid content senders"); return; } if (newsenders != priv->senders) { DEBUG ("changing senders from %s to %s", produce_senders (priv->senders), produce_senders (newsenders)); priv->senders = newsenders; g_object_notify ((GObject *) c, "senders"); } parse_description (c, desc_node, error); if (*error != NULL) return; priv->state = WOCKY_JINGLE_CONTENT_STATE_ACKNOWLEDGED; g_object_notify ((GObject *) c, "state"); if (trans_node != NULL) { wocky_jingle_transport_iface_parse_candidates (priv->transport, trans_node, NULL); } } void wocky_jingle_content_parse_description_info (WockyJingleContent *c, WockyNode *content_node, GError **error) { WockyJingleContentPrivate *priv = c->priv; WockyNode *desc_node; desc_node = wocky_node_get_child (content_node, "description"); if (desc_node == NULL) { SET_BAD_REQ ("invalid description-info action"); return; } if (priv->created_by_us && priv->state < WOCKY_JINGLE_CONTENT_STATE_ACKNOWLEDGED) { /* The stream was created by us and the other side didn't acknowledge it * yet, thus we don't have their codec information, thus the * description-info isn't meaningful and can be ignored */ DEBUG ("Ignoring description-info as we didn't receive the codecs yet"); return; } parse_description (c, desc_node, error); } void wocky_jingle_content_produce_node (WockyJingleContent *c, WockyNode *parent, gboolean include_description, gboolean include_transport, WockyNode **trans_node_out) { WockyJingleContentPrivate *priv = c->priv; WockyNode *content_node, *trans_node; WockyJingleDialect dialect = wocky_jingle_session_get_dialect (c->session); void (*produce_desc)(WockyJingleContent *, WockyNode *) = WOCKY_JINGLE_CONTENT_GET_CLASS (c)->produce_description; if ((dialect == WOCKY_JINGLE_DIALECT_GTALK3) || (dialect == WOCKY_JINGLE_DIALECT_GTALK4)) { content_node = parent; } else { content_node = wocky_node_add_child (parent, "content"); wocky_node_set_attributes (content_node, "name", priv->name, "senders", produce_senders (priv->senders), NULL); if (wocky_jingle_content_creator_is_initiator (c)) wocky_node_set_attribute (content_node, "creator", "initiator"); else wocky_node_set_attribute (content_node, "creator", "responder"); } if (include_description) produce_desc (c, content_node); if (include_transport) { if (dialect == WOCKY_JINGLE_DIALECT_GTALK3) { /* GTalk 03 doesn't use a transport, but assumes gtalk-p2p */ trans_node = parent; } else { trans_node = wocky_node_add_child_ns (content_node, "transport", priv->transport_ns); } if (trans_node_out != NULL) *trans_node_out = trans_node; } } void wocky_jingle_content_update_senders (WockyJingleContent *c, WockyNode *content_node, GError **error) { WockyJingleContentPrivate *priv = c->priv; WockyJingleContentSenders senders; senders = parse_senders (wocky_node_get_attribute (content_node, "senders")); if (senders == WOCKY_JINGLE_CONTENT_SENDERS_NONE) { SET_BAD_REQ ("invalid content senders in stream"); return; } priv->senders = senders; g_object_notify ((GObject *) c, "senders"); } void wocky_jingle_content_parse_transport_info (WockyJingleContent *self, WockyNode *trans_node, GError **error) { WockyJingleContentPrivate *priv = self->priv; wocky_jingle_transport_iface_parse_candidates (priv->transport, trans_node, error); } /** * wocky_jingle_content_add_candidates: * @self: the content * @li: (element-type WockyJingleCandidate) (transfer full): a list of * #WockyJingleCandidate structs, allocated with wocky_jingle_candidate_new(). * * Adds the candidates listed in @li to the content, communicating them to the * peer if appropriate. */ void wocky_jingle_content_add_candidates (WockyJingleContent *self, GList *li) { WockyJingleContentPrivate *priv = self->priv; DEBUG ("called content: %s created_by_us: %d", priv->name, priv->created_by_us); if (li == NULL) return; wocky_jingle_transport_iface_new_local_candidates (priv->transport, li); if (!priv->have_local_candidates) { priv->have_local_candidates = TRUE; /* Maybe we were waiting for at least one candidate? */ _maybe_ready (self); } /* If the content exists on the wire, let the transport send this candidate * if it wants to. */ if (priv->state > WOCKY_JINGLE_CONTENT_STATE_EMPTY) wocky_jingle_transport_iface_send_candidates (priv->transport, FALSE); } /* Returns whether the content is ready to be signalled (initiated, for local * streams, or acknowledged, for remote streams. */ gboolean wocky_jingle_content_is_ready (WockyJingleContent *self) { WockyJingleContentPrivate *priv = self->priv; if (priv->created_by_us) { /* If it's created by us, media ready, not signalled, and we have * at least one local candidate, it's ready to be added. */ if (priv->media_ready && priv->state == WOCKY_JINGLE_CONTENT_STATE_EMPTY && (!WOCKY_IS_JINGLE_MEDIA_RTP (self) || priv->have_local_candidates)) return TRUE; } else { /* If it's created by peer, media and transports ready, * and not acknowledged yet, it's ready for acceptance. */ if (priv->media_ready && priv->state == WOCKY_JINGLE_CONTENT_STATE_NEW && (!WOCKY_IS_JINGLE_MEDIA_RTP (self) || wocky_jingle_transport_iface_can_accept (priv->transport))) return TRUE; } return FALSE; } static void send_content_add_or_accept (WockyJingleContent *self) { WockyJingleContentPrivate *priv = self->priv; WockyStanza *msg; WockyNode *sess_node, *transport_node; WockyJingleAction action; WockyJingleContentState new_state = WOCKY_JINGLE_CONTENT_STATE_EMPTY; g_assert (wocky_jingle_content_is_ready (self)); if (priv->created_by_us) { /* TODO: set a timer for acknowledgement */ action = WOCKY_JINGLE_ACTION_CONTENT_ADD; new_state = WOCKY_JINGLE_CONTENT_STATE_SENT; } else { action = WOCKY_JINGLE_ACTION_CONTENT_ACCEPT; new_state = WOCKY_JINGLE_CONTENT_STATE_ACKNOWLEDGED; } msg = wocky_jingle_session_new_message (self->session, action, &sess_node); wocky_jingle_content_produce_node (self, sess_node, TRUE, TRUE, &transport_node); wocky_jingle_transport_iface_inject_candidates (priv->transport, transport_node); wocky_jingle_session_send (self->session, msg); priv->state = new_state; g_object_notify (G_OBJECT (self), "state"); } static void _maybe_ready (WockyJingleContent *self) { WockyJingleContentPrivate *priv = self->priv; WockyJingleState state; if (!wocky_jingle_content_is_ready (self)) return; /* If content disposition is session and session * is not yet acknowledged/active, we signall * the readiness to the session and let it take * care of it. Otherwise, we can deal with it * ourselves. */ g_object_get (self->session, "state", &state, NULL); if (!wocky_strdiff (priv->disposition, "session") && (state < WOCKY_JINGLE_STATE_PENDING_ACCEPT_SENT)) { /* Notify the session that we're ready for * session-initiate/session-accept */ g_signal_emit (self, signals[READY], 0); } else { if (state >= WOCKY_JINGLE_STATE_PENDING_INITIATE_SENT) { send_content_add_or_accept (self); /* if neccessary, transmit the candidates */ wocky_jingle_transport_iface_send_candidates (priv->transport, FALSE); } else { /* non session-disposition content ready without session * being initiated at all? */ DEBUG ("session not initiated yet, ignoring non-session ready content"); return; } } } void wocky_jingle_content_maybe_send_description (WockyJingleContent *self) { WockyJingleContentPrivate *priv = self->priv; /* If we didn't send the content yet there is no reason to send a * description-info to update it */ if (priv->state < WOCKY_JINGLE_CONTENT_STATE_SENT) return; if (wocky_jingle_session_defines_action (self->session, WOCKY_JINGLE_ACTION_DESCRIPTION_INFO)) { WockyNode *sess_node; WockyStanza *msg = wocky_jingle_session_new_message (self->session, WOCKY_JINGLE_ACTION_DESCRIPTION_INFO, &sess_node); wocky_jingle_content_produce_node (self, sess_node, TRUE, FALSE, NULL); wocky_jingle_session_send (self->session, msg); } else { DEBUG ("not sending description-info, speaking an old dialect"); } } /* Used when session-initiate is sent (so all initial contents transmit their * candidates), and when we detect gtalk3 after we've transmitted some * candidates. */ void wocky_jingle_content_retransmit_candidates (WockyJingleContent *self, gboolean all) { wocky_jingle_transport_iface_send_candidates (self->priv->transport, all); } void wocky_jingle_content_inject_candidates (WockyJingleContent *self, WockyNode *transport_node) { wocky_jingle_transport_iface_inject_candidates (self->priv->transport, transport_node); } /* Called by a subclass when the media is ready (e.g. we got local codecs) */ void _wocky_jingle_content_set_media_ready (WockyJingleContent *self) { WockyJingleContentPrivate *priv = self->priv; DEBUG ("media ready on content: %s created_by_us: %d", priv->name, priv->created_by_us); priv->media_ready = TRUE; _maybe_ready (self); } void wocky_jingle_content_set_transport_state (WockyJingleContent *self, WockyJingleTransportState state) { WockyJingleContentPrivate *priv = self->priv; g_object_set (priv->transport, "state", state, NULL); _maybe_ready (self); } GList * wocky_jingle_content_get_remote_candidates (WockyJingleContent *c) { WockyJingleContentPrivate *priv = c->priv; return wocky_jingle_transport_iface_get_remote_candidates (priv->transport); } GList * wocky_jingle_content_get_local_candidates (WockyJingleContent *c) { WockyJingleContentPrivate *priv = c->priv; return wocky_jingle_transport_iface_get_local_candidates (priv->transport); } gboolean wocky_jingle_content_get_credentials (WockyJingleContent *c, gchar **ufrag, gchar **pwd) { WockyJingleContentPrivate *priv = c->priv; return jingle_transport_get_credentials (priv->transport, ufrag, pwd); } gboolean wocky_jingle_content_change_direction (WockyJingleContent *c, WockyJingleContentSenders senders) { WockyJingleContentPrivate *priv = c->priv; WockyStanza *msg; WockyNode *sess_node; WockyJingleDialect dialect = wocky_jingle_session_get_dialect (c->session); if (senders == priv->senders) return TRUE; priv->senders = senders; g_object_notify (G_OBJECT (c), "senders"); if (WOCKY_JINGLE_DIALECT_IS_GOOGLE (dialect)) { DEBUG ("ignoring direction change request for GTalk stream"); return FALSE; } if (priv->state >= WOCKY_JINGLE_CONTENT_STATE_SENT) { msg = wocky_jingle_session_new_message (c->session, WOCKY_JINGLE_ACTION_CONTENT_MODIFY, &sess_node); wocky_jingle_content_produce_node (c, sess_node, FALSE, FALSE, NULL); wocky_jingle_session_send (c->session, msg); } /* FIXME: actually check whether remote end accepts our content-modify */ return TRUE; } static void _on_remove_reply ( GObject *source, GAsyncResult *result, gpointer user_data) { WockyJingleContent *c = WOCKY_JINGLE_CONTENT (user_data); WockyJingleContentPrivate *priv = c->priv; g_assert (priv->state == WOCKY_JINGLE_CONTENT_STATE_REMOVING); DEBUG ("%p", c); /* Everything holding a reference to a content should drop it after receiving * 'removed'. */ g_signal_emit (c, signals[REMOVED], 0); g_object_unref (c); } static void _content_remove (WockyJingleContent *c, gboolean signal_peer, WockyJingleReason reason) { WockyJingleContentPrivate *priv = c->priv; WockyStanza *msg; WockyNode *sess_node; DEBUG ("called for %p (%s)", c, priv->name); /* If we were already signalled and removal is not a side-effect of * something else (sesssion termination, or removal by peer), * we have to signal removal to the peer. */ if (signal_peer && (priv->state != WOCKY_JINGLE_CONTENT_STATE_EMPTY)) { if (priv->state == WOCKY_JINGLE_CONTENT_STATE_REMOVING) { DEBUG ("ignoring request to remove content which is already being removed"); return; } priv->state = WOCKY_JINGLE_CONTENT_STATE_REMOVING; g_object_notify ((GObject *) c, "state"); msg = wocky_jingle_session_new_message (c->session, reason == WOCKY_JINGLE_REASON_UNKNOWN ? WOCKY_JINGLE_ACTION_CONTENT_REMOVE : WOCKY_JINGLE_ACTION_CONTENT_REJECT, &sess_node); if (reason != WOCKY_JINGLE_REASON_UNKNOWN) { WockyNode *reason_node = wocky_node_add_child_with_content (sess_node, "reason", NULL); wocky_node_add_child_with_content (reason_node, wocky_jingle_session_get_reason_name (reason), NULL); } wocky_jingle_content_produce_node (c, sess_node, FALSE, FALSE, NULL); wocky_porter_send_iq_async (wocky_jingle_session_get_porter (c->session), msg, NULL, _on_remove_reply, g_object_ref (c)); g_object_unref (msg); } else { DEBUG ("signalling removed with %u refs", G_OBJECT (c)->ref_count); /* Everything holding a reference to a content should drop it after receiving * 'removed'. */ g_signal_emit (c, signals[REMOVED], 0); } } void wocky_jingle_content_remove (WockyJingleContent *c, gboolean signal_peer) { _content_remove (c, signal_peer, WOCKY_JINGLE_REASON_UNKNOWN); } void wocky_jingle_content_reject (WockyJingleContent *c, WockyJingleReason reason) { _content_remove (c, TRUE, reason); } gboolean wocky_jingle_content_is_created_by_us (WockyJingleContent *c) { return c->priv->created_by_us; } gboolean wocky_jingle_content_creator_is_initiator (WockyJingleContent *c) { gboolean session_created_by_us; g_object_get (c->session, "local-initiator", &session_created_by_us, NULL); return (c->priv->created_by_us == session_created_by_us); } const gchar * wocky_jingle_content_get_name (WockyJingleContent *self) { return self->priv->name; } const gchar * wocky_jingle_content_get_ns (WockyJingleContent *self) { return self->priv->content_ns; } const gchar * wocky_jingle_content_get_transport_ns (WockyJingleContent *self) { return self->priv->transport_ns; } const gchar * wocky_jingle_content_get_disposition (WockyJingleContent *self) { return self->priv->disposition; } WockyJingleTransportType wocky_jingle_content_get_transport_type (WockyJingleContent *c) { return wocky_jingle_transport_iface_get_transport_type (c->priv->transport); } static gboolean jingle_content_has_direction (WockyJingleContent *self, gboolean sending) { WockyJingleContentPrivate *priv = self->priv; gboolean initiated_by_us; g_object_get (self->session, "local-initiator", &initiated_by_us, NULL); switch (priv->senders) { case WOCKY_JINGLE_CONTENT_SENDERS_BOTH: return TRUE; case WOCKY_JINGLE_CONTENT_SENDERS_NONE: return FALSE; case WOCKY_JINGLE_CONTENT_SENDERS_INITIATOR: return sending ? initiated_by_us : !initiated_by_us; case WOCKY_JINGLE_CONTENT_SENDERS_RESPONDER: return sending ? !initiated_by_us : initiated_by_us; } return FALSE; } gboolean wocky_jingle_content_sending (WockyJingleContent *self) { return jingle_content_has_direction (self, TRUE); } gboolean wocky_jingle_content_receiving (WockyJingleContent *self) { return jingle_content_has_direction (self, FALSE); } void wocky_jingle_content_set_sending (WockyJingleContent *self, gboolean send) { WockyJingleContentPrivate *priv = self->priv; WockyJingleContentSenders senders; gboolean initiated_by_us; if (send == wocky_jingle_content_sending (self)) return; g_object_get (self->session, "local-initiator", &initiated_by_us, NULL); if (send) { if (priv->senders == WOCKY_JINGLE_CONTENT_SENDERS_NONE) senders = (initiated_by_us ? WOCKY_JINGLE_CONTENT_SENDERS_INITIATOR : WOCKY_JINGLE_CONTENT_SENDERS_RESPONDER); else senders = WOCKY_JINGLE_CONTENT_SENDERS_BOTH; } else { if (priv->senders == WOCKY_JINGLE_CONTENT_SENDERS_BOTH) senders = (initiated_by_us ? WOCKY_JINGLE_CONTENT_SENDERS_RESPONDER : WOCKY_JINGLE_CONTENT_SENDERS_INITIATOR); else senders = WOCKY_JINGLE_CONTENT_SENDERS_NONE; } if (senders == WOCKY_JINGLE_CONTENT_SENDERS_NONE) wocky_jingle_content_remove (self, TRUE); else wocky_jingle_content_change_direction (self, senders); } void wocky_jingle_content_request_receiving (WockyJingleContent *self, gboolean receive) { WockyJingleContentPrivate *priv = self->priv; WockyJingleContentSenders senders; gboolean initiated_by_us; if (receive == wocky_jingle_content_receiving (self)) return; g_object_get (self->session, "local-initiator", &initiated_by_us, NULL); if (receive) { if (priv->senders == WOCKY_JINGLE_CONTENT_SENDERS_NONE) senders = (initiated_by_us ? WOCKY_JINGLE_CONTENT_SENDERS_RESPONDER : WOCKY_JINGLE_CONTENT_SENDERS_INITIATOR); else senders = WOCKY_JINGLE_CONTENT_SENDERS_BOTH; } else { if (priv->senders == WOCKY_JINGLE_CONTENT_SENDERS_BOTH) senders = (initiated_by_us ? WOCKY_JINGLE_CONTENT_SENDERS_INITIATOR : WOCKY_JINGLE_CONTENT_SENDERS_RESPONDER); else senders = WOCKY_JINGLE_CONTENT_SENDERS_NONE; } if (senders == WOCKY_JINGLE_CONTENT_SENDERS_NONE) wocky_jingle_content_remove (self, TRUE); else wocky_jingle_content_change_direction (self, senders); } telepathy-gabble-0.18.2/lib/ext/wocky/wocky/wocky-jabber-auth-password.c0000644000175000017500000000734412200204546026200 0ustar00smcvsmcv00000000000000#ifdef HAVE_CONFIG_H #include "config.h" #endif #include "wocky-jabber-auth-password.h" #include "wocky-auth-registry.h" #define WOCKY_DEBUG_FLAG WOCKY_DEBUG_AUTH #include "wocky-debug-internal.h" static void auth_handler_iface_init (gpointer g_iface); G_DEFINE_TYPE_WITH_CODE (WockyJabberAuthPassword, wocky_jabber_auth_password, G_TYPE_OBJECT, G_IMPLEMENT_INTERFACE (WOCKY_TYPE_AUTH_HANDLER, auth_handler_iface_init)) enum { PROP_PASSWORD = 1 }; struct _WockyJabberAuthPasswordPrivate { gchar *password; }; static void wocky_jabber_auth_password_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { WockyJabberAuthPassword *self = WOCKY_JABBER_AUTH_PASSWORD (object); WockyJabberAuthPasswordPrivate *priv = self->priv; switch (property_id) { case PROP_PASSWORD: g_value_set_string (value, priv->password); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); } } static void wocky_jabber_auth_password_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { WockyJabberAuthPassword *self = WOCKY_JABBER_AUTH_PASSWORD (object); WockyJabberAuthPasswordPrivate *priv = self->priv; switch (property_id) { case PROP_PASSWORD: g_free (priv->password); priv->password = g_value_dup_string (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); } } static void wocky_jabber_auth_password_dispose (GObject *object) { WockyJabberAuthPassword *self = WOCKY_JABBER_AUTH_PASSWORD (object); WockyJabberAuthPasswordPrivate *priv = self->priv; g_free (priv->password); G_OBJECT_CLASS (wocky_jabber_auth_password_parent_class)->dispose (object); } static void wocky_jabber_auth_password_class_init (WockyJabberAuthPasswordClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); g_type_class_add_private (klass, sizeof (WockyJabberAuthPasswordPrivate)); object_class->get_property = wocky_jabber_auth_password_get_property; object_class->set_property = wocky_jabber_auth_password_set_property; object_class->dispose = wocky_jabber_auth_password_dispose; g_object_class_install_property (object_class, PROP_PASSWORD, g_param_spec_string ("password", "password", "The password to authenticate with", NULL, G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS)); } static gboolean password_initial_response (WockyAuthHandler *handler, GString **initial_data, GError **error); static void auth_handler_iface_init (gpointer g_iface) { WockyAuthHandlerIface *iface = g_iface; iface->mechanism = WOCKY_AUTH_MECH_JABBER_PASSWORD; iface->plain = TRUE; iface->initial_response_func = password_initial_response; } static void wocky_jabber_auth_password_init (WockyJabberAuthPassword *self) { self->priv = G_TYPE_INSTANCE_GET_PRIVATE ( self, WOCKY_TYPE_JABBER_AUTH_PASSWORD, WockyJabberAuthPasswordPrivate); } WockyJabberAuthPassword * wocky_jabber_auth_password_new (const gchar *password) { return g_object_new (WOCKY_TYPE_JABBER_AUTH_PASSWORD, "password", password, NULL); } static gboolean password_initial_response (WockyAuthHandler *handler, GString **initial_data, GError **error) { WockyJabberAuthPassword *self = WOCKY_JABBER_AUTH_PASSWORD (handler); WockyJabberAuthPasswordPrivate *priv = self->priv; if (priv->password == NULL) { g_set_error (error, WOCKY_AUTH_ERROR, WOCKY_AUTH_ERROR_NO_CREDENTIALS, "No password provided"); return FALSE; } DEBUG ("Got password"); *initial_data = g_string_new (priv->password); return TRUE; } telepathy-gabble-0.18.2/lib/ext/wocky/wocky/wocky-jabber-auth-digest.c0000644000175000017500000001117712200204546025614 0ustar00smcvsmcv00000000000000#ifdef HAVE_CONFIG_H #include "config.h" #endif #include "wocky-jabber-auth-digest.h" #include "wocky-auth-registry.h" #define WOCKY_DEBUG_FLAG WOCKY_DEBUG_AUTH #include "wocky-debug-internal.h" static void auth_handler_iface_init (gpointer g_iface); G_DEFINE_TYPE_WITH_CODE (WockyJabberAuthDigest, wocky_jabber_auth_digest, G_TYPE_OBJECT, G_IMPLEMENT_INTERFACE (WOCKY_TYPE_AUTH_HANDLER, auth_handler_iface_init)) enum { PROP_SESSION_ID = 1, PROP_PASSWORD, }; struct _WockyJabberAuthDigestPrivate { gchar *session_id; gchar *password; }; static void wocky_jabber_auth_digest_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { WockyJabberAuthDigest *self = WOCKY_JABBER_AUTH_DIGEST (object); WockyJabberAuthDigestPrivate *priv = self->priv; switch (property_id) { case PROP_SESSION_ID: g_value_set_string (value, priv->session_id); break; case PROP_PASSWORD: g_value_set_string (value, priv->password); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); } } static void wocky_jabber_auth_digest_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { WockyJabberAuthDigest *self = WOCKY_JABBER_AUTH_DIGEST (object); WockyJabberAuthDigestPrivate *priv = self->priv; switch (property_id) { case PROP_SESSION_ID: g_free (priv->session_id); priv->session_id = 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 (object, property_id, pspec); } } static void wocky_jabber_auth_digest_dispose (GObject *object) { WockyJabberAuthDigest *self = WOCKY_JABBER_AUTH_DIGEST (object); WockyJabberAuthDigestPrivate *priv = self->priv; g_free (priv->session_id); g_free (priv->password); G_OBJECT_CLASS (wocky_jabber_auth_digest_parent_class)->dispose (object); } static void wocky_jabber_auth_digest_class_init (WockyJabberAuthDigestClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); g_type_class_add_private (klass, sizeof (WockyJabberAuthDigestPrivate)); object_class->get_property = wocky_jabber_auth_digest_get_property; object_class->set_property = wocky_jabber_auth_digest_set_property; object_class->dispose = wocky_jabber_auth_digest_dispose; g_object_class_install_property (object_class, PROP_SESSION_ID, g_param_spec_string ("session-id", "session-id", "The session_id to authenticate with", NULL, G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (object_class, PROP_PASSWORD, g_param_spec_string ("password", "password", "The password to authenticate with", NULL, G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS)); } static gboolean digest_initial_response (WockyAuthHandler *handler, GString **initial_data, GError **error); static void auth_handler_iface_init (gpointer g_iface) { WockyAuthHandlerIface *iface = g_iface; iface->mechanism = WOCKY_AUTH_MECH_JABBER_DIGEST; iface->plain = FALSE; iface->initial_response_func = digest_initial_response; } static void wocky_jabber_auth_digest_init (WockyJabberAuthDigest *self) { self->priv = G_TYPE_INSTANCE_GET_PRIVATE ( self, WOCKY_TYPE_JABBER_AUTH_DIGEST, WockyJabberAuthDigestPrivate); } WockyJabberAuthDigest * wocky_jabber_auth_digest_new (const gchar *session_id, const gchar *password) { return g_object_new (WOCKY_TYPE_JABBER_AUTH_DIGEST, "session-id", session_id, "password", password, NULL); } static GString * digest_generate_initial_response (const gchar *session_id, const gchar *password) { gchar *hsrc = g_strconcat (session_id, password, NULL); gchar *sha1 = g_compute_checksum_for_string (G_CHECKSUM_SHA1, hsrc, -1); GString *response = g_string_new (sha1); g_free (hsrc); g_free (sha1); return response; } static gboolean digest_initial_response (WockyAuthHandler *handler, GString **initial_data, GError **error) { WockyJabberAuthDigest *self = WOCKY_JABBER_AUTH_DIGEST (handler); WockyJabberAuthDigestPrivate *priv = self->priv; if (priv->password == NULL || priv->session_id == NULL) { g_set_error (error, WOCKY_AUTH_ERROR, WOCKY_AUTH_ERROR_NO_CREDENTIALS, "No session-id or password provided"); return FALSE; } DEBUG ("Got session-id and password"); *initial_data = digest_generate_initial_response (priv->session_id, priv->password); return TRUE; } telepathy-gabble-0.18.2/lib/ext/wocky/wocky/wocky-jabber-auth.c0000644000175000017500000004320412200204546024333 0ustar00smcvsmcv00000000000000/* * wocky-jabber-auth.c - Source for WockyJabberAuth * Copyright (C) 2009-2010 Collabora Ltd. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include "wocky-jabber-auth.h" #include "wocky-signals-marshal.h" #include "wocky-namespaces.h" #include "wocky-utils.h" #define WOCKY_DEBUG_FLAG WOCKY_DEBUG_AUTH #include "wocky-debug-internal.h" G_DEFINE_TYPE(WockyJabberAuth, wocky_jabber_auth, G_TYPE_OBJECT) enum { PROP_SESSION_ID = 1, PROP_USERNAME, PROP_RESOURCE, PROP_PASSWORD, PROP_CONNECTION, PROP_AUTH_REGISTRY, }; /* private structure */ struct _WockyJabberAuthPrivate { gboolean dispose_has_run; WockyXmppConnection *connection; gchar *username; gchar *resource; gchar *password; gchar *session_id; GCancellable *cancel; GSimpleAsyncResult *result; WockyAuthRegistry *auth_registry; gboolean allow_plain; gboolean is_secure; }; static void wocky_jabber_auth_init (WockyJabberAuth *self) { self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, WOCKY_TYPE_JABBER_AUTH, WockyJabberAuthPrivate); } static void wocky_jabber_auth_dispose (GObject *object); static void wocky_jabber_auth_finalize (GObject *object); static void wocky_jabber_auth_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { WockyJabberAuth *self = WOCKY_JABBER_AUTH (object); WockyJabberAuthPrivate *priv = self->priv; switch (property_id) { case PROP_SESSION_ID: g_free (priv->session_id); priv->session_id = g_value_dup_string (value); break; case PROP_USERNAME: g_free (priv->username); priv->username = g_value_dup_string (value); break; case PROP_RESOURCE: g_free (priv->resource); priv->resource = g_value_dup_string (value); break; case PROP_PASSWORD: g_free (priv->password); priv->password = g_value_dup_string (value); break; case PROP_CONNECTION: priv->connection = g_value_dup_object (value); break; case PROP_AUTH_REGISTRY: if (g_value_get_object (value) == NULL) priv->auth_registry = wocky_auth_registry_new (); else priv->auth_registry = g_value_dup_object (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void wocky_jabber_auth_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { WockyJabberAuth *self = WOCKY_JABBER_AUTH (object); WockyJabberAuthPrivate *priv = self->priv; switch (property_id) { case PROP_SESSION_ID: g_value_set_string (value, priv->session_id); break; case PROP_CONNECTION: g_value_set_object (value, priv->connection); break; case PROP_AUTH_REGISTRY: g_value_set_object (value, priv->auth_registry); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void wocky_jabber_auth_class_init (WockyJabberAuthClass *wocky_jabber_auth_class) { GObjectClass *object_class = G_OBJECT_CLASS (wocky_jabber_auth_class); GParamSpec *spec; g_type_class_add_private (wocky_jabber_auth_class, sizeof (WockyJabberAuthPrivate)); object_class->set_property = wocky_jabber_auth_set_property; object_class->get_property = wocky_jabber_auth_get_property; spec = g_param_spec_string ("session-id", "session-id", "The XMPP session ID", NULL, G_PARAM_READWRITE|G_PARAM_CONSTRUCT); g_object_class_install_property (object_class, PROP_SESSION_ID, spec); spec = g_param_spec_string ("username", "username", "The username to authenticate with", NULL, G_PARAM_WRITABLE|G_PARAM_CONSTRUCT); g_object_class_install_property (object_class, PROP_USERNAME, spec); spec = g_param_spec_string ("resource", "resource", "The XMPP resource to bind to", NULL, G_PARAM_WRITABLE|G_PARAM_CONSTRUCT); g_object_class_install_property (object_class, PROP_RESOURCE, spec); spec = g_param_spec_string ("password", "password", "The password to authenticate with", NULL, G_PARAM_WRITABLE|G_PARAM_CONSTRUCT); g_object_class_install_property (object_class, PROP_PASSWORD, spec); spec = g_param_spec_object ("connection", "connection", "The Xmpp connection to user", WOCKY_TYPE_XMPP_CONNECTION, G_PARAM_READWRITE|G_PARAM_CONSTRUCT_ONLY); g_object_class_install_property (object_class, PROP_CONNECTION, spec); spec = g_param_spec_object ("auth-registry", "Authentication Registry", "Authentication Registry", WOCKY_TYPE_AUTH_REGISTRY, G_PARAM_READWRITE|G_PARAM_CONSTRUCT_ONLY); g_object_class_install_property (object_class, PROP_AUTH_REGISTRY, spec); object_class->dispose = wocky_jabber_auth_dispose; object_class->finalize = wocky_jabber_auth_finalize; } void wocky_jabber_auth_dispose (GObject *object) { WockyJabberAuth *self = WOCKY_JABBER_AUTH (object); WockyJabberAuthPrivate *priv = self->priv; if (priv->connection != NULL) g_object_unref (priv->connection); if (priv->auth_registry != NULL) g_object_unref (priv->auth_registry); if (G_OBJECT_CLASS (wocky_jabber_auth_parent_class)->dispose) G_OBJECT_CLASS (wocky_jabber_auth_parent_class)->dispose (object); } void wocky_jabber_auth_finalize (GObject *object) { WockyJabberAuth *self = WOCKY_JABBER_AUTH (object); WockyJabberAuthPrivate *priv = self->priv; /* free any data held directly by the object here */ g_free (priv->session_id); g_free (priv->username); g_free (priv->resource); g_free (priv->password); G_OBJECT_CLASS (wocky_jabber_auth_parent_class)->finalize (object); } static void auth_reset (WockyJabberAuth *self) { WockyJabberAuthPrivate *priv = self->priv; g_free (priv->session_id); priv->session_id = NULL; if (priv->connection != NULL) { g_object_unref (priv->connection); priv->connection = NULL; } if (priv->cancel != NULL) { g_object_unref (priv->cancel); priv->cancel = NULL; } } static void auth_succeeded (WockyJabberAuth *self) { WockyJabberAuthPrivate *priv = self->priv; GSimpleAsyncResult *r; DEBUG ("Authentication succeeded"); auth_reset (self); r = priv->result; priv->result = NULL; g_simple_async_result_complete (r); g_object_unref (r); } static void auth_failed (WockyJabberAuth *self, gint code, const gchar *format, ...) { gchar *message; va_list args; GSimpleAsyncResult *r; GError *error = NULL; WockyJabberAuthPrivate *priv = self->priv; auth_reset (self); va_start (args, format); message = g_strdup_vprintf (format, args); va_end (args); DEBUG ("Authentication failed!: %s", message); r = priv->result; priv->result = NULL; error = g_error_new_literal (WOCKY_AUTH_ERROR, code, message); g_simple_async_result_set_from_error (r, error); wocky_auth_registry_failure (priv->auth_registry, error); g_simple_async_result_complete (r); g_object_unref (r); g_error_free (error); g_free (message); } static gboolean stream_error (WockyJabberAuth *self, WockyStanza *stanza) { GError *error = NULL; if (stanza == NULL) { auth_failed (self, WOCKY_AUTH_ERROR_CONNRESET, "Disconnected"); return TRUE; } if (wocky_stanza_extract_stream_error (stanza, &error)) { auth_failed (self, WOCKY_AUTH_ERROR_STREAM, "%s: %s", wocky_enum_to_nick (WOCKY_TYPE_XMPP_STREAM_ERROR, error->code), error->message); g_error_free (error); return TRUE; } return FALSE; } WockyJabberAuth * wocky_jabber_auth_new (const gchar *session_id, const gchar *username, const gchar *resource, const gchar *password, WockyXmppConnection *connection, WockyAuthRegistry *auth_registry) { return g_object_new (WOCKY_TYPE_JABBER_AUTH, "session-id", session_id, "username", username, "resource", resource, "password", password, "connection", connection, "auth-registry", auth_registry, NULL); } gboolean wocky_jabber_auth_authenticate_finish (WockyJabberAuth *self, GAsyncResult *result, GError **error) { wocky_implement_finish_void (self, wocky_jabber_auth_authenticate_async); } static void wocky_jabber_auth_success_cb (GObject *source_object, GAsyncResult *res, gpointer user_data) { WockyJabberAuth *self = (WockyJabberAuth *) user_data; WockyJabberAuthPrivate *priv = self->priv; GError *error = NULL; if (!wocky_auth_registry_success_finish (priv->auth_registry, res, &error)) { auth_failed (self, error->code, error->message); g_error_free (error); } else { auth_succeeded (self); } } static void jabber_auth_reply (GObject *source, GAsyncResult *res, gpointer user_data) { WockyJabberAuth *self = (WockyJabberAuth *) user_data; WockyJabberAuthPrivate *priv = self->priv; WockyXmppConnection *conn = priv->connection; GError *error = NULL; WockyStanza *reply = NULL; WockyStanzaType type = WOCKY_STANZA_TYPE_NONE; WockyStanzaSubType sub = WOCKY_STANZA_SUB_TYPE_NONE; DEBUG (""); reply = wocky_xmpp_connection_recv_stanza_finish (conn, res, &error); if (stream_error (self, reply)) return; wocky_stanza_get_type_info (reply, &type, &sub); if (type != WOCKY_STANZA_TYPE_IQ) { auth_failed (self, WOCKY_AUTH_ERROR_INVALID_REPLY, "Jabber Auth Reply: Response Invalid"); goto out; } switch (sub) { WockyAuthError code; case WOCKY_STANZA_SUB_TYPE_ERROR: wocky_stanza_extract_errors (reply, NULL, &error, NULL, NULL); switch (error->code) { case WOCKY_XMPP_ERROR_NOT_AUTHORIZED: code = WOCKY_AUTH_ERROR_NOT_AUTHORIZED; break; case WOCKY_XMPP_ERROR_CONFLICT: code = WOCKY_AUTH_ERROR_RESOURCE_CONFLICT; break; case WOCKY_XMPP_ERROR_NOT_ACCEPTABLE: code = WOCKY_AUTH_ERROR_NO_CREDENTIALS; break; default: code = WOCKY_AUTH_ERROR_FAILURE; } auth_failed (self, code, "Authentication failed: %s", error->message); g_clear_error (&error); break; case WOCKY_STANZA_SUB_TYPE_RESULT: wocky_auth_registry_success_async (priv->auth_registry, wocky_jabber_auth_success_cb, self); break; default: auth_failed (self, WOCKY_AUTH_ERROR_INVALID_REPLY, "Bizarre response to Jabber Auth request"); break; } out: g_object_unref (reply); } static void jabber_auth_query (GObject *source, GAsyncResult *res, gpointer user_data) { WockyJabberAuth *self = (WockyJabberAuth *) user_data; WockyJabberAuthPrivate *priv = self->priv; WockyXmppConnection *conn = priv->connection; GError *error = NULL; DEBUG (""); if (!wocky_xmpp_connection_send_stanza_finish (conn, res, &error)) { auth_failed (self, error->code, "Jabber Auth IQ Set: %s", error->message); g_error_free (error); return; } wocky_xmpp_connection_recv_stanza_async (conn, priv->cancel, jabber_auth_reply, user_data); } static void wocky_jabber_auth_start_cb (GObject *source, GAsyncResult *res, gpointer user_data) { WockyJabberAuth *self = (WockyJabberAuth *) user_data; WockyJabberAuthPrivate *priv = self->priv; WockyXmppConnection *conn = priv->connection; gchar *iqid; WockyStanza *iq; const gchar *auth_field; GError *error = NULL; WockyAuthRegistryStartData *start_data = NULL; if (!wocky_auth_registry_start_auth_finish (priv->auth_registry, res, &start_data, &error)) { auth_failed (self, error->code, error->message); g_error_free (error); return; } g_assert (start_data->mechanism != NULL); g_assert (start_data->initial_response != NULL); if (g_strcmp0 (start_data->mechanism, "X-WOCKY-JABBER-PASSWORD") == 0) auth_field = "password"; else auth_field = "digest"; iqid = wocky_xmpp_connection_new_id (conn); iq = wocky_stanza_build (WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_SET, NULL, NULL, '@', "id", iqid, '(', "query", ':', WOCKY_JABBER_NS_AUTH, '(', "username", '$', priv->username, ')', '(', auth_field, '$', start_data->initial_response->str, ')', '(', "resource", '$', priv->resource, ')', ')', NULL); wocky_xmpp_connection_send_stanza_async (conn, iq, priv->cancel, jabber_auth_query, self); g_free (iqid); g_object_unref (iq); wocky_auth_registry_start_data_free (start_data); } static void jabber_auth_fields (GObject *source, GAsyncResult *res, gpointer user_data) { WockyJabberAuth *self = (WockyJabberAuth *) user_data; WockyJabberAuthPrivate *priv = self->priv; WockyXmppConnection *conn = priv->connection; GError *error = NULL; WockyStanza *fields = NULL; WockyStanzaType type = WOCKY_STANZA_TYPE_NONE; WockyStanzaSubType sub = WOCKY_STANZA_SUB_TYPE_NONE; fields = wocky_xmpp_connection_recv_stanza_finish (conn, res, &error); if (stream_error (self, fields)) return; wocky_stanza_get_type_info (fields, &type, &sub); if (type != WOCKY_STANZA_TYPE_IQ) { auth_failed (self, WOCKY_AUTH_ERROR_FAILURE, "Jabber Auth Init: Response Invalid"); goto out; } switch (sub) { WockyNode *node = NULL; WockyAuthError code; case WOCKY_STANZA_SUB_TYPE_ERROR: wocky_stanza_extract_errors (fields, NULL, &error, NULL, NULL); if (error->code == WOCKY_XMPP_ERROR_SERVICE_UNAVAILABLE) code = WOCKY_AUTH_ERROR_NOT_SUPPORTED; else code = WOCKY_AUTH_ERROR_FAILURE; auth_failed (self, code, "Jabber Auth: %s %s", wocky_xmpp_error_string (error->code), error->message); g_clear_error (&error); break; case WOCKY_STANZA_SUB_TYPE_RESULT: node = wocky_stanza_get_top_node (fields); node = wocky_node_get_child_ns (node, "query", WOCKY_JABBER_NS_AUTH); if ((node != NULL) && (wocky_node_get_child (node, "resource") != NULL) && (wocky_node_get_child (node, "username") != NULL)) { GSList *mechanisms = NULL; if (wocky_node_get_child (node, "password") != NULL) mechanisms = g_slist_append (mechanisms, WOCKY_AUTH_MECH_JABBER_PASSWORD); if (wocky_node_get_child (node, "digest") != NULL) mechanisms = g_slist_append (mechanisms, WOCKY_AUTH_MECH_JABBER_DIGEST); wocky_auth_registry_start_auth_async (priv->auth_registry, mechanisms, priv->allow_plain, priv->is_secure, priv->username, priv->password, NULL, priv->session_id, wocky_jabber_auth_start_cb, self); g_slist_free (mechanisms); } break; default: auth_failed (self, WOCKY_AUTH_ERROR_FAILURE, "Bizarre response to Jabber Auth request"); break; } out: g_object_unref (fields); } static void jabber_auth_init_sent (GObject *source, GAsyncResult *res, gpointer user_data) { WockyJabberAuth *self = (WockyJabberAuth *) user_data; WockyJabberAuthPrivate *priv = self->priv; WockyXmppConnection *conn = priv->connection; GError *error = NULL; DEBUG (""); if (!wocky_xmpp_connection_send_stanza_finish (conn, res, &error)) { auth_failed (self, error->code, error->message); g_error_free (error); return; } wocky_xmpp_connection_recv_stanza_async (conn, priv->cancel, jabber_auth_fields, user_data); } /* Initiate jabber auth. features should contain the stream features stanza as * receiver from the session_id */ void wocky_jabber_auth_authenticate_async (WockyJabberAuth *self, gboolean allow_plain, gboolean is_secure, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { WockyJabberAuthPrivate *priv = self->priv; WockyXmppConnection *conn = priv->connection; gchar *id = wocky_xmpp_connection_new_id (conn); WockyStanza *iq = NULL; DEBUG (""); priv->allow_plain = allow_plain; priv->is_secure = is_secure; priv->result = g_simple_async_result_new (G_OBJECT (self), callback, user_data, wocky_jabber_auth_authenticate_async); if (cancellable != NULL) priv->cancel = g_object_ref (cancellable); iq = wocky_stanza_build (WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_GET, NULL, NULL, '@', "id", id, '(', "query", ':', WOCKY_JABBER_NS_AUTH, /* This is a workaround for * : while * doesn't require * us to include a username, it seems to be required by jabberd 1.4. */ '(', "username", '$', priv->username, ')', ')', NULL); wocky_xmpp_connection_send_stanza_async (conn, iq, priv->cancel, jabber_auth_init_sent, self); g_free (id); g_object_unref (iq); } telepathy-gabble-0.18.2/lib/ext/wocky/wocky/wocky-google-relay.c0000644000175000017500000002040212200204546024530 0ustar00smcvsmcv00000000000000/* * google-relay.c - Support for Google relays for Jingle * * Copyright (C) 2006-2008 Collabora Ltd. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 "config.h" #include "wocky-google-relay.h" #include #ifdef ENABLE_GOOGLE_RELAY #include #endif #define WOCKY_DEBUG_FLAG WOCKY_DEBUG_JINGLE #ifdef G_OS_WIN32 #undef ERROR #endif #include "wocky-debug-internal.h" #define RELAY_HTTP_TIMEOUT 5 struct _WockyGoogleRelayResolver { #ifdef ENABLE_GOOGLE_RELAY SoupSession *soup; #else GObject *soup; #endif }; typedef struct { GPtrArray *relays; guint component; guint requests_to_do; WockyJingleInfoRelaySessionCb callback; gpointer user_data; } RelaySessionData; static RelaySessionData * relay_session_data_new (guint requests_to_do, WockyJingleInfoRelaySessionCb callback, gpointer user_data) { RelaySessionData *rsd = g_slice_new0 (RelaySessionData); rsd->relays = g_ptr_array_sized_new (requests_to_do); g_ptr_array_set_free_func (rsd->relays, (GDestroyNotify) wocky_jingle_relay_free); rsd->component = 1; rsd->requests_to_do = requests_to_do; rsd->callback = callback; rsd->user_data = user_data; return rsd; } /* This is a GSourceFunc */ static gboolean relay_session_data_call (gpointer p) { RelaySessionData *rsd = p; g_assert (rsd->callback != NULL); rsd->callback (rsd->relays, rsd->user_data); return FALSE; } /* This is a GDestroyNotify */ static void relay_session_data_destroy (gpointer p) { RelaySessionData *rsd = p; g_ptr_array_unref (rsd->relays); g_slice_free (RelaySessionData, rsd); } #ifdef ENABLE_GOOGLE_RELAY static void translate_relay_info (GPtrArray *relays, const gchar *relay_ip, const gchar *username, const gchar *password, WockyJingleRelayType relay_type, const gchar *port_string, guint component) { guint64 portll; guint port; if (port_string == NULL) { DEBUG ("no relay port for %u found", relay_type); return; } portll = g_ascii_strtoull (port_string, NULL, 10); if (portll == 0 || portll > G_MAXUINT16) { DEBUG ("failed to parse relay port '%s' for %u", port_string, relay_type); return; } port = (guint) portll; DEBUG ("type=%u ip=%s port=%u username=%s password=%s component=%u", relay_type, relay_ip, port, username, password, component); g_ptr_array_add (relays, wocky_jingle_relay_new (relay_type, relay_ip, port, username, password, component)); } static void on_http_response (SoupSession *soup, SoupMessage *msg, gpointer user_data) { RelaySessionData *rsd = user_data; if (msg->status_code != 200) { DEBUG ("Google session creation failed, relaying not used: %d %s", msg->status_code, msg->reason_phrase); } else { /* parse a=b lines into GHashTable * (key, value both borrowed from items of the strv 'lines') */ GHashTable *map = g_hash_table_new (g_str_hash, g_str_equal); gchar **lines; guint i; const gchar *relay_ip; const gchar *relay_udp_port; const gchar *relay_tcp_port; const gchar *relay_ssltcp_port; const gchar *username; const gchar *password; gchar *escaped_str; escaped_str = g_strescape (msg->response_body->data, "\r\n"); DEBUG ("Response from Google:\n====\n%s\n====", escaped_str); g_free (escaped_str); lines = g_strsplit (msg->response_body->data, "\n", 0); if (lines != NULL) { for (i = 0; lines[i] != NULL; i++) { gchar *delim = strchr (lines[i], '='); size_t len; if (delim == NULL || delim == lines[i]) { /* ignore empty keys or lines without '=' */ continue; } len = strlen (lines[i]); if (lines[i][len - 1] == '\r') { lines[i][len - 1] = '\0'; } *delim = '\0'; g_hash_table_insert (map, lines[i], delim + 1); } } relay_ip = g_hash_table_lookup (map, "relay.ip"); relay_udp_port = g_hash_table_lookup (map, "relay.udp_port"); relay_tcp_port = g_hash_table_lookup (map, "relay.tcp_port"); relay_ssltcp_port = g_hash_table_lookup (map, "relay.ssltcp_port"); username = g_hash_table_lookup (map, "username"); password = g_hash_table_lookup (map, "password"); if (relay_ip == NULL) { DEBUG ("No relay.ip found"); } else if (username == NULL) { DEBUG ("No username found"); } else if (password == NULL) { DEBUG ("No password found"); } else { translate_relay_info (rsd->relays, relay_ip, username, password, WOCKY_JINGLE_RELAY_TYPE_UDP, relay_udp_port, rsd->component); translate_relay_info (rsd->relays, relay_ip, username, password, WOCKY_JINGLE_RELAY_TYPE_TCP, relay_tcp_port, rsd->component); translate_relay_info (rsd->relays, relay_ip, username, password, WOCKY_JINGLE_RELAY_TYPE_TLS, relay_ssltcp_port, rsd->component); } g_strfreev (lines); g_hash_table_unref (map); } rsd->component++; if ((--rsd->requests_to_do) == 0) { relay_session_data_call (rsd); relay_session_data_destroy (rsd); } } #endif /* ENABLE_GOOGLE_RELAY */ WockyGoogleRelayResolver * wocky_google_relay_resolver_new (void) { WockyGoogleRelayResolver *resolver = g_slice_new0 (WockyGoogleRelayResolver); #ifdef ENABLE_GOOGLE_RELAY resolver->soup = soup_session_async_new (); /* If we don't get answer in a few seconds, relay won't do * us much help anyways. */ g_object_set (resolver->soup, "timeout", RELAY_HTTP_TIMEOUT, NULL); #endif return resolver; } void wocky_google_relay_resolver_destroy (WockyGoogleRelayResolver *self) { g_clear_object (&self->soup); g_slice_free (WockyGoogleRelayResolver, self); } void wocky_google_relay_resolver_resolve (WockyGoogleRelayResolver *self, guint components, const gchar *server, guint16 port, const gchar *token, WockyJingleInfoRelaySessionCb callback, gpointer user_data) { RelaySessionData *rsd = relay_session_data_new (components, callback, user_data); #ifdef ENABLE_GOOGLE_RELAY gchar *url; guint i; if (server == NULL) { DEBUG ("No relay server provided, not creating google relay session"); g_idle_add_full (G_PRIORITY_DEFAULT, relay_session_data_call, rsd, relay_session_data_destroy); return; } if (token == NULL) { DEBUG ("No relay token provided, not creating google relay session"); g_idle_add_full (G_PRIORITY_DEFAULT, relay_session_data_call, rsd, relay_session_data_destroy); return; } url = g_strdup_printf ("http://%s:%u/create_session", server, (guint) port); for (i = 0; i < components; i++) { SoupMessage *msg = soup_message_new ("GET", url); DEBUG ("Trying to create a new relay session on %s", url); /* libjingle sets both headers, so shall we */ soup_message_headers_append (msg->request_headers, "X-Talk-Google-Relay-Auth", token); soup_message_headers_append (msg->request_headers, "X-Google-Relay-Auth", token); soup_session_queue_message (self->soup, msg, on_http_response, rsd); } g_free (url); #else /* !ENABLE_GOOGLE_RELAY */ DEBUG ("Google relay service is not supported"); g_idle_add_full (G_PRIORITY_DEFAULT, relay_session_data_call, rsd, relay_session_data_destroy); #endif } telepathy-gabble-0.18.2/lib/ext/wocky/wocky/wocky-heartbeat-source.h0000644000175000017500000000262712200204546025415 0ustar00smcvsmcv00000000000000/* * wocky-heartbeat-source.h: header for a GSource wrapping libiphb. * Copyright © 2010 Collabora Ltd. * Copyright © 2010 Nokia Corporation * @author Will Thompson * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 */ #if !defined (WOCKY_COMPILATION) # error "This is an internal header." #endif #ifndef WOCKY_HEARTBEAT_SOURCE_H #define WOCKY_HEARTBEAT_SOURCE_H #include G_BEGIN_DECLS typedef void (*WockyHeartbeatCallback) ( gpointer user_data); GSource *wocky_heartbeat_source_new ( guint max_interval); void wocky_heartbeat_source_update_interval ( GSource *source, guint max_interval); G_END_DECLS #endif /* WOCKY_HEARTBEAT_SOURCE_H */ telepathy-gabble-0.18.2/lib/ext/wocky/wocky/wocky-heartbeat-source.c0000644000175000017500000002620612200204546025407 0ustar00smcvsmcv00000000000000/* * wocky-heartbeat-source.c: a GSource wrapping libiphb. * Copyright © 2010 Collabora Ltd. * Copyright © 2010 Nokia Corporation * @author Will Thompson * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "wocky-heartbeat-source.h" #include #define WOCKY_DEBUG_FLAG WOCKY_DEBUG_HEARTBEAT #include "wocky-debug-internal.h" #ifdef HAVE_IPHB # include #endif typedef struct _WockyHeartbeatSource { GSource parent; #ifdef HAVE_IPHB iphb_t heartbeat; GPollFD fd; #endif guint max_interval; gint64 next_wakeup; } WockyHeartbeatSource; #if HAVE_IPHB static void wocky_heartbeat_source_degrade (WockyHeartbeatSource *self) { /* If we were using the heartbeat before, stop using it. */ if (self->heartbeat != NULL) { GSource *source = (GSource *) self; /* If this is being called from wocky_heartbeat_source_finalize(), the * source has been destroyed (which implicitly removes all polls. */ if (!g_source_is_destroyed (source)) g_source_remove_poll (source, &self->fd); DEBUG ("closing heartbeat connection"); iphb_close (self->heartbeat); self->heartbeat = NULL; } } static guint recommended_intervals[] = { IPHB_GS_WAIT_10_HOURS, IPHB_GS_WAIT_2_HOURS, IPHB_GS_WAIT_1_HOUR, IPHB_GS_WAIT_30_MINS, IPHB_GS_WAIT_10_MINS * 2, /* It aligns with the 1 hour slot. */ IPHB_GS_WAIT_10_MINS, IPHB_GS_WAIT_5_MINS, IPHB_GS_WAIT_2_5_MINS, IPHB_GS_WAIT_30_SEC}; static guint get_system_sync_interval (guint max_interval) { guint i; for (i = 0; i < G_N_ELEMENTS (recommended_intervals); i++) { if (recommended_intervals[i] <= max_interval) return recommended_intervals[i]; } return max_interval; } static void wocky_heartbeat_source_wait ( WockyHeartbeatSource *self, guint max_interval) { guint interval; int ret; if (self->heartbeat == NULL) return; if (max_interval > 0) { /* Passing the same minimum and maximum interval to iphb_wait() means * that the iphb daemon will wake us up when its internal time is a * multiple of the interval. * By using recommended intervals across the platform we can get * multiple processes waken up at the same time. */ interval = get_system_sync_interval (max_interval); DEBUG ("requested %u as maximum interval; using the recommended %u " "interval", max_interval, interval); ret = iphb_wait (self->heartbeat, interval, interval, 0); } else { ret = iphb_I_woke_up (self->heartbeat); } if (ret == -1) { DEBUG ("waiting %u failed: %s; falling back to internal timeouts", max_interval, g_strerror (errno)); wocky_heartbeat_source_degrade (self); } } #endif static gboolean wocky_heartbeat_source_prepare ( GSource *source, gint *msec_to_poll) { WockyHeartbeatSource *self = (WockyHeartbeatSource *) source; gint64 now; #if HAVE_IPHB /* If we're listening to the system heartbeat, always rely on it to wake us * up. */ if (self->heartbeat != NULL) { *msec_to_poll = -1; return FALSE; } #endif if (self->max_interval == 0) return FALSE; now = g_source_get_time (source); /* If now > self->next_wakeup, it's already time to wake up. */ if (now > self->next_wakeup) { DEBUG ("ready to wake up (at %" G_GINT64_FORMAT ")", now); return TRUE; } /* Otherwise, we should only go back to sleep for a period of * (self->next_wakeup - now). Inconveniently, g_source_get_time() gives us µs * but we need to return ms; hence the scaling. * * The value calculated here will always be positive. The difference in * seconds is non-negative; if it's zero, the difference in microseconds is * positive. */ *msec_to_poll = (self->next_wakeup - now) / 1000; return FALSE; } static gboolean wocky_heartbeat_source_check ( GSource *source) { WockyHeartbeatSource *self = (WockyHeartbeatSource *) source; gint64 now; #ifdef HAVE_IPHB if (self->heartbeat != NULL) { if ((self->fd.revents & (G_IO_ERR | G_IO_HUP)) != 0) { DEBUG ("Heartbeat closed unexpectedly: %hu; " "falling back to internal timeouts", self->fd.revents); wocky_heartbeat_source_degrade (self); return FALSE; } else if ((self->fd.revents & G_IO_IN) != 0) { DEBUG ("Heartbeat fired"); return TRUE; } else { return FALSE; } } #endif if (self->max_interval == 0) return FALSE; now = g_source_get_time (source); return (now > self->next_wakeup); } #if HAVE_IPHB static inline guint get_min_interval ( WockyHeartbeatSource *self) { /* We allow the heartbeat service to wake us up up to a minute early. */ return self->max_interval > 60 ? self->max_interval - 60 : 0; } #endif static gboolean wocky_heartbeat_source_dispatch ( GSource *source, GSourceFunc callback, gpointer user_data) { WockyHeartbeatSource *self = (WockyHeartbeatSource *) source; if (callback == NULL) { g_warning ("No callback set for WockyHeartbeatSource %p", self); return FALSE; } /* Call our callback. We don't currently allow callbacks to stop future * heartbeats from occurring: this source is used for keepalives from the * time we're connected until we disconnect. */ if (DEBUGGING) { gint64 now; now = g_source_get_time (source); DEBUG ("calling %p (%p) at %" G_GINT64_FORMAT, callback, user_data, now); } ((WockyHeartbeatCallback) callback) (user_data); #if HAVE_IPHB wocky_heartbeat_source_wait (self, self->max_interval); #endif /* Record the time we next want to wake up. */ self->next_wakeup = g_source_get_time (source); self->next_wakeup += self->max_interval * G_USEC_PER_SEC; DEBUG ("next wakeup at %" G_GINT64_FORMAT, self->next_wakeup); return TRUE; } static void wocky_heartbeat_source_finalize (GSource *source) { #ifdef HAVE_IPHB WockyHeartbeatSource *self = (WockyHeartbeatSource *) source; wocky_heartbeat_source_degrade (self); #endif } static GSourceFuncs wocky_heartbeat_source_funcs = { wocky_heartbeat_source_prepare, wocky_heartbeat_source_check, wocky_heartbeat_source_dispatch, wocky_heartbeat_source_finalize, NULL, NULL }; #if HAVE_IPHB static void connect_to_heartbeat ( WockyHeartbeatSource *self) { GSource *source = (GSource *) self; self->heartbeat = iphb_open (NULL); if (self->heartbeat == NULL) { DEBUG ("Couldn't open connection to heartbeat service: %s", g_strerror (errno)); return; } self->fd.fd = iphb_get_fd (self->heartbeat); self->fd.events = G_IO_IN | G_IO_HUP | G_IO_ERR; g_source_add_poll (source, &self->fd); wocky_heartbeat_source_wait (self, self->max_interval); } #endif /** * wocky_heartbeat_source_new: * @max_interval: the maximum interval between calls to the source's callback, * in seconds. Pass 0 to prevent the callback being called. * * Creates a source which calls its callback at least every @max_interval * seconds. This is similar to g_timeout_source_new_seconds(), except that the * callback may be called slightly earlier than requested, in sync with other * periodic network activity (from other XMPP connections, or other * applications entirely). * * When calling g_source_set_callback() on this source, the supplied callback's * signature should match #WockyHeartbeatCallback. * * Returns: the newly-created source. */ GSource * wocky_heartbeat_source_new ( guint max_interval) { GSource *source = g_source_new (&wocky_heartbeat_source_funcs, sizeof (WockyHeartbeatSource)); WockyHeartbeatSource *self = (WockyHeartbeatSource *) source; /* We can't just call wocky_heartbeat_source_update_interval() because it * assumes that we're attached to a main context. I think this is probably a * reasonable assumption. */ self->max_interval = max_interval; self->next_wakeup = g_get_monotonic_time (); self->next_wakeup += max_interval * G_USEC_PER_SEC; #if HAVE_IPHB connect_to_heartbeat (self); #endif return source; } /** * wocky_heartbeat_source_update_interval: * @source: a source returned by wocky_heartbeat_source_new() * @max_interval: the new maximum interval between calls to the source's * callback, in seconds. Pass 0 to stop the callback being * called. * * Updates the interval between calls to @source's callback. The new interval * may not take effect until after the next call to the callback. */ void wocky_heartbeat_source_update_interval ( GSource *source, guint max_interval) { WockyHeartbeatSource *self = (WockyHeartbeatSource *) source; if (self->max_interval == max_interval) return; /* If we're not using the heartbeat, the new interval takes effect * immediately. * * If we are, we just wait for the next heartbeat to fire as * normal, and then use these new values when we ask it to wait again. * (Except if the heartbeat was previously disabled, or is being disabled, in * which case we have to be sure to schedule a wakeup, or cancel the pending * wakeup, respectively.) * * We could alternatively calculate the time already elapsed since we last * called iphb_wait(), and from that calculate how much longer we want to * wait with these new values, taking care to deal with the cases where one * or both of min_interval and max_interval have already passed. But life is * too short. */ #ifdef HAVE_IPHB /* We specify 0 as the lower bound here to give us a better chance of falling * into step with other connections, which may have started waiting at * slightly different times. */ if (self->max_interval == 0 || max_interval == 0) wocky_heartbeat_source_wait (self, max_interval); #endif /* If we were previously disabled, we need to re-initialize next_wakeup, not * just update it. */ if (self->max_interval == 0) self->next_wakeup = g_source_get_time (source); /* If this moves self->next_wakeup into the past, then we'll wake up ASAP, * which is what we want. */ self->next_wakeup += (max_interval - self->max_interval) * G_USEC_PER_SEC; self->max_interval = max_interval; if (self->max_interval == 0) DEBUG ("heartbeat disabled"); else DEBUG ("next wakeup at or before %" G_GINT64_FORMAT, self->next_wakeup); } telepathy-gabble-0.18.2/lib/ext/wocky/wocky/wocky-disco-identity.c0000644000175000017500000001310512200204546025074 0ustar00smcvsmcv00000000000000/* * wocky-disco-identity.c - Source for WockyDiscoIdentity * Copyright (C) 2010 Collabora Ltd. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 */ /** * SECTION: wocky-disco-identity * @title: WockyDiscoIdentity * @short_description: Structure holding XMPP disco identity information. * * Contains information regarding the identity information in disco * replies, as described in XEP-0030. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "wocky-disco-identity.h" #include #define WOCKY_DEBUG_FLAG WOCKY_DEBUG_PRESENCE #include "wocky-debug-internal.h" G_DEFINE_BOXED_TYPE (WockyDiscoIdentity, wocky_disco_identity, wocky_disco_identity_copy, wocky_disco_identity_free) /** * wocky_disco_identity_new: * @category: disco category * @type: disco type * @lang: disco language * @name: disco name * * * * Returns: a new #WockyDiscoIdentity which should be freed using * wocky_disco_identity_free(). */ WockyDiscoIdentity * wocky_disco_identity_new (const gchar *category, const gchar *type, const gchar *lang, const gchar *name) { WockyDiscoIdentity *ret; g_return_val_if_fail (category != NULL, NULL); g_return_val_if_fail (type != NULL, NULL); ret = g_slice_new (WockyDiscoIdentity); ret->category = g_strdup (category); ret->type = g_strdup (type); ret->lang = g_strdup (lang); ret->name = g_strdup (name); return ret; } /** * wocky_disco_identity_copy: * @source: the #WockyDiscoIdentity to copy * * Creates a new #WockyDiscoIdentity structure with the data given by * @source. The copy also copies the internal data so @source can be * freed after this function is called. * * Returns: a new #WockyDiscoIdentity which is a deep copy of @source */ WockyDiscoIdentity * wocky_disco_identity_copy (const WockyDiscoIdentity *source) { if (source == NULL) return NULL; return wocky_disco_identity_new (source->category, source->type, source->lang, source->name); } /** * wocky_disco_identity_free: * @identity: a #WockyDiscoIdentity * * Frees the memory used by @identity. */ void wocky_disco_identity_free (WockyDiscoIdentity *identity) { if (identity == NULL) return; g_free (identity->category); g_free (identity->type); g_free (identity->lang); g_free (identity->name); g_slice_free (WockyDiscoIdentity, identity); } /** * wocky_disco_identity_array_new: * * Creates a new array of #WockyDiscoIdentity structures. * * Returns: A newly instantiated * array. wocky_disco_identity_array_free() should beq used * to free the memory allocated by this array. * See: wocky_disco_identity_array_free() */ GPtrArray * wocky_disco_identity_array_new (void) { return g_ptr_array_new_with_free_func ( (GDestroyNotify) wocky_disco_identity_free); } /** * wocky_disco_identity_array_copy: * @source: The source array to be copied. * * Copies an array of #WockyDiscoIdentity objects. The returned array contains * new copies of the contents of the source array. * * Returns: A newly instantiated array with new copies of the contents of the * source array. * See: wocky_disco_identity_array_new() */ GPtrArray * wocky_disco_identity_array_copy (const GPtrArray *source) { GPtrArray *ret; guint i; g_return_val_if_fail (source != NULL, NULL); ret = g_ptr_array_sized_new (source->len); g_ptr_array_set_free_func (ret, (GDestroyNotify) wocky_disco_identity_free); for (i = 0; i < source->len; ++i) { g_ptr_array_add (ret, wocky_disco_identity_copy (g_ptr_array_index (source, i))); } return ret; } /** * wocky_disco_identity_array_free: * @arr: Array to be freed. * * Frees an array of #WockyDiscoIdentity objects created with * wocky_disco_identity_array_new() or returned by * wocky_disco_identity_array_copy(). * * Note that if this method is called with an array created with * g_ptr_array_new(), the caller should also free the array contents. * * See: wocky_disco_identity_array_new(), wocky_disco_identity_array_copy() */ void wocky_disco_identity_array_free (GPtrArray *arr) { if (arr == NULL) return; g_ptr_array_unref (arr); } /** * wocky_disco_identity_cmp: * @left: a #WockyDiscoIdentity * @right: a #WockyDiscoIdentity * * Compares @left and @right. It returns an integer less than, equal * to, or greater than zero if @left is found, respectively, to be * less than, to match, or be greater than %right. * * This function can be casted to a %GCompareFunc to sort a list of * #WockyDiscoIdentity structures. * * Returns: the result of comparing @left and @right */ gint wocky_disco_identity_cmp (WockyDiscoIdentity *left, WockyDiscoIdentity *right) { gint ret; if ((ret = strcmp (left->category, right->category)) != 0) return ret; if ((ret = strcmp (left->type, right->type)) != 0) return ret; if ((ret = strcmp (left->lang, right->lang)) != 0) return ret; return strcmp (left->name, right->name); } telepathy-gabble-0.18.2/lib/ext/wocky/wocky/wocky-debug-internal.h0000644000175000017500000000406112200204546025052 0ustar00smcvsmcv00000000000000#if !defined (WOCKY_COMPILATION) # error "This is an internal header." #endif #ifndef WOCKY_DEBUG_INTERNAL_H #define WOCKY_DEBUG_INTERNAL_H #include "config.h" #include #include "wocky-debug.h" #include "wocky-stanza.h" G_BEGIN_DECLS #ifdef ENABLE_DEBUG void wocky_debug_set_flags_from_env (void); gboolean wocky_debug_flag_is_set (WockyDebugFlags flag); void wocky_debug_valist (WockyDebugFlags flag, const gchar *format, va_list args); void wocky_debug (WockyDebugFlags flag, const gchar *format, ...) G_GNUC_PRINTF (2, 3); void wocky_debug_stanza (WockyDebugFlags flag, WockyStanza *stanza, const gchar *format, ...) G_GNUC_PRINTF (3, 4); void wocky_debug_node_tree (WockyDebugFlags flag, WockyNodeTree *tree, const gchar *format, ...) G_GNUC_PRINTF (3, 4); void wocky_debug_node (WockyDebugFlags flag, WockyNode *node, const gchar *format, ...) G_GNUC_PRINTF (3, 4); #ifdef WOCKY_DEBUG_FLAG #define DEBUG(format, ...) \ wocky_debug (WOCKY_DEBUG_FLAG, "%s: %s: " format, G_STRFUNC, G_STRLOC, \ ##__VA_ARGS__) #define DEBUG_STANZA(stanza, format, ...) \ wocky_debug_stanza (WOCKY_DEBUG_FLAG, stanza, "%s: " format, G_STRFUNC,\ ##__VA_ARGS__) #define DEBUG_NODE_TREE(tree, format, ...) \ wocky_debug_node_tree (WOCKY_DEBUG_FLAG, tree, "%s: " format, G_STRFUNC,\ ##__VA_ARGS__) #define DEBUG_NODE(node, format, ...) \ wocky_debug_node (WOCKY_DEBUG_FLAG, node, "%s: " format, G_STRFUNC,\ ##__VA_ARGS__) #define DEBUGGING wocky_debug_flag_is_set(WOCKY_DEBUG_FLAG) #endif /* WOCKY_DEBUG_FLAG */ #else /* ENABLE_DEBUG */ #ifdef WOCKY_DEBUG_FLAG static inline void DEBUG ( const gchar *format, ...) { /* blah blah blah */ } static inline void DEBUG_STANZA (WockyStanza *stanza, const gchar *format, ...) { } static inline void DEBUG_NODE_TREE (WockyNodeTree *tree, const gchar *format, ...) { } static inline void DEBUG_NODE (WockyNode *node, const gchar *format, ...) { } #define DEBUGGING 0 #endif /* WOCKY_DEBUG_FLAG */ #endif /* ENABLE_DEBUG */ G_END_DECLS #endif telepathy-gabble-0.18.2/lib/ext/wocky/wocky/wocky-debug.c0000644000175000017500000000764412200204546023245 0ustar00smcvsmcv00000000000000#ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include "wocky-debug-internal.h" #ifdef ENABLE_DEBUG static WockyDebugFlags flags = 0; static gboolean initialized = FALSE; static GDebugKey keys[] = { { "transport", WOCKY_DEBUG_TRANSPORT }, { "net", WOCKY_DEBUG_NET }, { "xmpp", WOCKY_DEBUG_XMPP }, { "xmpp-reader", WOCKY_DEBUG_XMPP_READER }, { "xmpp-writer", WOCKY_DEBUG_XMPP_WRITER }, { "auth", WOCKY_DEBUG_AUTH }, { "ssl", WOCKY_DEBUG_SSL }, { "rmulticast", WOCKY_DEBUG_RMULTICAST }, { "rmulticast-sender", WOCKY_DEBUG_RMULTICAST_SENDER }, { "muc-connection", WOCKY_DEBUG_MUC_CONNECTION }, { "bytestream", WOCKY_DEBUG_BYTESTREAM }, { "ft", WOCKY_DEBUG_FILE_TRANSFER }, { "porter", WOCKY_DEBUG_PORTER }, { "connector", WOCKY_DEBUG_CONNECTOR }, { "roster", WOCKY_DEBUG_ROSTER }, { "tls", WOCKY_DEBUG_TLS }, { "pubsub", WOCKY_DEBUG_PUBSUB }, { "dataform", WOCKY_DEBUG_DATA_FORM }, { "ping", WOCKY_DEBUG_PING }, { "heartbeat", WOCKY_DEBUG_HEARTBEAT }, { "presence", WOCKY_DEBUG_PRESENCE }, { "connection-factory",WOCKY_DEBUG_CONNECTION_FACTORY}, { "media", WOCKY_DEBUG_JINGLE }, { 0, }, }; void wocky_debug_set_flags_from_env () { guint nkeys; const gchar *flags_string; for (nkeys = 0; keys[nkeys].value; nkeys++); flags_string = g_getenv ("WOCKY_DEBUG"); if (flags_string) wocky_debug_set_flags (g_parse_debug_string (flags_string, keys, nkeys)); initialized = TRUE; } void wocky_debug_set_flags (WockyDebugFlags new_flags) { flags |= new_flags; initialized = TRUE; } gboolean wocky_debug_flag_is_set (WockyDebugFlags flag) { return flag & flags; } void wocky_debug (WockyDebugFlags flag, const gchar *format, ...) { va_list args; va_start (args, format); wocky_debug_valist (flag, format, args); va_end (args); } void wocky_debug_valist (WockyDebugFlags flag, const gchar *format, va_list args) { if (G_UNLIKELY(!initialized)) wocky_debug_set_flags_from_env (); if (flag & flags) g_logv (G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, format, args); } static void wocky_debug_node_va (WockyDebugFlags flag, WockyNode *node, const gchar *format, va_list args) { if (G_UNLIKELY(!initialized)) wocky_debug_set_flags_from_env (); if (flag & flags) { gchar *msg, *node_str; msg = g_strdup_vprintf (format, args); node_str = wocky_node_to_string (node); g_log (G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, "%s\n%s", msg, node_str); g_free (msg); g_free (node_str); } } void wocky_debug_node (WockyDebugFlags flag, WockyNode *node, const gchar *format, ...) { va_list args; va_start (args, format); wocky_debug_node_va (flag, node, format, args); va_end (args); } static void wocky_debug_node_tree_va (WockyDebugFlags flag, WockyNodeTree *tree, const gchar *format, va_list args) { wocky_debug_node_va (flag, wocky_node_tree_get_top_node (tree), format, args); } void wocky_debug_node_tree (WockyDebugFlags flag, WockyNodeTree *tree, const gchar *format, ...) { va_list args; va_start (args, format); wocky_debug_node_tree_va (flag, tree, format, args); va_end (args); } void wocky_debug_stanza (WockyDebugFlags flag, WockyStanza *stanza, const gchar *format, ...) { va_list args; va_start (args, format); wocky_debug_node_tree_va (flag, (WockyNodeTree *) stanza, format, args); va_end (args); } #else /* !ENABLE_DEBUG */ void wocky_debug_set_flags (WockyDebugFlags flags) { } #endif telepathy-gabble-0.18.2/lib/ext/wocky/wocky/wocky-data-form.c0000644000175000017500000007056112200204546024027 0ustar00smcvsmcv00000000000000/* * wocky-data-form.c - WockyDataForm * Copyright © 2009–2010 Collabora Ltd. * Copyright © 2010 Nokia Corporation * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 */ /** * SECTION: wocky-data-form * @title: WockyDataForm * @short_description: An object to represent an XMPP data form * @include: wocky/wocky-data-form.h * * An object that represents an XMPP data form as described in * XEP-0004. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "wocky-data-form.h" #include #include "wocky-namespaces.h" #include "wocky-utils.h" #define WOCKY_DEBUG_FLAG WOCKY_DEBUG_DATA_FORM #include "wocky-debug-internal.h" G_DEFINE_TYPE (WockyDataForm, wocky_data_form, G_TYPE_OBJECT) /* properties */ enum { PROP_TITLE = 1, PROP_INSTRUCTIONS, }; /* private structure */ struct _WockyDataFormPrivate { gchar *title; gchar *instructions; /* (gchar *) => owned (WockyDataFormField *) */ GHashTable *reported; gboolean dispose_has_run; }; GQuark wocky_data_form_error_quark (void) { static GQuark quark = 0; if (quark == 0) quark = g_quark_from_static_string ("wocky-data-form-error"); return quark; } static WockyDataFormFieldOption * wocky_data_form_field_option_new (const gchar *label, const gchar *value) { WockyDataFormFieldOption *option; g_assert (value != NULL); option = g_slice_new0 (WockyDataFormFieldOption); option->label = g_strdup (label); option->value = g_strdup (value); return option; } static void wocky_data_form_field_option_free (WockyDataFormFieldOption *option) { g_free (option->label); g_free (option->value); g_slice_free (WockyDataFormFieldOption, option); } /* pass ownership of the default_value, raw_value_contents, the value * and the options list */ static WockyDataFormField * wocky_data_form_field_new ( WockyDataFormFieldType type, const gchar *var, const gchar *label, const gchar *desc, gboolean required, GValue *default_value, gchar **raw_value_contents, GValue *value, GSList *options) { WockyDataFormField *field; field = g_slice_new0 (WockyDataFormField); field->type = type; field->var = g_strdup (var); field->label = g_strdup (label); field->desc = g_strdup (desc); field->required = required; field->default_value = default_value; field->raw_value_contents = raw_value_contents; field->value = value; field->options = options; return field; } static void wocky_data_form_field_free (WockyDataFormField *field) { if (field == NULL) return; g_free (field->var); g_free (field->label); g_free (field->desc); g_strfreev (field->raw_value_contents); if (field->default_value != NULL) wocky_g_value_slice_free (field->default_value); if (field->value != NULL) wocky_g_value_slice_free (field->value); g_slist_foreach (field->options, (GFunc) wocky_data_form_field_option_free, NULL); g_slist_free (field->options); g_slice_free (WockyDataFormField, field); } static void wocky_data_form_init (WockyDataForm *self) { self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, WOCKY_TYPE_DATA_FORM, WockyDataFormPrivate); self->fields = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, NULL); self->fields_list = NULL; self->priv->reported = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, (GDestroyNotify) wocky_data_form_field_free); self->results = NULL; } static void wocky_data_form_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { WockyDataForm *self = WOCKY_DATA_FORM (object); WockyDataFormPrivate *priv = self->priv; switch (property_id) { case PROP_TITLE: priv->title = g_value_dup_string (value); break; case PROP_INSTRUCTIONS: priv->instructions = g_value_dup_string (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void wocky_data_form_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { WockyDataForm *self = WOCKY_DATA_FORM (object); WockyDataFormPrivate *priv = self->priv; switch (property_id) { case PROP_TITLE: g_value_set_string (value, priv->title); break; case PROP_INSTRUCTIONS: g_value_set_string (value, priv->instructions); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void wocky_data_form_dispose (GObject *object) { WockyDataForm *self = WOCKY_DATA_FORM (object); WockyDataFormPrivate *priv = self->priv; if (priv->dispose_has_run) return; priv->dispose_has_run = TRUE; if (G_OBJECT_CLASS (wocky_data_form_parent_class)->dispose) G_OBJECT_CLASS (wocky_data_form_parent_class)->dispose (object); } static void data_form_field_list_free (GSList *fields) { g_slist_foreach (fields, (GFunc) wocky_data_form_field_free, NULL); g_slist_free (fields); } static void wocky_data_form_finalize (GObject *object) { WockyDataForm *self = WOCKY_DATA_FORM (object); WockyDataFormPrivate *priv = self->priv; g_free (priv->title); g_free (priv->instructions); g_hash_table_unref (self->fields); data_form_field_list_free (self->fields_list); g_slist_foreach (self->results, (GFunc) data_form_field_list_free, NULL); g_slist_free (self->results); g_hash_table_unref (priv->reported); G_OBJECT_CLASS (wocky_data_form_parent_class)->finalize (object); } static void wocky_data_form_class_init ( WockyDataFormClass *wocky_data_form_class) { GObjectClass *object_class = G_OBJECT_CLASS (wocky_data_form_class); GParamSpec *param_spec; g_type_class_add_private (wocky_data_form_class, sizeof (WockyDataFormPrivate)); object_class->set_property = wocky_data_form_set_property; object_class->get_property = wocky_data_form_get_property; object_class->dispose = wocky_data_form_dispose; object_class->finalize = wocky_data_form_finalize; param_spec = g_param_spec_string ("title", "title", "Title", NULL, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, PROP_TITLE, param_spec); param_spec = g_param_spec_string ("instructions", "instructions", "Instructions", NULL, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, PROP_INSTRUCTIONS, param_spec); } static const gchar * type_to_str (WockyDataFormFieldType type) { return wocky_enum_to_nick (WOCKY_TYPE_DATA_FORM_FIELD_TYPE, type); } /* * extract_options_list: * @node: a node * * Returns: a list of (WockyDataFormFieldOption *) containing all the *
]]>

If the receiver support multi-bytestreams as well, he sends a list of the methods supported instead of the normal SI reply. Bytestreams will be try by the sender in that order.

XEP-0047). Each bytestream is negotiated according the protocol described in its XEP. Once a bytestream has been sucessfully established, all the data are send using it and the other methods are not used.

None.

None.

None.

TODO telepathy-gabble-0.18.2/docs/muc-bytestream.xml0000644000175000017500000001631012200204332021415 0ustar00smcvsmcv00000000000000
MUC Bytestreams A protocol for message transfer within a MUC. This document is copyright 2007 Collabora Ltd. and may be distributed under the same terms as the Telepathy specification. proto-muc-bytestream ProtoXEP External extension Telepathy project Telepathy project XMPP Core XEP-0045 NOT YET ASSIGNED Simon McVittie simon.mcvittie@collabora.co.uk simon.mcvittie@collabora.co.uk 0.0.1 2007-09-07 smcv

First draft.

This document describes a protocol for tunneling binary message streams through an XMPP MUC (XEP-0045). It's designed for use in Tubes but could be useful for other similar protocols.

The XML namespace defined here is http://telepathy.freedesktop.org/xmpp/protocol/muc-bytestream (NS_MUC_BYTESTREAM in Gabble source code).

D-Bus Tubes require a mechanism by which binary messages, possibly larger than the MUC service's maximum message size, can be transmitted through a MUC, preserving message boundaries. Multicasting messages to all participants, and sending unicast messages to a single participant, are both required.

The protocol used is intentionally similar to IBB (XEP-0047).

MUC Bytestream messages are multiplexed using a stream ID similar to that used in In-Band Bytestreams. As with In-Band Bytestreams, the stream ID SHOULD be randomly generated in a way that will avoid collisions, and any specification that references this one will need to describe how the stream ID can be associated with a higher-level construct (e.g. a Tube).

The uniqueness requirement for stream IDs is per-MUC, not per-participant, so collision avoidance must occur with the same scope.

Within a particular message stream, some messages can be broadcast to all participants in the MUC while some messages can be sent to a particular participant.

base64base64...
]]> base64base64...
]]>

Messages which are too large for the MUC to relay them intact SHOULD be "fragmented", i.e. split into multiple stanzas.

To send messages which need to be fragmented, set the 'frag' attribute to "first" on the first part of the message, "middle" on any intermediate parts and "last" on the last part. Setting 'frag' to "complete", or omitting it, means the XMPP stanza is a complete message in the underlying message stream, i.e. it is simultaneously the first and last fragment.

When receiving messages, participants MUST buffer and reassemble fragmented messages independently for each (sender, 'sid') pair.

When a participant has started to send a fragmented message, it MUST send all the fragments of that message, finishing with one with 'frag' set to "last", before it starts to send any subsequent message with the same 'sid' attribute.

If a participant leaves the MUC, or signals via a higher-level protocol that it has left the MUC Bytestream stream with a particular 'sid', any buffered fragments from that sender representing an incomplete message SHOULD be discarded by recipients.

base64base64... base64base64... base64base64... ]]>

Senders can cause denial of service to recipients via memory exhaustion if they send very large fragmented messages. Recipients MUST impose a limit on the size of message they will reassemble; higher-level protocols that reference this one SHOULD recommend a suitable limit for that protocol.

None.

None.

]]> telepathy-gabble-0.18.2/docs/olpc.xml0000644000175000017500000010444512200204332017420 0ustar00smcvsmcv00000000000000 ]>
OLPC-specific properties, version 1.0 Properties for the Activity and Buddy abstractions used in the One Laptop per Child's Sugar environment. Copyright (c) 2007 Collabora Limited. This document may be distributed under the same terms as the Telepathy specification. proto-olpc1.0 ProtoXEP Extension Standards Telepathy project XMPP Core XEP-0045 XEP-proto-clique NOT YET ASSIGNED Simon McVittie simon.mcvittie@collabora.co.uk simon.mcvittie@collabora.co.uk 0.0.1 2007-10-01 smcv

First draft.

This document describes the protocol used to transfer OLPC-specific buddy (contact) and activity (chat room) information.

The OLPC Sugar environment has the abstractions of a Buddy (corresponding to a child and/or their laptop (XO), represented in XMPP by a JID and in link-local XMPP by a _presence._tcp record) and an Activity (corresponding to a shared activity, represented in XMPP by a MUC and in link-local XMPP by a llmuc chatroom). The API requires that various properties can be browsed for (without joining any activities or chatrooms).

Buddy properties are published in PEP. As currently implemented, all XOs subscribe to the presence and buddy properties of all other XOs on the same server.

The buddy properties currently supported are ip4-address, key and color.

10.0.0.123 Base64Base64... #005FE4,#00A0FF ]]>

Subscribers receive the change notification, as usual for PEP. (For all the other PEP nodes described in this document, the process is the same, and has been omitted from this document for conciseness.)

10.0.0.123 Base64Base64... #005FE4,#00A0FF
]]>

In link-local XMPP, none of this happens. Instead, the ip4-address property is implicitly available, and the key and color properties are encoded in the _presence._tcp TXT record.

The 'color' property is mapped to the 'olpc-color' TXT record key:

11111@xo-12-34-56._presence._tcp.local. IN TXT "olpc-color=#005FE4,#00A0FF"

The 'key' property is mapped to the 'olpc-key-part0', 'olpc-key-part1', ... properties, split as necessary to fit in the 255-byte limit for TXT records. Because mDNS TXT records are 8-bit-clean, the segments contain raw binary rather than the Base64 binary used in XMPP:

11111@xo-12-34-56._presence._tcp.local. IN TXT "olpc-key-part0=binarybinary..." 11111@xo-12-34-56._presence._tcp.local. IN TXT "olpc-key-part1=binarybinary..." 11111@xo-12-34-56._presence._tcp.local. IN TXT "olpc-key-part2=binarybinary..."

(As currently implemented, the "raw binary" is in fact *also* Base64, so the data seen in XMPP is doubly Base64'd. However, this is not guaranteed to remain the case, so the protocols need to assume the underlying byte array for the key can contain anything.)

Implementors must be careful to ensure that there is no key in the TXT record for the key segment 1 greater than the last one they need (in this case, olpc-key-part3 must be removed if present).

The Neighbourhood view on the OLPC groups XO icons by their current activity, so XOs must notify each other of current-activity changes.

As currently implemented, this is done for both public and private activities, which represents an information leak. (FIXME)

Note that the 'type' attribute is in fact the activity ID; the name is for historical reasons.

room="aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa@conference.jabber.laptop.org" type="aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"/> ]]>

In link-local XMPP, the current activity is mapped to a pair of keys in the TXT record: olpc-current-activity (the activity ID) and olpc-current-activity-room (the Salut rMulticast chat room name).

11111@xo-12-34-56._presence._tcp.local. IN TXT "olpc-current-activity=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" 11111@xo-12-34-56._presence._tcp.local. IN TXT "olpc-current-activity-room=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"

For activities to be handled by the Sugar environment, certain properties are needed. Because there is no server-side storage corresponding to a chat room and accessible by all participants, participants each publish the properties of all their activities.

To keep participants' idea of the activity properties in sync, when the properties change, the participant making the change MUST send a broadcast message to the chatroom, notifying other participants of the new properties.

This <message/> MUST be sent before updating the PEP node with the activity properties.

to='aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa@conference.jabber.laptop.org'> org.laptop.Connect Connect Activity #005FE4,#00A0FF 0
]]>

For public activities, the participants all announce all their activities and their properties in a PEP node.

When an activity becomes private, the participants all delete it from their activity-properties PEP nodes.

org.laptop.Connect Connect Activity #005FE4,#00A0FF 0 org.laptop.HelloMesh Example Activity #005FE4,#00A0FF 0 ]]>

The participants all announce all their list of activities in yet another PEP node, for backwards compatibility. This mechanism will be removed in a future version of this protocol.

Note that again, the 'type' attribute is in fact the activity ID; the name is for historical reasons.

]]>

In link-local XMPP, the activity properties are mapped to the _olpc-activity1._udp record. Each participant in each public activity publishes an mDNS service named chatroomname:publishedname@hostname, like so:

; Advertised by 11111... aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa:11111@xo-12-34-56._olpc-activity1._udp.local. IN SRV 0 xo-12-34-56.local. _olpc-activity1._udp.local. IN PTR aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa:11111@xo-12-34-56._olpc-activity1._udp.local. aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa:11111@xo-12-34-56._olpc-activity1._udp.local IN TXT "txtvers=0" aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa:11111@xo-12-34-56._olpc-activity1._udp.local IN TXT "room=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa:11111@xo-12-34-56._olpc-activity1._udp.local IN TXT "activity-id=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa:11111@xo-12-34-56._olpc-activity1._udp.local IN TXT "name=Connect Activity" aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa:11111@xo-12-34-56._olpc-activity1._udp.local IN TXT "color=#005FE4,#00A0FF" aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa:11111@xo-12-34-56._olpc-activity1._udp.local IN TXT "type=org.laptop.ConnectActivity" aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa:11111@xo-12-34-56._olpc-activity1._udp.local IN TXT "tags=" bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb:11111@xo-12-34-56._olpc-activity1._udp.local. IN SRV 0 xo-12-34-56.local. _olpc-activity1._udp.local. IN PTR bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb:11111@xo-12-34-56._olpc-activity1._udp.local. bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb:11111@xo-12-34-56._olpc-activity1._udp.local IN TXT "txtvers=0" bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb:11111@xo-12-34-56._olpc-activity1._udp.local IN TXT "room=bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb" bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb:11111@xo-12-34-56._olpc-activity1._udp.local IN TXT "activity-id=bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb" bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb:11111@xo-12-34-56._olpc-activity1._udp.local IN TXT "name=Example Activity" bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb:11111@xo-12-34-56._olpc-activity1._udp.local IN TXT "color=#005FE4,#00A0FF" bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb:11111@xo-12-34-56._olpc-activity1._udp.local IN TXT "type=org.laptop.HelloMesh" bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb:11111@xo-12-34-56._olpc-activity1._udp.local IN TXT "tags=" ; Advertised by 22222... - suppose here that 22222... is in the activity ; aaaaa... but not in bbbbb... aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa:22222@xo-22-22-22._olpc-activity1._udp.local. IN SRV 0 xo-22-22-22.local. _olpc_activity._udp.local. IN PTR aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa:22222@xo-22-22-22._olpc-activity1._udp.local. aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa:22222@xo-22-22-22._olpc-activity1._udp.local IN TXT "txtvers=0" aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa:22222@xo-22-22-22._olpc-activity1._udp.local IN TXT "room=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa:22222@xo-22-22-22._olpc-activity1._udp.local IN TXT "activity-id=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa:22222@xo-22-22-22._olpc-activity1._udp.local IN TXT "name=Connect Activity" aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa:22222@xo-22-22-22._olpc-activity1._udp.local IN TXT "color=#005FE4,#00A0FF" aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa:22222@xo-22-22-22._olpc-activity1._udp.local IN TXT "type=org.laptop.ConnectActivity" aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa:22222@xo-22-22-22._olpc-activity1._udp.local IN TXT "tags="

The <message/> sent to the chat room with the updated properties is much the same as for XMPP:

org.laptop.Connect Connect Activity #005FE4,#00A0FF 0 ]]>

When an activity becomes private, the participants all delete it from their activity-properties and activities PEP nodes. In this example, the XO whose JID starts with 11111 makes the activity whose ID starts with aaaaa private, and removes it from their PEP nodes; the XO whose JID starts with 22222 responds by removing it from their PEP nodes too.

To a third XO observing the PEP nodes, this is indistinguishable from both XOs leaving the activity simultaneously, which is what we want.

to='aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa@conference.jabber.laptop.org'> org.laptop.Connect Connect Activity #005FE4,#00A0FF 1
org.laptop.HelloMesh Example Activity #005FE4,#00A0FF 0 ]]>

Instead of deleting activities from their PEP records, link-local peers delete them from their mDNS records.

The <message/> sent to the chat room with the updated properties is, again, much the same as for XMPP.

Before inviting a buddy to an activity, OLPC users are expected to send them the activity properties. These are sent in a <message/> stanza referred to as a "pseudo-invitation". If the activity is private, then this is the only opportunity the invitee has to get the activity properties.

org.laptop.Connect Connect Activity #0d1c38,#49bce4 1 ]]>

If the activity properties change before the invitee joins the activity, the inviter must notify the invitee by re-sending the pseudo-invitation (only).

org.laptop.Connect this name changed #0d1c38,#49bce4 1
]]>

If the inviter leaves the activity before the invitee joins, the invitee can no longer rely on being notified about any changes; if the activity was private, the invitee can't even rely on being able to see whether it exists. Accordingly, the inviter should cancel their invitation; if all invitations to a private activity have been cancelled, the invitee should assume that the activity has disappeared.

]]>

There isn't the concept of sending an invitation via the MUC service in link-local XMPP, so the process can be made somewhat simpler.

aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
224.0.0.1
23453 org.laptop.Connect Connect Activity #0d1c38,#49bce4 1
]]>
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
224.0.0.1
23453 org.laptop.Connect The name changed #0d1c38,#49bce4 1
]]>
]]>

Properties have the same syntax and semantics as the "parameters" in XEP-proto-tubes.

Putting the current activity in a PEP node, even if it's private, leaks the room name.

None.

None.

]]> ]]> ]]> ]]> telepathy-gabble-0.18.2/docs/telepathy-gabble.8.in0000644000175000017500000000404112200204332021637 0ustar00smcvsmcv00000000000000.TH TELEPATHY-GABBLE "8" "October 2007" "Telepathy" "D-Bus services" \" This man page was written by Simon McVittie for the Debian project, \" but may be used by others. \" Copyright © 2007 Collabora Ltd. \" It may be distributed under the same terms as telepathy-gabble itself. .SH NAME telepathy-gabble \- Telepathy connection manager for XMPP (Jabber) .SH SYNOPSIS \fB@libexecdir@/telepathy\-gabble\fR .SH DESCRIPTION Gabble implements the Telepathy D-Bus specification for XMPP (Jabber), allowing Telepathy clients like .BR empathy (1) to connect to XMPP accounts, including Google Talk. .PP It is a D-Bus service which runs on the session bus, and should usually be started automatically by D-Bus activation. However, it might be useful to start it manually for debugging. .SH OPTIONS There are no command-line options. .SH ENVIRONMENT .TP \fBGABBLE_LOGFILE\fR=\fIfilename\fR If set, debug output will go to the given file rather than to stderr. If \fB+\fR is prepended to the \fIfilename\fR (e.g. \fBGABBLE_LOGFILE=+gabble.log\fR), debug output will be appended to the file; otherwise, any existing file will be replaced. .TP \fBGABBLE_DEBUG\fR=\fItype\fR May be set to "all" for full debug output, or various undocumented options (which may change from release to release) to filter the output. .TP \fBWOCKY_DEBUG\fR=\fItype\fR May be set to "all" for full debug output from the Wocky XMPP library used by Gabble, or various undocumented options (which may change from release to release) to filter the output. For general Gabble debugging, "net" is recommended. .TP \fBGABBLE_PERSIST\fR=\fI1\fR If set (to any value), Gabble will continue running until killed, rather than timing out if it has no open connections for a few seconds. .TP \fBGABBLE_PLUGIN_DIR\fR=\fIdirectory\fR If set, and Gabble was compiled with plugin support, plugins will be loaded from \fIdirectory\fR rather than from the default directory. .SH SEE ALSO .IR http://telepathy.freedesktop.org/ , .IR http://telepathy.freedesktop.org/wiki/CategoryGabble , .BR empathy (1) telepathy-gabble-0.18.2/docs/Makefile.am0000644000175000017500000000176512200204332017776 0ustar00smcvsmcv00000000000000tools_dir = $(top_srcdir)/tools # htmldir is not defined by autoconf < 2.59c htmldir = $(if $(filter-out @%@,@htmldir@),@htmldir@,$(datadir)/doc/$(PACKAGE)) man_MANS = telepathy-gabble.8 html_DATA = \ olpc.html \ muc-bytestream.html \ si-multiple.html \ tubes.html EXTRA_DIST = \ $(man_MANS:.8=.8.in) \ $(html_DATA:.html=.xml) \ tube-caps.txt \ xep.dtd \ xep.ent \ xep.xsd \ xep.xsl CLEANFILES = \ $(man_MANS) \ $(html_DATA) %.8: %.8.in Makefile $(AM_V_GEN)sed -e 's,[@]libexecdir[@],@libexecdir@,' < $< > $@ $(html_DATA): %.html: %.xml xep.xsl xep.dtd xep.ent $(AM_V_GEN)$(XSLTPROC) $(srcdir)/xep.xsl $< > $@ proto-xep-upload: $(html_DATA) rsync -P $(html_DATA) people.collabora.co.uk:public_html/ .PHONY: proto-xep-upload maintainer-update-from-xmpp.org: set -e; \ for x in xep.dtd xep.ent xep.xsd xep.xsl; do \ uri=svn://svn.xmpp.org:7938/xmpp/trunk/extensions/$$x; \ svn info $$uri; \ svn cat $$uri > $$x.tmp; \ mv $$x.tmp $$x; \ done telepathy-gabble-0.18.2/docs/Makefile.in0000644000175000017500000004303412312536073020020 0ustar00smcvsmcv00000000000000# Makefile.in generated by automake 1.14.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2013 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ VPATH = @srcdir@ am__is_gnu_make = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)' am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ subdir = docs DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/ax_config_dir.m4 \ $(top_srcdir)/m4/compiler.m4 $(top_srcdir)/m4/libtool.m4 \ $(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \ $(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \ $(top_srcdir)/m4/tp-compiler-flag.m4 \ $(top_srcdir)/m4/tp-compiler-warnings.m4 \ $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = SOURCES = DIST_SOURCES = am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; am__vpath_adj = case $$p in \ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ *) f=$$p;; \ esac; am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; am__install_max = 40 am__nobase_strip_setup = \ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` am__nobase_strip = \ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" am__nobase_list = $(am__nobase_strip_setup); \ for p in $$list; do echo "$$p $$p"; done | \ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ if (++n[$$2] == $(am__install_max)) \ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ END { for (dir in files) print dir, files[dir] }' am__base_list = \ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' am__uninstall_files_from_dir = { \ test -z "$$files" \ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ $(am__cd) "$$dir" && rm -f $$files; }; \ } man8dir = $(mandir)/man8 am__installdirs = "$(DESTDIR)$(man8dir)" "$(DESTDIR)$(htmldir)" NROFF = nroff MANS = $(man_MANS) DATA = $(html_DATA) am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ AR = @AR@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CFLAGS = @CFLAGS@ CLIENT_TYPE = @CLIENT_TYPE@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CYGPATH_W = @CYGPATH_W@ DBUS_CFLAGS = @DBUS_CFLAGS@ DBUS_LIBS = @DBUS_LIBS@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DLLTOOL = @DLLTOOL@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ ENABLE_CODING_STYLE_CHECKS = @ENABLE_CODING_STYLE_CHECKS@ ENABLE_PLUGINS = @ENABLE_PLUGINS@ ERROR_CFLAGS = @ERROR_CFLAGS@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ GLIB_CFLAGS = @GLIB_CFLAGS@ GLIB_GENMARSHAL = @GLIB_GENMARSHAL@ GLIB_LIBS = @GLIB_LIBS@ GMODULE_CFLAGS = @GMODULE_CFLAGS@ GMODULE_LIBS = @GMODULE_LIBS@ GREP = @GREP@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ LD = @LD@ LDFLAGS = @LDFLAGS@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBTOOL = @LIBTOOL@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBOBJS = @LTLIBOBJS@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ NICE_CFLAGS = @NICE_CFLAGS@ NICE_LIBS = @NICE_LIBS@ NM = @NM@ NMEDIT = @NMEDIT@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ PYTHON = @PYTHON@ PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@ PYTHON_PLATFORM = @PYTHON_PLATFORM@ PYTHON_PREFIX = @PYTHON_PREFIX@ PYTHON_VERSION = @PYTHON_VERSION@ RANLIB = @RANLIB@ SED = @SED@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ SOUP_CFLAGS = @SOUP_CFLAGS@ SOUP_LIBS = @SOUP_LIBS@ STRIP = @STRIP@ TEST_PYTHON = @TEST_PYTHON@ TP_GLIB_CFLAGS = @TP_GLIB_CFLAGS@ TP_GLIB_LIBS = @TP_GLIB_LIBS@ VERSION = @VERSION@ WOCKY_CFLAGS = @WOCKY_CFLAGS@ WOCKY_LIBS = @WOCKY_LIBS@ XSLTPROC = @XSLTPROC@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ gabbletestsdir = @gabbletestsdir@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ # htmldir is not defined by autoconf < 2.59c htmldir = $(if $(filter-out @%@,@htmldir@),@htmldir@,$(datadir)/doc/$(PACKAGE)) includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ mandir = @mandir@ mkdir_p = @mkdir_p@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ pkgpyexecdir = @pkgpyexecdir@ pkgpythondir = @pkgpythondir@ pluginexecdir = @pluginexecdir@ pluginexeclibdir = @pluginexeclibdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ pyexecdir = @pyexecdir@ pythondir = @pythondir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ srcdir = @srcdir@ sysconfdir = @sysconfdir@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ tools_dir = $(top_srcdir)/tools man_MANS = telepathy-gabble.8 html_DATA = \ olpc.html \ muc-bytestream.html \ si-multiple.html \ tubes.html EXTRA_DIST = \ $(man_MANS:.8=.8.in) \ $(html_DATA:.html=.xml) \ tube-caps.txt \ xep.dtd \ xep.ent \ xep.xsd \ xep.xsl CLEANFILES = \ $(man_MANS) \ $(html_DATA) all: all-am .SUFFIXES: $(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu docs/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --gnu docs/Makefile .PRECIOUS: Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs install-man8: $(man_MANS) @$(NORMAL_INSTALL) @list1=''; \ list2='$(man_MANS)'; \ test -n "$(man8dir)" \ && test -n "`echo $$list1$$list2`" \ || exit 0; \ echo " $(MKDIR_P) '$(DESTDIR)$(man8dir)'"; \ $(MKDIR_P) "$(DESTDIR)$(man8dir)" || exit 1; \ { for i in $$list1; do echo "$$i"; done; \ if test -n "$$list2"; then \ for i in $$list2; do echo "$$i"; done \ | sed -n '/\.8[a-z]*$$/p'; \ fi; \ } | while read p; do \ if test -f $$p; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; echo "$$p"; \ done | \ sed -e 'n;s,.*/,,;p;h;s,.*\.,,;s,^[^8][0-9a-z]*$$,8,;x' \ -e 's,\.[0-9a-z]*$$,,;$(transform);G;s,\n,.,' | \ sed 'N;N;s,\n, ,g' | { \ list=; while read file base inst; do \ if test "$$base" = "$$inst"; then list="$$list $$file"; else \ echo " $(INSTALL_DATA) '$$file' '$(DESTDIR)$(man8dir)/$$inst'"; \ $(INSTALL_DATA) "$$file" "$(DESTDIR)$(man8dir)/$$inst" || exit $$?; \ fi; \ done; \ for i in $$list; do echo "$$i"; done | $(am__base_list) | \ while read files; do \ test -z "$$files" || { \ echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(man8dir)'"; \ $(INSTALL_DATA) $$files "$(DESTDIR)$(man8dir)" || exit $$?; }; \ done; } uninstall-man8: @$(NORMAL_UNINSTALL) @list=''; test -n "$(man8dir)" || exit 0; \ files=`{ for i in $$list; do echo "$$i"; done; \ l2='$(man_MANS)'; for i in $$l2; do echo "$$i"; done | \ sed -n '/\.8[a-z]*$$/p'; \ } | sed -e 's,.*/,,;h;s,.*\.,,;s,^[^8][0-9a-z]*$$,8,;x' \ -e 's,\.[0-9a-z]*$$,,;$(transform);G;s,\n,.,'`; \ dir='$(DESTDIR)$(man8dir)'; $(am__uninstall_files_from_dir) install-htmlDATA: $(html_DATA) @$(NORMAL_INSTALL) @list='$(html_DATA)'; test -n "$(htmldir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(htmldir)'"; \ $(MKDIR_P) "$(DESTDIR)$(htmldir)" || exit 1; \ fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; \ done | $(am__base_list) | \ while read files; do \ echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(htmldir)'"; \ $(INSTALL_DATA) $$files "$(DESTDIR)$(htmldir)" || exit $$?; \ done uninstall-htmlDATA: @$(NORMAL_UNINSTALL) @list='$(html_DATA)'; test -n "$(htmldir)" || list=; \ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ dir='$(DESTDIR)$(htmldir)'; $(am__uninstall_files_from_dir) tags TAGS: ctags CTAGS: cscope cscopelist: distdir: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done check-am: all-am check: check-am all-am: Makefile $(MANS) $(DATA) installdirs: for dir in "$(DESTDIR)$(man8dir)" "$(DESTDIR)$(htmldir)"; do \ test -z "$$dir" || $(MKDIR_P) "$$dir"; \ done install: install-am install-exec: install-exec-am install-data: install-data-am uninstall: uninstall-am install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-am install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: clean-generic: -test -z "$(CLEANFILES)" || rm -f $(CLEANFILES) distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." clean: clean-am clean-am: clean-generic clean-libtool mostlyclean-am distclean: distclean-am -rm -f Makefile distclean-am: clean-am distclean-generic dvi: dvi-am dvi-am: html: html-am html-am: info: info-am info-am: install-data-am: install-htmlDATA install-man install-dvi: install-dvi-am install-dvi-am: install-exec-am: install-html: install-html-am install-html-am: install-info: install-info-am install-info-am: install-man: install-man8 install-pdf: install-pdf-am install-pdf-am: install-ps: install-ps-am install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-am -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-am mostlyclean-am: mostlyclean-generic mostlyclean-libtool pdf: pdf-am pdf-am: ps: ps-am ps-am: uninstall-am: uninstall-htmlDATA uninstall-man uninstall-man: uninstall-man8 .MAKE: install-am install-strip .PHONY: all all-am check check-am clean clean-generic clean-libtool \ cscopelist-am ctags-am distclean distclean-generic \ distclean-libtool distdir dvi dvi-am html html-am info info-am \ install install-am install-data install-data-am install-dvi \ install-dvi-am install-exec install-exec-am install-html \ install-html-am install-htmlDATA install-info install-info-am \ install-man install-man8 install-pdf install-pdf-am install-ps \ install-ps-am install-strip installcheck installcheck-am \ installdirs maintainer-clean maintainer-clean-generic \ mostlyclean mostlyclean-generic mostlyclean-libtool pdf pdf-am \ ps ps-am tags-am uninstall uninstall-am uninstall-htmlDATA \ uninstall-man uninstall-man8 %.8: %.8.in Makefile $(AM_V_GEN)sed -e 's,[@]libexecdir[@],@libexecdir@,' < $< > $@ $(html_DATA): %.html: %.xml xep.xsl xep.dtd xep.ent $(AM_V_GEN)$(XSLTPROC) $(srcdir)/xep.xsl $< > $@ proto-xep-upload: $(html_DATA) rsync -P $(html_DATA) people.collabora.co.uk:public_html/ .PHONY: proto-xep-upload maintainer-update-from-xmpp.org: set -e; \ for x in xep.dtd xep.ent xep.xsd xep.xsl; do \ uri=svn://svn.xmpp.org:7938/xmpp/trunk/extensions/$$x; \ svn info $$uri; \ svn cat $$uri > $$x.tmp; \ mv $$x.tmp $$x; \ done # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: telepathy-gabble-0.18.2/ltmain.sh0000644000175000017500000105202612312536063016644 0ustar00smcvsmcv00000000000000 # libtool (GNU libtool) 2.4.2 # Written by Gordon Matzigkeit , 1996 # Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2003, 2004, 2005, 2006, # 2007, 2008, 2009, 2010, 2011 Free Software Foundation, Inc. # This is free software; see the source for copying conditions. There is NO # warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # GNU Libtool 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 2 of the License, or # (at your option) any later version. # # As a special exception to the GNU General Public License, # if you distribute this file as part of a program or library that # is built using GNU Libtool, you may include this file under the # same distribution terms that you use for the rest of that program. # # GNU Libtool 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 GNU Libtool; see the file COPYING. If not, a copy # can be downloaded from http://www.gnu.org/licenses/gpl.html, # or obtained by writing to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. # Usage: $progname [OPTION]... [MODE-ARG]... # # Provide generalized library-building support services. # # --config show all configuration variables # --debug enable verbose shell tracing # -n, --dry-run display commands without modifying any files # --features display basic configuration information and exit # --mode=MODE use operation mode MODE # --preserve-dup-deps don't remove duplicate dependency libraries # --quiet, --silent don't print informational messages # --no-quiet, --no-silent # print informational messages (default) # --no-warn don't display warning messages # --tag=TAG use configuration variables from tag TAG # -v, --verbose print more informational messages than default # --no-verbose don't print the extra informational messages # --version print version information # -h, --help, --help-all print short, long, or detailed help message # # MODE must be one of the following: # # clean remove files from the build directory # compile compile a source file into a libtool object # execute automatically set library path, then run a program # finish complete the installation of libtool libraries # install install libraries or executables # link create a library or an executable # uninstall remove libraries from an installed directory # # MODE-ARGS vary depending on the MODE. When passed as first option, # `--mode=MODE' may be abbreviated as `MODE' or a unique abbreviation of that. # Try `$progname --help --mode=MODE' for a more detailed description of MODE. # # When reporting a bug, please describe a test case to reproduce it and # include the following information: # # host-triplet: $host # shell: $SHELL # compiler: $LTCC # compiler flags: $LTCFLAGS # linker: $LD (gnu? $with_gnu_ld) # $progname: (GNU libtool) 2.4.2 Debian-2.4.2-1.7 # automake: $automake_version # autoconf: $autoconf_version # # Report bugs to . # GNU libtool home page: . # General help using GNU software: . PROGRAM=libtool PACKAGE=libtool VERSION="2.4.2 Debian-2.4.2-1.7" TIMESTAMP="" package_revision=1.3337 # Be Bourne compatible if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then emulate sh NULLCMD=: # Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which # is contrary to our usage. Disable this feature. alias -g '${1+"$@"}'='"$@"' setopt NO_GLOB_SUBST else case `(set -o) 2>/dev/null` in *posix*) set -o posix;; esac fi BIN_SH=xpg4; export BIN_SH # for Tru64 DUALCASE=1; export DUALCASE # for MKS sh # A function that is used when there is no print builtin or printf. func_fallback_echo () { eval 'cat <<_LTECHO_EOF $1 _LTECHO_EOF' } # NLS nuisances: We save the old values to restore during execute mode. lt_user_locale= lt_safe_locale= for lt_var in LANG LANGUAGE LC_ALL LC_CTYPE LC_COLLATE LC_MESSAGES do eval "if test \"\${$lt_var+set}\" = set; then save_$lt_var=\$$lt_var $lt_var=C export $lt_var lt_user_locale=\"$lt_var=\\\$save_\$lt_var; \$lt_user_locale\" lt_safe_locale=\"$lt_var=C; \$lt_safe_locale\" fi" done LC_ALL=C LANGUAGE=C export LANGUAGE LC_ALL $lt_unset CDPATH # Work around backward compatibility issue on IRIX 6.5. On IRIX 6.4+, sh # is ksh but when the shell is invoked as "sh" and the current value of # the _XPG environment variable is not equal to 1 (one), the special # positional parameter $0, within a function call, is the name of the # function. progpath="$0" : ${CP="cp -f"} test "${ECHO+set}" = set || ECHO=${as_echo-'printf %s\n'} : ${MAKE="make"} : ${MKDIR="mkdir"} : ${MV="mv -f"} : ${RM="rm -f"} : ${SHELL="${CONFIG_SHELL-/bin/sh}"} : ${Xsed="$SED -e 1s/^X//"} # Global variables: EXIT_SUCCESS=0 EXIT_FAILURE=1 EXIT_MISMATCH=63 # $? = 63 is used to indicate version mismatch to missing. EXIT_SKIP=77 # $? = 77 is used to indicate a skipped test to automake. exit_status=$EXIT_SUCCESS # Make sure IFS has a sensible default lt_nl=' ' IFS=" $lt_nl" dirname="s,/[^/]*$,," basename="s,^.*/,," # func_dirname file append nondir_replacement # Compute the dirname of FILE. If nonempty, add APPEND to the result, # otherwise set result to NONDIR_REPLACEMENT. func_dirname () { func_dirname_result=`$ECHO "${1}" | $SED "$dirname"` if test "X$func_dirname_result" = "X${1}"; then func_dirname_result="${3}" else func_dirname_result="$func_dirname_result${2}" fi } # func_dirname may be replaced by extended shell implementation # func_basename file func_basename () { func_basename_result=`$ECHO "${1}" | $SED "$basename"` } # func_basename may be replaced by extended shell implementation # func_dirname_and_basename file append nondir_replacement # perform func_basename and func_dirname in a single function # call: # dirname: Compute the dirname of FILE. If nonempty, # add APPEND to the result, otherwise set result # to NONDIR_REPLACEMENT. # value returned in "$func_dirname_result" # basename: Compute filename of FILE. # value retuned in "$func_basename_result" # Implementation must be kept synchronized with func_dirname # and func_basename. For efficiency, we do not delegate to # those functions but instead duplicate the functionality here. func_dirname_and_basename () { # Extract subdirectory from the argument. func_dirname_result=`$ECHO "${1}" | $SED -e "$dirname"` if test "X$func_dirname_result" = "X${1}"; then func_dirname_result="${3}" else func_dirname_result="$func_dirname_result${2}" fi func_basename_result=`$ECHO "${1}" | $SED -e "$basename"` } # func_dirname_and_basename may be replaced by extended shell implementation # func_stripname prefix suffix name # strip PREFIX and SUFFIX off of NAME. # PREFIX and SUFFIX must not contain globbing or regex special # characters, hashes, percent signs, but SUFFIX may contain a leading # dot (in which case that matches only a dot). # func_strip_suffix prefix name func_stripname () { case ${2} in .*) func_stripname_result=`$ECHO "${3}" | $SED "s%^${1}%%; s%\\\\${2}\$%%"`;; *) func_stripname_result=`$ECHO "${3}" | $SED "s%^${1}%%; s%${2}\$%%"`;; esac } # func_stripname may be replaced by extended shell implementation # These SED scripts presuppose an absolute path with a trailing slash. pathcar='s,^/\([^/]*\).*$,\1,' pathcdr='s,^/[^/]*,,' removedotparts=':dotsl s@/\./@/@g t dotsl s,/\.$,/,' collapseslashes='s@/\{1,\}@/@g' finalslash='s,/*$,/,' # func_normal_abspath PATH # Remove doubled-up and trailing slashes, "." path components, # and cancel out any ".." path components in PATH after making # it an absolute path. # value returned in "$func_normal_abspath_result" func_normal_abspath () { # Start from root dir and reassemble the path. func_normal_abspath_result= func_normal_abspath_tpath=$1 func_normal_abspath_altnamespace= case $func_normal_abspath_tpath in "") # Empty path, that just means $cwd. func_stripname '' '/' "`pwd`" func_normal_abspath_result=$func_stripname_result return ;; # The next three entries are used to spot a run of precisely # two leading slashes without using negated character classes; # we take advantage of case's first-match behaviour. ///*) # Unusual form of absolute path, do nothing. ;; //*) # Not necessarily an ordinary path; POSIX reserves leading '//' # and for example Cygwin uses it to access remote file shares # over CIFS/SMB, so we conserve a leading double slash if found. func_normal_abspath_altnamespace=/ ;; /*) # Absolute path, do nothing. ;; *) # Relative path, prepend $cwd. func_normal_abspath_tpath=`pwd`/$func_normal_abspath_tpath ;; esac # Cancel out all the simple stuff to save iterations. We also want # the path to end with a slash for ease of parsing, so make sure # there is one (and only one) here. func_normal_abspath_tpath=`$ECHO "$func_normal_abspath_tpath" | $SED \ -e "$removedotparts" -e "$collapseslashes" -e "$finalslash"` while :; do # Processed it all yet? if test "$func_normal_abspath_tpath" = / ; then # If we ascended to the root using ".." the result may be empty now. if test -z "$func_normal_abspath_result" ; then func_normal_abspath_result=/ fi break fi func_normal_abspath_tcomponent=`$ECHO "$func_normal_abspath_tpath" | $SED \ -e "$pathcar"` func_normal_abspath_tpath=`$ECHO "$func_normal_abspath_tpath" | $SED \ -e "$pathcdr"` # Figure out what to do with it case $func_normal_abspath_tcomponent in "") # Trailing empty path component, ignore it. ;; ..) # Parent dir; strip last assembled component from result. func_dirname "$func_normal_abspath_result" func_normal_abspath_result=$func_dirname_result ;; *) # Actual path component, append it. func_normal_abspath_result=$func_normal_abspath_result/$func_normal_abspath_tcomponent ;; esac done # Restore leading double-slash if one was found on entry. func_normal_abspath_result=$func_normal_abspath_altnamespace$func_normal_abspath_result } # func_relative_path SRCDIR DSTDIR # generates a relative path from SRCDIR to DSTDIR, with a trailing # slash if non-empty, suitable for immediately appending a filename # without needing to append a separator. # value returned in "$func_relative_path_result" func_relative_path () { func_relative_path_result= func_normal_abspath "$1" func_relative_path_tlibdir=$func_normal_abspath_result func_normal_abspath "$2" func_relative_path_tbindir=$func_normal_abspath_result # Ascend the tree starting from libdir while :; do # check if we have found a prefix of bindir case $func_relative_path_tbindir in $func_relative_path_tlibdir) # found an exact match func_relative_path_tcancelled= break ;; $func_relative_path_tlibdir*) # found a matching prefix func_stripname "$func_relative_path_tlibdir" '' "$func_relative_path_tbindir" func_relative_path_tcancelled=$func_stripname_result if test -z "$func_relative_path_result"; then func_relative_path_result=. fi break ;; *) func_dirname $func_relative_path_tlibdir func_relative_path_tlibdir=${func_dirname_result} if test "x$func_relative_path_tlibdir" = x ; then # Have to descend all the way to the root! func_relative_path_result=../$func_relative_path_result func_relative_path_tcancelled=$func_relative_path_tbindir break fi func_relative_path_result=../$func_relative_path_result ;; esac done # Now calculate path; take care to avoid doubling-up slashes. func_stripname '' '/' "$func_relative_path_result" func_relative_path_result=$func_stripname_result func_stripname '/' '/' "$func_relative_path_tcancelled" if test "x$func_stripname_result" != x ; then func_relative_path_result=${func_relative_path_result}/${func_stripname_result} fi # Normalisation. If bindir is libdir, return empty string, # else relative path ending with a slash; either way, target # file name can be directly appended. if test ! -z "$func_relative_path_result"; then func_stripname './' '' "$func_relative_path_result/" func_relative_path_result=$func_stripname_result fi } # The name of this program: func_dirname_and_basename "$progpath" progname=$func_basename_result # Make sure we have an absolute path for reexecution: case $progpath in [\\/]*|[A-Za-z]:\\*) ;; *[\\/]*) progdir=$func_dirname_result progdir=`cd "$progdir" && pwd` progpath="$progdir/$progname" ;; *) save_IFS="$IFS" IFS=${PATH_SEPARATOR-:} for progdir in $PATH; do IFS="$save_IFS" test -x "$progdir/$progname" && break done IFS="$save_IFS" test -n "$progdir" || progdir=`pwd` progpath="$progdir/$progname" ;; esac # Sed substitution that helps us do robust quoting. It backslashifies # metacharacters that are still active within double-quoted strings. Xsed="${SED}"' -e 1s/^X//' sed_quote_subst='s/\([`"$\\]\)/\\\1/g' # Same as above, but do not quote variable references. double_quote_subst='s/\(["`\\]\)/\\\1/g' # Sed substitution that turns a string into a regex matching for the # string literally. sed_make_literal_regex='s,[].[^$\\*\/],\\&,g' # Sed substitution that converts a w32 file name or path # which contains forward slashes, into one that contains # (escaped) backslashes. A very naive implementation. lt_sed_naive_backslashify='s|\\\\*|\\|g;s|/|\\|g;s|\\|\\\\|g' # Re-`\' parameter expansions in output of double_quote_subst that were # `\'-ed in input to the same. If an odd number of `\' preceded a '$' # in input to double_quote_subst, that '$' was protected from expansion. # Since each input `\' is now two `\'s, look for any number of runs of # four `\'s followed by two `\'s and then a '$'. `\' that '$'. bs='\\' bs2='\\\\' bs4='\\\\\\\\' dollar='\$' sed_double_backslash="\ s/$bs4/&\\ /g s/^$bs2$dollar/$bs&/ s/\\([^$bs]\\)$bs2$dollar/\\1$bs2$bs$dollar/g s/\n//g" # Standard options: opt_dry_run=false opt_help=false opt_quiet=false opt_verbose=false opt_warning=: # func_echo arg... # Echo program name prefixed message, along with the current mode # name if it has been set yet. func_echo () { $ECHO "$progname: ${opt_mode+$opt_mode: }$*" } # func_verbose arg... # Echo program name prefixed message in verbose mode only. func_verbose () { $opt_verbose && func_echo ${1+"$@"} # A bug in bash halts the script if the last line of a function # fails when set -e is in force, so we need another command to # work around that: : } # func_echo_all arg... # Invoke $ECHO with all args, space-separated. func_echo_all () { $ECHO "$*" } # func_error arg... # Echo program name prefixed message to standard error. func_error () { $ECHO "$progname: ${opt_mode+$opt_mode: }"${1+"$@"} 1>&2 } # func_warning arg... # Echo program name prefixed warning message to standard error. func_warning () { $opt_warning && $ECHO "$progname: ${opt_mode+$opt_mode: }warning: "${1+"$@"} 1>&2 # bash bug again: : } # func_fatal_error arg... # Echo program name prefixed message to standard error, and exit. func_fatal_error () { func_error ${1+"$@"} exit $EXIT_FAILURE } # func_fatal_help arg... # Echo program name prefixed message to standard error, followed by # a help hint, and exit. func_fatal_help () { func_error ${1+"$@"} func_fatal_error "$help" } help="Try \`$progname --help' for more information." ## default # func_grep expression filename # Check whether EXPRESSION matches any line of FILENAME, without output. func_grep () { $GREP "$1" "$2" >/dev/null 2>&1 } # func_mkdir_p directory-path # Make sure the entire path to DIRECTORY-PATH is available. func_mkdir_p () { my_directory_path="$1" my_dir_list= if test -n "$my_directory_path" && test "$opt_dry_run" != ":"; then # Protect directory names starting with `-' case $my_directory_path in -*) my_directory_path="./$my_directory_path" ;; esac # While some portion of DIR does not yet exist... while test ! -d "$my_directory_path"; do # ...make a list in topmost first order. Use a colon delimited # list incase some portion of path contains whitespace. my_dir_list="$my_directory_path:$my_dir_list" # If the last portion added has no slash in it, the list is done case $my_directory_path in */*) ;; *) break ;; esac # ...otherwise throw away the child directory and loop my_directory_path=`$ECHO "$my_directory_path" | $SED -e "$dirname"` done my_dir_list=`$ECHO "$my_dir_list" | $SED 's,:*$,,'` save_mkdir_p_IFS="$IFS"; IFS=':' for my_dir in $my_dir_list; do IFS="$save_mkdir_p_IFS" # mkdir can fail with a `File exist' error if two processes # try to create one of the directories concurrently. Don't # stop in that case! $MKDIR "$my_dir" 2>/dev/null || : done IFS="$save_mkdir_p_IFS" # Bail out if we (or some other process) failed to create a directory. test -d "$my_directory_path" || \ func_fatal_error "Failed to create \`$1'" fi } # func_mktempdir [string] # Make a temporary directory that won't clash with other running # libtool processes, and avoids race conditions if possible. If # given, STRING is the basename for that directory. func_mktempdir () { my_template="${TMPDIR-/tmp}/${1-$progname}" if test "$opt_dry_run" = ":"; then # Return a directory name, but don't create it in dry-run mode my_tmpdir="${my_template}-$$" else # If mktemp works, use that first and foremost my_tmpdir=`mktemp -d "${my_template}-XXXXXXXX" 2>/dev/null` if test ! -d "$my_tmpdir"; then # Failing that, at least try and use $RANDOM to avoid a race my_tmpdir="${my_template}-${RANDOM-0}$$" save_mktempdir_umask=`umask` umask 0077 $MKDIR "$my_tmpdir" umask $save_mktempdir_umask fi # If we're not in dry-run mode, bomb out on failure test -d "$my_tmpdir" || \ func_fatal_error "cannot create temporary directory \`$my_tmpdir'" fi $ECHO "$my_tmpdir" } # func_quote_for_eval arg # Aesthetically quote ARG to be evaled later. # This function returns two values: FUNC_QUOTE_FOR_EVAL_RESULT # is double-quoted, suitable for a subsequent eval, whereas # FUNC_QUOTE_FOR_EVAL_UNQUOTED_RESULT has merely all characters # which are still active within double quotes backslashified. func_quote_for_eval () { case $1 in *[\\\`\"\$]*) func_quote_for_eval_unquoted_result=`$ECHO "$1" | $SED "$sed_quote_subst"` ;; *) func_quote_for_eval_unquoted_result="$1" ;; esac case $func_quote_for_eval_unquoted_result in # Double-quote args containing shell metacharacters to delay # word splitting, command substitution and and variable # expansion for a subsequent eval. # Many Bourne shells cannot handle close brackets correctly # in scan sets, so we specify it separately. *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*|"") func_quote_for_eval_result="\"$func_quote_for_eval_unquoted_result\"" ;; *) func_quote_for_eval_result="$func_quote_for_eval_unquoted_result" esac } # func_quote_for_expand arg # Aesthetically quote ARG to be evaled later; same as above, # but do not quote variable references. func_quote_for_expand () { case $1 in *[\\\`\"]*) my_arg=`$ECHO "$1" | $SED \ -e "$double_quote_subst" -e "$sed_double_backslash"` ;; *) my_arg="$1" ;; esac case $my_arg in # Double-quote args containing shell metacharacters to delay # word splitting and command substitution for a subsequent eval. # Many Bourne shells cannot handle close brackets correctly # in scan sets, so we specify it separately. *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*|"") my_arg="\"$my_arg\"" ;; esac func_quote_for_expand_result="$my_arg" } # func_show_eval cmd [fail_exp] # Unless opt_silent is true, then output CMD. Then, if opt_dryrun is # not true, evaluate CMD. If the evaluation of CMD fails, and FAIL_EXP # is given, then evaluate it. func_show_eval () { my_cmd="$1" my_fail_exp="${2-:}" ${opt_silent-false} || { func_quote_for_expand "$my_cmd" eval "func_echo $func_quote_for_expand_result" } if ${opt_dry_run-false}; then :; else eval "$my_cmd" my_status=$? if test "$my_status" -eq 0; then :; else eval "(exit $my_status); $my_fail_exp" fi fi } # func_show_eval_locale cmd [fail_exp] # Unless opt_silent is true, then output CMD. Then, if opt_dryrun is # not true, evaluate CMD. If the evaluation of CMD fails, and FAIL_EXP # is given, then evaluate it. Use the saved locale for evaluation. func_show_eval_locale () { my_cmd="$1" my_fail_exp="${2-:}" ${opt_silent-false} || { func_quote_for_expand "$my_cmd" eval "func_echo $func_quote_for_expand_result" } if ${opt_dry_run-false}; then :; else eval "$lt_user_locale $my_cmd" my_status=$? eval "$lt_safe_locale" if test "$my_status" -eq 0; then :; else eval "(exit $my_status); $my_fail_exp" fi fi } # func_tr_sh # Turn $1 into a string suitable for a shell variable name. # Result is stored in $func_tr_sh_result. All characters # not in the set a-zA-Z0-9_ are replaced with '_'. Further, # if $1 begins with a digit, a '_' is prepended as well. func_tr_sh () { case $1 in [0-9]* | *[!a-zA-Z0-9_]*) func_tr_sh_result=`$ECHO "$1" | $SED 's/^\([0-9]\)/_\1/; s/[^a-zA-Z0-9_]/_/g'` ;; * ) func_tr_sh_result=$1 ;; esac } # func_version # Echo version message to standard output and exit. func_version () { $opt_debug $SED -n '/(C)/!b go :more /\./!{ N s/\n# / / b more } :go /^# '$PROGRAM' (GNU /,/# warranty; / { s/^# // s/^# *$// s/\((C)\)[ 0-9,-]*\( [1-9][0-9]*\)/\1\2/ p }' < "$progpath" exit $? } # func_usage # Echo short help message to standard output and exit. func_usage () { $opt_debug $SED -n '/^# Usage:/,/^# *.*--help/ { s/^# // s/^# *$// s/\$progname/'$progname'/ p }' < "$progpath" echo $ECHO "run \`$progname --help | more' for full usage" exit $? } # func_help [NOEXIT] # Echo long help message to standard output and exit, # unless 'noexit' is passed as argument. func_help () { $opt_debug $SED -n '/^# Usage:/,/# Report bugs to/ { :print s/^# // s/^# *$// s*\$progname*'$progname'* s*\$host*'"$host"'* s*\$SHELL*'"$SHELL"'* s*\$LTCC*'"$LTCC"'* s*\$LTCFLAGS*'"$LTCFLAGS"'* s*\$LD*'"$LD"'* s/\$with_gnu_ld/'"$with_gnu_ld"'/ s/\$automake_version/'"`(${AUTOMAKE-automake} --version) 2>/dev/null |$SED 1q`"'/ s/\$autoconf_version/'"`(${AUTOCONF-autoconf} --version) 2>/dev/null |$SED 1q`"'/ p d } /^# .* home page:/b print /^# General help using/b print ' < "$progpath" ret=$? if test -z "$1"; then exit $ret fi } # func_missing_arg argname # Echo program name prefixed message to standard error and set global # exit_cmd. func_missing_arg () { $opt_debug func_error "missing argument for $1." exit_cmd=exit } # func_split_short_opt shortopt # Set func_split_short_opt_name and func_split_short_opt_arg shell # variables after splitting SHORTOPT after the 2nd character. func_split_short_opt () { my_sed_short_opt='1s/^\(..\).*$/\1/;q' my_sed_short_rest='1s/^..\(.*\)$/\1/;q' func_split_short_opt_name=`$ECHO "$1" | $SED "$my_sed_short_opt"` func_split_short_opt_arg=`$ECHO "$1" | $SED "$my_sed_short_rest"` } # func_split_short_opt may be replaced by extended shell implementation # func_split_long_opt longopt # Set func_split_long_opt_name and func_split_long_opt_arg shell # variables after splitting LONGOPT at the `=' sign. func_split_long_opt () { my_sed_long_opt='1s/^\(--[^=]*\)=.*/\1/;q' my_sed_long_arg='1s/^--[^=]*=//' func_split_long_opt_name=`$ECHO "$1" | $SED "$my_sed_long_opt"` func_split_long_opt_arg=`$ECHO "$1" | $SED "$my_sed_long_arg"` } # func_split_long_opt may be replaced by extended shell implementation exit_cmd=: magic="%%%MAGIC variable%%%" magic_exe="%%%MAGIC EXE variable%%%" # Global variables. nonopt= preserve_args= lo2o="s/\\.lo\$/.${objext}/" o2lo="s/\\.${objext}\$/.lo/" extracted_archives= extracted_serial=0 # If this variable is set in any of the actions, the command in it # will be execed at the end. This prevents here-documents from being # left over by shells. exec_cmd= # func_append var value # Append VALUE to the end of shell variable VAR. func_append () { eval "${1}=\$${1}\${2}" } # func_append may be replaced by extended shell implementation # func_append_quoted var value # Quote VALUE and append to the end of shell variable VAR, separated # by a space. func_append_quoted () { func_quote_for_eval "${2}" eval "${1}=\$${1}\\ \$func_quote_for_eval_result" } # func_append_quoted may be replaced by extended shell implementation # func_arith arithmetic-term... func_arith () { func_arith_result=`expr "${@}"` } # func_arith may be replaced by extended shell implementation # func_len string # STRING may not start with a hyphen. func_len () { func_len_result=`expr "${1}" : ".*" 2>/dev/null || echo $max_cmd_len` } # func_len may be replaced by extended shell implementation # func_lo2o object func_lo2o () { func_lo2o_result=`$ECHO "${1}" | $SED "$lo2o"` } # func_lo2o may be replaced by extended shell implementation # func_xform libobj-or-source func_xform () { func_xform_result=`$ECHO "${1}" | $SED 's/\.[^.]*$/.lo/'` } # func_xform may be replaced by extended shell implementation # func_fatal_configuration arg... # Echo program name prefixed message to standard error, followed by # a configuration failure hint, and exit. func_fatal_configuration () { func_error ${1+"$@"} func_error "See the $PACKAGE documentation for more information." func_fatal_error "Fatal configuration error." } # func_config # Display the configuration for all the tags in this script. func_config () { re_begincf='^# ### BEGIN LIBTOOL' re_endcf='^# ### END LIBTOOL' # Default configuration. $SED "1,/$re_begincf CONFIG/d;/$re_endcf CONFIG/,\$d" < "$progpath" # Now print the configurations for the tags. for tagname in $taglist; do $SED -n "/$re_begincf TAG CONFIG: $tagname\$/,/$re_endcf TAG CONFIG: $tagname\$/p" < "$progpath" done exit $? } # func_features # Display the features supported by this script. func_features () { echo "host: $host" if test "$build_libtool_libs" = yes; then echo "enable shared libraries" else echo "disable shared libraries" fi if test "$build_old_libs" = yes; then echo "enable static libraries" else echo "disable static libraries" fi exit $? } # func_enable_tag tagname # Verify that TAGNAME is valid, and either flag an error and exit, or # enable the TAGNAME tag. We also add TAGNAME to the global $taglist # variable here. func_enable_tag () { # Global variable: tagname="$1" re_begincf="^# ### BEGIN LIBTOOL TAG CONFIG: $tagname\$" re_endcf="^# ### END LIBTOOL TAG CONFIG: $tagname\$" sed_extractcf="/$re_begincf/,/$re_endcf/p" # Validate tagname. case $tagname in *[!-_A-Za-z0-9,/]*) func_fatal_error "invalid tag name: $tagname" ;; esac # Don't test for the "default" C tag, as we know it's # there but not specially marked. case $tagname in CC) ;; *) if $GREP "$re_begincf" "$progpath" >/dev/null 2>&1; then taglist="$taglist $tagname" # Evaluate the configuration. Be careful to quote the path # and the sed script, to avoid splitting on whitespace, but # also don't use non-portable quotes within backquotes within # quotes we have to do it in 2 steps: extractedcf=`$SED -n -e "$sed_extractcf" < "$progpath"` eval "$extractedcf" else func_error "ignoring unknown tag $tagname" fi ;; esac } # func_check_version_match # Ensure that we are using m4 macros, and libtool script from the same # release of libtool. func_check_version_match () { if test "$package_revision" != "$macro_revision"; then if test "$VERSION" != "$macro_version"; then if test -z "$macro_version"; then cat >&2 <<_LT_EOF $progname: Version mismatch error. This is $PACKAGE $VERSION, but the $progname: definition of this LT_INIT comes from an older release. $progname: You should recreate aclocal.m4 with macros from $PACKAGE $VERSION $progname: and run autoconf again. _LT_EOF else cat >&2 <<_LT_EOF $progname: Version mismatch error. This is $PACKAGE $VERSION, but the $progname: definition of this LT_INIT comes from $PACKAGE $macro_version. $progname: You should recreate aclocal.m4 with macros from $PACKAGE $VERSION $progname: and run autoconf again. _LT_EOF fi else cat >&2 <<_LT_EOF $progname: Version mismatch error. This is $PACKAGE $VERSION, revision $package_revision, $progname: but the definition of this LT_INIT comes from revision $macro_revision. $progname: You should recreate aclocal.m4 with macros from revision $package_revision $progname: of $PACKAGE $VERSION and run autoconf again. _LT_EOF fi exit $EXIT_MISMATCH fi } # Shorthand for --mode=foo, only valid as the first argument case $1 in clean|clea|cle|cl) shift; set dummy --mode clean ${1+"$@"}; shift ;; compile|compil|compi|comp|com|co|c) shift; set dummy --mode compile ${1+"$@"}; shift ;; execute|execut|execu|exec|exe|ex|e) shift; set dummy --mode execute ${1+"$@"}; shift ;; finish|finis|fini|fin|fi|f) shift; set dummy --mode finish ${1+"$@"}; shift ;; install|instal|insta|inst|ins|in|i) shift; set dummy --mode install ${1+"$@"}; shift ;; link|lin|li|l) shift; set dummy --mode link ${1+"$@"}; shift ;; uninstall|uninstal|uninsta|uninst|unins|unin|uni|un|u) shift; set dummy --mode uninstall ${1+"$@"}; shift ;; esac # Option defaults: opt_debug=: opt_dry_run=false opt_config=false opt_preserve_dup_deps=false opt_features=false opt_finish=false opt_help=false opt_help_all=false opt_silent=: opt_warning=: opt_verbose=: opt_silent=false opt_verbose=false # Parse options once, thoroughly. This comes as soon as possible in the # script to make things like `--version' happen as quickly as we can. { # this just eases exit handling while test $# -gt 0; do opt="$1" shift case $opt in --debug|-x) opt_debug='set -x' func_echo "enabling shell trace mode" $opt_debug ;; --dry-run|--dryrun|-n) opt_dry_run=: ;; --config) opt_config=: func_config ;; --dlopen|-dlopen) optarg="$1" opt_dlopen="${opt_dlopen+$opt_dlopen }$optarg" shift ;; --preserve-dup-deps) opt_preserve_dup_deps=: ;; --features) opt_features=: func_features ;; --finish) opt_finish=: set dummy --mode finish ${1+"$@"}; shift ;; --help) opt_help=: ;; --help-all) opt_help_all=: opt_help=': help-all' ;; --mode) test $# = 0 && func_missing_arg $opt && break optarg="$1" opt_mode="$optarg" case $optarg in # Valid mode arguments: clean|compile|execute|finish|install|link|relink|uninstall) ;; # Catch anything else as an error *) func_error "invalid argument for $opt" exit_cmd=exit break ;; esac shift ;; --no-silent|--no-quiet) opt_silent=false func_append preserve_args " $opt" ;; --no-warning|--no-warn) opt_warning=false func_append preserve_args " $opt" ;; --no-verbose) opt_verbose=false func_append preserve_args " $opt" ;; --silent|--quiet) opt_silent=: func_append preserve_args " $opt" opt_verbose=false ;; --verbose|-v) opt_verbose=: func_append preserve_args " $opt" opt_silent=false ;; --tag) test $# = 0 && func_missing_arg $opt && break optarg="$1" opt_tag="$optarg" func_append preserve_args " $opt $optarg" func_enable_tag "$optarg" shift ;; -\?|-h) func_usage ;; --help) func_help ;; --version) func_version ;; # Separate optargs to long options: --*=*) func_split_long_opt "$opt" set dummy "$func_split_long_opt_name" "$func_split_long_opt_arg" ${1+"$@"} shift ;; # Separate non-argument short options: -\?*|-h*|-n*|-v*) func_split_short_opt "$opt" set dummy "$func_split_short_opt_name" "-$func_split_short_opt_arg" ${1+"$@"} shift ;; --) break ;; -*) func_fatal_help "unrecognized option \`$opt'" ;; *) set dummy "$opt" ${1+"$@"}; shift; break ;; esac done # Validate options: # save first non-option argument if test "$#" -gt 0; then nonopt="$opt" shift fi # preserve --debug test "$opt_debug" = : || func_append preserve_args " --debug" case $host in *cygwin* | *mingw* | *pw32* | *cegcc*) # don't eliminate duplications in $postdeps and $predeps opt_duplicate_compiler_generated_deps=: ;; *) opt_duplicate_compiler_generated_deps=$opt_preserve_dup_deps ;; esac $opt_help || { # Sanity checks first: func_check_version_match if test "$build_libtool_libs" != yes && test "$build_old_libs" != yes; then func_fatal_configuration "not configured to build any kind of library" fi # Darwin sucks eval std_shrext=\"$shrext_cmds\" # Only execute mode is allowed to have -dlopen flags. if test -n "$opt_dlopen" && test "$opt_mode" != execute; then func_error "unrecognized option \`-dlopen'" $ECHO "$help" 1>&2 exit $EXIT_FAILURE fi # Change the help message to a mode-specific one. generic_help="$help" help="Try \`$progname --help --mode=$opt_mode' for more information." } # Bail if the options were screwed $exit_cmd $EXIT_FAILURE } ## ----------- ## ## Main. ## ## ----------- ## # func_lalib_p file # True iff FILE is a libtool `.la' library or `.lo' object file. # This function is only a basic sanity check; it will hardly flush out # determined imposters. func_lalib_p () { test -f "$1" && $SED -e 4q "$1" 2>/dev/null \ | $GREP "^# Generated by .*$PACKAGE" > /dev/null 2>&1 } # func_lalib_unsafe_p file # True iff FILE is a libtool `.la' library or `.lo' object file. # This function implements the same check as func_lalib_p without # resorting to external programs. To this end, it redirects stdin and # closes it afterwards, without saving the original file descriptor. # As a safety measure, use it only where a negative result would be # fatal anyway. Works if `file' does not exist. func_lalib_unsafe_p () { lalib_p=no if test -f "$1" && test -r "$1" && exec 5<&0 <"$1"; then for lalib_p_l in 1 2 3 4 do read lalib_p_line case "$lalib_p_line" in \#\ Generated\ by\ *$PACKAGE* ) lalib_p=yes; break;; esac done exec 0<&5 5<&- fi test "$lalib_p" = yes } # func_ltwrapper_script_p file # True iff FILE is a libtool wrapper script # This function is only a basic sanity check; it will hardly flush out # determined imposters. func_ltwrapper_script_p () { func_lalib_p "$1" } # func_ltwrapper_executable_p file # True iff FILE is a libtool wrapper executable # This function is only a basic sanity check; it will hardly flush out # determined imposters. func_ltwrapper_executable_p () { func_ltwrapper_exec_suffix= case $1 in *.exe) ;; *) func_ltwrapper_exec_suffix=.exe ;; esac $GREP "$magic_exe" "$1$func_ltwrapper_exec_suffix" >/dev/null 2>&1 } # func_ltwrapper_scriptname file # Assumes file is an ltwrapper_executable # uses $file to determine the appropriate filename for a # temporary ltwrapper_script. func_ltwrapper_scriptname () { func_dirname_and_basename "$1" "" "." func_stripname '' '.exe' "$func_basename_result" func_ltwrapper_scriptname_result="$func_dirname_result/$objdir/${func_stripname_result}_ltshwrapper" } # func_ltwrapper_p file # True iff FILE is a libtool wrapper script or wrapper executable # This function is only a basic sanity check; it will hardly flush out # determined imposters. func_ltwrapper_p () { func_ltwrapper_script_p "$1" || func_ltwrapper_executable_p "$1" } # func_execute_cmds commands fail_cmd # Execute tilde-delimited COMMANDS. # If FAIL_CMD is given, eval that upon failure. # FAIL_CMD may read-access the current command in variable CMD! func_execute_cmds () { $opt_debug save_ifs=$IFS; IFS='~' for cmd in $1; do IFS=$save_ifs eval cmd=\"$cmd\" func_show_eval "$cmd" "${2-:}" done IFS=$save_ifs } # func_source file # Source FILE, adding directory component if necessary. # Note that it is not necessary on cygwin/mingw to append a dot to # FILE even if both FILE and FILE.exe exist: automatic-append-.exe # behavior happens only for exec(3), not for open(2)! Also, sourcing # `FILE.' does not work on cygwin managed mounts. func_source () { $opt_debug case $1 in */* | *\\*) . "$1" ;; *) . "./$1" ;; esac } # func_resolve_sysroot PATH # Replace a leading = in PATH with a sysroot. Store the result into # func_resolve_sysroot_result func_resolve_sysroot () { func_resolve_sysroot_result=$1 case $func_resolve_sysroot_result in =*) func_stripname '=' '' "$func_resolve_sysroot_result" func_resolve_sysroot_result=$lt_sysroot$func_stripname_result ;; esac } # func_replace_sysroot PATH # If PATH begins with the sysroot, replace it with = and # store the result into func_replace_sysroot_result. func_replace_sysroot () { case "$lt_sysroot:$1" in ?*:"$lt_sysroot"*) func_stripname "$lt_sysroot" '' "$1" func_replace_sysroot_result="=$func_stripname_result" ;; *) # Including no sysroot. func_replace_sysroot_result=$1 ;; esac } # func_infer_tag arg # Infer tagged configuration to use if any are available and # if one wasn't chosen via the "--tag" command line option. # Only attempt this if the compiler in the base compile # command doesn't match the default compiler. # arg is usually of the form 'gcc ...' func_infer_tag () { $opt_debug if test -n "$available_tags" && test -z "$tagname"; then CC_quoted= for arg in $CC; do func_append_quoted CC_quoted "$arg" done CC_expanded=`func_echo_all $CC` CC_quoted_expanded=`func_echo_all $CC_quoted` case $@ in # Blanks in the command may have been stripped by the calling shell, # but not from the CC environment variable when configure was run. " $CC "* | "$CC "* | " $CC_expanded "* | "$CC_expanded "* | \ " $CC_quoted"* | "$CC_quoted "* | " $CC_quoted_expanded "* | "$CC_quoted_expanded "*) ;; # Blanks at the start of $base_compile will cause this to fail # if we don't check for them as well. *) for z in $available_tags; do if $GREP "^# ### BEGIN LIBTOOL TAG CONFIG: $z$" < "$progpath" > /dev/null; then # Evaluate the configuration. eval "`${SED} -n -e '/^# ### BEGIN LIBTOOL TAG CONFIG: '$z'$/,/^# ### END LIBTOOL TAG CONFIG: '$z'$/p' < $progpath`" CC_quoted= for arg in $CC; do # Double-quote args containing other shell metacharacters. func_append_quoted CC_quoted "$arg" done CC_expanded=`func_echo_all $CC` CC_quoted_expanded=`func_echo_all $CC_quoted` case "$@ " in " $CC "* | "$CC "* | " $CC_expanded "* | "$CC_expanded "* | \ " $CC_quoted"* | "$CC_quoted "* | " $CC_quoted_expanded "* | "$CC_quoted_expanded "*) # The compiler in the base compile command matches # the one in the tagged configuration. # Assume this is the tagged configuration we want. tagname=$z break ;; esac fi done # If $tagname still isn't set, then no tagged configuration # was found and let the user know that the "--tag" command # line option must be used. if test -z "$tagname"; then func_echo "unable to infer tagged configuration" func_fatal_error "specify a tag with \`--tag'" # else # func_verbose "using $tagname tagged configuration" fi ;; esac fi } # func_write_libtool_object output_name pic_name nonpic_name # Create a libtool object file (analogous to a ".la" file), # but don't create it if we're doing a dry run. func_write_libtool_object () { write_libobj=${1} if test "$build_libtool_libs" = yes; then write_lobj=\'${2}\' else write_lobj=none fi if test "$build_old_libs" = yes; then write_oldobj=\'${3}\' else write_oldobj=none fi $opt_dry_run || { cat >${write_libobj}T </dev/null` if test "$?" -eq 0 && test -n "${func_convert_core_file_wine_to_w32_tmp}"; then func_convert_core_file_wine_to_w32_result=`$ECHO "$func_convert_core_file_wine_to_w32_tmp" | $SED -e "$lt_sed_naive_backslashify"` else func_convert_core_file_wine_to_w32_result= fi fi } # end: func_convert_core_file_wine_to_w32 # func_convert_core_path_wine_to_w32 ARG # Helper function used by path conversion functions when $build is *nix, and # $host is mingw, cygwin, or some other w32 environment. Relies on a correctly # configured wine environment available, with the winepath program in $build's # $PATH. Assumes ARG has no leading or trailing path separator characters. # # ARG is path to be converted from $build format to win32. # Result is available in $func_convert_core_path_wine_to_w32_result. # Unconvertible file (directory) names in ARG are skipped; if no directory names # are convertible, then the result may be empty. func_convert_core_path_wine_to_w32 () { $opt_debug # unfortunately, winepath doesn't convert paths, only file names func_convert_core_path_wine_to_w32_result="" if test -n "$1"; then oldIFS=$IFS IFS=: for func_convert_core_path_wine_to_w32_f in $1; do IFS=$oldIFS func_convert_core_file_wine_to_w32 "$func_convert_core_path_wine_to_w32_f" if test -n "$func_convert_core_file_wine_to_w32_result" ; then if test -z "$func_convert_core_path_wine_to_w32_result"; then func_convert_core_path_wine_to_w32_result="$func_convert_core_file_wine_to_w32_result" else func_append func_convert_core_path_wine_to_w32_result ";$func_convert_core_file_wine_to_w32_result" fi fi done IFS=$oldIFS fi } # end: func_convert_core_path_wine_to_w32 # func_cygpath ARGS... # Wrapper around calling the cygpath program via LT_CYGPATH. This is used when # when (1) $build is *nix and Cygwin is hosted via a wine environment; or (2) # $build is MSYS and $host is Cygwin, or (3) $build is Cygwin. In case (1) or # (2), returns the Cygwin file name or path in func_cygpath_result (input # file name or path is assumed to be in w32 format, as previously converted # from $build's *nix or MSYS format). In case (3), returns the w32 file name # or path in func_cygpath_result (input file name or path is assumed to be in # Cygwin format). Returns an empty string on error. # # ARGS are passed to cygpath, with the last one being the file name or path to # be converted. # # Specify the absolute *nix (or w32) name to cygpath in the LT_CYGPATH # environment variable; do not put it in $PATH. func_cygpath () { $opt_debug if test -n "$LT_CYGPATH" && test -f "$LT_CYGPATH"; then func_cygpath_result=`$LT_CYGPATH "$@" 2>/dev/null` if test "$?" -ne 0; then # on failure, ensure result is empty func_cygpath_result= fi else func_cygpath_result= func_error "LT_CYGPATH is empty or specifies non-existent file: \`$LT_CYGPATH'" fi } #end: func_cygpath # func_convert_core_msys_to_w32 ARG # Convert file name or path ARG from MSYS format to w32 format. Return # result in func_convert_core_msys_to_w32_result. func_convert_core_msys_to_w32 () { $opt_debug # awkward: cmd appends spaces to result func_convert_core_msys_to_w32_result=`( cmd //c echo "$1" ) 2>/dev/null | $SED -e 's/[ ]*$//' -e "$lt_sed_naive_backslashify"` } #end: func_convert_core_msys_to_w32 # func_convert_file_check ARG1 ARG2 # Verify that ARG1 (a file name in $build format) was converted to $host # format in ARG2. Otherwise, emit an error message, but continue (resetting # func_to_host_file_result to ARG1). func_convert_file_check () { $opt_debug if test -z "$2" && test -n "$1" ; then func_error "Could not determine host file name corresponding to" func_error " \`$1'" func_error "Continuing, but uninstalled executables may not work." # Fallback: func_to_host_file_result="$1" fi } # end func_convert_file_check # func_convert_path_check FROM_PATHSEP TO_PATHSEP FROM_PATH TO_PATH # Verify that FROM_PATH (a path in $build format) was converted to $host # format in TO_PATH. Otherwise, emit an error message, but continue, resetting # func_to_host_file_result to a simplistic fallback value (see below). func_convert_path_check () { $opt_debug if test -z "$4" && test -n "$3"; then func_error "Could not determine the host path corresponding to" func_error " \`$3'" func_error "Continuing, but uninstalled executables may not work." # Fallback. This is a deliberately simplistic "conversion" and # should not be "improved". See libtool.info. if test "x$1" != "x$2"; then lt_replace_pathsep_chars="s|$1|$2|g" func_to_host_path_result=`echo "$3" | $SED -e "$lt_replace_pathsep_chars"` else func_to_host_path_result="$3" fi fi } # end func_convert_path_check # func_convert_path_front_back_pathsep FRONTPAT BACKPAT REPL ORIG # Modifies func_to_host_path_result by prepending REPL if ORIG matches FRONTPAT # and appending REPL if ORIG matches BACKPAT. func_convert_path_front_back_pathsep () { $opt_debug case $4 in $1 ) func_to_host_path_result="$3$func_to_host_path_result" ;; esac case $4 in $2 ) func_append func_to_host_path_result "$3" ;; esac } # end func_convert_path_front_back_pathsep ################################################## # $build to $host FILE NAME CONVERSION FUNCTIONS # ################################################## # invoked via `$to_host_file_cmd ARG' # # In each case, ARG is the path to be converted from $build to $host format. # Result will be available in $func_to_host_file_result. # func_to_host_file ARG # Converts the file name ARG from $build format to $host format. Return result # in func_to_host_file_result. func_to_host_file () { $opt_debug $to_host_file_cmd "$1" } # end func_to_host_file # func_to_tool_file ARG LAZY # converts the file name ARG from $build format to toolchain format. Return # result in func_to_tool_file_result. If the conversion in use is listed # in (the comma separated) LAZY, no conversion takes place. func_to_tool_file () { $opt_debug case ,$2, in *,"$to_tool_file_cmd",*) func_to_tool_file_result=$1 ;; *) $to_tool_file_cmd "$1" func_to_tool_file_result=$func_to_host_file_result ;; esac } # end func_to_tool_file # func_convert_file_noop ARG # Copy ARG to func_to_host_file_result. func_convert_file_noop () { func_to_host_file_result="$1" } # end func_convert_file_noop # func_convert_file_msys_to_w32 ARG # Convert file name ARG from (mingw) MSYS to (mingw) w32 format; automatic # conversion to w32 is not available inside the cwrapper. Returns result in # func_to_host_file_result. func_convert_file_msys_to_w32 () { $opt_debug func_to_host_file_result="$1" if test -n "$1"; then func_convert_core_msys_to_w32 "$1" func_to_host_file_result="$func_convert_core_msys_to_w32_result" fi func_convert_file_check "$1" "$func_to_host_file_result" } # end func_convert_file_msys_to_w32 # func_convert_file_cygwin_to_w32 ARG # Convert file name ARG from Cygwin to w32 format. Returns result in # func_to_host_file_result. func_convert_file_cygwin_to_w32 () { $opt_debug func_to_host_file_result="$1" if test -n "$1"; then # because $build is cygwin, we call "the" cygpath in $PATH; no need to use # LT_CYGPATH in this case. func_to_host_file_result=`cygpath -m "$1"` fi func_convert_file_check "$1" "$func_to_host_file_result" } # end func_convert_file_cygwin_to_w32 # func_convert_file_nix_to_w32 ARG # Convert file name ARG from *nix to w32 format. Requires a wine environment # and a working winepath. Returns result in func_to_host_file_result. func_convert_file_nix_to_w32 () { $opt_debug func_to_host_file_result="$1" if test -n "$1"; then func_convert_core_file_wine_to_w32 "$1" func_to_host_file_result="$func_convert_core_file_wine_to_w32_result" fi func_convert_file_check "$1" "$func_to_host_file_result" } # end func_convert_file_nix_to_w32 # func_convert_file_msys_to_cygwin ARG # Convert file name ARG from MSYS to Cygwin format. Requires LT_CYGPATH set. # Returns result in func_to_host_file_result. func_convert_file_msys_to_cygwin () { $opt_debug func_to_host_file_result="$1" if test -n "$1"; then func_convert_core_msys_to_w32 "$1" func_cygpath -u "$func_convert_core_msys_to_w32_result" func_to_host_file_result="$func_cygpath_result" fi func_convert_file_check "$1" "$func_to_host_file_result" } # end func_convert_file_msys_to_cygwin # func_convert_file_nix_to_cygwin ARG # Convert file name ARG from *nix to Cygwin format. Requires Cygwin installed # in a wine environment, working winepath, and LT_CYGPATH set. Returns result # in func_to_host_file_result. func_convert_file_nix_to_cygwin () { $opt_debug func_to_host_file_result="$1" if test -n "$1"; then # convert from *nix to w32, then use cygpath to convert from w32 to cygwin. func_convert_core_file_wine_to_w32 "$1" func_cygpath -u "$func_convert_core_file_wine_to_w32_result" func_to_host_file_result="$func_cygpath_result" fi func_convert_file_check "$1" "$func_to_host_file_result" } # end func_convert_file_nix_to_cygwin ############################################# # $build to $host PATH CONVERSION FUNCTIONS # ############################################# # invoked via `$to_host_path_cmd ARG' # # In each case, ARG is the path to be converted from $build to $host format. # The result will be available in $func_to_host_path_result. # # Path separators are also converted from $build format to $host format. If # ARG begins or ends with a path separator character, it is preserved (but # converted to $host format) on output. # # All path conversion functions are named using the following convention: # file name conversion function : func_convert_file_X_to_Y () # path conversion function : func_convert_path_X_to_Y () # where, for any given $build/$host combination the 'X_to_Y' value is the # same. If conversion functions are added for new $build/$host combinations, # the two new functions must follow this pattern, or func_init_to_host_path_cmd # will break. # func_init_to_host_path_cmd # Ensures that function "pointer" variable $to_host_path_cmd is set to the # appropriate value, based on the value of $to_host_file_cmd. to_host_path_cmd= func_init_to_host_path_cmd () { $opt_debug if test -z "$to_host_path_cmd"; then func_stripname 'func_convert_file_' '' "$to_host_file_cmd" to_host_path_cmd="func_convert_path_${func_stripname_result}" fi } # func_to_host_path ARG # Converts the path ARG from $build format to $host format. Return result # in func_to_host_path_result. func_to_host_path () { $opt_debug func_init_to_host_path_cmd $to_host_path_cmd "$1" } # end func_to_host_path # func_convert_path_noop ARG # Copy ARG to func_to_host_path_result. func_convert_path_noop () { func_to_host_path_result="$1" } # end func_convert_path_noop # func_convert_path_msys_to_w32 ARG # Convert path ARG from (mingw) MSYS to (mingw) w32 format; automatic # conversion to w32 is not available inside the cwrapper. Returns result in # func_to_host_path_result. func_convert_path_msys_to_w32 () { $opt_debug func_to_host_path_result="$1" if test -n "$1"; then # Remove leading and trailing path separator characters from ARG. MSYS # behavior is inconsistent here; cygpath turns them into '.;' and ';.'; # and winepath ignores them completely. func_stripname : : "$1" func_to_host_path_tmp1=$func_stripname_result func_convert_core_msys_to_w32 "$func_to_host_path_tmp1" func_to_host_path_result="$func_convert_core_msys_to_w32_result" func_convert_path_check : ";" \ "$func_to_host_path_tmp1" "$func_to_host_path_result" func_convert_path_front_back_pathsep ":*" "*:" ";" "$1" fi } # end func_convert_path_msys_to_w32 # func_convert_path_cygwin_to_w32 ARG # Convert path ARG from Cygwin to w32 format. Returns result in # func_to_host_file_result. func_convert_path_cygwin_to_w32 () { $opt_debug func_to_host_path_result="$1" if test -n "$1"; then # See func_convert_path_msys_to_w32: func_stripname : : "$1" func_to_host_path_tmp1=$func_stripname_result func_to_host_path_result=`cygpath -m -p "$func_to_host_path_tmp1"` func_convert_path_check : ";" \ "$func_to_host_path_tmp1" "$func_to_host_path_result" func_convert_path_front_back_pathsep ":*" "*:" ";" "$1" fi } # end func_convert_path_cygwin_to_w32 # func_convert_path_nix_to_w32 ARG # Convert path ARG from *nix to w32 format. Requires a wine environment and # a working winepath. Returns result in func_to_host_file_result. func_convert_path_nix_to_w32 () { $opt_debug func_to_host_path_result="$1" if test -n "$1"; then # See func_convert_path_msys_to_w32: func_stripname : : "$1" func_to_host_path_tmp1=$func_stripname_result func_convert_core_path_wine_to_w32 "$func_to_host_path_tmp1" func_to_host_path_result="$func_convert_core_path_wine_to_w32_result" func_convert_path_check : ";" \ "$func_to_host_path_tmp1" "$func_to_host_path_result" func_convert_path_front_back_pathsep ":*" "*:" ";" "$1" fi } # end func_convert_path_nix_to_w32 # func_convert_path_msys_to_cygwin ARG # Convert path ARG from MSYS to Cygwin format. Requires LT_CYGPATH set. # Returns result in func_to_host_file_result. func_convert_path_msys_to_cygwin () { $opt_debug func_to_host_path_result="$1" if test -n "$1"; then # See func_convert_path_msys_to_w32: func_stripname : : "$1" func_to_host_path_tmp1=$func_stripname_result func_convert_core_msys_to_w32 "$func_to_host_path_tmp1" func_cygpath -u -p "$func_convert_core_msys_to_w32_result" func_to_host_path_result="$func_cygpath_result" func_convert_path_check : : \ "$func_to_host_path_tmp1" "$func_to_host_path_result" func_convert_path_front_back_pathsep ":*" "*:" : "$1" fi } # end func_convert_path_msys_to_cygwin # func_convert_path_nix_to_cygwin ARG # Convert path ARG from *nix to Cygwin format. Requires Cygwin installed in a # a wine environment, working winepath, and LT_CYGPATH set. Returns result in # func_to_host_file_result. func_convert_path_nix_to_cygwin () { $opt_debug func_to_host_path_result="$1" if test -n "$1"; then # Remove leading and trailing path separator characters from # ARG. msys behavior is inconsistent here, cygpath turns them # into '.;' and ';.', and winepath ignores them completely. func_stripname : : "$1" func_to_host_path_tmp1=$func_stripname_result func_convert_core_path_wine_to_w32 "$func_to_host_path_tmp1" func_cygpath -u -p "$func_convert_core_path_wine_to_w32_result" func_to_host_path_result="$func_cygpath_result" func_convert_path_check : : \ "$func_to_host_path_tmp1" "$func_to_host_path_result" func_convert_path_front_back_pathsep ":*" "*:" : "$1" fi } # end func_convert_path_nix_to_cygwin # func_mode_compile arg... func_mode_compile () { $opt_debug # Get the compilation command and the source file. base_compile= srcfile="$nonopt" # always keep a non-empty value in "srcfile" suppress_opt=yes suppress_output= arg_mode=normal libobj= later= pie_flag= for arg do case $arg_mode in arg ) # do not "continue". Instead, add this to base_compile lastarg="$arg" arg_mode=normal ;; target ) libobj="$arg" arg_mode=normal continue ;; normal ) # Accept any command-line options. case $arg in -o) test -n "$libobj" && \ func_fatal_error "you cannot specify \`-o' more than once" arg_mode=target continue ;; -pie | -fpie | -fPIE) func_append pie_flag " $arg" continue ;; -shared | -static | -prefer-pic | -prefer-non-pic) func_append later " $arg" continue ;; -no-suppress) suppress_opt=no continue ;; -Xcompiler) arg_mode=arg # the next one goes into the "base_compile" arg list continue # The current "srcfile" will either be retained or ;; # replaced later. I would guess that would be a bug. -Wc,*) func_stripname '-Wc,' '' "$arg" args=$func_stripname_result lastarg= save_ifs="$IFS"; IFS=',' for arg in $args; do IFS="$save_ifs" func_append_quoted lastarg "$arg" done IFS="$save_ifs" func_stripname ' ' '' "$lastarg" lastarg=$func_stripname_result # Add the arguments to base_compile. func_append base_compile " $lastarg" continue ;; *) # Accept the current argument as the source file. # The previous "srcfile" becomes the current argument. # lastarg="$srcfile" srcfile="$arg" ;; esac # case $arg ;; esac # case $arg_mode # Aesthetically quote the previous argument. func_append_quoted base_compile "$lastarg" done # for arg case $arg_mode in arg) func_fatal_error "you must specify an argument for -Xcompile" ;; target) func_fatal_error "you must specify a target with \`-o'" ;; *) # Get the name of the library object. test -z "$libobj" && { func_basename "$srcfile" libobj="$func_basename_result" } ;; esac # Recognize several different file suffixes. # If the user specifies -o file.o, it is replaced with file.lo case $libobj in *.[cCFSifmso] | \ *.ada | *.adb | *.ads | *.asm | \ *.c++ | *.cc | *.ii | *.class | *.cpp | *.cxx | \ *.[fF][09]? | *.for | *.java | *.go | *.obj | *.sx | *.cu | *.cup) func_xform "$libobj" libobj=$func_xform_result ;; esac case $libobj in *.lo) func_lo2o "$libobj"; obj=$func_lo2o_result ;; *) func_fatal_error "cannot determine name of library object from \`$libobj'" ;; esac func_infer_tag $base_compile for arg in $later; do case $arg in -shared) test "$build_libtool_libs" != yes && \ func_fatal_configuration "can not build a shared library" build_old_libs=no continue ;; -static) build_libtool_libs=no build_old_libs=yes continue ;; -prefer-pic) pic_mode=yes continue ;; -prefer-non-pic) pic_mode=no continue ;; esac done func_quote_for_eval "$libobj" test "X$libobj" != "X$func_quote_for_eval_result" \ && $ECHO "X$libobj" | $GREP '[]~#^*{};<>?"'"'"' &()|`$[]' \ && func_warning "libobj name \`$libobj' may not contain shell special characters." func_dirname_and_basename "$obj" "/" "" objname="$func_basename_result" xdir="$func_dirname_result" lobj=${xdir}$objdir/$objname test -z "$base_compile" && \ func_fatal_help "you must specify a compilation command" # Delete any leftover library objects. if test "$build_old_libs" = yes; then removelist="$obj $lobj $libobj ${libobj}T" else removelist="$lobj $libobj ${libobj}T" fi # On Cygwin there's no "real" PIC flag so we must build both object types case $host_os in cygwin* | mingw* | pw32* | os2* | cegcc*) pic_mode=default ;; esac if test "$pic_mode" = no && test "$deplibs_check_method" != pass_all; then # non-PIC code in shared libraries is not supported pic_mode=default fi # Calculate the filename of the output object if compiler does # not support -o with -c if test "$compiler_c_o" = no; then output_obj=`$ECHO "$srcfile" | $SED 's%^.*/%%; s%\.[^.]*$%%'`.${objext} lockfile="$output_obj.lock" else output_obj= need_locks=no lockfile= fi # Lock this critical section if it is needed # We use this script file to make the link, it avoids creating a new file if test "$need_locks" = yes; then until $opt_dry_run || ln "$progpath" "$lockfile" 2>/dev/null; do func_echo "Waiting for $lockfile to be removed" sleep 2 done elif test "$need_locks" = warn; then if test -f "$lockfile"; then $ECHO "\ *** ERROR, $lockfile exists and contains: `cat $lockfile 2>/dev/null` This indicates that another process is trying to use the same temporary object file, and libtool could not work around it because your compiler does not support \`-c' and \`-o' together. If you repeat this compilation, it may succeed, by chance, but you had better avoid parallel builds (make -j) in this platform, or get a better compiler." $opt_dry_run || $RM $removelist exit $EXIT_FAILURE fi func_append removelist " $output_obj" $ECHO "$srcfile" > "$lockfile" fi $opt_dry_run || $RM $removelist func_append removelist " $lockfile" trap '$opt_dry_run || $RM $removelist; exit $EXIT_FAILURE' 1 2 15 func_to_tool_file "$srcfile" func_convert_file_msys_to_w32 srcfile=$func_to_tool_file_result func_quote_for_eval "$srcfile" qsrcfile=$func_quote_for_eval_result # Only build a PIC object if we are building libtool libraries. if test "$build_libtool_libs" = yes; then # Without this assignment, base_compile gets emptied. fbsd_hideous_sh_bug=$base_compile if test "$pic_mode" != no; then command="$base_compile $qsrcfile $pic_flag" else # Don't build PIC code command="$base_compile $qsrcfile" fi func_mkdir_p "$xdir$objdir" if test -z "$output_obj"; then # Place PIC objects in $objdir func_append command " -o $lobj" fi func_show_eval_locale "$command" \ 'test -n "$output_obj" && $RM $removelist; exit $EXIT_FAILURE' if test "$need_locks" = warn && test "X`cat $lockfile 2>/dev/null`" != "X$srcfile"; then $ECHO "\ *** ERROR, $lockfile contains: `cat $lockfile 2>/dev/null` but it should contain: $srcfile This indicates that another process is trying to use the same temporary object file, and libtool could not work around it because your compiler does not support \`-c' and \`-o' together. If you repeat this compilation, it may succeed, by chance, but you had better avoid parallel builds (make -j) in this platform, or get a better compiler." $opt_dry_run || $RM $removelist exit $EXIT_FAILURE fi # Just move the object if needed, then go on to compile the next one if test -n "$output_obj" && test "X$output_obj" != "X$lobj"; then func_show_eval '$MV "$output_obj" "$lobj"' \ 'error=$?; $opt_dry_run || $RM $removelist; exit $error' fi # Allow error messages only from the first compilation. if test "$suppress_opt" = yes; then suppress_output=' >/dev/null 2>&1' fi fi # Only build a position-dependent object if we build old libraries. if test "$build_old_libs" = yes; then if test "$pic_mode" != yes; then # Don't build PIC code command="$base_compile $qsrcfile$pie_flag" else command="$base_compile $qsrcfile $pic_flag" fi if test "$compiler_c_o" = yes; then func_append command " -o $obj" fi # Suppress compiler output if we already did a PIC compilation. func_append command "$suppress_output" func_show_eval_locale "$command" \ '$opt_dry_run || $RM $removelist; exit $EXIT_FAILURE' if test "$need_locks" = warn && test "X`cat $lockfile 2>/dev/null`" != "X$srcfile"; then $ECHO "\ *** ERROR, $lockfile contains: `cat $lockfile 2>/dev/null` but it should contain: $srcfile This indicates that another process is trying to use the same temporary object file, and libtool could not work around it because your compiler does not support \`-c' and \`-o' together. If you repeat this compilation, it may succeed, by chance, but you had better avoid parallel builds (make -j) in this platform, or get a better compiler." $opt_dry_run || $RM $removelist exit $EXIT_FAILURE fi # Just move the object if needed if test -n "$output_obj" && test "X$output_obj" != "X$obj"; then func_show_eval '$MV "$output_obj" "$obj"' \ 'error=$?; $opt_dry_run || $RM $removelist; exit $error' fi fi $opt_dry_run || { func_write_libtool_object "$libobj" "$objdir/$objname" "$objname" # Unlock the critical section if it was locked if test "$need_locks" != no; then removelist=$lockfile $RM "$lockfile" fi } exit $EXIT_SUCCESS } $opt_help || { test "$opt_mode" = compile && func_mode_compile ${1+"$@"} } func_mode_help () { # We need to display help for each of the modes. case $opt_mode in "") # Generic help is extracted from the usage comments # at the start of this file. func_help ;; clean) $ECHO \ "Usage: $progname [OPTION]... --mode=clean RM [RM-OPTION]... FILE... Remove files from the build directory. RM is the name of the program to use to delete files associated with each FILE (typically \`/bin/rm'). RM-OPTIONS are options (such as \`-f') to be passed to RM. If FILE is a libtool library, object or program, all the files associated with it are deleted. Otherwise, only FILE itself is deleted using RM." ;; compile) $ECHO \ "Usage: $progname [OPTION]... --mode=compile COMPILE-COMMAND... SOURCEFILE Compile a source file into a libtool library object. This mode accepts the following additional options: -o OUTPUT-FILE set the output file name to OUTPUT-FILE -no-suppress do not suppress compiler output for multiple passes -prefer-pic try to build PIC objects only -prefer-non-pic try to build non-PIC objects only -shared do not build a \`.o' file suitable for static linking -static only build a \`.o' file suitable for static linking -Wc,FLAG pass FLAG directly to the compiler COMPILE-COMMAND is a command to be used in creating a \`standard' object file from the given SOURCEFILE. The output file name is determined by removing the directory component from SOURCEFILE, then substituting the C source code suffix \`.c' with the library object suffix, \`.lo'." ;; execute) $ECHO \ "Usage: $progname [OPTION]... --mode=execute COMMAND [ARGS]... Automatically set library path, then run a program. This mode accepts the following additional options: -dlopen FILE add the directory containing FILE to the library path This mode sets the library path environment variable according to \`-dlopen' flags. If any of the ARGS are libtool executable wrappers, then they are translated into their corresponding uninstalled binary, and any of their required library directories are added to the library path. Then, COMMAND is executed, with ARGS as arguments." ;; finish) $ECHO \ "Usage: $progname [OPTION]... --mode=finish [LIBDIR]... Complete the installation of libtool libraries. Each LIBDIR is a directory that contains libtool libraries. The commands that this mode executes may require superuser privileges. Use the \`--dry-run' option if you just want to see what would be executed." ;; install) $ECHO \ "Usage: $progname [OPTION]... --mode=install INSTALL-COMMAND... Install executables or libraries. INSTALL-COMMAND is the installation command. The first component should be either the \`install' or \`cp' program. The following components of INSTALL-COMMAND are treated specially: -inst-prefix-dir PREFIX-DIR Use PREFIX-DIR as a staging area for installation The rest of the components are interpreted as arguments to that command (only BSD-compatible install options are recognized)." ;; link) $ECHO \ "Usage: $progname [OPTION]... --mode=link LINK-COMMAND... Link object files or libraries together to form another library, or to create an executable program. LINK-COMMAND is a command using the C compiler that you would use to create a program from several object files. The following components of LINK-COMMAND are treated specially: -all-static do not do any dynamic linking at all -avoid-version do not add a version suffix if possible -bindir BINDIR specify path to binaries directory (for systems where libraries must be found in the PATH setting at runtime) -dlopen FILE \`-dlpreopen' FILE if it cannot be dlopened at runtime -dlpreopen FILE link in FILE and add its symbols to lt_preloaded_symbols -export-dynamic allow symbols from OUTPUT-FILE to be resolved with dlsym(3) -export-symbols SYMFILE try to export only the symbols listed in SYMFILE -export-symbols-regex REGEX try to export only the symbols matching REGEX -LLIBDIR search LIBDIR for required installed libraries -lNAME OUTPUT-FILE requires the installed library libNAME -module build a library that can dlopened -no-fast-install disable the fast-install mode -no-install link a not-installable executable -no-undefined declare that a library does not refer to external symbols -o OUTPUT-FILE create OUTPUT-FILE from the specified objects -objectlist FILE Use a list of object files found in FILE to specify objects -precious-files-regex REGEX don't remove output files matching REGEX -release RELEASE specify package release information -rpath LIBDIR the created library will eventually be installed in LIBDIR -R[ ]LIBDIR add LIBDIR to the runtime path of programs and libraries -shared only do dynamic linking of libtool libraries -shrext SUFFIX override the standard shared library file extension -static do not do any dynamic linking of uninstalled libtool libraries -static-libtool-libs do not do any dynamic linking of libtool libraries -version-info CURRENT[:REVISION[:AGE]] specify library version info [each variable defaults to 0] -weak LIBNAME declare that the target provides the LIBNAME interface -Wc,FLAG -Xcompiler FLAG pass linker-specific FLAG directly to the compiler -Wl,FLAG -Xlinker FLAG pass linker-specific FLAG directly to the linker -XCClinker FLAG pass link-specific FLAG to the compiler driver (CC) All other options (arguments beginning with \`-') are ignored. Every other argument is treated as a filename. Files ending in \`.la' are treated as uninstalled libtool libraries, other files are standard or library object files. If the OUTPUT-FILE ends in \`.la', then a libtool library is created, only library objects (\`.lo' files) may be specified, and \`-rpath' is required, except when creating a convenience library. If OUTPUT-FILE ends in \`.a' or \`.lib', then a standard library is created using \`ar' and \`ranlib', or on Windows using \`lib'. If OUTPUT-FILE ends in \`.lo' or \`.${objext}', then a reloadable object file is created, otherwise an executable program is created." ;; uninstall) $ECHO \ "Usage: $progname [OPTION]... --mode=uninstall RM [RM-OPTION]... FILE... Remove libraries from an installation directory. RM is the name of the program to use to delete files associated with each FILE (typically \`/bin/rm'). RM-OPTIONS are options (such as \`-f') to be passed to RM. If FILE is a libtool library, all the files associated with it are deleted. Otherwise, only FILE itself is deleted using RM." ;; *) func_fatal_help "invalid operation mode \`$opt_mode'" ;; esac echo $ECHO "Try \`$progname --help' for more information about other modes." } # Now that we've collected a possible --mode arg, show help if necessary if $opt_help; then if test "$opt_help" = :; then func_mode_help else { func_help noexit for opt_mode in compile link execute install finish uninstall clean; do func_mode_help done } | sed -n '1p; 2,$s/^Usage:/ or: /p' { func_help noexit for opt_mode in compile link execute install finish uninstall clean; do echo func_mode_help done } | sed '1d /^When reporting/,/^Report/{ H d } $x /information about other modes/d /more detailed .*MODE/d s/^Usage:.*--mode=\([^ ]*\) .*/Description of \1 mode:/' fi exit $? fi # func_mode_execute arg... func_mode_execute () { $opt_debug # The first argument is the command name. cmd="$nonopt" test -z "$cmd" && \ func_fatal_help "you must specify a COMMAND" # Handle -dlopen flags immediately. for file in $opt_dlopen; do test -f "$file" \ || func_fatal_help "\`$file' is not a file" dir= case $file in *.la) func_resolve_sysroot "$file" file=$func_resolve_sysroot_result # Check to see that this really is a libtool archive. func_lalib_unsafe_p "$file" \ || func_fatal_help "\`$lib' is not a valid libtool archive" # Read the libtool library. dlname= library_names= func_source "$file" # Skip this library if it cannot be dlopened. if test -z "$dlname"; then # Warn if it was a shared library. test -n "$library_names" && \ func_warning "\`$file' was not linked with \`-export-dynamic'" continue fi func_dirname "$file" "" "." dir="$func_dirname_result" if test -f "$dir/$objdir/$dlname"; then func_append dir "/$objdir" else if test ! -f "$dir/$dlname"; then func_fatal_error "cannot find \`$dlname' in \`$dir' or \`$dir/$objdir'" fi fi ;; *.lo) # Just add the directory containing the .lo file. func_dirname "$file" "" "." dir="$func_dirname_result" ;; *) func_warning "\`-dlopen' is ignored for non-libtool libraries and objects" continue ;; esac # Get the absolute pathname. absdir=`cd "$dir" && pwd` test -n "$absdir" && dir="$absdir" # Now add the directory to shlibpath_var. if eval "test -z \"\$$shlibpath_var\""; then eval "$shlibpath_var=\"\$dir\"" else eval "$shlibpath_var=\"\$dir:\$$shlibpath_var\"" fi done # This variable tells wrapper scripts just to set shlibpath_var # rather than running their programs. libtool_execute_magic="$magic" # Check if any of the arguments is a wrapper script. args= for file do case $file in -* | *.la | *.lo ) ;; *) # Do a test to see if this is really a libtool program. if func_ltwrapper_script_p "$file"; then func_source "$file" # Transform arg to wrapped name. file="$progdir/$program" elif func_ltwrapper_executable_p "$file"; then func_ltwrapper_scriptname "$file" func_source "$func_ltwrapper_scriptname_result" # Transform arg to wrapped name. file="$progdir/$program" fi ;; esac # Quote arguments (to preserve shell metacharacters). func_append_quoted args "$file" done if test "X$opt_dry_run" = Xfalse; then if test -n "$shlibpath_var"; then # Export the shlibpath_var. eval "export $shlibpath_var" fi # Restore saved environment variables for lt_var in LANG LANGUAGE LC_ALL LC_CTYPE LC_COLLATE LC_MESSAGES do eval "if test \"\${save_$lt_var+set}\" = set; then $lt_var=\$save_$lt_var; export $lt_var else $lt_unset $lt_var fi" done # Now prepare to actually exec the command. exec_cmd="\$cmd$args" else # Display what would be done. if test -n "$shlibpath_var"; then eval "\$ECHO \"\$shlibpath_var=\$$shlibpath_var\"" echo "export $shlibpath_var" fi $ECHO "$cmd$args" exit $EXIT_SUCCESS fi } test "$opt_mode" = execute && func_mode_execute ${1+"$@"} # func_mode_finish arg... func_mode_finish () { $opt_debug libs= libdirs= admincmds= for opt in "$nonopt" ${1+"$@"} do if test -d "$opt"; then func_append libdirs " $opt" elif test -f "$opt"; then if func_lalib_unsafe_p "$opt"; then func_append libs " $opt" else func_warning "\`$opt' is not a valid libtool archive" fi else func_fatal_error "invalid argument \`$opt'" fi done if test -n "$libs"; then if test -n "$lt_sysroot"; then sysroot_regex=`$ECHO "$lt_sysroot" | $SED "$sed_make_literal_regex"` sysroot_cmd="s/\([ ']\)$sysroot_regex/\1/g;" else sysroot_cmd= fi # Remove sysroot references if $opt_dry_run; then for lib in $libs; do echo "removing references to $lt_sysroot and \`=' prefixes from $lib" done else tmpdir=`func_mktempdir` for lib in $libs; do sed -e "${sysroot_cmd} s/\([ ']-[LR]\)=/\1/g; s/\([ ']\)=/\1/g" $lib \ > $tmpdir/tmp-la mv -f $tmpdir/tmp-la $lib done ${RM}r "$tmpdir" fi fi if test -n "$finish_cmds$finish_eval" && test -n "$libdirs"; then for libdir in $libdirs; do if test -n "$finish_cmds"; then # Do each command in the finish commands. func_execute_cmds "$finish_cmds" 'admincmds="$admincmds '"$cmd"'"' fi if test -n "$finish_eval"; then # Do the single finish_eval. eval cmds=\"$finish_eval\" $opt_dry_run || eval "$cmds" || func_append admincmds " $cmds" fi done fi # Exit here if they wanted silent mode. $opt_silent && exit $EXIT_SUCCESS if test -n "$finish_cmds$finish_eval" && test -n "$libdirs"; then echo "----------------------------------------------------------------------" echo "Libraries have been installed in:" for libdir in $libdirs; do $ECHO " $libdir" done echo echo "If you ever happen to want to link against installed libraries" echo "in a given directory, LIBDIR, you must either use libtool, and" echo "specify the full pathname of the library, or use the \`-LLIBDIR'" echo "flag during linking and do at least one of the following:" if test -n "$shlibpath_var"; then echo " - add LIBDIR to the \`$shlibpath_var' environment variable" echo " during execution" fi if test -n "$runpath_var"; then echo " - add LIBDIR to the \`$runpath_var' environment variable" echo " during linking" fi if test -n "$hardcode_libdir_flag_spec"; then libdir=LIBDIR eval flag=\"$hardcode_libdir_flag_spec\" $ECHO " - use the \`$flag' linker flag" fi if test -n "$admincmds"; then $ECHO " - have your system administrator run these commands:$admincmds" fi if test -f /etc/ld.so.conf; then echo " - have your system administrator add LIBDIR to \`/etc/ld.so.conf'" fi echo echo "See any operating system documentation about shared libraries for" case $host in solaris2.[6789]|solaris2.1[0-9]) echo "more information, such as the ld(1), crle(1) and ld.so(8) manual" echo "pages." ;; *) echo "more information, such as the ld(1) and ld.so(8) manual pages." ;; esac echo "----------------------------------------------------------------------" fi exit $EXIT_SUCCESS } test "$opt_mode" = finish && func_mode_finish ${1+"$@"} # func_mode_install arg... func_mode_install () { $opt_debug # There may be an optional sh(1) argument at the beginning of # install_prog (especially on Windows NT). if test "$nonopt" = "$SHELL" || test "$nonopt" = /bin/sh || # Allow the use of GNU shtool's install command. case $nonopt in *shtool*) :;; *) false;; esac; then # Aesthetically quote it. func_quote_for_eval "$nonopt" install_prog="$func_quote_for_eval_result " arg=$1 shift else install_prog= arg=$nonopt fi # The real first argument should be the name of the installation program. # Aesthetically quote it. func_quote_for_eval "$arg" func_append install_prog "$func_quote_for_eval_result" install_shared_prog=$install_prog case " $install_prog " in *[\\\ /]cp\ *) install_cp=: ;; *) install_cp=false ;; esac # We need to accept at least all the BSD install flags. dest= files= opts= prev= install_type= isdir=no stripme= no_mode=: for arg do arg2= if test -n "$dest"; then func_append files " $dest" dest=$arg continue fi case $arg in -d) isdir=yes ;; -f) if $install_cp; then :; else prev=$arg fi ;; -g | -m | -o) prev=$arg ;; -s) stripme=" -s" continue ;; -*) ;; *) # If the previous option needed an argument, then skip it. if test -n "$prev"; then if test "x$prev" = x-m && test -n "$install_override_mode"; then arg2=$install_override_mode no_mode=false fi prev= else dest=$arg continue fi ;; esac # Aesthetically quote the argument. func_quote_for_eval "$arg" func_append install_prog " $func_quote_for_eval_result" if test -n "$arg2"; then func_quote_for_eval "$arg2" fi func_append install_shared_prog " $func_quote_for_eval_result" done test -z "$install_prog" && \ func_fatal_help "you must specify an install program" test -n "$prev" && \ func_fatal_help "the \`$prev' option requires an argument" if test -n "$install_override_mode" && $no_mode; then if $install_cp; then :; else func_quote_for_eval "$install_override_mode" func_append install_shared_prog " -m $func_quote_for_eval_result" fi fi if test -z "$files"; then if test -z "$dest"; then func_fatal_help "no file or destination specified" else func_fatal_help "you must specify a destination" fi fi # Strip any trailing slash from the destination. func_stripname '' '/' "$dest" dest=$func_stripname_result # Check to see that the destination is a directory. test -d "$dest" && isdir=yes if test "$isdir" = yes; then destdir="$dest" destname= else func_dirname_and_basename "$dest" "" "." destdir="$func_dirname_result" destname="$func_basename_result" # Not a directory, so check to see that there is only one file specified. set dummy $files; shift test "$#" -gt 1 && \ func_fatal_help "\`$dest' is not a directory" fi case $destdir in [\\/]* | [A-Za-z]:[\\/]*) ;; *) for file in $files; do case $file in *.lo) ;; *) func_fatal_help "\`$destdir' must be an absolute directory name" ;; esac done ;; esac # This variable tells wrapper scripts just to set variables rather # than running their programs. libtool_install_magic="$magic" staticlibs= future_libdirs= current_libdirs= for file in $files; do # Do each installation. case $file in *.$libext) # Do the static libraries later. func_append staticlibs " $file" ;; *.la) func_resolve_sysroot "$file" file=$func_resolve_sysroot_result # Check to see that this really is a libtool archive. func_lalib_unsafe_p "$file" \ || func_fatal_help "\`$file' is not a valid libtool archive" library_names= old_library= relink_command= func_source "$file" # Add the libdir to current_libdirs if it is the destination. if test "X$destdir" = "X$libdir"; then case "$current_libdirs " in *" $libdir "*) ;; *) func_append current_libdirs " $libdir" ;; esac else # Note the libdir as a future libdir. case "$future_libdirs " in *" $libdir "*) ;; *) func_append future_libdirs " $libdir" ;; esac fi func_dirname "$file" "/" "" dir="$func_dirname_result" func_append dir "$objdir" if test -n "$relink_command"; then # Determine the prefix the user has applied to our future dir. inst_prefix_dir=`$ECHO "$destdir" | $SED -e "s%$libdir\$%%"` # Don't allow the user to place us outside of our expected # location b/c this prevents finding dependent libraries that # are installed to the same prefix. # At present, this check doesn't affect windows .dll's that # are installed into $libdir/../bin (currently, that works fine) # but it's something to keep an eye on. test "$inst_prefix_dir" = "$destdir" && \ func_fatal_error "error: cannot install \`$file' to a directory not ending in $libdir" if test -n "$inst_prefix_dir"; then # Stick the inst_prefix_dir data into the link command. relink_command=`$ECHO "$relink_command" | $SED "s%@inst_prefix_dir@%-inst-prefix-dir $inst_prefix_dir%"` else relink_command=`$ECHO "$relink_command" | $SED "s%@inst_prefix_dir@%%"` fi func_warning "relinking \`$file'" func_show_eval "$relink_command" \ 'func_fatal_error "error: relink \`$file'\'' with the above command before installing it"' fi # See the names of the shared library. set dummy $library_names; shift if test -n "$1"; then realname="$1" shift srcname="$realname" test -n "$relink_command" && srcname="$realname"T # Install the shared library and build the symlinks. func_show_eval "$install_shared_prog $dir/$srcname $destdir/$realname" \ 'exit $?' tstripme="$stripme" case $host_os in cygwin* | mingw* | pw32* | cegcc*) case $realname in *.dll.a) tstripme="" ;; esac ;; esac if test -n "$tstripme" && test -n "$striplib"; then func_show_eval "$striplib $destdir/$realname" 'exit $?' fi if test "$#" -gt 0; then # Delete the old symlinks, and create new ones. # Try `ln -sf' first, because the `ln' binary might depend on # the symlink we replace! Solaris /bin/ln does not understand -f, # so we also need to try rm && ln -s. for linkname do test "$linkname" != "$realname" \ && func_show_eval "(cd $destdir && { $LN_S -f $realname $linkname || { $RM $linkname && $LN_S $realname $linkname; }; })" done fi # Do each command in the postinstall commands. lib="$destdir/$realname" func_execute_cmds "$postinstall_cmds" 'exit $?' fi # Install the pseudo-library for information purposes. func_basename "$file" name="$func_basename_result" instname="$dir/$name"i func_show_eval "$install_prog $instname $destdir/$name" 'exit $?' # Maybe install the static library, too. test -n "$old_library" && func_append staticlibs " $dir/$old_library" ;; *.lo) # Install (i.e. copy) a libtool object. # Figure out destination file name, if it wasn't already specified. if test -n "$destname"; then destfile="$destdir/$destname" else func_basename "$file" destfile="$func_basename_result" destfile="$destdir/$destfile" fi # Deduce the name of the destination old-style object file. case $destfile in *.lo) func_lo2o "$destfile" staticdest=$func_lo2o_result ;; *.$objext) staticdest="$destfile" destfile= ;; *) func_fatal_help "cannot copy a libtool object to \`$destfile'" ;; esac # Install the libtool object if requested. test -n "$destfile" && \ func_show_eval "$install_prog $file $destfile" 'exit $?' # Install the old object if enabled. if test "$build_old_libs" = yes; then # Deduce the name of the old-style object file. func_lo2o "$file" staticobj=$func_lo2o_result func_show_eval "$install_prog \$staticobj \$staticdest" 'exit $?' fi exit $EXIT_SUCCESS ;; *) # Figure out destination file name, if it wasn't already specified. if test -n "$destname"; then destfile="$destdir/$destname" else func_basename "$file" destfile="$func_basename_result" destfile="$destdir/$destfile" fi # If the file is missing, and there is a .exe on the end, strip it # because it is most likely a libtool script we actually want to # install stripped_ext="" case $file in *.exe) if test ! -f "$file"; then func_stripname '' '.exe' "$file" file=$func_stripname_result stripped_ext=".exe" fi ;; esac # Do a test to see if this is really a libtool program. case $host in *cygwin* | *mingw*) if func_ltwrapper_executable_p "$file"; then func_ltwrapper_scriptname "$file" wrapper=$func_ltwrapper_scriptname_result else func_stripname '' '.exe' "$file" wrapper=$func_stripname_result fi ;; *) wrapper=$file ;; esac if func_ltwrapper_script_p "$wrapper"; then notinst_deplibs= relink_command= func_source "$wrapper" # Check the variables that should have been set. test -z "$generated_by_libtool_version" && \ func_fatal_error "invalid libtool wrapper script \`$wrapper'" finalize=yes for lib in $notinst_deplibs; do # Check to see that each library is installed. libdir= if test -f "$lib"; then func_source "$lib" fi libfile="$libdir/"`$ECHO "$lib" | $SED 's%^.*/%%g'` ### testsuite: skip nested quoting test if test -n "$libdir" && test ! -f "$libfile"; then func_warning "\`$lib' has not been installed in \`$libdir'" finalize=no fi done relink_command= func_source "$wrapper" outputname= if test "$fast_install" = no && test -n "$relink_command"; then $opt_dry_run || { if test "$finalize" = yes; then tmpdir=`func_mktempdir` func_basename "$file$stripped_ext" file="$func_basename_result" outputname="$tmpdir/$file" # Replace the output file specification. relink_command=`$ECHO "$relink_command" | $SED 's%@OUTPUT@%'"$outputname"'%g'` $opt_silent || { func_quote_for_expand "$relink_command" eval "func_echo $func_quote_for_expand_result" } if eval "$relink_command"; then : else func_error "error: relink \`$file' with the above command before installing it" $opt_dry_run || ${RM}r "$tmpdir" continue fi file="$outputname" else func_warning "cannot relink \`$file'" fi } else # Install the binary that we compiled earlier. file=`$ECHO "$file$stripped_ext" | $SED "s%\([^/]*\)$%$objdir/\1%"` fi fi # remove .exe since cygwin /usr/bin/install will append another # one anyway case $install_prog,$host in */usr/bin/install*,*cygwin*) case $file:$destfile in *.exe:*.exe) # this is ok ;; *.exe:*) destfile=$destfile.exe ;; *:*.exe) func_stripname '' '.exe' "$destfile" destfile=$func_stripname_result ;; esac ;; esac func_show_eval "$install_prog\$stripme \$file \$destfile" 'exit $?' $opt_dry_run || if test -n "$outputname"; then ${RM}r "$tmpdir" fi ;; esac done for file in $staticlibs; do func_basename "$file" name="$func_basename_result" # Set up the ranlib parameters. oldlib="$destdir/$name" func_to_tool_file "$oldlib" func_convert_file_msys_to_w32 tool_oldlib=$func_to_tool_file_result func_show_eval "$install_prog \$file \$oldlib" 'exit $?' if test -n "$stripme" && test -n "$old_striplib"; then func_show_eval "$old_striplib $tool_oldlib" 'exit $?' fi # Do each command in the postinstall commands. func_execute_cmds "$old_postinstall_cmds" 'exit $?' done test -n "$future_libdirs" && \ func_warning "remember to run \`$progname --finish$future_libdirs'" if test -n "$current_libdirs"; then # Maybe just do a dry run. $opt_dry_run && current_libdirs=" -n$current_libdirs" exec_cmd='$SHELL $progpath $preserve_args --finish$current_libdirs' else exit $EXIT_SUCCESS fi } test "$opt_mode" = install && func_mode_install ${1+"$@"} # func_generate_dlsyms outputname originator pic_p # Extract symbols from dlprefiles and create ${outputname}S.o with # a dlpreopen symbol table. func_generate_dlsyms () { $opt_debug my_outputname="$1" my_originator="$2" my_pic_p="${3-no}" my_prefix=`$ECHO "$my_originator" | sed 's%[^a-zA-Z0-9]%_%g'` my_dlsyms= if test -n "$dlfiles$dlprefiles" || test "$dlself" != no; then if test -n "$NM" && test -n "$global_symbol_pipe"; then my_dlsyms="${my_outputname}S.c" else func_error "not configured to extract global symbols from dlpreopened files" fi fi if test -n "$my_dlsyms"; then case $my_dlsyms in "") ;; *.c) # Discover the nlist of each of the dlfiles. nlist="$output_objdir/${my_outputname}.nm" func_show_eval "$RM $nlist ${nlist}S ${nlist}T" # Parse the name list into a source file. func_verbose "creating $output_objdir/$my_dlsyms" $opt_dry_run || $ECHO > "$output_objdir/$my_dlsyms" "\ /* $my_dlsyms - symbol resolution table for \`$my_outputname' dlsym emulation. */ /* Generated by $PROGRAM (GNU $PACKAGE$TIMESTAMP) $VERSION */ #ifdef __cplusplus extern \"C\" { #endif #if defined(__GNUC__) && (((__GNUC__ == 4) && (__GNUC_MINOR__ >= 4)) || (__GNUC__ > 4)) #pragma GCC diagnostic ignored \"-Wstrict-prototypes\" #endif /* Keep this code in sync between libtool.m4, ltmain, lt_system.h, and tests. */ #if defined(_WIN32) || defined(__CYGWIN__) || defined(_WIN32_WCE) /* DATA imports from DLLs on WIN32 con't be const, because runtime relocations are performed -- see ld's documentation on pseudo-relocs. */ # define LT_DLSYM_CONST #elif defined(__osf__) /* This system does not cope well with relocations in const data. */ # define LT_DLSYM_CONST #else # define LT_DLSYM_CONST const #endif /* External symbol declarations for the compiler. */\ " if test "$dlself" = yes; then func_verbose "generating symbol list for \`$output'" $opt_dry_run || echo ': @PROGRAM@ ' > "$nlist" # Add our own program objects to the symbol list. progfiles=`$ECHO "$objs$old_deplibs" | $SP2NL | $SED "$lo2o" | $NL2SP` for progfile in $progfiles; do func_to_tool_file "$progfile" func_convert_file_msys_to_w32 func_verbose "extracting global C symbols from \`$func_to_tool_file_result'" $opt_dry_run || eval "$NM $func_to_tool_file_result | $global_symbol_pipe >> '$nlist'" done if test -n "$exclude_expsyms"; then $opt_dry_run || { eval '$EGREP -v " ($exclude_expsyms)$" "$nlist" > "$nlist"T' eval '$MV "$nlist"T "$nlist"' } fi if test -n "$export_symbols_regex"; then $opt_dry_run || { eval '$EGREP -e "$export_symbols_regex" "$nlist" > "$nlist"T' eval '$MV "$nlist"T "$nlist"' } fi # Prepare the list of exported symbols if test -z "$export_symbols"; then export_symbols="$output_objdir/$outputname.exp" $opt_dry_run || { $RM $export_symbols eval "${SED} -n -e '/^: @PROGRAM@ $/d' -e 's/^.* \(.*\)$/\1/p' "'< "$nlist" > "$export_symbols"' case $host in *cygwin* | *mingw* | *cegcc* ) eval "echo EXPORTS "'> "$output_objdir/$outputname.def"' eval 'cat "$export_symbols" >> "$output_objdir/$outputname.def"' ;; esac } else $opt_dry_run || { eval "${SED} -e 's/\([].[*^$]\)/\\\\\1/g' -e 's/^/ /' -e 's/$/$/'"' < "$export_symbols" > "$output_objdir/$outputname.exp"' eval '$GREP -f "$output_objdir/$outputname.exp" < "$nlist" > "$nlist"T' eval '$MV "$nlist"T "$nlist"' case $host in *cygwin* | *mingw* | *cegcc* ) eval "echo EXPORTS "'> "$output_objdir/$outputname.def"' eval 'cat "$nlist" >> "$output_objdir/$outputname.def"' ;; esac } fi fi for dlprefile in $dlprefiles; do func_verbose "extracting global C symbols from \`$dlprefile'" func_basename "$dlprefile" name="$func_basename_result" case $host in *cygwin* | *mingw* | *cegcc* ) # if an import library, we need to obtain dlname if func_win32_import_lib_p "$dlprefile"; then func_tr_sh "$dlprefile" eval "curr_lafile=\$libfile_$func_tr_sh_result" dlprefile_dlbasename="" if test -n "$curr_lafile" && func_lalib_p "$curr_lafile"; then # Use subshell, to avoid clobbering current variable values dlprefile_dlname=`source "$curr_lafile" && echo "$dlname"` if test -n "$dlprefile_dlname" ; then func_basename "$dlprefile_dlname" dlprefile_dlbasename="$func_basename_result" else # no lafile. user explicitly requested -dlpreopen . $sharedlib_from_linklib_cmd "$dlprefile" dlprefile_dlbasename=$sharedlib_from_linklib_result fi fi $opt_dry_run || { if test -n "$dlprefile_dlbasename" ; then eval '$ECHO ": $dlprefile_dlbasename" >> "$nlist"' else func_warning "Could not compute DLL name from $name" eval '$ECHO ": $name " >> "$nlist"' fi func_to_tool_file "$dlprefile" func_convert_file_msys_to_w32 eval "$NM \"$func_to_tool_file_result\" 2>/dev/null | $global_symbol_pipe | $SED -e '/I __imp/d' -e 's/I __nm_/D /;s/_nm__//' >> '$nlist'" } else # not an import lib $opt_dry_run || { eval '$ECHO ": $name " >> "$nlist"' func_to_tool_file "$dlprefile" func_convert_file_msys_to_w32 eval "$NM \"$func_to_tool_file_result\" 2>/dev/null | $global_symbol_pipe >> '$nlist'" } fi ;; *) $opt_dry_run || { eval '$ECHO ": $name " >> "$nlist"' func_to_tool_file "$dlprefile" func_convert_file_msys_to_w32 eval "$NM \"$func_to_tool_file_result\" 2>/dev/null | $global_symbol_pipe >> '$nlist'" } ;; esac done $opt_dry_run || { # Make sure we have at least an empty file. test -f "$nlist" || : > "$nlist" if test -n "$exclude_expsyms"; then $EGREP -v " ($exclude_expsyms)$" "$nlist" > "$nlist"T $MV "$nlist"T "$nlist" fi # Try sorting and uniquifying the output. if $GREP -v "^: " < "$nlist" | if sort -k 3 /dev/null 2>&1; then sort -k 3 else sort +2 fi | uniq > "$nlist"S; then : else $GREP -v "^: " < "$nlist" > "$nlist"S fi if test -f "$nlist"S; then eval "$global_symbol_to_cdecl"' < "$nlist"S >> "$output_objdir/$my_dlsyms"' else echo '/* NONE */' >> "$output_objdir/$my_dlsyms" fi echo >> "$output_objdir/$my_dlsyms" "\ /* The mapping between symbol names and symbols. */ typedef struct { const char *name; void *address; } lt_dlsymlist; extern LT_DLSYM_CONST lt_dlsymlist lt_${my_prefix}_LTX_preloaded_symbols[]; LT_DLSYM_CONST lt_dlsymlist lt_${my_prefix}_LTX_preloaded_symbols[] = {\ { \"$my_originator\", (void *) 0 }," case $need_lib_prefix in no) eval "$global_symbol_to_c_name_address" < "$nlist" >> "$output_objdir/$my_dlsyms" ;; *) eval "$global_symbol_to_c_name_address_lib_prefix" < "$nlist" >> "$output_objdir/$my_dlsyms" ;; esac echo >> "$output_objdir/$my_dlsyms" "\ {0, (void *) 0} }; /* This works around a problem in FreeBSD linker */ #ifdef FREEBSD_WORKAROUND static const void *lt_preloaded_setup() { return lt_${my_prefix}_LTX_preloaded_symbols; } #endif #ifdef __cplusplus } #endif\ " } # !$opt_dry_run pic_flag_for_symtable= case "$compile_command " in *" -static "*) ;; *) case $host in # compiling the symbol table file with pic_flag works around # a FreeBSD bug that causes programs to crash when -lm is # linked before any other PIC object. But we must not use # pic_flag when linking with -static. The problem exists in # FreeBSD 2.2.6 and is fixed in FreeBSD 3.1. *-*-freebsd2.*|*-*-freebsd3.0*|*-*-freebsdelf3.0*) pic_flag_for_symtable=" $pic_flag -DFREEBSD_WORKAROUND" ;; *-*-hpux*) pic_flag_for_symtable=" $pic_flag" ;; *) if test "X$my_pic_p" != Xno; then pic_flag_for_symtable=" $pic_flag" fi ;; esac ;; esac symtab_cflags= for arg in $LTCFLAGS; do case $arg in -pie | -fpie | -fPIE) ;; *) func_append symtab_cflags " $arg" ;; esac done # Now compile the dynamic symbol file. func_show_eval '(cd $output_objdir && $LTCC$symtab_cflags -c$no_builtin_flag$pic_flag_for_symtable "$my_dlsyms")' 'exit $?' # Clean up the generated files. func_show_eval '$RM "$output_objdir/$my_dlsyms" "$nlist" "${nlist}S" "${nlist}T"' # Transform the symbol file into the correct name. symfileobj="$output_objdir/${my_outputname}S.$objext" case $host in *cygwin* | *mingw* | *cegcc* ) if test -f "$output_objdir/$my_outputname.def"; then compile_command=`$ECHO "$compile_command" | $SED "s%@SYMFILE@%$output_objdir/$my_outputname.def $symfileobj%"` finalize_command=`$ECHO "$finalize_command" | $SED "s%@SYMFILE@%$output_objdir/$my_outputname.def $symfileobj%"` else compile_command=`$ECHO "$compile_command" | $SED "s%@SYMFILE@%$symfileobj%"` finalize_command=`$ECHO "$finalize_command" | $SED "s%@SYMFILE@%$symfileobj%"` fi ;; *) compile_command=`$ECHO "$compile_command" | $SED "s%@SYMFILE@%$symfileobj%"` finalize_command=`$ECHO "$finalize_command" | $SED "s%@SYMFILE@%$symfileobj%"` ;; esac ;; *) func_fatal_error "unknown suffix for \`$my_dlsyms'" ;; esac else # We keep going just in case the user didn't refer to # lt_preloaded_symbols. The linker will fail if global_symbol_pipe # really was required. # Nullify the symbol file. compile_command=`$ECHO "$compile_command" | $SED "s% @SYMFILE@%%"` finalize_command=`$ECHO "$finalize_command" | $SED "s% @SYMFILE@%%"` fi } # func_win32_libid arg # return the library type of file 'arg' # # Need a lot of goo to handle *both* DLLs and import libs # Has to be a shell function in order to 'eat' the argument # that is supplied when $file_magic_command is called. # Despite the name, also deal with 64 bit binaries. func_win32_libid () { $opt_debug win32_libid_type="unknown" win32_fileres=`file -L $1 2>/dev/null` case $win32_fileres in *ar\ archive\ import\ library*) # definitely import win32_libid_type="x86 archive import" ;; *ar\ archive*) # could be an import, or static # Keep the egrep pattern in sync with the one in _LT_CHECK_MAGIC_METHOD. if eval $OBJDUMP -f $1 | $SED -e '10q' 2>/dev/null | $EGREP 'file format (pei*-i386(.*architecture: i386)?|pe-arm-wince|pe-x86-64)' >/dev/null; then func_to_tool_file "$1" func_convert_file_msys_to_w32 win32_nmres=`eval $NM -f posix -A \"$func_to_tool_file_result\" | $SED -n -e ' 1,100{ / I /{ s,.*,import, p q } }'` case $win32_nmres in import*) win32_libid_type="x86 archive import";; *) win32_libid_type="x86 archive static";; esac fi ;; *DLL*) win32_libid_type="x86 DLL" ;; *executable*) # but shell scripts are "executable" too... case $win32_fileres in *MS\ Windows\ PE\ Intel*) win32_libid_type="x86 DLL" ;; esac ;; esac $ECHO "$win32_libid_type" } # func_cygming_dll_for_implib ARG # # Platform-specific function to extract the # name of the DLL associated with the specified # import library ARG. # Invoked by eval'ing the libtool variable # $sharedlib_from_linklib_cmd # Result is available in the variable # $sharedlib_from_linklib_result func_cygming_dll_for_implib () { $opt_debug sharedlib_from_linklib_result=`$DLLTOOL --identify-strict --identify "$1"` } # func_cygming_dll_for_implib_fallback_core SECTION_NAME LIBNAMEs # # The is the core of a fallback implementation of a # platform-specific function to extract the name of the # DLL associated with the specified import library LIBNAME. # # SECTION_NAME is either .idata$6 or .idata$7, depending # on the platform and compiler that created the implib. # # Echos the name of the DLL associated with the # specified import library. func_cygming_dll_for_implib_fallback_core () { $opt_debug match_literal=`$ECHO "$1" | $SED "$sed_make_literal_regex"` $OBJDUMP -s --section "$1" "$2" 2>/dev/null | $SED '/^Contents of section '"$match_literal"':/{ # Place marker at beginning of archive member dllname section s/.*/====MARK====/ p d } # These lines can sometimes be longer than 43 characters, but # are always uninteresting /:[ ]*file format pe[i]\{,1\}-/d /^In archive [^:]*:/d # Ensure marker is printed /^====MARK====/p # Remove all lines with less than 43 characters /^.\{43\}/!d # From remaining lines, remove first 43 characters s/^.\{43\}//' | $SED -n ' # Join marker and all lines until next marker into a single line /^====MARK====/ b para H $ b para b :para x s/\n//g # Remove the marker s/^====MARK====// # Remove trailing dots and whitespace s/[\. \t]*$// # Print /./p' | # we now have a list, one entry per line, of the stringified # contents of the appropriate section of all members of the # archive which possess that section. Heuristic: eliminate # all those which have a first or second character that is # a '.' (that is, objdump's representation of an unprintable # character.) This should work for all archives with less than # 0x302f exports -- but will fail for DLLs whose name actually # begins with a literal '.' or a single character followed by # a '.'. # # Of those that remain, print the first one. $SED -e '/^\./d;/^.\./d;q' } # func_cygming_gnu_implib_p ARG # This predicate returns with zero status (TRUE) if # ARG is a GNU/binutils-style import library. Returns # with nonzero status (FALSE) otherwise. func_cygming_gnu_implib_p () { $opt_debug func_to_tool_file "$1" func_convert_file_msys_to_w32 func_cygming_gnu_implib_tmp=`$NM "$func_to_tool_file_result" | eval "$global_symbol_pipe" | $EGREP ' (_head_[A-Za-z0-9_]+_[ad]l*|[A-Za-z0-9_]+_[ad]l*_iname)$'` test -n "$func_cygming_gnu_implib_tmp" } # func_cygming_ms_implib_p ARG # This predicate returns with zero status (TRUE) if # ARG is an MS-style import library. Returns # with nonzero status (FALSE) otherwise. func_cygming_ms_implib_p () { $opt_debug func_to_tool_file "$1" func_convert_file_msys_to_w32 func_cygming_ms_implib_tmp=`$NM "$func_to_tool_file_result" | eval "$global_symbol_pipe" | $GREP '_NULL_IMPORT_DESCRIPTOR'` test -n "$func_cygming_ms_implib_tmp" } # func_cygming_dll_for_implib_fallback ARG # Platform-specific function to extract the # name of the DLL associated with the specified # import library ARG. # # This fallback implementation is for use when $DLLTOOL # does not support the --identify-strict option. # Invoked by eval'ing the libtool variable # $sharedlib_from_linklib_cmd # Result is available in the variable # $sharedlib_from_linklib_result func_cygming_dll_for_implib_fallback () { $opt_debug if func_cygming_gnu_implib_p "$1" ; then # binutils import library sharedlib_from_linklib_result=`func_cygming_dll_for_implib_fallback_core '.idata$7' "$1"` elif func_cygming_ms_implib_p "$1" ; then # ms-generated import library sharedlib_from_linklib_result=`func_cygming_dll_for_implib_fallback_core '.idata$6' "$1"` else # unknown sharedlib_from_linklib_result="" fi } # func_extract_an_archive dir oldlib func_extract_an_archive () { $opt_debug f_ex_an_ar_dir="$1"; shift f_ex_an_ar_oldlib="$1" if test "$lock_old_archive_extraction" = yes; then lockfile=$f_ex_an_ar_oldlib.lock until $opt_dry_run || ln "$progpath" "$lockfile" 2>/dev/null; do func_echo "Waiting for $lockfile to be removed" sleep 2 done fi func_show_eval "(cd \$f_ex_an_ar_dir && $AR x \"\$f_ex_an_ar_oldlib\")" \ 'stat=$?; rm -f "$lockfile"; exit $stat' if test "$lock_old_archive_extraction" = yes; then $opt_dry_run || rm -f "$lockfile" fi if ($AR t "$f_ex_an_ar_oldlib" | sort | sort -uc >/dev/null 2>&1); then : else func_fatal_error "object name conflicts in archive: $f_ex_an_ar_dir/$f_ex_an_ar_oldlib" fi } # func_extract_archives gentop oldlib ... func_extract_archives () { $opt_debug my_gentop="$1"; shift my_oldlibs=${1+"$@"} my_oldobjs="" my_xlib="" my_xabs="" my_xdir="" for my_xlib in $my_oldlibs; do # Extract the objects. case $my_xlib in [\\/]* | [A-Za-z]:[\\/]*) my_xabs="$my_xlib" ;; *) my_xabs=`pwd`"/$my_xlib" ;; esac func_basename "$my_xlib" my_xlib="$func_basename_result" my_xlib_u=$my_xlib while :; do case " $extracted_archives " in *" $my_xlib_u "*) func_arith $extracted_serial + 1 extracted_serial=$func_arith_result my_xlib_u=lt$extracted_serial-$my_xlib ;; *) break ;; esac done extracted_archives="$extracted_archives $my_xlib_u" my_xdir="$my_gentop/$my_xlib_u" func_mkdir_p "$my_xdir" case $host in *-darwin*) func_verbose "Extracting $my_xabs" # Do not bother doing anything if just a dry run $opt_dry_run || { darwin_orig_dir=`pwd` cd $my_xdir || exit $? darwin_archive=$my_xabs darwin_curdir=`pwd` darwin_base_archive=`basename "$darwin_archive"` darwin_arches=`$LIPO -info "$darwin_archive" 2>/dev/null | $GREP Architectures 2>/dev/null || true` if test -n "$darwin_arches"; then darwin_arches=`$ECHO "$darwin_arches" | $SED -e 's/.*are://'` darwin_arch= func_verbose "$darwin_base_archive has multiple architectures $darwin_arches" for darwin_arch in $darwin_arches ; do func_mkdir_p "unfat-$$/${darwin_base_archive}-${darwin_arch}" $LIPO -thin $darwin_arch -output "unfat-$$/${darwin_base_archive}-${darwin_arch}/${darwin_base_archive}" "${darwin_archive}" cd "unfat-$$/${darwin_base_archive}-${darwin_arch}" func_extract_an_archive "`pwd`" "${darwin_base_archive}" cd "$darwin_curdir" $RM "unfat-$$/${darwin_base_archive}-${darwin_arch}/${darwin_base_archive}" done # $darwin_arches ## Okay now we've a bunch of thin objects, gotta fatten them up :) darwin_filelist=`find unfat-$$ -type f -name \*.o -print -o -name \*.lo -print | $SED -e "$basename" | sort -u` darwin_file= darwin_files= for darwin_file in $darwin_filelist; do darwin_files=`find unfat-$$ -name $darwin_file -print | sort | $NL2SP` $LIPO -create -output "$darwin_file" $darwin_files done # $darwin_filelist $RM -rf unfat-$$ cd "$darwin_orig_dir" else cd $darwin_orig_dir func_extract_an_archive "$my_xdir" "$my_xabs" fi # $darwin_arches } # !$opt_dry_run ;; *) func_extract_an_archive "$my_xdir" "$my_xabs" ;; esac my_oldobjs="$my_oldobjs "`find $my_xdir -name \*.$objext -print -o -name \*.lo -print | sort | $NL2SP` done func_extract_archives_result="$my_oldobjs" } # func_emit_wrapper [arg=no] # # Emit a libtool wrapper script on stdout. # Don't directly open a file because we may want to # incorporate the script contents within a cygwin/mingw # wrapper executable. Must ONLY be called from within # func_mode_link because it depends on a number of variables # set therein. # # ARG is the value that the WRAPPER_SCRIPT_BELONGS_IN_OBJDIR # variable will take. If 'yes', then the emitted script # will assume that the directory in which it is stored is # the $objdir directory. This is a cygwin/mingw-specific # behavior. func_emit_wrapper () { func_emit_wrapper_arg1=${1-no} $ECHO "\ #! $SHELL # $output - temporary wrapper script for $objdir/$outputname # Generated by $PROGRAM (GNU $PACKAGE$TIMESTAMP) $VERSION # # The $output program cannot be directly executed until all the libtool # libraries that it depends on are installed. # # This wrapper script should never be moved out of the build directory. # If it is, it will not operate correctly. # Sed substitution that helps us do robust quoting. It backslashifies # metacharacters that are still active within double-quoted strings. sed_quote_subst='$sed_quote_subst' # Be Bourne compatible if test -n \"\${ZSH_VERSION+set}\" && (emulate sh) >/dev/null 2>&1; then emulate sh NULLCMD=: # Zsh 3.x and 4.x performs word splitting on \${1+\"\$@\"}, which # is contrary to our usage. Disable this feature. alias -g '\${1+\"\$@\"}'='\"\$@\"' setopt NO_GLOB_SUBST else case \`(set -o) 2>/dev/null\` in *posix*) set -o posix;; esac fi BIN_SH=xpg4; export BIN_SH # for Tru64 DUALCASE=1; export DUALCASE # for MKS sh # The HP-UX ksh and POSIX shell print the target directory to stdout # if CDPATH is set. (unset CDPATH) >/dev/null 2>&1 && unset CDPATH relink_command=\"$relink_command\" # This environment variable determines our operation mode. if test \"\$libtool_install_magic\" = \"$magic\"; then # install mode needs the following variables: generated_by_libtool_version='$macro_version' notinst_deplibs='$notinst_deplibs' else # When we are sourced in execute mode, \$file and \$ECHO are already set. if test \"\$libtool_execute_magic\" != \"$magic\"; then file=\"\$0\"" qECHO=`$ECHO "$ECHO" | $SED "$sed_quote_subst"` $ECHO "\ # A function that is used when there is no print builtin or printf. func_fallback_echo () { eval 'cat <<_LTECHO_EOF \$1 _LTECHO_EOF' } ECHO=\"$qECHO\" fi # Very basic option parsing. These options are (a) specific to # the libtool wrapper, (b) are identical between the wrapper # /script/ and the wrapper /executable/ which is used only on # windows platforms, and (c) all begin with the string "--lt-" # (application programs are unlikely to have options which match # this pattern). # # There are only two supported options: --lt-debug and # --lt-dump-script. There is, deliberately, no --lt-help. # # The first argument to this parsing function should be the # script's $0 value, followed by "$@". lt_option_debug= func_parse_lt_options () { lt_script_arg0=\$0 shift for lt_opt do case \"\$lt_opt\" in --lt-debug) lt_option_debug=1 ;; --lt-dump-script) lt_dump_D=\`\$ECHO \"X\$lt_script_arg0\" | $SED -e 's/^X//' -e 's%/[^/]*$%%'\` test \"X\$lt_dump_D\" = \"X\$lt_script_arg0\" && lt_dump_D=. lt_dump_F=\`\$ECHO \"X\$lt_script_arg0\" | $SED -e 's/^X//' -e 's%^.*/%%'\` cat \"\$lt_dump_D/\$lt_dump_F\" exit 0 ;; --lt-*) \$ECHO \"Unrecognized --lt- option: '\$lt_opt'\" 1>&2 exit 1 ;; esac done # Print the debug banner immediately: if test -n \"\$lt_option_debug\"; then echo \"${outputname}:${output}:\${LINENO}: libtool wrapper (GNU $PACKAGE$TIMESTAMP) $VERSION\" 1>&2 fi } # Used when --lt-debug. Prints its arguments to stdout # (redirection is the responsibility of the caller) func_lt_dump_args () { lt_dump_args_N=1; for lt_arg do \$ECHO \"${outputname}:${output}:\${LINENO}: newargv[\$lt_dump_args_N]: \$lt_arg\" lt_dump_args_N=\`expr \$lt_dump_args_N + 1\` done } # Core function for launching the target application func_exec_program_core () { " case $host in # Backslashes separate directories on plain windows *-*-mingw | *-*-os2* | *-cegcc*) $ECHO "\ if test -n \"\$lt_option_debug\"; then \$ECHO \"${outputname}:${output}:\${LINENO}: newargv[0]: \$progdir\\\\\$program\" 1>&2 func_lt_dump_args \${1+\"\$@\"} 1>&2 fi exec \"\$progdir\\\\\$program\" \${1+\"\$@\"} " ;; *) $ECHO "\ if test -n \"\$lt_option_debug\"; then \$ECHO \"${outputname}:${output}:\${LINENO}: newargv[0]: \$progdir/\$program\" 1>&2 func_lt_dump_args \${1+\"\$@\"} 1>&2 fi exec \"\$progdir/\$program\" \${1+\"\$@\"} " ;; esac $ECHO "\ \$ECHO \"\$0: cannot exec \$program \$*\" 1>&2 exit 1 } # A function to encapsulate launching the target application # Strips options in the --lt-* namespace from \$@ and # launches target application with the remaining arguments. func_exec_program () { case \" \$* \" in *\\ --lt-*) for lt_wr_arg do case \$lt_wr_arg in --lt-*) ;; *) set x \"\$@\" \"\$lt_wr_arg\"; shift;; esac shift done ;; esac func_exec_program_core \${1+\"\$@\"} } # Parse options func_parse_lt_options \"\$0\" \${1+\"\$@\"} # Find the directory that this script lives in. thisdir=\`\$ECHO \"\$file\" | $SED 's%/[^/]*$%%'\` test \"x\$thisdir\" = \"x\$file\" && thisdir=. # Follow symbolic links until we get to the real thisdir. file=\`ls -ld \"\$file\" | $SED -n 's/.*-> //p'\` while test -n \"\$file\"; do destdir=\`\$ECHO \"\$file\" | $SED 's%/[^/]*\$%%'\` # If there was a directory component, then change thisdir. if test \"x\$destdir\" != \"x\$file\"; then case \"\$destdir\" in [\\\\/]* | [A-Za-z]:[\\\\/]*) thisdir=\"\$destdir\" ;; *) thisdir=\"\$thisdir/\$destdir\" ;; esac fi file=\`\$ECHO \"\$file\" | $SED 's%^.*/%%'\` file=\`ls -ld \"\$thisdir/\$file\" | $SED -n 's/.*-> //p'\` done # Usually 'no', except on cygwin/mingw when embedded into # the cwrapper. WRAPPER_SCRIPT_BELONGS_IN_OBJDIR=$func_emit_wrapper_arg1 if test \"\$WRAPPER_SCRIPT_BELONGS_IN_OBJDIR\" = \"yes\"; then # special case for '.' if test \"\$thisdir\" = \".\"; then thisdir=\`pwd\` fi # remove .libs from thisdir case \"\$thisdir\" in *[\\\\/]$objdir ) thisdir=\`\$ECHO \"\$thisdir\" | $SED 's%[\\\\/][^\\\\/]*$%%'\` ;; $objdir ) thisdir=. ;; esac fi # Try to get the absolute directory name. absdir=\`cd \"\$thisdir\" && pwd\` test -n \"\$absdir\" && thisdir=\"\$absdir\" " if test "$fast_install" = yes; then $ECHO "\ program=lt-'$outputname'$exeext progdir=\"\$thisdir/$objdir\" if test ! -f \"\$progdir/\$program\" || { file=\`ls -1dt \"\$progdir/\$program\" \"\$progdir/../\$program\" 2>/dev/null | ${SED} 1q\`; \\ test \"X\$file\" != \"X\$progdir/\$program\"; }; then file=\"\$\$-\$program\" if test ! -d \"\$progdir\"; then $MKDIR \"\$progdir\" else $RM \"\$progdir/\$file\" fi" $ECHO "\ # relink executable if necessary if test -n \"\$relink_command\"; then if relink_command_output=\`eval \$relink_command 2>&1\`; then : else $ECHO \"\$relink_command_output\" >&2 $RM \"\$progdir/\$file\" exit 1 fi fi $MV \"\$progdir/\$file\" \"\$progdir/\$program\" 2>/dev/null || { $RM \"\$progdir/\$program\"; $MV \"\$progdir/\$file\" \"\$progdir/\$program\"; } $RM \"\$progdir/\$file\" fi" else $ECHO "\ program='$outputname' progdir=\"\$thisdir/$objdir\" " fi $ECHO "\ if test -f \"\$progdir/\$program\"; then" # fixup the dll searchpath if we need to. # # Fix the DLL searchpath if we need to. Do this before prepending # to shlibpath, because on Windows, both are PATH and uninstalled # libraries must come first. if test -n "$dllsearchpath"; then $ECHO "\ # Add the dll search path components to the executable PATH PATH=$dllsearchpath:\$PATH " fi # Export our shlibpath_var if we have one. if test "$shlibpath_overrides_runpath" = yes && test -n "$shlibpath_var" && test -n "$temp_rpath"; then $ECHO "\ # Add our own library path to $shlibpath_var $shlibpath_var=\"$temp_rpath\$$shlibpath_var\" # Some systems cannot cope with colon-terminated $shlibpath_var # The second colon is a workaround for a bug in BeOS R4 sed $shlibpath_var=\`\$ECHO \"\$$shlibpath_var\" | $SED 's/::*\$//'\` export $shlibpath_var " fi $ECHO "\ if test \"\$libtool_execute_magic\" != \"$magic\"; then # Run the actual program with our arguments. func_exec_program \${1+\"\$@\"} fi else # The program doesn't exist. \$ECHO \"\$0: error: \\\`\$progdir/\$program' does not exist\" 1>&2 \$ECHO \"This script is just a wrapper for \$program.\" 1>&2 \$ECHO \"See the $PACKAGE documentation for more information.\" 1>&2 exit 1 fi fi\ " } # func_emit_cwrapperexe_src # emit the source code for a wrapper executable on stdout # Must ONLY be called from within func_mode_link because # it depends on a number of variable set therein. func_emit_cwrapperexe_src () { cat < #include #ifdef _MSC_VER # include # include # include #else # include # include # ifdef __CYGWIN__ # include # endif #endif #include #include #include #include #include #include #include #include /* declarations of non-ANSI functions */ #if defined(__MINGW32__) # ifdef __STRICT_ANSI__ int _putenv (const char *); # endif #elif defined(__CYGWIN__) # ifdef __STRICT_ANSI__ char *realpath (const char *, char *); int putenv (char *); int setenv (const char *, const char *, int); # endif /* #elif defined (other platforms) ... */ #endif /* portability defines, excluding path handling macros */ #if defined(_MSC_VER) # define setmode _setmode # define stat _stat # define chmod _chmod # define getcwd _getcwd # define putenv _putenv # define S_IXUSR _S_IEXEC # ifndef _INTPTR_T_DEFINED # define _INTPTR_T_DEFINED # define intptr_t int # endif #elif defined(__MINGW32__) # define setmode _setmode # define stat _stat # define chmod _chmod # define getcwd _getcwd # define putenv _putenv #elif defined(__CYGWIN__) # define HAVE_SETENV # define FOPEN_WB "wb" /* #elif defined (other platforms) ... */ #endif #if defined(PATH_MAX) # define LT_PATHMAX PATH_MAX #elif defined(MAXPATHLEN) # define LT_PATHMAX MAXPATHLEN #else # define LT_PATHMAX 1024 #endif #ifndef S_IXOTH # define S_IXOTH 0 #endif #ifndef S_IXGRP # define S_IXGRP 0 #endif /* path handling portability macros */ #ifndef DIR_SEPARATOR # define DIR_SEPARATOR '/' # define PATH_SEPARATOR ':' #endif #if defined (_WIN32) || defined (__MSDOS__) || defined (__DJGPP__) || \ defined (__OS2__) # define HAVE_DOS_BASED_FILE_SYSTEM # define FOPEN_WB "wb" # ifndef DIR_SEPARATOR_2 # define DIR_SEPARATOR_2 '\\' # endif # ifndef PATH_SEPARATOR_2 # define PATH_SEPARATOR_2 ';' # endif #endif #ifndef DIR_SEPARATOR_2 # define IS_DIR_SEPARATOR(ch) ((ch) == DIR_SEPARATOR) #else /* DIR_SEPARATOR_2 */ # define IS_DIR_SEPARATOR(ch) \ (((ch) == DIR_SEPARATOR) || ((ch) == DIR_SEPARATOR_2)) #endif /* DIR_SEPARATOR_2 */ #ifndef PATH_SEPARATOR_2 # define IS_PATH_SEPARATOR(ch) ((ch) == PATH_SEPARATOR) #else /* PATH_SEPARATOR_2 */ # define IS_PATH_SEPARATOR(ch) ((ch) == PATH_SEPARATOR_2) #endif /* PATH_SEPARATOR_2 */ #ifndef FOPEN_WB # define FOPEN_WB "w" #endif #ifndef _O_BINARY # define _O_BINARY 0 #endif #define XMALLOC(type, num) ((type *) xmalloc ((num) * sizeof(type))) #define XFREE(stale) do { \ if (stale) { free ((void *) stale); stale = 0; } \ } while (0) #if defined(LT_DEBUGWRAPPER) static int lt_debug = 1; #else static int lt_debug = 0; #endif const char *program_name = "libtool-wrapper"; /* in case xstrdup fails */ void *xmalloc (size_t num); char *xstrdup (const char *string); const char *base_name (const char *name); char *find_executable (const char *wrapper); char *chase_symlinks (const char *pathspec); int make_executable (const char *path); int check_executable (const char *path); char *strendzap (char *str, const char *pat); void lt_debugprintf (const char *file, int line, const char *fmt, ...); void lt_fatal (const char *file, int line, const char *message, ...); static const char *nonnull (const char *s); static const char *nonempty (const char *s); void lt_setenv (const char *name, const char *value); char *lt_extend_str (const char *orig_value, const char *add, int to_end); void lt_update_exe_path (const char *name, const char *value); void lt_update_lib_path (const char *name, const char *value); char **prepare_spawn (char **argv); void lt_dump_script (FILE *f); EOF cat <= 0) && (st.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH))) return 1; else return 0; } int make_executable (const char *path) { int rval = 0; struct stat st; lt_debugprintf (__FILE__, __LINE__, "(make_executable): %s\n", nonempty (path)); if ((!path) || (!*path)) return 0; if (stat (path, &st) >= 0) { rval = chmod (path, st.st_mode | S_IXOTH | S_IXGRP | S_IXUSR); } return rval; } /* Searches for the full path of the wrapper. Returns newly allocated full path name if found, NULL otherwise Does not chase symlinks, even on platforms that support them. */ char * find_executable (const char *wrapper) { int has_slash = 0; const char *p; const char *p_next; /* static buffer for getcwd */ char tmp[LT_PATHMAX + 1]; int tmp_len; char *concat_name; lt_debugprintf (__FILE__, __LINE__, "(find_executable): %s\n", nonempty (wrapper)); if ((wrapper == NULL) || (*wrapper == '\0')) return NULL; /* Absolute path? */ #if defined (HAVE_DOS_BASED_FILE_SYSTEM) if (isalpha ((unsigned char) wrapper[0]) && wrapper[1] == ':') { concat_name = xstrdup (wrapper); if (check_executable (concat_name)) return concat_name; XFREE (concat_name); } else { #endif if (IS_DIR_SEPARATOR (wrapper[0])) { concat_name = xstrdup (wrapper); if (check_executable (concat_name)) return concat_name; XFREE (concat_name); } #if defined (HAVE_DOS_BASED_FILE_SYSTEM) } #endif for (p = wrapper; *p; p++) if (*p == '/') { has_slash = 1; break; } if (!has_slash) { /* no slashes; search PATH */ const char *path = getenv ("PATH"); if (path != NULL) { for (p = path; *p; p = p_next) { const char *q; size_t p_len; for (q = p; *q; q++) if (IS_PATH_SEPARATOR (*q)) break; p_len = q - p; p_next = (*q == '\0' ? q : q + 1); if (p_len == 0) { /* empty path: current directory */ if (getcwd (tmp, LT_PATHMAX) == NULL) lt_fatal (__FILE__, __LINE__, "getcwd failed: %s", nonnull (strerror (errno))); tmp_len = strlen (tmp); concat_name = XMALLOC (char, tmp_len + 1 + strlen (wrapper) + 1); memcpy (concat_name, tmp, tmp_len); concat_name[tmp_len] = '/'; strcpy (concat_name + tmp_len + 1, wrapper); } else { concat_name = XMALLOC (char, p_len + 1 + strlen (wrapper) + 1); memcpy (concat_name, p, p_len); concat_name[p_len] = '/'; strcpy (concat_name + p_len + 1, wrapper); } if (check_executable (concat_name)) return concat_name; XFREE (concat_name); } } /* not found in PATH; assume curdir */ } /* Relative path | not found in path: prepend cwd */ if (getcwd (tmp, LT_PATHMAX) == NULL) lt_fatal (__FILE__, __LINE__, "getcwd failed: %s", nonnull (strerror (errno))); tmp_len = strlen (tmp); concat_name = XMALLOC (char, tmp_len + 1 + strlen (wrapper) + 1); memcpy (concat_name, tmp, tmp_len); concat_name[tmp_len] = '/'; strcpy (concat_name + tmp_len + 1, wrapper); if (check_executable (concat_name)) return concat_name; XFREE (concat_name); return NULL; } char * chase_symlinks (const char *pathspec) { #ifndef S_ISLNK return xstrdup (pathspec); #else char buf[LT_PATHMAX]; struct stat s; char *tmp_pathspec = xstrdup (pathspec); char *p; int has_symlinks = 0; while (strlen (tmp_pathspec) && !has_symlinks) { lt_debugprintf (__FILE__, __LINE__, "checking path component for symlinks: %s\n", tmp_pathspec); if (lstat (tmp_pathspec, &s) == 0) { if (S_ISLNK (s.st_mode) != 0) { has_symlinks = 1; break; } /* search backwards for last DIR_SEPARATOR */ p = tmp_pathspec + strlen (tmp_pathspec) - 1; while ((p > tmp_pathspec) && (!IS_DIR_SEPARATOR (*p))) p--; if ((p == tmp_pathspec) && (!IS_DIR_SEPARATOR (*p))) { /* no more DIR_SEPARATORS left */ break; } *p = '\0'; } else { lt_fatal (__FILE__, __LINE__, "error accessing file \"%s\": %s", tmp_pathspec, nonnull (strerror (errno))); } } XFREE (tmp_pathspec); if (!has_symlinks) { return xstrdup (pathspec); } tmp_pathspec = realpath (pathspec, buf); if (tmp_pathspec == 0) { lt_fatal (__FILE__, __LINE__, "could not follow symlinks for %s", pathspec); } return xstrdup (tmp_pathspec); #endif } char * strendzap (char *str, const char *pat) { size_t len, patlen; assert (str != NULL); assert (pat != NULL); len = strlen (str); patlen = strlen (pat); if (patlen <= len) { str += len - patlen; if (strcmp (str, pat) == 0) *str = '\0'; } return str; } void lt_debugprintf (const char *file, int line, const char *fmt, ...) { va_list args; if (lt_debug) { (void) fprintf (stderr, "%s:%s:%d: ", program_name, file, line); va_start (args, fmt); (void) vfprintf (stderr, fmt, args); va_end (args); } } static void lt_error_core (int exit_status, const char *file, int line, const char *mode, const char *message, va_list ap) { fprintf (stderr, "%s:%s:%d: %s: ", program_name, file, line, mode); vfprintf (stderr, message, ap); fprintf (stderr, ".\n"); if (exit_status >= 0) exit (exit_status); } void lt_fatal (const char *file, int line, const char *message, ...) { va_list ap; va_start (ap, message); lt_error_core (EXIT_FAILURE, file, line, "FATAL", message, ap); va_end (ap); } static const char * nonnull (const char *s) { return s ? s : "(null)"; } static const char * nonempty (const char *s) { return (s && !*s) ? "(empty)" : nonnull (s); } void lt_setenv (const char *name, const char *value) { lt_debugprintf (__FILE__, __LINE__, "(lt_setenv) setting '%s' to '%s'\n", nonnull (name), nonnull (value)); { #ifdef HAVE_SETENV /* always make a copy, for consistency with !HAVE_SETENV */ char *str = xstrdup (value); setenv (name, str, 1); #else int len = strlen (name) + 1 + strlen (value) + 1; char *str = XMALLOC (char, len); sprintf (str, "%s=%s", name, value); if (putenv (str) != EXIT_SUCCESS) { XFREE (str); } #endif } } char * lt_extend_str (const char *orig_value, const char *add, int to_end) { char *new_value; if (orig_value && *orig_value) { int orig_value_len = strlen (orig_value); int add_len = strlen (add); new_value = XMALLOC (char, add_len + orig_value_len + 1); if (to_end) { strcpy (new_value, orig_value); strcpy (new_value + orig_value_len, add); } else { strcpy (new_value, add); strcpy (new_value + add_len, orig_value); } } else { new_value = xstrdup (add); } return new_value; } void lt_update_exe_path (const char *name, const char *value) { lt_debugprintf (__FILE__, __LINE__, "(lt_update_exe_path) modifying '%s' by prepending '%s'\n", nonnull (name), nonnull (value)); if (name && *name && value && *value) { char *new_value = lt_extend_str (getenv (name), value, 0); /* some systems can't cope with a ':'-terminated path #' */ int len = strlen (new_value); while (((len = strlen (new_value)) > 0) && IS_PATH_SEPARATOR (new_value[len-1])) { new_value[len-1] = '\0'; } lt_setenv (name, new_value); XFREE (new_value); } } void lt_update_lib_path (const char *name, const char *value) { lt_debugprintf (__FILE__, __LINE__, "(lt_update_lib_path) modifying '%s' by prepending '%s'\n", nonnull (name), nonnull (value)); if (name && *name && value && *value) { char *new_value = lt_extend_str (getenv (name), value, 0); lt_setenv (name, new_value); XFREE (new_value); } } EOF case $host_os in mingw*) cat <<"EOF" /* Prepares an argument vector before calling spawn(). Note that spawn() does not by itself call the command interpreter (getenv ("COMSPEC") != NULL ? getenv ("COMSPEC") : ({ OSVERSIONINFO v; v.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); GetVersionEx(&v); v.dwPlatformId == VER_PLATFORM_WIN32_NT; }) ? "cmd.exe" : "command.com"). Instead it simply concatenates the arguments, separated by ' ', and calls CreateProcess(). We must quote the arguments since Win32 CreateProcess() interprets characters like ' ', '\t', '\\', '"' (but not '<' and '>') in a special way: - Space and tab are interpreted as delimiters. They are not treated as delimiters if they are surrounded by double quotes: "...". - Unescaped double quotes are removed from the input. Their only effect is that within double quotes, space and tab are treated like normal characters. - Backslashes not followed by double quotes are not special. - But 2*n+1 backslashes followed by a double quote become n backslashes followed by a double quote (n >= 0): \" -> " \\\" -> \" \\\\\" -> \\" */ #define SHELL_SPECIAL_CHARS "\"\\ \001\002\003\004\005\006\007\010\011\012\013\014\015\016\017\020\021\022\023\024\025\026\027\030\031\032\033\034\035\036\037" #define SHELL_SPACE_CHARS " \001\002\003\004\005\006\007\010\011\012\013\014\015\016\017\020\021\022\023\024\025\026\027\030\031\032\033\034\035\036\037" char ** prepare_spawn (char **argv) { size_t argc; char **new_argv; size_t i; /* Count number of arguments. */ for (argc = 0; argv[argc] != NULL; argc++) ; /* Allocate new argument vector. */ new_argv = XMALLOC (char *, argc + 1); /* Put quoted arguments into the new argument vector. */ for (i = 0; i < argc; i++) { const char *string = argv[i]; if (string[0] == '\0') new_argv[i] = xstrdup ("\"\""); else if (strpbrk (string, SHELL_SPECIAL_CHARS) != NULL) { int quote_around = (strpbrk (string, SHELL_SPACE_CHARS) != NULL); size_t length; unsigned int backslashes; const char *s; char *quoted_string; char *p; length = 0; backslashes = 0; if (quote_around) length++; for (s = string; *s != '\0'; s++) { char c = *s; if (c == '"') length += backslashes + 1; length++; if (c == '\\') backslashes++; else backslashes = 0; } if (quote_around) length += backslashes + 1; quoted_string = XMALLOC (char, length + 1); p = quoted_string; backslashes = 0; if (quote_around) *p++ = '"'; for (s = string; *s != '\0'; s++) { char c = *s; if (c == '"') { unsigned int j; for (j = backslashes + 1; j > 0; j--) *p++ = '\\'; } *p++ = c; if (c == '\\') backslashes++; else backslashes = 0; } if (quote_around) { unsigned int j; for (j = backslashes; j > 0; j--) *p++ = '\\'; *p++ = '"'; } *p = '\0'; new_argv[i] = quoted_string; } else new_argv[i] = (char *) string; } new_argv[argc] = NULL; return new_argv; } EOF ;; esac cat <<"EOF" void lt_dump_script (FILE* f) { EOF func_emit_wrapper yes | $SED -n -e ' s/^\(.\{79\}\)\(..*\)/\1\ \2/ h s/\([\\"]\)/\\\1/g s/$/\\n/ s/\([^\n]*\).*/ fputs ("\1", f);/p g D' cat <<"EOF" } EOF } # end: func_emit_cwrapperexe_src # func_win32_import_lib_p ARG # True if ARG is an import lib, as indicated by $file_magic_cmd func_win32_import_lib_p () { $opt_debug case `eval $file_magic_cmd \"\$1\" 2>/dev/null | $SED -e 10q` in *import*) : ;; *) false ;; esac } # func_mode_link arg... func_mode_link () { $opt_debug case $host in *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-cegcc*) # It is impossible to link a dll without this setting, and # we shouldn't force the makefile maintainer to figure out # which system we are compiling for in order to pass an extra # flag for every libtool invocation. # allow_undefined=no # FIXME: Unfortunately, there are problems with the above when trying # to make a dll which has undefined symbols, in which case not # even a static library is built. For now, we need to specify # -no-undefined on the libtool link line when we can be certain # that all symbols are satisfied, otherwise we get a static library. allow_undefined=yes ;; *) allow_undefined=yes ;; esac libtool_args=$nonopt base_compile="$nonopt $@" compile_command=$nonopt finalize_command=$nonopt compile_rpath= finalize_rpath= compile_shlibpath= finalize_shlibpath= convenience= old_convenience= deplibs= old_deplibs= compiler_flags= linker_flags= dllsearchpath= lib_search_path=`pwd` inst_prefix_dir= new_inherited_linker_flags= avoid_version=no bindir= dlfiles= dlprefiles= dlself=no export_dynamic=no export_symbols= export_symbols_regex= generated= libobjs= ltlibs= module=no no_install=no objs= non_pic_objects= precious_files_regex= prefer_static_libs=no preload=no prev= prevarg= release= rpath= xrpath= perm_rpath= temp_rpath= thread_safe=no vinfo= vinfo_number=no weak_libs= single_module="${wl}-single_module" func_infer_tag $base_compile # We need to know -static, to get the right output filenames. for arg do case $arg in -shared) test "$build_libtool_libs" != yes && \ func_fatal_configuration "can not build a shared library" build_old_libs=no break ;; -all-static | -static | -static-libtool-libs) case $arg in -all-static) if test "$build_libtool_libs" = yes && test -z "$link_static_flag"; then func_warning "complete static linking is impossible in this configuration" fi if test -n "$link_static_flag"; then dlopen_self=$dlopen_self_static fi prefer_static_libs=yes ;; -static) if test -z "$pic_flag" && test -n "$link_static_flag"; then dlopen_self=$dlopen_self_static fi prefer_static_libs=built ;; -static-libtool-libs) if test -z "$pic_flag" && test -n "$link_static_flag"; then dlopen_self=$dlopen_self_static fi prefer_static_libs=yes ;; esac build_libtool_libs=no build_old_libs=yes break ;; esac done # See if our shared archives depend on static archives. test -n "$old_archive_from_new_cmds" && build_old_libs=yes # Go through the arguments, transforming them on the way. while test "$#" -gt 0; do arg="$1" shift func_quote_for_eval "$arg" qarg=$func_quote_for_eval_unquoted_result func_append libtool_args " $func_quote_for_eval_result" # If the previous option needs an argument, assign it. if test -n "$prev"; then case $prev in output) func_append compile_command " @OUTPUT@" func_append finalize_command " @OUTPUT@" ;; esac case $prev in bindir) bindir="$arg" prev= continue ;; dlfiles|dlprefiles) if test "$preload" = no; then # Add the symbol object into the linking commands. func_append compile_command " @SYMFILE@" func_append finalize_command " @SYMFILE@" preload=yes fi case $arg in *.la | *.lo) ;; # We handle these cases below. force) if test "$dlself" = no; then dlself=needless export_dynamic=yes fi prev= continue ;; self) if test "$prev" = dlprefiles; then dlself=yes elif test "$prev" = dlfiles && test "$dlopen_self" != yes; then dlself=yes else dlself=needless export_dynamic=yes fi prev= continue ;; *) if test "$prev" = dlfiles; then func_append dlfiles " $arg" else func_append dlprefiles " $arg" fi prev= continue ;; esac ;; expsyms) export_symbols="$arg" test -f "$arg" \ || func_fatal_error "symbol file \`$arg' does not exist" prev= continue ;; expsyms_regex) export_symbols_regex="$arg" prev= continue ;; framework) case $host in *-*-darwin*) case "$deplibs " in *" $qarg.ltframework "*) ;; *) func_append deplibs " $qarg.ltframework" # this is fixed later ;; esac ;; esac prev= continue ;; inst_prefix) inst_prefix_dir="$arg" prev= continue ;; objectlist) if test -f "$arg"; then save_arg=$arg moreargs= for fil in `cat "$save_arg"` do # func_append moreargs " $fil" arg=$fil # A libtool-controlled object. # Check to see that this really is a libtool object. if func_lalib_unsafe_p "$arg"; then pic_object= non_pic_object= # Read the .lo file func_source "$arg" if test -z "$pic_object" || test -z "$non_pic_object" || test "$pic_object" = none && test "$non_pic_object" = none; then func_fatal_error "cannot find name of object for \`$arg'" fi # Extract subdirectory from the argument. func_dirname "$arg" "/" "" xdir="$func_dirname_result" if test "$pic_object" != none; then # Prepend the subdirectory the object is found in. pic_object="$xdir$pic_object" if test "$prev" = dlfiles; then if test "$build_libtool_libs" = yes && test "$dlopen_support" = yes; then func_append dlfiles " $pic_object" prev= continue else # If libtool objects are unsupported, then we need to preload. prev=dlprefiles fi fi # CHECK ME: I think I busted this. -Ossama if test "$prev" = dlprefiles; then # Preload the old-style object. func_append dlprefiles " $pic_object" prev= fi # A PIC object. func_append libobjs " $pic_object" arg="$pic_object" fi # Non-PIC object. if test "$non_pic_object" != none; then # Prepend the subdirectory the object is found in. non_pic_object="$xdir$non_pic_object" # A standard non-PIC object func_append non_pic_objects " $non_pic_object" if test -z "$pic_object" || test "$pic_object" = none ; then arg="$non_pic_object" fi else # If the PIC object exists, use it instead. # $xdir was prepended to $pic_object above. non_pic_object="$pic_object" func_append non_pic_objects " $non_pic_object" fi else # Only an error if not doing a dry-run. if $opt_dry_run; then # Extract subdirectory from the argument. func_dirname "$arg" "/" "" xdir="$func_dirname_result" func_lo2o "$arg" pic_object=$xdir$objdir/$func_lo2o_result non_pic_object=$xdir$func_lo2o_result func_append libobjs " $pic_object" func_append non_pic_objects " $non_pic_object" else func_fatal_error "\`$arg' is not a valid libtool object" fi fi done else func_fatal_error "link input file \`$arg' does not exist" fi arg=$save_arg prev= continue ;; precious_regex) precious_files_regex="$arg" prev= continue ;; release) release="-$arg" prev= continue ;; rpath | xrpath) # We need an absolute path. case $arg in [\\/]* | [A-Za-z]:[\\/]*) ;; *) func_fatal_error "only absolute run-paths are allowed" ;; esac if test "$prev" = rpath; then case "$rpath " in *" $arg "*) ;; *) func_append rpath " $arg" ;; esac else case "$xrpath " in *" $arg "*) ;; *) func_append xrpath " $arg" ;; esac fi prev= continue ;; shrext) shrext_cmds="$arg" prev= continue ;; weak) func_append weak_libs " $arg" prev= continue ;; xcclinker) func_append linker_flags " $qarg" func_append compiler_flags " $qarg" prev= func_append compile_command " $qarg" func_append finalize_command " $qarg" continue ;; xcompiler) func_append compiler_flags " $qarg" prev= func_append compile_command " $qarg" func_append finalize_command " $qarg" continue ;; xlinker) func_append linker_flags " $qarg" func_append compiler_flags " $wl$qarg" prev= func_append compile_command " $wl$qarg" func_append finalize_command " $wl$qarg" continue ;; *) eval "$prev=\"\$arg\"" prev= continue ;; esac fi # test -n "$prev" prevarg="$arg" case $arg in -all-static) if test -n "$link_static_flag"; then # See comment for -static flag below, for more details. func_append compile_command " $link_static_flag" func_append finalize_command " $link_static_flag" fi continue ;; -allow-undefined) # FIXME: remove this flag sometime in the future. func_fatal_error "\`-allow-undefined' must not be used because it is the default" ;; -avoid-version) avoid_version=yes continue ;; -bindir) prev=bindir continue ;; -dlopen) prev=dlfiles continue ;; -dlpreopen) prev=dlprefiles continue ;; -export-dynamic) export_dynamic=yes continue ;; -export-symbols | -export-symbols-regex) if test -n "$export_symbols" || test -n "$export_symbols_regex"; then func_fatal_error "more than one -exported-symbols argument is not allowed" fi if test "X$arg" = "X-export-symbols"; then prev=expsyms else prev=expsyms_regex fi continue ;; -framework) prev=framework continue ;; -inst-prefix-dir) prev=inst_prefix continue ;; # The native IRIX linker understands -LANG:*, -LIST:* and -LNO:* # so, if we see these flags be careful not to treat them like -L -L[A-Z][A-Z]*:*) case $with_gcc/$host in no/*-*-irix* | /*-*-irix*) func_append compile_command " $arg" func_append finalize_command " $arg" ;; esac continue ;; -L*) func_stripname "-L" '' "$arg" if test -z "$func_stripname_result"; then if test "$#" -gt 0; then func_fatal_error "require no space between \`-L' and \`$1'" else func_fatal_error "need path for \`-L' option" fi fi func_resolve_sysroot "$func_stripname_result" dir=$func_resolve_sysroot_result # We need an absolute path. case $dir in [\\/]* | [A-Za-z]:[\\/]*) ;; *) absdir=`cd "$dir" && pwd` test -z "$absdir" && \ func_fatal_error "cannot determine absolute directory name of \`$dir'" dir="$absdir" ;; esac case "$deplibs " in *" -L$dir "* | *" $arg "*) # Will only happen for absolute or sysroot arguments ;; *) # Preserve sysroot, but never include relative directories case $dir in [\\/]* | [A-Za-z]:[\\/]* | =*) func_append deplibs " $arg" ;; *) func_append deplibs " -L$dir" ;; esac func_append lib_search_path " $dir" ;; esac case $host in *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-cegcc*) testbindir=`$ECHO "$dir" | $SED 's*/lib$*/bin*'` case :$dllsearchpath: in *":$dir:"*) ;; ::) dllsearchpath=$dir;; *) func_append dllsearchpath ":$dir";; esac case :$dllsearchpath: in *":$testbindir:"*) ;; ::) dllsearchpath=$testbindir;; *) func_append dllsearchpath ":$testbindir";; esac ;; esac continue ;; -l*) if test "X$arg" = "X-lc" || test "X$arg" = "X-lm"; then case $host in *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-beos* | *-cegcc* | *-*-haiku*) # These systems don't actually have a C or math library (as such) continue ;; *-*-os2*) # These systems don't actually have a C library (as such) test "X$arg" = "X-lc" && continue ;; *-*-openbsd* | *-*-freebsd* | *-*-dragonfly*) # Do not include libc due to us having libc/libc_r. test "X$arg" = "X-lc" && continue ;; *-*-rhapsody* | *-*-darwin1.[012]) # Rhapsody C and math libraries are in the System framework func_append deplibs " System.ltframework" continue ;; *-*-sco3.2v5* | *-*-sco5v6*) # Causes problems with __ctype test "X$arg" = "X-lc" && continue ;; *-*-sysv4.2uw2* | *-*-sysv5* | *-*-unixware* | *-*-OpenUNIX*) # Compiler inserts libc in the correct place for threads to work test "X$arg" = "X-lc" && continue ;; esac elif test "X$arg" = "X-lc_r"; then case $host in *-*-openbsd* | *-*-freebsd* | *-*-dragonfly*) # Do not include libc_r directly, use -pthread flag. continue ;; esac fi func_append deplibs " $arg" continue ;; -module) module=yes continue ;; # Tru64 UNIX uses -model [arg] to determine the layout of C++ # classes, name mangling, and exception handling. # Darwin uses the -arch flag to determine output architecture. -model|-arch|-isysroot|--sysroot) func_append compiler_flags " $arg" func_append compile_command " $arg" func_append finalize_command " $arg" prev=xcompiler continue ;; -mt|-mthreads|-kthread|-Kthread|-pthread|-pthreads|--thread-safe \ |-threads|-fopenmp|-openmp|-mp|-xopenmp|-omp|-qsmp=*) func_append compiler_flags " $arg" func_append compile_command " $arg" func_append finalize_command " $arg" case "$new_inherited_linker_flags " in *" $arg "*) ;; * ) func_append new_inherited_linker_flags " $arg" ;; esac continue ;; -multi_module) single_module="${wl}-multi_module" continue ;; -no-fast-install) fast_install=no continue ;; -no-install) case $host in *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-*-darwin* | *-cegcc*) # The PATH hackery in wrapper scripts is required on Windows # and Darwin in order for the loader to find any dlls it needs. func_warning "\`-no-install' is ignored for $host" func_warning "assuming \`-no-fast-install' instead" fast_install=no ;; *) no_install=yes ;; esac continue ;; -no-undefined) allow_undefined=no continue ;; -objectlist) prev=objectlist continue ;; -o) prev=output ;; -precious-files-regex) prev=precious_regex continue ;; -release) prev=release continue ;; -rpath) prev=rpath continue ;; -R) prev=xrpath continue ;; -R*) func_stripname '-R' '' "$arg" dir=$func_stripname_result # We need an absolute path. case $dir in [\\/]* | [A-Za-z]:[\\/]*) ;; =*) func_stripname '=' '' "$dir" dir=$lt_sysroot$func_stripname_result ;; *) func_fatal_error "only absolute run-paths are allowed" ;; esac case "$xrpath " in *" $dir "*) ;; *) func_append xrpath " $dir" ;; esac continue ;; -shared) # The effects of -shared are defined in a previous loop. continue ;; -shrext) prev=shrext continue ;; -static | -static-libtool-libs) # The effects of -static are defined in a previous loop. # We used to do the same as -all-static on platforms that # didn't have a PIC flag, but the assumption that the effects # would be equivalent was wrong. It would break on at least # Digital Unix and AIX. continue ;; -thread-safe) thread_safe=yes continue ;; -version-info) prev=vinfo continue ;; -version-number) prev=vinfo vinfo_number=yes continue ;; -weak) prev=weak continue ;; -Wc,*) func_stripname '-Wc,' '' "$arg" args=$func_stripname_result arg= save_ifs="$IFS"; IFS=',' for flag in $args; do IFS="$save_ifs" func_quote_for_eval "$flag" func_append arg " $func_quote_for_eval_result" func_append compiler_flags " $func_quote_for_eval_result" done IFS="$save_ifs" func_stripname ' ' '' "$arg" arg=$func_stripname_result ;; -Wl,*) func_stripname '-Wl,' '' "$arg" args=$func_stripname_result arg= save_ifs="$IFS"; IFS=',' for flag in $args; do IFS="$save_ifs" func_quote_for_eval "$flag" func_append arg " $wl$func_quote_for_eval_result" func_append compiler_flags " $wl$func_quote_for_eval_result" func_append linker_flags " $func_quote_for_eval_result" done IFS="$save_ifs" func_stripname ' ' '' "$arg" arg=$func_stripname_result ;; -Xcompiler) prev=xcompiler continue ;; -Xlinker) prev=xlinker continue ;; -XCClinker) prev=xcclinker continue ;; # -msg_* for osf cc -msg_*) func_quote_for_eval "$arg" arg="$func_quote_for_eval_result" ;; # Flags to be passed through unchanged, with rationale: # -64, -mips[0-9] enable 64-bit mode for the SGI compiler # -r[0-9][0-9]* specify processor for the SGI compiler # -xarch=*, -xtarget=* enable 64-bit mode for the Sun compiler # +DA*, +DD* enable 64-bit mode for the HP compiler # -q* compiler args for the IBM compiler # -m*, -t[45]*, -txscale* architecture-specific flags for GCC # -F/path path to uninstalled frameworks, gcc on darwin # -p, -pg, --coverage, -fprofile-* profiling flags for GCC # @file GCC response files # -tp=* Portland pgcc target processor selection # --sysroot=* for sysroot support # -O*, -flto*, -fwhopr*, -fuse-linker-plugin GCC link-time optimization -64|-mips[0-9]|-r[0-9][0-9]*|-xarch=*|-xtarget=*|+DA*|+DD*|-q*|-m*| \ -t[45]*|-txscale*|-p|-pg|--coverage|-fprofile-*|-F*|@*|-tp=*|--sysroot=*| \ -O*|-flto*|-fwhopr*|-fuse-linker-plugin) func_quote_for_eval "$arg" arg="$func_quote_for_eval_result" func_append compile_command " $arg" func_append finalize_command " $arg" func_append compiler_flags " $arg" continue ;; # Some other compiler flag. -* | +*) func_quote_for_eval "$arg" arg="$func_quote_for_eval_result" ;; *.$objext) # A standard object. func_append objs " $arg" ;; *.lo) # A libtool-controlled object. # Check to see that this really is a libtool object. if func_lalib_unsafe_p "$arg"; then pic_object= non_pic_object= # Read the .lo file func_source "$arg" if test -z "$pic_object" || test -z "$non_pic_object" || test "$pic_object" = none && test "$non_pic_object" = none; then func_fatal_error "cannot find name of object for \`$arg'" fi # Extract subdirectory from the argument. func_dirname "$arg" "/" "" xdir="$func_dirname_result" if test "$pic_object" != none; then # Prepend the subdirectory the object is found in. pic_object="$xdir$pic_object" if test "$prev" = dlfiles; then if test "$build_libtool_libs" = yes && test "$dlopen_support" = yes; then func_append dlfiles " $pic_object" prev= continue else # If libtool objects are unsupported, then we need to preload. prev=dlprefiles fi fi # CHECK ME: I think I busted this. -Ossama if test "$prev" = dlprefiles; then # Preload the old-style object. func_append dlprefiles " $pic_object" prev= fi # A PIC object. func_append libobjs " $pic_object" arg="$pic_object" fi # Non-PIC object. if test "$non_pic_object" != none; then # Prepend the subdirectory the object is found in. non_pic_object="$xdir$non_pic_object" # A standard non-PIC object func_append non_pic_objects " $non_pic_object" if test -z "$pic_object" || test "$pic_object" = none ; then arg="$non_pic_object" fi else # If the PIC object exists, use it instead. # $xdir was prepended to $pic_object above. non_pic_object="$pic_object" func_append non_pic_objects " $non_pic_object" fi else # Only an error if not doing a dry-run. if $opt_dry_run; then # Extract subdirectory from the argument. func_dirname "$arg" "/" "" xdir="$func_dirname_result" func_lo2o "$arg" pic_object=$xdir$objdir/$func_lo2o_result non_pic_object=$xdir$func_lo2o_result func_append libobjs " $pic_object" func_append non_pic_objects " $non_pic_object" else func_fatal_error "\`$arg' is not a valid libtool object" fi fi ;; *.$libext) # An archive. func_append deplibs " $arg" func_append old_deplibs " $arg" continue ;; *.la) # A libtool-controlled library. func_resolve_sysroot "$arg" if test "$prev" = dlfiles; then # This library was specified with -dlopen. func_append dlfiles " $func_resolve_sysroot_result" prev= elif test "$prev" = dlprefiles; then # The library was specified with -dlpreopen. func_append dlprefiles " $func_resolve_sysroot_result" prev= else func_append deplibs " $func_resolve_sysroot_result" fi continue ;; # Some other compiler argument. *) # Unknown arguments in both finalize_command and compile_command need # to be aesthetically quoted because they are evaled later. func_quote_for_eval "$arg" arg="$func_quote_for_eval_result" ;; esac # arg # Now actually substitute the argument into the commands. if test -n "$arg"; then func_append compile_command " $arg" func_append finalize_command " $arg" fi done # argument parsing loop test -n "$prev" && \ func_fatal_help "the \`$prevarg' option requires an argument" if test "$export_dynamic" = yes && test -n "$export_dynamic_flag_spec"; then eval arg=\"$export_dynamic_flag_spec\" func_append compile_command " $arg" func_append finalize_command " $arg" fi oldlibs= # calculate the name of the file, without its directory func_basename "$output" outputname="$func_basename_result" libobjs_save="$libobjs" if test -n "$shlibpath_var"; then # get the directories listed in $shlibpath_var eval shlib_search_path=\`\$ECHO \"\${$shlibpath_var}\" \| \$SED \'s/:/ /g\'\` else shlib_search_path= fi eval sys_lib_search_path=\"$sys_lib_search_path_spec\" eval sys_lib_dlsearch_path=\"$sys_lib_dlsearch_path_spec\" func_dirname "$output" "/" "" output_objdir="$func_dirname_result$objdir" func_to_tool_file "$output_objdir/" tool_output_objdir=$func_to_tool_file_result # Create the object directory. func_mkdir_p "$output_objdir" # Determine the type of output case $output in "") func_fatal_help "you must specify an output file" ;; *.$libext) linkmode=oldlib ;; *.lo | *.$objext) linkmode=obj ;; *.la) linkmode=lib ;; *) linkmode=prog ;; # Anything else should be a program. esac specialdeplibs= libs= # Find all interdependent deplibs by searching for libraries # that are linked more than once (e.g. -la -lb -la) for deplib in $deplibs; do if $opt_preserve_dup_deps ; then case "$libs " in *" $deplib "*) func_append specialdeplibs " $deplib" ;; esac fi func_append libs " $deplib" done if test "$linkmode" = lib; then libs="$predeps $libs $compiler_lib_search_path $postdeps" # Compute libraries that are listed more than once in $predeps # $postdeps and mark them as special (i.e., whose duplicates are # not to be eliminated). pre_post_deps= if $opt_duplicate_compiler_generated_deps; then for pre_post_dep in $predeps $postdeps; do case "$pre_post_deps " in *" $pre_post_dep "*) func_append specialdeplibs " $pre_post_deps" ;; esac func_append pre_post_deps " $pre_post_dep" done fi pre_post_deps= fi deplibs= newdependency_libs= newlib_search_path= need_relink=no # whether we're linking any uninstalled libtool libraries notinst_deplibs= # not-installed libtool libraries notinst_path= # paths that contain not-installed libtool libraries case $linkmode in lib) passes="conv dlpreopen link" for file in $dlfiles $dlprefiles; do case $file in *.la) ;; *) func_fatal_help "libraries can \`-dlopen' only libtool libraries: $file" ;; esac done ;; prog) compile_deplibs= finalize_deplibs= alldeplibs=no newdlfiles= newdlprefiles= passes="conv scan dlopen dlpreopen link" ;; *) passes="conv" ;; esac for pass in $passes; do # The preopen pass in lib mode reverses $deplibs; put it back here # so that -L comes before libs that need it for instance... if test "$linkmode,$pass" = "lib,link"; then ## FIXME: Find the place where the list is rebuilt in the wrong ## order, and fix it there properly tmp_deplibs= for deplib in $deplibs; do tmp_deplibs="$deplib $tmp_deplibs" done deplibs="$tmp_deplibs" fi if test "$linkmode,$pass" = "lib,link" || test "$linkmode,$pass" = "prog,scan"; then libs="$deplibs" deplibs= fi if test "$linkmode" = prog; then case $pass in dlopen) libs="$dlfiles" ;; dlpreopen) libs="$dlprefiles" ;; link) libs="$deplibs %DEPLIBS%" test "X$link_all_deplibs" != Xno && libs="$libs $dependency_libs" ;; esac fi if test "$linkmode,$pass" = "lib,dlpreopen"; then # Collect and forward deplibs of preopened libtool libs for lib in $dlprefiles; do # Ignore non-libtool-libs dependency_libs= func_resolve_sysroot "$lib" case $lib in *.la) func_source "$func_resolve_sysroot_result" ;; esac # Collect preopened libtool deplibs, except any this library # has declared as weak libs for deplib in $dependency_libs; do func_basename "$deplib" deplib_base=$func_basename_result case " $weak_libs " in *" $deplib_base "*) ;; *) func_append deplibs " $deplib" ;; esac done done libs="$dlprefiles" fi if test "$pass" = dlopen; then # Collect dlpreopened libraries save_deplibs="$deplibs" deplibs= fi for deplib in $libs; do lib= found=no case $deplib in -mt|-mthreads|-kthread|-Kthread|-pthread|-pthreads|--thread-safe \ |-threads|-fopenmp|-openmp|-mp|-xopenmp|-omp|-qsmp=*) if test "$linkmode,$pass" = "prog,link"; then compile_deplibs="$deplib $compile_deplibs" finalize_deplibs="$deplib $finalize_deplibs" else func_append compiler_flags " $deplib" if test "$linkmode" = lib ; then case "$new_inherited_linker_flags " in *" $deplib "*) ;; * ) func_append new_inherited_linker_flags " $deplib" ;; esac fi fi continue ;; -l*) if test "$linkmode" != lib && test "$linkmode" != prog; then func_warning "\`-l' is ignored for archives/objects" continue fi func_stripname '-l' '' "$deplib" name=$func_stripname_result if test "$linkmode" = lib; then searchdirs="$newlib_search_path $lib_search_path $compiler_lib_search_dirs $sys_lib_search_path $shlib_search_path" else searchdirs="$newlib_search_path $lib_search_path $sys_lib_search_path $shlib_search_path" fi for searchdir in $searchdirs; do for search_ext in .la $std_shrext .so .a; do # Search the libtool library lib="$searchdir/lib${name}${search_ext}" if test -f "$lib"; then if test "$search_ext" = ".la"; then found=yes else found=no fi break 2 fi done done if test "$found" != yes; then # deplib doesn't seem to be a libtool library if test "$linkmode,$pass" = "prog,link"; then compile_deplibs="$deplib $compile_deplibs" finalize_deplibs="$deplib $finalize_deplibs" else deplibs="$deplib $deplibs" test "$linkmode" = lib && newdependency_libs="$deplib $newdependency_libs" fi continue else # deplib is a libtool library # If $allow_libtool_libs_with_static_runtimes && $deplib is a stdlib, # We need to do some special things here, and not later. if test "X$allow_libtool_libs_with_static_runtimes" = "Xyes" ; then case " $predeps $postdeps " in *" $deplib "*) if func_lalib_p "$lib"; then library_names= old_library= func_source "$lib" for l in $old_library $library_names; do ll="$l" done if test "X$ll" = "X$old_library" ; then # only static version available found=no func_dirname "$lib" "" "." ladir="$func_dirname_result" lib=$ladir/$old_library if test "$linkmode,$pass" = "prog,link"; then compile_deplibs="$deplib $compile_deplibs" finalize_deplibs="$deplib $finalize_deplibs" else deplibs="$deplib $deplibs" test "$linkmode" = lib && newdependency_libs="$deplib $newdependency_libs" fi continue fi fi ;; *) ;; esac fi fi ;; # -l *.ltframework) if test "$linkmode,$pass" = "prog,link"; then compile_deplibs="$deplib $compile_deplibs" finalize_deplibs="$deplib $finalize_deplibs" else deplibs="$deplib $deplibs" if test "$linkmode" = lib ; then case "$new_inherited_linker_flags " in *" $deplib "*) ;; * ) func_append new_inherited_linker_flags " $deplib" ;; esac fi fi continue ;; -L*) case $linkmode in lib) deplibs="$deplib $deplibs" test "$pass" = conv && continue newdependency_libs="$deplib $newdependency_libs" func_stripname '-L' '' "$deplib" func_resolve_sysroot "$func_stripname_result" func_append newlib_search_path " $func_resolve_sysroot_result" ;; prog) if test "$pass" = conv; then deplibs="$deplib $deplibs" continue fi if test "$pass" = scan; then deplibs="$deplib $deplibs" else compile_deplibs="$deplib $compile_deplibs" finalize_deplibs="$deplib $finalize_deplibs" fi func_stripname '-L' '' "$deplib" func_resolve_sysroot "$func_stripname_result" func_append newlib_search_path " $func_resolve_sysroot_result" ;; *) func_warning "\`-L' is ignored for archives/objects" ;; esac # linkmode continue ;; # -L -R*) if test "$pass" = link; then func_stripname '-R' '' "$deplib" func_resolve_sysroot "$func_stripname_result" dir=$func_resolve_sysroot_result # Make sure the xrpath contains only unique directories. case "$xrpath " in *" $dir "*) ;; *) func_append xrpath " $dir" ;; esac fi deplibs="$deplib $deplibs" continue ;; *.la) func_resolve_sysroot "$deplib" lib=$func_resolve_sysroot_result ;; *.$libext) if test "$pass" = conv; then deplibs="$deplib $deplibs" continue fi case $linkmode in lib) # Linking convenience modules into shared libraries is allowed, # but linking other static libraries is non-portable. case " $dlpreconveniencelibs " in *" $deplib "*) ;; *) valid_a_lib=no case $deplibs_check_method in match_pattern*) set dummy $deplibs_check_method; shift match_pattern_regex=`expr "$deplibs_check_method" : "$1 \(.*\)"` if eval "\$ECHO \"$deplib\"" 2>/dev/null | $SED 10q \ | $EGREP "$match_pattern_regex" > /dev/null; then valid_a_lib=yes fi ;; pass_all) valid_a_lib=yes ;; esac if test "$valid_a_lib" != yes; then echo $ECHO "*** Warning: Trying to link with static lib archive $deplib." echo "*** I have the capability to make that library automatically link in when" echo "*** you link to this library. But I can only do this if you have a" echo "*** shared version of the library, which you do not appear to have" echo "*** because the file extensions .$libext of this argument makes me believe" echo "*** that it is just a static archive that I should not use here." else echo $ECHO "*** Warning: Linking the shared library $output against the" $ECHO "*** static library $deplib is not portable!" deplibs="$deplib $deplibs" fi ;; esac continue ;; prog) if test "$pass" != link; then deplibs="$deplib $deplibs" else compile_deplibs="$deplib $compile_deplibs" finalize_deplibs="$deplib $finalize_deplibs" fi continue ;; esac # linkmode ;; # *.$libext *.lo | *.$objext) if test "$pass" = conv; then deplibs="$deplib $deplibs" elif test "$linkmode" = prog; then if test "$pass" = dlpreopen || test "$dlopen_support" != yes || test "$build_libtool_libs" = no; then # If there is no dlopen support or we're linking statically, # we need to preload. func_append newdlprefiles " $deplib" compile_deplibs="$deplib $compile_deplibs" finalize_deplibs="$deplib $finalize_deplibs" else func_append newdlfiles " $deplib" fi fi continue ;; %DEPLIBS%) alldeplibs=yes continue ;; esac # case $deplib if test "$found" = yes || test -f "$lib"; then : else func_fatal_error "cannot find the library \`$lib' or unhandled argument \`$deplib'" fi # Check to see that this really is a libtool archive. func_lalib_unsafe_p "$lib" \ || func_fatal_error "\`$lib' is not a valid libtool archive" func_dirname "$lib" "" "." ladir="$func_dirname_result" dlname= dlopen= dlpreopen= libdir= library_names= old_library= inherited_linker_flags= # If the library was installed with an old release of libtool, # it will not redefine variables installed, or shouldnotlink installed=yes shouldnotlink=no avoidtemprpath= # Read the .la file func_source "$lib" # Convert "-framework foo" to "foo.ltframework" if test -n "$inherited_linker_flags"; then tmp_inherited_linker_flags=`$ECHO "$inherited_linker_flags" | $SED 's/-framework \([^ $]*\)/\1.ltframework/g'` for tmp_inherited_linker_flag in $tmp_inherited_linker_flags; do case " $new_inherited_linker_flags " in *" $tmp_inherited_linker_flag "*) ;; *) func_append new_inherited_linker_flags " $tmp_inherited_linker_flag";; esac done fi dependency_libs=`$ECHO " $dependency_libs" | $SED 's% \([^ $]*\).ltframework% -framework \1%g'` if test "$linkmode,$pass" = "lib,link" || test "$linkmode,$pass" = "prog,scan" || { test "$linkmode" != prog && test "$linkmode" != lib; }; then test -n "$dlopen" && func_append dlfiles " $dlopen" test -n "$dlpreopen" && func_append dlprefiles " $dlpreopen" fi if test "$pass" = conv; then # Only check for convenience libraries deplibs="$lib $deplibs" if test -z "$libdir"; then if test -z "$old_library"; then func_fatal_error "cannot find name of link library for \`$lib'" fi # It is a libtool convenience library, so add in its objects. func_append convenience " $ladir/$objdir/$old_library" func_append old_convenience " $ladir/$objdir/$old_library" tmp_libs= for deplib in $dependency_libs; do deplibs="$deplib $deplibs" if $opt_preserve_dup_deps ; then case "$tmp_libs " in *" $deplib "*) func_append specialdeplibs " $deplib" ;; esac fi func_append tmp_libs " $deplib" done elif test "$linkmode" != prog && test "$linkmode" != lib; then func_fatal_error "\`$lib' is not a convenience library" fi continue fi # $pass = conv # Get the name of the library we link against. linklib= if test -n "$old_library" && { test "$prefer_static_libs" = yes || test "$prefer_static_libs,$installed" = "built,no"; }; then linklib=$old_library else for l in $old_library $library_names; do linklib="$l" done fi if test -z "$linklib"; then func_fatal_error "cannot find name of link library for \`$lib'" fi # This library was specified with -dlopen. if test "$pass" = dlopen; then if test -z "$libdir"; then func_fatal_error "cannot -dlopen a convenience library: \`$lib'" fi if test -z "$dlname" || test "$dlopen_support" != yes || test "$build_libtool_libs" = no; then # If there is no dlname, no dlopen support or we're linking # statically, we need to preload. We also need to preload any # dependent libraries so libltdl's deplib preloader doesn't # bomb out in the load deplibs phase. func_append dlprefiles " $lib $dependency_libs" else func_append newdlfiles " $lib" fi continue fi # $pass = dlopen # We need an absolute path. case $ladir in [\\/]* | [A-Za-z]:[\\/]*) abs_ladir="$ladir" ;; *) abs_ladir=`cd "$ladir" && pwd` if test -z "$abs_ladir"; then func_warning "cannot determine absolute directory name of \`$ladir'" func_warning "passing it literally to the linker, although it might fail" abs_ladir="$ladir" fi ;; esac func_basename "$lib" laname="$func_basename_result" # Find the relevant object directory and library name. if test "X$installed" = Xyes; then if test ! -f "$lt_sysroot$libdir/$linklib" && test -f "$abs_ladir/$linklib"; then func_warning "library \`$lib' was moved." dir="$ladir" absdir="$abs_ladir" libdir="$abs_ladir" else dir="$lt_sysroot$libdir" absdir="$lt_sysroot$libdir" fi test "X$hardcode_automatic" = Xyes && avoidtemprpath=yes else if test ! -f "$ladir/$objdir/$linklib" && test -f "$abs_ladir/$linklib"; then dir="$ladir" absdir="$abs_ladir" # Remove this search path later func_append notinst_path " $abs_ladir" else dir="$ladir/$objdir" absdir="$abs_ladir/$objdir" # Remove this search path later func_append notinst_path " $abs_ladir" fi fi # $installed = yes func_stripname 'lib' '.la' "$laname" name=$func_stripname_result # This library was specified with -dlpreopen. if test "$pass" = dlpreopen; then if test -z "$libdir" && test "$linkmode" = prog; then func_fatal_error "only libraries may -dlpreopen a convenience library: \`$lib'" fi case "$host" in # special handling for platforms with PE-DLLs. *cygwin* | *mingw* | *cegcc* ) # Linker will automatically link against shared library if both # static and shared are present. Therefore, ensure we extract # symbols from the import library if a shared library is present # (otherwise, the dlopen module name will be incorrect). We do # this by putting the import library name into $newdlprefiles. # We recover the dlopen module name by 'saving' the la file # name in a special purpose variable, and (later) extracting the # dlname from the la file. if test -n "$dlname"; then func_tr_sh "$dir/$linklib" eval "libfile_$func_tr_sh_result=\$abs_ladir/\$laname" func_append newdlprefiles " $dir/$linklib" else func_append newdlprefiles " $dir/$old_library" # Keep a list of preopened convenience libraries to check # that they are being used correctly in the link pass. test -z "$libdir" && \ func_append dlpreconveniencelibs " $dir/$old_library" fi ;; * ) # Prefer using a static library (so that no silly _DYNAMIC symbols # are required to link). if test -n "$old_library"; then func_append newdlprefiles " $dir/$old_library" # Keep a list of preopened convenience libraries to check # that they are being used correctly in the link pass. test -z "$libdir" && \ func_append dlpreconveniencelibs " $dir/$old_library" # Otherwise, use the dlname, so that lt_dlopen finds it. elif test -n "$dlname"; then func_append newdlprefiles " $dir/$dlname" else func_append newdlprefiles " $dir/$linklib" fi ;; esac fi # $pass = dlpreopen if test -z "$libdir"; then # Link the convenience library if test "$linkmode" = lib; then deplibs="$dir/$old_library $deplibs" elif test "$linkmode,$pass" = "prog,link"; then compile_deplibs="$dir/$old_library $compile_deplibs" finalize_deplibs="$dir/$old_library $finalize_deplibs" else deplibs="$lib $deplibs" # used for prog,scan pass fi continue fi if test "$linkmode" = prog && test "$pass" != link; then func_append newlib_search_path " $ladir" deplibs="$lib $deplibs" linkalldeplibs=no if test "$link_all_deplibs" != no || test -z "$library_names" || test "$build_libtool_libs" = no; then linkalldeplibs=yes fi tmp_libs= for deplib in $dependency_libs; do case $deplib in -L*) func_stripname '-L' '' "$deplib" func_resolve_sysroot "$func_stripname_result" func_append newlib_search_path " $func_resolve_sysroot_result" ;; esac # Need to link against all dependency_libs? if test "$linkalldeplibs" = yes; then deplibs="$deplib $deplibs" else # Need to hardcode shared library paths # or/and link against static libraries newdependency_libs="$deplib $newdependency_libs" fi if $opt_preserve_dup_deps ; then case "$tmp_libs " in *" $deplib "*) func_append specialdeplibs " $deplib" ;; esac fi func_append tmp_libs " $deplib" done # for deplib continue fi # $linkmode = prog... if test "$linkmode,$pass" = "prog,link"; then if test -n "$library_names" && { { test "$prefer_static_libs" = no || test "$prefer_static_libs,$installed" = "built,yes"; } || test -z "$old_library"; }; then # We need to hardcode the library path if test -n "$shlibpath_var" && test -z "$avoidtemprpath" ; then # Make sure the rpath contains only unique directories. case "$temp_rpath:" in *"$absdir:"*) ;; *) func_append temp_rpath "$absdir:" ;; esac fi # Hardcode the library path. # Skip directories that are in the system default run-time # search path. case " $sys_lib_dlsearch_path " in *" $absdir "*) ;; *) case "$compile_rpath " in *" $absdir "*) ;; *) func_append compile_rpath " $absdir" ;; esac ;; esac case " $sys_lib_dlsearch_path " in *" $libdir "*) ;; *) case "$finalize_rpath " in *" $libdir "*) ;; *) func_append finalize_rpath " $libdir" ;; esac ;; esac fi # $linkmode,$pass = prog,link... if test "$alldeplibs" = yes && { test "$deplibs_check_method" = pass_all || { test "$build_libtool_libs" = yes && test -n "$library_names"; }; }; then # We only need to search for static libraries continue fi fi link_static=no # Whether the deplib will be linked statically use_static_libs=$prefer_static_libs if test "$use_static_libs" = built && test "$installed" = yes; then use_static_libs=no fi if test -n "$library_names" && { test "$use_static_libs" = no || test -z "$old_library"; }; then case $host in *cygwin* | *mingw* | *cegcc*) # No point in relinking DLLs because paths are not encoded func_append notinst_deplibs " $lib" need_relink=no ;; *) if test "$installed" = no; then func_append notinst_deplibs " $lib" need_relink=yes fi ;; esac # This is a shared library # Warn about portability, can't link against -module's on some # systems (darwin). Don't bleat about dlopened modules though! dlopenmodule="" for dlpremoduletest in $dlprefiles; do if test "X$dlpremoduletest" = "X$lib"; then dlopenmodule="$dlpremoduletest" break fi done if test -z "$dlopenmodule" && test "$shouldnotlink" = yes && test "$pass" = link; then echo if test "$linkmode" = prog; then $ECHO "*** Warning: Linking the executable $output against the loadable module" else $ECHO "*** Warning: Linking the shared library $output against the loadable module" fi $ECHO "*** $linklib is not portable!" fi if test "$linkmode" = lib && test "$hardcode_into_libs" = yes; then # Hardcode the library path. # Skip directories that are in the system default run-time # search path. case " $sys_lib_dlsearch_path " in *" $absdir "*) ;; *) case "$compile_rpath " in *" $absdir "*) ;; *) func_append compile_rpath " $absdir" ;; esac ;; esac case " $sys_lib_dlsearch_path " in *" $libdir "*) ;; *) case "$finalize_rpath " in *" $libdir "*) ;; *) func_append finalize_rpath " $libdir" ;; esac ;; esac fi if test -n "$old_archive_from_expsyms_cmds"; then # figure out the soname set dummy $library_names shift realname="$1" shift libname=`eval "\\$ECHO \"$libname_spec\""` # use dlname if we got it. it's perfectly good, no? if test -n "$dlname"; then soname="$dlname" elif test -n "$soname_spec"; then # bleh windows case $host in *cygwin* | mingw* | *cegcc*) func_arith $current - $age major=$func_arith_result versuffix="-$major" ;; esac eval soname=\"$soname_spec\" else soname="$realname" fi # Make a new name for the extract_expsyms_cmds to use soroot="$soname" func_basename "$soroot" soname="$func_basename_result" func_stripname 'lib' '.dll' "$soname" newlib=libimp-$func_stripname_result.a # If the library has no export list, then create one now if test -f "$output_objdir/$soname-def"; then : else func_verbose "extracting exported symbol list from \`$soname'" func_execute_cmds "$extract_expsyms_cmds" 'exit $?' fi # Create $newlib if test -f "$output_objdir/$newlib"; then :; else func_verbose "generating import library for \`$soname'" func_execute_cmds "$old_archive_from_expsyms_cmds" 'exit $?' fi # make sure the library variables are pointing to the new library dir=$output_objdir linklib=$newlib fi # test -n "$old_archive_from_expsyms_cmds" if test "$linkmode" = prog || test "$opt_mode" != relink; then add_shlibpath= add_dir= add= lib_linked=yes case $hardcode_action in immediate | unsupported) if test "$hardcode_direct" = no; then add="$dir/$linklib" case $host in *-*-sco3.2v5.0.[024]*) add_dir="-L$dir" ;; *-*-sysv4*uw2*) add_dir="-L$dir" ;; *-*-sysv5OpenUNIX* | *-*-sysv5UnixWare7.[01].[10]* | \ *-*-unixware7*) add_dir="-L$dir" ;; *-*-darwin* ) # if the lib is a (non-dlopened) module then we can not # link against it, someone is ignoring the earlier warnings if /usr/bin/file -L $add 2> /dev/null | $GREP ": [^:]* bundle" >/dev/null ; then if test "X$dlopenmodule" != "X$lib"; then $ECHO "*** Warning: lib $linklib is a module, not a shared library" if test -z "$old_library" ; then echo echo "*** And there doesn't seem to be a static archive available" echo "*** The link will probably fail, sorry" else add="$dir/$old_library" fi elif test -n "$old_library"; then add="$dir/$old_library" fi fi esac elif test "$hardcode_minus_L" = no; then case $host in *-*-sunos*) add_shlibpath="$dir" ;; esac add_dir="-L$dir" add="-l$name" elif test "$hardcode_shlibpath_var" = no; then add_shlibpath="$dir" add="-l$name" else lib_linked=no fi ;; relink) if test "$hardcode_direct" = yes && test "$hardcode_direct_absolute" = no; then add="$dir/$linklib" elif test "$hardcode_minus_L" = yes; then add_dir="-L$absdir" # Try looking first in the location we're being installed to. if test -n "$inst_prefix_dir"; then case $libdir in [\\/]*) func_append add_dir " -L$inst_prefix_dir$libdir" ;; esac fi add="-l$name" elif test "$hardcode_shlibpath_var" = yes; then add_shlibpath="$dir" add="-l$name" else lib_linked=no fi ;; *) lib_linked=no ;; esac if test "$lib_linked" != yes; then func_fatal_configuration "unsupported hardcode properties" fi if test -n "$add_shlibpath"; then case :$compile_shlibpath: in *":$add_shlibpath:"*) ;; *) func_append compile_shlibpath "$add_shlibpath:" ;; esac fi if test "$linkmode" = prog; then test -n "$add_dir" && compile_deplibs="$add_dir $compile_deplibs" test -n "$add" && compile_deplibs="$add $compile_deplibs" else test -n "$add_dir" && deplibs="$add_dir $deplibs" test -n "$add" && deplibs="$add $deplibs" if test "$hardcode_direct" != yes && test "$hardcode_minus_L" != yes && test "$hardcode_shlibpath_var" = yes; then case :$finalize_shlibpath: in *":$libdir:"*) ;; *) func_append finalize_shlibpath "$libdir:" ;; esac fi fi fi if test "$linkmode" = prog || test "$opt_mode" = relink; then add_shlibpath= add_dir= add= # Finalize command for both is simple: just hardcode it. if test "$hardcode_direct" = yes && test "$hardcode_direct_absolute" = no; then add="$libdir/$linklib" elif test "$hardcode_minus_L" = yes; then add_dir="-L$libdir" add="-l$name" elif test "$hardcode_shlibpath_var" = yes; then case :$finalize_shlibpath: in *":$libdir:"*) ;; *) func_append finalize_shlibpath "$libdir:" ;; esac add="-l$name" elif test "$hardcode_automatic" = yes; then if test -n "$inst_prefix_dir" && test -f "$inst_prefix_dir$libdir/$linklib" ; then add="$inst_prefix_dir$libdir/$linklib" else add="$libdir/$linklib" fi else # We cannot seem to hardcode it, guess we'll fake it. add_dir="-L$libdir" # Try looking first in the location we're being installed to. if test -n "$inst_prefix_dir"; then case $libdir in [\\/]*) func_append add_dir " -L$inst_prefix_dir$libdir" ;; esac fi add="-l$name" fi if test "$linkmode" = prog; then test -n "$add_dir" && finalize_deplibs="$add_dir $finalize_deplibs" test -n "$add" && finalize_deplibs="$add $finalize_deplibs" else test -n "$add_dir" && deplibs="$add_dir $deplibs" test -n "$add" && deplibs="$add $deplibs" fi fi elif test "$linkmode" = prog; then # Here we assume that one of hardcode_direct or hardcode_minus_L # is not unsupported. This is valid on all known static and # shared platforms. if test "$hardcode_direct" != unsupported; then test -n "$old_library" && linklib="$old_library" compile_deplibs="$dir/$linklib $compile_deplibs" finalize_deplibs="$dir/$linklib $finalize_deplibs" else compile_deplibs="-l$name -L$dir $compile_deplibs" finalize_deplibs="-l$name -L$dir $finalize_deplibs" fi elif test "$build_libtool_libs" = yes; then # Not a shared library if test "$deplibs_check_method" != pass_all; then # We're trying link a shared library against a static one # but the system doesn't support it. # Just print a warning and add the library to dependency_libs so # that the program can be linked against the static library. echo $ECHO "*** Warning: This system can not link to static lib archive $lib." echo "*** I have the capability to make that library automatically link in when" echo "*** you link to this library. But I can only do this if you have a" echo "*** shared version of the library, which you do not appear to have." if test "$module" = yes; then echo "*** But as you try to build a module library, libtool will still create " echo "*** a static module, that should work as long as the dlopening application" echo "*** is linked with the -dlopen flag to resolve symbols at runtime." if test -z "$global_symbol_pipe"; then echo echo "*** However, this would only work if libtool was able to extract symbol" echo "*** lists from a program, using \`nm' or equivalent, but libtool could" echo "*** not find such a program. So, this module is probably useless." echo "*** \`nm' from GNU binutils and a full rebuild may help." fi if test "$build_old_libs" = no; then build_libtool_libs=module build_old_libs=yes else build_libtool_libs=no fi fi else deplibs="$dir/$old_library $deplibs" link_static=yes fi fi # link shared/static library? if test "$linkmode" = lib; then if test -n "$dependency_libs" && { test "$hardcode_into_libs" != yes || test "$build_old_libs" = yes || test "$link_static" = yes; }; then # Extract -R from dependency_libs temp_deplibs= for libdir in $dependency_libs; do case $libdir in -R*) func_stripname '-R' '' "$libdir" temp_xrpath=$func_stripname_result case " $xrpath " in *" $temp_xrpath "*) ;; *) func_append xrpath " $temp_xrpath";; esac;; *) func_append temp_deplibs " $libdir";; esac done dependency_libs="$temp_deplibs" fi func_append newlib_search_path " $absdir" # Link against this library test "$link_static" = no && newdependency_libs="$abs_ladir/$laname $newdependency_libs" # ... and its dependency_libs tmp_libs= for deplib in $dependency_libs; do newdependency_libs="$deplib $newdependency_libs" case $deplib in -L*) func_stripname '-L' '' "$deplib" func_resolve_sysroot "$func_stripname_result";; *) func_resolve_sysroot "$deplib" ;; esac if $opt_preserve_dup_deps ; then case "$tmp_libs " in *" $func_resolve_sysroot_result "*) func_append specialdeplibs " $func_resolve_sysroot_result" ;; esac fi func_append tmp_libs " $func_resolve_sysroot_result" done if test "$link_all_deplibs" != no; then # Add the search paths of all dependency libraries for deplib in $dependency_libs; do path= case $deplib in -L*) path="$deplib" ;; *.la) func_resolve_sysroot "$deplib" deplib=$func_resolve_sysroot_result func_dirname "$deplib" "" "." dir=$func_dirname_result # We need an absolute path. case $dir in [\\/]* | [A-Za-z]:[\\/]*) absdir="$dir" ;; *) absdir=`cd "$dir" && pwd` if test -z "$absdir"; then func_warning "cannot determine absolute directory name of \`$dir'" absdir="$dir" fi ;; esac if $GREP "^installed=no" $deplib > /dev/null; then case $host in *-*-darwin*) depdepl= eval deplibrary_names=`${SED} -n -e 's/^library_names=\(.*\)$/\1/p' $deplib` if test -n "$deplibrary_names" ; then for tmp in $deplibrary_names ; do depdepl=$tmp done if test -f "$absdir/$objdir/$depdepl" ; then depdepl="$absdir/$objdir/$depdepl" darwin_install_name=`${OTOOL} -L $depdepl | awk '{if (NR == 2) {print $1;exit}}'` if test -z "$darwin_install_name"; then darwin_install_name=`${OTOOL64} -L $depdepl | awk '{if (NR == 2) {print $1;exit}}'` fi func_append compiler_flags " ${wl}-dylib_file ${wl}${darwin_install_name}:${depdepl}" func_append linker_flags " -dylib_file ${darwin_install_name}:${depdepl}" path= fi fi ;; *) path="-L$absdir/$objdir" ;; esac else eval libdir=`${SED} -n -e 's/^libdir=\(.*\)$/\1/p' $deplib` test -z "$libdir" && \ func_fatal_error "\`$deplib' is not a valid libtool archive" test "$absdir" != "$libdir" && \ func_warning "\`$deplib' seems to be moved" path="-L$absdir" fi ;; esac case " $deplibs " in *" $path "*) ;; *) deplibs="$path $deplibs" ;; esac done fi # link_all_deplibs != no fi # linkmode = lib done # for deplib in $libs if test "$pass" = link; then if test "$linkmode" = "prog"; then compile_deplibs="$new_inherited_linker_flags $compile_deplibs" finalize_deplibs="$new_inherited_linker_flags $finalize_deplibs" else compiler_flags="$compiler_flags "`$ECHO " $new_inherited_linker_flags" | $SED 's% \([^ $]*\).ltframework% -framework \1%g'` fi fi dependency_libs="$newdependency_libs" if test "$pass" = dlpreopen; then # Link the dlpreopened libraries before other libraries for deplib in $save_deplibs; do deplibs="$deplib $deplibs" done fi if test "$pass" != dlopen; then if test "$pass" != conv; then # Make sure lib_search_path contains only unique directories. lib_search_path= for dir in $newlib_search_path; do case "$lib_search_path " in *" $dir "*) ;; *) func_append lib_search_path " $dir" ;; esac done newlib_search_path= fi if test "$linkmode,$pass" != "prog,link"; then vars="deplibs" else vars="compile_deplibs finalize_deplibs" fi for var in $vars dependency_libs; do # Add libraries to $var in reverse order eval tmp_libs=\"\$$var\" new_libs= for deplib in $tmp_libs; do # FIXME: Pedantically, this is the right thing to do, so # that some nasty dependency loop isn't accidentally # broken: #new_libs="$deplib $new_libs" # Pragmatically, this seems to cause very few problems in # practice: case $deplib in -L*) new_libs="$deplib $new_libs" ;; -R*) ;; *) # And here is the reason: when a library appears more # than once as an explicit dependence of a library, or # is implicitly linked in more than once by the # compiler, it is considered special, and multiple # occurrences thereof are not removed. Compare this # with having the same library being listed as a # dependency of multiple other libraries: in this case, # we know (pedantically, we assume) the library does not # need to be listed more than once, so we keep only the # last copy. This is not always right, but it is rare # enough that we require users that really mean to play # such unportable linking tricks to link the library # using -Wl,-lname, so that libtool does not consider it # for duplicate removal. case " $specialdeplibs " in *" $deplib "*) new_libs="$deplib $new_libs" ;; *) case " $new_libs " in *" $deplib "*) ;; *) new_libs="$deplib $new_libs" ;; esac ;; esac ;; esac done tmp_libs= for deplib in $new_libs; do case $deplib in -L*) case " $tmp_libs " in *" $deplib "*) ;; *) func_append tmp_libs " $deplib" ;; esac ;; *) func_append tmp_libs " $deplib" ;; esac done eval $var=\"$tmp_libs\" done # for var fi # Last step: remove runtime libs from dependency_libs # (they stay in deplibs) tmp_libs= for i in $dependency_libs ; do case " $predeps $postdeps $compiler_lib_search_path " in *" $i "*) i="" ;; esac if test -n "$i" ; then func_append tmp_libs " $i" fi done dependency_libs=$tmp_libs done # for pass if test "$linkmode" = prog; then dlfiles="$newdlfiles" fi if test "$linkmode" = prog || test "$linkmode" = lib; then dlprefiles="$newdlprefiles" fi case $linkmode in oldlib) if test -n "$dlfiles$dlprefiles" || test "$dlself" != no; then func_warning "\`-dlopen' is ignored for archives" fi case " $deplibs" in *\ -l* | *\ -L*) func_warning "\`-l' and \`-L' are ignored for archives" ;; esac test -n "$rpath" && \ func_warning "\`-rpath' is ignored for archives" test -n "$xrpath" && \ func_warning "\`-R' is ignored for archives" test -n "$vinfo" && \ func_warning "\`-version-info/-version-number' is ignored for archives" test -n "$release" && \ func_warning "\`-release' is ignored for archives" test -n "$export_symbols$export_symbols_regex" && \ func_warning "\`-export-symbols' is ignored for archives" # Now set the variables for building old libraries. build_libtool_libs=no oldlibs="$output" func_append objs "$old_deplibs" ;; lib) # Make sure we only generate libraries of the form `libNAME.la'. case $outputname in lib*) func_stripname 'lib' '.la' "$outputname" name=$func_stripname_result eval shared_ext=\"$shrext_cmds\" eval libname=\"$libname_spec\" ;; *) test "$module" = no && \ func_fatal_help "libtool library \`$output' must begin with \`lib'" if test "$need_lib_prefix" != no; then # Add the "lib" prefix for modules if required func_stripname '' '.la' "$outputname" name=$func_stripname_result eval shared_ext=\"$shrext_cmds\" eval libname=\"$libname_spec\" else func_stripname '' '.la' "$outputname" libname=$func_stripname_result fi ;; esac if test -n "$objs"; then if test "$deplibs_check_method" != pass_all; then func_fatal_error "cannot build libtool library \`$output' from non-libtool objects on this host:$objs" else echo $ECHO "*** Warning: Linking the shared library $output against the non-libtool" $ECHO "*** objects $objs is not portable!" func_append libobjs " $objs" fi fi test "$dlself" != no && \ func_warning "\`-dlopen self' is ignored for libtool libraries" set dummy $rpath shift test "$#" -gt 1 && \ func_warning "ignoring multiple \`-rpath's for a libtool library" install_libdir="$1" oldlibs= if test -z "$rpath"; then if test "$build_libtool_libs" = yes; then # Building a libtool convenience library. # Some compilers have problems with a `.al' extension so # convenience libraries should have the same extension an # archive normally would. oldlibs="$output_objdir/$libname.$libext $oldlibs" build_libtool_libs=convenience build_old_libs=yes fi test -n "$vinfo" && \ func_warning "\`-version-info/-version-number' is ignored for convenience libraries" test -n "$release" && \ func_warning "\`-release' is ignored for convenience libraries" else # Parse the version information argument. save_ifs="$IFS"; IFS=':' set dummy $vinfo 0 0 0 shift IFS="$save_ifs" test -n "$7" && \ func_fatal_help "too many parameters to \`-version-info'" # convert absolute version numbers to libtool ages # this retains compatibility with .la files and attempts # to make the code below a bit more comprehensible case $vinfo_number in yes) number_major="$1" number_minor="$2" number_revision="$3" # # There are really only two kinds -- those that # use the current revision as the major version # and those that subtract age and use age as # a minor version. But, then there is irix # which has an extra 1 added just for fun # case $version_type in # correct linux to gnu/linux during the next big refactor darwin|linux|osf|windows|none) func_arith $number_major + $number_minor current=$func_arith_result age="$number_minor" revision="$number_revision" ;; freebsd-aout|freebsd-elf|qnx|sunos) current="$number_major" revision="$number_minor" age="0" ;; irix|nonstopux) func_arith $number_major + $number_minor current=$func_arith_result age="$number_minor" revision="$number_minor" lt_irix_increment=no ;; *) func_fatal_configuration "$modename: unknown library version type \`$version_type'" ;; esac ;; no) current="$1" revision="$2" age="$3" ;; esac # Check that each of the things are valid numbers. case $current in 0|[1-9]|[1-9][0-9]|[1-9][0-9][0-9]|[1-9][0-9][0-9][0-9]|[1-9][0-9][0-9][0-9][0-9]) ;; *) func_error "CURRENT \`$current' must be a nonnegative integer" func_fatal_error "\`$vinfo' is not valid version information" ;; esac case $revision in 0|[1-9]|[1-9][0-9]|[1-9][0-9][0-9]|[1-9][0-9][0-9][0-9]|[1-9][0-9][0-9][0-9][0-9]) ;; *) func_error "REVISION \`$revision' must be a nonnegative integer" func_fatal_error "\`$vinfo' is not valid version information" ;; esac case $age in 0|[1-9]|[1-9][0-9]|[1-9][0-9][0-9]|[1-9][0-9][0-9][0-9]|[1-9][0-9][0-9][0-9][0-9]) ;; *) func_error "AGE \`$age' must be a nonnegative integer" func_fatal_error "\`$vinfo' is not valid version information" ;; esac if test "$age" -gt "$current"; then func_error "AGE \`$age' is greater than the current interface number \`$current'" func_fatal_error "\`$vinfo' is not valid version information" fi # Calculate the version variables. major= versuffix= verstring= case $version_type in none) ;; darwin) # Like Linux, but with the current version available in # verstring for coding it into the library header func_arith $current - $age major=.$func_arith_result versuffix="$major.$age.$revision" # Darwin ld doesn't like 0 for these options... func_arith $current + 1 minor_current=$func_arith_result xlcverstring="${wl}-compatibility_version ${wl}$minor_current ${wl}-current_version ${wl}$minor_current.$revision" verstring="-compatibility_version $minor_current -current_version $minor_current.$revision" ;; freebsd-aout) major=".$current" versuffix=".$current.$revision"; ;; freebsd-elf) major=".$current" versuffix=".$current" ;; irix | nonstopux) if test "X$lt_irix_increment" = "Xno"; then func_arith $current - $age else func_arith $current - $age + 1 fi major=$func_arith_result case $version_type in nonstopux) verstring_prefix=nonstopux ;; *) verstring_prefix=sgi ;; esac verstring="$verstring_prefix$major.$revision" # Add in all the interfaces that we are compatible with. loop=$revision while test "$loop" -ne 0; do func_arith $revision - $loop iface=$func_arith_result func_arith $loop - 1 loop=$func_arith_result verstring="$verstring_prefix$major.$iface:$verstring" done # Before this point, $major must not contain `.'. major=.$major versuffix="$major.$revision" ;; linux) # correct to gnu/linux during the next big refactor func_arith $current - $age major=.$func_arith_result versuffix="$major.$age.$revision" ;; osf) func_arith $current - $age major=.$func_arith_result versuffix=".$current.$age.$revision" verstring="$current.$age.$revision" # Add in all the interfaces that we are compatible with. loop=$age while test "$loop" -ne 0; do func_arith $current - $loop iface=$func_arith_result func_arith $loop - 1 loop=$func_arith_result verstring="$verstring:${iface}.0" done # Make executables depend on our current version. func_append verstring ":${current}.0" ;; qnx) major=".$current" versuffix=".$current" ;; sunos) major=".$current" versuffix=".$current.$revision" ;; windows) # Use '-' rather than '.', since we only want one # extension on DOS 8.3 filesystems. func_arith $current - $age major=$func_arith_result versuffix="-$major" ;; *) func_fatal_configuration "unknown library version type \`$version_type'" ;; esac # Clear the version info if we defaulted, and they specified a release. if test -z "$vinfo" && test -n "$release"; then major= case $version_type in darwin) # we can't check for "0.0" in archive_cmds due to quoting # problems, so we reset it completely verstring= ;; *) verstring="0.0" ;; esac if test "$need_version" = no; then versuffix= else versuffix=".0.0" fi fi # Remove version info from name if versioning should be avoided if test "$avoid_version" = yes && test "$need_version" = no; then major= versuffix= verstring="" fi # Check to see if the archive will have undefined symbols. if test "$allow_undefined" = yes; then if test "$allow_undefined_flag" = unsupported; then func_warning "undefined symbols not allowed in $host shared libraries" build_libtool_libs=no build_old_libs=yes fi else # Don't allow undefined symbols. allow_undefined_flag="$no_undefined_flag" fi fi func_generate_dlsyms "$libname" "$libname" "yes" func_append libobjs " $symfileobj" test "X$libobjs" = "X " && libobjs= if test "$opt_mode" != relink; then # Remove our outputs, but don't remove object files since they # may have been created when compiling PIC objects. removelist= tempremovelist=`$ECHO "$output_objdir/*"` for p in $tempremovelist; do case $p in *.$objext | *.gcno) ;; $output_objdir/$outputname | $output_objdir/$libname.* | $output_objdir/${libname}${release}.*) if test "X$precious_files_regex" != "X"; then if $ECHO "$p" | $EGREP -e "$precious_files_regex" >/dev/null 2>&1 then continue fi fi func_append removelist " $p" ;; *) ;; esac done test -n "$removelist" && \ func_show_eval "${RM}r \$removelist" fi # Now set the variables for building old libraries. if test "$build_old_libs" = yes && test "$build_libtool_libs" != convenience ; then func_append oldlibs " $output_objdir/$libname.$libext" # Transform .lo files to .o files. oldobjs="$objs "`$ECHO "$libobjs" | $SP2NL | $SED "/\.${libext}$/d; $lo2o" | $NL2SP` fi # Eliminate all temporary directories. #for path in $notinst_path; do # lib_search_path=`$ECHO "$lib_search_path " | $SED "s% $path % %g"` # deplibs=`$ECHO "$deplibs " | $SED "s% -L$path % %g"` # dependency_libs=`$ECHO "$dependency_libs " | $SED "s% -L$path % %g"` #done if test -n "$xrpath"; then # If the user specified any rpath flags, then add them. temp_xrpath= for libdir in $xrpath; do func_replace_sysroot "$libdir" func_append temp_xrpath " -R$func_replace_sysroot_result" case "$finalize_rpath " in *" $libdir "*) ;; *) func_append finalize_rpath " $libdir" ;; esac done if test "$hardcode_into_libs" != yes || test "$build_old_libs" = yes; then dependency_libs="$temp_xrpath $dependency_libs" fi fi # Make sure dlfiles contains only unique files that won't be dlpreopened old_dlfiles="$dlfiles" dlfiles= for lib in $old_dlfiles; do case " $dlprefiles $dlfiles " in *" $lib "*) ;; *) func_append dlfiles " $lib" ;; esac done # Make sure dlprefiles contains only unique files old_dlprefiles="$dlprefiles" dlprefiles= for lib in $old_dlprefiles; do case "$dlprefiles " in *" $lib "*) ;; *) func_append dlprefiles " $lib" ;; esac done if test "$build_libtool_libs" = yes; then if test -n "$rpath"; then case $host in *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-*-beos* | *-cegcc* | *-*-haiku*) # these systems don't actually have a c library (as such)! ;; *-*-rhapsody* | *-*-darwin1.[012]) # Rhapsody C library is in the System framework func_append deplibs " System.ltframework" ;; *-*-netbsd*) # Don't link with libc until the a.out ld.so is fixed. ;; *-*-openbsd* | *-*-freebsd* | *-*-dragonfly*) # Do not include libc due to us having libc/libc_r. ;; *-*-sco3.2v5* | *-*-sco5v6*) # Causes problems with __ctype ;; *-*-sysv4.2uw2* | *-*-sysv5* | *-*-unixware* | *-*-OpenUNIX*) # Compiler inserts libc in the correct place for threads to work ;; *) # Add libc to deplibs on all other systems if necessary. if test "$build_libtool_need_lc" = "yes"; then func_append deplibs " -lc" fi ;; esac fi # Transform deplibs into only deplibs that can be linked in shared. name_save=$name libname_save=$libname release_save=$release versuffix_save=$versuffix major_save=$major # I'm not sure if I'm treating the release correctly. I think # release should show up in the -l (ie -lgmp5) so we don't want to # add it in twice. Is that correct? release="" versuffix="" major="" newdeplibs= droppeddeps=no case $deplibs_check_method in pass_all) # Don't check for shared/static. Everything works. # This might be a little naive. We might want to check # whether the library exists or not. But this is on # osf3 & osf4 and I'm not really sure... Just # implementing what was already the behavior. newdeplibs=$deplibs ;; test_compile) # This code stresses the "libraries are programs" paradigm to its # limits. Maybe even breaks it. We compile a program, linking it # against the deplibs as a proxy for the library. Then we can check # whether they linked in statically or dynamically with ldd. $opt_dry_run || $RM conftest.c cat > conftest.c </dev/null` $nocaseglob else potential_libs=`ls $i/$libnameglob[.-]* 2>/dev/null` fi for potent_lib in $potential_libs; do # Follow soft links. if ls -lLd "$potent_lib" 2>/dev/null | $GREP " -> " >/dev/null; then continue fi # The statement above tries to avoid entering an # endless loop below, in case of cyclic links. # We might still enter an endless loop, since a link # loop can be closed while we follow links, # but so what? potlib="$potent_lib" while test -h "$potlib" 2>/dev/null; do potliblink=`ls -ld $potlib | ${SED} 's/.* -> //'` case $potliblink in [\\/]* | [A-Za-z]:[\\/]*) potlib="$potliblink";; *) potlib=`$ECHO "$potlib" | $SED 's,[^/]*$,,'`"$potliblink";; esac done if eval $file_magic_cmd \"\$potlib\" 2>/dev/null | $SED -e 10q | $EGREP "$file_magic_regex" > /dev/null; then func_append newdeplibs " $a_deplib" a_deplib="" break 2 fi done done fi if test -n "$a_deplib" ; then droppeddeps=yes echo $ECHO "*** Warning: linker path does not have real file for library $a_deplib." echo "*** I have the capability to make that library automatically link in when" echo "*** you link to this library. But I can only do this if you have a" echo "*** shared version of the library, which you do not appear to have" echo "*** because I did check the linker path looking for a file starting" if test -z "$potlib" ; then $ECHO "*** with $libname but no candidates were found. (...for file magic test)" else $ECHO "*** with $libname and none of the candidates passed a file format test" $ECHO "*** using a file magic. Last file checked: $potlib" fi fi ;; *) # Add a -L argument. func_append newdeplibs " $a_deplib" ;; esac done # Gone through all deplibs. ;; match_pattern*) set dummy $deplibs_check_method; shift match_pattern_regex=`expr "$deplibs_check_method" : "$1 \(.*\)"` for a_deplib in $deplibs; do case $a_deplib in -l*) func_stripname -l '' "$a_deplib" name=$func_stripname_result if test "X$allow_libtool_libs_with_static_runtimes" = "Xyes" ; then case " $predeps $postdeps " in *" $a_deplib "*) func_append newdeplibs " $a_deplib" a_deplib="" ;; esac fi if test -n "$a_deplib" ; then libname=`eval "\\$ECHO \"$libname_spec\""` for i in $lib_search_path $sys_lib_search_path $shlib_search_path; do potential_libs=`ls $i/$libname[.-]* 2>/dev/null` for potent_lib in $potential_libs; do potlib="$potent_lib" # see symlink-check above in file_magic test if eval "\$ECHO \"$potent_lib\"" 2>/dev/null | $SED 10q | \ $EGREP "$match_pattern_regex" > /dev/null; then func_append newdeplibs " $a_deplib" a_deplib="" break 2 fi done done fi if test -n "$a_deplib" ; then droppeddeps=yes echo $ECHO "*** Warning: linker path does not have real file for library $a_deplib." echo "*** I have the capability to make that library automatically link in when" echo "*** you link to this library. But I can only do this if you have a" echo "*** shared version of the library, which you do not appear to have" echo "*** because I did check the linker path looking for a file starting" if test -z "$potlib" ; then $ECHO "*** with $libname but no candidates were found. (...for regex pattern test)" else $ECHO "*** with $libname and none of the candidates passed a file format test" $ECHO "*** using a regex pattern. Last file checked: $potlib" fi fi ;; *) # Add a -L argument. func_append newdeplibs " $a_deplib" ;; esac done # Gone through all deplibs. ;; none | unknown | *) newdeplibs="" tmp_deplibs=`$ECHO " $deplibs" | $SED 's/ -lc$//; s/ -[LR][^ ]*//g'` if test "X$allow_libtool_libs_with_static_runtimes" = "Xyes" ; then for i in $predeps $postdeps ; do # can't use Xsed below, because $i might contain '/' tmp_deplibs=`$ECHO " $tmp_deplibs" | $SED "s,$i,,"` done fi case $tmp_deplibs in *[!\ \ ]*) echo if test "X$deplibs_check_method" = "Xnone"; then echo "*** Warning: inter-library dependencies are not supported in this platform." else echo "*** Warning: inter-library dependencies are not known to be supported." fi echo "*** All declared inter-library dependencies are being dropped." droppeddeps=yes ;; esac ;; esac versuffix=$versuffix_save major=$major_save release=$release_save libname=$libname_save name=$name_save case $host in *-*-rhapsody* | *-*-darwin1.[012]) # On Rhapsody replace the C library with the System framework newdeplibs=`$ECHO " $newdeplibs" | $SED 's/ -lc / System.ltframework /'` ;; esac if test "$droppeddeps" = yes; then if test "$module" = yes; then echo echo "*** Warning: libtool could not satisfy all declared inter-library" $ECHO "*** dependencies of module $libname. Therefore, libtool will create" echo "*** a static module, that should work as long as the dlopening" echo "*** application is linked with the -dlopen flag." if test -z "$global_symbol_pipe"; then echo echo "*** However, this would only work if libtool was able to extract symbol" echo "*** lists from a program, using \`nm' or equivalent, but libtool could" echo "*** not find such a program. So, this module is probably useless." echo "*** \`nm' from GNU binutils and a full rebuild may help." fi if test "$build_old_libs" = no; then oldlibs="$output_objdir/$libname.$libext" build_libtool_libs=module build_old_libs=yes else build_libtool_libs=no fi else echo "*** The inter-library dependencies that have been dropped here will be" echo "*** automatically added whenever a program is linked with this library" echo "*** or is declared to -dlopen it." if test "$allow_undefined" = no; then echo echo "*** Since this library must not contain undefined symbols," echo "*** because either the platform does not support them or" echo "*** it was explicitly requested with -no-undefined," echo "*** libtool will only create a static version of it." if test "$build_old_libs" = no; then oldlibs="$output_objdir/$libname.$libext" build_libtool_libs=module build_old_libs=yes else build_libtool_libs=no fi fi fi fi # Done checking deplibs! deplibs=$newdeplibs fi # Time to change all our "foo.ltframework" stuff back to "-framework foo" case $host in *-*-darwin*) newdeplibs=`$ECHO " $newdeplibs" | $SED 's% \([^ $]*\).ltframework% -framework \1%g'` new_inherited_linker_flags=`$ECHO " $new_inherited_linker_flags" | $SED 's% \([^ $]*\).ltframework% -framework \1%g'` deplibs=`$ECHO " $deplibs" | $SED 's% \([^ $]*\).ltframework% -framework \1%g'` ;; esac # move library search paths that coincide with paths to not yet # installed libraries to the beginning of the library search list new_libs= for path in $notinst_path; do case " $new_libs " in *" -L$path/$objdir "*) ;; *) case " $deplibs " in *" -L$path/$objdir "*) func_append new_libs " -L$path/$objdir" ;; esac ;; esac done for deplib in $deplibs; do case $deplib in -L*) case " $new_libs " in *" $deplib "*) ;; *) func_append new_libs " $deplib" ;; esac ;; *) func_append new_libs " $deplib" ;; esac done deplibs="$new_libs" # All the library-specific variables (install_libdir is set above). library_names= old_library= dlname= # Test again, we may have decided not to build it any more if test "$build_libtool_libs" = yes; then # Remove ${wl} instances when linking with ld. # FIXME: should test the right _cmds variable. case $archive_cmds in *\$LD\ *) wl= ;; esac if test "$hardcode_into_libs" = yes; then # Hardcode the library paths hardcode_libdirs= dep_rpath= rpath="$finalize_rpath" test "$opt_mode" != relink && rpath="$compile_rpath$rpath" for libdir in $rpath; do if test -n "$hardcode_libdir_flag_spec"; then if test -n "$hardcode_libdir_separator"; then func_replace_sysroot "$libdir" libdir=$func_replace_sysroot_result if test -z "$hardcode_libdirs"; then hardcode_libdirs="$libdir" else # Just accumulate the unique libdirs. case $hardcode_libdir_separator$hardcode_libdirs$hardcode_libdir_separator in *"$hardcode_libdir_separator$libdir$hardcode_libdir_separator"*) ;; *) func_append hardcode_libdirs "$hardcode_libdir_separator$libdir" ;; esac fi else eval flag=\"$hardcode_libdir_flag_spec\" func_append dep_rpath " $flag" fi elif test -n "$runpath_var"; then case "$perm_rpath " in *" $libdir "*) ;; *) func_append perm_rpath " $libdir" ;; esac fi done # Substitute the hardcoded libdirs into the rpath. if test -n "$hardcode_libdir_separator" && test -n "$hardcode_libdirs"; then libdir="$hardcode_libdirs" eval "dep_rpath=\"$hardcode_libdir_flag_spec\"" fi if test -n "$runpath_var" && test -n "$perm_rpath"; then # We should set the runpath_var. rpath= for dir in $perm_rpath; do func_append rpath "$dir:" done eval "$runpath_var='$rpath\$$runpath_var'; export $runpath_var" fi test -n "$dep_rpath" && deplibs="$dep_rpath $deplibs" fi shlibpath="$finalize_shlibpath" test "$opt_mode" != relink && shlibpath="$compile_shlibpath$shlibpath" if test -n "$shlibpath"; then eval "$shlibpath_var='$shlibpath\$$shlibpath_var'; export $shlibpath_var" fi # Get the real and link names of the library. eval shared_ext=\"$shrext_cmds\" eval library_names=\"$library_names_spec\" set dummy $library_names shift realname="$1" shift if test -n "$soname_spec"; then eval soname=\"$soname_spec\" else soname="$realname" fi if test -z "$dlname"; then dlname=$soname fi lib="$output_objdir/$realname" linknames= for link do func_append linknames " $link" done # Use standard objects if they are pic test -z "$pic_flag" && libobjs=`$ECHO "$libobjs" | $SP2NL | $SED "$lo2o" | $NL2SP` test "X$libobjs" = "X " && libobjs= delfiles= if test -n "$export_symbols" && test -n "$include_expsyms"; then $opt_dry_run || cp "$export_symbols" "$output_objdir/$libname.uexp" export_symbols="$output_objdir/$libname.uexp" func_append delfiles " $export_symbols" fi orig_export_symbols= case $host_os in cygwin* | mingw* | cegcc*) if test -n "$export_symbols" && test -z "$export_symbols_regex"; then # exporting using user supplied symfile if test "x`$SED 1q $export_symbols`" != xEXPORTS; then # and it's NOT already a .def file. Must figure out # which of the given symbols are data symbols and tag # them as such. So, trigger use of export_symbols_cmds. # export_symbols gets reassigned inside the "prepare # the list of exported symbols" if statement, so the # include_expsyms logic still works. orig_export_symbols="$export_symbols" export_symbols= always_export_symbols=yes fi fi ;; esac # Prepare the list of exported symbols if test -z "$export_symbols"; then if test "$always_export_symbols" = yes || test -n "$export_symbols_regex"; then func_verbose "generating symbol list for \`$libname.la'" export_symbols="$output_objdir/$libname.exp" $opt_dry_run || $RM $export_symbols cmds=$export_symbols_cmds save_ifs="$IFS"; IFS='~' for cmd1 in $cmds; do IFS="$save_ifs" # Take the normal branch if the nm_file_list_spec branch # doesn't work or if tool conversion is not needed. case $nm_file_list_spec~$to_tool_file_cmd in *~func_convert_file_noop | *~func_convert_file_msys_to_w32 | ~*) try_normal_branch=yes eval cmd=\"$cmd1\" func_len " $cmd" len=$func_len_result ;; *) try_normal_branch=no ;; esac if test "$try_normal_branch" = yes \ && { test "$len" -lt "$max_cmd_len" \ || test "$max_cmd_len" -le -1; } then func_show_eval "$cmd" 'exit $?' skipped_export=false elif test -n "$nm_file_list_spec"; then func_basename "$output" output_la=$func_basename_result save_libobjs=$libobjs save_output=$output output=${output_objdir}/${output_la}.nm func_to_tool_file "$output" libobjs=$nm_file_list_spec$func_to_tool_file_result func_append delfiles " $output" func_verbose "creating $NM input file list: $output" for obj in $save_libobjs; do func_to_tool_file "$obj" $ECHO "$func_to_tool_file_result" done > "$output" eval cmd=\"$cmd1\" func_show_eval "$cmd" 'exit $?' output=$save_output libobjs=$save_libobjs skipped_export=false else # The command line is too long to execute in one step. func_verbose "using reloadable object file for export list..." skipped_export=: # Break out early, otherwise skipped_export may be # set to false by a later but shorter cmd. break fi done IFS="$save_ifs" if test -n "$export_symbols_regex" && test "X$skipped_export" != "X:"; then func_show_eval '$EGREP -e "$export_symbols_regex" "$export_symbols" > "${export_symbols}T"' func_show_eval '$MV "${export_symbols}T" "$export_symbols"' fi fi fi if test -n "$export_symbols" && test -n "$include_expsyms"; then tmp_export_symbols="$export_symbols" test -n "$orig_export_symbols" && tmp_export_symbols="$orig_export_symbols" $opt_dry_run || eval '$ECHO "$include_expsyms" | $SP2NL >> "$tmp_export_symbols"' fi if test "X$skipped_export" != "X:" && test -n "$orig_export_symbols"; then # The given exports_symbols file has to be filtered, so filter it. func_verbose "filter symbol list for \`$libname.la' to tag DATA exports" # FIXME: $output_objdir/$libname.filter potentially contains lots of # 's' commands which not all seds can handle. GNU sed should be fine # though. Also, the filter scales superlinearly with the number of # global variables. join(1) would be nice here, but unfortunately # isn't a blessed tool. $opt_dry_run || $SED -e '/[ ,]DATA/!d;s,\(.*\)\([ \,].*\),s|^\1$|\1\2|,' < $export_symbols > $output_objdir/$libname.filter func_append delfiles " $export_symbols $output_objdir/$libname.filter" export_symbols=$output_objdir/$libname.def $opt_dry_run || $SED -f $output_objdir/$libname.filter < $orig_export_symbols > $export_symbols fi tmp_deplibs= for test_deplib in $deplibs; do case " $convenience " in *" $test_deplib "*) ;; *) func_append tmp_deplibs " $test_deplib" ;; esac done deplibs="$tmp_deplibs" if test -n "$convenience"; then if test -n "$whole_archive_flag_spec" && test "$compiler_needs_object" = yes && test -z "$libobjs"; then # extract the archives, so we have objects to list. # TODO: could optimize this to just extract one archive. whole_archive_flag_spec= fi if test -n "$whole_archive_flag_spec"; then save_libobjs=$libobjs eval libobjs=\"\$libobjs $whole_archive_flag_spec\" test "X$libobjs" = "X " && libobjs= else gentop="$output_objdir/${outputname}x" func_append generated " $gentop" func_extract_archives $gentop $convenience func_append libobjs " $func_extract_archives_result" test "X$libobjs" = "X " && libobjs= fi fi if test "$thread_safe" = yes && test -n "$thread_safe_flag_spec"; then eval flag=\"$thread_safe_flag_spec\" func_append linker_flags " $flag" fi # Make a backup of the uninstalled library when relinking if test "$opt_mode" = relink; then $opt_dry_run || eval '(cd $output_objdir && $RM ${realname}U && $MV $realname ${realname}U)' || exit $? fi # Do each of the archive commands. if test "$module" = yes && test -n "$module_cmds" ; then if test -n "$export_symbols" && test -n "$module_expsym_cmds"; then eval test_cmds=\"$module_expsym_cmds\" cmds=$module_expsym_cmds else eval test_cmds=\"$module_cmds\" cmds=$module_cmds fi else if test -n "$export_symbols" && test -n "$archive_expsym_cmds"; then eval test_cmds=\"$archive_expsym_cmds\" cmds=$archive_expsym_cmds else eval test_cmds=\"$archive_cmds\" cmds=$archive_cmds fi fi if test "X$skipped_export" != "X:" && func_len " $test_cmds" && len=$func_len_result && test "$len" -lt "$max_cmd_len" || test "$max_cmd_len" -le -1; then : else # The command line is too long to link in one step, link piecewise # or, if using GNU ld and skipped_export is not :, use a linker # script. # Save the value of $output and $libobjs because we want to # use them later. If we have whole_archive_flag_spec, we # want to use save_libobjs as it was before # whole_archive_flag_spec was expanded, because we can't # assume the linker understands whole_archive_flag_spec. # This may have to be revisited, in case too many # convenience libraries get linked in and end up exceeding # the spec. if test -z "$convenience" || test -z "$whole_archive_flag_spec"; then save_libobjs=$libobjs fi save_output=$output func_basename "$output" output_la=$func_basename_result # Clear the reloadable object creation command queue and # initialize k to one. test_cmds= concat_cmds= objlist= last_robj= k=1 if test -n "$save_libobjs" && test "X$skipped_export" != "X:" && test "$with_gnu_ld" = yes; then output=${output_objdir}/${output_la}.lnkscript func_verbose "creating GNU ld script: $output" echo 'INPUT (' > $output for obj in $save_libobjs do func_to_tool_file "$obj" $ECHO "$func_to_tool_file_result" >> $output done echo ')' >> $output func_append delfiles " $output" func_to_tool_file "$output" output=$func_to_tool_file_result elif test -n "$save_libobjs" && test "X$skipped_export" != "X:" && test "X$file_list_spec" != X; then output=${output_objdir}/${output_la}.lnk func_verbose "creating linker input file list: $output" : > $output set x $save_libobjs shift firstobj= if test "$compiler_needs_object" = yes; then firstobj="$1 " shift fi for obj do func_to_tool_file "$obj" $ECHO "$func_to_tool_file_result" >> $output done func_append delfiles " $output" func_to_tool_file "$output" output=$firstobj\"$file_list_spec$func_to_tool_file_result\" else if test -n "$save_libobjs"; then func_verbose "creating reloadable object files..." output=$output_objdir/$output_la-${k}.$objext eval test_cmds=\"$reload_cmds\" func_len " $test_cmds" len0=$func_len_result len=$len0 # Loop over the list of objects to be linked. for obj in $save_libobjs do func_len " $obj" func_arith $len + $func_len_result len=$func_arith_result if test "X$objlist" = X || test "$len" -lt "$max_cmd_len"; then func_append objlist " $obj" else # The command $test_cmds is almost too long, add a # command to the queue. if test "$k" -eq 1 ; then # The first file doesn't have a previous command to add. reload_objs=$objlist eval concat_cmds=\"$reload_cmds\" else # All subsequent reloadable object files will link in # the last one created. reload_objs="$objlist $last_robj" eval concat_cmds=\"\$concat_cmds~$reload_cmds~\$RM $last_robj\" fi last_robj=$output_objdir/$output_la-${k}.$objext func_arith $k + 1 k=$func_arith_result output=$output_objdir/$output_la-${k}.$objext objlist=" $obj" func_len " $last_robj" func_arith $len0 + $func_len_result len=$func_arith_result fi done # Handle the remaining objects by creating one last # reloadable object file. All subsequent reloadable object # files will link in the last one created. test -z "$concat_cmds" || concat_cmds=$concat_cmds~ reload_objs="$objlist $last_robj" eval concat_cmds=\"\${concat_cmds}$reload_cmds\" if test -n "$last_robj"; then eval concat_cmds=\"\${concat_cmds}~\$RM $last_robj\" fi func_append delfiles " $output" else output= fi if ${skipped_export-false}; then func_verbose "generating symbol list for \`$libname.la'" export_symbols="$output_objdir/$libname.exp" $opt_dry_run || $RM $export_symbols libobjs=$output # Append the command to create the export file. test -z "$concat_cmds" || concat_cmds=$concat_cmds~ eval concat_cmds=\"\$concat_cmds$export_symbols_cmds\" if test -n "$last_robj"; then eval concat_cmds=\"\$concat_cmds~\$RM $last_robj\" fi fi test -n "$save_libobjs" && func_verbose "creating a temporary reloadable object file: $output" # Loop through the commands generated above and execute them. save_ifs="$IFS"; IFS='~' for cmd in $concat_cmds; do IFS="$save_ifs" $opt_silent || { func_quote_for_expand "$cmd" eval "func_echo $func_quote_for_expand_result" } $opt_dry_run || eval "$cmd" || { lt_exit=$? # Restore the uninstalled library and exit if test "$opt_mode" = relink; then ( cd "$output_objdir" && \ $RM "${realname}T" && \ $MV "${realname}U" "$realname" ) fi exit $lt_exit } done IFS="$save_ifs" if test -n "$export_symbols_regex" && ${skipped_export-false}; then func_show_eval '$EGREP -e "$export_symbols_regex" "$export_symbols" > "${export_symbols}T"' func_show_eval '$MV "${export_symbols}T" "$export_symbols"' fi fi if ${skipped_export-false}; then if test -n "$export_symbols" && test -n "$include_expsyms"; then tmp_export_symbols="$export_symbols" test -n "$orig_export_symbols" && tmp_export_symbols="$orig_export_symbols" $opt_dry_run || eval '$ECHO "$include_expsyms" | $SP2NL >> "$tmp_export_symbols"' fi if test -n "$orig_export_symbols"; then # The given exports_symbols file has to be filtered, so filter it. func_verbose "filter symbol list for \`$libname.la' to tag DATA exports" # FIXME: $output_objdir/$libname.filter potentially contains lots of # 's' commands which not all seds can handle. GNU sed should be fine # though. Also, the filter scales superlinearly with the number of # global variables. join(1) would be nice here, but unfortunately # isn't a blessed tool. $opt_dry_run || $SED -e '/[ ,]DATA/!d;s,\(.*\)\([ \,].*\),s|^\1$|\1\2|,' < $export_symbols > $output_objdir/$libname.filter func_append delfiles " $export_symbols $output_objdir/$libname.filter" export_symbols=$output_objdir/$libname.def $opt_dry_run || $SED -f $output_objdir/$libname.filter < $orig_export_symbols > $export_symbols fi fi libobjs=$output # Restore the value of output. output=$save_output if test -n "$convenience" && test -n "$whole_archive_flag_spec"; then eval libobjs=\"\$libobjs $whole_archive_flag_spec\" test "X$libobjs" = "X " && libobjs= fi # Expand the library linking commands again to reset the # value of $libobjs for piecewise linking. # Do each of the archive commands. if test "$module" = yes && test -n "$module_cmds" ; then if test -n "$export_symbols" && test -n "$module_expsym_cmds"; then cmds=$module_expsym_cmds else cmds=$module_cmds fi else if test -n "$export_symbols" && test -n "$archive_expsym_cmds"; then cmds=$archive_expsym_cmds else cmds=$archive_cmds fi fi fi if test -n "$delfiles"; then # Append the command to remove temporary files to $cmds. eval cmds=\"\$cmds~\$RM $delfiles\" fi # Add any objects from preloaded convenience libraries if test -n "$dlprefiles"; then gentop="$output_objdir/${outputname}x" func_append generated " $gentop" func_extract_archives $gentop $dlprefiles func_append libobjs " $func_extract_archives_result" test "X$libobjs" = "X " && libobjs= fi save_ifs="$IFS"; IFS='~' for cmd in $cmds; do IFS="$save_ifs" eval cmd=\"$cmd\" $opt_silent || { func_quote_for_expand "$cmd" eval "func_echo $func_quote_for_expand_result" } $opt_dry_run || eval "$cmd" || { lt_exit=$? # Restore the uninstalled library and exit if test "$opt_mode" = relink; then ( cd "$output_objdir" && \ $RM "${realname}T" && \ $MV "${realname}U" "$realname" ) fi exit $lt_exit } done IFS="$save_ifs" # Restore the uninstalled library and exit if test "$opt_mode" = relink; then $opt_dry_run || eval '(cd $output_objdir && $RM ${realname}T && $MV $realname ${realname}T && $MV ${realname}U $realname)' || exit $? if test -n "$convenience"; then if test -z "$whole_archive_flag_spec"; then func_show_eval '${RM}r "$gentop"' fi fi exit $EXIT_SUCCESS fi # Create links to the real library. for linkname in $linknames; do if test "$realname" != "$linkname"; then func_show_eval '(cd "$output_objdir" && $RM "$linkname" && $LN_S "$realname" "$linkname")' 'exit $?' fi done # If -module or -export-dynamic was specified, set the dlname. if test "$module" = yes || test "$export_dynamic" = yes; then # On all known operating systems, these are identical. dlname="$soname" fi fi ;; obj) if test -n "$dlfiles$dlprefiles" || test "$dlself" != no; then func_warning "\`-dlopen' is ignored for objects" fi case " $deplibs" in *\ -l* | *\ -L*) func_warning "\`-l' and \`-L' are ignored for objects" ;; esac test -n "$rpath" && \ func_warning "\`-rpath' is ignored for objects" test -n "$xrpath" && \ func_warning "\`-R' is ignored for objects" test -n "$vinfo" && \ func_warning "\`-version-info' is ignored for objects" test -n "$release" && \ func_warning "\`-release' is ignored for objects" case $output in *.lo) test -n "$objs$old_deplibs" && \ func_fatal_error "cannot build library object \`$output' from non-libtool objects" libobj=$output func_lo2o "$libobj" obj=$func_lo2o_result ;; *) libobj= obj="$output" ;; esac # Delete the old objects. $opt_dry_run || $RM $obj $libobj # Objects from convenience libraries. This assumes # single-version convenience libraries. Whenever we create # different ones for PIC/non-PIC, this we'll have to duplicate # the extraction. reload_conv_objs= gentop= # reload_cmds runs $LD directly, so let us get rid of # -Wl from whole_archive_flag_spec and hope we can get by with # turning comma into space.. wl= if test -n "$convenience"; then if test -n "$whole_archive_flag_spec"; then eval tmp_whole_archive_flags=\"$whole_archive_flag_spec\" reload_conv_objs=$reload_objs\ `$ECHO "$tmp_whole_archive_flags" | $SED 's|,| |g'` else gentop="$output_objdir/${obj}x" func_append generated " $gentop" func_extract_archives $gentop $convenience reload_conv_objs="$reload_objs $func_extract_archives_result" fi fi # If we're not building shared, we need to use non_pic_objs test "$build_libtool_libs" != yes && libobjs="$non_pic_objects" # Create the old-style object. reload_objs="$objs$old_deplibs "`$ECHO "$libobjs" | $SP2NL | $SED "/\.${libext}$/d; /\.lib$/d; $lo2o" | $NL2SP`" $reload_conv_objs" ### testsuite: skip nested quoting test output="$obj" func_execute_cmds "$reload_cmds" 'exit $?' # Exit if we aren't doing a library object file. if test -z "$libobj"; then if test -n "$gentop"; then func_show_eval '${RM}r "$gentop"' fi exit $EXIT_SUCCESS fi if test "$build_libtool_libs" != yes; then if test -n "$gentop"; then func_show_eval '${RM}r "$gentop"' fi # Create an invalid libtool object if no PIC, so that we don't # accidentally link it into a program. # $show "echo timestamp > $libobj" # $opt_dry_run || eval "echo timestamp > $libobj" || exit $? exit $EXIT_SUCCESS fi if test -n "$pic_flag" || test "$pic_mode" != default; then # Only do commands if we really have different PIC objects. reload_objs="$libobjs $reload_conv_objs" output="$libobj" func_execute_cmds "$reload_cmds" 'exit $?' fi if test -n "$gentop"; then func_show_eval '${RM}r "$gentop"' fi exit $EXIT_SUCCESS ;; prog) case $host in *cygwin*) func_stripname '' '.exe' "$output" output=$func_stripname_result.exe;; esac test -n "$vinfo" && \ func_warning "\`-version-info' is ignored for programs" test -n "$release" && \ func_warning "\`-release' is ignored for programs" test "$preload" = yes \ && test "$dlopen_support" = unknown \ && test "$dlopen_self" = unknown \ && test "$dlopen_self_static" = unknown && \ func_warning "\`LT_INIT([dlopen])' not used. Assuming no dlopen support." case $host in *-*-rhapsody* | *-*-darwin1.[012]) # On Rhapsody replace the C library is the System framework compile_deplibs=`$ECHO " $compile_deplibs" | $SED 's/ -lc / System.ltframework /'` finalize_deplibs=`$ECHO " $finalize_deplibs" | $SED 's/ -lc / System.ltframework /'` ;; esac case $host in *-*-darwin*) # Don't allow lazy linking, it breaks C++ global constructors # But is supposedly fixed on 10.4 or later (yay!). if test "$tagname" = CXX ; then case ${MACOSX_DEPLOYMENT_TARGET-10.0} in 10.[0123]) func_append compile_command " ${wl}-bind_at_load" func_append finalize_command " ${wl}-bind_at_load" ;; esac fi # Time to change all our "foo.ltframework" stuff back to "-framework foo" compile_deplibs=`$ECHO " $compile_deplibs" | $SED 's% \([^ $]*\).ltframework% -framework \1%g'` finalize_deplibs=`$ECHO " $finalize_deplibs" | $SED 's% \([^ $]*\).ltframework% -framework \1%g'` ;; esac # move library search paths that coincide with paths to not yet # installed libraries to the beginning of the library search list new_libs= for path in $notinst_path; do case " $new_libs " in *" -L$path/$objdir "*) ;; *) case " $compile_deplibs " in *" -L$path/$objdir "*) func_append new_libs " -L$path/$objdir" ;; esac ;; esac done for deplib in $compile_deplibs; do case $deplib in -L*) case " $new_libs " in *" $deplib "*) ;; *) func_append new_libs " $deplib" ;; esac ;; *) func_append new_libs " $deplib" ;; esac done compile_deplibs="$new_libs" func_append compile_command " $compile_deplibs" func_append finalize_command " $finalize_deplibs" if test -n "$rpath$xrpath"; then # If the user specified any rpath flags, then add them. for libdir in $rpath $xrpath; do # This is the magic to use -rpath. case "$finalize_rpath " in *" $libdir "*) ;; *) func_append finalize_rpath " $libdir" ;; esac done fi # Now hardcode the library paths rpath= hardcode_libdirs= for libdir in $compile_rpath $finalize_rpath; do if test -n "$hardcode_libdir_flag_spec"; then if test -n "$hardcode_libdir_separator"; then if test -z "$hardcode_libdirs"; then hardcode_libdirs="$libdir" else # Just accumulate the unique libdirs. case $hardcode_libdir_separator$hardcode_libdirs$hardcode_libdir_separator in *"$hardcode_libdir_separator$libdir$hardcode_libdir_separator"*) ;; *) func_append hardcode_libdirs "$hardcode_libdir_separator$libdir" ;; esac fi else eval flag=\"$hardcode_libdir_flag_spec\" func_append rpath " $flag" fi elif test -n "$runpath_var"; then case "$perm_rpath " in *" $libdir "*) ;; *) func_append perm_rpath " $libdir" ;; esac fi case $host in *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-cegcc*) testbindir=`${ECHO} "$libdir" | ${SED} -e 's*/lib$*/bin*'` case :$dllsearchpath: in *":$libdir:"*) ;; ::) dllsearchpath=$libdir;; *) func_append dllsearchpath ":$libdir";; esac case :$dllsearchpath: in *":$testbindir:"*) ;; ::) dllsearchpath=$testbindir;; *) func_append dllsearchpath ":$testbindir";; esac ;; esac done # Substitute the hardcoded libdirs into the rpath. if test -n "$hardcode_libdir_separator" && test -n "$hardcode_libdirs"; then libdir="$hardcode_libdirs" eval rpath=\" $hardcode_libdir_flag_spec\" fi compile_rpath="$rpath" rpath= hardcode_libdirs= for libdir in $finalize_rpath; do if test -n "$hardcode_libdir_flag_spec"; then if test -n "$hardcode_libdir_separator"; then if test -z "$hardcode_libdirs"; then hardcode_libdirs="$libdir" else # Just accumulate the unique libdirs. case $hardcode_libdir_separator$hardcode_libdirs$hardcode_libdir_separator in *"$hardcode_libdir_separator$libdir$hardcode_libdir_separator"*) ;; *) func_append hardcode_libdirs "$hardcode_libdir_separator$libdir" ;; esac fi else eval flag=\"$hardcode_libdir_flag_spec\" func_append rpath " $flag" fi elif test -n "$runpath_var"; then case "$finalize_perm_rpath " in *" $libdir "*) ;; *) func_append finalize_perm_rpath " $libdir" ;; esac fi done # Substitute the hardcoded libdirs into the rpath. if test -n "$hardcode_libdir_separator" && test -n "$hardcode_libdirs"; then libdir="$hardcode_libdirs" eval rpath=\" $hardcode_libdir_flag_spec\" fi finalize_rpath="$rpath" if test -n "$libobjs" && test "$build_old_libs" = yes; then # Transform all the library objects into standard objects. compile_command=`$ECHO "$compile_command" | $SP2NL | $SED "$lo2o" | $NL2SP` finalize_command=`$ECHO "$finalize_command" | $SP2NL | $SED "$lo2o" | $NL2SP` fi func_generate_dlsyms "$outputname" "@PROGRAM@" "no" # template prelinking step if test -n "$prelink_cmds"; then func_execute_cmds "$prelink_cmds" 'exit $?' fi wrappers_required=yes case $host in *cegcc* | *mingw32ce*) # Disable wrappers for cegcc and mingw32ce hosts, we are cross compiling anyway. wrappers_required=no ;; *cygwin* | *mingw* ) if test "$build_libtool_libs" != yes; then wrappers_required=no fi ;; *) if test "$need_relink" = no || test "$build_libtool_libs" != yes; then wrappers_required=no fi ;; esac if test "$wrappers_required" = no; then # Replace the output file specification. compile_command=`$ECHO "$compile_command" | $SED 's%@OUTPUT@%'"$output"'%g'` link_command="$compile_command$compile_rpath" # We have no uninstalled library dependencies, so finalize right now. exit_status=0 func_show_eval "$link_command" 'exit_status=$?' if test -n "$postlink_cmds"; then func_to_tool_file "$output" postlink_cmds=`func_echo_all "$postlink_cmds" | $SED -e 's%@OUTPUT@%'"$output"'%g' -e 's%@TOOL_OUTPUT@%'"$func_to_tool_file_result"'%g'` func_execute_cmds "$postlink_cmds" 'exit $?' fi # Delete the generated files. if test -f "$output_objdir/${outputname}S.${objext}"; then func_show_eval '$RM "$output_objdir/${outputname}S.${objext}"' fi exit $exit_status fi if test -n "$compile_shlibpath$finalize_shlibpath"; then compile_command="$shlibpath_var=\"$compile_shlibpath$finalize_shlibpath\$$shlibpath_var\" $compile_command" fi if test -n "$finalize_shlibpath"; then finalize_command="$shlibpath_var=\"$finalize_shlibpath\$$shlibpath_var\" $finalize_command" fi compile_var= finalize_var= if test -n "$runpath_var"; then if test -n "$perm_rpath"; then # We should set the runpath_var. rpath= for dir in $perm_rpath; do func_append rpath "$dir:" done compile_var="$runpath_var=\"$rpath\$$runpath_var\" " fi if test -n "$finalize_perm_rpath"; then # We should set the runpath_var. rpath= for dir in $finalize_perm_rpath; do func_append rpath "$dir:" done finalize_var="$runpath_var=\"$rpath\$$runpath_var\" " fi fi if test "$no_install" = yes; then # We don't need to create a wrapper script. link_command="$compile_var$compile_command$compile_rpath" # Replace the output file specification. link_command=`$ECHO "$link_command" | $SED 's%@OUTPUT@%'"$output"'%g'` # Delete the old output file. $opt_dry_run || $RM $output # Link the executable and exit func_show_eval "$link_command" 'exit $?' if test -n "$postlink_cmds"; then func_to_tool_file "$output" postlink_cmds=`func_echo_all "$postlink_cmds" | $SED -e 's%@OUTPUT@%'"$output"'%g' -e 's%@TOOL_OUTPUT@%'"$func_to_tool_file_result"'%g'` func_execute_cmds "$postlink_cmds" 'exit $?' fi exit $EXIT_SUCCESS fi if test "$hardcode_action" = relink; then # Fast installation is not supported link_command="$compile_var$compile_command$compile_rpath" relink_command="$finalize_var$finalize_command$finalize_rpath" func_warning "this platform does not like uninstalled shared libraries" func_warning "\`$output' will be relinked during installation" else if test "$fast_install" != no; then link_command="$finalize_var$compile_command$finalize_rpath" if test "$fast_install" = yes; then relink_command=`$ECHO "$compile_var$compile_command$compile_rpath" | $SED 's%@OUTPUT@%\$progdir/\$file%g'` else # fast_install is set to needless relink_command= fi else link_command="$compile_var$compile_command$compile_rpath" relink_command="$finalize_var$finalize_command$finalize_rpath" fi fi # Replace the output file specification. link_command=`$ECHO "$link_command" | $SED 's%@OUTPUT@%'"$output_objdir/$outputname"'%g'` # Delete the old output files. $opt_dry_run || $RM $output $output_objdir/$outputname $output_objdir/lt-$outputname func_show_eval "$link_command" 'exit $?' if test -n "$postlink_cmds"; then func_to_tool_file "$output_objdir/$outputname" postlink_cmds=`func_echo_all "$postlink_cmds" | $SED -e 's%@OUTPUT@%'"$output_objdir/$outputname"'%g' -e 's%@TOOL_OUTPUT@%'"$func_to_tool_file_result"'%g'` func_execute_cmds "$postlink_cmds" 'exit $?' fi # Now create the wrapper script. func_verbose "creating $output" # Quote the relink command for shipping. if test -n "$relink_command"; then # Preserve any variables that may affect compiler behavior for var in $variables_saved_for_relink; do if eval test -z \"\${$var+set}\"; then relink_command="{ test -z \"\${$var+set}\" || $lt_unset $var || { $var=; export $var; }; }; $relink_command" elif eval var_value=\$$var; test -z "$var_value"; then relink_command="$var=; export $var; $relink_command" else func_quote_for_eval "$var_value" relink_command="$var=$func_quote_for_eval_result; export $var; $relink_command" fi done relink_command="(cd `pwd`; $relink_command)" relink_command=`$ECHO "$relink_command" | $SED "$sed_quote_subst"` fi # Only actually do things if not in dry run mode. $opt_dry_run || { # win32 will think the script is a binary if it has # a .exe suffix, so we strip it off here. case $output in *.exe) func_stripname '' '.exe' "$output" output=$func_stripname_result ;; esac # test for cygwin because mv fails w/o .exe extensions case $host in *cygwin*) exeext=.exe func_stripname '' '.exe' "$outputname" outputname=$func_stripname_result ;; *) exeext= ;; esac case $host in *cygwin* | *mingw* ) func_dirname_and_basename "$output" "" "." output_name=$func_basename_result output_path=$func_dirname_result cwrappersource="$output_path/$objdir/lt-$output_name.c" cwrapper="$output_path/$output_name.exe" $RM $cwrappersource $cwrapper trap "$RM $cwrappersource $cwrapper; exit $EXIT_FAILURE" 1 2 15 func_emit_cwrapperexe_src > $cwrappersource # The wrapper executable is built using the $host compiler, # because it contains $host paths and files. If cross- # compiling, it, like the target executable, must be # executed on the $host or under an emulation environment. $opt_dry_run || { $LTCC $LTCFLAGS -o $cwrapper $cwrappersource $STRIP $cwrapper } # Now, create the wrapper script for func_source use: func_ltwrapper_scriptname $cwrapper $RM $func_ltwrapper_scriptname_result trap "$RM $func_ltwrapper_scriptname_result; exit $EXIT_FAILURE" 1 2 15 $opt_dry_run || { # note: this script will not be executed, so do not chmod. if test "x$build" = "x$host" ; then $cwrapper --lt-dump-script > $func_ltwrapper_scriptname_result else func_emit_wrapper no > $func_ltwrapper_scriptname_result fi } ;; * ) $RM $output trap "$RM $output; exit $EXIT_FAILURE" 1 2 15 func_emit_wrapper no > $output chmod +x $output ;; esac } exit $EXIT_SUCCESS ;; esac # See if we need to build an old-fashioned archive. for oldlib in $oldlibs; do if test "$build_libtool_libs" = convenience; then oldobjs="$libobjs_save $symfileobj" addlibs="$convenience" build_libtool_libs=no else if test "$build_libtool_libs" = module; then oldobjs="$libobjs_save" build_libtool_libs=no else oldobjs="$old_deplibs $non_pic_objects" if test "$preload" = yes && test -f "$symfileobj"; then func_append oldobjs " $symfileobj" fi fi addlibs="$old_convenience" fi if test -n "$addlibs"; then gentop="$output_objdir/${outputname}x" func_append generated " $gentop" func_extract_archives $gentop $addlibs func_append oldobjs " $func_extract_archives_result" fi # Do each command in the archive commands. if test -n "$old_archive_from_new_cmds" && test "$build_libtool_libs" = yes; then cmds=$old_archive_from_new_cmds else # Add any objects from preloaded convenience libraries if test -n "$dlprefiles"; then gentop="$output_objdir/${outputname}x" func_append generated " $gentop" func_extract_archives $gentop $dlprefiles func_append oldobjs " $func_extract_archives_result" fi # POSIX demands no paths to be encoded in archives. We have # to avoid creating archives with duplicate basenames if we # might have to extract them afterwards, e.g., when creating a # static archive out of a convenience library, or when linking # the entirety of a libtool archive into another (currently # not supported by libtool). if (for obj in $oldobjs do func_basename "$obj" $ECHO "$func_basename_result" done | sort | sort -uc >/dev/null 2>&1); then : else echo "copying selected object files to avoid basename conflicts..." gentop="$output_objdir/${outputname}x" func_append generated " $gentop" func_mkdir_p "$gentop" save_oldobjs=$oldobjs oldobjs= counter=1 for obj in $save_oldobjs do func_basename "$obj" objbase="$func_basename_result" case " $oldobjs " in " ") oldobjs=$obj ;; *[\ /]"$objbase "*) while :; do # Make sure we don't pick an alternate name that also # overlaps. newobj=lt$counter-$objbase func_arith $counter + 1 counter=$func_arith_result case " $oldobjs " in *[\ /]"$newobj "*) ;; *) if test ! -f "$gentop/$newobj"; then break; fi ;; esac done func_show_eval "ln $obj $gentop/$newobj || cp $obj $gentop/$newobj" func_append oldobjs " $gentop/$newobj" ;; *) func_append oldobjs " $obj" ;; esac done fi func_to_tool_file "$oldlib" func_convert_file_msys_to_w32 tool_oldlib=$func_to_tool_file_result eval cmds=\"$old_archive_cmds\" func_len " $cmds" len=$func_len_result if test "$len" -lt "$max_cmd_len" || test "$max_cmd_len" -le -1; then cmds=$old_archive_cmds elif test -n "$archiver_list_spec"; then func_verbose "using command file archive linking..." for obj in $oldobjs do func_to_tool_file "$obj" $ECHO "$func_to_tool_file_result" done > $output_objdir/$libname.libcmd func_to_tool_file "$output_objdir/$libname.libcmd" oldobjs=" $archiver_list_spec$func_to_tool_file_result" cmds=$old_archive_cmds else # the command line is too long to link in one step, link in parts func_verbose "using piecewise archive linking..." save_RANLIB=$RANLIB RANLIB=: objlist= concat_cmds= save_oldobjs=$oldobjs oldobjs= # Is there a better way of finding the last object in the list? for obj in $save_oldobjs do last_oldobj=$obj done eval test_cmds=\"$old_archive_cmds\" func_len " $test_cmds" len0=$func_len_result len=$len0 for obj in $save_oldobjs do func_len " $obj" func_arith $len + $func_len_result len=$func_arith_result func_append objlist " $obj" if test "$len" -lt "$max_cmd_len"; then : else # the above command should be used before it gets too long oldobjs=$objlist if test "$obj" = "$last_oldobj" ; then RANLIB=$save_RANLIB fi test -z "$concat_cmds" || concat_cmds=$concat_cmds~ eval concat_cmds=\"\${concat_cmds}$old_archive_cmds\" objlist= len=$len0 fi done RANLIB=$save_RANLIB oldobjs=$objlist if test "X$oldobjs" = "X" ; then eval cmds=\"\$concat_cmds\" else eval cmds=\"\$concat_cmds~\$old_archive_cmds\" fi fi fi func_execute_cmds "$cmds" 'exit $?' done test -n "$generated" && \ func_show_eval "${RM}r$generated" # Now create the libtool archive. case $output in *.la) old_library= test "$build_old_libs" = yes && old_library="$libname.$libext" func_verbose "creating $output" # Preserve any variables that may affect compiler behavior for var in $variables_saved_for_relink; do if eval test -z \"\${$var+set}\"; then relink_command="{ test -z \"\${$var+set}\" || $lt_unset $var || { $var=; export $var; }; }; $relink_command" elif eval var_value=\$$var; test -z "$var_value"; then relink_command="$var=; export $var; $relink_command" else func_quote_for_eval "$var_value" relink_command="$var=$func_quote_for_eval_result; export $var; $relink_command" fi done # Quote the link command for shipping. relink_command="(cd `pwd`; $SHELL $progpath $preserve_args --mode=relink $libtool_args @inst_prefix_dir@)" relink_command=`$ECHO "$relink_command" | $SED "$sed_quote_subst"` if test "$hardcode_automatic" = yes ; then relink_command= fi # Only create the output if not a dry run. $opt_dry_run || { for installed in no yes; do if test "$installed" = yes; then if test -z "$install_libdir"; then break fi output="$output_objdir/$outputname"i # Replace all uninstalled libtool libraries with the installed ones newdependency_libs= for deplib in $dependency_libs; do case $deplib in *.la) func_basename "$deplib" name="$func_basename_result" func_resolve_sysroot "$deplib" eval libdir=`${SED} -n -e 's/^libdir=\(.*\)$/\1/p' $func_resolve_sysroot_result` test -z "$libdir" && \ func_fatal_error "\`$deplib' is not a valid libtool archive" func_append newdependency_libs " ${lt_sysroot:+=}$libdir/$name" ;; -L*) func_stripname -L '' "$deplib" func_replace_sysroot "$func_stripname_result" func_append newdependency_libs " -L$func_replace_sysroot_result" ;; -R*) func_stripname -R '' "$deplib" func_replace_sysroot "$func_stripname_result" func_append newdependency_libs " -R$func_replace_sysroot_result" ;; *) func_append newdependency_libs " $deplib" ;; esac done dependency_libs="$newdependency_libs" newdlfiles= for lib in $dlfiles; do case $lib in *.la) func_basename "$lib" name="$func_basename_result" eval libdir=`${SED} -n -e 's/^libdir=\(.*\)$/\1/p' $lib` test -z "$libdir" && \ func_fatal_error "\`$lib' is not a valid libtool archive" func_append newdlfiles " ${lt_sysroot:+=}$libdir/$name" ;; *) func_append newdlfiles " $lib" ;; esac done dlfiles="$newdlfiles" newdlprefiles= for lib in $dlprefiles; do case $lib in *.la) # Only pass preopened files to the pseudo-archive (for # eventual linking with the app. that links it) if we # didn't already link the preopened objects directly into # the library: func_basename "$lib" name="$func_basename_result" eval libdir=`${SED} -n -e 's/^libdir=\(.*\)$/\1/p' $lib` test -z "$libdir" && \ func_fatal_error "\`$lib' is not a valid libtool archive" func_append newdlprefiles " ${lt_sysroot:+=}$libdir/$name" ;; esac done dlprefiles="$newdlprefiles" else newdlfiles= for lib in $dlfiles; do case $lib in [\\/]* | [A-Za-z]:[\\/]*) abs="$lib" ;; *) abs=`pwd`"/$lib" ;; esac func_append newdlfiles " $abs" done dlfiles="$newdlfiles" newdlprefiles= for lib in $dlprefiles; do case $lib in [\\/]* | [A-Za-z]:[\\/]*) abs="$lib" ;; *) abs=`pwd`"/$lib" ;; esac func_append newdlprefiles " $abs" done dlprefiles="$newdlprefiles" fi $RM $output # place dlname in correct position for cygwin # In fact, it would be nice if we could use this code for all target # systems that can't hard-code library paths into their executables # and that have no shared library path variable independent of PATH, # but it turns out we can't easily determine that from inspecting # libtool variables, so we have to hard-code the OSs to which it # applies here; at the moment, that means platforms that use the PE # object format with DLL files. See the long comment at the top of # tests/bindir.at for full details. tdlname=$dlname case $host,$output,$installed,$module,$dlname in *cygwin*,*lai,yes,no,*.dll | *mingw*,*lai,yes,no,*.dll | *cegcc*,*lai,yes,no,*.dll) # If a -bindir argument was supplied, place the dll there. if test "x$bindir" != x ; then func_relative_path "$install_libdir" "$bindir" tdlname=$func_relative_path_result$dlname else # Otherwise fall back on heuristic. tdlname=../bin/$dlname fi ;; esac $ECHO > $output "\ # $outputname - a libtool library file # Generated by $PROGRAM (GNU $PACKAGE$TIMESTAMP) $VERSION # # Please DO NOT delete this file! # It is necessary for linking the library. # The name that we can dlopen(3). dlname='$tdlname' # Names of this library. library_names='$library_names' # The name of the static archive. old_library='$old_library' # Linker flags that can not go in dependency_libs. inherited_linker_flags='$new_inherited_linker_flags' # Libraries that this one depends upon. dependency_libs='$dependency_libs' # Names of additional weak libraries provided by this library weak_library_names='$weak_libs' # Version information for $libname. current=$current age=$age revision=$revision # Is this an already installed library? installed=$installed # Should we warn about portability when linking against -modules? shouldnotlink=$module # Files to dlopen/dlpreopen dlopen='$dlfiles' dlpreopen='$dlprefiles' # Directory that this library needs to be installed in: libdir='$install_libdir'" if test "$installed" = no && test "$need_relink" = yes; then $ECHO >> $output "\ relink_command=\"$relink_command\"" fi done } # Do a symbolic link so that the libtool archive can be found in # LD_LIBRARY_PATH before the program is installed. func_show_eval '( cd "$output_objdir" && $RM "$outputname" && $LN_S "../$outputname" "$outputname" )' 'exit $?' ;; esac exit $EXIT_SUCCESS } { test "$opt_mode" = link || test "$opt_mode" = relink; } && func_mode_link ${1+"$@"} # func_mode_uninstall arg... func_mode_uninstall () { $opt_debug RM="$nonopt" files= rmforce= exit_status=0 # This variable tells wrapper scripts just to set variables rather # than running their programs. libtool_install_magic="$magic" for arg do case $arg in -f) func_append RM " $arg"; rmforce=yes ;; -*) func_append RM " $arg" ;; *) func_append files " $arg" ;; esac done test -z "$RM" && \ func_fatal_help "you must specify an RM program" rmdirs= for file in $files; do func_dirname "$file" "" "." dir="$func_dirname_result" if test "X$dir" = X.; then odir="$objdir" else odir="$dir/$objdir" fi func_basename "$file" name="$func_basename_result" test "$opt_mode" = uninstall && odir="$dir" # Remember odir for removal later, being careful to avoid duplicates if test "$opt_mode" = clean; then case " $rmdirs " in *" $odir "*) ;; *) func_append rmdirs " $odir" ;; esac fi # Don't error if the file doesn't exist and rm -f was used. if { test -L "$file"; } >/dev/null 2>&1 || { test -h "$file"; } >/dev/null 2>&1 || test -f "$file"; then : elif test -d "$file"; then exit_status=1 continue elif test "$rmforce" = yes; then continue fi rmfiles="$file" case $name in *.la) # Possibly a libtool archive, so verify it. if func_lalib_p "$file"; then func_source $dir/$name # Delete the libtool libraries and symlinks. for n in $library_names; do func_append rmfiles " $odir/$n" done test -n "$old_library" && func_append rmfiles " $odir/$old_library" case "$opt_mode" in clean) case " $library_names " in *" $dlname "*) ;; *) test -n "$dlname" && func_append rmfiles " $odir/$dlname" ;; esac test -n "$libdir" && func_append rmfiles " $odir/$name $odir/${name}i" ;; uninstall) if test -n "$library_names"; then # Do each command in the postuninstall commands. func_execute_cmds "$postuninstall_cmds" 'test "$rmforce" = yes || exit_status=1' fi if test -n "$old_library"; then # Do each command in the old_postuninstall commands. func_execute_cmds "$old_postuninstall_cmds" 'test "$rmforce" = yes || exit_status=1' fi # FIXME: should reinstall the best remaining shared library. ;; esac fi ;; *.lo) # Possibly a libtool object, so verify it. if func_lalib_p "$file"; then # Read the .lo file func_source $dir/$name # Add PIC object to the list of files to remove. if test -n "$pic_object" && test "$pic_object" != none; then func_append rmfiles " $dir/$pic_object" fi # Add non-PIC object to the list of files to remove. if test -n "$non_pic_object" && test "$non_pic_object" != none; then func_append rmfiles " $dir/$non_pic_object" fi fi ;; *) if test "$opt_mode" = clean ; then noexename=$name case $file in *.exe) func_stripname '' '.exe' "$file" file=$func_stripname_result func_stripname '' '.exe' "$name" noexename=$func_stripname_result # $file with .exe has already been added to rmfiles, # add $file without .exe func_append rmfiles " $file" ;; esac # Do a test to see if this is a libtool program. if func_ltwrapper_p "$file"; then if func_ltwrapper_executable_p "$file"; then func_ltwrapper_scriptname "$file" relink_command= func_source $func_ltwrapper_scriptname_result func_append rmfiles " $func_ltwrapper_scriptname_result" else relink_command= func_source $dir/$noexename fi # note $name still contains .exe if it was in $file originally # as does the version of $file that was added into $rmfiles func_append rmfiles " $odir/$name $odir/${name}S.${objext}" if test "$fast_install" = yes && test -n "$relink_command"; then func_append rmfiles " $odir/lt-$name" fi if test "X$noexename" != "X$name" ; then func_append rmfiles " $odir/lt-${noexename}.c" fi fi fi ;; esac func_show_eval "$RM $rmfiles" 'exit_status=1' done # Try to remove the ${objdir}s in the directories where we deleted files for dir in $rmdirs; do if test -d "$dir"; then func_show_eval "rmdir $dir >/dev/null 2>&1" fi done exit $exit_status } { test "$opt_mode" = uninstall || test "$opt_mode" = clean; } && func_mode_uninstall ${1+"$@"} test -z "$opt_mode" && { help="$generic_help" func_fatal_help "you must specify a MODE" } test -z "$exec_cmd" && \ func_fatal_help "invalid operation mode \`$opt_mode'" if test -n "$exec_cmd"; then eval exec "$exec_cmd" exit $EXIT_FAILURE fi exit $exit_status # The TAGs below are defined such that we never get into a situation # in which we disable both kinds of libraries. Given conflicting # choices, we go for a static library, that is the most portable, # since we can't tell whether shared libraries were disabled because # the user asked for that or because the platform doesn't support # them. This is particularly important on AIX, because we don't # support having both static and shared libraries enabled at the same # time on that platform, so we default to a shared-only configuration. # If a disable-shared tag is given, we'll fallback to a static-only # configuration. But we'll never go from static-only to shared-only. # ### BEGIN LIBTOOL TAG CONFIG: disable-shared build_libtool_libs=no build_old_libs=yes # ### END LIBTOOL TAG CONFIG: disable-shared # ### BEGIN LIBTOOL TAG CONFIG: disable-static build_old_libs=`case $build_libtool_libs in yes) echo no;; *) echo yes;; esac` # ### END LIBTOOL TAG CONFIG: disable-static # Local Variables: # mode:shell-script # sh-indentation:2 # End: # vi:sw=2 telepathy-gabble-0.18.2/missing0000755000175000017500000001533012312536073016420 0ustar00smcvsmcv00000000000000#! /bin/sh # Common wrapper for a few potentially missing GNU programs. scriptversion=2013-10-28.13; # UTC # Copyright (C) 1996-2013 Free Software Foundation, Inc. # Originally written by Fran,cois Pinard , 1996. # This program 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 2, or (at your option) # any later version. # This program 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 this program. If not, see . # As a special exception to the GNU General Public License, if you # distribute this file as part of a program that contains a # configuration script generated by Autoconf, you may include it under # the same distribution terms that you use for the rest of that program. if test $# -eq 0; then echo 1>&2 "Try '$0 --help' for more information" exit 1 fi case $1 in --is-lightweight) # Used by our autoconf macros to check whether the available missing # script is modern enough. exit 0 ;; --run) # Back-compat with the calling convention used by older automake. shift ;; -h|--h|--he|--hel|--help) echo "\ $0 [OPTION]... PROGRAM [ARGUMENT]... Run 'PROGRAM [ARGUMENT]...', returning a proper advice when this fails due to PROGRAM being missing or too old. Options: -h, --help display this help and exit -v, --version output version information and exit Supported PROGRAM values: aclocal autoconf autoheader autom4te automake makeinfo bison yacc flex lex help2man Version suffixes to PROGRAM as well as the prefixes 'gnu-', 'gnu', and 'g' are ignored when checking the name. Send bug reports to ." exit $? ;; -v|--v|--ve|--ver|--vers|--versi|--versio|--version) echo "missing $scriptversion (GNU Automake)" exit $? ;; -*) echo 1>&2 "$0: unknown '$1' option" echo 1>&2 "Try '$0 --help' for more information" exit 1 ;; esac # Run the given program, remember its exit status. "$@"; st=$? # If it succeeded, we are done. test $st -eq 0 && exit 0 # Also exit now if we it failed (or wasn't found), and '--version' was # passed; such an option is passed most likely to detect whether the # program is present and works. case $2 in --version|--help) exit $st;; esac # Exit code 63 means version mismatch. This often happens when the user # tries to use an ancient version of a tool on a file that requires a # minimum version. if test $st -eq 63; then msg="probably too old" elif test $st -eq 127; then # Program was missing. msg="missing on your system" else # Program was found and executed, but failed. Give up. exit $st fi perl_URL=http://www.perl.org/ flex_URL=http://flex.sourceforge.net/ gnu_software_URL=http://www.gnu.org/software program_details () { case $1 in aclocal|automake) echo "The '$1' program is part of the GNU Automake package:" echo "<$gnu_software_URL/automake>" echo "It also requires GNU Autoconf, GNU m4 and Perl in order to run:" echo "<$gnu_software_URL/autoconf>" echo "<$gnu_software_URL/m4/>" echo "<$perl_URL>" ;; autoconf|autom4te|autoheader) echo "The '$1' program is part of the GNU Autoconf package:" echo "<$gnu_software_URL/autoconf/>" echo "It also requires GNU m4 and Perl in order to run:" echo "<$gnu_software_URL/m4/>" echo "<$perl_URL>" ;; esac } give_advice () { # Normalize program name to check for. normalized_program=`echo "$1" | sed ' s/^gnu-//; t s/^gnu//; t s/^g//; t'` printf '%s\n' "'$1' is $msg." configure_deps="'configure.ac' or m4 files included by 'configure.ac'" case $normalized_program in autoconf*) echo "You should only need it if you modified 'configure.ac'," echo "or m4 files included by it." program_details 'autoconf' ;; autoheader*) echo "You should only need it if you modified 'acconfig.h' or" echo "$configure_deps." program_details 'autoheader' ;; automake*) echo "You should only need it if you modified 'Makefile.am' or" echo "$configure_deps." program_details 'automake' ;; aclocal*) echo "You should only need it if you modified 'acinclude.m4' or" echo "$configure_deps." program_details 'aclocal' ;; autom4te*) echo "You might have modified some maintainer files that require" echo "the 'autom4te' program to be rebuilt." program_details 'autom4te' ;; bison*|yacc*) echo "You should only need it if you modified a '.y' file." echo "You may want to install the GNU Bison package:" echo "<$gnu_software_URL/bison/>" ;; lex*|flex*) echo "You should only need it if you modified a '.l' file." echo "You may want to install the Fast Lexical Analyzer package:" echo "<$flex_URL>" ;; help2man*) echo "You should only need it if you modified a dependency" \ "of a man page." echo "You may want to install the GNU Help2man package:" echo "<$gnu_software_URL/help2man/>" ;; makeinfo*) echo "You should only need it if you modified a '.texi' file, or" echo "any other file indirectly affecting the aspect of the manual." echo "You might want to install the Texinfo package:" echo "<$gnu_software_URL/texinfo/>" echo "The spurious makeinfo call might also be the consequence of" echo "using a buggy 'make' (AIX, DU, IRIX), in which case you might" echo "want to install GNU make:" echo "<$gnu_software_URL/make/>" ;; *) echo "You might have modified some files without having the proper" echo "tools for further handling them. Check the 'README' file, it" echo "often tells you about the needed prerequisites for installing" echo "this package. You may also peek at any GNU archive site, in" echo "case some other package contains this missing '$1' program." ;; esac } give_advice "$1" | sed -e '1s/^/WARNING: /' \ -e '2,$s/^/ /' >&2 # Propagate the correct exit status (expected to be 127 for a program # not found, 63 for a program that failed due to version mismatch). exit $st # Local variables: # eval: (add-hook 'write-file-hooks 'time-stamp) # time-stamp-start: "scriptversion=" # time-stamp-format: "%:y-%02m-%02d.%02H" # time-stamp-time-zone: "UTC" # time-stamp-end: "; # UTC" # End: telepathy-gabble-0.18.2/install-sh0000755000175000017500000003325512312536073017033 0ustar00smcvsmcv00000000000000#!/bin/sh # install - install a program, script, or datafile scriptversion=2011-11-20.07; # UTC # This originates from X11R5 (mit/util/scripts/install.sh), which was # later released in X11R6 (xc/config/util/install.sh) with the # following copyright and license. # # Copyright (C) 1994 X Consortium # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to # deal in the Software without restriction, including without limitation the # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or # sell copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN # AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNEC- # TION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # # Except as contained in this notice, the name of the X Consortium shall not # be used in advertising or otherwise to promote the sale, use or other deal- # ings in this Software without prior written authorization from the X Consor- # tium. # # # FSF changes to this file are in the public domain. # # Calling this script install-sh is preferred over install.sh, to prevent # 'make' implicit rules from creating a file called install from it # when there is no Makefile. # # This script is compatible with the BSD install script, but was written # from scratch. nl=' ' IFS=" "" $nl" # set DOITPROG to echo to test this script # Don't use :- since 4.3BSD and earlier shells don't like it. doit=${DOITPROG-} if test -z "$doit"; then doit_exec=exec else doit_exec=$doit fi # Put in absolute file names if you don't have them in your path; # or use environment vars. chgrpprog=${CHGRPPROG-chgrp} chmodprog=${CHMODPROG-chmod} chownprog=${CHOWNPROG-chown} cmpprog=${CMPPROG-cmp} cpprog=${CPPROG-cp} mkdirprog=${MKDIRPROG-mkdir} mvprog=${MVPROG-mv} rmprog=${RMPROG-rm} stripprog=${STRIPPROG-strip} posix_glob='?' initialize_posix_glob=' test "$posix_glob" != "?" || { if (set -f) 2>/dev/null; then posix_glob= else posix_glob=: fi } ' posix_mkdir= # Desired mode of installed file. mode=0755 chgrpcmd= chmodcmd=$chmodprog chowncmd= mvcmd=$mvprog rmcmd="$rmprog -f" stripcmd= src= dst= dir_arg= dst_arg= copy_on_change=false no_target_directory= usage="\ Usage: $0 [OPTION]... [-T] SRCFILE DSTFILE or: $0 [OPTION]... SRCFILES... DIRECTORY or: $0 [OPTION]... -t DIRECTORY SRCFILES... or: $0 [OPTION]... -d DIRECTORIES... In the 1st form, copy SRCFILE to DSTFILE. In the 2nd and 3rd, copy all SRCFILES to DIRECTORY. In the 4th, create DIRECTORIES. Options: --help display this help and exit. --version display version info and exit. -c (ignored) -C install only if different (preserve the last data modification time) -d create directories instead of installing files. -g GROUP $chgrpprog installed files to GROUP. -m MODE $chmodprog installed files to MODE. -o USER $chownprog installed files to USER. -s $stripprog installed files. -t DIRECTORY install into DIRECTORY. -T report an error if DSTFILE is a directory. Environment variables override the default commands: CHGRPPROG CHMODPROG CHOWNPROG CMPPROG CPPROG MKDIRPROG MVPROG RMPROG STRIPPROG " while test $# -ne 0; do case $1 in -c) ;; -C) copy_on_change=true;; -d) dir_arg=true;; -g) chgrpcmd="$chgrpprog $2" shift;; --help) echo "$usage"; exit $?;; -m) mode=$2 case $mode in *' '* | *' '* | *' '* | *'*'* | *'?'* | *'['*) echo "$0: invalid mode: $mode" >&2 exit 1;; esac shift;; -o) chowncmd="$chownprog $2" shift;; -s) stripcmd=$stripprog;; -t) dst_arg=$2 # Protect names problematic for 'test' and other utilities. case $dst_arg in -* | [=\(\)!]) dst_arg=./$dst_arg;; esac shift;; -T) no_target_directory=true;; --version) echo "$0 $scriptversion"; exit $?;; --) shift break;; -*) echo "$0: invalid option: $1" >&2 exit 1;; *) break;; esac shift done if test $# -ne 0 && test -z "$dir_arg$dst_arg"; then # When -d is used, all remaining arguments are directories to create. # When -t is used, the destination is already specified. # Otherwise, the last argument is the destination. Remove it from $@. for arg do if test -n "$dst_arg"; then # $@ is not empty: it contains at least $arg. set fnord "$@" "$dst_arg" shift # fnord fi shift # arg dst_arg=$arg # Protect names problematic for 'test' and other utilities. case $dst_arg in -* | [=\(\)!]) dst_arg=./$dst_arg;; esac done fi if test $# -eq 0; then if test -z "$dir_arg"; then echo "$0: no input file specified." >&2 exit 1 fi # It's OK to call 'install-sh -d' without argument. # This can happen when creating conditional directories. exit 0 fi if test -z "$dir_arg"; then do_exit='(exit $ret); exit $ret' trap "ret=129; $do_exit" 1 trap "ret=130; $do_exit" 2 trap "ret=141; $do_exit" 13 trap "ret=143; $do_exit" 15 # Set umask so as not to create temps with too-generous modes. # However, 'strip' requires both read and write access to temps. case $mode in # Optimize common cases. *644) cp_umask=133;; *755) cp_umask=22;; *[0-7]) if test -z "$stripcmd"; then u_plus_rw= else u_plus_rw='% 200' fi cp_umask=`expr '(' 777 - $mode % 1000 ')' $u_plus_rw`;; *) if test -z "$stripcmd"; then u_plus_rw= else u_plus_rw=,u+rw fi cp_umask=$mode$u_plus_rw;; esac fi for src do # Protect names problematic for 'test' and other utilities. case $src in -* | [=\(\)!]) src=./$src;; esac if test -n "$dir_arg"; then dst=$src dstdir=$dst test -d "$dstdir" dstdir_status=$? else # Waiting for this to be detected by the "$cpprog $src $dsttmp" command # might cause directories to be created, which would be especially bad # if $src (and thus $dsttmp) contains '*'. if test ! -f "$src" && test ! -d "$src"; then echo "$0: $src does not exist." >&2 exit 1 fi if test -z "$dst_arg"; then echo "$0: no destination specified." >&2 exit 1 fi dst=$dst_arg # If destination is a directory, append the input filename; won't work # if double slashes aren't ignored. if test -d "$dst"; then if test -n "$no_target_directory"; then echo "$0: $dst_arg: Is a directory" >&2 exit 1 fi dstdir=$dst dst=$dstdir/`basename "$src"` dstdir_status=0 else # Prefer dirname, but fall back on a substitute if dirname fails. dstdir=` (dirname "$dst") 2>/dev/null || expr X"$dst" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$dst" : 'X\(//\)[^/]' \| \ X"$dst" : 'X\(//\)$' \| \ X"$dst" : 'X\(/\)' \| . 2>/dev/null || echo X"$dst" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q' ` test -d "$dstdir" dstdir_status=$? fi fi obsolete_mkdir_used=false if test $dstdir_status != 0; then case $posix_mkdir in '') # Create intermediate dirs using mode 755 as modified by the umask. # This is like FreeBSD 'install' as of 1997-10-28. umask=`umask` case $stripcmd.$umask in # Optimize common cases. *[2367][2367]) mkdir_umask=$umask;; .*0[02][02] | .[02][02] | .[02]) mkdir_umask=22;; *[0-7]) mkdir_umask=`expr $umask + 22 \ - $umask % 100 % 40 + $umask % 20 \ - $umask % 10 % 4 + $umask % 2 `;; *) mkdir_umask=$umask,go-w;; esac # With -d, create the new directory with the user-specified mode. # Otherwise, rely on $mkdir_umask. if test -n "$dir_arg"; then mkdir_mode=-m$mode else mkdir_mode= fi posix_mkdir=false case $umask in *[123567][0-7][0-7]) # POSIX mkdir -p sets u+wx bits regardless of umask, which # is incompatible with FreeBSD 'install' when (umask & 300) != 0. ;; *) tmpdir=${TMPDIR-/tmp}/ins$RANDOM-$$ trap 'ret=$?; rmdir "$tmpdir/d" "$tmpdir" 2>/dev/null; exit $ret' 0 if (umask $mkdir_umask && exec $mkdirprog $mkdir_mode -p -- "$tmpdir/d") >/dev/null 2>&1 then if test -z "$dir_arg" || { # Check for POSIX incompatibilities with -m. # HP-UX 11.23 and IRIX 6.5 mkdir -m -p sets group- or # other-writable bit of parent directory when it shouldn't. # FreeBSD 6.1 mkdir -m -p sets mode of existing directory. ls_ld_tmpdir=`ls -ld "$tmpdir"` case $ls_ld_tmpdir in d????-?r-*) different_mode=700;; d????-?--*) different_mode=755;; *) false;; esac && $mkdirprog -m$different_mode -p -- "$tmpdir" && { ls_ld_tmpdir_1=`ls -ld "$tmpdir"` test "$ls_ld_tmpdir" = "$ls_ld_tmpdir_1" } } then posix_mkdir=: fi rmdir "$tmpdir/d" "$tmpdir" else # Remove any dirs left behind by ancient mkdir implementations. rmdir ./$mkdir_mode ./-p ./-- 2>/dev/null fi trap '' 0;; esac;; esac if $posix_mkdir && ( umask $mkdir_umask && $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir" ) then : else # The umask is ridiculous, or mkdir does not conform to POSIX, # or it failed possibly due to a race condition. Create the # directory the slow way, step by step, checking for races as we go. case $dstdir in /*) prefix='/';; [-=\(\)!]*) prefix='./';; *) prefix='';; esac eval "$initialize_posix_glob" oIFS=$IFS IFS=/ $posix_glob set -f set fnord $dstdir shift $posix_glob set +f IFS=$oIFS prefixes= for d do test X"$d" = X && continue prefix=$prefix$d if test -d "$prefix"; then prefixes= else if $posix_mkdir; then (umask=$mkdir_umask && $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir") && break # Don't fail if two instances are running concurrently. test -d "$prefix" || exit 1 else case $prefix in *\'*) qprefix=`echo "$prefix" | sed "s/'/'\\\\\\\\''/g"`;; *) qprefix=$prefix;; esac prefixes="$prefixes '$qprefix'" fi fi prefix=$prefix/ done if test -n "$prefixes"; then # Don't fail if two instances are running concurrently. (umask $mkdir_umask && eval "\$doit_exec \$mkdirprog $prefixes") || test -d "$dstdir" || exit 1 obsolete_mkdir_used=true fi fi fi if test -n "$dir_arg"; then { test -z "$chowncmd" || $doit $chowncmd "$dst"; } && { test -z "$chgrpcmd" || $doit $chgrpcmd "$dst"; } && { test "$obsolete_mkdir_used$chowncmd$chgrpcmd" = false || test -z "$chmodcmd" || $doit $chmodcmd $mode "$dst"; } || exit 1 else # Make a couple of temp file names in the proper directory. dsttmp=$dstdir/_inst.$$_ rmtmp=$dstdir/_rm.$$_ # Trap to clean up those temp files at exit. trap 'ret=$?; rm -f "$dsttmp" "$rmtmp" && exit $ret' 0 # Copy the file name to the temp name. (umask $cp_umask && $doit_exec $cpprog "$src" "$dsttmp") && # and set any options; do chmod last to preserve setuid bits. # # If any of these fail, we abort the whole thing. If we want to # ignore errors from any of these, just make sure not to ignore # errors from the above "$doit $cpprog $src $dsttmp" command. # { test -z "$chowncmd" || $doit $chowncmd "$dsttmp"; } && { test -z "$chgrpcmd" || $doit $chgrpcmd "$dsttmp"; } && { test -z "$stripcmd" || $doit $stripcmd "$dsttmp"; } && { test -z "$chmodcmd" || $doit $chmodcmd $mode "$dsttmp"; } && # If -C, don't bother to copy if it wouldn't change the file. if $copy_on_change && old=`LC_ALL=C ls -dlL "$dst" 2>/dev/null` && new=`LC_ALL=C ls -dlL "$dsttmp" 2>/dev/null` && eval "$initialize_posix_glob" && $posix_glob set -f && set X $old && old=:$2:$4:$5:$6 && set X $new && new=:$2:$4:$5:$6 && $posix_glob set +f && test "$old" = "$new" && $cmpprog "$dst" "$dsttmp" >/dev/null 2>&1 then rm -f "$dsttmp" else # Rename the file to the real destination. $doit $mvcmd -f "$dsttmp" "$dst" 2>/dev/null || # The rename failed, perhaps because mv can't rename something else # to itself, or perhaps because mv is so ancient that it does not # support -f. { # Now remove or move aside any old file at destination location. # We try this two ways since rm can't unlink itself on some # systems and the destination file might be busy for other # reasons. In this case, the final cleanup might fail but the new # file should still install successfully. { test ! -f "$dst" || $doit $rmcmd -f "$dst" 2>/dev/null || { $doit $mvcmd -f "$dst" "$rmtmp" 2>/dev/null && { $doit $rmcmd -f "$rmtmp" 2>/dev/null; :; } } || { echo "$0: cannot unlink or rename $dst" >&2 (exit 1); exit 1 } } && # Now rename the file to the real destination. $doit $mvcmd "$dsttmp" "$dst" } fi || exit 1 trap '' 0 fi done # Local variables: # eval: (add-hook 'write-file-hooks 'time-stamp) # time-stamp-start: "scriptversion=" # time-stamp-format: "%:y-%02m-%02d.%02H" # time-stamp-time-zone: "UTC" # time-stamp-end: "; # UTC" # End: telepathy-gabble-0.18.2/depcomp0000755000175000017500000005601612312536074016405 0ustar00smcvsmcv00000000000000#! /bin/sh # depcomp - compile a program generating dependencies as side-effects scriptversion=2013-05-30.07; # UTC # Copyright (C) 1999-2013 Free Software Foundation, Inc. # This program 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 2, or (at your option) # any later version. # This program 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 this program. If not, see . # As a special exception to the GNU General Public License, if you # distribute this file as part of a program that contains a # configuration script generated by Autoconf, you may include it under # the same distribution terms that you use for the rest of that program. # Originally written by Alexandre Oliva . case $1 in '') echo "$0: No command. Try '$0 --help' for more information." 1>&2 exit 1; ;; -h | --h*) cat <<\EOF Usage: depcomp [--help] [--version] PROGRAM [ARGS] Run PROGRAMS ARGS to compile a file, generating dependencies as side-effects. Environment variables: depmode Dependency tracking mode. source Source file read by 'PROGRAMS ARGS'. object Object file output by 'PROGRAMS ARGS'. DEPDIR directory where to store dependencies. depfile Dependency file to output. tmpdepfile Temporary file to use when outputting dependencies. libtool Whether libtool is used (yes/no). Report bugs to . EOF exit $? ;; -v | --v*) echo "depcomp $scriptversion" exit $? ;; esac # Get the directory component of the given path, and save it in the # global variables '$dir'. Note that this directory component will # be either empty or ending with a '/' character. This is deliberate. set_dir_from () { case $1 in */*) dir=`echo "$1" | sed -e 's|/[^/]*$|/|'`;; *) dir=;; esac } # Get the suffix-stripped basename of the given path, and save it the # global variable '$base'. set_base_from () { base=`echo "$1" | sed -e 's|^.*/||' -e 's/\.[^.]*$//'` } # If no dependency file was actually created by the compiler invocation, # we still have to create a dummy depfile, to avoid errors with the # Makefile "include basename.Plo" scheme. make_dummy_depfile () { echo "#dummy" > "$depfile" } # Factor out some common post-processing of the generated depfile. # Requires the auxiliary global variable '$tmpdepfile' to be set. aix_post_process_depfile () { # If the compiler actually managed to produce a dependency file, # post-process it. if test -f "$tmpdepfile"; then # Each line is of the form 'foo.o: dependency.h'. # Do two passes, one to just change these to # $object: dependency.h # and one to simply output # dependency.h: # which is needed to avoid the deleted-header problem. { sed -e "s,^.*\.[$lower]*:,$object:," < "$tmpdepfile" sed -e "s,^.*\.[$lower]*:[$tab ]*,," -e 's,$,:,' < "$tmpdepfile" } > "$depfile" rm -f "$tmpdepfile" else make_dummy_depfile fi } # A tabulation character. tab=' ' # A newline character. nl=' ' # Character ranges might be problematic outside the C locale. # These definitions help. upper=ABCDEFGHIJKLMNOPQRSTUVWXYZ lower=abcdefghijklmnopqrstuvwxyz digits=0123456789 alpha=${upper}${lower} if test -z "$depmode" || test -z "$source" || test -z "$object"; then echo "depcomp: Variables source, object and depmode must be set" 1>&2 exit 1 fi # Dependencies for sub/bar.o or sub/bar.obj go into sub/.deps/bar.Po. depfile=${depfile-`echo "$object" | sed 's|[^\\/]*$|'${DEPDIR-.deps}'/&|;s|\.\([^.]*\)$|.P\1|;s|Pobj$|Po|'`} tmpdepfile=${tmpdepfile-`echo "$depfile" | sed 's/\.\([^.]*\)$/.T\1/'`} rm -f "$tmpdepfile" # Avoid interferences from the environment. gccflag= dashmflag= # Some modes work just like other modes, but use different flags. We # parameterize here, but still list the modes in the big case below, # to make depend.m4 easier to write. Note that we *cannot* use a case # here, because this file can only contain one case statement. if test "$depmode" = hp; then # HP compiler uses -M and no extra arg. gccflag=-M depmode=gcc fi if test "$depmode" = dashXmstdout; then # This is just like dashmstdout with a different argument. dashmflag=-xM depmode=dashmstdout fi cygpath_u="cygpath -u -f -" if test "$depmode" = msvcmsys; then # This is just like msvisualcpp but w/o cygpath translation. # Just convert the backslash-escaped backslashes to single forward # slashes to satisfy depend.m4 cygpath_u='sed s,\\\\,/,g' depmode=msvisualcpp fi if test "$depmode" = msvc7msys; then # This is just like msvc7 but w/o cygpath translation. # Just convert the backslash-escaped backslashes to single forward # slashes to satisfy depend.m4 cygpath_u='sed s,\\\\,/,g' depmode=msvc7 fi if test "$depmode" = xlc; then # IBM C/C++ Compilers xlc/xlC can output gcc-like dependency information. gccflag=-qmakedep=gcc,-MF depmode=gcc fi case "$depmode" in gcc3) ## gcc 3 implements dependency tracking that does exactly what ## we want. Yay! Note: for some reason libtool 1.4 doesn't like ## it if -MD -MP comes after the -MF stuff. Hmm. ## Unfortunately, FreeBSD c89 acceptance of flags depends upon ## the command line argument order; so add the flags where they ## appear in depend2.am. Note that the slowdown incurred here ## affects only configure: in makefiles, %FASTDEP% shortcuts this. for arg do case $arg in -c) set fnord "$@" -MT "$object" -MD -MP -MF "$tmpdepfile" "$arg" ;; *) set fnord "$@" "$arg" ;; esac shift # fnord shift # $arg done "$@" stat=$? if test $stat -ne 0; then rm -f "$tmpdepfile" exit $stat fi mv "$tmpdepfile" "$depfile" ;; gcc) ## Note that this doesn't just cater to obsosete pre-3.x GCC compilers. ## but also to in-use compilers like IMB xlc/xlC and the HP C compiler. ## (see the conditional assignment to $gccflag above). ## There are various ways to get dependency output from gcc. Here's ## why we pick this rather obscure method: ## - Don't want to use -MD because we'd like the dependencies to end ## up in a subdir. Having to rename by hand is ugly. ## (We might end up doing this anyway to support other compilers.) ## - The DEPENDENCIES_OUTPUT environment variable makes gcc act like ## -MM, not -M (despite what the docs say). Also, it might not be ## supported by the other compilers which use the 'gcc' depmode. ## - Using -M directly means running the compiler twice (even worse ## than renaming). if test -z "$gccflag"; then gccflag=-MD, fi "$@" -Wp,"$gccflag$tmpdepfile" stat=$? if test $stat -ne 0; then rm -f "$tmpdepfile" exit $stat fi rm -f "$depfile" echo "$object : \\" > "$depfile" # The second -e expression handles DOS-style file names with drive # letters. sed -e 's/^[^:]*: / /' \ -e 's/^['$alpha']:\/[^:]*: / /' < "$tmpdepfile" >> "$depfile" ## This next piece of magic avoids the "deleted header file" problem. ## The problem is that when a header file which appears in a .P file ## is deleted, the dependency causes make to die (because there is ## typically no way to rebuild the header). We avoid this by adding ## dummy dependencies for each header file. Too bad gcc doesn't do ## this for us directly. ## Some versions of gcc put a space before the ':'. On the theory ## that the space means something, we add a space to the output as ## well. hp depmode also adds that space, but also prefixes the VPATH ## to the object. Take care to not repeat it in the output. ## Some versions of the HPUX 10.20 sed can't process this invocation ## correctly. Breaking it into two sed invocations is a workaround. tr ' ' "$nl" < "$tmpdepfile" \ | sed -e 's/^\\$//' -e '/^$/d' -e "s|.*$object$||" -e '/:$/d' \ | sed -e 's/$/ :/' >> "$depfile" rm -f "$tmpdepfile" ;; hp) # This case exists only to let depend.m4 do its work. It works by # looking at the text of this script. This case will never be run, # since it is checked for above. exit 1 ;; sgi) if test "$libtool" = yes; then "$@" "-Wp,-MDupdate,$tmpdepfile" else "$@" -MDupdate "$tmpdepfile" fi stat=$? if test $stat -ne 0; then rm -f "$tmpdepfile" exit $stat fi rm -f "$depfile" if test -f "$tmpdepfile"; then # yes, the sourcefile depend on other files echo "$object : \\" > "$depfile" # Clip off the initial element (the dependent). Don't try to be # clever and replace this with sed code, as IRIX sed won't handle # lines with more than a fixed number of characters (4096 in # IRIX 6.2 sed, 8192 in IRIX 6.5). We also remove comment lines; # the IRIX cc adds comments like '#:fec' to the end of the # dependency line. tr ' ' "$nl" < "$tmpdepfile" \ | sed -e 's/^.*\.o://' -e 's/#.*$//' -e '/^$/ d' \ | tr "$nl" ' ' >> "$depfile" echo >> "$depfile" # The second pass generates a dummy entry for each header file. tr ' ' "$nl" < "$tmpdepfile" \ | sed -e 's/^.*\.o://' -e 's/#.*$//' -e '/^$/ d' -e 's/$/:/' \ >> "$depfile" else make_dummy_depfile fi rm -f "$tmpdepfile" ;; xlc) # This case exists only to let depend.m4 do its work. It works by # looking at the text of this script. This case will never be run, # since it is checked for above. exit 1 ;; aix) # The C for AIX Compiler uses -M and outputs the dependencies # in a .u file. In older versions, this file always lives in the # current directory. Also, the AIX compiler puts '$object:' at the # start of each line; $object doesn't have directory information. # Version 6 uses the directory in both cases. set_dir_from "$object" set_base_from "$object" if test "$libtool" = yes; then tmpdepfile1=$dir$base.u tmpdepfile2=$base.u tmpdepfile3=$dir.libs/$base.u "$@" -Wc,-M else tmpdepfile1=$dir$base.u tmpdepfile2=$dir$base.u tmpdepfile3=$dir$base.u "$@" -M fi stat=$? if test $stat -ne 0; then rm -f "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3" exit $stat fi for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3" do test -f "$tmpdepfile" && break done aix_post_process_depfile ;; tcc) # tcc (Tiny C Compiler) understand '-MD -MF file' since version 0.9.26 # FIXME: That version still under development at the moment of writing. # Make that this statement remains true also for stable, released # versions. # It will wrap lines (doesn't matter whether long or short) with a # trailing '\', as in: # # foo.o : \ # foo.c \ # foo.h \ # # It will put a trailing '\' even on the last line, and will use leading # spaces rather than leading tabs (at least since its commit 0394caf7 # "Emit spaces for -MD"). "$@" -MD -MF "$tmpdepfile" stat=$? if test $stat -ne 0; then rm -f "$tmpdepfile" exit $stat fi rm -f "$depfile" # Each non-empty line is of the form 'foo.o : \' or ' dep.h \'. # We have to change lines of the first kind to '$object: \'. sed -e "s|.*:|$object :|" < "$tmpdepfile" > "$depfile" # And for each line of the second kind, we have to emit a 'dep.h:' # dummy dependency, to avoid the deleted-header problem. sed -n -e 's|^ *\(.*\) *\\$|\1:|p' < "$tmpdepfile" >> "$depfile" rm -f "$tmpdepfile" ;; ## The order of this option in the case statement is important, since the ## shell code in configure will try each of these formats in the order ## listed in this file. A plain '-MD' option would be understood by many ## compilers, so we must ensure this comes after the gcc and icc options. pgcc) # Portland's C compiler understands '-MD'. # Will always output deps to 'file.d' where file is the root name of the # source file under compilation, even if file resides in a subdirectory. # The object file name does not affect the name of the '.d' file. # pgcc 10.2 will output # foo.o: sub/foo.c sub/foo.h # and will wrap long lines using '\' : # foo.o: sub/foo.c ... \ # sub/foo.h ... \ # ... set_dir_from "$object" # Use the source, not the object, to determine the base name, since # that's sadly what pgcc will do too. set_base_from "$source" tmpdepfile=$base.d # For projects that build the same source file twice into different object # files, the pgcc approach of using the *source* file root name can cause # problems in parallel builds. Use a locking strategy to avoid stomping on # the same $tmpdepfile. lockdir=$base.d-lock trap " echo '$0: caught signal, cleaning up...' >&2 rmdir '$lockdir' exit 1 " 1 2 13 15 numtries=100 i=$numtries while test $i -gt 0; do # mkdir is a portable test-and-set. if mkdir "$lockdir" 2>/dev/null; then # This process acquired the lock. "$@" -MD stat=$? # Release the lock. rmdir "$lockdir" break else # If the lock is being held by a different process, wait # until the winning process is done or we timeout. while test -d "$lockdir" && test $i -gt 0; do sleep 1 i=`expr $i - 1` done fi i=`expr $i - 1` done trap - 1 2 13 15 if test $i -le 0; then echo "$0: failed to acquire lock after $numtries attempts" >&2 echo "$0: check lockdir '$lockdir'" >&2 exit 1 fi if test $stat -ne 0; then rm -f "$tmpdepfile" exit $stat fi rm -f "$depfile" # Each line is of the form `foo.o: dependent.h', # or `foo.o: dep1.h dep2.h \', or ` dep3.h dep4.h \'. # Do two passes, one to just change these to # `$object: dependent.h' and one to simply `dependent.h:'. sed "s,^[^:]*:,$object :," < "$tmpdepfile" > "$depfile" # Some versions of the HPUX 10.20 sed can't process this invocation # correctly. Breaking it into two sed invocations is a workaround. sed 's,^[^:]*: \(.*\)$,\1,;s/^\\$//;/^$/d;/:$/d' < "$tmpdepfile" \ | sed -e 's/$/ :/' >> "$depfile" rm -f "$tmpdepfile" ;; hp2) # The "hp" stanza above does not work with aCC (C++) and HP's ia64 # compilers, which have integrated preprocessors. The correct option # to use with these is +Maked; it writes dependencies to a file named # 'foo.d', which lands next to the object file, wherever that # happens to be. # Much of this is similar to the tru64 case; see comments there. set_dir_from "$object" set_base_from "$object" if test "$libtool" = yes; then tmpdepfile1=$dir$base.d tmpdepfile2=$dir.libs/$base.d "$@" -Wc,+Maked else tmpdepfile1=$dir$base.d tmpdepfile2=$dir$base.d "$@" +Maked fi stat=$? if test $stat -ne 0; then rm -f "$tmpdepfile1" "$tmpdepfile2" exit $stat fi for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2" do test -f "$tmpdepfile" && break done if test -f "$tmpdepfile"; then sed -e "s,^.*\.[$lower]*:,$object:," "$tmpdepfile" > "$depfile" # Add 'dependent.h:' lines. sed -ne '2,${ s/^ *// s/ \\*$// s/$/:/ p }' "$tmpdepfile" >> "$depfile" else make_dummy_depfile fi rm -f "$tmpdepfile" "$tmpdepfile2" ;; tru64) # The Tru64 compiler uses -MD to generate dependencies as a side # effect. 'cc -MD -o foo.o ...' puts the dependencies into 'foo.o.d'. # At least on Alpha/Redhat 6.1, Compaq CCC V6.2-504 seems to put # dependencies in 'foo.d' instead, so we check for that too. # Subdirectories are respected. set_dir_from "$object" set_base_from "$object" if test "$libtool" = yes; then # Libtool generates 2 separate objects for the 2 libraries. These # two compilations output dependencies in $dir.libs/$base.o.d and # in $dir$base.o.d. We have to check for both files, because # one of the two compilations can be disabled. We should prefer # $dir$base.o.d over $dir.libs/$base.o.d because the latter is # automatically cleaned when .libs/ is deleted, while ignoring # the former would cause a distcleancheck panic. tmpdepfile1=$dir$base.o.d # libtool 1.5 tmpdepfile2=$dir.libs/$base.o.d # Likewise. tmpdepfile3=$dir.libs/$base.d # Compaq CCC V6.2-504 "$@" -Wc,-MD else tmpdepfile1=$dir$base.d tmpdepfile2=$dir$base.d tmpdepfile3=$dir$base.d "$@" -MD fi stat=$? if test $stat -ne 0; then rm -f "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3" exit $stat fi for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3" do test -f "$tmpdepfile" && break done # Same post-processing that is required for AIX mode. aix_post_process_depfile ;; msvc7) if test "$libtool" = yes; then showIncludes=-Wc,-showIncludes else showIncludes=-showIncludes fi "$@" $showIncludes > "$tmpdepfile" stat=$? grep -v '^Note: including file: ' "$tmpdepfile" if test $stat -ne 0; then rm -f "$tmpdepfile" exit $stat fi rm -f "$depfile" echo "$object : \\" > "$depfile" # The first sed program below extracts the file names and escapes # backslashes for cygpath. The second sed program outputs the file # name when reading, but also accumulates all include files in the # hold buffer in order to output them again at the end. This only # works with sed implementations that can handle large buffers. sed < "$tmpdepfile" -n ' /^Note: including file: *\(.*\)/ { s//\1/ s/\\/\\\\/g p }' | $cygpath_u | sort -u | sed -n ' s/ /\\ /g s/\(.*\)/'"$tab"'\1 \\/p s/.\(.*\) \\/\1:/ H $ { s/.*/'"$tab"'/ G p }' >> "$depfile" echo >> "$depfile" # make sure the fragment doesn't end with a backslash rm -f "$tmpdepfile" ;; msvc7msys) # This case exists only to let depend.m4 do its work. It works by # looking at the text of this script. This case will never be run, # since it is checked for above. exit 1 ;; #nosideeffect) # This comment above is used by automake to tell side-effect # dependency tracking mechanisms from slower ones. dashmstdout) # Important note: in order to support this mode, a compiler *must* # always write the preprocessed file to stdout, regardless of -o. "$@" || exit $? # Remove the call to Libtool. if test "$libtool" = yes; then while test "X$1" != 'X--mode=compile'; do shift done shift fi # Remove '-o $object'. IFS=" " for arg do case $arg in -o) shift ;; $object) shift ;; *) set fnord "$@" "$arg" shift # fnord shift # $arg ;; esac done test -z "$dashmflag" && dashmflag=-M # Require at least two characters before searching for ':' # in the target name. This is to cope with DOS-style filenames: # a dependency such as 'c:/foo/bar' could be seen as target 'c' otherwise. "$@" $dashmflag | sed "s|^[$tab ]*[^:$tab ][^:][^:]*:[$tab ]*|$object: |" > "$tmpdepfile" rm -f "$depfile" cat < "$tmpdepfile" > "$depfile" # Some versions of the HPUX 10.20 sed can't process this sed invocation # correctly. Breaking it into two sed invocations is a workaround. tr ' ' "$nl" < "$tmpdepfile" \ | sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' \ | sed -e 's/$/ :/' >> "$depfile" rm -f "$tmpdepfile" ;; dashXmstdout) # This case only exists to satisfy depend.m4. It is never actually # run, as this mode is specially recognized in the preamble. exit 1 ;; makedepend) "$@" || exit $? # Remove any Libtool call if test "$libtool" = yes; then while test "X$1" != 'X--mode=compile'; do shift done shift fi # X makedepend shift cleared=no eat=no for arg do case $cleared in no) set ""; shift cleared=yes ;; esac if test $eat = yes; then eat=no continue fi case "$arg" in -D*|-I*) set fnord "$@" "$arg"; shift ;; # Strip any option that makedepend may not understand. Remove # the object too, otherwise makedepend will parse it as a source file. -arch) eat=yes ;; -*|$object) ;; *) set fnord "$@" "$arg"; shift ;; esac done obj_suffix=`echo "$object" | sed 's/^.*\././'` touch "$tmpdepfile" ${MAKEDEPEND-makedepend} -o"$obj_suffix" -f"$tmpdepfile" "$@" rm -f "$depfile" # makedepend may prepend the VPATH from the source file name to the object. # No need to regex-escape $object, excess matching of '.' is harmless. sed "s|^.*\($object *:\)|\1|" "$tmpdepfile" > "$depfile" # Some versions of the HPUX 10.20 sed can't process the last invocation # correctly. Breaking it into two sed invocations is a workaround. sed '1,2d' "$tmpdepfile" \ | tr ' ' "$nl" \ | sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' \ | sed -e 's/$/ :/' >> "$depfile" rm -f "$tmpdepfile" "$tmpdepfile".bak ;; cpp) # Important note: in order to support this mode, a compiler *must* # always write the preprocessed file to stdout. "$@" || exit $? # Remove the call to Libtool. if test "$libtool" = yes; then while test "X$1" != 'X--mode=compile'; do shift done shift fi # Remove '-o $object'. IFS=" " for arg do case $arg in -o) shift ;; $object) shift ;; *) set fnord "$@" "$arg" shift # fnord shift # $arg ;; esac done "$@" -E \ | sed -n -e '/^# [0-9][0-9]* "\([^"]*\)".*/ s:: \1 \\:p' \ -e '/^#line [0-9][0-9]* "\([^"]*\)".*/ s:: \1 \\:p' \ | sed '$ s: \\$::' > "$tmpdepfile" rm -f "$depfile" echo "$object : \\" > "$depfile" cat < "$tmpdepfile" >> "$depfile" sed < "$tmpdepfile" '/^$/d;s/^ //;s/ \\$//;s/$/ :/' >> "$depfile" rm -f "$tmpdepfile" ;; msvisualcpp) # Important note: in order to support this mode, a compiler *must* # always write the preprocessed file to stdout. "$@" || exit $? # Remove the call to Libtool. if test "$libtool" = yes; then while test "X$1" != 'X--mode=compile'; do shift done shift fi IFS=" " for arg do case "$arg" in -o) shift ;; $object) shift ;; "-Gm"|"/Gm"|"-Gi"|"/Gi"|"-ZI"|"/ZI") set fnord "$@" shift shift ;; *) set fnord "$@" "$arg" shift shift ;; esac done "$@" -E 2>/dev/null | sed -n '/^#line [0-9][0-9]* "\([^"]*\)"/ s::\1:p' | $cygpath_u | sort -u > "$tmpdepfile" rm -f "$depfile" echo "$object : \\" > "$depfile" sed < "$tmpdepfile" -n -e 's% %\\ %g' -e '/^\(.*\)$/ s::'"$tab"'\1 \\:p' >> "$depfile" echo "$tab" >> "$depfile" sed < "$tmpdepfile" -n -e 's% %\\ %g' -e '/^\(.*\)$/ s::\1\::p' >> "$depfile" rm -f "$tmpdepfile" ;; msvcmsys) # This case exists only to let depend.m4 do its work. It works by # looking at the text of this script. This case will never be run, # since it is checked for above. exit 1 ;; none) exec "$@" ;; *) echo "Unknown depmode $depmode" 1>&2 exit 1 ;; esac exit 0 # Local Variables: # mode: shell-script # sh-indentation: 2 # eval: (add-hook 'write-file-hooks 'time-stamp) # time-stamp-start: "scriptversion=" # time-stamp-format: "%:y-%02m-%02d.%02H" # time-stamp-time-zone: "UTC" # time-stamp-end: "; # UTC" # End: telepathy-gabble-0.18.2/config.sub0000755000175000017500000010535412312536073017012 0ustar00smcvsmcv00000000000000#! /bin/sh # Configuration validation subroutine script. # Copyright 1992-2013 Free Software Foundation, Inc. timestamp='2013-08-10' # This file 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. # # This program 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 this program; if not, see . # # As a special exception to the GNU General Public License, if you # distribute this file as part of a program that contains a # configuration script generated by Autoconf, you may include it under # the same distribution terms that you use for the rest of that # program. This Exception is an additional permission under section 7 # of the GNU General Public License, version 3 ("GPLv3"). # Please send patches with a ChangeLog entry to config-patches@gnu.org. # # Configuration subroutine to validate and canonicalize a configuration type. # Supply the specified configuration type as an argument. # If it is invalid, we print an error message on stderr and exit with code 1. # Otherwise, we print the canonical config type on stdout and succeed. # You can get the latest version of this script from: # http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.sub;hb=HEAD # This file is supposed to be the same for all GNU packages # and recognize all the CPU types, system types and aliases # that are meaningful with *any* GNU software. # Each package is responsible for reporting which valid configurations # it does not support. The user should be able to distinguish # a failure to support a valid configuration from a meaningless # configuration. # The goal of this file is to map all the various variations of a given # machine specification into a single specification in the form: # CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM # or in some cases, the newer four-part form: # CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM # It is wrong to echo any other type of specification. me=`echo "$0" | sed -e 's,.*/,,'` usage="\ Usage: $0 [OPTION] CPU-MFR-OPSYS $0 [OPTION] ALIAS Canonicalize a configuration name. Operation modes: -h, --help print this help, then exit -t, --time-stamp print date of last modification, then exit -v, --version print version number, then exit Report bugs and patches to ." version="\ GNU config.sub ($timestamp) Copyright 1992-2013 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." help=" Try \`$me --help' for more information." # Parse command line while test $# -gt 0 ; do case $1 in --time-stamp | --time* | -t ) echo "$timestamp" ; exit ;; --version | -v ) echo "$version" ; exit ;; --help | --h* | -h ) echo "$usage"; exit ;; -- ) # Stop option processing shift; break ;; - ) # Use stdin as input. break ;; -* ) echo "$me: invalid option $1$help" exit 1 ;; *local*) # First pass through any local machine types. echo $1 exit ;; * ) break ;; esac done case $# in 0) echo "$me: missing argument$help" >&2 exit 1;; 1) ;; *) echo "$me: too many arguments$help" >&2 exit 1;; esac # Separate what the user gave into CPU-COMPANY and OS or KERNEL-OS (if any). # Here we must recognize all the valid KERNEL-OS combinations. maybe_os=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\2/'` case $maybe_os in nto-qnx* | linux-gnu* | linux-android* | linux-dietlibc | linux-newlib* | \ linux-musl* | linux-uclibc* | uclinux-uclibc* | uclinux-gnu* | kfreebsd*-gnu* | \ knetbsd*-gnu* | netbsd*-gnu* | \ kopensolaris*-gnu* | \ storm-chaos* | os2-emx* | rtmk-nova*) os=-$maybe_os basic_machine=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'` ;; android-linux) os=-linux-android basic_machine=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'`-unknown ;; *) basic_machine=`echo $1 | sed 's/-[^-]*$//'` if [ $basic_machine != $1 ] then os=`echo $1 | sed 's/.*-/-/'` else os=; fi ;; esac ### Let's recognize common machines as not being operating systems so ### that things like config.sub decstation-3100 work. We also ### recognize some manufacturers as not being operating systems, so we ### can provide default operating systems below. case $os in -sun*os*) # Prevent following clause from handling this invalid input. ;; -dec* | -mips* | -sequent* | -encore* | -pc532* | -sgi* | -sony* | \ -att* | -7300* | -3300* | -delta* | -motorola* | -sun[234]* | \ -unicom* | -ibm* | -next | -hp | -isi* | -apollo | -altos* | \ -convergent* | -ncr* | -news | -32* | -3600* | -3100* | -hitachi* |\ -c[123]* | -convex* | -sun | -crds | -omron* | -dg | -ultra | -tti* | \ -harris | -dolphin | -highlevel | -gould | -cbm | -ns | -masscomp | \ -apple | -axis | -knuth | -cray | -microblaze*) os= basic_machine=$1 ;; -bluegene*) os=-cnk ;; -sim | -cisco | -oki | -wec | -winbond) os= basic_machine=$1 ;; -scout) ;; -wrs) os=-vxworks basic_machine=$1 ;; -chorusos*) os=-chorusos basic_machine=$1 ;; -chorusrdb) os=-chorusrdb basic_machine=$1 ;; -hiux*) os=-hiuxwe2 ;; -sco6) os=-sco5v6 basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` ;; -sco5) os=-sco3.2v5 basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` ;; -sco4) os=-sco3.2v4 basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` ;; -sco3.2.[4-9]*) os=`echo $os | sed -e 's/sco3.2./sco3.2v/'` basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` ;; -sco3.2v[4-9]*) # Don't forget version if it is 3.2v4 or newer. basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` ;; -sco5v6*) # Don't forget version if it is 3.2v4 or newer. basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` ;; -sco*) os=-sco3.2v2 basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` ;; -udk*) basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` ;; -isc) os=-isc2.2 basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` ;; -clix*) basic_machine=clipper-intergraph ;; -isc*) basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` ;; -lynx*178) os=-lynxos178 ;; -lynx*5) os=-lynxos5 ;; -lynx*) os=-lynxos ;; -ptx*) basic_machine=`echo $1 | sed -e 's/86-.*/86-sequent/'` ;; -windowsnt*) os=`echo $os | sed -e 's/windowsnt/winnt/'` ;; -psos*) os=-psos ;; -mint | -mint[0-9]*) basic_machine=m68k-atari os=-mint ;; esac # Decode aliases for certain CPU-COMPANY combinations. case $basic_machine in # Recognize the basic CPU types without company name. # Some are omitted here because they have special meanings below. 1750a | 580 \ | a29k \ | aarch64 | aarch64_be \ | alpha | alphaev[4-8] | alphaev56 | alphaev6[78] | alphapca5[67] \ | alpha64 | alpha64ev[4-8] | alpha64ev56 | alpha64ev6[78] | alpha64pca5[67] \ | am33_2.0 \ | arc | arceb \ | arm | arm[bl]e | arme[lb] | armv[2-8] | armv[3-8][lb] | armv7[arm] \ | avr | avr32 \ | be32 | be64 \ | bfin \ | c4x | c8051 | clipper \ | d10v | d30v | dlx | dsp16xx \ | epiphany \ | fido | fr30 | frv \ | h8300 | h8500 | hppa | hppa1.[01] | hppa2.0 | hppa2.0[nw] | hppa64 \ | hexagon \ | i370 | i860 | i960 | ia64 \ | ip2k | iq2000 \ | le32 | le64 \ | lm32 \ | m32c | m32r | m32rle | m68000 | m68k | m88k \ | maxq | mb | microblaze | microblazeel | mcore | mep | metag \ | mips | mipsbe | mipseb | mipsel | mipsle \ | mips16 \ | mips64 | mips64el \ | mips64octeon | mips64octeonel \ | mips64orion | mips64orionel \ | mips64r5900 | mips64r5900el \ | mips64vr | mips64vrel \ | mips64vr4100 | mips64vr4100el \ | mips64vr4300 | mips64vr4300el \ | mips64vr5000 | mips64vr5000el \ | mips64vr5900 | mips64vr5900el \ | mipsisa32 | mipsisa32el \ | mipsisa32r2 | mipsisa32r2el \ | mipsisa64 | mipsisa64el \ | mipsisa64r2 | mipsisa64r2el \ | mipsisa64sb1 | mipsisa64sb1el \ | mipsisa64sr71k | mipsisa64sr71kel \ | mipsr5900 | mipsr5900el \ | mipstx39 | mipstx39el \ | mn10200 | mn10300 \ | moxie \ | mt \ | msp430 \ | nds32 | nds32le | nds32be \ | nios | nios2 | nios2eb | nios2el \ | ns16k | ns32k \ | open8 \ | or1k | or32 \ | pdp10 | pdp11 | pj | pjl \ | powerpc | powerpc64 | powerpc64le | powerpcle \ | pyramid \ | rl78 | rx \ | score \ | sh | sh[1234] | sh[24]a | sh[24]aeb | sh[23]e | sh[34]eb | sheb | shbe | shle | sh[1234]le | sh3ele \ | sh64 | sh64le \ | sparc | sparc64 | sparc64b | sparc64v | sparc86x | sparclet | sparclite \ | sparcv8 | sparcv9 | sparcv9b | sparcv9v \ | spu \ | tahoe | tic4x | tic54x | tic55x | tic6x | tic80 | tron \ | ubicom32 \ | v850 | v850e | v850e1 | v850e2 | v850es | v850e2v3 \ | we32k \ | x86 | xc16x | xstormy16 | xtensa \ | z8k | z80) basic_machine=$basic_machine-unknown ;; c54x) basic_machine=tic54x-unknown ;; c55x) basic_machine=tic55x-unknown ;; c6x) basic_machine=tic6x-unknown ;; m6811 | m68hc11 | m6812 | m68hc12 | m68hcs12x | picochip) basic_machine=$basic_machine-unknown os=-none ;; m88110 | m680[12346]0 | m683?2 | m68360 | m5200 | v70 | w65 | z8k) ;; ms1) basic_machine=mt-unknown ;; strongarm | thumb | xscale) basic_machine=arm-unknown ;; xgate) basic_machine=$basic_machine-unknown os=-none ;; xscaleeb) basic_machine=armeb-unknown ;; xscaleel) basic_machine=armel-unknown ;; # We use `pc' rather than `unknown' # because (1) that's what they normally are, and # (2) the word "unknown" tends to confuse beginning users. i*86 | x86_64) basic_machine=$basic_machine-pc ;; # Object if more than one company name word. *-*-*) echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2 exit 1 ;; # Recognize the basic CPU types with company name. 580-* \ | a29k-* \ | aarch64-* | aarch64_be-* \ | alpha-* | alphaev[4-8]-* | alphaev56-* | alphaev6[78]-* \ | alpha64-* | alpha64ev[4-8]-* | alpha64ev56-* | alpha64ev6[78]-* \ | alphapca5[67]-* | alpha64pca5[67]-* | arc-* | arceb-* \ | arm-* | armbe-* | armle-* | armeb-* | armv*-* \ | avr-* | avr32-* \ | be32-* | be64-* \ | bfin-* | bs2000-* \ | c[123]* | c30-* | [cjt]90-* | c4x-* \ | c8051-* | clipper-* | craynv-* | cydra-* \ | d10v-* | d30v-* | dlx-* \ | elxsi-* \ | f30[01]-* | f700-* | fido-* | fr30-* | frv-* | fx80-* \ | h8300-* | h8500-* \ | hppa-* | hppa1.[01]-* | hppa2.0-* | hppa2.0[nw]-* | hppa64-* \ | hexagon-* \ | i*86-* | i860-* | i960-* | ia64-* \ | ip2k-* | iq2000-* \ | le32-* | le64-* \ | lm32-* \ | m32c-* | m32r-* | m32rle-* \ | m68000-* | m680[012346]0-* | m68360-* | m683?2-* | m68k-* \ | m88110-* | m88k-* | maxq-* | mcore-* | metag-* \ | microblaze-* | microblazeel-* \ | mips-* | mipsbe-* | mipseb-* | mipsel-* | mipsle-* \ | mips16-* \ | mips64-* | mips64el-* \ | mips64octeon-* | mips64octeonel-* \ | mips64orion-* | mips64orionel-* \ | mips64r5900-* | mips64r5900el-* \ | mips64vr-* | mips64vrel-* \ | mips64vr4100-* | mips64vr4100el-* \ | mips64vr4300-* | mips64vr4300el-* \ | mips64vr5000-* | mips64vr5000el-* \ | mips64vr5900-* | mips64vr5900el-* \ | mipsisa32-* | mipsisa32el-* \ | mipsisa32r2-* | mipsisa32r2el-* \ | mipsisa64-* | mipsisa64el-* \ | mipsisa64r2-* | mipsisa64r2el-* \ | mipsisa64sb1-* | mipsisa64sb1el-* \ | mipsisa64sr71k-* | mipsisa64sr71kel-* \ | mipsr5900-* | mipsr5900el-* \ | mipstx39-* | mipstx39el-* \ | mmix-* \ | mt-* \ | msp430-* \ | nds32-* | nds32le-* | nds32be-* \ | nios-* | nios2-* | nios2eb-* | nios2el-* \ | none-* | np1-* | ns16k-* | ns32k-* \ | open8-* \ | orion-* \ | pdp10-* | pdp11-* | pj-* | pjl-* | pn-* | power-* \ | powerpc-* | powerpc64-* | powerpc64le-* | powerpcle-* \ | pyramid-* \ | rl78-* | romp-* | rs6000-* | rx-* \ | sh-* | sh[1234]-* | sh[24]a-* | sh[24]aeb-* | sh[23]e-* | sh[34]eb-* | sheb-* | shbe-* \ | shle-* | sh[1234]le-* | sh3ele-* | sh64-* | sh64le-* \ | sparc-* | sparc64-* | sparc64b-* | sparc64v-* | sparc86x-* | sparclet-* \ | sparclite-* \ | sparcv8-* | sparcv9-* | sparcv9b-* | sparcv9v-* | sv1-* | sx?-* \ | tahoe-* \ | tic30-* | tic4x-* | tic54x-* | tic55x-* | tic6x-* | tic80-* \ | tile*-* \ | tron-* \ | ubicom32-* \ | v850-* | v850e-* | v850e1-* | v850es-* | v850e2-* | v850e2v3-* \ | vax-* \ | we32k-* \ | x86-* | x86_64-* | xc16x-* | xps100-* \ | xstormy16-* | xtensa*-* \ | ymp-* \ | z8k-* | z80-*) ;; # Recognize the basic CPU types without company name, with glob match. xtensa*) basic_machine=$basic_machine-unknown ;; # Recognize the various machine names and aliases which stand # for a CPU type and a company and sometimes even an OS. 386bsd) basic_machine=i386-unknown os=-bsd ;; 3b1 | 7300 | 7300-att | att-7300 | pc7300 | safari | unixpc) basic_machine=m68000-att ;; 3b*) basic_machine=we32k-att ;; a29khif) basic_machine=a29k-amd os=-udi ;; abacus) basic_machine=abacus-unknown ;; adobe68k) basic_machine=m68010-adobe os=-scout ;; alliant | fx80) basic_machine=fx80-alliant ;; altos | altos3068) basic_machine=m68k-altos ;; am29k) basic_machine=a29k-none os=-bsd ;; amd64) basic_machine=x86_64-pc ;; amd64-*) basic_machine=x86_64-`echo $basic_machine | sed 's/^[^-]*-//'` ;; amdahl) basic_machine=580-amdahl os=-sysv ;; amiga | amiga-*) basic_machine=m68k-unknown ;; amigaos | amigados) basic_machine=m68k-unknown os=-amigaos ;; amigaunix | amix) basic_machine=m68k-unknown os=-sysv4 ;; apollo68) basic_machine=m68k-apollo os=-sysv ;; apollo68bsd) basic_machine=m68k-apollo os=-bsd ;; aros) basic_machine=i386-pc os=-aros ;; aux) basic_machine=m68k-apple os=-aux ;; balance) basic_machine=ns32k-sequent os=-dynix ;; blackfin) basic_machine=bfin-unknown os=-linux ;; blackfin-*) basic_machine=bfin-`echo $basic_machine | sed 's/^[^-]*-//'` os=-linux ;; bluegene*) basic_machine=powerpc-ibm os=-cnk ;; c54x-*) basic_machine=tic54x-`echo $basic_machine | sed 's/^[^-]*-//'` ;; c55x-*) basic_machine=tic55x-`echo $basic_machine | sed 's/^[^-]*-//'` ;; c6x-*) basic_machine=tic6x-`echo $basic_machine | sed 's/^[^-]*-//'` ;; c90) basic_machine=c90-cray os=-unicos ;; cegcc) basic_machine=arm-unknown os=-cegcc ;; convex-c1) basic_machine=c1-convex os=-bsd ;; convex-c2) basic_machine=c2-convex os=-bsd ;; convex-c32) basic_machine=c32-convex os=-bsd ;; convex-c34) basic_machine=c34-convex os=-bsd ;; convex-c38) basic_machine=c38-convex os=-bsd ;; cray | j90) basic_machine=j90-cray os=-unicos ;; craynv) basic_machine=craynv-cray os=-unicosmp ;; cr16 | cr16-*) basic_machine=cr16-unknown os=-elf ;; crds | unos) basic_machine=m68k-crds ;; crisv32 | crisv32-* | etraxfs*) basic_machine=crisv32-axis ;; cris | cris-* | etrax*) basic_machine=cris-axis ;; crx) basic_machine=crx-unknown os=-elf ;; da30 | da30-*) basic_machine=m68k-da30 ;; decstation | decstation-3100 | pmax | pmax-* | pmin | dec3100 | decstatn) basic_machine=mips-dec ;; decsystem10* | dec10*) basic_machine=pdp10-dec os=-tops10 ;; decsystem20* | dec20*) basic_machine=pdp10-dec os=-tops20 ;; delta | 3300 | motorola-3300 | motorola-delta \ | 3300-motorola | delta-motorola) basic_machine=m68k-motorola ;; delta88) basic_machine=m88k-motorola os=-sysv3 ;; dicos) basic_machine=i686-pc os=-dicos ;; djgpp) basic_machine=i586-pc os=-msdosdjgpp ;; dpx20 | dpx20-*) basic_machine=rs6000-bull os=-bosx ;; dpx2* | dpx2*-bull) basic_machine=m68k-bull os=-sysv3 ;; ebmon29k) basic_machine=a29k-amd os=-ebmon ;; elxsi) basic_machine=elxsi-elxsi os=-bsd ;; encore | umax | mmax) basic_machine=ns32k-encore ;; es1800 | OSE68k | ose68k | ose | OSE) basic_machine=m68k-ericsson os=-ose ;; fx2800) basic_machine=i860-alliant ;; genix) basic_machine=ns32k-ns ;; gmicro) basic_machine=tron-gmicro os=-sysv ;; go32) basic_machine=i386-pc os=-go32 ;; h3050r* | hiux*) basic_machine=hppa1.1-hitachi os=-hiuxwe2 ;; h8300hms) basic_machine=h8300-hitachi os=-hms ;; h8300xray) basic_machine=h8300-hitachi os=-xray ;; h8500hms) basic_machine=h8500-hitachi os=-hms ;; harris) basic_machine=m88k-harris os=-sysv3 ;; hp300-*) basic_machine=m68k-hp ;; hp300bsd) basic_machine=m68k-hp os=-bsd ;; hp300hpux) basic_machine=m68k-hp os=-hpux ;; hp3k9[0-9][0-9] | hp9[0-9][0-9]) basic_machine=hppa1.0-hp ;; hp9k2[0-9][0-9] | hp9k31[0-9]) basic_machine=m68000-hp ;; hp9k3[2-9][0-9]) basic_machine=m68k-hp ;; hp9k6[0-9][0-9] | hp6[0-9][0-9]) basic_machine=hppa1.0-hp ;; hp9k7[0-79][0-9] | hp7[0-79][0-9]) basic_machine=hppa1.1-hp ;; hp9k78[0-9] | hp78[0-9]) # FIXME: really hppa2.0-hp basic_machine=hppa1.1-hp ;; hp9k8[67]1 | hp8[67]1 | hp9k80[24] | hp80[24] | hp9k8[78]9 | hp8[78]9 | hp9k893 | hp893) # FIXME: really hppa2.0-hp basic_machine=hppa1.1-hp ;; hp9k8[0-9][13679] | hp8[0-9][13679]) basic_machine=hppa1.1-hp ;; hp9k8[0-9][0-9] | hp8[0-9][0-9]) basic_machine=hppa1.0-hp ;; hppa-next) os=-nextstep3 ;; hppaosf) basic_machine=hppa1.1-hp os=-osf ;; hppro) basic_machine=hppa1.1-hp os=-proelf ;; i370-ibm* | ibm*) basic_machine=i370-ibm ;; i*86v32) basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` os=-sysv32 ;; i*86v4*) basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` os=-sysv4 ;; i*86v) basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` os=-sysv ;; i*86sol2) basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` os=-solaris2 ;; i386mach) basic_machine=i386-mach os=-mach ;; i386-vsta | vsta) basic_machine=i386-unknown os=-vsta ;; iris | iris4d) basic_machine=mips-sgi case $os in -irix*) ;; *) os=-irix4 ;; esac ;; isi68 | isi) basic_machine=m68k-isi os=-sysv ;; m68knommu) basic_machine=m68k-unknown os=-linux ;; m68knommu-*) basic_machine=m68k-`echo $basic_machine | sed 's/^[^-]*-//'` os=-linux ;; m88k-omron*) basic_machine=m88k-omron ;; magnum | m3230) basic_machine=mips-mips os=-sysv ;; merlin) basic_machine=ns32k-utek os=-sysv ;; microblaze*) basic_machine=microblaze-xilinx ;; mingw64) basic_machine=x86_64-pc os=-mingw64 ;; mingw32) basic_machine=i686-pc os=-mingw32 ;; mingw32ce) basic_machine=arm-unknown os=-mingw32ce ;; miniframe) basic_machine=m68000-convergent ;; *mint | -mint[0-9]* | *MiNT | *MiNT[0-9]*) basic_machine=m68k-atari os=-mint ;; mips3*-*) basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'` ;; mips3*) basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'`-unknown ;; monitor) basic_machine=m68k-rom68k os=-coff ;; morphos) basic_machine=powerpc-unknown os=-morphos ;; msdos) basic_machine=i386-pc os=-msdos ;; ms1-*) basic_machine=`echo $basic_machine | sed -e 's/ms1-/mt-/'` ;; msys) basic_machine=i686-pc os=-msys ;; mvs) basic_machine=i370-ibm os=-mvs ;; nacl) basic_machine=le32-unknown os=-nacl ;; ncr3000) basic_machine=i486-ncr os=-sysv4 ;; netbsd386) basic_machine=i386-unknown os=-netbsd ;; netwinder) basic_machine=armv4l-rebel os=-linux ;; news | news700 | news800 | news900) basic_machine=m68k-sony os=-newsos ;; news1000) basic_machine=m68030-sony os=-newsos ;; news-3600 | risc-news) basic_machine=mips-sony os=-newsos ;; necv70) basic_machine=v70-nec os=-sysv ;; next | m*-next ) basic_machine=m68k-next case $os in -nextstep* ) ;; -ns2*) os=-nextstep2 ;; *) os=-nextstep3 ;; esac ;; nh3000) basic_machine=m68k-harris os=-cxux ;; nh[45]000) basic_machine=m88k-harris os=-cxux ;; nindy960) basic_machine=i960-intel os=-nindy ;; mon960) basic_machine=i960-intel os=-mon960 ;; nonstopux) basic_machine=mips-compaq os=-nonstopux ;; np1) basic_machine=np1-gould ;; neo-tandem) basic_machine=neo-tandem ;; nse-tandem) basic_machine=nse-tandem ;; nsr-tandem) basic_machine=nsr-tandem ;; op50n-* | op60c-*) basic_machine=hppa1.1-oki os=-proelf ;; openrisc | openrisc-*) basic_machine=or32-unknown ;; os400) basic_machine=powerpc-ibm os=-os400 ;; OSE68000 | ose68000) basic_machine=m68000-ericsson os=-ose ;; os68k) basic_machine=m68k-none os=-os68k ;; pa-hitachi) basic_machine=hppa1.1-hitachi os=-hiuxwe2 ;; paragon) basic_machine=i860-intel os=-osf ;; parisc) basic_machine=hppa-unknown os=-linux ;; parisc-*) basic_machine=hppa-`echo $basic_machine | sed 's/^[^-]*-//'` os=-linux ;; pbd) basic_machine=sparc-tti ;; pbb) basic_machine=m68k-tti ;; pc532 | pc532-*) basic_machine=ns32k-pc532 ;; pc98) basic_machine=i386-pc ;; pc98-*) basic_machine=i386-`echo $basic_machine | sed 's/^[^-]*-//'` ;; pentium | p5 | k5 | k6 | nexgen | viac3) basic_machine=i586-pc ;; pentiumpro | p6 | 6x86 | athlon | athlon_*) basic_machine=i686-pc ;; pentiumii | pentium2 | pentiumiii | pentium3) basic_machine=i686-pc ;; pentium4) basic_machine=i786-pc ;; pentium-* | p5-* | k5-* | k6-* | nexgen-* | viac3-*) basic_machine=i586-`echo $basic_machine | sed 's/^[^-]*-//'` ;; pentiumpro-* | p6-* | 6x86-* | athlon-*) basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'` ;; pentiumii-* | pentium2-* | pentiumiii-* | pentium3-*) basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'` ;; pentium4-*) basic_machine=i786-`echo $basic_machine | sed 's/^[^-]*-//'` ;; pn) basic_machine=pn-gould ;; power) basic_machine=power-ibm ;; ppc | ppcbe) basic_machine=powerpc-unknown ;; ppc-* | ppcbe-*) basic_machine=powerpc-`echo $basic_machine | sed 's/^[^-]*-//'` ;; ppcle | powerpclittle | ppc-le | powerpc-little) basic_machine=powerpcle-unknown ;; ppcle-* | powerpclittle-*) basic_machine=powerpcle-`echo $basic_machine | sed 's/^[^-]*-//'` ;; ppc64) basic_machine=powerpc64-unknown ;; ppc64-*) basic_machine=powerpc64-`echo $basic_machine | sed 's/^[^-]*-//'` ;; ppc64le | powerpc64little | ppc64-le | powerpc64-little) basic_machine=powerpc64le-unknown ;; ppc64le-* | powerpc64little-*) basic_machine=powerpc64le-`echo $basic_machine | sed 's/^[^-]*-//'` ;; ps2) basic_machine=i386-ibm ;; pw32) basic_machine=i586-unknown os=-pw32 ;; rdos | rdos64) basic_machine=x86_64-pc os=-rdos ;; rdos32) basic_machine=i386-pc os=-rdos ;; rom68k) basic_machine=m68k-rom68k os=-coff ;; rm[46]00) basic_machine=mips-siemens ;; rtpc | rtpc-*) basic_machine=romp-ibm ;; s390 | s390-*) basic_machine=s390-ibm ;; s390x | s390x-*) basic_machine=s390x-ibm ;; sa29200) basic_machine=a29k-amd os=-udi ;; sb1) basic_machine=mipsisa64sb1-unknown ;; sb1el) basic_machine=mipsisa64sb1el-unknown ;; sde) basic_machine=mipsisa32-sde os=-elf ;; sei) basic_machine=mips-sei os=-seiux ;; sequent) basic_machine=i386-sequent ;; sh) basic_machine=sh-hitachi os=-hms ;; sh5el) basic_machine=sh5le-unknown ;; sh64) basic_machine=sh64-unknown ;; sparclite-wrs | simso-wrs) basic_machine=sparclite-wrs os=-vxworks ;; sps7) basic_machine=m68k-bull os=-sysv2 ;; spur) basic_machine=spur-unknown ;; st2000) basic_machine=m68k-tandem ;; stratus) basic_machine=i860-stratus os=-sysv4 ;; strongarm-* | thumb-*) basic_machine=arm-`echo $basic_machine | sed 's/^[^-]*-//'` ;; sun2) basic_machine=m68000-sun ;; sun2os3) basic_machine=m68000-sun os=-sunos3 ;; sun2os4) basic_machine=m68000-sun os=-sunos4 ;; sun3os3) basic_machine=m68k-sun os=-sunos3 ;; sun3os4) basic_machine=m68k-sun os=-sunos4 ;; sun4os3) basic_machine=sparc-sun os=-sunos3 ;; sun4os4) basic_machine=sparc-sun os=-sunos4 ;; sun4sol2) basic_machine=sparc-sun os=-solaris2 ;; sun3 | sun3-*) basic_machine=m68k-sun ;; sun4) basic_machine=sparc-sun ;; sun386 | sun386i | roadrunner) basic_machine=i386-sun ;; sv1) basic_machine=sv1-cray os=-unicos ;; symmetry) basic_machine=i386-sequent os=-dynix ;; t3e) basic_machine=alphaev5-cray os=-unicos ;; t90) basic_machine=t90-cray os=-unicos ;; tile*) basic_machine=$basic_machine-unknown os=-linux-gnu ;; tx39) basic_machine=mipstx39-unknown ;; tx39el) basic_machine=mipstx39el-unknown ;; toad1) basic_machine=pdp10-xkl os=-tops20 ;; tower | tower-32) basic_machine=m68k-ncr ;; tpf) basic_machine=s390x-ibm os=-tpf ;; udi29k) basic_machine=a29k-amd os=-udi ;; ultra3) basic_machine=a29k-nyu os=-sym1 ;; v810 | necv810) basic_machine=v810-nec os=-none ;; vaxv) basic_machine=vax-dec os=-sysv ;; vms) basic_machine=vax-dec os=-vms ;; vpp*|vx|vx-*) basic_machine=f301-fujitsu ;; vxworks960) basic_machine=i960-wrs os=-vxworks ;; vxworks68) basic_machine=m68k-wrs os=-vxworks ;; vxworks29k) basic_machine=a29k-wrs os=-vxworks ;; w65*) basic_machine=w65-wdc os=-none ;; w89k-*) basic_machine=hppa1.1-winbond os=-proelf ;; xbox) basic_machine=i686-pc os=-mingw32 ;; xps | xps100) basic_machine=xps100-honeywell ;; xscale-* | xscalee[bl]-*) basic_machine=`echo $basic_machine | sed 's/^xscale/arm/'` ;; ymp) basic_machine=ymp-cray os=-unicos ;; z8k-*-coff) basic_machine=z8k-unknown os=-sim ;; z80-*-coff) basic_machine=z80-unknown os=-sim ;; none) basic_machine=none-none os=-none ;; # Here we handle the default manufacturer of certain CPU types. It is in # some cases the only manufacturer, in others, it is the most popular. w89k) basic_machine=hppa1.1-winbond ;; op50n) basic_machine=hppa1.1-oki ;; op60c) basic_machine=hppa1.1-oki ;; romp) basic_machine=romp-ibm ;; mmix) basic_machine=mmix-knuth ;; rs6000) basic_machine=rs6000-ibm ;; vax) basic_machine=vax-dec ;; pdp10) # there are many clones, so DEC is not a safe bet basic_machine=pdp10-unknown ;; pdp11) basic_machine=pdp11-dec ;; we32k) basic_machine=we32k-att ;; sh[1234] | sh[24]a | sh[24]aeb | sh[34]eb | sh[1234]le | sh[23]ele) basic_machine=sh-unknown ;; sparc | sparcv8 | sparcv9 | sparcv9b | sparcv9v) basic_machine=sparc-sun ;; cydra) basic_machine=cydra-cydrome ;; orion) basic_machine=orion-highlevel ;; orion105) basic_machine=clipper-highlevel ;; mac | mpw | mac-mpw) basic_machine=m68k-apple ;; pmac | pmac-mpw) basic_machine=powerpc-apple ;; *-unknown) # Make sure to match an already-canonicalized machine name. ;; *) echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2 exit 1 ;; esac # Here we canonicalize certain aliases for manufacturers. case $basic_machine in *-digital*) basic_machine=`echo $basic_machine | sed 's/digital.*/dec/'` ;; *-commodore*) basic_machine=`echo $basic_machine | sed 's/commodore.*/cbm/'` ;; *) ;; esac # Decode manufacturer-specific aliases for certain operating systems. if [ x"$os" != x"" ] then case $os in # First match some system type aliases # that might get confused with valid system types. # -solaris* is a basic system type, with this one exception. -auroraux) os=-auroraux ;; -solaris1 | -solaris1.*) os=`echo $os | sed -e 's|solaris1|sunos4|'` ;; -solaris) os=-solaris2 ;; -svr4*) os=-sysv4 ;; -unixware*) os=-sysv4.2uw ;; -gnu/linux*) os=`echo $os | sed -e 's|gnu/linux|linux-gnu|'` ;; # First accept the basic system types. # The portable systems comes first. # Each alternative MUST END IN A *, to match a version number. # -sysv* is not here because it comes later, after sysvr4. -gnu* | -bsd* | -mach* | -minix* | -genix* | -ultrix* | -irix* \ | -*vms* | -sco* | -esix* | -isc* | -aix* | -cnk* | -sunos | -sunos[34]*\ | -hpux* | -unos* | -osf* | -luna* | -dgux* | -auroraux* | -solaris* \ | -sym* | -kopensolaris* | -plan9* \ | -amigaos* | -amigados* | -msdos* | -newsos* | -unicos* | -aof* \ | -aos* | -aros* \ | -nindy* | -vxsim* | -vxworks* | -ebmon* | -hms* | -mvs* \ | -clix* | -riscos* | -uniplus* | -iris* | -rtu* | -xenix* \ | -hiux* | -386bsd* | -knetbsd* | -mirbsd* | -netbsd* \ | -bitrig* | -openbsd* | -solidbsd* \ | -ekkobsd* | -kfreebsd* | -freebsd* | -riscix* | -lynxos* \ | -bosx* | -nextstep* | -cxux* | -aout* | -elf* | -oabi* \ | -ptx* | -coff* | -ecoff* | -winnt* | -domain* | -vsta* \ | -udi* | -eabi* | -lites* | -ieee* | -go32* | -aux* \ | -chorusos* | -chorusrdb* | -cegcc* \ | -cygwin* | -msys* | -pe* | -psos* | -moss* | -proelf* | -rtems* \ | -mingw32* | -mingw64* | -linux-gnu* | -linux-android* \ | -linux-newlib* | -linux-musl* | -linux-uclibc* \ | -uxpv* | -beos* | -mpeix* | -udk* \ | -interix* | -uwin* | -mks* | -rhapsody* | -darwin* | -opened* \ | -openstep* | -oskit* | -conix* | -pw32* | -nonstopux* \ | -storm-chaos* | -tops10* | -tenex* | -tops20* | -its* \ | -os2* | -vos* | -palmos* | -uclinux* | -nucleus* \ | -morphos* | -superux* | -rtmk* | -rtmk-nova* | -windiss* \ | -powermax* | -dnix* | -nx6 | -nx7 | -sei* | -dragonfly* \ | -skyos* | -haiku* | -rdos* | -toppers* | -drops* | -es*) # Remember, each alternative MUST END IN *, to match a version number. ;; -qnx*) case $basic_machine in x86-* | i*86-*) ;; *) os=-nto$os ;; esac ;; -nto-qnx*) ;; -nto*) os=`echo $os | sed -e 's|nto|nto-qnx|'` ;; -sim | -es1800* | -hms* | -xray | -os68k* | -none* | -v88r* \ | -windows* | -osx | -abug | -netware* | -os9* | -beos* | -haiku* \ | -macos* | -mpw* | -magic* | -mmixware* | -mon960* | -lnews*) ;; -mac*) os=`echo $os | sed -e 's|mac|macos|'` ;; -linux-dietlibc) os=-linux-dietlibc ;; -linux*) os=`echo $os | sed -e 's|linux|linux-gnu|'` ;; -sunos5*) os=`echo $os | sed -e 's|sunos5|solaris2|'` ;; -sunos6*) os=`echo $os | sed -e 's|sunos6|solaris3|'` ;; -opened*) os=-openedition ;; -os400*) os=-os400 ;; -wince*) os=-wince ;; -osfrose*) os=-osfrose ;; -osf*) os=-osf ;; -utek*) os=-bsd ;; -dynix*) os=-bsd ;; -acis*) os=-aos ;; -atheos*) os=-atheos ;; -syllable*) os=-syllable ;; -386bsd) os=-bsd ;; -ctix* | -uts*) os=-sysv ;; -nova*) os=-rtmk-nova ;; -ns2 ) os=-nextstep2 ;; -nsk*) os=-nsk ;; # Preserve the version number of sinix5. -sinix5.*) os=`echo $os | sed -e 's|sinix|sysv|'` ;; -sinix*) os=-sysv4 ;; -tpf*) os=-tpf ;; -triton*) os=-sysv3 ;; -oss*) os=-sysv3 ;; -svr4) os=-sysv4 ;; -svr3) os=-sysv3 ;; -sysvr4) os=-sysv4 ;; # This must come after -sysvr4. -sysv*) ;; -ose*) os=-ose ;; -es1800*) os=-ose ;; -xenix) os=-xenix ;; -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*) os=-mint ;; -aros*) os=-aros ;; -zvmoe) os=-zvmoe ;; -dicos*) os=-dicos ;; -nacl*) ;; -none) ;; *) # Get rid of the `-' at the beginning of $os. os=`echo $os | sed 's/[^-]*-//'` echo Invalid configuration \`$1\': system \`$os\' not recognized 1>&2 exit 1 ;; esac else # Here we handle the default operating systems that come with various machines. # The value should be what the vendor currently ships out the door with their # machine or put another way, the most popular os provided with the machine. # Note that if you're going to try to match "-MANUFACTURER" here (say, # "-sun"), then you have to tell the case statement up towards the top # that MANUFACTURER isn't an operating system. Otherwise, code above # will signal an error saying that MANUFACTURER isn't an operating # system, and we'll never get to this point. case $basic_machine in score-*) os=-elf ;; spu-*) os=-elf ;; *-acorn) os=-riscix1.2 ;; arm*-rebel) os=-linux ;; arm*-semi) os=-aout ;; c4x-* | tic4x-*) os=-coff ;; c8051-*) os=-elf ;; hexagon-*) os=-elf ;; tic54x-*) os=-coff ;; tic55x-*) os=-coff ;; tic6x-*) os=-coff ;; # This must come before the *-dec entry. pdp10-*) os=-tops20 ;; pdp11-*) os=-none ;; *-dec | vax-*) os=-ultrix4.2 ;; m68*-apollo) os=-domain ;; i386-sun) os=-sunos4.0.2 ;; m68000-sun) os=-sunos3 ;; m68*-cisco) os=-aout ;; mep-*) os=-elf ;; mips*-cisco) os=-elf ;; mips*-*) os=-elf ;; or1k-*) os=-elf ;; or32-*) os=-coff ;; *-tti) # must be before sparc entry or we get the wrong os. os=-sysv3 ;; sparc-* | *-sun) os=-sunos4.1.1 ;; *-be) os=-beos ;; *-haiku) os=-haiku ;; *-ibm) os=-aix ;; *-knuth) os=-mmixware ;; *-wec) os=-proelf ;; *-winbond) os=-proelf ;; *-oki) os=-proelf ;; *-hp) os=-hpux ;; *-hitachi) os=-hiux ;; i860-* | *-att | *-ncr | *-altos | *-motorola | *-convergent) os=-sysv ;; *-cbm) os=-amigaos ;; *-dg) os=-dgux ;; *-dolphin) os=-sysv3 ;; m68k-ccur) os=-rtu ;; m88k-omron*) os=-luna ;; *-next ) os=-nextstep ;; *-sequent) os=-ptx ;; *-crds) os=-unos ;; *-ns) os=-genix ;; i370-*) os=-mvs ;; *-next) os=-nextstep3 ;; *-gould) os=-sysv ;; *-highlevel) os=-bsd ;; *-encore) os=-bsd ;; *-sgi) os=-irix ;; *-siemens) os=-sysv4 ;; *-masscomp) os=-rtu ;; f30[01]-fujitsu | f700-fujitsu) os=-uxpv ;; *-rom68k) os=-coff ;; *-*bug) os=-coff ;; *-apple) os=-macos ;; *-atari*) os=-mint ;; *) os=-none ;; esac fi # Here we handle the case where we know the os, and the CPU type, but not the # manufacturer. We pick the logical manufacturer. vendor=unknown case $basic_machine in *-unknown) case $os in -riscix*) vendor=acorn ;; -sunos*) vendor=sun ;; -cnk*|-aix*) vendor=ibm ;; -beos*) vendor=be ;; -hpux*) vendor=hp ;; -mpeix*) vendor=hp ;; -hiux*) vendor=hitachi ;; -unos*) vendor=crds ;; -dgux*) vendor=dg ;; -luna*) vendor=omron ;; -genix*) vendor=ns ;; -mvs* | -opened*) vendor=ibm ;; -os400*) vendor=ibm ;; -ptx*) vendor=sequent ;; -tpf*) vendor=ibm ;; -vxsim* | -vxworks* | -windiss*) vendor=wrs ;; -aux*) vendor=apple ;; -hms*) vendor=hitachi ;; -mpw* | -macos*) vendor=apple ;; -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*) vendor=atari ;; -vos*) vendor=stratus ;; esac basic_machine=`echo $basic_machine | sed "s/unknown/$vendor/"` ;; esac echo $basic_machine$os exit # Local variables: # eval: (add-hook 'write-file-hooks 'time-stamp) # time-stamp-start: "timestamp='" # time-stamp-format: "%:y-%02m-%02d" # time-stamp-end: "'" # End: telepathy-gabble-0.18.2/config.guess0000755000175000017500000013036112312536073017343 0ustar00smcvsmcv00000000000000#! /bin/sh # Attempt to guess a canonical system name. # Copyright 1992-2013 Free Software Foundation, Inc. timestamp='2013-06-10' # This file 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. # # This program 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 this program; if not, see . # # As a special exception to the GNU General Public License, if you # distribute this file as part of a program that contains a # configuration script generated by Autoconf, you may include it under # the same distribution terms that you use for the rest of that # program. This Exception is an additional permission under section 7 # of the GNU General Public License, version 3 ("GPLv3"). # # Originally written by Per Bothner. # # You can get the latest version of this script from: # http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.guess;hb=HEAD # # Please send patches with a ChangeLog entry to config-patches@gnu.org. me=`echo "$0" | sed -e 's,.*/,,'` usage="\ Usage: $0 [OPTION] Output the configuration name of the system \`$me' is run on. Operation modes: -h, --help print this help, then exit -t, --time-stamp print date of last modification, then exit -v, --version print version number, then exit Report bugs and patches to ." version="\ GNU config.guess ($timestamp) Originally written by Per Bothner. Copyright 1992-2013 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." help=" Try \`$me --help' for more information." # Parse command line while test $# -gt 0 ; do case $1 in --time-stamp | --time* | -t ) echo "$timestamp" ; exit ;; --version | -v ) echo "$version" ; exit ;; --help | --h* | -h ) echo "$usage"; exit ;; -- ) # Stop option processing shift; break ;; - ) # Use stdin as input. break ;; -* ) echo "$me: invalid option $1$help" >&2 exit 1 ;; * ) break ;; esac done if test $# != 0; then echo "$me: too many arguments$help" >&2 exit 1 fi trap 'exit 1' 1 2 15 # CC_FOR_BUILD -- compiler used by this script. Note that the use of a # compiler to aid in system detection is discouraged as it requires # temporary files to be created and, as you can see below, it is a # headache to deal with in a portable fashion. # Historically, `CC_FOR_BUILD' used to be named `HOST_CC'. We still # use `HOST_CC' if defined, but it is deprecated. # Portable tmp directory creation inspired by the Autoconf team. set_cc_for_build=' trap "exitcode=\$?; (rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null) && exit \$exitcode" 0 ; trap "rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null; exit 1" 1 2 13 15 ; : ${TMPDIR=/tmp} ; { tmp=`(umask 077 && mktemp -d "$TMPDIR/cgXXXXXX") 2>/dev/null` && test -n "$tmp" && test -d "$tmp" ; } || { test -n "$RANDOM" && tmp=$TMPDIR/cg$$-$RANDOM && (umask 077 && mkdir $tmp) ; } || { tmp=$TMPDIR/cg-$$ && (umask 077 && mkdir $tmp) && echo "Warning: creating insecure temp directory" >&2 ; } || { echo "$me: cannot create a temporary directory in $TMPDIR" >&2 ; exit 1 ; } ; dummy=$tmp/dummy ; tmpfiles="$dummy.c $dummy.o $dummy.rel $dummy" ; case $CC_FOR_BUILD,$HOST_CC,$CC in ,,) echo "int x;" > $dummy.c ; for c in cc gcc c89 c99 ; do if ($c -c -o $dummy.o $dummy.c) >/dev/null 2>&1 ; then CC_FOR_BUILD="$c"; break ; fi ; done ; if test x"$CC_FOR_BUILD" = x ; then CC_FOR_BUILD=no_compiler_found ; fi ;; ,,*) CC_FOR_BUILD=$CC ;; ,*,*) CC_FOR_BUILD=$HOST_CC ;; esac ; set_cc_for_build= ;' # This is needed to find uname on a Pyramid OSx when run in the BSD universe. # (ghazi@noc.rutgers.edu 1994-08-24) if (test -f /.attbin/uname) >/dev/null 2>&1 ; then PATH=$PATH:/.attbin ; export PATH fi UNAME_MACHINE=`(uname -m) 2>/dev/null` || UNAME_MACHINE=unknown UNAME_RELEASE=`(uname -r) 2>/dev/null` || UNAME_RELEASE=unknown UNAME_SYSTEM=`(uname -s) 2>/dev/null` || UNAME_SYSTEM=unknown UNAME_VERSION=`(uname -v) 2>/dev/null` || UNAME_VERSION=unknown case "${UNAME_SYSTEM}" in Linux|GNU|GNU/*) # If the system lacks a compiler, then just pick glibc. # We could probably try harder. LIBC=gnu eval $set_cc_for_build cat <<-EOF > $dummy.c #include #if defined(__UCLIBC__) LIBC=uclibc #elif defined(__dietlibc__) LIBC=dietlibc #else LIBC=gnu #endif EOF eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep '^LIBC'` ;; esac # Note: order is significant - the case branches are not exclusive. case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in *:NetBSD:*:*) # NetBSD (nbsd) targets should (where applicable) match one or # more of the tuples: *-*-netbsdelf*, *-*-netbsdaout*, # *-*-netbsdecoff* and *-*-netbsd*. For targets that recently # switched to ELF, *-*-netbsd* would select the old # object file format. This provides both forward # compatibility and a consistent mechanism for selecting the # object file format. # # Note: NetBSD doesn't particularly care about the vendor # portion of the name. We always set it to "unknown". sysctl="sysctl -n hw.machine_arch" UNAME_MACHINE_ARCH=`(/sbin/$sysctl 2>/dev/null || \ /usr/sbin/$sysctl 2>/dev/null || echo unknown)` case "${UNAME_MACHINE_ARCH}" in armeb) machine=armeb-unknown ;; arm*) machine=arm-unknown ;; sh3el) machine=shl-unknown ;; sh3eb) machine=sh-unknown ;; sh5el) machine=sh5le-unknown ;; *) machine=${UNAME_MACHINE_ARCH}-unknown ;; esac # The Operating System including object format, if it has switched # to ELF recently, or will in the future. case "${UNAME_MACHINE_ARCH}" in arm*|i386|m68k|ns32k|sh3*|sparc|vax) eval $set_cc_for_build if echo __ELF__ | $CC_FOR_BUILD -E - 2>/dev/null \ | grep -q __ELF__ then # Once all utilities can be ECOFF (netbsdecoff) or a.out (netbsdaout). # Return netbsd for either. FIX? os=netbsd else os=netbsdelf fi ;; *) os=netbsd ;; esac # The OS release # Debian GNU/NetBSD machines have a different userland, and # thus, need a distinct triplet. However, they do not need # kernel version information, so it can be replaced with a # suitable tag, in the style of linux-gnu. case "${UNAME_VERSION}" in Debian*) release='-gnu' ;; *) release=`echo ${UNAME_RELEASE}|sed -e 's/[-_].*/\./'` ;; esac # Since CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM: # contains redundant information, the shorter form: # CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM is used. echo "${machine}-${os}${release}" exit ;; *:Bitrig:*:*) UNAME_MACHINE_ARCH=`arch | sed 's/Bitrig.//'` echo ${UNAME_MACHINE_ARCH}-unknown-bitrig${UNAME_RELEASE} exit ;; *:OpenBSD:*:*) UNAME_MACHINE_ARCH=`arch | sed 's/OpenBSD.//'` echo ${UNAME_MACHINE_ARCH}-unknown-openbsd${UNAME_RELEASE} exit ;; *:ekkoBSD:*:*) echo ${UNAME_MACHINE}-unknown-ekkobsd${UNAME_RELEASE} exit ;; *:SolidBSD:*:*) echo ${UNAME_MACHINE}-unknown-solidbsd${UNAME_RELEASE} exit ;; macppc:MirBSD:*:*) echo powerpc-unknown-mirbsd${UNAME_RELEASE} exit ;; *:MirBSD:*:*) echo ${UNAME_MACHINE}-unknown-mirbsd${UNAME_RELEASE} exit ;; alpha:OSF1:*:*) case $UNAME_RELEASE in *4.0) UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $3}'` ;; *5.*) UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $4}'` ;; esac # According to Compaq, /usr/sbin/psrinfo has been available on # OSF/1 and Tru64 systems produced since 1995. I hope that # covers most systems running today. This code pipes the CPU # types through head -n 1, so we only detect the type of CPU 0. ALPHA_CPU_TYPE=`/usr/sbin/psrinfo -v | sed -n -e 's/^ The alpha \(.*\) processor.*$/\1/p' | head -n 1` case "$ALPHA_CPU_TYPE" in "EV4 (21064)") UNAME_MACHINE="alpha" ;; "EV4.5 (21064)") UNAME_MACHINE="alpha" ;; "LCA4 (21066/21068)") UNAME_MACHINE="alpha" ;; "EV5 (21164)") UNAME_MACHINE="alphaev5" ;; "EV5.6 (21164A)") UNAME_MACHINE="alphaev56" ;; "EV5.6 (21164PC)") UNAME_MACHINE="alphapca56" ;; "EV5.7 (21164PC)") UNAME_MACHINE="alphapca57" ;; "EV6 (21264)") UNAME_MACHINE="alphaev6" ;; "EV6.7 (21264A)") UNAME_MACHINE="alphaev67" ;; "EV6.8CB (21264C)") UNAME_MACHINE="alphaev68" ;; "EV6.8AL (21264B)") UNAME_MACHINE="alphaev68" ;; "EV6.8CX (21264D)") UNAME_MACHINE="alphaev68" ;; "EV6.9A (21264/EV69A)") UNAME_MACHINE="alphaev69" ;; "EV7 (21364)") UNAME_MACHINE="alphaev7" ;; "EV7.9 (21364A)") UNAME_MACHINE="alphaev79" ;; esac # A Pn.n version is a patched version. # A Vn.n version is a released version. # A Tn.n version is a released field test version. # A Xn.n version is an unreleased experimental baselevel. # 1.2 uses "1.2" for uname -r. echo ${UNAME_MACHINE}-dec-osf`echo ${UNAME_RELEASE} | sed -e 's/^[PVTX]//' | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'` # Reset EXIT trap before exiting to avoid spurious non-zero exit code. exitcode=$? trap '' 0 exit $exitcode ;; Alpha\ *:Windows_NT*:*) # How do we know it's Interix rather than the generic POSIX subsystem? # Should we change UNAME_MACHINE based on the output of uname instead # of the specific Alpha model? echo alpha-pc-interix exit ;; 21064:Windows_NT:50:3) echo alpha-dec-winnt3.5 exit ;; Amiga*:UNIX_System_V:4.0:*) echo m68k-unknown-sysv4 exit ;; *:[Aa]miga[Oo][Ss]:*:*) echo ${UNAME_MACHINE}-unknown-amigaos exit ;; *:[Mm]orph[Oo][Ss]:*:*) echo ${UNAME_MACHINE}-unknown-morphos exit ;; *:OS/390:*:*) echo i370-ibm-openedition exit ;; *:z/VM:*:*) echo s390-ibm-zvmoe exit ;; *:OS400:*:*) echo powerpc-ibm-os400 exit ;; arm:RISC*:1.[012]*:*|arm:riscix:1.[012]*:*) echo arm-acorn-riscix${UNAME_RELEASE} exit ;; arm*:riscos:*:*|arm*:RISCOS:*:*) echo arm-unknown-riscos exit ;; SR2?01:HI-UX/MPP:*:* | SR8000:HI-UX/MPP:*:*) echo hppa1.1-hitachi-hiuxmpp exit ;; Pyramid*:OSx*:*:* | MIS*:OSx*:*:* | MIS*:SMP_DC-OSx*:*:*) # akee@wpdis03.wpafb.af.mil (Earle F. Ake) contributed MIS and NILE. if test "`(/bin/universe) 2>/dev/null`" = att ; then echo pyramid-pyramid-sysv3 else echo pyramid-pyramid-bsd fi exit ;; NILE*:*:*:dcosx) echo pyramid-pyramid-svr4 exit ;; DRS?6000:unix:4.0:6*) echo sparc-icl-nx6 exit ;; DRS?6000:UNIX_SV:4.2*:7* | DRS?6000:isis:4.2*:7*) case `/usr/bin/uname -p` in sparc) echo sparc-icl-nx7; exit ;; esac ;; s390x:SunOS:*:*) echo ${UNAME_MACHINE}-ibm-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` exit ;; sun4H:SunOS:5.*:*) echo sparc-hal-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` exit ;; sun4*:SunOS:5.*:* | tadpole*:SunOS:5.*:*) echo sparc-sun-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` exit ;; i86pc:AuroraUX:5.*:* | i86xen:AuroraUX:5.*:*) echo i386-pc-auroraux${UNAME_RELEASE} exit ;; i86pc:SunOS:5.*:* | i86xen:SunOS:5.*:*) eval $set_cc_for_build SUN_ARCH="i386" # If there is a compiler, see if it is configured for 64-bit objects. # Note that the Sun cc does not turn __LP64__ into 1 like gcc does. # This test works for both compilers. if [ "$CC_FOR_BUILD" != 'no_compiler_found' ]; then if (echo '#ifdef __amd64'; echo IS_64BIT_ARCH; echo '#endif') | \ (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | \ grep IS_64BIT_ARCH >/dev/null then SUN_ARCH="x86_64" fi fi echo ${SUN_ARCH}-pc-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` exit ;; sun4*:SunOS:6*:*) # According to config.sub, this is the proper way to canonicalize # SunOS6. Hard to guess exactly what SunOS6 will be like, but # it's likely to be more like Solaris than SunOS4. echo sparc-sun-solaris3`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` exit ;; sun4*:SunOS:*:*) case "`/usr/bin/arch -k`" in Series*|S4*) UNAME_RELEASE=`uname -v` ;; esac # Japanese Language versions have a version number like `4.1.3-JL'. echo sparc-sun-sunos`echo ${UNAME_RELEASE}|sed -e 's/-/_/'` exit ;; sun3*:SunOS:*:*) echo m68k-sun-sunos${UNAME_RELEASE} exit ;; sun*:*:4.2BSD:*) UNAME_RELEASE=`(sed 1q /etc/motd | awk '{print substr($5,1,3)}') 2>/dev/null` test "x${UNAME_RELEASE}" = "x" && UNAME_RELEASE=3 case "`/bin/arch`" in sun3) echo m68k-sun-sunos${UNAME_RELEASE} ;; sun4) echo sparc-sun-sunos${UNAME_RELEASE} ;; esac exit ;; aushp:SunOS:*:*) echo sparc-auspex-sunos${UNAME_RELEASE} exit ;; # The situation for MiNT is a little confusing. The machine name # can be virtually everything (everything which is not # "atarist" or "atariste" at least should have a processor # > m68000). The system name ranges from "MiNT" over "FreeMiNT" # to the lowercase version "mint" (or "freemint"). Finally # the system name "TOS" denotes a system which is actually not # MiNT. But MiNT is downward compatible to TOS, so this should # be no problem. atarist[e]:*MiNT:*:* | atarist[e]:*mint:*:* | atarist[e]:*TOS:*:*) echo m68k-atari-mint${UNAME_RELEASE} exit ;; atari*:*MiNT:*:* | atari*:*mint:*:* | atarist[e]:*TOS:*:*) echo m68k-atari-mint${UNAME_RELEASE} exit ;; *falcon*:*MiNT:*:* | *falcon*:*mint:*:* | *falcon*:*TOS:*:*) echo m68k-atari-mint${UNAME_RELEASE} exit ;; milan*:*MiNT:*:* | milan*:*mint:*:* | *milan*:*TOS:*:*) echo m68k-milan-mint${UNAME_RELEASE} exit ;; hades*:*MiNT:*:* | hades*:*mint:*:* | *hades*:*TOS:*:*) echo m68k-hades-mint${UNAME_RELEASE} exit ;; *:*MiNT:*:* | *:*mint:*:* | *:*TOS:*:*) echo m68k-unknown-mint${UNAME_RELEASE} exit ;; m68k:machten:*:*) echo m68k-apple-machten${UNAME_RELEASE} exit ;; powerpc:machten:*:*) echo powerpc-apple-machten${UNAME_RELEASE} exit ;; RISC*:Mach:*:*) echo mips-dec-mach_bsd4.3 exit ;; RISC*:ULTRIX:*:*) echo mips-dec-ultrix${UNAME_RELEASE} exit ;; VAX*:ULTRIX*:*:*) echo vax-dec-ultrix${UNAME_RELEASE} exit ;; 2020:CLIX:*:* | 2430:CLIX:*:*) echo clipper-intergraph-clix${UNAME_RELEASE} exit ;; mips:*:*:UMIPS | mips:*:*:RISCos) eval $set_cc_for_build sed 's/^ //' << EOF >$dummy.c #ifdef __cplusplus #include /* for printf() prototype */ int main (int argc, char *argv[]) { #else int main (argc, argv) int argc; char *argv[]; { #endif #if defined (host_mips) && defined (MIPSEB) #if defined (SYSTYPE_SYSV) printf ("mips-mips-riscos%ssysv\n", argv[1]); exit (0); #endif #if defined (SYSTYPE_SVR4) printf ("mips-mips-riscos%ssvr4\n", argv[1]); exit (0); #endif #if defined (SYSTYPE_BSD43) || defined(SYSTYPE_BSD) printf ("mips-mips-riscos%sbsd\n", argv[1]); exit (0); #endif #endif exit (-1); } EOF $CC_FOR_BUILD -o $dummy $dummy.c && dummyarg=`echo "${UNAME_RELEASE}" | sed -n 's/\([0-9]*\).*/\1/p'` && SYSTEM_NAME=`$dummy $dummyarg` && { echo "$SYSTEM_NAME"; exit; } echo mips-mips-riscos${UNAME_RELEASE} exit ;; Motorola:PowerMAX_OS:*:*) echo powerpc-motorola-powermax exit ;; Motorola:*:4.3:PL8-*) echo powerpc-harris-powermax exit ;; Night_Hawk:*:*:PowerMAX_OS | Synergy:PowerMAX_OS:*:*) echo powerpc-harris-powermax exit ;; Night_Hawk:Power_UNIX:*:*) echo powerpc-harris-powerunix exit ;; m88k:CX/UX:7*:*) echo m88k-harris-cxux7 exit ;; m88k:*:4*:R4*) echo m88k-motorola-sysv4 exit ;; m88k:*:3*:R3*) echo m88k-motorola-sysv3 exit ;; AViiON:dgux:*:*) # DG/UX returns AViiON for all architectures UNAME_PROCESSOR=`/usr/bin/uname -p` if [ $UNAME_PROCESSOR = mc88100 ] || [ $UNAME_PROCESSOR = mc88110 ] then if [ ${TARGET_BINARY_INTERFACE}x = m88kdguxelfx ] || \ [ ${TARGET_BINARY_INTERFACE}x = x ] then echo m88k-dg-dgux${UNAME_RELEASE} else echo m88k-dg-dguxbcs${UNAME_RELEASE} fi else echo i586-dg-dgux${UNAME_RELEASE} fi exit ;; M88*:DolphinOS:*:*) # DolphinOS (SVR3) echo m88k-dolphin-sysv3 exit ;; M88*:*:R3*:*) # Delta 88k system running SVR3 echo m88k-motorola-sysv3 exit ;; XD88*:*:*:*) # Tektronix XD88 system running UTekV (SVR3) echo m88k-tektronix-sysv3 exit ;; Tek43[0-9][0-9]:UTek:*:*) # Tektronix 4300 system running UTek (BSD) echo m68k-tektronix-bsd exit ;; *:IRIX*:*:*) echo mips-sgi-irix`echo ${UNAME_RELEASE}|sed -e 's/-/_/g'` exit ;; ????????:AIX?:[12].1:2) # AIX 2.2.1 or AIX 2.1.1 is RT/PC AIX. echo romp-ibm-aix # uname -m gives an 8 hex-code CPU id exit ;; # Note that: echo "'`uname -s`'" gives 'AIX ' i*86:AIX:*:*) echo i386-ibm-aix exit ;; ia64:AIX:*:*) if [ -x /usr/bin/oslevel ] ; then IBM_REV=`/usr/bin/oslevel` else IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE} fi echo ${UNAME_MACHINE}-ibm-aix${IBM_REV} exit ;; *:AIX:2:3) if grep bos325 /usr/include/stdio.h >/dev/null 2>&1; then eval $set_cc_for_build sed 's/^ //' << EOF >$dummy.c #include main() { if (!__power_pc()) exit(1); puts("powerpc-ibm-aix3.2.5"); exit(0); } EOF if $CC_FOR_BUILD -o $dummy $dummy.c && SYSTEM_NAME=`$dummy` then echo "$SYSTEM_NAME" else echo rs6000-ibm-aix3.2.5 fi elif grep bos324 /usr/include/stdio.h >/dev/null 2>&1; then echo rs6000-ibm-aix3.2.4 else echo rs6000-ibm-aix3.2 fi exit ;; *:AIX:*:[4567]) IBM_CPU_ID=`/usr/sbin/lsdev -C -c processor -S available | sed 1q | awk '{ print $1 }'` if /usr/sbin/lsattr -El ${IBM_CPU_ID} | grep ' POWER' >/dev/null 2>&1; then IBM_ARCH=rs6000 else IBM_ARCH=powerpc fi if [ -x /usr/bin/oslevel ] ; then IBM_REV=`/usr/bin/oslevel` else IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE} fi echo ${IBM_ARCH}-ibm-aix${IBM_REV} exit ;; *:AIX:*:*) echo rs6000-ibm-aix exit ;; ibmrt:4.4BSD:*|romp-ibm:BSD:*) echo romp-ibm-bsd4.4 exit ;; ibmrt:*BSD:*|romp-ibm:BSD:*) # covers RT/PC BSD and echo romp-ibm-bsd${UNAME_RELEASE} # 4.3 with uname added to exit ;; # report: romp-ibm BSD 4.3 *:BOSX:*:*) echo rs6000-bull-bosx exit ;; DPX/2?00:B.O.S.:*:*) echo m68k-bull-sysv3 exit ;; 9000/[34]??:4.3bsd:1.*:*) echo m68k-hp-bsd exit ;; hp300:4.4BSD:*:* | 9000/[34]??:4.3bsd:2.*:*) echo m68k-hp-bsd4.4 exit ;; 9000/[34678]??:HP-UX:*:*) HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'` case "${UNAME_MACHINE}" in 9000/31? ) HP_ARCH=m68000 ;; 9000/[34]?? ) HP_ARCH=m68k ;; 9000/[678][0-9][0-9]) if [ -x /usr/bin/getconf ]; then sc_cpu_version=`/usr/bin/getconf SC_CPU_VERSION 2>/dev/null` sc_kernel_bits=`/usr/bin/getconf SC_KERNEL_BITS 2>/dev/null` case "${sc_cpu_version}" in 523) HP_ARCH="hppa1.0" ;; # CPU_PA_RISC1_0 528) HP_ARCH="hppa1.1" ;; # CPU_PA_RISC1_1 532) # CPU_PA_RISC2_0 case "${sc_kernel_bits}" in 32) HP_ARCH="hppa2.0n" ;; 64) HP_ARCH="hppa2.0w" ;; '') HP_ARCH="hppa2.0" ;; # HP-UX 10.20 esac ;; esac fi if [ "${HP_ARCH}" = "" ]; then eval $set_cc_for_build sed 's/^ //' << EOF >$dummy.c #define _HPUX_SOURCE #include #include int main () { #if defined(_SC_KERNEL_BITS) long bits = sysconf(_SC_KERNEL_BITS); #endif long cpu = sysconf (_SC_CPU_VERSION); switch (cpu) { case CPU_PA_RISC1_0: puts ("hppa1.0"); break; case CPU_PA_RISC1_1: puts ("hppa1.1"); break; case CPU_PA_RISC2_0: #if defined(_SC_KERNEL_BITS) switch (bits) { case 64: puts ("hppa2.0w"); break; case 32: puts ("hppa2.0n"); break; default: puts ("hppa2.0"); break; } break; #else /* !defined(_SC_KERNEL_BITS) */ puts ("hppa2.0"); break; #endif default: puts ("hppa1.0"); break; } exit (0); } EOF (CCOPTS= $CC_FOR_BUILD -o $dummy $dummy.c 2>/dev/null) && HP_ARCH=`$dummy` test -z "$HP_ARCH" && HP_ARCH=hppa fi ;; esac if [ ${HP_ARCH} = "hppa2.0w" ] then eval $set_cc_for_build # hppa2.0w-hp-hpux* has a 64-bit kernel and a compiler generating # 32-bit code. hppa64-hp-hpux* has the same kernel and a compiler # generating 64-bit code. GNU and HP use different nomenclature: # # $ CC_FOR_BUILD=cc ./config.guess # => hppa2.0w-hp-hpux11.23 # $ CC_FOR_BUILD="cc +DA2.0w" ./config.guess # => hppa64-hp-hpux11.23 if echo __LP64__ | (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | grep -q __LP64__ then HP_ARCH="hppa2.0w" else HP_ARCH="hppa64" fi fi echo ${HP_ARCH}-hp-hpux${HPUX_REV} exit ;; ia64:HP-UX:*:*) HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'` echo ia64-hp-hpux${HPUX_REV} exit ;; 3050*:HI-UX:*:*) eval $set_cc_for_build sed 's/^ //' << EOF >$dummy.c #include int main () { long cpu = sysconf (_SC_CPU_VERSION); /* The order matters, because CPU_IS_HP_MC68K erroneously returns true for CPU_PA_RISC1_0. CPU_IS_PA_RISC returns correct results, however. */ if (CPU_IS_PA_RISC (cpu)) { switch (cpu) { case CPU_PA_RISC1_0: puts ("hppa1.0-hitachi-hiuxwe2"); break; case CPU_PA_RISC1_1: puts ("hppa1.1-hitachi-hiuxwe2"); break; case CPU_PA_RISC2_0: puts ("hppa2.0-hitachi-hiuxwe2"); break; default: puts ("hppa-hitachi-hiuxwe2"); break; } } else if (CPU_IS_HP_MC68K (cpu)) puts ("m68k-hitachi-hiuxwe2"); else puts ("unknown-hitachi-hiuxwe2"); exit (0); } EOF $CC_FOR_BUILD -o $dummy $dummy.c && SYSTEM_NAME=`$dummy` && { echo "$SYSTEM_NAME"; exit; } echo unknown-hitachi-hiuxwe2 exit ;; 9000/7??:4.3bsd:*:* | 9000/8?[79]:4.3bsd:*:* ) echo hppa1.1-hp-bsd exit ;; 9000/8??:4.3bsd:*:*) echo hppa1.0-hp-bsd exit ;; *9??*:MPE/iX:*:* | *3000*:MPE/iX:*:*) echo hppa1.0-hp-mpeix exit ;; hp7??:OSF1:*:* | hp8?[79]:OSF1:*:* ) echo hppa1.1-hp-osf exit ;; hp8??:OSF1:*:*) echo hppa1.0-hp-osf exit ;; i*86:OSF1:*:*) if [ -x /usr/sbin/sysversion ] ; then echo ${UNAME_MACHINE}-unknown-osf1mk else echo ${UNAME_MACHINE}-unknown-osf1 fi exit ;; parisc*:Lites*:*:*) echo hppa1.1-hp-lites exit ;; C1*:ConvexOS:*:* | convex:ConvexOS:C1*:*) echo c1-convex-bsd exit ;; C2*:ConvexOS:*:* | convex:ConvexOS:C2*:*) if getsysinfo -f scalar_acc then echo c32-convex-bsd else echo c2-convex-bsd fi exit ;; C34*:ConvexOS:*:* | convex:ConvexOS:C34*:*) echo c34-convex-bsd exit ;; C38*:ConvexOS:*:* | convex:ConvexOS:C38*:*) echo c38-convex-bsd exit ;; C4*:ConvexOS:*:* | convex:ConvexOS:C4*:*) echo c4-convex-bsd exit ;; CRAY*Y-MP:*:*:*) echo ymp-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' exit ;; CRAY*[A-Z]90:*:*:*) echo ${UNAME_MACHINE}-cray-unicos${UNAME_RELEASE} \ | sed -e 's/CRAY.*\([A-Z]90\)/\1/' \ -e y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/ \ -e 's/\.[^.]*$/.X/' exit ;; CRAY*TS:*:*:*) echo t90-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' exit ;; CRAY*T3E:*:*:*) echo alphaev5-cray-unicosmk${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' exit ;; CRAY*SV1:*:*:*) echo sv1-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' exit ;; *:UNICOS/mp:*:*) echo craynv-cray-unicosmp${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' exit ;; F30[01]:UNIX_System_V:*:* | F700:UNIX_System_V:*:*) FUJITSU_PROC=`uname -m | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'` FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'` FUJITSU_REL=`echo ${UNAME_RELEASE} | sed -e 's/ /_/'` echo "${FUJITSU_PROC}-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}" exit ;; 5000:UNIX_System_V:4.*:*) FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'` FUJITSU_REL=`echo ${UNAME_RELEASE} | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/ /_/'` echo "sparc-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}" exit ;; i*86:BSD/386:*:* | i*86:BSD/OS:*:* | *:Ascend\ Embedded/OS:*:*) echo ${UNAME_MACHINE}-pc-bsdi${UNAME_RELEASE} exit ;; sparc*:BSD/OS:*:*) echo sparc-unknown-bsdi${UNAME_RELEASE} exit ;; *:BSD/OS:*:*) echo ${UNAME_MACHINE}-unknown-bsdi${UNAME_RELEASE} exit ;; *:FreeBSD:*:*) UNAME_PROCESSOR=`/usr/bin/uname -p` case ${UNAME_PROCESSOR} in amd64) echo x86_64-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;; *) echo ${UNAME_PROCESSOR}-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;; esac exit ;; i*:CYGWIN*:*) echo ${UNAME_MACHINE}-pc-cygwin exit ;; *:MINGW64*:*) echo ${UNAME_MACHINE}-pc-mingw64 exit ;; *:MINGW*:*) echo ${UNAME_MACHINE}-pc-mingw32 exit ;; i*:MSYS*:*) echo ${UNAME_MACHINE}-pc-msys exit ;; i*:windows32*:*) # uname -m includes "-pc" on this system. echo ${UNAME_MACHINE}-mingw32 exit ;; i*:PW*:*) echo ${UNAME_MACHINE}-pc-pw32 exit ;; *:Interix*:*) case ${UNAME_MACHINE} in x86) echo i586-pc-interix${UNAME_RELEASE} exit ;; authenticamd | genuineintel | EM64T) echo x86_64-unknown-interix${UNAME_RELEASE} exit ;; IA64) echo ia64-unknown-interix${UNAME_RELEASE} exit ;; esac ;; [345]86:Windows_95:* | [345]86:Windows_98:* | [345]86:Windows_NT:*) echo i${UNAME_MACHINE}-pc-mks exit ;; 8664:Windows_NT:*) echo x86_64-pc-mks exit ;; i*:Windows_NT*:* | Pentium*:Windows_NT*:*) # How do we know it's Interix rather than the generic POSIX subsystem? # It also conflicts with pre-2.0 versions of AT&T UWIN. Should we # UNAME_MACHINE based on the output of uname instead of i386? echo i586-pc-interix exit ;; i*:UWIN*:*) echo ${UNAME_MACHINE}-pc-uwin exit ;; amd64:CYGWIN*:*:* | x86_64:CYGWIN*:*:*) echo x86_64-unknown-cygwin exit ;; p*:CYGWIN*:*) echo powerpcle-unknown-cygwin exit ;; prep*:SunOS:5.*:*) echo powerpcle-unknown-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` exit ;; *:GNU:*:*) # the GNU system echo `echo ${UNAME_MACHINE}|sed -e 's,[-/].*$,,'`-unknown-${LIBC}`echo ${UNAME_RELEASE}|sed -e 's,/.*$,,'` exit ;; *:GNU/*:*:*) # other systems with GNU libc and userland echo ${UNAME_MACHINE}-unknown-`echo ${UNAME_SYSTEM} | sed 's,^[^/]*/,,' | tr '[A-Z]' '[a-z]'``echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`-${LIBC} exit ;; i*86:Minix:*:*) echo ${UNAME_MACHINE}-pc-minix exit ;; aarch64:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; aarch64_be:Linux:*:*) UNAME_MACHINE=aarch64_be echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; alpha:Linux:*:*) case `sed -n '/^cpu model/s/^.*: \(.*\)/\1/p' < /proc/cpuinfo` in EV5) UNAME_MACHINE=alphaev5 ;; EV56) UNAME_MACHINE=alphaev56 ;; PCA56) UNAME_MACHINE=alphapca56 ;; PCA57) UNAME_MACHINE=alphapca56 ;; EV6) UNAME_MACHINE=alphaev6 ;; EV67) UNAME_MACHINE=alphaev67 ;; EV68*) UNAME_MACHINE=alphaev68 ;; esac objdump --private-headers /bin/sh | grep -q ld.so.1 if test "$?" = 0 ; then LIBC="gnulibc1" ; fi echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; arc:Linux:*:* | arceb:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; arm*:Linux:*:*) eval $set_cc_for_build if echo __ARM_EABI__ | $CC_FOR_BUILD -E - 2>/dev/null \ | grep -q __ARM_EABI__ then echo ${UNAME_MACHINE}-unknown-linux-${LIBC} else if echo __ARM_PCS_VFP | $CC_FOR_BUILD -E - 2>/dev/null \ | grep -q __ARM_PCS_VFP then echo ${UNAME_MACHINE}-unknown-linux-${LIBC}eabi else echo ${UNAME_MACHINE}-unknown-linux-${LIBC}eabihf fi fi exit ;; avr32*:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; cris:Linux:*:*) echo ${UNAME_MACHINE}-axis-linux-${LIBC} exit ;; crisv32:Linux:*:*) echo ${UNAME_MACHINE}-axis-linux-${LIBC} exit ;; frv:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; hexagon:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; i*86:Linux:*:*) echo ${UNAME_MACHINE}-pc-linux-${LIBC} exit ;; ia64:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; m32r*:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; m68*:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; mips:Linux:*:* | mips64:Linux:*:*) eval $set_cc_for_build sed 's/^ //' << EOF >$dummy.c #undef CPU #undef ${UNAME_MACHINE} #undef ${UNAME_MACHINE}el #if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL) CPU=${UNAME_MACHINE}el #else #if defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB) CPU=${UNAME_MACHINE} #else CPU= #endif #endif EOF eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep '^CPU'` test x"${CPU}" != x && { echo "${CPU}-unknown-linux-${LIBC}"; exit; } ;; or1k:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; or32:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; padre:Linux:*:*) echo sparc-unknown-linux-${LIBC} exit ;; parisc64:Linux:*:* | hppa64:Linux:*:*) echo hppa64-unknown-linux-${LIBC} exit ;; parisc:Linux:*:* | hppa:Linux:*:*) # Look for CPU level case `grep '^cpu[^a-z]*:' /proc/cpuinfo 2>/dev/null | cut -d' ' -f2` in PA7*) echo hppa1.1-unknown-linux-${LIBC} ;; PA8*) echo hppa2.0-unknown-linux-${LIBC} ;; *) echo hppa-unknown-linux-${LIBC} ;; esac exit ;; ppc64:Linux:*:*) echo powerpc64-unknown-linux-${LIBC} exit ;; ppc:Linux:*:*) echo powerpc-unknown-linux-${LIBC} exit ;; ppc64le:Linux:*:*) echo powerpc64le-unknown-linux-${LIBC} exit ;; ppcle:Linux:*:*) echo powerpcle-unknown-linux-${LIBC} exit ;; s390:Linux:*:* | s390x:Linux:*:*) echo ${UNAME_MACHINE}-ibm-linux-${LIBC} exit ;; sh64*:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; sh*:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; sparc:Linux:*:* | sparc64:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; tile*:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; vax:Linux:*:*) echo ${UNAME_MACHINE}-dec-linux-${LIBC} exit ;; x86_64:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; xtensa*:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; i*86:DYNIX/ptx:4*:*) # ptx 4.0 does uname -s correctly, with DYNIX/ptx in there. # earlier versions are messed up and put the nodename in both # sysname and nodename. echo i386-sequent-sysv4 exit ;; i*86:UNIX_SV:4.2MP:2.*) # Unixware is an offshoot of SVR4, but it has its own version # number series starting with 2... # I am not positive that other SVR4 systems won't match this, # I just have to hope. -- rms. # Use sysv4.2uw... so that sysv4* matches it. echo ${UNAME_MACHINE}-pc-sysv4.2uw${UNAME_VERSION} exit ;; i*86:OS/2:*:*) # If we were able to find `uname', then EMX Unix compatibility # is probably installed. echo ${UNAME_MACHINE}-pc-os2-emx exit ;; i*86:XTS-300:*:STOP) echo ${UNAME_MACHINE}-unknown-stop exit ;; i*86:atheos:*:*) echo ${UNAME_MACHINE}-unknown-atheos exit ;; i*86:syllable:*:*) echo ${UNAME_MACHINE}-pc-syllable exit ;; i*86:LynxOS:2.*:* | i*86:LynxOS:3.[01]*:* | i*86:LynxOS:4.[02]*:*) echo i386-unknown-lynxos${UNAME_RELEASE} exit ;; i*86:*DOS:*:*) echo ${UNAME_MACHINE}-pc-msdosdjgpp exit ;; i*86:*:4.*:* | i*86:SYSTEM_V:4.*:*) UNAME_REL=`echo ${UNAME_RELEASE} | sed 's/\/MP$//'` if grep Novell /usr/include/link.h >/dev/null 2>/dev/null; then echo ${UNAME_MACHINE}-univel-sysv${UNAME_REL} else echo ${UNAME_MACHINE}-pc-sysv${UNAME_REL} fi exit ;; i*86:*:5:[678]*) # UnixWare 7.x, OpenUNIX and OpenServer 6. case `/bin/uname -X | grep "^Machine"` in *486*) UNAME_MACHINE=i486 ;; *Pentium) UNAME_MACHINE=i586 ;; *Pent*|*Celeron) UNAME_MACHINE=i686 ;; esac echo ${UNAME_MACHINE}-unknown-sysv${UNAME_RELEASE}${UNAME_SYSTEM}${UNAME_VERSION} exit ;; i*86:*:3.2:*) if test -f /usr/options/cb.name; then UNAME_REL=`sed -n 's/.*Version //p' /dev/null >/dev/null ; then UNAME_REL=`(/bin/uname -X|grep Release|sed -e 's/.*= //')` (/bin/uname -X|grep i80486 >/dev/null) && UNAME_MACHINE=i486 (/bin/uname -X|grep '^Machine.*Pentium' >/dev/null) \ && UNAME_MACHINE=i586 (/bin/uname -X|grep '^Machine.*Pent *II' >/dev/null) \ && UNAME_MACHINE=i686 (/bin/uname -X|grep '^Machine.*Pentium Pro' >/dev/null) \ && UNAME_MACHINE=i686 echo ${UNAME_MACHINE}-pc-sco$UNAME_REL else echo ${UNAME_MACHINE}-pc-sysv32 fi exit ;; pc:*:*:*) # Left here for compatibility: # uname -m prints for DJGPP always 'pc', but it prints nothing about # the processor, so we play safe by assuming i586. # Note: whatever this is, it MUST be the same as what config.sub # prints for the "djgpp" host, or else GDB configury will decide that # this is a cross-build. echo i586-pc-msdosdjgpp exit ;; Intel:Mach:3*:*) echo i386-pc-mach3 exit ;; paragon:*:*:*) echo i860-intel-osf1 exit ;; i860:*:4.*:*) # i860-SVR4 if grep Stardent /usr/include/sys/uadmin.h >/dev/null 2>&1 ; then echo i860-stardent-sysv${UNAME_RELEASE} # Stardent Vistra i860-SVR4 else # Add other i860-SVR4 vendors below as they are discovered. echo i860-unknown-sysv${UNAME_RELEASE} # Unknown i860-SVR4 fi exit ;; mini*:CTIX:SYS*5:*) # "miniframe" echo m68010-convergent-sysv exit ;; mc68k:UNIX:SYSTEM5:3.51m) echo m68k-convergent-sysv exit ;; M680?0:D-NIX:5.3:*) echo m68k-diab-dnix exit ;; M68*:*:R3V[5678]*:*) test -r /sysV68 && { echo 'm68k-motorola-sysv'; exit; } ;; 3[345]??:*:4.0:3.0 | 3[34]??A:*:4.0:3.0 | 3[34]??,*:*:4.0:3.0 | 3[34]??/*:*:4.0:3.0 | 4400:*:4.0:3.0 | 4850:*:4.0:3.0 | SKA40:*:4.0:3.0 | SDS2:*:4.0:3.0 | SHG2:*:4.0:3.0 | S7501*:*:4.0:3.0) OS_REL='' test -r /etc/.relid \ && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid` /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ && { echo i486-ncr-sysv4.3${OS_REL}; exit; } /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \ && { echo i586-ncr-sysv4.3${OS_REL}; exit; } ;; 3[34]??:*:4.0:* | 3[34]??,*:*:4.0:*) /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ && { echo i486-ncr-sysv4; exit; } ;; NCR*:*:4.2:* | MPRAS*:*:4.2:*) OS_REL='.3' test -r /etc/.relid \ && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid` /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ && { echo i486-ncr-sysv4.3${OS_REL}; exit; } /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \ && { echo i586-ncr-sysv4.3${OS_REL}; exit; } /bin/uname -p 2>/dev/null | /bin/grep pteron >/dev/null \ && { echo i586-ncr-sysv4.3${OS_REL}; exit; } ;; m68*:LynxOS:2.*:* | m68*:LynxOS:3.0*:*) echo m68k-unknown-lynxos${UNAME_RELEASE} exit ;; mc68030:UNIX_System_V:4.*:*) echo m68k-atari-sysv4 exit ;; TSUNAMI:LynxOS:2.*:*) echo sparc-unknown-lynxos${UNAME_RELEASE} exit ;; rs6000:LynxOS:2.*:*) echo rs6000-unknown-lynxos${UNAME_RELEASE} exit ;; PowerPC:LynxOS:2.*:* | PowerPC:LynxOS:3.[01]*:* | PowerPC:LynxOS:4.[02]*:*) echo powerpc-unknown-lynxos${UNAME_RELEASE} exit ;; SM[BE]S:UNIX_SV:*:*) echo mips-dde-sysv${UNAME_RELEASE} exit ;; RM*:ReliantUNIX-*:*:*) echo mips-sni-sysv4 exit ;; RM*:SINIX-*:*:*) echo mips-sni-sysv4 exit ;; *:SINIX-*:*:*) if uname -p 2>/dev/null >/dev/null ; then UNAME_MACHINE=`(uname -p) 2>/dev/null` echo ${UNAME_MACHINE}-sni-sysv4 else echo ns32k-sni-sysv fi exit ;; PENTIUM:*:4.0*:*) # Unisys `ClearPath HMP IX 4000' SVR4/MP effort # says echo i586-unisys-sysv4 exit ;; *:UNIX_System_V:4*:FTX*) # From Gerald Hewes . # How about differentiating between stratus architectures? -djm echo hppa1.1-stratus-sysv4 exit ;; *:*:*:FTX*) # From seanf@swdc.stratus.com. echo i860-stratus-sysv4 exit ;; i*86:VOS:*:*) # From Paul.Green@stratus.com. echo ${UNAME_MACHINE}-stratus-vos exit ;; *:VOS:*:*) # From Paul.Green@stratus.com. echo hppa1.1-stratus-vos exit ;; mc68*:A/UX:*:*) echo m68k-apple-aux${UNAME_RELEASE} exit ;; news*:NEWS-OS:6*:*) echo mips-sony-newsos6 exit ;; R[34]000:*System_V*:*:* | R4000:UNIX_SYSV:*:* | R*000:UNIX_SV:*:*) if [ -d /usr/nec ]; then echo mips-nec-sysv${UNAME_RELEASE} else echo mips-unknown-sysv${UNAME_RELEASE} fi exit ;; BeBox:BeOS:*:*) # BeOS running on hardware made by Be, PPC only. echo powerpc-be-beos exit ;; BeMac:BeOS:*:*) # BeOS running on Mac or Mac clone, PPC only. echo powerpc-apple-beos exit ;; BePC:BeOS:*:*) # BeOS running on Intel PC compatible. echo i586-pc-beos exit ;; BePC:Haiku:*:*) # Haiku running on Intel PC compatible. echo i586-pc-haiku exit ;; x86_64:Haiku:*:*) echo x86_64-unknown-haiku exit ;; SX-4:SUPER-UX:*:*) echo sx4-nec-superux${UNAME_RELEASE} exit ;; SX-5:SUPER-UX:*:*) echo sx5-nec-superux${UNAME_RELEASE} exit ;; SX-6:SUPER-UX:*:*) echo sx6-nec-superux${UNAME_RELEASE} exit ;; SX-7:SUPER-UX:*:*) echo sx7-nec-superux${UNAME_RELEASE} exit ;; SX-8:SUPER-UX:*:*) echo sx8-nec-superux${UNAME_RELEASE} exit ;; SX-8R:SUPER-UX:*:*) echo sx8r-nec-superux${UNAME_RELEASE} exit ;; Power*:Rhapsody:*:*) echo powerpc-apple-rhapsody${UNAME_RELEASE} exit ;; *:Rhapsody:*:*) echo ${UNAME_MACHINE}-apple-rhapsody${UNAME_RELEASE} exit ;; *:Darwin:*:*) UNAME_PROCESSOR=`uname -p` || UNAME_PROCESSOR=unknown eval $set_cc_for_build if test "$UNAME_PROCESSOR" = unknown ; then UNAME_PROCESSOR=powerpc fi if [ "$CC_FOR_BUILD" != 'no_compiler_found' ]; then if (echo '#ifdef __LP64__'; echo IS_64BIT_ARCH; echo '#endif') | \ (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | \ grep IS_64BIT_ARCH >/dev/null then case $UNAME_PROCESSOR in i386) UNAME_PROCESSOR=x86_64 ;; powerpc) UNAME_PROCESSOR=powerpc64 ;; esac fi fi echo ${UNAME_PROCESSOR}-apple-darwin${UNAME_RELEASE} exit ;; *:procnto*:*:* | *:QNX:[0123456789]*:*) UNAME_PROCESSOR=`uname -p` if test "$UNAME_PROCESSOR" = "x86"; then UNAME_PROCESSOR=i386 UNAME_MACHINE=pc fi echo ${UNAME_PROCESSOR}-${UNAME_MACHINE}-nto-qnx${UNAME_RELEASE} exit ;; *:QNX:*:4*) echo i386-pc-qnx exit ;; NEO-?:NONSTOP_KERNEL:*:*) echo neo-tandem-nsk${UNAME_RELEASE} exit ;; NSE-*:NONSTOP_KERNEL:*:*) echo nse-tandem-nsk${UNAME_RELEASE} exit ;; NSR-?:NONSTOP_KERNEL:*:*) echo nsr-tandem-nsk${UNAME_RELEASE} exit ;; *:NonStop-UX:*:*) echo mips-compaq-nonstopux exit ;; BS2000:POSIX*:*:*) echo bs2000-siemens-sysv exit ;; DS/*:UNIX_System_V:*:*) echo ${UNAME_MACHINE}-${UNAME_SYSTEM}-${UNAME_RELEASE} exit ;; *:Plan9:*:*) # "uname -m" is not consistent, so use $cputype instead. 386 # is converted to i386 for consistency with other x86 # operating systems. if test "$cputype" = "386"; then UNAME_MACHINE=i386 else UNAME_MACHINE="$cputype" fi echo ${UNAME_MACHINE}-unknown-plan9 exit ;; *:TOPS-10:*:*) echo pdp10-unknown-tops10 exit ;; *:TENEX:*:*) echo pdp10-unknown-tenex exit ;; KS10:TOPS-20:*:* | KL10:TOPS-20:*:* | TYPE4:TOPS-20:*:*) echo pdp10-dec-tops20 exit ;; XKL-1:TOPS-20:*:* | TYPE5:TOPS-20:*:*) echo pdp10-xkl-tops20 exit ;; *:TOPS-20:*:*) echo pdp10-unknown-tops20 exit ;; *:ITS:*:*) echo pdp10-unknown-its exit ;; SEI:*:*:SEIUX) echo mips-sei-seiux${UNAME_RELEASE} exit ;; *:DragonFly:*:*) echo ${UNAME_MACHINE}-unknown-dragonfly`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` exit ;; *:*VMS:*:*) UNAME_MACHINE=`(uname -p) 2>/dev/null` case "${UNAME_MACHINE}" in A*) echo alpha-dec-vms ; exit ;; I*) echo ia64-dec-vms ; exit ;; V*) echo vax-dec-vms ; exit ;; esac ;; *:XENIX:*:SysV) echo i386-pc-xenix exit ;; i*86:skyos:*:*) echo ${UNAME_MACHINE}-pc-skyos`echo ${UNAME_RELEASE}` | sed -e 's/ .*$//' exit ;; i*86:rdos:*:*) echo ${UNAME_MACHINE}-pc-rdos exit ;; i*86:AROS:*:*) echo ${UNAME_MACHINE}-pc-aros exit ;; x86_64:VMkernel:*:*) echo ${UNAME_MACHINE}-unknown-esx exit ;; esac eval $set_cc_for_build cat >$dummy.c < # include #endif main () { #if defined (sony) #if defined (MIPSEB) /* BFD wants "bsd" instead of "newsos". Perhaps BFD should be changed, I don't know.... */ printf ("mips-sony-bsd\n"); exit (0); #else #include printf ("m68k-sony-newsos%s\n", #ifdef NEWSOS4 "4" #else "" #endif ); exit (0); #endif #endif #if defined (__arm) && defined (__acorn) && defined (__unix) printf ("arm-acorn-riscix\n"); exit (0); #endif #if defined (hp300) && !defined (hpux) printf ("m68k-hp-bsd\n"); exit (0); #endif #if defined (NeXT) #if !defined (__ARCHITECTURE__) #define __ARCHITECTURE__ "m68k" #endif int version; version=`(hostinfo | sed -n 's/.*NeXT Mach \([0-9]*\).*/\1/p') 2>/dev/null`; if (version < 4) printf ("%s-next-nextstep%d\n", __ARCHITECTURE__, version); else printf ("%s-next-openstep%d\n", __ARCHITECTURE__, version); exit (0); #endif #if defined (MULTIMAX) || defined (n16) #if defined (UMAXV) printf ("ns32k-encore-sysv\n"); exit (0); #else #if defined (CMU) printf ("ns32k-encore-mach\n"); exit (0); #else printf ("ns32k-encore-bsd\n"); exit (0); #endif #endif #endif #if defined (__386BSD__) printf ("i386-pc-bsd\n"); exit (0); #endif #if defined (sequent) #if defined (i386) printf ("i386-sequent-dynix\n"); exit (0); #endif #if defined (ns32000) printf ("ns32k-sequent-dynix\n"); exit (0); #endif #endif #if defined (_SEQUENT_) struct utsname un; uname(&un); if (strncmp(un.version, "V2", 2) == 0) { printf ("i386-sequent-ptx2\n"); exit (0); } if (strncmp(un.version, "V1", 2) == 0) { /* XXX is V1 correct? */ printf ("i386-sequent-ptx1\n"); exit (0); } printf ("i386-sequent-ptx\n"); exit (0); #endif #if defined (vax) # if !defined (ultrix) # include # if defined (BSD) # if BSD == 43 printf ("vax-dec-bsd4.3\n"); exit (0); # else # if BSD == 199006 printf ("vax-dec-bsd4.3reno\n"); exit (0); # else printf ("vax-dec-bsd\n"); exit (0); # endif # endif # else printf ("vax-dec-bsd\n"); exit (0); # endif # else printf ("vax-dec-ultrix\n"); exit (0); # endif #endif #if defined (alliant) && defined (i860) printf ("i860-alliant-bsd\n"); exit (0); #endif exit (1); } EOF $CC_FOR_BUILD -o $dummy $dummy.c 2>/dev/null && SYSTEM_NAME=`$dummy` && { echo "$SYSTEM_NAME"; exit; } # Apollos put the system type in the environment. test -d /usr/apollo && { echo ${ISP}-apollo-${SYSTYPE}; exit; } # Convex versions that predate uname can use getsysinfo(1) if [ -x /usr/convex/getsysinfo ] then case `getsysinfo -f cpu_type` in c1*) echo c1-convex-bsd exit ;; c2*) if getsysinfo -f scalar_acc then echo c32-convex-bsd else echo c2-convex-bsd fi exit ;; c34*) echo c34-convex-bsd exit ;; c38*) echo c38-convex-bsd exit ;; c4*) echo c4-convex-bsd exit ;; esac fi cat >&2 < in order to provide the needed information to handle your system. config.guess timestamp = $timestamp uname -m = `(uname -m) 2>/dev/null || echo unknown` uname -r = `(uname -r) 2>/dev/null || echo unknown` uname -s = `(uname -s) 2>/dev/null || echo unknown` uname -v = `(uname -v) 2>/dev/null || echo unknown` /usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null` /bin/uname -X = `(/bin/uname -X) 2>/dev/null` hostinfo = `(hostinfo) 2>/dev/null` /bin/universe = `(/bin/universe) 2>/dev/null` /usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null` /bin/arch = `(/bin/arch) 2>/dev/null` /usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null` /usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null` UNAME_MACHINE = ${UNAME_MACHINE} UNAME_RELEASE = ${UNAME_RELEASE} UNAME_SYSTEM = ${UNAME_SYSTEM} UNAME_VERSION = ${UNAME_VERSION} EOF exit 1 # Local variables: # eval: (add-hook 'write-file-hooks 'time-stamp) # time-stamp-start: "timestamp='" # time-stamp-format: "%:y-%02m-%02d" # time-stamp-end: "'" # End: telepathy-gabble-0.18.2/compile0000755000175000017500000001624512312536073016405 0ustar00smcvsmcv00000000000000#! /bin/sh # Wrapper for compilers which do not understand '-c -o'. scriptversion=2012-10-14.11; # UTC # Copyright (C) 1999-2013 Free Software Foundation, Inc. # Written by Tom Tromey . # # This program 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 2, or (at your option) # any later version. # # This program 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 this program. If not, see . # As a special exception to the GNU General Public License, if you # distribute this file as part of a program that contains a # configuration script generated by Autoconf, you may include it under # the same distribution terms that you use for the rest of that program. # This file is maintained in Automake, please report # bugs to or send patches to # . nl=' ' # We need space, tab and new line, in precisely that order. Quoting is # there to prevent tools from complaining about whitespace usage. IFS=" "" $nl" file_conv= # func_file_conv build_file lazy # Convert a $build file to $host form and store it in $file # Currently only supports Windows hosts. If the determined conversion # type is listed in (the comma separated) LAZY, no conversion will # take place. func_file_conv () { file=$1 case $file in / | /[!/]*) # absolute file, and not a UNC file if test -z "$file_conv"; then # lazily determine how to convert abs files case `uname -s` in MINGW*) file_conv=mingw ;; CYGWIN*) file_conv=cygwin ;; *) file_conv=wine ;; esac fi case $file_conv/,$2, in *,$file_conv,*) ;; mingw/*) file=`cmd //C echo "$file " | sed -e 's/"\(.*\) " *$/\1/'` ;; cygwin/*) file=`cygpath -m "$file" || echo "$file"` ;; wine/*) file=`winepath -w "$file" || echo "$file"` ;; esac ;; esac } # func_cl_dashL linkdir # Make cl look for libraries in LINKDIR func_cl_dashL () { func_file_conv "$1" if test -z "$lib_path"; then lib_path=$file else lib_path="$lib_path;$file" fi linker_opts="$linker_opts -LIBPATH:$file" } # func_cl_dashl library # Do a library search-path lookup for cl func_cl_dashl () { lib=$1 found=no save_IFS=$IFS IFS=';' for dir in $lib_path $LIB do IFS=$save_IFS if $shared && test -f "$dir/$lib.dll.lib"; then found=yes lib=$dir/$lib.dll.lib break fi if test -f "$dir/$lib.lib"; then found=yes lib=$dir/$lib.lib break fi if test -f "$dir/lib$lib.a"; then found=yes lib=$dir/lib$lib.a break fi done IFS=$save_IFS if test "$found" != yes; then lib=$lib.lib fi } # func_cl_wrapper cl arg... # Adjust compile command to suit cl func_cl_wrapper () { # Assume a capable shell lib_path= shared=: linker_opts= for arg do if test -n "$eat"; then eat= else case $1 in -o) # configure might choose to run compile as 'compile cc -o foo foo.c'. eat=1 case $2 in *.o | *.[oO][bB][jJ]) func_file_conv "$2" set x "$@" -Fo"$file" shift ;; *) func_file_conv "$2" set x "$@" -Fe"$file" shift ;; esac ;; -I) eat=1 func_file_conv "$2" mingw set x "$@" -I"$file" shift ;; -I*) func_file_conv "${1#-I}" mingw set x "$@" -I"$file" shift ;; -l) eat=1 func_cl_dashl "$2" set x "$@" "$lib" shift ;; -l*) func_cl_dashl "${1#-l}" set x "$@" "$lib" shift ;; -L) eat=1 func_cl_dashL "$2" ;; -L*) func_cl_dashL "${1#-L}" ;; -static) shared=false ;; -Wl,*) arg=${1#-Wl,} save_ifs="$IFS"; IFS=',' for flag in $arg; do IFS="$save_ifs" linker_opts="$linker_opts $flag" done IFS="$save_ifs" ;; -Xlinker) eat=1 linker_opts="$linker_opts $2" ;; -*) set x "$@" "$1" shift ;; *.cc | *.CC | *.cxx | *.CXX | *.[cC]++) func_file_conv "$1" set x "$@" -Tp"$file" shift ;; *.c | *.cpp | *.CPP | *.lib | *.LIB | *.Lib | *.OBJ | *.obj | *.[oO]) func_file_conv "$1" mingw set x "$@" "$file" shift ;; *) set x "$@" "$1" shift ;; esac fi shift done if test -n "$linker_opts"; then linker_opts="-link$linker_opts" fi exec "$@" $linker_opts exit 1 } eat= case $1 in '') echo "$0: No command. Try '$0 --help' for more information." 1>&2 exit 1; ;; -h | --h*) cat <<\EOF Usage: compile [--help] [--version] PROGRAM [ARGS] Wrapper for compilers which do not understand '-c -o'. Remove '-o dest.o' from ARGS, run PROGRAM with the remaining arguments, and rename the output as expected. If you are trying to build a whole package this is not the right script to run: please start by reading the file 'INSTALL'. Report bugs to . EOF exit $? ;; -v | --v*) echo "compile $scriptversion" exit $? ;; cl | *[/\\]cl | cl.exe | *[/\\]cl.exe ) func_cl_wrapper "$@" # Doesn't return... ;; esac ofile= cfile= for arg do if test -n "$eat"; then eat= else case $1 in -o) # configure might choose to run compile as 'compile cc -o foo foo.c'. # So we strip '-o arg' only if arg is an object. eat=1 case $2 in *.o | *.obj) ofile=$2 ;; *) set x "$@" -o "$2" shift ;; esac ;; *.c) cfile=$1 set x "$@" "$1" shift ;; *) set x "$@" "$1" shift ;; esac fi shift done if test -z "$ofile" || test -z "$cfile"; then # If no '-o' option was seen then we might have been invoked from a # pattern rule where we don't need one. That is ok -- this is a # normal compilation that the losing compiler can handle. If no # '.c' file was seen then we are probably linking. That is also # ok. exec "$@" fi # Name of file we expect compiler to create. cofile=`echo "$cfile" | sed 's|^.*[\\/]||; s|^[a-zA-Z]:||; s/\.c$/.o/'` # Create the lock directory. # Note: use '[/\\:.-]' here to ensure that we don't use the same name # that we are using for the .o file. Also, base the name on the expected # object file name, since that is what matters with a parallel build. lockdir=`echo "$cofile" | sed -e 's|[/\\:.-]|_|g'`.d while true; do if mkdir "$lockdir" >/dev/null 2>&1; then break fi sleep 1 done # FIXME: race condition here if user kills between mkdir and trap. trap "rmdir '$lockdir'; exit 1" 1 2 15 # Run the compile. "$@" ret=$? if test -f "$cofile"; then test "$cofile" = "$ofile" || mv "$cofile" "$ofile" elif test -f "${cofile}bj"; then test "${cofile}bj" = "$ofile" || mv "${cofile}bj" "$ofile" fi rmdir "$lockdir" exit $ret # Local Variables: # mode: shell-script # sh-indentation: 2 # eval: (add-hook 'write-file-hooks 'time-stamp) # time-stamp-start: "scriptversion=" # time-stamp-format: "%:y-%02m-%02d.%02H" # time-stamp-time-zone: "UTC" # time-stamp-end: "; # UTC" # End: telepathy-gabble-0.18.2/COPYING0000644000175000017500000006715212200204332016047 0ustar00smcvsmcv00000000000000Most of Gabble is licensed under the GNU Lesser General Public License, as published by the Free Software Foundation and reproduced below: either version 2.1 of the License, or (at your option) any later version. XEP tools in the the docs/ directory, used to generate HTML documentation for various non-standard XMPP extensions, are copyright 1999-2009 by the XMPP Standards Foundation (XSF) and is released under a MIT-style license, reproduced below. ------------------------------------------------------------------------ GNU LESSER GENERAL PUBLIC LICENSE Version 2.1, February 1999 Copyright (C) 1991, 1999 Free Software Foundation, Inc. 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. [This is the first released version of the Lesser GPL. It also counts as the successor of the GNU Library Public License, version 2, hence the version number 2.1.] Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public Licenses are intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This license, the Lesser General Public License, applies to some specially designated software packages--typically libraries--of the Free Software Foundation and other authors who decide to use it. You can use it too, but we suggest you first think carefully about whether this license or the ordinary General Public License is the better strategy to use in any particular case, based on the explanations below. When we speak of free software, we are referring to freedom of use, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish); that you receive source code or can get it if you want it; that you can change the software and use pieces of it in new free programs; and that you are informed that you can do these things. To protect your rights, we need to make restrictions that forbid distributors to deny you these rights or to ask you to surrender these rights. These restrictions translate to certain responsibilities for you if you distribute copies of the library or if you modify it. For example, if you distribute copies of the library, whether gratis or for a fee, you must give the recipients all the rights that we gave you. You must make sure that they, too, receive or can get the source code. If you link other code with the library, you must provide complete object files to the recipients, so that they can relink them with the library after making changes to the library and recompiling it. And you must show them these terms so they know their rights. We protect your rights with a two-step method: (1) we copyright the library, and (2) we offer you this license, which gives you legal permission to copy, distribute and/or modify the library. To protect each distributor, we want to make it very clear that there is no warranty for the free library. Also, if the library is modified by someone else and passed on, the recipients should know that what they have is not the original version, so that the original author's reputation will not be affected by problems that might be introduced by others. Finally, software patents pose a constant threat to the existence of any free program. We wish to make sure that a company cannot effectively restrict the users of a free program by obtaining a restrictive license from a patent holder. Therefore, we insist that any patent license obtained for a version of the library must be consistent with the full freedom of use specified in this license. Most GNU software, including some libraries, is covered by the ordinary GNU General Public License. This license, the GNU Lesser General Public License, applies to certain designated libraries, and is quite different from the ordinary General Public License. We use this license for certain libraries in order to permit linking those libraries into non-free programs. When a program is linked with a library, whether statically or using a shared library, the combination of the two is legally speaking a combined work, a derivative of the original library. The ordinary General Public License therefore permits such linking only if the entire combination fits its criteria of freedom. The Lesser General Public License permits more lax criteria for linking other code with the library. We call this license the "Lesser" General Public License because it does Less to protect the user's freedom than the ordinary General Public License. It also provides other free software developers Less of an advantage over competing non-free programs. These disadvantages are the reason we use the ordinary General Public License for many libraries. However, the Lesser license provides advantages in certain special circumstances. For example, on rare occasions, there may be a special need to encourage the widest possible use of a certain library, so that it becomes a de-facto standard. To achieve this, non-free programs must be allowed to use the library. A more frequent case is that a free library does the same job as widely used non-free libraries. In this case, there is little to gain by limiting the free library to free software only, so we use the Lesser General Public License. In other cases, permission to use a particular library in non-free programs enables a greater number of people to use a large body of free software. For example, permission to use the GNU C Library in non-free programs enables many more people to use the whole GNU operating system, as well as its variant, the GNU/Linux operating system. Although the Lesser General Public License is Less protective of the users' freedom, it does ensure that the user of a program that is linked with the Library has the freedom and the wherewithal to run that program using a modified version of the Library. The precise terms and conditions for copying, distribution and modification follow. Pay close attention to the difference between a "work based on the library" and a "work that uses the library". The former contains code derived from the library, whereas the latter must be combined with the library in order to run. GNU LESSER GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License Agreement applies to any software library or other program which contains a notice placed by the copyright holder or other authorized party saying it may be distributed under the terms of this Lesser General Public License (also called "this License"). Each licensee is addressed as "you". A "library" means a collection of software functions and/or data prepared so as to be conveniently linked with application programs (which use some of those functions and data) to form executables. The "Library", below, refers to any such software library or work which has been distributed under these terms. A "work based on the Library" means either the Library or any derivative work under copyright law: that is to say, a work containing the Library or a portion of it, either verbatim or with modifications and/or translated straightforwardly into another language. (Hereinafter, translation is included without limitation in the term "modification".) "Source code" for a work means the preferred form of the work for making modifications to it. For a library, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the library. Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running a program using the Library is not restricted, and output from such a program is covered only if its contents constitute a work based on the Library (independent of the use of the Library in a tool for writing it). Whether that is true depends on what the Library does and what the program that uses the Library does. 1. You may copy and distribute verbatim copies of the Library's complete source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and distribute a copy of this License along with the Library. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Library or any portion of it, thus forming a work based on the Library, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) The modified work must itself be a software library. b) You must cause the files modified to carry prominent notices stating that you changed the files and the date of any change. c) You must cause the whole of the work to be licensed at no charge to all third parties under the terms of this License. d) If a facility in the modified Library refers to a function or a table of data to be supplied by an application program that uses the facility, other than as an argument passed when the facility is invoked, then you must make a good faith effort to ensure that, in the event an application does not supply such function or table, the facility still operates, and performs whatever part of its purpose remains meaningful. (For example, a function in a library to compute square roots has a purpose that is entirely well-defined independent of the application. Therefore, Subsection 2d requires that any application-supplied function or table used by this function must be optional: if the application does not supply it, the square root function must still compute square roots.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Library, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Library, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Library. In addition, mere aggregation of another work not based on the Library with the Library (or with a work based on the Library) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may opt to apply the terms of the ordinary GNU General Public License instead of this License to a given copy of the Library. To do this, you must alter all the notices that refer to this License, so that they refer to the ordinary GNU General Public License, version 2, instead of to this License. (If a newer version than version 2 of the ordinary GNU General Public License has appeared, then you can specify that version instead if you wish.) Do not make any other change in these notices. Once this change is made in a given copy, it is irreversible for that copy, so the ordinary GNU General Public License applies to all subsequent copies and derivative works made from that copy. This option is useful when you wish to copy part of the code of the Library into a program that is not a library. 4. You may copy and distribute the Library (or a portion or derivative of it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange. If distribution of object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place satisfies the requirement to distribute the source code, even though third parties are not compelled to copy the source along with the object code. 5. A program that contains no derivative of any portion of the Library, but is designed to work with the Library by being compiled or linked with it, is called a "work that uses the Library". Such a work, in isolation, is not a derivative work of the Library, and therefore falls outside the scope of this License. However, linking a "work that uses the Library" with the Library creates an executable that is a derivative of the Library (because it contains portions of the Library), rather than a "work that uses the library". The executable is therefore covered by this License. Section 6 states terms for distribution of such executables. When a "work that uses the Library" uses material from a header file that is part of the Library, the object code for the work may be a derivative work of the Library even though the source code is not. Whether this is true is especially significant if the work can be linked without the Library, or if the work is itself a library. The threshold for this to be true is not precisely defined by law. If such an object file uses only numerical parameters, data structure layouts and accessors, and small macros and small inline functions (ten lines or less in length), then the use of the object file is unrestricted, regardless of whether it is legally a derivative work. (Executables containing this object code plus portions of the Library will still fall under Section 6.) Otherwise, if the work is a derivative of the Library, you may distribute the object code for the work under the terms of Section 6. Any executables containing that work also fall under Section 6, whether or not they are linked directly with the Library itself. 6. As an exception to the Sections above, you may also combine or link a "work that uses the Library" with the Library to produce a work containing portions of the Library, and distribute that work under terms of your choice, provided that the terms permit modification of the work for the customer's own use and reverse engineering for debugging such modifications. You must give prominent notice with each copy of the work that the Library is used in it and that the Library and its use are covered by this License. You must supply a copy of this License. If the work during execution displays copyright notices, you must include the copyright notice for the Library among them, as well as a reference directing the user to the copy of this License. Also, you must do one of these things: a) Accompany the work with the complete corresponding machine-readable source code for the Library including whatever changes were used in the work (which must be distributed under Sections 1 and 2 above); and, if the work is an executable linked with the Library, with the complete machine-readable "work that uses the Library", as object code and/or source code, so that the user can modify the Library and then relink to produce a modified executable containing the modified Library. (It is understood that the user who changes the contents of definitions files in the Library will not necessarily be able to recompile the application to use the modified definitions.) b) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (1) uses at run time a copy of the library already present on the user's computer system, rather than copying library functions into the executable, and (2) will operate properly with a modified version of the library, if the user installs one, as long as the modified version is interface-compatible with the version that the work was made with. c) Accompany the work with a written offer, valid for at least three years, to give the same user the materials specified in Subsection 6a, above, for a charge no more than the cost of performing this distribution. d) If distribution of the work is made by offering access to copy from a designated place, offer equivalent access to copy the above specified materials from the same place. e) Verify that the user has already received a copy of these materials or that you have already sent this user a copy. For an executable, the required form of the "work that uses the Library" must include any data and utility programs needed for reproducing the executable from it. However, as a special exception, the materials to be distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. It may happen that this requirement contradicts the license restrictions of other proprietary libraries that do not normally accompany the operating system. Such a contradiction means you cannot use both them and the Library together in an executable that you distribute. 7. You may place library facilities that are a work based on the Library side-by-side in a single library together with other library facilities not covered by this License, and distribute such a combined library, provided that the separate distribution of the work based on the Library and of the other library facilities is otherwise permitted, and provided that you do these two things: a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities. This must be distributed under the terms of the Sections above. b) Give prominent notice with the combined library of the fact that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. 8. You may not copy, modify, sublicense, link with, or distribute the Library except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense, link with, or distribute the Library is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 9. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Library or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Library (or any work based on the Library), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Library or works based on it. 10. Each time you redistribute the Library (or any work based on the Library), the recipient automatically receives a license from the original licensor to copy, distribute, link with or modify the Library subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties with this License. 11. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Library at all. For example, if a patent license would not permit royalty-free redistribution of the Library by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Library. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply, and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 12. If the distribution and/or use of the Library is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Library under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 13. The Free Software Foundation may publish revised and/or new versions of the Lesser General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Library specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Library does not specify a license version number, you may choose any version ever published by the Free Software Foundation. 14. If you wish to incorporate parts of the Library into other free programs whose distribution conditions are incompatible with these, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Libraries If you develop a new library, and you want it to be of the greatest possible use to the public, we recommend making it free software that everyone can redistribute and change. You can do so by permitting redistribution under these terms (or, alternatively, under the terms of the ordinary General Public License). To apply these terms, attach the following notices to the library. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. 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 Also add information on how to contact you by electronic and paper mail. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the library, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the library `Frob' (a library for tweaking knobs) written by James Random Hacker. , 1 April 1990 Ty Coon, President of Vice That's all there is to it! ------------------------------------------------------------------------ License of docs/xep.xsl etc. Copyright (c) 1999 - 2009 XMPP Standards Foundation Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. telepathy-gabble-0.18.2/config.h.in0000644000175000017500000001151212312536073017042 0ustar00smcvsmcv00000000000000/* config.h.in. Generated from configure.ac by autoheader. */ /* Client type from http://xmpp.org/registrar/disco-categories.html#client */ #undef CLIENT_TYPE /* Enable Channel.Type.Call */ #undef ENABLE_CHANNEL_TYPE_CALL /* Enable debug code */ #undef ENABLE_DEBUG /* Critical warnings will result in an assertion */ #undef ENABLE_FATAL_CRITICALS /* Enable file transfer */ #undef ENABLE_FILE_TRANSFER /* Enable Google Jingle relay support */ #undef ENABLE_GOOGLE_RELAY /* Make tests installable */ #undef ENABLE_INSTALLED_TESTS /* Enable file transfer */ #undef ENABLE_JINGLE_FILE_TRANSFER /* Enable plugins */ #undef ENABLE_PLUGINS /* Enable VoIP */ #undef ENABLE_VOIP /* Prevent post 2.32 APIs */ #undef GLIB_VERSION_MAX_ALLOWED /* Ignore post 2.30 deprecations */ #undef GLIB_VERSION_MIN_REQUIRED /* path to system Certificate Authority list */ #undef GTLS_SYSTEM_CA_CERTIFICATES /* Define to 1 if you have the header file. */ #undef HAVE_ARPA_INET_H /* Define to 1 if you have the header file. */ #undef HAVE_ARPA_NAMESER_H /* Define to 1 if you have the header file. */ #undef HAVE_DLFCN_H /* Define to 1 if you have the header file. */ #undef HAVE_FCNTL_H /* Define to 1 if you have the `getifaddrs' function. */ #undef HAVE_GETIFADDRS /* Define to 1 if you have the header file. */ #undef HAVE_IFADDRS_H /* Define to 1 if you have the header file. */ #undef HAVE_INTTYPES_H /* Define to 1 if you have the header file. */ #undef HAVE_MEMORY_H /* Define to 1 if you have the `memset' function. */ #undef HAVE_MEMSET /* Define to 1 if you have the header file. */ #undef HAVE_NETDB_H /* Define to 1 if you have the header file. */ #undef HAVE_NETINET_IN_H /* Define to 1 if you have the header file. */ #undef HAVE_NET_IF_H /* Define to 1 if you have the header file. */ #undef HAVE_RESOLV_H /* Define to 1 if you have the `select' function. */ #undef HAVE_SELECT /* Define to 1 if you have the `setresuid' function. */ #undef HAVE_SETRESUID /* Define to 1 if you have the `setreuid' function. */ #undef HAVE_SETREUID /* Define to 1 if you have the header file. */ #undef HAVE_STDINT_H /* Define to 1 if you have the header file. */ #undef HAVE_STDLIB_H /* Define to 1 if you have the `strerror' function. */ #undef HAVE_STRERROR /* Define to 1 if you have the header file. */ #undef HAVE_STRINGS_H /* Define to 1 if you have the header file. */ #undef HAVE_STRING_H /* Define to 1 if you have the `strndup' function. */ #undef HAVE_STRNDUP /* Define to 1 if you have the header file. */ #undef HAVE_SYS_IOCTL_H /* Define to 1 if you have the header file. */ #undef HAVE_SYS_SOCKET_H /* Define to 1 if you have the header file. */ #undef HAVE_SYS_STAT_H /* Define to 1 if you have the header file. */ #undef HAVE_SYS_TYPES_H /* Define to 1 if you have the header file. */ #undef HAVE_SYS_UN_H /* Define to 1 if you have the header file. */ #undef HAVE_UNISTD_H /* Define to the sub-directory in which libtool stores uninstalled libraries. */ #undef LT_OBJDIR /* Name of package */ #undef PACKAGE /* Define to the address where bug reports for this package should be sent. */ #undef PACKAGE_BUGREPORT /* Define to the full name of this package. */ #undef PACKAGE_NAME /* Define to the full name and version of this package. */ #undef PACKAGE_STRING /* Define to the one symbol short name of this package. */ #undef PACKAGE_TARNAME /* Define to the home page for this package. */ #undef PACKAGE_URL /* Define to the version of this package. */ #undef PACKAGE_VERSION /* Define to 1 if you have the ANSI C header files. */ #undef STDC_HEADERS /* Disable single header include */ #undef TP_DISABLE_SINGLE_INCLUDE /* Prevent to use sealed variables */ #undef TP_SEAL_ENABLE /* Prevent post 0.20 APIs */ #undef TP_VERSION_MAX_ALLOWED /* Ignore post 0.18 deprecations */ #undef TP_VERSION_MIN_REQUIRED /* Enable extensions on AIX 3, Interix. */ #ifndef _ALL_SOURCE # undef _ALL_SOURCE #endif /* Enable GNU extensions on systems that have them. */ #ifndef _GNU_SOURCE # undef _GNU_SOURCE #endif /* Enable threading extensions on Solaris. */ #ifndef _POSIX_PTHREAD_SEMANTICS # undef _POSIX_PTHREAD_SEMANTICS #endif /* Enable extensions on HP NonStop. */ #ifndef _TANDEM_SOURCE # undef _TANDEM_SOURCE #endif /* Enable general extensions on Solaris. */ #ifndef __EXTENSIONS__ # undef __EXTENSIONS__ #endif /* Version number of package */ #undef VERSION /* Define to 1 if on MINIX. */ #undef _MINIX /* Define to 2 if the system does not provide POSIX.1 features except with this defined. */ #undef _POSIX_1_SOURCE /* Define to 1 if you need to in order for `stat' and other things to work. */ #undef _POSIX_SOURCE telepathy-gabble-0.18.2/aclocal.m40000644000175000017500000016066412312536070016671 0ustar00smcvsmcv00000000000000# generated automatically by aclocal 1.14.1 -*- Autoconf -*- # Copyright (C) 1996-2013 Free Software Foundation, Inc. # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. m4_ifndef([AC_CONFIG_MACRO_DIRS], [m4_defun([_AM_CONFIG_MACRO_DIRS], [])m4_defun([AC_CONFIG_MACRO_DIRS], [_AM_CONFIG_MACRO_DIRS($@)])]) m4_ifndef([AC_AUTOCONF_VERSION], [m4_copy([m4_PACKAGE_VERSION], [AC_AUTOCONF_VERSION])])dnl m4_if(m4_defn([AC_AUTOCONF_VERSION]), [2.69],, [m4_warning([this file was generated for autoconf 2.69. You have another version of autoconf. It may work, but is not guaranteed to. If you have problems, you may need to regenerate the build system entirely. To do so, use the procedure documented by the package, typically 'autoreconf'.])]) # pkg.m4 - Macros to locate and utilise pkg-config. -*- Autoconf -*- # serial 1 (pkg-config-0.24) # # Copyright © 2004 Scott James Remnant . # # This program 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 2 of the License, or # (at your option) any later version. # # This program 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 this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # # As a special exception to the GNU General Public License, if you # distribute this file as part of a program that contains a # configuration script generated by Autoconf, you may include it under # the same distribution terms that you use for the rest of that program. # PKG_PROG_PKG_CONFIG([MIN-VERSION]) # ---------------------------------- AC_DEFUN([PKG_PROG_PKG_CONFIG], [m4_pattern_forbid([^_?PKG_[A-Z_]+$]) m4_pattern_allow([^PKG_CONFIG(_(PATH|LIBDIR|SYSROOT_DIR|ALLOW_SYSTEM_(CFLAGS|LIBS)))?$]) m4_pattern_allow([^PKG_CONFIG_(DISABLE_UNINSTALLED|TOP_BUILD_DIR|DEBUG_SPEW)$]) AC_ARG_VAR([PKG_CONFIG], [path to pkg-config utility]) AC_ARG_VAR([PKG_CONFIG_PATH], [directories to add to pkg-config's search path]) AC_ARG_VAR([PKG_CONFIG_LIBDIR], [path overriding pkg-config's built-in search path]) if test "x$ac_cv_env_PKG_CONFIG_set" != "xset"; then AC_PATH_TOOL([PKG_CONFIG], [pkg-config]) fi if test -n "$PKG_CONFIG"; then _pkg_min_version=m4_default([$1], [0.9.0]) AC_MSG_CHECKING([pkg-config is at least version $_pkg_min_version]) if $PKG_CONFIG --atleast-pkgconfig-version $_pkg_min_version; then AC_MSG_RESULT([yes]) else AC_MSG_RESULT([no]) PKG_CONFIG="" fi fi[]dnl ])# PKG_PROG_PKG_CONFIG # PKG_CHECK_EXISTS(MODULES, [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND]) # # Check to see whether a particular set of modules exists. Similar # to PKG_CHECK_MODULES(), but does not set variables or print errors. # # Please remember that m4 expands AC_REQUIRE([PKG_PROG_PKG_CONFIG]) # only at the first occurence in configure.ac, so if the first place # it's called might be skipped (such as if it is within an "if", you # have to call PKG_CHECK_EXISTS manually # -------------------------------------------------------------- AC_DEFUN([PKG_CHECK_EXISTS], [AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl if test -n "$PKG_CONFIG" && \ AC_RUN_LOG([$PKG_CONFIG --exists --print-errors "$1"]); then m4_default([$2], [:]) m4_ifvaln([$3], [else $3])dnl fi]) # _PKG_CONFIG([VARIABLE], [COMMAND], [MODULES]) # --------------------------------------------- m4_define([_PKG_CONFIG], [if test -n "$$1"; then pkg_cv_[]$1="$$1" elif test -n "$PKG_CONFIG"; then PKG_CHECK_EXISTS([$3], [pkg_cv_[]$1=`$PKG_CONFIG --[]$2 "$3" 2>/dev/null` test "x$?" != "x0" && pkg_failed=yes ], [pkg_failed=yes]) else pkg_failed=untried fi[]dnl ])# _PKG_CONFIG # _PKG_SHORT_ERRORS_SUPPORTED # ----------------------------- AC_DEFUN([_PKG_SHORT_ERRORS_SUPPORTED], [AC_REQUIRE([PKG_PROG_PKG_CONFIG]) if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then _pkg_short_errors_supported=yes else _pkg_short_errors_supported=no fi[]dnl ])# _PKG_SHORT_ERRORS_SUPPORTED # PKG_CHECK_MODULES(VARIABLE-PREFIX, MODULES, [ACTION-IF-FOUND], # [ACTION-IF-NOT-FOUND]) # # # Note that if there is a possibility the first call to # PKG_CHECK_MODULES might not happen, you should be sure to include an # explicit call to PKG_PROG_PKG_CONFIG in your configure.ac # # # -------------------------------------------------------------- AC_DEFUN([PKG_CHECK_MODULES], [AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl AC_ARG_VAR([$1][_CFLAGS], [C compiler flags for $1, overriding pkg-config])dnl AC_ARG_VAR([$1][_LIBS], [linker flags for $1, overriding pkg-config])dnl pkg_failed=no AC_MSG_CHECKING([for $1]) _PKG_CONFIG([$1][_CFLAGS], [cflags], [$2]) _PKG_CONFIG([$1][_LIBS], [libs], [$2]) m4_define([_PKG_TEXT], [Alternatively, you may set the environment variables $1[]_CFLAGS and $1[]_LIBS to avoid the need to call pkg-config. See the pkg-config man page for more details.]) if test $pkg_failed = yes; then AC_MSG_RESULT([no]) _PKG_SHORT_ERRORS_SUPPORTED if test $_pkg_short_errors_supported = yes; then $1[]_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "$2" 2>&1` else $1[]_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "$2" 2>&1` fi # Put the nasty error message in config.log where it belongs echo "$$1[]_PKG_ERRORS" >&AS_MESSAGE_LOG_FD m4_default([$4], [AC_MSG_ERROR( [Package requirements ($2) were not met: $$1_PKG_ERRORS Consider adjusting the PKG_CONFIG_PATH environment variable if you installed software in a non-standard prefix. _PKG_TEXT])[]dnl ]) elif test $pkg_failed = untried; then AC_MSG_RESULT([no]) m4_default([$4], [AC_MSG_FAILURE( [The pkg-config script could not be found or is too old. Make sure it is in your PATH or set the PKG_CONFIG environment variable to the full path to pkg-config. _PKG_TEXT To get pkg-config, see .])[]dnl ]) else $1[]_CFLAGS=$pkg_cv_[]$1[]_CFLAGS $1[]_LIBS=$pkg_cv_[]$1[]_LIBS AC_MSG_RESULT([yes]) $3 fi[]dnl ])# PKG_CHECK_MODULES # Copyright (C) 2002-2013 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # AM_AUTOMAKE_VERSION(VERSION) # ---------------------------- # Automake X.Y traces this macro to ensure aclocal.m4 has been # generated from the m4 files accompanying Automake X.Y. # (This private macro should not be called outside this file.) AC_DEFUN([AM_AUTOMAKE_VERSION], [am__api_version='1.14' dnl Some users find AM_AUTOMAKE_VERSION and mistake it for a way to dnl require some minimum version. Point them to the right macro. m4_if([$1], [1.14.1], [], [AC_FATAL([Do not call $0, use AM_INIT_AUTOMAKE([$1]).])])dnl ]) # _AM_AUTOCONF_VERSION(VERSION) # ----------------------------- # aclocal traces this macro to find the Autoconf version. # This is a private macro too. Using m4_define simplifies # the logic in aclocal, which can simply ignore this definition. m4_define([_AM_AUTOCONF_VERSION], []) # AM_SET_CURRENT_AUTOMAKE_VERSION # ------------------------------- # Call AM_AUTOMAKE_VERSION and AM_AUTOMAKE_VERSION so they can be traced. # This function is AC_REQUIREd by AM_INIT_AUTOMAKE. AC_DEFUN([AM_SET_CURRENT_AUTOMAKE_VERSION], [AM_AUTOMAKE_VERSION([1.14.1])dnl m4_ifndef([AC_AUTOCONF_VERSION], [m4_copy([m4_PACKAGE_VERSION], [AC_AUTOCONF_VERSION])])dnl _AM_AUTOCONF_VERSION(m4_defn([AC_AUTOCONF_VERSION]))]) # AM_AUX_DIR_EXPAND -*- Autoconf -*- # Copyright (C) 2001-2013 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # For projects using AC_CONFIG_AUX_DIR([foo]), Autoconf sets # $ac_aux_dir to '$srcdir/foo'. In other projects, it is set to # '$srcdir', '$srcdir/..', or '$srcdir/../..'. # # Of course, Automake must honor this variable whenever it calls a # tool from the auxiliary directory. The problem is that $srcdir (and # therefore $ac_aux_dir as well) can be either absolute or relative, # depending on how configure is run. This is pretty annoying, since # it makes $ac_aux_dir quite unusable in subdirectories: in the top # source directory, any form will work fine, but in subdirectories a # relative path needs to be adjusted first. # # $ac_aux_dir/missing # fails when called from a subdirectory if $ac_aux_dir is relative # $top_srcdir/$ac_aux_dir/missing # fails if $ac_aux_dir is absolute, # fails when called from a subdirectory in a VPATH build with # a relative $ac_aux_dir # # The reason of the latter failure is that $top_srcdir and $ac_aux_dir # are both prefixed by $srcdir. In an in-source build this is usually # harmless because $srcdir is '.', but things will broke when you # start a VPATH build or use an absolute $srcdir. # # So we could use something similar to $top_srcdir/$ac_aux_dir/missing, # iff we strip the leading $srcdir from $ac_aux_dir. That would be: # am_aux_dir='\$(top_srcdir)/'`expr "$ac_aux_dir" : "$srcdir//*\(.*\)"` # and then we would define $MISSING as # MISSING="\${SHELL} $am_aux_dir/missing" # This will work as long as MISSING is not called from configure, because # unfortunately $(top_srcdir) has no meaning in configure. # However there are other variables, like CC, which are often used in # configure, and could therefore not use this "fixed" $ac_aux_dir. # # Another solution, used here, is to always expand $ac_aux_dir to an # absolute PATH. The drawback is that using absolute paths prevent a # configured tree to be moved without reconfiguration. AC_DEFUN([AM_AUX_DIR_EXPAND], [dnl Rely on autoconf to set up CDPATH properly. AC_PREREQ([2.50])dnl # expand $ac_aux_dir to an absolute path am_aux_dir=`cd $ac_aux_dir && pwd` ]) # AM_CONDITIONAL -*- Autoconf -*- # Copyright (C) 1997-2013 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # AM_CONDITIONAL(NAME, SHELL-CONDITION) # ------------------------------------- # Define a conditional. AC_DEFUN([AM_CONDITIONAL], [AC_PREREQ([2.52])dnl m4_if([$1], [TRUE], [AC_FATAL([$0: invalid condition: $1])], [$1], [FALSE], [AC_FATAL([$0: invalid condition: $1])])dnl AC_SUBST([$1_TRUE])dnl AC_SUBST([$1_FALSE])dnl _AM_SUBST_NOTMAKE([$1_TRUE])dnl _AM_SUBST_NOTMAKE([$1_FALSE])dnl m4_define([_AM_COND_VALUE_$1], [$2])dnl if $2; then $1_TRUE= $1_FALSE='#' else $1_TRUE='#' $1_FALSE= fi AC_CONFIG_COMMANDS_PRE( [if test -z "${$1_TRUE}" && test -z "${$1_FALSE}"; then AC_MSG_ERROR([[conditional "$1" was never defined. Usually this means the macro was only invoked conditionally.]]) fi])]) # Copyright (C) 1999-2013 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # There are a few dirty hacks below to avoid letting 'AC_PROG_CC' be # written in clear, in which case automake, when reading aclocal.m4, # will think it sees a *use*, and therefore will trigger all it's # C support machinery. Also note that it means that autoscan, seeing # CC etc. in the Makefile, will ask for an AC_PROG_CC use... # _AM_DEPENDENCIES(NAME) # ---------------------- # See how the compiler implements dependency checking. # NAME is "CC", "CXX", "OBJC", "OBJCXX", "UPC", or "GJC". # We try a few techniques and use that to set a single cache variable. # # We don't AC_REQUIRE the corresponding AC_PROG_CC since the latter was # modified to invoke _AM_DEPENDENCIES(CC); we would have a circular # dependency, and given that the user is not expected to run this macro, # just rely on AC_PROG_CC. AC_DEFUN([_AM_DEPENDENCIES], [AC_REQUIRE([AM_SET_DEPDIR])dnl AC_REQUIRE([AM_OUTPUT_DEPENDENCY_COMMANDS])dnl AC_REQUIRE([AM_MAKE_INCLUDE])dnl AC_REQUIRE([AM_DEP_TRACK])dnl m4_if([$1], [CC], [depcc="$CC" am_compiler_list=], [$1], [CXX], [depcc="$CXX" am_compiler_list=], [$1], [OBJC], [depcc="$OBJC" am_compiler_list='gcc3 gcc'], [$1], [OBJCXX], [depcc="$OBJCXX" am_compiler_list='gcc3 gcc'], [$1], [UPC], [depcc="$UPC" am_compiler_list=], [$1], [GCJ], [depcc="$GCJ" am_compiler_list='gcc3 gcc'], [depcc="$$1" am_compiler_list=]) AC_CACHE_CHECK([dependency style of $depcc], [am_cv_$1_dependencies_compiler_type], [if test -z "$AMDEP_TRUE" && test -f "$am_depcomp"; then # We make a subdir and do the tests there. Otherwise we can end up # making bogus files that we don't know about and never remove. For # instance it was reported that on HP-UX the gcc test will end up # making a dummy file named 'D' -- because '-MD' means "put the output # in D". rm -rf conftest.dir mkdir conftest.dir # Copy depcomp to subdir because otherwise we won't find it if we're # using a relative directory. cp "$am_depcomp" conftest.dir cd conftest.dir # We will build objects and dependencies in a subdirectory because # it helps to detect inapplicable dependency modes. For instance # both Tru64's cc and ICC support -MD to output dependencies as a # side effect of compilation, but ICC will put the dependencies in # the current directory while Tru64 will put them in the object # directory. mkdir sub am_cv_$1_dependencies_compiler_type=none if test "$am_compiler_list" = ""; then am_compiler_list=`sed -n ['s/^#*\([a-zA-Z0-9]*\))$/\1/p'] < ./depcomp` fi am__universal=false m4_case([$1], [CC], [case " $depcc " in #( *\ -arch\ *\ -arch\ *) am__universal=true ;; esac], [CXX], [case " $depcc " in #( *\ -arch\ *\ -arch\ *) am__universal=true ;; esac]) for depmode in $am_compiler_list; do # Setup a source with many dependencies, because some compilers # like to wrap large dependency lists on column 80 (with \), and # we should not choose a depcomp mode which is confused by this. # # We need to recreate these files for each test, as the compiler may # overwrite some of them when testing with obscure command lines. # This happens at least with the AIX C compiler. : > sub/conftest.c for i in 1 2 3 4 5 6; do echo '#include "conftst'$i'.h"' >> sub/conftest.c # Using ": > sub/conftst$i.h" creates only sub/conftst1.h with # Solaris 10 /bin/sh. echo '/* dummy */' > sub/conftst$i.h done echo "${am__include} ${am__quote}sub/conftest.Po${am__quote}" > confmf # We check with '-c' and '-o' for the sake of the "dashmstdout" # mode. It turns out that the SunPro C++ compiler does not properly # handle '-M -o', and we need to detect this. Also, some Intel # versions had trouble with output in subdirs. am__obj=sub/conftest.${OBJEXT-o} am__minus_obj="-o $am__obj" case $depmode in gcc) # This depmode causes a compiler race in universal mode. test "$am__universal" = false || continue ;; nosideeffect) # After this tag, mechanisms are not by side-effect, so they'll # only be used when explicitly requested. if test "x$enable_dependency_tracking" = xyes; then continue else break fi ;; msvc7 | msvc7msys | msvisualcpp | msvcmsys) # This compiler won't grok '-c -o', but also, the minuso test has # not run yet. These depmodes are late enough in the game, and # so weak that their functioning should not be impacted. am__obj=conftest.${OBJEXT-o} am__minus_obj= ;; none) break ;; esac if depmode=$depmode \ source=sub/conftest.c object=$am__obj \ depfile=sub/conftest.Po tmpdepfile=sub/conftest.TPo \ $SHELL ./depcomp $depcc -c $am__minus_obj sub/conftest.c \ >/dev/null 2>conftest.err && grep sub/conftst1.h sub/conftest.Po > /dev/null 2>&1 && grep sub/conftst6.h sub/conftest.Po > /dev/null 2>&1 && grep $am__obj sub/conftest.Po > /dev/null 2>&1 && ${MAKE-make} -s -f confmf > /dev/null 2>&1; then # icc doesn't choke on unknown options, it will just issue warnings # or remarks (even with -Werror). So we grep stderr for any message # that says an option was ignored or not supported. # When given -MP, icc 7.0 and 7.1 complain thusly: # icc: Command line warning: ignoring option '-M'; no argument required # The diagnosis changed in icc 8.0: # icc: Command line remark: option '-MP' not supported if (grep 'ignoring option' conftest.err || grep 'not supported' conftest.err) >/dev/null 2>&1; then :; else am_cv_$1_dependencies_compiler_type=$depmode break fi fi done cd .. rm -rf conftest.dir else am_cv_$1_dependencies_compiler_type=none fi ]) AC_SUBST([$1DEPMODE], [depmode=$am_cv_$1_dependencies_compiler_type]) AM_CONDITIONAL([am__fastdep$1], [ test "x$enable_dependency_tracking" != xno \ && test "$am_cv_$1_dependencies_compiler_type" = gcc3]) ]) # AM_SET_DEPDIR # ------------- # Choose a directory name for dependency files. # This macro is AC_REQUIREd in _AM_DEPENDENCIES. AC_DEFUN([AM_SET_DEPDIR], [AC_REQUIRE([AM_SET_LEADING_DOT])dnl AC_SUBST([DEPDIR], ["${am__leading_dot}deps"])dnl ]) # AM_DEP_TRACK # ------------ AC_DEFUN([AM_DEP_TRACK], [AC_ARG_ENABLE([dependency-tracking], [dnl AS_HELP_STRING( [--enable-dependency-tracking], [do not reject slow dependency extractors]) AS_HELP_STRING( [--disable-dependency-tracking], [speeds up one-time build])]) if test "x$enable_dependency_tracking" != xno; then am_depcomp="$ac_aux_dir/depcomp" AMDEPBACKSLASH='\' am__nodep='_no' fi AM_CONDITIONAL([AMDEP], [test "x$enable_dependency_tracking" != xno]) AC_SUBST([AMDEPBACKSLASH])dnl _AM_SUBST_NOTMAKE([AMDEPBACKSLASH])dnl AC_SUBST([am__nodep])dnl _AM_SUBST_NOTMAKE([am__nodep])dnl ]) # Generate code to set up dependency tracking. -*- Autoconf -*- # Copyright (C) 1999-2013 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # _AM_OUTPUT_DEPENDENCY_COMMANDS # ------------------------------ AC_DEFUN([_AM_OUTPUT_DEPENDENCY_COMMANDS], [{ # Older Autoconf quotes --file arguments for eval, but not when files # are listed without --file. Let's play safe and only enable the eval # if we detect the quoting. case $CONFIG_FILES in *\'*) eval set x "$CONFIG_FILES" ;; *) set x $CONFIG_FILES ;; esac shift for mf do # Strip MF so we end up with the name of the file. mf=`echo "$mf" | sed -e 's/:.*$//'` # Check whether this is an Automake generated Makefile or not. # We used to match only the files named 'Makefile.in', but # some people rename them; so instead we look at the file content. # Grep'ing the first line is not enough: some people post-process # each Makefile.in and add a new line on top of each file to say so. # Grep'ing the whole file is not good either: AIX grep has a line # limit of 2048, but all sed's we know have understand at least 4000. if sed -n 's,^#.*generated by automake.*,X,p' "$mf" | grep X >/dev/null 2>&1; then dirpart=`AS_DIRNAME("$mf")` else continue fi # Extract the definition of DEPDIR, am__include, and am__quote # from the Makefile without running 'make'. DEPDIR=`sed -n 's/^DEPDIR = //p' < "$mf"` test -z "$DEPDIR" && continue am__include=`sed -n 's/^am__include = //p' < "$mf"` test -z "$am__include" && continue am__quote=`sed -n 's/^am__quote = //p' < "$mf"` # Find all dependency output files, they are included files with # $(DEPDIR) in their names. We invoke sed twice because it is the # simplest approach to changing $(DEPDIR) to its actual value in the # expansion. for file in `sed -n " s/^$am__include $am__quote\(.*(DEPDIR).*\)$am__quote"'$/\1/p' <"$mf" | \ sed -e 's/\$(DEPDIR)/'"$DEPDIR"'/g'`; do # Make sure the directory exists. test -f "$dirpart/$file" && continue fdir=`AS_DIRNAME(["$file"])` AS_MKDIR_P([$dirpart/$fdir]) # echo "creating $dirpart/$file" echo '# dummy' > "$dirpart/$file" done done } ])# _AM_OUTPUT_DEPENDENCY_COMMANDS # AM_OUTPUT_DEPENDENCY_COMMANDS # ----------------------------- # This macro should only be invoked once -- use via AC_REQUIRE. # # This code is only required when automatic dependency tracking # is enabled. FIXME. This creates each '.P' file that we will # need in order to bootstrap the dependency handling code. AC_DEFUN([AM_OUTPUT_DEPENDENCY_COMMANDS], [AC_CONFIG_COMMANDS([depfiles], [test x"$AMDEP_TRUE" != x"" || _AM_OUTPUT_DEPENDENCY_COMMANDS], [AMDEP_TRUE="$AMDEP_TRUE" ac_aux_dir="$ac_aux_dir"]) ]) # Do all the work for Automake. -*- Autoconf -*- # Copyright (C) 1996-2013 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This macro actually does too much. Some checks are only needed if # your package does certain things. But this isn't really a big deal. dnl Redefine AC_PROG_CC to automatically invoke _AM_PROG_CC_C_O. m4_define([AC_PROG_CC], m4_defn([AC_PROG_CC]) [_AM_PROG_CC_C_O ]) # AM_INIT_AUTOMAKE(PACKAGE, VERSION, [NO-DEFINE]) # AM_INIT_AUTOMAKE([OPTIONS]) # ----------------------------------------------- # The call with PACKAGE and VERSION arguments is the old style # call (pre autoconf-2.50), which is being phased out. PACKAGE # and VERSION should now be passed to AC_INIT and removed from # the call to AM_INIT_AUTOMAKE. # We support both call styles for the transition. After # the next Automake release, Autoconf can make the AC_INIT # arguments mandatory, and then we can depend on a new Autoconf # release and drop the old call support. AC_DEFUN([AM_INIT_AUTOMAKE], [AC_PREREQ([2.65])dnl dnl Autoconf wants to disallow AM_ names. We explicitly allow dnl the ones we care about. m4_pattern_allow([^AM_[A-Z]+FLAGS$])dnl AC_REQUIRE([AM_SET_CURRENT_AUTOMAKE_VERSION])dnl AC_REQUIRE([AC_PROG_INSTALL])dnl if test "`cd $srcdir && pwd`" != "`pwd`"; then # Use -I$(srcdir) only when $(srcdir) != ., so that make's output # is not polluted with repeated "-I." AC_SUBST([am__isrc], [' -I$(srcdir)'])_AM_SUBST_NOTMAKE([am__isrc])dnl # test to see if srcdir already configured if test -f $srcdir/config.status; then AC_MSG_ERROR([source directory already configured; run "make distclean" there first]) fi fi # test whether we have cygpath if test -z "$CYGPATH_W"; then if (cygpath --version) >/dev/null 2>/dev/null; then CYGPATH_W='cygpath -w' else CYGPATH_W=echo fi fi AC_SUBST([CYGPATH_W]) # Define the identity of the package. dnl Distinguish between old-style and new-style calls. m4_ifval([$2], [AC_DIAGNOSE([obsolete], [$0: two- and three-arguments forms are deprecated.]) m4_ifval([$3], [_AM_SET_OPTION([no-define])])dnl AC_SUBST([PACKAGE], [$1])dnl AC_SUBST([VERSION], [$2])], [_AM_SET_OPTIONS([$1])dnl dnl Diagnose old-style AC_INIT with new-style AM_AUTOMAKE_INIT. m4_if( m4_ifdef([AC_PACKAGE_NAME], [ok]):m4_ifdef([AC_PACKAGE_VERSION], [ok]), [ok:ok],, [m4_fatal([AC_INIT should be called with package and version arguments])])dnl AC_SUBST([PACKAGE], ['AC_PACKAGE_TARNAME'])dnl AC_SUBST([VERSION], ['AC_PACKAGE_VERSION'])])dnl _AM_IF_OPTION([no-define],, [AC_DEFINE_UNQUOTED([PACKAGE], ["$PACKAGE"], [Name of package]) AC_DEFINE_UNQUOTED([VERSION], ["$VERSION"], [Version number of package])])dnl # Some tools Automake needs. AC_REQUIRE([AM_SANITY_CHECK])dnl AC_REQUIRE([AC_ARG_PROGRAM])dnl AM_MISSING_PROG([ACLOCAL], [aclocal-${am__api_version}]) AM_MISSING_PROG([AUTOCONF], [autoconf]) AM_MISSING_PROG([AUTOMAKE], [automake-${am__api_version}]) AM_MISSING_PROG([AUTOHEADER], [autoheader]) AM_MISSING_PROG([MAKEINFO], [makeinfo]) AC_REQUIRE([AM_PROG_INSTALL_SH])dnl AC_REQUIRE([AM_PROG_INSTALL_STRIP])dnl AC_REQUIRE([AC_PROG_MKDIR_P])dnl # For better backward compatibility. To be removed once Automake 1.9.x # dies out for good. For more background, see: # # AC_SUBST([mkdir_p], ['$(MKDIR_P)']) # We need awk for the "check" target. The system "awk" is bad on # some platforms. AC_REQUIRE([AC_PROG_AWK])dnl AC_REQUIRE([AC_PROG_MAKE_SET])dnl AC_REQUIRE([AM_SET_LEADING_DOT])dnl _AM_IF_OPTION([tar-ustar], [_AM_PROG_TAR([ustar])], [_AM_IF_OPTION([tar-pax], [_AM_PROG_TAR([pax])], [_AM_PROG_TAR([v7])])]) _AM_IF_OPTION([no-dependencies],, [AC_PROVIDE_IFELSE([AC_PROG_CC], [_AM_DEPENDENCIES([CC])], [m4_define([AC_PROG_CC], m4_defn([AC_PROG_CC])[_AM_DEPENDENCIES([CC])])])dnl AC_PROVIDE_IFELSE([AC_PROG_CXX], [_AM_DEPENDENCIES([CXX])], [m4_define([AC_PROG_CXX], m4_defn([AC_PROG_CXX])[_AM_DEPENDENCIES([CXX])])])dnl AC_PROVIDE_IFELSE([AC_PROG_OBJC], [_AM_DEPENDENCIES([OBJC])], [m4_define([AC_PROG_OBJC], m4_defn([AC_PROG_OBJC])[_AM_DEPENDENCIES([OBJC])])])dnl AC_PROVIDE_IFELSE([AC_PROG_OBJCXX], [_AM_DEPENDENCIES([OBJCXX])], [m4_define([AC_PROG_OBJCXX], m4_defn([AC_PROG_OBJCXX])[_AM_DEPENDENCIES([OBJCXX])])])dnl ]) AC_REQUIRE([AM_SILENT_RULES])dnl dnl The testsuite driver may need to know about EXEEXT, so add the dnl 'am__EXEEXT' conditional if _AM_COMPILER_EXEEXT was seen. This dnl macro is hooked onto _AC_COMPILER_EXEEXT early, see below. AC_CONFIG_COMMANDS_PRE(dnl [m4_provide_if([_AM_COMPILER_EXEEXT], [AM_CONDITIONAL([am__EXEEXT], [test -n "$EXEEXT"])])])dnl # POSIX will say in a future version that running "rm -f" with no argument # is OK; and we want to be able to make that assumption in our Makefile # recipes. So use an aggressive probe to check that the usage we want is # actually supported "in the wild" to an acceptable degree. # See automake bug#10828. # To make any issue more visible, cause the running configure to be aborted # by default if the 'rm' program in use doesn't match our expectations; the # user can still override this though. if rm -f && rm -fr && rm -rf; then : OK; else cat >&2 <<'END' Oops! Your 'rm' program seems unable to run without file operands specified on the command line, even when the '-f' option is present. This is contrary to the behaviour of most rm programs out there, and not conforming with the upcoming POSIX standard: Please tell bug-automake@gnu.org about your system, including the value of your $PATH and any error possibly output before this message. This can help us improve future automake versions. END if test x"$ACCEPT_INFERIOR_RM_PROGRAM" = x"yes"; then echo 'Configuration will proceed anyway, since you have set the' >&2 echo 'ACCEPT_INFERIOR_RM_PROGRAM variable to "yes"' >&2 echo >&2 else cat >&2 <<'END' Aborting the configuration process, to ensure you take notice of the issue. You can download and install GNU coreutils to get an 'rm' implementation that behaves properly: . If you want to complete the configuration process using your problematic 'rm' anyway, export the environment variable ACCEPT_INFERIOR_RM_PROGRAM to "yes", and re-run configure. END AC_MSG_ERROR([Your 'rm' program is bad, sorry.]) fi fi ]) dnl Hook into '_AC_COMPILER_EXEEXT' early to learn its expansion. Do not dnl add the conditional right here, as _AC_COMPILER_EXEEXT may be further dnl mangled by Autoconf and run in a shell conditional statement. m4_define([_AC_COMPILER_EXEEXT], m4_defn([_AC_COMPILER_EXEEXT])[m4_provide([_AM_COMPILER_EXEEXT])]) # When config.status generates a header, we must update the stamp-h file. # This file resides in the same directory as the config header # that is generated. The stamp files are numbered to have different names. # Autoconf calls _AC_AM_CONFIG_HEADER_HOOK (when defined) in the # loop where config.status creates the headers, so we can generate # our stamp files there. AC_DEFUN([_AC_AM_CONFIG_HEADER_HOOK], [# Compute $1's index in $config_headers. _am_arg=$1 _am_stamp_count=1 for _am_header in $config_headers :; do case $_am_header in $_am_arg | $_am_arg:* ) break ;; * ) _am_stamp_count=`expr $_am_stamp_count + 1` ;; esac done echo "timestamp for $_am_arg" >`AS_DIRNAME(["$_am_arg"])`/stamp-h[]$_am_stamp_count]) # Copyright (C) 2001-2013 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # AM_PROG_INSTALL_SH # ------------------ # Define $install_sh. AC_DEFUN([AM_PROG_INSTALL_SH], [AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl if test x"${install_sh}" != xset; then case $am_aux_dir in *\ * | *\ *) install_sh="\${SHELL} '$am_aux_dir/install-sh'" ;; *) install_sh="\${SHELL} $am_aux_dir/install-sh" esac fi AC_SUBST([install_sh])]) # Copyright (C) 2003-2013 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # Check whether the underlying file-system supports filenames # with a leading dot. For instance MS-DOS doesn't. AC_DEFUN([AM_SET_LEADING_DOT], [rm -rf .tst 2>/dev/null mkdir .tst 2>/dev/null if test -d .tst; then am__leading_dot=. else am__leading_dot=_ fi rmdir .tst 2>/dev/null AC_SUBST([am__leading_dot])]) # Check to see how 'make' treats includes. -*- Autoconf -*- # Copyright (C) 2001-2013 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # AM_MAKE_INCLUDE() # ----------------- # Check to see how make treats includes. AC_DEFUN([AM_MAKE_INCLUDE], [am_make=${MAKE-make} cat > confinc << 'END' am__doit: @echo this is the am__doit target .PHONY: am__doit END # If we don't find an include directive, just comment out the code. AC_MSG_CHECKING([for style of include used by $am_make]) am__include="#" am__quote= _am_result=none # First try GNU make style include. echo "include confinc" > confmf # Ignore all kinds of additional output from 'make'. case `$am_make -s -f confmf 2> /dev/null` in #( *the\ am__doit\ target*) am__include=include am__quote= _am_result=GNU ;; esac # Now try BSD make style include. if test "$am__include" = "#"; then echo '.include "confinc"' > confmf case `$am_make -s -f confmf 2> /dev/null` in #( *the\ am__doit\ target*) am__include=.include am__quote="\"" _am_result=BSD ;; esac fi AC_SUBST([am__include]) AC_SUBST([am__quote]) AC_MSG_RESULT([$_am_result]) rm -f confinc confmf ]) # Fake the existence of programs that GNU maintainers use. -*- Autoconf -*- # Copyright (C) 1997-2013 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # AM_MISSING_PROG(NAME, PROGRAM) # ------------------------------ AC_DEFUN([AM_MISSING_PROG], [AC_REQUIRE([AM_MISSING_HAS_RUN]) $1=${$1-"${am_missing_run}$2"} AC_SUBST($1)]) # AM_MISSING_HAS_RUN # ------------------ # Define MISSING if not defined so far and test if it is modern enough. # If it is, set am_missing_run to use it, otherwise, to nothing. AC_DEFUN([AM_MISSING_HAS_RUN], [AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl AC_REQUIRE_AUX_FILE([missing])dnl if test x"${MISSING+set}" != xset; then case $am_aux_dir in *\ * | *\ *) MISSING="\${SHELL} \"$am_aux_dir/missing\"" ;; *) MISSING="\${SHELL} $am_aux_dir/missing" ;; esac fi # Use eval to expand $SHELL if eval "$MISSING --is-lightweight"; then am_missing_run="$MISSING " else am_missing_run= AC_MSG_WARN(['missing' script is too old or missing]) fi ]) # -*- Autoconf -*- # Obsolete and "removed" macros, that must however still report explicit # error messages when used, to smooth transition. # # Copyright (C) 1996-2013 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. AC_DEFUN([AM_CONFIG_HEADER], [AC_DIAGNOSE([obsolete], ['$0': this macro is obsolete. You should use the 'AC][_CONFIG_HEADERS' macro instead.])dnl AC_CONFIG_HEADERS($@)]) AC_DEFUN([AM_PROG_CC_STDC], [AC_PROG_CC am_cv_prog_cc_stdc=$ac_cv_prog_cc_stdc AC_DIAGNOSE([obsolete], ['$0': this macro is obsolete. You should simply use the 'AC][_PROG_CC' macro instead. Also, your code should no longer depend upon 'am_cv_prog_cc_stdc', but upon 'ac_cv_prog_cc_stdc'.])]) AC_DEFUN([AM_C_PROTOTYPES], [AC_FATAL([automatic de-ANSI-fication support has been removed])]) AU_DEFUN([fp_C_PROTOTYPES], [AM_C_PROTOTYPES]) # Helper functions for option handling. -*- Autoconf -*- # Copyright (C) 2001-2013 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # _AM_MANGLE_OPTION(NAME) # ----------------------- AC_DEFUN([_AM_MANGLE_OPTION], [[_AM_OPTION_]m4_bpatsubst($1, [[^a-zA-Z0-9_]], [_])]) # _AM_SET_OPTION(NAME) # -------------------- # Set option NAME. Presently that only means defining a flag for this option. AC_DEFUN([_AM_SET_OPTION], [m4_define(_AM_MANGLE_OPTION([$1]), [1])]) # _AM_SET_OPTIONS(OPTIONS) # ------------------------ # OPTIONS is a space-separated list of Automake options. AC_DEFUN([_AM_SET_OPTIONS], [m4_foreach_w([_AM_Option], [$1], [_AM_SET_OPTION(_AM_Option)])]) # _AM_IF_OPTION(OPTION, IF-SET, [IF-NOT-SET]) # ------------------------------------------- # Execute IF-SET if OPTION is set, IF-NOT-SET otherwise. AC_DEFUN([_AM_IF_OPTION], [m4_ifset(_AM_MANGLE_OPTION([$1]), [$2], [$3])]) # Copyright (C) 1999-2013 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # _AM_PROG_CC_C_O # --------------- # Like AC_PROG_CC_C_O, but changed for automake. We rewrite AC_PROG_CC # to automatically call this. AC_DEFUN([_AM_PROG_CC_C_O], [AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl AC_REQUIRE_AUX_FILE([compile])dnl AC_LANG_PUSH([C])dnl AC_CACHE_CHECK( [whether $CC understands -c and -o together], [am_cv_prog_cc_c_o], [AC_LANG_CONFTEST([AC_LANG_PROGRAM([])]) # Make sure it works both with $CC and with simple cc. # Following AC_PROG_CC_C_O, we do the test twice because some # compilers refuse to overwrite an existing .o file with -o, # though they will create one. am_cv_prog_cc_c_o=yes for am_i in 1 2; do if AM_RUN_LOG([$CC -c conftest.$ac_ext -o conftest2.$ac_objext]) \ && test -f conftest2.$ac_objext; then : OK else am_cv_prog_cc_c_o=no break fi done rm -f core conftest* unset am_i]) if test "$am_cv_prog_cc_c_o" != yes; then # Losing compiler, so override with the script. # FIXME: It is wrong to rewrite CC. # But if we don't then we get into trouble of one sort or another. # A longer-term fix would be to have automake use am__CC in this case, # and then we could set am__CC="\$(top_srcdir)/compile \$(CC)" CC="$am_aux_dir/compile $CC" fi AC_LANG_POP([C])]) # For backward compatibility. AC_DEFUN_ONCE([AM_PROG_CC_C_O], [AC_REQUIRE([AC_PROG_CC])]) # Copyright (C) 1999-2013 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # AM_PATH_PYTHON([MINIMUM-VERSION], [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND]) # --------------------------------------------------------------------------- # Adds support for distributing Python modules and packages. To # install modules, copy them to $(pythondir), using the python_PYTHON # automake variable. To install a package with the same name as the # automake package, install to $(pkgpythondir), or use the # pkgpython_PYTHON automake variable. # # The variables $(pyexecdir) and $(pkgpyexecdir) are provided as # locations to install python extension modules (shared libraries). # Another macro is required to find the appropriate flags to compile # extension modules. # # If your package is configured with a different prefix to python, # users will have to add the install directory to the PYTHONPATH # environment variable, or create a .pth file (see the python # documentation for details). # # If the MINIMUM-VERSION argument is passed, AM_PATH_PYTHON will # cause an error if the version of python installed on the system # doesn't meet the requirement. MINIMUM-VERSION should consist of # numbers and dots only. AC_DEFUN([AM_PATH_PYTHON], [ dnl Find a Python interpreter. Python versions prior to 2.0 are not dnl supported. (2.0 was released on October 16, 2000). m4_define_default([_AM_PYTHON_INTERPRETER_LIST], [python python2 python3 python3.3 python3.2 python3.1 python3.0 python2.7 dnl python2.6 python2.5 python2.4 python2.3 python2.2 python2.1 python2.0]) AC_ARG_VAR([PYTHON], [the Python interpreter]) m4_if([$1],[],[ dnl No version check is needed. # Find any Python interpreter. if test -z "$PYTHON"; then AC_PATH_PROGS([PYTHON], _AM_PYTHON_INTERPRETER_LIST, :) fi am_display_PYTHON=python ], [ dnl A version check is needed. if test -n "$PYTHON"; then # If the user set $PYTHON, use it and don't search something else. AC_MSG_CHECKING([whether $PYTHON version is >= $1]) AM_PYTHON_CHECK_VERSION([$PYTHON], [$1], [AC_MSG_RESULT([yes])], [AC_MSG_RESULT([no]) AC_MSG_ERROR([Python interpreter is too old])]) am_display_PYTHON=$PYTHON else # Otherwise, try each interpreter until we find one that satisfies # VERSION. AC_CACHE_CHECK([for a Python interpreter with version >= $1], [am_cv_pathless_PYTHON],[ for am_cv_pathless_PYTHON in _AM_PYTHON_INTERPRETER_LIST none; do test "$am_cv_pathless_PYTHON" = none && break AM_PYTHON_CHECK_VERSION([$am_cv_pathless_PYTHON], [$1], [break]) done]) # Set $PYTHON to the absolute path of $am_cv_pathless_PYTHON. if test "$am_cv_pathless_PYTHON" = none; then PYTHON=: else AC_PATH_PROG([PYTHON], [$am_cv_pathless_PYTHON]) fi am_display_PYTHON=$am_cv_pathless_PYTHON fi ]) if test "$PYTHON" = :; then dnl Run any user-specified action, or abort. m4_default([$3], [AC_MSG_ERROR([no suitable Python interpreter found])]) else dnl Query Python for its version number. Getting [:3] seems to be dnl the best way to do this; it's what "site.py" does in the standard dnl library. AC_CACHE_CHECK([for $am_display_PYTHON version], [am_cv_python_version], [am_cv_python_version=`$PYTHON -c "import sys; sys.stdout.write(sys.version[[:3]])"`]) AC_SUBST([PYTHON_VERSION], [$am_cv_python_version]) dnl Use the values of $prefix and $exec_prefix for the corresponding dnl values of PYTHON_PREFIX and PYTHON_EXEC_PREFIX. These are made dnl distinct variables so they can be overridden if need be. However, dnl general consensus is that you shouldn't need this ability. AC_SUBST([PYTHON_PREFIX], ['${prefix}']) AC_SUBST([PYTHON_EXEC_PREFIX], ['${exec_prefix}']) dnl At times (like when building shared libraries) you may want dnl to know which OS platform Python thinks this is. AC_CACHE_CHECK([for $am_display_PYTHON platform], [am_cv_python_platform], [am_cv_python_platform=`$PYTHON -c "import sys; sys.stdout.write(sys.platform)"`]) AC_SUBST([PYTHON_PLATFORM], [$am_cv_python_platform]) # Just factor out some code duplication. am_python_setup_sysconfig="\ import sys # Prefer sysconfig over distutils.sysconfig, for better compatibility # with python 3.x. See automake bug#10227. try: import sysconfig except ImportError: can_use_sysconfig = 0 else: can_use_sysconfig = 1 # Can't use sysconfig in CPython 2.7, since it's broken in virtualenvs: # try: from platform import python_implementation if python_implementation() == 'CPython' and sys.version[[:3]] == '2.7': can_use_sysconfig = 0 except ImportError: pass" dnl Set up 4 directories: dnl pythondir -- where to install python scripts. This is the dnl site-packages directory, not the python standard library dnl directory like in previous automake betas. This behavior dnl is more consistent with lispdir.m4 for example. dnl Query distutils for this directory. AC_CACHE_CHECK([for $am_display_PYTHON script directory], [am_cv_python_pythondir], [if test "x$prefix" = xNONE then am_py_prefix=$ac_default_prefix else am_py_prefix=$prefix fi am_cv_python_pythondir=`$PYTHON -c " $am_python_setup_sysconfig if can_use_sysconfig: sitedir = sysconfig.get_path('purelib', vars={'base':'$am_py_prefix'}) else: from distutils import sysconfig sitedir = sysconfig.get_python_lib(0, 0, prefix='$am_py_prefix') sys.stdout.write(sitedir)"` case $am_cv_python_pythondir in $am_py_prefix*) am__strip_prefix=`echo "$am_py_prefix" | sed 's|.|.|g'` am_cv_python_pythondir=`echo "$am_cv_python_pythondir" | sed "s,^$am__strip_prefix,$PYTHON_PREFIX,"` ;; *) case $am_py_prefix in /usr|/System*) ;; *) am_cv_python_pythondir=$PYTHON_PREFIX/lib/python$PYTHON_VERSION/site-packages ;; esac ;; esac ]) AC_SUBST([pythondir], [$am_cv_python_pythondir]) dnl pkgpythondir -- $PACKAGE directory under pythondir. Was dnl PYTHON_SITE_PACKAGE in previous betas, but this naming is dnl more consistent with the rest of automake. AC_SUBST([pkgpythondir], [\${pythondir}/$PACKAGE]) dnl pyexecdir -- directory for installing python extension modules dnl (shared libraries) dnl Query distutils for this directory. AC_CACHE_CHECK([for $am_display_PYTHON extension module directory], [am_cv_python_pyexecdir], [if test "x$exec_prefix" = xNONE then am_py_exec_prefix=$am_py_prefix else am_py_exec_prefix=$exec_prefix fi am_cv_python_pyexecdir=`$PYTHON -c " $am_python_setup_sysconfig if can_use_sysconfig: sitedir = sysconfig.get_path('platlib', vars={'platbase':'$am_py_prefix'}) else: from distutils import sysconfig sitedir = sysconfig.get_python_lib(1, 0, prefix='$am_py_prefix') sys.stdout.write(sitedir)"` case $am_cv_python_pyexecdir in $am_py_exec_prefix*) am__strip_prefix=`echo "$am_py_exec_prefix" | sed 's|.|.|g'` am_cv_python_pyexecdir=`echo "$am_cv_python_pyexecdir" | sed "s,^$am__strip_prefix,$PYTHON_EXEC_PREFIX,"` ;; *) case $am_py_exec_prefix in /usr|/System*) ;; *) am_cv_python_pyexecdir=$PYTHON_EXEC_PREFIX/lib/python$PYTHON_VERSION/site-packages ;; esac ;; esac ]) AC_SUBST([pyexecdir], [$am_cv_python_pyexecdir]) dnl pkgpyexecdir -- $(pyexecdir)/$(PACKAGE) AC_SUBST([pkgpyexecdir], [\${pyexecdir}/$PACKAGE]) dnl Run any user-specified action. $2 fi ]) # AM_PYTHON_CHECK_VERSION(PROG, VERSION, [ACTION-IF-TRUE], [ACTION-IF-FALSE]) # --------------------------------------------------------------------------- # Run ACTION-IF-TRUE if the Python interpreter PROG has version >= VERSION. # Run ACTION-IF-FALSE otherwise. # This test uses sys.hexversion instead of the string equivalent (first # word of sys.version), in order to cope with versions such as 2.2c1. # This supports Python 2.0 or higher. (2.0 was released on October 16, 2000). AC_DEFUN([AM_PYTHON_CHECK_VERSION], [prog="import sys # split strings by '.' and convert to numeric. Append some zeros # because we need at least 4 digits for the hex conversion. # map returns an iterator in Python 3.0 and a list in 2.x minver = list(map(int, '$2'.split('.'))) + [[0, 0, 0]] minverhex = 0 # xrange is not present in Python 3.0 and range returns an iterator for i in list(range(0, 4)): minverhex = (minverhex << 8) + minver[[i]] sys.exit(sys.hexversion < minverhex)" AS_IF([AM_RUN_LOG([$1 -c "$prog"])], [$3], [$4])]) # Copyright (C) 2001-2013 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # AM_RUN_LOG(COMMAND) # ------------------- # Run COMMAND, save the exit status in ac_status, and log it. # (This has been adapted from Autoconf's _AC_RUN_LOG macro.) AC_DEFUN([AM_RUN_LOG], [{ echo "$as_me:$LINENO: $1" >&AS_MESSAGE_LOG_FD ($1) >&AS_MESSAGE_LOG_FD 2>&AS_MESSAGE_LOG_FD ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&AS_MESSAGE_LOG_FD (exit $ac_status); }]) # Check to make sure that the build environment is sane. -*- Autoconf -*- # Copyright (C) 1996-2013 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # AM_SANITY_CHECK # --------------- AC_DEFUN([AM_SANITY_CHECK], [AC_MSG_CHECKING([whether build environment is sane]) # Reject unsafe characters in $srcdir or the absolute working directory # name. Accept space and tab only in the latter. am_lf=' ' case `pwd` in *[[\\\"\#\$\&\'\`$am_lf]]*) AC_MSG_ERROR([unsafe absolute working directory name]);; esac case $srcdir in *[[\\\"\#\$\&\'\`$am_lf\ \ ]]*) AC_MSG_ERROR([unsafe srcdir value: '$srcdir']);; esac # Do 'set' in a subshell so we don't clobber the current shell's # arguments. Must try -L first in case configure is actually a # symlink; some systems play weird games with the mod time of symlinks # (eg FreeBSD returns the mod time of the symlink's containing # directory). if ( am_has_slept=no for am_try in 1 2; do echo "timestamp, slept: $am_has_slept" > conftest.file set X `ls -Lt "$srcdir/configure" conftest.file 2> /dev/null` if test "$[*]" = "X"; then # -L didn't work. set X `ls -t "$srcdir/configure" conftest.file` fi if test "$[*]" != "X $srcdir/configure conftest.file" \ && test "$[*]" != "X conftest.file $srcdir/configure"; then # If neither matched, then we have a broken ls. This can happen # if, for instance, CONFIG_SHELL is bash and it inherits a # broken ls alias from the environment. This has actually # happened. Such a system could not be considered "sane". AC_MSG_ERROR([ls -t appears to fail. Make sure there is not a broken alias in your environment]) fi if test "$[2]" = conftest.file || test $am_try -eq 2; then break fi # Just in case. sleep 1 am_has_slept=yes done test "$[2]" = conftest.file ) then # Ok. : else AC_MSG_ERROR([newly created file is older than distributed files! Check your system clock]) fi AC_MSG_RESULT([yes]) # If we didn't sleep, we still need to ensure time stamps of config.status and # generated files are strictly newer. am_sleep_pid= if grep 'slept: no' conftest.file >/dev/null 2>&1; then ( sleep 1 ) & am_sleep_pid=$! fi AC_CONFIG_COMMANDS_PRE( [AC_MSG_CHECKING([that generated files are newer than configure]) if test -n "$am_sleep_pid"; then # Hide warnings about reused PIDs. wait $am_sleep_pid 2>/dev/null fi AC_MSG_RESULT([done])]) rm -f conftest.file ]) # Copyright (C) 2009-2013 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # AM_SILENT_RULES([DEFAULT]) # -------------------------- # Enable less verbose build rules; with the default set to DEFAULT # ("yes" being less verbose, "no" or empty being verbose). AC_DEFUN([AM_SILENT_RULES], [AC_ARG_ENABLE([silent-rules], [dnl AS_HELP_STRING( [--enable-silent-rules], [less verbose build output (undo: "make V=1")]) AS_HELP_STRING( [--disable-silent-rules], [verbose build output (undo: "make V=0")])dnl ]) case $enable_silent_rules in @%:@ ((( yes) AM_DEFAULT_VERBOSITY=0;; no) AM_DEFAULT_VERBOSITY=1;; *) AM_DEFAULT_VERBOSITY=m4_if([$1], [yes], [0], [1]);; esac dnl dnl A few 'make' implementations (e.g., NonStop OS and NextStep) dnl do not support nested variable expansions. dnl See automake bug#9928 and bug#10237. am_make=${MAKE-make} AC_CACHE_CHECK([whether $am_make supports nested variables], [am_cv_make_support_nested_variables], [if AS_ECHO([['TRUE=$(BAR$(V)) BAR0=false BAR1=true V=1 am__doit: @$(TRUE) .PHONY: am__doit']]) | $am_make -f - >/dev/null 2>&1; then am_cv_make_support_nested_variables=yes else am_cv_make_support_nested_variables=no fi]) if test $am_cv_make_support_nested_variables = yes; then dnl Using '$V' instead of '$(V)' breaks IRIX make. AM_V='$(V)' AM_DEFAULT_V='$(AM_DEFAULT_VERBOSITY)' else AM_V=$AM_DEFAULT_VERBOSITY AM_DEFAULT_V=$AM_DEFAULT_VERBOSITY fi AC_SUBST([AM_V])dnl AM_SUBST_NOTMAKE([AM_V])dnl AC_SUBST([AM_DEFAULT_V])dnl AM_SUBST_NOTMAKE([AM_DEFAULT_V])dnl AC_SUBST([AM_DEFAULT_VERBOSITY])dnl AM_BACKSLASH='\' AC_SUBST([AM_BACKSLASH])dnl _AM_SUBST_NOTMAKE([AM_BACKSLASH])dnl ]) # Copyright (C) 2001-2013 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # AM_PROG_INSTALL_STRIP # --------------------- # One issue with vendor 'install' (even GNU) is that you can't # specify the program used to strip binaries. This is especially # annoying in cross-compiling environments, where the build's strip # is unlikely to handle the host's binaries. # Fortunately install-sh will honor a STRIPPROG variable, so we # always use install-sh in "make install-strip", and initialize # STRIPPROG with the value of the STRIP variable (set by the user). AC_DEFUN([AM_PROG_INSTALL_STRIP], [AC_REQUIRE([AM_PROG_INSTALL_SH])dnl # Installed binaries are usually stripped using 'strip' when the user # run "make install-strip". However 'strip' might not be the right # tool to use in cross-compilation environments, therefore Automake # will honor the 'STRIP' environment variable to overrule this program. dnl Don't test for $cross_compiling = yes, because it might be 'maybe'. if test "$cross_compiling" != no; then AC_CHECK_TOOL([STRIP], [strip], :) fi INSTALL_STRIP_PROGRAM="\$(install_sh) -c -s" AC_SUBST([INSTALL_STRIP_PROGRAM])]) # Copyright (C) 2006-2013 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # _AM_SUBST_NOTMAKE(VARIABLE) # --------------------------- # Prevent Automake from outputting VARIABLE = @VARIABLE@ in Makefile.in. # This macro is traced by Automake. AC_DEFUN([_AM_SUBST_NOTMAKE]) # AM_SUBST_NOTMAKE(VARIABLE) # -------------------------- # Public sister of _AM_SUBST_NOTMAKE. AC_DEFUN([AM_SUBST_NOTMAKE], [_AM_SUBST_NOTMAKE($@)]) # Check how to create a tarball. -*- Autoconf -*- # Copyright (C) 2004-2013 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # _AM_PROG_TAR(FORMAT) # -------------------- # Check how to create a tarball in format FORMAT. # FORMAT should be one of 'v7', 'ustar', or 'pax'. # # Substitute a variable $(am__tar) that is a command # writing to stdout a FORMAT-tarball containing the directory # $tardir. # tardir=directory && $(am__tar) > result.tar # # Substitute a variable $(am__untar) that extract such # a tarball read from stdin. # $(am__untar) < result.tar # AC_DEFUN([_AM_PROG_TAR], [# Always define AMTAR for backward compatibility. Yes, it's still used # in the wild :-( We should find a proper way to deprecate it ... AC_SUBST([AMTAR], ['$${TAR-tar}']) # We'll loop over all known methods to create a tar archive until one works. _am_tools='gnutar m4_if([$1], [ustar], [plaintar]) pax cpio none' m4_if([$1], [v7], [am__tar='$${TAR-tar} chof - "$$tardir"' am__untar='$${TAR-tar} xf -'], [m4_case([$1], [ustar], [# The POSIX 1988 'ustar' format is defined with fixed-size fields. # There is notably a 21 bits limit for the UID and the GID. In fact, # the 'pax' utility can hang on bigger UID/GID (see automake bug#8343 # and bug#13588). am_max_uid=2097151 # 2^21 - 1 am_max_gid=$am_max_uid # The $UID and $GID variables are not portable, so we need to resort # to the POSIX-mandated id(1) utility. Errors in the 'id' calls # below are definitely unexpected, so allow the users to see them # (that is, avoid stderr redirection). am_uid=`id -u || echo unknown` am_gid=`id -g || echo unknown` AC_MSG_CHECKING([whether UID '$am_uid' is supported by ustar format]) if test $am_uid -le $am_max_uid; then AC_MSG_RESULT([yes]) else AC_MSG_RESULT([no]) _am_tools=none fi AC_MSG_CHECKING([whether GID '$am_gid' is supported by ustar format]) if test $am_gid -le $am_max_gid; then AC_MSG_RESULT([yes]) else AC_MSG_RESULT([no]) _am_tools=none fi], [pax], [], [m4_fatal([Unknown tar format])]) AC_MSG_CHECKING([how to create a $1 tar archive]) # Go ahead even if we have the value already cached. We do so because we # need to set the values for the 'am__tar' and 'am__untar' variables. _am_tools=${am_cv_prog_tar_$1-$_am_tools} for _am_tool in $_am_tools; do case $_am_tool in gnutar) for _am_tar in tar gnutar gtar; do AM_RUN_LOG([$_am_tar --version]) && break done am__tar="$_am_tar --format=m4_if([$1], [pax], [posix], [$1]) -chf - "'"$$tardir"' am__tar_="$_am_tar --format=m4_if([$1], [pax], [posix], [$1]) -chf - "'"$tardir"' am__untar="$_am_tar -xf -" ;; plaintar) # Must skip GNU tar: if it does not support --format= it doesn't create # ustar tarball either. (tar --version) >/dev/null 2>&1 && continue am__tar='tar chf - "$$tardir"' am__tar_='tar chf - "$tardir"' am__untar='tar xf -' ;; pax) am__tar='pax -L -x $1 -w "$$tardir"' am__tar_='pax -L -x $1 -w "$tardir"' am__untar='pax -r' ;; cpio) am__tar='find "$$tardir" -print | cpio -o -H $1 -L' am__tar_='find "$tardir" -print | cpio -o -H $1 -L' am__untar='cpio -i -H $1 -d' ;; none) am__tar=false am__tar_=false am__untar=false ;; esac # If the value was cached, stop now. We just wanted to have am__tar # and am__untar set. test -n "${am_cv_prog_tar_$1}" && break # tar/untar a dummy directory, and stop if the command works. rm -rf conftest.dir mkdir conftest.dir echo GrepMe > conftest.dir/file AM_RUN_LOG([tardir=conftest.dir && eval $am__tar_ >conftest.tar]) rm -rf conftest.dir if test -s conftest.tar; then AM_RUN_LOG([$am__untar /dev/null 2>&1 && break fi done rm -rf conftest.dir AC_CACHE_VAL([am_cv_prog_tar_$1], [am_cv_prog_tar_$1=$_am_tool]) AC_MSG_RESULT([$am_cv_prog_tar_$1])]) AC_SUBST([am__tar]) AC_SUBST([am__untar]) ]) # _AM_PROG_TAR m4_include([m4/ax_config_dir.m4]) m4_include([m4/compiler.m4]) m4_include([m4/libtool.m4]) m4_include([m4/ltoptions.m4]) m4_include([m4/ltsugar.m4]) m4_include([m4/ltversion.m4]) m4_include([m4/lt~obsolete.m4]) m4_include([m4/tp-compiler-flag.m4]) m4_include([m4/tp-compiler-warnings.m4]) telepathy-gabble-0.18.2/configure.ac0000644000175000017500000003525612312535614017320 0ustar00smcvsmcv00000000000000AC_PREREQ([2.59]) # Making releases: # set the new version number: # odd minor -> development series # even minor -> stable series # increment micro for each release within a series # set gabble_nano_version to 0. m4_define([gabble_major_version], [0]) m4_define([gabble_minor_version], [18]) m4_define([gabble_micro_version], [2]) m4_define([gabble_nano_version], [0]) # Some magic m4_define([gabble_base_version], [gabble_major_version.gabble_minor_version.gabble_micro_version]) m4_define([gabble_version], [m4_if(gabble_nano_version, 0, [gabble_base_version], [gabble_base_version].[gabble_nano_version])]) AC_INIT([Telepathy Gabble], [gabble_version], [https://bugs.freedesktop.org/enter_bug.cgi?product=Telepathy&component=gabble]) AC_CONFIG_MACRO_DIR([m4]) AM_INIT_AUTOMAKE([1.9 -Wno-portability tar-ustar]) AM_CONFIG_HEADER(config.h) AC_USE_SYSTEM_EXTENSIONS m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES]) dnl check for tools AC_PROG_CC AC_PROG_CC_STDC AC_PROG_INSTALL AC_PROG_LIBTOOL AC_PROG_MKDIR_P AC_CHECK_HEADERS_ONCE([ arpa/inet.h arpa/nameser.h fcntl.h ifaddrs.h netdb.h netinet/in.h sys/ioctl.h sys/un.h unistd.h ]) # on Darwin, these headers are interdependent, according to autoconf.info AC_CHECK_HEADERS([sys/socket.h], [], [], [ #include #include #include ]) AC_CHECK_HEADERS([net/if.h], [], [], [ #include #include #include #ifdef HAVE_SYS_SOCKET_H # include #endif ]) # Autoconf has a handy macro for this, since it tends to have dependencies AC_HEADER_RESOLV COMPILER_OPTIMISATIONS COMPILER_COVERAGE ifelse(gabble_nano_version, 0, [ official_release=yes ], [ official_release=no ]) AM_CONDITIONAL([OFFICIAL_RELEASE], [test "x$official_release" = xyes]) TP_COMPILER_WARNINGS([ERROR_CFLAGS], [test "x$official_release" = xno], [all \ extra \ declaration-after-statement \ shadow \ strict-prototypes \ missing-declarations \ missing-prototypes \ sign-compare \ nested-externs \ pointer-arith \ format-security \ init-self], [missing-field-initializers \ deprecated-declarations \ unused-parameter]) AC_SUBST([ERROR_CFLAGS]) #------------------------------------------------------------ # Detect Operating system based on $host #------------------------------------------------------------ AC_MSG_CHECKING([operating system]) case "$host" in *-*-*mingw*|*-*-*cygwin*) platform=win32 AC_MSG_RESULT($platform) ;; *) platform=unix AC_MSG_RESULT($platform) ;; esac AM_CONDITIONAL([WINDOWS], [test "$platform" = "win32"]) # ----------------------------------------------------------- # Make CA certificates path configurable # Stolen from GIO's TLS # ----------------------------------------------------------- AC_MSG_CHECKING([location of system Certificate Authority list]) AC_ARG_WITH(ca-certificates, [AC_HELP_STRING([--with-ca-certificates=@<:@path@:>@], [path to system Certificate Authority list])]) if test "$with_ca_certificates" = "no"; then AC_MSG_RESULT([disabled]) else if test -z "$with_ca_certificates"; then for f in /etc/pki/tls/certs/ca-bundle.crt \ /etc/ssl/certs/ca-certificates.crt; do if test -f "$f"; then with_ca_certificates="$f" fi done if test -z "$with_ca_certificates"; then AC_MSG_ERROR([could not find. Use --with-ca-certificates=path to set, or --without-ca-certificates to disable]) fi fi AC_MSG_RESULT($with_ca_certificates) AC_DEFINE_UNQUOTED([GTLS_SYSTEM_CA_CERTIFICATES], ["$with_ca_certificates"], [path to system Certificate Authority list]) fi if test -n "$with_ca_certificates"; then if ! test -f "$with_ca_certificates"; then AC_MSG_WARN([Specified certificate authority file '$with_ca_certificates' does not exist]) fi fi AC_ARG_ENABLE(debug, AC_HELP_STRING([--disable-debug],[compile without debug code]), enable_debug=$enableval, enable_debug=yes ) ifelse(gabble_nano_version, 0, [ # Gabble is version x.y.z - disable coding style checks by default AC_ARG_ENABLE(coding-style-checks, AC_HELP_STRING([--enable-coding-style-checks], [check coding style using grep]), [ENABLE_CODING_STYLE_CHECKS=$enableval], [ENABLE_CODING_STYLE_CHECKS=no] ) ], [ # Gabble is version x.y.z.1 - enable coding style checks by default AC_ARG_ENABLE(coding-style-checks, AC_HELP_STRING([--disable-coding-style-checks], [do not check coding style using grep]), [ENABLE_CODING_STYLE_CHECKS=$enableval], [ENABLE_CODING_STYLE_CHECKS=yes]) ]) if test x$enable_debug = xyes; then AC_DEFINE(ENABLE_DEBUG, [], [Enable debug code]) fi AM_CONDITIONAL([ENABLE_DEBUG], [test "x$enable_debug" = xyes]) AC_SUBST([ENABLE_CODING_STYLE_CHECKS]) AC_ARG_ENABLE([installed-tests], AC_HELP_STRING([--enable-installed-tests], [make tests installable]), [installed_tests=$enableval], [installed_tests=no]) if test x$installed_tests = xyes; then AC_DEFINE(ENABLE_INSTALLED_TESTS, [], [Make tests installable]) fi AM_CONDITIONAL([ENABLE_INSTALLED_TESTS], [test "x$installed_tests" = xyes]) gabbletestsdir=${libdir}/telepathy-gabble-tests AC_SUBST(gabbletestsdir) AC_ARG_ENABLE([is-a-phone], AC_HELP_STRING([--enable-is-a-phone], [advertise that we are a phone, not a PC]), [is_a_phone=$enableval], [is_a_phone=no]) if test x$is_a_phone = xyes; then AC_DEFINE(CLIENT_TYPE, ["phone"], [Client type from http://xmpp.org/registrar/disco-categories.html#client]) CLIENT_TYPE=phone else AC_DEFINE(CLIENT_TYPE, ["pc"], [Client type from http://xmpp.org/registrar/disco-categories.html#client]) CLIENT_TYPE=pc fi AC_SUBST(CLIENT_TYPE) # whether to assert when g_critical() is used AC_ARG_ENABLE([fatal-criticals], AC_HELP_STRING([--disable-fatal-criticals], [do not assert because of critical warnings]), [fatal_criticals=$enableval], [fatal_criticals=yes]) if test x$fatal_criticals = xyes; then AC_DEFINE(ENABLE_FATAL_CRITICALS, [], [Critical warnings will result in an assertion]) fi AM_CONDITIONAL([ENABLE_FATAL_CRITICALS], [test "x$fatal_criticals" = xyes]) dnl dummy check for gtk-doc AC_ARG_ENABLE(gtk-doc, AC_HELP_STRING([--enable-gtk-doc],[does not actually do anything]), enable_gtk_doc=yes, enable_gtk_doc=no) AM_CONDITIONAL([ENABLE_GTK_DOC], [test "x$enable_gtk_doc" = xyes]) dnl Check for Glib PKG_CHECK_MODULES(GLIB, [glib-2.0 >= 2.32, gobject-2.0, gthread-2.0, gio-2.0]) PKG_CHECK_MODULES(GMODULE, [gmodule-2.0 >= 2.32]) AC_DEFINE(GLIB_VERSION_MIN_REQUIRED, GLIB_VERSION_2_30, [Ignore post 2.30 deprecations]) AC_DEFINE(GLIB_VERSION_MAX_ALLOWED, GLIB_VERSION_2_32, [Prevent post 2.32 APIs]) AC_SUBST(GLIB_CFLAGS) AC_SUBST(GLIB_LIBS) GLIB_GENMARSHAL=`$PKG_CONFIG --variable=glib_genmarshal glib-2.0` AC_SUBST(GLIB_GENMARSHAL) # These must contain "exec" for automake to work right (install-exec, # not install-data). # # Private directory for Wocky and the gabble-plugins library if test "x$pluginexeclibdir" = x; then pluginexeclibdir='${libdir}/telepathy/gabble-0/lib' fi AC_ARG_VAR([pluginexeclibdir]) # The actual plugins if test "x$pluginexecdir" = x; then pluginexecdir='${libdir}/telepathy/gabble-0/plugins' fi AC_ARG_VAR([pluginexecdir]) AC_ARG_ENABLE(plugins, AC_HELP_STRING([--disable-plugins], [disable plugin loader]), [enable_plugins=$enableval], [enable_plugins=yes]) if test x$enable_plugins = xyes; then AC_DEFINE(ENABLE_PLUGINS, [], [Enable plugins]) PKG_CHECK_MODULES(GMODULE, [gmodule-2.0]) AC_ARG_ENABLE(plugin-api, AC_HELP_STRING([--enable-plugin-api], [install headers for third-party plugins (experimental)]), [ enable_plugin_api=$enableval wocky_install_headers_dir="${includedir}/telepathy-gabble-0" ], [enable_plugin_api=no]) fi AC_SUBST(GMODULE_CFLAGS) AC_SUBST(GMODULE_LIBS) AM_CONDITIONAL(ENABLE_PLUGINS, test x$enable_plugins = xyes) AC_SUBST(ENABLE_PLUGINS) AM_CONDITIONAL(ENABLE_PLUGIN_API, test x$enable_plugin_api = xyes) AC_ARG_ENABLE(channel-type-call, AC_HELP_STRING([--disable-channel-type-call], [disable support for the draft Channel.Type.Call]), [enable_channel_type_call=$enableval], [enable_channel_type_call=yes]) if test x$enable_channel_type_call = xyes; then AC_DEFINE(ENABLE_CHANNEL_TYPE_CALL, [], [Enable Channel.Type.Call]) fi AM_CONDITIONAL(ENABLE_CHANNEL_TYPE_CALL, test x$enable_channel_type_call = xyes) dnl Check for D-Bus PKG_CHECK_MODULES(DBUS, [dbus-1 >= 1.1.0, dbus-glib-1 >= 0.82]) AC_SUBST(DBUS_CFLAGS) AC_SUBST(DBUS_LIBS) AC_DEFINE(TP_SEAL_ENABLE, [], [Prevent to use sealed variables]) AC_DEFINE(TP_DISABLE_SINGLE_INCLUDE, [], [Disable single header include]) AC_DEFINE(TP_VERSION_MIN_REQUIRED, TP_VERSION_0_18, [Ignore post 0.18 deprecations]) AC_DEFINE(TP_VERSION_MAX_ALLOWED, TP_VERSION_0_20, [Prevent post 0.20 APIs]) PKG_CHECK_MODULES(TP_GLIB, [telepathy-glib >= 0.19.9]) AC_SUBST(TP_GLIB_CFLAGS) AC_SUBST(TP_GLIB_LIBS) dnl Check for code generation tools XSLTPROC= AC_CHECK_PROGS([XSLTPROC], [xsltproc]) if test -z "$XSLTPROC"; then AC_MSG_ERROR([xsltproc (from the libxslt source package) is required]) fi AM_PATH_PYTHON([2.5]) # Check for a Python >= 2.5 with Twisted, to run the tests AC_MSG_CHECKING([for Python with Twisted and XMPP protocol support]) if $PYTHON -c "import twisted.words.xish.domish, twisted.words.protocols.jabber, twisted.internet.reactor" >/dev/null 2>&1; then TEST_PYTHON="$PYTHON" else TEST_PYTHON=false fi AC_MSG_RESULT([$TEST_PYTHON]) AC_SUBST(TEST_PYTHON) AM_CONDITIONAL([WANT_TWISTED_TESTS], test false != "$TEST_PYTHON") # We have to run Wocky's configure *before* looking for it with PKG_CHECK_MODULES so wocky-uninstalled.pc has been generated # If you don't specify --prefix, it starts off as NONE. Autoconf # would normally do this defaulting for us later, but that's too # late to help Wocky. if test "x${prefix}" = "xNONE"; then prefix=/usr/local fi # We tell Wocky to install its headers alongside gabble's so that an actual # separate Wocky installation won't clash with them. This is a bit of a hack. # AX_CONFIG_DIR doesn't make it very easy to pass extra arguments to the # submodule's configure. prev_ac_configure_args=$ac_configure_args ac_configure_args="$ac_configure_args --with-installed-headers=${wocky_install_headers_dir} --enable-shared-suffix=${PACKAGE}-${VERSION} --libdir=${pluginexeclibdir}" if test "x$ENABLE_CODING_STYLE_CHECKS" = xyes ; then ac_configure_args="$ac_configure_args --enable-coding-style-checks" else ac_configure_args="$ac_configure_args --disable-coding-style-checks" fi if test "x$tp_werror" = xyes && test "x$official_release" = xno; then ac_configure_args="$ac_configure_args --enable-Werror" else ac_configure_args="$ac_configure_args --disable-Werror" fi dnl wocky prev_top_build_prefix=$ac_top_build_prefix AX_CONFIG_DIR([lib/ext/wocky]) ac_top_build_prefix=$prev_top_build_prefix ac_configure_args=$prev_ac_configure_args dnl Check if Android build AC_ARG_ENABLE(submodules, AS_HELP_STRING([--disable-submodules],[Use system versions of Wocky, rather than submodules]), build_submodules=$enableval, build_submodules=yes ) dnl Check for Wocky # re-enable once Wocky has been released as a lib if test x$build_submodules = xyes; then export PKG_CONFIG_PATH=$PKG_CONFIG_PATH:"$ac_top_build_prefix"lib/ext/wocky/wocky fi PKG_CHECK_MODULES(WOCKY, wocky >= 0.0.0) AC_SUBST(WOCKY_CFLAGS) AC_SUBST(WOCKY_LIBS) AC_ARG_ENABLE(google-relay, AC_HELP_STRING([--disable-google-relay], [disable Google Jingle relay support]), [enable_google_relay=$enableval], [enable_google_relay=yes]) if test x$enable_google_relay = xyes; then AC_DEFINE(ENABLE_GOOGLE_RELAY, [], [Enable Google Jingle relay support]) dnl Check for libsoup PKG_CHECK_MODULES(SOUP, libsoup-2.4) else SOUP_CFLAGS= SOUP_LIBS= fi AC_SUBST(SOUP_CFLAGS) AC_SUBST(SOUP_LIBS) AM_CONDITIONAL([ENABLE_GOOGLE_RELAY], [test "x$enable_google_relay" = xyes]) AC_ARG_ENABLE(file-transfer, AC_HELP_STRING([--disable-file-transfer], [disable file transfer support]), [enable_ft=$enableval], [enable_ft=yes]) if test x$enable_ft = xyes; then AC_DEFINE(ENABLE_FILE_TRANSFER, [], [Enable file transfer]) fi AM_CONDITIONAL([ENABLE_FILE_TRANSFER], [test "x$enable_ft" = xyes]) AC_ARG_ENABLE(voip, AC_HELP_STRING([--disable-voip], [disable VoIP support (and, consequently, Jingle-based file transfer support]), [enable_voip=$enableval], [enable_voip=yes]) if test x$enable_voip = xyes; then AC_DEFINE(ENABLE_VOIP, [], [Enable VoIP]) fi AM_CONDITIONAL([ENABLE_VOIP], [test "x$enable_voip" = xyes]) if test x$enable_voip = xyes -a x$enable_ft = xyes; then enable_jingle_ft=yes AC_DEFINE(ENABLE_JINGLE_FILE_TRANSFER, [], [Enable file transfer]) dnl Check for libnice PKG_CHECK_MODULES(NICE, nice >= 0.0.11) else enable_jingle_ft=no NICE_CFLAGS= NICE_LIBS= fi AC_SUBST(NICE_CFLAGS) AC_SUBST(NICE_LIBS) AM_CONDITIONAL([ENABLE_JINGLE_FILE_TRANSFER], [test "x$enable_jingle_ft" = xyes]) AC_CHECK_FUNCS(getifaddrs memset select strndup setresuid setreuid strerror) AC_OUTPUT( Makefile \ docs/Makefile \ extensions/Makefile \ src/Makefile \ m4/Makefile \ data/Makefile \ tests/Makefile \ tools/Makefile \ tests/suppressions/Makefile \ tests/twisted/Makefile \ lib/Makefile \ lib/ext/Makefile \ lib/gibber/Makefile \ plugins/Makefile \ gabble/Makefile \ gabble/telepathy-gabble-uninstalled.pc \ gabble/telepathy-gabble.pc ) if test false != "$TEST_PYTHON"; then tests_enabled=yes else tests_enabled=no fi echo " Configure summary: Compiler....................: ${CC} Compiler Flags..............: ${CFLAGS} ${ERROR_CFLAGS} Prefix......................: ${prefix} Coding style checks.........: ${ENABLE_CODING_STYLE_CHECKS} Enable debug................: ${enable_debug} Python tests................: ${tests_enabled} Install unit tests..........: ${installed_tests} Features: Client type.................: \"${CLIENT_TYPE}\" Plugin support..............: ${enable_plugins} Plugin headers installed....: ${enable_plugin_api} Channel.Type.Call support...: ${enable_channel_type_call} Google relay support........: ${enable_google_relay} File transfer support.......: ${enable_ft} Jingle file transfer support: ${enable_jingle_ft} VoIP support................: ${enable_voip} " telepathy-gabble-0.18.2/configure0000755000175000017500000177412012312536070016737 0ustar00smcvsmcv00000000000000#! /bin/sh # Guess values for system-dependent variables and create Makefiles. # Generated by GNU Autoconf 2.69 for Telepathy Gabble 0.18.2. # # Report bugs to . # # # Copyright (C) 1992-1996, 1998-2012 Free Software Foundation, Inc. # # # This configure script is free software; the Free Software Foundation # gives unlimited permission to copy, distribute and modify it. ## -------------------- ## ## M4sh Initialization. ## ## -------------------- ## # Be more Bourne compatible DUALCASE=1; export DUALCASE # for MKS sh if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then : emulate sh NULLCMD=: # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which # is contrary to our usage. Disable this feature. alias -g '${1+"$@"}'='"$@"' setopt NO_GLOB_SUBST else case `(set -o) 2>/dev/null` in #( *posix*) : set -o posix ;; #( *) : ;; esac fi as_nl=' ' export as_nl # Printing a long string crashes Solaris 7 /usr/bin/printf. as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo # Prefer a ksh shell builtin over an external printf program on Solaris, # but without wasting forks for bash or zsh. if test -z "$BASH_VERSION$ZSH_VERSION" \ && (test "X`print -r -- $as_echo`" = "X$as_echo") 2>/dev/null; then as_echo='print -r --' as_echo_n='print -rn --' elif (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then as_echo='printf %s\n' as_echo_n='printf %s' else if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"' as_echo_n='/usr/ucb/echo -n' else as_echo_body='eval expr "X$1" : "X\\(.*\\)"' as_echo_n_body='eval arg=$1; case $arg in #( *"$as_nl"*) expr "X$arg" : "X\\(.*\\)$as_nl"; arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;; esac; expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl" ' export as_echo_n_body as_echo_n='sh -c $as_echo_n_body as_echo' fi export as_echo_body as_echo='sh -c $as_echo_body as_echo' fi # The user is always right. if test "${PATH_SEPARATOR+set}" != set; then PATH_SEPARATOR=: (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && { (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 || PATH_SEPARATOR=';' } fi # IFS # We need space, tab and new line, in precisely that order. Quoting is # there to prevent editors from complaining about space-tab. # (If _AS_PATH_WALK were called with IFS unset, it would disable word # splitting by setting IFS to empty value.) IFS=" "" $as_nl" # Find who we are. Look in the path if we contain no directory separator. as_myself= case $0 in #(( *[\\/]* ) as_myself=$0 ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break done IFS=$as_save_IFS ;; esac # We did not find ourselves, most probably we were run as `sh COMMAND' # in which case we are not to be found in the path. if test "x$as_myself" = x; then as_myself=$0 fi if test ! -f "$as_myself"; then $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 exit 1 fi # Unset variables that we do not need and which cause bugs (e.g. in # pre-3.0 UWIN ksh). But do not cause bugs in bash 2.01; the "|| exit 1" # suppresses any "Segmentation fault" message there. '((' could # trigger a bug in pdksh 5.2.14. for as_var in BASH_ENV ENV MAIL MAILPATH do eval test x\${$as_var+set} = xset \ && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || : done PS1='$ ' PS2='> ' PS4='+ ' # NLS nuisances. LC_ALL=C export LC_ALL LANGUAGE=C export LANGUAGE # CDPATH. (unset CDPATH) >/dev/null 2>&1 && unset CDPATH # Use a proper internal environment variable to ensure we don't fall # into an infinite loop, continuously re-executing ourselves. if test x"${_as_can_reexec}" != xno && test "x$CONFIG_SHELL" != x; then _as_can_reexec=no; export _as_can_reexec; # We cannot yet assume a decent shell, so we have to provide a # neutralization value for shells without unset; and this also # works around shells that cannot unset nonexistent variables. # Preserve -v and -x to the replacement shell. BASH_ENV=/dev/null ENV=/dev/null (unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV case $- in # (((( *v*x* | *x*v* ) as_opts=-vx ;; *v* ) as_opts=-v ;; *x* ) as_opts=-x ;; * ) as_opts= ;; esac exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"} # Admittedly, this is quite paranoid, since all the known shells bail # out after a failed `exec'. $as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2 as_fn_exit 255 fi # We don't want this to propagate to other subprocesses. { _as_can_reexec=; unset _as_can_reexec;} if test "x$CONFIG_SHELL" = x; then as_bourne_compatible="if test -n \"\${ZSH_VERSION+set}\" && (emulate sh) >/dev/null 2>&1; then : emulate sh NULLCMD=: # Pre-4.2 versions of Zsh do word splitting on \${1+\"\$@\"}, which # is contrary to our usage. Disable this feature. alias -g '\${1+\"\$@\"}'='\"\$@\"' setopt NO_GLOB_SUBST else case \`(set -o) 2>/dev/null\` in #( *posix*) : set -o posix ;; #( *) : ;; esac fi " as_required="as_fn_return () { (exit \$1); } as_fn_success () { as_fn_return 0; } as_fn_failure () { as_fn_return 1; } as_fn_ret_success () { return 0; } as_fn_ret_failure () { return 1; } exitcode=0 as_fn_success || { exitcode=1; echo as_fn_success failed.; } as_fn_failure && { exitcode=1; echo as_fn_failure succeeded.; } as_fn_ret_success || { exitcode=1; echo as_fn_ret_success failed.; } as_fn_ret_failure && { exitcode=1; echo as_fn_ret_failure succeeded.; } if ( set x; as_fn_ret_success y && test x = \"\$1\" ); then : else exitcode=1; echo positional parameters were not saved. fi test x\$exitcode = x0 || exit 1 test -x / || exit 1" as_suggested=" as_lineno_1=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_1a=\$LINENO as_lineno_2=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_2a=\$LINENO eval 'test \"x\$as_lineno_1'\$as_run'\" != \"x\$as_lineno_2'\$as_run'\" && test \"x\`expr \$as_lineno_1'\$as_run' + 1\`\" = \"x\$as_lineno_2'\$as_run'\"' || exit 1 test \$(( 1 + 1 )) = 2 || exit 1 test -n \"\${ZSH_VERSION+set}\${BASH_VERSION+set}\" || ( ECHO='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' ECHO=\$ECHO\$ECHO\$ECHO\$ECHO\$ECHO ECHO=\$ECHO\$ECHO\$ECHO\$ECHO\$ECHO\$ECHO PATH=/empty FPATH=/empty; export PATH FPATH test \"X\`printf %s \$ECHO\`\" = \"X\$ECHO\" \\ || test \"X\`print -r -- \$ECHO\`\" = \"X\$ECHO\" ) || exit 1" if (eval "$as_required") 2>/dev/null; then : as_have_required=yes else as_have_required=no fi if test x$as_have_required = xyes && (eval "$as_suggested") 2>/dev/null; then : else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR as_found=false for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. as_found=: case $as_dir in #( /*) for as_base in sh bash ksh sh5; do # Try only shells that exist, to save several forks. as_shell=$as_dir/$as_base if { test -f "$as_shell" || test -f "$as_shell.exe"; } && { $as_echo "$as_bourne_compatible""$as_required" | as_run=a "$as_shell"; } 2>/dev/null; then : CONFIG_SHELL=$as_shell as_have_required=yes if { $as_echo "$as_bourne_compatible""$as_suggested" | as_run=a "$as_shell"; } 2>/dev/null; then : break 2 fi fi done;; esac as_found=false done $as_found || { if { test -f "$SHELL" || test -f "$SHELL.exe"; } && { $as_echo "$as_bourne_compatible""$as_required" | as_run=a "$SHELL"; } 2>/dev/null; then : CONFIG_SHELL=$SHELL as_have_required=yes fi; } IFS=$as_save_IFS if test "x$CONFIG_SHELL" != x; then : export CONFIG_SHELL # We cannot yet assume a decent shell, so we have to provide a # neutralization value for shells without unset; and this also # works around shells that cannot unset nonexistent variables. # Preserve -v and -x to the replacement shell. BASH_ENV=/dev/null ENV=/dev/null (unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV case $- in # (((( *v*x* | *x*v* ) as_opts=-vx ;; *v* ) as_opts=-v ;; *x* ) as_opts=-x ;; * ) as_opts= ;; esac exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"} # Admittedly, this is quite paranoid, since all the known shells bail # out after a failed `exec'. $as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2 exit 255 fi if test x$as_have_required = xno; then : $as_echo "$0: This script requires a shell more modern than all" $as_echo "$0: the shells that I found on your system." if test x${ZSH_VERSION+set} = xset ; then $as_echo "$0: In particular, zsh $ZSH_VERSION has bugs and should" $as_echo "$0: be upgraded to zsh 4.3.4 or later." else $as_echo "$0: Please tell bug-autoconf@gnu.org and $0: https://bugs.freedesktop.org/enter_bug.cgi?product=Telepathy&component=gabble $0: about your system, including any error possibly output $0: before this message. Then install a modern shell, or $0: manually run the script under such a shell if you do $0: have one." fi exit 1 fi fi fi SHELL=${CONFIG_SHELL-/bin/sh} export SHELL # Unset more variables known to interfere with behavior of common tools. CLICOLOR_FORCE= GREP_OPTIONS= unset CLICOLOR_FORCE GREP_OPTIONS ## --------------------- ## ## M4sh Shell Functions. ## ## --------------------- ## # as_fn_unset VAR # --------------- # Portably unset VAR. as_fn_unset () { { eval $1=; unset $1;} } as_unset=as_fn_unset # as_fn_set_status STATUS # ----------------------- # Set $? to STATUS, without forking. as_fn_set_status () { return $1 } # as_fn_set_status # as_fn_exit STATUS # ----------------- # Exit the shell with STATUS, even in a "trap 0" or "set -e" context. as_fn_exit () { set +e as_fn_set_status $1 exit $1 } # as_fn_exit # as_fn_mkdir_p # ------------- # Create "$as_dir" as a directory, including parents if necessary. as_fn_mkdir_p () { case $as_dir in #( -*) as_dir=./$as_dir;; esac test -d "$as_dir" || eval $as_mkdir_p || { as_dirs= while :; do case $as_dir in #( *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'( *) as_qdir=$as_dir;; esac as_dirs="'$as_qdir' $as_dirs" as_dir=`$as_dirname -- "$as_dir" || $as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$as_dir" : 'X\(//\)[^/]' \| \ X"$as_dir" : 'X\(//\)$' \| \ X"$as_dir" : 'X\(/\)' \| . 2>/dev/null || $as_echo X"$as_dir" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q'` test -d "$as_dir" && break done test -z "$as_dirs" || eval "mkdir $as_dirs" } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir" } # as_fn_mkdir_p # as_fn_executable_p FILE # ----------------------- # Test if FILE is an executable regular file. as_fn_executable_p () { test -f "$1" && test -x "$1" } # as_fn_executable_p # as_fn_append VAR VALUE # ---------------------- # Append the text in VALUE to the end of the definition contained in VAR. Take # advantage of any shell optimizations that allow amortized linear growth over # repeated appends, instead of the typical quadratic growth present in naive # implementations. if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null; then : eval 'as_fn_append () { eval $1+=\$2 }' else as_fn_append () { eval $1=\$$1\$2 } fi # as_fn_append # as_fn_arith ARG... # ------------------ # Perform arithmetic evaluation on the ARGs, and store the result in the # global $as_val. Take advantage of shells that can avoid forks. The arguments # must be portable across $(()) and expr. if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null; then : eval 'as_fn_arith () { as_val=$(( $* )) }' else as_fn_arith () { as_val=`expr "$@" || test $? -eq 1` } fi # as_fn_arith # as_fn_error STATUS ERROR [LINENO LOG_FD] # ---------------------------------------- # Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are # provided, also output the error to LOG_FD, referencing LINENO. Then exit the # script with STATUS, using 1 if that was 0. as_fn_error () { as_status=$1; test $as_status -eq 0 && as_status=1 if test "$4"; then as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack $as_echo "$as_me:${as_lineno-$LINENO}: error: $2" >&$4 fi $as_echo "$as_me: error: $2" >&2 as_fn_exit $as_status } # as_fn_error if expr a : '\(a\)' >/dev/null 2>&1 && test "X`expr 00001 : '.*\(...\)'`" = X001; then as_expr=expr else as_expr=false fi if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then as_basename=basename else as_basename=false fi if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then as_dirname=dirname else as_dirname=false fi as_me=`$as_basename -- "$0" || $as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ X"$0" : 'X\(//\)$' \| \ X"$0" : 'X\(/\)' \| . 2>/dev/null || $as_echo X/"$0" | sed '/^.*\/\([^/][^/]*\)\/*$/{ s//\1/ q } /^X\/\(\/\/\)$/{ s//\1/ q } /^X\/\(\/\).*/{ s//\1/ q } s/.*/./; q'` # Avoid depending upon Character Ranges. as_cr_letters='abcdefghijklmnopqrstuvwxyz' as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' as_cr_Letters=$as_cr_letters$as_cr_LETTERS as_cr_digits='0123456789' as_cr_alnum=$as_cr_Letters$as_cr_digits as_lineno_1=$LINENO as_lineno_1a=$LINENO as_lineno_2=$LINENO as_lineno_2a=$LINENO eval 'test "x$as_lineno_1'$as_run'" != "x$as_lineno_2'$as_run'" && test "x`expr $as_lineno_1'$as_run' + 1`" = "x$as_lineno_2'$as_run'"' || { # Blame Lee E. McMahon (1931-1989) for sed's syntax. :-) sed -n ' p /[$]LINENO/= ' <$as_myself | sed ' s/[$]LINENO.*/&-/ t lineno b :lineno N :loop s/[$]LINENO\([^'$as_cr_alnum'_].*\n\)\(.*\)/\2\1\2/ t loop s/-\n.*// ' >$as_me.lineno && chmod +x "$as_me.lineno" || { $as_echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2; as_fn_exit 1; } # If we had to re-execute with $CONFIG_SHELL, we're ensured to have # already done that, so ensure we don't try to do so again and fall # in an infinite loop. This has already happened in practice. _as_can_reexec=no; export _as_can_reexec # Don't try to exec as it changes $[0], causing all sort of problems # (the dirname of $[0] is not the place where we might find the # original and so on. Autoconf is especially sensitive to this). . "./$as_me.lineno" # Exit status is that of the last command. exit } ECHO_C= ECHO_N= ECHO_T= case `echo -n x` in #((((( -n*) case `echo 'xy\c'` in *c*) ECHO_T=' ';; # ECHO_T is single tab character. xy) ECHO_C='\c';; *) echo `echo ksh88 bug on AIX 6.1` > /dev/null ECHO_T=' ';; esac;; *) ECHO_N='-n';; esac rm -f conf$$ conf$$.exe conf$$.file if test -d conf$$.dir; then rm -f conf$$.dir/conf$$.file else rm -f conf$$.dir mkdir conf$$.dir 2>/dev/null fi if (echo >conf$$.file) 2>/dev/null; then if ln -s conf$$.file conf$$ 2>/dev/null; then as_ln_s='ln -s' # ... but there are two gotchas: # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. # In both cases, we have to default to `cp -pR'. ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || as_ln_s='cp -pR' elif ln conf$$.file conf$$ 2>/dev/null; then as_ln_s=ln else as_ln_s='cp -pR' fi else as_ln_s='cp -pR' fi rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file rmdir conf$$.dir 2>/dev/null if mkdir -p . 2>/dev/null; then as_mkdir_p='mkdir -p "$as_dir"' else test -d ./-p && rmdir ./-p as_mkdir_p=false fi as_test_x='test -x' as_executable_p=as_fn_executable_p # Sed expression to map a string onto a valid CPP name. as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" # Sed expression to map a string onto a valid variable name. as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" SHELL=${CONFIG_SHELL-/bin/sh} test -n "$DJDIR" || exec 7<&0 &1 # Name of the host. # hostname on some systems (SVR3.2, old GNU/Linux) returns a bogus exit status, # so uname gets run too. ac_hostname=`(hostname || uname -n) 2>/dev/null | sed 1q` # # Initializations. # ac_default_prefix=/usr/local ac_clean_files= ac_config_libobj_dir=. LIBOBJS= cross_compiling=no subdirs= MFLAGS= MAKEFLAGS= # Identity of this package. PACKAGE_NAME='Telepathy Gabble' PACKAGE_TARNAME='telepathy-gabble' PACKAGE_VERSION='0.18.2' PACKAGE_STRING='Telepathy Gabble 0.18.2' PACKAGE_BUGREPORT='https://bugs.freedesktop.org/enter_bug.cgi?product=Telepathy&component=gabble' PACKAGE_URL='' # Factoring default headers for most tests. ac_includes_default="\ #include #ifdef HAVE_SYS_TYPES_H # include #endif #ifdef HAVE_SYS_STAT_H # include #endif #ifdef STDC_HEADERS # include # include #else # ifdef HAVE_STDLIB_H # include # endif #endif #ifdef HAVE_STRING_H # if !defined STDC_HEADERS && defined HAVE_MEMORY_H # include # endif # include #endif #ifdef HAVE_STRINGS_H # include #endif #ifdef HAVE_INTTYPES_H # include #endif #ifdef HAVE_STDINT_H # include #endif #ifdef HAVE_UNISTD_H # include #endif" ac_header_list= enable_option_checking=no ac_subst_vars='am__EXEEXT_FALSE am__EXEEXT_TRUE LTLIBOBJS LIBOBJS ENABLE_JINGLE_FILE_TRANSFER_FALSE ENABLE_JINGLE_FILE_TRANSFER_TRUE NICE_LIBS NICE_CFLAGS ENABLE_VOIP_FALSE ENABLE_VOIP_TRUE ENABLE_FILE_TRANSFER_FALSE ENABLE_FILE_TRANSFER_TRUE ENABLE_GOOGLE_RELAY_FALSE ENABLE_GOOGLE_RELAY_TRUE SOUP_LIBS SOUP_CFLAGS WOCKY_LIBS WOCKY_CFLAGS WANT_TWISTED_TESTS_FALSE WANT_TWISTED_TESTS_TRUE TEST_PYTHON pkgpyexecdir pyexecdir pkgpythondir pythondir PYTHON_PLATFORM PYTHON_EXEC_PREFIX PYTHON_PREFIX PYTHON_VERSION PYTHON XSLTPROC TP_GLIB_LIBS TP_GLIB_CFLAGS DBUS_LIBS DBUS_CFLAGS ENABLE_CHANNEL_TYPE_CALL_FALSE ENABLE_CHANNEL_TYPE_CALL_TRUE ENABLE_PLUGIN_API_FALSE ENABLE_PLUGIN_API_TRUE ENABLE_PLUGINS ENABLE_PLUGINS_FALSE ENABLE_PLUGINS_TRUE pluginexecdir pluginexeclibdir GLIB_GENMARSHAL GMODULE_LIBS GMODULE_CFLAGS GLIB_LIBS GLIB_CFLAGS PKG_CONFIG_LIBDIR PKG_CONFIG_PATH PKG_CONFIG ENABLE_GTK_DOC_FALSE ENABLE_GTK_DOC_TRUE ENABLE_FATAL_CRITICALS_FALSE ENABLE_FATAL_CRITICALS_TRUE CLIENT_TYPE gabbletestsdir ENABLE_INSTALLED_TESTS_FALSE ENABLE_INSTALLED_TESTS_TRUE ENABLE_CODING_STYLE_CHECKS ENABLE_DEBUG_FALSE ENABLE_DEBUG_TRUE WINDOWS_FALSE WINDOWS_TRUE ERROR_CFLAGS OFFICIAL_RELEASE_FALSE OFFICIAL_RELEASE_TRUE OTOOL64 OTOOL LIPO NMEDIT DSYMUTIL MANIFEST_TOOL RANLIB ac_ct_AR AR DLLTOOL OBJDUMP LN_S NM ac_ct_DUMPBIN DUMPBIN LD FGREP SED host_os host_vendor host_cpu host build_os build_vendor build_cpu build LIBTOOL EGREP GREP CPP am__fastdepCC_FALSE am__fastdepCC_TRUE CCDEPMODE am__nodep AMDEPBACKSLASH AMDEP_FALSE AMDEP_TRUE am__quote am__include DEPDIR OBJEXT EXEEXT ac_ct_CC CPPFLAGS LDFLAGS CFLAGS CC AM_BACKSLASH AM_DEFAULT_VERBOSITY AM_DEFAULT_V AM_V am__untar am__tar AMTAR am__leading_dot SET_MAKE AWK mkdir_p MKDIR_P INSTALL_STRIP_PROGRAM STRIP install_sh MAKEINFO AUTOHEADER AUTOMAKE AUTOCONF ACLOCAL VERSION PACKAGE CYGPATH_W am__isrc INSTALL_DATA INSTALL_SCRIPT INSTALL_PROGRAM target_alias host_alias build_alias LIBS ECHO_T ECHO_N ECHO_C DEFS mandir localedir libdir psdir pdfdir dvidir htmldir infodir docdir oldincludedir includedir localstatedir sharedstatedir sysconfdir datadir datarootdir libexecdir sbindir bindir program_transform_name prefix exec_prefix PACKAGE_URL PACKAGE_BUGREPORT PACKAGE_STRING PACKAGE_VERSION PACKAGE_TARNAME PACKAGE_NAME PATH_SEPARATOR SHELL' ac_subst_files='' ac_user_opts=' enable_option_checking enable_silent_rules enable_dependency_tracking enable_shared enable_static with_pic enable_fast_install with_gnu_ld with_sysroot enable_libtool_lock enable_compiler_optimisations enable_compiler_coverage enable_Werror with_ca_certificates enable_debug enable_coding_style_checks enable_installed_tests enable_is_a_phone enable_fatal_criticals enable_gtk_doc enable_plugins enable_plugin_api enable_channel_type_call enable_submodules enable_google_relay enable_file_transfer enable_voip ' ac_precious_vars='build_alias host_alias target_alias CC CFLAGS LDFLAGS LIBS CPPFLAGS CPP PKG_CONFIG PKG_CONFIG_PATH PKG_CONFIG_LIBDIR GLIB_CFLAGS GLIB_LIBS GMODULE_CFLAGS GMODULE_LIBS pluginexeclibdir pluginexecdir DBUS_CFLAGS DBUS_LIBS TP_GLIB_CFLAGS TP_GLIB_LIBS PYTHON WOCKY_CFLAGS WOCKY_LIBS SOUP_CFLAGS SOUP_LIBS NICE_CFLAGS NICE_LIBS' ac_subdirs_all='lib/ext/wocky' # Initialize some variables set by options. ac_init_help= ac_init_version=false ac_unrecognized_opts= ac_unrecognized_sep= # The variables have the same names as the options, with # dashes changed to underlines. cache_file=/dev/null exec_prefix=NONE no_create= no_recursion= prefix=NONE program_prefix=NONE program_suffix=NONE program_transform_name=s,x,x, silent= site= srcdir= verbose= x_includes=NONE x_libraries=NONE # Installation directory options. # These are left unexpanded so users can "make install exec_prefix=/foo" # and all the variables that are supposed to be based on exec_prefix # by default will actually change. # Use braces instead of parens because sh, perl, etc. also accept them. # (The list follows the same order as the GNU Coding Standards.) bindir='${exec_prefix}/bin' sbindir='${exec_prefix}/sbin' libexecdir='${exec_prefix}/libexec' datarootdir='${prefix}/share' datadir='${datarootdir}' sysconfdir='${prefix}/etc' sharedstatedir='${prefix}/com' localstatedir='${prefix}/var' includedir='${prefix}/include' oldincludedir='/usr/include' docdir='${datarootdir}/doc/${PACKAGE_TARNAME}' infodir='${datarootdir}/info' htmldir='${docdir}' dvidir='${docdir}' pdfdir='${docdir}' psdir='${docdir}' libdir='${exec_prefix}/lib' localedir='${datarootdir}/locale' mandir='${datarootdir}/man' ac_prev= ac_dashdash= for ac_option do # If the previous option needs an argument, assign it. if test -n "$ac_prev"; then eval $ac_prev=\$ac_option ac_prev= continue fi case $ac_option in *=?*) ac_optarg=`expr "X$ac_option" : '[^=]*=\(.*\)'` ;; *=) ac_optarg= ;; *) ac_optarg=yes ;; esac # Accept the important Cygnus configure options, so we can diagnose typos. case $ac_dashdash$ac_option in --) ac_dashdash=yes ;; -bindir | --bindir | --bindi | --bind | --bin | --bi) ac_prev=bindir ;; -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*) bindir=$ac_optarg ;; -build | --build | --buil | --bui | --bu) ac_prev=build_alias ;; -build=* | --build=* | --buil=* | --bui=* | --bu=*) build_alias=$ac_optarg ;; -cache-file | --cache-file | --cache-fil | --cache-fi \ | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c) ac_prev=cache_file ;; -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \ | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*) cache_file=$ac_optarg ;; --config-cache | -C) cache_file=config.cache ;; -datadir | --datadir | --datadi | --datad) ac_prev=datadir ;; -datadir=* | --datadir=* | --datadi=* | --datad=*) datadir=$ac_optarg ;; -datarootdir | --datarootdir | --datarootdi | --datarootd | --dataroot \ | --dataroo | --dataro | --datar) ac_prev=datarootdir ;; -datarootdir=* | --datarootdir=* | --datarootdi=* | --datarootd=* \ | --dataroot=* | --dataroo=* | --dataro=* | --datar=*) datarootdir=$ac_optarg ;; -disable-* | --disable-*) ac_useropt=`expr "x$ac_option" : 'x-*disable-\(.*\)'` # Reject names that are not valid shell variable names. expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && as_fn_error $? "invalid feature name: $ac_useropt" ac_useropt_orig=$ac_useropt ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` case $ac_user_opts in *" "enable_$ac_useropt" "*) ;; *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--disable-$ac_useropt_orig" ac_unrecognized_sep=', ';; esac eval enable_$ac_useropt=no ;; -docdir | --docdir | --docdi | --doc | --do) ac_prev=docdir ;; -docdir=* | --docdir=* | --docdi=* | --doc=* | --do=*) docdir=$ac_optarg ;; -dvidir | --dvidir | --dvidi | --dvid | --dvi | --dv) ac_prev=dvidir ;; -dvidir=* | --dvidir=* | --dvidi=* | --dvid=* | --dvi=* | --dv=*) dvidir=$ac_optarg ;; -enable-* | --enable-*) ac_useropt=`expr "x$ac_option" : 'x-*enable-\([^=]*\)'` # Reject names that are not valid shell variable names. expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && as_fn_error $? "invalid feature name: $ac_useropt" ac_useropt_orig=$ac_useropt ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` case $ac_user_opts in *" "enable_$ac_useropt" "*) ;; *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--enable-$ac_useropt_orig" ac_unrecognized_sep=', ';; esac eval enable_$ac_useropt=\$ac_optarg ;; -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \ | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \ | --exec | --exe | --ex) ac_prev=exec_prefix ;; -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \ | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \ | --exec=* | --exe=* | --ex=*) exec_prefix=$ac_optarg ;; -gas | --gas | --ga | --g) # Obsolete; use --with-gas. with_gas=yes ;; -help | --help | --hel | --he | -h) ac_init_help=long ;; -help=r* | --help=r* | --hel=r* | --he=r* | -hr*) ac_init_help=recursive ;; -help=s* | --help=s* | --hel=s* | --he=s* | -hs*) ac_init_help=short ;; -host | --host | --hos | --ho) ac_prev=host_alias ;; -host=* | --host=* | --hos=* | --ho=*) host_alias=$ac_optarg ;; -htmldir | --htmldir | --htmldi | --htmld | --html | --htm | --ht) ac_prev=htmldir ;; -htmldir=* | --htmldir=* | --htmldi=* | --htmld=* | --html=* | --htm=* \ | --ht=*) htmldir=$ac_optarg ;; -includedir | --includedir | --includedi | --included | --include \ | --includ | --inclu | --incl | --inc) ac_prev=includedir ;; -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \ | --includ=* | --inclu=* | --incl=* | --inc=*) includedir=$ac_optarg ;; -infodir | --infodir | --infodi | --infod | --info | --inf) ac_prev=infodir ;; -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*) infodir=$ac_optarg ;; -libdir | --libdir | --libdi | --libd) ac_prev=libdir ;; -libdir=* | --libdir=* | --libdi=* | --libd=*) libdir=$ac_optarg ;; -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \ | --libexe | --libex | --libe) ac_prev=libexecdir ;; -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \ | --libexe=* | --libex=* | --libe=*) libexecdir=$ac_optarg ;; -localedir | --localedir | --localedi | --localed | --locale) ac_prev=localedir ;; -localedir=* | --localedir=* | --localedi=* | --localed=* | --locale=*) localedir=$ac_optarg ;; -localstatedir | --localstatedir | --localstatedi | --localstated \ | --localstate | --localstat | --localsta | --localst | --locals) ac_prev=localstatedir ;; -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \ | --localstate=* | --localstat=* | --localsta=* | --localst=* | --locals=*) localstatedir=$ac_optarg ;; -mandir | --mandir | --mandi | --mand | --man | --ma | --m) ac_prev=mandir ;; -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*) mandir=$ac_optarg ;; -nfp | --nfp | --nf) # Obsolete; use --without-fp. with_fp=no ;; -no-create | --no-create | --no-creat | --no-crea | --no-cre \ | --no-cr | --no-c | -n) no_create=yes ;; -no-recursion | --no-recursion | --no-recursio | --no-recursi \ | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r) no_recursion=yes ;; -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \ | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \ | --oldin | --oldi | --old | --ol | --o) ac_prev=oldincludedir ;; -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \ | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \ | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*) oldincludedir=$ac_optarg ;; -prefix | --prefix | --prefi | --pref | --pre | --pr | --p) ac_prev=prefix ;; -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*) prefix=$ac_optarg ;; -program-prefix | --program-prefix | --program-prefi | --program-pref \ | --program-pre | --program-pr | --program-p) ac_prev=program_prefix ;; -program-prefix=* | --program-prefix=* | --program-prefi=* \ | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*) program_prefix=$ac_optarg ;; -program-suffix | --program-suffix | --program-suffi | --program-suff \ | --program-suf | --program-su | --program-s) ac_prev=program_suffix ;; -program-suffix=* | --program-suffix=* | --program-suffi=* \ | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*) program_suffix=$ac_optarg ;; -program-transform-name | --program-transform-name \ | --program-transform-nam | --program-transform-na \ | --program-transform-n | --program-transform- \ | --program-transform | --program-transfor \ | --program-transfo | --program-transf \ | --program-trans | --program-tran \ | --progr-tra | --program-tr | --program-t) ac_prev=program_transform_name ;; -program-transform-name=* | --program-transform-name=* \ | --program-transform-nam=* | --program-transform-na=* \ | --program-transform-n=* | --program-transform-=* \ | --program-transform=* | --program-transfor=* \ | --program-transfo=* | --program-transf=* \ | --program-trans=* | --program-tran=* \ | --progr-tra=* | --program-tr=* | --program-t=*) program_transform_name=$ac_optarg ;; -pdfdir | --pdfdir | --pdfdi | --pdfd | --pdf | --pd) ac_prev=pdfdir ;; -pdfdir=* | --pdfdir=* | --pdfdi=* | --pdfd=* | --pdf=* | --pd=*) pdfdir=$ac_optarg ;; -psdir | --psdir | --psdi | --psd | --ps) ac_prev=psdir ;; -psdir=* | --psdir=* | --psdi=* | --psd=* | --ps=*) psdir=$ac_optarg ;; -q | -quiet | --quiet | --quie | --qui | --qu | --q \ | -silent | --silent | --silen | --sile | --sil) silent=yes ;; -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb) ac_prev=sbindir ;; -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \ | --sbi=* | --sb=*) sbindir=$ac_optarg ;; -sharedstatedir | --sharedstatedir | --sharedstatedi \ | --sharedstated | --sharedstate | --sharedstat | --sharedsta \ | --sharedst | --shareds | --shared | --share | --shar \ | --sha | --sh) ac_prev=sharedstatedir ;; -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \ | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \ | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \ | --sha=* | --sh=*) sharedstatedir=$ac_optarg ;; -site | --site | --sit) ac_prev=site ;; -site=* | --site=* | --sit=*) site=$ac_optarg ;; -srcdir | --srcdir | --srcdi | --srcd | --src | --sr) ac_prev=srcdir ;; -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*) srcdir=$ac_optarg ;; -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \ | --syscon | --sysco | --sysc | --sys | --sy) ac_prev=sysconfdir ;; -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \ | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*) sysconfdir=$ac_optarg ;; -target | --target | --targe | --targ | --tar | --ta | --t) ac_prev=target_alias ;; -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*) target_alias=$ac_optarg ;; -v | -verbose | --verbose | --verbos | --verbo | --verb) verbose=yes ;; -version | --version | --versio | --versi | --vers | -V) ac_init_version=: ;; -with-* | --with-*) ac_useropt=`expr "x$ac_option" : 'x-*with-\([^=]*\)'` # Reject names that are not valid shell variable names. expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && as_fn_error $? "invalid package name: $ac_useropt" ac_useropt_orig=$ac_useropt ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` case $ac_user_opts in *" "with_$ac_useropt" "*) ;; *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--with-$ac_useropt_orig" ac_unrecognized_sep=', ';; esac eval with_$ac_useropt=\$ac_optarg ;; -without-* | --without-*) ac_useropt=`expr "x$ac_option" : 'x-*without-\(.*\)'` # Reject names that are not valid shell variable names. expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && as_fn_error $? "invalid package name: $ac_useropt" ac_useropt_orig=$ac_useropt ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` case $ac_user_opts in *" "with_$ac_useropt" "*) ;; *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--without-$ac_useropt_orig" ac_unrecognized_sep=', ';; esac eval with_$ac_useropt=no ;; --x) # Obsolete; use --with-x. with_x=yes ;; -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \ | --x-incl | --x-inc | --x-in | --x-i) ac_prev=x_includes ;; -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \ | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*) x_includes=$ac_optarg ;; -x-libraries | --x-libraries | --x-librarie | --x-librari \ | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l) ac_prev=x_libraries ;; -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \ | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*) x_libraries=$ac_optarg ;; -*) as_fn_error $? "unrecognized option: \`$ac_option' Try \`$0 --help' for more information" ;; *=*) ac_envvar=`expr "x$ac_option" : 'x\([^=]*\)='` # Reject names that are not valid shell variable names. case $ac_envvar in #( '' | [0-9]* | *[!_$as_cr_alnum]* ) as_fn_error $? "invalid variable name: \`$ac_envvar'" ;; esac eval $ac_envvar=\$ac_optarg export $ac_envvar ;; *) # FIXME: should be removed in autoconf 3.0. $as_echo "$as_me: WARNING: you should use --build, --host, --target" >&2 expr "x$ac_option" : ".*[^-._$as_cr_alnum]" >/dev/null && $as_echo "$as_me: WARNING: invalid host type: $ac_option" >&2 : "${build_alias=$ac_option} ${host_alias=$ac_option} ${target_alias=$ac_option}" ;; esac done if test -n "$ac_prev"; then ac_option=--`echo $ac_prev | sed 's/_/-/g'` as_fn_error $? "missing argument to $ac_option" fi if test -n "$ac_unrecognized_opts"; then case $enable_option_checking in no) ;; fatal) as_fn_error $? "unrecognized options: $ac_unrecognized_opts" ;; *) $as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2 ;; esac fi # Check all directory arguments for consistency. for ac_var in exec_prefix prefix bindir sbindir libexecdir datarootdir \ datadir sysconfdir sharedstatedir localstatedir includedir \ oldincludedir docdir infodir htmldir dvidir pdfdir psdir \ libdir localedir mandir do eval ac_val=\$$ac_var # Remove trailing slashes. case $ac_val in */ ) ac_val=`expr "X$ac_val" : 'X\(.*[^/]\)' \| "X$ac_val" : 'X\(.*\)'` eval $ac_var=\$ac_val;; esac # Be sure to have absolute directory names. case $ac_val in [\\/$]* | ?:[\\/]* ) continue;; NONE | '' ) case $ac_var in *prefix ) continue;; esac;; esac as_fn_error $? "expected an absolute directory name for --$ac_var: $ac_val" done # There might be people who depend on the old broken behavior: `$host' # used to hold the argument of --host etc. # FIXME: To remove some day. build=$build_alias host=$host_alias target=$target_alias # FIXME: To remove some day. if test "x$host_alias" != x; then if test "x$build_alias" = x; then cross_compiling=maybe elif test "x$build_alias" != "x$host_alias"; then cross_compiling=yes fi fi ac_tool_prefix= test -n "$host_alias" && ac_tool_prefix=$host_alias- test "$silent" = yes && exec 6>/dev/null ac_pwd=`pwd` && test -n "$ac_pwd" && ac_ls_di=`ls -di .` && ac_pwd_ls_di=`cd "$ac_pwd" && ls -di .` || as_fn_error $? "working directory cannot be determined" test "X$ac_ls_di" = "X$ac_pwd_ls_di" || as_fn_error $? "pwd does not report name of working directory" # Find the source files, if location was not specified. if test -z "$srcdir"; then ac_srcdir_defaulted=yes # Try the directory containing this script, then the parent directory. ac_confdir=`$as_dirname -- "$as_myself" || $as_expr X"$as_myself" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$as_myself" : 'X\(//\)[^/]' \| \ X"$as_myself" : 'X\(//\)$' \| \ X"$as_myself" : 'X\(/\)' \| . 2>/dev/null || $as_echo X"$as_myself" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q'` srcdir=$ac_confdir if test ! -r "$srcdir/$ac_unique_file"; then srcdir=.. fi else ac_srcdir_defaulted=no fi if test ! -r "$srcdir/$ac_unique_file"; then test "$ac_srcdir_defaulted" = yes && srcdir="$ac_confdir or .." as_fn_error $? "cannot find sources ($ac_unique_file) in $srcdir" fi ac_msg="sources are in $srcdir, but \`cd $srcdir' does not work" ac_abs_confdir=`( cd "$srcdir" && test -r "./$ac_unique_file" || as_fn_error $? "$ac_msg" pwd)` # When building in place, set srcdir=. if test "$ac_abs_confdir" = "$ac_pwd"; then srcdir=. fi # Remove unnecessary trailing slashes from srcdir. # Double slashes in file names in object file debugging info # mess up M-x gdb in Emacs. case $srcdir in */) srcdir=`expr "X$srcdir" : 'X\(.*[^/]\)' \| "X$srcdir" : 'X\(.*\)'`;; esac for ac_var in $ac_precious_vars; do eval ac_env_${ac_var}_set=\${${ac_var}+set} eval ac_env_${ac_var}_value=\$${ac_var} eval ac_cv_env_${ac_var}_set=\${${ac_var}+set} eval ac_cv_env_${ac_var}_value=\$${ac_var} done # # Report the --help message. # if test "$ac_init_help" = "long"; then # Omit some internal or obsolete options to make the list less imposing. # This message is too long to be a string in the A/UX 3.1 sh. cat <<_ACEOF \`configure' configures Telepathy Gabble 0.18.2 to adapt to many kinds of systems. Usage: $0 [OPTION]... [VAR=VALUE]... To assign environment variables (e.g., CC, CFLAGS...), specify them as VAR=VALUE. See below for descriptions of some of the useful variables. Defaults for the options are specified in brackets. Configuration: -h, --help display this help and exit --help=short display options specific to this package --help=recursive display the short help of all the included packages -V, --version display version information and exit -q, --quiet, --silent do not print \`checking ...' messages --cache-file=FILE cache test results in FILE [disabled] -C, --config-cache alias for \`--cache-file=config.cache' -n, --no-create do not create output files --srcdir=DIR find the sources in DIR [configure dir or \`..'] Installation directories: --prefix=PREFIX install architecture-independent files in PREFIX [$ac_default_prefix] --exec-prefix=EPREFIX install architecture-dependent files in EPREFIX [PREFIX] By default, \`make install' will install all the files in \`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc. You can specify an installation prefix other than \`$ac_default_prefix' using \`--prefix', for instance \`--prefix=\$HOME'. For better control, use the options below. Fine tuning of the installation directories: --bindir=DIR user executables [EPREFIX/bin] --sbindir=DIR system admin executables [EPREFIX/sbin] --libexecdir=DIR program executables [EPREFIX/libexec] --sysconfdir=DIR read-only single-machine data [PREFIX/etc] --sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com] --localstatedir=DIR modifiable single-machine data [PREFIX/var] --libdir=DIR object code libraries [EPREFIX/lib] --includedir=DIR C header files [PREFIX/include] --oldincludedir=DIR C header files for non-gcc [/usr/include] --datarootdir=DIR read-only arch.-independent data root [PREFIX/share] --datadir=DIR read-only architecture-independent data [DATAROOTDIR] --infodir=DIR info documentation [DATAROOTDIR/info] --localedir=DIR locale-dependent data [DATAROOTDIR/locale] --mandir=DIR man documentation [DATAROOTDIR/man] --docdir=DIR documentation root [DATAROOTDIR/doc/telepathy-gabble] --htmldir=DIR html documentation [DOCDIR] --dvidir=DIR dvi documentation [DOCDIR] --pdfdir=DIR pdf documentation [DOCDIR] --psdir=DIR ps documentation [DOCDIR] _ACEOF cat <<\_ACEOF Program names: --program-prefix=PREFIX prepend PREFIX to installed program names --program-suffix=SUFFIX append SUFFIX to installed program names --program-transform-name=PROGRAM run sed PROGRAM on installed program names System types: --build=BUILD configure for building on BUILD [guessed] --host=HOST cross-compile to build programs to run on HOST [BUILD] _ACEOF fi if test -n "$ac_init_help"; then case $ac_init_help in short | recursive ) echo "Configuration of Telepathy Gabble 0.18.2:";; esac cat <<\_ACEOF Optional Features: --disable-option-checking ignore unrecognized --enable/--with options --disable-FEATURE do not include FEATURE (same as --enable-FEATURE=no) --enable-FEATURE[=ARG] include FEATURE [ARG=yes] --enable-silent-rules less verbose build output (undo: "make V=1") --disable-silent-rules verbose build output (undo: "make V=0") --enable-dependency-tracking do not reject slow dependency extractors --disable-dependency-tracking speeds up one-time build --enable-shared[=PKGS] build shared libraries [default=yes] --enable-static[=PKGS] build static libraries [default=yes] --enable-fast-install[=PKGS] optimize for fast installation [default=yes] --disable-libtool-lock avoid locking (might break parallel builds) --disable-compiler-optimisations Disable compiler optimisations --enable-compiler-coverage Enable generation of coverage data --disable-Werror compile without -Werror (normally enabled in development builds) --disable-debug compile without debug code --enable-coding-style-checks check coding style using grep --enable-installed-tests make tests installable --enable-is-a-phone advertise that we are a phone, not a PC --disable-fatal-criticals do not assert because of critical warnings --enable-gtk-doc does not actually do anything --disable-plugins disable plugin loader --enable-plugin-api install headers for third-party plugins (experimental) --disable-channel-type-call disable support for the draft Channel.Type.Call --disable-submodules Use system versions of Wocky, rather than submodules --disable-google-relay disable Google Jingle relay support --disable-file-transfer disable file transfer support --disable-voip disable VoIP support (and, consequently, Jingle-based file transfer support Optional Packages: --with-PACKAGE[=ARG] use PACKAGE [ARG=yes] --without-PACKAGE do not use PACKAGE (same as --with-PACKAGE=no) --with-pic[=PKGS] try to use only PIC/non-PIC objects [default=use both] --with-gnu-ld assume the C compiler uses GNU ld [default=no] --with-sysroot=DIR Search for dependent libraries within DIR (or the compiler's sysroot if not specified). --with-ca-certificates=[path] path to system Certificate Authority list Some influential environment variables: CC C compiler command CFLAGS C compiler flags LDFLAGS linker flags, e.g. -L if you have libraries in a nonstandard directory LIBS libraries to pass to the linker, e.g. -l CPPFLAGS (Objective) C/C++ preprocessor flags, e.g. -I if you have headers in a nonstandard directory CPP C preprocessor PKG_CONFIG path to pkg-config utility PKG_CONFIG_PATH directories to add to pkg-config's search path PKG_CONFIG_LIBDIR path overriding pkg-config's built-in search path GLIB_CFLAGS C compiler flags for GLIB, overriding pkg-config GLIB_LIBS linker flags for GLIB, overriding pkg-config GMODULE_CFLAGS C compiler flags for GMODULE, overriding pkg-config GMODULE_LIBS linker flags for GMODULE, overriding pkg-config pluginexeclibdir pluginexecdir DBUS_CFLAGS C compiler flags for DBUS, overriding pkg-config DBUS_LIBS linker flags for DBUS, overriding pkg-config TP_GLIB_CFLAGS C compiler flags for TP_GLIB, overriding pkg-config TP_GLIB_LIBS linker flags for TP_GLIB, overriding pkg-config PYTHON the Python interpreter WOCKY_CFLAGS C compiler flags for WOCKY, overriding pkg-config WOCKY_LIBS linker flags for WOCKY, overriding pkg-config SOUP_CFLAGS C compiler flags for SOUP, overriding pkg-config SOUP_LIBS linker flags for SOUP, overriding pkg-config NICE_CFLAGS C compiler flags for NICE, overriding pkg-config NICE_LIBS linker flags for NICE, overriding pkg-config Use these variables to override the choices made by `configure' or to help it to find libraries and programs with nonstandard names/locations. Report bugs to . _ACEOF ac_status=$? fi if test "$ac_init_help" = "recursive"; then # If there are subdirs, report their specific --help. for ac_dir in : $ac_subdirs_all; do test "x$ac_dir" = x: && continue test -d "$ac_dir" || { cd "$srcdir" && ac_pwd=`pwd` && srcdir=. && test -d "$ac_dir"; } || continue ac_builddir=. case "$ac_dir" in .) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;; *) ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'` # A ".." for each directory in $ac_dir_suffix. ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'` case $ac_top_builddir_sub in "") ac_top_builddir_sub=. ac_top_build_prefix= ;; *) ac_top_build_prefix=$ac_top_builddir_sub/ ;; esac ;; esac ac_abs_top_builddir=$ac_pwd ac_abs_builddir=$ac_pwd$ac_dir_suffix # for backward compatibility: ac_top_builddir=$ac_top_build_prefix case $srcdir in .) # We are building in place. ac_srcdir=. ac_top_srcdir=$ac_top_builddir_sub ac_abs_top_srcdir=$ac_pwd ;; [\\/]* | ?:[\\/]* ) # Absolute name. ac_srcdir=$srcdir$ac_dir_suffix; ac_top_srcdir=$srcdir ac_abs_top_srcdir=$srcdir ;; *) # Relative name. ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix ac_top_srcdir=$ac_top_build_prefix$srcdir ac_abs_top_srcdir=$ac_pwd/$srcdir ;; esac ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix cd "$ac_dir" || { ac_status=$?; continue; } # Check for guested configure. if test -f "$ac_srcdir/configure.gnu"; then echo && $SHELL "$ac_srcdir/configure.gnu" --help=recursive elif test -f "$ac_srcdir/configure"; then echo && $SHELL "$ac_srcdir/configure" --help=recursive else $as_echo "$as_me: WARNING: no configuration information is in $ac_dir" >&2 fi || ac_status=$? cd "$ac_pwd" || { ac_status=$?; break; } done fi test -n "$ac_init_help" && exit $ac_status if $ac_init_version; then cat <<\_ACEOF Telepathy Gabble configure 0.18.2 generated by GNU Autoconf 2.69 Copyright (C) 2012 Free Software Foundation, Inc. This configure script is free software; the Free Software Foundation gives unlimited permission to copy, distribute and modify it. _ACEOF exit fi ## ------------------------ ## ## Autoconf initialization. ## ## ------------------------ ## # ac_fn_c_try_compile LINENO # -------------------------- # Try to compile conftest.$ac_ext, and return whether this succeeded. ac_fn_c_try_compile () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack rm -f conftest.$ac_objext if { { ac_try="$ac_compile" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_compile") 2>conftest.err ac_status=$? if test -s conftest.err; then grep -v '^ *+' conftest.err >conftest.er1 cat conftest.er1 >&5 mv -f conftest.er1 conftest.err fi $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } && { test -z "$ac_c_werror_flag" || test ! -s conftest.err } && test -s conftest.$ac_objext; then : ac_retval=0 else $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_retval=1 fi eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno as_fn_set_status $ac_retval } # ac_fn_c_try_compile # ac_fn_c_try_cpp LINENO # ---------------------- # Try to preprocess conftest.$ac_ext, and return whether this succeeded. ac_fn_c_try_cpp () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack if { { ac_try="$ac_cpp conftest.$ac_ext" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_cpp conftest.$ac_ext") 2>conftest.err ac_status=$? if test -s conftest.err; then grep -v '^ *+' conftest.err >conftest.er1 cat conftest.er1 >&5 mv -f conftest.er1 conftest.err fi $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } > conftest.i && { test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" || test ! -s conftest.err }; then : ac_retval=0 else $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_retval=1 fi eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno as_fn_set_status $ac_retval } # ac_fn_c_try_cpp # ac_fn_c_check_header_mongrel LINENO HEADER VAR INCLUDES # ------------------------------------------------------- # Tests whether HEADER exists, giving a warning if it cannot be compiled using # the include files in INCLUDES and setting the cache variable VAR # accordingly. ac_fn_c_check_header_mongrel () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack if eval \${$3+:} false; then : { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 $as_echo_n "checking for $2... " >&6; } if eval \${$3+:} false; then : $as_echo_n "(cached) " >&6 fi eval ac_res=\$$3 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } else # Is the header compilable? { $as_echo "$as_me:${as_lineno-$LINENO}: checking $2 usability" >&5 $as_echo_n "checking $2 usability... " >&6; } cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $4 #include <$2> _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_header_compiler=yes else ac_header_compiler=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_header_compiler" >&5 $as_echo "$ac_header_compiler" >&6; } # Is the header present? { $as_echo "$as_me:${as_lineno-$LINENO}: checking $2 presence" >&5 $as_echo_n "checking $2 presence... " >&6; } cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include <$2> _ACEOF if ac_fn_c_try_cpp "$LINENO"; then : ac_header_preproc=yes else ac_header_preproc=no fi rm -f conftest.err conftest.i conftest.$ac_ext { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_header_preproc" >&5 $as_echo "$ac_header_preproc" >&6; } # So? What about this header? case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in #(( yes:no: ) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: accepted by the compiler, rejected by the preprocessor!" >&5 $as_echo "$as_me: WARNING: $2: accepted by the compiler, rejected by the preprocessor!" >&2;} { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: proceeding with the compiler's result" >&5 $as_echo "$as_me: WARNING: $2: proceeding with the compiler's result" >&2;} ;; no:yes:* ) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: present but cannot be compiled" >&5 $as_echo "$as_me: WARNING: $2: present but cannot be compiled" >&2;} { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: check for missing prerequisite headers?" >&5 $as_echo "$as_me: WARNING: $2: check for missing prerequisite headers?" >&2;} { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: see the Autoconf documentation" >&5 $as_echo "$as_me: WARNING: $2: see the Autoconf documentation" >&2;} { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: section \"Present But Cannot Be Compiled\"" >&5 $as_echo "$as_me: WARNING: $2: section \"Present But Cannot Be Compiled\"" >&2;} { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: proceeding with the compiler's result" >&5 $as_echo "$as_me: WARNING: $2: proceeding with the compiler's result" >&2;} ( $as_echo "## -------------------------------------------------------------------------------------------- ## ## Report this to https://bugs.freedesktop.org/enter_bug.cgi?product=Telepathy&component=gabble ## ## -------------------------------------------------------------------------------------------- ##" ) | sed "s/^/$as_me: WARNING: /" >&2 ;; esac { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 $as_echo_n "checking for $2... " >&6; } if eval \${$3+:} false; then : $as_echo_n "(cached) " >&6 else eval "$3=\$ac_header_compiler" fi eval ac_res=\$$3 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } fi eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno } # ac_fn_c_check_header_mongrel # ac_fn_c_try_run LINENO # ---------------------- # Try to link conftest.$ac_ext, and return whether this succeeded. Assumes # that executables *can* be run. ac_fn_c_try_run () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack if { { ac_try="$ac_link" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_link") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } && { ac_try='./conftest$ac_exeext' { { case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_try") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; }; then : ac_retval=0 else $as_echo "$as_me: program exited with status $ac_status" >&5 $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_retval=$ac_status fi rm -rf conftest.dSYM conftest_ipa8_conftest.oo eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno as_fn_set_status $ac_retval } # ac_fn_c_try_run # ac_fn_c_check_header_compile LINENO HEADER VAR INCLUDES # ------------------------------------------------------- # Tests whether HEADER exists and can be compiled using the include files in # INCLUDES, setting the cache variable VAR accordingly. ac_fn_c_check_header_compile () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 $as_echo_n "checking for $2... " >&6; } if eval \${$3+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $4 #include <$2> _ACEOF if ac_fn_c_try_compile "$LINENO"; then : eval "$3=yes" else eval "$3=no" fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi eval ac_res=\$$3 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno } # ac_fn_c_check_header_compile # ac_fn_c_try_link LINENO # ----------------------- # Try to link conftest.$ac_ext, and return whether this succeeded. ac_fn_c_try_link () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack rm -f conftest.$ac_objext conftest$ac_exeext if { { ac_try="$ac_link" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_link") 2>conftest.err ac_status=$? if test -s conftest.err; then grep -v '^ *+' conftest.err >conftest.er1 cat conftest.er1 >&5 mv -f conftest.er1 conftest.err fi $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } && { test -z "$ac_c_werror_flag" || test ! -s conftest.err } && test -s conftest$ac_exeext && { test "$cross_compiling" = yes || test -x conftest$ac_exeext }; then : ac_retval=0 else $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_retval=1 fi # Delete the IPA/IPO (Inter Procedural Analysis/Optimization) information # created by the PGI compiler (conftest_ipa8_conftest.oo), as it would # interfere with the next link command; also delete a directory that is # left behind by Apple's compiler. We do this before executing the actions. rm -rf conftest.dSYM conftest_ipa8_conftest.oo eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno as_fn_set_status $ac_retval } # ac_fn_c_try_link # ac_fn_c_check_func LINENO FUNC VAR # ---------------------------------- # Tests whether FUNC exists, setting the cache variable VAR accordingly ac_fn_c_check_func () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 $as_echo_n "checking for $2... " >&6; } if eval \${$3+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Define $2 to an innocuous variant, in case declares $2. For example, HP-UX 11i declares gettimeofday. */ #define $2 innocuous_$2 /* System header to define __stub macros and hopefully few prototypes, which can conflict with char $2 (); below. Prefer to if __STDC__ is defined, since exists even on freestanding compilers. */ #ifdef __STDC__ # include #else # include #endif #undef $2 /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char $2 (); /* The GNU C library defines this for functions which it implements to always fail with ENOSYS. Some functions are actually named something starting with __ and the normal name is an alias. */ #if defined __stub_$2 || defined __stub___$2 choke me #endif int main () { return $2 (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : eval "$3=yes" else eval "$3=no" fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext fi eval ac_res=\$$3 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno } # ac_fn_c_check_func cat >config.log <<_ACEOF This file contains any messages produced by compilers while running configure, to aid debugging if configure makes a mistake. It was created by Telepathy Gabble $as_me 0.18.2, which was generated by GNU Autoconf 2.69. Invocation command line was $ $0 $@ _ACEOF exec 5>>config.log { cat <<_ASUNAME ## --------- ## ## Platform. ## ## --------- ## hostname = `(hostname || uname -n) 2>/dev/null | sed 1q` uname -m = `(uname -m) 2>/dev/null || echo unknown` uname -r = `(uname -r) 2>/dev/null || echo unknown` uname -s = `(uname -s) 2>/dev/null || echo unknown` uname -v = `(uname -v) 2>/dev/null || echo unknown` /usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null || echo unknown` /bin/uname -X = `(/bin/uname -X) 2>/dev/null || echo unknown` /bin/arch = `(/bin/arch) 2>/dev/null || echo unknown` /usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null || echo unknown` /usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null || echo unknown` /usr/bin/hostinfo = `(/usr/bin/hostinfo) 2>/dev/null || echo unknown` /bin/machine = `(/bin/machine) 2>/dev/null || echo unknown` /usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null || echo unknown` /bin/universe = `(/bin/universe) 2>/dev/null || echo unknown` _ASUNAME as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. $as_echo "PATH: $as_dir" done IFS=$as_save_IFS } >&5 cat >&5 <<_ACEOF ## ----------- ## ## Core tests. ## ## ----------- ## _ACEOF # Keep a trace of the command line. # Strip out --no-create and --no-recursion so they do not pile up. # Strip out --silent because we don't want to record it for future runs. # Also quote any args containing shell meta-characters. # Make two passes to allow for proper duplicate-argument suppression. ac_configure_args= ac_configure_args0= ac_configure_args1= ac_must_keep_next=false for ac_pass in 1 2 do for ac_arg do case $ac_arg in -no-create | --no-c* | -n | -no-recursion | --no-r*) continue ;; -q | -quiet | --quiet | --quie | --qui | --qu | --q \ | -silent | --silent | --silen | --sile | --sil) continue ;; *\'*) ac_arg=`$as_echo "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;; esac case $ac_pass in 1) as_fn_append ac_configure_args0 " '$ac_arg'" ;; 2) as_fn_append ac_configure_args1 " '$ac_arg'" if test $ac_must_keep_next = true; then ac_must_keep_next=false # Got value, back to normal. else case $ac_arg in *=* | --config-cache | -C | -disable-* | --disable-* \ | -enable-* | --enable-* | -gas | --g* | -nfp | --nf* \ | -q | -quiet | --q* | -silent | --sil* | -v | -verb* \ | -with-* | --with-* | -without-* | --without-* | --x) case "$ac_configure_args0 " in "$ac_configure_args1"*" '$ac_arg' "* ) continue ;; esac ;; -* ) ac_must_keep_next=true ;; esac fi as_fn_append ac_configure_args " '$ac_arg'" ;; esac done done { ac_configure_args0=; unset ac_configure_args0;} { ac_configure_args1=; unset ac_configure_args1;} # When interrupted or exit'd, cleanup temporary files, and complete # config.log. We remove comments because anyway the quotes in there # would cause problems or look ugly. # WARNING: Use '\'' to represent an apostrophe within the trap. # WARNING: Do not start the trap code with a newline, due to a FreeBSD 4.0 bug. trap 'exit_status=$? # Save into config.log some information that might help in debugging. { echo $as_echo "## ---------------- ## ## Cache variables. ## ## ---------------- ##" echo # The following way of writing the cache mishandles newlines in values, ( for ac_var in `(set) 2>&1 | sed -n '\''s/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'\''`; do eval ac_val=\$$ac_var case $ac_val in #( *${as_nl}*) case $ac_var in #( *_cv_*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5 $as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;; esac case $ac_var in #( _ | IFS | as_nl) ;; #( BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #( *) { eval $ac_var=; unset $ac_var;} ;; esac ;; esac done (set) 2>&1 | case $as_nl`(ac_space='\'' '\''; set) 2>&1` in #( *${as_nl}ac_space=\ *) sed -n \ "s/'\''/'\''\\\\'\'''\''/g; s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\''\\2'\''/p" ;; #( *) sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p" ;; esac | sort ) echo $as_echo "## ----------------- ## ## Output variables. ## ## ----------------- ##" echo for ac_var in $ac_subst_vars do eval ac_val=\$$ac_var case $ac_val in *\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;; esac $as_echo "$ac_var='\''$ac_val'\''" done | sort echo if test -n "$ac_subst_files"; then $as_echo "## ------------------- ## ## File substitutions. ## ## ------------------- ##" echo for ac_var in $ac_subst_files do eval ac_val=\$$ac_var case $ac_val in *\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;; esac $as_echo "$ac_var='\''$ac_val'\''" done | sort echo fi if test -s confdefs.h; then $as_echo "## ----------- ## ## confdefs.h. ## ## ----------- ##" echo cat confdefs.h echo fi test "$ac_signal" != 0 && $as_echo "$as_me: caught signal $ac_signal" $as_echo "$as_me: exit $exit_status" } >&5 rm -f core *.core core.conftest.* && rm -f -r conftest* confdefs* conf$$* $ac_clean_files && exit $exit_status ' 0 for ac_signal in 1 2 13 15; do trap 'ac_signal='$ac_signal'; as_fn_exit 1' $ac_signal done ac_signal=0 # confdefs.h avoids OS command line length limits that DEFS can exceed. rm -f -r conftest* confdefs.h $as_echo "/* confdefs.h */" > confdefs.h # Predefined preprocessor variables. cat >>confdefs.h <<_ACEOF #define PACKAGE_NAME "$PACKAGE_NAME" _ACEOF cat >>confdefs.h <<_ACEOF #define PACKAGE_TARNAME "$PACKAGE_TARNAME" _ACEOF cat >>confdefs.h <<_ACEOF #define PACKAGE_VERSION "$PACKAGE_VERSION" _ACEOF cat >>confdefs.h <<_ACEOF #define PACKAGE_STRING "$PACKAGE_STRING" _ACEOF cat >>confdefs.h <<_ACEOF #define PACKAGE_BUGREPORT "$PACKAGE_BUGREPORT" _ACEOF cat >>confdefs.h <<_ACEOF #define PACKAGE_URL "$PACKAGE_URL" _ACEOF # Let the site file select an alternate cache file if it wants to. # Prefer an explicitly selected file to automatically selected ones. ac_site_file1=NONE ac_site_file2=NONE if test -n "$CONFIG_SITE"; then # We do not want a PATH search for config.site. case $CONFIG_SITE in #(( -*) ac_site_file1=./$CONFIG_SITE;; */*) ac_site_file1=$CONFIG_SITE;; *) ac_site_file1=./$CONFIG_SITE;; esac elif test "x$prefix" != xNONE; then ac_site_file1=$prefix/share/config.site ac_site_file2=$prefix/etc/config.site else ac_site_file1=$ac_default_prefix/share/config.site ac_site_file2=$ac_default_prefix/etc/config.site fi for ac_site_file in "$ac_site_file1" "$ac_site_file2" do test "x$ac_site_file" = xNONE && continue if test /dev/null != "$ac_site_file" && test -r "$ac_site_file"; then { $as_echo "$as_me:${as_lineno-$LINENO}: loading site script $ac_site_file" >&5 $as_echo "$as_me: loading site script $ac_site_file" >&6;} sed 's/^/| /' "$ac_site_file" >&5 . "$ac_site_file" \ || { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "failed to load site script $ac_site_file See \`config.log' for more details" "$LINENO" 5; } fi done if test -r "$cache_file"; then # Some versions of bash will fail to source /dev/null (special files # actually), so we avoid doing that. DJGPP emulates it as a regular file. if test /dev/null != "$cache_file" && test -f "$cache_file"; then { $as_echo "$as_me:${as_lineno-$LINENO}: loading cache $cache_file" >&5 $as_echo "$as_me: loading cache $cache_file" >&6;} case $cache_file in [\\/]* | ?:[\\/]* ) . "$cache_file";; *) . "./$cache_file";; esac fi else { $as_echo "$as_me:${as_lineno-$LINENO}: creating cache $cache_file" >&5 $as_echo "$as_me: creating cache $cache_file" >&6;} >$cache_file fi as_fn_append ac_header_list " arpa/inet.h" as_fn_append ac_header_list " arpa/nameser.h" as_fn_append ac_header_list " fcntl.h" as_fn_append ac_header_list " ifaddrs.h" as_fn_append ac_header_list " netdb.h" as_fn_append ac_header_list " netinet/in.h" as_fn_append ac_header_list " sys/ioctl.h" as_fn_append ac_header_list " sys/un.h" as_fn_append ac_header_list " unistd.h" # Check that the precious variables saved in the cache have kept the same # value. ac_cache_corrupted=false for ac_var in $ac_precious_vars; do eval ac_old_set=\$ac_cv_env_${ac_var}_set eval ac_new_set=\$ac_env_${ac_var}_set eval ac_old_val=\$ac_cv_env_${ac_var}_value eval ac_new_val=\$ac_env_${ac_var}_value case $ac_old_set,$ac_new_set in set,) { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&5 $as_echo "$as_me: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&2;} ac_cache_corrupted=: ;; ,set) { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was not set in the previous run" >&5 $as_echo "$as_me: error: \`$ac_var' was not set in the previous run" >&2;} ac_cache_corrupted=: ;; ,);; *) if test "x$ac_old_val" != "x$ac_new_val"; then # differences in whitespace do not lead to failure. ac_old_val_w=`echo x $ac_old_val` ac_new_val_w=`echo x $ac_new_val` if test "$ac_old_val_w" != "$ac_new_val_w"; then { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' has changed since the previous run:" >&5 $as_echo "$as_me: error: \`$ac_var' has changed since the previous run:" >&2;} ac_cache_corrupted=: else { $as_echo "$as_me:${as_lineno-$LINENO}: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&5 $as_echo "$as_me: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&2;} eval $ac_var=\$ac_old_val fi { $as_echo "$as_me:${as_lineno-$LINENO}: former value: \`$ac_old_val'" >&5 $as_echo "$as_me: former value: \`$ac_old_val'" >&2;} { $as_echo "$as_me:${as_lineno-$LINENO}: current value: \`$ac_new_val'" >&5 $as_echo "$as_me: current value: \`$ac_new_val'" >&2;} fi;; esac # Pass precious variables to config.status. if test "$ac_new_set" = set; then case $ac_new_val in *\'*) ac_arg=$ac_var=`$as_echo "$ac_new_val" | sed "s/'/'\\\\\\\\''/g"` ;; *) ac_arg=$ac_var=$ac_new_val ;; esac case " $ac_configure_args " in *" '$ac_arg' "*) ;; # Avoid dups. Use of quotes ensures accuracy. *) as_fn_append ac_configure_args " '$ac_arg'" ;; esac fi done if $ac_cache_corrupted; then { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} { $as_echo "$as_me:${as_lineno-$LINENO}: error: changes in the environment can compromise the build" >&5 $as_echo "$as_me: error: changes in the environment can compromise the build" >&2;} as_fn_error $? "run \`make distclean' and/or \`rm $cache_file' and start over" "$LINENO" 5 fi ## -------------------- ## ## Main body of script. ## ## -------------------- ## ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu am__api_version='1.14' ac_aux_dir= for ac_dir in "$srcdir" "$srcdir/.." "$srcdir/../.."; do if test -f "$ac_dir/install-sh"; then ac_aux_dir=$ac_dir ac_install_sh="$ac_aux_dir/install-sh -c" break elif test -f "$ac_dir/install.sh"; then ac_aux_dir=$ac_dir ac_install_sh="$ac_aux_dir/install.sh -c" break elif test -f "$ac_dir/shtool"; then ac_aux_dir=$ac_dir ac_install_sh="$ac_aux_dir/shtool install -c" break fi done if test -z "$ac_aux_dir"; then as_fn_error $? "cannot find install-sh, install.sh, or shtool in \"$srcdir\" \"$srcdir/..\" \"$srcdir/../..\"" "$LINENO" 5 fi # These three variables are undocumented and unsupported, # and are intended to be withdrawn in a future Autoconf release. # They can cause serious problems if a builder's source tree is in a directory # whose full name contains unusual characters. ac_config_guess="$SHELL $ac_aux_dir/config.guess" # Please don't use this var. ac_config_sub="$SHELL $ac_aux_dir/config.sub" # Please don't use this var. ac_configure="$SHELL $ac_aux_dir/configure" # Please don't use this var. # Find a good install program. We prefer a C program (faster), # so one script is as good as another. But avoid the broken or # incompatible versions: # SysV /etc/install, /usr/sbin/install # SunOS /usr/etc/install # IRIX /sbin/install # AIX /bin/install # AmigaOS /C/install, which installs bootblocks on floppy discs # AIX 4 /usr/bin/installbsd, which doesn't work without a -g flag # AFS /usr/afsws/bin/install, which mishandles nonexistent args # SVR4 /usr/ucb/install, which tries to use the nonexistent group "staff" # OS/2's system install, which has a completely different semantic # ./install, which can be erroneously created by make from ./install.sh. # Reject install programs that cannot install multiple files. { $as_echo "$as_me:${as_lineno-$LINENO}: checking for a BSD-compatible install" >&5 $as_echo_n "checking for a BSD-compatible install... " >&6; } if test -z "$INSTALL"; then if ${ac_cv_path_install+:} false; then : $as_echo_n "(cached) " >&6 else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. # Account for people who put trailing slashes in PATH elements. case $as_dir/ in #(( ./ | .// | /[cC]/* | \ /etc/* | /usr/sbin/* | /usr/etc/* | /sbin/* | /usr/afsws/bin/* | \ ?:[\\/]os2[\\/]install[\\/]* | ?:[\\/]OS2[\\/]INSTALL[\\/]* | \ /usr/ucb/* ) ;; *) # OSF1 and SCO ODT 3.0 have their own names for install. # Don't use installbsd from OSF since it installs stuff as root # by default. for ac_prog in ginstall scoinst install; do for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_prog$ac_exec_ext"; then if test $ac_prog = install && grep dspmsg "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then # AIX install. It has an incompatible calling convention. : elif test $ac_prog = install && grep pwplus "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then # program-specific install script used by HP pwplus--don't use. : else rm -rf conftest.one conftest.two conftest.dir echo one > conftest.one echo two > conftest.two mkdir conftest.dir if "$as_dir/$ac_prog$ac_exec_ext" -c conftest.one conftest.two "`pwd`/conftest.dir" && test -s conftest.one && test -s conftest.two && test -s conftest.dir/conftest.one && test -s conftest.dir/conftest.two then ac_cv_path_install="$as_dir/$ac_prog$ac_exec_ext -c" break 3 fi fi fi done done ;; esac done IFS=$as_save_IFS rm -rf conftest.one conftest.two conftest.dir fi if test "${ac_cv_path_install+set}" = set; then INSTALL=$ac_cv_path_install else # As a last resort, use the slow shell script. Don't cache a # value for INSTALL within a source directory, because that will # break other packages using the cache if that directory is # removed, or if the value is a relative name. INSTALL=$ac_install_sh fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $INSTALL" >&5 $as_echo "$INSTALL" >&6; } # Use test -z because SunOS4 sh mishandles braces in ${var-val}. # It thinks the first close brace ends the variable substitution. test -z "$INSTALL_PROGRAM" && INSTALL_PROGRAM='${INSTALL}' test -z "$INSTALL_SCRIPT" && INSTALL_SCRIPT='${INSTALL}' test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644' { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether build environment is sane" >&5 $as_echo_n "checking whether build environment is sane... " >&6; } # Reject unsafe characters in $srcdir or the absolute working directory # name. Accept space and tab only in the latter. am_lf=' ' case `pwd` in *[\\\"\#\$\&\'\`$am_lf]*) as_fn_error $? "unsafe absolute working directory name" "$LINENO" 5;; esac case $srcdir in *[\\\"\#\$\&\'\`$am_lf\ \ ]*) as_fn_error $? "unsafe srcdir value: '$srcdir'" "$LINENO" 5;; esac # Do 'set' in a subshell so we don't clobber the current shell's # arguments. Must try -L first in case configure is actually a # symlink; some systems play weird games with the mod time of symlinks # (eg FreeBSD returns the mod time of the symlink's containing # directory). if ( am_has_slept=no for am_try in 1 2; do echo "timestamp, slept: $am_has_slept" > conftest.file set X `ls -Lt "$srcdir/configure" conftest.file 2> /dev/null` if test "$*" = "X"; then # -L didn't work. set X `ls -t "$srcdir/configure" conftest.file` fi if test "$*" != "X $srcdir/configure conftest.file" \ && test "$*" != "X conftest.file $srcdir/configure"; then # If neither matched, then we have a broken ls. This can happen # if, for instance, CONFIG_SHELL is bash and it inherits a # broken ls alias from the environment. This has actually # happened. Such a system could not be considered "sane". as_fn_error $? "ls -t appears to fail. Make sure there is not a broken alias in your environment" "$LINENO" 5 fi if test "$2" = conftest.file || test $am_try -eq 2; then break fi # Just in case. sleep 1 am_has_slept=yes done test "$2" = conftest.file ) then # Ok. : else as_fn_error $? "newly created file is older than distributed files! Check your system clock" "$LINENO" 5 fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } # If we didn't sleep, we still need to ensure time stamps of config.status and # generated files are strictly newer. am_sleep_pid= if grep 'slept: no' conftest.file >/dev/null 2>&1; then ( sleep 1 ) & am_sleep_pid=$! fi rm -f conftest.file test "$program_prefix" != NONE && program_transform_name="s&^&$program_prefix&;$program_transform_name" # Use a double $ so make ignores it. test "$program_suffix" != NONE && program_transform_name="s&\$&$program_suffix&;$program_transform_name" # Double any \ or $. # By default was `s,x,x', remove it if useless. ac_script='s/[\\$]/&&/g;s/;s,x,x,$//' program_transform_name=`$as_echo "$program_transform_name" | sed "$ac_script"` # expand $ac_aux_dir to an absolute path am_aux_dir=`cd $ac_aux_dir && pwd` if test x"${MISSING+set}" != xset; then case $am_aux_dir in *\ * | *\ *) MISSING="\${SHELL} \"$am_aux_dir/missing\"" ;; *) MISSING="\${SHELL} $am_aux_dir/missing" ;; esac fi # Use eval to expand $SHELL if eval "$MISSING --is-lightweight"; then am_missing_run="$MISSING " else am_missing_run= { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: 'missing' script is too old or missing" >&5 $as_echo "$as_me: WARNING: 'missing' script is too old or missing" >&2;} fi if test x"${install_sh}" != xset; then case $am_aux_dir in *\ * | *\ *) install_sh="\${SHELL} '$am_aux_dir/install-sh'" ;; *) install_sh="\${SHELL} $am_aux_dir/install-sh" esac fi # Installed binaries are usually stripped using 'strip' when the user # run "make install-strip". However 'strip' might not be the right # tool to use in cross-compilation environments, therefore Automake # will honor the 'STRIP' environment variable to overrule this program. if test "$cross_compiling" != no; then if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}strip", so it can be a program name with args. set dummy ${ac_tool_prefix}strip; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_STRIP+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$STRIP"; then ac_cv_prog_STRIP="$STRIP" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_STRIP="${ac_tool_prefix}strip" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi STRIP=$ac_cv_prog_STRIP if test -n "$STRIP"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $STRIP" >&5 $as_echo "$STRIP" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$ac_cv_prog_STRIP"; then ac_ct_STRIP=$STRIP # Extract the first word of "strip", so it can be a program name with args. set dummy strip; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_ac_ct_STRIP+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_STRIP"; then ac_cv_prog_ac_ct_STRIP="$ac_ct_STRIP" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_STRIP="strip" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_STRIP=$ac_cv_prog_ac_ct_STRIP if test -n "$ac_ct_STRIP"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_STRIP" >&5 $as_echo "$ac_ct_STRIP" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test "x$ac_ct_STRIP" = x; then STRIP=":" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac STRIP=$ac_ct_STRIP fi else STRIP="$ac_cv_prog_STRIP" fi fi INSTALL_STRIP_PROGRAM="\$(install_sh) -c -s" { $as_echo "$as_me:${as_lineno-$LINENO}: checking for a thread-safe mkdir -p" >&5 $as_echo_n "checking for a thread-safe mkdir -p... " >&6; } if test -z "$MKDIR_P"; then if ${ac_cv_path_mkdir+:} false; then : $as_echo_n "(cached) " >&6 else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH$PATH_SEPARATOR/opt/sfw/bin do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_prog in mkdir gmkdir; do for ac_exec_ext in '' $ac_executable_extensions; do as_fn_executable_p "$as_dir/$ac_prog$ac_exec_ext" || continue case `"$as_dir/$ac_prog$ac_exec_ext" --version 2>&1` in #( 'mkdir (GNU coreutils) '* | \ 'mkdir (coreutils) '* | \ 'mkdir (fileutils) '4.1*) ac_cv_path_mkdir=$as_dir/$ac_prog$ac_exec_ext break 3;; esac done done done IFS=$as_save_IFS fi test -d ./--version && rmdir ./--version if test "${ac_cv_path_mkdir+set}" = set; then MKDIR_P="$ac_cv_path_mkdir -p" else # As a last resort, use the slow shell script. Don't cache a # value for MKDIR_P within a source directory, because that will # break other packages using the cache if that directory is # removed, or if the value is a relative name. MKDIR_P="$ac_install_sh -d" fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $MKDIR_P" >&5 $as_echo "$MKDIR_P" >&6; } for ac_prog in gawk mawk nawk awk do # Extract the first word of "$ac_prog", so it can be a program name with args. set dummy $ac_prog; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_AWK+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$AWK"; then ac_cv_prog_AWK="$AWK" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_AWK="$ac_prog" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi AWK=$ac_cv_prog_AWK if test -n "$AWK"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $AWK" >&5 $as_echo "$AWK" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi test -n "$AWK" && break done { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether ${MAKE-make} sets \$(MAKE)" >&5 $as_echo_n "checking whether ${MAKE-make} sets \$(MAKE)... " >&6; } set x ${MAKE-make} ac_make=`$as_echo "$2" | sed 's/+/p/g; s/[^a-zA-Z0-9_]/_/g'` if eval \${ac_cv_prog_make_${ac_make}_set+:} false; then : $as_echo_n "(cached) " >&6 else cat >conftest.make <<\_ACEOF SHELL = /bin/sh all: @echo '@@@%%%=$(MAKE)=@@@%%%' _ACEOF # GNU make sometimes prints "make[1]: Entering ...", which would confuse us. case `${MAKE-make} -f conftest.make 2>/dev/null` in *@@@%%%=?*=@@@%%%*) eval ac_cv_prog_make_${ac_make}_set=yes;; *) eval ac_cv_prog_make_${ac_make}_set=no;; esac rm -f conftest.make fi if eval test \$ac_cv_prog_make_${ac_make}_set = yes; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } SET_MAKE= else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } SET_MAKE="MAKE=${MAKE-make}" fi rm -rf .tst 2>/dev/null mkdir .tst 2>/dev/null if test -d .tst; then am__leading_dot=. else am__leading_dot=_ fi rmdir .tst 2>/dev/null # Check whether --enable-silent-rules was given. if test "${enable_silent_rules+set}" = set; then : enableval=$enable_silent_rules; fi case $enable_silent_rules in # ((( yes) AM_DEFAULT_VERBOSITY=0;; no) AM_DEFAULT_VERBOSITY=1;; *) AM_DEFAULT_VERBOSITY=1;; esac am_make=${MAKE-make} { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $am_make supports nested variables" >&5 $as_echo_n "checking whether $am_make supports nested variables... " >&6; } if ${am_cv_make_support_nested_variables+:} false; then : $as_echo_n "(cached) " >&6 else if $as_echo 'TRUE=$(BAR$(V)) BAR0=false BAR1=true V=1 am__doit: @$(TRUE) .PHONY: am__doit' | $am_make -f - >/dev/null 2>&1; then am_cv_make_support_nested_variables=yes else am_cv_make_support_nested_variables=no fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_make_support_nested_variables" >&5 $as_echo "$am_cv_make_support_nested_variables" >&6; } if test $am_cv_make_support_nested_variables = yes; then AM_V='$(V)' AM_DEFAULT_V='$(AM_DEFAULT_VERBOSITY)' else AM_V=$AM_DEFAULT_VERBOSITY AM_DEFAULT_V=$AM_DEFAULT_VERBOSITY fi AM_BACKSLASH='\' if test "`cd $srcdir && pwd`" != "`pwd`"; then # Use -I$(srcdir) only when $(srcdir) != ., so that make's output # is not polluted with repeated "-I." am__isrc=' -I$(srcdir)' # test to see if srcdir already configured if test -f $srcdir/config.status; then as_fn_error $? "source directory already configured; run \"make distclean\" there first" "$LINENO" 5 fi fi # test whether we have cygpath if test -z "$CYGPATH_W"; then if (cygpath --version) >/dev/null 2>/dev/null; then CYGPATH_W='cygpath -w' else CYGPATH_W=echo fi fi # Define the identity of the package. PACKAGE='telepathy-gabble' VERSION='0.18.2' cat >>confdefs.h <<_ACEOF #define PACKAGE "$PACKAGE" _ACEOF cat >>confdefs.h <<_ACEOF #define VERSION "$VERSION" _ACEOF # Some tools Automake needs. ACLOCAL=${ACLOCAL-"${am_missing_run}aclocal-${am__api_version}"} AUTOCONF=${AUTOCONF-"${am_missing_run}autoconf"} AUTOMAKE=${AUTOMAKE-"${am_missing_run}automake-${am__api_version}"} AUTOHEADER=${AUTOHEADER-"${am_missing_run}autoheader"} MAKEINFO=${MAKEINFO-"${am_missing_run}makeinfo"} # For better backward compatibility. To be removed once Automake 1.9.x # dies out for good. For more background, see: # # mkdir_p='$(MKDIR_P)' # We need awk for the "check" target. The system "awk" is bad on # some platforms. # Always define AMTAR for backward compatibility. Yes, it's still used # in the wild :-( We should find a proper way to deprecate it ... AMTAR='$${TAR-tar}' # We'll loop over all known methods to create a tar archive until one works. _am_tools='gnutar plaintar pax cpio none' # The POSIX 1988 'ustar' format is defined with fixed-size fields. # There is notably a 21 bits limit for the UID and the GID. In fact, # the 'pax' utility can hang on bigger UID/GID (see automake bug#8343 # and bug#13588). am_max_uid=2097151 # 2^21 - 1 am_max_gid=$am_max_uid # The $UID and $GID variables are not portable, so we need to resort # to the POSIX-mandated id(1) utility. Errors in the 'id' calls # below are definitely unexpected, so allow the users to see them # (that is, avoid stderr redirection). am_uid=`id -u || echo unknown` am_gid=`id -g || echo unknown` { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether UID '$am_uid' is supported by ustar format" >&5 $as_echo_n "checking whether UID '$am_uid' is supported by ustar format... " >&6; } if test $am_uid -le $am_max_uid; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } _am_tools=none fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether GID '$am_gid' is supported by ustar format" >&5 $as_echo_n "checking whether GID '$am_gid' is supported by ustar format... " >&6; } if test $am_gid -le $am_max_gid; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } _am_tools=none fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking how to create a ustar tar archive" >&5 $as_echo_n "checking how to create a ustar tar archive... " >&6; } # Go ahead even if we have the value already cached. We do so because we # need to set the values for the 'am__tar' and 'am__untar' variables. _am_tools=${am_cv_prog_tar_ustar-$_am_tools} for _am_tool in $_am_tools; do case $_am_tool in gnutar) for _am_tar in tar gnutar gtar; do { echo "$as_me:$LINENO: $_am_tar --version" >&5 ($_am_tar --version) >&5 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && break done am__tar="$_am_tar --format=ustar -chf - "'"$$tardir"' am__tar_="$_am_tar --format=ustar -chf - "'"$tardir"' am__untar="$_am_tar -xf -" ;; plaintar) # Must skip GNU tar: if it does not support --format= it doesn't create # ustar tarball either. (tar --version) >/dev/null 2>&1 && continue am__tar='tar chf - "$$tardir"' am__tar_='tar chf - "$tardir"' am__untar='tar xf -' ;; pax) am__tar='pax -L -x ustar -w "$$tardir"' am__tar_='pax -L -x ustar -w "$tardir"' am__untar='pax -r' ;; cpio) am__tar='find "$$tardir" -print | cpio -o -H ustar -L' am__tar_='find "$tardir" -print | cpio -o -H ustar -L' am__untar='cpio -i -H ustar -d' ;; none) am__tar=false am__tar_=false am__untar=false ;; esac # If the value was cached, stop now. We just wanted to have am__tar # and am__untar set. test -n "${am_cv_prog_tar_ustar}" && break # tar/untar a dummy directory, and stop if the command works. rm -rf conftest.dir mkdir conftest.dir echo GrepMe > conftest.dir/file { echo "$as_me:$LINENO: tardir=conftest.dir && eval $am__tar_ >conftest.tar" >&5 (tardir=conftest.dir && eval $am__tar_ >conftest.tar) >&5 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } rm -rf conftest.dir if test -s conftest.tar; then { echo "$as_me:$LINENO: $am__untar &5 ($am__untar &5 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } { echo "$as_me:$LINENO: cat conftest.dir/file" >&5 (cat conftest.dir/file) >&5 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } grep GrepMe conftest.dir/file >/dev/null 2>&1 && break fi done rm -rf conftest.dir if ${am_cv_prog_tar_ustar+:} false; then : $as_echo_n "(cached) " >&6 else am_cv_prog_tar_ustar=$_am_tool fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_prog_tar_ustar" >&5 $as_echo "$am_cv_prog_tar_ustar" >&6; } # POSIX will say in a future version that running "rm -f" with no argument # is OK; and we want to be able to make that assumption in our Makefile # recipes. So use an aggressive probe to check that the usage we want is # actually supported "in the wild" to an acceptable degree. # See automake bug#10828. # To make any issue more visible, cause the running configure to be aborted # by default if the 'rm' program in use doesn't match our expectations; the # user can still override this though. if rm -f && rm -fr && rm -rf; then : OK; else cat >&2 <<'END' Oops! Your 'rm' program seems unable to run without file operands specified on the command line, even when the '-f' option is present. This is contrary to the behaviour of most rm programs out there, and not conforming with the upcoming POSIX standard: Please tell bug-automake@gnu.org about your system, including the value of your $PATH and any error possibly output before this message. This can help us improve future automake versions. END if test x"$ACCEPT_INFERIOR_RM_PROGRAM" = x"yes"; then echo 'Configuration will proceed anyway, since you have set the' >&2 echo 'ACCEPT_INFERIOR_RM_PROGRAM variable to "yes"' >&2 echo >&2 else cat >&2 <<'END' Aborting the configuration process, to ensure you take notice of the issue. You can download and install GNU coreutils to get an 'rm' implementation that behaves properly: . If you want to complete the configuration process using your problematic 'rm' anyway, export the environment variable ACCEPT_INFERIOR_RM_PROGRAM to "yes", and re-run configure. END as_fn_error $? "Your 'rm' program is bad, sorry." "$LINENO" 5 fi fi ac_config_headers="$ac_config_headers config.h" DEPDIR="${am__leading_dot}deps" ac_config_commands="$ac_config_commands depfiles" am_make=${MAKE-make} cat > confinc << 'END' am__doit: @echo this is the am__doit target .PHONY: am__doit END # If we don't find an include directive, just comment out the code. { $as_echo "$as_me:${as_lineno-$LINENO}: checking for style of include used by $am_make" >&5 $as_echo_n "checking for style of include used by $am_make... " >&6; } am__include="#" am__quote= _am_result=none # First try GNU make style include. echo "include confinc" > confmf # Ignore all kinds of additional output from 'make'. case `$am_make -s -f confmf 2> /dev/null` in #( *the\ am__doit\ target*) am__include=include am__quote= _am_result=GNU ;; esac # Now try BSD make style include. if test "$am__include" = "#"; then echo '.include "confinc"' > confmf case `$am_make -s -f confmf 2> /dev/null` in #( *the\ am__doit\ target*) am__include=.include am__quote="\"" _am_result=BSD ;; esac fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $_am_result" >&5 $as_echo "$_am_result" >&6; } rm -f confinc confmf # Check whether --enable-dependency-tracking was given. if test "${enable_dependency_tracking+set}" = set; then : enableval=$enable_dependency_tracking; fi if test "x$enable_dependency_tracking" != xno; then am_depcomp="$ac_aux_dir/depcomp" AMDEPBACKSLASH='\' am__nodep='_no' fi if test "x$enable_dependency_tracking" != xno; then AMDEP_TRUE= AMDEP_FALSE='#' else AMDEP_TRUE='#' AMDEP_FALSE= fi ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args. set dummy ${ac_tool_prefix}gcc; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_CC+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_CC="${ac_tool_prefix}gcc" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 $as_echo "$CC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$ac_cv_prog_CC"; then ac_ct_CC=$CC # Extract the first word of "gcc", so it can be a program name with args. set dummy gcc; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_ac_ct_CC+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_CC"; then ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_CC="gcc" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_CC=$ac_cv_prog_ac_ct_CC if test -n "$ac_ct_CC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 $as_echo "$ac_ct_CC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test "x$ac_ct_CC" = x; then CC="" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac CC=$ac_ct_CC fi else CC="$ac_cv_prog_CC" fi if test -z "$CC"; then if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args. set dummy ${ac_tool_prefix}cc; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_CC+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_CC="${ac_tool_prefix}cc" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 $as_echo "$CC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi fi if test -z "$CC"; then # Extract the first word of "cc", so it can be a program name with args. set dummy cc; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_CC+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else ac_prog_rejected=no as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then if test "$as_dir/$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then ac_prog_rejected=yes continue fi ac_cv_prog_CC="cc" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS if test $ac_prog_rejected = yes; then # We found a bogon in the path, so make sure we never use it. set dummy $ac_cv_prog_CC shift if test $# != 0; then # We chose a different compiler from the bogus one. # However, it has the same basename, so the bogon will be chosen # first if we set CC to just the basename; use the full file name. shift ac_cv_prog_CC="$as_dir/$ac_word${1+' '}$@" fi fi fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 $as_echo "$CC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$CC"; then if test -n "$ac_tool_prefix"; then for ac_prog in cl.exe do # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. set dummy $ac_tool_prefix$ac_prog; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_CC+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_CC="$ac_tool_prefix$ac_prog" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 $as_echo "$CC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi test -n "$CC" && break done fi if test -z "$CC"; then ac_ct_CC=$CC for ac_prog in cl.exe do # Extract the first word of "$ac_prog", so it can be a program name with args. set dummy $ac_prog; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_ac_ct_CC+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_CC"; then ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_CC="$ac_prog" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_CC=$ac_cv_prog_ac_ct_CC if test -n "$ac_ct_CC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 $as_echo "$ac_ct_CC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi test -n "$ac_ct_CC" && break done if test "x$ac_ct_CC" = x; then CC="" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac CC=$ac_ct_CC fi fi fi test -z "$CC" && { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "no acceptable C compiler found in \$PATH See \`config.log' for more details" "$LINENO" 5; } # Provide some information about the compiler. $as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler version" >&5 set X $ac_compile ac_compiler=$2 for ac_option in --version -v -V -qversion; do { { ac_try="$ac_compiler $ac_option >&5" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_compiler $ac_option >&5") 2>conftest.err ac_status=$? if test -s conftest.err; then sed '10a\ ... rest of stderr output deleted ... 10q' conftest.err >conftest.er1 cat conftest.er1 >&5 fi rm -f conftest.er1 conftest.err $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } done cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF ac_clean_files_save=$ac_clean_files ac_clean_files="$ac_clean_files a.out a.out.dSYM a.exe b.out" # Try to create an executable without -o first, disregard a.out. # It will help us diagnose broken compilers, and finding out an intuition # of exeext. { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the C compiler works" >&5 $as_echo_n "checking whether the C compiler works... " >&6; } ac_link_default=`$as_echo "$ac_link" | sed 's/ -o *conftest[^ ]*//'` # The possible output files: ac_files="a.out conftest.exe conftest a.exe a_out.exe b.out conftest.*" ac_rmfiles= for ac_file in $ac_files do case $ac_file in *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; * ) ac_rmfiles="$ac_rmfiles $ac_file";; esac done rm -f $ac_rmfiles if { { ac_try="$ac_link_default" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_link_default") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then : # Autoconf-2.13 could set the ac_cv_exeext variable to `no'. # So ignore a value of `no', otherwise this would lead to `EXEEXT = no' # in a Makefile. We should not override ac_cv_exeext if it was cached, # so that the user can short-circuit this test for compilers unknown to # Autoconf. for ac_file in $ac_files '' do test -f "$ac_file" || continue case $ac_file in *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; [ab].out ) # We found the default executable, but exeext='' is most # certainly right. break;; *.* ) if test "${ac_cv_exeext+set}" = set && test "$ac_cv_exeext" != no; then :; else ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` fi # We set ac_cv_exeext here because the later test for it is not # safe: cross compilers may not add the suffix if given an `-o' # argument, so we may need to know it at that point already. # Even if this section looks crufty: it has the advantage of # actually working. break;; * ) break;; esac done test "$ac_cv_exeext" = no && ac_cv_exeext= else ac_file='' fi if test -z "$ac_file"; then : { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error 77 "C compiler cannot create executables See \`config.log' for more details" "$LINENO" 5; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler default output file name" >&5 $as_echo_n "checking for C compiler default output file name... " >&6; } { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_file" >&5 $as_echo "$ac_file" >&6; } ac_exeext=$ac_cv_exeext rm -f -r a.out a.out.dSYM a.exe conftest$ac_cv_exeext b.out ac_clean_files=$ac_clean_files_save { $as_echo "$as_me:${as_lineno-$LINENO}: checking for suffix of executables" >&5 $as_echo_n "checking for suffix of executables... " >&6; } if { { ac_try="$ac_link" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_link") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then : # If both `conftest.exe' and `conftest' are `present' (well, observable) # catch `conftest.exe'. For instance with Cygwin, `ls conftest' will # work properly (i.e., refer to `conftest.exe'), while it won't with # `rm'. for ac_file in conftest.exe conftest conftest.*; do test -f "$ac_file" || continue case $ac_file in *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; *.* ) ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` break;; * ) break;; esac done else { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "cannot compute suffix of executables: cannot compile and link See \`config.log' for more details" "$LINENO" 5; } fi rm -f conftest conftest$ac_cv_exeext { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_exeext" >&5 $as_echo "$ac_cv_exeext" >&6; } rm -f conftest.$ac_ext EXEEXT=$ac_cv_exeext ac_exeext=$EXEEXT cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int main () { FILE *f = fopen ("conftest.out", "w"); return ferror (f) || fclose (f) != 0; ; return 0; } _ACEOF ac_clean_files="$ac_clean_files conftest.out" # Check that the compiler produces executables we can run. If not, either # the compiler is broken, or we cross compile. { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are cross compiling" >&5 $as_echo_n "checking whether we are cross compiling... " >&6; } if test "$cross_compiling" != yes; then { { ac_try="$ac_link" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_link") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } if { ac_try='./conftest$ac_cv_exeext' { { case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_try") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; }; then cross_compiling=no else if test "$cross_compiling" = maybe; then cross_compiling=yes else { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "cannot run C compiled programs. If you meant to cross compile, use \`--host'. See \`config.log' for more details" "$LINENO" 5; } fi fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $cross_compiling" >&5 $as_echo "$cross_compiling" >&6; } rm -f conftest.$ac_ext conftest$ac_cv_exeext conftest.out ac_clean_files=$ac_clean_files_save { $as_echo "$as_me:${as_lineno-$LINENO}: checking for suffix of object files" >&5 $as_echo_n "checking for suffix of object files... " >&6; } if ${ac_cv_objext+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF rm -f conftest.o conftest.obj if { { ac_try="$ac_compile" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_compile") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then : for ac_file in conftest.o conftest.obj conftest.*; do test -f "$ac_file" || continue; case $ac_file in *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM ) ;; *) ac_cv_objext=`expr "$ac_file" : '.*\.\(.*\)'` break;; esac done else $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "cannot compute suffix of object files: cannot compile See \`config.log' for more details" "$LINENO" 5; } fi rm -f conftest.$ac_cv_objext conftest.$ac_ext fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_objext" >&5 $as_echo "$ac_cv_objext" >&6; } OBJEXT=$ac_cv_objext ac_objext=$OBJEXT { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are using the GNU C compiler" >&5 $as_echo_n "checking whether we are using the GNU C compiler... " >&6; } if ${ac_cv_c_compiler_gnu+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { #ifndef __GNUC__ choke me #endif ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_compiler_gnu=yes else ac_compiler_gnu=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext ac_cv_c_compiler_gnu=$ac_compiler_gnu fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_compiler_gnu" >&5 $as_echo "$ac_cv_c_compiler_gnu" >&6; } if test $ac_compiler_gnu = yes; then GCC=yes else GCC= fi ac_test_CFLAGS=${CFLAGS+set} ac_save_CFLAGS=$CFLAGS { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC accepts -g" >&5 $as_echo_n "checking whether $CC accepts -g... " >&6; } if ${ac_cv_prog_cc_g+:} false; then : $as_echo_n "(cached) " >&6 else ac_save_c_werror_flag=$ac_c_werror_flag ac_c_werror_flag=yes ac_cv_prog_cc_g=no CFLAGS="-g" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_cv_prog_cc_g=yes else CFLAGS="" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : else ac_c_werror_flag=$ac_save_c_werror_flag CFLAGS="-g" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_cv_prog_cc_g=yes fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext ac_c_werror_flag=$ac_save_c_werror_flag fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_g" >&5 $as_echo "$ac_cv_prog_cc_g" >&6; } if test "$ac_test_CFLAGS" = set; then CFLAGS=$ac_save_CFLAGS elif test $ac_cv_prog_cc_g = yes; then if test "$GCC" = yes; then CFLAGS="-g -O2" else CFLAGS="-g" fi else if test "$GCC" = yes; then CFLAGS="-O2" else CFLAGS= fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $CC option to accept ISO C89" >&5 $as_echo_n "checking for $CC option to accept ISO C89... " >&6; } if ${ac_cv_prog_cc_c89+:} false; then : $as_echo_n "(cached) " >&6 else ac_cv_prog_cc_c89=no ac_save_CC=$CC cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include struct stat; /* Most of the following tests are stolen from RCS 5.7's src/conf.sh. */ struct buf { int x; }; FILE * (*rcsopen) (struct buf *, struct stat *, int); static char *e (p, i) char **p; int i; { return p[i]; } static char *f (char * (*g) (char **, int), char **p, ...) { char *s; va_list v; va_start (v,p); s = g (p, va_arg (v,int)); va_end (v); return s; } /* OSF 4.0 Compaq cc is some sort of almost-ANSI by default. It has function prototypes and stuff, but not '\xHH' hex character constants. These don't provoke an error unfortunately, instead are silently treated as 'x'. The following induces an error, until -std is added to get proper ANSI mode. Curiously '\x00'!='x' always comes out true, for an array size at least. It's necessary to write '\x00'==0 to get something that's true only with -std. */ int osf4_cc_array ['\x00' == 0 ? 1 : -1]; /* IBM C 6 for AIX is almost-ANSI by default, but it replaces macro parameters inside strings and character constants. */ #define FOO(x) 'x' int xlc6_cc_array[FOO(a) == 'x' ? 1 : -1]; int test (int i, double x); struct s1 {int (*f) (int a);}; struct s2 {int (*f) (double a);}; int pairnames (int, char **, FILE *(*)(struct buf *, struct stat *, int), int, int); int argc; char **argv; int main () { return f (e, argv, 0) != argv[0] || f (e, argv, 1) != argv[1]; ; return 0; } _ACEOF for ac_arg in '' -qlanglvl=extc89 -qlanglvl=ansi -std \ -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__" do CC="$ac_save_CC $ac_arg" if ac_fn_c_try_compile "$LINENO"; then : ac_cv_prog_cc_c89=$ac_arg fi rm -f core conftest.err conftest.$ac_objext test "x$ac_cv_prog_cc_c89" != "xno" && break done rm -f conftest.$ac_ext CC=$ac_save_CC fi # AC_CACHE_VAL case "x$ac_cv_prog_cc_c89" in x) { $as_echo "$as_me:${as_lineno-$LINENO}: result: none needed" >&5 $as_echo "none needed" >&6; } ;; xno) { $as_echo "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5 $as_echo "unsupported" >&6; } ;; *) CC="$CC $ac_cv_prog_cc_c89" { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c89" >&5 $as_echo "$ac_cv_prog_cc_c89" >&6; } ;; esac if test "x$ac_cv_prog_cc_c89" != xno; then : fi ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC understands -c and -o together" >&5 $as_echo_n "checking whether $CC understands -c and -o together... " >&6; } if ${am_cv_prog_cc_c_o+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF # Make sure it works both with $CC and with simple cc. # Following AC_PROG_CC_C_O, we do the test twice because some # compilers refuse to overwrite an existing .o file with -o, # though they will create one. am_cv_prog_cc_c_o=yes for am_i in 1 2; do if { echo "$as_me:$LINENO: $CC -c conftest.$ac_ext -o conftest2.$ac_objext" >&5 ($CC -c conftest.$ac_ext -o conftest2.$ac_objext) >&5 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } \ && test -f conftest2.$ac_objext; then : OK else am_cv_prog_cc_c_o=no break fi done rm -f core conftest* unset am_i fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_prog_cc_c_o" >&5 $as_echo "$am_cv_prog_cc_c_o" >&6; } if test "$am_cv_prog_cc_c_o" != yes; then # Losing compiler, so override with the script. # FIXME: It is wrong to rewrite CC. # But if we don't then we get into trouble of one sort or another. # A longer-term fix would be to have automake use am__CC in this case, # and then we could set am__CC="\$(top_srcdir)/compile \$(CC)" CC="$am_aux_dir/compile $CC" fi ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu depcc="$CC" am_compiler_list= { $as_echo "$as_me:${as_lineno-$LINENO}: checking dependency style of $depcc" >&5 $as_echo_n "checking dependency style of $depcc... " >&6; } if ${am_cv_CC_dependencies_compiler_type+:} false; then : $as_echo_n "(cached) " >&6 else if test -z "$AMDEP_TRUE" && test -f "$am_depcomp"; then # We make a subdir and do the tests there. Otherwise we can end up # making bogus files that we don't know about and never remove. For # instance it was reported that on HP-UX the gcc test will end up # making a dummy file named 'D' -- because '-MD' means "put the output # in D". rm -rf conftest.dir mkdir conftest.dir # Copy depcomp to subdir because otherwise we won't find it if we're # using a relative directory. cp "$am_depcomp" conftest.dir cd conftest.dir # We will build objects and dependencies in a subdirectory because # it helps to detect inapplicable dependency modes. For instance # both Tru64's cc and ICC support -MD to output dependencies as a # side effect of compilation, but ICC will put the dependencies in # the current directory while Tru64 will put them in the object # directory. mkdir sub am_cv_CC_dependencies_compiler_type=none if test "$am_compiler_list" = ""; then am_compiler_list=`sed -n 's/^#*\([a-zA-Z0-9]*\))$/\1/p' < ./depcomp` fi am__universal=false case " $depcc " in #( *\ -arch\ *\ -arch\ *) am__universal=true ;; esac for depmode in $am_compiler_list; do # Setup a source with many dependencies, because some compilers # like to wrap large dependency lists on column 80 (with \), and # we should not choose a depcomp mode which is confused by this. # # We need to recreate these files for each test, as the compiler may # overwrite some of them when testing with obscure command lines. # This happens at least with the AIX C compiler. : > sub/conftest.c for i in 1 2 3 4 5 6; do echo '#include "conftst'$i'.h"' >> sub/conftest.c # Using ": > sub/conftst$i.h" creates only sub/conftst1.h with # Solaris 10 /bin/sh. echo '/* dummy */' > sub/conftst$i.h done echo "${am__include} ${am__quote}sub/conftest.Po${am__quote}" > confmf # We check with '-c' and '-o' for the sake of the "dashmstdout" # mode. It turns out that the SunPro C++ compiler does not properly # handle '-M -o', and we need to detect this. Also, some Intel # versions had trouble with output in subdirs. am__obj=sub/conftest.${OBJEXT-o} am__minus_obj="-o $am__obj" case $depmode in gcc) # This depmode causes a compiler race in universal mode. test "$am__universal" = false || continue ;; nosideeffect) # After this tag, mechanisms are not by side-effect, so they'll # only be used when explicitly requested. if test "x$enable_dependency_tracking" = xyes; then continue else break fi ;; msvc7 | msvc7msys | msvisualcpp | msvcmsys) # This compiler won't grok '-c -o', but also, the minuso test has # not run yet. These depmodes are late enough in the game, and # so weak that their functioning should not be impacted. am__obj=conftest.${OBJEXT-o} am__minus_obj= ;; none) break ;; esac if depmode=$depmode \ source=sub/conftest.c object=$am__obj \ depfile=sub/conftest.Po tmpdepfile=sub/conftest.TPo \ $SHELL ./depcomp $depcc -c $am__minus_obj sub/conftest.c \ >/dev/null 2>conftest.err && grep sub/conftst1.h sub/conftest.Po > /dev/null 2>&1 && grep sub/conftst6.h sub/conftest.Po > /dev/null 2>&1 && grep $am__obj sub/conftest.Po > /dev/null 2>&1 && ${MAKE-make} -s -f confmf > /dev/null 2>&1; then # icc doesn't choke on unknown options, it will just issue warnings # or remarks (even with -Werror). So we grep stderr for any message # that says an option was ignored or not supported. # When given -MP, icc 7.0 and 7.1 complain thusly: # icc: Command line warning: ignoring option '-M'; no argument required # The diagnosis changed in icc 8.0: # icc: Command line remark: option '-MP' not supported if (grep 'ignoring option' conftest.err || grep 'not supported' conftest.err) >/dev/null 2>&1; then :; else am_cv_CC_dependencies_compiler_type=$depmode break fi fi done cd .. rm -rf conftest.dir else am_cv_CC_dependencies_compiler_type=none fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_CC_dependencies_compiler_type" >&5 $as_echo "$am_cv_CC_dependencies_compiler_type" >&6; } CCDEPMODE=depmode=$am_cv_CC_dependencies_compiler_type if test "x$enable_dependency_tracking" != xno \ && test "$am_cv_CC_dependencies_compiler_type" = gcc3; then am__fastdepCC_TRUE= am__fastdepCC_FALSE='#' else am__fastdepCC_TRUE='#' am__fastdepCC_FALSE= fi ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu { $as_echo "$as_me:${as_lineno-$LINENO}: checking how to run the C preprocessor" >&5 $as_echo_n "checking how to run the C preprocessor... " >&6; } # On Suns, sometimes $CPP names a directory. if test -n "$CPP" && test -d "$CPP"; then CPP= fi if test -z "$CPP"; then if ${ac_cv_prog_CPP+:} false; then : $as_echo_n "(cached) " >&6 else # Double quotes because CPP needs to be expanded for CPP in "$CC -E" "$CC -E -traditional-cpp" "/lib/cpp" do ac_preproc_ok=false for ac_c_preproc_warn_flag in '' yes do # Use a header file that comes with gcc, so configuring glibc # with a fresh cross-compiler works. # Prefer to if __STDC__ is defined, since # exists even on freestanding compilers. # On the NeXT, cc -E runs the code through the compiler's parser, # not just through cpp. "Syntax error" is here to catch this case. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #ifdef __STDC__ # include #else # include #endif Syntax error _ACEOF if ac_fn_c_try_cpp "$LINENO"; then : else # Broken: fails on valid input. continue fi rm -f conftest.err conftest.i conftest.$ac_ext # OK, works on sane cases. Now check whether nonexistent headers # can be detected and how. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include _ACEOF if ac_fn_c_try_cpp "$LINENO"; then : # Broken: success on invalid input. continue else # Passes both tests. ac_preproc_ok=: break fi rm -f conftest.err conftest.i conftest.$ac_ext done # Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. rm -f conftest.i conftest.err conftest.$ac_ext if $ac_preproc_ok; then : break fi done ac_cv_prog_CPP=$CPP fi CPP=$ac_cv_prog_CPP else ac_cv_prog_CPP=$CPP fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CPP" >&5 $as_echo "$CPP" >&6; } ac_preproc_ok=false for ac_c_preproc_warn_flag in '' yes do # Use a header file that comes with gcc, so configuring glibc # with a fresh cross-compiler works. # Prefer to if __STDC__ is defined, since # exists even on freestanding compilers. # On the NeXT, cc -E runs the code through the compiler's parser, # not just through cpp. "Syntax error" is here to catch this case. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #ifdef __STDC__ # include #else # include #endif Syntax error _ACEOF if ac_fn_c_try_cpp "$LINENO"; then : else # Broken: fails on valid input. continue fi rm -f conftest.err conftest.i conftest.$ac_ext # OK, works on sane cases. Now check whether nonexistent headers # can be detected and how. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include _ACEOF if ac_fn_c_try_cpp "$LINENO"; then : # Broken: success on invalid input. continue else # Passes both tests. ac_preproc_ok=: break fi rm -f conftest.err conftest.i conftest.$ac_ext done # Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. rm -f conftest.i conftest.err conftest.$ac_ext if $ac_preproc_ok; then : else { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "C preprocessor \"$CPP\" fails sanity check See \`config.log' for more details" "$LINENO" 5; } fi ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu { $as_echo "$as_me:${as_lineno-$LINENO}: checking for grep that handles long lines and -e" >&5 $as_echo_n "checking for grep that handles long lines and -e... " >&6; } if ${ac_cv_path_GREP+:} false; then : $as_echo_n "(cached) " >&6 else if test -z "$GREP"; then ac_path_GREP_found=false # Loop through the user's path and test for each of PROGNAME-LIST as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_prog in grep ggrep; do for ac_exec_ext in '' $ac_executable_extensions; do ac_path_GREP="$as_dir/$ac_prog$ac_exec_ext" as_fn_executable_p "$ac_path_GREP" || continue # Check for GNU ac_path_GREP and select it if it is found. # Check for GNU $ac_path_GREP case `"$ac_path_GREP" --version 2>&1` in *GNU*) ac_cv_path_GREP="$ac_path_GREP" ac_path_GREP_found=:;; *) ac_count=0 $as_echo_n 0123456789 >"conftest.in" while : do cat "conftest.in" "conftest.in" >"conftest.tmp" mv "conftest.tmp" "conftest.in" cp "conftest.in" "conftest.nl" $as_echo 'GREP' >> "conftest.nl" "$ac_path_GREP" -e 'GREP$' -e '-(cannot match)-' < "conftest.nl" >"conftest.out" 2>/dev/null || break diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break as_fn_arith $ac_count + 1 && ac_count=$as_val if test $ac_count -gt ${ac_path_GREP_max-0}; then # Best one so far, save it but keep looking for a better one ac_cv_path_GREP="$ac_path_GREP" ac_path_GREP_max=$ac_count fi # 10*(2^10) chars as input seems more than enough test $ac_count -gt 10 && break done rm -f conftest.in conftest.tmp conftest.nl conftest.out;; esac $ac_path_GREP_found && break 3 done done done IFS=$as_save_IFS if test -z "$ac_cv_path_GREP"; then as_fn_error $? "no acceptable grep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5 fi else ac_cv_path_GREP=$GREP fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_GREP" >&5 $as_echo "$ac_cv_path_GREP" >&6; } GREP="$ac_cv_path_GREP" { $as_echo "$as_me:${as_lineno-$LINENO}: checking for egrep" >&5 $as_echo_n "checking for egrep... " >&6; } if ${ac_cv_path_EGREP+:} false; then : $as_echo_n "(cached) " >&6 else if echo a | $GREP -E '(a|b)' >/dev/null 2>&1 then ac_cv_path_EGREP="$GREP -E" else if test -z "$EGREP"; then ac_path_EGREP_found=false # Loop through the user's path and test for each of PROGNAME-LIST as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_prog in egrep; do for ac_exec_ext in '' $ac_executable_extensions; do ac_path_EGREP="$as_dir/$ac_prog$ac_exec_ext" as_fn_executable_p "$ac_path_EGREP" || continue # Check for GNU ac_path_EGREP and select it if it is found. # Check for GNU $ac_path_EGREP case `"$ac_path_EGREP" --version 2>&1` in *GNU*) ac_cv_path_EGREP="$ac_path_EGREP" ac_path_EGREP_found=:;; *) ac_count=0 $as_echo_n 0123456789 >"conftest.in" while : do cat "conftest.in" "conftest.in" >"conftest.tmp" mv "conftest.tmp" "conftest.in" cp "conftest.in" "conftest.nl" $as_echo 'EGREP' >> "conftest.nl" "$ac_path_EGREP" 'EGREP$' < "conftest.nl" >"conftest.out" 2>/dev/null || break diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break as_fn_arith $ac_count + 1 && ac_count=$as_val if test $ac_count -gt ${ac_path_EGREP_max-0}; then # Best one so far, save it but keep looking for a better one ac_cv_path_EGREP="$ac_path_EGREP" ac_path_EGREP_max=$ac_count fi # 10*(2^10) chars as input seems more than enough test $ac_count -gt 10 && break done rm -f conftest.in conftest.tmp conftest.nl conftest.out;; esac $ac_path_EGREP_found && break 3 done done done IFS=$as_save_IFS if test -z "$ac_cv_path_EGREP"; then as_fn_error $? "no acceptable egrep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5 fi else ac_cv_path_EGREP=$EGREP fi fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_EGREP" >&5 $as_echo "$ac_cv_path_EGREP" >&6; } EGREP="$ac_cv_path_EGREP" { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ANSI C header files" >&5 $as_echo_n "checking for ANSI C header files... " >&6; } if ${ac_cv_header_stdc+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include #include #include int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_cv_header_stdc=yes else ac_cv_header_stdc=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext if test $ac_cv_header_stdc = yes; then # SunOS 4.x string.h does not declare mem*, contrary to ANSI. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include _ACEOF if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | $EGREP "memchr" >/dev/null 2>&1; then : else ac_cv_header_stdc=no fi rm -f conftest* fi if test $ac_cv_header_stdc = yes; then # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include _ACEOF if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | $EGREP "free" >/dev/null 2>&1; then : else ac_cv_header_stdc=no fi rm -f conftest* fi if test $ac_cv_header_stdc = yes; then # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi. if test "$cross_compiling" = yes; then : : else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include #if ((' ' & 0x0FF) == 0x020) # define ISLOWER(c) ('a' <= (c) && (c) <= 'z') # define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c)) #else # define ISLOWER(c) \ (('a' <= (c) && (c) <= 'i') \ || ('j' <= (c) && (c) <= 'r') \ || ('s' <= (c) && (c) <= 'z')) # define TOUPPER(c) (ISLOWER(c) ? ((c) | 0x40) : (c)) #endif #define XOR(e, f) (((e) && !(f)) || (!(e) && (f))) int main () { int i; for (i = 0; i < 256; i++) if (XOR (islower (i), ISLOWER (i)) || toupper (i) != TOUPPER (i)) return 2; return 0; } _ACEOF if ac_fn_c_try_run "$LINENO"; then : else ac_cv_header_stdc=no fi rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ conftest.$ac_objext conftest.beam conftest.$ac_ext fi fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_header_stdc" >&5 $as_echo "$ac_cv_header_stdc" >&6; } if test $ac_cv_header_stdc = yes; then $as_echo "#define STDC_HEADERS 1" >>confdefs.h fi # On IRIX 5.3, sys/types and inttypes.h are conflicting. for ac_header in sys/types.h sys/stat.h stdlib.h string.h memory.h strings.h \ inttypes.h stdint.h unistd.h do : as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh` ac_fn_c_check_header_compile "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default " if eval test \"x\$"$as_ac_Header"\" = x"yes"; then : cat >>confdefs.h <<_ACEOF #define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1 _ACEOF fi done ac_fn_c_check_header_mongrel "$LINENO" "minix/config.h" "ac_cv_header_minix_config_h" "$ac_includes_default" if test "x$ac_cv_header_minix_config_h" = xyes; then : MINIX=yes else MINIX= fi if test "$MINIX" = yes; then $as_echo "#define _POSIX_SOURCE 1" >>confdefs.h $as_echo "#define _POSIX_1_SOURCE 2" >>confdefs.h $as_echo "#define _MINIX 1" >>confdefs.h fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether it is safe to define __EXTENSIONS__" >&5 $as_echo_n "checking whether it is safe to define __EXTENSIONS__... " >&6; } if ${ac_cv_safe_to_define___extensions__+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ # define __EXTENSIONS__ 1 $ac_includes_default int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_cv_safe_to_define___extensions__=yes else ac_cv_safe_to_define___extensions__=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_safe_to_define___extensions__" >&5 $as_echo "$ac_cv_safe_to_define___extensions__" >&6; } test $ac_cv_safe_to_define___extensions__ = yes && $as_echo "#define __EXTENSIONS__ 1" >>confdefs.h $as_echo "#define _ALL_SOURCE 1" >>confdefs.h $as_echo "#define _GNU_SOURCE 1" >>confdefs.h $as_echo "#define _POSIX_PTHREAD_SEMANTICS 1" >>confdefs.h $as_echo "#define _TANDEM_SOURCE 1" >>confdefs.h # Check whether --enable-silent-rules was given. if test "${enable_silent_rules+set}" = set; then : enableval=$enable_silent_rules; fi case $enable_silent_rules in # ((( yes) AM_DEFAULT_VERBOSITY=0;; no) AM_DEFAULT_VERBOSITY=1;; *) AM_DEFAULT_VERBOSITY=1;; esac am_make=${MAKE-make} { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $am_make supports nested variables" >&5 $as_echo_n "checking whether $am_make supports nested variables... " >&6; } if ${am_cv_make_support_nested_variables+:} false; then : $as_echo_n "(cached) " >&6 else if $as_echo 'TRUE=$(BAR$(V)) BAR0=false BAR1=true V=1 am__doit: @$(TRUE) .PHONY: am__doit' | $am_make -f - >/dev/null 2>&1; then am_cv_make_support_nested_variables=yes else am_cv_make_support_nested_variables=no fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_make_support_nested_variables" >&5 $as_echo "$am_cv_make_support_nested_variables" >&6; } if test $am_cv_make_support_nested_variables = yes; then AM_V='$(V)' AM_DEFAULT_V='$(AM_DEFAULT_VERBOSITY)' else AM_V=$AM_DEFAULT_VERBOSITY AM_DEFAULT_V=$AM_DEFAULT_VERBOSITY fi AM_BACKSLASH='\' ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args. set dummy ${ac_tool_prefix}gcc; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_CC+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_CC="${ac_tool_prefix}gcc" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 $as_echo "$CC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$ac_cv_prog_CC"; then ac_ct_CC=$CC # Extract the first word of "gcc", so it can be a program name with args. set dummy gcc; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_ac_ct_CC+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_CC"; then ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_CC="gcc" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_CC=$ac_cv_prog_ac_ct_CC if test -n "$ac_ct_CC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 $as_echo "$ac_ct_CC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test "x$ac_ct_CC" = x; then CC="" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac CC=$ac_ct_CC fi else CC="$ac_cv_prog_CC" fi if test -z "$CC"; then if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args. set dummy ${ac_tool_prefix}cc; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_CC+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_CC="${ac_tool_prefix}cc" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 $as_echo "$CC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi fi if test -z "$CC"; then # Extract the first word of "cc", so it can be a program name with args. set dummy cc; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_CC+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else ac_prog_rejected=no as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then if test "$as_dir/$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then ac_prog_rejected=yes continue fi ac_cv_prog_CC="cc" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS if test $ac_prog_rejected = yes; then # We found a bogon in the path, so make sure we never use it. set dummy $ac_cv_prog_CC shift if test $# != 0; then # We chose a different compiler from the bogus one. # However, it has the same basename, so the bogon will be chosen # first if we set CC to just the basename; use the full file name. shift ac_cv_prog_CC="$as_dir/$ac_word${1+' '}$@" fi fi fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 $as_echo "$CC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$CC"; then if test -n "$ac_tool_prefix"; then for ac_prog in cl.exe do # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. set dummy $ac_tool_prefix$ac_prog; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_CC+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_CC="$ac_tool_prefix$ac_prog" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 $as_echo "$CC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi test -n "$CC" && break done fi if test -z "$CC"; then ac_ct_CC=$CC for ac_prog in cl.exe do # Extract the first word of "$ac_prog", so it can be a program name with args. set dummy $ac_prog; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_ac_ct_CC+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_CC"; then ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_CC="$ac_prog" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_CC=$ac_cv_prog_ac_ct_CC if test -n "$ac_ct_CC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 $as_echo "$ac_ct_CC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi test -n "$ac_ct_CC" && break done if test "x$ac_ct_CC" = x; then CC="" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac CC=$ac_ct_CC fi fi fi test -z "$CC" && { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "no acceptable C compiler found in \$PATH See \`config.log' for more details" "$LINENO" 5; } # Provide some information about the compiler. $as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler version" >&5 set X $ac_compile ac_compiler=$2 for ac_option in --version -v -V -qversion; do { { ac_try="$ac_compiler $ac_option >&5" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_compiler $ac_option >&5") 2>conftest.err ac_status=$? if test -s conftest.err; then sed '10a\ ... rest of stderr output deleted ... 10q' conftest.err >conftest.er1 cat conftest.er1 >&5 fi rm -f conftest.er1 conftest.err $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } done { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are using the GNU C compiler" >&5 $as_echo_n "checking whether we are using the GNU C compiler... " >&6; } if ${ac_cv_c_compiler_gnu+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { #ifndef __GNUC__ choke me #endif ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_compiler_gnu=yes else ac_compiler_gnu=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext ac_cv_c_compiler_gnu=$ac_compiler_gnu fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_compiler_gnu" >&5 $as_echo "$ac_cv_c_compiler_gnu" >&6; } if test $ac_compiler_gnu = yes; then GCC=yes else GCC= fi ac_test_CFLAGS=${CFLAGS+set} ac_save_CFLAGS=$CFLAGS { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC accepts -g" >&5 $as_echo_n "checking whether $CC accepts -g... " >&6; } if ${ac_cv_prog_cc_g+:} false; then : $as_echo_n "(cached) " >&6 else ac_save_c_werror_flag=$ac_c_werror_flag ac_c_werror_flag=yes ac_cv_prog_cc_g=no CFLAGS="-g" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_cv_prog_cc_g=yes else CFLAGS="" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : else ac_c_werror_flag=$ac_save_c_werror_flag CFLAGS="-g" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_cv_prog_cc_g=yes fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext ac_c_werror_flag=$ac_save_c_werror_flag fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_g" >&5 $as_echo "$ac_cv_prog_cc_g" >&6; } if test "$ac_test_CFLAGS" = set; then CFLAGS=$ac_save_CFLAGS elif test $ac_cv_prog_cc_g = yes; then if test "$GCC" = yes; then CFLAGS="-g -O2" else CFLAGS="-g" fi else if test "$GCC" = yes; then CFLAGS="-O2" else CFLAGS= fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $CC option to accept ISO C89" >&5 $as_echo_n "checking for $CC option to accept ISO C89... " >&6; } if ${ac_cv_prog_cc_c89+:} false; then : $as_echo_n "(cached) " >&6 else ac_cv_prog_cc_c89=no ac_save_CC=$CC cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include struct stat; /* Most of the following tests are stolen from RCS 5.7's src/conf.sh. */ struct buf { int x; }; FILE * (*rcsopen) (struct buf *, struct stat *, int); static char *e (p, i) char **p; int i; { return p[i]; } static char *f (char * (*g) (char **, int), char **p, ...) { char *s; va_list v; va_start (v,p); s = g (p, va_arg (v,int)); va_end (v); return s; } /* OSF 4.0 Compaq cc is some sort of almost-ANSI by default. It has function prototypes and stuff, but not '\xHH' hex character constants. These don't provoke an error unfortunately, instead are silently treated as 'x'. The following induces an error, until -std is added to get proper ANSI mode. Curiously '\x00'!='x' always comes out true, for an array size at least. It's necessary to write '\x00'==0 to get something that's true only with -std. */ int osf4_cc_array ['\x00' == 0 ? 1 : -1]; /* IBM C 6 for AIX is almost-ANSI by default, but it replaces macro parameters inside strings and character constants. */ #define FOO(x) 'x' int xlc6_cc_array[FOO(a) == 'x' ? 1 : -1]; int test (int i, double x); struct s1 {int (*f) (int a);}; struct s2 {int (*f) (double a);}; int pairnames (int, char **, FILE *(*)(struct buf *, struct stat *, int), int, int); int argc; char **argv; int main () { return f (e, argv, 0) != argv[0] || f (e, argv, 1) != argv[1]; ; return 0; } _ACEOF for ac_arg in '' -qlanglvl=extc89 -qlanglvl=ansi -std \ -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__" do CC="$ac_save_CC $ac_arg" if ac_fn_c_try_compile "$LINENO"; then : ac_cv_prog_cc_c89=$ac_arg fi rm -f core conftest.err conftest.$ac_objext test "x$ac_cv_prog_cc_c89" != "xno" && break done rm -f conftest.$ac_ext CC=$ac_save_CC fi # AC_CACHE_VAL case "x$ac_cv_prog_cc_c89" in x) { $as_echo "$as_me:${as_lineno-$LINENO}: result: none needed" >&5 $as_echo "none needed" >&6; } ;; xno) { $as_echo "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5 $as_echo "unsupported" >&6; } ;; *) CC="$CC $ac_cv_prog_cc_c89" { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c89" >&5 $as_echo "$ac_cv_prog_cc_c89" >&6; } ;; esac if test "x$ac_cv_prog_cc_c89" != xno; then : fi ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC understands -c and -o together" >&5 $as_echo_n "checking whether $CC understands -c and -o together... " >&6; } if ${am_cv_prog_cc_c_o+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF # Make sure it works both with $CC and with simple cc. # Following AC_PROG_CC_C_O, we do the test twice because some # compilers refuse to overwrite an existing .o file with -o, # though they will create one. am_cv_prog_cc_c_o=yes for am_i in 1 2; do if { echo "$as_me:$LINENO: $CC -c conftest.$ac_ext -o conftest2.$ac_objext" >&5 ($CC -c conftest.$ac_ext -o conftest2.$ac_objext) >&5 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } \ && test -f conftest2.$ac_objext; then : OK else am_cv_prog_cc_c_o=no break fi done rm -f core conftest* unset am_i fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_prog_cc_c_o" >&5 $as_echo "$am_cv_prog_cc_c_o" >&6; } if test "$am_cv_prog_cc_c_o" != yes; then # Losing compiler, so override with the script. # FIXME: It is wrong to rewrite CC. # But if we don't then we get into trouble of one sort or another. # A longer-term fix would be to have automake use am__CC in this case, # and then we could set am__CC="\$(top_srcdir)/compile \$(CC)" CC="$am_aux_dir/compile $CC" fi ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu depcc="$CC" am_compiler_list= { $as_echo "$as_me:${as_lineno-$LINENO}: checking dependency style of $depcc" >&5 $as_echo_n "checking dependency style of $depcc... " >&6; } if ${am_cv_CC_dependencies_compiler_type+:} false; then : $as_echo_n "(cached) " >&6 else if test -z "$AMDEP_TRUE" && test -f "$am_depcomp"; then # We make a subdir and do the tests there. Otherwise we can end up # making bogus files that we don't know about and never remove. For # instance it was reported that on HP-UX the gcc test will end up # making a dummy file named 'D' -- because '-MD' means "put the output # in D". rm -rf conftest.dir mkdir conftest.dir # Copy depcomp to subdir because otherwise we won't find it if we're # using a relative directory. cp "$am_depcomp" conftest.dir cd conftest.dir # We will build objects and dependencies in a subdirectory because # it helps to detect inapplicable dependency modes. For instance # both Tru64's cc and ICC support -MD to output dependencies as a # side effect of compilation, but ICC will put the dependencies in # the current directory while Tru64 will put them in the object # directory. mkdir sub am_cv_CC_dependencies_compiler_type=none if test "$am_compiler_list" = ""; then am_compiler_list=`sed -n 's/^#*\([a-zA-Z0-9]*\))$/\1/p' < ./depcomp` fi am__universal=false case " $depcc " in #( *\ -arch\ *\ -arch\ *) am__universal=true ;; esac for depmode in $am_compiler_list; do # Setup a source with many dependencies, because some compilers # like to wrap large dependency lists on column 80 (with \), and # we should not choose a depcomp mode which is confused by this. # # We need to recreate these files for each test, as the compiler may # overwrite some of them when testing with obscure command lines. # This happens at least with the AIX C compiler. : > sub/conftest.c for i in 1 2 3 4 5 6; do echo '#include "conftst'$i'.h"' >> sub/conftest.c # Using ": > sub/conftst$i.h" creates only sub/conftst1.h with # Solaris 10 /bin/sh. echo '/* dummy */' > sub/conftst$i.h done echo "${am__include} ${am__quote}sub/conftest.Po${am__quote}" > confmf # We check with '-c' and '-o' for the sake of the "dashmstdout" # mode. It turns out that the SunPro C++ compiler does not properly # handle '-M -o', and we need to detect this. Also, some Intel # versions had trouble with output in subdirs. am__obj=sub/conftest.${OBJEXT-o} am__minus_obj="-o $am__obj" case $depmode in gcc) # This depmode causes a compiler race in universal mode. test "$am__universal" = false || continue ;; nosideeffect) # After this tag, mechanisms are not by side-effect, so they'll # only be used when explicitly requested. if test "x$enable_dependency_tracking" = xyes; then continue else break fi ;; msvc7 | msvc7msys | msvisualcpp | msvcmsys) # This compiler won't grok '-c -o', but also, the minuso test has # not run yet. These depmodes are late enough in the game, and # so weak that their functioning should not be impacted. am__obj=conftest.${OBJEXT-o} am__minus_obj= ;; none) break ;; esac if depmode=$depmode \ source=sub/conftest.c object=$am__obj \ depfile=sub/conftest.Po tmpdepfile=sub/conftest.TPo \ $SHELL ./depcomp $depcc -c $am__minus_obj sub/conftest.c \ >/dev/null 2>conftest.err && grep sub/conftst1.h sub/conftest.Po > /dev/null 2>&1 && grep sub/conftst6.h sub/conftest.Po > /dev/null 2>&1 && grep $am__obj sub/conftest.Po > /dev/null 2>&1 && ${MAKE-make} -s -f confmf > /dev/null 2>&1; then # icc doesn't choke on unknown options, it will just issue warnings # or remarks (even with -Werror). So we grep stderr for any message # that says an option was ignored or not supported. # When given -MP, icc 7.0 and 7.1 complain thusly: # icc: Command line warning: ignoring option '-M'; no argument required # The diagnosis changed in icc 8.0: # icc: Command line remark: option '-MP' not supported if (grep 'ignoring option' conftest.err || grep 'not supported' conftest.err) >/dev/null 2>&1; then :; else am_cv_CC_dependencies_compiler_type=$depmode break fi fi done cd .. rm -rf conftest.dir else am_cv_CC_dependencies_compiler_type=none fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_CC_dependencies_compiler_type" >&5 $as_echo "$am_cv_CC_dependencies_compiler_type" >&6; } CCDEPMODE=depmode=$am_cv_CC_dependencies_compiler_type if test "x$enable_dependency_tracking" != xno \ && test "$am_cv_CC_dependencies_compiler_type" = gcc3; then am__fastdepCC_TRUE= am__fastdepCC_FALSE='#' else am__fastdepCC_TRUE='#' am__fastdepCC_FALSE= fi case $ac_cv_prog_cc_stdc in #( no) : ac_cv_prog_cc_c99=no; ac_cv_prog_cc_c89=no ;; #( *) : { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $CC option to accept ISO C99" >&5 $as_echo_n "checking for $CC option to accept ISO C99... " >&6; } if ${ac_cv_prog_cc_c99+:} false; then : $as_echo_n "(cached) " >&6 else ac_cv_prog_cc_c99=no ac_save_CC=$CC cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include #include #include #include // Check varargs macros. These examples are taken from C99 6.10.3.5. #define debug(...) fprintf (stderr, __VA_ARGS__) #define showlist(...) puts (#__VA_ARGS__) #define report(test,...) ((test) ? puts (#test) : printf (__VA_ARGS__)) static void test_varargs_macros (void) { int x = 1234; int y = 5678; debug ("Flag"); debug ("X = %d\n", x); showlist (The first, second, and third items.); report (x>y, "x is %d but y is %d", x, y); } // Check long long types. #define BIG64 18446744073709551615ull #define BIG32 4294967295ul #define BIG_OK (BIG64 / BIG32 == 4294967297ull && BIG64 % BIG32 == 0) #if !BIG_OK your preprocessor is broken; #endif #if BIG_OK #else your preprocessor is broken; #endif static long long int bignum = -9223372036854775807LL; static unsigned long long int ubignum = BIG64; struct incomplete_array { int datasize; double data[]; }; struct named_init { int number; const wchar_t *name; double average; }; typedef const char *ccp; static inline int test_restrict (ccp restrict text) { // See if C++-style comments work. // Iterate through items via the restricted pointer. // Also check for declarations in for loops. for (unsigned int i = 0; *(text+i) != '\0'; ++i) continue; return 0; } // Check varargs and va_copy. static void test_varargs (const char *format, ...) { va_list args; va_start (args, format); va_list args_copy; va_copy (args_copy, args); const char *str; int number; float fnumber; while (*format) { switch (*format++) { case 's': // string str = va_arg (args_copy, const char *); break; case 'd': // int number = va_arg (args_copy, int); break; case 'f': // float fnumber = va_arg (args_copy, double); break; default: break; } } va_end (args_copy); va_end (args); } int main () { // Check bool. _Bool success = false; // Check restrict. if (test_restrict ("String literal") == 0) success = true; char *restrict newvar = "Another string"; // Check varargs. test_varargs ("s, d' f .", "string", 65, 34.234); test_varargs_macros (); // Check flexible array members. struct incomplete_array *ia = malloc (sizeof (struct incomplete_array) + (sizeof (double) * 10)); ia->datasize = 10; for (int i = 0; i < ia->datasize; ++i) ia->data[i] = i * 1.234; // Check named initializers. struct named_init ni = { .number = 34, .name = L"Test wide string", .average = 543.34343, }; ni.number = 58; int dynamic_array[ni.number]; dynamic_array[ni.number - 1] = 543; // work around unused variable warnings return (!success || bignum == 0LL || ubignum == 0uLL || newvar[0] == 'x' || dynamic_array[ni.number - 1] != 543); ; return 0; } _ACEOF for ac_arg in '' -std=gnu99 -std=c99 -c99 -AC99 -D_STDC_C99= -qlanglvl=extc99 do CC="$ac_save_CC $ac_arg" if ac_fn_c_try_compile "$LINENO"; then : ac_cv_prog_cc_c99=$ac_arg fi rm -f core conftest.err conftest.$ac_objext test "x$ac_cv_prog_cc_c99" != "xno" && break done rm -f conftest.$ac_ext CC=$ac_save_CC fi # AC_CACHE_VAL case "x$ac_cv_prog_cc_c99" in x) { $as_echo "$as_me:${as_lineno-$LINENO}: result: none needed" >&5 $as_echo "none needed" >&6; } ;; xno) { $as_echo "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5 $as_echo "unsupported" >&6; } ;; *) CC="$CC $ac_cv_prog_cc_c99" { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c99" >&5 $as_echo "$ac_cv_prog_cc_c99" >&6; } ;; esac if test "x$ac_cv_prog_cc_c99" != xno; then : ac_cv_prog_cc_stdc=$ac_cv_prog_cc_c99 else { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $CC option to accept ISO C89" >&5 $as_echo_n "checking for $CC option to accept ISO C89... " >&6; } if ${ac_cv_prog_cc_c89+:} false; then : $as_echo_n "(cached) " >&6 else ac_cv_prog_cc_c89=no ac_save_CC=$CC cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include struct stat; /* Most of the following tests are stolen from RCS 5.7's src/conf.sh. */ struct buf { int x; }; FILE * (*rcsopen) (struct buf *, struct stat *, int); static char *e (p, i) char **p; int i; { return p[i]; } static char *f (char * (*g) (char **, int), char **p, ...) { char *s; va_list v; va_start (v,p); s = g (p, va_arg (v,int)); va_end (v); return s; } /* OSF 4.0 Compaq cc is some sort of almost-ANSI by default. It has function prototypes and stuff, but not '\xHH' hex character constants. These don't provoke an error unfortunately, instead are silently treated as 'x'. The following induces an error, until -std is added to get proper ANSI mode. Curiously '\x00'!='x' always comes out true, for an array size at least. It's necessary to write '\x00'==0 to get something that's true only with -std. */ int osf4_cc_array ['\x00' == 0 ? 1 : -1]; /* IBM C 6 for AIX is almost-ANSI by default, but it replaces macro parameters inside strings and character constants. */ #define FOO(x) 'x' int xlc6_cc_array[FOO(a) == 'x' ? 1 : -1]; int test (int i, double x); struct s1 {int (*f) (int a);}; struct s2 {int (*f) (double a);}; int pairnames (int, char **, FILE *(*)(struct buf *, struct stat *, int), int, int); int argc; char **argv; int main () { return f (e, argv, 0) != argv[0] || f (e, argv, 1) != argv[1]; ; return 0; } _ACEOF for ac_arg in '' -qlanglvl=extc89 -qlanglvl=ansi -std \ -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__" do CC="$ac_save_CC $ac_arg" if ac_fn_c_try_compile "$LINENO"; then : ac_cv_prog_cc_c89=$ac_arg fi rm -f core conftest.err conftest.$ac_objext test "x$ac_cv_prog_cc_c89" != "xno" && break done rm -f conftest.$ac_ext CC=$ac_save_CC fi # AC_CACHE_VAL case "x$ac_cv_prog_cc_c89" in x) { $as_echo "$as_me:${as_lineno-$LINENO}: result: none needed" >&5 $as_echo "none needed" >&6; } ;; xno) { $as_echo "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5 $as_echo "unsupported" >&6; } ;; *) CC="$CC $ac_cv_prog_cc_c89" { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c89" >&5 $as_echo "$ac_cv_prog_cc_c89" >&6; } ;; esac if test "x$ac_cv_prog_cc_c89" != xno; then : ac_cv_prog_cc_stdc=$ac_cv_prog_cc_c89 else ac_cv_prog_cc_stdc=no fi fi ;; esac { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $CC option to accept ISO Standard C" >&5 $as_echo_n "checking for $CC option to accept ISO Standard C... " >&6; } if ${ac_cv_prog_cc_stdc+:} false; then : $as_echo_n "(cached) " >&6 fi case $ac_cv_prog_cc_stdc in #( no) : { $as_echo "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5 $as_echo "unsupported" >&6; } ;; #( '') : { $as_echo "$as_me:${as_lineno-$LINENO}: result: none needed" >&5 $as_echo "none needed" >&6; } ;; #( *) : { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_stdc" >&5 $as_echo "$ac_cv_prog_cc_stdc" >&6; } ;; esac case `pwd` in *\ * | *\ *) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: Libtool does not cope well with whitespace in \`pwd\`" >&5 $as_echo "$as_me: WARNING: Libtool does not cope well with whitespace in \`pwd\`" >&2;} ;; esac macro_version='2.4.2' macro_revision='1.3337' ltmain="$ac_aux_dir/ltmain.sh" # Make sure we can run config.sub. $SHELL "$ac_aux_dir/config.sub" sun4 >/dev/null 2>&1 || as_fn_error $? "cannot run $SHELL $ac_aux_dir/config.sub" "$LINENO" 5 { $as_echo "$as_me:${as_lineno-$LINENO}: checking build system type" >&5 $as_echo_n "checking build system type... " >&6; } if ${ac_cv_build+:} false; then : $as_echo_n "(cached) " >&6 else ac_build_alias=$build_alias test "x$ac_build_alias" = x && ac_build_alias=`$SHELL "$ac_aux_dir/config.guess"` test "x$ac_build_alias" = x && as_fn_error $? "cannot guess build type; you must specify one" "$LINENO" 5 ac_cv_build=`$SHELL "$ac_aux_dir/config.sub" $ac_build_alias` || as_fn_error $? "$SHELL $ac_aux_dir/config.sub $ac_build_alias failed" "$LINENO" 5 fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_build" >&5 $as_echo "$ac_cv_build" >&6; } case $ac_cv_build in *-*-*) ;; *) as_fn_error $? "invalid value of canonical build" "$LINENO" 5;; esac build=$ac_cv_build ac_save_IFS=$IFS; IFS='-' set x $ac_cv_build shift build_cpu=$1 build_vendor=$2 shift; shift # Remember, the first character of IFS is used to create $*, # except with old shells: build_os=$* IFS=$ac_save_IFS case $build_os in *\ *) build_os=`echo "$build_os" | sed 's/ /-/g'`;; esac { $as_echo "$as_me:${as_lineno-$LINENO}: checking host system type" >&5 $as_echo_n "checking host system type... " >&6; } if ${ac_cv_host+:} false; then : $as_echo_n "(cached) " >&6 else if test "x$host_alias" = x; then ac_cv_host=$ac_cv_build else ac_cv_host=`$SHELL "$ac_aux_dir/config.sub" $host_alias` || as_fn_error $? "$SHELL $ac_aux_dir/config.sub $host_alias failed" "$LINENO" 5 fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_host" >&5 $as_echo "$ac_cv_host" >&6; } case $ac_cv_host in *-*-*) ;; *) as_fn_error $? "invalid value of canonical host" "$LINENO" 5;; esac host=$ac_cv_host ac_save_IFS=$IFS; IFS='-' set x $ac_cv_host shift host_cpu=$1 host_vendor=$2 shift; shift # Remember, the first character of IFS is used to create $*, # except with old shells: host_os=$* IFS=$ac_save_IFS case $host_os in *\ *) host_os=`echo "$host_os" | sed 's/ /-/g'`;; esac # Backslashify metacharacters that are still active within # double-quoted strings. sed_quote_subst='s/\(["`$\\]\)/\\\1/g' # Same as above, but do not quote variable references. double_quote_subst='s/\(["`\\]\)/\\\1/g' # Sed substitution to delay expansion of an escaped shell variable in a # double_quote_subst'ed string. delay_variable_subst='s/\\\\\\\\\\\$/\\\\\\$/g' # Sed substitution to delay expansion of an escaped single quote. delay_single_quote_subst='s/'\''/'\'\\\\\\\'\''/g' # Sed substitution to avoid accidental globbing in evaled expressions no_glob_subst='s/\*/\\\*/g' ECHO='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' ECHO=$ECHO$ECHO$ECHO$ECHO$ECHO ECHO=$ECHO$ECHO$ECHO$ECHO$ECHO$ECHO { $as_echo "$as_me:${as_lineno-$LINENO}: checking how to print strings" >&5 $as_echo_n "checking how to print strings... " >&6; } # Test print first, because it will be a builtin if present. if test "X`( print -r -- -n ) 2>/dev/null`" = X-n && \ test "X`print -r -- $ECHO 2>/dev/null`" = "X$ECHO"; then ECHO='print -r --' elif test "X`printf %s $ECHO 2>/dev/null`" = "X$ECHO"; then ECHO='printf %s\n' else # Use this function as a fallback that always works. func_fallback_echo () { eval 'cat <<_LTECHO_EOF $1 _LTECHO_EOF' } ECHO='func_fallback_echo' fi # func_echo_all arg... # Invoke $ECHO with all args, space-separated. func_echo_all () { $ECHO "" } case "$ECHO" in printf*) { $as_echo "$as_me:${as_lineno-$LINENO}: result: printf" >&5 $as_echo "printf" >&6; } ;; print*) { $as_echo "$as_me:${as_lineno-$LINENO}: result: print -r" >&5 $as_echo "print -r" >&6; } ;; *) { $as_echo "$as_me:${as_lineno-$LINENO}: result: cat" >&5 $as_echo "cat" >&6; } ;; esac { $as_echo "$as_me:${as_lineno-$LINENO}: checking for a sed that does not truncate output" >&5 $as_echo_n "checking for a sed that does not truncate output... " >&6; } if ${ac_cv_path_SED+:} false; then : $as_echo_n "(cached) " >&6 else ac_script=s/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb/ for ac_i in 1 2 3 4 5 6 7; do ac_script="$ac_script$as_nl$ac_script" done echo "$ac_script" 2>/dev/null | sed 99q >conftest.sed { ac_script=; unset ac_script;} if test -z "$SED"; then ac_path_SED_found=false # Loop through the user's path and test for each of PROGNAME-LIST as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_prog in sed gsed; do for ac_exec_ext in '' $ac_executable_extensions; do ac_path_SED="$as_dir/$ac_prog$ac_exec_ext" as_fn_executable_p "$ac_path_SED" || continue # Check for GNU ac_path_SED and select it if it is found. # Check for GNU $ac_path_SED case `"$ac_path_SED" --version 2>&1` in *GNU*) ac_cv_path_SED="$ac_path_SED" ac_path_SED_found=:;; *) ac_count=0 $as_echo_n 0123456789 >"conftest.in" while : do cat "conftest.in" "conftest.in" >"conftest.tmp" mv "conftest.tmp" "conftest.in" cp "conftest.in" "conftest.nl" $as_echo '' >> "conftest.nl" "$ac_path_SED" -f conftest.sed < "conftest.nl" >"conftest.out" 2>/dev/null || break diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break as_fn_arith $ac_count + 1 && ac_count=$as_val if test $ac_count -gt ${ac_path_SED_max-0}; then # Best one so far, save it but keep looking for a better one ac_cv_path_SED="$ac_path_SED" ac_path_SED_max=$ac_count fi # 10*(2^10) chars as input seems more than enough test $ac_count -gt 10 && break done rm -f conftest.in conftest.tmp conftest.nl conftest.out;; esac $ac_path_SED_found && break 3 done done done IFS=$as_save_IFS if test -z "$ac_cv_path_SED"; then as_fn_error $? "no acceptable sed could be found in \$PATH" "$LINENO" 5 fi else ac_cv_path_SED=$SED fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_SED" >&5 $as_echo "$ac_cv_path_SED" >&6; } SED="$ac_cv_path_SED" rm -f conftest.sed test -z "$SED" && SED=sed Xsed="$SED -e 1s/^X//" { $as_echo "$as_me:${as_lineno-$LINENO}: checking for fgrep" >&5 $as_echo_n "checking for fgrep... " >&6; } if ${ac_cv_path_FGREP+:} false; then : $as_echo_n "(cached) " >&6 else if echo 'ab*c' | $GREP -F 'ab*c' >/dev/null 2>&1 then ac_cv_path_FGREP="$GREP -F" else if test -z "$FGREP"; then ac_path_FGREP_found=false # Loop through the user's path and test for each of PROGNAME-LIST as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_prog in fgrep; do for ac_exec_ext in '' $ac_executable_extensions; do ac_path_FGREP="$as_dir/$ac_prog$ac_exec_ext" as_fn_executable_p "$ac_path_FGREP" || continue # Check for GNU ac_path_FGREP and select it if it is found. # Check for GNU $ac_path_FGREP case `"$ac_path_FGREP" --version 2>&1` in *GNU*) ac_cv_path_FGREP="$ac_path_FGREP" ac_path_FGREP_found=:;; *) ac_count=0 $as_echo_n 0123456789 >"conftest.in" while : do cat "conftest.in" "conftest.in" >"conftest.tmp" mv "conftest.tmp" "conftest.in" cp "conftest.in" "conftest.nl" $as_echo 'FGREP' >> "conftest.nl" "$ac_path_FGREP" FGREP < "conftest.nl" >"conftest.out" 2>/dev/null || break diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break as_fn_arith $ac_count + 1 && ac_count=$as_val if test $ac_count -gt ${ac_path_FGREP_max-0}; then # Best one so far, save it but keep looking for a better one ac_cv_path_FGREP="$ac_path_FGREP" ac_path_FGREP_max=$ac_count fi # 10*(2^10) chars as input seems more than enough test $ac_count -gt 10 && break done rm -f conftest.in conftest.tmp conftest.nl conftest.out;; esac $ac_path_FGREP_found && break 3 done done done IFS=$as_save_IFS if test -z "$ac_cv_path_FGREP"; then as_fn_error $? "no acceptable fgrep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5 fi else ac_cv_path_FGREP=$FGREP fi fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_FGREP" >&5 $as_echo "$ac_cv_path_FGREP" >&6; } FGREP="$ac_cv_path_FGREP" test -z "$GREP" && GREP=grep # Check whether --with-gnu-ld was given. if test "${with_gnu_ld+set}" = set; then : withval=$with_gnu_ld; test "$withval" = no || with_gnu_ld=yes else with_gnu_ld=no fi ac_prog=ld if test "$GCC" = yes; then # Check if gcc -print-prog-name=ld gives a path. { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ld used by $CC" >&5 $as_echo_n "checking for ld used by $CC... " >&6; } case $host in *-*-mingw*) # gcc leaves a trailing carriage return which upsets mingw ac_prog=`($CC -print-prog-name=ld) 2>&5 | tr -d '\015'` ;; *) ac_prog=`($CC -print-prog-name=ld) 2>&5` ;; esac case $ac_prog in # Accept absolute paths. [\\/]* | ?:[\\/]*) re_direlt='/[^/][^/]*/\.\./' # Canonicalize the pathname of ld ac_prog=`$ECHO "$ac_prog"| $SED 's%\\\\%/%g'` while $ECHO "$ac_prog" | $GREP "$re_direlt" > /dev/null 2>&1; do ac_prog=`$ECHO $ac_prog| $SED "s%$re_direlt%/%"` done test -z "$LD" && LD="$ac_prog" ;; "") # If it fails, then pretend we aren't using GCC. ac_prog=ld ;; *) # If it is relative, then search for the first ld in PATH. with_gnu_ld=unknown ;; esac elif test "$with_gnu_ld" = yes; then { $as_echo "$as_me:${as_lineno-$LINENO}: checking for GNU ld" >&5 $as_echo_n "checking for GNU ld... " >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: checking for non-GNU ld" >&5 $as_echo_n "checking for non-GNU ld... " >&6; } fi if ${lt_cv_path_LD+:} false; then : $as_echo_n "(cached) " >&6 else if test -z "$LD"; then lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR for ac_dir in $PATH; do IFS="$lt_save_ifs" test -z "$ac_dir" && ac_dir=. if test -f "$ac_dir/$ac_prog" || test -f "$ac_dir/$ac_prog$ac_exeext"; then lt_cv_path_LD="$ac_dir/$ac_prog" # Check to see if the program is GNU ld. I'd rather use --version, # but apparently some variants of GNU ld only accept -v. # Break only if it was the GNU/non-GNU ld that we prefer. case `"$lt_cv_path_LD" -v 2>&1 &5 $as_echo "$LD" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi test -z "$LD" && as_fn_error $? "no acceptable ld found in \$PATH" "$LINENO" 5 { $as_echo "$as_me:${as_lineno-$LINENO}: checking if the linker ($LD) is GNU ld" >&5 $as_echo_n "checking if the linker ($LD) is GNU ld... " >&6; } if ${lt_cv_prog_gnu_ld+:} false; then : $as_echo_n "(cached) " >&6 else # I'd rather use --version here, but apparently some GNU lds only accept -v. case `$LD -v 2>&1 &5 $as_echo "$lt_cv_prog_gnu_ld" >&6; } with_gnu_ld=$lt_cv_prog_gnu_ld { $as_echo "$as_me:${as_lineno-$LINENO}: checking for BSD- or MS-compatible name lister (nm)" >&5 $as_echo_n "checking for BSD- or MS-compatible name lister (nm)... " >&6; } if ${lt_cv_path_NM+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$NM"; then # Let the user override the test. lt_cv_path_NM="$NM" else lt_nm_to_check="${ac_tool_prefix}nm" if test -n "$ac_tool_prefix" && test "$build" = "$host"; then lt_nm_to_check="$lt_nm_to_check nm" fi for lt_tmp_nm in $lt_nm_to_check; do lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR for ac_dir in $PATH /usr/ccs/bin/elf /usr/ccs/bin /usr/ucb /bin; do IFS="$lt_save_ifs" test -z "$ac_dir" && ac_dir=. tmp_nm="$ac_dir/$lt_tmp_nm" if test -f "$tmp_nm" || test -f "$tmp_nm$ac_exeext" ; then # Check to see if the nm accepts a BSD-compat flag. # Adding the `sed 1q' prevents false positives on HP-UX, which says: # nm: unknown option "B" ignored # Tru64's nm complains that /dev/null is an invalid object file case `"$tmp_nm" -B /dev/null 2>&1 | sed '1q'` in */dev/null* | *'Invalid file or object type'*) lt_cv_path_NM="$tmp_nm -B" break ;; *) case `"$tmp_nm" -p /dev/null 2>&1 | sed '1q'` in */dev/null*) lt_cv_path_NM="$tmp_nm -p" break ;; *) lt_cv_path_NM=${lt_cv_path_NM="$tmp_nm"} # keep the first match, but continue # so that we can try to find one that supports BSD flags ;; esac ;; esac fi done IFS="$lt_save_ifs" done : ${lt_cv_path_NM=no} fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_path_NM" >&5 $as_echo "$lt_cv_path_NM" >&6; } if test "$lt_cv_path_NM" != "no"; then NM="$lt_cv_path_NM" else # Didn't find any BSD compatible name lister, look for dumpbin. if test -n "$DUMPBIN"; then : # Let the user override the test. else if test -n "$ac_tool_prefix"; then for ac_prog in dumpbin "link -dump" do # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. set dummy $ac_tool_prefix$ac_prog; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_DUMPBIN+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$DUMPBIN"; then ac_cv_prog_DUMPBIN="$DUMPBIN" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_DUMPBIN="$ac_tool_prefix$ac_prog" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi DUMPBIN=$ac_cv_prog_DUMPBIN if test -n "$DUMPBIN"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $DUMPBIN" >&5 $as_echo "$DUMPBIN" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi test -n "$DUMPBIN" && break done fi if test -z "$DUMPBIN"; then ac_ct_DUMPBIN=$DUMPBIN for ac_prog in dumpbin "link -dump" do # Extract the first word of "$ac_prog", so it can be a program name with args. set dummy $ac_prog; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_ac_ct_DUMPBIN+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_DUMPBIN"; then ac_cv_prog_ac_ct_DUMPBIN="$ac_ct_DUMPBIN" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_DUMPBIN="$ac_prog" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_DUMPBIN=$ac_cv_prog_ac_ct_DUMPBIN if test -n "$ac_ct_DUMPBIN"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_DUMPBIN" >&5 $as_echo "$ac_ct_DUMPBIN" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi test -n "$ac_ct_DUMPBIN" && break done if test "x$ac_ct_DUMPBIN" = x; then DUMPBIN=":" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac DUMPBIN=$ac_ct_DUMPBIN fi fi case `$DUMPBIN -symbols /dev/null 2>&1 | sed '1q'` in *COFF*) DUMPBIN="$DUMPBIN -symbols" ;; *) DUMPBIN=: ;; esac fi if test "$DUMPBIN" != ":"; then NM="$DUMPBIN" fi fi test -z "$NM" && NM=nm { $as_echo "$as_me:${as_lineno-$LINENO}: checking the name lister ($NM) interface" >&5 $as_echo_n "checking the name lister ($NM) interface... " >&6; } if ${lt_cv_nm_interface+:} false; then : $as_echo_n "(cached) " >&6 else lt_cv_nm_interface="BSD nm" echo "int some_variable = 0;" > conftest.$ac_ext (eval echo "\"\$as_me:$LINENO: $ac_compile\"" >&5) (eval "$ac_compile" 2>conftest.err) cat conftest.err >&5 (eval echo "\"\$as_me:$LINENO: $NM \\\"conftest.$ac_objext\\\"\"" >&5) (eval "$NM \"conftest.$ac_objext\"" 2>conftest.err > conftest.out) cat conftest.err >&5 (eval echo "\"\$as_me:$LINENO: output\"" >&5) cat conftest.out >&5 if $GREP 'External.*some_variable' conftest.out > /dev/null; then lt_cv_nm_interface="MS dumpbin" fi rm -f conftest* fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_nm_interface" >&5 $as_echo "$lt_cv_nm_interface" >&6; } { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether ln -s works" >&5 $as_echo_n "checking whether ln -s works... " >&6; } LN_S=$as_ln_s if test "$LN_S" = "ln -s"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no, using $LN_S" >&5 $as_echo "no, using $LN_S" >&6; } fi # find the maximum length of command line arguments { $as_echo "$as_me:${as_lineno-$LINENO}: checking the maximum length of command line arguments" >&5 $as_echo_n "checking the maximum length of command line arguments... " >&6; } if ${lt_cv_sys_max_cmd_len+:} false; then : $as_echo_n "(cached) " >&6 else i=0 teststring="ABCD" case $build_os in msdosdjgpp*) # On DJGPP, this test can blow up pretty badly due to problems in libc # (any single argument exceeding 2000 bytes causes a buffer overrun # during glob expansion). Even if it were fixed, the result of this # check would be larger than it should be. lt_cv_sys_max_cmd_len=12288; # 12K is about right ;; gnu*) # Under GNU Hurd, this test is not required because there is # no limit to the length of command line arguments. # Libtool will interpret -1 as no limit whatsoever lt_cv_sys_max_cmd_len=-1; ;; cygwin* | mingw* | cegcc*) # On Win9x/ME, this test blows up -- it succeeds, but takes # about 5 minutes as the teststring grows exponentially. # Worse, since 9x/ME are not pre-emptively multitasking, # you end up with a "frozen" computer, even though with patience # the test eventually succeeds (with a max line length of 256k). # Instead, let's just punt: use the minimum linelength reported by # all of the supported platforms: 8192 (on NT/2K/XP). lt_cv_sys_max_cmd_len=8192; ;; mint*) # On MiNT this can take a long time and run out of memory. lt_cv_sys_max_cmd_len=8192; ;; amigaos*) # On AmigaOS with pdksh, this test takes hours, literally. # So we just punt and use a minimum line length of 8192. lt_cv_sys_max_cmd_len=8192; ;; netbsd* | freebsd* | openbsd* | darwin* | dragonfly*) # This has been around since 386BSD, at least. Likely further. if test -x /sbin/sysctl; then lt_cv_sys_max_cmd_len=`/sbin/sysctl -n kern.argmax` elif test -x /usr/sbin/sysctl; then lt_cv_sys_max_cmd_len=`/usr/sbin/sysctl -n kern.argmax` else lt_cv_sys_max_cmd_len=65536 # usable default for all BSDs fi # And add a safety zone lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 4` lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \* 3` ;; interix*) # We know the value 262144 and hardcode it with a safety zone (like BSD) lt_cv_sys_max_cmd_len=196608 ;; os2*) # The test takes a long time on OS/2. lt_cv_sys_max_cmd_len=8192 ;; osf*) # Dr. Hans Ekkehard Plesser reports seeing a kernel panic running configure # due to this test when exec_disable_arg_limit is 1 on Tru64. It is not # nice to cause kernel panics so lets avoid the loop below. # First set a reasonable default. lt_cv_sys_max_cmd_len=16384 # if test -x /sbin/sysconfig; then case `/sbin/sysconfig -q proc exec_disable_arg_limit` in *1*) lt_cv_sys_max_cmd_len=-1 ;; esac fi ;; sco3.2v5*) lt_cv_sys_max_cmd_len=102400 ;; sysv5* | sco5v6* | sysv4.2uw2*) kargmax=`grep ARG_MAX /etc/conf/cf.d/stune 2>/dev/null` if test -n "$kargmax"; then lt_cv_sys_max_cmd_len=`echo $kargmax | sed 's/.*[ ]//'` else lt_cv_sys_max_cmd_len=32768 fi ;; *) lt_cv_sys_max_cmd_len=`(getconf ARG_MAX) 2> /dev/null` if test -n "$lt_cv_sys_max_cmd_len" && \ test undefined != "$lt_cv_sys_max_cmd_len"; then lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 4` lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \* 3` else # Make teststring a little bigger before we do anything with it. # a 1K string should be a reasonable start. for i in 1 2 3 4 5 6 7 8 ; do teststring=$teststring$teststring done SHELL=${SHELL-${CONFIG_SHELL-/bin/sh}} # If test is not a shell built-in, we'll probably end up computing a # maximum length that is only half of the actual maximum length, but # we can't tell. while { test "X"`env echo "$teststring$teststring" 2>/dev/null` \ = "X$teststring$teststring"; } >/dev/null 2>&1 && test $i != 17 # 1/2 MB should be enough do i=`expr $i + 1` teststring=$teststring$teststring done # Only check the string length outside the loop. lt_cv_sys_max_cmd_len=`expr "X$teststring" : ".*" 2>&1` teststring= # Add a significant safety factor because C++ compilers can tack on # massive amounts of additional arguments before passing them to the # linker. It appears as though 1/2 is a usable value. lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 2` fi ;; esac fi if test -n $lt_cv_sys_max_cmd_len ; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_sys_max_cmd_len" >&5 $as_echo "$lt_cv_sys_max_cmd_len" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: none" >&5 $as_echo "none" >&6; } fi max_cmd_len=$lt_cv_sys_max_cmd_len : ${CP="cp -f"} : ${MV="mv -f"} : ${RM="rm -f"} { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the shell understands some XSI constructs" >&5 $as_echo_n "checking whether the shell understands some XSI constructs... " >&6; } # Try some XSI features xsi_shell=no ( _lt_dummy="a/b/c" test "${_lt_dummy##*/},${_lt_dummy%/*},${_lt_dummy#??}"${_lt_dummy%"$_lt_dummy"}, \ = c,a/b,b/c, \ && eval 'test $(( 1 + 1 )) -eq 2 \ && test "${#_lt_dummy}" -eq 5' ) >/dev/null 2>&1 \ && xsi_shell=yes { $as_echo "$as_me:${as_lineno-$LINENO}: result: $xsi_shell" >&5 $as_echo "$xsi_shell" >&6; } { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the shell understands \"+=\"" >&5 $as_echo_n "checking whether the shell understands \"+=\"... " >&6; } lt_shell_append=no ( foo=bar; set foo baz; eval "$1+=\$2" && test "$foo" = barbaz ) \ >/dev/null 2>&1 \ && lt_shell_append=yes { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_shell_append" >&5 $as_echo "$lt_shell_append" >&6; } if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then lt_unset=unset else lt_unset=false fi # test EBCDIC or ASCII case `echo X|tr X '\101'` in A) # ASCII based system # \n is not interpreted correctly by Solaris 8 /usr/ucb/tr lt_SP2NL='tr \040 \012' lt_NL2SP='tr \015\012 \040\040' ;; *) # EBCDIC based system lt_SP2NL='tr \100 \n' lt_NL2SP='tr \r\n \100\100' ;; esac { $as_echo "$as_me:${as_lineno-$LINENO}: checking how to convert $build file names to $host format" >&5 $as_echo_n "checking how to convert $build file names to $host format... " >&6; } if ${lt_cv_to_host_file_cmd+:} false; then : $as_echo_n "(cached) " >&6 else case $host in *-*-mingw* ) case $build in *-*-mingw* ) # actually msys lt_cv_to_host_file_cmd=func_convert_file_msys_to_w32 ;; *-*-cygwin* ) lt_cv_to_host_file_cmd=func_convert_file_cygwin_to_w32 ;; * ) # otherwise, assume *nix lt_cv_to_host_file_cmd=func_convert_file_nix_to_w32 ;; esac ;; *-*-cygwin* ) case $build in *-*-mingw* ) # actually msys lt_cv_to_host_file_cmd=func_convert_file_msys_to_cygwin ;; *-*-cygwin* ) lt_cv_to_host_file_cmd=func_convert_file_noop ;; * ) # otherwise, assume *nix lt_cv_to_host_file_cmd=func_convert_file_nix_to_cygwin ;; esac ;; * ) # unhandled hosts (and "normal" native builds) lt_cv_to_host_file_cmd=func_convert_file_noop ;; esac fi to_host_file_cmd=$lt_cv_to_host_file_cmd { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_to_host_file_cmd" >&5 $as_echo "$lt_cv_to_host_file_cmd" >&6; } { $as_echo "$as_me:${as_lineno-$LINENO}: checking how to convert $build file names to toolchain format" >&5 $as_echo_n "checking how to convert $build file names to toolchain format... " >&6; } if ${lt_cv_to_tool_file_cmd+:} false; then : $as_echo_n "(cached) " >&6 else #assume ordinary cross tools, or native build. lt_cv_to_tool_file_cmd=func_convert_file_noop case $host in *-*-mingw* ) case $build in *-*-mingw* ) # actually msys lt_cv_to_tool_file_cmd=func_convert_file_msys_to_w32 ;; esac ;; esac fi to_tool_file_cmd=$lt_cv_to_tool_file_cmd { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_to_tool_file_cmd" >&5 $as_echo "$lt_cv_to_tool_file_cmd" >&6; } { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $LD option to reload object files" >&5 $as_echo_n "checking for $LD option to reload object files... " >&6; } if ${lt_cv_ld_reload_flag+:} false; then : $as_echo_n "(cached) " >&6 else lt_cv_ld_reload_flag='-r' fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_ld_reload_flag" >&5 $as_echo "$lt_cv_ld_reload_flag" >&6; } reload_flag=$lt_cv_ld_reload_flag case $reload_flag in "" | " "*) ;; *) reload_flag=" $reload_flag" ;; esac reload_cmds='$LD$reload_flag -o $output$reload_objs' case $host_os in cygwin* | mingw* | pw32* | cegcc*) if test "$GCC" != yes; then reload_cmds=false fi ;; darwin*) if test "$GCC" = yes; then reload_cmds='$LTCC $LTCFLAGS -nostdlib ${wl}-r -o $output$reload_objs' else reload_cmds='$LD$reload_flag -o $output$reload_objs' fi ;; esac if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}objdump", so it can be a program name with args. set dummy ${ac_tool_prefix}objdump; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_OBJDUMP+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$OBJDUMP"; then ac_cv_prog_OBJDUMP="$OBJDUMP" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_OBJDUMP="${ac_tool_prefix}objdump" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi OBJDUMP=$ac_cv_prog_OBJDUMP if test -n "$OBJDUMP"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $OBJDUMP" >&5 $as_echo "$OBJDUMP" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$ac_cv_prog_OBJDUMP"; then ac_ct_OBJDUMP=$OBJDUMP # Extract the first word of "objdump", so it can be a program name with args. set dummy objdump; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_ac_ct_OBJDUMP+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_OBJDUMP"; then ac_cv_prog_ac_ct_OBJDUMP="$ac_ct_OBJDUMP" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_OBJDUMP="objdump" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_OBJDUMP=$ac_cv_prog_ac_ct_OBJDUMP if test -n "$ac_ct_OBJDUMP"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_OBJDUMP" >&5 $as_echo "$ac_ct_OBJDUMP" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test "x$ac_ct_OBJDUMP" = x; then OBJDUMP="false" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac OBJDUMP=$ac_ct_OBJDUMP fi else OBJDUMP="$ac_cv_prog_OBJDUMP" fi test -z "$OBJDUMP" && OBJDUMP=objdump { $as_echo "$as_me:${as_lineno-$LINENO}: checking how to recognize dependent libraries" >&5 $as_echo_n "checking how to recognize dependent libraries... " >&6; } if ${lt_cv_deplibs_check_method+:} false; then : $as_echo_n "(cached) " >&6 else lt_cv_file_magic_cmd='$MAGIC_CMD' lt_cv_file_magic_test_file= lt_cv_deplibs_check_method='unknown' # Need to set the preceding variable on all platforms that support # interlibrary dependencies. # 'none' -- dependencies not supported. # `unknown' -- same as none, but documents that we really don't know. # 'pass_all' -- all dependencies passed with no checks. # 'test_compile' -- check by making test program. # 'file_magic [[regex]]' -- check by looking for files in library path # which responds to the $file_magic_cmd with a given extended regex. # If you have `file' or equivalent on your system and you're not sure # whether `pass_all' will *always* work, you probably want this one. case $host_os in aix[4-9]*) lt_cv_deplibs_check_method=pass_all ;; beos*) lt_cv_deplibs_check_method=pass_all ;; bsdi[45]*) lt_cv_deplibs_check_method='file_magic ELF [0-9][0-9]*-bit [ML]SB (shared object|dynamic lib)' lt_cv_file_magic_cmd='/usr/bin/file -L' lt_cv_file_magic_test_file=/shlib/libc.so ;; cygwin*) # func_win32_libid is a shell function defined in ltmain.sh lt_cv_deplibs_check_method='file_magic ^x86 archive import|^x86 DLL' lt_cv_file_magic_cmd='func_win32_libid' ;; mingw* | pw32*) # Base MSYS/MinGW do not provide the 'file' command needed by # func_win32_libid shell function, so use a weaker test based on 'objdump', # unless we find 'file', for example because we are cross-compiling. # func_win32_libid assumes BSD nm, so disallow it if using MS dumpbin. if ( test "$lt_cv_nm_interface" = "BSD nm" && file / ) >/dev/null 2>&1; then lt_cv_deplibs_check_method='file_magic ^x86 archive import|^x86 DLL' lt_cv_file_magic_cmd='func_win32_libid' else # Keep this pattern in sync with the one in func_win32_libid. lt_cv_deplibs_check_method='file_magic file format (pei*-i386(.*architecture: i386)?|pe-arm-wince|pe-x86-64)' lt_cv_file_magic_cmd='$OBJDUMP -f' fi ;; cegcc*) # use the weaker test based on 'objdump'. See mingw*. lt_cv_deplibs_check_method='file_magic file format pe-arm-.*little(.*architecture: arm)?' lt_cv_file_magic_cmd='$OBJDUMP -f' ;; darwin* | rhapsody*) lt_cv_deplibs_check_method=pass_all ;; freebsd* | dragonfly*) if echo __ELF__ | $CC -E - | $GREP __ELF__ > /dev/null; then case $host_cpu in i*86 ) # Not sure whether the presence of OpenBSD here was a mistake. # Let's accept both of them until this is cleared up. lt_cv_deplibs_check_method='file_magic (FreeBSD|OpenBSD|DragonFly)/i[3-9]86 (compact )?demand paged shared library' lt_cv_file_magic_cmd=/usr/bin/file lt_cv_file_magic_test_file=`echo /usr/lib/libc.so.*` ;; esac else lt_cv_deplibs_check_method=pass_all fi ;; haiku*) lt_cv_deplibs_check_method=pass_all ;; hpux10.20* | hpux11*) lt_cv_file_magic_cmd=/usr/bin/file case $host_cpu in ia64*) lt_cv_deplibs_check_method='file_magic (s[0-9][0-9][0-9]|ELF-[0-9][0-9]) shared object file - IA64' lt_cv_file_magic_test_file=/usr/lib/hpux32/libc.so ;; hppa*64*) lt_cv_deplibs_check_method='file_magic (s[0-9][0-9][0-9]|ELF[ -][0-9][0-9])(-bit)?( [LM]SB)? shared object( file)?[, -]* PA-RISC [0-9]\.[0-9]' lt_cv_file_magic_test_file=/usr/lib/pa20_64/libc.sl ;; *) lt_cv_deplibs_check_method='file_magic (s[0-9][0-9][0-9]|PA-RISC[0-9]\.[0-9]) shared library' lt_cv_file_magic_test_file=/usr/lib/libc.sl ;; esac ;; interix[3-9]*) # PIC code is broken on Interix 3.x, that's why |\.a not |_pic\.a here lt_cv_deplibs_check_method='match_pattern /lib[^/]+(\.so|\.a)$' ;; irix5* | irix6* | nonstopux*) case $LD in *-32|*"-32 ") libmagic=32-bit;; *-n32|*"-n32 ") libmagic=N32;; *-64|*"-64 ") libmagic=64-bit;; *) libmagic=never-match;; esac lt_cv_deplibs_check_method=pass_all ;; # This must be glibc/ELF. linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*) lt_cv_deplibs_check_method=pass_all ;; netbsd* | netbsdelf*-gnu) if echo __ELF__ | $CC -E - | $GREP __ELF__ > /dev/null; then lt_cv_deplibs_check_method='match_pattern /lib[^/]+(\.so\.[0-9]+\.[0-9]+|_pic\.a)$' else lt_cv_deplibs_check_method='match_pattern /lib[^/]+(\.so|_pic\.a)$' fi ;; newos6*) lt_cv_deplibs_check_method='file_magic ELF [0-9][0-9]*-bit [ML]SB (executable|dynamic lib)' lt_cv_file_magic_cmd=/usr/bin/file lt_cv_file_magic_test_file=/usr/lib/libnls.so ;; *nto* | *qnx*) lt_cv_deplibs_check_method=pass_all ;; openbsd*) if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then lt_cv_deplibs_check_method='match_pattern /lib[^/]+(\.so\.[0-9]+\.[0-9]+|\.so|_pic\.a)$' else lt_cv_deplibs_check_method='match_pattern /lib[^/]+(\.so\.[0-9]+\.[0-9]+|_pic\.a)$' fi ;; osf3* | osf4* | osf5*) lt_cv_deplibs_check_method=pass_all ;; rdos*) lt_cv_deplibs_check_method=pass_all ;; solaris*) lt_cv_deplibs_check_method=pass_all ;; sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX* | sysv4*uw2*) lt_cv_deplibs_check_method=pass_all ;; sysv4 | sysv4.3*) case $host_vendor in motorola) lt_cv_deplibs_check_method='file_magic ELF [0-9][0-9]*-bit [ML]SB (shared object|dynamic lib) M[0-9][0-9]* Version [0-9]' lt_cv_file_magic_test_file=`echo /usr/lib/libc.so*` ;; ncr) lt_cv_deplibs_check_method=pass_all ;; sequent) lt_cv_file_magic_cmd='/bin/file' lt_cv_deplibs_check_method='file_magic ELF [0-9][0-9]*-bit [LM]SB (shared object|dynamic lib )' ;; sni) lt_cv_file_magic_cmd='/bin/file' lt_cv_deplibs_check_method="file_magic ELF [0-9][0-9]*-bit [LM]SB dynamic lib" lt_cv_file_magic_test_file=/lib/libc.so ;; siemens) lt_cv_deplibs_check_method=pass_all ;; pc) lt_cv_deplibs_check_method=pass_all ;; esac ;; tpf*) lt_cv_deplibs_check_method=pass_all ;; esac fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_deplibs_check_method" >&5 $as_echo "$lt_cv_deplibs_check_method" >&6; } file_magic_glob= want_nocaseglob=no if test "$build" = "$host"; then case $host_os in mingw* | pw32*) if ( shopt | grep nocaseglob ) >/dev/null 2>&1; then want_nocaseglob=yes else file_magic_glob=`echo aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvVwWxXyYzZ | $SED -e "s/\(..\)/s\/[\1]\/[\1]\/g;/g"` fi ;; esac fi file_magic_cmd=$lt_cv_file_magic_cmd deplibs_check_method=$lt_cv_deplibs_check_method test -z "$deplibs_check_method" && deplibs_check_method=unknown if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}dlltool", so it can be a program name with args. set dummy ${ac_tool_prefix}dlltool; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_DLLTOOL+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$DLLTOOL"; then ac_cv_prog_DLLTOOL="$DLLTOOL" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_DLLTOOL="${ac_tool_prefix}dlltool" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi DLLTOOL=$ac_cv_prog_DLLTOOL if test -n "$DLLTOOL"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $DLLTOOL" >&5 $as_echo "$DLLTOOL" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$ac_cv_prog_DLLTOOL"; then ac_ct_DLLTOOL=$DLLTOOL # Extract the first word of "dlltool", so it can be a program name with args. set dummy dlltool; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_ac_ct_DLLTOOL+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_DLLTOOL"; then ac_cv_prog_ac_ct_DLLTOOL="$ac_ct_DLLTOOL" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_DLLTOOL="dlltool" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_DLLTOOL=$ac_cv_prog_ac_ct_DLLTOOL if test -n "$ac_ct_DLLTOOL"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_DLLTOOL" >&5 $as_echo "$ac_ct_DLLTOOL" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test "x$ac_ct_DLLTOOL" = x; then DLLTOOL="false" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac DLLTOOL=$ac_ct_DLLTOOL fi else DLLTOOL="$ac_cv_prog_DLLTOOL" fi test -z "$DLLTOOL" && DLLTOOL=dlltool { $as_echo "$as_me:${as_lineno-$LINENO}: checking how to associate runtime and link libraries" >&5 $as_echo_n "checking how to associate runtime and link libraries... " >&6; } if ${lt_cv_sharedlib_from_linklib_cmd+:} false; then : $as_echo_n "(cached) " >&6 else lt_cv_sharedlib_from_linklib_cmd='unknown' case $host_os in cygwin* | mingw* | pw32* | cegcc*) # two different shell functions defined in ltmain.sh # decide which to use based on capabilities of $DLLTOOL case `$DLLTOOL --help 2>&1` in *--identify-strict*) lt_cv_sharedlib_from_linklib_cmd=func_cygming_dll_for_implib ;; *) lt_cv_sharedlib_from_linklib_cmd=func_cygming_dll_for_implib_fallback ;; esac ;; *) # fallback: assume linklib IS sharedlib lt_cv_sharedlib_from_linklib_cmd="$ECHO" ;; esac fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_sharedlib_from_linklib_cmd" >&5 $as_echo "$lt_cv_sharedlib_from_linklib_cmd" >&6; } sharedlib_from_linklib_cmd=$lt_cv_sharedlib_from_linklib_cmd test -z "$sharedlib_from_linklib_cmd" && sharedlib_from_linklib_cmd=$ECHO if test -n "$ac_tool_prefix"; then for ac_prog in ar do # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. set dummy $ac_tool_prefix$ac_prog; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_AR+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$AR"; then ac_cv_prog_AR="$AR" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_AR="$ac_tool_prefix$ac_prog" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi AR=$ac_cv_prog_AR if test -n "$AR"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $AR" >&5 $as_echo "$AR" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi test -n "$AR" && break done fi if test -z "$AR"; then ac_ct_AR=$AR for ac_prog in ar do # Extract the first word of "$ac_prog", so it can be a program name with args. set dummy $ac_prog; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_ac_ct_AR+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_AR"; then ac_cv_prog_ac_ct_AR="$ac_ct_AR" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_AR="$ac_prog" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_AR=$ac_cv_prog_ac_ct_AR if test -n "$ac_ct_AR"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_AR" >&5 $as_echo "$ac_ct_AR" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi test -n "$ac_ct_AR" && break done if test "x$ac_ct_AR" = x; then AR="false" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac AR=$ac_ct_AR fi fi : ${AR=ar} : ${AR_FLAGS=cru} { $as_echo "$as_me:${as_lineno-$LINENO}: checking for archiver @FILE support" >&5 $as_echo_n "checking for archiver @FILE support... " >&6; } if ${lt_cv_ar_at_file+:} false; then : $as_echo_n "(cached) " >&6 else lt_cv_ar_at_file=no cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : echo conftest.$ac_objext > conftest.lst lt_ar_try='$AR $AR_FLAGS libconftest.a @conftest.lst >&5' { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$lt_ar_try\""; } >&5 (eval $lt_ar_try) 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } if test "$ac_status" -eq 0; then # Ensure the archiver fails upon bogus file names. rm -f conftest.$ac_objext libconftest.a { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$lt_ar_try\""; } >&5 (eval $lt_ar_try) 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } if test "$ac_status" -ne 0; then lt_cv_ar_at_file=@ fi fi rm -f conftest.* libconftest.a fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_ar_at_file" >&5 $as_echo "$lt_cv_ar_at_file" >&6; } if test "x$lt_cv_ar_at_file" = xno; then archiver_list_spec= else archiver_list_spec=$lt_cv_ar_at_file fi if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}strip", so it can be a program name with args. set dummy ${ac_tool_prefix}strip; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_STRIP+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$STRIP"; then ac_cv_prog_STRIP="$STRIP" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_STRIP="${ac_tool_prefix}strip" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi STRIP=$ac_cv_prog_STRIP if test -n "$STRIP"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $STRIP" >&5 $as_echo "$STRIP" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$ac_cv_prog_STRIP"; then ac_ct_STRIP=$STRIP # Extract the first word of "strip", so it can be a program name with args. set dummy strip; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_ac_ct_STRIP+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_STRIP"; then ac_cv_prog_ac_ct_STRIP="$ac_ct_STRIP" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_STRIP="strip" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_STRIP=$ac_cv_prog_ac_ct_STRIP if test -n "$ac_ct_STRIP"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_STRIP" >&5 $as_echo "$ac_ct_STRIP" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test "x$ac_ct_STRIP" = x; then STRIP=":" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac STRIP=$ac_ct_STRIP fi else STRIP="$ac_cv_prog_STRIP" fi test -z "$STRIP" && STRIP=: if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}ranlib", so it can be a program name with args. set dummy ${ac_tool_prefix}ranlib; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_RANLIB+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$RANLIB"; then ac_cv_prog_RANLIB="$RANLIB" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_RANLIB="${ac_tool_prefix}ranlib" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi RANLIB=$ac_cv_prog_RANLIB if test -n "$RANLIB"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $RANLIB" >&5 $as_echo "$RANLIB" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$ac_cv_prog_RANLIB"; then ac_ct_RANLIB=$RANLIB # Extract the first word of "ranlib", so it can be a program name with args. set dummy ranlib; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_ac_ct_RANLIB+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_RANLIB"; then ac_cv_prog_ac_ct_RANLIB="$ac_ct_RANLIB" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_RANLIB="ranlib" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_RANLIB=$ac_cv_prog_ac_ct_RANLIB if test -n "$ac_ct_RANLIB"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_RANLIB" >&5 $as_echo "$ac_ct_RANLIB" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test "x$ac_ct_RANLIB" = x; then RANLIB=":" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac RANLIB=$ac_ct_RANLIB fi else RANLIB="$ac_cv_prog_RANLIB" fi test -z "$RANLIB" && RANLIB=: # Determine commands to create old-style static archives. old_archive_cmds='$AR $AR_FLAGS $oldlib$oldobjs' old_postinstall_cmds='chmod 644 $oldlib' old_postuninstall_cmds= if test -n "$RANLIB"; then case $host_os in openbsd*) old_postinstall_cmds="$old_postinstall_cmds~\$RANLIB -t \$tool_oldlib" ;; *) old_postinstall_cmds="$old_postinstall_cmds~\$RANLIB \$tool_oldlib" ;; esac old_archive_cmds="$old_archive_cmds~\$RANLIB \$tool_oldlib" fi case $host_os in darwin*) lock_old_archive_extraction=yes ;; *) lock_old_archive_extraction=no ;; esac # If no C compiler was specified, use CC. LTCC=${LTCC-"$CC"} # If no C compiler flags were specified, use CFLAGS. LTCFLAGS=${LTCFLAGS-"$CFLAGS"} # Allow CC to be a program name with arguments. compiler=$CC # Check for command to grab the raw symbol name followed by C symbol from nm. { $as_echo "$as_me:${as_lineno-$LINENO}: checking command to parse $NM output from $compiler object" >&5 $as_echo_n "checking command to parse $NM output from $compiler object... " >&6; } if ${lt_cv_sys_global_symbol_pipe+:} false; then : $as_echo_n "(cached) " >&6 else # These are sane defaults that work on at least a few old systems. # [They come from Ultrix. What could be older than Ultrix?!! ;)] # Character class describing NM global symbol codes. symcode='[BCDEGRST]' # Regexp to match symbols that can be accessed directly from C. sympat='\([_A-Za-z][_A-Za-z0-9]*\)' # Define system-specific variables. case $host_os in aix*) symcode='[BCDT]' ;; cygwin* | mingw* | pw32* | cegcc*) symcode='[ABCDGISTW]' ;; hpux*) if test "$host_cpu" = ia64; then symcode='[ABCDEGRST]' fi ;; irix* | nonstopux*) symcode='[BCDEGRST]' ;; osf*) symcode='[BCDEGQRST]' ;; solaris*) symcode='[BDRT]' ;; sco3.2v5*) symcode='[DT]' ;; sysv4.2uw2*) symcode='[DT]' ;; sysv5* | sco5v6* | unixware* | OpenUNIX*) symcode='[ABDT]' ;; sysv4) symcode='[DFNSTU]' ;; esac # If we're using GNU nm, then use its standard symbol codes. case `$NM -V 2>&1` in *GNU* | *'with BFD'*) symcode='[ABCDGIRSTW]' ;; esac # Transform an extracted symbol line into a proper C declaration. # Some systems (esp. on ia64) link data and code symbols differently, # so use this general approach. lt_cv_sys_global_symbol_to_cdecl="sed -n -e 's/^T .* \(.*\)$/extern int \1();/p' -e 's/^$symcode* .* \(.*\)$/extern char \1;/p'" # Transform an extracted symbol line into symbol name and symbol address lt_cv_sys_global_symbol_to_c_name_address="sed -n -e 's/^: \([^ ]*\)[ ]*$/ {\\\"\1\\\", (void *) 0},/p' -e 's/^$symcode* \([^ ]*\) \([^ ]*\)$/ {\"\2\", (void *) \&\2},/p'" lt_cv_sys_global_symbol_to_c_name_address_lib_prefix="sed -n -e 's/^: \([^ ]*\)[ ]*$/ {\\\"\1\\\", (void *) 0},/p' -e 's/^$symcode* \([^ ]*\) \(lib[^ ]*\)$/ {\"\2\", (void *) \&\2},/p' -e 's/^$symcode* \([^ ]*\) \([^ ]*\)$/ {\"lib\2\", (void *) \&\2},/p'" # Handle CRLF in mingw tool chain opt_cr= case $build_os in mingw*) opt_cr=`$ECHO 'x\{0,1\}' | tr x '\015'` # option cr in regexp ;; esac # Try without a prefix underscore, then with it. for ac_symprfx in "" "_"; do # Transform symcode, sympat, and symprfx into a raw symbol and a C symbol. symxfrm="\\1 $ac_symprfx\\2 \\2" # Write the raw and C identifiers. if test "$lt_cv_nm_interface" = "MS dumpbin"; then # Fake it for dumpbin and say T for any non-static function # and D for any global variable. # Also find C++ and __fastcall symbols from MSVC++, # which start with @ or ?. lt_cv_sys_global_symbol_pipe="$AWK '"\ " {last_section=section; section=\$ 3};"\ " /^COFF SYMBOL TABLE/{for(i in hide) delete hide[i]};"\ " /Section length .*#relocs.*(pick any)/{hide[last_section]=1};"\ " \$ 0!~/External *\|/{next};"\ " / 0+ UNDEF /{next}; / UNDEF \([^|]\)*()/{next};"\ " {if(hide[section]) next};"\ " {f=0}; \$ 0~/\(\).*\|/{f=1}; {printf f ? \"T \" : \"D \"};"\ " {split(\$ 0, a, /\||\r/); split(a[2], s)};"\ " s[1]~/^[@?]/{print s[1], s[1]; next};"\ " s[1]~prfx {split(s[1],t,\"@\"); print t[1], substr(t[1],length(prfx))}"\ " ' prfx=^$ac_symprfx" else lt_cv_sys_global_symbol_pipe="sed -n -e 's/^.*[ ]\($symcode$symcode*\)[ ][ ]*$ac_symprfx$sympat$opt_cr$/$symxfrm/p'" fi lt_cv_sys_global_symbol_pipe="$lt_cv_sys_global_symbol_pipe | sed '/ __gnu_lto/d'" # Check to see that the pipe works correctly. pipe_works=no rm -f conftest* cat > conftest.$ac_ext <<_LT_EOF #ifdef __cplusplus extern "C" { #endif char nm_test_var; void nm_test_func(void); void nm_test_func(void){} #ifdef __cplusplus } #endif int main(){nm_test_var='a';nm_test_func();return(0);} _LT_EOF if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5 (eval $ac_compile) 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then # Now try to grab the symbols. nlist=conftest.nm if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$NM conftest.$ac_objext \| "$lt_cv_sys_global_symbol_pipe" \> $nlist\""; } >&5 (eval $NM conftest.$ac_objext \| "$lt_cv_sys_global_symbol_pipe" \> $nlist) 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } && test -s "$nlist"; then # Try sorting and uniquifying the output. if sort "$nlist" | uniq > "$nlist"T; then mv -f "$nlist"T "$nlist" else rm -f "$nlist"T fi # Make sure that we snagged all the symbols we need. if $GREP ' nm_test_var$' "$nlist" >/dev/null; then if $GREP ' nm_test_func$' "$nlist" >/dev/null; then cat <<_LT_EOF > conftest.$ac_ext /* Keep this code in sync between libtool.m4, ltmain, lt_system.h, and tests. */ #if defined(_WIN32) || defined(__CYGWIN__) || defined(_WIN32_WCE) /* DATA imports from DLLs on WIN32 con't be const, because runtime relocations are performed -- see ld's documentation on pseudo-relocs. */ # define LT_DLSYM_CONST #elif defined(__osf__) /* This system does not cope well with relocations in const data. */ # define LT_DLSYM_CONST #else # define LT_DLSYM_CONST const #endif #ifdef __cplusplus extern "C" { #endif _LT_EOF # Now generate the symbol file. eval "$lt_cv_sys_global_symbol_to_cdecl"' < "$nlist" | $GREP -v main >> conftest.$ac_ext' cat <<_LT_EOF >> conftest.$ac_ext /* The mapping between symbol names and symbols. */ LT_DLSYM_CONST struct { const char *name; void *address; } lt__PROGRAM__LTX_preloaded_symbols[] = { { "@PROGRAM@", (void *) 0 }, _LT_EOF $SED "s/^$symcode$symcode* \(.*\) \(.*\)$/ {\"\2\", (void *) \&\2},/" < "$nlist" | $GREP -v main >> conftest.$ac_ext cat <<\_LT_EOF >> conftest.$ac_ext {0, (void *) 0} }; /* This works around a problem in FreeBSD linker */ #ifdef FREEBSD_WORKAROUND static const void *lt_preloaded_setup() { return lt__PROGRAM__LTX_preloaded_symbols; } #endif #ifdef __cplusplus } #endif _LT_EOF # Now try linking the two files. mv conftest.$ac_objext conftstm.$ac_objext lt_globsym_save_LIBS=$LIBS lt_globsym_save_CFLAGS=$CFLAGS LIBS="conftstm.$ac_objext" CFLAGS="$CFLAGS$lt_prog_compiler_no_builtin_flag" if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_link\""; } >&5 (eval $ac_link) 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } && test -s conftest${ac_exeext}; then pipe_works=yes fi LIBS=$lt_globsym_save_LIBS CFLAGS=$lt_globsym_save_CFLAGS else echo "cannot find nm_test_func in $nlist" >&5 fi else echo "cannot find nm_test_var in $nlist" >&5 fi else echo "cannot run $lt_cv_sys_global_symbol_pipe" >&5 fi else echo "$progname: failed program was:" >&5 cat conftest.$ac_ext >&5 fi rm -rf conftest* conftst* # Do not use the global_symbol_pipe unless it works. if test "$pipe_works" = yes; then break else lt_cv_sys_global_symbol_pipe= fi done fi if test -z "$lt_cv_sys_global_symbol_pipe"; then lt_cv_sys_global_symbol_to_cdecl= fi if test -z "$lt_cv_sys_global_symbol_pipe$lt_cv_sys_global_symbol_to_cdecl"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: failed" >&5 $as_echo "failed" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: ok" >&5 $as_echo "ok" >&6; } fi # Response file support. if test "$lt_cv_nm_interface" = "MS dumpbin"; then nm_file_list_spec='@' elif $NM --help 2>/dev/null | grep '[@]FILE' >/dev/null; then nm_file_list_spec='@' fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for sysroot" >&5 $as_echo_n "checking for sysroot... " >&6; } # Check whether --with-sysroot was given. if test "${with_sysroot+set}" = set; then : withval=$with_sysroot; else with_sysroot=no fi lt_sysroot= case ${with_sysroot} in #( yes) if test "$GCC" = yes; then lt_sysroot=`$CC --print-sysroot 2>/dev/null` fi ;; #( /*) lt_sysroot=`echo "$with_sysroot" | sed -e "$sed_quote_subst"` ;; #( no|'') ;; #( *) { $as_echo "$as_me:${as_lineno-$LINENO}: result: ${with_sysroot}" >&5 $as_echo "${with_sysroot}" >&6; } as_fn_error $? "The sysroot must be an absolute path." "$LINENO" 5 ;; esac { $as_echo "$as_me:${as_lineno-$LINENO}: result: ${lt_sysroot:-no}" >&5 $as_echo "${lt_sysroot:-no}" >&6; } # Check whether --enable-libtool-lock was given. if test "${enable_libtool_lock+set}" = set; then : enableval=$enable_libtool_lock; fi test "x$enable_libtool_lock" != xno && enable_libtool_lock=yes # Some flags need to be propagated to the compiler or linker for good # libtool support. case $host in ia64-*-hpux*) # Find out which ABI we are using. echo 'int i;' > conftest.$ac_ext if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5 (eval $ac_compile) 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then case `/usr/bin/file conftest.$ac_objext` in *ELF-32*) HPUX_IA64_MODE="32" ;; *ELF-64*) HPUX_IA64_MODE="64" ;; esac fi rm -rf conftest* ;; *-*-irix6*) # Find out which ABI we are using. echo '#line '$LINENO' "configure"' > conftest.$ac_ext if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5 (eval $ac_compile) 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then if test "$lt_cv_prog_gnu_ld" = yes; then case `/usr/bin/file conftest.$ac_objext` in *32-bit*) LD="${LD-ld} -melf32bsmip" ;; *N32*) LD="${LD-ld} -melf32bmipn32" ;; *64-bit*) LD="${LD-ld} -melf64bmip" ;; esac else case `/usr/bin/file conftest.$ac_objext` in *32-bit*) LD="${LD-ld} -32" ;; *N32*) LD="${LD-ld} -n32" ;; *64-bit*) LD="${LD-ld} -64" ;; esac fi fi rm -rf conftest* ;; x86_64-*kfreebsd*-gnu|x86_64-*linux*|powerpc*-*linux*| \ s390*-*linux*|s390*-*tpf*|sparc*-*linux*) # Find out which ABI we are using. echo 'int i;' > conftest.$ac_ext if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5 (eval $ac_compile) 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then case `/usr/bin/file conftest.o` in *32-bit*) case $host in x86_64-*kfreebsd*-gnu) LD="${LD-ld} -m elf_i386_fbsd" ;; x86_64-*linux*) case `/usr/bin/file conftest.o` in *x86-64*) LD="${LD-ld} -m elf32_x86_64" ;; *) LD="${LD-ld} -m elf_i386" ;; esac ;; powerpc64le-*) LD="${LD-ld} -m elf32lppclinux" ;; powerpc64-*) LD="${LD-ld} -m elf32ppclinux" ;; s390x-*linux*) LD="${LD-ld} -m elf_s390" ;; sparc64-*linux*) LD="${LD-ld} -m elf32_sparc" ;; esac ;; *64-bit*) case $host in x86_64-*kfreebsd*-gnu) LD="${LD-ld} -m elf_x86_64_fbsd" ;; x86_64-*linux*) LD="${LD-ld} -m elf_x86_64" ;; powerpcle-*) LD="${LD-ld} -m elf64lppc" ;; powerpc-*) LD="${LD-ld} -m elf64ppc" ;; s390*-*linux*|s390*-*tpf*) LD="${LD-ld} -m elf64_s390" ;; sparc*-*linux*) LD="${LD-ld} -m elf64_sparc" ;; esac ;; esac fi rm -rf conftest* ;; *-*-sco3.2v5*) # On SCO OpenServer 5, we need -belf to get full-featured binaries. SAVE_CFLAGS="$CFLAGS" CFLAGS="$CFLAGS -belf" { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the C compiler needs -belf" >&5 $as_echo_n "checking whether the C compiler needs -belf... " >&6; } if ${lt_cv_cc_needs_belf+:} false; then : $as_echo_n "(cached) " >&6 else ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : lt_cv_cc_needs_belf=yes else lt_cv_cc_needs_belf=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_cc_needs_belf" >&5 $as_echo "$lt_cv_cc_needs_belf" >&6; } if test x"$lt_cv_cc_needs_belf" != x"yes"; then # this is probably gcc 2.8.0, egcs 1.0 or newer; no need for -belf CFLAGS="$SAVE_CFLAGS" fi ;; *-*solaris*) # Find out which ABI we are using. echo 'int i;' > conftest.$ac_ext if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5 (eval $ac_compile) 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then case `/usr/bin/file conftest.o` in *64-bit*) case $lt_cv_prog_gnu_ld in yes*) case $host in i?86-*-solaris*) LD="${LD-ld} -m elf_x86_64" ;; sparc*-*-solaris*) LD="${LD-ld} -m elf64_sparc" ;; esac # GNU ld 2.21 introduced _sol2 emulations. Use them if available. if ${LD-ld} -V | grep _sol2 >/dev/null 2>&1; then LD="${LD-ld}_sol2" fi ;; *) if ${LD-ld} -64 -r -o conftest2.o conftest.o >/dev/null 2>&1; then LD="${LD-ld} -64" fi ;; esac ;; esac fi rm -rf conftest* ;; esac need_locks="$enable_libtool_lock" if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}mt", so it can be a program name with args. set dummy ${ac_tool_prefix}mt; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_MANIFEST_TOOL+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$MANIFEST_TOOL"; then ac_cv_prog_MANIFEST_TOOL="$MANIFEST_TOOL" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_MANIFEST_TOOL="${ac_tool_prefix}mt" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi MANIFEST_TOOL=$ac_cv_prog_MANIFEST_TOOL if test -n "$MANIFEST_TOOL"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $MANIFEST_TOOL" >&5 $as_echo "$MANIFEST_TOOL" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$ac_cv_prog_MANIFEST_TOOL"; then ac_ct_MANIFEST_TOOL=$MANIFEST_TOOL # Extract the first word of "mt", so it can be a program name with args. set dummy mt; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_ac_ct_MANIFEST_TOOL+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_MANIFEST_TOOL"; then ac_cv_prog_ac_ct_MANIFEST_TOOL="$ac_ct_MANIFEST_TOOL" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_MANIFEST_TOOL="mt" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_MANIFEST_TOOL=$ac_cv_prog_ac_ct_MANIFEST_TOOL if test -n "$ac_ct_MANIFEST_TOOL"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_MANIFEST_TOOL" >&5 $as_echo "$ac_ct_MANIFEST_TOOL" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test "x$ac_ct_MANIFEST_TOOL" = x; then MANIFEST_TOOL=":" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac MANIFEST_TOOL=$ac_ct_MANIFEST_TOOL fi else MANIFEST_TOOL="$ac_cv_prog_MANIFEST_TOOL" fi test -z "$MANIFEST_TOOL" && MANIFEST_TOOL=mt { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $MANIFEST_TOOL is a manifest tool" >&5 $as_echo_n "checking if $MANIFEST_TOOL is a manifest tool... " >&6; } if ${lt_cv_path_mainfest_tool+:} false; then : $as_echo_n "(cached) " >&6 else lt_cv_path_mainfest_tool=no echo "$as_me:$LINENO: $MANIFEST_TOOL '-?'" >&5 $MANIFEST_TOOL '-?' 2>conftest.err > conftest.out cat conftest.err >&5 if $GREP 'Manifest Tool' conftest.out > /dev/null; then lt_cv_path_mainfest_tool=yes fi rm -f conftest* fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_path_mainfest_tool" >&5 $as_echo "$lt_cv_path_mainfest_tool" >&6; } if test "x$lt_cv_path_mainfest_tool" != xyes; then MANIFEST_TOOL=: fi case $host_os in rhapsody* | darwin*) if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}dsymutil", so it can be a program name with args. set dummy ${ac_tool_prefix}dsymutil; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_DSYMUTIL+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$DSYMUTIL"; then ac_cv_prog_DSYMUTIL="$DSYMUTIL" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_DSYMUTIL="${ac_tool_prefix}dsymutil" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi DSYMUTIL=$ac_cv_prog_DSYMUTIL if test -n "$DSYMUTIL"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $DSYMUTIL" >&5 $as_echo "$DSYMUTIL" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$ac_cv_prog_DSYMUTIL"; then ac_ct_DSYMUTIL=$DSYMUTIL # Extract the first word of "dsymutil", so it can be a program name with args. set dummy dsymutil; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_ac_ct_DSYMUTIL+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_DSYMUTIL"; then ac_cv_prog_ac_ct_DSYMUTIL="$ac_ct_DSYMUTIL" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_DSYMUTIL="dsymutil" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_DSYMUTIL=$ac_cv_prog_ac_ct_DSYMUTIL if test -n "$ac_ct_DSYMUTIL"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_DSYMUTIL" >&5 $as_echo "$ac_ct_DSYMUTIL" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test "x$ac_ct_DSYMUTIL" = x; then DSYMUTIL=":" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac DSYMUTIL=$ac_ct_DSYMUTIL fi else DSYMUTIL="$ac_cv_prog_DSYMUTIL" fi if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}nmedit", so it can be a program name with args. set dummy ${ac_tool_prefix}nmedit; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_NMEDIT+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$NMEDIT"; then ac_cv_prog_NMEDIT="$NMEDIT" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_NMEDIT="${ac_tool_prefix}nmedit" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi NMEDIT=$ac_cv_prog_NMEDIT if test -n "$NMEDIT"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $NMEDIT" >&5 $as_echo "$NMEDIT" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$ac_cv_prog_NMEDIT"; then ac_ct_NMEDIT=$NMEDIT # Extract the first word of "nmedit", so it can be a program name with args. set dummy nmedit; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_ac_ct_NMEDIT+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_NMEDIT"; then ac_cv_prog_ac_ct_NMEDIT="$ac_ct_NMEDIT" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_NMEDIT="nmedit" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_NMEDIT=$ac_cv_prog_ac_ct_NMEDIT if test -n "$ac_ct_NMEDIT"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_NMEDIT" >&5 $as_echo "$ac_ct_NMEDIT" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test "x$ac_ct_NMEDIT" = x; then NMEDIT=":" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac NMEDIT=$ac_ct_NMEDIT fi else NMEDIT="$ac_cv_prog_NMEDIT" fi if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}lipo", so it can be a program name with args. set dummy ${ac_tool_prefix}lipo; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_LIPO+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$LIPO"; then ac_cv_prog_LIPO="$LIPO" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_LIPO="${ac_tool_prefix}lipo" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi LIPO=$ac_cv_prog_LIPO if test -n "$LIPO"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $LIPO" >&5 $as_echo "$LIPO" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$ac_cv_prog_LIPO"; then ac_ct_LIPO=$LIPO # Extract the first word of "lipo", so it can be a program name with args. set dummy lipo; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_ac_ct_LIPO+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_LIPO"; then ac_cv_prog_ac_ct_LIPO="$ac_ct_LIPO" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_LIPO="lipo" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_LIPO=$ac_cv_prog_ac_ct_LIPO if test -n "$ac_ct_LIPO"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_LIPO" >&5 $as_echo "$ac_ct_LIPO" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test "x$ac_ct_LIPO" = x; then LIPO=":" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac LIPO=$ac_ct_LIPO fi else LIPO="$ac_cv_prog_LIPO" fi if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}otool", so it can be a program name with args. set dummy ${ac_tool_prefix}otool; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_OTOOL+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$OTOOL"; then ac_cv_prog_OTOOL="$OTOOL" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_OTOOL="${ac_tool_prefix}otool" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi OTOOL=$ac_cv_prog_OTOOL if test -n "$OTOOL"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $OTOOL" >&5 $as_echo "$OTOOL" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$ac_cv_prog_OTOOL"; then ac_ct_OTOOL=$OTOOL # Extract the first word of "otool", so it can be a program name with args. set dummy otool; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_ac_ct_OTOOL+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_OTOOL"; then ac_cv_prog_ac_ct_OTOOL="$ac_ct_OTOOL" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_OTOOL="otool" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_OTOOL=$ac_cv_prog_ac_ct_OTOOL if test -n "$ac_ct_OTOOL"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_OTOOL" >&5 $as_echo "$ac_ct_OTOOL" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test "x$ac_ct_OTOOL" = x; then OTOOL=":" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac OTOOL=$ac_ct_OTOOL fi else OTOOL="$ac_cv_prog_OTOOL" fi if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}otool64", so it can be a program name with args. set dummy ${ac_tool_prefix}otool64; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_OTOOL64+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$OTOOL64"; then ac_cv_prog_OTOOL64="$OTOOL64" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_OTOOL64="${ac_tool_prefix}otool64" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi OTOOL64=$ac_cv_prog_OTOOL64 if test -n "$OTOOL64"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $OTOOL64" >&5 $as_echo "$OTOOL64" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$ac_cv_prog_OTOOL64"; then ac_ct_OTOOL64=$OTOOL64 # Extract the first word of "otool64", so it can be a program name with args. set dummy otool64; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_ac_ct_OTOOL64+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_OTOOL64"; then ac_cv_prog_ac_ct_OTOOL64="$ac_ct_OTOOL64" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_OTOOL64="otool64" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_OTOOL64=$ac_cv_prog_ac_ct_OTOOL64 if test -n "$ac_ct_OTOOL64"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_OTOOL64" >&5 $as_echo "$ac_ct_OTOOL64" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test "x$ac_ct_OTOOL64" = x; then OTOOL64=":" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac OTOOL64=$ac_ct_OTOOL64 fi else OTOOL64="$ac_cv_prog_OTOOL64" fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for -single_module linker flag" >&5 $as_echo_n "checking for -single_module linker flag... " >&6; } if ${lt_cv_apple_cc_single_mod+:} false; then : $as_echo_n "(cached) " >&6 else lt_cv_apple_cc_single_mod=no if test -z "${LT_MULTI_MODULE}"; then # By default we will add the -single_module flag. You can override # by either setting the environment variable LT_MULTI_MODULE # non-empty at configure time, or by adding -multi_module to the # link flags. rm -rf libconftest.dylib* echo "int foo(void){return 1;}" > conftest.c echo "$LTCC $LTCFLAGS $LDFLAGS -o libconftest.dylib \ -dynamiclib -Wl,-single_module conftest.c" >&5 $LTCC $LTCFLAGS $LDFLAGS -o libconftest.dylib \ -dynamiclib -Wl,-single_module conftest.c 2>conftest.err _lt_result=$? # If there is a non-empty error log, and "single_module" # appears in it, assume the flag caused a linker warning if test -s conftest.err && $GREP single_module conftest.err; then cat conftest.err >&5 # Otherwise, if the output was created with a 0 exit code from # the compiler, it worked. elif test -f libconftest.dylib && test $_lt_result -eq 0; then lt_cv_apple_cc_single_mod=yes else cat conftest.err >&5 fi rm -rf libconftest.dylib* rm -f conftest.* fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_apple_cc_single_mod" >&5 $as_echo "$lt_cv_apple_cc_single_mod" >&6; } { $as_echo "$as_me:${as_lineno-$LINENO}: checking for -exported_symbols_list linker flag" >&5 $as_echo_n "checking for -exported_symbols_list linker flag... " >&6; } if ${lt_cv_ld_exported_symbols_list+:} false; then : $as_echo_n "(cached) " >&6 else lt_cv_ld_exported_symbols_list=no save_LDFLAGS=$LDFLAGS echo "_main" > conftest.sym LDFLAGS="$LDFLAGS -Wl,-exported_symbols_list,conftest.sym" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : lt_cv_ld_exported_symbols_list=yes else lt_cv_ld_exported_symbols_list=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LDFLAGS="$save_LDFLAGS" fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_ld_exported_symbols_list" >&5 $as_echo "$lt_cv_ld_exported_symbols_list" >&6; } { $as_echo "$as_me:${as_lineno-$LINENO}: checking for -force_load linker flag" >&5 $as_echo_n "checking for -force_load linker flag... " >&6; } if ${lt_cv_ld_force_load+:} false; then : $as_echo_n "(cached) " >&6 else lt_cv_ld_force_load=no cat > conftest.c << _LT_EOF int forced_loaded() { return 2;} _LT_EOF echo "$LTCC $LTCFLAGS -c -o conftest.o conftest.c" >&5 $LTCC $LTCFLAGS -c -o conftest.o conftest.c 2>&5 echo "$AR cru libconftest.a conftest.o" >&5 $AR cru libconftest.a conftest.o 2>&5 echo "$RANLIB libconftest.a" >&5 $RANLIB libconftest.a 2>&5 cat > conftest.c << _LT_EOF int main() { return 0;} _LT_EOF echo "$LTCC $LTCFLAGS $LDFLAGS -o conftest conftest.c -Wl,-force_load,./libconftest.a" >&5 $LTCC $LTCFLAGS $LDFLAGS -o conftest conftest.c -Wl,-force_load,./libconftest.a 2>conftest.err _lt_result=$? if test -s conftest.err && $GREP force_load conftest.err; then cat conftest.err >&5 elif test -f conftest && test $_lt_result -eq 0 && $GREP forced_load conftest >/dev/null 2>&1 ; then lt_cv_ld_force_load=yes else cat conftest.err >&5 fi rm -f conftest.err libconftest.a conftest conftest.c rm -rf conftest.dSYM fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_ld_force_load" >&5 $as_echo "$lt_cv_ld_force_load" >&6; } case $host_os in rhapsody* | darwin1.[012]) _lt_dar_allow_undefined='${wl}-undefined ${wl}suppress' ;; darwin1.*) _lt_dar_allow_undefined='${wl}-flat_namespace ${wl}-undefined ${wl}suppress' ;; darwin*) # darwin 5.x on # if running on 10.5 or later, the deployment target defaults # to the OS version, if on x86, and 10.4, the deployment # target defaults to 10.4. Don't you love it? case ${MACOSX_DEPLOYMENT_TARGET-10.0},$host in 10.0,*86*-darwin8*|10.0,*-darwin[91]*) _lt_dar_allow_undefined='${wl}-undefined ${wl}dynamic_lookup' ;; 10.[012]*) _lt_dar_allow_undefined='${wl}-flat_namespace ${wl}-undefined ${wl}suppress' ;; 10.*) _lt_dar_allow_undefined='${wl}-undefined ${wl}dynamic_lookup' ;; esac ;; esac if test "$lt_cv_apple_cc_single_mod" = "yes"; then _lt_dar_single_mod='$single_module' fi if test "$lt_cv_ld_exported_symbols_list" = "yes"; then _lt_dar_export_syms=' ${wl}-exported_symbols_list,$output_objdir/${libname}-symbols.expsym' else _lt_dar_export_syms='~$NMEDIT -s $output_objdir/${libname}-symbols.expsym ${lib}' fi if test "$DSYMUTIL" != ":" && test "$lt_cv_ld_force_load" = "no"; then _lt_dsymutil='~$DSYMUTIL $lib || :' else _lt_dsymutil= fi ;; esac for ac_header in dlfcn.h do : ac_fn_c_check_header_compile "$LINENO" "dlfcn.h" "ac_cv_header_dlfcn_h" "$ac_includes_default " if test "x$ac_cv_header_dlfcn_h" = xyes; then : cat >>confdefs.h <<_ACEOF #define HAVE_DLFCN_H 1 _ACEOF fi done # Set options enable_dlopen=no enable_win32_dll=no # Check whether --enable-shared was given. if test "${enable_shared+set}" = set; then : enableval=$enable_shared; p=${PACKAGE-default} case $enableval in yes) enable_shared=yes ;; no) enable_shared=no ;; *) enable_shared=no # Look at the argument we got. We use all the common list separators. lt_save_ifs="$IFS"; IFS="${IFS}$PATH_SEPARATOR," for pkg in $enableval; do IFS="$lt_save_ifs" if test "X$pkg" = "X$p"; then enable_shared=yes fi done IFS="$lt_save_ifs" ;; esac else enable_shared=yes fi # Check whether --enable-static was given. if test "${enable_static+set}" = set; then : enableval=$enable_static; p=${PACKAGE-default} case $enableval in yes) enable_static=yes ;; no) enable_static=no ;; *) enable_static=no # Look at the argument we got. We use all the common list separators. lt_save_ifs="$IFS"; IFS="${IFS}$PATH_SEPARATOR," for pkg in $enableval; do IFS="$lt_save_ifs" if test "X$pkg" = "X$p"; then enable_static=yes fi done IFS="$lt_save_ifs" ;; esac else enable_static=yes fi # Check whether --with-pic was given. if test "${with_pic+set}" = set; then : withval=$with_pic; lt_p=${PACKAGE-default} case $withval in yes|no) pic_mode=$withval ;; *) pic_mode=default # Look at the argument we got. We use all the common list separators. lt_save_ifs="$IFS"; IFS="${IFS}$PATH_SEPARATOR," for lt_pkg in $withval; do IFS="$lt_save_ifs" if test "X$lt_pkg" = "X$lt_p"; then pic_mode=yes fi done IFS="$lt_save_ifs" ;; esac else pic_mode=default fi test -z "$pic_mode" && pic_mode=default # Check whether --enable-fast-install was given. if test "${enable_fast_install+set}" = set; then : enableval=$enable_fast_install; p=${PACKAGE-default} case $enableval in yes) enable_fast_install=yes ;; no) enable_fast_install=no ;; *) enable_fast_install=no # Look at the argument we got. We use all the common list separators. lt_save_ifs="$IFS"; IFS="${IFS}$PATH_SEPARATOR," for pkg in $enableval; do IFS="$lt_save_ifs" if test "X$pkg" = "X$p"; then enable_fast_install=yes fi done IFS="$lt_save_ifs" ;; esac else enable_fast_install=yes fi # This can be used to rebuild libtool when needed LIBTOOL_DEPS="$ltmain" # Always use our own libtool. LIBTOOL='$(SHELL) $(top_builddir)/libtool' test -z "$LN_S" && LN_S="ln -s" if test -n "${ZSH_VERSION+set}" ; then setopt NO_GLOB_SUBST fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for objdir" >&5 $as_echo_n "checking for objdir... " >&6; } if ${lt_cv_objdir+:} false; then : $as_echo_n "(cached) " >&6 else rm -f .libs 2>/dev/null mkdir .libs 2>/dev/null if test -d .libs; then lt_cv_objdir=.libs else # MS-DOS does not allow filenames that begin with a dot. lt_cv_objdir=_libs fi rmdir .libs 2>/dev/null fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_objdir" >&5 $as_echo "$lt_cv_objdir" >&6; } objdir=$lt_cv_objdir cat >>confdefs.h <<_ACEOF #define LT_OBJDIR "$lt_cv_objdir/" _ACEOF case $host_os in aix3*) # AIX sometimes has problems with the GCC collect2 program. For some # reason, if we set the COLLECT_NAMES environment variable, the problems # vanish in a puff of smoke. if test "X${COLLECT_NAMES+set}" != Xset; then COLLECT_NAMES= export COLLECT_NAMES fi ;; esac # Global variables: ofile=libtool can_build_shared=yes # All known linkers require a `.a' archive for static linking (except MSVC, # which needs '.lib'). libext=a with_gnu_ld="$lt_cv_prog_gnu_ld" old_CC="$CC" old_CFLAGS="$CFLAGS" # Set sane defaults for various variables test -z "$CC" && CC=cc test -z "$LTCC" && LTCC=$CC test -z "$LTCFLAGS" && LTCFLAGS=$CFLAGS test -z "$LD" && LD=ld test -z "$ac_objext" && ac_objext=o for cc_temp in $compiler""; do case $cc_temp in compile | *[\\/]compile | ccache | *[\\/]ccache ) ;; distcc | *[\\/]distcc | purify | *[\\/]purify ) ;; \-*) ;; *) break;; esac done cc_basename=`$ECHO "$cc_temp" | $SED "s%.*/%%; s%^$host_alias-%%"` # Only perform the check for file, if the check method requires it test -z "$MAGIC_CMD" && MAGIC_CMD=file case $deplibs_check_method in file_magic*) if test "$file_magic_cmd" = '$MAGIC_CMD'; then { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ${ac_tool_prefix}file" >&5 $as_echo_n "checking for ${ac_tool_prefix}file... " >&6; } if ${lt_cv_path_MAGIC_CMD+:} false; then : $as_echo_n "(cached) " >&6 else case $MAGIC_CMD in [\\/*] | ?:[\\/]*) lt_cv_path_MAGIC_CMD="$MAGIC_CMD" # Let the user override the test with a path. ;; *) lt_save_MAGIC_CMD="$MAGIC_CMD" lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR ac_dummy="/usr/bin$PATH_SEPARATOR$PATH" for ac_dir in $ac_dummy; do IFS="$lt_save_ifs" test -z "$ac_dir" && ac_dir=. if test -f $ac_dir/${ac_tool_prefix}file; then lt_cv_path_MAGIC_CMD="$ac_dir/${ac_tool_prefix}file" if test -n "$file_magic_test_file"; then case $deplibs_check_method in "file_magic "*) file_magic_regex=`expr "$deplibs_check_method" : "file_magic \(.*\)"` MAGIC_CMD="$lt_cv_path_MAGIC_CMD" if eval $file_magic_cmd \$file_magic_test_file 2> /dev/null | $EGREP "$file_magic_regex" > /dev/null; then : else cat <<_LT_EOF 1>&2 *** Warning: the command libtool uses to detect shared libraries, *** $file_magic_cmd, produces output that libtool cannot recognize. *** The result is that libtool may fail to recognize shared libraries *** as such. This will affect the creation of libtool libraries that *** depend on shared libraries, but programs linked with such libtool *** libraries will work regardless of this problem. Nevertheless, you *** may want to report the problem to your system manager and/or to *** bug-libtool@gnu.org _LT_EOF fi ;; esac fi break fi done IFS="$lt_save_ifs" MAGIC_CMD="$lt_save_MAGIC_CMD" ;; esac fi MAGIC_CMD="$lt_cv_path_MAGIC_CMD" if test -n "$MAGIC_CMD"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $MAGIC_CMD" >&5 $as_echo "$MAGIC_CMD" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test -z "$lt_cv_path_MAGIC_CMD"; then if test -n "$ac_tool_prefix"; then { $as_echo "$as_me:${as_lineno-$LINENO}: checking for file" >&5 $as_echo_n "checking for file... " >&6; } if ${lt_cv_path_MAGIC_CMD+:} false; then : $as_echo_n "(cached) " >&6 else case $MAGIC_CMD in [\\/*] | ?:[\\/]*) lt_cv_path_MAGIC_CMD="$MAGIC_CMD" # Let the user override the test with a path. ;; *) lt_save_MAGIC_CMD="$MAGIC_CMD" lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR ac_dummy="/usr/bin$PATH_SEPARATOR$PATH" for ac_dir in $ac_dummy; do IFS="$lt_save_ifs" test -z "$ac_dir" && ac_dir=. if test -f $ac_dir/file; then lt_cv_path_MAGIC_CMD="$ac_dir/file" if test -n "$file_magic_test_file"; then case $deplibs_check_method in "file_magic "*) file_magic_regex=`expr "$deplibs_check_method" : "file_magic \(.*\)"` MAGIC_CMD="$lt_cv_path_MAGIC_CMD" if eval $file_magic_cmd \$file_magic_test_file 2> /dev/null | $EGREP "$file_magic_regex" > /dev/null; then : else cat <<_LT_EOF 1>&2 *** Warning: the command libtool uses to detect shared libraries, *** $file_magic_cmd, produces output that libtool cannot recognize. *** The result is that libtool may fail to recognize shared libraries *** as such. This will affect the creation of libtool libraries that *** depend on shared libraries, but programs linked with such libtool *** libraries will work regardless of this problem. Nevertheless, you *** may want to report the problem to your system manager and/or to *** bug-libtool@gnu.org _LT_EOF fi ;; esac fi break fi done IFS="$lt_save_ifs" MAGIC_CMD="$lt_save_MAGIC_CMD" ;; esac fi MAGIC_CMD="$lt_cv_path_MAGIC_CMD" if test -n "$MAGIC_CMD"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $MAGIC_CMD" >&5 $as_echo "$MAGIC_CMD" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi else MAGIC_CMD=: fi fi fi ;; esac # Use C for the default configuration in the libtool script lt_save_CC="$CC" ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu # Source file extension for C test sources. ac_ext=c # Object file extension for compiled C test sources. objext=o objext=$objext # Code to be used in simple compile tests lt_simple_compile_test_code="int some_variable = 0;" # Code to be used in simple link tests lt_simple_link_test_code='int main(){return(0);}' # If no C compiler was specified, use CC. LTCC=${LTCC-"$CC"} # If no C compiler flags were specified, use CFLAGS. LTCFLAGS=${LTCFLAGS-"$CFLAGS"} # Allow CC to be a program name with arguments. compiler=$CC # Save the default compiler, since it gets overwritten when the other # tags are being tested, and _LT_TAGVAR(compiler, []) is a NOP. compiler_DEFAULT=$CC # save warnings/boilerplate of simple test code ac_outfile=conftest.$ac_objext echo "$lt_simple_compile_test_code" >conftest.$ac_ext eval "$ac_compile" 2>&1 >/dev/null | $SED '/^$/d; /^ *+/d' >conftest.err _lt_compiler_boilerplate=`cat conftest.err` $RM conftest* ac_outfile=conftest.$ac_objext echo "$lt_simple_link_test_code" >conftest.$ac_ext eval "$ac_link" 2>&1 >/dev/null | $SED '/^$/d; /^ *+/d' >conftest.err _lt_linker_boilerplate=`cat conftest.err` $RM -r conftest* ## CAVEAT EMPTOR: ## There is no encapsulation within the following macros, do not change ## the running order or otherwise move them around unless you know exactly ## what you are doing... if test -n "$compiler"; then lt_prog_compiler_no_builtin_flag= if test "$GCC" = yes; then case $cc_basename in nvcc*) lt_prog_compiler_no_builtin_flag=' -Xcompiler -fno-builtin' ;; *) lt_prog_compiler_no_builtin_flag=' -fno-builtin' ;; esac { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler supports -fno-rtti -fno-exceptions" >&5 $as_echo_n "checking if $compiler supports -fno-rtti -fno-exceptions... " >&6; } if ${lt_cv_prog_compiler_rtti_exceptions+:} false; then : $as_echo_n "(cached) " >&6 else lt_cv_prog_compiler_rtti_exceptions=no ac_outfile=conftest.$ac_objext echo "$lt_simple_compile_test_code" > conftest.$ac_ext lt_compiler_flag="-fno-rtti -fno-exceptions" # Insert the option either (1) after the last *FLAGS variable, or # (2) before a word containing "conftest.", or (3) at the end. # Note that $ac_compile itself does not contain backslashes and begins # with a dollar sign (not a hyphen), so the echo should work correctly. # The option is referenced via a variable to avoid confusing sed. lt_compile=`echo "$ac_compile" | $SED \ -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ -e 's:$: $lt_compiler_flag:'` (eval echo "\"\$as_me:$LINENO: $lt_compile\"" >&5) (eval "$lt_compile" 2>conftest.err) ac_status=$? cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 if (exit $ac_status) && test -s "$ac_outfile"; then # The compiler can only warn and ignore the option if not recognized # So say no if there are warnings other than the usual output. $ECHO "$_lt_compiler_boilerplate" | $SED '/^$/d' >conftest.exp $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2 if test ! -s conftest.er2 || diff conftest.exp conftest.er2 >/dev/null; then lt_cv_prog_compiler_rtti_exceptions=yes fi fi $RM conftest* fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_rtti_exceptions" >&5 $as_echo "$lt_cv_prog_compiler_rtti_exceptions" >&6; } if test x"$lt_cv_prog_compiler_rtti_exceptions" = xyes; then lt_prog_compiler_no_builtin_flag="$lt_prog_compiler_no_builtin_flag -fno-rtti -fno-exceptions" else : fi fi lt_prog_compiler_wl= lt_prog_compiler_pic= lt_prog_compiler_static= if test "$GCC" = yes; then lt_prog_compiler_wl='-Wl,' lt_prog_compiler_static='-static' case $host_os in aix*) # All AIX code is PIC. if test "$host_cpu" = ia64; then # AIX 5 now supports IA64 processor lt_prog_compiler_static='-Bstatic' fi ;; amigaos*) case $host_cpu in powerpc) # see comment about AmigaOS4 .so support lt_prog_compiler_pic='-fPIC' ;; m68k) # FIXME: we need at least 68020 code to build shared libraries, but # adding the `-m68020' flag to GCC prevents building anything better, # like `-m68040'. lt_prog_compiler_pic='-m68020 -resident32 -malways-restore-a4' ;; esac ;; beos* | irix5* | irix6* | nonstopux* | osf3* | osf4* | osf5*) # PIC is the default for these OSes. ;; mingw* | cygwin* | pw32* | os2* | cegcc*) # This hack is so that the source file can tell whether it is being # built for inclusion in a dll (and should export symbols for example). # Although the cygwin gcc ignores -fPIC, still need this for old-style # (--disable-auto-import) libraries lt_prog_compiler_pic='-DDLL_EXPORT' ;; darwin* | rhapsody*) # PIC is the default on this platform # Common symbols not allowed in MH_DYLIB files lt_prog_compiler_pic='-fno-common' ;; haiku*) # PIC is the default for Haiku. # The "-static" flag exists, but is broken. lt_prog_compiler_static= ;; hpux*) # PIC is the default for 64-bit PA HP-UX, but not for 32-bit # PA HP-UX. On IA64 HP-UX, PIC is the default but the pic flag # sets the default TLS model and affects inlining. case $host_cpu in hppa*64*) # +Z the default ;; *) lt_prog_compiler_pic='-fPIC' ;; esac ;; interix[3-9]*) # Interix 3.x gcc -fpic/-fPIC options generate broken code. # Instead, we relocate shared libraries at runtime. ;; msdosdjgpp*) # Just because we use GCC doesn't mean we suddenly get shared libraries # on systems that don't support them. lt_prog_compiler_can_build_shared=no enable_shared=no ;; *nto* | *qnx*) # QNX uses GNU C++, but need to define -shared option too, otherwise # it will coredump. lt_prog_compiler_pic='-fPIC -shared' ;; sysv4*MP*) if test -d /usr/nec; then lt_prog_compiler_pic=-Kconform_pic fi ;; *) lt_prog_compiler_pic='-fPIC' ;; esac case $cc_basename in nvcc*) # Cuda Compiler Driver 2.2 lt_prog_compiler_wl='-Xlinker ' if test -n "$lt_prog_compiler_pic"; then lt_prog_compiler_pic="-Xcompiler $lt_prog_compiler_pic" fi ;; esac else # PORTME Check for flag to pass linker flags through the system compiler. case $host_os in aix*) lt_prog_compiler_wl='-Wl,' if test "$host_cpu" = ia64; then # AIX 5 now supports IA64 processor lt_prog_compiler_static='-Bstatic' else lt_prog_compiler_static='-bnso -bI:/lib/syscalls.exp' fi ;; mingw* | cygwin* | pw32* | os2* | cegcc*) # This hack is so that the source file can tell whether it is being # built for inclusion in a dll (and should export symbols for example). lt_prog_compiler_pic='-DDLL_EXPORT' ;; hpux9* | hpux10* | hpux11*) lt_prog_compiler_wl='-Wl,' # PIC is the default for IA64 HP-UX and 64-bit HP-UX, but # not for PA HP-UX. case $host_cpu in hppa*64*|ia64*) # +Z the default ;; *) lt_prog_compiler_pic='+Z' ;; esac # Is there a better lt_prog_compiler_static that works with the bundled CC? lt_prog_compiler_static='${wl}-a ${wl}archive' ;; irix5* | irix6* | nonstopux*) lt_prog_compiler_wl='-Wl,' # PIC (with -KPIC) is the default. lt_prog_compiler_static='-non_shared' ;; linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*) case $cc_basename in # old Intel for x86_64 which still supported -KPIC. ecc*) lt_prog_compiler_wl='-Wl,' lt_prog_compiler_pic='-KPIC' lt_prog_compiler_static='-static' ;; # icc used to be incompatible with GCC. # ICC 10 doesn't accept -KPIC any more. icc* | ifort*) lt_prog_compiler_wl='-Wl,' lt_prog_compiler_pic='-fPIC' lt_prog_compiler_static='-static' ;; # Lahey Fortran 8.1. lf95*) lt_prog_compiler_wl='-Wl,' lt_prog_compiler_pic='--shared' lt_prog_compiler_static='--static' ;; nagfor*) # NAG Fortran compiler lt_prog_compiler_wl='-Wl,-Wl,,' lt_prog_compiler_pic='-PIC' lt_prog_compiler_static='-Bstatic' ;; pgcc* | pgf77* | pgf90* | pgf95* | pgfortran*) # Portland Group compilers (*not* the Pentium gcc compiler, # which looks to be a dead project) lt_prog_compiler_wl='-Wl,' lt_prog_compiler_pic='-fpic' lt_prog_compiler_static='-Bstatic' ;; ccc*) lt_prog_compiler_wl='-Wl,' # All Alpha code is PIC. lt_prog_compiler_static='-non_shared' ;; xl* | bgxl* | bgf* | mpixl*) # IBM XL C 8.0/Fortran 10.1, 11.1 on PPC and BlueGene lt_prog_compiler_wl='-Wl,' lt_prog_compiler_pic='-qpic' lt_prog_compiler_static='-qstaticlink' ;; *) case `$CC -V 2>&1 | sed 5q` in *Sun\ Ceres\ Fortran* | *Sun*Fortran*\ [1-7].* | *Sun*Fortran*\ 8.[0-3]*) # Sun Fortran 8.3 passes all unrecognized flags to the linker lt_prog_compiler_pic='-KPIC' lt_prog_compiler_static='-Bstatic' lt_prog_compiler_wl='' ;; *Sun\ F* | *Sun*Fortran*) lt_prog_compiler_pic='-KPIC' lt_prog_compiler_static='-Bstatic' lt_prog_compiler_wl='-Qoption ld ' ;; *Sun\ C*) # Sun C 5.9 lt_prog_compiler_pic='-KPIC' lt_prog_compiler_static='-Bstatic' lt_prog_compiler_wl='-Wl,' ;; *Intel*\ [CF]*Compiler*) lt_prog_compiler_wl='-Wl,' lt_prog_compiler_pic='-fPIC' lt_prog_compiler_static='-static' ;; *Portland\ Group*) lt_prog_compiler_wl='-Wl,' lt_prog_compiler_pic='-fpic' lt_prog_compiler_static='-Bstatic' ;; esac ;; esac ;; newsos6) lt_prog_compiler_pic='-KPIC' lt_prog_compiler_static='-Bstatic' ;; *nto* | *qnx*) # QNX uses GNU C++, but need to define -shared option too, otherwise # it will coredump. lt_prog_compiler_pic='-fPIC -shared' ;; osf3* | osf4* | osf5*) lt_prog_compiler_wl='-Wl,' # All OSF/1 code is PIC. lt_prog_compiler_static='-non_shared' ;; rdos*) lt_prog_compiler_static='-non_shared' ;; solaris*) lt_prog_compiler_pic='-KPIC' lt_prog_compiler_static='-Bstatic' case $cc_basename in f77* | f90* | f95* | sunf77* | sunf90* | sunf95*) lt_prog_compiler_wl='-Qoption ld ';; *) lt_prog_compiler_wl='-Wl,';; esac ;; sunos4*) lt_prog_compiler_wl='-Qoption ld ' lt_prog_compiler_pic='-PIC' lt_prog_compiler_static='-Bstatic' ;; sysv4 | sysv4.2uw2* | sysv4.3*) lt_prog_compiler_wl='-Wl,' lt_prog_compiler_pic='-KPIC' lt_prog_compiler_static='-Bstatic' ;; sysv4*MP*) if test -d /usr/nec ;then lt_prog_compiler_pic='-Kconform_pic' lt_prog_compiler_static='-Bstatic' fi ;; sysv5* | unixware* | sco3.2v5* | sco5v6* | OpenUNIX*) lt_prog_compiler_wl='-Wl,' lt_prog_compiler_pic='-KPIC' lt_prog_compiler_static='-Bstatic' ;; unicos*) lt_prog_compiler_wl='-Wl,' lt_prog_compiler_can_build_shared=no ;; uts4*) lt_prog_compiler_pic='-pic' lt_prog_compiler_static='-Bstatic' ;; *) lt_prog_compiler_can_build_shared=no ;; esac fi case $host_os in # For platforms which do not support PIC, -DPIC is meaningless: *djgpp*) lt_prog_compiler_pic= ;; *) lt_prog_compiler_pic="$lt_prog_compiler_pic -DPIC" ;; esac { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $compiler option to produce PIC" >&5 $as_echo_n "checking for $compiler option to produce PIC... " >&6; } if ${lt_cv_prog_compiler_pic+:} false; then : $as_echo_n "(cached) " >&6 else lt_cv_prog_compiler_pic=$lt_prog_compiler_pic fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_pic" >&5 $as_echo "$lt_cv_prog_compiler_pic" >&6; } lt_prog_compiler_pic=$lt_cv_prog_compiler_pic # # Check to make sure the PIC flag actually works. # if test -n "$lt_prog_compiler_pic"; then { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler PIC flag $lt_prog_compiler_pic works" >&5 $as_echo_n "checking if $compiler PIC flag $lt_prog_compiler_pic works... " >&6; } if ${lt_cv_prog_compiler_pic_works+:} false; then : $as_echo_n "(cached) " >&6 else lt_cv_prog_compiler_pic_works=no ac_outfile=conftest.$ac_objext echo "$lt_simple_compile_test_code" > conftest.$ac_ext lt_compiler_flag="$lt_prog_compiler_pic -DPIC" # Insert the option either (1) after the last *FLAGS variable, or # (2) before a word containing "conftest.", or (3) at the end. # Note that $ac_compile itself does not contain backslashes and begins # with a dollar sign (not a hyphen), so the echo should work correctly. # The option is referenced via a variable to avoid confusing sed. lt_compile=`echo "$ac_compile" | $SED \ -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ -e 's:$: $lt_compiler_flag:'` (eval echo "\"\$as_me:$LINENO: $lt_compile\"" >&5) (eval "$lt_compile" 2>conftest.err) ac_status=$? cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 if (exit $ac_status) && test -s "$ac_outfile"; then # The compiler can only warn and ignore the option if not recognized # So say no if there are warnings other than the usual output. $ECHO "$_lt_compiler_boilerplate" | $SED '/^$/d' >conftest.exp $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2 if test ! -s conftest.er2 || diff conftest.exp conftest.er2 >/dev/null; then lt_cv_prog_compiler_pic_works=yes fi fi $RM conftest* fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_pic_works" >&5 $as_echo "$lt_cv_prog_compiler_pic_works" >&6; } if test x"$lt_cv_prog_compiler_pic_works" = xyes; then case $lt_prog_compiler_pic in "" | " "*) ;; *) lt_prog_compiler_pic=" $lt_prog_compiler_pic" ;; esac else lt_prog_compiler_pic= lt_prog_compiler_can_build_shared=no fi fi # # Check to make sure the static flag actually works. # wl=$lt_prog_compiler_wl eval lt_tmp_static_flag=\"$lt_prog_compiler_static\" { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler static flag $lt_tmp_static_flag works" >&5 $as_echo_n "checking if $compiler static flag $lt_tmp_static_flag works... " >&6; } if ${lt_cv_prog_compiler_static_works+:} false; then : $as_echo_n "(cached) " >&6 else lt_cv_prog_compiler_static_works=no save_LDFLAGS="$LDFLAGS" LDFLAGS="$LDFLAGS $lt_tmp_static_flag" echo "$lt_simple_link_test_code" > conftest.$ac_ext if (eval $ac_link 2>conftest.err) && test -s conftest$ac_exeext; then # The linker can only warn and ignore the option if not recognized # So say no if there are warnings if test -s conftest.err; then # Append any errors to the config.log. cat conftest.err 1>&5 $ECHO "$_lt_linker_boilerplate" | $SED '/^$/d' > conftest.exp $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2 if diff conftest.exp conftest.er2 >/dev/null; then lt_cv_prog_compiler_static_works=yes fi else lt_cv_prog_compiler_static_works=yes fi fi $RM -r conftest* LDFLAGS="$save_LDFLAGS" fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_static_works" >&5 $as_echo "$lt_cv_prog_compiler_static_works" >&6; } if test x"$lt_cv_prog_compiler_static_works" = xyes; then : else lt_prog_compiler_static= fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler supports -c -o file.$ac_objext" >&5 $as_echo_n "checking if $compiler supports -c -o file.$ac_objext... " >&6; } if ${lt_cv_prog_compiler_c_o+:} false; then : $as_echo_n "(cached) " >&6 else lt_cv_prog_compiler_c_o=no $RM -r conftest 2>/dev/null mkdir conftest cd conftest mkdir out echo "$lt_simple_compile_test_code" > conftest.$ac_ext lt_compiler_flag="-o out/conftest2.$ac_objext" # Insert the option either (1) after the last *FLAGS variable, or # (2) before a word containing "conftest.", or (3) at the end. # Note that $ac_compile itself does not contain backslashes and begins # with a dollar sign (not a hyphen), so the echo should work correctly. lt_compile=`echo "$ac_compile" | $SED \ -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ -e 's:$: $lt_compiler_flag:'` (eval echo "\"\$as_me:$LINENO: $lt_compile\"" >&5) (eval "$lt_compile" 2>out/conftest.err) ac_status=$? cat out/conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 if (exit $ac_status) && test -s out/conftest2.$ac_objext then # The compiler can only warn and ignore the option if not recognized # So say no if there are warnings $ECHO "$_lt_compiler_boilerplate" | $SED '/^$/d' > out/conftest.exp $SED '/^$/d; /^ *+/d' out/conftest.err >out/conftest.er2 if test ! -s out/conftest.er2 || diff out/conftest.exp out/conftest.er2 >/dev/null; then lt_cv_prog_compiler_c_o=yes fi fi chmod u+w . 2>&5 $RM conftest* # SGI C++ compiler will create directory out/ii_files/ for # template instantiation test -d out/ii_files && $RM out/ii_files/* && rmdir out/ii_files $RM out/* && rmdir out cd .. $RM -r conftest $RM conftest* fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_c_o" >&5 $as_echo "$lt_cv_prog_compiler_c_o" >&6; } { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler supports -c -o file.$ac_objext" >&5 $as_echo_n "checking if $compiler supports -c -o file.$ac_objext... " >&6; } if ${lt_cv_prog_compiler_c_o+:} false; then : $as_echo_n "(cached) " >&6 else lt_cv_prog_compiler_c_o=no $RM -r conftest 2>/dev/null mkdir conftest cd conftest mkdir out echo "$lt_simple_compile_test_code" > conftest.$ac_ext lt_compiler_flag="-o out/conftest2.$ac_objext" # Insert the option either (1) after the last *FLAGS variable, or # (2) before a word containing "conftest.", or (3) at the end. # Note that $ac_compile itself does not contain backslashes and begins # with a dollar sign (not a hyphen), so the echo should work correctly. lt_compile=`echo "$ac_compile" | $SED \ -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ -e 's:$: $lt_compiler_flag:'` (eval echo "\"\$as_me:$LINENO: $lt_compile\"" >&5) (eval "$lt_compile" 2>out/conftest.err) ac_status=$? cat out/conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 if (exit $ac_status) && test -s out/conftest2.$ac_objext then # The compiler can only warn and ignore the option if not recognized # So say no if there are warnings $ECHO "$_lt_compiler_boilerplate" | $SED '/^$/d' > out/conftest.exp $SED '/^$/d; /^ *+/d' out/conftest.err >out/conftest.er2 if test ! -s out/conftest.er2 || diff out/conftest.exp out/conftest.er2 >/dev/null; then lt_cv_prog_compiler_c_o=yes fi fi chmod u+w . 2>&5 $RM conftest* # SGI C++ compiler will create directory out/ii_files/ for # template instantiation test -d out/ii_files && $RM out/ii_files/* && rmdir out/ii_files $RM out/* && rmdir out cd .. $RM -r conftest $RM conftest* fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_c_o" >&5 $as_echo "$lt_cv_prog_compiler_c_o" >&6; } hard_links="nottested" if test "$lt_cv_prog_compiler_c_o" = no && test "$need_locks" != no; then # do not overwrite the value of need_locks provided by the user { $as_echo "$as_me:${as_lineno-$LINENO}: checking if we can lock with hard links" >&5 $as_echo_n "checking if we can lock with hard links... " >&6; } hard_links=yes $RM conftest* ln conftest.a conftest.b 2>/dev/null && hard_links=no touch conftest.a ln conftest.a conftest.b 2>&5 || hard_links=no ln conftest.a conftest.b 2>/dev/null && hard_links=no { $as_echo "$as_me:${as_lineno-$LINENO}: result: $hard_links" >&5 $as_echo "$hard_links" >&6; } if test "$hard_links" = no; then { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: \`$CC' does not support \`-c -o', so \`make -j' may be unsafe" >&5 $as_echo "$as_me: WARNING: \`$CC' does not support \`-c -o', so \`make -j' may be unsafe" >&2;} need_locks=warn fi else need_locks=no fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the $compiler linker ($LD) supports shared libraries" >&5 $as_echo_n "checking whether the $compiler linker ($LD) supports shared libraries... " >&6; } runpath_var= allow_undefined_flag= always_export_symbols=no archive_cmds= archive_expsym_cmds= compiler_needs_object=no enable_shared_with_static_runtimes=no export_dynamic_flag_spec= export_symbols_cmds='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols' hardcode_automatic=no hardcode_direct=no hardcode_direct_absolute=no hardcode_libdir_flag_spec= hardcode_libdir_separator= hardcode_minus_L=no hardcode_shlibpath_var=unsupported inherit_rpath=no link_all_deplibs=unknown module_cmds= module_expsym_cmds= old_archive_from_new_cmds= old_archive_from_expsyms_cmds= thread_safe_flag_spec= whole_archive_flag_spec= # include_expsyms should be a list of space-separated symbols to be *always* # included in the symbol list include_expsyms= # exclude_expsyms can be an extended regexp of symbols to exclude # it will be wrapped by ` (' and `)$', so one must not match beginning or # end of line. Example: `a|bc|.*d.*' will exclude the symbols `a' and `bc', # as well as any symbol that contains `d'. exclude_expsyms='_GLOBAL_OFFSET_TABLE_|_GLOBAL__F[ID]_.*' # Although _GLOBAL_OFFSET_TABLE_ is a valid symbol C name, most a.out # platforms (ab)use it in PIC code, but their linkers get confused if # the symbol is explicitly referenced. Since portable code cannot # rely on this symbol name, it's probably fine to never include it in # preloaded symbol tables. # Exclude shared library initialization/finalization symbols. extract_expsyms_cmds= case $host_os in cygwin* | mingw* | pw32* | cegcc*) # FIXME: the MSVC++ port hasn't been tested in a loooong time # When not using gcc, we currently assume that we are using # Microsoft Visual C++. if test "$GCC" != yes; then with_gnu_ld=no fi ;; interix*) # we just hope/assume this is gcc and not c89 (= MSVC++) with_gnu_ld=yes ;; openbsd*) with_gnu_ld=no ;; linux* | k*bsd*-gnu | gnu*) link_all_deplibs=no ;; esac ld_shlibs=yes # On some targets, GNU ld is compatible enough with the native linker # that we're better off using the native interface for both. lt_use_gnu_ld_interface=no if test "$with_gnu_ld" = yes; then case $host_os in aix*) # The AIX port of GNU ld has always aspired to compatibility # with the native linker. However, as the warning in the GNU ld # block says, versions before 2.19.5* couldn't really create working # shared libraries, regardless of the interface used. case `$LD -v 2>&1` in *\ \(GNU\ Binutils\)\ 2.19.5*) ;; *\ \(GNU\ Binutils\)\ 2.[2-9]*) ;; *\ \(GNU\ Binutils\)\ [3-9]*) ;; *) lt_use_gnu_ld_interface=yes ;; esac ;; *) lt_use_gnu_ld_interface=yes ;; esac fi if test "$lt_use_gnu_ld_interface" = yes; then # If archive_cmds runs LD, not CC, wlarc should be empty wlarc='${wl}' # Set some defaults for GNU ld with shared library support. These # are reset later if shared libraries are not supported. Putting them # here allows them to be overridden if necessary. runpath_var=LD_RUN_PATH hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir' export_dynamic_flag_spec='${wl}--export-dynamic' # ancient GNU ld didn't support --whole-archive et. al. if $LD --help 2>&1 | $GREP 'no-whole-archive' > /dev/null; then whole_archive_flag_spec="$wlarc"'--whole-archive$convenience '"$wlarc"'--no-whole-archive' else whole_archive_flag_spec= fi supports_anon_versioning=no case `$LD -v 2>&1` in *GNU\ gold*) supports_anon_versioning=yes ;; *\ [01].* | *\ 2.[0-9].* | *\ 2.10.*) ;; # catch versions < 2.11 *\ 2.11.93.0.2\ *) supports_anon_versioning=yes ;; # RH7.3 ... *\ 2.11.92.0.12\ *) supports_anon_versioning=yes ;; # Mandrake 8.2 ... *\ 2.11.*) ;; # other 2.11 versions *) supports_anon_versioning=yes ;; esac # See if GNU ld supports shared libraries. case $host_os in aix[3-9]*) # On AIX/PPC, the GNU linker is very broken if test "$host_cpu" != ia64; then ld_shlibs=no cat <<_LT_EOF 1>&2 *** Warning: the GNU linker, at least up to release 2.19, is reported *** to be unable to reliably create shared libraries on AIX. *** Therefore, libtool is disabling shared libraries support. If you *** really care for shared libraries, you may want to install binutils *** 2.20 or above, or modify your PATH so that a non-GNU linker is found. *** You will then need to restart the configuration process. _LT_EOF fi ;; amigaos*) case $host_cpu in powerpc) # see comment about AmigaOS4 .so support archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' archive_expsym_cmds='' ;; m68k) archive_cmds='$RM $output_objdir/a2ixlibrary.data~$ECHO "#define NAME $libname" > $output_objdir/a2ixlibrary.data~$ECHO "#define LIBRARY_ID 1" >> $output_objdir/a2ixlibrary.data~$ECHO "#define VERSION $major" >> $output_objdir/a2ixlibrary.data~$ECHO "#define REVISION $revision" >> $output_objdir/a2ixlibrary.data~$AR $AR_FLAGS $lib $libobjs~$RANLIB $lib~(cd $output_objdir && a2ixlibrary -32)' hardcode_libdir_flag_spec='-L$libdir' hardcode_minus_L=yes ;; esac ;; beos*) if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then allow_undefined_flag=unsupported # Joseph Beckenbach says some releases of gcc # support --undefined. This deserves some investigation. FIXME archive_cmds='$CC -nostart $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' else ld_shlibs=no fi ;; cygwin* | mingw* | pw32* | cegcc*) # _LT_TAGVAR(hardcode_libdir_flag_spec, ) is actually meaningless, # as there is no search path for DLLs. hardcode_libdir_flag_spec='-L$libdir' export_dynamic_flag_spec='${wl}--export-all-symbols' allow_undefined_flag=unsupported always_export_symbols=no enable_shared_with_static_runtimes=yes export_symbols_cmds='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[BCDGRS][ ]/s/.*[ ]\([^ ]*\)/\1 DATA/;s/^.*[ ]__nm__\([^ ]*\)[ ][^ ]*/\1 DATA/;/^I[ ]/d;/^[AITW][ ]/s/.* //'\'' | sort | uniq > $export_symbols' exclude_expsyms='[_]+GLOBAL_OFFSET_TABLE_|[_]+GLOBAL__[FID]_.*|[_]+head_[A-Za-z0-9_]+_dll|[A-Za-z0-9_]+_dll_iname' if $LD --help 2>&1 | $GREP 'auto-import' > /dev/null; then archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags -o $output_objdir/$soname ${wl}--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib' # If the export-symbols file already is a .def file (1st line # is EXPORTS), use it as is; otherwise, prepend... archive_expsym_cmds='if test "x`$SED 1q $export_symbols`" = xEXPORTS; then cp $export_symbols $output_objdir/$soname.def; else echo EXPORTS > $output_objdir/$soname.def; cat $export_symbols >> $output_objdir/$soname.def; fi~ $CC -shared $output_objdir/$soname.def $libobjs $deplibs $compiler_flags -o $output_objdir/$soname ${wl}--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib' else ld_shlibs=no fi ;; haiku*) archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' link_all_deplibs=yes ;; interix[3-9]*) hardcode_direct=no hardcode_shlibpath_var=no hardcode_libdir_flag_spec='${wl}-rpath,$libdir' export_dynamic_flag_spec='${wl}-E' # Hack: On Interix 3.x, we cannot compile PIC because of a broken gcc. # Instead, shared libraries are loaded at an image base (0x10000000 by # default) and relocated if they conflict, which is a slow very memory # consuming and fragmenting process. To avoid this, we pick a random, # 256 KiB-aligned image base between 0x50000000 and 0x6FFC0000 at link # time. Moving up from 0x10000000 also allows more sbrk(2) space. archive_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-h,$soname ${wl}--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' archive_expsym_cmds='sed "s,^,_," $export_symbols >$output_objdir/$soname.expsym~$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-h,$soname ${wl}--retain-symbols-file,$output_objdir/$soname.expsym ${wl}--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' ;; gnu* | linux* | tpf* | k*bsd*-gnu | kopensolaris*-gnu) tmp_diet=no if test "$host_os" = linux-dietlibc; then case $cc_basename in diet\ *) tmp_diet=yes;; # linux-dietlibc with static linking (!diet-dyn) esac fi if $LD --help 2>&1 | $EGREP ': supported targets:.* elf' > /dev/null \ && test "$tmp_diet" = no then tmp_addflag=' $pic_flag' tmp_sharedflag='-shared' case $cc_basename,$host_cpu in pgcc*) # Portland Group C compiler whole_archive_flag_spec='${wl}--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` ${wl}--no-whole-archive' tmp_addflag=' $pic_flag' ;; pgf77* | pgf90* | pgf95* | pgfortran*) # Portland Group f77 and f90 compilers whole_archive_flag_spec='${wl}--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` ${wl}--no-whole-archive' tmp_addflag=' $pic_flag -Mnomain' ;; ecc*,ia64* | icc*,ia64*) # Intel C compiler on ia64 tmp_addflag=' -i_dynamic' ;; efc*,ia64* | ifort*,ia64*) # Intel Fortran compiler on ia64 tmp_addflag=' -i_dynamic -nofor_main' ;; ifc* | ifort*) # Intel Fortran compiler tmp_addflag=' -nofor_main' ;; lf95*) # Lahey Fortran 8.1 whole_archive_flag_spec= tmp_sharedflag='--shared' ;; xl[cC]* | bgxl[cC]* | mpixl[cC]*) # IBM XL C 8.0 on PPC (deal with xlf below) tmp_sharedflag='-qmkshrobj' tmp_addflag= ;; nvcc*) # Cuda Compiler Driver 2.2 whole_archive_flag_spec='${wl}--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` ${wl}--no-whole-archive' compiler_needs_object=yes ;; esac case `$CC -V 2>&1 | sed 5q` in *Sun\ C*) # Sun C 5.9 whole_archive_flag_spec='${wl}--whole-archive`new_convenience=; for conv in $convenience\"\"; do test -z \"$conv\" || new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` ${wl}--no-whole-archive' compiler_needs_object=yes tmp_sharedflag='-G' ;; *Sun\ F*) # Sun Fortran 8.3 tmp_sharedflag='-G' ;; esac archive_cmds='$CC '"$tmp_sharedflag""$tmp_addflag"' $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' if test "x$supports_anon_versioning" = xyes; then archive_expsym_cmds='echo "{ global:" > $output_objdir/$libname.ver~ cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~ echo "local: *; };" >> $output_objdir/$libname.ver~ $CC '"$tmp_sharedflag""$tmp_addflag"' $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-version-script ${wl}$output_objdir/$libname.ver -o $lib' fi case $cc_basename in xlf* | bgf* | bgxlf* | mpixlf*) # IBM XL Fortran 10.1 on PPC cannot create shared libs itself whole_archive_flag_spec='--whole-archive$convenience --no-whole-archive' hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir' archive_cmds='$LD -shared $libobjs $deplibs $linker_flags -soname $soname -o $lib' if test "x$supports_anon_versioning" = xyes; then archive_expsym_cmds='echo "{ global:" > $output_objdir/$libname.ver~ cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~ echo "local: *; };" >> $output_objdir/$libname.ver~ $LD -shared $libobjs $deplibs $linker_flags -soname $soname -version-script $output_objdir/$libname.ver -o $lib' fi ;; esac else ld_shlibs=no fi ;; netbsd* | netbsdelf*-gnu) if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then archive_cmds='$LD -Bshareable $libobjs $deplibs $linker_flags -o $lib' wlarc= else archive_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' archive_expsym_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' fi ;; solaris*) if $LD -v 2>&1 | $GREP 'BFD 2\.8' > /dev/null; then ld_shlibs=no cat <<_LT_EOF 1>&2 *** Warning: The releases 2.8.* of the GNU linker cannot reliably *** create shared libraries on Solaris systems. Therefore, libtool *** is disabling shared libraries support. We urge you to upgrade GNU *** binutils to release 2.9.1 or newer. Another option is to modify *** your PATH or compiler configuration so that the native linker is *** used, and then restart. _LT_EOF elif $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then archive_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' archive_expsym_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' else ld_shlibs=no fi ;; sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX*) case `$LD -v 2>&1` in *\ [01].* | *\ 2.[0-9].* | *\ 2.1[0-5].*) ld_shlibs=no cat <<_LT_EOF 1>&2 *** Warning: Releases of the GNU linker prior to 2.16.91.0.3 can not *** reliably create shared libraries on SCO systems. Therefore, libtool *** is disabling shared libraries support. We urge you to upgrade GNU *** binutils to release 2.16.91.0.3 or newer. Another option is to modify *** your PATH or compiler configuration so that the native linker is *** used, and then restart. _LT_EOF ;; *) # For security reasons, it is highly recommended that you always # use absolute paths for naming shared libraries, and exclude the # DT_RUNPATH tag from executables and libraries. But doing so # requires that you compile everything twice, which is a pain. if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir' archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' archive_expsym_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' else ld_shlibs=no fi ;; esac ;; sunos4*) archive_cmds='$LD -assert pure-text -Bshareable -o $lib $libobjs $deplibs $linker_flags' wlarc= hardcode_direct=yes hardcode_shlibpath_var=no ;; *) if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then archive_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' archive_expsym_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' else ld_shlibs=no fi ;; esac if test "$ld_shlibs" = no; then runpath_var= hardcode_libdir_flag_spec= export_dynamic_flag_spec= whole_archive_flag_spec= fi else # PORTME fill in a description of your system's linker (not GNU ld) case $host_os in aix3*) allow_undefined_flag=unsupported always_export_symbols=yes archive_expsym_cmds='$LD -o $output_objdir/$soname $libobjs $deplibs $linker_flags -bE:$export_symbols -T512 -H512 -bM:SRE~$AR $AR_FLAGS $lib $output_objdir/$soname' # Note: this linker hardcodes the directories in LIBPATH if there # are no directories specified by -L. hardcode_minus_L=yes if test "$GCC" = yes && test -z "$lt_prog_compiler_static"; then # Neither direct hardcoding nor static linking is supported with a # broken collect2. hardcode_direct=unsupported fi ;; aix[4-9]*) if test "$host_cpu" = ia64; then # On IA64, the linker does run time linking by default, so we don't # have to do anything special. aix_use_runtimelinking=no exp_sym_flag='-Bexport' no_entry_flag="" else # If we're using GNU nm, then we don't want the "-C" option. # -C means demangle to AIX nm, but means don't demangle with GNU nm # Also, AIX nm treats weak defined symbols like other global # defined symbols, whereas GNU nm marks them as "W". if $NM -V 2>&1 | $GREP 'GNU' > /dev/null; then export_symbols_cmds='$NM -Bpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B") || (\$ 2 == "W")) && (substr(\$ 3,1,1) != ".")) { print \$ 3 } }'\'' | sort -u > $export_symbols' else export_symbols_cmds='$NM -BCpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B")) && (substr(\$ 3,1,1) != ".")) { print \$ 3 } }'\'' | sort -u > $export_symbols' fi aix_use_runtimelinking=no # Test if we are trying to use run time linking or normal # AIX style linking. If -brtl is somewhere in LDFLAGS, we # need to do runtime linking. case $host_os in aix4.[23]|aix4.[23].*|aix[5-9]*) for ld_flag in $LDFLAGS; do if (test $ld_flag = "-brtl" || test $ld_flag = "-Wl,-brtl"); then aix_use_runtimelinking=yes break fi done ;; esac exp_sym_flag='-bexport' no_entry_flag='-bnoentry' fi # When large executables or shared objects are built, AIX ld can # have problems creating the table of contents. If linking a library # or program results in "error TOC overflow" add -mminimal-toc to # CXXFLAGS/CFLAGS for g++/gcc. In the cases where that is not # enough to fix the problem, add -Wl,-bbigtoc to LDFLAGS. archive_cmds='' hardcode_direct=yes hardcode_direct_absolute=yes hardcode_libdir_separator=':' link_all_deplibs=yes file_list_spec='${wl}-f,' if test "$GCC" = yes; then case $host_os in aix4.[012]|aix4.[012].*) # We only want to do this on AIX 4.2 and lower, the check # below for broken collect2 doesn't work under 4.3+ collect2name=`${CC} -print-prog-name=collect2` if test -f "$collect2name" && strings "$collect2name" | $GREP resolve_lib_name >/dev/null then # We have reworked collect2 : else # We have old collect2 hardcode_direct=unsupported # It fails to find uninstalled libraries when the uninstalled # path is not listed in the libpath. Setting hardcode_minus_L # to unsupported forces relinking hardcode_minus_L=yes hardcode_libdir_flag_spec='-L$libdir' hardcode_libdir_separator= fi ;; esac shared_flag='-shared' if test "$aix_use_runtimelinking" = yes; then shared_flag="$shared_flag "'${wl}-G' fi link_all_deplibs=no else # not using gcc if test "$host_cpu" = ia64; then # VisualAge C++, Version 5.5 for AIX 5L for IA-64, Beta 3 Release # chokes on -Wl,-G. The following line is correct: shared_flag='-G' else if test "$aix_use_runtimelinking" = yes; then shared_flag='${wl}-G' else shared_flag='${wl}-bM:SRE' fi fi fi export_dynamic_flag_spec='${wl}-bexpall' # It seems that -bexpall does not export symbols beginning with # underscore (_), so it is better to generate a list of symbols to export. always_export_symbols=yes if test "$aix_use_runtimelinking" = yes; then # Warning - without using the other runtime loading flags (-brtl), # -berok will link without error, but may produce a broken library. allow_undefined_flag='-berok' # Determine the default libpath from the value encoded in an # empty executable. if test "${lt_cv_aix_libpath+set}" = set; then aix_libpath=$lt_cv_aix_libpath else if ${lt_cv_aix_libpath_+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : lt_aix_libpath_sed=' /Import File Strings/,/^$/ { /^0/ { s/^0 *\([^ ]*\) *$/\1/ p } }' lt_cv_aix_libpath_=`dump -H conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"` # Check for a 64-bit object if we didn't find anything. if test -z "$lt_cv_aix_libpath_"; then lt_cv_aix_libpath_=`dump -HX64 conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"` fi fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext if test -z "$lt_cv_aix_libpath_"; then lt_cv_aix_libpath_="/usr/lib:/lib" fi fi aix_libpath=$lt_cv_aix_libpath_ fi hardcode_libdir_flag_spec='${wl}-blibpath:$libdir:'"$aix_libpath" archive_expsym_cmds='$CC -o $output_objdir/$soname $libobjs $deplibs '"\${wl}$no_entry_flag"' $compiler_flags `if test "x${allow_undefined_flag}" != "x"; then func_echo_all "${wl}${allow_undefined_flag}"; else :; fi` '"\${wl}$exp_sym_flag:\$export_symbols $shared_flag" else if test "$host_cpu" = ia64; then hardcode_libdir_flag_spec='${wl}-R $libdir:/usr/lib:/lib' allow_undefined_flag="-z nodefs" archive_expsym_cmds="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs '"\${wl}$no_entry_flag"' $compiler_flags ${wl}${allow_undefined_flag} '"\${wl}$exp_sym_flag:\$export_symbols" else # Determine the default libpath from the value encoded in an # empty executable. if test "${lt_cv_aix_libpath+set}" = set; then aix_libpath=$lt_cv_aix_libpath else if ${lt_cv_aix_libpath_+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : lt_aix_libpath_sed=' /Import File Strings/,/^$/ { /^0/ { s/^0 *\([^ ]*\) *$/\1/ p } }' lt_cv_aix_libpath_=`dump -H conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"` # Check for a 64-bit object if we didn't find anything. if test -z "$lt_cv_aix_libpath_"; then lt_cv_aix_libpath_=`dump -HX64 conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"` fi fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext if test -z "$lt_cv_aix_libpath_"; then lt_cv_aix_libpath_="/usr/lib:/lib" fi fi aix_libpath=$lt_cv_aix_libpath_ fi hardcode_libdir_flag_spec='${wl}-blibpath:$libdir:'"$aix_libpath" # Warning - without using the other run time loading flags, # -berok will link without error, but may produce a broken library. no_undefined_flag=' ${wl}-bernotok' allow_undefined_flag=' ${wl}-berok' if test "$with_gnu_ld" = yes; then # We only use this code for GNU lds that support --whole-archive. whole_archive_flag_spec='${wl}--whole-archive$convenience ${wl}--no-whole-archive' else # Exported symbols can be pulled into shared objects from archives whole_archive_flag_spec='$convenience' fi archive_cmds_need_lc=yes # This is similar to how AIX traditionally builds its shared libraries. archive_expsym_cmds="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs ${wl}-bnoentry $compiler_flags ${wl}-bE:$export_symbols${allow_undefined_flag}~$AR $AR_FLAGS $output_objdir/$libname$release.a $output_objdir/$soname' fi fi ;; amigaos*) case $host_cpu in powerpc) # see comment about AmigaOS4 .so support archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' archive_expsym_cmds='' ;; m68k) archive_cmds='$RM $output_objdir/a2ixlibrary.data~$ECHO "#define NAME $libname" > $output_objdir/a2ixlibrary.data~$ECHO "#define LIBRARY_ID 1" >> $output_objdir/a2ixlibrary.data~$ECHO "#define VERSION $major" >> $output_objdir/a2ixlibrary.data~$ECHO "#define REVISION $revision" >> $output_objdir/a2ixlibrary.data~$AR $AR_FLAGS $lib $libobjs~$RANLIB $lib~(cd $output_objdir && a2ixlibrary -32)' hardcode_libdir_flag_spec='-L$libdir' hardcode_minus_L=yes ;; esac ;; bsdi[45]*) export_dynamic_flag_spec=-rdynamic ;; cygwin* | mingw* | pw32* | cegcc*) # When not using gcc, we currently assume that we are using # Microsoft Visual C++. # hardcode_libdir_flag_spec is actually meaningless, as there is # no search path for DLLs. case $cc_basename in cl*) # Native MSVC hardcode_libdir_flag_spec=' ' allow_undefined_flag=unsupported always_export_symbols=yes file_list_spec='@' # Tell ltmain to make .lib files, not .a files. libext=lib # Tell ltmain to make .dll files, not .so files. shrext_cmds=".dll" # FIXME: Setting linknames here is a bad hack. archive_cmds='$CC -o $output_objdir/$soname $libobjs $compiler_flags $deplibs -Wl,-dll~linknames=' archive_expsym_cmds='if test "x`$SED 1q $export_symbols`" = xEXPORTS; then sed -n -e 's/\\\\\\\(.*\\\\\\\)/-link\\\ -EXPORT:\\\\\\\1/' -e '1\\\!p' < $export_symbols > $output_objdir/$soname.exp; else sed -e 's/\\\\\\\(.*\\\\\\\)/-link\\\ -EXPORT:\\\\\\\1/' < $export_symbols > $output_objdir/$soname.exp; fi~ $CC -o $tool_output_objdir$soname $libobjs $compiler_flags $deplibs "@$tool_output_objdir$soname.exp" -Wl,-DLL,-IMPLIB:"$tool_output_objdir$libname.dll.lib"~ linknames=' # The linker will not automatically build a static lib if we build a DLL. # _LT_TAGVAR(old_archive_from_new_cmds, )='true' enable_shared_with_static_runtimes=yes exclude_expsyms='_NULL_IMPORT_DESCRIPTOR|_IMPORT_DESCRIPTOR_.*' export_symbols_cmds='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[BCDGRS][ ]/s/.*[ ]\([^ ]*\)/\1,DATA/'\'' | $SED -e '\''/^[AITW][ ]/s/.*[ ]//'\'' | sort | uniq > $export_symbols' # Don't use ranlib old_postinstall_cmds='chmod 644 $oldlib' postlink_cmds='lt_outputfile="@OUTPUT@"~ lt_tool_outputfile="@TOOL_OUTPUT@"~ case $lt_outputfile in *.exe|*.EXE) ;; *) lt_outputfile="$lt_outputfile.exe" lt_tool_outputfile="$lt_tool_outputfile.exe" ;; esac~ if test "$MANIFEST_TOOL" != ":" && test -f "$lt_outputfile.manifest"; then $MANIFEST_TOOL -manifest "$lt_tool_outputfile.manifest" -outputresource:"$lt_tool_outputfile" || exit 1; $RM "$lt_outputfile.manifest"; fi' ;; *) # Assume MSVC wrapper hardcode_libdir_flag_spec=' ' allow_undefined_flag=unsupported # Tell ltmain to make .lib files, not .a files. libext=lib # Tell ltmain to make .dll files, not .so files. shrext_cmds=".dll" # FIXME: Setting linknames here is a bad hack. archive_cmds='$CC -o $lib $libobjs $compiler_flags `func_echo_all "$deplibs" | $SED '\''s/ -lc$//'\''` -link -dll~linknames=' # The linker will automatically build a .lib file if we build a DLL. old_archive_from_new_cmds='true' # FIXME: Should let the user specify the lib program. old_archive_cmds='lib -OUT:$oldlib$oldobjs$old_deplibs' enable_shared_with_static_runtimes=yes ;; esac ;; darwin* | rhapsody*) archive_cmds_need_lc=no hardcode_direct=no hardcode_automatic=yes hardcode_shlibpath_var=unsupported if test "$lt_cv_ld_force_load" = "yes"; then whole_archive_flag_spec='`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience ${wl}-force_load,$conv\"; done; func_echo_all \"$new_convenience\"`' else whole_archive_flag_spec='' fi link_all_deplibs=yes allow_undefined_flag="$_lt_dar_allow_undefined" case $cc_basename in ifort*) _lt_dar_can_shared=yes ;; *) _lt_dar_can_shared=$GCC ;; esac if test "$_lt_dar_can_shared" = "yes"; then output_verbose_link_cmd=func_echo_all archive_cmds="\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring $_lt_dar_single_mod${_lt_dsymutil}" module_cmds="\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dsymutil}" archive_expsym_cmds="sed 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring ${_lt_dar_single_mod}${_lt_dar_export_syms}${_lt_dsymutil}" module_expsym_cmds="sed -e 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dar_export_syms}${_lt_dsymutil}" else ld_shlibs=no fi ;; dgux*) archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' hardcode_libdir_flag_spec='-L$libdir' hardcode_shlibpath_var=no ;; # FreeBSD 2.2.[012] allows us to include c++rt0.o to get C++ constructor # support. Future versions do this automatically, but an explicit c++rt0.o # does not break anything, and helps significantly (at the cost of a little # extra space). freebsd2.2*) archive_cmds='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags /usr/lib/c++rt0.o' hardcode_libdir_flag_spec='-R$libdir' hardcode_direct=yes hardcode_shlibpath_var=no ;; # Unfortunately, older versions of FreeBSD 2 do not have this feature. freebsd2.*) archive_cmds='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags' hardcode_direct=yes hardcode_minus_L=yes hardcode_shlibpath_var=no ;; # FreeBSD 3 and greater uses gcc -shared to do shared libraries. freebsd* | dragonfly*) archive_cmds='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags' hardcode_libdir_flag_spec='-R$libdir' hardcode_direct=yes hardcode_shlibpath_var=no ;; hpux9*) if test "$GCC" = yes; then archive_cmds='$RM $output_objdir/$soname~$CC -shared $pic_flag ${wl}+b ${wl}$install_libdir -o $output_objdir/$soname $libobjs $deplibs $compiler_flags~test $output_objdir/$soname = $lib || mv $output_objdir/$soname $lib' else archive_cmds='$RM $output_objdir/$soname~$LD -b +b $install_libdir -o $output_objdir/$soname $libobjs $deplibs $linker_flags~test $output_objdir/$soname = $lib || mv $output_objdir/$soname $lib' fi hardcode_libdir_flag_spec='${wl}+b ${wl}$libdir' hardcode_libdir_separator=: hardcode_direct=yes # hardcode_minus_L: Not really in the search PATH, # but as the default location of the library. hardcode_minus_L=yes export_dynamic_flag_spec='${wl}-E' ;; hpux10*) if test "$GCC" = yes && test "$with_gnu_ld" = no; then archive_cmds='$CC -shared $pic_flag ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $libobjs $deplibs $compiler_flags' else archive_cmds='$LD -b +h $soname +b $install_libdir -o $lib $libobjs $deplibs $linker_flags' fi if test "$with_gnu_ld" = no; then hardcode_libdir_flag_spec='${wl}+b ${wl}$libdir' hardcode_libdir_separator=: hardcode_direct=yes hardcode_direct_absolute=yes export_dynamic_flag_spec='${wl}-E' # hardcode_minus_L: Not really in the search PATH, # but as the default location of the library. hardcode_minus_L=yes fi ;; hpux11*) if test "$GCC" = yes && test "$with_gnu_ld" = no; then case $host_cpu in hppa*64*) archive_cmds='$CC -shared ${wl}+h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags' ;; ia64*) archive_cmds='$CC -shared $pic_flag ${wl}+h ${wl}$soname ${wl}+nodefaultrpath -o $lib $libobjs $deplibs $compiler_flags' ;; *) archive_cmds='$CC -shared $pic_flag ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $libobjs $deplibs $compiler_flags' ;; esac else case $host_cpu in hppa*64*) archive_cmds='$CC -b ${wl}+h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags' ;; ia64*) archive_cmds='$CC -b ${wl}+h ${wl}$soname ${wl}+nodefaultrpath -o $lib $libobjs $deplibs $compiler_flags' ;; *) # Older versions of the 11.00 compiler do not understand -b yet # (HP92453-01 A.11.01.20 doesn't, HP92453-01 B.11.X.35175-35176.GP does) { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $CC understands -b" >&5 $as_echo_n "checking if $CC understands -b... " >&6; } if ${lt_cv_prog_compiler__b+:} false; then : $as_echo_n "(cached) " >&6 else lt_cv_prog_compiler__b=no save_LDFLAGS="$LDFLAGS" LDFLAGS="$LDFLAGS -b" echo "$lt_simple_link_test_code" > conftest.$ac_ext if (eval $ac_link 2>conftest.err) && test -s conftest$ac_exeext; then # The linker can only warn and ignore the option if not recognized # So say no if there are warnings if test -s conftest.err; then # Append any errors to the config.log. cat conftest.err 1>&5 $ECHO "$_lt_linker_boilerplate" | $SED '/^$/d' > conftest.exp $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2 if diff conftest.exp conftest.er2 >/dev/null; then lt_cv_prog_compiler__b=yes fi else lt_cv_prog_compiler__b=yes fi fi $RM -r conftest* LDFLAGS="$save_LDFLAGS" fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler__b" >&5 $as_echo "$lt_cv_prog_compiler__b" >&6; } if test x"$lt_cv_prog_compiler__b" = xyes; then archive_cmds='$CC -b ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $libobjs $deplibs $compiler_flags' else archive_cmds='$LD -b +h $soname +b $install_libdir -o $lib $libobjs $deplibs $linker_flags' fi ;; esac fi if test "$with_gnu_ld" = no; then hardcode_libdir_flag_spec='${wl}+b ${wl}$libdir' hardcode_libdir_separator=: case $host_cpu in hppa*64*|ia64*) hardcode_direct=no hardcode_shlibpath_var=no ;; *) hardcode_direct=yes hardcode_direct_absolute=yes export_dynamic_flag_spec='${wl}-E' # hardcode_minus_L: Not really in the search PATH, # but as the default location of the library. hardcode_minus_L=yes ;; esac fi ;; irix5* | irix6* | nonstopux*) if test "$GCC" = yes; then archive_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && func_echo_all "${wl}-set_version ${wl}$verstring"` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib' # Try to use the -exported_symbol ld option, if it does not # work, assume that -exports_file does not work either and # implicitly export all symbols. # This should be the same for all languages, so no per-tag cache variable. { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the $host_os linker accepts -exported_symbol" >&5 $as_echo_n "checking whether the $host_os linker accepts -exported_symbol... " >&6; } if ${lt_cv_irix_exported_symbol+:} false; then : $as_echo_n "(cached) " >&6 else save_LDFLAGS="$LDFLAGS" LDFLAGS="$LDFLAGS -shared ${wl}-exported_symbol ${wl}foo ${wl}-update_registry ${wl}/dev/null" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int foo (void) { return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : lt_cv_irix_exported_symbol=yes else lt_cv_irix_exported_symbol=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LDFLAGS="$save_LDFLAGS" fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_irix_exported_symbol" >&5 $as_echo "$lt_cv_irix_exported_symbol" >&6; } if test "$lt_cv_irix_exported_symbol" = yes; then archive_expsym_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && func_echo_all "${wl}-set_version ${wl}$verstring"` ${wl}-update_registry ${wl}${output_objdir}/so_locations ${wl}-exports_file ${wl}$export_symbols -o $lib' fi else archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry ${output_objdir}/so_locations -o $lib' archive_expsym_cmds='$CC -shared $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry ${output_objdir}/so_locations -exports_file $export_symbols -o $lib' fi archive_cmds_need_lc='no' hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir' hardcode_libdir_separator=: inherit_rpath=yes link_all_deplibs=yes ;; netbsd* | netbsdelf*-gnu) if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then archive_cmds='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags' # a.out else archive_cmds='$LD -shared -o $lib $libobjs $deplibs $linker_flags' # ELF fi hardcode_libdir_flag_spec='-R$libdir' hardcode_direct=yes hardcode_shlibpath_var=no ;; newsos6) archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' hardcode_direct=yes hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir' hardcode_libdir_separator=: hardcode_shlibpath_var=no ;; *nto* | *qnx*) ;; openbsd*) if test -f /usr/libexec/ld.so; then hardcode_direct=yes hardcode_shlibpath_var=no hardcode_direct_absolute=yes if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then archive_cmds='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags' archive_expsym_cmds='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags ${wl}-retain-symbols-file,$export_symbols' hardcode_libdir_flag_spec='${wl}-rpath,$libdir' export_dynamic_flag_spec='${wl}-E' else case $host_os in openbsd[01].* | openbsd2.[0-7] | openbsd2.[0-7].*) archive_cmds='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags' hardcode_libdir_flag_spec='-R$libdir' ;; *) archive_cmds='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags' hardcode_libdir_flag_spec='${wl}-rpath,$libdir' ;; esac fi else ld_shlibs=no fi ;; os2*) hardcode_libdir_flag_spec='-L$libdir' hardcode_minus_L=yes allow_undefined_flag=unsupported archive_cmds='$ECHO "LIBRARY $libname INITINSTANCE" > $output_objdir/$libname.def~$ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~echo DATA >> $output_objdir/$libname.def~echo " SINGLE NONSHARED" >> $output_objdir/$libname.def~echo EXPORTS >> $output_objdir/$libname.def~emxexp $libobjs >> $output_objdir/$libname.def~$CC -Zdll -Zcrtdll -o $lib $libobjs $deplibs $compiler_flags $output_objdir/$libname.def' old_archive_from_new_cmds='emximp -o $output_objdir/$libname.a $output_objdir/$libname.def' ;; osf3*) if test "$GCC" = yes; then allow_undefined_flag=' ${wl}-expect_unresolved ${wl}\*' archive_cmds='$CC -shared${allow_undefined_flag} $libobjs $deplibs $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && func_echo_all "${wl}-set_version ${wl}$verstring"` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib' else allow_undefined_flag=' -expect_unresolved \*' archive_cmds='$CC -shared${allow_undefined_flag} $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry ${output_objdir}/so_locations -o $lib' fi archive_cmds_need_lc='no' hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir' hardcode_libdir_separator=: ;; osf4* | osf5*) # as osf3* with the addition of -msym flag if test "$GCC" = yes; then allow_undefined_flag=' ${wl}-expect_unresolved ${wl}\*' archive_cmds='$CC -shared${allow_undefined_flag} $pic_flag $libobjs $deplibs $compiler_flags ${wl}-msym ${wl}-soname ${wl}$soname `test -n "$verstring" && func_echo_all "${wl}-set_version ${wl}$verstring"` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib' hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir' else allow_undefined_flag=' -expect_unresolved \*' archive_cmds='$CC -shared${allow_undefined_flag} $libobjs $deplibs $compiler_flags -msym -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry ${output_objdir}/so_locations -o $lib' archive_expsym_cmds='for i in `cat $export_symbols`; do printf "%s %s\\n" -exported_symbol "\$i" >> $lib.exp; done; printf "%s\\n" "-hidden">> $lib.exp~ $CC -shared${allow_undefined_flag} ${wl}-input ${wl}$lib.exp $compiler_flags $libobjs $deplibs -soname $soname `test -n "$verstring" && $ECHO "-set_version $verstring"` -update_registry ${output_objdir}/so_locations -o $lib~$RM $lib.exp' # Both c and cxx compiler support -rpath directly hardcode_libdir_flag_spec='-rpath $libdir' fi archive_cmds_need_lc='no' hardcode_libdir_separator=: ;; solaris*) no_undefined_flag=' -z defs' if test "$GCC" = yes; then wlarc='${wl}' archive_cmds='$CC -shared $pic_flag ${wl}-z ${wl}text ${wl}-h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags' archive_expsym_cmds='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ $CC -shared $pic_flag ${wl}-z ${wl}text ${wl}-M ${wl}$lib.exp ${wl}-h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags~$RM $lib.exp' else case `$CC -V 2>&1` in *"Compilers 5.0"*) wlarc='' archive_cmds='$LD -G${allow_undefined_flag} -h $soname -o $lib $libobjs $deplibs $linker_flags' archive_expsym_cmds='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ $LD -G${allow_undefined_flag} -M $lib.exp -h $soname -o $lib $libobjs $deplibs $linker_flags~$RM $lib.exp' ;; *) wlarc='${wl}' archive_cmds='$CC -G${allow_undefined_flag} -h $soname -o $lib $libobjs $deplibs $compiler_flags' archive_expsym_cmds='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ $CC -G${allow_undefined_flag} -M $lib.exp -h $soname -o $lib $libobjs $deplibs $compiler_flags~$RM $lib.exp' ;; esac fi hardcode_libdir_flag_spec='-R$libdir' hardcode_shlibpath_var=no case $host_os in solaris2.[0-5] | solaris2.[0-5].*) ;; *) # The compiler driver will combine and reorder linker options, # but understands `-z linker_flag'. GCC discards it without `$wl', # but is careful enough not to reorder. # Supported since Solaris 2.6 (maybe 2.5.1?) if test "$GCC" = yes; then whole_archive_flag_spec='${wl}-z ${wl}allextract$convenience ${wl}-z ${wl}defaultextract' else whole_archive_flag_spec='-z allextract$convenience -z defaultextract' fi ;; esac link_all_deplibs=yes ;; sunos4*) if test "x$host_vendor" = xsequent; then # Use $CC to link under sequent, because it throws in some extra .o # files that make .init and .fini sections work. archive_cmds='$CC -G ${wl}-h $soname -o $lib $libobjs $deplibs $compiler_flags' else archive_cmds='$LD -assert pure-text -Bstatic -o $lib $libobjs $deplibs $linker_flags' fi hardcode_libdir_flag_spec='-L$libdir' hardcode_direct=yes hardcode_minus_L=yes hardcode_shlibpath_var=no ;; sysv4) case $host_vendor in sni) archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' hardcode_direct=yes # is this really true??? ;; siemens) ## LD is ld it makes a PLAMLIB ## CC just makes a GrossModule. archive_cmds='$LD -G -o $lib $libobjs $deplibs $linker_flags' reload_cmds='$CC -r -o $output$reload_objs' hardcode_direct=no ;; motorola) archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' hardcode_direct=no #Motorola manual says yes, but my tests say they lie ;; esac runpath_var='LD_RUN_PATH' hardcode_shlibpath_var=no ;; sysv4.3*) archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' hardcode_shlibpath_var=no export_dynamic_flag_spec='-Bexport' ;; sysv4*MP*) if test -d /usr/nec; then archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' hardcode_shlibpath_var=no runpath_var=LD_RUN_PATH hardcode_runpath_var=yes ld_shlibs=yes fi ;; sysv4*uw2* | sysv5OpenUNIX* | sysv5UnixWare7.[01].[10]* | unixware7* | sco3.2v5.0.[024]*) no_undefined_flag='${wl}-z,text' archive_cmds_need_lc=no hardcode_shlibpath_var=no runpath_var='LD_RUN_PATH' if test "$GCC" = yes; then archive_cmds='$CC -shared ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' archive_expsym_cmds='$CC -shared ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' else archive_cmds='$CC -G ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' archive_expsym_cmds='$CC -G ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' fi ;; sysv5* | sco3.2v5* | sco5v6*) # Note: We can NOT use -z defs as we might desire, because we do not # link with -lc, and that would cause any symbols used from libc to # always be unresolved, which means just about no library would # ever link correctly. If we're not using GNU ld we use -z text # though, which does catch some bad symbols but isn't as heavy-handed # as -z defs. no_undefined_flag='${wl}-z,text' allow_undefined_flag='${wl}-z,nodefs' archive_cmds_need_lc=no hardcode_shlibpath_var=no hardcode_libdir_flag_spec='${wl}-R,$libdir' hardcode_libdir_separator=':' link_all_deplibs=yes export_dynamic_flag_spec='${wl}-Bexport' runpath_var='LD_RUN_PATH' if test "$GCC" = yes; then archive_cmds='$CC -shared ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' archive_expsym_cmds='$CC -shared ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' else archive_cmds='$CC -G ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' archive_expsym_cmds='$CC -G ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' fi ;; uts4*) archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' hardcode_libdir_flag_spec='-L$libdir' hardcode_shlibpath_var=no ;; *) ld_shlibs=no ;; esac if test x$host_vendor = xsni; then case $host in sysv4 | sysv4.2uw2* | sysv4.3* | sysv5*) export_dynamic_flag_spec='${wl}-Blargedynsym' ;; esac fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ld_shlibs" >&5 $as_echo "$ld_shlibs" >&6; } test "$ld_shlibs" = no && can_build_shared=no with_gnu_ld=$with_gnu_ld # # Do we need to explicitly link libc? # case "x$archive_cmds_need_lc" in x|xyes) # Assume -lc should be added archive_cmds_need_lc=yes if test "$enable_shared" = yes && test "$GCC" = yes; then case $archive_cmds in *'~'*) # FIXME: we may have to deal with multi-command sequences. ;; '$CC '*) # Test whether the compiler implicitly links with -lc since on some # systems, -lgcc has to come before -lc. If gcc already passes -lc # to ld, don't add -lc before -lgcc. { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether -lc should be explicitly linked in" >&5 $as_echo_n "checking whether -lc should be explicitly linked in... " >&6; } if ${lt_cv_archive_cmds_need_lc+:} false; then : $as_echo_n "(cached) " >&6 else $RM conftest* echo "$lt_simple_compile_test_code" > conftest.$ac_ext if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5 (eval $ac_compile) 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } 2>conftest.err; then soname=conftest lib=conftest libobjs=conftest.$ac_objext deplibs= wl=$lt_prog_compiler_wl pic_flag=$lt_prog_compiler_pic compiler_flags=-v linker_flags=-v verstring= output_objdir=. libname=conftest lt_save_allow_undefined_flag=$allow_undefined_flag allow_undefined_flag= if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$archive_cmds 2\>\&1 \| $GREP \" -lc \" \>/dev/null 2\>\&1\""; } >&5 (eval $archive_cmds 2\>\&1 \| $GREP \" -lc \" \>/dev/null 2\>\&1) 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } then lt_cv_archive_cmds_need_lc=no else lt_cv_archive_cmds_need_lc=yes fi allow_undefined_flag=$lt_save_allow_undefined_flag else cat conftest.err 1>&5 fi $RM conftest* fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_archive_cmds_need_lc" >&5 $as_echo "$lt_cv_archive_cmds_need_lc" >&6; } archive_cmds_need_lc=$lt_cv_archive_cmds_need_lc ;; esac fi ;; esac { $as_echo "$as_me:${as_lineno-$LINENO}: checking dynamic linker characteristics" >&5 $as_echo_n "checking dynamic linker characteristics... " >&6; } if test "$GCC" = yes; then case $host_os in darwin*) lt_awk_arg="/^libraries:/,/LR/" ;; *) lt_awk_arg="/^libraries:/" ;; esac case $host_os in mingw* | cegcc*) lt_sed_strip_eq="s,=\([A-Za-z]:\),\1,g" ;; *) lt_sed_strip_eq="s,=/,/,g" ;; esac lt_search_path_spec=`$CC -print-search-dirs | awk $lt_awk_arg | $SED -e "s/^libraries://" -e $lt_sed_strip_eq` case $lt_search_path_spec in *\;*) # if the path contains ";" then we assume it to be the separator # otherwise default to the standard path separator (i.e. ":") - it is # assumed that no part of a normal pathname contains ";" but that should # okay in the real world where ";" in dirpaths is itself problematic. lt_search_path_spec=`$ECHO "$lt_search_path_spec" | $SED 's/;/ /g'` ;; *) lt_search_path_spec=`$ECHO "$lt_search_path_spec" | $SED "s/$PATH_SEPARATOR/ /g"` ;; esac # Ok, now we have the path, separated by spaces, we can step through it # and add multilib dir if necessary. lt_tmp_lt_search_path_spec= lt_multi_os_dir=`$CC $CPPFLAGS $CFLAGS $LDFLAGS -print-multi-os-directory 2>/dev/null` for lt_sys_path in $lt_search_path_spec; do if test -d "$lt_sys_path/$lt_multi_os_dir"; then lt_tmp_lt_search_path_spec="$lt_tmp_lt_search_path_spec $lt_sys_path/$lt_multi_os_dir" else test -d "$lt_sys_path" && \ lt_tmp_lt_search_path_spec="$lt_tmp_lt_search_path_spec $lt_sys_path" fi done lt_search_path_spec=`$ECHO "$lt_tmp_lt_search_path_spec" | awk ' BEGIN {RS=" "; FS="/|\n";} { lt_foo=""; lt_count=0; for (lt_i = NF; lt_i > 0; lt_i--) { if ($lt_i != "" && $lt_i != ".") { if ($lt_i == "..") { lt_count++; } else { if (lt_count == 0) { lt_foo="/" $lt_i lt_foo; } else { lt_count--; } } } } if (lt_foo != "") { lt_freq[lt_foo]++; } if (lt_freq[lt_foo] == 1) { print lt_foo; } }'` # AWK program above erroneously prepends '/' to C:/dos/paths # for these hosts. case $host_os in mingw* | cegcc*) lt_search_path_spec=`$ECHO "$lt_search_path_spec" |\ $SED 's,/\([A-Za-z]:\),\1,g'` ;; esac sys_lib_search_path_spec=`$ECHO "$lt_search_path_spec" | $lt_NL2SP` else sys_lib_search_path_spec="/lib /usr/lib /usr/local/lib" fi library_names_spec= libname_spec='lib$name' soname_spec= shrext_cmds=".so" postinstall_cmds= postuninstall_cmds= finish_cmds= finish_eval= shlibpath_var= shlibpath_overrides_runpath=unknown version_type=none dynamic_linker="$host_os ld.so" sys_lib_dlsearch_path_spec="/lib /usr/lib" need_lib_prefix=unknown hardcode_into_libs=no # when you set need_version to no, make sure it does not cause -set_version # flags to be left without arguments need_version=unknown case $host_os in aix3*) version_type=linux # correct to gnu/linux during the next big refactor library_names_spec='${libname}${release}${shared_ext}$versuffix $libname.a' shlibpath_var=LIBPATH # AIX 3 has no versioning support, so we append a major version to the name. soname_spec='${libname}${release}${shared_ext}$major' ;; aix[4-9]*) version_type=linux # correct to gnu/linux during the next big refactor need_lib_prefix=no need_version=no hardcode_into_libs=yes if test "$host_cpu" = ia64; then # AIX 5 supports IA64 library_names_spec='${libname}${release}${shared_ext}$major ${libname}${release}${shared_ext}$versuffix $libname${shared_ext}' shlibpath_var=LD_LIBRARY_PATH else # With GCC up to 2.95.x, collect2 would create an import file # for dependence libraries. The import file would start with # the line `#! .'. This would cause the generated library to # depend on `.', always an invalid library. This was fixed in # development snapshots of GCC prior to 3.0. case $host_os in aix4 | aix4.[01] | aix4.[01].*) if { echo '#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 97)' echo ' yes ' echo '#endif'; } | ${CC} -E - | $GREP yes > /dev/null; then : else can_build_shared=no fi ;; esac # AIX (on Power*) has no versioning support, so currently we can not hardcode correct # soname into executable. Probably we can add versioning support to # collect2, so additional links can be useful in future. if test "$aix_use_runtimelinking" = yes; then # If using run time linking (on AIX 4.2 or later) use lib.so # instead of lib.a to let people know that these are not # typical AIX shared libraries. library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' else # We preserve .a as extension for shared libraries through AIX4.2 # and later when we are not doing run time linking. library_names_spec='${libname}${release}.a $libname.a' soname_spec='${libname}${release}${shared_ext}$major' fi shlibpath_var=LIBPATH fi ;; amigaos*) case $host_cpu in powerpc) # Since July 2007 AmigaOS4 officially supports .so libraries. # When compiling the executable, add -use-dynld -Lsobjs: to the compileline. library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' ;; m68k) library_names_spec='$libname.ixlibrary $libname.a' # Create ${libname}_ixlibrary.a entries in /sys/libs. finish_eval='for lib in `ls $libdir/*.ixlibrary 2>/dev/null`; do libname=`func_echo_all "$lib" | $SED '\''s%^.*/\([^/]*\)\.ixlibrary$%\1%'\''`; test $RM /sys/libs/${libname}_ixlibrary.a; $show "cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a"; cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a || exit 1; done' ;; esac ;; beos*) library_names_spec='${libname}${shared_ext}' dynamic_linker="$host_os ld.so" shlibpath_var=LIBRARY_PATH ;; bsdi[45]*) version_type=linux # correct to gnu/linux during the next big refactor need_version=no library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' soname_spec='${libname}${release}${shared_ext}$major' finish_cmds='PATH="\$PATH:/sbin" ldconfig $libdir' shlibpath_var=LD_LIBRARY_PATH sys_lib_search_path_spec="/shlib /usr/lib /usr/X11/lib /usr/contrib/lib /lib /usr/local/lib" sys_lib_dlsearch_path_spec="/shlib /usr/lib /usr/local/lib" # the default ld.so.conf also contains /usr/contrib/lib and # /usr/X11R6/lib (/usr/X11 is a link to /usr/X11R6), but let us allow # libtool to hard-code these into programs ;; cygwin* | mingw* | pw32* | cegcc*) version_type=windows shrext_cmds=".dll" need_version=no need_lib_prefix=no case $GCC,$cc_basename in yes,*) # gcc library_names_spec='$libname.dll.a' # DLL is installed to $(libdir)/../bin by postinstall_cmds postinstall_cmds='base_file=`basename \${file}`~ dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\${base_file}'\''i; echo \$dlname'\''`~ dldir=$destdir/`dirname \$dlpath`~ test -d \$dldir || mkdir -p \$dldir~ $install_prog $dir/$dlname \$dldir/$dlname~ chmod a+x \$dldir/$dlname~ if test -n '\''$stripme'\'' && test -n '\''$striplib'\''; then eval '\''$striplib \$dldir/$dlname'\'' || exit \$?; fi' postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; echo \$dlname'\''`~ dlpath=$dir/\$dldll~ $RM \$dlpath' shlibpath_overrides_runpath=yes case $host_os in cygwin*) # Cygwin DLLs use 'cyg' prefix rather than 'lib' soname_spec='`echo ${libname} | sed -e 's/^lib/cyg/'``echo ${release} | $SED -e 's/[.]/-/g'`${versuffix}${shared_ext}' sys_lib_search_path_spec="$sys_lib_search_path_spec /usr/lib/w32api" ;; mingw* | cegcc*) # MinGW DLLs use traditional 'lib' prefix soname_spec='${libname}`echo ${release} | $SED -e 's/[.]/-/g'`${versuffix}${shared_ext}' ;; pw32*) # pw32 DLLs use 'pw' prefix rather than 'lib' library_names_spec='`echo ${libname} | sed -e 's/^lib/pw/'``echo ${release} | $SED -e 's/[.]/-/g'`${versuffix}${shared_ext}' ;; esac dynamic_linker='Win32 ld.exe' ;; *,cl*) # Native MSVC libname_spec='$name' soname_spec='${libname}`echo ${release} | $SED -e 's/[.]/-/g'`${versuffix}${shared_ext}' library_names_spec='${libname}.dll.lib' case $build_os in mingw*) sys_lib_search_path_spec= lt_save_ifs=$IFS IFS=';' for lt_path in $LIB do IFS=$lt_save_ifs # Let DOS variable expansion print the short 8.3 style file name. lt_path=`cd "$lt_path" 2>/dev/null && cmd //C "for %i in (".") do @echo %~si"` sys_lib_search_path_spec="$sys_lib_search_path_spec $lt_path" done IFS=$lt_save_ifs # Convert to MSYS style. sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | sed -e 's|\\\\|/|g' -e 's| \\([a-zA-Z]\\):| /\\1|g' -e 's|^ ||'` ;; cygwin*) # Convert to unix form, then to dos form, then back to unix form # but this time dos style (no spaces!) so that the unix form looks # like /cygdrive/c/PROGRA~1:/cygdr... sys_lib_search_path_spec=`cygpath --path --unix "$LIB"` sys_lib_search_path_spec=`cygpath --path --dos "$sys_lib_search_path_spec" 2>/dev/null` sys_lib_search_path_spec=`cygpath --path --unix "$sys_lib_search_path_spec" | $SED -e "s/$PATH_SEPARATOR/ /g"` ;; *) sys_lib_search_path_spec="$LIB" if $ECHO "$sys_lib_search_path_spec" | $GREP ';[c-zC-Z]:/' >/dev/null; then # It is most probably a Windows format PATH. sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | $SED -e 's/;/ /g'` else sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | $SED -e "s/$PATH_SEPARATOR/ /g"` fi # FIXME: find the short name or the path components, as spaces are # common. (e.g. "Program Files" -> "PROGRA~1") ;; esac # DLL is installed to $(libdir)/../bin by postinstall_cmds postinstall_cmds='base_file=`basename \${file}`~ dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\${base_file}'\''i; echo \$dlname'\''`~ dldir=$destdir/`dirname \$dlpath`~ test -d \$dldir || mkdir -p \$dldir~ $install_prog $dir/$dlname \$dldir/$dlname' postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; echo \$dlname'\''`~ dlpath=$dir/\$dldll~ $RM \$dlpath' shlibpath_overrides_runpath=yes dynamic_linker='Win32 link.exe' ;; *) # Assume MSVC wrapper library_names_spec='${libname}`echo ${release} | $SED -e 's/[.]/-/g'`${versuffix}${shared_ext} $libname.lib' dynamic_linker='Win32 ld.exe' ;; esac # FIXME: first we should search . and the directory the executable is in shlibpath_var=PATH ;; darwin* | rhapsody*) dynamic_linker="$host_os dyld" version_type=darwin need_lib_prefix=no need_version=no library_names_spec='${libname}${release}${major}$shared_ext ${libname}$shared_ext' soname_spec='${libname}${release}${major}$shared_ext' shlibpath_overrides_runpath=yes shlibpath_var=DYLD_LIBRARY_PATH shrext_cmds='`test .$module = .yes && echo .so || echo .dylib`' sys_lib_search_path_spec="$sys_lib_search_path_spec /usr/local/lib" sys_lib_dlsearch_path_spec='/usr/local/lib /lib /usr/lib' ;; dgux*) version_type=linux # correct to gnu/linux during the next big refactor need_lib_prefix=no need_version=no library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname$shared_ext' soname_spec='${libname}${release}${shared_ext}$major' shlibpath_var=LD_LIBRARY_PATH ;; freebsd* | dragonfly*) # DragonFly does not have aout. When/if they implement a new # versioning mechanism, adjust this. if test -x /usr/bin/objformat; then objformat=`/usr/bin/objformat` else case $host_os in freebsd[23].*) objformat=aout ;; *) objformat=elf ;; esac fi version_type=freebsd-$objformat case $version_type in freebsd-elf*) library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext} $libname${shared_ext}' need_version=no need_lib_prefix=no ;; freebsd-*) library_names_spec='${libname}${release}${shared_ext}$versuffix $libname${shared_ext}$versuffix' need_version=yes ;; esac shlibpath_var=LD_LIBRARY_PATH case $host_os in freebsd2.*) shlibpath_overrides_runpath=yes ;; freebsd3.[01]* | freebsdelf3.[01]*) shlibpath_overrides_runpath=yes hardcode_into_libs=yes ;; freebsd3.[2-9]* | freebsdelf3.[2-9]* | \ freebsd4.[0-5] | freebsdelf4.[0-5] | freebsd4.1.1 | freebsdelf4.1.1) shlibpath_overrides_runpath=no hardcode_into_libs=yes ;; *) # from 4.6 on, and DragonFly shlibpath_overrides_runpath=yes hardcode_into_libs=yes ;; esac ;; haiku*) version_type=linux # correct to gnu/linux during the next big refactor need_lib_prefix=no need_version=no dynamic_linker="$host_os runtime_loader" library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}${major} ${libname}${shared_ext}' soname_spec='${libname}${release}${shared_ext}$major' shlibpath_var=LIBRARY_PATH shlibpath_overrides_runpath=yes sys_lib_dlsearch_path_spec='/boot/home/config/lib /boot/common/lib /boot/system/lib' hardcode_into_libs=yes ;; hpux9* | hpux10* | hpux11*) # Give a soname corresponding to the major version so that dld.sl refuses to # link against other versions. version_type=sunos need_lib_prefix=no need_version=no case $host_cpu in ia64*) shrext_cmds='.so' hardcode_into_libs=yes dynamic_linker="$host_os dld.so" shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes # Unless +noenvvar is specified. library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' soname_spec='${libname}${release}${shared_ext}$major' if test "X$HPUX_IA64_MODE" = X32; then sys_lib_search_path_spec="/usr/lib/hpux32 /usr/local/lib/hpux32 /usr/local/lib" else sys_lib_search_path_spec="/usr/lib/hpux64 /usr/local/lib/hpux64" fi sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec ;; hppa*64*) shrext_cmds='.sl' hardcode_into_libs=yes dynamic_linker="$host_os dld.sl" shlibpath_var=LD_LIBRARY_PATH # How should we handle SHLIB_PATH shlibpath_overrides_runpath=yes # Unless +noenvvar is specified. library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' soname_spec='${libname}${release}${shared_ext}$major' sys_lib_search_path_spec="/usr/lib/pa20_64 /usr/ccs/lib/pa20_64" sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec ;; *) shrext_cmds='.sl' dynamic_linker="$host_os dld.sl" shlibpath_var=SHLIB_PATH shlibpath_overrides_runpath=no # +s is required to enable SHLIB_PATH library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' soname_spec='${libname}${release}${shared_ext}$major' ;; esac # HP-UX runs *really* slowly unless shared libraries are mode 555, ... postinstall_cmds='chmod 555 $lib' # or fails outright, so override atomically: install_override_mode=555 ;; interix[3-9]*) version_type=linux # correct to gnu/linux during the next big refactor need_lib_prefix=no need_version=no library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}' soname_spec='${libname}${release}${shared_ext}$major' dynamic_linker='Interix 3.x ld.so.1 (PE, like ELF)' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=no hardcode_into_libs=yes ;; irix5* | irix6* | nonstopux*) case $host_os in nonstopux*) version_type=nonstopux ;; *) if test "$lt_cv_prog_gnu_ld" = yes; then version_type=linux # correct to gnu/linux during the next big refactor else version_type=irix fi ;; esac need_lib_prefix=no need_version=no soname_spec='${libname}${release}${shared_ext}$major' library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${release}${shared_ext} $libname${shared_ext}' case $host_os in irix5* | nonstopux*) libsuff= shlibsuff= ;; *) case $LD in # libtool.m4 will add one of these switches to LD *-32|*"-32 "|*-melf32bsmip|*"-melf32bsmip ") libsuff= shlibsuff= libmagic=32-bit;; *-n32|*"-n32 "|*-melf32bmipn32|*"-melf32bmipn32 ") libsuff=32 shlibsuff=N32 libmagic=N32;; *-64|*"-64 "|*-melf64bmip|*"-melf64bmip ") libsuff=64 shlibsuff=64 libmagic=64-bit;; *) libsuff= shlibsuff= libmagic=never-match;; esac ;; esac shlibpath_var=LD_LIBRARY${shlibsuff}_PATH shlibpath_overrides_runpath=no sys_lib_search_path_spec="/usr/lib${libsuff} /lib${libsuff} /usr/local/lib${libsuff}" sys_lib_dlsearch_path_spec="/usr/lib${libsuff} /lib${libsuff}" hardcode_into_libs=yes ;; # No shared lib support for Linux oldld, aout, or coff. linux*oldld* | linux*aout* | linux*coff*) dynamic_linker=no ;; # This must be glibc/ELF. linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*) version_type=linux # correct to gnu/linux during the next big refactor need_lib_prefix=no need_version=no library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' soname_spec='${libname}${release}${shared_ext}$major' finish_cmds='PATH="\$PATH:/sbin" ldconfig -n $libdir' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=no # Some binutils ld are patched to set DT_RUNPATH if ${lt_cv_shlibpath_overrides_runpath+:} false; then : $as_echo_n "(cached) " >&6 else lt_cv_shlibpath_overrides_runpath=no save_LDFLAGS=$LDFLAGS save_libdir=$libdir eval "libdir=/foo; wl=\"$lt_prog_compiler_wl\"; \ LDFLAGS=\"\$LDFLAGS $hardcode_libdir_flag_spec\"" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : if ($OBJDUMP -p conftest$ac_exeext) 2>/dev/null | grep "RUNPATH.*$libdir" >/dev/null; then : lt_cv_shlibpath_overrides_runpath=yes fi fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LDFLAGS=$save_LDFLAGS libdir=$save_libdir fi shlibpath_overrides_runpath=$lt_cv_shlibpath_overrides_runpath # This implies no fast_install, which is unacceptable. # Some rework will be needed to allow for fast_install # before this can be enabled. hardcode_into_libs=yes # Append ld.so.conf contents to the search path if test -f /etc/ld.so.conf; then lt_ld_extra=`awk '/^include / { system(sprintf("cd /etc; cat %s 2>/dev/null", \$2)); skip = 1; } { if (!skip) print \$0; skip = 0; }' < /etc/ld.so.conf | $SED -e 's/#.*//;/^[ ]*hwcap[ ]/d;s/[:, ]/ /g;s/=[^=]*$//;s/=[^= ]* / /g;s/"//g;/^$/d' | tr '\n' ' '` sys_lib_dlsearch_path_spec="/lib /usr/lib $lt_ld_extra" fi # We used to test for /lib/ld.so.1 and disable shared libraries on # powerpc, because MkLinux only supported shared libraries with the # GNU dynamic linker. Since this was broken with cross compilers, # most powerpc-linux boxes support dynamic linking these days and # people can always --disable-shared, the test was removed, and we # assume the GNU/Linux dynamic linker is in use. dynamic_linker='GNU/Linux ld.so' ;; netbsdelf*-gnu) version_type=linux need_lib_prefix=no need_version=no library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}' soname_spec='${libname}${release}${shared_ext}$major' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=no hardcode_into_libs=yes dynamic_linker='NetBSD ld.elf_so' ;; netbsd*) version_type=sunos need_lib_prefix=no need_version=no if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${shared_ext}$versuffix' finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir' dynamic_linker='NetBSD (a.out) ld.so' else library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}' soname_spec='${libname}${release}${shared_ext}$major' dynamic_linker='NetBSD ld.elf_so' fi shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes hardcode_into_libs=yes ;; newsos6) version_type=linux # correct to gnu/linux during the next big refactor library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes ;; *nto* | *qnx*) version_type=qnx need_lib_prefix=no need_version=no library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' soname_spec='${libname}${release}${shared_ext}$major' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=no hardcode_into_libs=yes dynamic_linker='ldqnx.so' ;; openbsd*) version_type=sunos sys_lib_dlsearch_path_spec="/usr/lib" need_lib_prefix=no # Some older versions of OpenBSD (3.3 at least) *do* need versioned libs. case $host_os in openbsd3.3 | openbsd3.3.*) need_version=yes ;; *) need_version=no ;; esac library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${shared_ext}$versuffix' finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir' shlibpath_var=LD_LIBRARY_PATH if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then case $host_os in openbsd2.[89] | openbsd2.[89].*) shlibpath_overrides_runpath=no ;; *) shlibpath_overrides_runpath=yes ;; esac else shlibpath_overrides_runpath=yes fi ;; os2*) libname_spec='$name' shrext_cmds=".dll" need_lib_prefix=no library_names_spec='$libname${shared_ext} $libname.a' dynamic_linker='OS/2 ld.exe' shlibpath_var=LIBPATH ;; osf3* | osf4* | osf5*) version_type=osf need_lib_prefix=no need_version=no soname_spec='${libname}${release}${shared_ext}$major' library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' shlibpath_var=LD_LIBRARY_PATH sys_lib_search_path_spec="/usr/shlib /usr/ccs/lib /usr/lib/cmplrs/cc /usr/lib /usr/local/lib /var/shlib" sys_lib_dlsearch_path_spec="$sys_lib_search_path_spec" ;; rdos*) dynamic_linker=no ;; solaris*) version_type=linux # correct to gnu/linux during the next big refactor need_lib_prefix=no need_version=no library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' soname_spec='${libname}${release}${shared_ext}$major' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes hardcode_into_libs=yes # ldd complains unless libraries are executable postinstall_cmds='chmod +x $lib' ;; sunos4*) version_type=sunos library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${shared_ext}$versuffix' finish_cmds='PATH="\$PATH:/usr/etc" ldconfig $libdir' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes if test "$with_gnu_ld" = yes; then need_lib_prefix=no fi need_version=yes ;; sysv4 | sysv4.3*) version_type=linux # correct to gnu/linux during the next big refactor library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' soname_spec='${libname}${release}${shared_ext}$major' shlibpath_var=LD_LIBRARY_PATH case $host_vendor in sni) shlibpath_overrides_runpath=no need_lib_prefix=no runpath_var=LD_RUN_PATH ;; siemens) need_lib_prefix=no ;; motorola) need_lib_prefix=no need_version=no shlibpath_overrides_runpath=no sys_lib_search_path_spec='/lib /usr/lib /usr/ccs/lib' ;; esac ;; sysv4*MP*) if test -d /usr/nec ;then version_type=linux # correct to gnu/linux during the next big refactor library_names_spec='$libname${shared_ext}.$versuffix $libname${shared_ext}.$major $libname${shared_ext}' soname_spec='$libname${shared_ext}.$major' shlibpath_var=LD_LIBRARY_PATH fi ;; sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX* | sysv4*uw2*) version_type=freebsd-elf need_lib_prefix=no need_version=no library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext} $libname${shared_ext}' soname_spec='${libname}${release}${shared_ext}$major' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes hardcode_into_libs=yes if test "$with_gnu_ld" = yes; then sys_lib_search_path_spec='/usr/local/lib /usr/gnu/lib /usr/ccs/lib /usr/lib /lib' else sys_lib_search_path_spec='/usr/ccs/lib /usr/lib' case $host_os in sco3.2v5*) sys_lib_search_path_spec="$sys_lib_search_path_spec /lib" ;; esac fi sys_lib_dlsearch_path_spec='/usr/lib' ;; tpf*) # TPF is a cross-target only. Preferred cross-host = GNU/Linux. version_type=linux # correct to gnu/linux during the next big refactor need_lib_prefix=no need_version=no library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=no hardcode_into_libs=yes ;; uts4*) version_type=linux # correct to gnu/linux during the next big refactor library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' soname_spec='${libname}${release}${shared_ext}$major' shlibpath_var=LD_LIBRARY_PATH ;; *) dynamic_linker=no ;; esac { $as_echo "$as_me:${as_lineno-$LINENO}: result: $dynamic_linker" >&5 $as_echo "$dynamic_linker" >&6; } test "$dynamic_linker" = no && can_build_shared=no variables_saved_for_relink="PATH $shlibpath_var $runpath_var" if test "$GCC" = yes; then variables_saved_for_relink="$variables_saved_for_relink GCC_EXEC_PREFIX COMPILER_PATH LIBRARY_PATH" fi if test "${lt_cv_sys_lib_search_path_spec+set}" = set; then sys_lib_search_path_spec="$lt_cv_sys_lib_search_path_spec" fi if test "${lt_cv_sys_lib_dlsearch_path_spec+set}" = set; then sys_lib_dlsearch_path_spec="$lt_cv_sys_lib_dlsearch_path_spec" fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking how to hardcode library paths into programs" >&5 $as_echo_n "checking how to hardcode library paths into programs... " >&6; } hardcode_action= if test -n "$hardcode_libdir_flag_spec" || test -n "$runpath_var" || test "X$hardcode_automatic" = "Xyes" ; then # We can hardcode non-existent directories. if test "$hardcode_direct" != no && # If the only mechanism to avoid hardcoding is shlibpath_var, we # have to relink, otherwise we might link with an installed library # when we should be linking with a yet-to-be-installed one ## test "$_LT_TAGVAR(hardcode_shlibpath_var, )" != no && test "$hardcode_minus_L" != no; then # Linking always hardcodes the temporary library directory. hardcode_action=relink else # We can link without hardcoding, and we can hardcode nonexisting dirs. hardcode_action=immediate fi else # We cannot hardcode anything, or else we can only hardcode existing # directories. hardcode_action=unsupported fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $hardcode_action" >&5 $as_echo "$hardcode_action" >&6; } if test "$hardcode_action" = relink || test "$inherit_rpath" = yes; then # Fast installation is not supported enable_fast_install=no elif test "$shlibpath_overrides_runpath" = yes || test "$enable_shared" = no; then # Fast installation is not necessary enable_fast_install=needless fi if test "x$enable_dlopen" != xyes; then enable_dlopen=unknown enable_dlopen_self=unknown enable_dlopen_self_static=unknown else lt_cv_dlopen=no lt_cv_dlopen_libs= case $host_os in beos*) lt_cv_dlopen="load_add_on" lt_cv_dlopen_libs= lt_cv_dlopen_self=yes ;; mingw* | pw32* | cegcc*) lt_cv_dlopen="LoadLibrary" lt_cv_dlopen_libs= ;; cygwin*) lt_cv_dlopen="dlopen" lt_cv_dlopen_libs= ;; darwin*) # if libdl is installed we need to link against it { $as_echo "$as_me:${as_lineno-$LINENO}: checking for dlopen in -ldl" >&5 $as_echo_n "checking for dlopen in -ldl... " >&6; } if ${ac_cv_lib_dl_dlopen+:} false; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-ldl $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char dlopen (); int main () { return dlopen (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ac_cv_lib_dl_dlopen=yes else ac_cv_lib_dl_dlopen=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_dl_dlopen" >&5 $as_echo "$ac_cv_lib_dl_dlopen" >&6; } if test "x$ac_cv_lib_dl_dlopen" = xyes; then : lt_cv_dlopen="dlopen" lt_cv_dlopen_libs="-ldl" else lt_cv_dlopen="dyld" lt_cv_dlopen_libs= lt_cv_dlopen_self=yes fi ;; *) ac_fn_c_check_func "$LINENO" "shl_load" "ac_cv_func_shl_load" if test "x$ac_cv_func_shl_load" = xyes; then : lt_cv_dlopen="shl_load" else { $as_echo "$as_me:${as_lineno-$LINENO}: checking for shl_load in -ldld" >&5 $as_echo_n "checking for shl_load in -ldld... " >&6; } if ${ac_cv_lib_dld_shl_load+:} false; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-ldld $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char shl_load (); int main () { return shl_load (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ac_cv_lib_dld_shl_load=yes else ac_cv_lib_dld_shl_load=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_dld_shl_load" >&5 $as_echo "$ac_cv_lib_dld_shl_load" >&6; } if test "x$ac_cv_lib_dld_shl_load" = xyes; then : lt_cv_dlopen="shl_load" lt_cv_dlopen_libs="-ldld" else ac_fn_c_check_func "$LINENO" "dlopen" "ac_cv_func_dlopen" if test "x$ac_cv_func_dlopen" = xyes; then : lt_cv_dlopen="dlopen" else { $as_echo "$as_me:${as_lineno-$LINENO}: checking for dlopen in -ldl" >&5 $as_echo_n "checking for dlopen in -ldl... " >&6; } if ${ac_cv_lib_dl_dlopen+:} false; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-ldl $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char dlopen (); int main () { return dlopen (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ac_cv_lib_dl_dlopen=yes else ac_cv_lib_dl_dlopen=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_dl_dlopen" >&5 $as_echo "$ac_cv_lib_dl_dlopen" >&6; } if test "x$ac_cv_lib_dl_dlopen" = xyes; then : lt_cv_dlopen="dlopen" lt_cv_dlopen_libs="-ldl" else { $as_echo "$as_me:${as_lineno-$LINENO}: checking for dlopen in -lsvld" >&5 $as_echo_n "checking for dlopen in -lsvld... " >&6; } if ${ac_cv_lib_svld_dlopen+:} false; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-lsvld $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char dlopen (); int main () { return dlopen (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ac_cv_lib_svld_dlopen=yes else ac_cv_lib_svld_dlopen=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_svld_dlopen" >&5 $as_echo "$ac_cv_lib_svld_dlopen" >&6; } if test "x$ac_cv_lib_svld_dlopen" = xyes; then : lt_cv_dlopen="dlopen" lt_cv_dlopen_libs="-lsvld" else { $as_echo "$as_me:${as_lineno-$LINENO}: checking for dld_link in -ldld" >&5 $as_echo_n "checking for dld_link in -ldld... " >&6; } if ${ac_cv_lib_dld_dld_link+:} false; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-ldld $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char dld_link (); int main () { return dld_link (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ac_cv_lib_dld_dld_link=yes else ac_cv_lib_dld_dld_link=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_dld_dld_link" >&5 $as_echo "$ac_cv_lib_dld_dld_link" >&6; } if test "x$ac_cv_lib_dld_dld_link" = xyes; then : lt_cv_dlopen="dld_link" lt_cv_dlopen_libs="-ldld" fi fi fi fi fi fi ;; esac if test "x$lt_cv_dlopen" != xno; then enable_dlopen=yes else enable_dlopen=no fi case $lt_cv_dlopen in dlopen) save_CPPFLAGS="$CPPFLAGS" test "x$ac_cv_header_dlfcn_h" = xyes && CPPFLAGS="$CPPFLAGS -DHAVE_DLFCN_H" save_LDFLAGS="$LDFLAGS" wl=$lt_prog_compiler_wl eval LDFLAGS=\"\$LDFLAGS $export_dynamic_flag_spec\" save_LIBS="$LIBS" LIBS="$lt_cv_dlopen_libs $LIBS" { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether a program can dlopen itself" >&5 $as_echo_n "checking whether a program can dlopen itself... " >&6; } if ${lt_cv_dlopen_self+:} false; then : $as_echo_n "(cached) " >&6 else if test "$cross_compiling" = yes; then : lt_cv_dlopen_self=cross else lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 lt_status=$lt_dlunknown cat > conftest.$ac_ext <<_LT_EOF #line $LINENO "configure" #include "confdefs.h" #if HAVE_DLFCN_H #include #endif #include #ifdef RTLD_GLOBAL # define LT_DLGLOBAL RTLD_GLOBAL #else # ifdef DL_GLOBAL # define LT_DLGLOBAL DL_GLOBAL # else # define LT_DLGLOBAL 0 # endif #endif /* We may have to define LT_DLLAZY_OR_NOW in the command line if we find out it does not work in some platform. */ #ifndef LT_DLLAZY_OR_NOW # ifdef RTLD_LAZY # define LT_DLLAZY_OR_NOW RTLD_LAZY # else # ifdef DL_LAZY # define LT_DLLAZY_OR_NOW DL_LAZY # else # ifdef RTLD_NOW # define LT_DLLAZY_OR_NOW RTLD_NOW # else # ifdef DL_NOW # define LT_DLLAZY_OR_NOW DL_NOW # else # define LT_DLLAZY_OR_NOW 0 # endif # endif # endif # endif #endif /* When -fvisbility=hidden is used, assume the code has been annotated correspondingly for the symbols needed. */ #if defined(__GNUC__) && (((__GNUC__ == 3) && (__GNUC_MINOR__ >= 3)) || (__GNUC__ > 3)) int fnord () __attribute__((visibility("default"))); #endif int fnord () { return 42; } int main () { void *self = dlopen (0, LT_DLGLOBAL|LT_DLLAZY_OR_NOW); int status = $lt_dlunknown; if (self) { if (dlsym (self,"fnord")) status = $lt_dlno_uscore; else { if (dlsym( self,"_fnord")) status = $lt_dlneed_uscore; else puts (dlerror ()); } /* dlclose (self); */ } else puts (dlerror ()); return status; } _LT_EOF if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_link\""; } >&5 (eval $ac_link) 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } && test -s conftest${ac_exeext} 2>/dev/null; then (./conftest; exit; ) >&5 2>/dev/null lt_status=$? case x$lt_status in x$lt_dlno_uscore) lt_cv_dlopen_self=yes ;; x$lt_dlneed_uscore) lt_cv_dlopen_self=yes ;; x$lt_dlunknown|x*) lt_cv_dlopen_self=no ;; esac else : # compilation failed lt_cv_dlopen_self=no fi fi rm -fr conftest* fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_dlopen_self" >&5 $as_echo "$lt_cv_dlopen_self" >&6; } if test "x$lt_cv_dlopen_self" = xyes; then wl=$lt_prog_compiler_wl eval LDFLAGS=\"\$LDFLAGS $lt_prog_compiler_static\" { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether a statically linked program can dlopen itself" >&5 $as_echo_n "checking whether a statically linked program can dlopen itself... " >&6; } if ${lt_cv_dlopen_self_static+:} false; then : $as_echo_n "(cached) " >&6 else if test "$cross_compiling" = yes; then : lt_cv_dlopen_self_static=cross else lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 lt_status=$lt_dlunknown cat > conftest.$ac_ext <<_LT_EOF #line $LINENO "configure" #include "confdefs.h" #if HAVE_DLFCN_H #include #endif #include #ifdef RTLD_GLOBAL # define LT_DLGLOBAL RTLD_GLOBAL #else # ifdef DL_GLOBAL # define LT_DLGLOBAL DL_GLOBAL # else # define LT_DLGLOBAL 0 # endif #endif /* We may have to define LT_DLLAZY_OR_NOW in the command line if we find out it does not work in some platform. */ #ifndef LT_DLLAZY_OR_NOW # ifdef RTLD_LAZY # define LT_DLLAZY_OR_NOW RTLD_LAZY # else # ifdef DL_LAZY # define LT_DLLAZY_OR_NOW DL_LAZY # else # ifdef RTLD_NOW # define LT_DLLAZY_OR_NOW RTLD_NOW # else # ifdef DL_NOW # define LT_DLLAZY_OR_NOW DL_NOW # else # define LT_DLLAZY_OR_NOW 0 # endif # endif # endif # endif #endif /* When -fvisbility=hidden is used, assume the code has been annotated correspondingly for the symbols needed. */ #if defined(__GNUC__) && (((__GNUC__ == 3) && (__GNUC_MINOR__ >= 3)) || (__GNUC__ > 3)) int fnord () __attribute__((visibility("default"))); #endif int fnord () { return 42; } int main () { void *self = dlopen (0, LT_DLGLOBAL|LT_DLLAZY_OR_NOW); int status = $lt_dlunknown; if (self) { if (dlsym (self,"fnord")) status = $lt_dlno_uscore; else { if (dlsym( self,"_fnord")) status = $lt_dlneed_uscore; else puts (dlerror ()); } /* dlclose (self); */ } else puts (dlerror ()); return status; } _LT_EOF if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_link\""; } >&5 (eval $ac_link) 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } && test -s conftest${ac_exeext} 2>/dev/null; then (./conftest; exit; ) >&5 2>/dev/null lt_status=$? case x$lt_status in x$lt_dlno_uscore) lt_cv_dlopen_self_static=yes ;; x$lt_dlneed_uscore) lt_cv_dlopen_self_static=yes ;; x$lt_dlunknown|x*) lt_cv_dlopen_self_static=no ;; esac else : # compilation failed lt_cv_dlopen_self_static=no fi fi rm -fr conftest* fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_dlopen_self_static" >&5 $as_echo "$lt_cv_dlopen_self_static" >&6; } fi CPPFLAGS="$save_CPPFLAGS" LDFLAGS="$save_LDFLAGS" LIBS="$save_LIBS" ;; esac case $lt_cv_dlopen_self in yes|no) enable_dlopen_self=$lt_cv_dlopen_self ;; *) enable_dlopen_self=unknown ;; esac case $lt_cv_dlopen_self_static in yes|no) enable_dlopen_self_static=$lt_cv_dlopen_self_static ;; *) enable_dlopen_self_static=unknown ;; esac fi striplib= old_striplib= { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether stripping libraries is possible" >&5 $as_echo_n "checking whether stripping libraries is possible... " >&6; } if test -n "$STRIP" && $STRIP -V 2>&1 | $GREP "GNU strip" >/dev/null; then test -z "$old_striplib" && old_striplib="$STRIP --strip-debug" test -z "$striplib" && striplib="$STRIP --strip-unneeded" { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } else # FIXME - insert some real tests, host_os isn't really good enough case $host_os in darwin*) if test -n "$STRIP" ; then striplib="$STRIP -x" old_striplib="$STRIP -S" { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi ;; *) { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } ;; esac fi # Report which library types will actually be built { $as_echo "$as_me:${as_lineno-$LINENO}: checking if libtool supports shared libraries" >&5 $as_echo_n "checking if libtool supports shared libraries... " >&6; } { $as_echo "$as_me:${as_lineno-$LINENO}: result: $can_build_shared" >&5 $as_echo "$can_build_shared" >&6; } { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to build shared libraries" >&5 $as_echo_n "checking whether to build shared libraries... " >&6; } test "$can_build_shared" = "no" && enable_shared=no # On AIX, shared libraries and static libraries use the same namespace, and # are all built from PIC. case $host_os in aix3*) test "$enable_shared" = yes && enable_static=no if test -n "$RANLIB"; then archive_cmds="$archive_cmds~\$RANLIB \$lib" postinstall_cmds='$RANLIB $lib' fi ;; aix[4-9]*) if test "$host_cpu" != ia64 && test "$aix_use_runtimelinking" = no ; then test "$enable_shared" = yes && enable_static=no fi ;; esac { $as_echo "$as_me:${as_lineno-$LINENO}: result: $enable_shared" >&5 $as_echo "$enable_shared" >&6; } { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to build static libraries" >&5 $as_echo_n "checking whether to build static libraries... " >&6; } # Make sure either enable_shared or enable_static is yes. test "$enable_shared" = yes || enable_static=yes { $as_echo "$as_me:${as_lineno-$LINENO}: result: $enable_static" >&5 $as_echo "$enable_static" >&6; } fi ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu CC="$lt_save_CC" ac_config_commands="$ac_config_commands libtool" # Only expand once: for ac_header in $ac_header_list do : as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh` ac_fn_c_check_header_compile "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default " if eval test \"x\$"$as_ac_Header"\" = x"yes"; then : cat >>confdefs.h <<_ACEOF #define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1 _ACEOF fi done # on Darwin, these headers are interdependent, according to autoconf.info for ac_header in sys/socket.h do : ac_fn_c_check_header_compile "$LINENO" "sys/socket.h" "ac_cv_header_sys_socket_h" " #include #include #include " if test "x$ac_cv_header_sys_socket_h" = xyes; then : cat >>confdefs.h <<_ACEOF #define HAVE_SYS_SOCKET_H 1 _ACEOF fi done for ac_header in net/if.h do : ac_fn_c_check_header_compile "$LINENO" "net/if.h" "ac_cv_header_net_if_h" " #include #include #include #ifdef HAVE_SYS_SOCKET_H # include #endif " if test "x$ac_cv_header_net_if_h" = xyes; then : cat >>confdefs.h <<_ACEOF #define HAVE_NET_IF_H 1 _ACEOF fi done # Autoconf has a handy macro for this, since it tends to have dependencies for ac_header in sys/types.h netinet/in.h arpa/nameser.h netdb.h resolv.h do : as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh` ac_fn_c_check_header_compile "$LINENO" "$ac_header" "$as_ac_Header" "#ifdef HAVE_SYS_TYPES_H # include #endif #ifdef HAVE_NETINET_IN_H # include /* inet_ functions / structs */ #endif #ifdef HAVE_ARPA_NAMESER_H # include /* DNS HEADER struct */ #endif #ifdef HAVE_NETDB_H # include #endif " if eval test \"x\$"$as_ac_Header"\" = x"yes"; then : cat >>confdefs.h <<_ACEOF #define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1 _ACEOF fi done # Check whether --enable-compiler-optimisations was given. if test "${enable_compiler_optimisations+set}" = set; then : enableval=$enable_compiler_optimisations; if test "x$enable_compiler_optimisations" = "xno"; then CFLAGS=`echo "$CFLAGS" | sed -e "s/ -O[1-9]*\b/ -O0/g"` fi fi # Check whether --enable-compiler-coverage was given. if test "${enable_compiler_coverage+set}" = set; then : enableval=$enable_compiler_coverage; if test "x$enable_compiler_coverage" = "xyes"; then if test "x$GCC" = "xyes"; then CFLAGS="$CFLAGS -fprofile-arcs -ftest-coverage" fi fi fi official_release=yes if test "x$official_release" = xyes; then OFFICIAL_RELEASE_TRUE= OFFICIAL_RELEASE_FALSE='#' else OFFICIAL_RELEASE_TRUE='#' OFFICIAL_RELEASE_FALSE= fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking to see if compiler understands " >&5 $as_echo_n "checking to see if compiler understands ... " >&6; } save_CFLAGS="$CFLAGS" save_CXXFLAGS="$CXXFLAGS" CFLAGS="$CFLAGS " CXXFLAGS="$CXXFLAGS " cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : flag_ok=yes else flag_ok=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext CFLAGS="$save_CFLAGS" CXXFLAGS="$save_CXXFLAGS" if test "X$flag_ok" = Xyes ; then true else true fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $flag_ok" >&5 $as_echo "$flag_ok" >&6; } tp_warnings="" for tp_flag in all \ extra \ declaration-after-statement \ shadow \ strict-prototypes \ missing-declarations \ missing-prototypes \ sign-compare \ nested-externs \ pointer-arith \ format-security \ init-self; do { $as_echo "$as_me:${as_lineno-$LINENO}: checking to see if compiler understands -W$tp_flag" >&5 $as_echo_n "checking to see if compiler understands -W$tp_flag... " >&6; } save_CFLAGS="$CFLAGS" save_CXXFLAGS="$CXXFLAGS" CFLAGS="$CFLAGS -W$tp_flag" CXXFLAGS="$CXXFLAGS -W$tp_flag" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : flag_ok=yes else flag_ok=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext CFLAGS="$save_CFLAGS" CXXFLAGS="$save_CXXFLAGS" if test "X$flag_ok" = Xyes ; then tp_warnings="$tp_warnings -W$tp_flag" true else true fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $flag_ok" >&5 $as_echo "$flag_ok" >&6; } done tp_error_flags="-Werror" { $as_echo "$as_me:${as_lineno-$LINENO}: checking to see if compiler understands -Werror" >&5 $as_echo_n "checking to see if compiler understands -Werror... " >&6; } save_CFLAGS="$CFLAGS" save_CXXFLAGS="$CXXFLAGS" CFLAGS="$CFLAGS -Werror" CXXFLAGS="$CXXFLAGS -Werror" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : flag_ok=yes else flag_ok=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext CFLAGS="$save_CFLAGS" CXXFLAGS="$save_CXXFLAGS" if test "X$flag_ok" = Xyes ; then tp_werror=yes true else tp_werror=no true fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $flag_ok" >&5 $as_echo "$flag_ok" >&6; } for tp_flag in missing-field-initializers \ deprecated-declarations \ unused-parameter; do { $as_echo "$as_me:${as_lineno-$LINENO}: checking to see if compiler understands -Wno-$tp_flag" >&5 $as_echo_n "checking to see if compiler understands -Wno-$tp_flag... " >&6; } save_CFLAGS="$CFLAGS" save_CXXFLAGS="$CXXFLAGS" CFLAGS="$CFLAGS -Wno-$tp_flag" CXXFLAGS="$CXXFLAGS -Wno-$tp_flag" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : flag_ok=yes else flag_ok=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext CFLAGS="$save_CFLAGS" CXXFLAGS="$save_CXXFLAGS" if test "X$flag_ok" = Xyes ; then tp_warnings="$tp_warnings -Wno-$tp_flag" true else true fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $flag_ok" >&5 $as_echo "$flag_ok" >&6; } { $as_echo "$as_me:${as_lineno-$LINENO}: checking to see if compiler understands -Wno-error=$tp_flag" >&5 $as_echo_n "checking to see if compiler understands -Wno-error=$tp_flag... " >&6; } save_CFLAGS="$CFLAGS" save_CXXFLAGS="$CXXFLAGS" CFLAGS="$CFLAGS -Wno-error=$tp_flag" CXXFLAGS="$CXXFLAGS -Wno-error=$tp_flag" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : flag_ok=yes else flag_ok=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext CFLAGS="$save_CFLAGS" CXXFLAGS="$save_CXXFLAGS" if test "X$flag_ok" = Xyes ; then tp_error_flags="$tp_error_flags -Wno-error=$tp_flag" true else tp_werror=no true fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $flag_ok" >&5 $as_echo "$flag_ok" >&6; } done # Check whether --enable-Werror was given. if test "${enable_Werror+set}" = set; then : enableval=$enable_Werror; tp_werror=$enableval else : fi if test "x$tp_werror" = xyes && test "x$official_release" = xno; then ERROR_CFLAGS="$tp_warnings $tp_error_flags" else ERROR_CFLAGS="$tp_warnings" fi #------------------------------------------------------------ # Detect Operating system based on $host #------------------------------------------------------------ { $as_echo "$as_me:${as_lineno-$LINENO}: checking operating system" >&5 $as_echo_n "checking operating system... " >&6; } case "$host" in *-*-*mingw*|*-*-*cygwin*) platform=win32 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $platform" >&5 $as_echo "$platform" >&6; } ;; *) platform=unix { $as_echo "$as_me:${as_lineno-$LINENO}: result: $platform" >&5 $as_echo "$platform" >&6; } ;; esac if test "$platform" = "win32"; then WINDOWS_TRUE= WINDOWS_FALSE='#' else WINDOWS_TRUE='#' WINDOWS_FALSE= fi # ----------------------------------------------------------- # Make CA certificates path configurable # Stolen from GIO's TLS # ----------------------------------------------------------- { $as_echo "$as_me:${as_lineno-$LINENO}: checking location of system Certificate Authority list" >&5 $as_echo_n "checking location of system Certificate Authority list... " >&6; } # Check whether --with-ca-certificates was given. if test "${with_ca_certificates+set}" = set; then : withval=$with_ca_certificates; fi if test "$with_ca_certificates" = "no"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: disabled" >&5 $as_echo "disabled" >&6; } else if test -z "$with_ca_certificates"; then for f in /etc/pki/tls/certs/ca-bundle.crt \ /etc/ssl/certs/ca-certificates.crt; do if test -f "$f"; then with_ca_certificates="$f" fi done if test -z "$with_ca_certificates"; then as_fn_error $? "could not find. Use --with-ca-certificates=path to set, or --without-ca-certificates to disable" "$LINENO" 5 fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $with_ca_certificates" >&5 $as_echo "$with_ca_certificates" >&6; } cat >>confdefs.h <<_ACEOF #define GTLS_SYSTEM_CA_CERTIFICATES "$with_ca_certificates" _ACEOF fi if test -n "$with_ca_certificates"; then if ! test -f "$with_ca_certificates"; then { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: Specified certificate authority file '$with_ca_certificates' does not exist" >&5 $as_echo "$as_me: WARNING: Specified certificate authority file '$with_ca_certificates' does not exist" >&2;} fi fi # Check whether --enable-debug was given. if test "${enable_debug+set}" = set; then : enableval=$enable_debug; enable_debug=$enableval else enable_debug=yes fi # Gabble is version x.y.z - disable coding style checks by default # Check whether --enable-coding-style-checks was given. if test "${enable_coding_style_checks+set}" = set; then : enableval=$enable_coding_style_checks; ENABLE_CODING_STYLE_CHECKS=$enableval else ENABLE_CODING_STYLE_CHECKS=no fi if test x$enable_debug = xyes; then $as_echo "#define ENABLE_DEBUG /**/" >>confdefs.h fi if test "x$enable_debug" = xyes; then ENABLE_DEBUG_TRUE= ENABLE_DEBUG_FALSE='#' else ENABLE_DEBUG_TRUE='#' ENABLE_DEBUG_FALSE= fi # Check whether --enable-installed-tests was given. if test "${enable_installed_tests+set}" = set; then : enableval=$enable_installed_tests; installed_tests=$enableval else installed_tests=no fi if test x$installed_tests = xyes; then $as_echo "#define ENABLE_INSTALLED_TESTS /**/" >>confdefs.h fi if test "x$installed_tests" = xyes; then ENABLE_INSTALLED_TESTS_TRUE= ENABLE_INSTALLED_TESTS_FALSE='#' else ENABLE_INSTALLED_TESTS_TRUE='#' ENABLE_INSTALLED_TESTS_FALSE= fi gabbletestsdir=${libdir}/telepathy-gabble-tests # Check whether --enable-is-a-phone was given. if test "${enable_is_a_phone+set}" = set; then : enableval=$enable_is_a_phone; is_a_phone=$enableval else is_a_phone=no fi if test x$is_a_phone = xyes; then $as_echo "#define CLIENT_TYPE \"phone\"" >>confdefs.h CLIENT_TYPE=phone else $as_echo "#define CLIENT_TYPE \"pc\"" >>confdefs.h CLIENT_TYPE=pc fi # whether to assert when g_critical() is used # Check whether --enable-fatal-criticals was given. if test "${enable_fatal_criticals+set}" = set; then : enableval=$enable_fatal_criticals; fatal_criticals=$enableval else fatal_criticals=yes fi if test x$fatal_criticals = xyes; then $as_echo "#define ENABLE_FATAL_CRITICALS /**/" >>confdefs.h fi if test "x$fatal_criticals" = xyes; then ENABLE_FATAL_CRITICALS_TRUE= ENABLE_FATAL_CRITICALS_FALSE='#' else ENABLE_FATAL_CRITICALS_TRUE='#' ENABLE_FATAL_CRITICALS_FALSE= fi # Check whether --enable-gtk-doc was given. if test "${enable_gtk_doc+set}" = set; then : enableval=$enable_gtk_doc; enable_gtk_doc=yes else enable_gtk_doc=no fi if test "x$enable_gtk_doc" = xyes; then ENABLE_GTK_DOC_TRUE= ENABLE_GTK_DOC_FALSE='#' else ENABLE_GTK_DOC_TRUE='#' ENABLE_GTK_DOC_FALSE= fi if test "x$ac_cv_env_PKG_CONFIG_set" != "xset"; then if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}pkg-config", so it can be a program name with args. set dummy ${ac_tool_prefix}pkg-config; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_path_PKG_CONFIG+:} false; then : $as_echo_n "(cached) " >&6 else case $PKG_CONFIG in [\\/]* | ?:[\\/]*) ac_cv_path_PKG_CONFIG="$PKG_CONFIG" # Let the user override the test with a path. ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_path_PKG_CONFIG="$as_dir/$ac_word$ac_exec_ext" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS ;; esac fi PKG_CONFIG=$ac_cv_path_PKG_CONFIG if test -n "$PKG_CONFIG"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $PKG_CONFIG" >&5 $as_echo "$PKG_CONFIG" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$ac_cv_path_PKG_CONFIG"; then ac_pt_PKG_CONFIG=$PKG_CONFIG # Extract the first word of "pkg-config", so it can be a program name with args. set dummy pkg-config; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_path_ac_pt_PKG_CONFIG+:} false; then : $as_echo_n "(cached) " >&6 else case $ac_pt_PKG_CONFIG in [\\/]* | ?:[\\/]*) ac_cv_path_ac_pt_PKG_CONFIG="$ac_pt_PKG_CONFIG" # Let the user override the test with a path. ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_path_ac_pt_PKG_CONFIG="$as_dir/$ac_word$ac_exec_ext" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS ;; esac fi ac_pt_PKG_CONFIG=$ac_cv_path_ac_pt_PKG_CONFIG if test -n "$ac_pt_PKG_CONFIG"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_pt_PKG_CONFIG" >&5 $as_echo "$ac_pt_PKG_CONFIG" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test "x$ac_pt_PKG_CONFIG" = x; then PKG_CONFIG="" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac PKG_CONFIG=$ac_pt_PKG_CONFIG fi else PKG_CONFIG="$ac_cv_path_PKG_CONFIG" fi fi if test -n "$PKG_CONFIG"; then _pkg_min_version=0.9.0 { $as_echo "$as_me:${as_lineno-$LINENO}: checking pkg-config is at least version $_pkg_min_version" >&5 $as_echo_n "checking pkg-config is at least version $_pkg_min_version... " >&6; } if $PKG_CONFIG --atleast-pkgconfig-version $_pkg_min_version; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } PKG_CONFIG="" fi fi pkg_failed=no { $as_echo "$as_me:${as_lineno-$LINENO}: checking for GLIB" >&5 $as_echo_n "checking for GLIB... " >&6; } if test -n "$GLIB_CFLAGS"; then pkg_cv_GLIB_CFLAGS="$GLIB_CFLAGS" elif test -n "$PKG_CONFIG"; then if test -n "$PKG_CONFIG" && \ { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"glib-2.0 >= 2.32, gobject-2.0, gthread-2.0, gio-2.0\""; } >&5 ($PKG_CONFIG --exists --print-errors "glib-2.0 >= 2.32, gobject-2.0, gthread-2.0, gio-2.0") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then pkg_cv_GLIB_CFLAGS=`$PKG_CONFIG --cflags "glib-2.0 >= 2.32, gobject-2.0, gthread-2.0, gio-2.0" 2>/dev/null` test "x$?" != "x0" && pkg_failed=yes else pkg_failed=yes fi else pkg_failed=untried fi if test -n "$GLIB_LIBS"; then pkg_cv_GLIB_LIBS="$GLIB_LIBS" elif test -n "$PKG_CONFIG"; then if test -n "$PKG_CONFIG" && \ { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"glib-2.0 >= 2.32, gobject-2.0, gthread-2.0, gio-2.0\""; } >&5 ($PKG_CONFIG --exists --print-errors "glib-2.0 >= 2.32, gobject-2.0, gthread-2.0, gio-2.0") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then pkg_cv_GLIB_LIBS=`$PKG_CONFIG --libs "glib-2.0 >= 2.32, gobject-2.0, gthread-2.0, gio-2.0" 2>/dev/null` test "x$?" != "x0" && pkg_failed=yes else pkg_failed=yes fi else pkg_failed=untried fi if test $pkg_failed = yes; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then _pkg_short_errors_supported=yes else _pkg_short_errors_supported=no fi if test $_pkg_short_errors_supported = yes; then GLIB_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "glib-2.0 >= 2.32, gobject-2.0, gthread-2.0, gio-2.0" 2>&1` else GLIB_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "glib-2.0 >= 2.32, gobject-2.0, gthread-2.0, gio-2.0" 2>&1` fi # Put the nasty error message in config.log where it belongs echo "$GLIB_PKG_ERRORS" >&5 as_fn_error $? "Package requirements (glib-2.0 >= 2.32, gobject-2.0, gthread-2.0, gio-2.0) were not met: $GLIB_PKG_ERRORS Consider adjusting the PKG_CONFIG_PATH environment variable if you installed software in a non-standard prefix. Alternatively, you may set the environment variables GLIB_CFLAGS and GLIB_LIBS to avoid the need to call pkg-config. See the pkg-config man page for more details." "$LINENO" 5 elif test $pkg_failed = untried; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "The pkg-config script could not be found or is too old. Make sure it is in your PATH or set the PKG_CONFIG environment variable to the full path to pkg-config. Alternatively, you may set the environment variables GLIB_CFLAGS and GLIB_LIBS to avoid the need to call pkg-config. See the pkg-config man page for more details. To get pkg-config, see . See \`config.log' for more details" "$LINENO" 5; } else GLIB_CFLAGS=$pkg_cv_GLIB_CFLAGS GLIB_LIBS=$pkg_cv_GLIB_LIBS { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } fi pkg_failed=no { $as_echo "$as_me:${as_lineno-$LINENO}: checking for GMODULE" >&5 $as_echo_n "checking for GMODULE... " >&6; } if test -n "$GMODULE_CFLAGS"; then pkg_cv_GMODULE_CFLAGS="$GMODULE_CFLAGS" elif test -n "$PKG_CONFIG"; then if test -n "$PKG_CONFIG" && \ { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"gmodule-2.0 >= 2.32\""; } >&5 ($PKG_CONFIG --exists --print-errors "gmodule-2.0 >= 2.32") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then pkg_cv_GMODULE_CFLAGS=`$PKG_CONFIG --cflags "gmodule-2.0 >= 2.32" 2>/dev/null` test "x$?" != "x0" && pkg_failed=yes else pkg_failed=yes fi else pkg_failed=untried fi if test -n "$GMODULE_LIBS"; then pkg_cv_GMODULE_LIBS="$GMODULE_LIBS" elif test -n "$PKG_CONFIG"; then if test -n "$PKG_CONFIG" && \ { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"gmodule-2.0 >= 2.32\""; } >&5 ($PKG_CONFIG --exists --print-errors "gmodule-2.0 >= 2.32") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then pkg_cv_GMODULE_LIBS=`$PKG_CONFIG --libs "gmodule-2.0 >= 2.32" 2>/dev/null` test "x$?" != "x0" && pkg_failed=yes else pkg_failed=yes fi else pkg_failed=untried fi if test $pkg_failed = yes; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then _pkg_short_errors_supported=yes else _pkg_short_errors_supported=no fi if test $_pkg_short_errors_supported = yes; then GMODULE_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "gmodule-2.0 >= 2.32" 2>&1` else GMODULE_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "gmodule-2.0 >= 2.32" 2>&1` fi # Put the nasty error message in config.log where it belongs echo "$GMODULE_PKG_ERRORS" >&5 as_fn_error $? "Package requirements (gmodule-2.0 >= 2.32) were not met: $GMODULE_PKG_ERRORS Consider adjusting the PKG_CONFIG_PATH environment variable if you installed software in a non-standard prefix. Alternatively, you may set the environment variables GMODULE_CFLAGS and GMODULE_LIBS to avoid the need to call pkg-config. See the pkg-config man page for more details." "$LINENO" 5 elif test $pkg_failed = untried; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "The pkg-config script could not be found or is too old. Make sure it is in your PATH or set the PKG_CONFIG environment variable to the full path to pkg-config. Alternatively, you may set the environment variables GMODULE_CFLAGS and GMODULE_LIBS to avoid the need to call pkg-config. See the pkg-config man page for more details. To get pkg-config, see . See \`config.log' for more details" "$LINENO" 5; } else GMODULE_CFLAGS=$pkg_cv_GMODULE_CFLAGS GMODULE_LIBS=$pkg_cv_GMODULE_LIBS { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } fi $as_echo "#define GLIB_VERSION_MIN_REQUIRED GLIB_VERSION_2_30" >>confdefs.h $as_echo "#define GLIB_VERSION_MAX_ALLOWED GLIB_VERSION_2_32" >>confdefs.h GLIB_GENMARSHAL=`$PKG_CONFIG --variable=glib_genmarshal glib-2.0` # These must contain "exec" for automake to work right (install-exec, # not install-data). # # Private directory for Wocky and the gabble-plugins library if test "x$pluginexeclibdir" = x; then pluginexeclibdir='${libdir}/telepathy/gabble-0/lib' fi # The actual plugins if test "x$pluginexecdir" = x; then pluginexecdir='${libdir}/telepathy/gabble-0/plugins' fi # Check whether --enable-plugins was given. if test "${enable_plugins+set}" = set; then : enableval=$enable_plugins; enable_plugins=$enableval else enable_plugins=yes fi if test x$enable_plugins = xyes; then $as_echo "#define ENABLE_PLUGINS /**/" >>confdefs.h pkg_failed=no { $as_echo "$as_me:${as_lineno-$LINENO}: checking for GMODULE" >&5 $as_echo_n "checking for GMODULE... " >&6; } if test -n "$GMODULE_CFLAGS"; then pkg_cv_GMODULE_CFLAGS="$GMODULE_CFLAGS" elif test -n "$PKG_CONFIG"; then if test -n "$PKG_CONFIG" && \ { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"gmodule-2.0\""; } >&5 ($PKG_CONFIG --exists --print-errors "gmodule-2.0") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then pkg_cv_GMODULE_CFLAGS=`$PKG_CONFIG --cflags "gmodule-2.0" 2>/dev/null` test "x$?" != "x0" && pkg_failed=yes else pkg_failed=yes fi else pkg_failed=untried fi if test -n "$GMODULE_LIBS"; then pkg_cv_GMODULE_LIBS="$GMODULE_LIBS" elif test -n "$PKG_CONFIG"; then if test -n "$PKG_CONFIG" && \ { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"gmodule-2.0\""; } >&5 ($PKG_CONFIG --exists --print-errors "gmodule-2.0") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then pkg_cv_GMODULE_LIBS=`$PKG_CONFIG --libs "gmodule-2.0" 2>/dev/null` test "x$?" != "x0" && pkg_failed=yes else pkg_failed=yes fi else pkg_failed=untried fi if test $pkg_failed = yes; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then _pkg_short_errors_supported=yes else _pkg_short_errors_supported=no fi if test $_pkg_short_errors_supported = yes; then GMODULE_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "gmodule-2.0" 2>&1` else GMODULE_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "gmodule-2.0" 2>&1` fi # Put the nasty error message in config.log where it belongs echo "$GMODULE_PKG_ERRORS" >&5 as_fn_error $? "Package requirements (gmodule-2.0) were not met: $GMODULE_PKG_ERRORS Consider adjusting the PKG_CONFIG_PATH environment variable if you installed software in a non-standard prefix. Alternatively, you may set the environment variables GMODULE_CFLAGS and GMODULE_LIBS to avoid the need to call pkg-config. See the pkg-config man page for more details." "$LINENO" 5 elif test $pkg_failed = untried; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "The pkg-config script could not be found or is too old. Make sure it is in your PATH or set the PKG_CONFIG environment variable to the full path to pkg-config. Alternatively, you may set the environment variables GMODULE_CFLAGS and GMODULE_LIBS to avoid the need to call pkg-config. See the pkg-config man page for more details. To get pkg-config, see . See \`config.log' for more details" "$LINENO" 5; } else GMODULE_CFLAGS=$pkg_cv_GMODULE_CFLAGS GMODULE_LIBS=$pkg_cv_GMODULE_LIBS { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } fi # Check whether --enable-plugin-api was given. if test "${enable_plugin_api+set}" = set; then : enableval=$enable_plugin_api; enable_plugin_api=$enableval wocky_install_headers_dir="${includedir}/telepathy-gabble-0" else enable_plugin_api=no fi fi if test x$enable_plugins = xyes; then ENABLE_PLUGINS_TRUE= ENABLE_PLUGINS_FALSE='#' else ENABLE_PLUGINS_TRUE='#' ENABLE_PLUGINS_FALSE= fi if test x$enable_plugin_api = xyes; then ENABLE_PLUGIN_API_TRUE= ENABLE_PLUGIN_API_FALSE='#' else ENABLE_PLUGIN_API_TRUE='#' ENABLE_PLUGIN_API_FALSE= fi # Check whether --enable-channel-type-call was given. if test "${enable_channel_type_call+set}" = set; then : enableval=$enable_channel_type_call; enable_channel_type_call=$enableval else enable_channel_type_call=yes fi if test x$enable_channel_type_call = xyes; then $as_echo "#define ENABLE_CHANNEL_TYPE_CALL /**/" >>confdefs.h fi if test x$enable_channel_type_call = xyes; then ENABLE_CHANNEL_TYPE_CALL_TRUE= ENABLE_CHANNEL_TYPE_CALL_FALSE='#' else ENABLE_CHANNEL_TYPE_CALL_TRUE='#' ENABLE_CHANNEL_TYPE_CALL_FALSE= fi pkg_failed=no { $as_echo "$as_me:${as_lineno-$LINENO}: checking for DBUS" >&5 $as_echo_n "checking for DBUS... " >&6; } if test -n "$DBUS_CFLAGS"; then pkg_cv_DBUS_CFLAGS="$DBUS_CFLAGS" elif test -n "$PKG_CONFIG"; then if test -n "$PKG_CONFIG" && \ { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"dbus-1 >= 1.1.0, dbus-glib-1 >= 0.82\""; } >&5 ($PKG_CONFIG --exists --print-errors "dbus-1 >= 1.1.0, dbus-glib-1 >= 0.82") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then pkg_cv_DBUS_CFLAGS=`$PKG_CONFIG --cflags "dbus-1 >= 1.1.0, dbus-glib-1 >= 0.82" 2>/dev/null` test "x$?" != "x0" && pkg_failed=yes else pkg_failed=yes fi else pkg_failed=untried fi if test -n "$DBUS_LIBS"; then pkg_cv_DBUS_LIBS="$DBUS_LIBS" elif test -n "$PKG_CONFIG"; then if test -n "$PKG_CONFIG" && \ { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"dbus-1 >= 1.1.0, dbus-glib-1 >= 0.82\""; } >&5 ($PKG_CONFIG --exists --print-errors "dbus-1 >= 1.1.0, dbus-glib-1 >= 0.82") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then pkg_cv_DBUS_LIBS=`$PKG_CONFIG --libs "dbus-1 >= 1.1.0, dbus-glib-1 >= 0.82" 2>/dev/null` test "x$?" != "x0" && pkg_failed=yes else pkg_failed=yes fi else pkg_failed=untried fi if test $pkg_failed = yes; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then _pkg_short_errors_supported=yes else _pkg_short_errors_supported=no fi if test $_pkg_short_errors_supported = yes; then DBUS_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "dbus-1 >= 1.1.0, dbus-glib-1 >= 0.82" 2>&1` else DBUS_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "dbus-1 >= 1.1.0, dbus-glib-1 >= 0.82" 2>&1` fi # Put the nasty error message in config.log where it belongs echo "$DBUS_PKG_ERRORS" >&5 as_fn_error $? "Package requirements (dbus-1 >= 1.1.0, dbus-glib-1 >= 0.82) were not met: $DBUS_PKG_ERRORS Consider adjusting the PKG_CONFIG_PATH environment variable if you installed software in a non-standard prefix. Alternatively, you may set the environment variables DBUS_CFLAGS and DBUS_LIBS to avoid the need to call pkg-config. See the pkg-config man page for more details." "$LINENO" 5 elif test $pkg_failed = untried; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "The pkg-config script could not be found or is too old. Make sure it is in your PATH or set the PKG_CONFIG environment variable to the full path to pkg-config. Alternatively, you may set the environment variables DBUS_CFLAGS and DBUS_LIBS to avoid the need to call pkg-config. See the pkg-config man page for more details. To get pkg-config, see . See \`config.log' for more details" "$LINENO" 5; } else DBUS_CFLAGS=$pkg_cv_DBUS_CFLAGS DBUS_LIBS=$pkg_cv_DBUS_LIBS { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } fi $as_echo "#define TP_SEAL_ENABLE /**/" >>confdefs.h $as_echo "#define TP_DISABLE_SINGLE_INCLUDE /**/" >>confdefs.h $as_echo "#define TP_VERSION_MIN_REQUIRED TP_VERSION_0_18" >>confdefs.h $as_echo "#define TP_VERSION_MAX_ALLOWED TP_VERSION_0_20" >>confdefs.h pkg_failed=no { $as_echo "$as_me:${as_lineno-$LINENO}: checking for TP_GLIB" >&5 $as_echo_n "checking for TP_GLIB... " >&6; } if test -n "$TP_GLIB_CFLAGS"; then pkg_cv_TP_GLIB_CFLAGS="$TP_GLIB_CFLAGS" elif test -n "$PKG_CONFIG"; then if test -n "$PKG_CONFIG" && \ { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"telepathy-glib >= 0.19.9\""; } >&5 ($PKG_CONFIG --exists --print-errors "telepathy-glib >= 0.19.9") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then pkg_cv_TP_GLIB_CFLAGS=`$PKG_CONFIG --cflags "telepathy-glib >= 0.19.9" 2>/dev/null` test "x$?" != "x0" && pkg_failed=yes else pkg_failed=yes fi else pkg_failed=untried fi if test -n "$TP_GLIB_LIBS"; then pkg_cv_TP_GLIB_LIBS="$TP_GLIB_LIBS" elif test -n "$PKG_CONFIG"; then if test -n "$PKG_CONFIG" && \ { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"telepathy-glib >= 0.19.9\""; } >&5 ($PKG_CONFIG --exists --print-errors "telepathy-glib >= 0.19.9") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then pkg_cv_TP_GLIB_LIBS=`$PKG_CONFIG --libs "telepathy-glib >= 0.19.9" 2>/dev/null` test "x$?" != "x0" && pkg_failed=yes else pkg_failed=yes fi else pkg_failed=untried fi if test $pkg_failed = yes; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then _pkg_short_errors_supported=yes else _pkg_short_errors_supported=no fi if test $_pkg_short_errors_supported = yes; then TP_GLIB_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "telepathy-glib >= 0.19.9" 2>&1` else TP_GLIB_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "telepathy-glib >= 0.19.9" 2>&1` fi # Put the nasty error message in config.log where it belongs echo "$TP_GLIB_PKG_ERRORS" >&5 as_fn_error $? "Package requirements (telepathy-glib >= 0.19.9) were not met: $TP_GLIB_PKG_ERRORS Consider adjusting the PKG_CONFIG_PATH environment variable if you installed software in a non-standard prefix. Alternatively, you may set the environment variables TP_GLIB_CFLAGS and TP_GLIB_LIBS to avoid the need to call pkg-config. See the pkg-config man page for more details." "$LINENO" 5 elif test $pkg_failed = untried; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "The pkg-config script could not be found or is too old. Make sure it is in your PATH or set the PKG_CONFIG environment variable to the full path to pkg-config. Alternatively, you may set the environment variables TP_GLIB_CFLAGS and TP_GLIB_LIBS to avoid the need to call pkg-config. See the pkg-config man page for more details. To get pkg-config, see . See \`config.log' for more details" "$LINENO" 5; } else TP_GLIB_CFLAGS=$pkg_cv_TP_GLIB_CFLAGS TP_GLIB_LIBS=$pkg_cv_TP_GLIB_LIBS { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } fi XSLTPROC= for ac_prog in xsltproc do # Extract the first word of "$ac_prog", so it can be a program name with args. set dummy $ac_prog; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_XSLTPROC+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$XSLTPROC"; then ac_cv_prog_XSLTPROC="$XSLTPROC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_XSLTPROC="$ac_prog" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi XSLTPROC=$ac_cv_prog_XSLTPROC if test -n "$XSLTPROC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $XSLTPROC" >&5 $as_echo "$XSLTPROC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi test -n "$XSLTPROC" && break done if test -z "$XSLTPROC"; then as_fn_error $? "xsltproc (from the libxslt source package) is required" "$LINENO" 5 fi if test -n "$PYTHON"; then # If the user set $PYTHON, use it and don't search something else. { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $PYTHON version is >= 2.5" >&5 $as_echo_n "checking whether $PYTHON version is >= 2.5... " >&6; } prog="import sys # split strings by '.' and convert to numeric. Append some zeros # because we need at least 4 digits for the hex conversion. # map returns an iterator in Python 3.0 and a list in 2.x minver = list(map(int, '2.5'.split('.'))) + [0, 0, 0] minverhex = 0 # xrange is not present in Python 3.0 and range returns an iterator for i in list(range(0, 4)): minverhex = (minverhex << 8) + minver[i] sys.exit(sys.hexversion < minverhex)" if { echo "$as_me:$LINENO: $PYTHON -c "$prog"" >&5 ($PYTHON -c "$prog") >&5 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; then : { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } as_fn_error $? "Python interpreter is too old" "$LINENO" 5 fi am_display_PYTHON=$PYTHON else # Otherwise, try each interpreter until we find one that satisfies # VERSION. { $as_echo "$as_me:${as_lineno-$LINENO}: checking for a Python interpreter with version >= 2.5" >&5 $as_echo_n "checking for a Python interpreter with version >= 2.5... " >&6; } if ${am_cv_pathless_PYTHON+:} false; then : $as_echo_n "(cached) " >&6 else for am_cv_pathless_PYTHON in python python2 python3 python3.3 python3.2 python3.1 python3.0 python2.7 python2.6 python2.5 python2.4 python2.3 python2.2 python2.1 python2.0 none; do test "$am_cv_pathless_PYTHON" = none && break prog="import sys # split strings by '.' and convert to numeric. Append some zeros # because we need at least 4 digits for the hex conversion. # map returns an iterator in Python 3.0 and a list in 2.x minver = list(map(int, '2.5'.split('.'))) + [0, 0, 0] minverhex = 0 # xrange is not present in Python 3.0 and range returns an iterator for i in list(range(0, 4)): minverhex = (minverhex << 8) + minver[i] sys.exit(sys.hexversion < minverhex)" if { echo "$as_me:$LINENO: $am_cv_pathless_PYTHON -c "$prog"" >&5 ($am_cv_pathless_PYTHON -c "$prog") >&5 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; then : break fi done fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_pathless_PYTHON" >&5 $as_echo "$am_cv_pathless_PYTHON" >&6; } # Set $PYTHON to the absolute path of $am_cv_pathless_PYTHON. if test "$am_cv_pathless_PYTHON" = none; then PYTHON=: else # Extract the first word of "$am_cv_pathless_PYTHON", so it can be a program name with args. set dummy $am_cv_pathless_PYTHON; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_path_PYTHON+:} false; then : $as_echo_n "(cached) " >&6 else case $PYTHON in [\\/]* | ?:[\\/]*) ac_cv_path_PYTHON="$PYTHON" # Let the user override the test with a path. ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_path_PYTHON="$as_dir/$ac_word$ac_exec_ext" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS ;; esac fi PYTHON=$ac_cv_path_PYTHON if test -n "$PYTHON"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $PYTHON" >&5 $as_echo "$PYTHON" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi am_display_PYTHON=$am_cv_pathless_PYTHON fi if test "$PYTHON" = :; then as_fn_error $? "no suitable Python interpreter found" "$LINENO" 5 else { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $am_display_PYTHON version" >&5 $as_echo_n "checking for $am_display_PYTHON version... " >&6; } if ${am_cv_python_version+:} false; then : $as_echo_n "(cached) " >&6 else am_cv_python_version=`$PYTHON -c "import sys; sys.stdout.write(sys.version[:3])"` fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_python_version" >&5 $as_echo "$am_cv_python_version" >&6; } PYTHON_VERSION=$am_cv_python_version PYTHON_PREFIX='${prefix}' PYTHON_EXEC_PREFIX='${exec_prefix}' { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $am_display_PYTHON platform" >&5 $as_echo_n "checking for $am_display_PYTHON platform... " >&6; } if ${am_cv_python_platform+:} false; then : $as_echo_n "(cached) " >&6 else am_cv_python_platform=`$PYTHON -c "import sys; sys.stdout.write(sys.platform)"` fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_python_platform" >&5 $as_echo "$am_cv_python_platform" >&6; } PYTHON_PLATFORM=$am_cv_python_platform # Just factor out some code duplication. am_python_setup_sysconfig="\ import sys # Prefer sysconfig over distutils.sysconfig, for better compatibility # with python 3.x. See automake bug#10227. try: import sysconfig except ImportError: can_use_sysconfig = 0 else: can_use_sysconfig = 1 # Can't use sysconfig in CPython 2.7, since it's broken in virtualenvs: # try: from platform import python_implementation if python_implementation() == 'CPython' and sys.version[:3] == '2.7': can_use_sysconfig = 0 except ImportError: pass" { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $am_display_PYTHON script directory" >&5 $as_echo_n "checking for $am_display_PYTHON script directory... " >&6; } if ${am_cv_python_pythondir+:} false; then : $as_echo_n "(cached) " >&6 else if test "x$prefix" = xNONE then am_py_prefix=$ac_default_prefix else am_py_prefix=$prefix fi am_cv_python_pythondir=`$PYTHON -c " $am_python_setup_sysconfig if can_use_sysconfig: sitedir = sysconfig.get_path('purelib', vars={'base':'$am_py_prefix'}) else: from distutils import sysconfig sitedir = sysconfig.get_python_lib(0, 0, prefix='$am_py_prefix') sys.stdout.write(sitedir)"` case $am_cv_python_pythondir in $am_py_prefix*) am__strip_prefix=`echo "$am_py_prefix" | sed 's|.|.|g'` am_cv_python_pythondir=`echo "$am_cv_python_pythondir" | sed "s,^$am__strip_prefix,$PYTHON_PREFIX,"` ;; *) case $am_py_prefix in /usr|/System*) ;; *) am_cv_python_pythondir=$PYTHON_PREFIX/lib/python$PYTHON_VERSION/site-packages ;; esac ;; esac fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_python_pythondir" >&5 $as_echo "$am_cv_python_pythondir" >&6; } pythondir=$am_cv_python_pythondir pkgpythondir=\${pythondir}/$PACKAGE { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $am_display_PYTHON extension module directory" >&5 $as_echo_n "checking for $am_display_PYTHON extension module directory... " >&6; } if ${am_cv_python_pyexecdir+:} false; then : $as_echo_n "(cached) " >&6 else if test "x$exec_prefix" = xNONE then am_py_exec_prefix=$am_py_prefix else am_py_exec_prefix=$exec_prefix fi am_cv_python_pyexecdir=`$PYTHON -c " $am_python_setup_sysconfig if can_use_sysconfig: sitedir = sysconfig.get_path('platlib', vars={'platbase':'$am_py_prefix'}) else: from distutils import sysconfig sitedir = sysconfig.get_python_lib(1, 0, prefix='$am_py_prefix') sys.stdout.write(sitedir)"` case $am_cv_python_pyexecdir in $am_py_exec_prefix*) am__strip_prefix=`echo "$am_py_exec_prefix" | sed 's|.|.|g'` am_cv_python_pyexecdir=`echo "$am_cv_python_pyexecdir" | sed "s,^$am__strip_prefix,$PYTHON_EXEC_PREFIX,"` ;; *) case $am_py_exec_prefix in /usr|/System*) ;; *) am_cv_python_pyexecdir=$PYTHON_EXEC_PREFIX/lib/python$PYTHON_VERSION/site-packages ;; esac ;; esac fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_python_pyexecdir" >&5 $as_echo "$am_cv_python_pyexecdir" >&6; } pyexecdir=$am_cv_python_pyexecdir pkgpyexecdir=\${pyexecdir}/$PACKAGE fi # Check for a Python >= 2.5 with Twisted, to run the tests { $as_echo "$as_me:${as_lineno-$LINENO}: checking for Python with Twisted and XMPP protocol support" >&5 $as_echo_n "checking for Python with Twisted and XMPP protocol support... " >&6; } if $PYTHON -c "import twisted.words.xish.domish, twisted.words.protocols.jabber, twisted.internet.reactor" >/dev/null 2>&1; then TEST_PYTHON="$PYTHON" else TEST_PYTHON=false fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $TEST_PYTHON" >&5 $as_echo "$TEST_PYTHON" >&6; } if test false != "$TEST_PYTHON"; then WANT_TWISTED_TESTS_TRUE= WANT_TWISTED_TESTS_FALSE='#' else WANT_TWISTED_TESTS_TRUE='#' WANT_TWISTED_TESTS_FALSE= fi # We have to run Wocky's configure *before* looking for it with PKG_CHECK_MODULES so wocky-uninstalled.pc has been generated # If you don't specify --prefix, it starts off as NONE. Autoconf # would normally do this defaulting for us later, but that's too # late to help Wocky. if test "x${prefix}" = "xNONE"; then prefix=/usr/local fi # We tell Wocky to install its headers alongside gabble's so that an actual # separate Wocky installation won't clash with them. This is a bit of a hack. # AX_CONFIG_DIR doesn't make it very easy to pass extra arguments to the # submodule's configure. prev_ac_configure_args=$ac_configure_args ac_configure_args="$ac_configure_args --with-installed-headers=${wocky_install_headers_dir} --enable-shared-suffix=${PACKAGE}-${VERSION} --libdir=${pluginexeclibdir}" if test "x$ENABLE_CODING_STYLE_CHECKS" = xyes ; then ac_configure_args="$ac_configure_args --enable-coding-style-checks" else ac_configure_args="$ac_configure_args --disable-coding-style-checks" fi if test "x$tp_werror" = xyes && test "x$official_release" = xno; then ac_configure_args="$ac_configure_args --enable-Werror" else ac_configure_args="$ac_configure_args --disable-Werror" fi prev_top_build_prefix=$ac_top_build_prefix # Remove --cache-file and --srcdir arguments so they do not pile up. ax_sub_configure_args= ax_prev= eval "set x $ac_configure_args" shift for ax_arg do if test -n "$ax_prev"; then ax_prev= continue fi case $ax_arg in -cache-file | --cache-file | --cache-fil | --cache-fi \ | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c) ax_prev=cache_file ;; -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \ | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* \ | --c=*) ;; --config-cache | -C) ;; -srcdir | --srcdir | --srcdi | --srcd | --src | --sr) ax_prev=srcdir ;; -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*) ;; -prefix | --prefix | --prefi | --pref | --pre | --pr | --p) ax_prev=prefix ;; -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*) ;; *) case $ax_arg in *\'*) ax_arg=`echo "$ax_arg" | sed "s/'/'\\\\\\\\''/g"` ;; esac ax_sub_configure_args="$ax_sub_configure_args '$ax_arg'" ;; esac done # Always prepend --prefix to ensure using the same prefix # in subdir configurations. ax_arg="--prefix=$prefix" case $ax_arg in *\'*) ax_arg=`echo "$ax_arg" | sed "s/'/'\\\\\\\\''/g"` ;; esac ax_sub_configure_args="'$ax_arg' $ax_sub_configure_args" # Pass --silent if test "$silent" = yes; then ax_sub_configure_args="--silent $ax_sub_configure_args" fi ax_popdir=`pwd` { $as_echo "$as_me:${as_lineno-$LINENO}: Configuring sources in lib/ext/wocky" >&5 $as_echo "$as_me: Configuring sources in lib/ext/wocky" >&6;} as_dir="lib/ext/wocky"; as_fn_mkdir_p ac_builddir=. case "lib/ext/wocky" in .) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;; *) ac_dir_suffix=/`$as_echo "lib/ext/wocky" | sed 's|^\.[\\/]||'` # A ".." for each directory in $ac_dir_suffix. ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'` case $ac_top_builddir_sub in "") ac_top_builddir_sub=. ac_top_build_prefix= ;; *) ac_top_build_prefix=$ac_top_builddir_sub/ ;; esac ;; esac ac_abs_top_builddir=$ac_pwd ac_abs_builddir=$ac_pwd$ac_dir_suffix # for backward compatibility: ac_top_builddir=$ac_top_build_prefix case $srcdir in .) # We are building in place. ac_srcdir=. ac_top_srcdir=$ac_top_builddir_sub ac_abs_top_srcdir=$ac_pwd ;; [\\/]* | ?:[\\/]* ) # Absolute name. ac_srcdir=$srcdir$ac_dir_suffix; ac_top_srcdir=$srcdir ac_abs_top_srcdir=$srcdir ;; *) # Relative name. ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix ac_top_srcdir=$ac_top_build_prefix$srcdir ac_abs_top_srcdir=$ac_pwd/$srcdir ;; esac ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix cd "lib/ext/wocky" # Check for guested configure; otherwise get Cygnus style configure. if test -f "configure.gnu"; then ax_sub_configure=$ac_srcdir/configure.gnu elif test -f "$ac_srcdir/configure"; then ax_sub_configure=$ac_srcdir/configure elif test -f "$ac_srcdir/configure.in"; then # This should be Cygnus configure. ax_sub_configure=$ac_aux_dir/configure else { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: no configuration information is in lib/ext/wocky" >&5 $as_echo "$as_me: WARNING: no configuration information is in lib/ext/wocky" >&2;} ax_sub_configure= fi # The recursion is here. if test -n "$ax_sub_configure"; then # Make the cache file name correct relative to the subdirectory. case $cache_file in [\\/]* | ?:[\\/]* ) ax_sub_cache_file=$cache_file ;; *) # Relative name. ax_sub_cache_file=$ac_top_build_prefix$cache_file ;; esac { $as_echo "$as_me:${as_lineno-$LINENO}: running $SHELL $ax_sub_configure $ax_sub_configure_args --cache-file=$ax_sub_cache_file --srcdir=$ac_srcdir" >&5 $as_echo "$as_me: running $SHELL $ax_sub_configure $ax_sub_configure_args --cache-file=$ax_sub_cache_file --srcdir=$ac_srcdir" >&6;} # The eval makes quoting arguments work. eval "\$SHELL \"\$ax_sub_configure\" $ax_sub_configure_args \ --cache-file=\"\$ax_sub_cache_file\" --srcdir=\"\$ax_srcdir\"" fi cd "$ax_popdir" { $as_echo "$as_me:${as_lineno-$LINENO}: Done configuring in lib/ext/wocky" >&5 $as_echo "$as_me: Done configuring in lib/ext/wocky" >&6;} ac_top_build_prefix=$prev_top_build_prefix ac_configure_args=$prev_ac_configure_args # Check whether --enable-submodules was given. if test "${enable_submodules+set}" = set; then : enableval=$enable_submodules; build_submodules=$enableval else build_submodules=yes fi # re-enable once Wocky has been released as a lib if test x$build_submodules = xyes; then export PKG_CONFIG_PATH=$PKG_CONFIG_PATH:"$ac_top_build_prefix"lib/ext/wocky/wocky fi pkg_failed=no { $as_echo "$as_me:${as_lineno-$LINENO}: checking for WOCKY" >&5 $as_echo_n "checking for WOCKY... " >&6; } if test -n "$WOCKY_CFLAGS"; then pkg_cv_WOCKY_CFLAGS="$WOCKY_CFLAGS" elif test -n "$PKG_CONFIG"; then if test -n "$PKG_CONFIG" && \ { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"wocky >= 0.0.0\""; } >&5 ($PKG_CONFIG --exists --print-errors "wocky >= 0.0.0") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then pkg_cv_WOCKY_CFLAGS=`$PKG_CONFIG --cflags "wocky >= 0.0.0" 2>/dev/null` test "x$?" != "x0" && pkg_failed=yes else pkg_failed=yes fi else pkg_failed=untried fi if test -n "$WOCKY_LIBS"; then pkg_cv_WOCKY_LIBS="$WOCKY_LIBS" elif test -n "$PKG_CONFIG"; then if test -n "$PKG_CONFIG" && \ { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"wocky >= 0.0.0\""; } >&5 ($PKG_CONFIG --exists --print-errors "wocky >= 0.0.0") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then pkg_cv_WOCKY_LIBS=`$PKG_CONFIG --libs "wocky >= 0.0.0" 2>/dev/null` test "x$?" != "x0" && pkg_failed=yes else pkg_failed=yes fi else pkg_failed=untried fi if test $pkg_failed = yes; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then _pkg_short_errors_supported=yes else _pkg_short_errors_supported=no fi if test $_pkg_short_errors_supported = yes; then WOCKY_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "wocky >= 0.0.0" 2>&1` else WOCKY_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "wocky >= 0.0.0" 2>&1` fi # Put the nasty error message in config.log where it belongs echo "$WOCKY_PKG_ERRORS" >&5 as_fn_error $? "Package requirements (wocky >= 0.0.0) were not met: $WOCKY_PKG_ERRORS Consider adjusting the PKG_CONFIG_PATH environment variable if you installed software in a non-standard prefix. Alternatively, you may set the environment variables WOCKY_CFLAGS and WOCKY_LIBS to avoid the need to call pkg-config. See the pkg-config man page for more details." "$LINENO" 5 elif test $pkg_failed = untried; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "The pkg-config script could not be found or is too old. Make sure it is in your PATH or set the PKG_CONFIG environment variable to the full path to pkg-config. Alternatively, you may set the environment variables WOCKY_CFLAGS and WOCKY_LIBS to avoid the need to call pkg-config. See the pkg-config man page for more details. To get pkg-config, see . See \`config.log' for more details" "$LINENO" 5; } else WOCKY_CFLAGS=$pkg_cv_WOCKY_CFLAGS WOCKY_LIBS=$pkg_cv_WOCKY_LIBS { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } fi # Check whether --enable-google-relay was given. if test "${enable_google_relay+set}" = set; then : enableval=$enable_google_relay; enable_google_relay=$enableval else enable_google_relay=yes fi if test x$enable_google_relay = xyes; then $as_echo "#define ENABLE_GOOGLE_RELAY /**/" >>confdefs.h pkg_failed=no { $as_echo "$as_me:${as_lineno-$LINENO}: checking for SOUP" >&5 $as_echo_n "checking for SOUP... " >&6; } if test -n "$SOUP_CFLAGS"; then pkg_cv_SOUP_CFLAGS="$SOUP_CFLAGS" elif test -n "$PKG_CONFIG"; then if test -n "$PKG_CONFIG" && \ { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libsoup-2.4\""; } >&5 ($PKG_CONFIG --exists --print-errors "libsoup-2.4") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then pkg_cv_SOUP_CFLAGS=`$PKG_CONFIG --cflags "libsoup-2.4" 2>/dev/null` test "x$?" != "x0" && pkg_failed=yes else pkg_failed=yes fi else pkg_failed=untried fi if test -n "$SOUP_LIBS"; then pkg_cv_SOUP_LIBS="$SOUP_LIBS" elif test -n "$PKG_CONFIG"; then if test -n "$PKG_CONFIG" && \ { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libsoup-2.4\""; } >&5 ($PKG_CONFIG --exists --print-errors "libsoup-2.4") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then pkg_cv_SOUP_LIBS=`$PKG_CONFIG --libs "libsoup-2.4" 2>/dev/null` test "x$?" != "x0" && pkg_failed=yes else pkg_failed=yes fi else pkg_failed=untried fi if test $pkg_failed = yes; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then _pkg_short_errors_supported=yes else _pkg_short_errors_supported=no fi if test $_pkg_short_errors_supported = yes; then SOUP_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "libsoup-2.4" 2>&1` else SOUP_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "libsoup-2.4" 2>&1` fi # Put the nasty error message in config.log where it belongs echo "$SOUP_PKG_ERRORS" >&5 as_fn_error $? "Package requirements (libsoup-2.4) were not met: $SOUP_PKG_ERRORS Consider adjusting the PKG_CONFIG_PATH environment variable if you installed software in a non-standard prefix. Alternatively, you may set the environment variables SOUP_CFLAGS and SOUP_LIBS to avoid the need to call pkg-config. See the pkg-config man page for more details." "$LINENO" 5 elif test $pkg_failed = untried; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "The pkg-config script could not be found or is too old. Make sure it is in your PATH or set the PKG_CONFIG environment variable to the full path to pkg-config. Alternatively, you may set the environment variables SOUP_CFLAGS and SOUP_LIBS to avoid the need to call pkg-config. See the pkg-config man page for more details. To get pkg-config, see . See \`config.log' for more details" "$LINENO" 5; } else SOUP_CFLAGS=$pkg_cv_SOUP_CFLAGS SOUP_LIBS=$pkg_cv_SOUP_LIBS { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } fi else SOUP_CFLAGS= SOUP_LIBS= fi if test "x$enable_google_relay" = xyes; then ENABLE_GOOGLE_RELAY_TRUE= ENABLE_GOOGLE_RELAY_FALSE='#' else ENABLE_GOOGLE_RELAY_TRUE='#' ENABLE_GOOGLE_RELAY_FALSE= fi # Check whether --enable-file-transfer was given. if test "${enable_file_transfer+set}" = set; then : enableval=$enable_file_transfer; enable_ft=$enableval else enable_ft=yes fi if test x$enable_ft = xyes; then $as_echo "#define ENABLE_FILE_TRANSFER /**/" >>confdefs.h fi if test "x$enable_ft" = xyes; then ENABLE_FILE_TRANSFER_TRUE= ENABLE_FILE_TRANSFER_FALSE='#' else ENABLE_FILE_TRANSFER_TRUE='#' ENABLE_FILE_TRANSFER_FALSE= fi # Check whether --enable-voip was given. if test "${enable_voip+set}" = set; then : enableval=$enable_voip; enable_voip=$enableval else enable_voip=yes fi if test x$enable_voip = xyes; then $as_echo "#define ENABLE_VOIP /**/" >>confdefs.h fi if test "x$enable_voip" = xyes; then ENABLE_VOIP_TRUE= ENABLE_VOIP_FALSE='#' else ENABLE_VOIP_TRUE='#' ENABLE_VOIP_FALSE= fi if test x$enable_voip = xyes -a x$enable_ft = xyes; then enable_jingle_ft=yes $as_echo "#define ENABLE_JINGLE_FILE_TRANSFER /**/" >>confdefs.h pkg_failed=no { $as_echo "$as_me:${as_lineno-$LINENO}: checking for NICE" >&5 $as_echo_n "checking for NICE... " >&6; } if test -n "$NICE_CFLAGS"; then pkg_cv_NICE_CFLAGS="$NICE_CFLAGS" elif test -n "$PKG_CONFIG"; then if test -n "$PKG_CONFIG" && \ { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"nice >= 0.0.11\""; } >&5 ($PKG_CONFIG --exists --print-errors "nice >= 0.0.11") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then pkg_cv_NICE_CFLAGS=`$PKG_CONFIG --cflags "nice >= 0.0.11" 2>/dev/null` test "x$?" != "x0" && pkg_failed=yes else pkg_failed=yes fi else pkg_failed=untried fi if test -n "$NICE_LIBS"; then pkg_cv_NICE_LIBS="$NICE_LIBS" elif test -n "$PKG_CONFIG"; then if test -n "$PKG_CONFIG" && \ { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"nice >= 0.0.11\""; } >&5 ($PKG_CONFIG --exists --print-errors "nice >= 0.0.11") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then pkg_cv_NICE_LIBS=`$PKG_CONFIG --libs "nice >= 0.0.11" 2>/dev/null` test "x$?" != "x0" && pkg_failed=yes else pkg_failed=yes fi else pkg_failed=untried fi if test $pkg_failed = yes; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then _pkg_short_errors_supported=yes else _pkg_short_errors_supported=no fi if test $_pkg_short_errors_supported = yes; then NICE_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "nice >= 0.0.11" 2>&1` else NICE_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "nice >= 0.0.11" 2>&1` fi # Put the nasty error message in config.log where it belongs echo "$NICE_PKG_ERRORS" >&5 as_fn_error $? "Package requirements (nice >= 0.0.11) were not met: $NICE_PKG_ERRORS Consider adjusting the PKG_CONFIG_PATH environment variable if you installed software in a non-standard prefix. Alternatively, you may set the environment variables NICE_CFLAGS and NICE_LIBS to avoid the need to call pkg-config. See the pkg-config man page for more details." "$LINENO" 5 elif test $pkg_failed = untried; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "The pkg-config script could not be found or is too old. Make sure it is in your PATH or set the PKG_CONFIG environment variable to the full path to pkg-config. Alternatively, you may set the environment variables NICE_CFLAGS and NICE_LIBS to avoid the need to call pkg-config. See the pkg-config man page for more details. To get pkg-config, see . See \`config.log' for more details" "$LINENO" 5; } else NICE_CFLAGS=$pkg_cv_NICE_CFLAGS NICE_LIBS=$pkg_cv_NICE_LIBS { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } fi else enable_jingle_ft=no NICE_CFLAGS= NICE_LIBS= fi if test "x$enable_jingle_ft" = xyes; then ENABLE_JINGLE_FILE_TRANSFER_TRUE= ENABLE_JINGLE_FILE_TRANSFER_FALSE='#' else ENABLE_JINGLE_FILE_TRANSFER_TRUE='#' ENABLE_JINGLE_FILE_TRANSFER_FALSE= fi for ac_func in getifaddrs memset select strndup setresuid setreuid strerror do : as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh` ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var" if eval test \"x\$"$as_ac_var"\" = x"yes"; then : cat >>confdefs.h <<_ACEOF #define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1 _ACEOF fi done ac_config_files="$ac_config_files Makefile docs/Makefile extensions/Makefile src/Makefile m4/Makefile data/Makefile tests/Makefile tools/Makefile tests/suppressions/Makefile tests/twisted/Makefile lib/Makefile lib/ext/Makefile lib/gibber/Makefile plugins/Makefile gabble/Makefile gabble/telepathy-gabble-uninstalled.pc gabble/telepathy-gabble.pc" cat >confcache <<\_ACEOF # This file is a shell script that caches the results of configure # tests run on this system so they can be shared between configure # scripts and configure runs, see configure's option --config-cache. # It is not useful on other systems. If it contains results you don't # want to keep, you may remove or edit it. # # config.status only pays attention to the cache file if you give it # the --recheck option to rerun configure. # # `ac_cv_env_foo' variables (set or unset) will be overridden when # loading this file, other *unset* `ac_cv_foo' will be assigned the # following values. _ACEOF # The following way of writing the cache mishandles newlines in values, # but we know of no workaround that is simple, portable, and efficient. # So, we kill variables containing newlines. # Ultrix sh set writes to stderr and can't be redirected directly, # and sets the high bit in the cache file unless we assign to the vars. ( for ac_var in `(set) 2>&1 | sed -n 's/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'`; do eval ac_val=\$$ac_var case $ac_val in #( *${as_nl}*) case $ac_var in #( *_cv_*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5 $as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;; esac case $ac_var in #( _ | IFS | as_nl) ;; #( BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #( *) { eval $ac_var=; unset $ac_var;} ;; esac ;; esac done (set) 2>&1 | case $as_nl`(ac_space=' '; set) 2>&1` in #( *${as_nl}ac_space=\ *) # `set' does not quote correctly, so add quotes: double-quote # substitution turns \\\\ into \\, and sed turns \\ into \. sed -n \ "s/'/'\\\\''/g; s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\\2'/p" ;; #( *) # `set' quotes correctly as required by POSIX, so do not add quotes. sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p" ;; esac | sort ) | sed ' /^ac_cv_env_/b end t clear :clear s/^\([^=]*\)=\(.*[{}].*\)$/test "${\1+set}" = set || &/ t end s/^\([^=]*\)=\(.*\)$/\1=${\1=\2}/ :end' >>confcache if diff "$cache_file" confcache >/dev/null 2>&1; then :; else if test -w "$cache_file"; then if test "x$cache_file" != "x/dev/null"; then { $as_echo "$as_me:${as_lineno-$LINENO}: updating cache $cache_file" >&5 $as_echo "$as_me: updating cache $cache_file" >&6;} if test ! -f "$cache_file" || test -h "$cache_file"; then cat confcache >"$cache_file" else case $cache_file in #( */* | ?:*) mv -f confcache "$cache_file"$$ && mv -f "$cache_file"$$ "$cache_file" ;; #( *) mv -f confcache "$cache_file" ;; esac fi fi else { $as_echo "$as_me:${as_lineno-$LINENO}: not updating unwritable cache $cache_file" >&5 $as_echo "$as_me: not updating unwritable cache $cache_file" >&6;} fi fi rm -f confcache test "x$prefix" = xNONE && prefix=$ac_default_prefix # Let make expand exec_prefix. test "x$exec_prefix" = xNONE && exec_prefix='${prefix}' DEFS=-DHAVE_CONFIG_H ac_libobjs= ac_ltlibobjs= for ac_i in : $LIBOBJS; do test "x$ac_i" = x: && continue # 1. Remove the extension, and $U if already installed. ac_script='s/\$U\././;s/\.o$//;s/\.obj$//' ac_i=`$as_echo "$ac_i" | sed "$ac_script"` # 2. Prepend LIBOBJDIR. When used with automake>=1.10 LIBOBJDIR # will be set to the directory where LIBOBJS objects are built. as_fn_append ac_libobjs " \${LIBOBJDIR}$ac_i\$U.$ac_objext" as_fn_append ac_ltlibobjs " \${LIBOBJDIR}$ac_i"'$U.lo' done LIBOBJS=$ac_libobjs LTLIBOBJS=$ac_ltlibobjs { $as_echo "$as_me:${as_lineno-$LINENO}: checking that generated files are newer than configure" >&5 $as_echo_n "checking that generated files are newer than configure... " >&6; } if test -n "$am_sleep_pid"; then # Hide warnings about reused PIDs. wait $am_sleep_pid 2>/dev/null fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: done" >&5 $as_echo "done" >&6; } if test -n "$EXEEXT"; then am__EXEEXT_TRUE= am__EXEEXT_FALSE='#' else am__EXEEXT_TRUE='#' am__EXEEXT_FALSE= fi if test -z "${AMDEP_TRUE}" && test -z "${AMDEP_FALSE}"; then as_fn_error $? "conditional \"AMDEP\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${am__fastdepCC_TRUE}" && test -z "${am__fastdepCC_FALSE}"; then as_fn_error $? "conditional \"am__fastdepCC\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${am__fastdepCC_TRUE}" && test -z "${am__fastdepCC_FALSE}"; then as_fn_error $? "conditional \"am__fastdepCC\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${OFFICIAL_RELEASE_TRUE}" && test -z "${OFFICIAL_RELEASE_FALSE}"; then as_fn_error $? "conditional \"OFFICIAL_RELEASE\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${WINDOWS_TRUE}" && test -z "${WINDOWS_FALSE}"; then as_fn_error $? "conditional \"WINDOWS\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${ENABLE_DEBUG_TRUE}" && test -z "${ENABLE_DEBUG_FALSE}"; then as_fn_error $? "conditional \"ENABLE_DEBUG\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${ENABLE_INSTALLED_TESTS_TRUE}" && test -z "${ENABLE_INSTALLED_TESTS_FALSE}"; then as_fn_error $? "conditional \"ENABLE_INSTALLED_TESTS\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${ENABLE_FATAL_CRITICALS_TRUE}" && test -z "${ENABLE_FATAL_CRITICALS_FALSE}"; then as_fn_error $? "conditional \"ENABLE_FATAL_CRITICALS\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${ENABLE_GTK_DOC_TRUE}" && test -z "${ENABLE_GTK_DOC_FALSE}"; then as_fn_error $? "conditional \"ENABLE_GTK_DOC\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${ENABLE_PLUGINS_TRUE}" && test -z "${ENABLE_PLUGINS_FALSE}"; then as_fn_error $? "conditional \"ENABLE_PLUGINS\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${ENABLE_PLUGIN_API_TRUE}" && test -z "${ENABLE_PLUGIN_API_FALSE}"; then as_fn_error $? "conditional \"ENABLE_PLUGIN_API\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${ENABLE_CHANNEL_TYPE_CALL_TRUE}" && test -z "${ENABLE_CHANNEL_TYPE_CALL_FALSE}"; then as_fn_error $? "conditional \"ENABLE_CHANNEL_TYPE_CALL\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${WANT_TWISTED_TESTS_TRUE}" && test -z "${WANT_TWISTED_TESTS_FALSE}"; then as_fn_error $? "conditional \"WANT_TWISTED_TESTS\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${ENABLE_GOOGLE_RELAY_TRUE}" && test -z "${ENABLE_GOOGLE_RELAY_FALSE}"; then as_fn_error $? "conditional \"ENABLE_GOOGLE_RELAY\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${ENABLE_FILE_TRANSFER_TRUE}" && test -z "${ENABLE_FILE_TRANSFER_FALSE}"; then as_fn_error $? "conditional \"ENABLE_FILE_TRANSFER\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${ENABLE_VOIP_TRUE}" && test -z "${ENABLE_VOIP_FALSE}"; then as_fn_error $? "conditional \"ENABLE_VOIP\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${ENABLE_JINGLE_FILE_TRANSFER_TRUE}" && test -z "${ENABLE_JINGLE_FILE_TRANSFER_FALSE}"; then as_fn_error $? "conditional \"ENABLE_JINGLE_FILE_TRANSFER\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi : "${CONFIG_STATUS=./config.status}" ac_write_fail=0 ac_clean_files_save=$ac_clean_files ac_clean_files="$ac_clean_files $CONFIG_STATUS" { $as_echo "$as_me:${as_lineno-$LINENO}: creating $CONFIG_STATUS" >&5 $as_echo "$as_me: creating $CONFIG_STATUS" >&6;} as_write_fail=0 cat >$CONFIG_STATUS <<_ASEOF || as_write_fail=1 #! $SHELL # Generated by $as_me. # Run this file to recreate the current configuration. # Compiler output produced by configure, useful for debugging # configure, is in config.log if it exists. debug=false ac_cs_recheck=false ac_cs_silent=false SHELL=\${CONFIG_SHELL-$SHELL} export SHELL _ASEOF cat >>$CONFIG_STATUS <<\_ASEOF || as_write_fail=1 ## -------------------- ## ## M4sh Initialization. ## ## -------------------- ## # Be more Bourne compatible DUALCASE=1; export DUALCASE # for MKS sh if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then : emulate sh NULLCMD=: # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which # is contrary to our usage. Disable this feature. alias -g '${1+"$@"}'='"$@"' setopt NO_GLOB_SUBST else case `(set -o) 2>/dev/null` in #( *posix*) : set -o posix ;; #( *) : ;; esac fi as_nl=' ' export as_nl # Printing a long string crashes Solaris 7 /usr/bin/printf. as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo # Prefer a ksh shell builtin over an external printf program on Solaris, # but without wasting forks for bash or zsh. if test -z "$BASH_VERSION$ZSH_VERSION" \ && (test "X`print -r -- $as_echo`" = "X$as_echo") 2>/dev/null; then as_echo='print -r --' as_echo_n='print -rn --' elif (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then as_echo='printf %s\n' as_echo_n='printf %s' else if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"' as_echo_n='/usr/ucb/echo -n' else as_echo_body='eval expr "X$1" : "X\\(.*\\)"' as_echo_n_body='eval arg=$1; case $arg in #( *"$as_nl"*) expr "X$arg" : "X\\(.*\\)$as_nl"; arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;; esac; expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl" ' export as_echo_n_body as_echo_n='sh -c $as_echo_n_body as_echo' fi export as_echo_body as_echo='sh -c $as_echo_body as_echo' fi # The user is always right. if test "${PATH_SEPARATOR+set}" != set; then PATH_SEPARATOR=: (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && { (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 || PATH_SEPARATOR=';' } fi # IFS # We need space, tab and new line, in precisely that order. Quoting is # there to prevent editors from complaining about space-tab. # (If _AS_PATH_WALK were called with IFS unset, it would disable word # splitting by setting IFS to empty value.) IFS=" "" $as_nl" # Find who we are. Look in the path if we contain no directory separator. as_myself= case $0 in #(( *[\\/]* ) as_myself=$0 ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break done IFS=$as_save_IFS ;; esac # We did not find ourselves, most probably we were run as `sh COMMAND' # in which case we are not to be found in the path. if test "x$as_myself" = x; then as_myself=$0 fi if test ! -f "$as_myself"; then $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 exit 1 fi # Unset variables that we do not need and which cause bugs (e.g. in # pre-3.0 UWIN ksh). But do not cause bugs in bash 2.01; the "|| exit 1" # suppresses any "Segmentation fault" message there. '((' could # trigger a bug in pdksh 5.2.14. for as_var in BASH_ENV ENV MAIL MAILPATH do eval test x\${$as_var+set} = xset \ && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || : done PS1='$ ' PS2='> ' PS4='+ ' # NLS nuisances. LC_ALL=C export LC_ALL LANGUAGE=C export LANGUAGE # CDPATH. (unset CDPATH) >/dev/null 2>&1 && unset CDPATH # as_fn_error STATUS ERROR [LINENO LOG_FD] # ---------------------------------------- # Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are # provided, also output the error to LOG_FD, referencing LINENO. Then exit the # script with STATUS, using 1 if that was 0. as_fn_error () { as_status=$1; test $as_status -eq 0 && as_status=1 if test "$4"; then as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack $as_echo "$as_me:${as_lineno-$LINENO}: error: $2" >&$4 fi $as_echo "$as_me: error: $2" >&2 as_fn_exit $as_status } # as_fn_error # as_fn_set_status STATUS # ----------------------- # Set $? to STATUS, without forking. as_fn_set_status () { return $1 } # as_fn_set_status # as_fn_exit STATUS # ----------------- # Exit the shell with STATUS, even in a "trap 0" or "set -e" context. as_fn_exit () { set +e as_fn_set_status $1 exit $1 } # as_fn_exit # as_fn_unset VAR # --------------- # Portably unset VAR. as_fn_unset () { { eval $1=; unset $1;} } as_unset=as_fn_unset # as_fn_append VAR VALUE # ---------------------- # Append the text in VALUE to the end of the definition contained in VAR. Take # advantage of any shell optimizations that allow amortized linear growth over # repeated appends, instead of the typical quadratic growth present in naive # implementations. if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null; then : eval 'as_fn_append () { eval $1+=\$2 }' else as_fn_append () { eval $1=\$$1\$2 } fi # as_fn_append # as_fn_arith ARG... # ------------------ # Perform arithmetic evaluation on the ARGs, and store the result in the # global $as_val. Take advantage of shells that can avoid forks. The arguments # must be portable across $(()) and expr. if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null; then : eval 'as_fn_arith () { as_val=$(( $* )) }' else as_fn_arith () { as_val=`expr "$@" || test $? -eq 1` } fi # as_fn_arith if expr a : '\(a\)' >/dev/null 2>&1 && test "X`expr 00001 : '.*\(...\)'`" = X001; then as_expr=expr else as_expr=false fi if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then as_basename=basename else as_basename=false fi if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then as_dirname=dirname else as_dirname=false fi as_me=`$as_basename -- "$0" || $as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ X"$0" : 'X\(//\)$' \| \ X"$0" : 'X\(/\)' \| . 2>/dev/null || $as_echo X/"$0" | sed '/^.*\/\([^/][^/]*\)\/*$/{ s//\1/ q } /^X\/\(\/\/\)$/{ s//\1/ q } /^X\/\(\/\).*/{ s//\1/ q } s/.*/./; q'` # Avoid depending upon Character Ranges. as_cr_letters='abcdefghijklmnopqrstuvwxyz' as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' as_cr_Letters=$as_cr_letters$as_cr_LETTERS as_cr_digits='0123456789' as_cr_alnum=$as_cr_Letters$as_cr_digits ECHO_C= ECHO_N= ECHO_T= case `echo -n x` in #((((( -n*) case `echo 'xy\c'` in *c*) ECHO_T=' ';; # ECHO_T is single tab character. xy) ECHO_C='\c';; *) echo `echo ksh88 bug on AIX 6.1` > /dev/null ECHO_T=' ';; esac;; *) ECHO_N='-n';; esac rm -f conf$$ conf$$.exe conf$$.file if test -d conf$$.dir; then rm -f conf$$.dir/conf$$.file else rm -f conf$$.dir mkdir conf$$.dir 2>/dev/null fi if (echo >conf$$.file) 2>/dev/null; then if ln -s conf$$.file conf$$ 2>/dev/null; then as_ln_s='ln -s' # ... but there are two gotchas: # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. # In both cases, we have to default to `cp -pR'. ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || as_ln_s='cp -pR' elif ln conf$$.file conf$$ 2>/dev/null; then as_ln_s=ln else as_ln_s='cp -pR' fi else as_ln_s='cp -pR' fi rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file rmdir conf$$.dir 2>/dev/null # as_fn_mkdir_p # ------------- # Create "$as_dir" as a directory, including parents if necessary. as_fn_mkdir_p () { case $as_dir in #( -*) as_dir=./$as_dir;; esac test -d "$as_dir" || eval $as_mkdir_p || { as_dirs= while :; do case $as_dir in #( *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'( *) as_qdir=$as_dir;; esac as_dirs="'$as_qdir' $as_dirs" as_dir=`$as_dirname -- "$as_dir" || $as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$as_dir" : 'X\(//\)[^/]' \| \ X"$as_dir" : 'X\(//\)$' \| \ X"$as_dir" : 'X\(/\)' \| . 2>/dev/null || $as_echo X"$as_dir" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q'` test -d "$as_dir" && break done test -z "$as_dirs" || eval "mkdir $as_dirs" } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir" } # as_fn_mkdir_p if mkdir -p . 2>/dev/null; then as_mkdir_p='mkdir -p "$as_dir"' else test -d ./-p && rmdir ./-p as_mkdir_p=false fi # as_fn_executable_p FILE # ----------------------- # Test if FILE is an executable regular file. as_fn_executable_p () { test -f "$1" && test -x "$1" } # as_fn_executable_p as_test_x='test -x' as_executable_p=as_fn_executable_p # Sed expression to map a string onto a valid CPP name. as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" # Sed expression to map a string onto a valid variable name. as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" exec 6>&1 ## ----------------------------------- ## ## Main body of $CONFIG_STATUS script. ## ## ----------------------------------- ## _ASEOF test $as_write_fail = 0 && chmod +x $CONFIG_STATUS || ac_write_fail=1 cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # Save the log message, to keep $0 and so on meaningful, and to # report actual input values of CONFIG_FILES etc. instead of their # values after options handling. ac_log=" This file was extended by Telepathy Gabble $as_me 0.18.2, which was generated by GNU Autoconf 2.69. Invocation command line was CONFIG_FILES = $CONFIG_FILES CONFIG_HEADERS = $CONFIG_HEADERS CONFIG_LINKS = $CONFIG_LINKS CONFIG_COMMANDS = $CONFIG_COMMANDS $ $0 $@ on `(hostname || uname -n) 2>/dev/null | sed 1q` " _ACEOF case $ac_config_files in *" "*) set x $ac_config_files; shift; ac_config_files=$*;; esac case $ac_config_headers in *" "*) set x $ac_config_headers; shift; ac_config_headers=$*;; esac cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 # Files that config.status was made for. config_files="$ac_config_files" config_headers="$ac_config_headers" config_commands="$ac_config_commands" _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 ac_cs_usage="\ \`$as_me' instantiates files and other configuration actions from templates according to the current configuration. Unless the files and actions are specified as TAGs, all are instantiated by default. Usage: $0 [OPTION]... [TAG]... -h, --help print this help, then exit -V, --version print version number and configuration settings, then exit --config print configuration, then exit -q, --quiet, --silent do not print progress messages -d, --debug don't remove temporary files --recheck update $as_me by reconfiguring in the same conditions --file=FILE[:TEMPLATE] instantiate the configuration file FILE --header=FILE[:TEMPLATE] instantiate the configuration header FILE Configuration files: $config_files Configuration headers: $config_headers Configuration commands: $config_commands Report bugs to ." _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`" ac_cs_version="\\ Telepathy Gabble config.status 0.18.2 configured by $0, generated by GNU Autoconf 2.69, with options \\"\$ac_cs_config\\" Copyright (C) 2012 Free Software Foundation, Inc. This config.status script is free software; the Free Software Foundation gives unlimited permission to copy, distribute and modify it." ac_pwd='$ac_pwd' srcdir='$srcdir' INSTALL='$INSTALL' MKDIR_P='$MKDIR_P' AWK='$AWK' test -n "\$AWK" || AWK=awk _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # The default lists apply if the user does not specify any file. ac_need_defaults=: while test $# != 0 do case $1 in --*=?*) ac_option=`expr "X$1" : 'X\([^=]*\)='` ac_optarg=`expr "X$1" : 'X[^=]*=\(.*\)'` ac_shift=: ;; --*=) ac_option=`expr "X$1" : 'X\([^=]*\)='` ac_optarg= ac_shift=: ;; *) ac_option=$1 ac_optarg=$2 ac_shift=shift ;; esac case $ac_option in # Handling of the options. -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r) ac_cs_recheck=: ;; --version | --versio | --versi | --vers | --ver | --ve | --v | -V ) $as_echo "$ac_cs_version"; exit ;; --config | --confi | --conf | --con | --co | --c ) $as_echo "$ac_cs_config"; exit ;; --debug | --debu | --deb | --de | --d | -d ) debug=: ;; --file | --fil | --fi | --f ) $ac_shift case $ac_optarg in *\'*) ac_optarg=`$as_echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;; '') as_fn_error $? "missing file argument" ;; esac as_fn_append CONFIG_FILES " '$ac_optarg'" ac_need_defaults=false;; --header | --heade | --head | --hea ) $ac_shift case $ac_optarg in *\'*) ac_optarg=`$as_echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;; esac as_fn_append CONFIG_HEADERS " '$ac_optarg'" ac_need_defaults=false;; --he | --h) # Conflict between --help and --header as_fn_error $? "ambiguous option: \`$1' Try \`$0 --help' for more information.";; --help | --hel | -h ) $as_echo "$ac_cs_usage"; exit ;; -q | -quiet | --quiet | --quie | --qui | --qu | --q \ | -silent | --silent | --silen | --sile | --sil | --si | --s) ac_cs_silent=: ;; # This is an error. -*) as_fn_error $? "unrecognized option: \`$1' Try \`$0 --help' for more information." ;; *) as_fn_append ac_config_targets " $1" ac_need_defaults=false ;; esac shift done ac_configure_extra_args= if $ac_cs_silent; then exec 6>/dev/null ac_configure_extra_args="$ac_configure_extra_args --silent" fi _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 if \$ac_cs_recheck; then set X $SHELL '$0' $ac_configure_args \$ac_configure_extra_args --no-create --no-recursion shift \$as_echo "running CONFIG_SHELL=$SHELL \$*" >&6 CONFIG_SHELL='$SHELL' export CONFIG_SHELL exec "\$@" fi _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 exec 5>>config.log { echo sed 'h;s/./-/g;s/^.../## /;s/...$/ ##/;p;x;p;x' <<_ASBOX ## Running $as_me. ## _ASBOX $as_echo "$ac_log" } >&5 _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 # # INIT-COMMANDS # AMDEP_TRUE="$AMDEP_TRUE" ac_aux_dir="$ac_aux_dir" # The HP-UX ksh and POSIX shell print the target directory to stdout # if CDPATH is set. (unset CDPATH) >/dev/null 2>&1 && unset CDPATH sed_quote_subst='$sed_quote_subst' double_quote_subst='$double_quote_subst' delay_variable_subst='$delay_variable_subst' macro_version='`$ECHO "$macro_version" | $SED "$delay_single_quote_subst"`' macro_revision='`$ECHO "$macro_revision" | $SED "$delay_single_quote_subst"`' enable_shared='`$ECHO "$enable_shared" | $SED "$delay_single_quote_subst"`' enable_static='`$ECHO "$enable_static" | $SED "$delay_single_quote_subst"`' pic_mode='`$ECHO "$pic_mode" | $SED "$delay_single_quote_subst"`' enable_fast_install='`$ECHO "$enable_fast_install" | $SED "$delay_single_quote_subst"`' SHELL='`$ECHO "$SHELL" | $SED "$delay_single_quote_subst"`' ECHO='`$ECHO "$ECHO" | $SED "$delay_single_quote_subst"`' PATH_SEPARATOR='`$ECHO "$PATH_SEPARATOR" | $SED "$delay_single_quote_subst"`' host_alias='`$ECHO "$host_alias" | $SED "$delay_single_quote_subst"`' host='`$ECHO "$host" | $SED "$delay_single_quote_subst"`' host_os='`$ECHO "$host_os" | $SED "$delay_single_quote_subst"`' build_alias='`$ECHO "$build_alias" | $SED "$delay_single_quote_subst"`' build='`$ECHO "$build" | $SED "$delay_single_quote_subst"`' build_os='`$ECHO "$build_os" | $SED "$delay_single_quote_subst"`' SED='`$ECHO "$SED" | $SED "$delay_single_quote_subst"`' Xsed='`$ECHO "$Xsed" | $SED "$delay_single_quote_subst"`' GREP='`$ECHO "$GREP" | $SED "$delay_single_quote_subst"`' EGREP='`$ECHO "$EGREP" | $SED "$delay_single_quote_subst"`' FGREP='`$ECHO "$FGREP" | $SED "$delay_single_quote_subst"`' LD='`$ECHO "$LD" | $SED "$delay_single_quote_subst"`' NM='`$ECHO "$NM" | $SED "$delay_single_quote_subst"`' LN_S='`$ECHO "$LN_S" | $SED "$delay_single_quote_subst"`' max_cmd_len='`$ECHO "$max_cmd_len" | $SED "$delay_single_quote_subst"`' ac_objext='`$ECHO "$ac_objext" | $SED "$delay_single_quote_subst"`' exeext='`$ECHO "$exeext" | $SED "$delay_single_quote_subst"`' lt_unset='`$ECHO "$lt_unset" | $SED "$delay_single_quote_subst"`' lt_SP2NL='`$ECHO "$lt_SP2NL" | $SED "$delay_single_quote_subst"`' lt_NL2SP='`$ECHO "$lt_NL2SP" | $SED "$delay_single_quote_subst"`' lt_cv_to_host_file_cmd='`$ECHO "$lt_cv_to_host_file_cmd" | $SED "$delay_single_quote_subst"`' lt_cv_to_tool_file_cmd='`$ECHO "$lt_cv_to_tool_file_cmd" | $SED "$delay_single_quote_subst"`' reload_flag='`$ECHO "$reload_flag" | $SED "$delay_single_quote_subst"`' reload_cmds='`$ECHO "$reload_cmds" | $SED "$delay_single_quote_subst"`' OBJDUMP='`$ECHO "$OBJDUMP" | $SED "$delay_single_quote_subst"`' deplibs_check_method='`$ECHO "$deplibs_check_method" | $SED "$delay_single_quote_subst"`' file_magic_cmd='`$ECHO "$file_magic_cmd" | $SED "$delay_single_quote_subst"`' file_magic_glob='`$ECHO "$file_magic_glob" | $SED "$delay_single_quote_subst"`' want_nocaseglob='`$ECHO "$want_nocaseglob" | $SED "$delay_single_quote_subst"`' DLLTOOL='`$ECHO "$DLLTOOL" | $SED "$delay_single_quote_subst"`' sharedlib_from_linklib_cmd='`$ECHO "$sharedlib_from_linklib_cmd" | $SED "$delay_single_quote_subst"`' AR='`$ECHO "$AR" | $SED "$delay_single_quote_subst"`' AR_FLAGS='`$ECHO "$AR_FLAGS" | $SED "$delay_single_quote_subst"`' archiver_list_spec='`$ECHO "$archiver_list_spec" | $SED "$delay_single_quote_subst"`' STRIP='`$ECHO "$STRIP" | $SED "$delay_single_quote_subst"`' RANLIB='`$ECHO "$RANLIB" | $SED "$delay_single_quote_subst"`' old_postinstall_cmds='`$ECHO "$old_postinstall_cmds" | $SED "$delay_single_quote_subst"`' old_postuninstall_cmds='`$ECHO "$old_postuninstall_cmds" | $SED "$delay_single_quote_subst"`' old_archive_cmds='`$ECHO "$old_archive_cmds" | $SED "$delay_single_quote_subst"`' lock_old_archive_extraction='`$ECHO "$lock_old_archive_extraction" | $SED "$delay_single_quote_subst"`' CC='`$ECHO "$CC" | $SED "$delay_single_quote_subst"`' CFLAGS='`$ECHO "$CFLAGS" | $SED "$delay_single_quote_subst"`' compiler='`$ECHO "$compiler" | $SED "$delay_single_quote_subst"`' GCC='`$ECHO "$GCC" | $SED "$delay_single_quote_subst"`' lt_cv_sys_global_symbol_pipe='`$ECHO "$lt_cv_sys_global_symbol_pipe" | $SED "$delay_single_quote_subst"`' lt_cv_sys_global_symbol_to_cdecl='`$ECHO "$lt_cv_sys_global_symbol_to_cdecl" | $SED "$delay_single_quote_subst"`' lt_cv_sys_global_symbol_to_c_name_address='`$ECHO "$lt_cv_sys_global_symbol_to_c_name_address" | $SED "$delay_single_quote_subst"`' lt_cv_sys_global_symbol_to_c_name_address_lib_prefix='`$ECHO "$lt_cv_sys_global_symbol_to_c_name_address_lib_prefix" | $SED "$delay_single_quote_subst"`' nm_file_list_spec='`$ECHO "$nm_file_list_spec" | $SED "$delay_single_quote_subst"`' lt_sysroot='`$ECHO "$lt_sysroot" | $SED "$delay_single_quote_subst"`' objdir='`$ECHO "$objdir" | $SED "$delay_single_quote_subst"`' MAGIC_CMD='`$ECHO "$MAGIC_CMD" | $SED "$delay_single_quote_subst"`' lt_prog_compiler_no_builtin_flag='`$ECHO "$lt_prog_compiler_no_builtin_flag" | $SED "$delay_single_quote_subst"`' lt_prog_compiler_pic='`$ECHO "$lt_prog_compiler_pic" | $SED "$delay_single_quote_subst"`' lt_prog_compiler_wl='`$ECHO "$lt_prog_compiler_wl" | $SED "$delay_single_quote_subst"`' lt_prog_compiler_static='`$ECHO "$lt_prog_compiler_static" | $SED "$delay_single_quote_subst"`' lt_cv_prog_compiler_c_o='`$ECHO "$lt_cv_prog_compiler_c_o" | $SED "$delay_single_quote_subst"`' need_locks='`$ECHO "$need_locks" | $SED "$delay_single_quote_subst"`' MANIFEST_TOOL='`$ECHO "$MANIFEST_TOOL" | $SED "$delay_single_quote_subst"`' DSYMUTIL='`$ECHO "$DSYMUTIL" | $SED "$delay_single_quote_subst"`' NMEDIT='`$ECHO "$NMEDIT" | $SED "$delay_single_quote_subst"`' LIPO='`$ECHO "$LIPO" | $SED "$delay_single_quote_subst"`' OTOOL='`$ECHO "$OTOOL" | $SED "$delay_single_quote_subst"`' OTOOL64='`$ECHO "$OTOOL64" | $SED "$delay_single_quote_subst"`' libext='`$ECHO "$libext" | $SED "$delay_single_quote_subst"`' shrext_cmds='`$ECHO "$shrext_cmds" | $SED "$delay_single_quote_subst"`' extract_expsyms_cmds='`$ECHO "$extract_expsyms_cmds" | $SED "$delay_single_quote_subst"`' archive_cmds_need_lc='`$ECHO "$archive_cmds_need_lc" | $SED "$delay_single_quote_subst"`' enable_shared_with_static_runtimes='`$ECHO "$enable_shared_with_static_runtimes" | $SED "$delay_single_quote_subst"`' export_dynamic_flag_spec='`$ECHO "$export_dynamic_flag_spec" | $SED "$delay_single_quote_subst"`' whole_archive_flag_spec='`$ECHO "$whole_archive_flag_spec" | $SED "$delay_single_quote_subst"`' compiler_needs_object='`$ECHO "$compiler_needs_object" | $SED "$delay_single_quote_subst"`' old_archive_from_new_cmds='`$ECHO "$old_archive_from_new_cmds" | $SED "$delay_single_quote_subst"`' old_archive_from_expsyms_cmds='`$ECHO "$old_archive_from_expsyms_cmds" | $SED "$delay_single_quote_subst"`' archive_cmds='`$ECHO "$archive_cmds" | $SED "$delay_single_quote_subst"`' archive_expsym_cmds='`$ECHO "$archive_expsym_cmds" | $SED "$delay_single_quote_subst"`' module_cmds='`$ECHO "$module_cmds" | $SED "$delay_single_quote_subst"`' module_expsym_cmds='`$ECHO "$module_expsym_cmds" | $SED "$delay_single_quote_subst"`' with_gnu_ld='`$ECHO "$with_gnu_ld" | $SED "$delay_single_quote_subst"`' allow_undefined_flag='`$ECHO "$allow_undefined_flag" | $SED "$delay_single_quote_subst"`' no_undefined_flag='`$ECHO "$no_undefined_flag" | $SED "$delay_single_quote_subst"`' hardcode_libdir_flag_spec='`$ECHO "$hardcode_libdir_flag_spec" | $SED "$delay_single_quote_subst"`' hardcode_libdir_separator='`$ECHO "$hardcode_libdir_separator" | $SED "$delay_single_quote_subst"`' hardcode_direct='`$ECHO "$hardcode_direct" | $SED "$delay_single_quote_subst"`' hardcode_direct_absolute='`$ECHO "$hardcode_direct_absolute" | $SED "$delay_single_quote_subst"`' hardcode_minus_L='`$ECHO "$hardcode_minus_L" | $SED "$delay_single_quote_subst"`' hardcode_shlibpath_var='`$ECHO "$hardcode_shlibpath_var" | $SED "$delay_single_quote_subst"`' hardcode_automatic='`$ECHO "$hardcode_automatic" | $SED "$delay_single_quote_subst"`' inherit_rpath='`$ECHO "$inherit_rpath" | $SED "$delay_single_quote_subst"`' link_all_deplibs='`$ECHO "$link_all_deplibs" | $SED "$delay_single_quote_subst"`' always_export_symbols='`$ECHO "$always_export_symbols" | $SED "$delay_single_quote_subst"`' export_symbols_cmds='`$ECHO "$export_symbols_cmds" | $SED "$delay_single_quote_subst"`' exclude_expsyms='`$ECHO "$exclude_expsyms" | $SED "$delay_single_quote_subst"`' include_expsyms='`$ECHO "$include_expsyms" | $SED "$delay_single_quote_subst"`' prelink_cmds='`$ECHO "$prelink_cmds" | $SED "$delay_single_quote_subst"`' postlink_cmds='`$ECHO "$postlink_cmds" | $SED "$delay_single_quote_subst"`' file_list_spec='`$ECHO "$file_list_spec" | $SED "$delay_single_quote_subst"`' variables_saved_for_relink='`$ECHO "$variables_saved_for_relink" | $SED "$delay_single_quote_subst"`' need_lib_prefix='`$ECHO "$need_lib_prefix" | $SED "$delay_single_quote_subst"`' need_version='`$ECHO "$need_version" | $SED "$delay_single_quote_subst"`' version_type='`$ECHO "$version_type" | $SED "$delay_single_quote_subst"`' runpath_var='`$ECHO "$runpath_var" | $SED "$delay_single_quote_subst"`' shlibpath_var='`$ECHO "$shlibpath_var" | $SED "$delay_single_quote_subst"`' shlibpath_overrides_runpath='`$ECHO "$shlibpath_overrides_runpath" | $SED "$delay_single_quote_subst"`' libname_spec='`$ECHO "$libname_spec" | $SED "$delay_single_quote_subst"`' library_names_spec='`$ECHO "$library_names_spec" | $SED "$delay_single_quote_subst"`' soname_spec='`$ECHO "$soname_spec" | $SED "$delay_single_quote_subst"`' install_override_mode='`$ECHO "$install_override_mode" | $SED "$delay_single_quote_subst"`' postinstall_cmds='`$ECHO "$postinstall_cmds" | $SED "$delay_single_quote_subst"`' postuninstall_cmds='`$ECHO "$postuninstall_cmds" | $SED "$delay_single_quote_subst"`' finish_cmds='`$ECHO "$finish_cmds" | $SED "$delay_single_quote_subst"`' finish_eval='`$ECHO "$finish_eval" | $SED "$delay_single_quote_subst"`' hardcode_into_libs='`$ECHO "$hardcode_into_libs" | $SED "$delay_single_quote_subst"`' sys_lib_search_path_spec='`$ECHO "$sys_lib_search_path_spec" | $SED "$delay_single_quote_subst"`' sys_lib_dlsearch_path_spec='`$ECHO "$sys_lib_dlsearch_path_spec" | $SED "$delay_single_quote_subst"`' hardcode_action='`$ECHO "$hardcode_action" | $SED "$delay_single_quote_subst"`' enable_dlopen='`$ECHO "$enable_dlopen" | $SED "$delay_single_quote_subst"`' enable_dlopen_self='`$ECHO "$enable_dlopen_self" | $SED "$delay_single_quote_subst"`' enable_dlopen_self_static='`$ECHO "$enable_dlopen_self_static" | $SED "$delay_single_quote_subst"`' old_striplib='`$ECHO "$old_striplib" | $SED "$delay_single_quote_subst"`' striplib='`$ECHO "$striplib" | $SED "$delay_single_quote_subst"`' LTCC='$LTCC' LTCFLAGS='$LTCFLAGS' compiler='$compiler_DEFAULT' # A function that is used when there is no print builtin or printf. func_fallback_echo () { eval 'cat <<_LTECHO_EOF \$1 _LTECHO_EOF' } # Quote evaled strings. for var in SHELL \ ECHO \ PATH_SEPARATOR \ SED \ GREP \ EGREP \ FGREP \ LD \ NM \ LN_S \ lt_SP2NL \ lt_NL2SP \ reload_flag \ OBJDUMP \ deplibs_check_method \ file_magic_cmd \ file_magic_glob \ want_nocaseglob \ DLLTOOL \ sharedlib_from_linklib_cmd \ AR \ AR_FLAGS \ archiver_list_spec \ STRIP \ RANLIB \ CC \ CFLAGS \ compiler \ lt_cv_sys_global_symbol_pipe \ lt_cv_sys_global_symbol_to_cdecl \ lt_cv_sys_global_symbol_to_c_name_address \ lt_cv_sys_global_symbol_to_c_name_address_lib_prefix \ nm_file_list_spec \ lt_prog_compiler_no_builtin_flag \ lt_prog_compiler_pic \ lt_prog_compiler_wl \ lt_prog_compiler_static \ lt_cv_prog_compiler_c_o \ need_locks \ MANIFEST_TOOL \ DSYMUTIL \ NMEDIT \ LIPO \ OTOOL \ OTOOL64 \ shrext_cmds \ export_dynamic_flag_spec \ whole_archive_flag_spec \ compiler_needs_object \ with_gnu_ld \ allow_undefined_flag \ no_undefined_flag \ hardcode_libdir_flag_spec \ hardcode_libdir_separator \ exclude_expsyms \ include_expsyms \ file_list_spec \ variables_saved_for_relink \ libname_spec \ library_names_spec \ soname_spec \ install_override_mode \ finish_eval \ old_striplib \ striplib; do case \`eval \\\\\$ECHO \\\\""\\\\\$\$var"\\\\"\` in *[\\\\\\\`\\"\\\$]*) eval "lt_\$var=\\\\\\"\\\`\\\$ECHO \\"\\\$\$var\\" | \\\$SED \\"\\\$sed_quote_subst\\"\\\`\\\\\\"" ;; *) eval "lt_\$var=\\\\\\"\\\$\$var\\\\\\"" ;; esac done # Double-quote double-evaled strings. for var in reload_cmds \ old_postinstall_cmds \ old_postuninstall_cmds \ old_archive_cmds \ extract_expsyms_cmds \ old_archive_from_new_cmds \ old_archive_from_expsyms_cmds \ archive_cmds \ archive_expsym_cmds \ module_cmds \ module_expsym_cmds \ export_symbols_cmds \ prelink_cmds \ postlink_cmds \ postinstall_cmds \ postuninstall_cmds \ finish_cmds \ sys_lib_search_path_spec \ sys_lib_dlsearch_path_spec; do case \`eval \\\\\$ECHO \\\\""\\\\\$\$var"\\\\"\` in *[\\\\\\\`\\"\\\$]*) eval "lt_\$var=\\\\\\"\\\`\\\$ECHO \\"\\\$\$var\\" | \\\$SED -e \\"\\\$double_quote_subst\\" -e \\"\\\$sed_quote_subst\\" -e \\"\\\$delay_variable_subst\\"\\\`\\\\\\"" ;; *) eval "lt_\$var=\\\\\\"\\\$\$var\\\\\\"" ;; esac done ac_aux_dir='$ac_aux_dir' xsi_shell='$xsi_shell' lt_shell_append='$lt_shell_append' # See if we are running on zsh, and set the options which allow our # commands through without removal of \ escapes INIT. if test -n "\${ZSH_VERSION+set}" ; then setopt NO_GLOB_SUBST fi PACKAGE='$PACKAGE' VERSION='$VERSION' TIMESTAMP='$TIMESTAMP' RM='$RM' ofile='$ofile' _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # Handling of arguments. for ac_config_target in $ac_config_targets do case $ac_config_target in "config.h") CONFIG_HEADERS="$CONFIG_HEADERS config.h" ;; "depfiles") CONFIG_COMMANDS="$CONFIG_COMMANDS depfiles" ;; "libtool") CONFIG_COMMANDS="$CONFIG_COMMANDS libtool" ;; "Makefile") CONFIG_FILES="$CONFIG_FILES Makefile" ;; "docs/Makefile") CONFIG_FILES="$CONFIG_FILES docs/Makefile" ;; "extensions/Makefile") CONFIG_FILES="$CONFIG_FILES extensions/Makefile" ;; "src/Makefile") CONFIG_FILES="$CONFIG_FILES src/Makefile" ;; "m4/Makefile") CONFIG_FILES="$CONFIG_FILES m4/Makefile" ;; "data/Makefile") CONFIG_FILES="$CONFIG_FILES data/Makefile" ;; "tests/Makefile") CONFIG_FILES="$CONFIG_FILES tests/Makefile" ;; "tools/Makefile") CONFIG_FILES="$CONFIG_FILES tools/Makefile" ;; "tests/suppressions/Makefile") CONFIG_FILES="$CONFIG_FILES tests/suppressions/Makefile" ;; "tests/twisted/Makefile") CONFIG_FILES="$CONFIG_FILES tests/twisted/Makefile" ;; "lib/Makefile") CONFIG_FILES="$CONFIG_FILES lib/Makefile" ;; "lib/ext/Makefile") CONFIG_FILES="$CONFIG_FILES lib/ext/Makefile" ;; "lib/gibber/Makefile") CONFIG_FILES="$CONFIG_FILES lib/gibber/Makefile" ;; "plugins/Makefile") CONFIG_FILES="$CONFIG_FILES plugins/Makefile" ;; "gabble/Makefile") CONFIG_FILES="$CONFIG_FILES gabble/Makefile" ;; "gabble/telepathy-gabble-uninstalled.pc") CONFIG_FILES="$CONFIG_FILES gabble/telepathy-gabble-uninstalled.pc" ;; "gabble/telepathy-gabble.pc") CONFIG_FILES="$CONFIG_FILES gabble/telepathy-gabble.pc" ;; *) as_fn_error $? "invalid argument: \`$ac_config_target'" "$LINENO" 5;; esac done # If the user did not use the arguments to specify the items to instantiate, # then the envvar interface is used. Set only those that are not. # We use the long form for the default assignment because of an extremely # bizarre bug on SunOS 4.1.3. if $ac_need_defaults; then test "${CONFIG_FILES+set}" = set || CONFIG_FILES=$config_files test "${CONFIG_HEADERS+set}" = set || CONFIG_HEADERS=$config_headers test "${CONFIG_COMMANDS+set}" = set || CONFIG_COMMANDS=$config_commands fi # Have a temporary directory for convenience. Make it in the build tree # simply because there is no reason against having it here, and in addition, # creating and moving files from /tmp can sometimes cause problems. # Hook for its removal unless debugging. # Note that there is a small window in which the directory will not be cleaned: # after its creation but before its name has been assigned to `$tmp'. $debug || { tmp= ac_tmp= trap 'exit_status=$? : "${ac_tmp:=$tmp}" { test ! -d "$ac_tmp" || rm -fr "$ac_tmp"; } && exit $exit_status ' 0 trap 'as_fn_exit 1' 1 2 13 15 } # Create a (secure) tmp directory for tmp files. { tmp=`(umask 077 && mktemp -d "./confXXXXXX") 2>/dev/null` && test -d "$tmp" } || { tmp=./conf$$-$RANDOM (umask 077 && mkdir "$tmp") } || as_fn_error $? "cannot create a temporary directory in ." "$LINENO" 5 ac_tmp=$tmp # Set up the scripts for CONFIG_FILES section. # No need to generate them if there are no CONFIG_FILES. # This happens for instance with `./config.status config.h'. if test -n "$CONFIG_FILES"; then ac_cr=`echo X | tr X '\015'` # On cygwin, bash can eat \r inside `` if the user requested igncr. # But we know of no other shell where ac_cr would be empty at this # point, so we can use a bashism as a fallback. if test "x$ac_cr" = x; then eval ac_cr=\$\'\\r\' fi ac_cs_awk_cr=`$AWK 'BEGIN { print "a\rb" }' /dev/null` if test "$ac_cs_awk_cr" = "a${ac_cr}b"; then ac_cs_awk_cr='\\r' else ac_cs_awk_cr=$ac_cr fi echo 'BEGIN {' >"$ac_tmp/subs1.awk" && _ACEOF { echo "cat >conf$$subs.awk <<_ACEOF" && echo "$ac_subst_vars" | sed 's/.*/&!$&$ac_delim/' && echo "_ACEOF" } >conf$$subs.sh || as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 ac_delim_num=`echo "$ac_subst_vars" | grep -c '^'` ac_delim='%!_!# ' for ac_last_try in false false false false false :; do . ./conf$$subs.sh || as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 ac_delim_n=`sed -n "s/.*$ac_delim\$/X/p" conf$$subs.awk | grep -c X` if test $ac_delim_n = $ac_delim_num; then break elif $ac_last_try; then as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 else ac_delim="$ac_delim!$ac_delim _$ac_delim!! " fi done rm -f conf$$subs.sh cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 cat >>"\$ac_tmp/subs1.awk" <<\\_ACAWK && _ACEOF sed -n ' h s/^/S["/; s/!.*/"]=/ p g s/^[^!]*!// :repl t repl s/'"$ac_delim"'$// t delim :nl h s/\(.\{148\}\)..*/\1/ t more1 s/["\\]/\\&/g; s/^/"/; s/$/\\n"\\/ p n b repl :more1 s/["\\]/\\&/g; s/^/"/; s/$/"\\/ p g s/.\{148\}// t nl :delim h s/\(.\{148\}\)..*/\1/ t more2 s/["\\]/\\&/g; s/^/"/; s/$/"/ p b :more2 s/["\\]/\\&/g; s/^/"/; s/$/"\\/ p g s/.\{148\}// t delim ' >$CONFIG_STATUS || ac_write_fail=1 rm -f conf$$subs.awk cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 _ACAWK cat >>"\$ac_tmp/subs1.awk" <<_ACAWK && for (key in S) S_is_set[key] = 1 FS = "" } { line = $ 0 nfields = split(line, field, "@") substed = 0 len = length(field[1]) for (i = 2; i < nfields; i++) { key = field[i] keylen = length(key) if (S_is_set[key]) { value = S[key] line = substr(line, 1, len) "" value "" substr(line, len + keylen + 3) len += length(value) + length(field[++i]) substed = 1 } else len += 1 + keylen } print line } _ACAWK _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 if sed "s/$ac_cr//" < /dev/null > /dev/null 2>&1; then sed "s/$ac_cr\$//; s/$ac_cr/$ac_cs_awk_cr/g" else cat fi < "$ac_tmp/subs1.awk" > "$ac_tmp/subs.awk" \ || as_fn_error $? "could not setup config files machinery" "$LINENO" 5 _ACEOF # VPATH may cause trouble with some makes, so we remove sole $(srcdir), # ${srcdir} and @srcdir@ entries from VPATH if srcdir is ".", strip leading and # trailing colons and then remove the whole line if VPATH becomes empty # (actually we leave an empty line to preserve line numbers). if test "x$srcdir" = x.; then ac_vpsub='/^[ ]*VPATH[ ]*=[ ]*/{ h s/// s/^/:/ s/[ ]*$/:/ s/:\$(srcdir):/:/g s/:\${srcdir}:/:/g s/:@srcdir@:/:/g s/^:*// s/:*$// x s/\(=[ ]*\).*/\1/ G s/\n// s/^[^=]*=[ ]*$// }' fi cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 fi # test -n "$CONFIG_FILES" # Set up the scripts for CONFIG_HEADERS section. # No need to generate them if there are no CONFIG_HEADERS. # This happens for instance with `./config.status Makefile'. if test -n "$CONFIG_HEADERS"; then cat >"$ac_tmp/defines.awk" <<\_ACAWK || BEGIN { _ACEOF # Transform confdefs.h into an awk script `defines.awk', embedded as # here-document in config.status, that substitutes the proper values into # config.h.in to produce config.h. # Create a delimiter string that does not exist in confdefs.h, to ease # handling of long lines. ac_delim='%!_!# ' for ac_last_try in false false :; do ac_tt=`sed -n "/$ac_delim/p" confdefs.h` if test -z "$ac_tt"; then break elif $ac_last_try; then as_fn_error $? "could not make $CONFIG_HEADERS" "$LINENO" 5 else ac_delim="$ac_delim!$ac_delim _$ac_delim!! " fi done # For the awk script, D is an array of macro values keyed by name, # likewise P contains macro parameters if any. Preserve backslash # newline sequences. ac_word_re=[_$as_cr_Letters][_$as_cr_alnum]* sed -n ' s/.\{148\}/&'"$ac_delim"'/g t rset :rset s/^[ ]*#[ ]*define[ ][ ]*/ / t def d :def s/\\$// t bsnl s/["\\]/\\&/g s/^ \('"$ac_word_re"'\)\(([^()]*)\)[ ]*\(.*\)/P["\1"]="\2"\ D["\1"]=" \3"/p s/^ \('"$ac_word_re"'\)[ ]*\(.*\)/D["\1"]=" \2"/p d :bsnl s/["\\]/\\&/g s/^ \('"$ac_word_re"'\)\(([^()]*)\)[ ]*\(.*\)/P["\1"]="\2"\ D["\1"]=" \3\\\\\\n"\\/p t cont s/^ \('"$ac_word_re"'\)[ ]*\(.*\)/D["\1"]=" \2\\\\\\n"\\/p t cont d :cont n s/.\{148\}/&'"$ac_delim"'/g t clear :clear s/\\$// t bsnlc s/["\\]/\\&/g; s/^/"/; s/$/"/p d :bsnlc s/["\\]/\\&/g; s/^/"/; s/$/\\\\\\n"\\/p b cont ' >$CONFIG_STATUS || ac_write_fail=1 cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 for (key in D) D_is_set[key] = 1 FS = "" } /^[\t ]*#[\t ]*(define|undef)[\t ]+$ac_word_re([\t (]|\$)/ { line = \$ 0 split(line, arg, " ") if (arg[1] == "#") { defundef = arg[2] mac1 = arg[3] } else { defundef = substr(arg[1], 2) mac1 = arg[2] } split(mac1, mac2, "(") #) macro = mac2[1] prefix = substr(line, 1, index(line, defundef) - 1) if (D_is_set[macro]) { # Preserve the white space surrounding the "#". print prefix "define", macro P[macro] D[macro] next } else { # Replace #undef with comments. This is necessary, for example, # in the case of _POSIX_SOURCE, which is predefined and required # on some systems where configure will not decide to define it. if (defundef == "undef") { print "/*", prefix defundef, macro, "*/" next } } } { print } _ACAWK _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 as_fn_error $? "could not setup config headers machinery" "$LINENO" 5 fi # test -n "$CONFIG_HEADERS" eval set X " :F $CONFIG_FILES :H $CONFIG_HEADERS :C $CONFIG_COMMANDS" shift for ac_tag do case $ac_tag in :[FHLC]) ac_mode=$ac_tag; continue;; esac case $ac_mode$ac_tag in :[FHL]*:*);; :L* | :C*:*) as_fn_error $? "invalid tag \`$ac_tag'" "$LINENO" 5;; :[FH]-) ac_tag=-:-;; :[FH]*) ac_tag=$ac_tag:$ac_tag.in;; esac ac_save_IFS=$IFS IFS=: set x $ac_tag IFS=$ac_save_IFS shift ac_file=$1 shift case $ac_mode in :L) ac_source=$1;; :[FH]) ac_file_inputs= for ac_f do case $ac_f in -) ac_f="$ac_tmp/stdin";; *) # Look for the file first in the build tree, then in the source tree # (if the path is not absolute). The absolute path cannot be DOS-style, # because $ac_f cannot contain `:'. test -f "$ac_f" || case $ac_f in [\\/$]*) false;; *) test -f "$srcdir/$ac_f" && ac_f="$srcdir/$ac_f";; esac || as_fn_error 1 "cannot find input file: \`$ac_f'" "$LINENO" 5;; esac case $ac_f in *\'*) ac_f=`$as_echo "$ac_f" | sed "s/'/'\\\\\\\\''/g"`;; esac as_fn_append ac_file_inputs " '$ac_f'" done # Let's still pretend it is `configure' which instantiates (i.e., don't # use $as_me), people would be surprised to read: # /* config.h. Generated by config.status. */ configure_input='Generated from '` $as_echo "$*" | sed 's|^[^:]*/||;s|:[^:]*/|, |g' `' by configure.' if test x"$ac_file" != x-; then configure_input="$ac_file. $configure_input" { $as_echo "$as_me:${as_lineno-$LINENO}: creating $ac_file" >&5 $as_echo "$as_me: creating $ac_file" >&6;} fi # Neutralize special characters interpreted by sed in replacement strings. case $configure_input in #( *\&* | *\|* | *\\* ) ac_sed_conf_input=`$as_echo "$configure_input" | sed 's/[\\\\&|]/\\\\&/g'`;; #( *) ac_sed_conf_input=$configure_input;; esac case $ac_tag in *:-:* | *:-) cat >"$ac_tmp/stdin" \ || as_fn_error $? "could not create $ac_file" "$LINENO" 5 ;; esac ;; esac ac_dir=`$as_dirname -- "$ac_file" || $as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$ac_file" : 'X\(//\)[^/]' \| \ X"$ac_file" : 'X\(//\)$' \| \ X"$ac_file" : 'X\(/\)' \| . 2>/dev/null || $as_echo X"$ac_file" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q'` as_dir="$ac_dir"; as_fn_mkdir_p ac_builddir=. case "$ac_dir" in .) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;; *) ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'` # A ".." for each directory in $ac_dir_suffix. ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'` case $ac_top_builddir_sub in "") ac_top_builddir_sub=. ac_top_build_prefix= ;; *) ac_top_build_prefix=$ac_top_builddir_sub/ ;; esac ;; esac ac_abs_top_builddir=$ac_pwd ac_abs_builddir=$ac_pwd$ac_dir_suffix # for backward compatibility: ac_top_builddir=$ac_top_build_prefix case $srcdir in .) # We are building in place. ac_srcdir=. ac_top_srcdir=$ac_top_builddir_sub ac_abs_top_srcdir=$ac_pwd ;; [\\/]* | ?:[\\/]* ) # Absolute name. ac_srcdir=$srcdir$ac_dir_suffix; ac_top_srcdir=$srcdir ac_abs_top_srcdir=$srcdir ;; *) # Relative name. ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix ac_top_srcdir=$ac_top_build_prefix$srcdir ac_abs_top_srcdir=$ac_pwd/$srcdir ;; esac ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix case $ac_mode in :F) # # CONFIG_FILE # case $INSTALL in [\\/$]* | ?:[\\/]* ) ac_INSTALL=$INSTALL ;; *) ac_INSTALL=$ac_top_build_prefix$INSTALL ;; esac ac_MKDIR_P=$MKDIR_P case $MKDIR_P in [\\/$]* | ?:[\\/]* ) ;; */*) ac_MKDIR_P=$ac_top_build_prefix$MKDIR_P ;; esac _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # If the template does not know about datarootdir, expand it. # FIXME: This hack should be removed a few years after 2.60. ac_datarootdir_hack=; ac_datarootdir_seen= ac_sed_dataroot=' /datarootdir/ { p q } /@datadir@/p /@docdir@/p /@infodir@/p /@localedir@/p /@mandir@/p' case `eval "sed -n \"\$ac_sed_dataroot\" $ac_file_inputs"` in *datarootdir*) ac_datarootdir_seen=yes;; *@datadir@*|*@docdir@*|*@infodir@*|*@localedir@*|*@mandir@*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&5 $as_echo "$as_me: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&2;} _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_datarootdir_hack=' s&@datadir@&$datadir&g s&@docdir@&$docdir&g s&@infodir@&$infodir&g s&@localedir@&$localedir&g s&@mandir@&$mandir&g s&\\\${datarootdir}&$datarootdir&g' ;; esac _ACEOF # Neutralize VPATH when `$srcdir' = `.'. # Shell code in configure.ac might set extrasub. # FIXME: do we really want to maintain this feature? cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_sed_extra="$ac_vpsub $extrasub _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 :t /@[a-zA-Z_][a-zA-Z_0-9]*@/!b s|@configure_input@|$ac_sed_conf_input|;t t s&@top_builddir@&$ac_top_builddir_sub&;t t s&@top_build_prefix@&$ac_top_build_prefix&;t t s&@srcdir@&$ac_srcdir&;t t s&@abs_srcdir@&$ac_abs_srcdir&;t t s&@top_srcdir@&$ac_top_srcdir&;t t s&@abs_top_srcdir@&$ac_abs_top_srcdir&;t t s&@builddir@&$ac_builddir&;t t s&@abs_builddir@&$ac_abs_builddir&;t t s&@abs_top_builddir@&$ac_abs_top_builddir&;t t s&@INSTALL@&$ac_INSTALL&;t t s&@MKDIR_P@&$ac_MKDIR_P&;t t $ac_datarootdir_hack " eval sed \"\$ac_sed_extra\" "$ac_file_inputs" | $AWK -f "$ac_tmp/subs.awk" \ >$ac_tmp/out || as_fn_error $? "could not create $ac_file" "$LINENO" 5 test -z "$ac_datarootdir_hack$ac_datarootdir_seen" && { ac_out=`sed -n '/\${datarootdir}/p' "$ac_tmp/out"`; test -n "$ac_out"; } && { ac_out=`sed -n '/^[ ]*datarootdir[ ]*:*=/p' \ "$ac_tmp/out"`; test -z "$ac_out"; } && { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file contains a reference to the variable \`datarootdir' which seems to be undefined. Please make sure it is defined" >&5 $as_echo "$as_me: WARNING: $ac_file contains a reference to the variable \`datarootdir' which seems to be undefined. Please make sure it is defined" >&2;} rm -f "$ac_tmp/stdin" case $ac_file in -) cat "$ac_tmp/out" && rm -f "$ac_tmp/out";; *) rm -f "$ac_file" && mv "$ac_tmp/out" "$ac_file";; esac \ || as_fn_error $? "could not create $ac_file" "$LINENO" 5 ;; :H) # # CONFIG_HEADER # if test x"$ac_file" != x-; then { $as_echo "/* $configure_input */" \ && eval '$AWK -f "$ac_tmp/defines.awk"' "$ac_file_inputs" } >"$ac_tmp/config.h" \ || as_fn_error $? "could not create $ac_file" "$LINENO" 5 if diff "$ac_file" "$ac_tmp/config.h" >/dev/null 2>&1; then { $as_echo "$as_me:${as_lineno-$LINENO}: $ac_file is unchanged" >&5 $as_echo "$as_me: $ac_file is unchanged" >&6;} else rm -f "$ac_file" mv "$ac_tmp/config.h" "$ac_file" \ || as_fn_error $? "could not create $ac_file" "$LINENO" 5 fi else $as_echo "/* $configure_input */" \ && eval '$AWK -f "$ac_tmp/defines.awk"' "$ac_file_inputs" \ || as_fn_error $? "could not create -" "$LINENO" 5 fi # Compute "$ac_file"'s index in $config_headers. _am_arg="$ac_file" _am_stamp_count=1 for _am_header in $config_headers :; do case $_am_header in $_am_arg | $_am_arg:* ) break ;; * ) _am_stamp_count=`expr $_am_stamp_count + 1` ;; esac done echo "timestamp for $_am_arg" >`$as_dirname -- "$_am_arg" || $as_expr X"$_am_arg" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$_am_arg" : 'X\(//\)[^/]' \| \ X"$_am_arg" : 'X\(//\)$' \| \ X"$_am_arg" : 'X\(/\)' \| . 2>/dev/null || $as_echo X"$_am_arg" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q'`/stamp-h$_am_stamp_count ;; :C) { $as_echo "$as_me:${as_lineno-$LINENO}: executing $ac_file commands" >&5 $as_echo "$as_me: executing $ac_file commands" >&6;} ;; esac case $ac_file$ac_mode in "depfiles":C) test x"$AMDEP_TRUE" != x"" || { # Older Autoconf quotes --file arguments for eval, but not when files # are listed without --file. Let's play safe and only enable the eval # if we detect the quoting. case $CONFIG_FILES in *\'*) eval set x "$CONFIG_FILES" ;; *) set x $CONFIG_FILES ;; esac shift for mf do # Strip MF so we end up with the name of the file. mf=`echo "$mf" | sed -e 's/:.*$//'` # Check whether this is an Automake generated Makefile or not. # We used to match only the files named 'Makefile.in', but # some people rename them; so instead we look at the file content. # Grep'ing the first line is not enough: some people post-process # each Makefile.in and add a new line on top of each file to say so. # Grep'ing the whole file is not good either: AIX grep has a line # limit of 2048, but all sed's we know have understand at least 4000. if sed -n 's,^#.*generated by automake.*,X,p' "$mf" | grep X >/dev/null 2>&1; then dirpart=`$as_dirname -- "$mf" || $as_expr X"$mf" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$mf" : 'X\(//\)[^/]' \| \ X"$mf" : 'X\(//\)$' \| \ X"$mf" : 'X\(/\)' \| . 2>/dev/null || $as_echo X"$mf" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q'` else continue fi # Extract the definition of DEPDIR, am__include, and am__quote # from the Makefile without running 'make'. DEPDIR=`sed -n 's/^DEPDIR = //p' < "$mf"` test -z "$DEPDIR" && continue am__include=`sed -n 's/^am__include = //p' < "$mf"` test -z "$am__include" && continue am__quote=`sed -n 's/^am__quote = //p' < "$mf"` # Find all dependency output files, they are included files with # $(DEPDIR) in their names. We invoke sed twice because it is the # simplest approach to changing $(DEPDIR) to its actual value in the # expansion. for file in `sed -n " s/^$am__include $am__quote\(.*(DEPDIR).*\)$am__quote"'$/\1/p' <"$mf" | \ sed -e 's/\$(DEPDIR)/'"$DEPDIR"'/g'`; do # Make sure the directory exists. test -f "$dirpart/$file" && continue fdir=`$as_dirname -- "$file" || $as_expr X"$file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$file" : 'X\(//\)[^/]' \| \ X"$file" : 'X\(//\)$' \| \ X"$file" : 'X\(/\)' \| . 2>/dev/null || $as_echo X"$file" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q'` as_dir=$dirpart/$fdir; as_fn_mkdir_p # echo "creating $dirpart/$file" echo '# dummy' > "$dirpart/$file" done done } ;; "libtool":C) # See if we are running on zsh, and set the options which allow our # commands through without removal of \ escapes. if test -n "${ZSH_VERSION+set}" ; then setopt NO_GLOB_SUBST fi cfgfile="${ofile}T" trap "$RM \"$cfgfile\"; exit 1" 1 2 15 $RM "$cfgfile" cat <<_LT_EOF >> "$cfgfile" #! $SHELL # `$ECHO "$ofile" | sed 's%^.*/%%'` - Provide generalized library-building support services. # Generated automatically by $as_me ($PACKAGE$TIMESTAMP) $VERSION # Libtool was configured on host `(hostname || uname -n) 2>/dev/null | sed 1q`: # NOTE: Changes made to this file will be lost: look at ltmain.sh. # # Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2003, 2004, 2005, # 2006, 2007, 2008, 2009, 2010, 2011 Free Software # Foundation, Inc. # Written by Gordon Matzigkeit, 1996 # # This file is part of GNU Libtool. # # GNU Libtool 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 2 of # the License, or (at your option) any later version. # # As a special exception to the GNU General Public License, # if you distribute this file as part of a program or library that # is built using GNU Libtool, you may include this file under the # same distribution terms that you use for the rest of that program. # # GNU Libtool 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 GNU Libtool; see the file COPYING. If not, a copy # can be downloaded from http://www.gnu.org/licenses/gpl.html, or # obtained by writing to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. # The names of the tagged configurations supported by this script. available_tags="" # ### BEGIN LIBTOOL CONFIG # Which release of libtool.m4 was used? macro_version=$macro_version macro_revision=$macro_revision # Whether or not to build shared libraries. build_libtool_libs=$enable_shared # Whether or not to build static libraries. build_old_libs=$enable_static # What type of objects to build. pic_mode=$pic_mode # Whether or not to optimize for fast installation. fast_install=$enable_fast_install # Shell to use when invoking shell scripts. SHELL=$lt_SHELL # An echo program that protects backslashes. ECHO=$lt_ECHO # The PATH separator for the build system. PATH_SEPARATOR=$lt_PATH_SEPARATOR # The host system. host_alias=$host_alias host=$host host_os=$host_os # The build system. build_alias=$build_alias build=$build build_os=$build_os # A sed program that does not truncate output. SED=$lt_SED # Sed that helps us avoid accidentally triggering echo(1) options like -n. Xsed="\$SED -e 1s/^X//" # A grep program that handles long lines. GREP=$lt_GREP # An ERE matcher. EGREP=$lt_EGREP # A literal string matcher. FGREP=$lt_FGREP # A BSD- or MS-compatible name lister. NM=$lt_NM # Whether we need soft or hard links. LN_S=$lt_LN_S # What is the maximum length of a command? max_cmd_len=$max_cmd_len # Object file suffix (normally "o"). objext=$ac_objext # Executable file suffix (normally ""). exeext=$exeext # whether the shell understands "unset". lt_unset=$lt_unset # turn spaces into newlines. SP2NL=$lt_lt_SP2NL # turn newlines into spaces. NL2SP=$lt_lt_NL2SP # convert \$build file names to \$host format. to_host_file_cmd=$lt_cv_to_host_file_cmd # convert \$build files to toolchain format. to_tool_file_cmd=$lt_cv_to_tool_file_cmd # An object symbol dumper. OBJDUMP=$lt_OBJDUMP # Method to check whether dependent libraries are shared objects. deplibs_check_method=$lt_deplibs_check_method # Command to use when deplibs_check_method = "file_magic". file_magic_cmd=$lt_file_magic_cmd # How to find potential files when deplibs_check_method = "file_magic". file_magic_glob=$lt_file_magic_glob # Find potential files using nocaseglob when deplibs_check_method = "file_magic". want_nocaseglob=$lt_want_nocaseglob # DLL creation program. DLLTOOL=$lt_DLLTOOL # Command to associate shared and link libraries. sharedlib_from_linklib_cmd=$lt_sharedlib_from_linklib_cmd # The archiver. AR=$lt_AR # Flags to create an archive. AR_FLAGS=$lt_AR_FLAGS # How to feed a file listing to the archiver. archiver_list_spec=$lt_archiver_list_spec # A symbol stripping program. STRIP=$lt_STRIP # Commands used to install an old-style archive. RANLIB=$lt_RANLIB old_postinstall_cmds=$lt_old_postinstall_cmds old_postuninstall_cmds=$lt_old_postuninstall_cmds # Whether to use a lock for old archive extraction. lock_old_archive_extraction=$lock_old_archive_extraction # A C compiler. LTCC=$lt_CC # LTCC compiler flags. LTCFLAGS=$lt_CFLAGS # Take the output of nm and produce a listing of raw symbols and C names. global_symbol_pipe=$lt_lt_cv_sys_global_symbol_pipe # Transform the output of nm in a proper C declaration. global_symbol_to_cdecl=$lt_lt_cv_sys_global_symbol_to_cdecl # Transform the output of nm in a C name address pair. global_symbol_to_c_name_address=$lt_lt_cv_sys_global_symbol_to_c_name_address # Transform the output of nm in a C name address pair when lib prefix is needed. global_symbol_to_c_name_address_lib_prefix=$lt_lt_cv_sys_global_symbol_to_c_name_address_lib_prefix # Specify filename containing input files for \$NM. nm_file_list_spec=$lt_nm_file_list_spec # The root where to search for dependent libraries,and in which our libraries should be installed. lt_sysroot=$lt_sysroot # The name of the directory that contains temporary libtool files. objdir=$objdir # Used to examine libraries when file_magic_cmd begins with "file". MAGIC_CMD=$MAGIC_CMD # Must we lock files when doing compilation? need_locks=$lt_need_locks # Manifest tool. MANIFEST_TOOL=$lt_MANIFEST_TOOL # Tool to manipulate archived DWARF debug symbol files on Mac OS X. DSYMUTIL=$lt_DSYMUTIL # Tool to change global to local symbols on Mac OS X. NMEDIT=$lt_NMEDIT # Tool to manipulate fat objects and archives on Mac OS X. LIPO=$lt_LIPO # ldd/readelf like tool for Mach-O binaries on Mac OS X. OTOOL=$lt_OTOOL # ldd/readelf like tool for 64 bit Mach-O binaries on Mac OS X 10.4. OTOOL64=$lt_OTOOL64 # Old archive suffix (normally "a"). libext=$libext # Shared library suffix (normally ".so"). shrext_cmds=$lt_shrext_cmds # The commands to extract the exported symbol list from a shared archive. extract_expsyms_cmds=$lt_extract_expsyms_cmds # Variables whose values should be saved in libtool wrapper scripts and # restored at link time. variables_saved_for_relink=$lt_variables_saved_for_relink # Do we need the "lib" prefix for modules? need_lib_prefix=$need_lib_prefix # Do we need a version for libraries? need_version=$need_version # Library versioning type. version_type=$version_type # Shared library runtime path variable. runpath_var=$runpath_var # Shared library path variable. shlibpath_var=$shlibpath_var # Is shlibpath searched before the hard-coded library search path? shlibpath_overrides_runpath=$shlibpath_overrides_runpath # Format of library name prefix. libname_spec=$lt_libname_spec # List of archive names. First name is the real one, the rest are links. # The last name is the one that the linker finds with -lNAME library_names_spec=$lt_library_names_spec # The coded name of the library, if different from the real name. soname_spec=$lt_soname_spec # Permission mode override for installation of shared libraries. install_override_mode=$lt_install_override_mode # Command to use after installation of a shared archive. postinstall_cmds=$lt_postinstall_cmds # Command to use after uninstallation of a shared archive. postuninstall_cmds=$lt_postuninstall_cmds # Commands used to finish a libtool library installation in a directory. finish_cmds=$lt_finish_cmds # As "finish_cmds", except a single script fragment to be evaled but # not shown. finish_eval=$lt_finish_eval # Whether we should hardcode library paths into libraries. hardcode_into_libs=$hardcode_into_libs # Compile-time system search path for libraries. sys_lib_search_path_spec=$lt_sys_lib_search_path_spec # Run-time system search path for libraries. sys_lib_dlsearch_path_spec=$lt_sys_lib_dlsearch_path_spec # Whether dlopen is supported. dlopen_support=$enable_dlopen # Whether dlopen of programs is supported. dlopen_self=$enable_dlopen_self # Whether dlopen of statically linked programs is supported. dlopen_self_static=$enable_dlopen_self_static # Commands to strip libraries. old_striplib=$lt_old_striplib striplib=$lt_striplib # The linker used to build libraries. LD=$lt_LD # How to create reloadable object files. reload_flag=$lt_reload_flag reload_cmds=$lt_reload_cmds # Commands used to build an old-style archive. old_archive_cmds=$lt_old_archive_cmds # A language specific compiler. CC=$lt_compiler # Is the compiler the GNU compiler? with_gcc=$GCC # Compiler flag to turn off builtin functions. no_builtin_flag=$lt_lt_prog_compiler_no_builtin_flag # Additional compiler flags for building library objects. pic_flag=$lt_lt_prog_compiler_pic # How to pass a linker flag through the compiler. wl=$lt_lt_prog_compiler_wl # Compiler flag to prevent dynamic linking. link_static_flag=$lt_lt_prog_compiler_static # Does compiler simultaneously support -c and -o options? compiler_c_o=$lt_lt_cv_prog_compiler_c_o # Whether or not to add -lc for building shared libraries. build_libtool_need_lc=$archive_cmds_need_lc # Whether or not to disallow shared libs when runtime libs are static. allow_libtool_libs_with_static_runtimes=$enable_shared_with_static_runtimes # Compiler flag to allow reflexive dlopens. export_dynamic_flag_spec=$lt_export_dynamic_flag_spec # Compiler flag to generate shared objects directly from archives. whole_archive_flag_spec=$lt_whole_archive_flag_spec # Whether the compiler copes with passing no objects directly. compiler_needs_object=$lt_compiler_needs_object # Create an old-style archive from a shared archive. old_archive_from_new_cmds=$lt_old_archive_from_new_cmds # Create a temporary old-style archive to link instead of a shared archive. old_archive_from_expsyms_cmds=$lt_old_archive_from_expsyms_cmds # Commands used to build a shared archive. archive_cmds=$lt_archive_cmds archive_expsym_cmds=$lt_archive_expsym_cmds # Commands used to build a loadable module if different from building # a shared archive. module_cmds=$lt_module_cmds module_expsym_cmds=$lt_module_expsym_cmds # Whether we are building with GNU ld or not. with_gnu_ld=$lt_with_gnu_ld # Flag that allows shared libraries with undefined symbols to be built. allow_undefined_flag=$lt_allow_undefined_flag # Flag that enforces no undefined symbols. no_undefined_flag=$lt_no_undefined_flag # Flag to hardcode \$libdir into a binary during linking. # This must work even if \$libdir does not exist hardcode_libdir_flag_spec=$lt_hardcode_libdir_flag_spec # Whether we need a single "-rpath" flag with a separated argument. hardcode_libdir_separator=$lt_hardcode_libdir_separator # Set to "yes" if using DIR/libNAME\${shared_ext} during linking hardcodes # DIR into the resulting binary. hardcode_direct=$hardcode_direct # Set to "yes" if using DIR/libNAME\${shared_ext} during linking hardcodes # DIR into the resulting binary and the resulting library dependency is # "absolute",i.e impossible to change by setting \${shlibpath_var} if the # library is relocated. hardcode_direct_absolute=$hardcode_direct_absolute # Set to "yes" if using the -LDIR flag during linking hardcodes DIR # into the resulting binary. hardcode_minus_L=$hardcode_minus_L # Set to "yes" if using SHLIBPATH_VAR=DIR during linking hardcodes DIR # into the resulting binary. hardcode_shlibpath_var=$hardcode_shlibpath_var # Set to "yes" if building a shared library automatically hardcodes DIR # into the library and all subsequent libraries and executables linked # against it. hardcode_automatic=$hardcode_automatic # Set to yes if linker adds runtime paths of dependent libraries # to runtime path list. inherit_rpath=$inherit_rpath # Whether libtool must link a program against all its dependency libraries. link_all_deplibs=$link_all_deplibs # Set to "yes" if exported symbols are required. always_export_symbols=$always_export_symbols # The commands to list exported symbols. export_symbols_cmds=$lt_export_symbols_cmds # Symbols that should not be listed in the preloaded symbols. exclude_expsyms=$lt_exclude_expsyms # Symbols that must always be exported. include_expsyms=$lt_include_expsyms # Commands necessary for linking programs (against libraries) with templates. prelink_cmds=$lt_prelink_cmds # Commands necessary for finishing linking programs. postlink_cmds=$lt_postlink_cmds # Specify filename containing input files. file_list_spec=$lt_file_list_spec # How to hardcode a shared library path into an executable. hardcode_action=$hardcode_action # ### END LIBTOOL CONFIG _LT_EOF case $host_os in aix3*) cat <<\_LT_EOF >> "$cfgfile" # AIX sometimes has problems with the GCC collect2 program. For some # reason, if we set the COLLECT_NAMES environment variable, the problems # vanish in a puff of smoke. if test "X${COLLECT_NAMES+set}" != Xset; then COLLECT_NAMES= export COLLECT_NAMES fi _LT_EOF ;; esac ltmain="$ac_aux_dir/ltmain.sh" # We use sed instead of cat because bash on DJGPP gets confused if # if finds mixed CR/LF and LF-only lines. Since sed operates in # text mode, it properly converts lines to CR/LF. This bash problem # is reportedly fixed, but why not run on old versions too? sed '$q' "$ltmain" >> "$cfgfile" \ || (rm -f "$cfgfile"; exit 1) if test x"$xsi_shell" = xyes; then sed -e '/^func_dirname ()$/,/^} # func_dirname /c\ func_dirname ()\ {\ \ case ${1} in\ \ */*) func_dirname_result="${1%/*}${2}" ;;\ \ * ) func_dirname_result="${3}" ;;\ \ esac\ } # Extended-shell func_dirname implementation' "$cfgfile" > $cfgfile.tmp \ && mv -f "$cfgfile.tmp" "$cfgfile" \ || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp") test 0 -eq $? || _lt_function_replace_fail=: sed -e '/^func_basename ()$/,/^} # func_basename /c\ func_basename ()\ {\ \ func_basename_result="${1##*/}"\ } # Extended-shell func_basename implementation' "$cfgfile" > $cfgfile.tmp \ && mv -f "$cfgfile.tmp" "$cfgfile" \ || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp") test 0 -eq $? || _lt_function_replace_fail=: sed -e '/^func_dirname_and_basename ()$/,/^} # func_dirname_and_basename /c\ func_dirname_and_basename ()\ {\ \ case ${1} in\ \ */*) func_dirname_result="${1%/*}${2}" ;;\ \ * ) func_dirname_result="${3}" ;;\ \ esac\ \ func_basename_result="${1##*/}"\ } # Extended-shell func_dirname_and_basename implementation' "$cfgfile" > $cfgfile.tmp \ && mv -f "$cfgfile.tmp" "$cfgfile" \ || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp") test 0 -eq $? || _lt_function_replace_fail=: sed -e '/^func_stripname ()$/,/^} # func_stripname /c\ func_stripname ()\ {\ \ # pdksh 5.2.14 does not do ${X%$Y} correctly if both X and Y are\ \ # positional parameters, so assign one to ordinary parameter first.\ \ func_stripname_result=${3}\ \ func_stripname_result=${func_stripname_result#"${1}"}\ \ func_stripname_result=${func_stripname_result%"${2}"}\ } # Extended-shell func_stripname implementation' "$cfgfile" > $cfgfile.tmp \ && mv -f "$cfgfile.tmp" "$cfgfile" \ || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp") test 0 -eq $? || _lt_function_replace_fail=: sed -e '/^func_split_long_opt ()$/,/^} # func_split_long_opt /c\ func_split_long_opt ()\ {\ \ func_split_long_opt_name=${1%%=*}\ \ func_split_long_opt_arg=${1#*=}\ } # Extended-shell func_split_long_opt implementation' "$cfgfile" > $cfgfile.tmp \ && mv -f "$cfgfile.tmp" "$cfgfile" \ || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp") test 0 -eq $? || _lt_function_replace_fail=: sed -e '/^func_split_short_opt ()$/,/^} # func_split_short_opt /c\ func_split_short_opt ()\ {\ \ func_split_short_opt_arg=${1#??}\ \ func_split_short_opt_name=${1%"$func_split_short_opt_arg"}\ } # Extended-shell func_split_short_opt implementation' "$cfgfile" > $cfgfile.tmp \ && mv -f "$cfgfile.tmp" "$cfgfile" \ || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp") test 0 -eq $? || _lt_function_replace_fail=: sed -e '/^func_lo2o ()$/,/^} # func_lo2o /c\ func_lo2o ()\ {\ \ case ${1} in\ \ *.lo) func_lo2o_result=${1%.lo}.${objext} ;;\ \ *) func_lo2o_result=${1} ;;\ \ esac\ } # Extended-shell func_lo2o implementation' "$cfgfile" > $cfgfile.tmp \ && mv -f "$cfgfile.tmp" "$cfgfile" \ || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp") test 0 -eq $? || _lt_function_replace_fail=: sed -e '/^func_xform ()$/,/^} # func_xform /c\ func_xform ()\ {\ func_xform_result=${1%.*}.lo\ } # Extended-shell func_xform implementation' "$cfgfile" > $cfgfile.tmp \ && mv -f "$cfgfile.tmp" "$cfgfile" \ || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp") test 0 -eq $? || _lt_function_replace_fail=: sed -e '/^func_arith ()$/,/^} # func_arith /c\ func_arith ()\ {\ func_arith_result=$(( $* ))\ } # Extended-shell func_arith implementation' "$cfgfile" > $cfgfile.tmp \ && mv -f "$cfgfile.tmp" "$cfgfile" \ || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp") test 0 -eq $? || _lt_function_replace_fail=: sed -e '/^func_len ()$/,/^} # func_len /c\ func_len ()\ {\ func_len_result=${#1}\ } # Extended-shell func_len implementation' "$cfgfile" > $cfgfile.tmp \ && mv -f "$cfgfile.tmp" "$cfgfile" \ || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp") test 0 -eq $? || _lt_function_replace_fail=: fi if test x"$lt_shell_append" = xyes; then sed -e '/^func_append ()$/,/^} # func_append /c\ func_append ()\ {\ eval "${1}+=\\${2}"\ } # Extended-shell func_append implementation' "$cfgfile" > $cfgfile.tmp \ && mv -f "$cfgfile.tmp" "$cfgfile" \ || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp") test 0 -eq $? || _lt_function_replace_fail=: sed -e '/^func_append_quoted ()$/,/^} # func_append_quoted /c\ func_append_quoted ()\ {\ \ func_quote_for_eval "${2}"\ \ eval "${1}+=\\\\ \\$func_quote_for_eval_result"\ } # Extended-shell func_append_quoted implementation' "$cfgfile" > $cfgfile.tmp \ && mv -f "$cfgfile.tmp" "$cfgfile" \ || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp") test 0 -eq $? || _lt_function_replace_fail=: # Save a `func_append' function call where possible by direct use of '+=' sed -e 's%func_append \([a-zA-Z_]\{1,\}\) "%\1+="%g' $cfgfile > $cfgfile.tmp \ && mv -f "$cfgfile.tmp" "$cfgfile" \ || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp") test 0 -eq $? || _lt_function_replace_fail=: else # Save a `func_append' function call even when '+=' is not available sed -e 's%func_append \([a-zA-Z_]\{1,\}\) "%\1="$\1%g' $cfgfile > $cfgfile.tmp \ && mv -f "$cfgfile.tmp" "$cfgfile" \ || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp") test 0 -eq $? || _lt_function_replace_fail=: fi if test x"$_lt_function_replace_fail" = x":"; then { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: Unable to substitute extended shell functions in $ofile" >&5 $as_echo "$as_me: WARNING: Unable to substitute extended shell functions in $ofile" >&2;} fi mv -f "$cfgfile" "$ofile" || (rm -f "$ofile" && cp "$cfgfile" "$ofile" && rm -f "$cfgfile") chmod +x "$ofile" ;; esac done # for ac_tag as_fn_exit 0 _ACEOF ac_clean_files=$ac_clean_files_save test $ac_write_fail = 0 || as_fn_error $? "write failure creating $CONFIG_STATUS" "$LINENO" 5 # configure is writing to config.log, and then calls config.status. # config.status does its own redirection, appending to config.log. # Unfortunately, on DOS this fails, as config.log is still kept open # by configure, so config.status won't be able to write to it; its # output is simply discarded. So we exec the FD to /dev/null, # effectively closing config.log, so it can be properly (re)opened and # appended to by config.status. When coming back to configure, we # need to make the FD available again. if test "$no_create" != yes; then ac_cs_success=: ac_config_status_args= test "$silent" = yes && ac_config_status_args="$ac_config_status_args --quiet" exec 5>/dev/null $SHELL $CONFIG_STATUS $ac_config_status_args || ac_cs_success=false exec 5>>config.log # Use ||, not &&, to avoid exiting from the if with $? = 1, which # would make configure fail if this is the last instruction. $ac_cs_success || as_fn_exit 1 fi if test -n "$ac_unrecognized_opts" && test "$enable_option_checking" != no; then { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: unrecognized options: $ac_unrecognized_opts" >&5 $as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2;} fi if test false != "$TEST_PYTHON"; then tests_enabled=yes else tests_enabled=no fi echo " Configure summary: Compiler....................: ${CC} Compiler Flags..............: ${CFLAGS} ${ERROR_CFLAGS} Prefix......................: ${prefix} Coding style checks.........: ${ENABLE_CODING_STYLE_CHECKS} Enable debug................: ${enable_debug} Python tests................: ${tests_enabled} Install unit tests..........: ${installed_tests} Features: Client type.................: \"${CLIENT_TYPE}\" Plugin support..............: ${enable_plugins} Plugin headers installed....: ${enable_plugin_api} Channel.Type.Call support...: ${enable_channel_type_call} Google relay support........: ${enable_google_relay} File transfer support.......: ${enable_ft} Jingle file transfer support: ${enable_jingle_ft} VoIP support................: ${enable_voip} " telepathy-gabble-0.18.2/Makefile.am0000644000175000017500000000115312200204332017035 0ustar00smcvsmcv00000000000000ACLOCAL_AMFLAGS = -I m4 SUBDIRS = docs tools extensions lib src data m4 plugins tests gabble DISTCHECK_CONFIGURE_FLAGS = --disable-debug --enable-gtk-doc CLEANFILES = FIXME.out check-twisted: all $(MAKE) -C tests/twisted check-twisted check-all: check check-twisted check-local:: egrep -A 5 '[F]IXME|[T]ODO|[X]XX' $(srcdir)/src/*.[ch] \ > FIXME.out || true include tools/lcov.am LCOV_CHECK_ARGS = CHECK_TWISTED_SLEEP=6 CHANGELOG_RANGE = telepathy-gabble-0.12.0.. CHECK_FOR_UNRELEASED = \ $(srcdir)/NEWS \ $(wildcard $(srcdir)/gabble/*.[ch]) \ $(wildcard $(srcdir)/src/*.[ch]) include tools/telepathy.am telepathy-gabble-0.18.2/Makefile.in0000644000175000017500000007337012312536073017076 0ustar00smcvsmcv00000000000000# Makefile.in generated by automake 1.14.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2013 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ VPATH = @srcdir@ am__is_gnu_make = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)' am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ DIST_COMMON = $(srcdir)/tools/lcov.am $(srcdir)/tools/telepathy.am \ INSTALL NEWS README AUTHORS ChangeLog $(srcdir)/Makefile.in \ $(srcdir)/Makefile.am $(top_srcdir)/configure \ $(am__configure_deps) $(srcdir)/config.h.in COPYING compile \ config.guess config.sub depcomp install-sh missing ltmain.sh subdir = . ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/ax_config_dir.m4 \ $(top_srcdir)/m4/compiler.m4 $(top_srcdir)/m4/libtool.m4 \ $(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \ $(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \ $(top_srcdir)/m4/tp-compiler-flag.m4 \ $(top_srcdir)/m4/tp-compiler-warnings.m4 \ $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) am__CONFIG_DISTCLEAN_FILES = config.status config.cache config.log \ configure.lineno config.status.lineno mkinstalldirs = $(install_sh) -d CONFIG_HEADER = config.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = SOURCES = DIST_SOURCES = RECURSIVE_TARGETS = all-recursive check-recursive cscopelist-recursive \ ctags-recursive dvi-recursive html-recursive info-recursive \ install-data-recursive install-dvi-recursive \ install-exec-recursive install-html-recursive \ install-info-recursive install-pdf-recursive \ install-ps-recursive install-recursive installcheck-recursive \ installdirs-recursive pdf-recursive ps-recursive \ tags-recursive uninstall-recursive am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive \ distclean-recursive maintainer-clean-recursive am__recursive_targets = \ $(RECURSIVE_TARGETS) \ $(RECURSIVE_CLEAN_TARGETS) \ $(am__extra_recursive_targets) AM_RECURSIVE_TARGETS = $(am__recursive_targets:-recursive=) TAGS CTAGS \ cscope distdir dist dist-all distcheck am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) \ $(LISP)config.h.in # Read a list of newline-separated strings from the standard input, # and print each of them once, without duplicates. Input order is # *not* preserved. am__uniquify_input = $(AWK) '\ BEGIN { nonempty = 0; } \ { items[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in items) print i; }; } \ ' # Make sure the list of sources is unique. This is necessary because, # e.g., the same source file might be shared among _SOURCES variables # for different programs/libraries. am__define_uniq_tagged_files = \ list='$(am__tagged_files)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | $(am__uniquify_input)` ETAGS = etags CTAGS = ctags CSCOPE = cscope DIST_SUBDIRS = $(SUBDIRS) DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) distdir = $(PACKAGE)-$(VERSION) top_distdir = $(distdir) am__remove_distdir = \ if test -d "$(distdir)"; then \ find "$(distdir)" -type d ! -perm -200 -exec chmod u+w {} ';' \ && rm -rf "$(distdir)" \ || { sleep 5 && rm -rf "$(distdir)"; }; \ else :; fi am__post_remove_distdir = $(am__remove_distdir) am__relativize = \ dir0=`pwd`; \ sed_first='s,^\([^/]*\)/.*$$,\1,'; \ sed_rest='s,^[^/]*/*,,'; \ sed_last='s,^.*/\([^/]*\)$$,\1,'; \ sed_butlast='s,/*[^/]*$$,,'; \ while test -n "$$dir1"; do \ first=`echo "$$dir1" | sed -e "$$sed_first"`; \ if test "$$first" != "."; then \ if test "$$first" = ".."; then \ dir2=`echo "$$dir0" | sed -e "$$sed_last"`/"$$dir2"; \ dir0=`echo "$$dir0" | sed -e "$$sed_butlast"`; \ else \ first2=`echo "$$dir2" | sed -e "$$sed_first"`; \ if test "$$first2" = "$$first"; then \ dir2=`echo "$$dir2" | sed -e "$$sed_rest"`; \ else \ dir2="../$$dir2"; \ fi; \ dir0="$$dir0"/"$$first"; \ fi; \ fi; \ dir1=`echo "$$dir1" | sed -e "$$sed_rest"`; \ done; \ reldir="$$dir2" DIST_ARCHIVES = $(distdir).tar.gz GZIP_ENV = --best DIST_TARGETS = dist-gzip distuninstallcheck_listfiles = find . -type f -print am__distuninstallcheck_listfiles = $(distuninstallcheck_listfiles) \ | sed 's|^\./|$(prefix)/|' | grep -v '$(infodir)/dir$$' distcleancheck_listfiles = find . -type f -print ACLOCAL = @ACLOCAL@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ AR = @AR@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CFLAGS = @CFLAGS@ CLIENT_TYPE = @CLIENT_TYPE@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CYGPATH_W = @CYGPATH_W@ DBUS_CFLAGS = @DBUS_CFLAGS@ DBUS_LIBS = @DBUS_LIBS@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DLLTOOL = @DLLTOOL@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ ENABLE_CODING_STYLE_CHECKS = @ENABLE_CODING_STYLE_CHECKS@ ENABLE_PLUGINS = @ENABLE_PLUGINS@ ERROR_CFLAGS = @ERROR_CFLAGS@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ GLIB_CFLAGS = @GLIB_CFLAGS@ GLIB_GENMARSHAL = @GLIB_GENMARSHAL@ GLIB_LIBS = @GLIB_LIBS@ GMODULE_CFLAGS = @GMODULE_CFLAGS@ GMODULE_LIBS = @GMODULE_LIBS@ GREP = @GREP@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ LD = @LD@ LDFLAGS = @LDFLAGS@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBTOOL = @LIBTOOL@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBOBJS = @LTLIBOBJS@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ NICE_CFLAGS = @NICE_CFLAGS@ NICE_LIBS = @NICE_LIBS@ NM = @NM@ NMEDIT = @NMEDIT@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ PYTHON = @PYTHON@ PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@ PYTHON_PLATFORM = @PYTHON_PLATFORM@ PYTHON_PREFIX = @PYTHON_PREFIX@ PYTHON_VERSION = @PYTHON_VERSION@ RANLIB = @RANLIB@ SED = @SED@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ SOUP_CFLAGS = @SOUP_CFLAGS@ SOUP_LIBS = @SOUP_LIBS@ STRIP = @STRIP@ TEST_PYTHON = @TEST_PYTHON@ TP_GLIB_CFLAGS = @TP_GLIB_CFLAGS@ TP_GLIB_LIBS = @TP_GLIB_LIBS@ VERSION = @VERSION@ WOCKY_CFLAGS = @WOCKY_CFLAGS@ WOCKY_LIBS = @WOCKY_LIBS@ XSLTPROC = @XSLTPROC@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ gabbletestsdir = @gabbletestsdir@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ mandir = @mandir@ mkdir_p = @mkdir_p@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ pkgpyexecdir = @pkgpyexecdir@ pkgpythondir = @pkgpythondir@ pluginexecdir = @pluginexecdir@ pluginexeclibdir = @pluginexeclibdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ pyexecdir = @pyexecdir@ pythondir = @pythondir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ srcdir = @srcdir@ sysconfdir = @sysconfdir@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ ACLOCAL_AMFLAGS = -I m4 SUBDIRS = docs tools extensions lib src data m4 plugins tests gabble DISTCHECK_CONFIGURE_FLAGS = --disable-debug --enable-gtk-doc CLEANFILES = FIXME.out LCOV_CHECK_ARGS = CHECK_TWISTED_SLEEP=6 CHANGELOG_RANGE = telepathy-gabble-0.12.0.. CHECK_FOR_UNRELEASED = \ $(srcdir)/NEWS \ $(wildcard $(srcdir)/gabble/*.[ch]) \ $(wildcard $(srcdir)/src/*.[ch]) all: config.h $(MAKE) $(AM_MAKEFLAGS) all-recursive .SUFFIXES: am--refresh: Makefile @: $(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(srcdir)/tools/lcov.am $(srcdir)/tools/telepathy.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ echo ' cd $(srcdir) && $(AUTOMAKE) --gnu'; \ $(am__cd) $(srcdir) && $(AUTOMAKE) --gnu \ && exit 0; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --gnu Makefile .PRECIOUS: Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ echo ' $(SHELL) ./config.status'; \ $(SHELL) ./config.status;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $@ $(am__depfiles_maybe)'; \ cd $(top_builddir) && $(SHELL) ./config.status $@ $(am__depfiles_maybe);; \ esac; $(srcdir)/tools/lcov.am $(srcdir)/tools/telepathy.am: $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) $(SHELL) ./config.status --recheck $(top_srcdir)/configure: $(am__configure_deps) $(am__cd) $(srcdir) && $(AUTOCONF) $(ACLOCAL_M4): $(am__aclocal_m4_deps) $(am__cd) $(srcdir) && $(ACLOCAL) $(ACLOCAL_AMFLAGS) $(am__aclocal_m4_deps): config.h: stamp-h1 @test -f $@ || rm -f stamp-h1 @test -f $@ || $(MAKE) $(AM_MAKEFLAGS) stamp-h1 stamp-h1: $(srcdir)/config.h.in $(top_builddir)/config.status @rm -f stamp-h1 cd $(top_builddir) && $(SHELL) ./config.status config.h $(srcdir)/config.h.in: $(am__configure_deps) ($(am__cd) $(top_srcdir) && $(AUTOHEADER)) rm -f stamp-h1 touch $@ distclean-hdr: -rm -f config.h stamp-h1 mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs distclean-libtool: -rm -f libtool config.lt # This directory's subdirectories are mostly independent; you can cd # into them and run 'make' without going through this Makefile. # To change the values of 'make' variables: instead of editing Makefiles, # (1) if the variable is set in 'config.status', edit 'config.status' # (which will cause the Makefiles to be regenerated when you run 'make'); # (2) otherwise, pass the desired values on the 'make' command line. $(am__recursive_targets): @fail=; \ if $(am__make_keepgoing); then \ failcom='fail=yes'; \ else \ failcom='exit 1'; \ fi; \ dot_seen=no; \ target=`echo $@ | sed s/-recursive//`; \ case "$@" in \ distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \ *) list='$(SUBDIRS)' ;; \ esac; \ for subdir in $$list; do \ echo "Making $$target in $$subdir"; \ if test "$$subdir" = "."; then \ dot_seen=yes; \ local_target="$$target-am"; \ else \ local_target="$$target"; \ fi; \ ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ || eval $$failcom; \ done; \ if test "$$dot_seen" = "no"; then \ $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \ fi; test -z "$$fail" ID: $(am__tagged_files) $(am__define_uniq_tagged_files); mkid -fID $$unique tags: tags-recursive TAGS: tags tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) set x; \ here=`pwd`; \ if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \ include_option=--etags-include; \ empty_fix=.; \ else \ include_option=--include; \ empty_fix=; \ fi; \ list='$(SUBDIRS)'; for subdir in $$list; do \ if test "$$subdir" = .; then :; else \ test ! -f $$subdir/TAGS || \ set "$$@" "$$include_option=$$here/$$subdir/TAGS"; \ fi; \ done; \ $(am__define_uniq_tagged_files); \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ if test $$# -gt 0; then \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ "$$@" $$unique; \ else \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ $$unique; \ fi; \ fi ctags: ctags-recursive CTAGS: ctags ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) $(am__define_uniq_tagged_files); \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" cscope: cscope.files test ! -s cscope.files \ || $(CSCOPE) -b -q $(AM_CSCOPEFLAGS) $(CSCOPEFLAGS) -i cscope.files $(CSCOPE_ARGS) clean-cscope: -rm -f cscope.files cscope.files: clean-cscope cscopelist cscopelist: cscopelist-recursive cscopelist-am: $(am__tagged_files) list='$(am__tagged_files)'; \ case "$(srcdir)" in \ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ *) sdir=$(subdir)/$(srcdir) ;; \ esac; \ for i in $$list; do \ if test -f "$$i"; then \ echo "$(subdir)/$$i"; \ else \ echo "$$sdir/$$i"; \ fi; \ done >> $(top_builddir)/cscope.files distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags -rm -f cscope.out cscope.in.out cscope.po.out cscope.files distdir: $(DISTFILES) $(am__remove_distdir) test -d "$(distdir)" || mkdir "$(distdir)" @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done @list='$(DIST_SUBDIRS)'; for subdir in $$list; do \ if test "$$subdir" = .; then :; else \ $(am__make_dryrun) \ || test -d "$(distdir)/$$subdir" \ || $(MKDIR_P) "$(distdir)/$$subdir" \ || exit 1; \ dir1=$$subdir; dir2="$(distdir)/$$subdir"; \ $(am__relativize); \ new_distdir=$$reldir; \ dir1=$$subdir; dir2="$(top_distdir)"; \ $(am__relativize); \ new_top_distdir=$$reldir; \ echo " (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) top_distdir="$$new_top_distdir" distdir="$$new_distdir" \\"; \ echo " am__remove_distdir=: am__skip_length_check=: am__skip_mode_fix=: distdir)"; \ ($(am__cd) $$subdir && \ $(MAKE) $(AM_MAKEFLAGS) \ top_distdir="$$new_top_distdir" \ distdir="$$new_distdir" \ am__remove_distdir=: \ am__skip_length_check=: \ am__skip_mode_fix=: \ distdir) \ || exit 1; \ fi; \ done $(MAKE) $(AM_MAKEFLAGS) \ top_distdir="$(top_distdir)" distdir="$(distdir)" \ dist-hook -test -n "$(am__skip_mode_fix)" \ || find "$(distdir)" -type d ! -perm -755 \ -exec chmod u+rwx,go+rx {} \; -o \ ! -type d ! -perm -444 -links 1 -exec chmod a+r {} \; -o \ ! -type d ! -perm -400 -exec chmod a+r {} \; -o \ ! -type d ! -perm -444 -exec $(install_sh) -c -m a+r {} {} \; \ || chmod -R a+r "$(distdir)" dist-gzip: distdir tardir=$(distdir) && $(am__tar) | GZIP=$(GZIP_ENV) gzip -c >$(distdir).tar.gz $(am__post_remove_distdir) dist-bzip2: distdir tardir=$(distdir) && $(am__tar) | BZIP2=$${BZIP2--9} bzip2 -c >$(distdir).tar.bz2 $(am__post_remove_distdir) dist-lzip: distdir tardir=$(distdir) && $(am__tar) | lzip -c $${LZIP_OPT--9} >$(distdir).tar.lz $(am__post_remove_distdir) dist-xz: distdir tardir=$(distdir) && $(am__tar) | XZ_OPT=$${XZ_OPT--e} xz -c >$(distdir).tar.xz $(am__post_remove_distdir) dist-tarZ: distdir @echo WARNING: "Support for shar distribution archives is" \ "deprecated." >&2 @echo WARNING: "It will be removed altogether in Automake 2.0" >&2 tardir=$(distdir) && $(am__tar) | compress -c >$(distdir).tar.Z $(am__post_remove_distdir) dist-shar: distdir @echo WARNING: "Support for distribution archives compressed with" \ "legacy program 'compress' is deprecated." >&2 @echo WARNING: "It will be removed altogether in Automake 2.0" >&2 shar $(distdir) | GZIP=$(GZIP_ENV) gzip -c >$(distdir).shar.gz $(am__post_remove_distdir) dist-zip: distdir -rm -f $(distdir).zip zip -rq $(distdir).zip $(distdir) $(am__post_remove_distdir) dist dist-all: $(MAKE) $(AM_MAKEFLAGS) $(DIST_TARGETS) am__post_remove_distdir='@:' $(am__post_remove_distdir) # This target untars the dist file and tries a VPATH configuration. Then # it guarantees that the distribution is self-contained by making another # tarfile. distcheck: dist case '$(DIST_ARCHIVES)' in \ *.tar.gz*) \ GZIP=$(GZIP_ENV) gzip -dc $(distdir).tar.gz | $(am__untar) ;;\ *.tar.bz2*) \ bzip2 -dc $(distdir).tar.bz2 | $(am__untar) ;;\ *.tar.lz*) \ lzip -dc $(distdir).tar.lz | $(am__untar) ;;\ *.tar.xz*) \ xz -dc $(distdir).tar.xz | $(am__untar) ;;\ *.tar.Z*) \ uncompress -c $(distdir).tar.Z | $(am__untar) ;;\ *.shar.gz*) \ GZIP=$(GZIP_ENV) gzip -dc $(distdir).shar.gz | unshar ;;\ *.zip*) \ unzip $(distdir).zip ;;\ esac chmod -R a-w $(distdir) chmod u+w $(distdir) mkdir $(distdir)/_build $(distdir)/_inst chmod a-w $(distdir) test -d $(distdir)/_build || exit 0; \ dc_install_base=`$(am__cd) $(distdir)/_inst && pwd | sed -e 's,^[^:\\/]:[\\/],/,'` \ && dc_destdir="$${TMPDIR-/tmp}/am-dc-$$$$/" \ && $(MAKE) $(AM_MAKEFLAGS) distcheck-hook \ && am__cwd=`pwd` \ && $(am__cd) $(distdir)/_build \ && ../configure \ $(AM_DISTCHECK_CONFIGURE_FLAGS) \ $(DISTCHECK_CONFIGURE_FLAGS) \ --srcdir=.. --prefix="$$dc_install_base" \ && $(MAKE) $(AM_MAKEFLAGS) \ && $(MAKE) $(AM_MAKEFLAGS) dvi \ && $(MAKE) $(AM_MAKEFLAGS) check \ && $(MAKE) $(AM_MAKEFLAGS) install \ && $(MAKE) $(AM_MAKEFLAGS) installcheck \ && $(MAKE) $(AM_MAKEFLAGS) uninstall \ && $(MAKE) $(AM_MAKEFLAGS) distuninstallcheck_dir="$$dc_install_base" \ distuninstallcheck \ && chmod -R a-w "$$dc_install_base" \ && ({ \ (cd ../.. && umask 077 && mkdir "$$dc_destdir") \ && $(MAKE) $(AM_MAKEFLAGS) DESTDIR="$$dc_destdir" install \ && $(MAKE) $(AM_MAKEFLAGS) DESTDIR="$$dc_destdir" uninstall \ && $(MAKE) $(AM_MAKEFLAGS) DESTDIR="$$dc_destdir" \ distuninstallcheck_dir="$$dc_destdir" distuninstallcheck; \ } || { rm -rf "$$dc_destdir"; exit 1; }) \ && rm -rf "$$dc_destdir" \ && $(MAKE) $(AM_MAKEFLAGS) dist \ && rm -rf $(DIST_ARCHIVES) \ && $(MAKE) $(AM_MAKEFLAGS) distcleancheck \ && cd "$$am__cwd" \ || exit 1 $(am__post_remove_distdir) @(echo "$(distdir) archives ready for distribution: "; \ list='$(DIST_ARCHIVES)'; for i in $$list; do echo $$i; done) | \ sed -e 1h -e 1s/./=/g -e 1p -e 1x -e '$$p' -e '$$x' distuninstallcheck: @test -n '$(distuninstallcheck_dir)' || { \ echo 'ERROR: trying to run $@ with an empty' \ '$$(distuninstallcheck_dir)' >&2; \ exit 1; \ }; \ $(am__cd) '$(distuninstallcheck_dir)' || { \ echo 'ERROR: cannot chdir into $(distuninstallcheck_dir)' >&2; \ exit 1; \ }; \ test `$(am__distuninstallcheck_listfiles) | wc -l` -eq 0 \ || { echo "ERROR: files left after uninstall:" ; \ if test -n "$(DESTDIR)"; then \ echo " (check DESTDIR support)"; \ fi ; \ $(distuninstallcheck_listfiles) ; \ exit 1; } >&2 distcleancheck: distclean @if test '$(srcdir)' = . ; then \ echo "ERROR: distcleancheck can only run from a VPATH build" ; \ exit 1 ; \ fi @test `$(distcleancheck_listfiles) | wc -l` -eq 0 \ || { echo "ERROR: files left in build directory after distclean:" ; \ $(distcleancheck_listfiles) ; \ exit 1; } >&2 check-am: all-am $(MAKE) $(AM_MAKEFLAGS) check-local check: check-recursive all-am: Makefile config.h installdirs: installdirs-recursive installdirs-am: install: install-recursive install-exec: install-exec-recursive install-data: install-data-recursive uninstall: uninstall-recursive install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-recursive install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: clean-generic: -test -z "$(CLEANFILES)" || rm -f $(CLEANFILES) distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." clean: clean-recursive clean-am: clean-generic clean-libtool mostlyclean-am distclean: distclean-recursive -rm -f $(am__CONFIG_DISTCLEAN_FILES) -rm -f Makefile distclean-am: clean-am distclean-generic distclean-hdr \ distclean-libtool distclean-tags dvi: dvi-recursive dvi-am: html: html-recursive html-am: info: info-recursive info-am: install-data-am: install-dvi: install-dvi-recursive install-dvi-am: install-exec-am: install-html: install-html-recursive install-html-am: install-info: install-info-recursive install-info-am: install-man: install-pdf: install-pdf-recursive install-pdf-am: install-ps: install-ps-recursive install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-recursive -rm -f $(am__CONFIG_DISTCLEAN_FILES) -rm -rf $(top_srcdir)/autom4te.cache -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-recursive mostlyclean-am: mostlyclean-generic mostlyclean-libtool pdf: pdf-recursive pdf-am: ps: ps-recursive ps-am: uninstall-am: .MAKE: $(am__recursive_targets) all check-am install-am install-strip .PHONY: $(am__recursive_targets) CTAGS GTAGS TAGS all all-am \ am--refresh check check-am check-local clean clean-cscope \ clean-generic clean-libtool cscope cscopelist-am ctags \ ctags-am dist dist-all dist-bzip2 dist-gzip dist-hook \ dist-lzip dist-shar dist-tarZ dist-xz dist-zip distcheck \ distclean distclean-generic distclean-hdr distclean-libtool \ distclean-tags distcleancheck distdir distuninstallcheck dvi \ dvi-am html html-am info info-am install install-am \ install-data install-data-am install-dvi install-dvi-am \ install-exec install-exec-am install-html install-html-am \ install-info install-info-am install-man install-pdf \ install-pdf-am install-ps install-ps-am install-strip \ installcheck installcheck-am installdirs installdirs-am \ maintainer-clean maintainer-clean-generic mostlyclean \ mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ tags tags-am uninstall uninstall-am check-twisted: all $(MAKE) -C tests/twisted check-twisted check-all: check check-twisted check-local:: egrep -A 5 '[F]IXME|[T]ODO|[X]XX' $(srcdir)/src/*.[ch] \ > FIXME.out || true lcov-reset: lcov --directory @top_srcdir@ --zerocounters lcov-report: lcov --directory @top_srcdir@ --capture \ --output-file @top_builddir@/lcov.info.tmp lcov --directory @top_srcdir@ --output-file @top_builddir@/lcov.info \ --remove @top_builddir@/lcov.info.tmp telepathy-glib-scan.c rm @top_builddir@/lcov.info.tmp $(mkdir_p) @top_builddir@/lcov.html echo "Coming soon!" > @top_builddir@/lcov.html/index.html git_commit=`GIT_DIR=@top_srcdir@/.git git log -1 --pretty=format:%h 2>/dev/null`;\ genhtml --title "@PACKAGE_STRING@ $$git_commit" \ --output-directory @top_builddir@/lcov.html lcov.info @echo @echo 'lcov report can be found in:' @echo 'file://@abs_top_builddir@/lcov.html/index.html' @echo lcov-check: $(MAKE) lcov-reset $(MAKE) check $(LCOV_CHECK_ARGS) $(MAKE) lcov-report dist-hook: chmod u+w ${distdir}/ChangeLog if test -d ${top_srcdir}/.git; then \ ( cd ${top_srcdir} && git log --date=iso $(CHANGELOG_RANGE) ) > ${distdir}/ChangeLog; \ fi distcheck-hook: @test "z$(CHECK_FOR_UNRELEASED)" = z || \ case @VERSION@ in \ *.*.*.*|*+) ;; \ *) \ if grep -r UNRELEASED $(CHECK_FOR_UNRELEASED); \ then \ echo "^^^ This is meant to be a release, but some files say UNRELEASED" >&2; \ exit 2; \ fi \ ;; \ esac _is-release-check: @case @VERSION@ in \ (*.*.*.*|*+) \ echo "Hey! @VERSION@ is not a release!" >&2; \ exit 2; \ ;; \ esac @cd ${top_srcdir} && \ if ! git diff --no-ext-diff --quiet --exit-code; then \ echo "Hey! Your tree is dirty! No release for you." >&2; \ exit 2; \ fi @cd ${top_srcdir} && \ if ! git diff --cached --no-ext-diff --quiet --exit-code; then \ echo "Hey! You have changes staged! No release for you." >&2; \ exit 2; \ fi @ENABLE_GTK_DOC_FALSE@ @echo "Hey! You need to pass --enable-gtk-doc to configure!" @ENABLE_GTK_DOC_FALSE@ @exit 2; %.tar.gz.asc: %.tar.gz $(AM_V_GEN)gpg --detach-sign --armor $@ @PACKAGE@-@VERSION@.tar.gz: $(MAKE) _is-release-check $(MAKE) check $(MAKE) distcheck maintainer-prepare-release: $(MAKE) _is-release-check $(MAKE) all $(MAKE) distcheck $(MAKE) release-mail git tag -s @PACKAGE@-@VERSION@ -m @PACKAGE@' '@VERSION@ gpg --detach-sign --armor @PACKAGE@-@VERSION@.tar.gz release-mail: NEWS $(AM_V_GEN)(python $(top_srcdir)/tools/make-release-mail.py \ @PACKAGE@ @VERSION@ $(top_srcdir)/NEWS > $@.tmp && \ mv $@.tmp $@) maintainer-upload-release: _maintainer-upload-release _maintainer-upload-release-check: _is-release-check test -f @PACKAGE@-@VERSION@.tar.gz test -f @PACKAGE@-@VERSION@.tar.gz.asc gpg --verify @PACKAGE@-@VERSION@.tar.gz.asc _maintainer-upload-release: _maintainer-upload-release-check rsync -vzP @PACKAGE@-@VERSION@.tar.gz telepathy.freedesktop.org:/srv/telepathy.freedesktop.org/www/releases/@PACKAGE@/@PACKAGE@-@VERSION@.tar.gz rsync -vzP @PACKAGE@-@VERSION@.tar.gz.asc telepathy.freedesktop.org:/srv/telepathy.freedesktop.org/www/releases/@PACKAGE@/@PACKAGE@-@VERSION@.tar.gz.asc maintainer-make-release: $(MAKE) maintainer-prepare-release $(MAKE) maintainer-upload-release @echo "Now:" @echo " • bump the nano-version;" @echo " • push the branch and tags upstream; and" @echo " • send release-mail to ." # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: telepathy-gabble-0.18.2/ChangeLog0000644000175000017500000124545312312537052016605 0ustar00smcvsmcv00000000000000commit 8eda2330d0353845167018e962157053d039a9c0 Author: Simon McVittie Date: 2014-03-20 09:57:39 +0000 0.18.2 commit 865a6d69604782326eb5aa3dcc67c744f2b1680c Author: Simon McVittie Date: 2014-03-19 14:05:35 +0000 Update Wocky * data-form: reformat blocks so recent gtk-doc can cope * jingle-content: reset idle ID in its callback commit 226f1eae705a3c05715b4213fbff5b05b995af5e Author: Will Thompson Date: 2013-06-22 19:16:55 +0100 plugin-loader: clear GError after g_dir_open fails Bug: https://bugs.freedesktop.org/show_bug.cgi?id=66085 Reviewed-by: Simon McVittie commit 892d6d57b6e8bec11bc7693b5f58cffac158a184 Author: Guillaume Desmottes Date: 2013-10-28 16:21:08 +0100 Use the proper 'Ice' Call capability Also, use token constants when possible. commit c2d7b345aa52e3f5d70d4657f0dbab7c3f62042a Author: Guillaume Desmottes Date: 2013-10-02 15:58:59 +0200 private-tube-factory: tube ID is a guint64 Fix a crash on 64 bits archs. https://bugs.freedesktop.org/show_bug.cgi?id=70038 commit a1faa740ca8773d731d168f19569d6a7d230df85 Author: Guillaume Desmottes Date: 2013-10-02 15:39:46 +0200 conn-presence: fix enum cast warning Thanks clang... https://bugs.freedesktop.org/show_bug.cgi?id=70038 commit 527de4d26ef96e2a049781cdecc4e6e1a05622d3 Author: Simon McVittie Date: 2013-09-09 13:00:30 +0100 Add a regression test for #68829 Bug: https://bugs.freedesktop.org/show_bug.cgi?id=68829 Reviewed-by: Guillaume Desmottes commit 0899652383fa881d275ab6ab578009e898675762 Author: Guillaume Desmottes Date: 2013-09-06 12:38:19 +0200 set nano flag commit 8ca68d3c4dd43d974dfd4d2a0e0d9b55605a4a10 Author: Guillaume Desmottes Date: 2013-09-06 12:17:18 +0200 prepare 0.18.1 commit 82a38589b201e71a613567fff2df98d82aaad232 Author: Guillaume Desmottes Date: 2013-09-06 12:11:05 +0200 wocky: update to gabble-0.18 HEAD Needed to fix connection to Facebok (fdo#68829). commit 8e27c66d058a72583579384f3dd307e798a8ba73 Author: Simon McVittie Date: 2013-08-09 17:31:07 +0100 nano version, release name commit 1537fdb051a75fc432eb6cb18d42a5f33afb28ac Author: Simon McVittie Date: 2013-08-09 15:54:45 +0100 0.18.0 commit 80f731d13c3a6d97f531955f1806a436c545236a Author: Simon McVittie Date: 2013-08-09 15:53:15 +0100 more NEWS commit 2d22f5d22a5d859998971e6c20ab7b0e8108bcb8 Author: Simon McVittie Date: 2013-08-09 16:44:56 +0100 tests/twisted: make the necessary directories to put *.conf in This fails during parallel distcheck. Signed-off-by: Simon McVittie commit 11971e65916a87e240504f0c105a12f151ca797f Author: Simon McVittie Date: 2013-08-09 16:44:21 +0100 Update Wocky again Bug: https://bugs.freedesktop.org/show_bug.cgi?id=67953 commit b5138f5abc201c6f3140e1bafa0c8f52031339f4 Author: Samuel Thibault Date: 2013-08-09 15:49:24 +0100 errno is actually a reserved word in C, avoid it [Commit message added -smcv] Bug-Debian: http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=714534 Reviewed-by: Simon McVittie commit cb6fa296255213c842ca119ab4b0e1796e8a2c6e Author: Simon McVittie Date: 2013-08-09 15:27:42 +0100 NEWS for 0.18 commit 3a01321d2b2f49fdb2788126513408242d8a114e Author: Simon McVittie Date: 2013-08-09 15:24:51 +0100 Update Wocky to a version whose tests pass Bug: https://bugs.freedesktop.org/show_bug.cgi?id=67875 Bug: https://bugs.freedesktop.org/show_bug.cgi?id=67900 Signed-off-by: Simon McVittie commit a4be3bf914b0a3ab7ecba92e3a199310241f1197 Author: Simon McVittie Date: 2013-08-06 15:37:18 +0100 Turn off deprecation warnings, this is a stable branch Signed-off-by: Simon McVittie commit 1a5bfd386fb23b0690fe99722ed46d2eb1608a85 Merge: 6df8c13 42c0c8f Author: Simon McVittie Date: 2013-08-06 15:29:35 +0100 Merge branch 'telepathy-gabble-0.16' Conflicts: NEWS commit 42c0c8f2b03a8ac9b94f268d41f379f8773c885f Author: Simon McVittie Date: 2013-08-06 15:28:30 +0100 NEWS commit 08ac84725bdc135e199146f180be2ea474b452c9 Author: Simon McVittie Date: 2013-08-06 15:28:24 +0100 roster/groups: don't rely on an implementation detail Before telepathy-glib 0.20.3 and 0.21.1, we had this incorrect sequence (pseudocode) for each group: * NewChannels(the group) * GroupsChanged([the group], added: [...], removed: []) * NewChannels(the group) In 0.20.3 and 0.20.1, we removed the second emission of NewChannels. Unfortunately, that broke this test, which was specifically expecting GroupsChanged followed by NewChannels. Rather than reversing the assumption, I'm doing it properly, by expecting the events in no particular order. Signed-off-by: Simon McVittie Bug: https://bugs.freedesktop.org/show_bug.cgi?id=67828 Reviewed-by: Guillaume Desmottes commit 6df8c13f5817b3c9f6e7f0592195257a3d857147 Author: Simon McVittie Date: 2013-05-30 14:26:19 +0100 run-test.sh.in: make the indentation make sense The Python invocation is indented, because it's a command-line argument for the sh invocation. The case shouldn't be. Reviewed-by: Guillaume Desmottes Bug: https://bugs.freedesktop.org/show_bug.cgi?id=65290 commit 54d037ab9b532e70a7fa80ad69a18bed58ccb480 Author: Will Thompson Date: 2013-04-11 19:33:29 +0100 gabbletest: add hooks for hazetest I need these so that hazetest can just override what it needs, rather than actually modifying the copy of gabbletest. https://bugs.freedesktop.org/show_bug.cgi?id=65658 commit 4b23e0ec83024ebbfcaaeb12cdcd086f7a640b9f Merge: 4d4fc2f 794a02b Author: Simon McVittie Date: 2013-06-11 14:49:31 +0100 Merge branch 'telepathy-gabble-0.16' and update NEWS commit 794a02b84472ddbbadd6f5f920e2c523f68b27fc Author: Simon McVittie Date: 2013-06-11 14:46:51 +0100 NEWS for recent changes commit fa48076ccef7e5c8c4a08a88c399d0dbbb3ac624 Author: Simon McVittie Date: 2013-06-11 14:39:42 +0100 Avoid having two of the same set of commands run in parallel A rule like this: _gen/x.c _gen/x.h: prerequisites $(AM_V_GEN)x-generator doesn't consider x.c and x.h together. Instead, it expands to two rules, one to generate x.c and one to generate x.h, which happen to run the same commands. This means that in the worst case, you can end up running x-generator twice in parallel, and they'll race with each other and overwrite or delete each other's output. Based on commit 36c2a545c from telepathy-glib. Bug: https://bugs.freedesktop.org/show_bug.cgi?id=64285 Signed-off-by: Simon McVittie Reviewed-by: Xavier Claessens Conflicts: extensions/Makefile.am commit 4d4fc2f948dcaaa029c065d5c7e48238eefa3423 Author: Simon McVittie Date: 2013-06-03 11:14:12 +0100 Use run-test.sh to run uninstalled tests commit fcc3900f4e6f6c9a58d95d15af8175b5d04fdaf4 Author: Simon McVittie Date: 2013-06-03 09:14:54 +0100 Adapt run-test.sh to be able to run the tests uninstalled When autoreconfiscated with Automake 1.13, the way in which we were (ab?)using Automake's test driver no longer works. We can't just switch back to the old serial test driver without a dependency on at least Automake 1.12, either. However, run-test.sh (which was already used for installed tests) is quite capable of running uninstalled tests, with a bit of adjustment: * if GABBLE_TEST_UNINSTALLED is set, expect GABBLE_ABS_TOP_SRCDIR, GABBLE_ABS_TOP_BUILDDIR and optionally GABBLE_TEST_SLEEP in the environment * look for installed or uninstalled files, as appropriate * use TEST_PYTHON (which might differ from PYTHON) * use python -u (unbuffered stdout) for better debugging Bug: https://bugs.freedesktop.org/show_bug.cgi?id=65290 Signed-off-by: Simon McVittie Reviewed-by: Xavier Claessens commit 2a54c7cd0c55d636f33666f4a6ac626e3c6f0f55 Author: Simon McVittie Date: 2013-05-30 13:57:57 +0100 .gitignore: automake 1.13 installs test-driver Bug: https://bugs.freedesktop.org/show_bug.cgi?id=65290 Signed-off-by: Simon McVittie Reviewed-by: Xavier Claessens commit 7931b9960bc4d357ff04e7381c9d91e161be969d Author: Simon McVittie Date: 2013-06-04 12:41:12 +0100 Remove obsolete GLib < 2.32 code paths We now depend on 2.32. Bug: https://bugs.freedesktop.org/show_bug.cgi?id=65290 Signed-off-by: Simon McVittie Reviewed-by: Xavier Claessens commit 2a8a6a50be5241627108a23f1dd1ba8d12693f8b Author: Simon McVittie Date: 2013-05-30 14:02:19 +0100 Officially depend on GLib 2.32 In practice we depend on it anyway, via telepathy-glib 0.19.9. Also update the telepathy-glib dependency in the .pc files to match configure.ac. Bug: https://bugs.freedesktop.org/show_bug.cgi?id=65290 Signed-off-by: Simon McVittie Reviewed-by: Xavier Claessens commit bae4f7493376cccb2cd629e56b4e4cc0624720b6 Merge: 991c397 5a349b2 Author: Simon McVittie Date: 2013-06-06 16:28:31 +0100 Merge branch 'telepathy-gabble-0.16' Conflicts: NEWS configure.ac commit 991c397c6cd33fa970afe8fa46143ae9201b2002 Author: Simon McVittie Date: 2013-06-06 16:27:58 +0100 nano version commit 5a349b2323f8bfd8a5d18c1bc2d79ef14689c0fe Author: Simon McVittie Date: 2013-06-04 18:34:14 +0100 Disable unreliable test-case It has a race condition or something. Bug: https://bugs.freedesktop.org/show_bug.cgi?id=49595 Reviewed-by: Guillaume Desmottes commit 83bb468e0d4b386d88821ba2981f981214e9ebc6 Author: Simon McVittie Date: 2013-06-04 12:40:45 +0100 Initialize libdbus for thread-safety libdbus is not thread-safe by default. This is a long-standing design flaw (). We call into GIO, which calls into glib-networking, which can (at least in recent versions) invoke libproxy in a thread. libproxy apparently has a Network-Manager plugin, which uses libdbus in that thread; meanwhile, we use libdbus in the main thread and everything goes badly for us. (It's possible that this crash is only reproducible with broken connectivity: I wrote this patch on a train, with intermittent mobile broadband coverage.) In libdbus < 1.7.4, libraries cannot safely initialize libdbus for multi-threading, because that initialization is not itself thread-safe (!); in particular, glib-networking cannot safely initialize libdbus. So, we have to do it. I have written patches to make libdbus thread-safe-by-default, but they haven't all been reviewed and merged yet, and in any case they won't be in a stable libdbus until 1.8. Until then, each application has to discover and fix this bug individually. Bug: https://bugs.freedesktop.org/show_bug.cgi?id=65296 Reviewed-by: Xavier Claessens commit f14dfc2f19025a0f9252e58d8b1f6cd8ba6a2744 Author: Simon McVittie Date: 2013-06-06 13:43:22 +0100 Prepare 0.17.5 commit f844ca21c4074201ebb4f3e78531ae29dff97e6f Author: Simon McVittie Date: 2013-06-04 18:34:14 +0100 Disable unreliable test-case It has a race condition or something. Bug: https://bugs.freedesktop.org/show_bug.cgi?id=49595 Reviewed-by: Guillaume Desmottes commit 67d009ba2e6ac6d21c659d7586670e3e40b5ec3e Author: Simon McVittie Date: 2013-06-04 12:40:45 +0100 Initialize libdbus for thread-safety libdbus is not thread-safe by default. This is a long-standing design flaw (). We call into GIO, which calls into glib-networking, which can (at least in recent versions) invoke libproxy in a thread. libproxy apparently has a Network-Manager plugin, which uses libdbus in that thread; meanwhile, we use libdbus in the main thread and everything goes badly for us. (It's possible that this crash is only reproducible with broken connectivity: I wrote this patch on a train, with intermittent mobile broadband coverage.) In libdbus < 1.7.4, libraries cannot safely initialize libdbus for multi-threading, because that initialization is not itself thread-safe (!); in particular, glib-networking cannot safely initialize libdbus. So, we have to do it. I have written patches to make libdbus thread-safe-by-default, but they haven't all been reviewed and merged yet, and in any case they won't be in a stable libdbus until 1.8. Until then, each application has to discover and fix this bug individually. Bug: https://bugs.freedesktop.org/show_bug.cgi?id=65296 Reviewed-by: Xavier Claessens commit e7efe3e1470e060b5c214f9678b7b16b7f154b47 Author: Simon McVittie Date: 2013-06-04 12:38:38 +0100 NEWS commit 72c46475b6b116b431ef26ea711a53e951c2abab Author: Simon McVittie Date: 2013-06-04 12:33:16 +0100 Update Wocky to fix a regression caused by #65131 commit d26688c2472ffe697e7495b2e480a1ca335ddce8 Author: Simon McVittie Date: 2013-06-03 12:40:50 +0100 Avoid having two of the same set of commands run in parallel A rule like this: _gen/x.c _gen/x.h: prerequisites $(AM_V_GEN)x-generator doesn't consider x.c and x.h together. Instead, it expands to two rules, one to generate x.c and one to generate x.h, which happen to run the same commands. This means that in the worst case, you can end up running x-generator twice in parallel, and they'll race with each other and overwrite or delete each other's output. Based on commit 36c2a545c from telepathy-glib. Bug: https://bugs.freedesktop.org/show_bug.cgi?id=64285 Signed-off-by: Simon McVittie Reviewed-by: Xavier Claessens commit 0c7953c80da6aa0fef030669c0588a3451678498 Author: Simon McVittie Date: 2013-05-30 20:34:47 +0100 Update Wocky for fd.o #65131, and test it Bug: https://bugs.freedesktop.org/show_bug.cgi?id=65131 Reviewed-by: Xavier Claessens commit 7e8aec731742c07513419e3c3582337871b066d3 Author: Simon McVittie Date: 2013-05-30 17:03:11 +0100 nano version and retroactive release name commit 9e7500178fa2395c8955680e5c17c40a4b981407 Author: Simon McVittie Date: 2013-05-30 17:01:16 +0100 nano version commit cface3d679b42bb30e4d489fd493bb6784c275e2 Author: Simon McVittie Date: 2013-05-30 16:24:38 +0100 prepare 0.17.4 commit 4cf2247825fba29a4432bbea9fb1522cd80af599 Author: Simon McVittie Date: 2013-05-30 14:32:51 +0100 vcard/supported-fields: catch up with fd.o #64319 Reviewed-by: Xavier Claessens commit 880a75bbf354d48a706c56afafd381040a1d9de8 Merge: 3157284 84a2068 Author: Simon McVittie Date: 2013-05-30 12:55:31 +0100 Merge branch 'telepathy-gabble-0.16' Conflicts: NEWS configure.ac lib/ext/wocky commit 84a20687c54c84aa61c9f3a606c8eb3bd14a8544 Author: Simon McVittie Date: 2013-05-29 17:31:47 +0100 Prepare version 0.16.6 commit 1e99c77f8d8a686c4c1714a959c062bda6dc0c44 Author: Simon McVittie Date: 2013-05-29 12:05:09 +0100 Add a regression test for fd.o #65036 Bug: https://bugs.freedesktop.org/show_bug.cgi?id=65036 Reviewed-by: Will Thompson commit c1d101558de76e3ebacd05fb032764a126d28468 Author: Simon McVittie Date: 2013-05-28 12:04:44 +0100 tests: fix JabberAuthenticator when self.emit_events is False We don't currently use JabberAuthenticator in this mode, so nobody noticed that it didn't work. I'm about to add a test that does use it. commit e8623e79ec356d4e72286140aa31656dc87b0567 Author: Simon McVittie Date: 2013-05-29 17:22:51 +0100 NEWS: update and describe configuration changes for fd.o #65036 Bug: https://bugs.freedesktop.org/show_bug.cgi?id=65036 Reviewed-by: Will Thompson [added CVE ID now that we have one -smcv] commit f35fa75276d6e04ea9f99e2df05a75284be920e8 Author: Simon McVittie Date: 2013-05-29 17:21:00 +0100 Update Wocky for fd.o #65036 Bug: https://bugs.freedesktop.org/show_bug.cgi?id=65036 commit 3157284e58195cab12a404752f7d96e3c4ea650b Author: Xavier Claessens Date: 2013-05-08 13:07:34 +0200 X-TELEPATHY-PASSWORD is not supported if username isn't known Fixes https://bugs.freedesktop.org/show_bug.cgi?id=64354 commit 57e62a7871be3209fca3046aaf99cdd055fef59d Author: Xavier Claessens Date: 2013-05-07 15:48:55 +0200 Google vcard can now have an URL field Fixes https://bugs.freedesktop.org/show_bug.cgi?id=64319 commit 7d81f2e320573647b2049c02a31db0aa3d554e77 Merge: ac6b926 f53ea2d Author: Simon McVittie Date: 2013-04-04 16:22:08 +0100 Merge branch 'telepathy-gabble-0.16' commit f53ea2d64807cf625ca49f6163b77a36a8282f0d Author: Simon McVittie Date: 2013-04-04 16:21:33 +0100 Merge with-session-bus.sh changes from telepathy-glib We also have local changes, which should go "upstream" to telepathy-glib at some point. Bug: https://bugs.freedesktop.org/show_bug.cgi?id=63119 commit ac6b926a59d30888a5a62779da16d45f99eeb292 Author: Simon McVittie Date: 2013-03-18 11:46:12 +0000 Update Wocky for #61792 commit 712d271f74b4ff1cd904c66d3e258ec544a933a4 Author: Will Thompson Date: 2013-03-04 12:22:00 +0000 Bump nano-version commit 39aa8ff053de4d127af69bdbe2e896a4f098b284 Author: Will Thompson Date: 2013-03-01 16:45:42 +0000 Update Wocky to fix distcheck commit f831ac1e20cdc9212ffae24fadd38621e735d61b Author: Will Thompson Date: 2013-03-01 15:16:04 +0000 Version 0.17.3 commit e5ff011d3d17cef59e48f47f06f3adae91d9b657 Author: Will Thompson Date: 2013-03-01 15:15:52 +0000 NEWS for 0.17.3 commit 675565c73c265b7a545f45165ed97299890453fc Merge: 73c8044 ff28128 Author: Will Thompson Date: 2013-03-01 15:11:53 +0000 Merge branch 'telepathy-gabble-0.16' Conflicts: NEWS configure.ac lib/ext/wocky commit 73c80443fd8594212f1a0f9fb8a99a0a4d691dbc Merge: 0bffd2d fa8be7c Author: Will Thompson Date: 2013-03-01 15:09:04 +0000 Merge branch '43166-muc-rate-limit' commit ff28128c7699b7fee5757a742f07b4cf9c98764c Author: Will Thompson Date: 2013-03-01 15:02:50 +0000 Bump nano-version to 0.16.5.1 commit ed05203947880a0f4c45a7f95316e4cc28b23442 Author: Will Thompson Date: 2013-03-01 12:24:16 +0000 Version 0.16.5 commit 4882535eeeb5843e26dd2b6f0aeaff3f201cbfe6 Author: Will Thompson Date: 2013-03-01 09:16:25 +0000 NEWS for 0.16.5 commit 110c5af4da0dfd7cd658a1d12047f0dbc79ae2d6 Author: Will Thompson Date: 2013-02-25 09:51:22 +0000 Update Wocky snapshot to fix 61433 commit fa8be7c05185ce92696a5771b556e70b8eb99a00 Author: Will Thompson Date: 2013-02-22 08:31:18 +0000 MUC: stop sending chat states after Fixes: https://bugs.freedesktop.org/show_bug.cgi?id=43166#c0 commit f843f499be4c16683a4a5b054a5bf747254b2050 Author: Will Thompson Date: 2013-02-22 08:16:23 +0000 muc/chat-states: fix and uncomment commented chunk This didn't work as-is because the had no child. Using make_muc_presence() does the job nicely. commit 114c5bc092a9645ecb26cb3310b01d6c8fc08cbc Author: Will Thompson Date: 2013-02-22 08:15:30 +0000 muc/chat-states: tidy up checking message bodies commit 62fa6bd989afaec604f420692e10e7716a28496c Author: Will Thompson Date: 2013-02-15 08:57:14 +0000 muc: test resource-constraint is exposed reasonably https://bugs.freedesktop.org/show_bug.cgi?id=43166#c7 commit cc90a503a9a9ef4aab30b60512f3129de0c0b752 Author: Will Thompson Date: 2013-02-20 09:26:39 +0000 Test Prosody's MUC's rate limit is exposed correctly now There were a combination of problems before: * The server's error message was not exposed in the delivery report; * Wocky didn't recognise policy-violation; * There was a bug in Wocky's error parsing code which clobbered the message type. So many bugs for such a small stanza! commit 96e5379434adde498624517188be9183e3d4a443 Author: Will Thompson Date: 2013-02-20 09:19:56 +0000 Map policy-violation to Send_Error_Permission_Denied This code is used by prosody's MUC rate-limiter. commit 21b752bf71edc6889161dca905c3c8300c254355 Author: Will Thompson Date: 2013-02-20 09:15:04 +0000 Update Wocky snapshot The improvements are: * A new error code, WOCKY_XMPP_ERROR_POLICY_VIOLATION, added in RFC 6120 and used by Prosody's MUC rate-limiting; * Fixes to the error parsing code when error conditions are not recognised; * Tweaks to WockyMuc's error signals to include the full GError and WockyXmppErrorType in both. commit 8f315a9dc0e575375a8e592bf84591bdec64afc0 Author: Will Thompson Date: 2013-02-15 08:55:25 +0000 muc-channel: include error message in delivery reports commit 717c765b11fe101d9f32bb352f0377ccc762726d Author: Will Thompson Date: 2013-02-15 08:56:42 +0000 muc/send-error: refactor There will be more cases shortly commit f842fbbb1c09cbb60c379006958f178e7f2d34aa Author: Will Thompson Date: 2013-02-15 08:39:36 +0000 muc-channel: pass full error into _receive commit e80923ff9fb329d39962e4fdb7ba800a1f2a2cb3 Author: Will Thompson Date: 2013-02-15 08:21:11 +0000 muc-channel: pull extracting stanza errors up Really, WockyMuc::message-error should give the error text too, not just the error code, but we'll come to that. commit 0bffd2d8302d81a804f5186f17445abae16b1d85 Author: Will Thompson Date: 2013-02-12 10:21:54 +0000 tests: don't block on Disconnect() without sending These three tests were making blocking calls to Disconnect() rather than using the disconnect_conn() helper. As a result, Gabble was waiting 5 seconds for the test server to send back before timing out. Using disconnect_conn(), which sends the stream close back properly, shaves between 15 and 18 wasted seconds off the test suite. commit 0d65aee3d0c72ebf4c2713945524ae5b80e01c08 Author: Will Thompson Date: 2013-01-17 12:51:22 +0000 NEWS for the Jingle code moving to Wocky commit 1741adad14a19046aaea8109751d6f7680df0631 Author: Will Thompson Date: 2013-01-16 10:41:32 +0000 Update Wocky snapshot for wocky.h with Jingle headers commit 1cdfcf9b9be0b76c3c9fc2cc98683f9e10a8f8a8 Author: Will Thompson Date: 2013-01-08 15:09:19 +0000 Remove reference to GabbleMediaSessionMode This enum is obsolete. commit e9cc205c266f628e74c47b4a1bb949be8cc4b8c3 Author: Will Thompson Date: 2012-12-12 16:08:34 +0000 Delete all the Jingle code which moved to Wocky The astute reader will notice that I added a call to jingle_share_register() to jingle-mint.c. commit f91a1063b57b8b50bd3301633e8620051be2e906 Author: Will Thompson Date: 2012-12-12 15:25:15 +0000 Update Wocky snapshot to contain Jingle code! commit 8bb1176913d7bbb7beaf63c61b3f95dcc4b8c3a5 Author: Will Thompson Date: 2012-12-11 16:43:39 +0000 jingle-types: Remove reference to GabbleJingleShare This isn't getting moved into Wocky. commit 3936c9dfd5e184abc9d807be8582657aaed81c9a Author: Will Thompson Date: 2012-12-11 15:09:14 +0000 jingle-transport-iface: fix header comments commit efc093b82db19196d66e02100316295c0cc7e92e Author: Will Thompson Date: 2012-02-21 16:11:32 +0000 Rename most of GabbleJingle* to WockyJingle* This renames the classes I'm moving to Wocky, and updates all the remaining code to cope. A few things are missed out because it's only reasonable to change them when the code is in Wocky: • GABBLE_TYPE_STUN_SERVER_SOURCE (the GEnum for WockyStunServerSource) can't be renamed easily because it is generated by Gabble's enumtype thing. • GABBLE_DEBUG_MEDIA. Here's the program I used, for posterity. #!/usr/bin/perl -pi use strict; BEGIN { undef $/; } my @Classes = qw(Factory Session Transport_Iface Transport_IceUdp Transport_RawUdp Transport_Google Content Info Media_Rtp Relay); for my $Class (@Classes) { my $FullClass = "Jingle$Class"; $FullClass =~ s/_//g; my $under_class = "jingle_" . lc($Class); my $UNDER_CLASS = uc($under_class); s/Gabble$FullClass/Wocky$FullClass/g; s/gabble_$under_class/wocky_$under_class/g; s/GABBLE_$UNDER_CLASS/WOCKY_$UNDER_CLASS/g; s/GABBLE_TYPE_$UNDER_CLASS/WOCKY_TYPE_$UNDER_CLASS/g; s/GABBLE_IS_$UNDER_CLASS/WOCKY_IS_$UNDER_CLASS/g; } s/GabbleGoogleRelay/WockyGoogleRelay/g; s/gabble_google_relay/wocky_google_relay/g; s/GABBLE_N_JINGLE_RELAY_TYPES/WOCKY_N_JINGLE_RELAY_TYPES/g; s/gabble_stun_server/wocky_stun_server/g; s/GABBLE_STUN_SERVER/WOCKY_STUN_SERVER/g; # Can't rename GABBLE_TYPE_STUN_SERVER_SOURCE because it's generated. Blah. s/GabbleStunServer/WockyStunServer/g; my @Blah_Blah = qw(Candidate State Action Content_Senders Transport_Type Transport_Protocol Candidate_Type Reason Transport_State Dialect Content_State Media_Type Codec Media_Description Feedback_Message Rtp_Header_Extension Media_Profile); for my $Class (@Blah_Blah) { my $FullClass = "Jingle$Class"; $FullClass =~ s/_//g; my $under_class = "jingle_" . lc($Class); my $UNDER_CLASS = uc($under_class); s/\b$FullClass/Wocky$FullClass/g; s/\b_$FullClass/_Wocky$FullClass/g; s/\b$under_class/wocky_$under_class/g; s/\b$UNDER_CLASS/WOCKY_$UNDER_CLASS/g; } s/MAX_JINGLE_STATES/WOCKY_N_JINGLE_STATES/g; s/JINGLE_IS_GOOGLE_DIALECT/WOCKY_JINGLE_DIALECT_IS_GOOGLE/g; commit 0f1435f548071f873d7626643f250b04b53931c9 Author: Will Thompson Date: 2012-12-07 19:31:31 +0000 Bump nano-version to 0.17.2.1 commit 301a588cbac0745f5f737c0d8d3e45d69e88dddf Author: Will Thompson Date: 2012-12-07 18:53:18 +0000 Version 0.17.2 commit bf5dcce04b9063c9482def3041dd80b79d47f5b7 Author: Will Thompson Date: 2012-12-07 16:13:16 +0000 tests: don't crash in verbose mode on unicode in stanzas We have this hack that sets __repr__ on domish.Element to call its toXml() method, which ends up being used by the verbose logging code when it dumps all of an event's attributes. Unfortunately, this blows up if a stanza contains non-ASCII characters, because repr() tries to convert unicode to str using .encode('ascii'). This made running jingle/test-send-file in verbose mode fail, because the file being sent by the test has a non-ASCII filename. The fix is to make the __repr__ hack escape non-ASCII characters using the unicode-escape codec. commit 55930f39965c9d9ba356aaba4a24fe4f687a5d3d Author: Will Thompson Date: 2012-12-07 12:59:12 +0000 NEWS for 0.17.2 to date. commit 51e301d6366e7e5f0f203f19fd594cef27ae05db Merge: c33a817 52beb82 Author: Will Thompson Date: 2012-12-06 18:35:37 +0000 Merge branch 'xep-0184' https://bugs.freedesktop.org/show_bug.cgi?id=47378 commit 52beb82932c6c31f43e6b4465a22f5543cff1d06 Author: Will Thompson Date: 2012-11-01 15:55:35 +0000 Advertise support for message receipts commit a997153f14872719d9be2a0947b58ab29e1ec3a1 Author: Will Thompson Date: 2012-11-01 15:54:57 +0000 Add missing cap for jabber:iq:last commit 70765bfe80f49dabe5396d8be50fa61daf66b705 Author: Will Thompson Date: 2012-10-31 23:32:41 +0000 Test all this receipt stuff. I did write this test as I went along, but I forgot to commit it. Whoops. commit 121fa91e6939dca982e631d104405cb2735b1ecb Author: Will Thompson Date: 2012-10-31 23:06:04 +0000 im-channel: send delivery reports when requested... ...and the sender is able to see that we are online, is not blocked, and so on. This is not a presence leak: all this allows the peer to do that they cannot already do is determine that our internet connection has not died without our server noticing. Sending a receipt as soon as the message is signalled to the UI, rather than after it's acknowledged, is absolutely compliant with XEP-0184 §4: > Finally, this protocol does not enable the sender to know that the intended > recipient has read the message[, ...] that an end user client has > presented the message to a human user (if any), etc. commit 4af3fe1066bfd36894fb10f80148269ed94d6c11 Author: Will Thompson Date: 2012-10-31 23:04:22 +0000 roster: obey blocked-ness in gets_presence_from_us This should be irrelevant because blocked contacts shouldn't be able to send us anything which would cause us to send back a reply (eg. jabber:iq:last would be blocked by our server), but just to reassure the reader. commit 6077e9b79db0b7a36bc581f4dd049030289cdf72 Author: Will Thompson Date: 2012-10-31 22:52:52 +0000 im-channel: clean up obsolete docstring In 574d146 I removed the “magical adventure” aspect of _gabble_im_channel_receive(). commit ee3a7d4deec58856d7d1a01ad71157a9b88185b8 Author: Will Thompson Date: 2012-10-31 22:44:07 +0000 Support requesting delivery reports when asked to. commit a990012f6e48af641686741ea52506230cdcaaad Author: Will Thompson Date: 2012-10-31 20:19:18 +0000 Emit delivery reports for XEP-0184 receipts. commit c33a817b5bcf9f6bec7c744e4c5a3f0b96df4118 Author: Will Thompson Date: 2012-11-20 17:46:50 +0000 Don't crash on after closing auth channel. commit a743f0475e440b8b9712df20bb8d997ff6f23177 Author: Will Thompson Date: 2012-11-20 16:21:30 +0000 tests: add a couple more cases to sasl/abort There's no reason these should be any different, but I thought it might be worth trying them anyway. commit be0be56f6c097f1a54c64421303dfeb3b930d7a6 Author: Will Thompson Date: 2012-11-20 16:06:17 +0000 Don't crash on after closing auth channel https://bugs.freedesktop.org/show_bug.cgi?id=52146 commit e33a5de2806bcf63543fdabb3e8607e9d699ca1c Author: Will Thompson Date: 2012-11-20 16:02:04 +0000 auth-manager: clarify _challenge_async() Keeping our own GSimpleAsyncResult rather than leaving it up to the channel means _finish() doesn't have to worry about what happens if the channel closes. The g_assert_not_reached () is not safe, but is equivalent to the previous behaviour (wrongly chaining up to the parent class makes it assert). Next, a fix… commit 9854f7cb36f71093855af32747aea0031679cf6a Author: Will Thompson Date: 2012-11-20 14:45:13 +0000 auth-manager: propagate failure both up and across If we're delegating up to Wocky's internal SASL machinery, it might want to know about the handshake failing. If a SASL channel is open, we should let it know so that it can let its handler know that the handshake failed. If we're using X-TELEPATHY-PASSWORD, both these conditions are true. commit 5077f330e3c43d6b0ad5747a4151f9b8492cb21f Author: Will Thompson Date: 2012-11-20 14:09:42 +0000 auth-manager: rename 'falling_back' field to 'chaining_up' commit 37cbec4ae810668e08764fd5f364708cd6f4c766 Author: Will Thompson Date: 2012-11-20 14:09:19 +0000 auth-manager: ensure falling_back is TRUE when we're chaining up commit 6b8656685c4c640a4e753cc94379b87a2ffbfadf Author: Will Thompson Date: 2012-11-20 14:04:50 +0000 auth-manager: simplify chaining up in _start_auth_async() Rather than using whether or not priv->channel is NULL in _start_auth_finish() to determine how to handle the result, let's always keep our own GSimpleAsyncResult. In this case it doesn't matter very much, because priv->channel in practice can't become NULL in the gap between the callback being called and Wocky calling _start_auth_finish(). In the other cases it does matter, and causes crashes. I'll be fixing those next. commit 8fd563ccf66e89aaa20bb37f397aa21b23b7d012 Author: Will Thompson Date: 2012-11-19 18:00:52 +0000 auth-manager: save GError from channel when it closes commit 2b05a3888537cda55710c804d79010677456e503 Author: Will Thompson Date: 2012-11-19 17:56:55 +0000 sasl-channel: stash a GError when Close()d commit 2dcfd322daa652c4aeb5adbf7b523e5cd36c8309 Author: Will Thompson Date: 2012-11-19 17:51:23 +0000 sasl-channel: stash a GError in more cases. commit 84e56d9c2c1c1ec20c8228c887140149dad3c07a Author: Will Thompson Date: 2012-11-19 17:25:11 +0000 sasl-channel: split changing state and setting errors commit d096c9b10d5c0d7787dccd05cd3dae23ffed99a4 Author: Will Thompson Date: 2012-11-13 18:02:15 +0000 sasl-channel: don't crash on Abort() then commit 0d347435075681741a6ffba1b75fdeb147e63143 Author: Will Thompson Date: 2012-11-13 17:53:30 +0000 sasl-channel: refactor completing operations to a function commit a4a45536a7a3c3b489d8f229e6305b014e18e983 Author: Will Thompson Date: 2012-11-06 18:11:45 +0000 Add commented-out regression tests for fd.o#52146 test_close_then_challenge() and test_close_then_success() each trigger similar bugs. test_abort_then_challenge() and test_abort_then_success() both trigger the same bugs by a different route, which I don't think my first attempt at fixing the original bug would have caught. test_close_then_failure() and test_abort_then_failure() pass, and are here for completeness. commit 05c044cc47d87222176aee3bfdc278d176d93abe Author: Will Thompson Date: 2012-11-13 16:47:05 +0000 sasl/abort: clean up common test code commit ea9c62166997004b97dc4a044d44df57ea4c9d91 Author: Will Thompson Date: 2012-11-06 17:51:52 +0000 sasl/abort.py: clean up invoking test cases commit 19b535ebbacf6cc20c0c3fe82cf51815e391f362 Author: Will Thompson Date: 2012-11-13 17:13:43 +0000 gabbletest: exit if we can't connect to the bus Previously, if the bus wasn't there (perhaps I killed it when trying and failing to ^C the test suite?) the test would just sit there forever after the unhandled exception made its way up to the mainloop. commit 13470c3c4f1602190bcf427911ceb6bb784d9cda Merge: d2db709 204d30f Author: Will Thompson Date: 2012-12-06 17:37:01 +0000 Merge branch 'vcard-manager-use-nodes' https://bugs.freedesktop.org/show_bug.cgi?id=57603 commit 204d30fa664a2fde56529f451348c457a6e8a57a Author: Will Thompson Date: 2012-11-27 10:37:37 +0000 vcard-manager: find no-op replacements with wocky_node_equal By representing the replacement we have in mind as a WockyNode[Tree], we don't have to reimplement the comparison. commit dbe5edf811d8585866eb4ac605676963a7f19601 Author: Will Thompson Date: 2012-11-27 10:27:40 +0000 vcard-manager: represent new node tree as a node tree. commit 1327065e89774ae1f7ffe07dcc473f21d4bdac60 Author: Will Thompson Date: 2012-11-27 09:46:00 +0000 vcard-manager: refactor applying edits I found it incredibly hard to follow the old structure; a despatch table with one function per case is much clearer, albeit a little longer. commit ad66082f7e91932db4b8cdd07941951c4d6174f7 Author: Will Thompson Date: 2012-11-26 17:13:35 +0000 vcard-manager: don't repeatedly copy while editing Previouly, the entire would be copied for every single edit in the queue, which is dumb: we can just make a copy, patch it according to each edit, and send it if it's changed. commit f7baa0f159e36f0f859fecca4e2af726ac1a6553 Author: Will Thompson Date: 2012-11-26 17:08:29 +0000 vcard-manager: fix is_significant() gabble_vcard_manager_replace_is_significant() is meant to check whether we're actually changing a value, but in fact it basically always claimed we're not, because it forgot to make a note of whether it's seen the relevant element. By fixing this, we can remove a redundant check at the end of gabble_vcard_manager_edit_info_apply(). commit dbd66dd645e5e17f8915cce28163f7ba34dba73f Author: Will Thompson Date: 2012-11-26 17:00:28 +0000 vcard-manager: clarify commentary in CLEAR path commit de157563532a3196dc55a14ee9e3887a4529ec4b Author: Will Thompson Date: 2012-11-26 16:58:48 +0000 Test ClearAvatar() I wasn't sure from reading the code that this actually worked any more, and it wasn't tested. I'm happy to say that it does work. commit 2deced34fe350538fceb9ca8775f0cd8f91ddc15 Author: Will Thompson Date: 2012-11-26 16:43:43 +0000 vcard-manager: split up gargantuan edit_info_apply() commit b4604cc6017a7426f716ac4664ccd82b10bad1aa Author: Will Thompson Date: 2012-11-26 15:11:25 +0000 vcard-manager: copy vcards using WockyNodeTree. This is actually less efficient than what was there before, because it copies the tree twice. I think this is symptomatic of Gabble's edit representation being wrong. commit 9f3562e849f56bb3ecfbdf985722db3deeb00a40 Author: Will Thompson Date: 2012-11-26 16:01:19 +0000 Update Wocky snapshot for beautiful new node API commit 5497c9d72b41b47c0e2bc7c6c2ec1bec8bca14be Author: Will Thompson Date: 2012-11-26 14:38:30 +0000 vcard-manager: use WockyNodeTree to copy vCard nodes There's actually another implementation of copying a node tree in this file which is a little more involved. I wasn't going to touch it when I thought there was just one, but finding TWO… commit 202014297342e5ba2361879734c6784f71b2cd54 Author: Will Thompson Date: 2012-11-26 13:51:54 +0000 vcard-manager: add children more neatly commit 7ff6d247658199261df73db47b0216fd53a05c09 Author: Will Thompson Date: 2012-11-26 16:09:30 +0000 ft-channel: add metadata forms a bit less stupidly commit d2db709101b0784e8689433ec547cb826f469a6b Author: Will Thompson Date: 2012-11-27 23:35:58 +0000 conn-presence: stop using _gabble_connection_send_with_reply() _gabble_connection_send_with_reply() does not call its callback if it doesn't get a reply. This means all these cases were leaking the GSimpleAsyncResult if the connection died while the IQ was in flight. commit 9687c65355f885bc17d7f5f6c849c5f333397021 Author: Will Thompson Date: 2012-11-27 18:40:29 +0000 Regression test for malformed privacy list replies commit 86ba1bdac4b88ae23b5f37e349afabacb840f03c Author: Will Thompson Date: 2012-11-27 18:07:56 +0000 Tidy up privacy list test very slightly commit 2c1e66315f34380a387ea11a81c227066c38e204 Author: Will Thompson Date: 2012-11-27 18:06:44 +0000 Regression test for 57521 commit c7d2dfbe19eb5a3c1c9dfbefa61fb48b3416e064 Merge: 3fe57e7 3b10a7f Author: Will Thompson Date: 2012-12-06 16:28:34 +0000 Merge branch 'telepathy-gabble-0.16' commit 3b10a7f1b0fcb728210eb12231df8b1a4c289c3b Author: Will Thompson Date: 2012-11-27 18:40:06 +0000 privacy lists: don't crash on malformed replies commit 0d908c122903a384882eff7de0e9ec6d6058d661 Author: Will Thompson Date: 2012-11-27 17:45:51 +0000 Don't crash when creating invisible privacy list fails. Fixes: https://bugs.freedesktop.org/show_bug.cgi?id=57521 commit 3fe57e731b2518e22ba21efb4d16f50564363a2f Author: Will Thompson Date: 2012-03-29 15:27:11 +0100 bytestream-socks5: send empty offer if we can't initiate. This looks pretty ridiculous, so needs some explanation. If you have no network connection (suppose you're on a train, testing tubes using Prosody on localhost), then get_local_interfaces_ips() returns NULL (the loopback interface is explicitly skipped). So previously, after the recipient has accepted the tube, gabble_bytestream_socks5_initiate() would fail for the initiator. bytestream-multiple doesn't fall back if it fails immediately; so the whole bytestream-establishment fails. The initiator's tube channel would close, but no stanza gets sent to the peer to tell it to stop waiting for the bytestream offer, so it's just hanging there waiting forever. Just making bytestream-multiple fall back immediately doesn't help, because the peer is still expecting a SOCKS5 offer, rather than a in-band bytestream offer, so rejects it. Unlike Jingle, SI doesn't have a way to cancel an offer once it's been accepted but before the transport has been negotiated. So… if we send the peer an empty offer, they'll send back an error, and both sides know to fall back (if they understand bytestream-multiple) or give up (if not). There is no test case for this, because it's difficult to make get_local_interfaces_ips() or gibber_listener_listen_tcp() fail from the test suite. I tested it with a pair of simple D-Bus tube clients and no network connection, and/or hacking get_local_interfaces_ips() to always return NULL. Although this trick is schema-compliant , it's not compliant with the wording of XEP-0065. Quoth §5.3.1 : > The element MUST contain one or more elements, > each of which MUST possess the 'host', 'jid', and 'port' attributes. But it also says: > If the request is malformed …, the Target MUST return an error of > . So this should work with any (robust) implementation. The option would be to (try to) start listening before sending the SI offer—if it fails, don't offer to use SOCKS5 bytestreams. https://bugs.freedesktop.org/show_bug.cgi?id=48050 commit dfe1f729d0026ee72202f43733d9d3376b9d8447 Author: Will Thompson Date: 2012-03-29 13:45:47 +0100 bytestream_factory_negotiate_stream(): don't pass stream id to callbacks None of them use it, so… commit 6f95a1e49fb7e59eeeb3d4581e678a6c3141868d Author: Will Thompson Date: 2012-03-29 13:41:25 +0100 bytestream_factory_negotiate_stream(): remove return value It's always TRUE, so it's unneeded. commit aacafd899c775acada617c61c0418ba25166e274 Author: Will Thompson Date: 2012-03-29 13:35:29 +0100 bytestream_factory_negotiate_stream(): use send_iq_async This clarifies that this function can never fail, which can be the next thing we clean up… commit a6dcef8cd434e05ffbdb569ecdceca68cd2bc04a Author: Will Thompson Date: 2012-03-29 13:29:04 +0100 bytestream-factory: stop NIHing TpWeakRef Step one in cleaning up gabble_bytestream_factory_negotiate_stream() is to simplify its context structure. commit 191c3fbfa4f5309227e664273cd8b300871b04b9 Author: Will Thompson Date: 2012-03-29 13:06:24 +0100 Split gabble_bytestream_socks5_initiate() in two This more clearly separates the code which sets up the listener (which can fail) and the subsequent code to transmit IP addresses to the peer (which cannot fail), I think. commit a188bd209fc34195a0ccd2d12485c1da68265299 Author: Will Thompson Date: 2012-03-29 11:56:58 +0100 bytestream-socks5: stop using _conn_send_with_reply This makes it clearer that gabble_bytestream_socks5_initiate() can only fail synchronously for local reasons. _gabble_connection_send_with_reply() only returns FALSE if we have no porter; but from the moment we go to state CONNECTED (before which you can't request channels) to the moment the connection is disconnected, we do have a porter. commit 0adea4c2574e53a687e20177175c18a5ce038767 Merge: d5b1bc6 89239d4 Author: Will Thompson Date: 2012-12-06 12:43:41 +0000 Merge branch '57546-there-can-be-only-one-jingletest' https://bugs.freedesktop.org/show_bug.cgi?id=57546 commit d5b1bc6cc7d76aa32bd441703c2da72213fa33ea Merge: de8905b 6bc2931 Author: Will Thompson Date: 2012-12-06 12:30:46 +0000 Merge branch '57615-misc-cleanup' commit de8905b5bb098c6f0468e9a2856006d1583ad80c Merge: b735dc9 fbd33aa Author: Will Thompson Date: 2012-12-06 12:26:28 +0000 Merge branch '25961-stun-srv' https://bugs.freedesktop.org/show_bug.cgi?id=25961 https://bugs.freedesktop.org/show_bug.cgi?id=25385 commit b735dc9c1320b9a6c206a0856758c60c130428a8 Author: Will Thompson Date: 2012-12-06 12:04:17 +0000 test-multift: clean up a bit commit dd3d88b53132946ed313da29e30523761e4f9fcd Author: Will Thompson Date: 2012-12-06 11:55:14 +0000 test-multift: don't rely on q.expect timing out Previously, this test checked that events didn't happen by waiting for them to happen, asserting if they did, and catching the TimeoutError which occurred if they didn't. But it's more obvious what's happening to forbid the event, take all the actions, and then sync the stream to make sure it hasn't appeared. Plus, it shaves 6 seconds off the test. commit 57ba5c87466279069bf332006e2d0947c4807633 Author: Will Thompson Date: 2012-12-06 11:18:15 +0000 Fix file transfer tests for these new interfaces commit cf1795dec89679ec14cf8d48d0826d616299a654 Author: Daniele E. Domenichelli Date: 2012-11-29 15:16:01 +0100 Export o.f.T.Channel.Type.FileTransfer.FUTURE interface commit 2bbb612f31af9b8a84f8db44c68aab92a424d92d Author: Daniele E. Domenichelli Date: 2012-11-29 12:45:36 +0100 Fix o.f.T.Channel.Interface.FileTransfer.Metadata Fixes: fd.o#57267 commit 6bc2931e859d9c23b8d81fede23c0a962514dfae Author: Will Thompson Date: 2012-11-27 15:40:29 +0000 Don't use add_child_with_content with NULL/"" content This is harmless, but looks messy. It's leftover clutter from the loudmouth era. commit 6dacd571ae133e9f8ada918e6222b79c932e0788 Author: Will Thompson Date: 2012-11-27 15:08:04 +0000 Avoid manually setting WockyNode.ns where sensible. There's still one case in the Jingle code which seems justifiable. commit 0a7fd199e0ce6b1e67fc18cdae023c9ce9b608b4 Author: Will Thompson Date: 2012-11-27 14:45:13 +0000 jingle-session: tidy up sending content-reject in idle commit 89239d474941b766d2f41e9ee2a89094a3d4d980 Author: Will Thompson Date: 2012-11-26 09:50:12 +0000 Remove the olde jingletest commit 8e19ae80d2696f42fc472440ec10c89bb844d932 Author: Will Thompson Date: 2012-11-26 09:37:02 +0000 jingle/google-relay: use jingletest2 commit 240e6529fdc298cc399aa78a45947664c6f2c2dc Author: Will Thompson Date: 2012-11-26 09:34:17 +0000 jingle/test-wait-for-caps-incomplete: don't use jingletest This test didn't actually use any of the jingle protocol stuff at all—it's just a caps test. commit 01bd7ab1c9088ce4151cdca32e58585fb95334e1 Author: Will Thompson Date: 2012-11-26 09:31:56 +0000 jingle/test-wait-for-caps-incomplete: unskip half the test The connection doesn't seem to leak any more. I added this in 07822b272 because . I haven't investigated fully, but the test passes, sooooo... :) commit d4ffb25c14d5686c9419ece0826fe2e0de9491fe Author: Will Thompson Date: 2012-11-26 09:20:46 +0000 jingle/test-wait-for-caps: use jingletest2 commit 4aed845bc67b1d98b6d6f69bc1a5c4dbd2ac4deb Author: Will Thompson Date: 2012-11-26 09:13:54 +0000 jingle/stream-handler: use jingletest2 commit d31e229e8bdc3dad3e7d98bf291c103cb4948c8f Author: Will Thompson Date: 2012-11-26 09:10:39 +0000 jingle/payload-types: use jingletest2 commit c52894a5facb58465bc6933277e16307f978a767 Author: Will Thompson Date: 2012-11-26 09:01:29 +0000 jingle/payload-types: clean up a bit commit 7cdccfed7508e5a915dcd62f8936393e67884b56 Author: Will Thompson Date: 2012-11-26 08:56:05 +0000 jingle/payload-types: remove obsolete FIXME commit 804cf93f08c79b400cdca43b45895b2922f05464 Author: Will Thompson Date: 2012-11-24 13:34:46 +0000 jingle/outgoing-many-streams: use jingletest2 commit c953579854451a79a005ca2a5663f3230c30efc8 Author: Will Thompson Date: 2012-11-23 18:47:45 +0000 jingle/decloak-peer: use jingletest2 commit a6b807a29ec90046327a937c15bfe37e9f1fb6c9 Author: Will Thompson Date: 2012-11-23 18:36:35 +0000 jingletest2: make classes new-style commit fbd33aa489c1d9c129e2815ed86678ceacb0ad0d Author: Will Thompson Date: 2012-11-26 09:46:21 +0000 jingle/stun-server: use jingletest2 In passing, test all dialects. (Though I think this may be a little overzealous.) https://bugs.freedesktop.org/show_bug.cgi?id=25385 commit 84b84e7a04cc5e5d4ca51b187f7be6ebf7886062 Author: Will Thompson Date: 2012-11-23 17:20:39 +0000 JingleInfo: add a TODO for Google returning >1 STUN servers commit 5e5080f7cd7984fb4c62cee1a1856406dc210d0e Author: Will Thompson Date: 2012-11-23 16:29:56 +0000 tls/server-tls-channel: use with statement when reading certs commit 783e0913e2c70290ef99b86241fd60478d4abdba Author: Will Thompson Date: 2012-11-23 10:04:08 +0000 jingle-info: discover a STUN server using SRV Derived from a patch by Mike Ruprecht. https://bugs.freedesktop.org/show_bug.cgi?id=25961 commit 286eadd02129048ec5c7ee7c693358db5916cf9a Author: Will Thompson Date: 2012-11-23 14:17:51 +0000 jingle-info: don't discard google:jingleinfo pushes. Previously, Google's reply to our original google:jingleinfo query would be used correctly, but the act of passing it to gabble_jingle_info_take_stun_server would set get_stun_from_jingle to FALSE, causing any later calls to got_jingle_info_stanza() to ignore the updated STUN server information. We address this by distinguishing between user-set and server-provided STUN servers, as opposed to just between fallback or not. commit 149ef25677a0d4cf366c4fd0217f839d5d364ded Author: Will Thompson Date: 2012-11-23 09:56:52 +0000 jingle/stun-server: pluralize expected stun servers commit c342df10d1a2e7f04bd40afd46549cc0a9f8ca8c Author: Will Thompson Date: 2012-11-23 09:47:25 +0000 gabbletest: properly support domains other than @localhost I need to be able to vary the domain to do a variety of STUN SRV tests. Previously, whatever domain Gabble tried to connect as, the server would just bind you back to @localhost. The only test which tried to do something exotic was the Facebook own-message test, and that one seemed to work anyway… oh well! commit aae6367f43cf9fd509571e52ea87c7bee5d6124f Author: Will Thompson Date: 2012-11-23 15:51:11 +0000 tests: share more code between XmppAuthenticator subclasses commit 106749d45b6075798f15c970d8cb44501fb4eced Author: Will Thompson Date: 2012-11-22 18:04:30 +0000 jingle-info: make get_stun_server plural In practice it still returns at most one server, but just you wait… commit da988bf629a94eaaa7cd5971705f3aec4b3cb1c5 Author: Will Thompson Date: 2012-11-22 17:41:42 +0000 JingleInfo: refactor google:jingleinfo feature stuff commit e4396c735cd7ba89ab5131bc345c609d927c2ee6 Author: Will Thompson Date: 2012-11-22 16:27:37 +0000 test-resolver: remove unused field for a real GResolver commit a12bb8705eb0810b67f83dae7185a182bb1225bd Author: Will Thompson Date: 2012-11-22 16:21:35 +0000 jingle/stun-server: remove unneeded do_connect=False This used to be needed to ensure that the query Gabble sends right after connecting wasn't swallowed by the expect() calls that gabbletest makes after Connect(). But now that Sjoerd's split the dbus and stream event queues, this is no longer a problem: the only stream event it awaits is stream-authenticated, which is certainly before the jingleinfo query. commit 98add3fb1749366229fc0c211f0fa939d3dfc267 Author: Will Thompson Date: 2012-11-22 16:17:51 +0000 tests: use partial in a few places for neatness commit 7852403fa7f216d43fe583a1345cd57eef0ac5a8 Author: Will Thompson Date: 2012-11-22 14:44:05 +0000 tests: unduplicate fallback stun server check commit 8a7399906b40e99b6a0b5b3a7eff0879f7cca361 Author: Will Thompson Date: 2012-11-22 14:42:12 +0000 tests: use ns.GOOGLE_JINGLE_INFO constant commit ecee60dfc33f05e56eb2d2b34faf188943ff8931 Author: Will Thompson Date: 2012-11-23 18:26:20 +0000 jingle: correctly send back unknown-session errors I broke this in c1be75d9 but it wasn't tested. Luckily WOCKY_JINGLE_ERROR_UNKNOWN_SESSION (2) is a valid WockyXmppError too so we didn't crash before, we just sent instead. https://bugs.freedesktop.org/show_bug.cgi?id=33789 is relevant. commit 1e107a46a34f2251968478052dd4a8239ec2356b Author: Will Thompson Date: 2012-11-22 10:44:37 +0000 Update Wocky snapshot for deconsting G[S]Lists commit 28ddadbd3a3ce84bf3619d83dee005d320f3a915 Author: Olivier Crête Date: 2011-03-01 14:22:37 -0500 Don't put const before GSList, the g_slist* functions aren't const-marked commit 1d6c3044f13488e5457a31be631dec8e12068b6b Author: Will Thompson Date: 2012-11-12 14:10:57 +0000 Update Wocky snapshot to validate stanza namespaces This breaks the console plugin, which checks if an entered stanza is of a known type, but is not the same as so Wocky now says the former has type UNKNOWN. The plugin already had some code to fix up empty namespaces, but it's after the type check. For a better fix, I added API to give non-streaming WockyXmppReaders a default namespace, and used it here. In the course of fixing this, I found that telling the console to send this: hai would send this: hai which is wrong: the empty namespace was not being fixed up recursively. This is fixed as a side-effect of the default-namespace property, but this patch also adds a test. https://bugs.freedesktop.org/show_bug.cgi?id=57016 commit b09e286aa47dfafcc0809cee0e4fe97fd87f95c8 Author: Will Thompson Date: 2012-11-14 15:37:05 +0000 vcard/set-avatar: improve test commit e27b7af0d486b80fe370bd9bcbe6e109dfe1aec9 Author: Will Thompson Date: 2012-11-14 15:32:42 +0000 Make line-wrapped base64 allocation clearer commit fbee99b49de53621a8a5a38534d4cc423296aeef Author: Will Thompson Date: 2012-11-14 15:27:13 +0000 Add a regression test for line-wrapping avatars commit eae50abaec48f182b9cbe4cd2d0882f20c04be65 Author: Alban Browaeys Date: 2012-11-13 18:49:17 +0100 Fix allocation of the string passed to g_base64_encode_step. As per g_base64 documentation the minimum size is : avatar->len / 3 + 1) *4 + 4) and if line breaks are enabled: + ((avatar->len / 3 + 1) * 4 + 4) / 72 + 1 https://bugs.freedesktop.org/show_bug.cgi?id=57080 commit 67bbddce3fcc200ba97957963939b770797d68a1 Merge: 37102bb 89346a8 Author: Will Thompson Date: 2012-11-09 17:18:10 +0000 Merge branch 'telepathy-gabble-0.16' Conflicts: NEWS configure.ac commit 89346a8c8f48add8b090b31a9c63f8239f9bf761 Author: Will Thompson Date: 2012-11-09 17:12:48 +0000 post-release nano-version bump to 0.16.4.1 commit b71ded41ee9ddad4a6b6be85759526dc39c55c79 Author: Will Thompson Date: 2012-11-09 16:47:26 +0000 Version 0.16.4 commit b27c3518e2efd5151a24bbc7232155a32effefa7 Author: Will Thompson Date: 2012-11-09 16:47:12 +0000 NEWS for 0.16.4 commit 37102bbdc665c58cd17dbb635fde31027e5d6111 Author: Debarshi Ray Date: 2012-11-09 16:55:42 +0100 caps-channel-manager: Drop reset_caps, which is now unused Fixes: https://bugs.freedesktop.org/56181 commit 64b405cb025a00c3962a415dd90119771517cf6a Author: Will Thompson Date: 2012-11-06 16:46:44 +0000 muc_factory_broadcast_presence(): don't crash if disconnected Fixes: https://bugs.freedesktop.org/show_bug.cgi?id=52362 (I hope) commit 1bd50bc9f5e1cab09b506cea8dd1985b98dfe5e1 Author: Will Thompson Date: 2012-11-06 16:46:44 +0000 muc_factory_broadcast_presence(): don't crash if disconnected Fixes: https://bugs.freedesktop.org/show_bug.cgi?id=52362 (I hope) commit d300a2e432294c77a55cdc45cd5d7b3f653e13ff Author: Will Thompson Date: 2012-11-05 16:06:06 +0000 muc-factory: clarify cancelling queued requests. commit 5f262b5741724c0210f51e07691ba830682f5f25 Author: Will Thompson Date: 2012-11-05 15:53:23 +0000 muc-factory: use find_text_channel function. Reducing the number of places we directly access priv->text_channels will make it easier to do the right thing when it's NULL. commit 42f4043b305a6e07f8ad87deaf96c6fc6e9f2b59 Author: Will Thompson Date: 2012-11-05 15:12:22 +0000 sidecars: fix enqueuing requests made before connecting tp_base_connection_get_status() returns DISCONNECTED in two states: when the connection is yet to connect (internally, it is in state NEW) and when the connection is defunct. tp_base_connection_is_destroyed() allows us to distinguish the two in this case. This was introduced in commit fde8437 and was caught by gateways.py. Fixes: https://bugs.freedesktop.org/show_bug.cgi?id=55908 commit 8d4226c28153f054afc16d6b7e7d738b5359ee42 Author: Heiher Date: 2012-11-05 14:29:37 +0000 Use GLib's base64 implementation rather than Gabble's own (Patch modified to add aggravating signed <-> unsigned casts, and to adjust some tests to cope with GLib appending a trailing newline where Gabble's home-grown line-breaking did not.) Fixes: https://bugs.freedesktop.org/show_bug.cgi?id=54760 Signed-off-by: Will Thompson commit 1e5c99563e671ca7dece95a1fa66d5a20cb9c722 Author: Alban Crequy Date: 2012-02-09 15:12:02 +0000 test: protocol.py: make the test pass when plugins add new statuses https://bugs.freedesktop.org/show_bug.cgi?id=45988 commit 683414706b5f83f193143e4765cd1437698d8fd3 Author: Will Thompson Date: 2012-11-05 13:51:44 +0000 tubes/accept-muc-stream-tube: remove unused imports commit 4002ab7685dc58053133f3998d31dbc2270f8829 Author: Will Thompson Date: 2012-11-05 13:49:58 +0000 accept-muc-*-tube.py: accept the list of interfaces in either order The order swapped in commit fde8437a, and in principle it should never have mattered anyway. (Simon noticed this. I changed the patch in response to Jonny's review.) Bug: https://bugs.freedesktop.org/show_bug.cgi?id=55911 commit e4c126a0ded1eb0c68bdf03d86acd64728c7beb0 Author: Debarshi Ray Date: 2012-10-19 16:17:35 +0200 connection: Don't reset the capabilities in UpdateCapabilities This ensures that a running Gabble process will always create Call1 channels once it has seen such a client in its lifetime. Remove gabble_caps_channel_manager_reset_capabilities and its implementation in GabbleMediaFactory, since it is not used anymore. Bug: https://bugs.freedesktop.org/show_bug.cgi?id=56181 Reviewed-by: Sjoerd Simons commit 3a068ebe96a0586247f4f597877a0dab78b47779 Author: Debarshi Ray Date: 2012-10-19 16:17:35 +0200 connection: Don't reset the capabilities in UpdateCapabilities This ensures that a running Gabble process will always create Call1 channels once it has seen such a client in its lifetime. Remove gabble_caps_channel_manager_reset_capabilities and its implementation in GabbleMediaFactory, since it is not used anymore. Bug: https://bugs.freedesktop.org/show_bug.cgi?id=56181 Reviewed-by: Sjoerd Simons commit 5c161a8c2eea38dd847c7e0c5a30aec529eb68e8 Author: Jonny Lamb Date: 2012-09-17 15:10:56 +0100 update wocky snapshot This is for the WockyConnector::connection-established signal. Signed-off-by: Jonny Lamb commit d2ca56c6a3850e58e66d9dc86eba14aa2a32b53c Merge: 373713d 6a82d4d Author: Jonny Lamb Date: 2012-09-17 15:08:08 +0100 Merge branch 'tcp-nodelay' commit 373713d8c9d5a0a2810e57b5f5033a6b9ac516f2 Author: Xavier Claessens Date: 2012-09-11 16:25:35 +0200 Remove useless tp_handle_unref() commit fde8437a4c41f20dfe8da2439db73a0008ed4fce Author: Xavier Claessens Date: 2012-09-11 15:36:13 +0200 Stop accessing sealed tp-glib structs commit 5fd48d98fe83c974d6e024cf4fcb8735a0016460 Author: Xavier Claessens Date: 2012-09-10 17:48:51 +0200 Disable tp-glib single include commit 351b8e5f6eae3e7d75479e65173049a8dfa4bd04 Author: Xavier Claessens Date: 2012-09-11 12:15:55 +0200 Prevent post 0.20 tp-glib API usage commit 9801e21b3489f20f8f6c88e2e40154e8e7da726a Author: Xavier Claessens Date: 2012-09-10 17:47:10 +0200 Update tools from telepathy-glib I know this adds useless tools, but it's easier to have a raw copy of tp-glib's directory. We should consider using a git submodule tbh. commit c7fd1d5ef73cf7ef64b54b378ae7940bb5ba369c Author: Xavier Claessens Date: 2012-09-11 15:58:04 +0200 Bump nano version commit 86f15f2e3057dea4fd52ad0840c6b77cf379c47e Author: Xavier Claessens Date: 2012-09-11 15:54:55 +0200 Prepare 0.17.1 release commit eb53bcaffc16ae388d3cda3b13e7088d84f80388 Merge: 4c720bc 2a17888 Author: Xavier Claessens Date: 2012-09-11 15:49:33 +0200 Merge branch 'telepathy-gabble-0.16' Conflicts: NEWS configure.ac commit 2a1788842f38aaadb028ddd96f7e82f6db7e70b8 Author: Xavier Claessens Date: 2012-09-11 15:43:05 +0200 Bump nano version commit 406c9d74b100184197780d8bd6c68c6e393eeb76 Author: Xavier Claessens Date: 2012-09-11 15:39:40 +0200 Prepare 0.16.3 release commit 4c720bc262ea35b802606910b63b57f28929bdc6 Merge: 36b4819 5c2184c Author: Simon McVittie Date: 2012-09-11 12:02:09 +0100 Merge branch 'telepathy-gabble-0.16' Conflicts: NEWS configure.ac src/conn-addressing.c src/jingle-info.c src/media-channel-hold.c src/message-util.c src/muc-tube-dbus.c src/muc-tube-stream.c src/olpc-activity.c src/presence-cache.c src/protocol.c src/room-config.c commit 5c2184c21b1e8878394834a3b1ef9096a3c51087 Author: Simon McVittie Date: 2012-09-11 11:26:03 +0100 NEWS for 0.16 commit 8a6e9461f9cc12788532441bcc6f43fa7df4913f Author: Simon McVittie Date: 2012-09-07 15:22:30 +0100 Verify that every caps 'ext' we ever advertise can be disco'd without error Bug: https://bugs.freedesktop.org/show_bug.cgi?id=54634 Signed-off-by: Simon McVittie Reviewed-by: Sjoerd Simons commit da14b4e86d503b30054b10b5e7b00eae7ba1add5 Author: Simon McVittie Date: 2012-09-07 15:19:30 +0100 Now that camera-v1 has a caps URI, don't treat it as part of video-v1 Bug: https://bugs.freedesktop.org/show_bug.cgi?id=54634 Reviewed-by: Sjoerd Simons Signed-off-by: Simon McVittie commit fbfa8c138da1a4f00c74af4bda98e858a203a58b Author: Simon McVittie Date: 2012-09-07 15:18:23 +0100 Add Google camera-v1 as a first-class caps bundle This is partly a point of principle - given any caps bundle that we have ever advertised support for, we should be prepared to define when asked - but mainly a workaround for the iChat bug mentioned in commit af55ea3d. If we return an error, it will keep disco'ing us repeatedly in a loop. This leaves us with the problem of finding out what the bundle contains. In Google's usage it is only its name that is important (ignoring that XEP-0115 explicitly makes bundle names opaque), but replying to disco requests for it requires us to be able to turn it into a set of 0 or more capability URIs. Because of the Google server bug mentioned in commit cd0da0a8, we can't just ask a Google client, because they're all on Google servers, so they can't usefully be disco'd. We assume here that it behaves like the voice-v1 and video-v1 bundles in containing exactly one URI, and that that URI corresponds to the bundle name in the same way. Signed-off-by: Simon McVittie Reviewed-by: Sjoerd Simons Bug: https://bugs.freedesktop.org/show_bug.cgi?id=54634 commit 1866bc54eecf2fa6ce00b37d196cd904f8a0203b Author: Sjoerd Simons Date: 2012-09-11 11:45:16 +0200 Pass RemoteContact key into UpdateLocalMediaDescription Now that telepathy-glib actually correctly checks what is passed to UpdateLocalMediaDescription ensure we always pass the RemoteContact key in the dictionary. commit 61cb4d227d1cbbf85d08f94c28036ba9d831f2fb Author: Sjoerd Simons Date: 2012-09-11 11:06:28 +0200 Don't assume NewDebugMessage will occur before NewChannel commit e3aae2b2143c8b851e5814f6711aff75d4a1d197 Author: Sjoerd Simons Date: 2012-09-11 10:30:36 +0200 Make the installed file generation depend on Makefile Make the rules for generating files part of the installed tests depend on the Makefile such that it gets regenerated after runing configure with a different prefix commit 217f39faabeece4946030ea14b6f26bda40fa276 Author: Sjoerd Simons Date: 2012-09-11 10:14:48 +0200 sed pluginexecdir in run-gabble.sh Commit 352d2da805561ec0164a6e800cd3c3e3bbc629f3 starting using pluginexecdir instead of ${libdir}/telepathy/gabble-0. Unfortunately the generation of run-gabble.sh wasn't adapated commit ccc8f443c94d14d9dd94a5f4fbc84183159e8d33 Author: Sjoerd Simons Date: 2012-09-11 10:10:52 +0200 Set gabble twisted location both for installed and uninstalled tests Set GABBLE_TWISTED_PATH in the environment which points to either the installed (when using run-test.sh) or the srcdir location (when using make check) commit 36b4819e116fbbcca22c7c87d72d31353b6802bb Author: Sjoerd Simons Date: 2012-09-11 11:45:16 +0200 Pass RemoteContact key into UpdateLocalMediaDescription Now that telepathy-glib actually correctly checks what is passed to UpdateLocalMediaDescription ensure we always pass the RemoteContact key in the dictionary. commit ed9e8cf54b708276cfa02a589a77af7ce6ac447c Author: Sjoerd Simons Date: 2012-09-11 11:06:28 +0200 Don't assume NewDebugMessage will occur before NewChannel commit 204fa429bbe263d0f88540957b613572f002259c Author: Sjoerd Simons Date: 2012-09-11 10:30:36 +0200 Make the installed file generation depend on Makefile Make the rules for generating files part of the installed tests depend on the Makefile such that it gets regenerated after runing configure with a different prefix commit a8bab9887ce33c580b74e43d75fe4b64a1edfecb Author: Sjoerd Simons Date: 2012-09-11 10:14:48 +0200 sed pluginexecdir in run-gabble.sh Commit 352d2da805561ec0164a6e800cd3c3e3bbc629f3 starting using pluginexecdir instead of ${libdir}/telepathy/gabble-0. Unfortunately the generation of run-gabble.sh wasn't adapated commit 41cccaa8baf6970810ea406a86c04029c2230849 Author: Sjoerd Simons Date: 2012-09-11 10:10:52 +0200 Set gabble twisted location both for installed and uninstalled tests Set GABBLE_TWISTED_PATH in the environment which points to either the installed (when using run-test.sh) or the srcdir location (when using make check) commit 223a234ad1b30608b122670d8504d33fd6c05d42 Author: Luis Araujo Date: 2012-09-07 17:12:47 +0100 run-test.sh.in: print pass/fail status Some automated testing frameworks, including LAVA, ignore exit status and rely on screen-scraping results from log output. Use the Automake convention here (each match of ^(PASS|FAIL|SKIP): (.*)$ is a test result). [commit message added -smcv] Reviewed-by: Simon McVittie Bug: https://bugs.freedesktop.org/show_bug.cgi?id=52049 commit 6f4aeea22388d325f056882224b87429da1e702c Author: Simon McVittie Date: 2012-09-07 15:26:19 +0100 Turn off deprecation warnings, this is a stable branch Signed-off-by: Simon McVittie commit 6a82d4dab2a3d89bb15d4e93f9dfe10be609d210 Author: Jonny Lamb Date: 2012-08-30 21:27:03 +0100 only set TCP_NODELAY if GABBLE_NODELAY is set Signed-off-by: Jonny Lamb commit 1600f7b3759a095f76754b62de0ad920221bb721 Author: Jonny Lamb Date: 2012-05-04 11:34:56 +0100 gabbletest: set TCP_NODELAY on Twisted's XMPP server socket http://i.imgur.com/mWH7b.jpg Signed-off-by: Jonny Lamb commit 1420fafea0aa209323f6245afd3f3d9f6452fe8d Author: Jonny Lamb Date: 2012-05-04 11:34:22 +0100 debug build: set TCP_NODELAY on sockets that Wocky creates Signed-off-by: Jonny Lamb commit e193a39e1272931c9470884a42f1a97655438e9a Author: Jonny Lamb Date: 2012-08-28 14:19:19 +0100 NEWS: updated Signed-off-by: Jonny Lamb commit d4c7189ee40a9eb8a0831c78b2dbd5a54e221237 Merge: e5aed68 1ed31aa Author: Jonny Lamb Date: 2012-08-28 12:19:56 +0100 Merge branch 'only-one-tube' Conflicts: src/tubes-channel.c src/tubes-channel.h Signed-off-by: Jonny Lamb commit e5aed68fb70d5450f3dd463b13aef7e52900232f Author: Jonny Lamb Date: 2012-08-28 12:19:12 +0100 configure: bump dependency on tp-glib Signed-off-by: Jonny Lamb commit 97c72b2036065b7b3f4862692656b87e9a07ae62 Author: Xavier Claessens Date: 2012-08-14 16:39:35 +0200 Bump nano version commit e60e4f72356ccfe6f8b62c04709430d8debbb737 Author: Xavier Claessens Date: 2012-08-14 16:07:30 +0200 Prepare 0.17.0 commit 1bdfdfcfc0457a33d19df9d8deaeb3bc42d424a6 Author: Xavier Claessens Date: 2012-08-14 15:49:41 +0200 Make sure "make -j3 maintainer-foo" works commit 9f6c4a07d6a3f4776dca92864fe356442b75d19c Author: Xavier Claessens Date: 2012-08-14 15:53:18 +0200 Bump nano version commit 46be2e2be7e11d9270907b886085c962ee361861 Author: Xavier Claessens Date: 2012-08-14 15:41:52 +0200 Prepare 0.16.2 commit 49dc42c7ee6a77858d6e57df919a3008122661c1 Author: Xavier Claessens Date: 2012-08-14 15:49:41 +0200 Make sure "make -j3 maintainer-foo" works commit 1ed31aae5ef79aa7ffc92143ad1b80400c37197e Author: Jonny Lamb Date: 2012-07-24 13:15:39 +0100 muc-factory: clarify code deciding whether to announce text channels Signed-off-by: Jonny Lamb commit 5c8c83a0637f3c44d7e608372454b6aa6410b032 Author: Jonny Lamb Date: 2012-07-20 12:44:15 +0100 muc-channel: remove ::appeared signal, replace with is_respawning check Signed-off-by: Jonny Lamb commit 73b0cbbf9eda1ca4922f562bc99f209e7fac326e Author: Jonny Lamb Date: 2012-07-20 12:17:07 +0100 private-tubes-factory: add further assertions in parsing a tube invite Signed-off-by: Jonny Lamb commit 77dafcbc1a7f1de2c6de8f82fd60c3b7eac2a3d3 Author: Jonny Lamb Date: 2012-07-20 12:03:50 +0100 private-tubes-factory: ensure (not lookup) handles from tube invites Signed-off-by: Jonny Lamb commit 745b5ddcaf5220f2976cabd60a966c52eb25f440 Author: Jonny Lamb Date: 2012-07-19 17:30:25 +0100 tubes: use 64bit uints for tube IDs everywhere Signed-off-by: Jonny Lamb commit 18f0ca014a92fcc40be0f4658a247687739fc1dd Author: Jonny Lamb Date: 2012-07-18 17:40:32 +0100 private-tubes-factory: simplify emit_new_channel code Signed-off-by: Jonny Lamb commit dbae4d1275181d9c792616a53b328d5de02035b5 Author: Jonny Lamb Date: 2012-07-18 17:34:16 +0100 private-tubes-factory: add a comment about who owns new channels Signed-off-by: Jonny Lamb commit 1ed620dc1a64533f8aeaa668f510c930c2aa6f5e Author: Jonny Lamb Date: 2012-07-18 17:27:38 +0100 private-tubes-factory: correct logic in looking up existing tubes Good catch, Simon! Signed-off-by: Jonny Lamb commit 7dca54edf28f95a911d95aa7fc6c6d333e31b915 Author: Jonny Lamb Date: 2012-07-18 17:25:17 +0100 private-tubes-factory: appease the code style checker Signed-off-by: Jonny Lamb commit eeb872711256df80c71f3dac2f8de718f0c0d64d Author: Jonny Lamb Date: 2012-04-05 12:11:59 -0400 muc-channel: disappear from the bus when not requested Signed-off-by: Jonny Lamb commit 3b045801e426844af922b5a34a86f4ca0765a6f3 Author: Jonny Lamb Date: 2012-05-18 09:44:58 +0100 muc-channel: use TP_PROP more than appending the property to an iface name Signed-off-by: Jonny Lamb commit ba5b6373c4b42c572f44f9559aad995a81115d8f Author: Jonny Lamb Date: 2012-03-26 17:57:54 -0400 tubes-channel: remove Hooray! Signed-off-by: Jonny Lamb commit faaec035ccef8d2b3f196d923c412f7fecfef1bc Author: Jonny Lamb Date: 2012-03-26 17:56:59 -0400 muc-channel: remove "tube" property Signed-off-by: Jonny Lamb commit 1b08b38de7166ae229555ff9aecf49278d67a97c Author: Jonny Lamb Date: 2012-03-26 17:53:31 -0400 muc-factory: simplify code to get IM channel from InitialChannels Signed-off-by: Jonny Lamb commit 5b5ffc4516a4fe2ef2ac5b0c131ae1b2610de789 Author: Jonny Lamb Date: 2012-03-26 17:53:20 -0400 muc-factory: remove dead comment Signed-off-by: Jonny Lamb commit 353db90ad65b2f5749384505bd8372ca8264f44c Author: Jonny Lamb Date: 2012-03-26 17:52:33 -0400 muc-channel: enable SI stream requests again This code came from GabbleTubesChannel. Signed-off-by: Jonny Lamb commit f5b269b50df5b859fee0c168deafa97b107d9ad7 Author: Jonny Lamb Date: 2012-03-26 17:35:17 -0400 muc-factory: fix listing of channels Signed-off-by: Jonny Lamb commit 2aea52346a9c1a4adc6e373888298d9a6c50e52a Author: Jonny Lamb Date: 2012-03-26 17:29:57 -0400 tubes test: fix up now that we return text & tube channels together Signed-off-by: Jonny Lamb commit 07dca90ececeee6613a6c0057f3d1b68a6b8ea5c Author: Jonny Lamb Date: 2012-03-26 17:27:23 -0400 muc-channel: remove unused methods Signed-off-by: Jonny Lamb commit 8305b566af9b3b0219a417dd9e854d32a8ec06fb Author: Jonny Lamb Date: 2012-03-26 17:24:37 -0400 muc-channel: send Tube presence where appropriate This was removed GabbleTubesChannel. Signed-off-by: Jonny Lamb commit d6ade9e4a2cfae7054dfc75a39cc6d724bdb7933 Author: Jonny Lamb Date: 2012-03-26 17:11:53 -0400 muc-channel: add way to request single Tube channels Signed-off-by: Jonny Lamb commit 9568e3b1c68648781b39d4968d71128ba07ede96 Author: Jonny Lamb Date: 2012-03-26 16:51:24 -0400 muc-factory: remove Tubes channels from Tube channel requests Signed-off-by: Jonny Lamb commit 87fc93eba8527d5c183c0121190ac75d07f39beb Author: Jonny Lamb Date: 2012-03-23 19:16:25 -0400 muc-channel: store Tube channels here Signed-off-by: Jonny Lamb commit 64cf190a7fc4ed8e8bbd340c9581e3ddfff9d4e6 Author: Jonny Lamb Date: 2012-03-23 18:55:04 -0400 muc-factory: remove Tubes channel handling code Signed-off-by: Jonny Lamb commit 88777ca5ca7847d6f7b27a590d26f283a286f7ad Author: Jonny Lamb Date: 2012-03-23 18:51:37 -0400 muc-factory: stop exposing Tubes channels in RCCs Signed-off-by: Jonny Lamb commit 95ea89f326c9d2b34eda734a77344664e6f56bf0 Author: Jonny Lamb Date: 2012-03-23 18:49:39 -0400 private-tubes-factory: make extract_tube_information public This should probably go in a tubes-util file or something... Signed-off-by: Jonny Lamb commit 6ae35c68a9451257b6cc136ed3dbd857241fdcd6 Author: Jonny Lamb Date: 2012-03-23 18:48:38 -0400 private-tubes-factory: make error messages more specific Signed-off-by: Jonny Lamb commit 2fd80cbc062c60402d3bc93e0b08dcba3b6d6732 Author: Jonny Lamb Date: 2012-07-16 17:48:14 +0100 jingle caps test: add a test for Google caps with a random node Signed-off-by: Jonny Lamb commit 5752b2a7b92f91fb80874272137a89151c630f21 Author: Jonny Lamb Date: 2012-07-16 17:45:55 +0100 presence-cache: ignore caps nodes when we see google 'ext' features Google keeps changing the node for its voice-v1, video-v1, share-v1, etc. features. Let's just ignore it from now on and if there's no hash treat it as if it might be Google. Signed-off-by: Jonny Lamb commit 1158ce4649fe88dc410769665def19e5e702c9ec Author: Jonny Lamb Date: 2012-07-16 14:04:12 +0100 presence-cache: parse caps URIs as a pair of node and fragment Previously we'd always throw the node and fragment together like: node + '#' + fragment Separating them has the advantage of being able to only look at the fragment later (without using string tricks) and ignore the node. Signed-off-by: Jonny Lamb commit f88ae541292e0ff4abd9214bef4c7b99a55dc4e9 Author: Jonny Lamb Date: 2012-07-11 11:53:03 +0100 server-tls-manager: deal with modification of the GList while iterating it Signed-off-by: Jonny Lamb commit 5de7189fa93918cc1dda3bfdf8d5833b63e43020 Author: Jonny Lamb Date: 2012-07-11 11:53:03 +0100 server-tls-manager: deal with modification of the GList while iterating it Signed-off-by: Jonny Lamb commit c04d52520e405649a2878902a0c825558b5a91ac Author: Xavier Claessens Date: 2012-05-28 10:30:55 +0200 Implement WLM jidlookup See description: http://msdn.microsoft.com/en-us/library/live/hh550849.aspx https://bugs.freedesktop.org/show_bug.cgi?id=50341 commit 8947be225e17d1cee96cf41586f495dcd35c6904 Author: Xavier Claessens Date: 2012-06-20 15:24:58 +0200 0.16.1 release commit 9abf25885dd71047746ea496ea3bdaff7f3499e7 Author: Xavier Claessens Date: 2012-06-13 14:41:10 +0200 GabbleServerTLSManager: Support multiple consecutive TLS verifications WockyConnector could now restart connection with another host, and so needs to re-verify TLS. commit 788a8fcfc2946eae5327665b9e7c8a71a9475a9c Author: Xavier Claessens Date: 2012-06-13 14:41:10 +0200 GabbleServerTLSManager: Support multiple consecutive TLS verifications WockyConnector could now restart connection with another host, and so needs to re-verify TLS. commit 287fb2d9fabd2703c17020c4fd9493e8531c0a03 Author: Marcus Lundblad Date: 2012-05-18 13:29:12 +0200 Fix for initiating a video call from an Android tablet. Reviewed-by: Sjoerd Simons Bug: https://bugs.freedesktop.org/show_bug.cgi?id=36998 commit bced2f61312ab407964ac3ef0653183eb3b8c6cc Author: Marcus Lundblad Date: 2012-05-15 22:40:04 +0200 Make Android 4.0 GTalk contacts appear as capable of a/v calls. Reviewed-by: Sjoerd Simons Bug: https://bugs.freedesktop.org/show_bug.cgi?id=36998 commit e1673f4fb2818a8c2906b056a0a62d28cdd54097 Author: Marcus Lundblad Date: 2012-05-18 13:29:12 +0200 Fix for initiating a video call from an Android tablet. Reviewed-by: Sjoerd Simons Bug: https://bugs.freedesktop.org/show_bug.cgi?id=36998 commit dad87309ae85ca4a45e393c1e351b309fc8097a6 Author: Marcus Lundblad Date: 2012-05-15 22:40:04 +0200 Make Android 4.0 GTalk contacts appear as capable of a/v calls. Reviewed-by: Sjoerd Simons Bug: https://bugs.freedesktop.org/show_bug.cgi?id=36998 commit 89751765986de2390c53e83ec41a4ea59aabd62c Author: Simon McVittie Date: 2012-05-30 13:56:20 +0100 Include missing config.h at the beginning of jingle-info.c commit fa3b39e7a84fa985a5f7bac664fa6af8a355e622 Author: Simon McVittie Date: 2012-05-30 13:34:20 +0100 Use meta-headers for everything Reviewed-by: Xavier Claessens Bug: https://bugs.freedesktop.org/show_bug.cgi?id=49384 commit a71f4c62ac847c8d2cf78cb6b7db62786b82dd1b Author: Jonny Lamb Date: 2012-03-20 19:01:46 -0400 private-tubes-factory: remove some duplication Signed-off-by: Jonny Lamb commit f9eca443d693dbde73e92cd7a2eee6980b69114f Author: Jonny Lamb Date: 2012-03-20 15:22:25 -0400 connection: make a better guess at the final number of channel managers Signed-off-by: Jonny Lamb commit d9f90c7b16df1ed85fbabbdaa34ea418e29e38f3 Author: Jonny Lamb Date: 2012-03-20 15:11:13 -0400 private-tubes-factory: remove old Tubes code Signed-off-by: Jonny Lamb commit 7490deaff7d3acf2cbecb947d3dfe984e782eb94 Author: Jonny Lamb Date: 2012-03-20 15:04:05 -0400 private-tubes-factory: move SI tube request handling to here Signed-off-by: Jonny Lamb commit 5d804b3926c93becaea7d017fcdae766d13fe056 Author: Jonny Lamb Date: 2012-03-20 11:22:11 -0400 private-tubes-factory: implement handle_si_stream_request ...instead of popping into TubesChannel. Signed-off-by: Jonny Lamb commit 143bdcde2369f6bd001d3b86eef15ffb20a499d3 Author: Jonny Lamb Date: 2012-03-15 16:49:49 -0400 private-tubes-factory: only create Tube channels for tube offers extract_tube_information has been copied verbatum from tubes-channel.c. Signed-off-by: Jonny Lamb commit 867dea173d0c9ac876199fca0b532ff1126987d6 Author: Jonny Lamb Date: 2012-03-15 16:41:39 -0400 private-tubes-factory: remove Tubes channels from RCCs Signed-off-by: Jonny Lamb commit 5747291212d7f6ccc92ceb4cc198a292985d203a Author: Jonny Lamb Date: 2012-03-15 16:41:11 -0400 private-tubes-factory: create only Tube channels for requests Signed-off-by: Jonny Lamb commit c10809c97c20c1d3a55740f4e93eac26664b88a9 Author: Jonny Lamb Date: 2012-03-15 16:36:42 -0400 tube tests: remove all Chan.T.Tubes tests Signed-off-by: Jonny Lamb commit 4f67d0e00694967c31ab35ab6eec0a325bc19d5d Author: Jonny Lamb Date: 2012-05-17 17:57:50 +0100 configure: ignore post 0.18 deprecations so we can build against tp-glib master Gabble still uses emit_new_channels and tp_handle_{,un}ref, but for now we can ignore it with this. config.h had to be included in the right place for a lot of source files. Signed-off-by: Jonny Lamb commit 7667bb74e99e236c15f34b7f9266073441d3afde Author: Xavier Claessens Date: 2012-05-10 15:11:56 +0200 Fix a leak in: tp_handles_unref() does not unref the array commit b7eaf004dd62f4425cc6027420533b493640d70d Author: Xavier Claessens Date: 2012-05-10 14:59:46 +0200 Stop using tp_handle_ref/unref commit 591ee25d44a016b38067d1f9a8772dca0fbca09c Author: Xavier Claessens Date: 2012-05-10 15:11:56 +0200 Fix a leak in: tp_handles_unref() does not unref the array commit df5e6d51ff66b5e84efdb1a1dfb28e584c2d5f3e Author: Xavier Claessens Date: 2012-05-09 18:44:05 +0200 Bump telepathy-glib required version to 0.19.0 commit 13eebb3cb90c31bcb27e70958e4664174cdce621 Author: Simon McVittie Date: 2012-05-01 15:17:26 +0200 Improve ChatState test commit 956a5f821854494228981c2b7d3095a9a0687427 Author: Xavier Claessens Date: 2012-04-30 23:24:01 +0200 Use TpMessageMixin to implement ChatState commit cbfa9d06a8e4cc93b15e4a8b53819638ab09d67c Author: Simon McVittie Date: 2012-05-07 18:54:08 +0100 Set G_MESSAGES_DEBUG during testing With recent GLib, we don't see debug messages otherwise. Bug: https://bugs.freedesktop.org/show_bug.cgi?id=49596 Reviewed-by: Xavier Claessens commit 3ee4f3fe45cd9b045081532bb4213582938a00f8 Author: Simon McVittie Date: 2012-05-07 17:29:22 +0100 Use non-deprecated TpIntset APIs Bug: https://bugs.freedesktop.org/show_bug.cgi?id=49596 Reviewed-by: Xavier Claessens commit c4b8ceb361748495be477bd3de49ac87c1a5bb2a Author: Simon McVittie Date: 2012-05-07 16:58:51 +0100 Use TP_ERROR instead of deprecated TP_ERRORS Bug: https://bugs.freedesktop.org/show_bug.cgi?id=49596 Reviewed-by: Xavier Claessens commit eda09f9123a74dcc7e1b5ad52816ff70d794dec2 Author: Simon McVittie Date: 2012-05-04 14:47:49 +0100 Stop using deprecated debug-ansi.h Bug: https://bugs.freedesktop.org/show_bug.cgi?id=49596 Reviewed-by: Xavier Claessens commit 0b2ba1be64a70c74408919b8717457042ce23648 Author: Jonny Lamb Date: 2012-04-05 13:28:10 -0400 servicetest: TextTestRunner might not be in unittest.runner Signed-off-by: Jonny Lamb commit f9fd6bebc5f8774395433dfb055b68ddd5de84de Merge: 208a634 b8dd9b0 Author: Jonny Lamb Date: 2012-04-05 12:49:14 -0400 Merge branch 'shutup-servicetest' commit 0015b83d5746ec5fab3e4205c56ec599a5726bfa Author: Guillaume Desmottes Date: 2012-04-04 09:49:48 +0200 use new GLib API checking macros commit 70aaaad2f9391453f7d8c9cb1ebad349d4b96421 Author: Guillaume Desmottes Date: 2012-04-04 09:44:51 +0200 Depends on GLib 2.30 telepathy-glib 0.18.0 already depends on this version so that won't change much in practice and it will allow us to use GLIB_VERSION_* macros (GLIB_VERSION_2_24 doesn't exist). commit 7eac882fe8f48cc3145cff24fecf499b87c38f2d Author: Guillaume Desmottes Date: 2012-04-04 09:48:28 +0200 gibber-unix-transport: stop defining _GNU_SOURCE config.h does it for us now. commit b89caf381ba7a38c1ac98fe4b04a261cf62c1ad1 Author: Guillaume Desmottes Date: 2012-04-04 09:34:41 +0200 add missing config.h includes commit 579eb4f247b7383d4f50f79e7802f9b48ffadf7a Author: Guillaume Desmottes Date: 2012-04-04 09:34:25 +0200 include config.h in the generated gabble-enumtypes.c commit 208a634498e965a6087c6fb59a6d220e91163faf Author: Jonny Lamb Date: 2012-04-02 17:47:49 -0400 start on version 0.17.0 Signed-off-by: Jonny Lamb commit ae9e1ce5a377bcd2d19d169bdede8ca264032754 Author: Jonny Lamb Date: 2012-04-02 17:43:20 -0400 start on version 0.16.1 Signed-off-by: Jonny Lamb commit 4291d1d472bc22ea1423e5ef2dc063eb1782ac15 Author: Jonny Lamb Date: 2012-04-02 17:05:53 -0400 version 0.16.0 Signed-off-by: Jonny Lamb commit b0112d03c77dfe4b506168ccffcc1eb4bb83d29f Author: Jonny Lamb Date: 2012-04-02 17:04:52 -0400 NEWS: summary for 0.16.0 Signed-off-by: Jonny Lamb commit 40322ddb3228a9687bee25ee4c85a5dffeccefe2 Author: Jonny Lamb Date: 2012-04-02 17:13:10 -0400 configure: update dependency on tp-glib Signed-off-by: Jonny Lamb commit 4d9133a523d95207699b3ede02b26e7e2c272fdd Merge: 71dcf75 44582af Author: Will Thompson Date: 2012-04-02 17:50:28 +0100 Merge branch '47999-handle-ibb-errors' commit 44582af04f111e4ba855afceb814997fce812a4e Author: Will Thompson Date: 2012-03-28 14:53:43 +0100 bytestream-ibb: handle IQ send errors This is untested, but the existing tests still pass… https://bugs.freedesktop.org/show_bug.cgi?id=47999 commit b8f84c653318b18c5070085e66fca011f04bcb65 Author: Will Thompson Date: 2012-03-28 14:26:12 +0100 bytestream-ibb: correct send_data's return value. This function returns the number of messages sent, not a boolean. commit 71dcf75998ea8a332903def7b6513df5e4e6d819 Author: Alban Crequy Date: 2011-12-13 16:43:57 +0000 New test: connect/test-connection-params.py https://bugs.freedesktop.org/show_bug.cgi?id=43828 commit 45d81af51417a79bd2fd7c98b0980ba7e725dd5c Author: Alban Crequy Date: 2011-12-13 12:16:07 +0000 ContactList: downloading the roster at connection depends on a connection parameter https://bugs.freedesktop.org/show_bug.cgi?id=43828 commit deb67c35893b1770533eb48c4e7ec0840afc8926 Author: Alban Crequy Date: 2012-02-23 16:29:49 +0000 Update dependency on telepathy-glib https://bugs.freedesktop.org/show_bug.cgi?id=43826 https://bugs.freedesktop.org/show_bug.cgi?id=43828 commit 687ec45023294d6003f987ff0dfc72986fdb7f46 Author: Alban Crequy Date: 2012-01-04 14:22:02 +0000 gabble_roster_unpublish_async: correct the source tag commit 5d7a8f948c504b842781e99f77bebcb94bf610a0 Author: Alban Crequy Date: 2012-01-04 14:21:42 +0000 gabble_roster_authorize_publication_async: correct the source tag commit 352d2da805561ec0164a6e800cd3c3e3bbc629f3 Author: Simon McVittie Date: 2012-03-09 13:38:04 +0000 Replace plugindir with an AC_ARG_VAR so it can be passed to configure This lets you configure the plugin directory: ./configure pluginexecdir='${libdir}/my-gabble-plugins' The directory-name variable has 'exec' in it because Automake installs unknown directory names with 'exec' in their variable name during "make install-exec", and other unknown directory names during "make install-data". Bug: https://bugs.freedesktop.org/show_bug.cgi?id=46417 Reviewed-by: Jonny Lamb commit f06469da5cfc7c8dc750bce82099dde4b528a20b Author: Simon McVittie Date: 2012-03-09 13:35:37 +0000 Install non-ABI-stable libraries used by plugins to a private directory This avoids having Gabble and Salut, or old Gabble and a future stable Wocky, fight over the libwocky.so symlink. If you're building for a tightly controlled platform where Gabble and Salut are definitely using the same Wocky version, you can put them in the normal libdir with ./configure pluginexeclibdir='${libdir}' or (when Salut has been updated with this change) make them share a private library directory: ./configure pluginexeclibdir='${libdir}/telepathy/ytstenut-1.0' Bug: https://bugs.freedesktop.org/show_bug.cgi?id=46417 Reviewed-by: Jonny Lamb commit c535a29358002b3b48c3df7bedecfbf73ab4553f Author: Simon McVittie Date: 2012-03-09 12:42:10 +0000 Avoid non-portable use of += in configure.ac Bug: https://bugs.freedesktop.org/show_bug.cgi?id=46417 Reviewed-by: Jonny Lamb commit b942885bb6bca2e56b1a24737fcb8cf266d9cb61 Author: Simon McVittie Date: 2012-03-09 13:04:13 +0000 Avoid Wocky trying to install into --prefix=NONE Bug: https://bugs.freedesktop.org/show_bug.cgi?id=46417 Reviewed-by: Jonny Lamb commit 531bffcfaf8bed572259981d4d900e7f1e42e473 Author: Simon McVittie Date: 2012-03-09 12:41:46 +0000 configure.ac: remove apostrophe from a help message so syntax highlighting works Bug: https://bugs.freedesktop.org/show_bug.cgi?id=46417 Reviewed-by: Jonny Lamb commit fed5695cc026ab4076197de2a28a5f7bbe69dacb Author: Simon McVittie Date: 2012-03-09 12:24:44 +0000 telepathy-gabble.pc: link to Wocky Bug: https://bugs.freedesktop.org/show_bug.cgi?id=46417 Reviewed-by: Jonny Lamb commit c8da384dd9f311296145cdb49a2279f624502708 Author: Jonny Lamb Date: 2012-03-22 19:20:56 -0400 start on 0.15.6 Signed-off-by: Jonny Lamb commit b26a704c87968bc1db29a2917c2ca0bdf10ed999 Author: Jonny Lamb Date: 2012-03-22 19:09:06 -0400 version 0.15.5 Signed-off-by: Jonny Lamb commit b7350b195bae44bda93e767b7a8a5616c558102a Author: Jonny Lamb Date: 2012-03-22 18:52:51 -0400 configure: depend on telepathy-glib 0.17.7 Signed-off-by: Jonny Lamb commit b82352ce6eb3bab55e09f58ce07a3fd1d90ce319 Merge: 21ec409 27fa0aa Author: Marco Barisione Date: 2012-03-22 17:52:20 +0000 Merge branch 'disable-voip' Fixes: https://bugs.freedesktop.org/show_bug.cgi?id=47502 Reviewed-by: Jonny Lamb commit 27fa0aa72eb61ecffb26998f535f66e4ae992ae6 Author: Marco Barisione Date: 2012-03-09 08:33:54 +0000 Allow to disable VoIP-related code with --disable-voip commit a69250eac91ac827ff90cf8a03d6540b68a5d001 Author: Marco Barisione Date: 2012-03-16 16:14:15 +0000 There is no need to include non-compiled files in EXTRA_DIST Automake makes sure that all the source files, even if they are not being compiled at the moment, will be distributed when doing make dist. commit 21ec409017f58d1ff863cc8c26d42108931ecabb Author: Olivier Crête Date: 2012-03-16 14:18:25 -0400 CallStream: Replace want_send with the local sending state of the Stream Also require the newest telepathy-glib commit 78db4e43dcbc8e8fabe87e05a9231b3b6555116a Author: Olivier Crête Date: 2012-03-16 14:17:51 -0400 CallStream: Set local sending to true if a locally-created content wants to send commit b9926cf5413dde094ae134a84b349e26ffeabfa4 Author: Olivier Crête Date: 2012-03-13 19:01:35 -0400 Also check the sending state of non-initial streams commit a1414b8e991a4a51bb9cc09af29f4e3b56e23289 Author: Olivier Crête Date: 2012-03-13 18:44:30 -0400 tests: Expect new contents to be pending_start receiving commit 12e43de2942da80fb31e59ba334d5813af895fd6 Author: Olivier Crête Date: 2012-03-09 17:12:24 -0500 Fix unit tests to use the Telepathy candidate types commit 72e98283ca6a53b31f45a3fb410324bfba7ec1e1 Author: Olivier Crête Date: 2012-03-09 16:52:32 -0500 Convert Telepathy types to Jingle types correctly commit ceb7985d76f8d2c614344b4b6c68d152384b695b Author: Olivier Crête Date: 2012-03-09 16:45:47 -0500 Correctly convert JingleCandidateType to Telepathy types commit 8bbc8e408464f65f5cc93ff18109661f297b39d8 Author: Jonny Lamb Date: 2012-03-09 10:29:40 -0500 tube-iface: remove duplicated properties These wouldn't *have* to disappear, but GObject was complaining because GabbleTubeIface:connection was implemented in TpBaseChannel as type TpBaseConnection, when it was expecting a GabbleConnection. Stop crying, you baby. Signed-off-by: Jonny Lamb commit 0d57767b5f669d51049257bac918a98de798b9e0 Author: Simon McVittie Date: 2012-02-23 12:19:42 +0000 Link plugins in the same way on Unix as on Windows Reviewed-by: Olli Salli Bug: https://bugs.freedesktop.org/show_bug.cgi?id=46417 commit a167423acad1bbc41d764710fbc1de4347906da6 Author: Simon McVittie Date: 2012-02-23 12:01:26 +0000 Build Wocky as a version-specific shared library Reviewed-by: Olli Salli Bug: https://bugs.freedesktop.org/show_bug.cgi?id=46417 commit 9a52bb20676a5290b3778cecb9d5bbc74a928db7 Author: Simon McVittie Date: 2012-02-23 12:29:42 +0000 Use the standard EXEEXT variable rather than reinventing it commit b8dd9b07c8a90a84bccc0cb348c5801a29d6c68b Author: Jonny Lamb Date: 2012-03-07 15:28:14 -0500 servicetest: silence unittest The output when running all the tests is made really ugly by this appearing out of nowhere. Only print the unittest stuff if CHECK_TWISTED_VERBOSE is set. Signed-off-by: Jonny Lamb commit 0a7c66f48e2a4f5f1b80237def4063fa952ccc71 Merge: 52fac35 c8f08ce Author: Will Thompson Date: 2012-03-01 17:07:08 +0000 Merge branch 'detp-jingle' https://bugs.freedesktop.org/show_bug.cgi?id=46513 Conflicts: src/ft-channel.c commit 52fac3507be8db06a9e1ae1b7a3c98a28d02da44 Merge: 10785ff 491db17 Author: Jonny Lamb Date: 2012-02-29 15:02:55 -0500 Merge branch 'base-channels' Conflicts: src/Makefile.am src/tube-dbus.c Signed-off-by: Jonny Lamb commit c8f08ceb6e7f44303d1950a565e47f170129e524 Author: Will Thompson Date: 2012-02-29 13:22:22 +0000 CallStream style: add a missing blank line. commit de5eb4bebefa0d68d7ec081ed5845479c5aeae9e Author: Will Thompson Date: 2012-02-29 13:20:50 +0000 JingleMint: rename ::new-session to ::incoming-session This is Simon's idea, and it's a good one. It makes it clearly a different signal to JingleFactory::new-session, too. commit f6d4a38fc058300dc2422f61f469ed6a86697ed8 Author: Will Thompson Date: 2012-02-29 13:14:05 +0000 JingleInfo: chain up at the end of dispose Simon noticed: > Surely dispose should chain up at the end, not the beginning? I see > dispose and finalize as progressively breaking the functionality of > the object's classes, most-derived first. Yup! commit cdc02ea04db52b24d84be250893b3bdc750baafe Author: Will Thompson Date: 2012-02-29 13:12:47 +0000 JingleSession: don't leak in initiate/accept callbacks Simon noticed: > "Send session-{initiate,accept} IQs using porter directly.", > 72ea73343252: I think you're leaking sess in the callbacks, except in > the early-return cases where the state has become inappropriate? He's quite right. commit 10785ffc0e61d98d6bc23b5281872d7268b3a3a1 Author: Xavier Claessens Date: 2012-02-29 11:26:31 +0100 Include config.h to have GLIB_DISABLE_DEPRECATION_WARNINGS defined commit a6ef0c0b9d2747baaaa049a6c441da7b94d65265 Author: Will Thompson Date: 2012-02-23 12:59:29 +0000 JingleSession: make _new() take a dialect. Everywhere that creates a new session knows the dialect it wants to use, so let's pass it right into the constructor. commit da9f352766edf270c907421ea610549593c46325 Author: Will Thompson Date: 2012-02-20 15:24:12 +0000 Represent Jingle relays by a struct, not an aa{sv} This has the dual benefits of removing Telepathy data structures from google-relay.c, and of simplifying the use of these structures in gtalk-file-collection.c. commit 089a5f2de461d90f9089a68dc11ada1cfc6f58b1 Author: Will Thompson Date: 2012-02-20 12:35:02 +0000 Jingle*: stop including util.h commit ae1d315c9bae65a5dd9c1eacbdcda1aa2ec6408f Author: Will Thompson Date: 2012-02-20 12:32:12 +0000 JingleFactory: drop use of gabble_signal_connect_weak() Wocky doesn't have an equivalent of this function. It's relatively little work to explicitly disconnect from the sessions' ::query-cap signals, so let's do that. commit ca86afb5cde89cc1832ed002a59cef6f51916ef4 Author: Will Thompson Date: 2012-02-17 19:19:44 +0000 Remove unused JingleInitiator type. It would actually be better if a two-element version of this without the INVALID element had been used throughout rather than booleans to represent the direction of a session… but oh well. commit d88193074f1f1d9f4d618932821dbab36a474182 Author: Will Thompson Date: 2012-02-17 19:07:19 +0000 Move Jingle enums and typedefs to a new jingle-types.h For some reason the enums lived in jingle-factory.h before. We do need a separate header for the typedefs because some of the Jingle headers are mutually dependent. commit ac9ee4a66f56f8c033f150eb5c8a4a9999e475e3 Author: Will Thompson Date: 2012-02-17 19:10:27 +0000 JingleFactory: correct TpChannelGroupChangeReason reference commit 003e72bc1d8cf70aa595f036ebcfb7e2a542efbb Author: Will Thompson Date: 2012-02-17 18:55:27 +0000 JingleFactory: drop GabbleConnection dependency. So with this patch, GabbleJingleFactory takes a construct-time WockySession property rather than a GabbleConnection; GabbleJingleMint only creats the factory when the porter (and hence session) becomes available. With this, we've removed the core Jingle code's last significant Gabble/Telepathy dependency. commit 745b27e041cb9db5b6a1eb920fdaf480a65d4762 Author: Will Thompson Date: 2012-02-17 18:35:05 +0000 JingleFactory: store WockySession as well as Porter This makes ::porter-available the only place in jingle-factory.c which uses the connection non-trivially, laying the ground for removing it… commit 081f2e7776614e6e8147909c0e5ab9593a85f2dd Author: Will Thompson Date: 2012-02-17 18:28:08 +0000 JingleFactory: propagate caps queries out to Mint commit 4a27eccf389050be52eb4701263e832634fe63ca Author: Will Thompson Date: 2012-02-17 18:22:01 +0000 JingleFactory: move pre-initiate presence pushes to Mint commit 035f99f9c5ea8dbff493555921b6056756106215 Author: Will Thompson Date: 2012-02-17 18:18:44 +0000 JingleFactory: emit ::new-session for outgoing sessions too commit 46c47589c95f923453b9f4e3fe4aa6d648e8447b Author: Will Thompson Date: 2012-02-17 18:11:12 +0000 JingleFactory: explain why create_session() can't emit new-session commit ad631083a88dfdea01b2a9957eeff9594a97ed7b Author: Will Thompson Date: 2012-02-17 17:55:21 +0000 JingleFactory: move connection status monitoring to Mint commit b6ff8bff994babae4afb0e00b8d388b2c37cff8b Author: Will Thompson Date: 2012-02-17 17:49:12 +0000 JingleFactory: call stop() from dispose() This is currently redundant, but will prevent crashing in a hypothetical situation where the factory gets disposed without anyone calling stop() on it first. commit 3676e46472aad8a8205cf8618a15b45d07690e2e Author: Will Thompson Date: 2012-02-17 17:47:48 +0000 JingleFactory: refactor handler-unregistration to a method This will be used by the mint to stop the factory accepting new incoming sessions when the connection starts disconnecting. commit 618234580dcb23f076d0cc75b69f2cbcc92b96a6 Author: Will Thompson Date: 2012-02-17 17:42:19 +0000 Wrap JingleFactory in a new class, JingleMint. The intention is to move all Gabble-specific code into JingleMint, leaving only the pure angle-bracket distillate behind in JingleFactory, which can then be moved to Wocky. But as of this commit, JingleMint is basically an empty shell: it immediately creates a JingleFactory, and proxies its ::new-session signal. As described in the comments, “mint” is meant in the “factory which makes coins” sense. It seemed like a better name than JingleFactoryFactory. commit 8b958fc8c845755832b44e00ffb30e235c82994e Author: Will Thompson Date: 2012-02-17 17:32:14 +0000 JingleFactory: add _new() method commit 77d5d99e9b608d080cc46c523880eccdc1d5360c Author: Will Thompson Date: 2012-02-17 13:59:35 +0000 JingleFactory: correct ::new-session's signature GabbleJingleSessions are not mere pointers: they are objects! commit f40f6c4db3079aa7b4befe70c8a709d49144cc6f Author: Will Thompson Date: 2012-02-17 12:07:07 +0000 Split Telepathy<->Jingle enum mapping out of Jingle code commit 51ec259c494d42c30adfb982646eda2805b2ec3d Author: Will Thompson Date: 2012-02-17 11:56:35 +0000 JingleMediaRtp: drop use of TP_ERRORS Depending on whether an illegal codec update comes from the network or from the local system, we end up wanting either a WOCKY_XMPP_ERROR or a TP_ERROR (to throw back over the wire or in a D-Bus method error, respectively). Previously, this choice was made inside JingleMediaRtp; this patch moves the conversion out to GabbleMediaStream. (The Call codepaths all pass NULL for the GError **... great.) commit 729d3f4f06eba69e499805b004383aba521c7a35 Author: Will Thompson Date: 2012-02-17 10:41:17 +0000 Jingle*: spell out tp_clear_{boxed,pointer} Wocky doesn't have things like this. GLib, however, does have g_clear_object() so we can use that in place of tp_clear_object. commit feb1266ce56e12359a9f2d6ff2325acd0c3a8b56 Author: Will Thompson Date: 2012-02-17 10:28:27 +0000 JingleSession: spell out tp_str_empty() Wocky doesn't have an equivalent function. commit b1be04b448e5860f7cadc27ac7a20a81367f4b3a Author: Will Thompson Date: 2012-02-17 10:26:53 +0000 Jingle*: use wocky_strdiff rather than tp_strdiff commit 10f9eccfb8e3e1e8f88315b41e6a60a26185db26 Author: Will Thompson Date: 2012-02-16 20:00:23 +0000 JingleSession: remove GabbleConnection property. commit dada0afc57d852209a78494c3af12e3704790f1c Author: Will Thompson Date: 2012-02-16 19:48:01 +0000 JingleSession: move pre-initiate presence pushes to Factory This change has the same rationale as the previous. commit 78629e08e943bcca2a0d28648c853f5ac50e93b5 Author: Will Thompson Date: 2012-02-16 18:02:50 +0000 JingleSession: delegate capability queries to factory The two remaining bits of code that use GabbleConnection inside GabbleJingleSession use the caps cache and the roster. There is really no good code for these inside Wocky just now (well, WockyRoster exists, but it's not what Gabble uses so the point is moot). I've resigned myself to the fact that GabbleJingleFactory will have to stick around in some form—not least to listen for ::porter-available, and also to dig stun server parameters out of GabbleConnection. So I think the best path to breaking GabbleJingleSession's Gabble addiction is to add some signals which get handled by the Gabble-specific remnants of the JingleFactory. Meanwhile, I noticed that the caps-checking code didn't actually work for the bare-JID case, so fixed that up while moving the code. commit 28f25af9b2f95103bacb3ab3a005b6cb102950fc Author: Will Thompson Date: 2012-02-16 17:36:48 +0000 JingleSession: get own full JID from porter. commit e1a08344fcfaac717bd533c7ea990f82b3c3732d Author: Will Thompson Date: 2012-02-16 17:34:38 +0000 JingleSession: add a porter property. commit fd17643d8495e4f3fe87fe3660f7008ee1953eb7 Author: Will Thompson Date: 2012-02-16 17:26:31 +0000 JingleFactory: pass self to JingleSession This avoids the JingleSession having to go via its GabbleConnection to get back to the JingleFactory which created it. commit 39b90872eb4939ba4b4262ababc7ae54743fa465 Author: Will Thompson Date: 2012-02-16 16:04:47 +0000 JingleContent: don't store a GabbleConnection. commit 41c4e316f12c7bbd285702ca74e3e4c2d7af6ee2 Author: Will Thompson Date: 2012-02-16 16:27:16 +0000 CallStream: don't grab Connection from JingleContent. commit 2863cdcee0c4d464a0f15f587e20f3a8f67c5014 Author: Will Thompson Date: 2012-02-16 16:03:06 +0000 MediaStream: don't grab Connection from JingleContent. Previously, GabbleMediaStream was getting to the JingleFactory for its content via the connection object stored by the content. Instead, go via the JingleSession stored by the content (so that we can stop storing the connection in the content). commit 679cfd663064781d1ca60d5ed8dae8cab85cb594 Author: Will Thompson Date: 2012-02-16 16:21:06 +0000 MediaStream: add a TpDBusDaemon property. Previously it was retrieved from the JingleContent, via the GabbleConnection it stores. But the Jingle code shouldn't know anything about Telepathy. commit 5410295679bb8ff4a0aad3eb38cc5717124fb5c5 Author: Will Thompson Date: 2012-02-16 15:41:11 +0000 JingleFactory: remove prototype for long-dead function. I removed _jingle_factory_unregister_session() way back in 2009 with commit fd410e9. commit a101f15d96f43c4a5c3da10d6acf485ad22bafa9 Author: Will Thompson Date: 2012-02-16 13:11:57 +0000 JingleSession: remove unused handle-repo-dynamic import. commit 570ad5f69f99f4eace580998d0e5df76f100e74f Author: Will Thompson Date: 2012-02-23 12:36:36 +0000 JingleInfo: don't crash when disconnecting before reply. For some reason I can't quite discern, this didn't crash before the previous patch but does afterwards. Oh well. Here's a fix. commit 51767ee2ec4423c00199fe4e0411f0b83d61aa2f Author: Will Thompson Date: 2012-02-16 11:21:40 +0000 Split google:jingleinfo code out from JingleFactory I am a terrible person and included a couple of changes in this which are not just moving code around: • When parsing STUN server info out of a jingle:info stanza, we no longer use GABBLE_PARAMS_DEFAULT_STUN_PORT as the default if the element does not have a udp='' attribute. (Which seems uncontroversial: if the server erroneously leaves out that attribute, the chances of it happening to listen on GABBLE_PARAMS_DEFAULT_STUN_PORT is pretty low.) • Rather than using conn_util_send_iq_async(), we use wocky_porter_send_iq_async() plus wocky_stanza_extract_errors() to send the jingleinfo request. Wocky doesn't have something like conn_util_send_iq_async(), and this branch is really about removing Gabble dependencies from the Jingle code. commit f2b8df3800135b115bd801921a626bc7edde90e0 Author: Will Thompson Date: 2012-02-15 15:56:26 +0000 JingleFactory: store WockyPorter commit 90b7b2911a76ed9335d161d88fd6a7dd7a2ba41e Author: Will Thompson Date: 2012-02-15 15:42:21 +0000 gabble_jingle_session_send: drop callback arguments. Since nothing uses the weak_object argument any more, we can drop it, and hence stop using _gabble_connection_send_with_reply() in favour of wocky_porter_send_iq_async() directly. One less bit of Gabble inside the Jingle code! commit 72ea73343252d8ac6b792f9f4035cd511290a33d Author: Will Thompson Date: 2012-02-15 15:41:40 +0000 Send session-{initiate,accept} IQs using porter directly. This is the second and final place where the callback arguments to gabble_jingle_session_send() were used. commit a2e03f323dcb96d3043baef52a04eb41f22c6440 Author: Will Thompson Date: 2012-02-15 15:40:22 +0000 Content: use porter directly to send content-remove This is one of only two places which passes a callback to gabble_jingle_session_send(), so let's just spell it out. commit d039d1900888a828e2fc686c331cdfcaca8931ef Author: Will Thompson Date: 2012-02-15 15:12:04 +0000 Session: tolerate being outlived by contents Never disconnecting these signals happens to be safe because GabbleJingleSession always holds the last reference to GabbleJingleContent, but it feels fragile. commit 91e8bc6b3a74cfdf2aaf685fc8883f3344c4e228 Author: Will Thompson Date: 2012-02-15 12:56:23 +0000 Jingle transports: stop using GabbleConnection commit 534bfa15133ead76a46a22f9f8e788c159fb2541 Author: Will Thompson Date: 2012-02-15 12:48:08 +0000 JingleSession: add and use factory accessor. commit 8080a905390ad9ca51059db15277930af6cc6bae Author: Will Thompson Date: 2012-02-15 12:06:15 +0000 JingleSession: localize use of peer handle. Now that we only use a handle in one place, let's move everything to do with it there. commit 1dbbf70e672bcccef88c19b66ebb47bf4897a7fa Author: Will Thompson Date: 2012-02-15 12:04:30 +0000 JingleSession: remove "peer" GObject property. commit bd654d2d812a1d5edae9796a20fca0b81dbfbaf7 Author: Will Thompson Date: 2012-02-15 12:02:32 +0000 JingleSession: remove get_peer_handle. The session unfortunately still uses the handle internally… commit a15ae7cf17528dd7a0d24386025cfe450c93cda2 Author: Will Thompson Date: 2012-02-15 11:59:50 +0000 media-factory: stop using gabble_jingle_session_get_peer_handle commit 7f40e47b980f917fa01d8d1f1012e4fb5a6f1559 Author: Will Thompson Date: 2012-02-15 11:54:10 +0000 ft-manager: stop using gabble_jingle_session_get_peer_handle commit b8ac1245d91828644f8ef37a9d107a12ae087275 Author: Will Thompson Date: 2012-02-14 18:00:52 +0000 Call: stop using gabble_jingle_session_get_peer_handle commit 363b2c580cb5500979020221b74e4889c59f93c2 Author: Will Thompson Date: 2012-02-07 11:55:42 +0000 MediaChannel: store peer's handle. This removes its dependency on GabbleJingleSession knowing about handles, and is less code. commit d884caf42e3e4608b5db84a6d1411c67aa9db925 Author: Will Thompson Date: 2012-02-03 17:38:09 +0100 MediaChannel: use gabble_jingle_session_get_peer_handle() Previously for some reason this was using g_object_get (..., "peer"). Calling a function instead makes it easier to see what needs changing. commit 819be150a66f99a1be524f31e0c8985aa613553d Author: Will Thompson Date: 2012-02-03 16:29:58 +0000 JingleSession: look up presence by WockyContact. commit ca9f3991776c9c277178f88c51941b6cad09800a Author: Will Thompson Date: 2012-02-03 16:27:31 +0000 PresenceCache: add WockyContact-based lookup. commit c5328de7a89e2882036b64ba19a2c5bc3e8e70ce Author: Will Thompson Date: 2012-02-07 19:26:01 +0000 util: add helper to fetch TpHandle for WockyContact commit 97d9c5ae3afc425eb8db91cbe83c37289e1baa5d Author: Will Thompson Date: 2012-02-16 15:21:34 +0000 JingleMediaRTP: use jingle_session_peer_has_cap() The body of content_has_cap() is basically identical to the body of gabble_jingle_session_peer_has_cap(), so let's delete the former. commit 63ade9c2226b72739b53039e26a31b483162aba5 Author: Will Thompson Date: 2012-02-16 15:15:56 +0000 JingleSession: rename _peer_has_quirk to _peer_has_cap There's nothing quirk-specific about the function. commit 8bd15af6cae7e90c97b81690a44bd2e43e1ab5e8 Author: Will Thompson Date: 2012-02-03 16:25:24 +0000 JingleFactory: stop dabbling in handles. commit bc53ec13c956191ab00cdf29236effac6d98651e Author: Will Thompson Date: 2012-02-03 16:18:11 +0000 JingleSession: remove defunct handle refcounting commit 2ad876aee650c4977f214a6d14b6852e164a6a82 Author: Will Thompson Date: 2012-02-03 16:15:34 +0000 JingleSession: grab resource from WockyResourceContact commit 439380da7a6a49f5e5255dcf691c24f3aec669ad Author: Will Thompson Date: 2012-02-03 16:11:19 +0000 JingleSession: hold a WockyContact. commit b516378e41944381ccea6f29821ce7ac41a4cada Author: Will Thompson Date: 2012-02-03 15:40:02 +0000 JingleFactory: exclude handle from session map keys The handle doesn't provide any additional information over the JID; it is a relic of when the format was "\n\n". commit 512c7492c2cb925e0260043a89170cda0907566c Author: Will Thompson Date: 2012-02-01 16:55:46 +0000 JingleSession: move TpHandle peer to private structure commit 1fc97cba57211fa8075c08ed63d882950b0d9451 Author: Guillaume Desmottes Date: 2012-02-13 13:25:25 +0100 groups.py: test RenameGroup() This has been started as a regression test for fdo#45982 which turned to be a tp-glib bug. So we shouldn't merge this commit to master until we depend on a fixed tp-glib. commit f6eda33b8127fd18a81495c221fed4ac57eba37e Author: Guillaume Desmottes Date: 2012-02-13 11:51:11 +0100 groups.py: factor out send_roster_push() https://bugs.freedesktop.org/show_bug.cgi?id=45982 commit 40283718ad184b014f4941a3126e5dfa0ed0640c Author: Guillaume Desmottes Date: 2012-02-13 11:43:17 +0100 groups.py: factor out parse_roster_change_request() https://bugs.freedesktop.org/show_bug.cgi?id=45982 commit dcc73750bceb0e7b1bd865fde5744583cb75300c Author: Simon McVittie Date: 2012-02-21 21:59:58 +0000 Nano version commit 04c7829858da40f943363cfc3e61aa76f68c59ac Author: Simon McVittie Date: 2012-02-21 21:02:10 +0000 Distribute jingle/call_helper.py too commit c59a2f0b3612ba830154156279d0c6e03daf1e5c Author: Simon McVittie Date: 2012-02-21 20:31:25 +0000 Distribute headers more normally, and stop trying to distribute gabble/debug.h There is no gabble/debug.h. The only effect of putting a .h file in SOURCES is to get it included in the distribution tarball, so this wasn't visible until we tried to make a release, and apparently nobody runs distcheck any more or something. commit 920b112a30b0c0f86d150d5fa6aa07779f22c50a Author: Simon McVittie Date: 2012-02-21 19:44:54 +0000 Update telepathy.am from telepathy-glib 0.17.5 to fix out-of-tree releases commit 5cb548e7650846ae51142e8d9464de693775ffe0 Author: Simon McVittie Date: 2012-02-21 19:41:30 +0000 Prepare 0.15.4 commit 8d7efad30c8633d6df4a0635eb7f78f2fa326e9a Author: Guillaume Desmottes Date: 2012-02-21 16:56:09 +0100 accept-*-stream-tube.py: don't test with Credentials access control It fails on recent Linux because of Twisted; see fdo #45443. commit ce467279e0df62ef43ccf30a2724323f2290aac9 Author: Simon McVittie Date: 2012-02-21 13:30:40 +0000 Update Wocky for #46379 commit b2bf243653dfd394648913cca94f379fc09bb71a Author: Guillaume Desmottes Date: 2012-01-18 15:22:51 +0100 presence-cache: hardcode gtalk's bot caps Thanks Google for not implementing disco query on the bot... https://bugs.freedesktop.org/show_bug.cgi?id=44855 commit 205df6480b16d66cf74144b37b197251678a565d Merge: 3d6ba70 b40042c Author: Guillaume Desmottes Date: 2012-02-21 10:18:53 +0100 Merge branch 'call1' into master+call1 I added the dependency on tp-glib 0.17.5 as it's required for Call1. Conflicts: src/Makefile.am commit 3d6ba70935c402e1e2b39716ca6014c22aa1520e Merge: 571037b 299ab6d Author: Olli Salli Date: 2012-02-20 15:49:04 +0200 Merge remote-tracking branch 'siraj/plugin-api-update' Reviewed-by: Olli Salli (oggis) commit b40042c598476bc14f578b1d121c14eb9da59c3e Merge: 2aff6d0 7a0e8d7 Author: Guillaume Desmottes Date: 2012-02-20 10:51:16 +0100 Merge remote-tracking branch 'tester/call1-addcontent-direction' into call1 Conflicts: tests/twisted/jingle/call-dtmf.py commit 299ab6dbdd3fb0e290b57201c92bdf1b46b58aa3 Author: Siraj Razick Date: 2012-02-17 16:16:00 -0500 test-plugin: Remove the duplicate TpBaseConnection Gabble and Salut Plugin API was updated to remove the dupliate TpBaseconnection from create_channel_managers. This patch updates the plugin-base to match the API change commit e651270a8a58da63e3f6fe739a6491942604cea5 Author: Siraj Razick Date: 2012-02-17 15:35:49 -0500 plugins: Remove the dupliate TpBaseConnection from create_channel_managers create_channel_managers already passes a GabblePluginConnection which can be casted into TpBaseConnection. So removing the duplicate TpBaseConnection pointer from the API commit 2aff6d0ac4102835c5e125850eef6062011d910d Author: Olivier Crête Date: 2012-02-16 16:55:05 -0500 tests: the SendingTones signal now only happens when actually sending tones commit 9a90f92a6a4f50b33800ca2885efeb7785ad94ca Author: Olivier Crête Date: 2012-02-16 16:54:36 -0500 tests: Correct the order of the DTMFChangeRequested signal according to the spec commit 7a0e8d7f6edf24c96857a72af39940b555a1cf8c Author: Olivier Crête Date: 2012-02-16 16:19:28 -0500 Add a direction parameter to AddContent commit 491db173ad4da5ce4f5ff8d90d1a7ea20aed7785 Author: Jonny Lamb Date: 2012-02-14 18:54:37 -0500 tube-stream: subclass TpBaseChannel See also: exactly the same commit to GabbleTubeDBus. Signed-off-by: Jonny Lamb commit efbde4c8755e1bf55752ddb170d80a122f482afa Author: Jonny Lamb Date: 2012-02-03 19:44:09 -0500 tube-dbus: subclass TpBaseChannel This one is complicated because the old GabbleTubeDBus handled both TargetHandleType as Contact *and* Room. TpBaseChannel sets the TargetHandleType per class so makes this hard. The easiest way to do this is to have a really dummy subclass for MUCs to change the TargetHandleType (and interfaces, to add Group). It would be nice in future to move the actual MUC stuff into GabbleMucTubeDbus, but that's for another day. Signed-off-by: Jonny Lamb commit 571037bf029f2cd46f85a50d7d2b0892ae50be10 Author: Guillaume Desmottes Date: 2012-02-09 18:39:19 +0100 Disable GLib deprecation warnings commit 8b537d968d0fb0047d9143ab05ce5a5bc2c4a1d7 Author: Alvaro Soliverez Date: 2012-02-09 08:13:53 -0300 Fixed android build after plugin API refactor Reviewed-by: Simon McVittie Bug: https://bugs.freedesktop.org/show_bug.cgi?id=45824 commit e3ddd734e9c9c711ad44837b6d1f548718b0fbbf Merge: 359a1ae 07c6ad3 Author: Olivier Crête Date: 2012-02-08 14:16:47 +0100 Merge remote-tracking branch 'nicolas/call1' into call1 commit 07c6ad32bbfc377c7fe09cb6e5bc8c0ce10ef464 Author: Nicolas Dufresne Date: 2012-02-08 14:00:47 +0100 Remove unwanted debug print in call-content-adding-removal commit ee5304dc3ed64d11670d53b9068a69ff1df598c2 Author: Nicolas Dufresne Date: 2012-02-08 14:00:27 +0100 Add missing call test into makefile list commit 2e543e74a65f9cb2da584f35d2348b6b57fabd78 Author: Nicolas Dufresne Date: 2012-02-08 11:25:20 +0100 Implement a call version of content-adding-removal commit daa6d7252861dbb9c7951330d413975573d45b89 Author: Nicolas Dufresne Date: 2012-02-08 11:24:57 +0100 Remove now unused advertise_call helper commit ba58cf5e5784e51c4c311d4c4a273bd383ee7173 Author: Nicolas Dufresne Date: 2012-02-08 11:24:33 +0100 Remove unused header in call-muc commit c33c3060db31e3d6578a00ea2a611181a760e062 Author: Nicolas Dufresne Date: 2012-02-08 11:24:16 +0100 Port call-hold-av test to CallTest commit 7f27e6ffcb7f67f5e3c624015f28082852fee9a5 Author: Nicolas Dufresne Date: 2012-02-08 11:24:05 +0100 Port call-hold-audio test to CallTest commit d3864c978337e6bff417b48e2f51844030b8365d Author: Nicolas Dufresne Date: 2012-02-08 11:23:48 +0100 Port call-google-relay test to CallTest commit 9bd7bc624705ffc897225027cc47c2ed5ee0d339 Author: Nicolas Dufresne Date: 2012-02-08 11:23:24 +0100 Port call-dtmf test to CallTest commit 8e8501ce2b8d843b42254cb87e0f67d540006005 Author: Nicolas Dufresne Date: 2012-02-08 11:23:14 +0100 Port call-dtmf-no-audio test to CallTest commit 4da073f860d3d994b614d0c8bb33dceaa8e4f0a9 Author: Nicolas Dufresne Date: 2012-02-08 11:22:50 +0100 Remove uneeded header in call-codecoffer commit 729a6e979e0b302ccd3d582549118c3787bd3b7b Author: Nicolas Dufresne Date: 2012-02-08 11:22:26 +0100 Port call-basics test to CallTest commit 00a76e3668c2c10ab2b3cceaf6cc4e1a3eb70c97 Author: Nicolas Dufresne Date: 2012-02-08 11:21:33 +0100 Add CallTest base class This base class can be use to greatly reduce Call test code size commit 0c9c11eee409aba15c58d9e91ab7d8f0eb1b7c7b Author: Olli Salli Date: 2012-02-07 18:37:47 +0200 Only pass -fPIC cflag to submodules if compiler doesn't warn or error from it Reviewed-by: Siraj Razick commit 5c780cf06292a80271bbe5a35d5d10a9eba5e8d2 Author: Olli Salli Date: 2012-02-07 15:28:26 +0200 Update NEWS commit 5ed3540f9321fed01df2729a37477f219ab1b9cb Author: Olli Salli Date: 2012-02-07 15:02:14 +0200 Compile submodules with -fPIC to fix gabble-plugins.so link on linux/x86_64 Reviewed-by: Simon McVittie (smcv) commit 415706b45a63915dcd442fab7364a6d9ade1d79c Author: Nicolas Dufresne Date: 2012-02-07 13:40:11 +0100 call: Fix last content remove special case when the last content in a session is removed, the XEP says the content-remove signal must be replaced with session-terminated. This case is handled in gabble_jingle_session_remove_content, but call was bypassing it by using gabble_jingle_content_remove() directly. commit 82d8ca3198eef823c7bfca836e537926ebc49149 Author: Nicolas Dufresne Date: 2012-02-07 13:38:20 +0100 Add CallStreamComponent enum in constants commit 0e697dc4f035d78efc27a652efa8c5c3bdaf52a2 Author: Nicolas Dufresne Date: 2012-02-07 13:37:59 +0100 Add CallStateChangeReason enum in constants commit 78b6994fab54d317f77bbfdcfc47446ed4ba3701 Author: Nicolas Dufresne Date: 2012-02-07 13:37:21 +0100 jingle: Fix comment typo commit 359a1ae278a9c33dd0bc41394912166ac2b5ecda Merge: e073905 b33adc6 Author: Will Thompson Date: 2012-02-06 17:47:58 +0000 Merge branch 'master' into call1 Conflicts: src/Makefile.am For reasons I don't fully understand (presumably headers moving around a bit?) I had to add includes of or to: • src/call-content.c • src/private-tubes-factory.c • src/protocol.c • src/util.c But otherwise this has been an uneventful merge! commit 0c0dccb8135ca48e4222a408043dc93b3fcf4a98 Author: Siraj Razick Date: 2012-01-25 16:32:32 -0500 Windows specfic changes to produce plugins as dll's Using -module doesn't produce .dll files when compiling for windows These changes enable us to output .dll files for plugins. https://bugs.freedesktop.org/show_bug.cgi?id=44649 commit f54da139bb483fce675d6f11bfc000ec2d6732aa Author: Siraj Razick Date: 2012-01-13 11:32:48 -0500 fd.o#44649 - Gabble plugin API symbols should be factored out to a separate library This patch refactors gabble connection by introducing a new GInterface which the plugins will link agaist. And GabbleConnection implements the new Interface. https://bugs.freedesktop.org/show_bug.cgi?id=44649 commit bb63a668664a2003c966b7e3d9806c98d0875bf5 Author: Siraj Razick Date: 2012-02-06 11:02:19 -0500 Change the plugin API to create_sidecar_async and create_sidecar_finish All gabble plugins should implement these two methods hereafter. This patch also updates all the internal plugins to use this new API. https://bugs.freedesktop.org/show_bug.cgi?id=44331 commit b33adc647bf4e6dc75efba1abd74380d4bbb18d1 Author: Will Thompson Date: 2012-02-06 15:22:43 +0000 Update Wocky snapshot for DEBUG_* namespacing commit 87841786d0ed60fb093c3343e185805db500de7a Author: Will Thompson Date: 2012-02-06 14:40:36 +0000 NEWS for single-header Wocky. commit ee48ee9a8c440bbd0c1efa49fc9cd346a92883d3 Author: Will Thompson Date: 2012-02-01 16:09:56 +0000 Update Wocky snapshot for One Big Header. https://bugs.freedesktop.org/show_bug.cgi?id=27489 commit 3e0c8b55b81207f7842495b4ab9f59f0de655ae3 Author: Jonny Lamb Date: 2012-02-03 14:31:13 -0500 ft-channel: subclass TpBaseChannel Signed-off-by: Jonny Lamb commit ab745cbdead64a313fe3e8859cec5bf7c4382dc9 Author: Jonny Lamb Date: 2012-02-01 16:07:25 -0500 Revert "Merge remote-tracking branch 'siraj/plugin-api-change'" This reverts commit bf805ba0b2ecced81e5c2830a79d021a42da91a7, reversing changes made to 1296a2f5ce46e77787ca42eadb1c2ca4a957a09b. commit 1d41beb99b03f828fd204e6e18fa7499360937b9 Author: Jonny Lamb Date: 2012-02-01 16:07:06 -0500 Revert "Merge remote-tracking branch 'siraj/windows-compile-fix'" This reverts commit a687785628216f8f73c699096e9aae4a07b811c6, reversing changes made to bf805ba0b2ecced81e5c2830a79d021a42da91a7. commit a687785628216f8f73c699096e9aae4a07b811c6 Merge: bf805ba e4cccf0 Author: Jonny Lamb Date: 2012-02-01 13:54:00 -0500 Merge remote-tracking branch 'siraj/windows-compile-fix' Conflicts: lib/loudmouth/Makefile.am plugins/console.c src/Makefile.am src/connection.c src/error.c src/plugin-loader.c src/plugin.c Signed-off-by: Jonny Lamb commit bf805ba0b2ecced81e5c2830a79d021a42da91a7 Merge: 1296a2f 026e84f Author: Jonny Lamb Date: 2012-02-01 13:40:56 -0500 Merge remote-tracking branch 'siraj/plugin-api-change' Conflicts: lib/loudmouth/Makefile.am Signed-off-by: Jonny Lamb commit e4cccf0a604c7cdf1107aa9b5d9cdaa996bcc702 Author: Siraj Razick Date: 2012-01-25 16:32:32 -0500 Windows specfic changes to produce plugins as dll's Using -module doesn't produce .dll files when compiling for windows These changes enable us to output .dll files for plugins. https://bugs.freedesktop.org/show_bug.cgi?id=44649 commit 1296a2f5ce46e77787ca42eadb1c2ca4a957a09b Author: Will Thompson Date: 2012-02-01 12:49:04 +0000 NEWS for improvement commit e7fbb73258ab0eb00eb239f277ae5ad4ed0f9ee5 Author: Will Thompson Date: 2012-02-01 11:16:51 +0000 Presence: show better error status messages. When you get , any element in the presence is an echo of a stanza you sent out. So the message in that is not the contact's presence: it's either your status, or maybe the message you sent with a subscription request, depending on whether we got the for a roster contact or someone we tried to subscribe to. So instead, I think we should use the text of the error (if any) as the status for such contacts, falling back to the error element name if the server is unkind and doesn't include an error message. Yes, this means strings will show up in the UI in the server's locale, but this is hardly news. https://bugs.freedesktop.org/show_bug.cgi?id=45491 Reviewed-by: Simon McVittie commit cdcbaaa7a6e2b1056be96dda77d8a8363f78539d Author: Will Thompson Date: 2012-02-01 11:30:01 +0000 NEWS for deLoudmouthing etc. commit de0f8a21f82234e1fcc2a84994b07dc2bf1cc45a Merge: fbf798b c0a4dca Author: Will Thompson Date: 2012-02-01 11:22:50 +0000 Merge branch 'unhandled-iqs' Fixes: https://bugs.freedesktop.org/show_bug.cgi?id=34975 Reviewed-by: Simon McVittie Reviewed-by: Jonny Lamb commit fbf798b5fce94bc932fde45bc5579652acb05cd7 Merge: 281673e 6c68414 Author: Will Thompson Date: 2012-02-01 11:21:36 +0000 Merge branch 'node-iter-get-child-with-namespace' Fixes: https://bugs.freedesktop.org/show_bug.cgi?id=33911 Reviewed-by: Simon McVittie commit c0a4dca930b1e43d716df1295545fbf1856dae1a Author: Will Thompson Date: 2012-01-31 15:15:04 +0000 send_with_reply: remove stale reference to iq_unknown_cb commit d8feabf6ef67e5b7e5b0a4d01ef983bea877f0b5 Author: Will Thompson Date: 2012-01-31 17:15:28 +0000 Connection: rely on Wocky replying to unhandled IQs. https://bugs.freedesktop.org/show_bug.cgi?id=34975 commit 6c684143294c9dc2becc3af0887f907c760bf8af Author: Will Thompson Date: 2012-01-30 17:29:09 +0000 conn-olpc: give _get_child_with_namespace a clearer name. commit d8a2087662fe895eef24296fab7755c5b5ead197 Author: Will Thompson Date: 2012-01-30 17:27:17 +0000 conn-olpc: adopt lm_message_node_get_child_with_namespace This is now only used within this code, and I am too scared of breaking code I know nothing about to stop using it here too, so I'm just going to leave it here, probably with a new name. commit 7b258fc5ea423927878cd63e6f0a01ffe11d2869 Author: Will Thompson Date: 2012-01-30 17:23:32 +0000 conn-aliasing: use passed to ::changed handler. commit 821409f5203dc6b640ef6e0c00e0c343facc3a82 Author: Will Thompson Date: 2012-01-30 17:21:30 +0000 Update Wocky submodule for PepService changes This new snapshot changes WockyPepService to provide a pointer to the node in wocky_pep_service_get_finish() and WockyPepService::changed. This commit only updates conn-location.c to actually take advantage of it, but soon… commit 8601d29953e44416e5413c8e8053f980397f04b3 Author: Will Thompson Date: 2012-01-30 15:26:01 +0000 location: stop using lm_message_node_get_child_with_namespace() The test change illustrates the problem with using lm_message_node_get_child_with_namespace(): the test suite was sending a broken stanza, and we still accepted it. It should have sent: But instead it sent: This patch would be smaller if were fixed. commit 3403ab38582559b2293feb17f69f2df90e6ed229 Author: Will Thompson Date: 2012-01-30 14:58:32 +0000 tubes: stop using lm_message_node_get_child_with_namespace These cases weren't completely obvious to me, being unfamiliar with the tubes protocol. But they all wanted to be wocky_node_get_child_ns() too, really. commit 603aeb65562002ca1aa6273acf8580f88486e345 Author: Will Thompson Date: 2012-01-30 14:31:46 +0000 ft-channel: spell out finding node. commit aa11a7ed781c48ffda4d00f303776603d2f6ada5 Author: Will Thompson Date: 2012-01-28 10:47:32 +0000 _grab_nickname: use wocky_node_get_child_ns This is called on a variety of nodes from different stanza types, but in all cases (as far as I can tell) the element is a direct child of 'node'. commit 2db196ba241a2a22fd07ef6aa97ca9b559c26934 Author: Will Thompson Date: 2012-01-28 10:32:42 +0000 presence: explicitly hunt for privacy list node. commit bd3c28af3b96bf73256e8e0fc42470f0cf43039e Author: Will Thompson Date: 2012-01-28 10:17:54 +0000 Replace trivial uses of lm_message_node_get_child_with_namespace These are the cases where it was relatively quickly apparent to me that we only needed a direct child, not a deeper descendent. commit 5d91fadfb646437363c9f1e60f1deb279b09e180 Author: Will Thompson Date: 2012-01-28 09:59:21 +0000 util: remove node_iter commit 25980c6c7f70e2d3ff7d012b8e3a6a7cbed644e8 Author: Will Thompson Date: 2012-01-28 09:58:13 +0000 bytestream-socks5: remove degenerate node_iter use. commit 12a2a93c80a33723fdc6bfc031850b20e462b1b6 Author: Will Thompson Date: 2012-01-28 09:56:00 +0000 disco: move to WockyNodeIter commit 582a4e1fa932a81397c9b47992ceec673fe62419 Author: Will Thompson Date: 2012-01-28 09:51:34 +0000 conn-*: move to WockyNodeIter commit 48175abc011072b8c4e5fb4c01342f71ab33a982 Author: Will Thompson Date: 2012-01-28 09:50:53 +0000 conn-olpc: use WockyNodeIter The huge change is just reindenting the body of that loop inside an if block. There's no clean way to initialize a WockyNodeIter to be empty if your starting node is NULL. commit 50d3ef27080c66f95fa254f7c39fcc1f2c8b6d4b Author: Will Thompson Date: 2012-01-28 09:42:59 +0000 tubes: move to WockyNodeIter commit d61893c1abc031c9179a6b7565ba5d55075d1b8e Author: Will Thompson Date: 2012-01-28 09:41:08 +0000 muc: move to WockyNodeIter commit 5a7f08a3c78e8f4e33d3ddcf0a4450ae69cb92fd Author: Will Thompson Date: 2012-01-28 09:38:37 +0000 roster: move to WockyNodeIter commit 1c53458f88e5aa97b47640a58e41b1db46b0092b Author: Will Thompson Date: 2012-01-28 09:35:58 +0000 vcard-manager: move to WockyNodeIter commit ca262307fcbd3cf9bf6cd5b6e2268fcfa4badac4 Author: Will Thompson Date: 2012-01-28 09:33:20 +0000 Connection: move to WockyNodeIter commit c1d1d52bb4ab166a4df0ac5c46e46257698db5f0 Author: Will Thompson Date: 2012-01-27 23:40:10 +0000 search: move to WockyNodeIter In one place, I actually replaced a confusing use of NodeIter with wocky_node_get_content_from_child(). I think the result is much clearer. commit dd33dbffeefd72d3db74569d59f4975001e554d9 Author: Will Thompson Date: 2012-01-27 23:37:17 +0000 Jingle: migrate to WockyNodeIter commit c2c973e622f77d30a3d5cecf8ca2308dcb291b51 Author: Will Thompson Date: 2012-01-27 23:29:19 +0000 bytestream-factory: move to WockyNodeIter commit c7ee7f1ea0d9f0898679f892ce42a583967108e0 Author: Will Thompson Date: 2012-01-27 22:55:40 +0000 util: remove unused wocky_node_get_attribute_with_namespace commit 051d07d18a364c054d6f5ff50c8636281198fba2 Author: Will Thompson Date: 2012-01-27 22:54:46 +0000 disco: use wocky_node_get_child_ns commit 32ab1ebc7d68611187a15f545f82af8dd2399c0b Author: Will Thompson Date: 2012-01-27 22:53:25 +0000 search-channel: stop using lm_message_node_get_child_with_namespace In all cases, we wanted a direct child. commit 9dc5bfd7a376277180cecf9a77cf0882d3e6d292 Author: Will Thompson Date: 2012-01-27 22:53:10 +0000 message-util: stop using lm_message_node_get_child_with_namespace In every case, we wanted a direct child of some node. commit 383a34f1b2c2405c9b940f0024fe9ca92754dee7 Author: Will Thompson Date: 2012-01-27 22:52:55 +0000 util: explain why lm_message_node_get_child_with_namespace is bad commit 281673e30aa5012ec3425a59fddcb24d8c2ac18b Merge: c2279a6 14732bf Author: Will Thompson Date: 2012-01-31 17:18:13 +0000 Merge branch 'quietmouth' commit c2279a6b7dda2060e198d5407f22228d2c3be9ed Author: Simon McVittie Date: 2012-01-26 19:00:24 +0000 Fix a typo commit 5d7c87b1c753558c8c2525623a88103c1084fe2a Author: Simon McVittie Date: 2012-01-31 11:25:51 +0000 tubetestutil: clear list in a way that doesn't make it a local variable commit 2a2893a89248f22af2894cc2c6555da0ec365ed4 Author: Simon McVittie Date: 2012-01-31 11:25:34 +0000 Update Wocky for fd.o #43992 commit f1e67f8d9a348aac23821878779f7a45dad9719c Author: Nicolas Dufresne Date: 2012-01-30 16:27:37 -0500 Cleanup twisted tests import commit 2b5dbb4be2fa9890f332368d21cd92dd4dda67d9 Author: Jonny Lamb Date: 2011-12-21 12:37:02 +0000 tubetestutil: use temporary files as unix socket paths This is all an attempt to make the unix socket path shorter. Fixes: fd.o#43981 Signed-off-by: Jonny Lamb commit e0739058a22dae127e5410fd8ab4fb5dc2d49ad1 Author: Olivier Crête Date: 2012-01-30 16:08:54 +0000 Disable call-muc.py tests that isn't ported commit 4fe564149c374a2b30a4bfb8467b5d41f1fd4816 Author: Olivier Crête Date: 2012-01-30 16:05:34 +0000 Make call-muc-re-re-request.py test pass commit 14732bf5081f20aa314c3530e4c6a38155f75a62 Author: Will Thompson Date: 2012-01-28 12:55:34 +0000 disco: don't crash on replies whose is missing This is a regression from the BYE-BYE-LOUDMOUTH branch. The logic in request_reply_cb is meant to be: • if the stanza is an error, pass (with 'err' having been filled in); • else, if there is no node in the reply, set 'err' to that effect; • now call the callback; it is guaranteed that if err is NULL, query_node is not NULL. I inverted the check for the return value of wocky_stanza_extract_errors(): it returns TRUE if 'reply_msg' was an error, not FALSE. commit a7c0fccb5bbf66e05a0062a3d7056b6d1c57bd9a Author: Will Thompson Date: 2012-01-27 22:08:56 +0000 Remove vestigial Loudmouth headers and build system commit fb5672370cad426fa90a4906824704a4f5699cc9 Author: Will Thompson Date: 2012-01-27 21:56:19 +0000 Delete LmConnection API. This is it! There is now nothing left but a build system and some empty headers. The last functional vestige was the GCancellable used in _gabble_connection_send_with_reply(), which I basically just moved to GabbleConnection. That function should really die as well, but I'll leave that for another lifetime. commit 9fed5574a65c7d7318cd1c86e7e9ffc741139966 Author: Will Thompson Date: 2012-01-27 21:39:44 +0000 Remove LmHandlerResult The callback type for _gabble_connection_send_with_reply returned one of these. All instances, bar two, returned LM_HANDLER_RESULT_REMOVE_MESSAGE, which makes sense, because if you send an IQ you had better be prepared to handle the reply. When I removed lm_connection_send_with_reply(), I stopped _gabble_connection_send_with_reply() paying attention to the return value, with no apparent ill effects. The two which returned LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS were: • the disco pipeline's reply handler, in the case where the request is no longer in the pipeline (such as in a timeout). I checked, and as far as I can tell nothing is trying to scrape any information out of zombie disco replies. • the request pipeline's reply handler, in the same case. I couldn't be bothered to check for this, but I don't think it will pose any ill effects because normally the handler is the highest-priority callback, so anyone trying to catch these would not catch the ones which were not cancelled… which seems unlikely. commit cea17f274de06f7a7db6bcc68fa10cbdc083feb6 Author: Will Thompson Date: 2012-01-27 21:38:03 +0000 conn-olpc: remove an orphaned prototype commit 7319d124c880f0d8858ac137768bddd5c6ebc869 Author: Will Thompson Date: 2012-01-27 21:24:26 +0000 Remove LmMessageHandler API. LmHandlerResult shows up in _gabble_connection_send_with_reply(), so we can't ditch that just yet. commit 5881a6456bad0b47dba58134dfabd7f766b2cd17 Author: Will Thompson Date: 2012-01-27 21:18:43 +0000 connect/torture: skip if $REALLY_TORTURE is unset commit 0e1bb7601788afb19dea55e3fae5d4243bda4898 Author: Will Thompson Date: 2012-01-27 21:18:35 +0000 BytestreamFactory: stop using LmMessageHandler commit 9a567a7cf16759f8f692cf827211db81a76b2b1e Author: Will Thompson Date: 2012-01-27 20:46:25 +0000 PrivateTubesFactory: stop using LmMessageHandler commit afef96c60473b99b0fee8c3fbd7d777a3c9e4883 Author: Will Thompson Date: 2012-01-27 20:31:52 +0000 PresenceCache: use wocky_node_get_content_from_child This shortens some code a bit. commit 0bb2b04020b63e760f919f7b9c725b878f4df419 Author: Will Thompson Date: 2012-01-27 20:29:17 +0000 PresenceCache: remove duplicated code. commit 6d80ecf861640a76297ba8963027bc280931307f Author: Will Thompson Date: 2012-01-27 20:27:36 +0000 PresenceCache: refactor looking for decloak requests This was a little bit intertwingled, and long functions offend me. commit 7ab3e8a878e79a84c3c13d2a03e3bfe4148d2032 Author: Will Thompson Date: 2012-01-27 20:23:05 +0000 PresenceCache: stop using LmMessageHandler commit b76a9f08c6d9b6a410a1fda567ec81316dce47a8 Author: Will Thompson Date: 2012-01-27 19:34:37 +0000 conn-presence: stop using LmMessageHandler commit 8ee8a2027a38b67448eb56720baac842c6d218a1 Author: Will Thompson Date: 2012-01-27 19:21:21 +0000 MucFactory: stop using LmMessageHandler commit f676f5045909587dbf612a19ef35e6188f6308ca Author: Will Thompson Date: 2012-01-27 18:41:46 +0000 IMFactory: stop using LmMessageHandler. commit 62303c98f15f8e23e3bfa0a03e7ba109de0ac653 Author: Will Thompson Date: 2012-01-27 18:13:51 +0000 Remove lm-message.[ch] commit 89f7b1a1daafe6b9159c5cb42fb0c7fb7acf6871 Author: Will Thompson Date: 2012-01-27 18:08:32 +0000 Delete LmMessage{,Type,SubType} typedefs commit 6e3d12a641453d1a00b5271b3094edb7a6845a6c Author: Will Thompson Date: 2012-01-27 18:04:40 +0000 Remove lm_message_new[_with_subtype] commit 3c0a4208c89e0f2fc2acbf2da8a4563386da7870 Author: Will Thompson Date: 2012-01-27 17:43:07 +0000 Remove lm_message_get_[sub_]type. Wocky is a little less convenient here, but wocky_stanza_extract_errors() comes to the rescue. commit ee42d5f8329229aa8b7ed34cc96b4b888f60f307 Author: Will Thompson Date: 2012-01-27 17:40:48 +0000 Remove lm_connection_send_with_reply This basically inlines the call to wocky_porter_send_iq_async() in _gabble_connection_send_with_reply(), and removes a layer of indirection in calling the callback. commit 6ce72d766d274ab84c00b8533ae7f665067ea2e2 Author: Will Thompson Date: 2012-01-27 17:03:45 +0000 test-parse-message: g_assert_*-ify commit 89f7ea25ae4d0afdfc807747b543392a27a89220 Author: Will Thompson Date: 2012-01-27 17:01:26 +0000 test-parse-message: GTestify commit d89833fb55289705c6791a7a07a9c662b8a85df9 Author: Will Thompson Date: 2012-01-27 16:46:40 +0000 Remove duplicated AMP rules. I noticed this duplication while deleting lm_message_build and thought it might be better moved to some random place. commit 722536a79ae18e6bc060e97a7a520e89a13b371c Author: Will Thompson Date: 2012-01-27 16:20:45 +0000 Remove lm_message_get_node() It's completely unused! commit a615d8380375c3d5266c1b17e58488868fda23fb Author: Will Thompson Date: 2012-01-27 16:16:35 +0000 Expunge lm_message_node_get_name() commit d1758a79e0f9287fed177574b5c39e324128b15a Author: Will Thompson Date: 2012-01-27 15:55:09 +0000 Remove lm_message_build[_with_subtype] The build specs aren't *quite* compatible. lm_message_build()'s '(' accepted two arguments: tag name, and content (usually ""). So: '(', "foo", "" becomes '(', "foo" '(', "foo", "bar" becomes '(', "foo", '$', "bar" I didn't actually do this with sed as a result. commit af1a389766cad96903932f9231219ca5e80d5f98 Author: Nicolas Dufresne Date: 2012-01-26 16:04:59 -0500 Port google-relay test to call1 commit 89ccb036c015a2988b282198f74ec7ad5fbd51c9 Author: Nicolas Dufresne Date: 2012-01-26 16:04:26 -0500 Move new check_and_accept_offer to callutils commit a6ffa908693db3183a274e133c5cbdb37eb60d58 Author: Nicolas Dufresne Date: 2012-01-26 16:03:38 -0500 Update CALL_STREAM_TRANSPORT enum to new API Add missing and renamed to what is in the spec. commit 84403989062485a3ef0c5e131b8e11f47d964104 Author: Will Thompson Date: 2012-01-26 18:56:15 +0000 Correct NODE_DEBUG's first argument type. My previous patch was wrong. I didn't compile it. Sorry. commit bc55f4cd2ccba08642b39dcdb576dd3df05cd901 Author: Will Thompson Date: 2012-01-26 18:05:13 +0000 debug: remove leftover reference to LmMessageNode This is only on the --disable-debug path. commit 8cd1fe08bee1f31cd05b7fee5629496e0d2f806e Author: Will Thompson Date: 2012-01-26 17:03:26 +0000 Update Wocky submodule Something has gone screwy here. This supposedly only adds a single new commit: commit 44a326f3ab2aafea7a78df6ac75e9b4c014fe0e8 Author: Siraj Razick Date: Wed Dec 21 12:29:29 2011 -0500 Build fixes for Windows These changes enables us to cross-compile wocky for windows with mingw32 but the buildbots are confused and are using a ridiculously old snapshot. commit 450c82611c7921c6d0711bfa3170a6f1cc70f336 Author: Will Thompson Date: 2012-01-26 14:25:33 +0000 Expunge lm_message_ref and lm_message_unref. And also a couple of uses of lm_message_get_node(). commit c3e951a71acd69f2c3c3196675e2b2979e8a9a4a Author: Will Thompson Date: 2012-01-26 14:14:36 +0000 Jingle*: (mostly) de-Loudmouth-ify The only remaining reference is a reference to LmHandlerResult forced upon us by _gabble_connection_send_with_reply(). I'm sure we can deal with that later. commit e142bebe3e112d715af2b4784b24730eb9c310a0 Merge: a2a603b 035a6b0 Author: Will Thompson Date: 2012-01-26 13:09:47 +0000 Merge branch 'master' into BYE-BYE-LOUDMOUTH Conflicts: src/connection.c src/connection.h src/ft-manager.c src/jingle-content.c src/jingle-factory.c src/jingle-session.c src/message-util.c src/muc-channel.c src/muc-channel.h commit f8e208971a06a1f2b911066014900437559dd758 Author: Nicolas Dufresne Date: 2012-01-25 17:38:42 -0500 call-test: Add advertise_call utility commit debda34210cf48069c08ac8395592ef00f2f0ba6 Author: Nicolas Dufresne Date: 2012-01-25 17:16:08 -0500 call-test: Use callutils check_state instead of a copy commit e5bf54da204ff67396ee77835b25c8aea77c198c Author: Nicolas Dufresne Date: 2012-01-25 17:15:13 -0500 call-test: Remove unused exec_test import commit 3323f531d06bbaf9494c5b6bc56a8f14d68f3900 Author: Nicolas Dufresne Date: 2012-01-25 16:59:49 -0500 call: Connect signals in sync constructor The sync constructor was not connecting to jingle session "terminate" and "content-removed" signal. This was causing incoming call to not send the ENDED state. commit 4214f06caf4c809958e6cd33cd730f76c52bda49 Author: Nicolas Dufresne Date: 2012-01-25 16:58:12 -0500 call: Add trace on jingle session terminate commit d3beae79aadab2e0adfc206082068fc6689ff290 Author: Siraj Razick Date: 2012-01-13 11:32:48 -0500 fd.o#44649 - Gabble plugin API symbols should be factored out to a separate library This patch refactors gabble connection by introducing a new GInterface which the plugins will link agaist. And GabbleConnection implements the new Interface. https://bugs.freedesktop.org/show_bug.cgi?id=44649 commit 93943569220656d2939c96f50515da5ed7877af6 Author: Olivier Crête Date: 2012-01-20 20:23:47 -0500 Add the other reason from Jingle to the Call channel termination commit 7a207a7a42a6c6fe9737d9ce0032bab820c4ce58 Author: Nicolas Dufresne Date: 2012-01-20 15:29:55 -0500 Add Call1 version of hold-av test commit 8bf0743da622c2adf7a93f82c9a86dd28d277e3b Author: Nicolas Dufresne Date: 2012-01-20 15:29:06 -0500 Stream failure brings call to pending_hold state commit 123a0d473ad742c85f6d5242c1009403e7f222ba Author: Nicolas Dufresne Date: 2012-01-16 15:41:40 -0500 Add no-audio content DTMF call This test simply check that DTMF method does not exist on non-audio content object. commit 9ee76cdc02c550850c8490ddc97dc0c0fa24c38a Author: Siraj Razick Date: 2012-01-06 16:32:47 -0500 Change the plugin API to create_sidecar_async and create_sidecar_finish All gabble plugins should implement these two methods hereafter. This patch also updates all the internal plugins to use this new API https://bugs.freedesktop.org/show_bug.cgi?id=44331 commit 026e84f10bbfa52f813d30d95b0efbd2ec7a5228 Author: Siraj Razick Date: 2012-01-06 16:32:47 -0500 Change the plugin API to create_sidecar_async and create_sidecar_finish All gabble plugins should implement these two methods hereafter. This patch also updates all the internal plugins to use this new API https://bugs.freedesktop.org/show_bug.cgi?id=44331 commit b642a5dab5535bb3787529de26b6d69be98210e0 Author: Xavier Claessens Date: 2012-01-12 13:56:51 +0100 call-channel: Fix build warning commit 8c9c9fe6e67066a3208371734be54fa1e264af09 Author: Nicolas Dufresne Date: 2012-01-11 17:35:20 -0500 Check that DefferedTones is reset when sending new tones commit 47994713103ae137ca86810e0f032dce3418120c Author: Nicolas Dufresne Date: 2012-01-11 17:34:35 -0500 Check that CurrentlySendingTones is triggered commit 606d64610704dcdbfbc80e56a2c4e5f032350091 Author: Nicolas Dufresne Date: 2012-01-11 16:28:39 -0500 Test deferred tones in Call1 commit 0cb44ec85906f4ea21c55c9ead449a386eacbcd4 Author: Nicolas Dufresne Date: 2012-01-10 13:03:17 -0500 Fix reason when a call is rejected commit 635ee7183038d7bec1b10f2a56b41fbf087dcbef Author: Olivier Crête Date: 2012-01-06 20:24:49 -0500 Add fixme to remember to handle rejected codecs commit ca0726474896e546f40f251d4a25ecbbb98bbb0a Author: Olivier Crête Date: 2012-01-06 15:51:49 -0500 Set the Call termination reason commit 9aa42222621917e040f778d8ecea7f9f1663de76 Author: Olivier Crête Date: 2012-01-05 20:17:55 -0500 Port DTMF test to recent Call commit 25dce42e3b0786d84ef017648ceb31268702b680 Author: Olivier Crête Date: 2012-01-05 19:06:17 -0500 Update candidate capitalisation in tests too commit 75826de1194146ddc7c9a3e1677d6fcbe37dcb13 Author: Nicolas Dufresne Date: 2012-01-04 16:48:25 -0500 Candidate info names are no longer capitalised commit c253cfd561152df8a7f12459c3787d15a738d823 Author: Xavier Claessens Date: 2012-01-03 17:06:22 +0100 Fix build warning commit c5dea13a289fbaa29068a834252c9b8a29b02193 Author: Xavier Claessens Date: 2011-12-19 11:48:57 +0100 GabbleMediaFactory: Use TP_PROP_ where appropriate commit dd0f6115ada3ad038f383662e02805f2641cfa9e Author: Olivier Crête Date: 2011-12-31 02:25:47 -0500 Add test for Hold in Call commit 3a1a8fc0e062ac268ed54a2b56b70062ed4e2195 Author: Olivier Crête Date: 2011-12-30 17:14:15 -0500 Implement local hold for Call commit b555f952361fce1e037e67be2d90770eec5ffd02 Author: Olivier Crête Date: 2011-12-29 22:49:45 -0500 Update to new tp-glib api for setting the local sending state commit 47cc822f25c4100fd91758ee34a49a9edc31f9c2 Author: Olivier Crête Date: 2011-12-28 01:18:13 -0500 Add test for the remote side changing the direction commit 20483522b56a08c5fe01b1012db89c8e7239b850 Author: Olivier Crête Date: 2011-12-27 03:06:59 -0500 Add test for pending states when the other side asks to send commit 8fe2c028aedd1b9daae79546cbb07cb9e1b50759 Author: Olivier Crête Date: 2011-12-27 01:46:21 -0500 Implement per-dialect CanRequestReceiving Also add unit test for it commit db3883574a31350605f03555616be01ef0bfe3b3 Author: Olivier Crête Date: 2011-12-27 00:54:18 -0500 Fix Request receiving Also add tests commit f4c69b786b2c33930f7b6625a161c1b6b4013a2a Author: Olivier Crête Date: 2011-12-26 23:08:29 -0500 Add test for endpoint state changes caused by reconnection commit b50e272f22ca8c8f94b00ec273beef36ea1c0aa1 Author: Olivier Crête Date: 2011-12-26 21:06:40 -0500 Update codec offer test for updated spec commit 80f2cc4c085c1a7e76150f939f2c65fac06de370 Author: Olivier Crête Date: 2011-12-26 20:59:03 -0500 Update to correctly set the local and remote sending states Also update the unit tests and move some work to the base class commit 3940ed863349a6f1ab6be6764b79c00cf303798a Author: Olivier Crête Date: 2011-12-23 15:03:39 -0500 Implement request_receiving using the base class commit 6af603c3edcfdcd0f836ec34fc720f50b90a73e4 Author: Olivier Crête Date: 2011-12-23 15:03:01 -0500 Port to BaseMediaCallChannel commit a8c774fcd09e4298ade48982112ac0cc69b89321 Author: Olivier Crête Date: 2011-12-23 11:54:22 -0500 Export the ICE credentials from the transport to the stream commit 10d618e5098ec0b5a8baada8c1dec67428382797 Author: Olivier Crête Date: 2011-12-13 17:55:05 -0500 WIP: Half port DTMF test commit e97ab16a643a7a45c9e2e09d28777218759436b2 Author: Olivier Crête Date: 2011-12-13 17:47:33 -0500 Restore flags for StreamedMedia tests commit 591755cc3fcb5fe257eca33462cddef26aef71a2 Author: Olivier Crête Date: 2011-12-13 17:42:36 -0500 Port the codec offer test commit 395bb80683f5830326db422f9ed9ee3123c30ab1 Author: Olivier Crête Date: 2011-12-13 16:02:24 -0500 Listen to endpoint state change notification commit b25eb1fe6845f72bf2f4fb991c09cbdd8d9ed671 Author: Olivier Crête Date: 2011-12-13 15:47:45 -0500 Update tests to new Call1 spec commit 0009ecf2483e1ddb935266a4a8d03c31ca5ddb36 Author: Xavier Claessens Date: 2011-12-09 15:30:57 +0100 WIP: port unit tests to Call1 commit e946c179c33cac58de14443ed877cbd829e8ed04 Author: Xavier Claessens Date: 2011-11-28 14:23:17 +0100 Replace tp-yell with tp-glib's Call1 API commit ab43a9f94d0f304003b581a67c7dff6391917b5b Author: Siraj Razick Date: 2011-12-30 17:14:00 -0500 build libgabble-convenience as a .dll for windows when building 3rd part plugins for gabble, all the symbols as to be defined at link time, not at run time. to solve this problem libgabble-convenience has to compiled as libgabble-runtime.dll. also by this patch we avoid static linking telepath-gabble.exe commit 035a6b0a6e8ce1787abf2583bb918436282026fe Author: Guillaume Desmottes Date: 2011-12-22 16:02:51 +0100 telepathy-gabble-xmpp-console: no need to import pygtk In gobject-introspection we trust. https://bugs.freedesktop.org/show_bug.cgi?id=44056 commit 748caada6a36c26e7b8752a39138b4eaf42296db Author: Siraj Razick Date: 2012-01-04 05:10:37 -0500 Use ";" as the seperator on windows. With this fix, gabble can locate the plugin directory properly on Windows commit b5bc31a5084e05d7e4251a37f628f7835928ab1c Author: Siraj Razick Date: 2012-01-02 13:12:08 -0500 Make gabble cross compile with mingw32 The patch provides the changes required to make gabble compile under mingw32. - Index is replaced with strchr since index is deprecated and not included in mingw32 sdk - D_WIN32_WINNT=0x0501 is defined to make getnameinfo work - And other compile fixes commit 3cdfbe659bbe3a7e217486c2b0a957880b3c24ea Author: Andre Moreira Magalhaes (andrunko) Date: 2011-12-22 12:04:53 -0200 Update NEWS commit 5df4b3abb3909f4cdac2f88580acba762f7b07d1 Merge: dfb062f e1524fd Author: Andre Moreira Magalhaes (andrunko) Date: 2011-12-22 12:02:40 -0200 Merge branch 'ft-optional' Reviewed-by: Jonny Lamb commit dfb062f2a580d431713742904791627c2044155d Author: Alban Crequy Date: 2011-12-22 11:48:37 +0000 Start on version 0.15.4 commit bf29681bd6ac87c63b1ce6624d1efc58957af920 Author: Alban Crequy Date: 2011-12-22 10:59:49 +0000 version 0.15.3 commit 3e7bb6cedcca537126422799a33a11d95bc8808a Author: Alban Crequy Date: 2011-12-22 10:43:24 +0000 NEWS: add reference to fd.o#43891 commit 845816e8c451bd18412b4c616ce549863689714a Author: Alban Crequy Date: 2011-12-22 10:33:05 +0000 Update wocky to fix wocky_data_form_set_type() https://bugs.freedesktop.org/show_bug.cgi?id=43891 commit ead4381be6e3ab46d454458d54c86fc98c4fe61d Author: Jonny Lamb Date: 2011-12-21 09:28:06 +0000 start on version 0.15.3 Signed-off-by: Jonny Lamb commit ac5b2184c9b89f527d8ce4ee83484fde084f8499 Author: Jonny Lamb Date: 2011-12-21 09:15:53 +0000 version 0.15.2 Signed-off-by: Jonny Lamb commit e1524fd2acb29611f8e9055e227b86c4809b546f Author: Andre Moreira Magalhaes (andrunko) Date: 2011-12-20 21:40:36 -0200 Add configure option to disable file transfer support. commit 109ae37c08613b9d1b240ab325f993d466a9be55 Author: Xavier Claessens Date: 2011-12-20 15:09:27 +0100 Update wocky commit to fix build warnings commit 09cae896ad821071e7d1fcfc69503deeefb57534 Author: Alban Crequy Date: 2011-12-16 16:02:01 +0000 Compute the caps verification string correctly with dataforms in plugins https://bugs.freedesktop.org/show_bug.cgi?id=43889 commit a17ea171acb4847ada48782a8b57feff78aee123 Author: Alvaro Soliverez Date: 2011-12-14 17:00:11 -0300 Added missing build flag commit 2348d1d320bd99560427875de45351691c018274 Author: Xavier Claessens Date: 2011-12-14 12:33:48 +0100 Fix the build with glib 2.32 deprecated symbols commit e61ea6b4baa4080e072f69699bc876c762797b5d Author: Alban Crequy Date: 2011-12-07 13:19:22 +0000 Add gabble_connection_add_sidecar_own_caps_full() with dataforms https://bugs.freedesktop.org/show_bug.cgi?id=43588 commit a7740569c39532c1e508ff707ddecadbcd5acd0b Author: Sjoerd Simons Date: 2011-11-29 12:58:32 +0100 Update wocky snapshot to fix issues with non-character data commit 956da5d8d2df4aad54e44315ae9df4b54a3ec0fc Author: Will Thompson Date: 2011-11-24 16:48:18 +0000 Rhythm is a dancer. commit f7555112d4d9fb17709befd5068a4fdac7365886 Author: Will Thompson Date: 2011-11-24 16:17:58 +0000 Version 0.15.1 commit 4284793d64274349ced1d4c0ceb6f69cb136425d Author: Will Thompson Date: 2011-11-24 16:14:49 +0000 NEWS for 0.15.1 commit 7d6e1fa81492520fcd020fe6a1ab4b0a8a1e85ef Author: Will Thompson Date: 2011-11-24 15:32:29 +0000 Knock .DRAFT off Conn.I.Addressing. I've taken the executive decision to turn it into a version number, so that we don't force ourselves to break compatibility if we decide the interface is perfect. commit 05e7ea30f2588a098ad52c1969ca3f25795e7dba Merge: f467b8c 9e4bd73 Author: Will Thompson Date: 2011-11-24 15:25:21 +0000 Merge remote-tracking branch 'andrunko/fb-addressing' Fixes: commit f467b8cd4dffca20516be46d6da58f0bb4868df3 Merge: 6a0ed9a 7fa869b Author: Will Thompson Date: 2011-11-24 15:15:58 +0000 Merge remote-tracking branch 'andrunko/conn-addressing' Fixes: commit 6a0ed9a2f9c49d21060dd0053c8b71864ee59e10 Author: Will Thompson Date: 2011-11-24 14:22:23 +0000 extensions: add Make rules for *-gtk-doc.h 3c830e9 fixed `make clean` but in the process it broke building from nothing… since these are now included in nodist_libgabble_extensions_la_SOURCES, we need to tell Make how to build them. commit 210aeb46df44531b1b3c348b5b75e743afe2503c Merge: b775d24 43cbe47 Author: Will Thompson Date: 2011-11-24 14:06:42 +0000 Merge remote-tracking branch 'andrunko/protocol-addressing' Fixes: commit b775d249a13db7a31a0306572f670f6b76b3c804 Author: Will Thompson Date: 2011-11-24 13:58:29 +0000 configure: depend on tp-glib 0.17.2 This is needed to support Protocol.Interface.Addressing, coming right up… commit 6554c595fe1fd7684ac8efb2505b589601660c3b Author: Alban Crequy Date: 2011-11-22 17:26:17 +0000 Fix missing capabilities.h and caps-channel-manager.h The problem was introduced by this commit: |commit 587da15932d178ad289e81b039dcabb4e90984c5 |Author: Jonny Lamb |Date: Thu Sep 1 14:19:52 2011 +0100 | | gabble: add capabilities.h and caps-channel-manager.h as public API | | Signed-off-by: Jonny Lamb commit c6e76ac44acd46e8d5a1c9f2cc950548d7a30ead Author: Alvaro Soliverez Date: 2011-11-15 13:05:47 -0300 Support building on Android This patch makes it possible to use installed versions of Wocky and Yell, rather than submodules; adds a missing explicit dependency on gio; and adds Androgenizer build targets. Modified from an original patch by Derek Foreman. https://bugs.freedesktop.org/show_bug.cgi?id=42446 commit 9e4bd7355548bc64aeda0c997dbd14e66522193b Author: Andre Moreira Magalhaes (andrunko) Date: 2011-11-17 14:36:38 -0200 addressing-util: Properly check if "x-facebook-id" vcard address is a number. commit f3d2d2e95ed23e080e5bd4ec935491916efd7bbf Author: Andre Moreira Magalhaes (andrunko) Date: 2011-11-17 13:10:03 -0200 addressing-util: Refactor gabble_parse_vcard_address and make "x-facebook-id" parsing expect a number, not a JID. Refactor gabble_parse_vcard_address in gabble_normalize_vcard_address, gabble_vcard_address_to_jid and gabble_jid_to_vcard_address and and make "x-facebook-id" parsing expect a number, not a JID. commit a3c018452d6e4bf48c6eeb16bfd55b35b70c3aad Author: Andre Moreira Magalhaes (andrunko) Date: 2011-11-16 21:31:18 -0200 Expand tests for Conn.I.Addressing to test normalization of vCard addresses with 'x-facebook-id' field.fb-addressing. commit fdf0b93faa48797b739a4d6275baad0f1069f0ce Author: Andre Moreira Magalhaes (andrunko) Date: 2011-11-10 21:33:21 -0200 Expand tests for Protocol.I.Addressing to test normalization of vCard addresses with 'x-facebook-id' field. commit 3f4ce2c1a14aba3c5f0c922e527ff9e648311207 Author: Andre Moreira Magalhaes (andrunko) Date: 2011-11-16 20:32:45 -0200 protocol: Add support for normalizing vCard addresses with 'x-facebook-id' field. commit 7fa869b1f319ab42c75d61fdd3b22859f3bb1dfb Author: Andre Moreira Magalhaes (andrunko) Date: 2011-11-21 13:25:27 -0200 Update Conn.I.Addressing from tp-spec. commit aa5280ce5e76c65b310379d73671f4bce826a286 Author: Andre Moreira Magalhaes (andrunko) Date: 2011-11-21 13:11:07 -0200 addressing-util: Rename gabble_normalize_uri to gabble_normalize_contact_uri. commit b203be272d60153eef7d00b5edab30a4f0021759 Author: Andre Moreira Magalhaes (andrunko) Date: 2011-11-17 13:39:22 -0200 addressing-util: Replace gabble_decode_jid with wocky_decode_jid. commit 25c3d3469e587467008d0f7942125ff9a421a6d9 Author: Andre Moreira Magalhaes (andrunko) Date: 2011-11-17 13:34:56 -0200 addressing-util: Remove redundant checks when calling g_uri_escape_string. commit 4192c92ae505489050da9f89fcbb756d83f66bb3 Author: Andre Moreira Magalhaes (andrunko) Date: 2011-11-17 12:00:23 -0200 twisted/addressing.py: Expand tests for normalizing URIs. commit 2d661e26a114dcf91e0461a814072be4c095e0f4 Author: Andre Moreira Magalhaes (andrunko) Date: 2011-11-17 11:40:05 -0200 addressing-util: Use gabble_parse_xmpp_uri on gabble_uri_to_jid. commit 9f53dc66c3a9b29ba987e5bb1d9a33ad34567beb Author: Andre Moreira Magalhaes (andrunko) Date: 2011-11-17 11:39:46 -0200 addressing-util: Add gabble_parse_xmpp_uri helper method. commit 909d5428e4187ffe276587e726a790e7b1653636 Author: Andre Moreira Magalhaes (andrunko) Date: 2011-11-17 11:10:19 -0200 addressing-util: Refactor gabble_jid_to_uri and gabble_normalize_uri. commit dad62245979ea66a63c4e2ad8ad99386c0007e07 Author: Andre Moreira Magalhaes (andrunko) Date: 2011-11-17 10:42:07 -0200 conn-addressing: Remove unnecessary cast. commit 26594e8d47c0af050128d9133892f676cde96e55 Author: Andre Moreira Magalhaes (andrunko) Date: 2011-11-17 10:41:33 -0200 Update docs for Conn.I.Addressing. commit ef5685459b4815aac89d3e0ed4202e277e127705 Author: Andre Moreira Magalhaes (andrunko) Date: 2011-11-16 20:36:26 -0200 protocol: Use gabble_get_addressable_uri_schemes/vcard_fields. commit 687adff7ab22e0f2dc5f81b895726372acc28a98 Author: Andre Moreira Magalhaes (andrunko) Date: 2011-11-16 21:30:49 -0200 addressing-util: Make gabble_uri/vcard_address_for_handle generic. commit 2e33ef47c7fc2c2630c084b590610f5bf086fdab Author: Andre Moreira Magalhaes (andrunko) Date: 2011-11-16 21:27:39 -0200 addressing-util: Use vcard address as the identifier for tp_handle_inspect in gabble_ensure_handle_from_vcard_address. We need to parse the vcard address to make sure the field/address is supported, but we cannot use the return of it as the identifier to tp_handle_ensure, as the return may be invalid (depending on the vcard field). commit ab6aef86574495c699756f13f7fe3e8e33b48c97 Author: Andre Moreira Magalhaes (andrunko) Date: 2011-11-16 20:08:28 -0200 addressing-util: Split gabble_parse_uri in gabble_normalize_uri, gabble_jid_to_uri and gabble_uri_to_jid. Also properly escape URIs. commit 846339c2ad4fe280a1f89ca60ff4bfcd59ed7063 Author: Andre Moreira Magalhaes (andrunko) Date: 2011-11-16 17:44:35 -0200 conn-addressing: Use g_str_hash/equal and dup/free the key for the requested hash on conn_addressing_get_contacts_by_uri/vcard_field. commit 1393bc432a2a271e94d528df15b9166c9fa9a8b7 Author: Andre Moreira Magalhaes (andrunko) Date: 2011-11-16 17:41:42 -0200 Update docs for Conn.I.Addressing. commit 774995d3b527ced96f236ac7937d8861c9403c03 Author: Andre Moreira Magalhaes (andrunko) Date: 2011-11-16 17:37:08 -0200 Fix typo in Conn.I.Addressing. commit f84a9c17a256a17ad0118a9a932cc90650399143 Author: Andre Moreira Magalhaes (andrunko) Date: 2011-11-16 17:16:56 -0200 Split requested contacts into a separate argument in Conn.I.Addressing. Remove the contact attributes ".../requested-*" and return the satisfied requested addresses on GetContactsByURI/VCardField. commit a1c71d47685d266be24f3a6deebddb5d184c781d Author: Andre Moreira Magalhaes (andrunko) Date: 2011-11-16 13:27:18 -0200 protocol: Use g_error_matches to properly match for errors and don't crash if error is NULL. commit bee30c01c77fcd76fe046bf962d1568f16149980 Author: Andre Moreira Magalhaes (andrunko) Date: 2011-11-16 13:23:49 -0200 conn-addressing: Use g_error_matches to properly match for errors. commit 158fbf5c3987b90f27345cd54ba3885790caddd3 Author: Andre Moreira Magalhaes (andrunko) Date: 2011-11-16 13:19:31 -0200 conn-addressing: Add comments for g_hash_table_get_keys usage. commit cd210ac5a6d04fb46d532e1421b11cc9baa34197 Author: Andre Moreira Magalhaes (andrunko) Date: 2011-11-16 13:18:25 -0200 conn-addressing: Rename variables starting with "in_". commit 4137aba89752ae3b773c44fa59e238376a2561dc Author: Andre Moreira Magalhaes (andrunko) Date: 2011-11-16 13:14:47 -0200 conn-addressing: Reorder includes. commit 36be157b1edb9776a320d377588b2ec8fd1cce56 Author: Andre Moreira Magalhaes (andrunko) Date: 2011-11-16 13:11:50 -0200 conn-addressing: No need to include debug.h and define a debug flag as we don't use them. commit 2a1708b475e527bd2d8662d30777c910d08c5df1 Author: Andre Moreira Magalhaes (andrunko) Date: 2011-11-16 13:08:42 -0200 addressing-util: Refactor gabble_vcard_address_for_handle. commit 082f79dbf29e3a11d641b6339000e2bcf639f2c9 Author: Andre Moreira Magalhaes (andrunko) Date: 2011-11-16 13:01:22 -0200 addressing-util: Refactor gabble_vcard_addresses_for_handle. commit 24f94aad9af73af03486f0612e4f99a723b30e3e Author: Andre Moreira Magalhaes (andrunko) Date: 2011-11-16 12:32:51 -0200 addressing-util: Refactor gabble_parse_vcard_address. commit 218ae8f0dc091444b34df18f9e79797a9bfdcc37 Author: Andre Moreira Magalhaes (andrunko) Date: 2011-11-16 13:06:59 -0200 addressing-util: Refactor gabble_uri_for_handle. commit 44184e6acbcd8cd6cd6baa39811833d39c90e600 Author: Andre Moreira Magalhaes (andrunko) Date: 2011-11-16 12:52:44 -0200 addressing-util: Refactor gabble_uris_for_handle. commit 5e74e1d55948636645b654f267398253d329ffdc Author: Andre Moreira Magalhaes (andrunko) Date: 2011-11-16 12:24:00 -0200 addressing-util: Refactor gabble_parse_uri. commit 3210556571ef281f2afe6586c35474be0906681b Author: Andre Moreira Magalhaes (andrunko) Date: 2011-11-14 16:33:30 -0200 addressing-util: Put includes in alphabetical order. commit 78af6ffac2fccf1f26ad7c38b369e9c493f726f1 Author: Andre Moreira Magalhaes (andrunko) Date: 2011-11-14 16:31:38 -0200 addressing-util: No need to include debug.h and define a debug flag as we don't use them. commit d977ec1feaf34a19979309ca1a8e2e1808aa3ece Author: Andre Moreira Magalhaes (andrunko) Date: 2011-11-14 16:32:22 -0200 addressing-util: No need to include config.h in the header file. commit c07bb73548709f427c4b47b5a0d93c77806d631e Author: Andre Moreira Magalhaes (andrunko) Date: 2011-11-11 01:30:05 -0200 addressing-util: Fix some coding style issues. commit dda584a302134223185a36d589e48d8d2a1ec07d Author: Andre Moreira Magalhaes (andrunko) Date: 2011-11-11 01:00:34 -0200 addressing-util: Do not fill output uri if input uri cannot be parsed in gabble_parse_uri. commit 33394ec5a4f1acfc3f6235626d98e0267e27fa53 Author: Andre Moreira Magalhaes (andrunko) Date: 2011-11-11 00:53:31 -0200 twisted/addressing.py: Re-enable Protocol.I.Addressing test. commit f4a87f5d3584d19993206992f768638a0209bdea Author: Eitan Isaacson Date: 2010-09-22 10:32:09 -0700 Synced with latest tp_contacts_mixin_get_contact_attributes. commit b4cc4692ee10fa84f92922bfabb22153232187a5 Author: Eitan Isaacson Date: 2010-09-21 13:07:32 -0700 More centralizing. commit c1610996707159f8c09ed0077df169eadd253d59 Author: Eitan Isaacson Date: 2010-09-21 12:07:35 -0700 Added addressing-utils.[ch] to aide in addressing consistency. commit 3c86293fa5dc2b24447eeaddc5cd8b7a1e3cf9da Author: Eitan Isaacson Date: 2010-09-20 11:54:33 -0700 Added Conn.I.Addressing tests. commit d7618fe18e64c05625d95531c3c918a1c238a5da Author: Eitan Isaacson Date: 2010-09-20 00:05:06 -0700 Implemented getters in Conn.I.Addressing. commit 2524367ba1531d27a1c201651119a44aba0a999a Author: Eitan Isaacson Date: 2010-09-20 00:04:40 -0700 Factored out URI parsing. commit f1b72e248195b25f629bb2042135066c34b482a4 Author: Eitan Isaacson Date: 2010-09-19 16:20:18 -0700 Started work on connection addressing iface. commit 4c0aa45e6a69de77cc9e685edac9213ba34192dd Author: Eitan Isaacson Date: 2010-09-19 09:19:42 -0700 Added Conn.I.Addressing as an extension. commit 43cbe4770f28bc52115bec727edc07a6c9142841 Author: Andre Moreira Magalhaes (andrunko) Date: 2011-11-21 13:05:55 -0200 protocol: Update to latest changes in Proto.I.Addressing. commit a9608bc48432be0e18441b931c60d6438acc40e9 Author: Andre Moreira Magalhaes (andrunko) Date: 2011-11-14 15:05:14 -0200 twisted/constants.py: Re-use PROTOCOL definition when defining PROTOCOL_IFACE_ADDRESSING. commit 9c745c78daa57bb374fdcb536ead75ba3d50c9aa Author: Andre Moreira Magalhaes (andrunko) Date: 2011-11-14 15:04:34 -0200 write-mgr-file: Use one property per line when calling g_object_get, to emphasize the paired arguments. commit b7504155ef6656ee155439fef7b35b3e6226dcfb Author: Andre Moreira Magalhaes (andrunko) Date: 2011-11-14 14:57:45 -0200 protocol: Reorder logic of checking for uri schemes in addressing_normalize_uri. commit be3b8aa3f9b6d8aa5e933d7ee44475ab4356c5c3 Author: Andre Moreira Magalhaes (andrunko) Date: 2011-11-14 14:56:09 -0200 protocol: Reorder logic of checking for vcard fields in addressing_normalize_vcard_address. commit c02c3f6d08d890627ed19b8513cc3609e4ed8303 Author: Andre Moreira Magalhaes (andrunko) Date: 2011-11-14 14:53:38 -0200 Remove debug area for protocol as it's not being used. commit f498451348f9053ff3e4b01a854862d00ba57a80 Author: Andre Moreira Magalhaes (andrunko) Date: 2011-11-14 14:49:52 -0200 protocol: Updated with latest changes in tp-glib to support Protocol.I.Addressing. commit 6cdcedb7faca3d2fa43e01d32d9ae7bade2a0a5b Author: Andre Moreira Magalhaes (andrunko) Date: 2011-11-10 21:34:37 -0200 write-mgr-file: Make it comply with ISO C90. commit fb792b824efcc0960db7143ff69bf79ee76ac278 Author: Eitan Isaacson Date: 2011-01-03 16:24:43 -0800 Use new addressing GInterface API. Fix review nitpicks. commit bd20b6a9c8b4ba138a77112efd698af126965afc Author: Eitan Isaacson Date: 2010-12-27 15:56:53 -0800 Update manager file writer for new addressing features. commit 83095016b23bc2096dd7fcd567bde3ac56017ac4 Author: Eitan Isaacson Date: 2010-12-27 15:56:06 -0800 Added tests for Protocol.Interface.Addressing. commit fb0b624a751807821dcaa91196457dc0be70406c Author: Eitan Isaacson Date: 2010-09-15 15:56:54 -0700 Added Addressing support to protocol. commit 3c830e93b65121c2a6450c97b68922b49d85fde3 Author: Will Thompson Date: 2011-11-21 14:19:35 +0000 extensions: add generated -gtk-doc.h files to Makefile The updated code generator added in f9332611 puts gtk-doc comments into separate source files. Since these weren't mentioned in extensions/Makefile.am, `make distcheck` failed, protesting about “files left in build directory after distclean”. commit 357aef5d882f461a05037255f3f0dee844d18d6f Merge: cbe05e9 ac3ed72 Author: Will Thompson Date: 2011-11-21 13:14:04 +0000 Merge branch 'xmpp-console' Fixes: Reviewed-by: Guillaume Desmottes commit ac3ed729bed7d8c4fb62a11ade54f54b6139c0d0 Author: Will Thompson Date: 2011-11-21 10:49:00 +0000 console plugin: add a FIXME re. client dying commit f28d2d99353e91c925986f8ee7abde562384f884 Author: Will Thompson Date: 2011-11-18 11:00:46 +0000 console UI: turn off monitoring when we quit commit 936059fedae244ec4ea9cf40d500f9b761639bfb Author: Will Thompson Date: 2011-11-18 11:00:21 +0000 console UI: allow passing account identifiers commit 77f77a88c3a28a68cdbd5656912580cf2e235338 Author: Will Thompson Date: 2011-11-17 23:50:33 +0000 console UI: fix handling errors from SendIQ commit 2622e65507e83956a2dd0b7f349135771ec8f335 Author: Will Thompson Date: 2011-11-17 17:42:43 +0000 console UI: add a page for sending arbitrary stanzas commit 5bcba4d971ebf48ba827067ff4b1c5d28c36295f Author: Will Thompson Date: 2011-11-17 23:49:50 +0000 console UI: factor out spinner wrapper notebook thing commit ff861b9904b4d200c33107fd39178585310918d0 Author: Will Thompson Date: 2011-11-17 11:07:32 +0000 console plugin: add SendStanza method commit 0212d45fcddd528cf3f3b66c930b0b57e0788d6e Author: Will Thompson Date: 2011-11-16 10:23:42 +0000 console UI: add stanza monitor pane commit 9391ee87e77315f3405722846d6b2bec14736497 Author: Will Thompson Date: 2011-11-16 09:38:46 +0000 console UI: split out UI helpers The traffic monitor window will need to show nicely-formatted stanzas, and it will also need a grid with the same spacing. commit bd8c210005270e42b09feb08abf521bd64a1237d Author: Will Thompson Date: 2011-11-16 09:29:12 +0000 console UI: pack IQ page into a notebook. commit ace82cb0e39516c6df7b055b5a2219c45578c746 Author: Will Thompson Date: 2011-11-16 09:19:29 +0000 console UI: refactor IQ interface into a class This just pulls out the entire Gtk.Grid containing the UI for sending an IQ into its own subclass of Gtk.Grid, paving the way for another page in the UI for watching the stanzas fly past. commit 54dc3ce758b1f47f2a46590297765c5baf5dbd77 Author: Will Thompson Date: 2011-11-16 09:18:38 +0000 Add a smoke test for the XMPP console commit 1a6f7fff8022d9f4bf17ded58771c3c7a8582049 Author: Will Thompson Date: 2011-11-16 09:09:39 +0000 console plugin: implement received/sent signals Ideally the SpewStanzas property would become False when the client which asked for it falls off the bus. Later... commit 27afc946f679d649d57026ffd7d81bbaae00b4a2 Author: Will Thompson Date: 2011-11-16 09:08:18 +0000 Add Sending/Receiving signals to Console API commit f9332611f8c19a0bf96a7c822c7e03da796813f7 Author: Will Thompson Date: 2011-11-11 19:45:25 +0000 tools: update to respect EmitsChangedSignal commit 20901a521f66d1106e69ca967c2a60af10b3c486 Author: Will Thompson Date: 2011-11-11 19:27:50 +0000 Update Wocky snapshot This new snapshot allows Gabble (specifically, our plugin) to register match-all stanza handlers, and also adds the stanza currently being sent as an argument to the WockyPorter::sending signal. commit 9f3e22edb1b13ff37b124a5fe3a5e9a1522dbebb Author: Will Thompson Date: 2011-09-21 18:07:09 +0100 xmpp-console: add some better error reporting commit f59235111739cde7b746de4b93d9090f039dc494 Author: Will Thompson Date: 2011-09-21 17:38:54 +0100 xmpp-console: Readd a crucial feature from 0.12.5. commit 4fd5fd804b45b3b1c8f8de5a71210099663d688c Author: Will Thompson Date: 2011-09-21 16:49:39 +0100 xmpp-console: show a spinner while waiting for a reply commit c63263f038c938839e5a3ec5e4809955b877a381 Author: Will Thompson Date: 2011-09-21 16:28:13 +0100 xmpp-console: add radio buttons for get vs. set commit 00e1ca483ad07ddcd2a059130e3e186ae12fbd63 Author: Will Thompson Date: 2011-09-21 16:17:12 +0100 xmpp-console: split up entry-adding function I'd like to use a combobox or something for the get/set selection. commit 880edbed11b23262afed535ce51dc3e36ebf3698 Author: Will Thompson Date: 2011-09-21 10:43:59 +0100 Add XMPP console UI It is only installed if the plugin is installed. commit 59fbf7d5e24859595d230f6249c30370f09a6281 Author: Will Thompson Date: 2011-09-21 10:43:50 +0100 test plugin: include version number commit 98ab5aa93c9574187bf47ff7bad10f7a1fe2b935 Author: Will Thompson Date: 2011-06-22 14:33:52 +0100 Add an XMPP console sidecar It currently only supports sending an arbitrary IQ and getting the reply. Obviously this is for developer use only. commit cbe05e97476160fab38ba491717f4d5a42c17887 Merge: 7c77196 2f1c385 Author: Andre Moreira Magalhaes (andrunko) Date: 2011-11-17 14:46:33 -0200 Merge branch 'trivia' commit 2f1c3854ed8860caeb8410d4a9c0882124641e10 Author: Andre Moreira Magalhaes (andrunko) Date: 2011-11-17 14:18:18 -0200 Remove gabble_decode_jid in favour of wocky_decode_jid. commit 7c77196d24c77a31bb51928ee3875f1161b9fd08 Author: Xavier Claessens Date: 2011-11-16 15:21:04 +0100 Add coding style check for g_hash_table_destroy and g_array_free usage commit 769a97c2ed9e6cbfda656a65483f6142423adc36 Author: Xavier Claessens Date: 2011-11-16 14:39:30 +0100 Use _unref instead of _free _destroy when possible.unref Replace g_(ptr_)array_free (foo, TRUE) and g_hash_table_destroy with respectively g_(ptr_)array_unref (foo) and g_hash_table_unref. I used this command to generate this patch: for f in `find -name "*.c"`; do sed -i $f -re 's/g_ptr_array_free \(([^ ,]+), TRUE\)/g_ptr_array_unref \(\1\)/'; done See Danielle's blog for explanation of possible bug _free can do: http://blogs.gnome.org/danni/2011/11/16/mistakes-with-g_value_set_boxed/ commit dfccd984ac6c694a689c1f224efb4f4e2b27e01c Author: Jonny Lamb Date: 2011-11-16 11:37:13 +0000 bump nano-version Signed-off-by: Jonny Lamb commit 241356cdbd74bca44e8c0c9c583a2d1f4daf903a Author: Jonny Lamb Date: 2011-11-16 10:07:32 +0000 version 0.15.0 Signed-off-by: Jonny Lamb commit 65c8b8f3edb446e3a0411a7401d9e3947ba858db Author: Jonny Lamb Date: 2011-11-16 11:24:10 +0000 jingle outgoing test: add MembersChangedDetailed flag to base flags telepathy-glib ≥ 0.16.2 always sets the Members_Changed_Detailed flag in the group mixin, so now that we depend on tp-glib ≥ 0.17.1 we should expect that too. Signed-off-by: Jonny Lamb commit 9f8e0db4ba039eb78d055a0c04bb8fb283c89751 Author: Jonny Lamb Date: 2011-11-16 10:01:14 +0000 NEWS: further updates about Wocky Signed-off-by: Jonny Lamb commit 1e61c2ef8d352294f369eebe3493f135c7dca16c Author: Jonny Lamb Date: 2011-11-16 09:48:34 +0000 NEWS: update Signed-off-by: Jonny Lamb commit 65628acf0d363f0868d643cffcb01b950d6dcf5f Author: Jonny Lamb Date: 2011-11-16 08:44:02 +0000 file transfer helper: use crazier stock metadata in file transfer tests Signed-off-by: Jonny Lamb commit 78af31f043327c194ed4db4c2db2d8b04d57f07f Merge: f903c62 80ffa6e Author: Jonny Lamb Date: 2011-11-16 08:07:09 +0000 Merge branch 'ft-metadata' Conflicts: src/namespaces.h tests/twisted/Makefile.am Signed-off-by: Jonny Lamb commit f903c62e4de60610638931cc14ed3118de2d37c6 Merge: 9309cb4 566c0f7 Author: Will Thompson Date: 2011-11-15 15:34:41 +0000 Merge branch 'telepathy-gabble-0.14' commit 566c0f72fe54d44752c3704537e42c3e3640488c Merge: ebcb135 7524ce7 Author: Will Thompson Date: 2011-11-15 15:34:27 +0000 Merge branch 'telepathy-gabble-0.12' into telepathy-gabble-0.14 commit 7524ce77ef3989418d2e8b0a2af47e472c7c982d Merge: 9cf653e 6cb2b5d Author: Will Thompson Date: 2011-11-15 15:34:11 +0000 Merge branch 'local-pending-flicker2' into telepathy-gabble-0.12 commit 6cb2b5d0f9e4a164537e3861f787e603d02bb8e4 Author: Cosimo Alfarano Date: 2011-11-02 13:24:09 +0000 add case for "cancel subscription" to test_local_pending commit 31c2cd55912c72c82cba28202f220a80c8b4faca Author: Cosimo Alfarano Date: 2011-11-02 13:20:15 +0000 expect MembersChanged AND ContactsChanged on sub-request We should explicitly expect the relevant state transition on the publish channel; and when forbidding events, we just forbid them from occurring on the publish channel. (fix by Will Thompson, thanks Will) commit 284779859a67e2dfceb4d9930df81e452d5a4b3a Author: Cosimo Alfarano Date: 2011-11-02 11:39:46 +0000 fix comment: Alice is just requesting subscription commit 273654bfb07e5ff593b162dd455f739f697b71b8 Author: Cosimo Alfarano Date: 2011-11-02 11:39:21 +0000 Remove useless symbol in test_local_pending commit 8fcf944c1afbcd98a5cbf582e33d88d877a6980f Author: Marco Barisione Date: 2011-08-31 17:31:24 +0100 Roster: don't hide local pending contacts from stored commit 9309cb456645ff7643ebe36c50e3b447641b6c6b Author: Will Thompson Date: 2011-11-15 12:13:46 +0000 Fully expunge HANDLE_LEAK_DEBUG_CFLAGS Sorry, internet. commit 36a50290e5d9324cf55ed7a53cb9c221d7386d95 Author: Will Thompson Date: 2011-11-15 11:34:42 +0000 configure: remove --enable-handle-leak-debug. There's no such thing as a handle leak any more, so this is vestigial. Reviewed-by: Jonny Lamb commit 5b3105a5a357cdd1f1d098fcc3e2c26a40e5b93f Author: Jonny Lamb Date: 2011-11-15 10:12:55 +0000 wocky: update snapshot for sasl fix again Signed-off-by: Jonny Lamb commit 30ff2ae153dfa5b45072df344d129d909429a63e Author: Jonny Lamb Date: 2011-11-15 09:16:56 +0000 wocky: update snapshot to fix sasl build failure Signed-off-by: Jonny Lamb commit 80ffa6e731f2ecb5c31bbaec1083d13e6757d912 Author: Jonny Lamb Date: 2011-11-15 09:16:05 +0000 ft metadata: use tp-glib generated code rather than using the draft interface Signed-off-by: Jonny Lamb commit 13835c53f6e7b0cf1d576ca8ae031282494ed473 Author: Jonny Lamb Date: 2011-11-15 09:15:36 +0000 configure: up the tp-glib dependency Signed-off-by: Jonny Lamb commit 275667c68ed51ffd4dc988c5916a11e85401f4ac Author: Jonny Lamb Date: 2011-11-11 11:11:14 +0000 metadata test: test for odd metadata with a key but no values Signed-off-by: Jonny Lamb commit 631d3beed4585d9c2b1530ab4c9657907234c9fe Author: Jonny Lamb Date: 2011-11-11 11:10:51 +0000 caps-helper: deal with no value nodes in data forms Signed-off-by: Jonny Lamb commit 798edc37548cbcea3c4c16edbe65aa62f3ba3ff4 Merge: 585f946 ebcb135 Author: Will Thompson Date: 2011-11-10 17:09:40 +0000 Merge branch 'telepathy-gabble-0.14' Conflicts: NEWS configure.ac commit ebcb1350c3b316f8fe510caacacf0b2a59b95458 Author: Will Thompson Date: 2011-11-10 16:40:09 +0000 conn-olpc: handle activities with NULL ids. Other places in the code seem to handle GabbleOlpcActivity.id being NULL; and is a crash on this strcmp. There are no logs, and the first argument to strcmp is not on record, so this suggests to me that this might be the cause! commit 5677e158d6518e53bc06c131e3b2c8b9c779a74a Author: Alban Crequy Date: 2011-11-09 17:46:04 +0000 Remove double definition gabbletestsdir is defined in configure.ac https://bugs.freedesktop.org/show_bug.cgi?id=42679 commit c5afb4dc7f6ea3a55ca35bbd04e16ce87e57cacc Author: Jonny Lamb Date: 2011-11-09 19:34:03 +0000 metadata: update to new draft Signed-off-by: Jonny Lamb commit a1f943c809232021db6d831f0e9869bb0584dcfc Author: Jonny Lamb Date: 2011-11-09 18:07:37 +0000 extensions: update FT.Metadata draft Signed-off-by: Jonny Lamb commit 585f9467b8916b5d3ebc0e06c00019b916099672 Author: Alban Crequy Date: 2011-11-09 17:46:04 +0000 Remove double definition gabbletestsdir is defined in configure.ac https://bugs.freedesktop.org/show_bug.cgi?id=42679 commit dd7cbb323b1ed31f34f2babff8d066f7e88bc3c9 Author: Jonny Lamb Date: 2011-11-09 17:33:55 +0000 namespaces: update metadata namespaces Signed-off-by: Jonny Lamb commit bdf5c1535079d7f69e8b0d50def2bf0acd2a7d1a Author: Jonny Lamb Date: 2011-11-09 08:20:31 +0000 connection: fix typo when indexing pointer array using wrong counter D'oh! Fixes: fd.o#42706 Signed-off-by: Jonny Lamb commit b4b5aa0be69a171615801c13626f5221e8b1cb6c Author: Jonny Lamb Date: 2011-11-09 08:20:31 +0000 connection: fix typo when indexing pointer array using wrong counter D'oh! Fixes: fd.o#42706 Signed-off-by: Jonny Lamb commit e5d6c92c9ff74fdba49d751ed1f7b5d87bf17336 Author: Will Thompson Date: 2011-11-07 20:34:38 +0000 lay the foundations for 0.15.x commit ed197893f4e303276480649330b9c362c60f6442 Author: Will Thompson Date: 2011-11-07 20:34:04 +0000 Nano-version bump to 0.14.0.1 commit 2c134e18d24d1daebba6963f73c1bff9b960b46c Author: Will Thompson Date: 2011-11-07 19:59:19 +0000 Version 0.14.0 commit 152c12a2e393e6c135a975df847f55990fc8d0f5 Author: Will Thompson Date: 2011-11-07 19:58:40 +0000 Hey, let's call this 0.14.0 commit 0f86ad63a641a780b0e812725f7a7bd24d57a467 Author: Will Thompson Date: 2011-11-07 19:20:28 +0000 NEWS for 0.13.8 commit df3882d34528ccb320835fafbad74034a589dc64 Merge: 6ef3ca9 6895340 Author: Will Thompson Date: 2011-11-07 17:46:04 +0000 Merge branch 'facebook-own-message' Reviewed-by: Jonny Lamb commit 6895340badaff9f97683230848ae8e0f9f6f99d8 Author: Will Thompson Date: 2011-11-04 08:58:45 +0000 ImFactory: handle Facebook's own-message extension Facebook's XMPP server sends us IQs containing messages which we send to contacts, whether on this connection or via another connection (such as the web interface). We can expose these as Delivery_Status_Accepted delivery reports, allowing UIs to log them and show them up or whatever. This could help keep things coherent if the user hops between chatting in the browser and/or Empathy, say. https://bugs.freedesktop.org/show_bug.cgi?id=41417 commit 6ef3ca940f0b4a4ae1aedb2c03ddc794fe659582 Author: Danielle Madeley Date: 2011-11-07 22:18:17 +1100 Use commit ids that actually exist for Wocky What happened here was that git-bz was rewriting my commit ids when I submitted the patch to bugzilla for review, but my Gabble was still pointing to the original commit I had made. SURPRISE! commit e61dc0318e7a7c00c268105599ea3eada2c1491b Author: Danielle Madeley Date: 2011-11-07 21:45:02 +1100 Return the appropriate value in the case of an unknown timestamp This updates Gabble for the latest spec. Fixes: https://bugs.freedesktop.org/show_bug.cgi?id=42652 commit 465b3aef2330ae73c2f643eeec4676da657c2222 Merge: ed09e89 a04b9e5 Author: Danielle Madeley Date: 2011-11-07 20:54:44 +1100 Merge branch 'wocky-muc-gdatetime' commit 4312e1f6789227e658ee55349b91b0980128ab51 Author: Will Thompson Date: 2011-11-04 16:21:04 +0000 IMChannel: reindent receiving/delivery report code This patch just gets rid of the funky formatting mentioned in an earlier patch; there are no code changes. commit 574d1468432bd066dfe7a50fed8c2bbae5d71975 Author: Will Thompson Date: 2011-11-04 12:22:09 +0000 IMChannel: split incoming message and report handling As a nice side effect, the new _gabble_im_channel_report_delivery() function can do successful delivery reports if send_error == GABBLE_TEXT_CHANNEL_SEND_NO_ERROR. This will become useful momentarily... The funky formatting is to make clearer that this patch is mostly moving code around unmodified; a subsequent patch will clean it up. commit 46da670d5c0fa51b92a168d454d23e9b119e349a Author: Will Thompson Date: 2011-11-04 12:11:42 +0000 im_channel_receive: split out building basic message This is about the only part of this function which is shared between the delivery report code path and the normal message code path. commit deb5a15cb02344891f5871cd5f8dcf8faa464d4a Author: Will Thompson Date: 2011-10-11 17:01:13 +0100 ImFactory: refactor creating unrequested channels We'll need similar logic for handling incoming facebook:own-message notifications. commit 214c0b08245fc264985e4504309abb7b7e82f68c Author: Will Thompson Date: 2011-11-03 23:50:24 +0000 exec-with-log.sh.in: set WOCKY_DEBUG. I'm delighted that 3a1fe026 made us compatible with hypothetical shells, but it also reduced the amount of debug output in gabble-testing.log. commit b34a0207287a3976e1b880c5b958b9215c5930ad Author: Will Thompson Date: 2011-11-03 23:27:24 +0000 im_channel_receive: remove redundant 'sender' parameter If we're passing an incoming message to a 1-1 IM channel, we don't need to tell the channel who sent the message: it knows perfectly well. In fact, we were telling it twice: once in handle form (which is redundant), and once in full JID form (which is more useful: it lets the channel update its resource binding). commit 9b81a8da064954b714474208d2fb4e7d5cb2d6b4 Author: Will Thompson Date: 2011-10-11 15:54:47 +0100 ImFactory new_im_channel: remove redundant typecheck By the time we get this far we can be sure that the factory really is a factory. commit 54104dac1cbfa6a13e8aba199a2043eb061e9a87 Author: Will Thompson Date: 2011-10-11 15:53:26 +0100 ImFactory: remove GET_PRIV anti-idiom commit 34b600adcb8894615fc2b40400ce6e740230f7c2 Author: Will Thompson Date: 2011-10-11 15:50:24 +0100 IMChannel: implement get_object_path_suffix I think this is simpler than the factory passing the path in. Though it's actually longer, which offends me on some level. commit b3d5848804b8128b0962f4860efcd758e19d7fc3 Author: Will Thompson Date: 2011-10-11 15:42:42 +0100 ImFactory: simplify new_im_channel's arguments We can infer from @request_token whether the channel was requested locally or is being created in response to a incoming event; hence, we don't need a separate @initiator argument. commit 7a9200ca2a426f4f794a4fd43291bcd511a9420f Author: Will Thompson Date: 2011-10-11 15:34:57 +0100 ImFactory: document new_im_channel more usefully commit 33cd9527f7c0d1f4a2b72e8cfadc417b69590336 Author: Will Thompson Date: 2011-10-04 01:15:32 +0100 Remove unneeded im_channel_closed_cb prototype commit eb0dc2a0694451453c40c0e75da3ba9093837339 Author: Will Thompson Date: 2011-10-04 00:24:40 +0100 Document _gabble_im_channel_receive usefully commit 933af070afdc7a3a1df333c318743e0ba115f3a5 Author: Will Thompson Date: 2011-10-04 00:20:14 +0100 Remove redundant CHECK_STR_EMPTY macro commit 2941264d571f3bd23739f7bbdbb350c53355398b Author: Will Thompson Date: 2011-10-04 00:08:02 +0100 IMFactory: use constructed, not constructor commit aa4b8472a09fafd5bc8046c01f2163d6d7e4495c Author: Will Thompson Date: 2011-10-04 00:05:22 +0100 Remove obsolete DBUS_API_SUBJECT_TO_CHANGE defines commit d3607abee80d237ea1d00aca55e14eadc7ff5bb4 Author: Will Thompson Date: 2011-05-30 12:16:16 +0100 muc-factory: use a dispatch table for handling requests commit 517cce66448836d6f3de0eef34b9c36d7a4a2bf9 Author: Will Thompson Date: 2011-05-30 11:45:40 +0100 muc-factory: use fully-qualified property constants commit 45b54e41a49f60b4e0c620546492a91e0c1b1b4c Author: Will Thompson Date: 2011-05-27 12:09:52 +0100 MucFactory: remove stupid priv anti-idiom commit 616ccbaf5f8b04e1ab1f74f0441490731c1044eb Author: Jonny Lamb Date: 2011-10-31 17:09:39 +0000 ft-client-caps test: change client name I've no idea why but if caps/tube-caps.py is run before this test (it has the same idea and design as this test was copied from it) then this one fails due to the same client name?! Another day... Signed-off-by: Jonny Lamb commit ed09e896b3c249568192e92b5195462e626918fb Merge: 08da102 f5f7cc6 Author: Andre Moreira Magalhaes (andrunko) Date: 2011-10-31 14:05:12 -0200 Merge branch 'memory-leaks' Reviewed-by: Will Thompson commit f5f7cc6b3407af00eb594aca4c2ffc797f987846 Author: Andre Moreira Magalhaes (andrunko) Date: 2011-10-31 13:55:18 -0200 roster: Do not leak the roster stanza. commit 1d61ece67ffac336e4beb430cb1da6e2b4f971cb Author: Andre Moreira Magalhaes (andrunko) Date: 2011-10-31 13:54:40 -0200 connection: Remove unnecessary extra copy of caps when updating capabilities. commit cb97cf5832904d9ce05c93318f43d8b83fb5aa37 Author: Andre Moreira Magalhaes (andrunko) Date: 2011-10-31 13:48:15 -0200 conn-presence: Properly free priv member. commit 107d1867af1cd827cf41cc202b0f91bace124f20 Author: Jonny Lamb Date: 2011-10-31 14:51:35 +0000 ft-client-caps test: clarify caps dict building Signed-off-by: Jonny Lamb commit 8ecdc16893fc2208233252dd5054b88925392601 Author: Jonny Lamb Date: 2011-10-31 14:30:39 +0000 ft-client-caps: remove duplication by using caps_helper.presence_and_disco Signed-off-by: Jonny Lamb commit c5ea43c6c5bac3cc29695df4f2c2889775eb94a4 Author: Jonny Lamb Date: 2011-10-31 13:45:25 +0000 ft-manager: use nicer tp_g_value_slice_new_* functions Signed-off-by: Jonny Lamb commit 70d83e788df4acd2218447696e5517b27b416530 Author: Jonny Lamb Date: 2011-10-31 13:43:51 +0000 ft-manager: clarify argument name Signed-off-by: Jonny Lamb commit 534678998700786a5bc5c6e66e863fc64e353ee6 Author: Jonny Lamb Date: 2011-10-31 13:43:01 +0000 twisted tests: use conn.Requests isntead of making a dbus.Interface Signed-off-by: Jonny Lamb commit 5cdf63160451cb2142eb1915eb369d9c248bd80b Author: Jonny Lamb Date: 2011-10-31 13:40:31 +0000 ft-channel: simplify adding fields to dataform Signed-off-by: Jonny Lamb commit 56086a17c480737973cc28e6c82b7b98d8eb9f6e Author: Jonny Lamb Date: 2011-10-31 13:29:07 +0000 namespaces: change FT metadata namespace "and… im: is not a URI scheme. http://telepathy.im/..." Signed-off-by: Jonny Lamb commit 7560587e9a14e13b1cb6f1ce2b0c5451bfc67c65 Author: Jonny Lamb Date: 2011-10-31 13:01:21 +0000 ft-manager: deal with bad ServiceName fields Signed-off-by: Jonny Lamb commit 08da102662f74cf084e4d2bdfc86f2b15e33ca66 Merge: f9529b7 9cf653e Author: Will Thompson Date: 2011-10-31 09:38:35 +0000 Merge branch 'telepathy-gabble-0.12' commit 9cf653e93ba9beb8ff2df73627cd2b1f5bedf6bc Author: Will Thompson Date: 2011-10-28 18:34:25 +0100 roster: ignore multiple replies to roster query. The XMPP server running on vk.com is buggy, and replies to our roster query twice. Gabble just blindly assumed that any with a roster in it was the singular reply to our singular query. This is a pretty reasonable assumption but this buggy server violates it, and Gabble calls tp_base_contact_list_set_list_received() more than once, which triggers a critical in that function. A more invasive fix would use wocky_porter_send_iq_async() rather than scraping all the roster IQs out of everything using the LM API, but I wanted to quickly fix a bug on a Friday afternoon. Fixes: Reviewed-by: Jonny Lamb commit abbfb9b9fd90cb9f279449a9b43e3bff66167d87 Author: Jonny Lamb Date: 2011-10-28 18:54:57 +0100 ft metadata test: fix subclass of file receiving test Oops, this was subclassing the wrong FT helper test class. No other changes necessary (like a boss). Signed-off-by: Jonny Lamb commit 735bea1282b3299030869d41920d7dbaabaaddce Author: Jonny Lamb Date: 2011-10-27 17:23:57 +0100 capabilities: only add metadata cap when we have normal FT caps Signed-off-by: Jonny Lamb commit f9529b7d27b0cc9edc9b7c39573c533963d87079 Author: Alban Crequy Date: 2011-10-21 15:20:03 +0100 Update .gitignore with installed tests commit f4b4768caabf44f0c2955e97bf8c8af592ff23b1 Author: Alban Crequy Date: 2011-10-12 18:45:34 +0100 Install the test plugin for the installed tests commit 49925bb583375e19b2d0fa0aeda7902f79fca370 Author: Alban Crequy Date: 2011-09-29 16:55:06 -0400 tests: add --enable-installed-tests This configure option makes it possible to install the twisted tests in ${libdir}/telepathy-gabble-tests and run them without the sources. The dbus configuration is duplicated in tests/twisted/servicedir/ for the installed tests and tests/twisted/servicedir-uninstalled/ for the uninstalled tests. commit 3a1fe026d8a43f9bad8c6820c73310245929fb4c Author: Alban Crequy Date: 2011-10-21 15:36:18 +0100 exec-with-log.sh.in: POSIX shells, avoid "export var=foo" commit eec2603bd4f8644cc165ce5c8ce42d21c4c73f66 Author: Alban Crequy Date: 2011-10-21 15:11:03 +0100 Use autoconf's $(MKDIR_P) commit fa01616db7028029832cb6da6fa898dac0605a82 Author: Alban Crequy Date: 2011-10-21 14:19:01 +0100 tests/twisted/Makefile.am: Move additional tests in the main list commit b1d83dcd017bac58e00461ed5cc2b1c967d426fe Author: Alban Crequy Date: 2011-10-21 13:54:06 +0100 tests/twisted/Makefile.am: order lists in alphabetic order commit ade61fe2442ee0787c253b9aff7848216427a71f Author: Jonny Lamb Date: 2011-10-26 18:55:45 +0100 ft tests: add tests for FT service name caps Signed-off-by: Jonny Lamb commit 3107077eaafd32548273e79363fa3dce4ddc3d25 Author: Jonny Lamb Date: 2011-10-26 18:26:25 +0100 ft-manager: advertise file transfer services with capabilities Signed-off-by: Jonny Lamb commit d92500bcd3911dbddad5fff2d40cfbc674663b41 Author: Guillaume Desmottes Date: 2011-10-26 16:19:58 +0200 actually implement ContactBlocking interface https://bugs.freedesktop.org/show_bug.cgi?id=42281 commit a8f305d39ee09748ac258ce577dd9738278f9561 Author: Guillaume Desmottes Date: 2011-10-26 15:50:31 +0200 Advertise ContactBlocking if supported https://bugs.freedesktop.org/show_bug.cgi?id=42281 commit 16060fa8422c7ba530461732821064c3576136c8 Author: Jonny Lamb Date: 2011-10-26 13:10:15 +0100 ft-channel: fail to create channel if the contact doesn't have the right caps Signed-off-by: Jonny Lamb commit bd6cb214613a95dc5870ac256fd95349576f5fce Author: Jonny Lamb Date: 2011-10-26 11:50:34 +0100 ft-manager: only advertise Metadata support for those who support it Signed-off-by: Jonny Lamb commit 583cb3ae5b8538fa24a882462a453d5865587352 Author: Jonny Lamb Date: 2011-10-26 11:48:11 +0100 capabilities: advertise support for the TP FT metadata extension Signed-off-by: Jonny Lamb commit 08599349b9681f588826393b4c8c2debe228a50b Author: Jonny Lamb Date: 2011-10-26 11:05:58 +0100 metadata test: ensure we get an error with a bad Metadata property Signed-off-by: Jonny Lamb commit b4f1e9b52846826faf4b843cf4c3bdd00f75150c Author: Jonny Lamb Date: 2011-10-26 09:09:40 +0100 metadata test: test receiving an offer with no dataforms Signed-off-by: Jonny Lamb commit 82e7b8a0f7461665e58381e7690e3c0f2ebeacc0 Author: Jonny Lamb Date: 2011-10-26 09:09:33 +0100 ft-helper: test parsing incoming offer metadata Signed-off-by: Jonny Lamb commit fcf41a7e505a2d38cba5a40e82150bb03eb8d9a1 Author: Jonny Lamb Date: 2011-10-26 09:08:00 +0100 caps-helper: split out function to add data forms to a node Signed-off-by: Jonny Lamb commit 396e49c5492c8142cfc3fc45054eee0bbba36e23 Author: Jonny Lamb Date: 2011-10-26 08:48:31 +0100 file-transfer tests: add simple Metadata test Signed-off-by: Jonny Lamb commit 52e76fbe1a5348eccdda726133e2705a1f78f44d Author: Jonny Lamb Date: 2011-10-26 08:46:54 +0100 ft-channel: include metadata in file transfer offer Signed-off-by: Jonny Lamb commit c2f0b06cb87a62ca9e15bb9bfa870a9854e0117e Author: Jonny Lamb Date: 2011-10-26 08:44:40 +0100 caps-helper: allow extract_data_forms to be called with no x nodes Signed-off-by: Jonny Lamb commit b9f1bc49edd64959ff0ea57f216672d3067b1118 Author: Jonny Lamb Date: 2011-10-26 08:35:16 +0100 caps-helper: move data form parsing code into its own function Signed-off-by: Jonny Lamb commit 2933e974314887c283276c904832c69d470849b5 Author: Jonny Lamb Date: 2011-10-26 08:33:00 +0100 ft-manager: disallow FORM_TYPE keys in Metadata property Signed-off-by: Jonny Lamb commit 30899a8616c660cc0852af003a4ad40609a5b52d Author: Jonny Lamb Date: 2011-10-26 08:30:25 +0100 ft-channel: make Metadata props immutable and test them Signed-off-by: Jonny Lamb commit 7a1da4c951c3bda7793f4511e3b470e7226d8108 Author: Jonny Lamb Date: 2011-10-25 21:13:09 +0100 namespaces: define NSs for Metadata dataforms Signed-off-by: Jonny Lamb commit 5472db8eb4a59c6261a36ddcea407489d488c3eb Author: Jonny Lamb Date: 2011-10-25 21:05:04 +0100 ft-manager: get Metadata props from SI file transfer offer Signed-off-by: Jonny Lamb commit 64c9d82af61b25ab06bd74607acf0f336c6c463a Author: Jonny Lamb Date: 2011-10-25 21:04:33 +0100 ft-manager: set Metadata props from channel request Signed-off-by: Jonny Lamb commit ab38d52b84ddd80f356a3d804eeae0aeb99b1520 Author: Jonny Lamb Date: 2011-10-25 21:03:03 +0100 ft-channel: set Metadata props on construction Signed-off-by: Jonny Lamb commit 50e3c855268d9e43f0a2853cc71757f5e88b1ab3 Author: Jonny Lamb Date: 2011-10-25 21:00:28 +0100 ft-manager: add Metadata props to Allowed_Properties Signed-off-by: Jonny Lamb commit a5fd662a8f3a70eccef1a3d943f060513d2658f3 Author: Jonny Lamb Date: 2011-10-25 20:58:42 +0100 ft-channel: implement Metadata properties Signed-off-by: Jonny Lamb commit d903da03658e26e574ed6d8ffdd58cc20023857b Author: Jonny Lamb Date: 2011-10-25 20:57:16 +0100 extensions: add FileTransfer.Metadata draft Signed-off-by: Jonny Lamb commit e1973c15669a6043cd474cb48b89baafc4bb0d45 Merge: bf1c8dd 3cdbae8 Author: Will Thompson Date: 2011-10-24 11:15:41 +0100 Merge branch '41743-show-contacts-as-offline-even-after-a-really-early-message' Reviewed-by: Jonny Lamb commit 3cdbae82cb09dd8218e1c783b7bad1f84b84498e Author: Will Thompson Date: 2011-10-13 11:33:08 +0100 Fix offline contacts showing up as unknown, not offline Due to a weird interaction between the presence cache, IM channels, and scraping nicknames out of s, receiving a message from an offline contact before the roster is received would cause their status to erroneously show up as unknown, not offline. This fix is a bit of a hack, but it is much smaller than refactoring to make the IM channel store the alias (which would allow us to expunge keep_unavailable). This regressed as a side-effect of e0cda61. Fixes: commit 3b79e21684aa31b568aeb3969d034e772ac9e610 Author: Will Thompson Date: 2011-10-14 12:00:50 +0100 Test grabbing from If a contact is not on your roster, you typically have no idea what their nickname is: no roster, no PEP, no vCard (assuming the server doesn't let random people fetch your vCard). In this situation, contacts who message you out of the blue can include in the message itself. This is implemented in a kind of dodgy way in Gabble at the moment: the IM channel forcibly retains an entry for the contact in the presence cache, and then the presence cache stashes the nickname as if it came from presence… It was also previously untested, so I thought it worth adding a test before I even thought about fixing how it's implemented. commit a04b9e505e6a5baec65fbb52522f1b52656ca72e Author: Danielle Madeley Date: 2011-10-18 13:47:55 +1100 [muc-channel] WockyMuc now signals timestamps as GDateTime instead of time_t Fixes assumptions in the code where timestamp was assumed to be gint64. commit bf1c8dd299d02ad433af2c31c879601d712e8d3b Author: Danielle Madeley Date: 2011-10-17 23:04:00 +1100 [muc-channel] timestamp is a time_t not a gint64 Cast to the correct type so that Gabble compiles correctly on 32-bit. I propose fixing this properly by using either gint64 or GDateTime in Wocky, but not tonight. commit 009bec00c4d83f401179fd7fb442158875238a34 Author: Will Thompson Date: 2011-10-12 17:46:33 +0100 Update Wocky snapshot for glib/gtypes.h fix. commit fca53f10536c4e758c24227c956296534ce2a4a3 Author: Cosimo Cecchi Date: 2011-10-12 12:33:33 -0400 test-resolver: don't include glib/types.h directly Use the glib.h header instead. commit f065fb6165f18441b00cbfc263acd50c3eb05b38 Author: Will Thompson Date: 2011-10-12 16:20:54 +0100 nano version bump to 0.13.8.1 •_________________________• commit 9f1ddd0ae7edf1686dedb7c12d373c99b7444221 Author: Will Thompson Date: 2011-10-12 15:55:33 +0100 Version 0.13.7 commit 51f01ab2f802ba5f40a10f5cab207d73d9914dcd Author: Will Thompson Date: 2011-10-12 15:51:16 +0100 Truncate generated ChangeLog at version 0.12.0 It used to be 2.4M, which is almost as big as the entire contents of the 'src' directory! Truncating it trims it down to 68K. commit b109b58411387c2139704f13923453bbeaf15a21 Author: Will Thompson Date: 2011-10-12 15:28:08 +0100 NEWS for 0.13.7 commit 3f1091e0f6b576f266472fbf96d271332e7686ad Author: Xavier Claessens Date: 2011-10-01 20:33:17 +0200 Do not assume we can authenticate with a password If Wocky does not understand any of the authentication mechanisms supported by the server, then we cannot authenticate with just a password; hence, we should not claim to support the X-TELEPATHY-PASSWORD pseudo-mechanism. (This is the case for the Windows Live Messenger XMPP server, for example.) commit 9f64fd21ede78e42396c93c81892738011045404 Merge: e435637 aff2d84 Author: Will Thompson Date: 2011-10-12 12:21:28 +0100 Merge branch 'moar-room' Fixes: commit aff2d8453d577020658ce47d06f07864f6194b1d Author: Will Thompson Date: 2011-09-15 18:40:40 +0100 MUC: ref properties_being_updated commit 12e4a9ac245617b43f3fbd3e69ad55ddb1a88fcd Author: Will Thompson Date: 2011-09-12 18:03:08 +0100 MUC: add more error-checking to SetSubject commit d9160a13c236bcf8a1b789c70dee5497ca17ee4b Author: Will Thompson Date: 2011-09-06 17:48:29 +0100 muc/subject: fix obsolete FIXME about discoing MUCs fd.o#21152 was fixed in 2009. We do have to make the test echo back our unavailable presence if we want to re-use the JID, but that's fine, we can do that. commit 92810af8b77c45981ff736050667161085abb099 Author: Will Thompson Date: 2011-09-06 14:35:45 +0100 MUC: update for RoomConfig.update_async API change I decided it was clearer for TpBaseRoomConfigClass.update_async to take a TpBaseRoomConfig as its first argument, rather than a TpBaseChannel. This makes it reasonable to implement the update_async vfunc in GabbleRoomConfig! Yay, no more monkey-patching. I think it would be even better to move all the configuration-frobbing code to GabbleRoomConfig. But this branch is already plenty long enough. commit 4cd04e5366889a5cf8be84d9b096023cba999598 Author: Will Thompson Date: 2011-09-05 19:51:07 +0100 RoomConfig: move the base class to tp-glib commit 8d298302188f33ab9336c186b3e03b458d56d629 Author: Will Thompson Date: 2011-09-05 18:29:35 +0100 muc/room-config: test disconnecting with requests in flight commit 9efe5f730a4ba61f8b28b0350d4b567069e7a7c7 Author: Will Thompson Date: 2011-09-05 16:19:05 +0100 muc/room-config: expand test coverage This tests one more field of room configuration, and also checks the transitions to and from being a room owner. commit 1b3e6d52e2f2969ab944460fb5c7e2b8350ec416 Author: Will Thompson Date: 2011-09-05 15:21:22 +0100 muc/room-config.py: refactor form building/parsing commit 983d327aa1731781d74334716adcf5baaac15f80 Author: Will Thompson Date: 2011-09-05 14:47:56 +0100 muc/subject.py: fix disco reply should be a child of the element, not of the element. commit b620985fb445fc7d54a90080232531248f7899dc Author: Will Thompson Date: 2011-09-02 14:27:32 +0100 MUC: emit ConfigurationRetrieved commit dff8ddbc721ef5791bb089ed9aed19b918553a4d Author: Will Thompson Date: 2011-09-02 14:27:26 +0100 RoomConfig: implement ConfigurationRetrieved commit 5ef367af72b53e4c7d115be037139410f2c03a3e Author: Will Thompson Date: 2011-09-02 11:30:49 +0100 subject: test for PropertiesChanged Removing the Telepathy.Properties interface from GabbleMucChannel miraculously makes this work. \o/ commit 675367323b8617b88f9f156545e0ea63b21d88e6 Author: Will Thompson Date: 2011-09-02 11:26:02 +0100 Rename muc/test-muc-properties to muc/room-config This more accurately reflects its current nature! commit e3b9601367429f2272949995c71b8fd425c28e93 Author: Will Thompson Date: 2011-09-02 11:16:21 +0100 MUC: emit PropertiesChanged for RoomConfig commit ede43dda5a6fbed9c7a90b735b24f59fc50a1589 Author: Will Thompson Date: 2011-09-02 11:15:19 +0100 RoomConfig: track changed properties With this patch, GabbleRoomConfig tracks which properties have changed, and grows a method to emit PropertiesChanged for those properties. I am not very happy with the macroification of the set_property implementation, but it's the best I could come up with. commit 4c05dc0910cc1d58f71047fa53162d750fc8c6fb Author: Will Thompson Date: 2011-09-01 18:48:04 +0100 RoomConfig: refactor validating property types commit 413157a5b3a66343a7b811a3fd72b516ef86bf2e Author: Will Thompson Date: 2011-09-01 18:17:02 +0100 MUC: remove Telepathy.Properties \o\ /o/ \o\ /o/ Now I can get stuck into implementing change notification. commit 16a480c39631481c17fd788a197f7445a7b1effe Author: Will Thompson Date: 2011-09-06 17:01:22 +0100 muc/test-muc: neaten up interface assertions commit 59ee55354fee1a9425790886ea98f342340e390b Author: Will Thompson Date: 2011-09-01 14:35:59 +0100 MUC: hook up UpdateConfiguration This simultaneously un-hooks-up SetProperties. I could have refactored to make it possible to keep both, but I don't think it would buy us anything since we're just about to delete old properties anyway. commit 3a7eadb2ce77968034832ce7d0f039d15fdb3256 Author: Will Thompson Date: 2011-09-01 13:39:20 +0100 RoomConfig: add a vfunc for updating configuration commit e70dccee1faf596ff8d00613d7651d3ae751c342 Author: Will Thompson Date: 2011-08-30 12:57:09 +0100 MUC: mark appropriate properties as mutable This is a *lot* simpler than in the old days. We no longer have to do something per-property on every permission change, because whether or not we are currently an owner of the MUC has no bearing on which properties would be mutable if we were. commit 26dc9c3df3340bb2662a870301f233274d5fdc47 Author: Will Thompson Date: 2011-08-30 12:32:18 +0100 RoomConfig: track ostensibly-mutable properties This isn't hooked up in MucChannel yet, so we currently report all properties as immutable. It occurs to me that “immutable” means something else elsewhere in Telepathy so we should probably call this something else. commit 4d5b16af21c1dfde2426456409182603b6063ee0 Author: Will Thompson Date: 2011-08-30 11:42:31 +0100 RoomConfig: implement CanUpdateConfiguration commit 757718b7993f17d95184795634519968553ed8eb Author: Will Thompson Date: 2011-08-29 16:39:46 +0100 RoomConfig: hook up reading most fields This doesn't cover emitting PropertiesChanged (impossible until we expunge old-style properties), nor the current room password, nor changing any fields. commit 8d686b421db6a1b7b38ab30b0d3739f768ee3232 Author: Will Thompson Date: 2011-08-29 14:09:47 +0100 Add the start of a RoomConfig object. I'm implementing this as a separate object rather than as part of GabbleMucChannel in the hope of sticking it into tp-glib. commit 54dd251e25734eb097e2a4ba278f1d8ff1dde2e6 Author: Will Thompson Date: 2011-08-30 12:55:08 +0100 test-muc-properties: make clearer assertions The formatting of these assertions was unhelpful, and using assertEquals gives us nicer messages when the assertions do fail. commit 82c09a7a031ff2726d56a6d28c01d7c911ef3c23 Author: Will Thompson Date: 2011-08-30 12:34:08 +0100 MUC: remove half-baked allowinvites support Previously, we allowed the user to configure whether or not occupants should be allowed to invite others to the room, but didn't actually provide any way to retrieve the current setting. This is a pretty useless MUC configuration flag anyway, given that occupants can always send direct (as opposed to mediated) invitations … so I have no qualms about expunging it. commit 59e35681f2762383e05e696d176a21c6c3b81344 Author: Will Thompson Date: 2011-08-30 10:32:44 +0100 MUC: wockify submitting configuration form commit 1edd10efc2c7f16025a2de56b34740b37f849bf4 Author: Will Thompson Date: 2011-08-30 09:56:29 +0100 MUC: fill in config form using a lookup table Again, this is shorter than the huge chain of ifs, and I think it's more readable. commit a9f7e51872bdf63a421cb4c26b6e61a59c4c992f Author: Will Thompson Date: 2011-08-29 17:46:47 +0100 MUC: correctly send default configuration to the MUC Ahem. Previously we were sending it to our own server. Not so useful. commit bd60fe08cb47fd30d5471c08eed8464bf19646a2 Author: Will Thompson Date: 2011-08-29 17:42:14 +0100 MUC: remove faux error handling when accepting default config _gabble_connection_send_with_reply always returns TRUE. I guess the only case this doesn't handle is when the channel (and hence the WockyMuc) has somehow miraculously outlived the connection—I don't think this happens. commit 9e2d76bc981fae2b1c309d11c52a6c369c9d0c57 Author: Will Thompson Date: 2011-08-29 17:20:16 +0100 MUC: wockify one use of muc#owner. We request this and parse the reply in two-and-a-half places … this is the simpler one of the two places where we actually parse the reply. commit 0d63603fe7835b18cc12b9e3d101dd30e700d8fa Author: Will Thompson Date: 2011-08-29 15:39:06 +0100 MUC: refactor handling forms in disco reply This only works because we only handle one field. As and when we handle more, we should switch to WockyDataForm anyway. commit 772c8df4a54228b08f1d58ccd8de7d688a374a47 Author: Will Thompson Date: 2011-08-29 15:18:15 +0100 MUC: simplify feature mapping with a lookup table This is shorter than the previous chain of if-else, and (I believe) much clearer. commit d7e694b31c191bbe9d2dc56922cb171e8ed2dbd8 Author: Will Thompson Date: 2011-08-26 17:27:06 +0100 MUC: remove redundant role and affiliation enums Wocky has these. No need to duplicate them. commit f4b4a4b00011c8f40fc7e245d842e09526095d3c Author: Will Thompson Date: 2011-08-26 17:08:56 +0100 MUC: Remove subject from old Properties commit b06ab5b631006a512d1d811cb306aaa2f3702edf Author: Will Thompson Date: 2011-08-26 16:49:09 +0100 MUC: Wockify parsing Subject-setting errors commit f53172e06c29586a32989b11cf66aa2d28368ad0 Author: Will Thompson Date: 2011-09-06 16:42:08 +0100 MUC: use id='' to catch SetSubject errors Servers don't strictly have to echo the element in their reply if they reject our subject change message. But they do have to preserve the id='' attribute. This patch makes Gabble set an id='' attribute on s sent to change the subject, and look for that ID when it receives a from a MUC. (It continues to look for errors, too.) Prosody is an example of a server that behaves like this. commit 2f4d98398c24b95b1178871f62cf89a7bf467dd8 Author: Will Thompson Date: 2011-08-26 16:38:19 +0100 MUC: return asynchronously from SetSubject commit 23451bbeb0d06f14d5d2726d92b79a1f618baffe Author: Will Thompson Date: 2011-08-26 16:35:05 +0100 MUC: handle errors when setting subject So this hasn't worked since the WockyMUC port. If you tried to set the subject, and got an error back, the SetProperties() call would never terminate (and you wouldn't be able to call it again for the lifetime of the channel). commit 1e94a3c37c1e134dfb0f88bff1cfe4ad5b0628c7 Author: Will Thompson Date: 2011-08-26 16:34:46 +0100 MUC: remove unused argument from _handle_subject commit 2f3df38c90f06698a16fa95d6d4c676c07b28efe Author: Will Thompson Date: 2011-08-26 16:04:25 +0100 muc-channel.h: clean up stale prototypes Most of these no longer exist. Those which do are only used in muc-channel.c, and one of those three is a one-liner which I just inlined at the single point it's called. commit a1463a4f488b62a8891dd3d1e98ffb91f1014f1c Author: Will Thompson Date: 2011-08-03 14:17:03 +0100 Wockify SetSubject implementation commit f40f170f058fd00a46f74fafca761cbbe6affa14 Author: Will Thompson Date: 2011-08-02 18:35:52 +0100 Subject: use tp_svc_dbus_properties_emit_properties_changed commit 5f3afbcb08a3e686ff536dc024aff91a50782549 Author: Will Thompson Date: 2011-08-02 18:22:06 +0100 MUCChannel: simplify password flags implementation We only have one flag, so we may as well just store a boolean. commit c7d28425eefc77826d3235647f0370c8813cd180 Author: Will Thompson Date: 2011-08-02 12:52:55 +0100 Build against Room & Subject from tp-glib telepathy-glib 0.15.8 was technically the first release that had Room and Subject, but 0.15.9 was released 10 minutes later. :) commit 6f2c9640bb568e6672989cee9ea078661afd541e Author: Will Thompson Date: 2011-07-31 23:03:48 +0100 Update to Jonny's last Subject draft I had to comment out the change notification tests because this object implements both Telepathy.Properties and DBus.Properties; both of them have a signal called PropertiesChanged, and dbus-glib does not allow the two to co-exist. commit 082d84aeef688809fe255098bac75cbc40ef30f9 Author: Jonny Lamb Date: 2011-01-04 09:01:45 +0000 muc-{channel,factory}: update to latest draft spec Split the subject stuff from Room into a different interface and rename RoomID to RoomName. Signed-off-by: Jonny Lamb commit 81bbbae26a884771b0cd7be5da742dc5b7a2b4e7 Author: Jonny Lamb Date: 2011-01-04 08:49:52 +0000 extensions: update to latest Room draft and add Chan.I.Subject Signed-off-by: Jonny Lamb commit 5fe7678d2bd0c5af56adc753d7eb97b47b840b1a Author: Jonny Lamb Date: 2011-01-03 17:09:43 +0000 muc-channel: XMPP always supports room subjects Signed-off-by: Jonny Lamb commit 160e92fd1d4987019f63b27d1392e97fb4ec270c Author: Jonny Lamb Date: 2011-01-03 16:51:33 +0000 muc/subject.py: make more assertions when setting the subject Signed-off-by: Jonny Lamb commit 1b934c6f47bdbf5b53f670f9f55794afc65630a9 Author: Jonny Lamb Date: 2011-01-03 16:50:31 +0000 muc/subject.py: replace magic numbers with text Signed-off-by: Jonny Lamb commit 636e252a569e231d3ec1db4eeb2ba49a88938428 Author: Jonny Lamb Date: 2011-01-03 16:48:45 +0000 muc-channel: add a convenience emit_subject_changed method Signed-off-by: Jonny Lamb commit 0d54d8155d2f02d106dab6f45aa58a05f22fa3e2 Author: Jonny Lamb Date: 2011-01-03 12:45:22 +0000 muc/subject.py: test calling SetSubject Signed-off-by: Jonny Lamb commit 9a3b1476583ab2726f40a917924cbdb7164827f2 Author: Jonny Lamb Date: 2011-01-03 12:34:16 +0000 muc-channel: implement SetSubject Signed-off-by: Jonny Lamb commit b4f1b66d4916144c6155653678592318037b0095 Author: Jonny Lamb Date: 2011-01-03 12:33:41 +0000 muc/subject.py: test for subject flags and SubjectChanged signals Signed-off-by: Jonny Lamb commit 72b0a0a4dce061c12cbf406a54059c31beb1a6f7 Author: Jonny Lamb Date: 2011-01-03 12:33:19 +0000 muc-channel: emit SubjectChanged where appropriate Signed-off-by: Jonny Lamb commit e785bb839a8d9e0af6314497a28641c1e2a1c615 Author: Jonny Lamb Date: 2011-01-03 11:40:31 +0000 extensions: update to unmerged Room draft Signed-off-by: Jonny Lamb commit e435637b78212c6604489bf031d8bf0c56d549d5 Author: Mikhail Zabaluev Date: 2011-10-11 20:55:05 +0300 Disable STUN server tests that exercise Google relay, if that is disabled commit 3e3bf4b79322ec5352c21f5e12019decb56ac44f Author: Will Thompson Date: 2011-10-03 14:47:20 +0100 ServerTLSManager: remove useless DEBUG output Reviewed-by: Sjoerd Simons commit a64cd72472d48ad8e88bfebe5e6046f02d4b5c93 Merge: 5f1ad2c 9c86f44 Author: Jonny Lamb Date: 2011-09-28 18:58:10 +0100 Merge branch 'moar-caps' commit 5f1ad2c76f43842856fb01a2dc51648a42f24284 Author: Will Thompson Date: 2011-09-28 18:45:23 +0100 Nano-version bump to 0.13.6.1 commit 9c86f446c6a00142c373aae1fa357f5c00f0f2c6 Author: Jonny Lamb Date: 2011-09-28 18:45:08 +0100 connection: document why data forms might cause warnings to appear Signed-off-by: Jonny Lamb commit 30315eacd21cd322d0b7adaf743fa57b373f6417 Author: Will Thompson Date: 2011-09-28 18:26:55 +0100 Version 0.13.6 commit ca01a7df1a6d7efa06021dba1594ca27380a9129 Author: Will Thompson Date: 2011-09-28 18:20:46 +0100 More NEWS for 0.13.6 commit 7fb0ccab3ce97fabcb2009faaa50f5c3e7d04019 Merge: 6211097 c3b6d21 Author: Will Thompson Date: 2011-09-28 18:10:41 +0100 Merge branch 'telepathy-gabble-0.12' Conflicts: NEWS configure.ac commit c3b6d2101aa5257a6f1ec927f3b0f3b3911cd2a6 Author: Will Thompson Date: 2011-09-28 17:53:39 +0100 nano-version bump! 0.12.7.1 commit 8acd2b0e95fa4b732e1b82eacbc85f0fddf05fee Author: Will Thompson Date: 2011-09-28 17:29:32 +0100 Version 0.12.7 commit c48f3da4949aac74b2c455983ca982166ff031db Author: Will Thompson Date: 2011-09-28 17:29:23 +0100 NEWS for 0.12.7 commit 498920f1f69cdccbcc3c5f2146a1b1b9b5cd5976 Author: Jonny Lamb Date: 2011-09-28 14:17:15 +0100 connection: drop invalid or duplicate data forms from channel managers Signed-off-by: Jonny Lamb commit f670d0082e850835b93405eac32da24d94496975 Author: Jonny Lamb Date: 2011-09-28 15:07:02 +0100 Revert "connection: deal with not being able to hash self presence" This reverts commit 9f8f181fd943f1e96f898c736ee4977d310b94a1. commit 6d7811ac1c1e0b7b98431d567626bff1f4161d3b Author: Jussi Kukkonen Date: 2011-09-01 23:48:35 +0300 mail notification: ensure google notification setting is correct Google XMPP server has an (extension) setting that can be used to enable mail notifications, see https://code.google.com/apis/talk/jep_extensions/usersettings.html https://bugs.freedesktop.org/show_bug.cgi?id=40565 commit 5e27d123f8a24509a619010cf2dedbbd632bbf0b Author: Jussi Kukkonen Date: 2011-09-01 23:45:54 +0300 mail notification: recognize "google:setting" namespace commit 55b092e583fd16ac18440c989bfc603176dbd610 Author: Jonny Lamb Date: 2011-09-27 14:59:21 +0100 connection: log what form types clients contribute Signed-off-by: Jonny Lamb commit b9c8a79c4f562a6971eb051848685b69d6b986d9 Author: Jonny Lamb Date: 2011-09-27 14:42:05 +0100 presence-cache: refactor data form replacing into its own function Signed-off-by: Jonny Lamb commit 1edf9a4dbc1bbbd24a9141b676eb310d6e4be919 Author: Jonny Lamb Date: 2011-09-27 14:39:20 +0100 dataforms.py: be sure to call UpdateCapabilities with arguments Signed-off-by: Jonny Lamb commit c005728f1cab3ae10aeae2cc51c4691f9aa03593 Author: Jonny Lamb Date: 2011-09-27 14:38:23 +0100 presence-cache: use hash table iters instead of foreach This is nicer and can terminate early. Signed-off-by: Jonny Lamb commit 084793e6ec80af7d7c6b891a6bdb896ab9f03d1a Author: Jonny Lamb Date: 2011-09-27 14:34:32 +0100 presence-cache: clean up code to replace data form arrays This was broken in the case of data_forms == NULL as it left info->data_forms pointing to a freed GPtrArray. Good catch, Will! Signed-off-by: Jonny Lamb commit 3d42307058b3a1bc31138b53b21e7ed8bf681e1b Author: Jonny Lamb Date: 2011-09-27 11:58:50 +0100 connection: fix comment placement in an if/else block Signed-off-by: Jonny Lamb commit 6211097e8b2df75b79313746b6f7c4e081921eb5 Merge: 060acfb c4bfa12 Author: Will Thompson Date: 2011-09-26 16:38:48 +0100 Merge branch 'soup-is-optional' Conflicts: tests/twisted/jingle/google-relay.py commit c4bfa12099858594001c15a2b1750977aaef814d Author: Will Thompson Date: 2011-09-26 15:26:21 +0100 tests: when built without soup, don't test google relay commit 060acfb55bdafcafff595eff6e2ed3ae24877c04 Author: Will Thompson Date: 2011-09-21 13:47:21 +0100 Rename test-initial-aliases to match Makefile commit c84617178fd951ece907cec4eb5c1da44c2123bb Author: Will Thompson Date: 2011-09-20 15:58:37 +0100 Call: handle google relay reply after channel dies Previously, if the Google relay server replied to our HTTP request after the Call channel had already gone away, we'd crash. Fixes: commit e7cc7f9b9b41923ab57b284776911f82102d06b3 Author: Will Thompson Date: 2011-09-20 15:58:37 +0100 Call: test google relay reply after channel dies It's kind of funny: the bug fixed by the previous commit repeatedly surfaced for StreamedMedia channels in a variety of situations (you closed the channel, you removed yourself from Members, you disconnected the connection), and hence there's a lovingly-written exhaustive set of test cases for those scenarios for incoming and outgoing channels. But of course they weren't run for Call channels… This patch only makes the bare minimum number of changes to the test to at least slightly exercise the Call code. It's not as exhaustive as it is for StreamedMedia, and works almost by accident, but hey... commit 06d98c31504139136336160aac4b1f094a8ccc88 Author: Will Thompson Date: 2011-09-20 15:58:37 +0100 Call: handle google relay reply after channel dies Previously, if the Google relay server replied to our HTTP request after the Call channel had already gone away, we'd crash. Fixes: commit f1ece52985d24d734462f96e8a33db8ced771fdb Author: Will Thompson Date: 2011-09-20 15:50:28 +0100 jingle/google-relay: use functools.partial commit 114a25a2cb9a1f57560cca30bddfeaeaeec0e3b6 Author: Will Thompson Date: 2011-09-20 15:21:50 +0100 Roster: rename updates_nicknames to updated_nicknames This is a typo! commit b070c914d5f59f2f5b7a5cd1ad476b9c4729eaaf Author: Will Thompson Date: 2011-09-20 15:19:17 +0100 Roster: don't emit nicknames-update with no nicknames. This was triggering an assertion failure in one of the signal handlers, which asserts that the array is non-empty. Reviewed-by: Marco Barisione commit 2413a026e53c42fa0973b0a861587dff12c1e65e Author: Derek Foreman Date: 2011-05-16 12:19:44 -0400 Remove duplicates from source file lists Signed-off-by: Jonny Lamb commit acaf2d4c7f54ecad880450987f716aface11d741 Author: Derek Foreman Date: 2011-05-16 12:17:50 -0400 Check for presence of gmodule Signed-off-by: Jonny Lamb commit 3967b4ccf908c70be173c1b27617ebdc19fd6c48 Merge: bc40410 40b5b45 Author: Marco Barisione Date: 2011-09-20 12:44:00 +0100 Merge branch 'grouped-alias-updates' Fixes: https://bugs.freedesktop.org/show_bug.cgi?id=40943 Reviewed-by: Will Thompson commit 40b5b45203098ce308a51b6c88c4ab31d40e047d Author: Marco Barisione Date: 2011-09-16 18:57:56 +0100 roster: emit a single signal for all the aliases updated at the same time commit d4cfda9cd64923c81d87b8f875b5f20658023a17 Author: Marco Barisione Date: 2011-09-16 16:39:02 +0100 conn-aliasing: add a plural version of the nickname-update signal handler commit bc40410024314a700ef70702502f868630e02e8b Author: Will Thompson Date: 2011-09-15 16:05:03 +0100 with-session-bus: import --also-for-system from Salut Reviewed-by: Jonny Lamb commit e829ba1c802f8b06bf678772046833d0a1c6fd0b Author: Will Thompson Date: 2011-09-12 18:03:27 +0100 with-session-bus.sh: retain newline when forking dbus-monitor commit 30a7ac413f66178c3cceae63baab487365ef8845 Author: Jonny Lamb Date: 2011-09-07 17:02:18 +0100 connection: add more public helper functions for getting caps This is useful so in a Gabble plugin you can get the TpBaseContactList and then look at each contact's caps for whatever reason. Signed-off-by: Jonny Lamb commit 485c9d8ad1365cbee11b8c7769425d5d5c02fc27 Author: Jonny Lamb Date: 2011-09-07 15:25:14 +0100 connection: fix typo when checking whether a client can do anything Naughty Simon. Signed-off-by: Jonny Lamb commit 9ca541ae76b6dc1838f11eaea21d6e284d5d7965 Author: Jonny Lamb Date: 2011-09-07 15:18:40 +0100 connection: stop trying to print data form titles as debug messages Approximately no data forms in entity capabilities have titles set properly anyway, so outputting: (null) (null) was getting dull. Signed-off-by: Jonny Lamb commit c0e658ed4e99555466e5d1c09140f43cb4faf898 Author: Jonny Lamb Date: 2011-09-06 14:21:51 +0100 connection: add pick_best_resource_for_caps function Signed-off-by: Jonny Lamb commit d96a1a927959b43586a888c56d45ab8b23b60de0 Author: Jonny Lamb Date: 2011-09-06 14:21:31 +0100 connection: add get_jid_for_caps utility function Signed-off-by: Jonny Lamb commit 70580a776d7b54a950042ea6c08b4949d96f2332 Author: Jonny Lamb Date: 2011-09-06 14:17:44 +0100 connection: add get_session public function Signed-off-by: Jonny Lamb commit 003e6186d70640c32b85cdf5bc920cbd94882e43 Author: Jonny Lamb Date: 2011-09-06 14:17:01 +0100 connection: make get_full_jid part of the public API Signed-off-by: Jonny Lamb commit 7436f9a3144929ffb10e6c9b1c240d153529a5a3 Author: Jonny Lamb Date: 2011-09-06 14:15:57 +0100 presence: implement WockyXep0115Capabilities interface Signed-off-by: Jonny Lamb commit e34fde96b5e3c5a766f66380e1e09a3f9a9074d2 Author: Jonny Lamb Date: 2011-09-06 14:15:30 +0100 presence-cache: add get_handle utility function Signed-off-by: Jonny Lamb commit d63ca76093a79a630a69fc2d49384004f3f5fe33 Author: Jussi Kukkonen Date: 2011-09-01 23:48:35 +0300 mail notification: ensure google notification setting is correct Google XMPP server has an (extension) setting that can be used to enable mail notifications, see https://code.google.com/apis/talk/jep_extensions/usersettings.html https://bugs.freedesktop.org/show_bug.cgi?id=40565 commit dc7ba1b82823a8e637f1bf3e8257ff44132f44b1 Author: Jussi Kukkonen Date: 2011-09-01 23:45:54 +0300 mail notification: recognize "google:setting" namespace commit 9b3b88ab8a7fdeeeb98faae23e6b53b1051e3415 Merge: 9b3e977 97175d8 Author: Will Thompson Date: 2011-09-05 18:53:26 +0100 Merge branch 'telepathy-gabble-0.12' commit 97175d86d43f0d9ebe7fa3f3f0ed96e825ae8094 Author: Will Thompson Date: 2011-08-03 08:32:16 +0100 MUC: add password test I left this out of 2d7c9db :( commit 9b3e977b7b08c6cfa055183f1914ab1b1b891c4b Merge: e3d4b85 6c102c0 Author: Will Thompson Date: 2011-09-05 18:49:55 +0100 Merge branch 'CAPS-LOCK' https://bugs.freedesktop.org/show_bug.cgi?id=40390 commit 6c102c09557e2988386751ffb1ee7186f0747578 Author: Will Thompson Date: 2011-08-26 11:00:49 +0100 Connection: assume all handles have ContactCaps Previously, some places in Gabble which called gabble_connection_get_handle_contact_capabilities() did not assume that it never returns NULL. But (due to the implicit text caps for everyone) it is guaranteed always to return a non-NULL array of capabilities, so we can simplify the places that call it. commit 00de9b2849013d7bc011ef34d3a3cb5a2730a83c Author: Will Thompson Date: 2011-08-26 10:56:49 +0100 CapsChannelManager: correct English in a comment commit 29d365c626445bd3f0c30d9cd6a1103cc87c3d96 Author: Will Thompson Date: 2011-08-26 10:55:20 +0100 Connection: simplify getting ContactCaps without presence If we don't have presence for a contact, we do not have any capabilities for them. But we always want to ensure that all contacts are capable of IM. So, currently there is a special-case in the caps-gathering code: if we have no presence, we only call _get_contact_capabilities on the IM factory: its implementation of this virtual method, unlike all others, doesn't care if it's passed a NULL set of caps. Otherwise, if we do have a set of capabilities, we call gabble_connection_build_contact_caps(), which is essentially a foreach over all channel managers, including the IM factory. I think it's neater to avoid the special case, and treat "no presence" as equivalent (for capabilities purposes) to "presence with no caps". commit e3d4b85839b465e160b91ac7b581dabd1097fc6e Author: Will Thompson Date: 2011-09-03 11:23:33 +0100 Remove support for legacy Presence. This was Olli's idea. He claims that building the old-school signals causes measurable CPU usage on accounts with lots of contacts (think: Facebook). It certainly causes D-Bus spam. Neither Telepathy-GLib nor TelepathyQt4 use this old interface. Empathy doesn't, either. I think this should be safe. Fixes: https://bugs.freedesktop.org/show_bug.cgi?id=40598 Reviewed-by: Jonny Lamb commit b16296db19ecbca6cfaf8d669c8997dbedb8e5fc Merge: 8569bab 2d7c9db Author: Will Thompson Date: 2011-09-05 18:39:02 +0100 Merge branch 'telepathy-gabble-0.12' commit 2d7c9db4766394d39ff3b3fc42cf7e928e4f4751 Author: Will Thompson Date: 2011-08-03 08:32:16 +0100 MUC: don't forget password when handling nick conflicts WockyMuc has a property, :password, representing the current password being used to join the MUC. GabbleMuc previously had a private variable, 'password', which was used for this. In the port to WockyMuc, setting the private variable was removed, but it was still used when re-attempting to join after a nick conflict. (I think the password should be a parameter to a hypothetical wocky_muc_join_async() which does all the nick conflict crap for you. Having this as state that kicks around on the WockyMuc for ever is bizarre—once you're in the room, you usually don't use the password, unless you're the owner, in which case you can retrieve the current password *which may be different*!) This patch expunges the zombie private variable, and ensures WockyMuc:password is only set when the user provides a password, not at every join attempt. It adds a test for this case, and some of the basic functionality of Password (which subsumes some incidental testing of the Password interface in muc/presence-before-closing: the only test that touched Password at all!). Fixes: https://bugs.freedesktop.org/show_bug.cgi?id=39790 Reviewed-by: Jonny Lamb commit 25c3f4dd8b9360cb531f0fc3a653094ce1e9b042 Author: Jonny Lamb Date: 2011-09-01 14:30:15 +0100 test plugin: make TestChannelManager implement CapsChannelManager ...and give back a new data form in the represent_client function. Signed-off-by: Jonny Lamb commit 9f8f181fd943f1e96f898c736ee4977d310b94a1 Author: Jonny Lamb Date: 2011-09-02 16:29:04 +0100 connection: deal with not being able to hash self presence If a data form is bad we might not be able to, so we shouldn't crash. Signed-off-by: Jonny Lamb commit 00a7c2fb7195374c39f2b9c9be8254822e5484e8 Author: Jonny Lamb Date: 2011-09-02 16:38:43 +0100 caps_helper: give data forms back to disco utility functions Signed-off-by: Jonny Lamb commit adea0ce7c2e48190fcf18c11717380e551432e04 Author: Jonny Lamb Date: 2011-09-02 16:35:48 +0100 jingle-share tests: also consider data forms in disco replies Signed-off-by: Jonny Lamb commit 6dfe751e17618a5f29f6611b649e7a1bbe0969d3 Author: Jonny Lamb Date: 2011-09-02 16:31:41 +0100 caps_helper: split out code to read disco replies Signed-off-by: Jonny Lamb commit 3968fd5da0e56006fde7bf6832e605ef8f544572 Author: Marco Barisione Date: 2011-09-02 11:24:30 +0100 Stable Gabble was accidentally using the master branch of wocky commit 587da15932d178ad289e81b039dcabb4e90984c5 Author: Jonny Lamb Date: 2011-09-01 14:19:52 +0100 gabble: add capabilities.h and caps-channel-manager.h as public API Signed-off-by: Jonny Lamb commit 1afd2d8e2fab78542467b2f36f08fc6e84523f91 Author: Jonny Lamb Date: 2011-09-01 12:13:16 +0100 caps_helper.py: add initial data form parser We want to be able to check that disco replies with data forms still have the correct hash. Signed-off-by: Jonny Lamb commit 45dc8f926f18be249f24f66deb589907bda47f98 Author: Jonny Lamb Date: 2011-08-31 16:29:43 +0100 connection: send data forms in disco query replies Signed-off-by: Jonny Lamb commit 7c6971a75a6eb32c77b64a7e9754e1fdb2a694af Author: Jonny Lamb Date: 2011-08-31 11:40:45 +0100 presence-cache: store data forms from disco replies and the caps cache Signed-off-by: Jonny Lamb commit d887fdc2d23bf00322c9923e8eead7c5f0607302 Author: Jonny Lamb Date: 2011-08-31 08:58:55 +0100 presence: start holding a list of data forms Signed-off-by: Jonny Lamb commit 28b64163e8bbca02de308612f6653ed291e2b1df Author: Mikhail Zabaluev Date: 2011-09-01 13:22:42 +0300 Don't allow the stub for GabbleGoogleRelayResolver to be empty g_slice_alloc() behavior for zero-sized blocks is undocumented and leads to obscure code paths taken. commit 39fc20df8c978ebf072998cc1077a48ec6b41593 Author: Mikhail Zabaluev Date: 2011-09-01 11:32:08 +0300 Ignore the relay info in Jingle stanza if Google relay support is disabled commit d2b3153f84e1f010b02ce4b084f3711ab95060e8 Author: Mikhail Zabaluev Date: 2011-08-31 16:14:09 +0300 Make Google proxy support optional The intent is to make optional the dependency on libsoup (fd.o #40537). commit 60b5bc0cf55ee60b0c66384e544a9f35d3b3688d Author: Mikhail Zabaluev Date: 2011-08-31 13:56:10 +0300 Isolated the Google relay resolution code in a separate source file commit 8569bab0c49f88e397aebc9fcf2d045a21b5be75 Author: Olivier Crête Date: 2011-08-29 17:46:58 -0400 Announce the camera-v1 caps bundle so that the Google UIs put a little camera for us https://bugs.freedesktop.org/show_bug.cgi?id=40471 commit 5dcf95eaf432b5af1edf82e87f40f795a3265a35 Author: Jonny Lamb Date: 2011-08-30 11:42:50 +0100 connection: save data forms created from UpdateCapabilities These aren't used yet. Signed-off-by: Jonny Lamb commit d9ecbbc1bbe573049e8cbb3348c600ec7512c84e Author: Jonny Lamb Date: 2011-08-30 11:18:45 +0100 caps-channel-manager: add data form argument to represent_client Signed-off-by: Jonny Lamb commit 81176c13d19530ac37401e7b9e597ebfbd2a2904 Author: Olivier Crête Date: 2011-07-19 21:01:10 +0300 presence-cache.c: Add Android presence cache bundle Let's add another "well-known" bundle https://bugs.freedesktop.org/show_bug.cgi?id=36996 commit 78015e5dc12c37d6a99b417d8e1abea9e975e607 Author: Will Thompson Date: 2010-02-05 13:41:29 +0000 Add a summary of options to the end of configure commit 068445479b27c036e01e6e2f7134f7cfd2f48d6f Author: Will Thompson Date: 2011-08-17 16:25:07 +0100 nano version bump commit 6c86d9bc863af7504bfafeb1212360cfd0f2b70d Author: Will Thompson Date: 2011-08-17 16:22:35 +0100 bump nano-version to 0.13.5.1 commit 6f7803086bdce403b2b32e83d16a083bce9d0ee9 Author: Will Thompson Date: 2011-08-17 16:18:11 +0100 0.13.5 commit 881c1802539f5ee4a8d095d0c97abc6088b301bc Merge: 7d56957 45254c4 Author: Will Thompson Date: 2011-08-17 16:17:42 +0100 Merge branch 'telepathy-gabble-0.12' Conflicts: NEWS configure.ac commit 45254c4fd2bd13f6ddb7cdf0e26f2d35cb4ba8ff Author: Will Thompson Date: 2011-08-17 16:13:07 +0100 Version 0.12.6 commit 7d569578494b20abd7889bfd7d3fd4defef07568 Author: Will Thompson Date: 2011-08-17 15:54:44 +0100 nano version! 0.13.4.1 commit a94dbcdd0d3eed3cecc84e2c825b2002ec61d60f Author: Will Thompson Date: 2011-08-17 15:30:41 +0100 version 0.13.4 commit ddea8e83665ae5e5fa9f256091992f3065c77797 Author: Will Thompson Date: 2011-08-17 15:30:16 +0100 NEWS for 0.13.4 commit e4590e849801ff4b0f2ddb9ad23a7da210212395 Merge: a89eb63 ac4485a Author: Will Thompson Date: 2011-08-17 13:12:57 +0100 Merge branch 'telepathy-gabble-0.12' Conflicts: NEWS configure.ac commit ac4485aa2b217b622fca2529f456b39ec4a0e0b7 Author: Will Thompson Date: 2011-08-17 13:09:20 +0100 nano-version bump! commit 7ae6e69ddc96f5d8fb38208e46a2f44c7daca245 Author: Will Thompson Date: 2011-08-17 12:41:09 +0100 Version 0.12.5 commit 1bc901f50cf3d5bb7dec0729e018ba00e24f6f06 Author: Will Thompson Date: 2011-08-17 12:40:43 +0100 NEWS for 0.12.5 commit a89eb63f14b55f5e291490c8cbaa114e2a7a2b53 Merge: b183b4f b32a81d Author: Will Thompson Date: 2011-08-17 12:27:14 +0100 Merge branch 'old-bugs' commit b183b4f20361f96cc7326da62b1705bb4758f09e Author: Olli Salli Date: 2011-08-10 21:53:26 +0300 Update NEWS commit 5c201dffc0c71d1a0500a341709b4930d6187f97 Merge: df2e4bd 5a62326 Author: Olli Salli Date: 2011-08-10 21:43:13 +0300 Merge branch 'tube-caps' Reviewed-by: Simon McVittie (smcv) commit 5a62326a6d3e91820d6ccec78635151ec403dd1d Author: Olli Salli Date: 2011-08-10 19:22:02 +0300 tube-caps: Test for a bidirectional (no Requested property) stream tube filter commit bd22f64177ae9532b5dd3f6697355545bccc8625 Author: Olli Salli Date: 2011-08-10 19:20:07 +0300 Drop an extra == TRUE in a conditional commit fb81504e14cbd02ae051f06bf39ed7b9f2e85280 Author: Olli Salli Date: 2011-08-10 19:19:36 +0300 private-tubes-factory.c: Use TP_PROP constants commit c44f4f1c601b2f71d08567c94f349f8c719a1a36 Author: Olli Salli Date: 2011-08-10 18:24:02 +0300 tube-caps test: Test that Requested=True filters don't make tube services advertised commit 8ca532d73b98c828b0620bd2ded172fd90a4b893 Author: Olli Salli Date: 2011-08-10 17:52:48 +0300 Don't advertise being able to receive tubes if we can only initiate them commit df2e4bd487e26bf785dbdc85fe50db7a9d35c989 Author: Marco Barisione Date: 2011-08-05 15:24:57 +0100 Update wocky to fix PEP when power saving is enabled commit cee0ee6d6a8daa2c32b139e28a88b07e3746853f Author: Marco Barisione Date: 2011-08-05 15:22:02 +0100 Update wocky to fix PEP when power saving is enabled commit 18b99be3be0c0b3ed31e52c25ac545ad6152e85d Author: Marco Barisione Date: 2011-08-01 14:20:34 +0100 Update the Wocky snaphost and adapt the changes in WockyPing Reviewed-by: Will Thompson commit 77155451e3145d0c856d4bd3d2e2ae53923463ad Merge: fbb15c3 82d3fbe Author: Will Thompson Date: 2011-08-03 10:21:10 +0100 Merge branch 'telepathy-gabble-0.12' commit 82d3fbe13b68caa242727ad7cc5d60e53926f7c9 Author: Will Thompson Date: 2011-08-03 10:11:36 +0100 Connection: correctly install power-saving property. Previously :power-saving was actually mistakenly implemented as a synonym for :decloak-automatically. This only showed up when (on master) I changed the default for "decloak-automatically" to be TRUE (not FALSE, as is the default for :power-saving). commit fbb15c3cb82e6c8944a8797e6f3b7a4a9c24b427 Author: Will Thompson Date: 2011-08-02 17:46:05 +0100 Turn on DecloakAutomatically by default There should be UI to turn this off, but since people can (in general) IM you even if they're not subscribed to your presence, they should be able (by default) to call you too. Reviewed-by: Sjoerd Simons commit d5d3107396225812841078dc8f0e3661b916f506 Merge: 7db8678 d0707d6 Author: Marco Barisione Date: 2011-08-01 17:38:23 +0100 Merge branch 'telepathy-gabble-0.12' commit d0707d61636e16b6acfbb84c0846b9373681b747 Merge: cbe0592 3b87f9d Author: Marco Barisione Date: 2011-08-01 17:36:23 +0100 Merge branch 'initial-invisibility' into telepathy-gabble-0.12 Reviewed-by: Will Thompson Fixes: commit 3b87f9d655f5ecdaca7fffa7de0394037fbec3df Author: Marco Barisione Date: 2011-07-26 14:46:32 +0200 presence/shared-status.py: test invisibility works with updated servers commit c2e977cf73bb961d3130428e2b5a68f1d54a42fe Author: Marco Barisione Date: 2011-07-26 14:39:20 +0200 conn-presence.c: make shared invisibility work after the server update The new version of the Google Talk servers supports a newer version of shared status. Because of an unknown version string we were assuming invisibility was not supported. We now just fall back from "hidden" to "dnd" if another resource doesn't support shared invisibility. Fixes: commit 7db86780825ed1c3459c380fa5b0b209c6287fd3 Merge: 2ec472c 22f6849 Author: Will Thompson Date: 2011-07-29 18:54:28 +0100 Merge branch 'null-caps-39464' Fixes: http://bugs.freedesktop.org/show_bug.cgi?id=39464 commit 2ec472c9deb1bbca578c4f3cb939ef8ae3065bd8 Author: Will Thompson Date: 2011-07-28 15:57:31 +0100 test bytestreams: also only listen on localhost commit 885d9cdc175f58c5490c64d38e585678e8e10ebb Author: Paul Seidler Date: 2011-04-03 15:16:37 +0200 tests: use localhost https://bugs.freedesktop.org/show_bug.cgi?id=35968 commit 22f684987e53d0043a7739c6c1b1929f639c659f Author: Will Thompson Date: 2011-07-28 15:28:53 +0100 Add a regression test for fd.o#39464 commit 147ebf5404c5818aba122e25cf1c7844cbd99a59 Merge: 5b423d6 49606ef Author: Will Thompson Date: 2011-07-27 17:36:27 +0100 Merge branch 'rudolph' Reviewed-by: Simon McVittie commit 5b423d671523b7f3b28755d0de4a064540d8e814 Author: Will Thompson Date: 2011-07-27 16:25:57 +0100 bump nano-version to 0.13.3.1 commit f50a1e625217d528c6a6406d6ce610773910d1d8 Author: Will Thompson Date: 2011-07-27 16:22:36 +0100 make-release-mail: detect headers more robustly. I just broke this by having a line beginning 'telepathy-gabble' which was not actually a version header. commit 7f64afa396c7e639e7920a60d46b980a12d5c2ee Author: Will Thompson Date: 2011-07-27 16:01:51 +0100 Version 0.13.3 commit b099c2a269804857cf41a393cca22f8b82704ec6 Author: Will Thompson Date: 2011-07-27 15:58:16 +0100 NEWS for 0.13.3 commit b5ec55b7056634a679ce277f3575f09214d5372a Author: Will Thompson Date: 2011-07-27 15:42:41 +0100 Update Wocky submodule to fix fd.o#36077 commit a0ad5e286229d90b79942bf32e29e2874d278a31 Merge: 2b4881b cbe0592 Author: Will Thompson Date: 2011-07-27 15:40:16 +0100 Merge branch 'telepathy-gabble-0.12' Conflicts: lib/ext/wocky commit cbe0592fbfbd2706f3c306da860b88e61e33a82e Author: Will Thompson Date: 2011-07-27 15:20:01 +0100 bump nano-version commit ecf29e6a17441829bd369bd2d25245c8dcb7aa73 Author: Will Thompson Date: 2011-07-27 15:07:59 +0100 Version 0.12.4 commit 3ad658923c085f7ecad5cca06ab0de77fa7eadf4 Author: Will Thompson Date: 2011-07-27 14:49:52 +0100 Update Wocky submodule to fix fd.o#36077 No other changes are included: this is the gabble-0.12 branch of Wocky. commit 49606ef2f8637792e0b4fc5cfc77851259867fd5 Author: Will Thompson Date: 2011-07-27 14:08:18 +0100 JingleFactory: stop including fake loudmouth! commit 50aa29eceeaa7eefa336a5dee2641a02b2c8e846 Author: Will Thompson Date: 2011-07-27 13:40:44 +0100 JingleFactory: wockify google:jingleinfo callback This squashes the spurious "ignoring jingleinfo from '%s', not ourself nor the server" debug messages that showed up on pretty much every incoming IQ set, because some idiot (me) put that check before the check for a element. Using _register_handler_from_server() and pattern-matching makes this a *lot* neater, I think—and objectively, it's half as much code. commit 319c851c095dea4a4425f80c4db67947cb1abdbd Author: Will Thompson Date: 2011-07-27 13:33:32 +0100 JingleFactory: wockify jingle action callback This could be better: we could match dialects up-front. But hey. commit fa61e14c10296b945cbb75585ad87d673b577426 Author: Will Thompson Date: 2011-07-26 15:37:54 +0100 JingleFactory: use constructed, not constructor. This also expunges a weird FIXME! commit 138cea6ca12d5a929d52aa2e86b9f0e34919e35a Author: David Laban Date: 2011-07-22 02:28:15 -0400 presence-cache: don't crash if computed_hash == NULL commit 2b4881bb7df570c4840dc4cfdea8ee3ca9176c92 Merge: 4080a24 05faadf Author: Will Thompson Date: 2011-07-13 17:13:09 +0100 Merge branch 'encryption' Fixes: commit 4080a244d604207bac505073295b7ed1f1556538 Author: Will Thompson Date: 2011-07-11 17:30:04 +0100 test-debug: use ProxyWrapper Obsessive? Moi? commit 133c168d3a2838293c2d63b7796a6a942e67ea39 Author: Will Thompson Date: 2011-07-11 17:29:35 +0100 ProxyWrapper: add a sensible ‘no other interfaces’ default commit 818528a2f36a74c0a493fe77daa9f12f35de9285 Author: Will Thompson Date: 2011-07-11 17:20:45 +0100 Test Debug object's absence with --disable-debug 38a6178 changed test-debug.py to verify that the Debug object is present but non-functional when --disable-debug is passed to configure. Gabble actually doesn't have a Debug object at all in that situation, which I think is reasonable—so this now tests that calling GetMessages fails. The test doesn't make any particular assertions about the error. Right now it happens to be 'org.freedesktop.DBus.Error.UnknownMethod' but it really ought to be some kind of ‘unknown object’ method. So. commit feed269a8d964c61fa6b271591ed2311e6fba75e Merge: ebb6963 1e3b981 Author: Will Thompson Date: 2011-07-11 16:19:15 +0100 Merge branch 'debug-stubs' https://bugs.freedesktop.org/show_bug.cgi?id=39046 Reviewed-by: Guillaume Desmottes commit 1e3b9812fce185efb830b5aec5f5912472e49cd4 Author: Will Thompson Date: 2011-07-08 14:13:15 +0100 Update Wocky for corresponding --disable-debug fixes commit 99ee60ef205efa3d6b77134bd2003d373477f9cb Author: Will Thompson Date: 2011-07-08 10:46:36 +0100 Define a static inline no-op DEBUG with --disable-debug commit ebb6963dc788faa9683f2b976b1db19187819351 Merge: 38a6178 74e4699 Author: Marco Barisione Date: 2011-07-07 14:23:16 +0100 Merge branch 'extra-certificate-identities' Fixes: Reviewed-by: Will Thompson commit 74e46998689e8049faa9eb87eca0886ab1f72481 Author: Marco Barisione Date: 2011-06-28 15:39:33 +0100 server-tls-channel.py: test that gabble verify hostnames Gabble should accept certificates for hostnames if they match the hostname in the JID or one of the hostnames in the extra-certificate-identities parameter. commit 2672b6c7e03a303aed2649f925dac486ab9fb469 Author: Marco Barisione Date: 2011-06-28 15:39:07 +0100 Move the extra domains to trust from the TLS channel to the manager commit ed99546dc9ee402f42727702305b3125a73724af Author: Marco Barisione Date: 2011-06-28 15:38:29 +0100 TLSManager: check the connection before the non-interactive verification commit 86726ddce8ad38cfca1a753d3b7b8940bfe5f82b Author: Marco Barisione Date: 2011-06-28 15:37:45 +0100 ServerTLSChannel: make reference_identities a GStrv and not a GPtrArray In this case the GPtrArray doesn't give us any advantage and doesn't make clear that the content is a NULL terminated array of strings. commit a6c42b97d4e1984451c919048cc04df72b4c454d Author: Marco Barisione Date: 2011-06-28 15:36:53 +0100 Add an extra_identify argument to the TLS verification functions Update the Wocky snaphost and adapt the TLS manager to the changes. Now the TLS verification functions have an extra argument for the additional hostnames that we have to consider valid. This is useful, for instance, when using Google apps. Rather than providing a certificate for the JID’s domain part, the server provides a certificate for talk.google.com; if the user has explicitly configured a ‘Google Talk’ account, it's reasonable to accept certificates for this domain. commit 38a6178a7529e3b1bf1ab8439185541e01c18dd5 Author: David Laban Date: 2011-05-31 16:10:16 -0400 test-debug.py: actually check that no debugging happens Reviewed-by: Will Thompson commit f7cd57841f39ca7d007623745ea2c33acaa97c88 Author: Vincent Penquerc'h Date: 2011-05-23 12:29:52 +0300 Fix tp-yell includes being read from /usr/include instead of the submodule Since tp-yell gets installed in ${PREFIX}/telepathy-1.0, adding include paths for some other telepathy library (such as tp-glib) would automatically make tp-yell headers discoverable. Fixed by moving the tp-yell submodule paths before the ones for any telepathy library, so these will be found first. This fixes the build if you have a version of tp-yell installed which does not have a symbol that tp-gabble needs. https://bugs.freedesktop.org/show_bug.cgi?id=37492 Reviewed-by: Will Thompson commit c356abc2de2b33cb250b143bb5a8b28068971f4d Author: Vincent Penquerc'h Date: 2011-05-23 12:29:52 +0300 Fix tp-yell includes being read from /usr/include instead of the submodule Since tp-yell gets installed in ${PREFIX}/telepathy-1.0, adding include paths for some other telepathy library (such as tp-glib) would automatically make tp-yell headers discoverable. Fixed by moving the tp-yell submodule paths before the ones for any telepathy library, so these will be found first. This fixes the build if you have a version of tp-yell installed which does not have a symbol that tp-gabble needs. https://bugs.freedesktop.org/show_bug.cgi?id=37492 Reviewed-by: Will Thompson commit 5c06d57058f169401449039b7148470ca1784699 Author: Thomas Flueeli Date: 2011-02-02 22:59:56 +0100 gibber: fix TCP connection establishment on Windows https://bugs.freedesktop.org/show_bug.cgi?id=31621 Reviewed-by: Will Thompson commit 9086cd4c9be315aa91a6284ffecb65178c89c8c1 Author: Thomas Flueeli Date: 2011-02-02 21:02:54 +0100 gibber: add workaround for GLib bug on Windows https://bugs.freedesktop.org/show_bug.cgi?id=31621 Reviewed-by: Will Thompson commit 04763e4bc8ed32c208c75209b66221ab46290005 Author: Will Thompson Date: 2011-07-06 18:40:13 +0100 Set the DBus_Property flag on DecloakAutomatically Without this, MC won't update the property on running connections. Reviewed-by: Jonny Lamb commit 51ddcba7fde8c2f9db5cd01930652318093c0032 Merge: f54614c 0acb80d Author: Will Thompson Date: 2011-06-28 14:00:59 +0100 Merge branch 'more-alias-and-vcard-tweaks' Fixes: Reviewed-by: Simon McVittie commit 05faadffcce2573f788c979b9d85bf78721f84c4 Author: Will Thompson Date: 2011-06-28 13:49:21 +0100 Fix Make dependencies for write-mgr-file All the parameter information lives in protocol.c these days. commit 0acb80d4c7669d26fe543e515473ad3f7dfbe11d Author: Will Thompson Date: 2011-06-28 10:59:20 +0100 Correctly clear our own alias when asked to. Previously, Gabble would include an empty node in your vCard, which kind of offends my inner purist: it should just leave it out. En passant, this should also fix clearing the name='' attribute on the roster for yourself (if you're on your own roster) but this is untested. commit 0f30bcdcaab39a2ebcaafb42272056d79f126c11 Author: Will Thompson Date: 2011-06-28 10:57:41 +0100 gabbletest: fix expect_and_handle_[sg]et_vcard I wrote these, and they've *always* been broken: • _get_ would include two elements in the reply: the first (from the query) would always be empty. • _set_ tries to update the global 'current_vcard' variable, but because it wasn't declared 'global' it did not update it. Astonishingly all the tests pass... commit 4080addd6c2fa879b69e1e9b4567ccac0cf15697 Author: Will Thompson Date: 2011-06-28 10:30:24 +0100 test-set-alias: clean up vCard check. This makes the test more thorough: it actually checks the structure of the vCard… commit 22382935be99e027da46303107926a15cd8f3017 Author: Will Thompson Date: 2011-06-28 10:19:12 +0100 Test setting our own alias via PEP Astonishingly, this was untested... commit 17d99f5e7ed7b4837d5367950a99773fb61bf646 Author: Will Thompson Date: 2011-06-28 10:11:08 +0100 Aliasing: handle SetAlias({contact: ''}) better Previously, if the user set a contact's alias to '', Gabble would change its alias on your roster to the empty string… this is not really ideal. This patch makes Gabble switch to using a cached remote alias for the contact, if any; if there is none, it simply removes the name='' attribute from the roster entirely, and tries to make a request for a better nickname. commit abd99988770e2fdbd8bd811c8e08662fa21e7d0d Author: Will Thompson Date: 2011-06-28 09:57:20 +0100 Aliasing: split out cached remote alias lookup There are no code changes in this patch: it simply moves the middle section of _gabble_connection_get_cached_alias(), which deals with looking for cached aliases that the contact specified for themself (on PEP, in their vCard, in their MUC JID, etc.) into a separate function. It also adds a docstring to _gabble_connection_get_cached_alias(). commit 928ef53affeb52d6ed380078d1acabd7f1b4dfec Author: Will Thompson Date: 2011-06-22 00:27:42 +0100 test-save-alias-to-roster: ack and echo roster pushes I was adding some more stuff to this test and realised that it's technically misbehaving as a server: the server MUST (of course) ack the roster update the client says, but it also MUST send roster pushes to all connected resources, event to the resource who made the change. Gabble relies on this, and only respects changes to the roster when the server pushes them to it. (I think this is fine, it means there's only one code path.) This test happened not to ever update a roster item more than once, so not acking the pushes didn't matter so much. It also never depended on reading back stuff that it added to the roster. But I'm about to add a section which will. commit e4450664ff14d65b76f869aeb0aeb7cc9fe51438 Author: Will Thompson Date: 2011-06-22 00:26:25 +0100 Aliasing: remove a hateful g_hash_table_foreach commit 7bea4d961ec7361455258ed01750df28a7207c26 Author: Will Thompson Date: 2011-06-22 12:34:42 +0100 ContactInfo: don't claim PHOTO is unknown Previously this code would log “unknown vCard node in XML: PHOTO” for every vCard that flew past. This is misleading: PHOTO is perfectly well-known, it's just handled by a different bit of code. So this patch adds the notion of an ignored field, which the ContactInfo code knows about but does nothing with. I didn't add any of the other fields mentioned in the comment at the end of the field table: they're not handled anywhere, so it may arguably be worth logging something about these. commit b32a81d5436cb6f6d0c6435674fc424cabfed9e3 Author: Will Thompson Date: 2011-06-27 18:49:00 +0100 MucChannel: fix coding style violations commit 12ea43016a73e853a1a6ad8227f2a6f109474abf Author: Michael Scherer Date: 2011-06-27 18:41:35 +0100 Support XEP-0012: Last Activity Patch largely rewritten by Will, since Gabble has changed a lot in the two years since he rejected it on (in retrospect, shaky and unnecessarily hostile) grounds. Fixes: Signed-off-by: Will Thompson commit 901577fd23a6e7f7e639be6c8d8b086a3531a091 Author: Will Thompson Date: 2011-06-27 18:03:36 +0100 Remove TpBaseConnection.connected callback. This OLPC-specific function can just be called from the ::status-changed signal handler. I've called it at the end of that function because the 'connected' vfunc is called just after ::status-changed is emitted, so this shouldn't change the order of operations in the OLPC code. Fixes: commit 0f00b08488ef0459fdb6d8f85d55b049d4dc0245 Author: Will Thompson Date: 2011-06-20 12:51:40 +0100 Enable require-encryption by default This seems like a reasonable thing to do in 2011. commit f54614c40756f382d1a608054f1b711ff6390d5c Merge: 7d58d83 11ddfc2 Author: Will Thompson Date: 2011-06-24 20:12:12 +0100 Merge remote-tracking branch 'origin/master' commit 7d58d83eee6624c66001582d1f56caadedaf82ab Author: Will Thompson Date: 2011-06-24 20:11:30 +0100 Bump version to 0.13.2.1 commit ea425adba81a2681af4c4ed7184972deae378331 Author: Will Thompson Date: 2011-06-24 19:57:18 +0100 Version 0.13.2 commit ac2df5eacd34852d6d6ddf584bd2394812e66ad6 Merge: cf9ed52 fcfbc06 Author: Will Thompson Date: 2011-06-24 19:55:03 +0100 Merge branch 'telepathy-gabble-0.12' Conflicts: NEWS configure.ac src/presence-cache.c commit fcfbc06d3f679a8266f13921ff3771bd07cf7f24 Author: Will Thompson Date: 2011-06-24 19:50:08 +0100 bump version to 0.12.3.1 commit a1d33d82c8645303e279e20033d8f96bc6f6e567 Author: Will Thompson Date: 2011-06-24 18:48:03 +0100 Version 0.12.3 commit 18a8b11833af6b8b2e62e4ee3bd3037a8b5b2c2f Author: Will Thompson Date: 2011-06-24 18:47:45 +0100 NEWS for 0.12.3 commit 712bbe2b59ed5746cee50604956b0cb842ea9cc4 Author: Will Thompson Date: 2011-06-24 19:35:23 +0100 FTChannel: don't assert about handle-type in setter telepathy-glib's default for TpChannelIface:handle-type has changed in 0.15.2: it used to be NONE, and now it's UNKNOWN. This broke both Salut and Gabble… But as the comment says, it's not meaningfully writeable. It's not at all obviously why this assertion is there. commit 276abd5ff84d635eb5f8cd3eee56978f75631722 Author: Colin Walters Date: 2011-06-24 18:42:23 +0100 Honor NOCONFIGURE for compatibility with gnome-autogen.sh See also: http://people.gnome.org/~walters/docs/build-api.txt commit 11ddfc2e24bbb2fc5297c714dbea88c377a1911e Merge: cf9ed52 1ff589c Author: Will Thompson Date: 2011-06-24 15:48:44 +0100 Merge branch 'telepathy-gabble-0.12' Conflicts: src/presence-cache.c commit 1ff589c1e89cf9bb90f59a961c23f2ffa9c3dff0 Merge: cdff268 f7cdb73 Author: Will Thompson Date: 2011-06-24 15:31:29 +0100 Merge branch 'fd.o-38603-initial-contact-presence' into telepathy-gabble-0.12 commit f7cdb735461e81b428f9a5d776e2b2487263c8aa Author: Will Thompson Date: 2011-06-23 16:53:23 +0100 Cite fd.o#38603 (presence-before-roster doesn't work) commit fa0990a9810b9629d32dd53840aa415e2d5dbe7d Author: Will Thompson Date: 2011-06-23 16:39:59 +0100 Roster: don't emit presence updates for available contacts Previously, when we received the roster we would announce that the presence of everyone on it we're subscribed to has changed, just for the side-effect of signalling that offline contacts really are offline, not just unknown. This offended me. :) commit d1f7941c3343b1c498e55543c66ae72fa2b880f5 Author: Will Thompson Date: 2011-06-23 16:28:20 +0100 initial-contact-presence: reduce duplication of presences commit e0cda613a166e9c538ccdcd8cf5d5feae72a2596 Author: Will Thompson Date: 2011-06-23 16:19:11 +0100 GabblePresence: start in state Unknown This has the effect of ensuring that receiving for a contact not previously in the presence cache will not cause a change from Unknown to Unknown to be signalled. commit 66788076de9fb0062d685346d4bb5c444befd3ea Author: Will Thompson Date: 2011-06-23 15:58:11 +0100 PresenceCache: discard UNKNOWN presences in maybe_remove In various places, gabble_presence_cache_maybe_remove() is called to allow the cache to relinquish its grip on content-free offline presences. It should also be able to drop unknown presences: they're basically equivalent, based on whether or not the contact is on our roster. Specifically, this means that UNKNOWN presences for contacts received before the roster has arrived do not linger in the cache, and hence these contacts are correctly signalled as OFFLINE when the roster arrives. Really, I think a better fix would be to change the presence cache to not have this distinction between Unknown and Offline: it's completely based on whether or not the contact is (known to be) on the roster, and this is reflected in the way that the handler for incoming passes a different value into the presence cache based on whether or not the contact is on the roster. But this would be a much more invasive change, so I'm not prepared to make it just yet. commit 01d600f1f9f52ac7f021fed2a75e6aa8e7168274 Author: Will Thompson Date: 2011-06-23 15:46:28 +0100 Presence: debug GabblePresenceId names where possible (I may have missed a few places, but these are the two I came across.) commit b2616cbb5e25fcd28dbeaad68055b18a399db8b5 Author: Will Thompson Date: 2011-06-23 13:48:27 +0100 Build GEnum/GFlags for enums in connection.h Specifically, I want a GEnum for GabblePresenceId so I can include the stringy versions in debug strings. commit a6c0404419c7c84924e1b98783860f8ad99aa18f Author: Will Thompson Date: 2011-06-23 12:51:58 +0100 initial-contact-presence: split expect from assertion I think this makes things clearer: we're expecting some presences to change in response to the roster arriving, and we're expecting the changes to be these. This means that the test failing manifests itself as an assertion failure, not a timeout. commit cf9ed5291015fd97b2121dcf38933e8bf885013d Merge: 0b6f5f5 cdff268 Author: Will Thompson Date: 2011-06-21 14:24:00 +0100 Merge branch 'telepathy-gabble-0.12' commit cdff268c3965046baa37aac100cceda3e9aafe37 Merge: 63c4d27 6df83a1 Author: Will Thompson Date: 2011-06-21 14:21:26 +0100 Merge branch 'aliases' into telepathy-gabble-0.12 Reviewed-by: Sjoerd Simons commit 6df83a17ff312dc3ad99c79bb40163eba687449c Author: Will Thompson Date: 2011-06-21 12:25:05 +0100 Look for better aliases if roster says name=jid. When we discover that a contact doesn't have an alias at all, we fall back to their JID, and write this to the roster (as we do for any other alias we use, to spare our users' precious bandwidth). This is fine and all, but it means that we ignore any subsequent updates from the contact to say that they have a better alias. For instance, we might get a PEP nickname update, or fetch their vCard for some other reason (getting an avatar, ContactInfo, &c &c). In those cases, we would like to have any better alias obtained passively to replace the JID fallback. This is only ever passive: we still don't query for a better alias if the roster says it's the JID. (Clever users can set the alias to the empty string, which has the effect of forcing a refresh.) Fixes: https://bugs.freedesktop.org/show_bug.cgi?id=27361 commit 7a221f03f2a8796895bd48febe1b04168d1b5a1a Author: Will Thompson Date: 2011-06-20 17:00:56 +0100 servicetest: Stringify events more nicely commit e619592d8bfbba0d0679f918f514fdfc08b829cb Author: Will Thompson Date: 2011-06-20 16:51:08 +0100 gabbletest: make stanza events subclasses of Event This looks neater to me. commit a85a67c99ab9085eafc8afe17437253a082e0e08 Author: Will Thompson Date: 2011-06-20 16:13:49 +0100 Ignore empty name='' attributes on the roster. Previously, if a roster item had an empty name='' attribute, Gabble would be content to show this to the user, and never try PEP or their vCard for a better nickname. The empty alias is never useful. commit 6ba62283b7052ff49cc223b65d4ac06937ec1043 Author: Will Thompson Date: 2011-06-20 16:09:50 +0100 rostertest: support name='' attrs in roster pushes commit 7ac26b27921b9217066c68c2a3333ff616c69a31 Author: Will Thompson Date: 2011-06-20 13:26:58 +0100 test-save-alias-to-roster: use contact list fetching helper commit c033c160dfd91a9253aaaac7804922067116cb4e Author: Will Thompson Date: 2011-06-20 13:17:55 +0100 test-save-alias-to-roster: expect AddMembers to return This is an ancient FIXME. I think it's safe to say that we depend on a new enough tp-glib. commit 0b6f5f529ac72a479956663175cb6205c41ea394 Author: Will Thompson Date: 2011-06-16 18:02:03 +0100 Bump nano-version to 0.13.1.1 commit 17e3e817f5faa349f08b8b541c1ecd65f83234a4 Author: Will Thompson Date: 2011-06-16 17:46:27 +0100 Version 0.13.1 commit bd88fdcc79243df03d3fa31259696e1743bd7b78 Merge: a2d6254 63c4d27 Author: Will Thompson Date: 2011-06-16 17:45:23 +0100 Merge branch 'telepathy-gabble-0.12' Conflicts: NEWS configure.ac commit 63c4d275abab3aad53923f556473ee902965e2b6 Author: Will Thompson Date: 2011-06-16 17:37:36 +0100 National novel versus ionization commit 5e59111c54577650316749a262d6c891f415e603 Author: Will Thompson Date: 2011-06-16 17:22:32 +0100 version 0.12.2 commit 052ca8131c8e145714372db249efd9bd04c70f94 Merge: e8e2d23 a88dd55 Author: Will Thompson Date: 2011-06-16 17:07:55 +0100 Merge branch 'gtalk-jingle-workarounds' into telepathy-gabble-0.12 These aren't perfect, but they'll do for now. https://bugs.freedesktop.org/show_bug.cgi?id=38352 commit a88dd5529d038f32b3c01b9eb6109eaa10e88604 Author: Will Thompson Date: 2011-06-15 18:08:08 +0100 Add a test for working around GMail commit 714e066f8b566f107cef819d4ae6fb7786c8739f Author: Will Thompson Date: 2011-06-15 17:53:17 +0100 JingleSession: special-case that GMail can't modify contents. In theory, it can, because it now speaks modern Jingle which has this concept. But in practice it cannot, at least for now. commit b1e774762806b1e0446aaccd42657437e96b01a2 Author: Will Thompson Date: 2011-06-15 17:44:40 +0100 MediaChannel: clean up setting ImmutableStreams commit bbf7da0ca74749cfec6701e9cd16f8d7595c4bb8 Author: Will Thompson Date: 2011-06-15 17:38:31 +0100 RTP: use video_rtp/_rtcp component names with GMail commit fcc5df48e59c4141151fe6c9d8348a1b676cc88c Author: Will Thompson Date: 2011-06-15 17:13:12 +0100 JingleContent: work around GMail omitting content creators commit a29379f10e8ff96bfcd206cd92a12cff25bc5c72 Author: Will Thompson Date: 2011-06-15 16:51:33 +0100 PresenceCache: set Omits Content Creators quirk for GMail commit 519504984f9ec70657fdf0bff0eb580bee253237 Author: Will Thompson Date: 2011-06-15 16:50:12 +0100 JingleSession: refactor quirk-checking code. commit cd4a003443ff6f9b6e022e9a21146e59c2c56463 Author: Will Thompson Date: 2011-06-13 20:15:09 +0100 TransportIface: correct a contents vs. candidates thinko commit a2d6254646182c10d55290f8b3ce53d7a399ca96 Merge: 0a7c31e e8e2d23 Author: Olivier Crête Date: 2011-06-13 15:16:07 -0400 Merge branch 'telepathy-gabble-0.12' commit e8e2d23490846ebabb7c640bb905b7549883b3af Author: Olivier Crête Date: 2011-06-13 14:20:53 -0400 jingle-media-rtp: Codec encoding names are not case sensitive commit 0a7c31ea51c5096e86111f73f157bb87f0f46d0e Author: Will Thompson Date: 2011-06-02 19:46:38 +0100 Post-release nano-version and NEWS bump to 0.13.1 commit c4a118850bcaccd6502b6a19cbb61238de1d157e Author: Will Thompson Date: 2011-06-02 19:20:59 +0100 Version 0.13.0 commit 3fbd382bf99ce3584d9e167f60c47b9f973cd8c3 Author: Will Thompson Date: 2011-06-02 19:20:36 +0100 NEWS for 0.13.0 commit 9f1fa8b567e7f792013ab8a624ad46aa0735e606 Merge: 8d5a2f5 828f2e7 Author: Will Thompson Date: 2011-06-02 17:10:08 +0100 Merge branch 'telepathy-gabble-0.12' Conflicts: configure.ac lib/ext/wocky commit 828f2e79137acfd1079a6d1efcc527d8bec724b1 Author: Will Thompson Date: 2011-06-02 17:06:55 +0100 Post-release nano-version and NEWS bump commit fe551853438ca44f6f28e5ce334a53e9b9c6b693 Author: Will Thompson Date: 2011-06-02 16:11:37 +0100 Version 0.12.1 commit dbc9a937f335ac403436c6fddbd0129693247e49 Author: Will Thompson Date: 2011-06-02 16:08:51 +0100 NEWS for 0.12.1 commit b756564ad0a27e1d210b38bf7583ec41d7007574 Author: Will Thompson Date: 2011-06-01 14:54:45 +0100 httptest: chain up from HTTPFactory constructor. This fixes jingle/google-relay.py with recent versions of Twisted Web. commit 8f19db01eac316b81d76f4533399fac2d05d72ac Author: Will Thompson Date: 2011-06-02 14:59:09 +0100 Update Wocky snapshot to fix self avatar in MUCs (That is: fd.o#32017) commit 8d5a2f593c4f6c3b2db161be74a03bb3f05270b9 Merge: 5bfe3ed 34b9066 Author: Will Thompson Date: 2011-06-01 18:12:51 +0100 Merge branch 'fd.o-32017-muc-self-avatar' commit 34b90665e4860ff5dc022cc7549f070d1de645de Author: Will Thompson Date: 2011-05-31 15:07:24 +0100 Add a regression test for avatars in MUCs. commit ef057a2fb297ce639618e89dae4b6cdbd732ff3c Author: Will Thompson Date: 2011-05-31 17:42:51 +0100 Update Wocky snapshot for WockyMuc changes This update changes the signature of various signals to include MUC status codes as bits in a guint rather than in a hash table. It also fixes WockyMuc stealing stanzas before Gabble's handlers have a chance to get stuff out of them; test for that to follow. Fixes: but I am not very happy with the fix. commit 4f80d8e0aa86a0b8759c29f0e9e91aaeb909e76e Author: Will Thompson Date: 2011-05-31 15:14:46 +0100 mucutil: return event from try_to_join_muc commit 0fb508d1b269069aa25f701f82e14060b90051ef Author: Will Thompson Date: 2011-06-01 14:54:45 +0100 httptest: chain up from HTTPFactory constructor. This fixes jingle/google-relay.py with recent versions of Twisted Web. commit 5bfe3ede2c17b7dd10d258e5661892967cabcc60 Author: Will Thompson Date: 2011-05-24 19:41:00 +0100 Explicitly cast to guint64 when passing varargs Without any additional type information, integers used in varargs lists can get passed as the wrong type. In this call to tp_value_array_build() in gabble_muc_channel_constructed(), we should be passing 0 as a a gint64, but I think in practice we're passing it as a pointer-sized thing. (I'm not a C language lawyer, I'm sure someone could correct me on this.) If the type we're passing it as is not the same size as a gint64, this will cause tp_value_array_build() to read off the end of its varargs, probably crashing in the process (because what it reads will probably not be a valid GType). The other call to tp_value_array_build() added in ec8ca6e (for the Room interface) does cast correctly. Fixes: Reviewed-by: Jonny Lamb commit 9b81db6ba12edb6e5c8f9b901f83b9a92d9e23fa Merge: e27aa6f 04f044f Author: Will Thompson Date: 2011-05-24 14:17:52 +0100 Merge branch 'telepathy-gabble-0.12' commit 04f044fe0ce68dd73d6bb92477106a09fd43dbdd Merge: 53401b9 487dce0 Author: Will Thompson Date: 2011-05-24 14:17:17 +0100 Merge remote-tracking branch 'origin/telepathy-gabble-0.12' into telepathy-gabble-0.12 commit e27aa6f6fd58533c22b0a2c30579f969378bad3e Merge: efaebdd 53401b9 Author: Will Thompson Date: 2011-05-24 14:16:31 +0100 Merge branch 'telepathy-gabble-0.12' commit 53401b9d16dc6ed2c2e15b7af5f7c2ad254aac1f Author: Chandni Verma Date: 2011-05-10 20:25:43 +0530 Filter out google-rbc-announcement messages They are server generated containing no interesting information and appear in clients as if sent by peer Fixes: https://bugs.freedesktop.org/show_bug.cgi?id=36647 commit efaebdd96fdfec40aec3e257176e9d16faf44112 Author: Debarshi Ray Date: 2011-05-24 01:49:08 +0530 Update wocky (fixes build failure with GCC 4.6) Fixes: commit 51fdf722717a5646116b554f5feb860d82a26566 Author: Siraj Razick Date: 2011-05-23 19:43:57 +0530 fixes : telepathy gabble build break build breaks due to my previous two patches when doing make check this commit fixes those errors Reviewed-by: Will Thompson commit 1794df70dd67d70e6c8649e47a4f3719fee268d7 Merge: 987722a 4087b2a Author: Marco Barisione Date: 2011-05-23 14:18:37 +0100 Merge branch 'error_reports' Reviewed-by: Simon McVittie commit 4087b2a87a9ceb6553030f3514771e7e8ffaf2f8 Author: Siraj Razick Date: 2011-05-20 23:52:14 +0530 gabble_message_util_build_stanza: clearer renaming of variables The commit renames INVALID_ARGUMENT to RETURN_INVALID_ARGUMENT, and msg to stanza renamed msg to stanza, the difference between message (a TpMessagerepresenting an abstract message) and msg (a WockyStanza representing an XMPPXML stanza) would be clearer. (based on the comments by:Simon McVittie on f.d.o BUG#33460) commit 1c86beea6be1cb0b5b946da2345d98139072a3ea Author: Siraj Razick Date: 2011-04-28 18:53:23 +0530 Fixes : fd.o#33460 by using wocky_porter_send_async() Bug - 33460 - IM and MUC channels should use wocky_porter_send_async(), and emit failing delivery reports url : https://bugs.freedesktop.org/show_bug.cgi?id=33460 commit 5aa9d93472ba3a90410496f453d861ce16f6bd5a Author: Will Thompson Date: 2011-05-19 16:47:11 +0100 Smoke-test MUC delivery report flags Reviewed-by: Simon McVittie commit 5ba21edcf88ef60be5558acc0b0259b5f29e98e9 Author: Jonny Lamb Date: 2011-05-18 16:06:52 +0100 muc-factory: don't use a string after freeing it Uh oh! Signed-off-by: Jonny Lamb commit 987722a87f929035c4c6dcbcd069e0ba414b25ee Merge: ba3c764 487dce0 Author: Marco Barisione Date: 2011-05-17 16:42:29 +0100 Merge branch 'telepathy-gabble-0.12' commit 487dce00f8f54ea30edb807b1e1c8eb3c717a85d Merge: e704ea0 a776a13 Author: Marco Barisione Date: 2011-05-17 16:33:19 +0100 Merge branch 'shared-presence' into telepathy-gabble-0.12 Reviewed-by: Will Thompson Fixes: commit a776a13404c9af8f29a0e52d8645bd66622c27d4 Author: Marco Barisione Date: 2011-05-17 11:41:47 +0100 presence/shared-status.py: test that remote presence doesn't override away commit ba640e0f9d1fd0ab7aa42e21ec5ad2f2bee5622b Author: Marco Barisione Date: 2011-05-17 15:51:34 +0100 presence/shared-status.py: don't use a weird and avoidable "or" construct commit ca600286bd3f42771a2b8bd35ca2e5d278644d45 Author: Marco Barisione Date: 2011-05-17 11:39:40 +0100 conn-presence.c: don't override away when the remote status changes commit ba3c764bf634fad3af4ca0a8f35b180ca9ba221d Merge: 52b2d76 e704ea0 Author: Simon McVittie Date: 2011-05-16 10:12:01 +0100 Merge branch 'telepathy-gabble-0.12' commit e704ea03ac1cc6a4e38670e3b8f89386154f8894 Author: Simon McVittie Date: 2011-05-10 18:52:07 +0100 set_status_to_connected: do nothing if we already disconnected Now that we're making more use of GAsyncResult, we can easily get into this situation: * all but one of the preconditions for being CONNECTED have happened; the remaining one uses GAsyncResult (currently that can only be conn_presence_set_initial_presence_async) * conn_presence_set_initial_presence succeeds, and schedules a call to its callback in an idle; we are now ready to be CONNECTED, but because of GAsyncResult calling conventions, we won't call the callback right now * someone calls Disconnect(), and we do so, synchronously; we are now DISCONNECTED * the idle goes off and connection_initial_presence_cb is called, with success, while DISCONNECTED! Bug: https://bugs.freedesktop.org/show_bug.cgi?id=37078 Reviewed-by: Vivek Dasmohapatra commit 52b2d7691404457a559fc3fbb09e6f064d7dc33b Merge: 02ad9c4 8f08873 Author: Marco Barisione Date: 2011-05-12 16:55:32 +0100 Merge branch 'telepathy-gabble-0.12' commit 8f088732c364f1b77f805d65590e30abaf61a707 Author: Marco Barisione Date: 2011-05-12 16:40:38 +0100 conn-presence.c: remove a line forgotten when fixing a merge conflict Reviewed-by: Will Thompson commit 02ad9c4dcb40e047eab8fb8777ddf1ccb5e35dce Author: Debarshi Ray Date: 2011-05-12 10:10:23 +0300 Update the release mail automation script to contain the Git URL commit 44c5d0ec3de8014efa79f15d85c630ee120db0c9 Merge: d4ddbf7 f99b6f4 Author: Marco Barisione Date: 2011-05-12 14:20:11 +0100 Merge branch 'telepathy-gabble-0.12' commit f99b6f43f92299c7eb99ccbfef7b806f5b52c1de Merge: fe15601 d5421d1 Author: Marco Barisione Date: 2011-05-12 13:07:21 +0100 Merge branch 'shared-presence' into telepathy-gabble-0.12 Reviewed-by: Will Thompson Fixes: commit d5421d18d1fcb03a86ecfb81e37029c3f015c791 Author: Marco Barisione Date: 2011-05-10 18:46:05 +0100 conn-presence.c: handle the transition from hidden to away on GTalk commit d895028ef03577768e19476e263455e7f7cbe4a7 Author: Marco Barisione Date: 2011-05-10 18:33:12 +0100 conn-presence.c: add a function to know if a GabblePresenceId is away commit c5fc80a4d42c794d43332b3626e9ec50ea03580a Author: Marco Barisione Date: 2011-05-10 14:33:22 +0100 conn-presence.c: leave an away status using too When setting an away status on a server that supports shared status, we need to use instead of shared status. When we then leave the away status, we want to use the shared status, but GTalk also expects us to also send a . commit 1ee540de1b939c676a76cbcf046a4cfb7b77deab Author: Marco Barisione Date: 2011-05-10 18:47:06 +0100 presence/shared-status.py: test the transition from hidden to away commit 951991f33fea6525469cb712d205ffd1ba2b1eec Author: Marco Barisione Date: 2011-05-10 14:29:06 +0100 presence/shared-status.py: test the transition from away to non-away commit d669b203f4c09396b3ff04c6dd392c314211ee59 Author: Marco Barisione Date: 2011-05-10 14:04:34 +0100 presence/shared-status.py: fix a typo (even → event) commit cb9807eb74a70df762d173b57ee04368572c1113 Author: Marco Barisione Date: 2011-05-10 13:57:36 +0100 presence/shared-status.py: don't unforbid events too early commit d4ddbf79d3b4b845a01cdfcfb14b024d69105dc4 Merge: 6ad9e1e b85a525 Author: Jonny Lamb Date: 2011-05-06 11:34:42 +0100 Merge branch 'room' Conflicts: src/muc-channel.c tests/twisted/constants.py Signed-off-by: Jonny Lamb commit 6ad9e1e3d6a553405394032ae7258beff37c6f2a Merge: d467f27 fe15601 Author: Will Thompson Date: 2011-05-05 15:26:02 +0100 Merge branch 'telepathy-gabble-0.12' commit fe156016db44b3cfd8bb3276d3c5a8944e7ff22f Author: Will Thompson Date: 2011-04-15 14:27:07 +0100 Don't assume google:queue support from google:roster Dave Cridland asked the Google Talk server team to advertise a google:queue stream feature, and as of this month some time, they do! So we can stop looking for google:roster as well. Reviewed-by: Jonny Lamb Fixes: commit 5d2927d9155d2388145406c189fdf9d9f8dfcac6 Merge: cacd814 e4ae0fe Author: Marco Barisione Date: 2011-05-05 15:01:59 +0100 Merge branch 'shared-presence' into telepathy-gabble-0.12 Reviewed-by: Will Thompson Fixes: commit e4ae0fe527dbbffe403026b25eb4c44b3b325715 Author: Marco Barisione Date: 2011-05-03 18:43:22 +0100 presence/shared-status.py: test that "chat" falls back to "available" commit d010bb78008681707fc6cd4b4c2393c15affd442 Author: Marco Barisione Date: 2011-05-03 18:41:05 +0100 conn-presence.c: don't allow the "chat" status when using shared status commit 42ca8dd32fa930c85fcdddc16b64220d45429f4f Author: Marco Barisione Date: 2011-05-03 16:19:06 +0100 conn-presence.c: simplify the non-connected case in status_available_cb commit 49633baeb861f9798cf87809f7421a087e9c9cff Author: Marco Barisione Date: 2011-04-18 18:32:39 +0100 presence/shared-status.py: always expect an initial commit c2b8e2d64edfec296554f72a2d59cca26cbacfe4 Author: Marco Barisione Date: 2011-04-18 18:29:20 +0100 conn-presence.c: send an initial when using shared status commit c0ff90f6850ff80e682a86d45a4a5bda27e75425 Author: Marco Barisione Date: 2011-05-04 11:54:25 +0100 presence/invisible_helper.py: don't handle privacy lists in XmppXmlStream commit 6bc9759f7140d0ac544659a4a6776c57e405699f Author: Marco Barisione Date: 2011-05-04 11:53:14 +0100 presence/shared-status.py: don't allow privacy list stanzas commit d04b628113fb93300f6dc82e88addbcf1e58b80e Author: Marco Barisione Date: 2011-04-15 17:13:11 +0100 conn-presence.c: don't try privacy lists when using shared status commit 06cc941cbada06c876e922df097d93956d88e00f Author: Marco Barisione Date: 2011-04-07 18:44:14 +0100 presence/shared-status.py: test the correct behaviour with away and xa commit 2a53547a92ec30a31cf4442e60f3f318269952d6 Author: Marco Barisione Date: 2011-04-07 18:41:30 +0100 conn-presence.c: don't use shared presence for away statuses Away and extended away are mapped to idle in GTalk and are per connection, so they are not set through shared presence. commit 2fb53a5a09c7dea0eee5bf7f54ab0d6c24e8b246 Author: Marco Barisione Date: 2011-04-07 18:40:43 +0100 conn-presence.c: fix indentation commit c49ea89ae115ad8a0c537a1a491a79604c819072 Author: Marco Barisione Date: 2011-04-07 18:38:53 +0100 conn-presence.c: don't send if we already used shared status commit d467f276329264be0b24f5af6c854fd7a182b531 Author: Will Thompson Date: 2011-05-05 12:05:33 +0100 Bump micro version for unstable branch. The next release from master will be 0.13.0 commit a2a603bdfb12bc9aa80726bf1f822cc22c76917e Author: Will Thompson Date: 2011-05-03 13:15:32 +0100 Request pipeline: remove FIXME I checked everything that uses the request pipeline (directly or indirectly), and nothing checks for error codes outside of those specified in XMPP Core. (Previously this code used gabble_message_get_xmpp_error, which would also fetch Jingle, SI and a couple of other class of error.) commit c1be75d93784a9b1b37b0dd717b1494029a675b8 Author: Will Thompson Date: 2011-03-07 14:12:22 +0000 Remove GabbleXmppError. There is a FIXME buried in here … but the tests pass. commit ea21cdcd49ba24235f97060c056fe9cd9744e69e Author: Will Thompson Date: 2011-03-04 11:09:11 +0000 _gabble_connection_acknowledge_set_iq: use Wocky Really, this function should go away, but this will be much easier when we get rid of LmMessageHandler since then (almost) all the places calling it will have a WockyPorter * already. commit 66c905575590cf30b0c3307de55718a813833a09 Author: Will Thompson Date: 2011-03-01 10:36:33 +0000 Delete my favourite function. :'( commit f510988de177d4c60ce10cc93ec15e1e85bd29bb Author: Will Thompson Date: 2011-03-01 10:36:07 +0000 _gabble_connection_send_iq_error: use Wocky commit b32211ace00f3591d6e77d14313787f8d440a483 Author: Will Thompson Date: 2011-03-01 10:47:21 +0000 Remove lm_iq_message_make_result() commit e1e754f9440a35a69a3ac6dc15ed5190da184047 Author: Will Thompson Date: 2011-02-28 21:46:39 +0000 Remove three redundant node functions from util.[ch] lm_message_node_has_namespace(), wocky_node_get_child_any_ns() and lm_message_node_get_namespace() have exact equivalents in Wocky. commit 46410a32d5eef705a48b74a6d5aa4f61aa1c3fef Author: Jonny Lamb Date: 2011-02-04 17:09:04 +0000 loudmouth: Remove LmMessageNode Signed-off-by: Jonny Lamb commit 3cd3ffc609f92a534317f006eb4401f39bc4b279 Author: Jonny Lamb Date: 2011-02-04 17:06:54 +0000 stop using LmMessageNode wrapper WockyNode is the future. Signed-off-by: Jonny Lamb commit 0de3d249d69bb99fddbcf72e4dafe36660245eb7 Author: Jonny Lamb Date: 2011-02-04 17:05:53 +0000 use the new xmlns and xml:lang build tags Signed-off-by: Jonny Lamb commit 36c8f4e67979c60d635d1448b1c2afe15d2a96ec Author: Jonny Lamb Date: 2011-02-04 16:55:45 +0000 util: add xml:lang and xmlns lm_message_build tags Signed-off-by: Jonny Lamb commit 160289f855182e9e2d00a91b38541d8e989eee17 Merge: 4722b73 0d865ef Author: Jonny Lamb Date: 2011-05-02 09:32:54 +0100 Merge branch 'caps-hash' commit 0d865efce5ca0840925abdca673a3f171258f767 Author: Jonny Lamb Date: 2011-05-02 09:11:57 +0100 caps-hash test: removed This has moved to wocky. Signed-off-by: Jonny Lamb commit 4722b731516f98a810b97d1be0c5762cb87d9432 Author: Jonny Lamb Date: 2011-05-02 09:32:02 +0100 update wocky snapshot Signed-off-by: Jonny Lamb commit cacd8147f3d0c0f4b0b851731b76dcacb4a9fb35 Author: Will Thompson Date: 2011-04-21 18:48:03 +0100 naaaa nanananano version katamari damacy commit 8b00635e0c048bae7219c06f5e18ac8b33a9dacf Author: Jonny Lamb Date: 2011-02-04 11:16:15 +0000 caps-hash: misc fixes to cleanup code wocky_caps_hash_compute_from_lists allows dataforms to be NULL if there aren't any and will no longer assert on dataforms=NULL. Also, we no longer dup the feature strings as we free the pointer array in the same function so we know what the memory allocation of the array is. Signed-off-by: Jonny Lamb commit d34197fb00196aff94142d38216acf4efc36d2af Author: Jonny Lamb Date: 2011-02-04 10:21:09 +0000 caps-hash: use wocky_caps_hash_compute_from_lists Signed-off-by: Jonny Lamb commit c7bba6807c7c236a3a7c7121e1e096f626c56dcb Author: Jonny Lamb Date: 2011-02-04 10:19:31 +0000 connection: clarify add_sidecar_own_caps' identity arg Signed-off-by: Jonny Lamb commit 0917818df48202cf979e5911c968966b6384a84a Author: Jonny Lamb Date: 2011-02-04 10:19:14 +0000 presence-cache: don't try and copy a NULL disco identity array Signed-off-by: Jonny Lamb commit c330f99490d65b87698ecc3677cc7a521955a74a Author: Jonny Lamb Date: 2011-02-03 09:32:28 +0000 caps-hash: remove anything to do with dataforms We don't need it for self presence, and we're moving to Wocky. Signed-off-by: Jonny Lamb commit bfd15d09556a01079842e5f56b2fb3729f991cf8 Author: Jonny Lamb Date: 2011-01-24 13:05:41 +0000 caps-hash: remove _from_lm_node We're slowly moving this into Wocky. Signed-off-by: Jonny Lamb commit 969c73f63979dada565e9c36d51d34da0e5a68c3 Author: Jonny Lamb Date: 2011-01-24 13:04:21 +0000 disco-identity: remove Signed-off-by: Jonny Lamb commit df58879f27bced1367fe3c9011cce733952cfae5 Author: Jonny Lamb Date: 2011-01-24 12:48:17 +0000 use WockyDiscoIdentity instead of the gabble version Signed-off-by: Jonny Lamb commit 926a07f594b303888ab98fe71735d288dfaf62cf Author: Jonny Lamb Date: 2011-01-24 12:47:57 +0000 use wocky_caps_hash_ functions Signed-off-by: Jonny Lamb commit b85a52558e9260cf28dba25b60f894aa8075383c Author: Jonny Lamb Date: 2011-01-13 09:20:14 +0000 muc-factory: remove more assertions with side effects I forgot these last time, whoops. Signed-off-by: Jonny Lamb commit 0986e22bcbff90cc12d4bef5cf27e42d8bde9853 Author: Jonny Lamb Date: 2011-01-13 09:18:14 +0000 muc-factory: improve debug messages when TargetID and room props conflict Signed-off-by: Jonny Lamb commit 9806dea0ec26d3803f6f05e12666296bbfa527b7 Author: Jonny Lamb Date: 2011-01-04 08:47:20 +0000 muc/room.py: fix copy-paste error Signed-off-by: Jonny Lamb commit 921f6ee9813814baead9a0672af6b42beab654c9 Author: Jonny Lamb Date: 2011-01-04 08:46:18 +0000 muc-factory: give temporary variables better names Signed-off-by: Jonny Lamb commit 558fd89929b0b4dc978f0e02b9f694ab7f93ffb5 Author: Jonny Lamb Date: 2011-01-04 08:42:33 +0000 muc-factory: also note why asserting on decoding a JID Signed-off-by: Jonny Lamb commit 86f10810b3f244d34d2cc79ac3f46176fabe1c64 Author: Jonny Lamb Date: 2011-01-04 08:40:58 +0000 muc-channel: re-add comment about valid TargetID Signed-off-by: Jonny Lamb commit 9ca2e3f94bd2ddb062d5eed6aff2f64fc929ad2b Author: Jonny Lamb Date: 2011-01-03 16:45:05 +0000 muc-factory: ensure RoomID and ServerID don't conflict with TargetID Signed-off-by: Jonny Lamb commit 35356cd6fda7dd10d223fd99b204c51c7aa3c28c Author: Jonny Lamb Date: 2011-01-03 16:03:44 +0000 muc-channel: check the result of gabble_decode_jid properly Signed-off-by: Jonny Lamb commit 23d4b72664d671374b6eaa48b703d7ea8e936f7d Author: Jonny Lamb Date: 2011-01-03 16:01:51 +0000 muc-channel: add newlines when calling g_object_get for clarity Signed-off-by: Jonny Lamb commit 4312f26c61c9a61ca007575d23a521500931bc8b Author: Jonny Lamb Date: 2010-12-23 17:06:06 +0000 tests: add a test for Room interface Signed-off-by: Jonny Lamb commit 637ba03783fff269933a7802728d680f5a8b82e6 Author: Jonny Lamb Date: 2010-12-23 17:04:55 +0000 muc-factory: allow channel requests with RoomID + Server set Signed-off-by: Jonny Lamb commit 51c40a378b1b1434b6ad3705fbd2744216c35bd8 Author: Jonny Lamb Date: 2010-12-23 12:22:57 +0000 muc/subject.py: test the Room Subject property is correct Signed-off-by: Jonny Lamb commit a8053079c2fafe334684be6ee34b768f89abd8ab Author: Jonny Lamb Date: 2010-12-23 12:22:34 +0000 muc-channel: set Room properties as appropriate Signed-off-by: Jonny Lamb commit ec8ca6ea5d945fae643b45cc5fdaa757cb070afa Author: Jonny Lamb Date: 2010-12-23 10:30:08 +0000 muc-channel: implement Room properties Signed-off-by: Jonny Lamb commit 7c67a8eeac4a65597beb860a9750e4cd639061b1 Author: Jonny Lamb Date: 2010-12-23 09:54:43 +0000 extensions: add Chan_Iface_Room draft Signed-off-by: Jonny Lamb telepathy-gabble-0.18.2/AUTHORS0000644000175000017500000000074012200204332016052 0ustar00smcvsmcv00000000000000Ross Burton Guillaume Desmottes Dafydd Harries Robert McQueen Simon McVittie Mads Chr. Olesen Senko Rasic Ole André Ravnaas Olli Salli Sjoerd Simons Rob Taylor telepathy-gabble-0.18.2/README0000644000175000017500000000550312200204332015664 0ustar00smcvsmcv00000000000000================ telepathy-gabble ================ Gabble is a Jabber/XMPP connection manager for the Telepathy framework, currently supporting: * single-user chats * multi-user chats * voice/video calling * file transfer with Jabber/XMPP and Google Talk interoperability. Telepathy is a D-Bus framework for unifying real time communication, including instant messaging, voice calls and video calls. It abstracts differences between protocols to provide a unified interface for applications. Requirements ============ telepathy-gabble requires: telepathy-glib GLib, GObject libdbus The D-Bus GLib bindings libxml2 libnice libsoup and either: GNUTLS or OpenSSL At build time, it also requires: GNU make pkg-config libxslt, xsltproc Python See configure.ac for full details, including versions required. Building from git also requires the GNU build system (Autoconf, Automake, libtool). Bugs, feature requests and to-do list ===================================== Report all bugs, feature requests and "to-do" items here: Versioning policy ================= We use an "odd/even" versioning scheme where the minor version (the y in x.y.z) determines stability - stable branches have y even, development branches have y odd. Unreleased builds straight from git identify themselves as version "x.y.z.1". These are compiled with -Werror, so they might stop working if your gcc version issues more warnings than ours. If this is a problem for you, use a release tarball. Contact info ============ This package is maintained by the Telepathy project: Telepathy development is supported by Collabora Ltd. . Hacking ======= The current version of telepathy-gabble is always available from the 'master' branch of: (gitweb) Stable branches are in the same repository, and are called telepathy-gabble-0.6 etc. Proposed patches awaiting review can usually be found in Merge Monkey: Please follow . telepathy-gabble-0.18.2/NEWS0000644000175000017500000017630712312535604015533 0ustar00smcvsmcv00000000000000telepathy-gabble 0.18.2 (2014-03-20) ==================================== The “mithril boxing gloves” release. Fixes: • update Wocky: · don't try to cancel a source ID twice, which issues a critical warning in GLib 2.39 · fix failure to build with recent (Markdown-based) gtk-doc • plugin loader: don't crash if g_dir_open() fails twice (fd.o #66085, Will) • fix a crash in 1-1 Tubes on 64-bit machines (fd.o #70038, Guillaume) • fix enum cast warnings under clang (fd.o #70038, Guillaume) • add a regression test for fd.o #68829 (Simon) telepathy-gabble 0.18.1 (2013-09-06) ==================================== This stable release fixes connection issues with Facebook which have been recently introduced because of a change in their XMPP servers. Fixes: • update Wocky: · fd.o #68829: If we send an IQ to a server allow "from" to be empty (David Edmundson) telepathy-gabble 0.18.0 (2013-08-09) ==================================== The “sea view over the steelworks” release. This is a new stable branch, recommended for use with GNOME 3.10. Summary of changes since 0.16.x: • GLib 2.32 is (directly) required • better interop with Google webmail for audio/video calls • better interop with Google and MSN servers • better interop with iChat • don't claim to support X-TELEPATHY-PASSWORD if the username is unknown • look up a STUN server automatically, using DNS SRV • old-style Tubes channels have been removed • bug fixes Enhancements since 0.17.6: • Check for GLib 2.32 in the top-level configure.ac, and drop fallback paths for older GLib (Simon) Fixes since 0.17.6: • Update Wocky to its new gabble-0.18 branch: · fd.o #67875: make the documentation build successfully (Simon) · fd.o #67900: make the tests pass with GLib 2.36 (Simon) • fd.o #65290: wrap uninstalled regression tests in run-test.sh so they can work under Automake ≥ 1.13 (Simon) • fd.o #67828: fix a test failure with telepathy-glib 0.20.3/0.21.1 or later (Simon) • Debian #714534: avoid naming a function parameter "errno", which is technically a reserved word and might be a macro expanding to an lvalue expression (Samuel Thibault) telepathy-gabble 0.17.5 (2013-06-06) ==================================== The “4.5kg of laptops” release. Dependencies: • GLib 2.32 is now directly required. It was already indirectly required by telepathy-glib 0.19.x. Fixes: • update Wocky: · fd.o #65131: interoperate with non-XMPP-Core-compliant Jingle IQs sent by Google's webmail UI (Simon) · improve reference-counting for better stability (Simon) • fd.o #65296: initialize libdbus for thread-safety (Simon) • fd.o #64285: avoid running the same commands twice in parallel when doing a highly parallel build (Simon) • fd.o #49595: disable an unreliable test-case (Simon) telepathy-gabble 0.17.4 (2013-05-30) ==================================== The “unattainably high curry standards” release. This development release fixes the same man-in-the-middle attack as 0.16.6. If you use an unencrypted connection to a "legacy Jabber" (pre-XMPP) server, this version of Gabble will not connect until you make one of these configuration changes: • upgrade the server software to something that supports XMPP 1.0; or • use an encrypted "old SSL" connection, typically on port 5223 (old-ssl); or • turn off "Encryption required (TLS/SSL)" (require-encryption) Fixes: • update Wocky: · fd.o #61792: fix linking an example program with ld versions that default to --no-copy-dt-needed-entries · fd.o #65036 (CVE-2013-1431): update Wocky to respect the tls-required flag on legacy Jabber servers • fd.o #63119: improve regression tests' isolation from the session bus (Simon) • fd.o #64319: consider the field to be "supported" on Google servers (it's currently read-only and contains a Google+ URL) (Xavier) • fd.o #64354: don't claim we support X-TELEPATHY-PASSWORD if we don't know the username (Xavier) telepathy-gabble 0.17.3 (2013-03-01) ==================================== The “less resplendent backup ruffs” release. This includes the fixes from telepathy-gabble 0.16.5, including fixing a remotely-triggered denial-of-service bug. You should upgrade. Fixes: • fd.o#57521: don't crash when the server sends back malformed or error replies to privacy list queries. (wjt) • fd.o#61433: don't crash on weirdly-shaped data forms in caps query replies. This issue is tracked as CVE-2013-1769. Unfortunately, this bug can be triggered by any XMPP user who knows your bare JID, not just by people you've authorized to see your presence. Fortunately, it is just a NULL pointer dereference, rather than allowing the attacker to do anything more nefarious like execute code. (wjt) • fd.o#43166: handle rate-limiting by MUCs better, including disabling typing notifications if we get rate-limited, and including the error message from the server in the D-Bus signal so that the user interface could, in principle, show it to the user. (wjt) Enhancements: • fd.o#58198: the Jingle protocol code now lives in Wocky. This should make no functional difference to Gabble. (wjt) telepathy-gabble 0.17.2 (2012-12-07) ==================================== Hooray! Hooray! Hooray! Dependencies: • telepathy-glib 0.19.9 is now required. Enhancements: • fd.o#54760, fd.o#57080: Gabble now uses GLib's base64 implementation rather than including its own. (Heiher, Alban Browaeys) • fd.o#25961, fd.o#25385: Gabble now looks up a STUN server from the _stun._udp SRV record for the user's domain. (Maiku, wjt) • fd.o#47378: XEP-0184 delivery reports are now supported. Note that Empathy neither requests nor displays them. (wjt) Fixes: • fd.o#56181: don't inadvertantly disable creating Call1 channels. (rishi) • fd.o#52362: hopefully, don't crash if we disconnect in the middle of trying to change our Google Talk presence. (wjt) • The Jingle code now sends back unknown-session errors correctly, as mentioned on fd.o#33789. (wjt) • fd.o#57267: File transfer channels now include FileTransfer.FUTURE and FileTransfer.METADATA in their Interfaces property (Daniele Domenichelli) • fd.o#57521: Don't crash when connecting to the League of Legends XMPP server. (wjt) • fd.o#52146: Don't crash in sympathy if the auth channel handler crashes. (wjt) telepathy-gabble 0.17.1 (2012-09-11) ==================================== Dependencies: • telepathy-glib 0.19.7 is now required. (sorry about that) Enhancements: • fd.o#32612: Old-style Tubes channels have been removed. (Jonny) • Tube and Text channels are no longer announced together. (Jonny) Fixes: • Make sure capability discovery works for the camera-v1 capability bundle, avoiding an iChat bug in which it repeats failed capability discovery requests in a rapid loop (fd.o #54634, Simon) • Fix some race conditions and other brokenness in the tests (Sjoerd) • Make sure capability discovery works for the camera-v1 capability bundle, avoiding an iChat bug in which it repeats failed capability discovery requests in a rapid loop (fd.o #54634, Simon) • Fix some race conditions and other brokenness in the tests (Sjoerd) telepathy-gabble 0.17.0 (2012-08-14) ==================================== Dependencies: • telepathy-glib 0.19.2 is now required. Changes since 0.16.2: • Fix calls with android devices (Marcus) • Implement WLM jidlookup. This makes possible to add MSN contacts using XMPP. (Xavier) • Fix google caps parsing (Jonny) telepathy-gabble 0.16.0 (2012-04-02) ==================================== This is the start of a new stable branch. Changes since 0.15.5: • telepathy-glib 0.18.0 is now required. • Install plugins in their own special (versioned) gabble directory so we're not installing unversioned ABI-unstable libraries. (Simon) • The DownloadAtConnection and Download ContactList members have been implemented. (Alban) • Handle errors in IBB bytestreams. (fd.o#47999, Will) Summary of particularly noteworthy changes since 0.14.x: • telepathy-glib 0.18.0 is now required. • Gabble can now be built on Android, using 'androgenizer'. • Add support for the final version of Call1 and remove the telepathy-yell submodule. • Many other miscellanous fixes and small interface implementations. telepathy-gabble 0.15.5 (2012-03-22) ==================================== Requirements: • telepathy-glib 0.17.7 is now required. Enhancements: • fd.o#46513: Refactor Jingle code to remove Telepathy in preparation of moving it to Wocky. (Will) • fd.o#45602: Subclass TpBaseChannel in more channel implemenations. (Jonny) • fd.o#47502: Add a --disable-voip configure flag to disable building gabble with VoIP support. (Marco) Fixes: • Correctly convert between Telepathy and Jingle candidate types. (Olivier) • Start sending automatically on accepting bidirectional calls. (Olivier) telepathy-gabble 0.15.4 (2012-02-21) ==================================== The "disproportionate retribution" release. Requirements: • telepathy-glib 0.17.5 is now required Enhancements: • Add support for the final version of Call1 from telepathy-spec 0.25.2 and remove the telepathy-yell submodule. (Many contributors) • fd.o#41790 - Make file transfer support optional (andrunko) • fd.o#44056 - telepathy-gabble-xmpp-console no longer mixes GIR and pygtk. (Guillaume) • fd.o#33911 - The Loudmouth API compatibility layer has been removed. It's possible this will cause regressions in weird edge cases but it should make the code base more maintainable. (Jonny, Will) • fd.o#45491 - Error messages provided by the server in stanzas are now exposed via the SimplePresence API. This makes it easier for users to distinguish contacts being offline from contacts' servers being broken. (Will) • fd.o#44649 - Gabble now has a gabble-plugins.so library, similarly to mission-control. This enables plugins to link correctly as DLLs on Windows. This also means that plugins don't directly access GabbleConnections anymore, but instead invoke the same functionality through a GabblePluginConnection GInterface. (siraj) API changes to Wocky snapshot: • fd.o#45400 - WockyPepService's API has changed a little bit. wocky_pep_service_get_finish() has grown an extra out argument to give the caller the element found, if any; similarly, the ::changed signal has grown a similar extra argument. (Will) • fd.o#34975 - WockyPorter is now responsible for sending back error replies for unhandled IQs, whereas previously this was up to Gabble. Plugin authors shouldn't notice this change. (Will) • fd.o#27489 - including now includes all public API from Wocky, and including any other header directly is forbidden. (This is similar to how GLib's headers are structured.) Out-of-tree plugins will need to be updated. (Will) Fixes: • fd.o#44331 - Gabble plugin API fails at runtime on Windows : gabble_plugin_create_sidecar function is renamed to gabble_plugin_create_sidecar_async and new virtual function gabble_plugin_create_sidecar_finish is introduced. All gabble plugins should implement these two methods and all internal plugins are updated to use this new API. (siraj) • fd.o #45443 (workaround): avoid testing Credentials access control, since recent Linux has stricter requirements for credentials-passing (it's now opt-in) which we're not yet meeting. (Guillaume) • fd.o #46379: don't raise a GError with domain 0. (Simon) • fd.o #44855: work around Google's unimplemented capability discovery by hard-coding the capabilities of the GTalk echo bot. (Guillaume) • Work around the deprecation of GValueArray. (Guillaume) telepathy-gabble 0.15.3 (2011-12-22) ==================================== Fixes: • fd.o#43891: Update wocky snapshot to fix wocky_data_form_set_type() (Alban) telepathy-gabble 0.15.2 (2011-12-21) ==================================== Enhancements: • fd.o#43588, fd.o#43889: Add public gabble_connection_add_sidecar_own_caps_full() function which includes data forms. (Alban) Fixes: • fd.o#42462: Update wocky snapshot to fix gabble getting kicked from D-Bus when non-character utf-8 is used by remote clients (Sjoerd) • Fix the build when using GLib 2.32. (Xavier) telepathy-gabble 0.15.1 (2011-11-24) ==================================== The “goodbye desolate railyard” release. Dependencies: • telepathy-glib 0.17.2 is now required. Enhancements: • fd.o#38568: Ladies and gentlemen, prepare your angle brackets: Gabble now ships with an XMPP console interface! You can send IQs and get their replies, send arbitrary stanzas, or monitor all incoming and outgoing stanzas, all with beautiful GtkSourceView syntax highlighting. UI love and caring welcome. Anyone want to make a version of the Telepathy “heads” icon with stanzas in place of the thought balloons? (wjt) • fd.o#32692, fd.o#30296, fd.o#41789: Gabble now implements the freshly-undrafted Protocol.Interface.Addressing and the still-unstable Connection.Interface.Addressing1, and uses them to expose Facebook contacts' integer IDs. (andrunko, eeejay) • fd.o#42446: Gabble can now be built on Android, using Androgenizer. (Derek Foreman, Alvaro Soliverez) Fixes: • capabilities.h and caps-channel-manager.h are no longer erroneously omitted. (albanc) telepathy-gabble 0.15.0 (2011-11-16) ==================================== This is the start of a new development branch that will lead to 0.16 in roughly six months. Dependencies: • telepathy-glib 0.17.1 is now required. Enhancements: • fd.o#42288: the Chan.I.FileTransfer.Metadata interface has been implemented. (jonny) • Updated Wocky: · The SASL auth server test now builds with new and old versions of libsasl2. Bug fixes: • fd.o#42706: fix a typo when indexing a pointer array by using the wrong counter! (jonny) • fd.o#32050: fix a crasher when using OLPC activities. (wjt) • Wocky: · fd.o#41719: don't bail on hashing caps if there's no FORM_TYPE. (jonny) · fdo#39057: Accept from="server.com" as stanzas coming from server. (xclaesse) telepathy-gabble 0.14.0 (2011-11-07) ==================================== The “Ironically, downforeveryoneorjustme.com is down.” release. This is the start of a glorious new stable series of Gabble! Major changes since 0.12.x which might disrupt your life: • Gabble implements the new Room, Subject and RoomConfig interfaces, and no longer supports the old, crummy Telepathy.Properties interface. • Gabble no longer supports the legacy Presence interace. • require-encryption is now enabled by default. • The plugin API has been extended; plugins for 0.12.x may need updating to compile and run with 0.14.x. Enhancements since 0.13.7: • It's now possible to install Gabble's test suite. (albanc) • fd.o#41417: when connected to Facebook, text channels now produce 'accepted' delivery reports when the user sends a message to a contact using another device. (wjt) Fixes since 0.13.7: • Compile against unstable GLib, where including glib/gtypes.h directly (instead of glib.h) is forbidden. (cosimoc) • Correct type mismatches for timestamps in muc-channel.c, which lead to Gabble not compiling correctly on 32-bit. (danni) • fd.o#41743: Offline contacts to whom we have a presence subscription and also a channel open at the time the roster arrives are now correctly shown as offline, rather than unknown. (wjt) • fd.o#42281: Actually implement the ContactBlocking interface. (cassidy) • fd.o#42186: Gabble no longer crashes if the server replies to its roster query IQ twice (as vk.com's XMPP server does). (wjt) • Various memory leak fixes. (andrunko) • Gabble now returns MAXINT64 for the subject timestamp when it is unknown, as per the latest spec. (danni) telepathy-gabble 0.13.7 (2011-10-12) ==================================== The “toast the air” release. Dependencies: • telepathy-glib 0.15.9 is now required. Enhancements: • fd.o#32611: Gabble now implements the Room, Subject and RoomConfig interfaces. It no longer implements old-style Telepathy.Properties on text channels. Applications which used those will need to be updated to use Subject and RoomConfig instead; they should be pleasantly surprised at how much easier the new APIs are to use. (Jonny, Will) • Plugins which implement channel managers may now expose capabilities from them, using the GabbleCapsChannelManager interface; other, less dramatic pieces of API for plugins have also been added. (Jonny) Fixes: • fd.o#41388: SASL authentication channels no longer claim that X-TELEPATHY-PASSWORD authentication is supported on servers that don't actually support authenticating with a password (such as the WLM server). (Xavier) • Wocky no longer confuses the realm and the server (which are usually, but not always, the same) when performing DIGEST-MD5 authentication. (Marco) • We now correctly submit the default configuration form to the MUC we have just created, rather than to the user's own server. (Will) telepathy-gabble 0.13.6 (2011-09-28) ==================================== The “grab yourself a bottle” release. Enhancements: • Video-capable Android phones are now considered to be video-capable by Gabble. (fd.o#36996, Olivier Crête) • Gabble now informs Google UIs that we have a camera connected if we have any video-capable user interfaces. While this is not strictly accurate, it's an improvement: they now show a little camera icon for us. (fd.o#40471, Olivier Crête) • Google proxy support—and hence the libsoup dependency—is now optional. (fd.o#40537, Mikhail Zabaluev) • Gabble no longer support the legacy Presence API. This reduces D-Bus traffic, particularly during login. (fd.o#40598, Will Thompson) • Gabble now signals all aliases from the roster in a single AliasesChanged signal. This *dramatically* reduces D-Bus traffic during login on services like Facebook. (fd.o#40943, Marco Barisione) Fixes: • Joining a password-protected chat room where someone else already has the nickname you wanted to use—a pretty obscure scenario—now works again. (fd.o#39790, Will Thompson) • Closing a Call channel immediately after it is created—specifically, before the Google relay server has had a chance to reply—no longer crashes Gabble. (fd.o#39768, Will Thompson) • The Google Talk setting controlling the sending of mail notifications is now explicitly enabled if a client is interested in mail notifications. (fd.o#40565, Jussi Kukkonen) telepathy-gabble 0.13.5 (2011-08-17) ==================================== This is a fixed version of the botched 0.13.4 release. telepathy-gabble 0.13.4 (2011-08-17) ==================================== The “giant iris of the wide blue sky” release. This contains all the fixes from telepathy-gabble 0.12.5, and other exciting things. Enhancements: • XEP-0012 (Last Activity) is implemented at long last. (Michael Scherer, fd.o#11688) • DecloakAutomatically is now enabled by default. This means that any contact who can IM you can also call you (if they're using Gabble). (Will Thompson) Fixes: • StreamTube services are now only advertised to the network, if a Client filter has a channel class with Requested=false (incoming tube) or no Requested property at all (tube with any direction) (Olli Salli, fd.o#39933) • Gabble no longer crashes if a peer advertises malformed capabilities. (David Laban) • The test suite's fake XMPP server now listens exclusively on localhost (Paul Seidler, fd.o#35968) • Invisibility now works on Google Talk again, following a server-side change. (Marco Barisione, fd.o#38706) • The DecloakAutomatically and PowerSavingActive properties are now decoupled, rather than being erroneously linked. (Will Thompson) • Only certain known PEP events are now queued when power saving is active, as opposed to all PEP events. (Siraj Rasick) telepathy-gabble 0.13.3 (2011-07-27) ==================================== The “I got 99 poplars” release. This contains all the fixes from telepathy-gabble 0.12.4, and more. Changes: • 'require-encryption' is now enabled by default. This seems like a reasonable thing to do in 2011. It will obviously cause connection errors if any of the user's configured Jabber servers do not support encryption. It may also cause problems if the user is connecting to servers with untrustworthy SSL certificates. When Gabble encounters a certificate it does not trust, it creates a ServerTLSConnection channel, in the hope that it will be handled by an application which can prompt the user. Empathy has a handler for such channels. On other platforms where no handler is available, if require-encryption is disabled, Gabble falls back to accepting those certificates (on the basis that encryption was being used opportunistically), but if it is enabled, Gabble falls back to rejecting them. • Relatedly, Gabble will itself accept certificates for domains listed in 'extra-certificate-identities' if they have no other problems. Previously it would include these identities in a property on the ServerTLSConnection channel, but do nothing with them itself. (Marco Barisione, fd.o#38749) Fixes: • If telepathy-yell is installed to /usr/include, the included copy will still be used. (Vincent Penquerc'h, fd.o#37492) • With GnuTLS 2.12, sending very large amounts of data will no longer cause the connection to stall. (Sjoerd Simons, fd.o#36077) • File transfer channels should now work on Windows. (Thomas Flüeli, fd.o#31621) telepathy-gabble 0.13.2 (2011-06-24) ==================================== The “Minotaur Shock” release. This includes all of the fixes from the 0.12.3 release (and no other changes since 0.13.1). Enhancements: • autogen.sh now honours the NOCONFIGURE environment variable, for compatibility with gnome-autogen.sh. (Colin Walters) Fixes: • Empty aliases stored on the roster are now ignored. Additionally, if we've cached a contact's JID as their alias on our roster (because we couldn't find a better alias), and we come across a better alias for the contact (via a PEP nickname push, or by fetching their vCard for some other reason), we now replace the JID with that better alias. (fd.o#27361, Will) • We now handle receiving unavailable presence from our contacts before we receive the roster correctly. Previously, this would cause those contacts to be stuck in state 'unknown' rather than 'offline' until they next came online. (fd.o#38603, Will) • Don't make assertions about TpChannelIface:handle-type's value in GabbleFileTransferChannel's set_property implementation. The property's not really writeable—the getter returns a constant value for it anyway—and this fixes a crash with telepathy-glib 0.15.2, in which the default value has changed from NONE to UNKNOWN. (Will) telepathy-gabble 0.13.1 (2011-06-16) ==================================== The “old piano” release. This includes all of the fixes from the 0.12.2 release (and no other changes since 0.13.0). Fixes: • We now interoperate with Google Mail's brand new implementation of Jingle. Some of the workarounds added are temporary, and some are not. (fd.o#38352, Will) • Compare codec names case-insensitively. (fd.o#38264, Olivier) telepathy-gabble 0.13.0 (2011-06-02) ==================================== The “Belgian Wake-Up Drill” release. This is the first release in an all-new unstable series of Gabble. Enhancements: • The draft Channel.Interface.Room is implemented. (Jonny) • IM and MUC channels now signal failure to send messages in some situations. (fd.o#33460, Siraj) telepathy-gabble 0.12.1 (2011-06-02) ==================================== The “Have you seen this cat?” release. Enhancements: • Spurious server-generated messages sent by the Google Mail client's “reply to email over chat” feature are now filtered out. (fd.o#36647, Chandni) Fixes: • No longer crashes if Disconnect() is called just before a connection declares itself Connected. (fd.o#37078, Simon) • No longer assumes Google's Queue extension is supported if Google's Roster extension is supported. (fd.o#36260, Will) • Many corner cases of the transitions to and from Away and Invisible when using Google's Shared Status extension have been fixed. (fd.o#37069, fd.o#36058, fd.o#37283, Marco) • You can now see your own avatar in MUCs. (fd.o#32017, Will) telepathy-gabble 0.12.0 (2011-04-21) ==================================== The “Is that Noam Chomsky on the telephone” release. This is the long-awaited start of the 0.12 stable branch. Dependencies: • telepathy-glib 0.14.5 is now required. Enhancements: • Plugins can now implement channel managers. (Jonny) Fixes: • No longer crashes when joining MUCs fails, or when kicked from a MUC. (Will, fd.o#35120) • Handles MUC nickname conflicts more accurately, resolving an issue triggered by a Google Talk server bug. (Will, fd.o#35619) • No longer gets contacts' client types confused (Jonny, fd.o#32139) • Exposes the status message length limit imposed by Google Talk, and truncates status messages if necessary. (André, fd.o#33054) telepathy-gabble 0.11.10 (2011-03-31) ===================================== The “I am going to bake a cake with beetroot in it” release. Dependencies: • telepathy-glib 0.14.3 is now required! Enhancements: • Non-urgent stanzas are now queued locally if PowerSaving is turned on but the google:queue extension is not available. (Senko) • The ReferenceIdentities property on server TLS channels is now implemented, populated partly from a new 'extra-certificate-identities' parameter. This allows Empathy/Gabble to safely trust the Google Talk server's certificate when using a JID hosted by Google Apps without prompting the user. (fd.o#35410 and fd.o#35415, Stef Walter) • Fatal criticals can now be turned off at configure time (fd.o#35840, Marco Barisione) • The new StreamHandler methods from spec 0.22.1 for rtcp-fb and rtp-hdrext are now implemented. (fd.o#34985, Olivier) Fixes: • `make check -C tests/twisted` now prints a helpful message if Twisted is not available, and the regular tests now pass if Twisted is not available. (Will) • Fix memory leaks in ServerTLSManager (fd.o#35395, Stef Walter) telepathy-gabble 0.11.9 (never) =============================== There is no telepathy-gabble 0.11.9. telepathy-gabble 0.11.8 (2011-03-14) ==================================== Enhancements: • Updated Wocky: · wocky_porter_register_handler() has been split into three functions: wocky_porter_register_handler_from(), wocky_porter_register_handler_from_server(), and wocky_porter_register_handler_from_anyone(). These make it harder to introduce bugs like fd.o#34048. · Since wocky_porter_register_handler() has been removed entirely, out-of-tree plugins may need to be updated to work with this release. Fixes: • fd.o#27693: Gabble no longer polls for contacts' geolocation information; it relies on PEP working properly. (wjt) • fd.o#35137: Gabble now sets the Overwritten_By_Nickname flag for appropriate contact info fields. (jonny) telepathy-gabble 0.11.7 (2011-02-16) ==================================== Dependencies: • telepathy-glib ≥ 0.13.12 is now required. Fixes: • fd.o#32390: Gabble now treats a request for a ContactSearch channel with Server set to the empty string as equivalent to not specifying a server, and rejects requests where the JID specified for Server is invalid. (wjt) • fd.o#32874: Offline contacts are now assumed to support 1–1 text channels. (jonnylamb) • fd.o#34048: Malicious contacts can no longer trick Gabble into relaying audio/video data via a server of their choosing. (wjt, sjoerd) Enhancements: • fd.o#32815: fallback-conference-server now defaults to conference.telepathy.im. Thus, if the user's server doesn't have a conference component configured, upgrading a 1-1 chat into an ad-hoc conference still works. • fd.o#11291: support for xep-0092, Software Version. (Robot101, Michael Scherer) • fd.o#33471: support for the FileTransfer.URI property. (cassidy) telepathy-gabble 0.11.6 (2011-01-27) ==================================== Interesting changes: • Generated code for the Call channel type has been moved to an external submodule called telepathy-yell. This is included in the source tarballs for telepathy-gabble (like how wocky is) so there should be no changes necessary for packagers. Dependencies: Fixes: • fd.o#19930: MUC channels are now only closed properly when the server acknowledges our request to leave. This fixes the case of trying to join a MUC you left only seconds ago before the server had a chance to notice. (jonny) • fd.o#26385: gabble's media stream code now uses TpDBusPropertiesMixin for increased maintainability. (jonny) • Include Protocol.AuthenticationTypes in the manager file. (jonny) Enhancements: • More miscellaneous updates to the Call channel type including: » Update to newest Call spec. (sjoerd) » Base classes have been extracted from Gabble code and moved into telepathy-yell. (wjt, sjoerd, alsuren) » Some test updates to ensure Call works as it should. (sjoerd) • The caps cache has been moved from Gabble to Wocky. (jonny) Test suite changes: • Tests no longer need to call Connect() and wait for the connection to connect but they can choose to do so if they need to. (jonny) • If a test hits an unhandled exception (a timeout, some other python error or perhaps a DBus error?) the queue switches off verbose mode, so users aren't forced to scroll up pages and pages of disconnection logs to find the error. (jonny) • with-session-bus.sh is now as silent as it can be without verbose mode turned on. (jonny) telepathy-gabble 0.11.5 (2011-01-10) ==================================== Fixes: • Support for Google Talk's google:queue extension now works against Google's server again. This regressed in 0.11.1, which changed gabble to send iq gets rather than iq sets. (wjt) • Fix crash caused by timing out items in the iq pipeline before sending them out. (sjoerd) • Correctly parse jingle transport-info message that refer to multiple contents. (sjoerd) • Updated Wocky: » Fix a bug in WockyHeartbeatSource which caused criticals when it was destroyed. (wjt) » Fix parsing of chat state messages in MUCs. (wjt) telepathy-gabble 0.11.4 (2010-12-14) ==================================== Potentially-incompatible changes: • Following telepathy-spec 0.21.5, the message-token field in message headers is no longer guaranteed to be globally-unique. This is known to be problematic for rtcom-event-logger, as used in Maemo 5. Dependencies: • telepathy-glib ≥ 0.13.9 is now required Enhancements: • Implement Protocol.AuthenticationTypes (jonny) • Improve error reporting in Group and StreamedMedia channels (eeejay) Fixes: • fd.o #29147: you will no longer get the scrollback from MUCs hosted by M-Link (and possibly other servers) when you change your presence (wjt) • fd.o #32278: fix an assertion failure when accepting X-TELEPATHY-PASSWORD authentication and immediately closing the channel (jonny) • Make the regression tests pass with telepathy-glib ≥ 0.13.8 (smcv) • Fix the build without system CA certificates (ptlo) telepathy-gabble 0.11.3 (2010-11-30) ==================================== The “Hey, I'm so desperately waiting for telepathy-gablle 0.11 that I can hardly eat.” release. Dependencies: • telepathy-glib >= 0.13.7 is now required. Enhancements: • fd.o #31106: add Protocol.Interface.Presence (vivek) • fd.o #31874: implement and refactor undrafted SASL interface (smcv) Fixes: • fd.o #31998: relax assertions about how fast AddMembers/RemoveMembers succeed (smcv) telepathy-gabble 0.11.2 (2010-11-22) ==================================== The “Please enjoy this refreshing beverage” release. Fixes: • fd.o #31412: fix crashes during disconnection if a PEP alias request is in-flight (smcv) • Loosen an assertion to fix test failure with telepathy-glib >= 0.13.5, which releases connections' object paths sooner (smcv) • fd.o #31772: fix a crash if a contact goes invisible while we're discovering their capabilities (wjt) • Remove accidentally re-added libuuid dependency (jonnylamb) • --with-ca-certificates, formerly known as --with-ca-file, now warns—rather than making configure fail—if the specified path does not exist. This is kinder to buildds which may not actually have the CAs installed. telepathy-gabble 0.11.1 (2010-11-16) ==================================== Enhancements: • Implement Conn.I.PowerSaving (eeejay) • fd.o #31474: replace --with-ca-certificates configure option with --with-ca-file. The default is to use the normal location for either Red Hat or Debian, or fail if neither exists. Use --with-ca-file to force a particular location (e.g. if your build system doesn't guarantee to have the CA certificate bundle installed), or use --without-ca-file if your system doesn't have a standard CA certificate store. (Brian Pepple) Fixes: • Don't generate unused bindings for channel bundles (wjt) • Update wocky snapshot · Add an optional dependency on libiphb and link it up to the main loop to provide non-regular XMPP pings where appropriate. (wjt) · Misc OpenSSL fixes. (wjt) telepathy-gabble 0.11.0 (2010-11-04) ==================================== The "your gmail theme is horrible and offensive" release. Enhancements: • Implement the ContactList, ContactGroups interfaces, refactoring GabbleRoster considerably in the process (smcv) • fd.o #28413: implement DTMF on StreamedMedia and Call channels, up-to-date for spec 0.21.3 (Jack Bates, smcv, jonny) • Implement the ClientTypes interface (jonny) • Make the tests considerably less prone to race conditions (sjoerd) • Adapt GabbleCapsChannelManager to be useful in Salut too (smcv) • fd.o #31216: implement the stable version of MailNotification (smcv) • fd.o #28726: use Google's Shared Status extension to support being invisible/hidden on Google Talk (eeejay) • Add a summary of how Tubes capabilities work (wjt) Fixes: • Poll GMail servers periodically for a short time after each mail notification, to work around the server throttling notifications (stormer) • fd.o #30950: advertise the immutable properties of Messages channels (smcv) • fd.o #28279: don't announce new Tubes channels until ready (sjoerd) telepathy-gabble 0.10.4 (2010-11-02) ==================================== The “Well, this super analysis i made!!!!! could be useful for you!!!!!!!!!!!!” release. There are plenty more bangs where these come from, I assure you. Enhancements: • fd.o #24775: implement local IPv4 address discovery on Windows, allowing out-of-band SOCKS5 bytestreams to work there (Thomas Flueeli) Fixes: • Updated Wocky snapshot: · Added HTTP proxy support. This is not technically a fix, but it's nice to have. ;-) (stormer) · Miscellaneous leak fixes. (stormer) · Miscellaneous other fixes. (wjt) • Prefer XEP-0186 invisibility over privacy list-based invisibility when both are available (eeejay) • fd.o #31197: improve portability to Windows (Thomas Flueeli) telepathy-gabble 0.10.3 (2010-10-06) ==================================== The "we ran out of bangs" release. Fixes: • Emit ActivitiesChanged when the server aknowledges our own update (tomeu) • Removed dependency to libuuid (stormer) • Slacker_message_filter can survive invalid/non-signal/unexpected messages (fledermaus) • Remove support for gadget (tomeu) telepathy-gabble 0.10.2 (2010-09-30) ==================================== The “I guess that you don't want to have a shared library!! because you fear to have a shared library!!!!!!!! yeah!!!” release. Fixes: • Updated Wocky snapshot: · Made it compile when built with OpenSSL (wjt) · Handle the connection being disconnected by the remote side more gracefully (sjoerd) · Make the XML parser more tolerant of recoverable errors in the incoming stream. In particular, this fixes an issue with illegal namespaces in some contacts' PEP nodes (sjoerd) • Fixed a memory allocation error which could cause corruption when using plugins which deal with capabilities (wjt) • Ensure only one OLPC activity is created for a given channel (tomeu) telepathy-gabble 0.10.1 (2010-09-29) ==================================== The “That command worked with no errors!!!!!!!!!!! but i don't know if it works for real!!!!!!!!!!!” release. Fixes: • From updated Wocky snapshot: · Check the return value of wocky_xmpp_connection_send_stanza_finish() in starttls_sent_cb() (wjt; found by Coverity) · Don't retry indefinitely on failed OpenSSL writes (fledermaus) • Don't leak local candidates, stun servers, and remote candidates when getting Stream/Endpoint properties (smcv) • Don't assume privacy list lists are well-formed (wjt; found by Coverity) • When a contact becomes unavailable, check the correct roster subscription attribute to decide whether their status should be Offline or Unknown (smcv) telepathy-gabble 0.10.0 (2010-09-16) ==================================== The "we demand additional lumber" release, starting the 0.10.x stable branch. Highlights since 0.8.x ---------------------- Gabble no longer uses Loudmouth for XMPP; instead, it contains a snapshot of Wocky, a new XMPP library based on GIO. Wocky will be split into a fully independent project when it reaches API stability. New features and significant bugfixes include: • Jingle calls now interoperate with Pidgin, Gajim, and XEP-0100 SIP gateways • file transfers now interoperate with the Google Talk desktop client • support for Google Talk PMUC chatrooms • contact information (vCards) on servers supporting XEP-0054 • searching for contacts on servers supporting XEP-0055 • improved capability-discovery support (ContactCapabilities) • compile-time selection of GNUTLS or OpenSSL for SSL support • support for JIDs with non-ASCII domains • experimental support for plugins • experimental support for multi-user Jingle calls (Muji) • compilation on Unix platforms other than Linux should now work Compatibility notes ------------------- • telepathy-glib 0.11.16, GLib 2.24, libxml 2, SQLite 3, libnice 0.0.11, libsoup and libuuid are required. • libsasl 2 is optional. • Either GNUTLS >= 2.8.2 or OpenSSL >= 0.9.8g is also required. • At the time of release, GNUTLS 2.8 is recommended. For versions 2.10.0 and 2.10.1, some fixes need to be backported from 2.11.1 to fix connection failures with "-59: GNUTLS_E_INTERNAL_ERROR" in the debug log; we're tracking this as . • If you use telepathy-mission-control, version 5.5.0 or later is required. A 5.6.x stable branch suitable for this will be released soon. Packagers, please make Gabble conflict with Mission Control < 5.5.0 (e.g. the Breaks or Conflicts relationships in dpkg), or if that's not possible in your packaging system, depend on Mission Control >= 5.5.0. Changes since 0.9.18 -------------------- • Fix a crash if the connection closes while verifying a TLS certificate (wjt) • Allow an in-progress connection to the server to be cancelled with Disconnect (cosimoc) • Add regression tests for TLS certificate verification (cosimoc) telepathy-gabble 0.9.18 (2010-09-13) ==================================== The “What, more work? Off I go then” release. Enhancements: • fd.o #30065: implement the stable API for ContactSearch (wjt) • Implement the stable API for interactive TLS certificate checking (cosimoc) • Implement the stable API for Conference channels (smcv) • Add the fallback-servers parameter, which can be used to make connections to Google Talk on some networks more reliable (stormer) • Prompt the user about unknown TLS certificates if neither require-encryption nor ignore-ssl-errors is set (cosimoc) Fixes: • Make the --with-ca-certificates configure option work (Vincent Untz) • fd.o #30117: always emit PresencesChanged when we change our own presence, and fix a related memory leak (wjt) • update our copy of Wocky: › fd.o#28051: SRV errors on some networks no longer break Facebook logins (wjt) › don't fail on missing CRLs if not in strict TLS verification mode (fledermaus) • fd.o#28990: removed dependency to libuuid (stormer) telepathy-gabble 0.9.17 (2010-08-26) ==================================== The "Mona Lisa overdrive" release. Enhancements: • Implement TLS certificate support (cosimoc) • Use TpBaseChannel instead of GabbleBaseChannel (wjt) • Plugin-provided, privacy-list based presence statuses (ptlo) Fixes: • fd.o #29721: start the presence unsure timeout once we connect (wjt) • fd.o #29000: make CA certificates path configurable (stormer) • Fix several leaks, improve tests in case of connection leaks (wjt) telepathy-gabble 0.9.16 (2010-08-19) ==================================== The “still at it” release. Enhancements: • Implement XEP-0126: Invisibility (wjt and eeejay) • Implement XEP-0186: Invisible Command (wjt and eeejay) • implement org.freedesktop.Telepathy.Protocol (ptlo) • Add a ActivityProperties.GetActivity method for retrieving an activity's room handle from its id (tomeu) • Add a BuddyInfo.AddActivity method so activities can advertise themselves without having to track all the other shared activities (tomeu) Fixes: • fd.o #25533: muc-factory: don't associate a request with tubes if a tube was requested (jonnylamb) • muc-factory: rearrange which NewChannels signals come first (jonnylamb) • Cope if Jingle sharing requests omit (wjt) • fd.o #29113: Skip file collection candidates with invalid addresses (wjt) • several improvements to the test suite (smcv) • fixes for building under gcc-4.5 (smcv) • adapt to the new TLS API (sjoerd) telepathy-gabble 0.9.15 (2010-06-30) ==================================== The “wave forlornly through the gates” release. Enhancements: ❖ in Wocky, build enum types for all error domains (smcv) Fixes: ❖ Debian #586936: in Wocky, clear the parser state when resetting, fixing assertion failures if the caps cache gets corrupted (sjoerd) ❖ fd.o #28712: in Wocky, fix SCRAM-SHA1 authentication by initializing state correctly (sjoerd) telepathy-gabble 0.9.14 (2010-06-22) ==================================== The "missing frame" release. Enhancements: • When a call is ended, mention who ended it in the debug log (wjt) Fixes: • fd.o #28599: ignore errors from disco#info query on our own bare JID, fixing connections to older versions of jabberd, jabberd2, Prosody etc., which regressed in 0.9.13 (wjt) • Use the self-handle as actor for changes to the 'deny' list, and improve roster regression tests, in preparation for future ContactList API (smcv) • Update Wocky snapshot: • fd.o #28643: stop applying GNUTLS_VERIFY_DO_NOT_ALLOW_SAME flag, allowing connection to (for instance) jabber.ccc.de (Lars Noschinski) • Apply more workarounds for TLS server misbehaviour (wjt) • fd.o #28647: be more tolerant if servers send an IQ reply without a 'from' attribute (smcv) • fd.o #27488: wocky_connector_connect_finish, _register_finish parameters have been re-ordered (smcv) telepathy-gabble 0.9.13 (2010-06-14) ==================================== The “But today, you ARE the law!” release. Dependencies: • libnice >= 0.0.11 is now required. • telepathy-glib >= 0.11.6 is now required. Enhancements: • Gabble is now able to exchange files with the Google Talk desktop client! (KaKaRoTo) • On Maemo, when connected to Google Talk, suppress presence updates while the device is idle. (wjt) • Plugins may now specify additional capabilities for Gabble to advertise, and add responses to new disco#info queries. (andrunko) • From Wocky: › The SCRAM-SHA-1 SASL mechanism is now supported. (sjoerd) Fixes: • From Wocky: › Messages from JIDs at non-ASCII domains are no longer silently dropped. (wjt) › Whether or not the connection is secure is now correctly propagated from Wocky up to Gabble. (eeejay) telepathy-gabble 0.9.12 (2010-06-03) ==================================== The “Show me the face of you” release. Dependencies: • libuuid is now required. Enhancements: • The capabilities cache now persists on-disk. (daf) • Multi-user calls are now supported, using the Muji protocol! Frabjous day, etc. Empathy does not yet support multi-user calls; there's a test client written in Python, but you'll need bleeding-edge Farsight to run it. (sjoerd, Maiku) • Support the /info contact attribute for ContactInfo. (andrunko) • XMPP keepalives are now sent and acked; this fixes the biggest remaining regression from 0.8! (ptlo) • When making outgoing calls, Gabble now prefers to call resources which claim to be phones. (fd.o#28265, wjt) • The current draft of SASL authentication channels is implemented, allowing channel handlers to support SASL mechanisms like X-GOOGLE-TOKEN. As a result, 'account' and 'password' are now optional connection parameters, since SASL mechanisms such as ANONYMOUS (see XEP-0175) require neither. (eeejay, danni) Fixes: • From Wocky: › Don't treat error replies from PEP nodes as PEP updates. (fd.o#28232, cassidy) › Don't drop backlog messages from former members of MUCs. (fd.o#27913, wjt) • Blocked contacts are no longer accidentally unblocked if you remove them from the 'stored' list. (fd.o#20597, wjt) • Gabble MailNotification.RequestInboxURL no longer returns an empty string in some situations (fd.o#27157, stormer) • Various fixes for issues found by Coverity (wjt) telepathy-gabble 0.9.11 (2010-04-26) ==================================== The “This place is a narrative mess” release. Enhancements: • The ContactInfo interface is now implemented! (andrunko, smcv) • Updated Wocky: · Permit ASCII mnemonics in wocky_stanza_build(), and detect more errors (wjt) · Renamed WockyXmpp{Stanza,Node} to Wocky{Stanza,Node}; add more helper code for building, storing, and serializing trees of nodes (sjoerd) · Refactored PubSub response distillery, and added support for retriving node configuration and modifying affiliates (wjt) Fixes: • fd.o#25775: crash in pep_reply_cb() (cassidy) • fd.o#27694: GetContactAttributes should return {} as location if there is no location for the contact (cassidy) telepathy-gabble 0.9.10 (2010-04-08) ==================================== The “Cartoons and Macramé Wounds” release. Enhancements: • Updated Wocky: - Improve Windows portability (Ole André Vadla Ravnås) - Fix a double-free (KaKaRoTo) - Even more exciting PubSub code (wjt) Fixes: • Properly expose more authentication failure errors from Wocky in Connection.StatusChanged (sjoerd) • Run tests in a temporary D-Bus session, which fixes fd.o#25816 (sjoerd) • Fix tests on machines with no ipv6 (sjoerd) • Avoid doing undefined void-pointer arithmetic (smcv) telepathy-gabble 0.9.9 (2010-03-26) =================================== The “It's release o'clock, quick, say something funny” release. Enhancements: • Update Wocky: - New and exiting Dataforms code! (wjt) • Various portability improvements (smcv) Fixes: • Fix mail notification on GTalk showing at most 30 unread mails (Nicolas) telepathy-gabble 0.9.8 (2010-03-16) =================================== The “Chat Roulette Funny Piano Improv #1” release. Enhancements: • Update Wocky: - Lots of exciting new Pubsub code! (cassidy, wjt) - Plus some crash fixes • Various correctness improvements to the test suite. (KaKaRoTo) Fixes: • No longer crashes on receiving a stanza error without an node, which was a regression in 0.9.5. (wjt) • We can now spell “received” and “separate”! (smcv, courtesy of Lintian) telepathy-gabble 0.9.7 (2010-03-03) =================================== The "Faraday cake" release. Dependencies: * Gabble now needs telepathy-glib ≥ 0.9.2 Enhancements: * Add support for draft 1 of Connection.Interface.MailNotification (stormer) * Improve regression test coverage (KaKaRoTo) * Update Wocky: - data-form utility code (cassidy) - node iterator utility functions (sjoerd) - better gtk-doc, improve portability (smcv) Fixes: * Implement the 'accuracy' key in Location, not the deprecated XEP-0080 'error' (which is not in telepathy-spec) (cassidy) telepathy-gabble 0.9.6 (2010-02-23) =================================== The "save us from this existential quagmire!" release. Enhancements: • The plugin loader is now built by default, and the gateway-registration plugin is installed by default. Since the API is still experimental and likely to change from one release to the next, the development headers etc. necessary to write third-party plugins are only installed if you configure with --enable-plugin-api (this is not recommended). (smcv) Fixes: • fd.o #26658: queue up any data received on the FileTransfer socket until the channel moves to state OPEN (KaKaRoTo) • Clean up signal connections in GabbleJingleFactory to avoid a possible use-after-free if a Jingle session, or the Connection, outlives the Jingle factory (KaKaRoTo) • Improve the gateway-registration plugin to exchange presence subscriptions and fix a memory leak (smcv) • Don't install the plugin unless the plugin loader has been enabled, and don't compile a useless static-library version of the plugin (smcv) telepathy-gabble 0.9.5 (2010-02-19) =================================== The "eclectic mandate whittling" release. Enhancements: • Update Wocky for improved error-wrangling code (wjt) Fixes: • When delaying a stream request until service discovery has finished in order to interpret capabilities, correctly resume the request after service discovery (smcv) • Calculate capabilities hash correctly when compiled with --enable-is-a-phone (wjt) • Don't emit a potentially-non-UTF-8 Google relay magic_cookie in debug output (Maiku) telepathy-gabble 0.9.4 (2010-01-28) =================================== The “Samuel Clemens had four cats!” release. Enhancements: • Added an --enable-is-a-phone configure switch, which makes Gabble identify itself as a "phone" (rather than a "pc") in XEP-0115 disco replies. (wjt) • Added preliminary support for the Channel.Type.Call draft. By default, incoming Jingle calls are exposed as Channel.Type.Call if a handler is available; pass --disable-channel-type-call to configure to always expose incoming calls as StreamedMedia. (sjoerd, Maiku) • One-to-one text channels can now be upgraded to multi-user conferences using the draft Conference interface. This will use a PMUC room on Google's conference server if at least one participant is using a Google Talk client, or an instant room on your own conference server (XEP-0045 §10.1.2) otherwise. (danni) • Implements XEP-0276, to permit calling contacts whose presence you are not subscribed to (controlled by a simple Gabble-specific connection interface). (smcv) • Plugins may now specify their own version number, for improved debug logs. (wjt) • Added a simple sidecar, implemented as a plugin, to support registering to gateways which require nothing more than a username and password. (smcv) Fixes: • We can now accept calls from, and place calls to, bare JIDs (that is, contacts with no resource). This is required for calling XEP-0100-compliant SIP gateways. (smcv) • We now deal correctly with setting a vCard if there's no existing vCard on the server. (fd.o#25987, wjt) • ignore-ssl-errors is now implied by require-encryption being false. This was a regression from 0.8. (wjt) • ContactCapabilities are now exposed on the Contacts interface with the correct attribute name. (fd.o#26210, wjt) telepathy-gabble 0.9.3 (2009-12-21) =================================== The ``TRANSPORT CHAOS threat level: PANDEMONIUM'' release Highlights: • Various updates to the OpenSSL backend causing it not to stall when reading multiple ssl records at the same time Enhancements: • Gabble now advertises a 'pmuc-v1' capability bundle, telling the Google Mail client that we support being invited to MUCs. (fd.o#22768, jonnylamb) • Rather than hard-coding a list of SOCKS5 proxy servers to use if the user's own XMPP server does not provide one, Gabble now falls back to a list of proxy servers returned by discoing the newly-set-up proxies.telepathy.im. (fd.o#25304, cassidy) • Various leaks fixed as pointed out by valgrind (daf, sjoerd) Fixes: • Do not re-publish our own vCard if nothing's changed (fd.o#25341, andrunko) • The default resource is now unique to the machine Gabble is running on, rather than being randomly generated for each connection. Fixes fd.o#23630: “Per-connection resource randomization increases the window in which you lose messages”. (wjt) • Always assume that MUC servers will let us change the subject, mitigating fd.o #13157; previously we assumed they would only let moderators change the subject. (smcv) • fd.o #21152: allow joining chatrooms on servers that don't respond to disco queries, like talk.google.com (jonny) • fd.o #22456: append fallback conference server if the room name has no @, rather than just failing (if using the Requests API) (jonny) • fd.o #22768 (partial): reassure talk.google.com that we can be invited to chatrooms. This enables us to be invited to private MUCs by the Google client, but doesn't currently support creating them. (jonny) • fd.o #24558: correctly flag the password parameter as secret (smcv) • Use TpDebugSender for debug output, instead of reimplementing it (jonny) • Allow sending chat state to contacts whose capabilities we don't know, such as invisible Google Talk users (wjt) • Only disco SOCKS5 proxies when they're needed for a file transfer or tube. (fd.o#21151, cassidy) • Do not advertise support for file transfers unless a handler is available. (fd.o#25243, smcv) • Do not re-publish our own vCard if nothing's changed (fd.o#25341, andrunko) telepathy-gabble 0.9.2 (2009-10-27) =================================== The ``The photo device is down'' release. Highlights: * Add support for using OpenSSL instead of GNUTLS for SSL support Fixes: * Honour errors that tell us to wait and try again when fetching vCards. This was erroneously claimed to be in 0.9.1. (Alban) * Don't re-fetch our own avatar in a loop when connecting to Google Talk. This should fix #23684 once and for all. (Alban) * Don't trust other people's IDs to be globally unique: in particular, Google Talk uses simple incrementing integers (wjt) * Use the correct marshaller for the pre-presence signal, fixing a cr 64-bit platforms (wjt) * Make sure the Connection object disappears from the bus when disconnected (Vivek) telepathy-gabble 0.9.1 (2009-09-25) =================================== The “even children are made of atoms” release. Highlights: * Jingle call interoperability with Pidgin and Gajim. Fixes: * When receiving a file, Gabble now closes the local socket once all the data has been written. (Guillaume) * fd.o #24043: Doesn't parse candidates in a Jingle session-accept stanza This fix lets us interoperate with Pidgin's Jingle implementation. (Sjoerd, David) * fd.o #24023: Accepting initial streams for a call is racy. (Sjoerd, Daf) * fd.o #20629: DBus events in tests should contain full path. (Daf) * fd.o #22795: jingle/google-relay.py is secretly made of cheese. (Daf) * fd.o #23903: Gabble crashes in File Transfer. (Guillaume) * fd.o #23685: build Gibber with fno-strict-aliasing so asyncns.c builds with new GCC. (Guillaume) * fd.o #20565: Contacts should be initially offline and not unknown. (Daf) * When members are removed from a call due to a stream error, always indicate so. (Daf) * Fix corner cases in SetLocation()'s language handling. (Daf) * fd.o #24195: Doesn't think clients without google p2p tranport are media capable. This fix lets us interoperate with Gajim's Jingle implementation. (Sjoerd, Daf) * Make stun-server.py not fail if the default STUN server hostname can't be resolved. (David) * fd.o #23684: Gabble advertizes an avatar's sha1 in its presence stanza without following XEP-0153. (Alban) * Honour errors that tell us to wait and try again when fetching vCards. (Alban) telepathy-gabble 0.9.0 (2009-09-16) =================================== The "Use STAPLE REMOVER on TREMENDOUS DANGEROUS-LOOKING YAK" release. This is the first release in the 0.9 development branch. Most users should continue to use the 0.8.x stable branch for now. This release introduces some regressions: proxies and keep-alive aren't supported any more. These features will be back in future releases. Dependencies: * Gabble doesn't depend on loudmouth anymore. Instead, it ships a copy of Wocky, a new XMPP library based on gio. As a side effect of this, gio >= 2.21 and gnutls >= 2.8.2 are now needed to build Gabble. * telepathy-glib >= 0.7.37 is now required Enhancements: * Add the ability to send a message when terminating a VoIP call (wjt) * Add ContactSearch channels using spec draft 2 (wjt, cassidy) * Implement the final ContactCapabilities spec, and refactor Capabilities code to represent capabilities as sets of XML namespaces, rather than bitfields (wjt, smcv) * fd.o#19952: Support requesting channels with InitialAudio/InitialVideo through the final API from telepathy-spec 0.17.28 (smcv) * Gabble now loads certificates from ~/.config/telepathy/certs/ as well as from the system-wide location (/etc/ssl/certs/ca-certificates.crt). Fixes: * Improve pubsub.c test coverage (cassidy) * fd.o #22968: don't try to pass credentials through Unix sockets on non-Linux, since the way we currently do it is known to be non-portable. Patches to implement Credentials on more OSs would be welcomed. (smcv) telepathy-gabble 0.8.5 (2009-10-02) =================================== The “a page out of Remembrance of Things Past and a blowtorch with which to set it on fire” release. Fixes: * Don't re-fetch our own avatar in a loop when connecting to Google Talk. This should fix #23684 once and for all. (Alban) * Fix a crash introduced by the vCard-related fixes in 0.8.4. (Alban) telepathy-gabble 0.8.4 (2009-09-25) =================================== The “bourgeois traditional omelette form” release. Highlights: * Jingle call interoperability with Pidgin and Gajim. Fixes: * When receiving a file, Gabble now closes the local socket once all the data has been written. (Guillaume) * fd.o #24043: Doesn't parse candidates in a Jingle session-accept stanza This fix lets us interoperate with Pidgin's Jingle implementation. (Sjoerd, David) * fd.o #24023: Accepting initial streams for a call is racy. (Sjoerd, Daf) * fd.o #20629: DBus events in tests should contain full path. (Daf) * fd.o #22795: jingle/google-relay.py is secretly made of cheese. (Daf) * fd.o #23903: Gabble crashes in File Transfer. (Guillaume) * fd.o #23685: build Gibber with fno-strict-aliasing so asyncns.c builds with new GCC. (Guillaume) * fd.o #20565: Contacts should be initially offline and not unknown. (Daf) * When members are removed from a call due to a stream error, always indicate so. (Daf) * Fix corner cases in SetLocation()'s language handling. (Daf) * fd.o #24195: Doesn't think clients without google p2p tranport are media capable. This fix lets us interoperate with Gajim's Jingle implementation. (Sjoerd, Daf) * Make stun-server.py not fail if the default STUN server hostname can't be resolved. (David) * fd.o #23684: Gabble advertizes an avatar's sha1 in its presence stanza without following XEP-0153. (Alban) * Honour errors that tell us to wait and try again when fetching vCards. (Alban) telepathy-gabble 0.8.3 (2009-09-10) =================================== The “one cigarette, some coffee, and four tiny stones” release. Enhancements: * fd.o #23681: Allow setting presence on a connection before it goes online. This avoids e.g. going from Available -> Busy immediately when signing on. (daf) Fixes: * fd.o #23684: fix handling of avatar conflict with several resources, and a possible infinite ping-pong of presence stanzas from the server (albanc) * Time out disco requests after 20, not 20,000, seconds! (grundleborg) * Correctly respond to disco requests for video-v1 bundle, avoiding a loop when iChat blindly retries failed disco requests (smcv) * Fix Requested and State properties of muc D-Bus tubes that we previously created and are still present when we re-join the muc. These tubes are now listed in Tubes.ListTubes(). fd.o #23678. (cassidy) * Don't send the same disco request to the same (full) JID more than once. fd.o #23841. (wjt) * Update the Jingle raw-udp and ice-udp namespaces we claim to support to the current version. (wjt) * fd.o #23348, #23349: fix compilation on NetBSD by including more headers (Thomas Klausner) * fd.o #21327: force ISO date format in ChangeLog (wjt) * Reduce the size of the ChangeLog by truncating at version 0.6.0 and not including diffstats (previously, the changelog.gz in our Debian packages was larger than Gabble itself!) (smcv) telepathy-gabble 0.8.2 (2009-09-03) =================================== The “tape two fried eggs over your eyes and walk the streets of Paris for an hour” release. Enhancements: * Improve jid validation, so that obviously-invalid jids are rejected. (daf) Fixes: * Don't crash when a vCard set fails, and there are edits pending. This can happen if you're trying to set your avatar and then disconnect. (daf) * fd.o#23013: ContactCapabilities.SetSelfCapabilities can crash gabble with wrong parms (sjoerd) * Fix parsing of incoming session accept from Google Video Chat. This should make outgoing calls to Google Video Chat users work, as well as incoming calls. Hooray! (wjt, with help from sjoerd and Olivier Crête) telepathy-gabble 0.8.1 (2009-08-20) =================================== The “five pounds of cherries and a live beaver” release. Fixes: * fd.o#22535: Gabble no longer crashes if you disconnect while it's trying to start a Google relay session for a call. This should have been fixed in 0.7.31, but it's really fixed now. :-) (wjt) * Fix an occasional crash when PEP requests time out, or are cancelled when you disconnect. (daf) * Correct an assertion about vCard edits not to fire incorrectly. (daf) * Clarify some correct-but-confusing behaviour in libjingle 0.3 mode, which fixes a Coverity false-positive. (smcv) telepathy-gabble 0.8.0 (2009-08-18) =================================== The “place a chair facing the oven and sit in it forever” release. This is the first release in the 0.8 stable series. Dependencies: * telepathy-glib >= 0.7.34 is now required as Gabble implements the Location API. Enhancements: * Location and Debug are now implemented as stable interfaces. * Timeouts are synchronised to the second where possible, leading to fewer wakeups. Fixes: * Fix race condition introduced by fix for fd.o #22023. * Make vCard request less likely to time out. * Fix a bug where a vCard request failure could cause SetAvatar or SetAliases not to return. telepathy-gabble-0.18.2/INSTALL0000644000175000017500000003661012312536073016056 0ustar00smcvsmcv00000000000000Installation Instructions ************************* Copyright (C) 1994-1996, 1999-2002, 2004-2013 Free Software Foundation, Inc. Copying and distribution of this file, with or without modification, are permitted in any medium without royalty provided the copyright notice and this notice are preserved. This file is offered as-is, without warranty of any kind. Basic Installation ================== Briefly, the shell command `./configure && make && make install' should configure, build, and install this package. The following more-detailed instructions are generic; see the `README' file for instructions specific to this package. Some packages provide this `INSTALL' file but do not implement all of the features documented below. The lack of an optional feature in a given package is not necessarily a bug. More recommendations for GNU packages can be found in *note Makefile Conventions: (standards)Makefile Conventions. The `configure' shell script attempts to guess correct values for various system-dependent variables used during compilation. It uses those values to create a `Makefile' in each directory of the package. It may also create one or more `.h' files containing system-dependent definitions. Finally, it creates a shell script `config.status' that you can run in the future to recreate the current configuration, and a file `config.log' containing compiler output (useful mainly for debugging `configure'). It can also use an optional file (typically called `config.cache' and enabled with `--cache-file=config.cache' or simply `-C') that saves the results of its tests to speed up reconfiguring. Caching is disabled by default to prevent problems with accidental use of stale cache files. If you need to do unusual things to compile the package, please try to figure out how `configure' could check whether to do them, and mail diffs or instructions to the address given in the `README' so they can be considered for the next release. If you are using the cache, and at some point `config.cache' contains results you don't want to keep, you may remove or edit it. The file `configure.ac' (or `configure.in') is used to create `configure' by a program called `autoconf'. You need `configure.ac' if you want to change it or regenerate `configure' using a newer version of `autoconf'. The simplest way to compile this package is: 1. `cd' to the directory containing the package's source code and type `./configure' to configure the package for your system. Running `configure' might take a while. While running, it prints some messages telling which features it is checking for. 2. Type `make' to compile the package. 3. Optionally, type `make check' to run any self-tests that come with the package, generally using the just-built uninstalled binaries. 4. Type `make install' to install the programs and any data files and documentation. When installing into a prefix owned by root, it is recommended that the package be configured and built as a regular user, and only the `make install' phase executed with root privileges. 5. Optionally, type `make installcheck' to repeat any self-tests, but this time using the binaries in their final installed location. This target does not install anything. Running this target as a regular user, particularly if the prior `make install' required root privileges, verifies that the installation completed correctly. 6. You can remove the program binaries and object files from the source code directory by typing `make clean'. To also remove the files that `configure' created (so you can compile the package for a different kind of computer), type `make distclean'. There is also a `make maintainer-clean' target, but that is intended mainly for the package's developers. If you use it, you may have to get all sorts of other programs in order to regenerate files that came with the distribution. 7. Often, you can also type `make uninstall' to remove the installed files again. In practice, not all packages have tested that uninstallation works correctly, even though it is required by the GNU Coding Standards. 8. Some packages, particularly those that use Automake, provide `make distcheck', which can by used by developers to test that all other targets like `make install' and `make uninstall' work correctly. This target is generally not run by end users. Compilers and Options ===================== Some systems require unusual options for compilation or linking that the `configure' script does not know about. Run `./configure --help' for details on some of the pertinent environment variables. You can give `configure' initial values for configuration parameters by setting variables in the command line or in the environment. Here is an example: ./configure CC=c99 CFLAGS=-g LIBS=-lposix *Note Defining Variables::, for more details. Compiling For Multiple Architectures ==================================== You can compile the package for more than one kind of computer at the same time, by placing the object files for each architecture in their own directory. To do this, you can use GNU `make'. `cd' to the directory where you want the object files and executables to go and run the `configure' script. `configure' automatically checks for the source code in the directory that `configure' is in and in `..'. This is known as a "VPATH" build. With a non-GNU `make', it is safer to compile the package for one architecture at a time in the source code directory. After you have installed the package for one architecture, use `make distclean' before reconfiguring for another architecture. On MacOS X 10.5 and later systems, you can create libraries and executables that work on multiple system types--known as "fat" or "universal" binaries--by specifying multiple `-arch' options to the compiler but only a single `-arch' option to the preprocessor. Like this: ./configure CC="gcc -arch i386 -arch x86_64 -arch ppc -arch ppc64" \ CXX="g++ -arch i386 -arch x86_64 -arch ppc -arch ppc64" \ CPP="gcc -E" CXXCPP="g++ -E" This is not guaranteed to produce working output in all cases, you may have to build one architecture at a time and combine the results using the `lipo' tool if you have problems. Installation Names ================== By default, `make install' installs the package's commands under `/usr/local/bin', include files under `/usr/local/include', etc. You can specify an installation prefix other than `/usr/local' by giving `configure' the option `--prefix=PREFIX', where PREFIX must be an absolute file name. You can specify separate installation prefixes for architecture-specific files and architecture-independent files. If you pass the option `--exec-prefix=PREFIX' to `configure', the package uses PREFIX as the prefix for installing programs and libraries. Documentation and other data files still use the regular prefix. In addition, if you use an unusual directory layout you can give options like `--bindir=DIR' to specify different values for particular kinds of files. Run `configure --help' for a list of the directories you can set and what kinds of files go in them. In general, the default for these options is expressed in terms of `${prefix}', so that specifying just `--prefix' will affect all of the other directory specifications that were not explicitly provided. The most portable way to affect installation locations is to pass the correct locations to `configure'; however, many packages provide one or both of the following shortcuts of passing variable assignments to the `make install' command line to change installation locations without having to reconfigure or recompile. The first method involves providing an override variable for each affected directory. For example, `make install prefix=/alternate/directory' will choose an alternate location for all directory configuration variables that were expressed in terms of `${prefix}'. Any directories that were specified during `configure', but not in terms of `${prefix}', must each be overridden at install time for the entire installation to be relocated. The approach of makefile variable overrides for each directory variable is required by the GNU Coding Standards, and ideally causes no recompilation. However, some platforms have known limitations with the semantics of shared libraries that end up requiring recompilation when using this method, particularly noticeable in packages that use GNU Libtool. The second method involves providing the `DESTDIR' variable. For example, `make install DESTDIR=/alternate/directory' will prepend `/alternate/directory' before all installation names. The approach of `DESTDIR' overrides is not required by the GNU Coding Standards, and does not work on platforms that have drive letters. On the other hand, it does better at avoiding recompilation issues, and works well even when some directory options were not specified in terms of `${prefix}' at `configure' time. Optional Features ================= If the package supports it, you can cause programs to be installed with an extra prefix or suffix on their names by giving `configure' the option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'. Some packages pay attention to `--enable-FEATURE' options to `configure', where FEATURE indicates an optional part of the package. They may also pay attention to `--with-PACKAGE' options, where PACKAGE is something like `gnu-as' or `x' (for the X Window System). The `README' should mention any `--enable-' and `--with-' options that the package recognizes. For packages that use the X Window System, `configure' can usually find the X include and library files automatically, but if it doesn't, you can use the `configure' options `--x-includes=DIR' and `--x-libraries=DIR' to specify their locations. Some packages offer the ability to configure how verbose the execution of `make' will be. For these packages, running `./configure --enable-silent-rules' sets the default to minimal output, which can be overridden with `make V=1'; while running `./configure --disable-silent-rules' sets the default to verbose, which can be overridden with `make V=0'. Particular systems ================== On HP-UX, the default C compiler is not ANSI C compatible. If GNU CC is not installed, it is recommended to use the following options in order to use an ANSI C compiler: ./configure CC="cc -Ae -D_XOPEN_SOURCE=500" and if that doesn't work, install pre-built binaries of GCC for HP-UX. HP-UX `make' updates targets which have the same time stamps as their prerequisites, which makes it generally unusable when shipped generated files such as `configure' are involved. Use GNU `make' instead. On OSF/1 a.k.a. Tru64, some versions of the default C compiler cannot parse its `' header file. The option `-nodtk' can be used as a workaround. If GNU CC is not installed, it is therefore recommended to try ./configure CC="cc" and if that doesn't work, try ./configure CC="cc -nodtk" On Solaris, don't put `/usr/ucb' early in your `PATH'. This directory contains several dysfunctional programs; working variants of these programs are available in `/usr/bin'. So, if you need `/usr/ucb' in your `PATH', put it _after_ `/usr/bin'. On Haiku, software installed for all users goes in `/boot/common', not `/usr/local'. It is recommended to use the following options: ./configure --prefix=/boot/common Specifying the System Type ========================== There may be some features `configure' cannot figure out automatically, but needs to determine by the type of machine the package will run on. Usually, assuming the package is built to be run on the _same_ architectures, `configure' can figure that out, but if it prints a message saying it cannot guess the machine type, give it the `--build=TYPE' option. TYPE can either be a short name for the system type, such as `sun4', or a canonical name which has the form: CPU-COMPANY-SYSTEM where SYSTEM can have one of these forms: OS KERNEL-OS See the file `config.sub' for the possible values of each field. If `config.sub' isn't included in this package, then this package doesn't need to know the machine type. If you are _building_ compiler tools for cross-compiling, you should use the option `--target=TYPE' to select the type of system they will produce code for. If you want to _use_ a cross compiler, that generates code for a platform different from the build platform, you should specify the "host" platform (i.e., that on which the generated programs will eventually be run) with `--host=TYPE'. Sharing Defaults ================ If you want to set default values for `configure' scripts to share, you can create a site shell script called `config.site' that gives default values for variables like `CC', `cache_file', and `prefix'. `configure' looks for `PREFIX/share/config.site' if it exists, then `PREFIX/etc/config.site' if it exists. Or, you can set the `CONFIG_SITE' environment variable to the location of the site script. A warning: not all `configure' scripts look for a site script. Defining Variables ================== Variables not defined in a site shell script can be set in the environment passed to `configure'. However, some packages may run configure again during the build, and the customized values of these variables may be lost. In order to avoid this problem, you should set them in the `configure' command line, using `VAR=value'. For example: ./configure CC=/usr/local2/bin/gcc causes the specified `gcc' to be used as the C compiler (unless it is overridden in the site shell script). Unfortunately, this technique does not work for `CONFIG_SHELL' due to an Autoconf limitation. Until the limitation is lifted, you can use this workaround: CONFIG_SHELL=/bin/bash ./configure CONFIG_SHELL=/bin/bash `configure' Invocation ====================== `configure' recognizes the following options to control how it operates. `--help' `-h' Print a summary of all of the options to `configure', and exit. `--help=short' `--help=recursive' Print a summary of the options unique to this package's `configure', and exit. The `short' variant lists options used only in the top level, while the `recursive' variant lists options also present in any nested packages. `--version' `-V' Print the version of Autoconf used to generate the `configure' script, and exit. `--cache-file=FILE' Enable the cache: use and save the results of the tests in FILE, traditionally `config.cache'. FILE defaults to `/dev/null' to disable caching. `--config-cache' `-C' Alias for `--cache-file=config.cache'. `--quiet' `--silent' `-q' Do not print messages saying which checks are being made. To suppress all normal output, redirect it to `/dev/null' (any error messages will still be shown). `--srcdir=DIR' Look for the package's source code in directory DIR. Usually `configure' can determine that directory automatically. `--prefix=DIR' Use DIR as the installation prefix. *note Installation Names:: for more details, including other options available for fine-tuning the installation locations. `--no-create' `-n' Run the configure checks, but stop before creating any output files. `configure' also accepts some other, not widely useful, options. Run `configure --help' for more details. telepathy-gabble-0.18.2/tools/0000755000175000017500000000000012312537047016161 5ustar00smcvsmcv00000000000000telepathy-gabble-0.18.2/tools/xincludator.py0000644000175000017500000000241312200204333021050 0ustar00smcvsmcv00000000000000#!/usr/bin/python from sys import argv, stdout, stderr import codecs, locale import os import xml.dom.minidom stdout = codecs.getwriter('utf-8')(stdout) NS_XI = 'http://www.w3.org/2001/XInclude' def xincludate(dom, base, dropns = []): remove_attrs = [] for i in xrange(dom.documentElement.attributes.length): attr = dom.documentElement.attributes.item(i) if attr.prefix == 'xmlns': if attr.localName in dropns: remove_attrs.append(attr) else: dropns.append(attr.localName) for attr in remove_attrs: dom.documentElement.removeAttributeNode(attr) for include in dom.getElementsByTagNameNS(NS_XI, 'include'): href = include.getAttribute('href') # FIXME: assumes Unixy paths filename = os.path.join(os.path.dirname(base), href) subdom = xml.dom.minidom.parse(filename) xincludate(subdom, filename, dropns) if './' in href: subdom.documentElement.setAttribute('xml:base', href) include.parentNode.replaceChild(subdom.documentElement, include) if __name__ == '__main__': argv = argv[1:] dom = xml.dom.minidom.parse(argv[0]) xincludate(dom, argv[0]) xml = dom.toxml() stdout.write(xml) stdout.write('\n') telepathy-gabble-0.18.2/tools/with-session-bus.sh0000755000175000017500000000452612200204333021733 0ustar00smcvsmcv00000000000000#!/bin/sh # with-session-bus.sh - run a program with a temporary D-Bus session daemon # # The canonical location of this program is the telepathy-glib tools/ # directory, please synchronize any changes with that copy. # # Copyright (C) 2007-2008 Collabora Ltd. # # Copying and distribution of this file, with or without modification, # are permitted in any medium without royalty provided the copyright # notice and this notice are preserved. set -e me=with-session-bus dbus_daemon_args="--print-address=5 --print-pid=6 --fork" sleep=0 usage () { echo "usage: $me [options] -- program [program_options]" >&2 echo "Requires write access to the current directory." >&2 echo "" >&2 echo "If \$WITH_SESSION_BUS_FORK_DBUS_MONITOR is set, fork dbus-monitor" >&2 echo "with the arguments in \$WITH_SESSION_BUS_FORK_DBUS_MONITOR_OPT." >&2 echo "The output of dbus-monitor is saved in $me-.dbus-monitor-logs" >&2 exit 2 } while test "z$1" != "z--"; do case "$1" in --sleep=*) sleep="$1" sleep="${sleep#--sleep=}" shift ;; --session) dbus_daemon_args="$dbus_daemon_args --session" shift ;; --config-file=*) # FIXME: assumes config file doesn't contain any special characters dbus_daemon_args="$dbus_daemon_args $1" shift ;; *) usage ;; esac done shift if test "z$1" = "z"; then usage; fi exec 5> $me-$$.address exec 6> $me-$$.pid cleanup () { pid=`head -n1 $me-$$.pid` if test -n "$pid" ; then if [ -n "$VERBOSE_TESTS" ]; then echo "Killing temporary bus daemon: $pid" >&2 fi kill -INT "$pid" fi rm -f $me-$$.address rm -f $me-$$.pid } trap cleanup INT HUP TERM dbus-daemon $dbus_daemon_args if [ -n "$VERBOSE_TESTS" ]; then { echo -n "Temporary bus daemon is "; cat $me-$$.address; } >&2 { echo -n "Temporary bus daemon PID is "; head -n1 $me-$$.pid; } >&2 fi e=0 DBUS_SESSION_BUS_ADDRESS="`cat $me-$$.address`" export DBUS_SESSION_BUS_ADDRESS DBUS_SESSION_BUS_PID="`cat $me-$$.pid`" export DBUS_SESSION_BUS_PID if [ -n "$WITH_SESSION_BUS_FORK_DBUS_MONITOR" ] ; then echo -n "Forking dbus-monitor $WITH_SESSION_BUS_FORK_DBUS_MONITOR_OPT" >&2 dbus-monitor $WITH_SESSION_BUS_FORK_DBUS_MONITOR_OPT \ > $me-$$.dbus-monitor-logs 2>&1 & fi "$@" || e=$? if test $sleep != 0; then sleep $sleep fi trap - INT HUP TERM cleanup exit $e telepathy-gabble-0.18.2/tools/test-wrapper.sh0000755000175000017500000000144712200204333021144 0ustar00smcvsmcv00000000000000#!/bin/sh # Make tests shut up. On success, if stdout is a tty, we only output messages # about skipped tests; on failure, or if stdout is a file or pipe, we output # the lot. # # Usage: test-wrapper.sh PROGRAM [ARGS...] set -e if test -t 1 && test "z$CHECK_VERBOSE" = z; then : # continue with the output-suppressed code path, below else "$@" || e=$? exit $e fi e=0 "$@" > capture-$$.log 2>&1 || e=$? if test z$e = z0; then grep -i skipped capture-$$.log || true rm -f capture-$$.log else cat capture-$$.log exit $e fi # Copyright © 2010 Collabora Ltd. # Copying and distribution of this file, with or without modification, # are permitted in any medium without royalty provided the copyright # notice and this notice are preserved. There is no warranty. telepathy-gabble-0.18.2/tools/telepathy-glib-env.in0000644000175000017500000000043712200204333022176 0ustar00smcvsmcv00000000000000#!/bin/sh abs_top_builddir="@abs_top_builddir@" export abs_top_builddir LD_LIBRARY_PATH="${abs_top_builddir}/telepathy-glib/.libs${LD_LIBRARY_PATH:+":${LD_LIBRARY_PATH}"}" export LD_LIBRARY_PATH G_DEBUG="fatal_criticals,fatal_warnings${G_DEBUG:+",${G_DEBUG}"}" export G_DEBUG exec "$@" telepathy-gabble-0.18.2/tools/telepathy-glib.supp0000644000175000017500000001451312200204333021771 0ustar00smcvsmcv00000000000000# Valgrind error suppression file # ============================= libc ================================== { ld.so initialization + selinux Memcheck:Leak ... fun:_dl_init obj:/lib/ld-*.so } { dlopen initialization, triggered by handle-leak-debug code Memcheck:Leak ... fun:__libc_dlopen_mode fun:init fun:backtrace fun:handle_leak_debug_bt fun:dynamic_ensure_handle fun:tp_handle_ensure } # default.supp has these for 2.10, but they're too specific { Debian libc6 (2.10.x, 2.11.x) stripped dynamic linker Memcheck:Cond fun:index fun:expand_dynamic_string_token fun:_dl_map_object fun:map_doit fun:_dl_catch_error fun:do_preload fun:dl_main fun:_dl_sysdep_start fun:_dl_start obj:/lib/ld-*.so } { Debian libc6 (2.9.x - 2.11.x) stripped dynamic linker Memcheck:Cond fun:_dl_relocate_object fun:dl_main fun:_dl_sysdep_start fun:_dl_start obj:/lib/ld-*.so } { ld.so initialization on glibc 2.9 Memcheck:Cond fun:strlen fun:_dl_init_paths fun:dl_main fun:_dl_sysdep_start fun:_dl_start obj:/lib/ld-2.9.so } # ======================= libselinux on Debian amd64 ===================== { I have no idea what SELinux is doing but it's not my problem Memcheck:Cond ... obj:/lib/libselinux.so.1 obj:/lib/libselinux.so.1 obj:/lib/libselinux.so.1 } { I have no idea what SELinux is doing but it's not my problem Memcheck:Value8 ... obj:/lib/libselinux.so.1 obj:/lib/libselinux.so.1 obj:/lib/libselinux.so.1 } { I have no idea what SELinux is doing but it's not my problem Memcheck:Leak ... obj:/lib/libselinux.so.1 obj:/lib/libselinux.so.1 obj:/lib/libselinux.so.1 } # ============================= GLib ================================== { g_set_prgname copies its argument Memcheck:Leak ... fun:g_set_prgname } { one g_get_charset per child^Wprocess Memcheck:Leak ... fun:g_get_charset } { one g_get_home_dir per process Memcheck:Leak ... fun:g_get_home_dir } { GQuarks can't be freed Memcheck:Leak ... fun:g_quark_from_static_string } { GQuarks can't be freed Memcheck:Leak ... fun:g_quark_from_string } { interned strings can't be freed Memcheck:Leak ... fun:g_intern_string } { interned strings can't be freed Memcheck:Leak ... fun:g_intern_static_string } { shared global default g_main_context Memcheck:Leak ... fun:g_main_context_new fun:g_main_context_default } { GTest initialization Memcheck:Leak ... fun:g_test_init fun:main } { GTest admin Memcheck:Leak ... fun:g_test_add_vtable } { GTest pseudorandomness Memcheck:Leak ... fun:g_rand_new_with_seed_array fun:test_run_seed ... fun:g_test_run } { GSLice initialization Memcheck:Leak ... fun:g_malloc0 fun:g_slice_init_nomessage fun:g_slice_alloc } # ============================= GObject =============================== { g_type_init Memcheck:Leak ... fun:g_type_init } { g_type_init_with_debug_flags Memcheck:Leak ... fun:g_type_init_with_debug_flags } { g_type_register_static Memcheck:Leak ... fun:g_type_register_static } { g_type_add_interface_static Memcheck:Leak ... fun:g_type_add_interface_static } { initialization of interfaces Memcheck:Leak ... fun:type_iface_vtable_base_init_Wm fun:g_type_class_ref } # ============================= GIO =================================== { GIO init Memcheck:Leak ... fun:g_inet_address_class_intern_init } { g_simple_async_result class Memcheck:Leak ... fun:g_type_class_ref ... fun:g_simple_async_result_new } # ============================= dbus-glib ============================= { registering marshallers is permanent Memcheck:Leak ... fun:dbus_g_object_register_marshaller_array fun:dbus_g_object_register_marshaller } { dbus-glib specialized GTypes are permanent Memcheck:Leak ... fun:dbus_g_type_specialized_init } { libdbus shared connection Memcheck:Leak ... fun:dbus_g_bus_get } { dbus-gobject registrations aren't freed unless we fall off the bus Memcheck:Leak ... fun:g_slist_append fun:dbus_g_connection_register_g_object } { DBusGProxy slots aren't freed unless we fall off the bus Memcheck:Leak ... fun:dbus_connection_allocate_data_slot ... fun:dbus_g_proxy_constructor } { error registrations are for life, not just for Christmas Memcheck:Leak ... fun:dbus_g_error_domain_register } { DBusGProxy class init Memcheck:Leak ... fun:dbus_g_proxy_class_init } # ============================= telepathy-glib ======================== { tp_dbus_daemon_constructor @daemons once per DBusConnection Memcheck:Leak ... fun:g_slice_alloc fun:tp_dbus_daemon_constructor } { tp_proxy_subclass_add_error_mapping refs the enum Memcheck:Leak ... fun:g_type_class_ref fun:tp_proxy_subclass_add_error_mapping } { tp_proxy_or_subclass_hook_on_interface_add never frees its list Memcheck:Leak ... fun:tp_proxy_or_subclass_hook_on_interface_add } { tp_dbus_daemon_constructor filter not freed til we fall off the bus Memcheck:Leak ... fun:dbus_connection_add_filter fun:tp_dbus_daemon_constructor } { tp_g_socket_address_from_variant reffing GNIO types Memcheck:Leak ... fun:g_type_class_ref ... fun:tp_g_socket_address_from_variant } { creating classes for DBusGProxy Memcheck:Leak ... fun:g_type_class_ref ... fun:g_object_new ... fun:tp_proxy_borrow_interface_by_id } { creating classes for tp_dbus_daemon_new Memcheck:Leak ... fun:g_type_class_ref ... fun:g_object_new ... fun:tp_dbus_daemon_new } { creating classes for TpCHannel Memcheck:Leak ... fun:g_type_class_ref ... fun:g_object_new ... fun:tp_channel_new } { creating a boxed type to use in TpCapabilities Memcheck:Leak ... fun:g_type_class_ref ... fun:g_param_spec_boxed fun:tp_capabilities_class_intern_init } # ============================= questionable ========================== { creating classes for instances (this is a pretty big hammer) Memcheck:Leak ... fun:g_type_class_ref ... fun:g_type_create_instance ... fun:g_param_spec_string } telepathy-gabble-0.18.2/tools/shave.mk0000644000175000017500000000004712200204333017602 0ustar00smcvsmcv00000000000000QUIET_GEN = $(Q:@=@echo ' GEN '$@;) telepathy-gabble-0.18.2/tools/manager-file.py0000644000175000017500000001425012200204333021045 0ustar00smcvsmcv00000000000000#!/usr/bin/python # manager-file.py: generate .manager files and TpCMParamSpec arrays from the # same data (should be suitable for all connection managers that don't have # plugins) # # The master copy of this program is in the telepathy-glib repository - # please make any changes there. # # Copyright (c) Collabora Ltd. # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # # 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 import re import sys _NOT_C_STR = re.compile(r'[^A-Za-z0-9_-]') def c_string(x): # whitelist-based brute force and ignorance - escape nearly all punctuation return '"' + _NOT_C_STR.sub(lambda c: r'\x%02x' % ord(c), x) + '"' def desktop_string(x): return x.replace(' ', r'\s').replace('\n', r'\n').replace('\r', r'\r').replace('\t', r'\t') supported = list('sbuiqn') fdefaultencoders = { 's': desktop_string, 'b': (lambda b: b and '1' or '0'), 'u': (lambda n: '%u' % n), 'i': (lambda n: '%d' % n), 'q': (lambda n: '%u' % n), 'n': (lambda n: '%d' % n), } for x in supported: assert x in fdefaultencoders gtypes = { 's': 'G_TYPE_STRING', 'b': 'G_TYPE_BOOLEAN', 'u': 'G_TYPE_UINT', 'i': 'G_TYPE_INT', 'q': 'G_TYPE_UINT', 'n': 'G_TYPE_INT', } for x in supported: assert x in gtypes gdefaultencoders = { 's': c_string, 'b': (lambda b: b and 'GINT_TO_POINTER (TRUE)' or 'GINT_TO_POINTER (FALSE)'), 'u': (lambda n: 'GUINT_TO_POINTER (%u)' % n), 'i': (lambda n: 'GINT_TO_POINTER (%d)' % n), 'q': (lambda n: 'GUINT_TO_POINTER (%u)' % n), 'n': (lambda n: 'GINT_TO_POINTER (%d)' % n), } for x in supported: assert x in gdefaultencoders gdefaultdefaults = { 's': 'NULL', 'b': 'GINT_TO_POINTER (FALSE)', 'u': 'GUINT_TO_POINTER (0)', 'i': 'GINT_TO_POINTER (0)', 'q': 'GUINT_TO_POINTER (0)', 'n': 'GINT_TO_POINTER (0)', } for x in supported: assert x in gdefaultdefaults gflags = { 'has-default': 'TP_CONN_MGR_PARAM_FLAG_HAS_DEFAULT', 'register': 'TP_CONN_MGR_PARAM_FLAG_REGISTER', 'required': 'TP_CONN_MGR_PARAM_FLAG_REQUIRED', 'secret': 'TP_CONN_MGR_PARAM_FLAG_SECRET', 'dbus-property': 'TP_CONN_MGR_PARAM_FLAG_DBUS_PROPERTY', } def write_manager(f, manager, protos): # pointless backwards compat section print >> f, '[ConnectionManager]' print >> f, 'BusName=org.freedesktop.Telepathy.ConnectionManager.' + manager print >> f, 'ObjectPath=/org/freedesktop/Telepathy/ConnectionManager/' + manager # protocols for proto, params in protos.iteritems(): print >> f print >> f, '[Protocol %s]' % proto defaults = {} for param, info in params.iteritems(): dtype = info['dtype'] flags = info.get('flags', '').split() struct_field = info.get('struct_field', param.replace('-', '_')) filter = info.get('filter', 'NULL') filter_data = info.get('filter_data', 'NULL') setter_data = 'NULL' if 'default' in info: default = fdefaultencoders[dtype](info['default']) defaults[param] = default if flags: flags = ' ' + ' '.join(flags) else: flags = '' print >> f, 'param-%s=%s%s' % (param, desktop_string(dtype), flags) for param, default in defaults.iteritems(): print >> f, 'default-%s=%s' % (param, default) def write_c_params(f, manager, proto, struct, params): print >> f, "static const TpCMParamSpec %s_%s_params[] = {" % (manager, proto) for param, info in params.iteritems(): dtype = info['dtype'] flags = info.get('flags', '').split() struct_field = info.get('struct_field', param.replace('-', '_')) filter = info.get('filter', 'NULL') filter_data = info.get('filter_data', 'NULL') setter_data = 'NULL' if 'default' in info: default = gdefaultencoders[dtype](info['default']) else: default = gdefaultdefaults[dtype] if flags: flags = ' | '.join([gflags[flag] for flag in flags]) else: flags = '0' if struct is None or struct_field is None: struct_offset = '0' else: struct_offset = 'G_STRUCT_OFFSET (%s, %s)' % (struct, struct_field) print >> f, (''' { %s, %s, %s, %s, %s, /* default */ %s, /* struct offset */ %s, /* filter */ %s, /* filter data */ %s /* setter data */ },''' % (c_string(param), c_string(dtype), gtypes[dtype], flags, default, struct_offset, filter, filter_data, setter_data)) print >> f, " { NULL }" print >> f, "};" if __name__ == '__main__': environment = {} execfile(sys.argv[1], environment) filename = '%s/%s.manager' % (sys.argv[2], environment['MANAGER']) try: os.remove(filename) except OSError: pass f = open(filename + '.tmp', 'w') write_manager(f, environment['MANAGER'], environment['PARAMS']) f.close() os.rename(filename + '.tmp', filename) filename = '%s/param-spec-struct.h' % sys.argv[2] try: os.remove(filename) except OSError: pass f = open(filename + '.tmp', 'w') for protocol in environment['PARAMS']: write_c_params(f, environment['MANAGER'], protocol, environment['STRUCTS'][protocol], environment['PARAMS'][protocol]) f.close() os.rename(filename + '.tmp', filename) telepathy-gabble-0.18.2/tools/make-version-script.py0000644000175000017500000001407612200204333022426 0ustar00smcvsmcv00000000000000#!/usr/bin/python """Construct a GNU ld or Debian dpkg version-script from a set of RFC822-style symbol lists. Usage: make-version-script.py [--symbols SYMBOLS] [--unreleased-version VER] [--dpkg "LIBRARY.so.0 LIBRARY0 #MINVER#"] [--dpkg-build-depends-package LIBRARY-dev] [FILES...] Each FILE starts with RFC822-style headers "Version:" (the name of the symbol version, e.g. FOO_1.2.3) and "Extends:" (either the previous version, or "-" if this is the first version). Next there is a blank line, then a list of C symbols one per line. Comments (lines starting with whitespace + "#") are allowed and ignored. If --symbols is given, SYMBOLS lists the symbols actually exported by the library (one per line). If --unreleased-version is given, any symbols in SYMBOLS but not in FILES are assigned to that version; otherwise, any such symbols cause an error. If --dpkg is given, produce a Debian dpkg-gensymbols file instead of a GNU ld version-script. The argument to --dpkg is the first line of the resulting symbols file, and --dpkg-build-depends-package can optionally be used to set the Build-Depends-Package field. This script originates in telepathy-glib - please send us any changes that are needed. """ # Copyright (C) 2008-2010 Collabora Ltd. # Copyright (C) 2008 Nokia Corporation # # Copying and distribution of this file, with or without modification, # are permitted in any medium without royalty provided the copyright # notice and this notice are preserved. import sys from getopt import gnu_getopt def e(format, *args): sys.stderr.write((format + '\n') % args) def main(abifiles, symbols=None, unreleased_version=None, dpkg=False, dpkg_first_line=None, dpkg_build_depends_package=None): gnuld = not dpkg symbol_set = None if symbols is not None: symbol_set = open(symbols, 'r').readlines() symbol_set = map(str.strip, symbol_set) symbol_set = set(symbol_set) versioned_symbols = set() dpkg_symbols = [] dpkg_versions = [] if dpkg: assert dpkg_first_line is not None print dpkg_first_line if dpkg_build_depends_package is not None: print "* Build-Depends-Package: %s" % dpkg_build_depends_package for filename in abifiles: lines = open(filename, 'r').readlines() version = None extends = None release = None for i, line in enumerate(lines): line = line.strip() if line.startswith('#'): continue elif not line: # the transition betwen headers and symbols cut = i + 1 break elif line.lower().startswith('version:'): line = line[8:].strip() version = line continue elif line.lower().startswith('extends:'): line = line[8:].strip() extends = line continue elif line.lower().startswith('release:'): release = line[8:].strip() continue else: e('Could not understand line in %s header: %s', filename, line) raise SystemExit(1) else: e('No symbols in %s', filename) raise SystemExit(1) if version is None: e('No Versions: header in %s', filename) raise SystemExit(1) if extends is None: e('No Extends: header in %s', filename) raise SystemExit(1) if release is None and dpkg: e('No Release: header in %s', filename) raise SystemExit(1) if dpkg: dpkg_versions.append('%s@%s %s' % (version, version, release)) lines = lines[cut:] if gnuld: print "%s {" % version print " global:" for symbol in lines: symbol = symbol.strip() if symbol.startswith('#'): continue if gnuld: print " %s;" % symbol elif dpkg: dpkg_symbols.append('%s@%s %s' % (symbol, version, release)) if symbol in versioned_symbols: raise AssertionError('Symbol %s is in version %s and an ' 'earlier version' % (symbol, version)) versioned_symbols.add(symbol) if gnuld: if extends == '-': print " local:" print " *;" print "};" else: print "} %s;" % extends print if dpkg: dpkg_symbols.sort() dpkg_versions.sort() for x in dpkg_versions: print " %s" % x for x in dpkg_symbols: print " %s" % x if symbol_set is not None: missing = versioned_symbols - symbol_set if missing: e('These symbols have disappeared:') for symbol in missing: e(' %s', symbol) raise SystemExit(1) unreleased = symbol_set - versioned_symbols if unreleased: if unreleased_version is None: e('Unversioned symbols are not allowed in releases:') for symbol in unreleased: e(' %s', symbol) raise SystemExit(1) if gnuld: print "%s {" % unreleased_version print " global:" for symbol in unreleased: print " %s;" % symbol print "} %s;" % version if __name__ == '__main__': options, argv = gnu_getopt (sys.argv[1:], '', ['symbols=', 'unreleased-version=', 'dpkg=', 'dpkg-build-depends-package=']) opts = {'dpkg': False} for option, value in options: if option == '--dpkg': opts['dpkg'] = True opts['dpkg_first_line'] = value else: opts[option.lstrip('-').replace('-', '_')] = value main(argv, **opts) telepathy-gabble-0.18.2/tools/make-release-mail.py0000644000175000017500000000534512200204333021776 0ustar00smcvsmcv00000000000000#!/usr/bin/env python # vim: set fileencoding=utf-8 : # # Hello. This is make-release-mail.py from the Telepathy project. It's # designed to turn an item from a NEWS file into a mail suitable for sending # to . I hope that you enjoy your stay. import sys import re def looks_like_a_header(line, package, version=None): if version is None: pattern = "^%s .* \(.*\)$" % package else: pattern = "^%s %s \(.*\)$" % (package, version) return re.match(pattern, line) is not None def extract_description(package, version, news_path): release_name = [] details = [] with open(news_path) as f: lines = (line for line in f.readlines()) for line in lines: # Find the 'telepathy-foo 0.1.2' header if looks_like_a_header(line, package, version): break # Skip the ====== line, and the first blank line lines.next() lines.next() got_release_name = False for line in lines: line = line.rstrip() # If we hit the next version header, we're done if looks_like_a_header(line, package): break # Else, if we hit a blank line and we're still reading the release # name, we're done with the release name. elif not got_release_name and line == '': got_release_name = True # Otherwise, append this to the relevant list elif not got_release_name: release_name.append(line) else: details.append(line) assert got_release_name, (release_name, details) # We rstrip details because it picks up a trailing blank line return ('\n'.join(release_name), '\n'.join(details).rstrip()) BASE_URL = 'http://telepathy.freedesktop.org/releases' GIT_URL = 'http://cgit.freedesktop.org/telepathy' def main(package, version, news_path): release_name, details = extract_description(package, version, news_path) print """ %(release_name)s tarball: %(base_url)s/%(package)s/%(package)s-%(version)s.tar.gz signature: %(base_url)s/%(package)s/%(package)s-%(version)s.tar.gz.asc git: %(git_url)s/%(package)s %(details)s""".strip().rstrip() % { 'base_url': BASE_URL, 'git_url': GIT_URL, 'package': package, 'version': version, 'release_name': release_name, 'details': details, } if __name__ == '__main__': try: package, version, news_path = sys.argv[1:] main(package, version, news_path) except ValueError, e: sys.stderr.write( 'Usage: %s package-name package.version.number path/to/NEWS\n' % sys.argv[0]) sys.stderr.flush() sys.exit(1) telepathy-gabble-0.18.2/tools/libglibcodegen.py0000644000175000017500000001456312200204333021456 0ustar00smcvsmcv00000000000000"""Library code for GLib/D-Bus-related code generation. The master copy of this library is in the telepathy-glib repository - please make any changes there. """ # Copyright (C) 2006-2008 Collabora Limited # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # # 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 from libtpcodegen import NS_TP, \ Signature, \ cmp_by_name, \ escape_as_identifier, \ get_by_path, \ get_descendant_text, \ get_docstring, \ xml_escape, \ get_deprecated def dbus_gutils_wincaps_to_uscore(s): """Bug-for-bug compatible Python port of _dbus_gutils_wincaps_to_uscore which gets sequences of capital letters wrong in the same way. (e.g. in Telepathy, SendDTMF -> send_dt_mf) """ ret = '' for c in s: if c >= 'A' and c <= 'Z': length = len(ret) if length > 0 and (length < 2 or ret[length-2] != '_'): ret += '_' ret += c.lower() else: ret += c return ret def signal_to_marshal_type(signal): """ return a list of strings indicating the marshalling type for this signal. """ mtype=[] for i in signal.getElementsByTagName("arg"): name =i.getAttribute("name") type = i.getAttribute("type") mtype.append(type_to_gtype(type)[2]) return mtype _glib_marshallers = ['VOID', 'BOOLEAN', 'CHAR', 'UCHAR', 'INT', 'STRING', 'UINT', 'LONG', 'ULONG', 'ENUM', 'FLAGS', 'FLOAT', 'DOUBLE', 'STRING', 'PARAM', 'BOXED', 'POINTER', 'OBJECT', 'UINT_POINTER'] def signal_to_marshal_name(signal, prefix): mtype = signal_to_marshal_type(signal) if len(mtype): name = '_'.join(mtype) else: name = 'VOID' if name in _glib_marshallers: return 'g_cclosure_marshal_VOID__' + name else: return prefix + '_marshal_VOID__' + name def method_to_glue_marshal_name(method, prefix): mtype = [] for i in method.getElementsByTagName("arg"): if i.getAttribute("direction") != "out": type = i.getAttribute("type") mtype.append(type_to_gtype(type)[2]) mtype.append('POINTER') name = '_'.join(mtype) if name in _glib_marshallers: return 'g_cclosure_marshal_VOID__' + name else: return prefix + '_marshal_VOID__' + name def type_to_gtype(s): if s == 'y': #byte return ("guchar ", "G_TYPE_UCHAR","UCHAR", False) elif s == 'b': #boolean return ("gboolean ", "G_TYPE_BOOLEAN","BOOLEAN", False) elif s == 'n': #int16 return ("gint ", "G_TYPE_INT","INT", False) elif s == 'q': #uint16 return ("guint ", "G_TYPE_UINT","UINT", False) elif s == 'i': #int32 return ("gint ", "G_TYPE_INT","INT", False) elif s == 'u': #uint32 return ("guint ", "G_TYPE_UINT","UINT", False) elif s == 'x': #int64 return ("gint64 ", "G_TYPE_INT64","INT64", False) elif s == 't': #uint64 return ("guint64 ", "G_TYPE_UINT64","UINT64", False) elif s == 'd': #double return ("gdouble ", "G_TYPE_DOUBLE","DOUBLE", False) elif s == 's': #string return ("gchar *", "G_TYPE_STRING", "STRING", True) elif s == 'g': #signature - FIXME return ("gchar *", "DBUS_TYPE_G_SIGNATURE", "STRING", True) elif s == 'o': #object path return ("gchar *", "DBUS_TYPE_G_OBJECT_PATH", "BOXED", True) elif s == 'v': #variant return ("GValue *", "G_TYPE_VALUE", "BOXED", True) elif s == 'as': #array of strings return ("gchar **", "G_TYPE_STRV", "BOXED", True) elif s == 'ay': #byte array return ("GArray *", "dbus_g_type_get_collection (\"GArray\", G_TYPE_UCHAR)", "BOXED", True) elif s == 'au': #uint array return ("GArray *", "DBUS_TYPE_G_UINT_ARRAY", "BOXED", True) elif s == 'ai': #int array return ("GArray *", "DBUS_TYPE_G_INT_ARRAY", "BOXED", True) elif s == 'ax': #int64 array return ("GArray *", "DBUS_TYPE_G_INT64_ARRAY", "BOXED", True) elif s == 'at': #uint64 array return ("GArray *", "DBUS_TYPE_G_UINT64_ARRAY", "BOXED", True) elif s == 'ad': #double array return ("GArray *", "DBUS_TYPE_G_DOUBLE_ARRAY", "BOXED", True) elif s == 'ab': #boolean array return ("GArray *", "DBUS_TYPE_G_BOOLEAN_ARRAY", "BOXED", True) elif s == 'ao': #object path array return ("GPtrArray *", 'dbus_g_type_get_collection ("GPtrArray",' ' DBUS_TYPE_G_OBJECT_PATH)', "BOXED", True) elif s == 'a{ss}': #hash table of string to string return ("GHashTable *", "DBUS_TYPE_G_STRING_STRING_HASHTABLE", "BOXED", False) elif s[:2] == 'a{': #some arbitrary hash tables if s[2] not in ('y', 'b', 'n', 'q', 'i', 'u', 's', 'o', 'g'): raise Exception, "can't index a hashtable off non-basic type " + s first = type_to_gtype(s[2]) second = type_to_gtype(s[3:-1]) return ("GHashTable *", "(dbus_g_type_get_map (\"GHashTable\", " + first[1] + ", " + second[1] + "))", "BOXED", False) elif s[:2] in ('a(', 'aa'): # array of structs or arrays, recurse gtype = type_to_gtype(s[1:])[1] return ("GPtrArray *", "(dbus_g_type_get_collection (\"GPtrArray\", "+gtype+"))", "BOXED", True) elif s[:1] == '(': #struct gtype = "(dbus_g_type_get_struct (\"GValueArray\", " for subsig in Signature(s[1:-1]): gtype = gtype + type_to_gtype(subsig)[1] + ", " gtype = gtype + "G_TYPE_INVALID))" return ("GValueArray *", gtype, "BOXED", True) # we just don't know .. raise Exception, "don't know the GType for " + s telepathy-gabble-0.18.2/tools/libtpcodegen.py0000644000175000017500000001537012200204333021161 0ustar00smcvsmcv00000000000000"""Library code for language-independent D-Bus-related code generation. The master copy of this library is in the telepathy-glib repository - please make any changes there. """ # Copyright (C) 2006-2008 Collabora Limited # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # # 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 import os from string import ascii_letters, digits NS_TP = "http://telepathy.freedesktop.org/wiki/DbusSpec#extensions-v0" _ASCII_ALNUM = ascii_letters + digits def file_set_contents(filename, contents): try: os.remove(filename) except OSError: pass try: os.remove(filename + '.tmp') except OSError: pass open(filename + '.tmp', 'w').write(contents) os.rename(filename + '.tmp', filename) def cmp_by_name(node1, node2): return cmp(node1.getAttributeNode("name").nodeValue, node2.getAttributeNode("name").nodeValue) def escape_as_identifier(identifier): """Escape the given string to be a valid D-Bus object path or service name component, using a reversible encoding to ensure uniqueness. The reversible encoding is as follows: * The empty string becomes '_' * Otherwise, each non-alphanumeric character is replaced by '_' plus two lower-case hex digits; the same replacement is carried out on the first character, if it's a digit """ # '' -> '_' if not identifier: return '_' # A bit of a fast path for strings which are already OK. # We deliberately omit '_' because, for reversibility, that must also # be escaped. if (identifier.strip(_ASCII_ALNUM) == '' and identifier[0] in ascii_letters): return identifier # The first character may not be a digit if identifier[0] not in ascii_letters: ret = ['_%02x' % ord(identifier[0])] else: ret = [identifier[0]] # Subsequent characters may be digits or ASCII letters for c in identifier[1:]: if c in _ASCII_ALNUM: ret.append(c) else: ret.append('_%02x' % ord(c)) return ''.join(ret) def get_by_path(element, path): branches = path.split('/') branch = branches[0] # Is the current branch an attribute, if so, return the attribute value if branch[0] == '@': return element.getAttribute(branch[1:]) # Find matching children for the branch children = [] if branch == '..': children.append(element.parentNode) else: for x in element.childNodes: if x.localName == branch: children.append(x) ret = [] # If this is not the last path element, recursively gather results from # children if len(branches) > 1: for x in children: add = get_by_path(x, '/'.join(branches[1:])) if isinstance(add, list): ret += add else: return add else: ret = children return ret def get_docstring(element): docstring = None for x in element.childNodes: if x.namespaceURI == NS_TP and x.localName == 'docstring': docstring = x if docstring is not None: docstring = docstring.toxml().replace('\n', ' ').strip() if docstring.startswith(''): docstring = docstring[14:].lstrip() if docstring.endswith(''): docstring = docstring[:-15].rstrip() if docstring in ('', ''): docstring = '' return docstring def get_deprecated(element): text = [] for x in element.childNodes: if hasattr(x, 'data'): text.append(x.data.replace('\n', ' ').strip()) else: # This caters for tp:dbus-ref elements, but little else. if x.childNodes and hasattr(x.childNodes[0], 'data'): text.append(x.childNodes[0].data.replace('\n', ' ').strip()) return ' '.join(text) def get_descendant_text(element_or_elements): if not element_or_elements: return '' if isinstance(element_or_elements, list): return ''.join(map(get_descendant_text, element_or_elements)) parts = [] for x in element_or_elements.childNodes: if x.nodeType == x.TEXT_NODE: parts.append(x.nodeValue) elif x.nodeType == x.ELEMENT_NODE: parts.append(get_descendant_text(x)) else: pass return ''.join(parts) class _SignatureIter: """Iterator over a D-Bus signature. Copied from dbus-python 0.71 so we can run genginterface in a limited environment with only Python (like Scratchbox). """ def __init__(self, string): self.remaining = string def next(self): if self.remaining == '': raise StopIteration signature = self.remaining block_depth = 0 block_type = None end = len(signature) for marker in range(0, end): cur_sig = signature[marker] if cur_sig == 'a': pass elif cur_sig == '{' or cur_sig == '(': if block_type == None: block_type = cur_sig if block_type == cur_sig: block_depth = block_depth + 1 elif cur_sig == '}': if block_type == '{': block_depth = block_depth - 1 if block_depth == 0: end = marker break elif cur_sig == ')': if block_type == '(': block_depth = block_depth - 1 if block_depth == 0: end = marker break else: if block_depth == 0: end = marker break end = end + 1 self.remaining = signature[end:] return Signature(signature[0:end]) class Signature(str): """A string, iteration over which is by D-Bus single complete types rather than characters. """ def __iter__(self): return _SignatureIter(self) def xml_escape(s): s = s.replace('&', '&').replace("'", ''').replace('"', '"') return s.replace('<', '<').replace('>', '>') telepathy-gabble-0.18.2/tools/gobject-foo.py0000644000175000017500000000607712200204333020724 0ustar00smcvsmcv00000000000000#!/usr/bin/python # gobject-foo.py: generate standard GObject type macros etc. # # The master copy of this program is in the telepathy-glib repository - # please make any changes there. # # Copyright (C) 2007-2010 Collabora Ltd. # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # # 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 def gobject_header(head, tail, as_interface=False): out = [] o = out.append name = head + '_' + tail MixedCase = name.replace('_', '') lower_case = name.lower() UPPER_CASE = name.upper() gtype = head.upper() + '_TYPE_' + tail.upper() o("typedef struct _%s %s;" % (MixedCase, MixedCase)) if as_interface: o("typedef struct _%sInterface %sInterface;" % (MixedCase, MixedCase)) else: o("typedef struct _%sClass %sClass;" % (MixedCase, MixedCase)) o("typedef struct _%sPrivate %sPrivate;" % (MixedCase, MixedCase)) o("") o("GType %s_get_type (void);" % lower_case) o("") o("#define %s \\" % gtype) o(" (%s_get_type ())" % lower_case) o("#define %s(obj) \\" % UPPER_CASE) o(" (G_TYPE_CHECK_INSTANCE_CAST ((obj), %s, \\" % gtype) o(" %s))" % MixedCase) if not as_interface: o("#define %s_CLASS(klass) \\" % UPPER_CASE) o(" (G_TYPE_CHECK_CLASS_CAST ((klass), %s, \\" % gtype) o(" %sClass))" % MixedCase) o("#define %s_IS_%s(obj) \\" % (head.upper(), tail.upper())) o(" (G_TYPE_CHECK_INSTANCE_TYPE ((obj), %s))" % gtype) if as_interface: o("#define %s_GET_IFACE(obj) \\" % UPPER_CASE) o(" (G_TYPE_INSTANCE_GET_INTERFACE ((obj), %s, \\" % gtype) o(" %sInterface))" % MixedCase) else: o("#define %s_IS_%s_CLASS(klass) \\" % (head.upper(), tail.upper())) o(" (G_TYPE_CHECK_CLASS_TYPE ((klass), %s))" % gtype) o("#define %s_GET_CLASS(obj) \\" % UPPER_CASE) o(" (G_TYPE_INSTANCE_GET_CLASS ((obj), %s, \\" % gtype) o(" %sClass))" % MixedCase) return out if __name__ == '__main__': import sys from getopt import gnu_getopt options, argv = gnu_getopt(sys.argv[1:], '', ['interface']) as_interface = False for opt, val in options: if opt == '--interface': as_interface = True head, tail = argv print '\n'.join(gobject_header(head, tail, as_interface=as_interface)) telepathy-gabble-0.18.2/tools/glib-interfaces-gen.py0000644000175000017500000001455312200204333022331 0ustar00smcvsmcv00000000000000#!/usr/bin/python from sys import argv, stdout, stderr import xml.dom.minidom from libtpcodegen import file_set_contents from libglibcodegen import NS_TP, get_docstring, \ get_descendant_text, get_by_path class Generator(object): def __init__(self, prefix, implfile, declfile, dom): self.prefix = prefix + '_' assert declfile.endswith('.h') docfile = declfile[:-2] + '-gtk-doc.h' self.implfile = implfile self.declfile = declfile self.docfile = docfile self.impls = [] self.decls = [] self.docs = [] self.spec = get_by_path(dom, "spec")[0] def h(self, code): self.decls.append(code.encode('utf-8')) def c(self, code): self.impls.append(code.encode('utf-8')) def d(self, code): self.docs.append(code.encode('utf-8')) def __call__(self): for f in self.h, self.c: self.do_header(f) self.do_body() file_set_contents(self.implfile, ''.join(self.impls)) file_set_contents(self.declfile, ''.join(self.decls)) file_set_contents(self.docfile, ''.join(self.docs)) # Header def do_header(self, f): f('/* Generated from: ') f(get_descendant_text(get_by_path(self.spec, 'title'))) version = get_by_path(self.spec, "version") if version: f(' version ' + get_descendant_text(version)) f('\n\n') for copyright in get_by_path(self.spec, 'copyright'): f(get_descendant_text(copyright)) f('\n') f('\n') f(get_descendant_text(get_by_path(self.spec, 'license'))) f(get_descendant_text(get_by_path(self.spec, 'docstring'))) f(""" */ #include """) # Body def do_body(self): for iface in self.spec.getElementsByTagName('interface'): self.do_iface(iface) def do_iface(self, iface): parent_name = get_by_path(iface, '../@name') self.d("""\ /** * %(IFACE_DEFINE)s: * * The interface name "%(name)s" */ """ % {'IFACE_DEFINE' : (self.prefix + 'IFACE_' + \ parent_name).upper().replace('/', ''), 'name' : iface.getAttribute('name')}) self.h(""" #define %(IFACE_DEFINE)s \\ "%(name)s" """ % {'IFACE_DEFINE' : (self.prefix + 'IFACE_' + \ parent_name).upper().replace('/', ''), 'name' : iface.getAttribute('name')}) self.d(""" /** * %(IFACE_QUARK_DEFINE)s: * * Expands to a call to a function that returns a quark for the interface \ name "%(name)s" */ """ % {'IFACE_QUARK_DEFINE' : (self.prefix + 'IFACE_QUARK_' + \ parent_name).upper().replace('/', ''), 'iface_quark_func' : (self.prefix + 'iface_quark_' + \ parent_name).lower().replace('/', ''), 'name' : iface.getAttribute('name')}) self.h(""" #define %(IFACE_QUARK_DEFINE)s \\ (%(iface_quark_func)s ()) GQuark %(iface_quark_func)s (void); """ % {'IFACE_QUARK_DEFINE' : (self.prefix + 'IFACE_QUARK_' + \ parent_name).upper().replace('/', ''), 'iface_quark_func' : (self.prefix + 'iface_quark_' + \ parent_name).lower().replace('/', ''), 'name' : iface.getAttribute('name')}) self.c("""\ GQuark %(iface_quark_func)s (void) { static GQuark quark = 0; if (G_UNLIKELY (quark == 0)) { quark = g_quark_from_static_string ("%(name)s"); } return quark; } """ % {'iface_quark_func' : (self.prefix + 'iface_quark_' + \ parent_name).lower().replace('/', ''), 'name' : iface.getAttribute('name')}) for prop in iface.getElementsByTagNameNS(None, 'property'): self.d(""" /** * %(IFACE_PREFIX)s_%(PROP_UC)s: * * The fully-qualified property name "%(name)s.%(prop)s" */ """ % {'IFACE_PREFIX' : (self.prefix + 'PROP_' + \ parent_name).upper().replace('/', ''), 'PROP_UC': prop.getAttributeNS(NS_TP, "name-for-bindings").upper(), 'name' : iface.getAttribute('name'), 'prop' : prop.getAttribute('name'), }) self.h(""" #define %(IFACE_PREFIX)s_%(PROP_UC)s \\ "%(name)s.%(prop)s" """ % {'IFACE_PREFIX' : (self.prefix + 'PROP_' + \ parent_name).upper().replace('/', ''), 'PROP_UC': prop.getAttributeNS(NS_TP, "name-for-bindings").upper(), 'name' : iface.getAttribute('name'), 'prop' : prop.getAttribute('name'), }) for prop in iface.getElementsByTagNameNS(NS_TP, 'contact-attribute'): self.d(""" /** * %(TOKEN_PREFIX)s_%(TOKEN_UC)s: * * The fully-qualified contact attribute token name "%(name)s/%(prop)s" */ """ % {'TOKEN_PREFIX' : (self.prefix + 'TOKEN_' + \ parent_name).upper().replace('/', ''), 'TOKEN_UC': prop.getAttributeNS(None, "name").upper().replace("-", "_").replace(".", "_"), 'name' : iface.getAttribute('name'), 'prop' : prop.getAttribute('name'), }) self.h(""" #define %(TOKEN_PREFIX)s_%(TOKEN_UC)s \\ "%(name)s/%(prop)s" """ % {'TOKEN_PREFIX' : (self.prefix + 'TOKEN_' + \ parent_name).upper().replace('/', ''), 'TOKEN_UC': prop.getAttributeNS(None, "name").upper().replace("-", "_").replace(".", "_"), 'name' : iface.getAttribute('name'), 'prop' : prop.getAttribute('name'), }) for prop in iface.getElementsByTagNameNS(NS_TP, 'hct'): if (prop.getAttribute('is-family') != "yes"): self.d(""" /** * %(TOKEN_PREFIX)s_%(TOKEN_UC)s: * * The fully-qualified capability token name "%(name)s/%(prop)s" */ """ % {'TOKEN_PREFIX' : (self.prefix + 'TOKEN_' + \ parent_name).upper().replace('/', ''), 'TOKEN_UC': prop.getAttributeNS(None, "name").upper().replace("-", "_").replace(".", "_"), 'name' : iface.getAttribute('name'), 'prop' : prop.getAttribute('name'), }) self.h(""" #define %(TOKEN_PREFIX)s_%(TOKEN_UC)s \\ "%(name)s/%(prop)s" """ % {'TOKEN_PREFIX' : (self.prefix + 'TOKEN_' + \ parent_name).upper().replace('/', ''), 'TOKEN_UC': prop.getAttributeNS(None, "name").upper().replace("-", "_").replace(".", "_"), 'name' : iface.getAttribute('name'), 'prop' : prop.getAttribute('name'), }) if __name__ == '__main__': argv = argv[1:] Generator(argv[0], argv[1], argv[2], xml.dom.minidom.parse(argv[3]))() telepathy-gabble-0.18.2/tools/glib-gtypes-generator.py0000644000175000017500000003071012200204333022727 0ustar00smcvsmcv00000000000000#!/usr/bin/python # Generate GLib GInterfaces from the Telepathy specification. # The master copy of this program is in the telepathy-glib repository - # please make any changes there. # # Copyright (C) 2006, 2007 Collabora Limited # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # # 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 import sys import xml.dom.minidom from libtpcodegen import file_set_contents from libglibcodegen import escape_as_identifier, \ get_docstring, \ NS_TP, \ Signature, \ type_to_gtype, \ xml_escape def types_to_gtypes(types): return [type_to_gtype(t)[1] for t in types] class GTypesGenerator(object): def __init__(self, dom, output, mixed_case_prefix): self.dom = dom self.Prefix = mixed_case_prefix self.PREFIX_ = self.Prefix.upper() + '_' self.prefix_ = self.Prefix.lower() + '_' self.header = [] self.body = [] self.docs = [] self.output = output for f in (self.header, self.body, self.docs): f.append('/* Auto-generated, do not edit.\n *\n' ' * This file may be distributed under the same terms\n' ' * as the specification from which it was generated.\n' ' */\n\n') # keys are e.g. 'sv', values are the key escaped self.need_mappings = {} # keys are the contents of the struct (e.g. 'sssu'), values are the # key escaped self.need_structs = {} # keys are the contents of the struct (e.g. 'sssu'), values are the # key escaped self.need_struct_arrays = {} # keys are the contents of the array (unlike need_struct_arrays!), # values are the key escaped self.need_other_arrays = {} def h(self, code): self.header.append(code.encode("utf-8")) def c(self, code): self.body.append(code.encode("utf-8")) def d(self, code): self.docs.append(code.encode('utf-8')) def do_mapping_header(self, mapping): members = mapping.getElementsByTagNameNS(NS_TP, 'member') assert len(members) == 2 impl_sig = ''.join([elt.getAttribute('type') for elt in members]) esc_impl_sig = escape_as_identifier(impl_sig) name = (self.PREFIX_ + 'HASH_TYPE_' + mapping.getAttribute('name').upper()) impl = self.prefix_ + 'type_dbus_hash_' + esc_impl_sig docstring = get_docstring(mapping) or '(Undocumented)' self.d('/**\n * %s:\n *\n' % name.strip()) self.d(' * %s\n' % xml_escape(docstring)) self.d(' *\n') self.d(' * This macro expands to a call to a function\n') self.d(' * that returns the #GType of a #GHashTable\n') self.d(' * appropriate for representing a D-Bus\n') self.d(' * dictionary of signature\n') self.d(' * a{%s}.\n' % impl_sig) self.d(' *\n') key, value = members self.d(' * Keys (D-Bus type %s,\n' % key.getAttribute('type')) tp_type = key.getAttributeNS(NS_TP, 'type') if tp_type: self.d(' * type %s,\n' % tp_type) self.d(' * named %s):\n' % key.getAttribute('name')) docstring = get_docstring(key) or '(Undocumented)' self.d(' * %s\n' % xml_escape(docstring)) self.d(' *\n') self.d(' * Values (D-Bus type %s,\n' % value.getAttribute('type')) tp_type = value.getAttributeNS(NS_TP, 'type') if tp_type: self.d(' * type %s,\n' % tp_type) self.d(' * named %s):\n' % value.getAttribute('name')) docstring = get_docstring(value) or '(Undocumented)' self.d(' * %s\n' % xml_escape(docstring)) self.d(' *\n') self.d(' */\n') self.h('#define %s (%s ())\n\n' % (name, impl)) self.need_mappings[impl_sig] = esc_impl_sig array_name = mapping.getAttribute('array-name') if array_name: gtype_name = self.PREFIX_ + 'ARRAY_TYPE_' + array_name.upper() contents_sig = 'a{' + impl_sig + '}' esc_contents_sig = escape_as_identifier(contents_sig) impl = self.prefix_ + 'type_dbus_array_of_' + esc_contents_sig self.d('/**\n * %s:\n\n' % gtype_name) self.d(' * Expands to a call to a function\n') self.d(' * that returns the #GType of a #GPtrArray\n') self.d(' * of #%s.\n' % name) self.d(' */\n\n') self.h('#define %s (%s ())\n\n' % (gtype_name, impl)) self.need_other_arrays[contents_sig] = esc_contents_sig def do_struct_header(self, struct): members = struct.getElementsByTagNameNS(NS_TP, 'member') impl_sig = ''.join([elt.getAttribute('type') for elt in members]) esc_impl_sig = escape_as_identifier(impl_sig) name = (self.PREFIX_ + 'STRUCT_TYPE_' + struct.getAttribute('name').upper()) impl = self.prefix_ + 'type_dbus_struct_' + esc_impl_sig docstring = struct.getElementsByTagNameNS(NS_TP, 'docstring') if docstring: docstring = docstring[0].toprettyxml() if docstring.startswith(''): docstring = docstring[14:] if docstring.endswith('\n'): docstring = docstring[:-16] if docstring.strip() in ('', ''): docstring = '(Undocumented)' else: docstring = '(Undocumented)' self.d('/**\n * %s:\n\n' % name) self.d(' * %s\n' % xml_escape(docstring)) self.d(' *\n') self.d(' * This macro expands to a call to a function\n') self.d(' * that returns the #GType of a #GValueArray\n') self.d(' * appropriate for representing a D-Bus struct\n') self.d(' * with signature (%s).\n' % impl_sig) self.d(' *\n') for i, member in enumerate(members): self.d(' * Member %d (D-Bus type ' '%s,\n' % (i, member.getAttribute('type'))) tp_type = member.getAttributeNS(NS_TP, 'type') if tp_type: self.d(' * type %s,\n' % tp_type) self.d(' * named %s):\n' % member.getAttribute('name')) docstring = get_docstring(member) or '(Undocumented)' self.d(' * %s\n' % xml_escape(docstring)) self.d(' *\n') self.d(' */\n\n') self.h('#define %s (%s ())\n\n' % (name, impl)) array_name = struct.getAttribute('array-name') if array_name != '': array_name = (self.PREFIX_ + 'ARRAY_TYPE_' + array_name.upper()) impl = self.prefix_ + 'type_dbus_array_' + esc_impl_sig self.d('/**\n * %s:\n\n' % array_name) self.d(' * Expands to a call to a function\n') self.d(' * that returns the #GType of a #GPtrArray\n') self.d(' * of #%s.\n' % name) self.d(' */\n\n') self.h('#define %s (%s ())\n\n' % (array_name, impl)) self.need_struct_arrays[impl_sig] = esc_impl_sig self.need_structs[impl_sig] = esc_impl_sig def __call__(self): mappings = self.dom.getElementsByTagNameNS(NS_TP, 'mapping') structs = self.dom.getElementsByTagNameNS(NS_TP, 'struct') for mapping in mappings: self.do_mapping_header(mapping) for sig in self.need_mappings: self.h('GType %stype_dbus_hash_%s (void);\n\n' % (self.prefix_, self.need_mappings[sig])) self.c('GType\n%stype_dbus_hash_%s (void)\n{\n' % (self.prefix_, self.need_mappings[sig])) self.c(' static GType t = 0;\n\n') self.c(' if (G_UNLIKELY (t == 0))\n') # FIXME: translate sig into two GTypes items = tuple(Signature(sig)) gtypes = types_to_gtypes(items) self.c(' t = dbus_g_type_get_map ("GHashTable", ' '%s, %s);\n' % (gtypes[0], gtypes[1])) self.c(' return t;\n') self.c('}\n\n') for struct in structs: self.do_struct_header(struct) for sig in self.need_structs: self.h('GType %stype_dbus_struct_%s (void);\n\n' % (self.prefix_, self.need_structs[sig])) self.c('GType\n%stype_dbus_struct_%s (void)\n{\n' % (self.prefix_, self.need_structs[sig])) self.c(' static GType t = 0;\n\n') self.c(' if (G_UNLIKELY (t == 0))\n') self.c(' t = dbus_g_type_get_struct ("GValueArray",\n') items = tuple(Signature(sig)) gtypes = types_to_gtypes(items) for gtype in gtypes: self.c(' %s,\n' % gtype) self.c(' G_TYPE_INVALID);\n') self.c(' return t;\n') self.c('}\n\n') for sig in self.need_struct_arrays: self.h('GType %stype_dbus_array_%s (void);\n\n' % (self.prefix_, self.need_struct_arrays[sig])) self.c('GType\n%stype_dbus_array_%s (void)\n{\n' % (self.prefix_, self.need_struct_arrays[sig])) self.c(' static GType t = 0;\n\n') self.c(' if (G_UNLIKELY (t == 0))\n') self.c(' t = dbus_g_type_get_collection ("GPtrArray", ' '%stype_dbus_struct_%s ());\n' % (self.prefix_, self.need_struct_arrays[sig])) self.c(' return t;\n') self.c('}\n\n') for sig in self.need_other_arrays: self.h('GType %stype_dbus_array_of_%s (void);\n\n' % (self.prefix_, self.need_other_arrays[sig])) self.c('GType\n%stype_dbus_array_of_%s (void)\n{\n' % (self.prefix_, self.need_other_arrays[sig])) self.c(' static GType t = 0;\n\n') self.c(' if (G_UNLIKELY (t == 0))\n') if sig[:2] == 'a{' and sig[-1:] == '}': # array of mappings self.c(' t = dbus_g_type_get_collection (' '"GPtrArray", ' '%stype_dbus_hash_%s ());\n' % (self.prefix_, escape_as_identifier(sig[2:-1]))) elif sig[:2] == 'a(' and sig[-1:] == ')': # array of arrays of struct self.c(' t = dbus_g_type_get_collection (' '"GPtrArray", ' '%stype_dbus_array_%s ());\n' % (self.prefix_, escape_as_identifier(sig[2:-1]))) elif sig[:1] == 'a': # array of arrays of non-struct self.c(' t = dbus_g_type_get_collection (' '"GPtrArray", ' '%stype_dbus_array_of_%s ());\n' % (self.prefix_, escape_as_identifier(sig[1:]))) else: raise AssertionError("array of '%s' not supported" % sig) self.c(' return t;\n') self.c('}\n\n') file_set_contents(self.output + '.h', ''.join(self.header)) file_set_contents(self.output + '-body.h', ''.join(self.body)) file_set_contents(self.output + '-gtk-doc.h', ''.join(self.docs)) if __name__ == '__main__': argv = sys.argv[1:] dom = xml.dom.minidom.parse(argv[0]) GTypesGenerator(dom, argv[1], argv[2])() telepathy-gabble-0.18.2/tools/glib-ginterface-gen.py0000644000175000017500000007411012200204333022310 0ustar00smcvsmcv00000000000000#!/usr/bin/python # glib-ginterface-gen.py: service-side interface generator # # Generate dbus-glib 0.x service GInterfaces from the Telepathy specification. # The master copy of this program is in the telepathy-glib repository - # please make any changes there. # # Copyright (C) 2006, 2007 Collabora Limited # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # # 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 import sys import os.path import xml.dom.minidom from libtpcodegen import file_set_contents from libglibcodegen import Signature, type_to_gtype, cmp_by_name, \ NS_TP, dbus_gutils_wincaps_to_uscore NS_TP = "http://telepathy.freedesktop.org/wiki/DbusSpec#extensions-v0" def get_emits_changed(node): try: return [ annotation.getAttribute('value') for annotation in node.getElementsByTagName('annotation') if annotation.getAttribute('name') == 'org.freedesktop.DBus.Property.EmitsChangedSignal' ][0] except IndexError: return None class Generator(object): def __init__(self, dom, prefix, basename, signal_marshal_prefix, headers, end_headers, not_implemented_func, allow_havoc): self.dom = dom self.__header = [] self.__body = [] self.__docs = [] assert prefix.endswith('_') assert not signal_marshal_prefix.endswith('_') # The main_prefix, sub_prefix thing is to get: # FOO_ -> (FOO_, _) # FOO_SVC_ -> (FOO_, _SVC_) # but # FOO_BAR/ -> (FOO_BAR_, _) # FOO_BAR/SVC_ -> (FOO_BAR_, _SVC_) if '/' in prefix: main_prefix, sub_prefix = prefix.upper().split('/', 1) prefix = prefix.replace('/', '_') else: main_prefix, sub_prefix = prefix.upper().split('_', 1) self.MAIN_PREFIX_ = main_prefix + '_' self._SUB_PREFIX_ = '_' + sub_prefix self.Prefix_ = prefix self.Prefix = prefix.replace('_', '') self.prefix_ = prefix.lower() self.PREFIX_ = prefix.upper() self.basename = basename self.signal_marshal_prefix = signal_marshal_prefix self.headers = headers self.end_headers = end_headers self.not_implemented_func = not_implemented_func self.allow_havoc = allow_havoc def h(self, s): if isinstance(s, unicode): s = s.encode('utf-8') self.__header.append(s) def b(self, s): if isinstance(s, unicode): s = s.encode('utf-8') self.__body.append(s) def d(self, s): if isinstance(s, unicode): s = s.encode('utf-8') self.__docs.append(s) def do_node(self, node): node_name = node.getAttribute('name').replace('/', '') node_name_mixed = self.node_name_mixed = node_name.replace('_', '') node_name_lc = self.node_name_lc = node_name.lower() node_name_uc = self.node_name_uc = node_name.upper() interfaces = node.getElementsByTagName('interface') assert len(interfaces) == 1, interfaces interface = interfaces[0] self.iface_name = interface.getAttribute('name') tmp = interface.getAttribute('tp:implement-service') if tmp == "no": return tmp = interface.getAttribute('tp:causes-havoc') if tmp and not self.allow_havoc: raise AssertionError('%s is %s' % (self.iface_name, tmp)) iface_emits_changed = get_emits_changed(interface) self.b('static const DBusGObjectInfo _%s%s_object_info;' % (self.prefix_, node_name_lc)) self.b('') methods = interface.getElementsByTagName('method') signals = interface.getElementsByTagName('signal') properties = interface.getElementsByTagName('property') # Don't put properties in dbus-glib glue glue_properties = [] self.b('struct _%s%sClass {' % (self.Prefix, node_name_mixed)) self.b(' GTypeInterface parent_class;') for method in methods: self.b(' %s %s;' % self.get_method_impl_names(method)) self.b('};') self.b('') if signals: self.b('enum {') for signal in signals: self.b(' %s,' % self.get_signal_const_entry(signal)) self.b(' N_%s_SIGNALS' % node_name_uc) self.b('};') self.b('static guint %s_signals[N_%s_SIGNALS] = {0};' % (node_name_lc, node_name_uc)) self.b('') self.b('static void %s%s_base_init (gpointer klass);' % (self.prefix_, node_name_lc)) self.b('') self.b('GType') self.b('%s%s_get_type (void)' % (self.prefix_, node_name_lc)) self.b('{') self.b(' static GType type = 0;') self.b('') self.b(' if (G_UNLIKELY (type == 0))') self.b(' {') self.b(' static const GTypeInfo info = {') self.b(' sizeof (%s%sClass),' % (self.Prefix, node_name_mixed)) self.b(' %s%s_base_init, /* base_init */' % (self.prefix_, node_name_lc)) self.b(' NULL, /* base_finalize */') self.b(' NULL, /* class_init */') self.b(' NULL, /* class_finalize */') self.b(' NULL, /* class_data */') self.b(' 0,') self.b(' 0, /* n_preallocs */') self.b(' NULL /* instance_init */') self.b(' };') self.b('') self.b(' type = g_type_register_static (G_TYPE_INTERFACE,') self.b(' "%s%s", &info, 0);' % (self.Prefix, node_name_mixed)) self.b(' }') self.b('') self.b(' return type;') self.b('}') self.b('') self.d('/**') self.d(' * %s%s:' % (self.Prefix, node_name_mixed)) self.d(' *') self.d(' * Dummy typedef representing any implementation of this ' 'interface.') self.d(' */') self.h('typedef struct _%s%s %s%s;' % (self.Prefix, node_name_mixed, self.Prefix, node_name_mixed)) self.h('') self.d('/**') self.d(' * %s%sClass:' % (self.Prefix, node_name_mixed)) self.d(' *') self.d(' * The class of %s%s.' % (self.Prefix, node_name_mixed)) if methods: self.d(' *') self.d(' * In a full implementation of this interface (i.e. all') self.d(' * methods implemented), the interface initialization') self.d(' * function used in G_IMPLEMENT_INTERFACE() would') self.d(' * typically look like this:') self.d(' *') self.d(' * ') self.d(' * static void') self.d(' * implement_%s (gpointer klass,' % self.node_name_lc) self.d(' * gpointer unused G_GNUC_UNUSED)') self.d(' * {') self.d(' * #define IMPLEMENT(x) %s%s_implement_##x (\\' % (self.prefix_, self.node_name_lc)) self.d(' * klass, my_object_##x)') for method in methods: class_member_name = method.getAttribute('tp:name-for-bindings') class_member_name = class_member_name.lower() self.d(' * IMPLEMENT (%s);' % class_member_name) self.d(' * #undef IMPLEMENT') self.d(' * }') self.d(' * ') else: self.d(' * This interface has no D-Bus methods, so an') self.d(' * implementation can typically pass %NULL to') self.d(' * G_IMPLEMENT_INTERFACE() as the interface') self.d(' * initialization function.') self.d(' */') self.d('') self.h('typedef struct _%s%sClass %s%sClass;' % (self.Prefix, node_name_mixed, self.Prefix, node_name_mixed)) self.h('') self.h('GType %s%s_get_type (void);' % (self.prefix_, node_name_lc)) gtype = self.current_gtype = \ self.MAIN_PREFIX_ + 'TYPE' + self._SUB_PREFIX_ + node_name_uc classname = self.Prefix + node_name_mixed self.h('#define %s \\\n (%s%s_get_type ())' % (gtype, self.prefix_, node_name_lc)) self.h('#define %s%s(obj) \\\n' ' (G_TYPE_CHECK_INSTANCE_CAST((obj), %s, %s))' % (self.PREFIX_, node_name_uc, gtype, classname)) self.h('#define %sIS%s%s(obj) \\\n' ' (G_TYPE_CHECK_INSTANCE_TYPE((obj), %s))' % (self.MAIN_PREFIX_, self._SUB_PREFIX_, node_name_uc, gtype)) self.h('#define %s%s_GET_CLASS(obj) \\\n' ' (G_TYPE_INSTANCE_GET_INTERFACE((obj), %s, %sClass))' % (self.PREFIX_, node_name_uc, gtype, classname)) self.h('') self.h('') base_init_code = [] for method in methods: self.do_method(method) for signal in signals: base_init_code.extend(self.do_signal(signal)) self.b('static inline void') self.b('%s%s_base_init_once (gpointer klass G_GNUC_UNUSED)' % (self.prefix_, node_name_lc)) self.b('{') if properties: self.b(' static TpDBusPropertiesMixinPropInfo properties[%d] = {' % (len(properties) + 1)) for m in properties: access = m.getAttribute('access') assert access in ('read', 'write', 'readwrite') if access == 'read': flags = 'TP_DBUS_PROPERTIES_MIXIN_FLAG_READ' elif access == 'write': flags = 'TP_DBUS_PROPERTIES_MIXIN_FLAG_WRITE' else: flags = ('TP_DBUS_PROPERTIES_MIXIN_FLAG_READ | ' 'TP_DBUS_PROPERTIES_MIXIN_FLAG_WRITE') prop_emits_changed = get_emits_changed(m) if prop_emits_changed is None: prop_emits_changed = iface_emits_changed if prop_emits_changed == 'true': flags += ' | TP_DBUS_PROPERTIES_MIXIN_FLAG_EMITS_CHANGED' elif prop_emits_changed == 'invalidates': flags += ' | TP_DBUS_PROPERTIES_MIXIN_FLAG_EMITS_INVALIDATED' self.b(' { 0, %s, "%s", 0, NULL, NULL }, /* %s */' % (flags, m.getAttribute('type'), m.getAttribute('name'))) self.b(' { 0, 0, NULL, 0, NULL, NULL }') self.b(' };') self.b(' static TpDBusPropertiesMixinIfaceInfo interface =') self.b(' { 0, properties, NULL, NULL };') self.b('') self.b(' dbus_g_object_type_install_info (%s%s_get_type (),' % (self.prefix_, node_name_lc)) self.b(' &_%s%s_object_info);' % (self.prefix_, node_name_lc)) self.b('') if properties: self.b(' interface.dbus_interface = g_quark_from_static_string ' '("%s");' % self.iface_name) for i, m in enumerate(properties): self.b(' properties[%d].name = g_quark_from_static_string ("%s");' % (i, m.getAttribute('name'))) self.b(' properties[%d].type = %s;' % (i, type_to_gtype(m.getAttribute('type'))[1])) self.b(' tp_svc_interface_set_dbus_properties_info (%s, &interface);' % self.current_gtype) self.b('') for s in base_init_code: self.b(s) self.b('}') self.b('static void') self.b('%s%s_base_init (gpointer klass)' % (self.prefix_, node_name_lc)) self.b('{') self.b(' static gboolean initialized = FALSE;') self.b('') self.b(' if (!initialized)') self.b(' {') self.b(' initialized = TRUE;') self.b(' %s%s_base_init_once (klass);' % (self.prefix_, node_name_lc)) self.b(' }') # insert anything we need to do per implementation here self.b('}') self.h('') self.b('static const DBusGMethodInfo _%s%s_methods[] = {' % (self.prefix_, node_name_lc)) method_blob, offsets = self.get_method_glue(methods) for method, offset in zip(methods, offsets): self.do_method_glue(method, offset) if len(methods) == 0: # empty arrays are a gcc extension, so put in a dummy member self.b(" { NULL, NULL, 0 }") self.b('};') self.b('') self.b('static const DBusGObjectInfo _%s%s_object_info = {' % (self.prefix_, node_name_lc)) self.b(' 0,') # version self.b(' _%s%s_methods,' % (self.prefix_, node_name_lc)) self.b(' %d,' % len(methods)) self.b('"' + method_blob.replace('\0', '\\0') + '",') self.b('"' + self.get_signal_glue(signals).replace('\0', '\\0') + '",') self.b('"' + self.get_property_glue(glue_properties).replace('\0', '\\0') + '",') self.b('};') self.b('') self.node_name_mixed = None self.node_name_lc = None self.node_name_uc = None def get_method_glue(self, methods): info = [] offsets = [] for method in methods: offsets.append(len(''.join(info))) info.append(self.iface_name + '\0') info.append(method.getAttribute('name') + '\0') info.append('A\0') # async counter = 0 for arg in method.getElementsByTagName('arg'): out = arg.getAttribute('direction') == 'out' name = arg.getAttribute('name') if not name: assert out name = 'arg%u' % counter counter += 1 info.append(name + '\0') if out: info.append('O\0') else: info.append('I\0') if out: info.append('F\0') # not const info.append('N\0') # not error or return info.append(arg.getAttribute('type') + '\0') info.append('\0') return ''.join(info) + '\0', offsets def do_method_glue(self, method, offset): lc_name = method.getAttribute('tp:name-for-bindings') if method.getAttribute('name') != lc_name.replace('_', ''): raise AssertionError('Method %s tp:name-for-bindings (%s) does ' 'not match' % (method.getAttribute('name'), lc_name)) lc_name = lc_name.lower() marshaller = 'g_cclosure_marshal_generic' wrapper = self.prefix_ + self.node_name_lc + '_' + lc_name self.b(" { (GCallback) %s, %s, %d }," % (wrapper, marshaller, offset)) def get_signal_glue(self, signals): info = [] for signal in signals: info.append(self.iface_name) info.append(signal.getAttribute('name')) return '\0'.join(info) + '\0\0' # the implementation can be the same get_property_glue = get_signal_glue def get_method_impl_names(self, method): dbus_method_name = method.getAttribute('name') class_member_name = method.getAttribute('tp:name-for-bindings') if dbus_method_name != class_member_name.replace('_', ''): raise AssertionError('Method %s tp:name-for-bindings (%s) does ' 'not match' % (dbus_method_name, class_member_name)) class_member_name = class_member_name.lower() stub_name = (self.prefix_ + self.node_name_lc + '_' + class_member_name) return (stub_name + '_impl', class_member_name + '_cb') def do_method(self, method): assert self.node_name_mixed is not None in_class = [] # Examples refer to Thing.DoStuff (su) -> ii # DoStuff dbus_method_name = method.getAttribute('name') # do_stuff class_member_name = method.getAttribute('tp:name-for-bindings') if dbus_method_name != class_member_name.replace('_', ''): raise AssertionError('Method %s tp:name-for-bindings (%s) does ' 'not match' % (dbus_method_name, class_member_name)) class_member_name = class_member_name.lower() # void tp_svc_thing_do_stuff (TpSvcThing *, const char *, guint, # DBusGMethodInvocation *); stub_name = (self.prefix_ + self.node_name_lc + '_' + class_member_name) # typedef void (*tp_svc_thing_do_stuff_impl) (TpSvcThing *, # const char *, guint, DBusGMethodInvocation); impl_name = stub_name + '_impl' # void tp_svc_thing_return_from_do_stuff (DBusGMethodInvocation *, # gint, gint); ret_name = (self.prefix_ + self.node_name_lc + '_return_from_' + class_member_name) # Gather arguments in_args = [] out_args = [] for i in method.getElementsByTagName('arg'): name = i.getAttribute('name') direction = i.getAttribute('direction') or 'in' dtype = i.getAttribute('type') assert direction in ('in', 'out') if name: name = direction + '_' + name elif direction == 'in': name = direction + str(len(in_args)) else: name = direction + str(len(out_args)) ctype, gtype, marshaller, pointer = type_to_gtype(dtype) if pointer: ctype = 'const ' + ctype struct = (ctype, name) if direction == 'in': in_args.append(struct) else: out_args.append(struct) # Implementation type declaration (in header, docs separated) self.d('/**') self.d(' * %s:' % impl_name) self.d(' * @self: The object implementing this interface') for (ctype, name) in in_args: self.d(' * @%s: %s (FIXME, generate documentation)' % (name, ctype)) self.d(' * @context: Used to return values or throw an error') self.d(' *') self.d(' * The signature of an implementation of the D-Bus method') self.d(' * %s on interface %s.' % (dbus_method_name, self.iface_name)) self.d(' */') self.h('typedef void (*%s) (%s%s *self,' % (impl_name, self.Prefix, self.node_name_mixed)) for (ctype, name) in in_args: self.h(' %s%s,' % (ctype, name)) self.h(' DBusGMethodInvocation *context);') # Class member (in class definition) in_class.append(' %s %s;' % (impl_name, class_member_name)) # Stub definition (in body only - it's static) self.b('static void') self.b('%s (%s%s *self,' % (stub_name, self.Prefix, self.node_name_mixed)) for (ctype, name) in in_args: self.b(' %s%s,' % (ctype, name)) self.b(' DBusGMethodInvocation *context)') self.b('{') self.b(' %s impl = (%s%s_GET_CLASS (self)->%s_cb);' % (impl_name, self.PREFIX_, self.node_name_uc, class_member_name)) self.b('') self.b(' if (impl != NULL)') tmp = ['self'] + [name for (ctype, name) in in_args] + ['context'] self.b(' {') self.b(' (impl) (%s);' % ',\n '.join(tmp)) self.b(' }') self.b(' else') self.b(' {') if self.not_implemented_func: self.b(' %s (context);' % self.not_implemented_func) else: self.b(' GError e = { DBUS_GERROR, ') self.b(' DBUS_GERROR_UNKNOWN_METHOD,') self.b(' "Method not implemented" };') self.b('') self.b(' dbus_g_method_return_error (context, &e);') self.b(' }') self.b('}') self.b('') # Implementation registration (in both header and body) self.h('void %s%s_implement_%s (%s%sClass *klass, %s impl);' % (self.prefix_, self.node_name_lc, class_member_name, self.Prefix, self.node_name_mixed, impl_name)) self.d('/**') self.d(' * %s%s_implement_%s:' % (self.prefix_, self.node_name_lc, class_member_name)) self.d(' * @klass: A class whose instances implement this interface') self.d(' * @impl: A callback used to implement the %s D-Bus method' % dbus_method_name) self.d(' *') self.d(' * Register an implementation for the %s method in the vtable' % dbus_method_name) self.d(' * of an implementation of this interface. To be called from') self.d(' * the interface init function.') self.d(' */') self.b('void') self.b('%s%s_implement_%s (%s%sClass *klass, %s impl)' % (self.prefix_, self.node_name_lc, class_member_name, self.Prefix, self.node_name_mixed, impl_name)) self.b('{') self.b(' klass->%s_cb = impl;' % class_member_name) self.b('}') self.b('') # Return convenience function (static inline, in header) self.d('/**') self.d(' * %s:' % ret_name) self.d(' * @context: The D-Bus method invocation context') for (ctype, name) in out_args: self.d(' * @%s: %s (FIXME, generate documentation)' % (name, ctype)) self.d(' *') self.d(' * Return successfully by calling dbus_g_method_return().') self.d(' * This inline function exists only to provide type-safety.') self.d(' */') self.d('') tmp = (['DBusGMethodInvocation *context'] + [ctype + name for (ctype, name) in out_args]) self.h('static inline') self.h('/* this comment is to stop gtkdoc realising this is static */') self.h(('void %s (' % ret_name) + (',\n '.join(tmp)) + ');') self.h('static inline void') self.h(('%s (' % ret_name) + (',\n '.join(tmp)) + ')') self.h('{') tmp = ['context'] + [name for (ctype, name) in out_args] self.h(' dbus_g_method_return (' + ',\n '.join(tmp) + ');') self.h('}') self.h('') return in_class def get_signal_const_entry(self, signal): assert self.node_name_uc is not None return ('SIGNAL_%s_%s' % (self.node_name_uc, signal.getAttribute('name'))) def do_signal(self, signal): assert self.node_name_mixed is not None in_base_init = [] # for signal: Thing::StuffHappened (s, u) # we want to emit: # void tp_svc_thing_emit_stuff_happened (gpointer instance, # const char *arg0, guint arg1); dbus_name = signal.getAttribute('name') ugly_name = signal.getAttribute('tp:name-for-bindings') if dbus_name != ugly_name.replace('_', ''): raise AssertionError('Signal %s tp:name-for-bindings (%s) does ' 'not match' % (dbus_name, ugly_name)) stub_name = (self.prefix_ + self.node_name_lc + '_emit_' + ugly_name.lower()) const_name = self.get_signal_const_entry(signal) # Gather arguments args = [] for i in signal.getElementsByTagName('arg'): name = i.getAttribute('name') dtype = i.getAttribute('type') tp_type = i.getAttribute('tp:type') if name: name = 'arg_' + name else: name = 'arg' + str(len(args)) ctype, gtype, marshaller, pointer = type_to_gtype(dtype) if pointer: ctype = 'const ' + ctype struct = (ctype, name, gtype) args.append(struct) tmp = (['gpointer instance'] + [ctype + name for (ctype, name, gtype) in args]) self.h(('void %s (' % stub_name) + (',\n '.join(tmp)) + ');') # FIXME: emit docs self.d('/**') self.d(' * %s:' % stub_name) self.d(' * @instance: The object implementing this interface') for (ctype, name, gtype) in args: self.d(' * @%s: %s (FIXME, generate documentation)' % (name, ctype)) self.d(' *') self.d(' * Type-safe wrapper around g_signal_emit to emit the') self.d(' * %s signal on interface %s.' % (dbus_name, self.iface_name)) self.d(' */') self.b('void') self.b(('%s (' % stub_name) + (',\n '.join(tmp)) + ')') self.b('{') self.b(' g_assert (instance != NULL);') self.b(' g_assert (G_TYPE_CHECK_INSTANCE_TYPE (instance, %s));' % (self.current_gtype)) tmp = (['instance', '%s_signals[%s]' % (self.node_name_lc, const_name), '0'] + [name for (ctype, name, gtype) in args]) self.b(' g_signal_emit (' + ',\n '.join(tmp) + ');') self.b('}') self.b('') signal_name = dbus_gutils_wincaps_to_uscore(dbus_name).replace('_', '-') self.d('/**') self.d(' * %s%s::%s:' % (self.Prefix, self.node_name_mixed, signal_name)) self.d(' * @self: an object') for (ctype, name, gtype) in args: self.d(' * @%s: %s (FIXME, generate documentation)' % (name, ctype)) self.d(' *') self.d(' * The %s D-Bus signal is emitted whenever ' 'this GObject signal is.' % dbus_name) self.d(' */') self.d('') in_base_init.append(' %s_signals[%s] =' % (self.node_name_lc, const_name)) in_base_init.append(' g_signal_new ("%s",' % signal_name) in_base_init.append(' G_OBJECT_CLASS_TYPE (klass),') in_base_init.append(' G_SIGNAL_RUN_LAST|G_SIGNAL_DETAILED,') in_base_init.append(' 0,') in_base_init.append(' NULL, NULL,') in_base_init.append(' g_cclosure_marshal_generic,') in_base_init.append(' G_TYPE_NONE,') tmp = ['%d' % len(args)] + [gtype for (ctype, name, gtype) in args] in_base_init.append(' %s);' % ',\n '.join(tmp)) in_base_init.append('') return in_base_init def have_properties(self, nodes): for node in nodes: interface = node.getElementsByTagName('interface')[0] if interface.getElementsByTagName('property'): return True return False def __call__(self): nodes = self.dom.getElementsByTagName('node') nodes.sort(cmp_by_name) self.h('#include ') self.h('#include ') for header in self.headers: self.h('#include %s' % header) self.h('') self.h('') self.h('G_BEGIN_DECLS') self.h('') self.b('#include "%s.h"' % self.basename) self.b('') for node in nodes: self.do_node(node) self.h('') self.h('G_END_DECLS') self.b('') for header in self.end_headers: self.b('#include %s' % header) self.h('') self.b('') file_set_contents(self.basename + '.h', '\n'.join(self.__header)) file_set_contents(self.basename + '.c', '\n'.join(self.__body)) file_set_contents(self.basename + '-gtk-doc.h', '\n'.join(self.__docs)) def cmdline_error(): print """\ usage: gen-ginterface [OPTIONS] xmlfile Prefix_ options: --include='' (may be repeated) --include='"header.h"' (ditto) --include-end='"header.h"' (ditto) Include extra headers in the generated .c file --signal-marshal-prefix='prefix' Use the given prefix on generated signal marshallers (default is prefix.lower()). --filename='BASENAME' Set the basename for the output files (default is prefix.lower() + 'ginterfaces') --not-implemented-func='symbol' Set action when methods not implemented in the interface vtable are called. symbol must have signature void symbol (DBusGMethodInvocation *context) and return some sort of "not implemented" error via dbus_g_method_return_error (context, ...) """ sys.exit(1) if __name__ == '__main__': from getopt import gnu_getopt options, argv = gnu_getopt(sys.argv[1:], '', ['filename=', 'signal-marshal-prefix=', 'include=', 'include-end=', 'allow-unstable', 'not-implemented-func=']) try: prefix = argv[1] except IndexError: cmdline_error() basename = prefix.lower() + 'ginterfaces' signal_marshal_prefix = prefix.lower().rstrip('_') headers = [] end_headers = [] not_implemented_func = '' allow_havoc = False for option, value in options: if option == '--filename': basename = value elif option == '--signal-marshal-prefix': signal_marshal_prefix = value elif option == '--include': if value[0] not in '<"': value = '"%s"' % value headers.append(value) elif option == '--include-end': if value[0] not in '<"': value = '"%s"' % value end_headers.append(value) elif option == '--not-implemented-func': not_implemented_func = value elif option == '--allow-unstable': allow_havoc = True try: dom = xml.dom.minidom.parse(argv[0]) except IndexError: cmdline_error() Generator(dom, prefix, basename, signal_marshal_prefix, headers, end_headers, not_implemented_func, allow_havoc)() telepathy-gabble-0.18.2/tools/glib-errors-str-gen.py0000644000175000017500000000470012200204333022321 0ustar00smcvsmcv00000000000000#!/usr/bin/python import sys import xml.dom.minidom from libtpcodegen import file_set_contents from libglibcodegen import NS_TP, get_docstring, xml_escape class Generator(object): def __init__(self, dom, basename): self.dom = dom self.errors = self.dom.getElementsByTagNameNS(NS_TP, 'errors')[0] self.basename = basename self.__header = [] self.__body = [] self.__docs = [] def h(self, s): if isinstance(s, unicode): s = s.encode('utf-8') self.__header.append(s) def b(self, s): if isinstance(s, unicode): s = s.encode('utf-8') self.__body.append(s) def d(self, s): if isinstance(s, unicode): s = s.encode('utf-8') self.__docs.append(s) def __call__(self): errors = self.errors.getElementsByTagNameNS(NS_TP, 'error') self.b('#include ') self.b('') self.b('const gchar *') self.b('tp_error_get_dbus_name (TpError error)') self.b('{') self.b(' switch (error)') self.b(' {') for error in errors: ns = error.parentNode.getAttribute('namespace') nick = error.getAttribute('name').replace(' ', '') uc_nick = error.getAttribute('name').replace(' ', '_').replace('.', '_').upper() name = 'TP_ERROR_STR_' + uc_nick error_name = '%s.%s' % (ns, nick) self.d('/**') self.d(' * %s:' % name) self.d(' *') self.d(' * The D-Bus error name %s' % error_name) self.d(' *') self.d(' * %s' % xml_escape(get_docstring(error))) self.d(' */') self.d('') self.h('#define %s "%s"' % (name, error_name)) self.b(' case TP_ERROR_%s:' % uc_nick) self.b(' return %s;' % name) self.b(' default:') self.b(' g_return_val_if_reached (NULL);') self.b(' }') self.b('}') # make both files end with a newline self.h('') self.b('') file_set_contents(self.basename + '.h', '\n'.join(self.__header)) file_set_contents(self.basename + '.c', '\n'.join(self.__body)) file_set_contents(self.basename + '-gtk-doc.h', '\n'.join(self.__docs)) if __name__ == '__main__': argv = sys.argv[1:] basename = argv[0] Generator(xml.dom.minidom.parse(argv[1]), basename)() telepathy-gabble-0.18.2/tools/glib-errors-check-gen.py0000644000175000017500000000431112200204333022564 0ustar00smcvsmcv00000000000000#!/usr/bin/python import sys import xml.dom.minidom from libglibcodegen import NS_TP, get_docstring, get_descendant_text class Generator(object): def __init__(self, dom): self.dom = dom self.errors = self.dom.getElementsByTagNameNS(NS_TP, 'errors')[0] def __call__(self): print '{' print ' GEnumClass *klass;' print ' GEnumValue *value_by_name;' print ' GEnumValue *value_by_nick;' print '' print ' g_type_init ();' print ' klass = g_type_class_ref (TP_TYPE_ERROR);' for error in self.errors.getElementsByTagNameNS(NS_TP, 'error'): ns = error.parentNode.getAttribute('namespace') nick = error.getAttribute('name').replace(' ', '') enum = ('TP_ERROR_' + error.getAttribute('name').replace(' ', '_').replace('.', '_').upper()) s = ('TP_ERROR_STR_' + error.getAttribute('name').replace(' ', '_').replace('.', '_').upper()) print '' print ' /* %s.%s */' % (ns, nick) print (' value_by_name = g_enum_get_value_by_name (klass, "%s");' % enum) print (' value_by_nick = g_enum_get_value_by_nick (klass, "%s");' % nick) print (' g_assert (value_by_name != NULL);') print (' g_assert (value_by_nick != NULL);') print (' g_assert_cmpint (value_by_name->value, ==, %s);' % enum) print (' g_assert_cmpint (value_by_nick->value, ==, %s);' % enum) print (' g_assert_cmpstr (value_by_name->value_name, ==, "%s");' % enum) print (' g_assert_cmpstr (value_by_nick->value_name, ==, "%s");' % enum) print (' g_assert_cmpstr (value_by_name->value_nick, ==, "%s");' % nick) print (' g_assert_cmpstr (value_by_nick->value_nick, ==, "%s");' % nick) print (' g_assert_cmpstr (%s, ==, TP_ERROR_PREFIX ".%s");' % (s, nick)) print '}' if __name__ == '__main__': argv = sys.argv[1:] Generator(xml.dom.minidom.parse(argv[0]))() telepathy-gabble-0.18.2/tools/glib-client-marshaller-gen.py0000644000175000017500000000277212200204333023614 0ustar00smcvsmcv00000000000000#!/usr/bin/python import sys import xml.dom.minidom from string import ascii_letters, digits from libglibcodegen import signal_to_marshal_name NS_TP = "http://telepathy.freedesktop.org/wiki/DbusSpec#extensions-v0" class Generator(object): def __init__(self, dom, prefix): self.dom = dom self.marshallers = {} self.prefix = prefix def do_signal(self, signal): marshaller = signal_to_marshal_name(signal, self.prefix) assert '__' in marshaller rhs = marshaller.split('__', 1)[1].split('_') self.marshallers[marshaller] = rhs def __call__(self): signals = self.dom.getElementsByTagName('signal') for signal in signals: self.do_signal(signal) print 'void' print '%s_register_dbus_glib_marshallers (void)' % self.prefix print '{' all = self.marshallers.keys() all.sort() for marshaller in all: rhs = self.marshallers[marshaller] print ' dbus_g_object_register_marshaller (' print ' g_cclosure_marshal_generic,' print ' G_TYPE_NONE, /* return */' for type in rhs: print ' G_TYPE_%s,' % type.replace('VOID', 'NONE') print ' G_TYPE_INVALID);' print '}' def types_to_gtypes(types): return [type_to_gtype(t)[1] for t in types] if __name__ == '__main__': argv = sys.argv[1:] dom = xml.dom.minidom.parse(argv[0]) Generator(dom, argv[1])() telepathy-gabble-0.18.2/tools/glib-client-gen.py0000644000175000017500000013656112200204333021470 0ustar00smcvsmcv00000000000000#!/usr/bin/python # glib-client-gen.py: "I Can't Believe It's Not dbus-binding-tool" # # Generate GLib client wrappers from the Telepathy specification. # The master copy of this program is in the telepathy-glib repository - # please make any changes there. # # Copyright (C) 2006-2008 Collabora Ltd. # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # # 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 import sys import os.path import xml.dom.minidom from getopt import gnu_getopt from libtpcodegen import file_set_contents from libglibcodegen import Signature, type_to_gtype, cmp_by_name, \ get_docstring, xml_escape, get_deprecated NS_TP = "http://telepathy.freedesktop.org/wiki/DbusSpec#extensions-v0" class Generator(object): def __init__(self, dom, prefix, basename, opts): self.dom = dom self.__header = [] self.__body = [] self.__docs = [] self.prefix_lc = prefix.lower() self.prefix_uc = prefix.upper() self.prefix_mc = prefix.replace('_', '') self.basename = basename self.group = opts.get('--group', None) self.iface_quark_prefix = opts.get('--iface-quark-prefix', None) self.tp_proxy_api = tuple(map(int, opts.get('--tp-proxy-api', '0').split('.'))) self.proxy_cls = opts.get('--subclass', 'TpProxy') + ' *' self.proxy_arg = opts.get('--subclass', 'void') + ' *' self.proxy_assert = opts.get('--subclass-assert', 'TP_IS_PROXY') self.proxy_doc = ('A #%s or subclass' % opts.get('--subclass', 'TpProxy')) if self.proxy_arg == 'void *': self.proxy_arg = 'gpointer ' self.reentrant_symbols = set() try: filename = opts['--generate-reentrant'] with open(filename, 'r') as f: for line in f.readlines(): self.reentrant_symbols.add(line.strip()) except KeyError: pass self.deprecate_reentrant = opts.get('--deprecate-reentrant', None) self.deprecation_attribute = opts.get('--deprecation-attribute', 'G_GNUC_DEPRECATED') self.guard = opts.get('--guard', None) def h(self, s): if isinstance(s, unicode): s = s.encode('utf-8') self.__header.append(s) def b(self, s): if isinstance(s, unicode): s = s.encode('utf-8') self.__body.append(s) def d(self, s): if isinstance(s, unicode): s = s.encode('utf-8') self.__docs.append(s) def get_iface_quark(self): assert self.iface_dbus is not None assert self.iface_uc is not None if self.iface_quark_prefix is None: return 'g_quark_from_static_string (\"%s\")' % self.iface_dbus else: return '%s_%s' % (self.iface_quark_prefix, self.iface_uc) def do_signal(self, iface, signal): iface_lc = iface.lower() member = signal.getAttribute('name') member_lc = signal.getAttribute('tp:name-for-bindings') if member != member_lc.replace('_', ''): raise AssertionError('Signal %s tp:name-for-bindings (%s) does ' 'not match' % (member, member_lc)) member_lc = member_lc.lower() member_uc = member_lc.upper() arg_count = 0 args = [] out_args = [] for arg in signal.getElementsByTagName('arg'): name = arg.getAttribute('name') type = arg.getAttribute('type') tp_type = arg.getAttribute('tp:type') if not name: name = 'arg%u' % arg_count arg_count += 1 else: name = 'arg_%s' % name info = type_to_gtype(type) args.append((name, info, tp_type, arg)) callback_name = ('%s_%s_signal_callback_%s' % (self.prefix_lc, iface_lc, member_lc)) collect_name = ('_%s_%s_collect_args_of_%s' % (self.prefix_lc, iface_lc, member_lc)) invoke_name = ('_%s_%s_invoke_callback_for_%s' % (self.prefix_lc, iface_lc, member_lc)) # Example: # # typedef void (*tp_cli_connection_signal_callback_new_channel) # (TpConnection *proxy, const gchar *arg_object_path, # const gchar *arg_channel_type, guint arg_handle_type, # guint arg_handle, gboolean arg_suppress_handler, # gpointer user_data, GObject *weak_object); self.d('/**') self.d(' * %s:' % callback_name) self.d(' * @proxy: The proxy on which %s_%s_connect_to_%s ()' % (self.prefix_lc, iface_lc, member_lc)) self.d(' * was called') for arg in args: name, info, tp_type, elt = arg ctype, gtype, marshaller, pointer = info docs = get_docstring(elt) or '(Undocumented)' if ctype == 'guint ' and tp_type != '': docs += ' (#%s)' % ('Tp' + tp_type.replace('_', '')) self.d(' * @%s: %s' % (name, xml_escape(docs))) self.d(' * @user_data: User-supplied data') self.d(' * @weak_object: User-supplied weakly referenced object') self.d(' *') self.d(' * Represents the signature of a callback for the signal %s.' % member) self.d(' */') self.d('') self.h('typedef void (*%s) (%sproxy,' % (callback_name, self.proxy_cls)) for arg in args: name, info, tp_type, elt = arg ctype, gtype, marshaller, pointer = info const = pointer and 'const ' or '' self.h(' %s%s%s,' % (const, ctype, name)) self.h(' gpointer user_data, GObject *weak_object);') if args: self.b('static void') self.b('%s (DBusGProxy *proxy G_GNUC_UNUSED,' % collect_name) for arg in args: name, info, tp_type, elt = arg ctype, gtype, marshaller, pointer = info const = pointer and 'const ' or '' self.b(' %s%s%s,' % (const, ctype, name)) self.b(' TpProxySignalConnection *sc)') self.b('{') self.b(' GValueArray *args = g_value_array_new (%d);' % len(args)) self.b(' GValue blank = { 0 };') self.b(' guint i;') self.b('') self.b(' g_value_init (&blank, G_TYPE_INT);') self.b('') self.b(' for (i = 0; i < %d; i++)' % len(args)) self.b(' g_value_array_append (args, &blank);') self.b('') for i, arg in enumerate(args): name, info, tp_type, elt = arg ctype, gtype, marshaller, pointer = info self.b(' g_value_unset (args->values + %d);' % i) self.b(' g_value_init (args->values + %d, %s);' % (i, gtype)) if gtype == 'G_TYPE_STRING': self.b(' g_value_set_string (args->values + %d, %s);' % (i, name)) elif marshaller == 'BOXED': self.b(' g_value_set_boxed (args->values + %d, %s);' % (i, name)) elif gtype == 'G_TYPE_UCHAR': self.b(' g_value_set_uchar (args->values + %d, %s);' % (i, name)) elif gtype == 'G_TYPE_BOOLEAN': self.b(' g_value_set_boolean (args->values + %d, %s);' % (i, name)) elif gtype == 'G_TYPE_INT': self.b(' g_value_set_int (args->values + %d, %s);' % (i, name)) elif gtype == 'G_TYPE_UINT': self.b(' g_value_set_uint (args->values + %d, %s);' % (i, name)) elif gtype == 'G_TYPE_INT64': self.b(' g_value_set_int (args->values + %d, %s);' % (i, name)) elif gtype == 'G_TYPE_UINT64': self.b(' g_value_set_uint64 (args->values + %d, %s);' % (i, name)) elif gtype == 'G_TYPE_DOUBLE': self.b(' g_value_set_double (args->values + %d, %s);' % (i, name)) else: assert False, ("Don't know how to put %s in a GValue" % gtype) self.b('') self.b(' tp_proxy_signal_connection_v0_take_results (sc, args);') self.b('}') self.b('static void') self.b('%s (TpProxy *tpproxy,' % invoke_name) self.b(' GError *error G_GNUC_UNUSED,') self.b(' GValueArray *args,') self.b(' GCallback generic_callback,') self.b(' gpointer user_data,') self.b(' GObject *weak_object)') self.b('{') self.b(' %s callback =' % callback_name) self.b(' (%s) generic_callback;' % callback_name) self.b('') self.b(' if (callback != NULL)') self.b(' callback (g_object_ref (tpproxy),') # FIXME: factor out into a function for i, arg in enumerate(args): name, info, tp_type, elt = arg ctype, gtype, marshaller, pointer = info if marshaller == 'BOXED': self.b(' g_value_get_boxed (args->values + %d),' % i) elif gtype == 'G_TYPE_STRING': self.b(' g_value_get_string (args->values + %d),' % i) elif gtype == 'G_TYPE_UCHAR': self.b(' g_value_get_uchar (args->values + %d),' % i) elif gtype == 'G_TYPE_BOOLEAN': self.b(' g_value_get_boolean (args->values + %d),' % i) elif gtype == 'G_TYPE_UINT': self.b(' g_value_get_uint (args->values + %d),' % i) elif gtype == 'G_TYPE_INT': self.b(' g_value_get_int (args->values + %d),' % i) elif gtype == 'G_TYPE_UINT64': self.b(' g_value_get_uint64 (args->values + %d),' % i) elif gtype == 'G_TYPE_INT64': self.b(' g_value_get_int64 (args->values + %d),' % i) elif gtype == 'G_TYPE_DOUBLE': self.b(' g_value_get_double (args->values + %d),' % i) else: assert False, "Don't know how to get %s from a GValue" % gtype self.b(' user_data,') self.b(' weak_object);') self.b('') if len(args) > 0: self.b(' g_value_array_free (args);') else: self.b(' if (args != NULL)') self.b(' g_value_array_free (args);') self.b('') self.b(' g_object_unref (tpproxy);') self.b('}') # Example: # # TpProxySignalConnection * # tp_cli_connection_connect_to_new_channel # (TpConnection *proxy, # tp_cli_connection_signal_callback_new_channel callback, # gpointer user_data, # GDestroyNotify destroy); # # destroy is invoked when the signal becomes disconnected. This # is either because the signal has been disconnected explicitly # by the user, because the TpProxy has become invalid and # emitted the 'invalidated' signal, or because the weakly referenced # object has gone away. self.d('/**') self.d(' * %s_%s_connect_to_%s:' % (self.prefix_lc, iface_lc, member_lc)) self.d(' * @proxy: %s' % self.proxy_doc) self.d(' * @callback: Callback to be called when the signal is') self.d(' * received') self.d(' * @user_data: User-supplied data for the callback') self.d(' * @destroy: Destructor for the user-supplied data, which') self.d(' * will be called when this signal is disconnected, or') self.d(' * before this function returns %NULL') self.d(' * @weak_object: A #GObject which will be weakly referenced; ') self.d(' * if it is destroyed, this callback will automatically be') self.d(' * disconnected') self.d(' * @error: If not %NULL, used to raise an error if %NULL is') self.d(' * returned') self.d(' *') self.d(' * Connect a handler to the signal %s.' % member) self.d(' *') self.d(' * %s' % xml_escape(get_docstring(signal) or '(Undocumented)')) self.d(' *') self.d(' * Returns: a #TpProxySignalConnection containing all of the') self.d(' * above, which can be used to disconnect the signal; or') self.d(' * %NULL if the proxy does not have the desired interface') self.d(' * or has become invalid.') self.d(' */') self.d('') self.h('TpProxySignalConnection *%s_%s_connect_to_%s (%sproxy,' % (self.prefix_lc, iface_lc, member_lc, self.proxy_arg)) self.h(' %s callback,' % callback_name) self.h(' gpointer user_data,') self.h(' GDestroyNotify destroy,') self.h(' GObject *weak_object,') self.h(' GError **error);') self.h('') self.b('TpProxySignalConnection *') self.b('%s_%s_connect_to_%s (%sproxy,' % (self.prefix_lc, iface_lc, member_lc, self.proxy_arg)) self.b(' %s callback,' % callback_name) self.b(' gpointer user_data,') self.b(' GDestroyNotify destroy,') self.b(' GObject *weak_object,') self.b(' GError **error)') self.b('{') self.b(' GType expected_types[%d] = {' % (len(args) + 1)) for arg in args: name, info, tp_type, elt = arg ctype, gtype, marshaller, pointer = info self.b(' %s,' % gtype) self.b(' G_TYPE_INVALID };') self.b('') self.b(' g_return_val_if_fail (%s (proxy), NULL);' % self.proxy_assert) self.b(' g_return_val_if_fail (callback != NULL, NULL);') self.b('') self.b(' return tp_proxy_signal_connection_v0_new ((TpProxy *) proxy,') self.b(' %s, \"%s\",' % (self.get_iface_quark(), member)) self.b(' expected_types,') if args: self.b(' G_CALLBACK (%s),' % collect_name) else: self.b(' NULL, /* no args => no collector function */') self.b(' %s,' % invoke_name) self.b(' G_CALLBACK (callback), user_data, destroy,') self.b(' weak_object, error);') self.b('}') self.b('') def do_method(self, iface, method): iface_lc = iface.lower() member = method.getAttribute('name') member_lc = method.getAttribute('tp:name-for-bindings') if member != member_lc.replace('_', ''): raise AssertionError('Method %s tp:name-for-bindings (%s) does ' 'not match' % (member, member_lc)) member_lc = member_lc.lower() member_uc = member_lc.upper() in_count = 0 ret_count = 0 in_args = [] out_args = [] for arg in method.getElementsByTagName('arg'): name = arg.getAttribute('name') direction = arg.getAttribute('direction') type = arg.getAttribute('type') tp_type = arg.getAttribute('tp:type') if direction != 'out': if not name: name = 'in%u' % in_count in_count += 1 else: name = 'in_%s' % name else: if not name: name = 'out%u' % ret_count ret_count += 1 else: name = 'out_%s' % name info = type_to_gtype(type) if direction != 'out': in_args.append((name, info, tp_type, arg)) else: out_args.append((name, info, tp_type, arg)) # Async reply callback type # Example: # void (*tp_cli_properties_interface_callback_for_get_properties) # (TpProxy *proxy, # const GPtrArray *out0, # const GError *error, # gpointer user_data, # GObject *weak_object); self.d('/**') self.d(' * %s_%s_callback_for_%s:' % (self.prefix_lc, iface_lc, member_lc)) self.d(' * @proxy: the proxy on which the call was made') for arg in out_args: name, info, tp_type, elt = arg ctype, gtype, marshaller, pointer = info docs = xml_escape(get_docstring(elt) or '(Undocumented)') if ctype == 'guint ' and tp_type != '': docs += ' (#%s)' % ('Tp' + tp_type.replace('_', '')) self.d(' * @%s: Used to return an \'out\' argument if @error is ' '%%NULL: %s' % (name, docs)) self.d(' * @error: %NULL on success, or an error on failure') self.d(' * @user_data: user-supplied data') self.d(' * @weak_object: user-supplied object') self.d(' *') self.d(' * Signature of the callback called when a %s method call' % member) self.d(' * succeeds or fails.') deprecated = method.getElementsByTagName('tp:deprecated') if deprecated: d = deprecated[0] self.d(' *') self.d(' * Deprecated: %s' % xml_escape(get_deprecated(d))) self.d(' */') self.d('') callback_name = '%s_%s_callback_for_%s' % (self.prefix_lc, iface_lc, member_lc) self.h('typedef void (*%s) (%sproxy,' % (callback_name, self.proxy_cls)) for arg in out_args: name, info, tp_type, elt = arg ctype, gtype, marshaller, pointer = info const = pointer and 'const ' or '' self.h(' %s%s%s,' % (const, ctype, name)) self.h(' const GError *error, gpointer user_data,') self.h(' GObject *weak_object);') self.h('') # Async callback implementation invoke_callback = '_%s_%s_invoke_callback_%s' % (self.prefix_lc, iface_lc, member_lc) collect_callback = '_%s_%s_collect_callback_%s' % (self.prefix_lc, iface_lc, member_lc) # The callback called by dbus-glib; this ends the call and collects # the results into a GValueArray. self.b('static void') self.b('%s (DBusGProxy *proxy,' % collect_callback) self.b(' DBusGProxyCall *call,') self.b(' gpointer user_data)') self.b('{') self.b(' GError *error = NULL;') if len(out_args) > 0: self.b(' GValueArray *args;') self.b(' GValue blank = { 0 };') self.b(' guint i;') for arg in out_args: name, info, tp_type, elt = arg ctype, gtype, marshaller, pointer = info # "We handle variants specially; the caller is expected to # have already allocated storage for them". Thanks, # dbus-glib... if gtype == 'G_TYPE_VALUE': self.b(' GValue *%s = g_new0 (GValue, 1);' % name) else: self.b(' %s%s;' % (ctype, name)) self.b('') self.b(' dbus_g_proxy_end_call (proxy, call, &error,') for arg in out_args: name, info, tp_type, elt = arg ctype, gtype, marshaller, pointer = info if gtype == 'G_TYPE_VALUE': self.b(' %s, %s,' % (gtype, name)) else: self.b(' %s, &%s,' % (gtype, name)) self.b(' G_TYPE_INVALID);') if len(out_args) == 0: self.b(' tp_proxy_pending_call_v0_take_results (user_data, error,' 'NULL);') else: self.b('') self.b(' if (error != NULL)') self.b(' {') self.b(' tp_proxy_pending_call_v0_take_results (user_data, error,') self.b(' NULL);') for arg in out_args: name, info, tp_type, elt = arg ctype, gtype, marshaller, pointer = info if gtype == 'G_TYPE_VALUE': self.b(' g_free (%s);' % name) self.b(' return;') self.b(' }') self.b('') self.b(' args = g_value_array_new (%d);' % len(out_args)) self.b(' g_value_init (&blank, G_TYPE_INT);') self.b('') self.b(' for (i = 0; i < %d; i++)' % len(out_args)) self.b(' g_value_array_append (args, &blank);') for i, arg in enumerate(out_args): name, info, tp_type, elt = arg ctype, gtype, marshaller, pointer = info self.b('') self.b(' g_value_unset (args->values + %d);' % i) self.b(' g_value_init (args->values + %d, %s);' % (i, gtype)) if gtype == 'G_TYPE_STRING': self.b(' g_value_take_string (args->values + %d, %s);' % (i, name)) elif marshaller == 'BOXED': self.b(' g_value_take_boxed (args->values + %d, %s);' % (i, name)) elif gtype == 'G_TYPE_UCHAR': self.b(' g_value_set_uchar (args->values + %d, %s);' % (i, name)) elif gtype == 'G_TYPE_BOOLEAN': self.b(' g_value_set_boolean (args->values + %d, %s);' % (i, name)) elif gtype == 'G_TYPE_INT': self.b(' g_value_set_int (args->values + %d, %s);' % (i, name)) elif gtype == 'G_TYPE_UINT': self.b(' g_value_set_uint (args->values + %d, %s);' % (i, name)) elif gtype == 'G_TYPE_INT64': self.b(' g_value_set_int (args->values + %d, %s);' % (i, name)) elif gtype == 'G_TYPE_UINT64': self.b(' g_value_set_uint (args->values + %d, %s);' % (i, name)) elif gtype == 'G_TYPE_DOUBLE': self.b(' g_value_set_double (args->values + %d, %s);' % (i, name)) else: assert False, ("Don't know how to put %s in a GValue" % gtype) self.b(' tp_proxy_pending_call_v0_take_results (user_data, ' 'NULL, args);') self.b('}') self.b('static void') self.b('%s (TpProxy *self,' % invoke_callback) self.b(' GError *error,') self.b(' GValueArray *args,') self.b(' GCallback generic_callback,') self.b(' gpointer user_data,') self.b(' GObject *weak_object)') self.b('{') self.b(' %s callback = (%s) generic_callback;' % (callback_name, callback_name)) self.b('') self.b(' if (error != NULL)') self.b(' {') self.b(' callback ((%s) self,' % self.proxy_cls) for arg in out_args: name, info, tp_type, elt = arg ctype, gtype, marshaller, pointer = info if marshaller == 'BOXED' or pointer: self.b(' NULL,') elif gtype == 'G_TYPE_DOUBLE': self.b(' 0.0,') else: self.b(' 0,') self.b(' error, user_data, weak_object);') self.b(' g_error_free (error);') self.b(' return;') self.b(' }') self.b(' callback ((%s) self,' % self.proxy_cls) # FIXME: factor out into a function for i, arg in enumerate(out_args): name, info, tp_type, elt = arg ctype, gtype, marshaller, pointer = info if marshaller == 'BOXED': self.b(' g_value_get_boxed (args->values + %d),' % i) elif gtype == 'G_TYPE_STRING': self.b(' g_value_get_string (args->values + %d),' % i) elif gtype == 'G_TYPE_UCHAR': self.b(' g_value_get_uchar (args->values + %d),' % i) elif gtype == 'G_TYPE_BOOLEAN': self.b(' g_value_get_boolean (args->values + %d),' % i) elif gtype == 'G_TYPE_UINT': self.b(' g_value_get_uint (args->values + %d),' % i) elif gtype == 'G_TYPE_INT': self.b(' g_value_get_int (args->values + %d),' % i) elif gtype == 'G_TYPE_UINT64': self.b(' g_value_get_uint64 (args->values + %d),' % i) elif gtype == 'G_TYPE_INT64': self.b(' g_value_get_int64 (args->values + %d),' % i) elif gtype == 'G_TYPE_DOUBLE': self.b(' g_value_get_double (args->values + %d),' % i) else: assert False, "Don't know how to get %s from a GValue" % gtype self.b(' error, user_data, weak_object);') self.b('') if len(out_args) > 0: self.b(' g_value_array_free (args);') else: self.b(' if (args != NULL)') self.b(' g_value_array_free (args);') self.b('}') self.b('') # Async stub # Example: # TpProxyPendingCall * # tp_cli_properties_interface_call_get_properties # (gpointer proxy, # gint timeout_ms, # const GArray *in_properties, # tp_cli_properties_interface_callback_for_get_properties callback, # gpointer user_data, # GDestroyNotify *destructor); self.h('TpProxyPendingCall *%s_%s_call_%s (%sproxy,' % (self.prefix_lc, iface_lc, member_lc, self.proxy_arg)) self.h(' gint timeout_ms,') self.d('/**') self.d(' * %s_%s_call_%s:' % (self.prefix_lc, iface_lc, member_lc)) self.d(' * @proxy: the #TpProxy') self.d(' * @timeout_ms: the timeout in milliseconds, or -1 to use the') self.d(' * default') for arg in in_args: name, info, tp_type, elt = arg ctype, gtype, marshaller, pointer = info docs = xml_escape(get_docstring(elt) or '(Undocumented)') if ctype == 'guint ' and tp_type != '': docs += ' (#%s)' % ('Tp' + tp_type.replace('_', '')) self.d(' * @%s: Used to pass an \'in\' argument: %s' % (name, docs)) self.d(' * @callback: called when the method call succeeds or fails;') self.d(' * may be %NULL to make a "fire and forget" call with no ') self.d(' * reply tracking') self.d(' * @user_data: user-supplied data passed to the callback;') self.d(' * must be %NULL if @callback is %NULL') self.d(' * @destroy: called with the user_data as argument, after the') self.d(' * call has succeeded, failed or been cancelled;') self.d(' * must be %NULL if @callback is %NULL') self.d(' * @weak_object: If not %NULL, a #GObject which will be ') self.d(' * weakly referenced; if it is destroyed, this call ') self.d(' * will automatically be cancelled. Must be %NULL if ') self.d(' * @callback is %NULL') self.d(' *') self.d(' * Start a %s method call.' % member) self.d(' *') self.d(' * %s' % xml_escape(get_docstring(method) or '(Undocumented)')) self.d(' *') self.d(' * Returns: a #TpProxyPendingCall representing the call in') self.d(' * progress. It is borrowed from the object, and will become') self.d(' * invalid when the callback is called, the call is') self.d(' * cancelled or the #TpProxy becomes invalid.') deprecated = method.getElementsByTagName('tp:deprecated') if deprecated: d = deprecated[0] self.d(' *') self.d(' * Deprecated: %s' % xml_escape(get_deprecated(d))) self.d(' */') self.d('') self.b('TpProxyPendingCall *\n%s_%s_call_%s (%sproxy,' % (self.prefix_lc, iface_lc, member_lc, self.proxy_arg)) self.b(' gint timeout_ms,') for arg in in_args: name, info, tp_type, elt = arg ctype, gtype, marshaller, pointer = info const = pointer and 'const ' or '' self.h(' %s%s%s,' % (const, ctype, name)) self.b(' %s%s%s,' % (const, ctype, name)) self.h(' %s callback,' % callback_name) self.h(' gpointer user_data,') self.h(' GDestroyNotify destroy,') self.h(' GObject *weak_object);') self.h('') self.b(' %s callback,' % callback_name) self.b(' gpointer user_data,') self.b(' GDestroyNotify destroy,') self.b(' GObject *weak_object)') self.b('{') self.b(' GError *error = NULL;') self.b(' GQuark interface = %s;' % self.get_iface_quark()) self.b(' DBusGProxy *iface;') self.b('') self.b(' g_return_val_if_fail (%s (proxy), NULL);' % self.proxy_assert) self.b(' g_return_val_if_fail (callback != NULL || ' 'user_data == NULL, NULL);') self.b(' g_return_val_if_fail (callback != NULL || ' 'destroy == NULL, NULL);') self.b(' g_return_val_if_fail (callback != NULL || ' 'weak_object == NULL, NULL);') self.b('') self.b(' G_GNUC_BEGIN_IGNORE_DEPRECATIONS') self.b(' iface = tp_proxy_borrow_interface_by_id (') self.b(' (TpProxy *) proxy,') self.b(' interface, &error);') self.b(' G_GNUC_END_IGNORE_DEPRECATIONS') self.b('') self.b(' if (iface == NULL)') self.b(' {') self.b(' if (callback != NULL)') self.b(' callback (proxy,') for arg in out_args: name, info, tp_type, elt = arg ctype, gtype, marshaller, pointer = info if pointer: self.b(' NULL,') else: self.b(' 0,') self.b(' error, user_data, weak_object);') self.b('') self.b(' if (destroy != NULL)') self.b(' destroy (user_data);') self.b('') self.b(' g_error_free (error);') self.b(' return NULL;') self.b(' }') self.b('') self.b(' if (callback == NULL)') self.b(' {') self.b(' dbus_g_proxy_call_no_reply (iface, "%s",' % member) for arg in in_args: name, info, tp_type, elt = arg ctype, gtype, marshaller, pointer = info const = pointer and 'const ' or '' self.b(' %s, %s,' % (gtype, name)) self.b(' G_TYPE_INVALID);') self.b(' return NULL;') self.b(' }') self.b(' else') self.b(' {') self.b(' TpProxyPendingCall *data;') self.b('') self.b(' data = tp_proxy_pending_call_v0_new ((TpProxy *) proxy,') self.b(' interface, "%s", iface,' % member) self.b(' %s,' % invoke_callback) self.b(' G_CALLBACK (callback), user_data, destroy,') self.b(' weak_object, FALSE);') self.b(' tp_proxy_pending_call_v0_take_pending_call (data,') self.b(' dbus_g_proxy_begin_call_with_timeout (iface,') self.b(' "%s",' % member) self.b(' %s,' % collect_callback) self.b(' data,') self.b(' tp_proxy_pending_call_v0_completed,') self.b(' timeout_ms,') for arg in in_args: name, info, tp_type, elt = arg ctype, gtype, marshaller, pointer = info const = pointer and 'const ' or '' self.b(' %s, %s,' % (gtype, name)) self.b(' G_TYPE_INVALID));') self.b('') self.b(' return data;') self.b(' }') self.b('}') self.b('') self.do_method_reentrant(method, iface_lc, member, member_lc, in_args, out_args, collect_callback) # leave a gap for the end of the method self.d('') self.b('') self.h('') def do_method_reentrant(self, method, iface_lc, member, member_lc, in_args, out_args, collect_callback): # Reentrant blocking calls # Example: # gboolean tp_cli_properties_interface_run_get_properties # (gpointer proxy, # gint timeout_ms, # const GArray *in_properties, # GPtrArray **out0, # GError **error, # GMainLoop **loop); run_method_name = '%s_%s_run_%s' % (self.prefix_lc, iface_lc, member_lc) if run_method_name not in self.reentrant_symbols: return self.b('typedef struct {') self.b(' GMainLoop *loop;') self.b(' GError **error;') for arg in out_args: name, info, tp_type, elt = arg ctype, gtype, marshaller, pointer = info self.b(' %s*%s;' % (ctype, name)) self.b(' unsigned success:1;') self.b(' unsigned completed:1;') self.b('} _%s_%s_run_state_%s;' % (self.prefix_lc, iface_lc, member_lc)) reentrant_invoke = '_%s_%s_finish_running_%s' % (self.prefix_lc, iface_lc, member_lc) self.b('static void') self.b('%s (TpProxy *self G_GNUC_UNUSED,' % reentrant_invoke) self.b(' GError *error,') self.b(' GValueArray *args,') self.b(' GCallback unused G_GNUC_UNUSED,') self.b(' gpointer user_data G_GNUC_UNUSED,') self.b(' GObject *unused2 G_GNUC_UNUSED)') self.b('{') self.b(' _%s_%s_run_state_%s *state = user_data;' % (self.prefix_lc, iface_lc, member_lc)) self.b('') self.b(' state->success = (error == NULL);') self.b(' state->completed = TRUE;') self.b(' g_main_loop_quit (state->loop);') self.b('') self.b(' if (error != NULL)') self.b(' {') self.b(' if (state->error != NULL)') self.b(' *state->error = error;') self.b(' else') self.b(' g_error_free (error);') self.b('') self.b(' return;') self.b(' }') self.b('') for i, arg in enumerate(out_args): name, info, tp_type, elt = arg ctype, gtype, marshaller, pointer = info self.b(' if (state->%s != NULL)' % name) if marshaller == 'BOXED': self.b(' *state->%s = g_value_dup_boxed (' 'args->values + %d);' % (name, i)) elif marshaller == 'STRING': self.b(' *state->%s = g_value_dup_string ' '(args->values + %d);' % (name, i)) elif marshaller in ('UCHAR', 'BOOLEAN', 'INT', 'UINT', 'INT64', 'UINT64', 'DOUBLE'): self.b(' *state->%s = g_value_get_%s (args->values + %d);' % (name, marshaller.lower(), i)) else: assert False, "Don't know how to copy %s" % gtype self.b('') if len(out_args) > 0: self.b(' g_value_array_free (args);') else: self.b(' if (args != NULL)') self.b(' g_value_array_free (args);') self.b('}') self.b('') if self.deprecate_reentrant: self.h('#ifndef %s' % self.deprecate_reentrant) self.h('gboolean %s (%sproxy,' % (run_method_name, self.proxy_arg)) self.h(' gint timeout_ms,') self.d('/**') self.d(' * %s:' % run_method_name) self.d(' * @proxy: %s' % self.proxy_doc) self.d(' * @timeout_ms: Timeout in milliseconds, or -1 for default') for arg in in_args: name, info, tp_type, elt = arg ctype, gtype, marshaller, pointer = info docs = xml_escape(get_docstring(elt) or '(Undocumented)') if ctype == 'guint ' and tp_type != '': docs += ' (#%s)' % ('Tp' + tp_type.replace('_', '')) self.d(' * @%s: Used to pass an \'in\' argument: %s' % (name, docs)) for arg in out_args: name, info, tp_type, elt = arg ctype, gtype, marshaller, pointer = info self.d(' * @%s: Used to return an \'out\' argument if %%TRUE is ' 'returned: %s' % (name, xml_escape(get_docstring(elt) or '(Undocumented)'))) self.d(' * @error: If not %NULL, used to return errors if %FALSE ') self.d(' * is returned') self.d(' * @loop: If not %NULL, set before re-entering ') self.d(' * the main loop, to point to a #GMainLoop ') self.d(' * which can be used to cancel this call with ') self.d(' * g_main_loop_quit(), causing a return of ') self.d(' * %FALSE with @error set to %TP_DBUS_ERROR_CANCELLED') self.d(' *') self.d(' * Call the method %s and run the main loop' % member) self.d(' * until it returns. Before calling this method, you must') self.d(' * add a reference to any borrowed objects you need to keep,') self.d(' * and generally ensure that everything is in a consistent') self.d(' * state.') self.d(' *') self.d(' * %s' % xml_escape(get_docstring(method) or '(Undocumented)')) self.d(' *') self.d(' * Returns: TRUE on success, FALSE and sets @error on error') deprecated = method.getElementsByTagName('tp:deprecated') if deprecated: d = deprecated[0] self.d(' *') self.d(' * Deprecated: %s' % xml_escape(get_deprecated(d))) self.d(' */') self.d('') self.b('gboolean\n%s (%sproxy,' % (run_method_name, self.proxy_arg)) self.b(' gint timeout_ms,') for arg in in_args: name, info, tp_type, elt = arg ctype, gtype, marshaller, pointer = info const = pointer and 'const ' or '' self.h(' %s%s%s,' % (const, ctype, name)) self.b(' %s%s%s,' % (const, ctype, name)) for arg in out_args: name, info, tp_type, elt = arg ctype, gtype, marshaller, pointer = info self.h(' %s*%s,' % (ctype, name)) self.b(' %s*%s,' % (ctype, name)) self.h(' GError **error,') if self.deprecate_reentrant: self.h(' GMainLoop **loop) %s;' % self.deprecation_attribute) self.h('#endif /* not %s */' % self.deprecate_reentrant) else: self.h(' GMainLoop **loop);') self.h('') self.b(' GError **error,') self.b(' GMainLoop **loop)') self.b('{') self.b(' DBusGProxy *iface;') self.b(' GQuark interface = %s;' % self.get_iface_quark()) self.b(' TpProxyPendingCall *pc;') self.b(' _%s_%s_run_state_%s state = {' % (self.prefix_lc, iface_lc, member_lc)) self.b(' NULL /* loop */, error,') for arg in out_args: name, info, tp_type, elt = arg self.b(' %s,' % name) self.b(' FALSE /* completed */, FALSE /* success */ };') self.b('') self.b(' g_return_val_if_fail (%s (proxy), FALSE);' % self.proxy_assert) self.b('') self.b(' G_GNUC_BEGIN_IGNORE_DEPRECATIONS') self.b(' iface = tp_proxy_borrow_interface_by_id') self.b(' ((TpProxy *) proxy, interface, error);') self.b(' G_GNUC_END_IGNORE_DEPRECATIONS') self.b('') self.b(' if (iface == NULL)') self.b(' return FALSE;') self.b('') self.b(' state.loop = g_main_loop_new (NULL, FALSE);') self.b('') self.b(' pc = tp_proxy_pending_call_v0_new ((TpProxy *) proxy,') self.b(' interface, "%s", iface,' % member) self.b(' %s,' % reentrant_invoke) self.b(' NULL, &state, NULL, NULL, TRUE);') self.b('') self.b(' if (loop != NULL)') self.b(' *loop = state.loop;') self.b('') self.b(' tp_proxy_pending_call_v0_take_pending_call (pc,') self.b(' dbus_g_proxy_begin_call_with_timeout (iface,') self.b(' "%s",' % member) self.b(' %s,' % collect_callback) self.b(' pc,') self.b(' tp_proxy_pending_call_v0_completed,') self.b(' timeout_ms,') for arg in in_args: name, info, tp_type, elt = arg ctype, gtype, marshaller, pointer = info const = pointer and 'const ' or '' self.b(' %s, %s,' % (gtype, name)) self.b(' G_TYPE_INVALID));') self.b('') self.b(' if (!state.completed)') self.b(' g_main_loop_run (state.loop);') self.b('') self.b(' if (!state.completed)') self.b(' tp_proxy_pending_call_cancel (pc);') self.b('') self.b(' if (loop != NULL)') self.b(' *loop = NULL;') self.b('') self.b(' g_main_loop_unref (state.loop);') self.b('') self.b(' return state.success;') self.b('}') self.b('') def do_signal_add(self, signal): marshaller_items = [] gtypes = [] for i in signal.getElementsByTagName('arg'): name = i.getAttribute('name') type = i.getAttribute('type') info = type_to_gtype(type) # type, GType, STRING, is a pointer gtypes.append(info[1]) self.b(' dbus_g_proxy_add_signal (proxy, "%s",' % signal.getAttribute('name')) for gtype in gtypes: self.b(' %s,' % gtype) self.b(' G_TYPE_INVALID);') def do_interface(self, node): ifaces = node.getElementsByTagName('interface') assert len(ifaces) == 1 iface = ifaces[0] name = node.getAttribute('name').replace('/', '') self.iface = name self.iface_lc = name.lower() self.iface_uc = name.upper() self.iface_mc = name.replace('_', '') self.iface_dbus = iface.getAttribute('name') signals = node.getElementsByTagName('signal') methods = node.getElementsByTagName('method') if signals: self.b('static inline void') self.b('%s_add_signals_for_%s (DBusGProxy *proxy)' % (self.prefix_lc, name.lower())) self.b('{') if self.tp_proxy_api >= (0, 7, 6): self.b(' if (!tp_proxy_dbus_g_proxy_claim_for_signal_adding ' '(proxy))') self.b(' return;') for signal in signals: self.do_signal_add(signal) self.b('}') self.b('') self.b('') for signal in signals: self.do_signal(name, signal) for method in methods: self.do_method(name, method) self.iface_dbus = None def __call__(self): if self.guard is not None: self.h('#ifndef %s' % self.guard) self.h('#define %s' % self.guard) self.h('') self.h('G_BEGIN_DECLS') self.h('') self.b('/* We don\'t want gtkdoc scanning this file, it\'ll get') self.b(' * confused by seeing function definitions, so mark it as: */') self.b('/**/') self.b('') nodes = self.dom.getElementsByTagName('node') nodes.sort(cmp_by_name) for node in nodes: self.do_interface(node) if self.group is not None: self.b('/*') self.b(' * %s_%s_add_signals:' % (self.prefix_lc, self.group)) self.b(' * @self: the #TpProxy') self.b(' * @quark: a quark whose string value is the interface') self.b(' * name whose signals should be added') self.b(' * @proxy: the D-Bus proxy to which to add the signals') self.b(' * @unused: not used for anything') self.b(' *') self.b(' * Tell dbus-glib that @proxy has the signatures of all') self.b(' * signals on the given interface, if it\'s one we') self.b(' * support.') self.b(' *') self.b(' * This function should be used as a signal handler for') self.b(' * #TpProxy::interface-added.') self.b(' */') self.b('static void') self.b('%s_%s_add_signals (TpProxy *self G_GNUC_UNUSED,' % (self.prefix_lc, self.group)) self.b(' guint quark,') self.b(' DBusGProxy *proxy,') self.b(' gpointer unused G_GNUC_UNUSED)') self.b('{') for node in nodes: iface = node.getElementsByTagName('interface')[0] self.iface_dbus = iface.getAttribute('name') signals = node.getElementsByTagName('signal') if not signals: continue name = node.getAttribute('name').replace('/', '').lower() self.iface_uc = name.upper() self.b(' if (quark == %s)' % self.get_iface_quark()) self.b(' %s_add_signals_for_%s (proxy);' % (self.prefix_lc, name)) self.b('}') self.b('') self.h('G_END_DECLS') self.h('') if self.guard is not None: self.h('#endif /* defined (%s) */' % self.guard) self.h('') file_set_contents(self.basename + '.h', '\n'.join(self.__header)) file_set_contents(self.basename + '-body.h', '\n'.join(self.__body)) file_set_contents(self.basename + '-gtk-doc.h', '\n'.join(self.__docs)) def types_to_gtypes(types): return [type_to_gtype(t)[1] for t in types] if __name__ == '__main__': options, argv = gnu_getopt(sys.argv[1:], '', ['group=', 'subclass=', 'subclass-assert=', 'iface-quark-prefix=', 'tp-proxy-api=', 'generate-reentrant=', 'deprecate-reentrant=', 'deprecation-attribute=', 'guard=']) opts = {} for option, value in options: opts[option] = value dom = xml.dom.minidom.parse(argv[0]) Generator(dom, argv[1], argv[2], opts)() telepathy-gabble-0.18.2/tools/git-which-branch.sh0000644000175000017500000000125312200204333021615 0ustar00smcvsmcv00000000000000#!/bin/sh # git-which-branch.sh - output the name of the current git branch # # The canonical location of this program is the telepathy-spec tools/ # directory, please synchronize any changes with that copy. # # Copyright (C) 2008 Collabora Ltd. # # Copying and distribution of this file, with or without modification, # are permitted in any medium without royalty provided the copyright # notice and this notice are preserved. default="$1" if { ref="`git symbolic-ref HEAD 2>/dev/null`"; }; then echo ${ref#refs/heads/} exit 0 fi if test -n "$default"; then echo "$default" >/dev/null exit 0 fi echo "no git branch found" >&2 exit 1 telepathy-gabble-0.18.2/tools/flymake.mk0000644000175000017500000000014412200204333020122 0ustar00smcvsmcv00000000000000check-syntax: $(CC) $(AM_CPPFLAGS) $(AM_CFLAGS) -fsyntax-only $(CHK_SOURCES) .PHONY: check-syntax telepathy-gabble-0.18.2/tools/doc-generator.xsl0000644000175000017500000012056712200204333021436 0ustar00smcvsmcv00000000000000 . ERR: cannot find D-Bus interface, method, signal or property called ' ' ERR: Cannot use tp:member-ref when not in an <interface> ERR: interface has no signal/method/property called

Added in version .

Changed in version :

Changed in version

Deprecated since version .

Errors

Generic types

Simple types

Enumerated types:

Sets of flags:

Structure types

Mapping types

Types defined elsewhere

This interface is and is likely to cause havoc to your API/ABI if bindings are generated. Don't include it in libraries that care about compatibility.

Implementations of this interface must also implement:

Methods:

Interface has no methods.

Signals:

Interface has no signals.

Telepathy Properties:

Accessed using the Telepathy Properties interface.

Interface has no Telepathy properties.

D-Bus core Properties:

Accessed using the org.freedesktop.DBus.Properties interface.

Interface has no D-Bus core properties.

ERR: missing @name on a tp:flags type ERR: missing @type on tp:flags type

=
(Undocumented)
ERR: missing @name on a tp:enum type ERR: missing @type on tp:enum type

=
(Undocumented)
ERR: property does not have an interface as parent ERR: missing @name on a property of ERR: missing @type on property : ' '
, read-only write-only read/write ERR: unknown or missing value for @access on property : ' '

− a{ : }

In bindings that need a separate name, arrays of should be called .

Members

ERR: missing @name on a tp:simple-type ERR: missing @type on tp:simple-type

ERR: missing @name on a tp:external-type ERR: missing @type on tp:external-type
Defined by:
− ( , ) − a{ }

− ( : , )

In bindings that need a separate name, arrays of should be called .

Arrays of don't generally make sense.

Members

ERR: method does not have an interface as parent ERR: missing @name on a method of ERR: an arg of method has no type ERR: an 'in' arg of method has no name ERR: an arg of method has direction neither 'in' nor 'out'

( : , ) → , nothing

Parameters

Returns

Possible errors

( ) a{ } ERR: Unable to find type ' ' ERR: tp:type ' ' has D-Bus type ' ' but has been used with type=' ' ( )
(undocumented)
(generic description) (Undocumented.)
ERR: signal does not have an interface as parent ERR: missing @name on a signal of ERR: an arg of signal has no type ERR: an arg of signal has no name INFO: an arg of signal has unnecessary direction 'in' ERR: an arg of signal has direction other than 'in'

( : , )

Parameters

<xsl:value-of select="tp:title"/> <xsl:if test="tp:version"> <xsl:text> version </xsl:text> <xsl:value-of select="tp:version"/> </xsl:if>

Version

Interfaces

Index

Index of interfaces

Index of types

Stray text: {{{ }}} Unrecognised element: { }
telepathy-gabble-0.18.2/tools/check-whitespace.sh0000644000175000017500000000032312200204333021703 0ustar00smcvsmcv00000000000000#!/bin/sh fail=0 if grep -n ' $' "$@" then echo "^^^ The above files contain unwanted trailing spaces" fail=1 fi if grep -n ' ' "$@" then echo "^^^ The above files contain tabs" fail=1 fi exit $fail telepathy-gabble-0.18.2/tools/check-misc.sh0000644000175000017500000000036012200204333020503 0ustar00smcvsmcv00000000000000#!/bin/sh fail=0 ( . "${tools_dir}"/check-whitespace.sh ) || fail=$? if egrep '(Free\s*Software\s*Foundation.*02139|02111-1307)' "$@" then echo "^^^ The above files contain the FSF's old address in GPL headers" fail=1 fi exit $fail telepathy-gabble-0.18.2/tools/check-c-style.sh0000644000175000017500000000347012200204333021135 0ustar00smcvsmcv00000000000000#!/bin/sh fail=0 ( . "${tools_dir}"/check-misc.sh ) || fail=$? # The first regex finds function calls like foo() (as opposed to foo ()). # It attempts to ignore string constants (may cause false negatives). # The second and third ignore block comments (gtkdoc uses foo() as markup). # The fourth ignores cpp so you can # #define foo(bar) (_real_foo (__FUNC__, bar)) (cpp insists on foo() style). if grep -n '^[^"]*[[:lower:]](' "$@" \ | grep -v '^[-[:alnum:]_./]*:[[:digit:]]*: *\*' \ | grep -v '^[-[:alnum:]_./]*:[[:digit:]]*: */\*' \ | grep -v '^[-[:alnum:]_./]*:[[:digit:]]*: *#' then echo "^^^ Our coding style is to use function calls like foo (), not foo()" fail=1 fi if grep -En '[(][[:alnum:]_]+ ?\*[)][(]?[[:alpha:]_]' "$@"; then echo "^^^ Our coding style is to have a space between a cast and the " echo " thing being cast" fail=1 fi # this only spots casts if grep -En '[(][[:alnum:]_]+\*+[)]' "$@"; then echo "^^^ Our coding style is to have a space before the * of pointer types" echo " (regex 1)" fail=1 fi # ... and this only spots variable declarations and function return types if grep -En '^ *(static |const |)* *[[:alnum:]_]+\*+([[:alnum:]_]|;|$)' \ "$@"; then echo "^^^ Our coding style is to have a space before the * of pointer types" echo " (regex 2)" fail=1 fi if grep -n 'g_hash_table_destroy' "$@"; then echo "^^^ Our coding style is to use g_hash_table_unref" fail=1 fi for p in "" "ptr_" "byte_"; do if grep -En "g_${p}array_free \(([^ ,]+), TRUE\)" "$@"; then echo "^^^ Our coding style is to use g_${p}array_unref in the case " echo " the underlying C array is not used" fail=1 fi done if test -n "$CHECK_FOR_LONG_LINES" then if egrep -n '.{80,}' "$@" then echo "^^^ The above files contain long lines" fail=1 fi fi exit $fail telepathy-gabble-0.18.2/tools/check-coding-style.mk0000644000175000017500000000076412200204333022156 0ustar00smcvsmcv00000000000000check-coding-style: @fail=0; \ if test -n "$(check_misc_sources)"; then \ tools_dir=$(top_srcdir)/tools \ sh $(top_srcdir)/tools/check-misc.sh \ $(addprefix $(srcdir)/,$(check_misc_sources)) || fail=1; \ fi; \ if test -n "$(check_c_sources)"; then \ tools_dir=$(top_srcdir)/tools \ sh $(top_srcdir)/tools/check-c-style.sh \ $(addprefix $(srcdir)/,$(check_c_sources)) || fail=1; \ fi;\ if test yes = "$(ENABLE_CODING_STYLE_CHECKS)"; then \ exit "$$fail";\ else \ exit 0;\ fi telepathy-gabble-0.18.2/tools/c-constants-gen.py0000644000175000017500000001316112200473406021532 0ustar00smcvsmcv00000000000000#!/usr/bin/python from sys import argv, stdout, stderr import xml.dom.minidom from libtpcodegen import file_set_contents from libglibcodegen import NS_TP, get_docstring, \ get_descendant_text, get_by_path class Generator(object): def __init__(self, prefix, dom, output_base): self.prefix = prefix + '_' self.spec = get_by_path(dom, "spec")[0] self.output_base = output_base self.__header = [] self.__docs = [] def __call__(self): self.do_header() self.do_body() self.do_footer() file_set_contents(self.output_base + '.h', ''.join(self.__header)) file_set_contents(self.output_base + '-gtk-doc.h', ''.join(self.__docs)) def write(self, code): self.__header.append(code.encode('utf-8')) def d(self, code): self.__docs.append(code.encode('utf-8')) # Header def do_header(self): self.write('/* Generated from ') self.write(get_descendant_text(get_by_path(self.spec, 'title'))) version = get_by_path(self.spec, "version") if version: self.write(', version ' + get_descendant_text(version)) self.write('\n\n') for copyright in get_by_path(self.spec, 'copyright'): self.write(get_descendant_text(copyright)) self.write('\n') self.write(get_descendant_text(get_by_path(self.spec, 'license'))) self.write('\n') self.write(get_descendant_text(get_by_path(self.spec, 'docstring'))) self.write(""" */ #ifdef __cplusplus extern "C" { #endif \n""") # Body def do_body(self): for elem in self.spec.getElementsByTagNameNS(NS_TP, '*'): if elem.localName == 'flags': self.do_flags(elem) elif elem.localName == 'enum': self.do_enum(elem) def do_flags(self, flags): name = flags.getAttribute('plural') or flags.getAttribute('name') value_prefix = flags.getAttribute('singular') or \ flags.getAttribute('value-prefix') or \ flags.getAttribute('name') self.d("""\ /** * %s: """ % (self.prefix + name).replace('_', '')) for flag in get_by_path(flags, 'flag'): self.do_gtkdoc(flag, value_prefix) self.d(' *\n') docstrings = get_by_path(flags, 'docstring') if docstrings: self.d("""\ * * """ % get_descendant_text(docstrings).replace('\n', ' ')) self.d("""\ * Bitfield/set of flags generated from the Telepathy specification. */ """) self.write("typedef enum /*< flags >*/ {\n") for flag in get_by_path(flags, 'flag'): self.do_val(flag, value_prefix) self.write("""\ } %s; """ % (self.prefix + name).replace('_', '')) def do_enum(self, enum): name = enum.getAttribute('singular') or enum.getAttribute('name') value_prefix = enum.getAttribute('singular') or \ enum.getAttribute('value-prefix') or \ enum.getAttribute('name') name_plural = enum.getAttribute('plural') or \ enum.getAttribute('name') + 's' self.d("""\ /** * %s: """ % (self.prefix + name).replace('_', '')) vals = get_by_path(enum, 'enumvalue') for val in vals: self.do_gtkdoc(val, value_prefix) self.d(' *\n') docstrings = get_by_path(enum, 'docstring') if docstrings: self.d("""\ * * """ % get_descendant_text(docstrings).replace('\n', ' ')) self.d("""\ * Bitfield/set of flags generated from the Telepathy specification. */ """) self.write("typedef enum {\n") for val in vals: self.do_val(val, value_prefix) self.write("} %s;\n" % (self.prefix + name).replace('_', '')) self.d("""\ /** * %(upper-prefix)sNUM_%(upper-plural)s: * * 1 higher than the highest valid value of #%(mixed-name)s. */ /** * NUM_%(upper-prefix)s%(upper-plural)s: (skip) * * 1 higher than the highest valid value of #%(mixed-name)s. * In new code, use %(upper-prefix)sNUM_%(upper-plural)s instead. */ """ % {'mixed-name' : (self.prefix + name).replace('_', ''), 'upper-prefix' : self.prefix.upper(), 'upper-plural' : name_plural.upper(), 'last-val' : vals[-1].getAttribute('value')}) self.write("""\ #define %(upper-prefix)sNUM_%(upper-plural)s (%(last-val)s+1) #define NUM_%(upper-prefix)s%(upper-plural)s %(upper-prefix)sNUM_%(upper-plural)s """ % {'mixed-name' : (self.prefix + name).replace('_', ''), 'upper-prefix' : self.prefix.upper(), 'upper-plural' : name_plural.upper(), 'last-val' : vals[-1].getAttribute('value')}) def do_val(self, val, value_prefix): name = val.getAttribute('name') suffix = val.getAttribute('suffix') use_name = (self.prefix + value_prefix + '_' + \ (suffix or name)).upper() assert not (name and suffix) or name == suffix, \ 'Flag/enumvalue name %s != suffix %s' % (name, suffix) self.write(' %s = %s,\n' % (use_name, val.getAttribute('value'))) def do_gtkdoc(self, node, value_prefix): self.d(' * @') self.d((self.prefix + value_prefix + '_' + node.getAttribute('suffix')).upper()) self.d(': \n') # Footer def do_footer(self): self.write(""" #ifdef __cplusplus } #endif """) if __name__ == '__main__': argv = argv[1:] Generator(argv[0], xml.dom.minidom.parse(argv[1]), argv[2])() telepathy-gabble-0.18.2/tools/Makefile.am0000644000175000017500000000436712200204333020210 0ustar00smcvsmcv00000000000000abs_top_builddir = @abs_top_builddir@ noinst_SCRIPTS = telepathy-glib-env telepathy-glib-env: telepathy-glib-env.in Makefile sed -e 's![@]abs_top_builddir[@]!$(abs_top_builddir)!' $< > $@ chmod +x $@ if ENABLE_INSTALLED_TESTS toolsdir = @tpglibtestsdir@/tools tools_SCRIPTS = \ with-session-bus.sh \ test-wrapper.sh \ libglibcodegen.py \ libtpcodegen.py \ $(NULL) endif EXTRA_DIST = \ c-constants-gen.py \ check-coding-style.mk \ check-c-style.sh \ check-misc.sh \ check-whitespace.sh \ doc-generator.xsl \ flymake.mk \ git-which-branch.sh \ glib-client-gen.py \ glib-client-marshaller-gen.py \ glib-errors-check-gen.py \ glib-errors-str-gen.py \ glib-ginterface-gen.py \ glib-gtypes-generator.py \ glib-interfaces-gen.py \ gobject-foo.py \ lcov.am \ libtpcodegen.py \ libglibcodegen.py \ make-release-mail.py \ make-version-script.py \ manager-file.py \ shave.mk \ telepathy.am \ telepathy-glib.supp \ telepathy-glib-env.in \ test-wrapper.sh \ with-session-bus.sh \ xincludator.py CLEANFILES = libtpcodegen.pyc libtpcodegen.pyo libglibcodegen.pyc libglibcodegen.pyo $(noinst_SCRIPTS) all: $(EXTRA_DIST) libglibcodegen.py: libtpcodegen.py $(AM_V_GEN)test -e ${srcdir}/$@ && touch ${srcdir}/$@ c-constants-gen.py: libglibcodegen.py $(AM_V_GEN)test -e ${srcdir}/$@ && touch ${srcdir}/$@ glib-client-marshaller-gen.py: libglibcodegen.py $(AM_V_GEN)test -e ${srcdir}/$@ && touch ${srcdir}/$@ glib-errors-enum-body-gen.py: libglibcodegen.py $(AM_V_GEN)test -e ${srcdir}/$@ && touch ${srcdir}/$@ glib-errors-enum-header-gen.py: libglibcodegen.py $(AM_V_GEN)test -e ${srcdir}/$@ && touch ${srcdir}/$@ glib-ginterface-gen.py: libglibcodegen.py $(AM_V_GEN)test -e ${srcdir}/$@ && touch ${srcdir}/$@ glib-gtypes-generator.py: libglibcodegen.py $(AM_V_GEN)test -e ${srcdir}/$@ && touch ${srcdir}/$@ glib-interfaces-gen.py: libglibcodegen.py $(AM_V_GEN)test -e ${srcdir}/$@ && touch ${srcdir}/$@ TELEPATHY_GLIB_SRCDIR = $(top_srcdir)/../telepathy-glib maintainer-update-from-telepathy-glib: set -e && cd $(srcdir) && \ for x in $(EXTRA_DIST); do \ if test -f $(TELEPATHY_GLIB_SRCDIR)/tools/$$x; then \ cp $(TELEPATHY_GLIB_SRCDIR)/tools/$$x $$x; \ fi; \ done telepathy-gabble-0.18.2/tools/Makefile.in0000644000175000017500000004357312312536074020241 0ustar00smcvsmcv00000000000000# Makefile.in generated by automake 1.14.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2013 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ VPATH = @srcdir@ am__is_gnu_make = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)' am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ subdir = tools DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/ax_config_dir.m4 \ $(top_srcdir)/m4/compiler.m4 $(top_srcdir)/m4/libtool.m4 \ $(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \ $(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \ $(top_srcdir)/m4/tp-compiler-flag.m4 \ $(top_srcdir)/m4/tp-compiler-warnings.m4 \ $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; am__vpath_adj = case $$p in \ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ *) f=$$p;; \ esac; am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; am__install_max = 40 am__nobase_strip_setup = \ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` am__nobase_strip = \ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" am__nobase_list = $(am__nobase_strip_setup); \ for p in $$list; do echo "$$p $$p"; done | \ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ if (++n[$$2] == $(am__install_max)) \ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ END { for (dir in files) print dir, files[dir] }' am__base_list = \ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' am__uninstall_files_from_dir = { \ test -z "$$files" \ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ $(am__cd) "$$dir" && rm -f $$files; }; \ } am__installdirs = "$(DESTDIR)$(toolsdir)" SCRIPTS = $(noinst_SCRIPTS) $(tools_SCRIPTS) AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = SOURCES = DIST_SOURCES = am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ AR = @AR@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CFLAGS = @CFLAGS@ CLIENT_TYPE = @CLIENT_TYPE@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CYGPATH_W = @CYGPATH_W@ DBUS_CFLAGS = @DBUS_CFLAGS@ DBUS_LIBS = @DBUS_LIBS@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DLLTOOL = @DLLTOOL@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ ENABLE_CODING_STYLE_CHECKS = @ENABLE_CODING_STYLE_CHECKS@ ENABLE_PLUGINS = @ENABLE_PLUGINS@ ERROR_CFLAGS = @ERROR_CFLAGS@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ GLIB_CFLAGS = @GLIB_CFLAGS@ GLIB_GENMARSHAL = @GLIB_GENMARSHAL@ GLIB_LIBS = @GLIB_LIBS@ GMODULE_CFLAGS = @GMODULE_CFLAGS@ GMODULE_LIBS = @GMODULE_LIBS@ GREP = @GREP@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ LD = @LD@ LDFLAGS = @LDFLAGS@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBTOOL = @LIBTOOL@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBOBJS = @LTLIBOBJS@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ NICE_CFLAGS = @NICE_CFLAGS@ NICE_LIBS = @NICE_LIBS@ NM = @NM@ NMEDIT = @NMEDIT@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ PYTHON = @PYTHON@ PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@ PYTHON_PLATFORM = @PYTHON_PLATFORM@ PYTHON_PREFIX = @PYTHON_PREFIX@ PYTHON_VERSION = @PYTHON_VERSION@ RANLIB = @RANLIB@ SED = @SED@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ SOUP_CFLAGS = @SOUP_CFLAGS@ SOUP_LIBS = @SOUP_LIBS@ STRIP = @STRIP@ TEST_PYTHON = @TEST_PYTHON@ TP_GLIB_CFLAGS = @TP_GLIB_CFLAGS@ TP_GLIB_LIBS = @TP_GLIB_LIBS@ VERSION = @VERSION@ WOCKY_CFLAGS = @WOCKY_CFLAGS@ WOCKY_LIBS = @WOCKY_LIBS@ XSLTPROC = @XSLTPROC@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ gabbletestsdir = @gabbletestsdir@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ mandir = @mandir@ mkdir_p = @mkdir_p@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ pkgpyexecdir = @pkgpyexecdir@ pkgpythondir = @pkgpythondir@ pluginexecdir = @pluginexecdir@ pluginexeclibdir = @pluginexeclibdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ pyexecdir = @pyexecdir@ pythondir = @pythondir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ srcdir = @srcdir@ sysconfdir = @sysconfdir@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ noinst_SCRIPTS = telepathy-glib-env @ENABLE_INSTALLED_TESTS_TRUE@toolsdir = @tpglibtestsdir@/tools @ENABLE_INSTALLED_TESTS_TRUE@tools_SCRIPTS = \ @ENABLE_INSTALLED_TESTS_TRUE@ with-session-bus.sh \ @ENABLE_INSTALLED_TESTS_TRUE@ test-wrapper.sh \ @ENABLE_INSTALLED_TESTS_TRUE@ libglibcodegen.py \ @ENABLE_INSTALLED_TESTS_TRUE@ libtpcodegen.py \ @ENABLE_INSTALLED_TESTS_TRUE@ $(NULL) EXTRA_DIST = \ c-constants-gen.py \ check-coding-style.mk \ check-c-style.sh \ check-misc.sh \ check-whitespace.sh \ doc-generator.xsl \ flymake.mk \ git-which-branch.sh \ glib-client-gen.py \ glib-client-marshaller-gen.py \ glib-errors-check-gen.py \ glib-errors-str-gen.py \ glib-ginterface-gen.py \ glib-gtypes-generator.py \ glib-interfaces-gen.py \ gobject-foo.py \ lcov.am \ libtpcodegen.py \ libglibcodegen.py \ make-release-mail.py \ make-version-script.py \ manager-file.py \ shave.mk \ telepathy.am \ telepathy-glib.supp \ telepathy-glib-env.in \ test-wrapper.sh \ with-session-bus.sh \ xincludator.py CLEANFILES = libtpcodegen.pyc libtpcodegen.pyo libglibcodegen.pyc libglibcodegen.pyo $(noinst_SCRIPTS) TELEPATHY_GLIB_SRCDIR = $(top_srcdir)/../telepathy-glib all: all-am .SUFFIXES: $(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu tools/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --gnu tools/Makefile .PRECIOUS: Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): install-toolsSCRIPTS: $(tools_SCRIPTS) @$(NORMAL_INSTALL) @list='$(tools_SCRIPTS)'; test -n "$(toolsdir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(toolsdir)'"; \ $(MKDIR_P) "$(DESTDIR)$(toolsdir)" || exit 1; \ fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ if test -f "$$d$$p"; then echo "$$d$$p"; echo "$$p"; else :; fi; \ done | \ sed -e 'p;s,.*/,,;n' \ -e 'h;s|.*|.|' \ -e 'p;x;s,.*/,,;$(transform)' | sed 'N;N;N;s,\n, ,g' | \ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1; } \ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \ if ($$2 == $$4) { files[d] = files[d] " " $$1; \ if (++n[d] == $(am__install_max)) { \ print "f", d, files[d]; n[d] = 0; files[d] = "" } } \ else { print "f", d "/" $$4, $$1 } } \ END { for (d in files) print "f", d, files[d] }' | \ while read type dir files; do \ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \ test -z "$$files" || { \ echo " $(INSTALL_SCRIPT) $$files '$(DESTDIR)$(toolsdir)$$dir'"; \ $(INSTALL_SCRIPT) $$files "$(DESTDIR)$(toolsdir)$$dir" || exit $$?; \ } \ ; done uninstall-toolsSCRIPTS: @$(NORMAL_UNINSTALL) @list='$(tools_SCRIPTS)'; test -n "$(toolsdir)" || exit 0; \ files=`for p in $$list; do echo "$$p"; done | \ sed -e 's,.*/,,;$(transform)'`; \ dir='$(DESTDIR)$(toolsdir)'; $(am__uninstall_files_from_dir) mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs tags TAGS: ctags CTAGS: cscope cscopelist: distdir: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done check-am: all-am check: check-am all-am: Makefile $(SCRIPTS) installdirs: for dir in "$(DESTDIR)$(toolsdir)"; do \ test -z "$$dir" || $(MKDIR_P) "$$dir"; \ done install: install-am install-exec: install-exec-am install-data: install-data-am uninstall: uninstall-am install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-am install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: clean-generic: -test -z "$(CLEANFILES)" || rm -f $(CLEANFILES) distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." clean: clean-am clean-am: clean-generic clean-libtool mostlyclean-am distclean: distclean-am -rm -f Makefile distclean-am: clean-am distclean-generic dvi: dvi-am dvi-am: html: html-am html-am: info: info-am info-am: install-data-am: install-toolsSCRIPTS install-dvi: install-dvi-am install-dvi-am: install-exec-am: install-html: install-html-am install-html-am: install-info: install-info-am install-info-am: install-man: install-pdf: install-pdf-am install-pdf-am: install-ps: install-ps-am install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-am -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-am mostlyclean-am: mostlyclean-generic mostlyclean-libtool pdf: pdf-am pdf-am: ps: ps-am ps-am: uninstall-am: uninstall-toolsSCRIPTS .MAKE: install-am install-strip .PHONY: all all-am check check-am clean clean-generic clean-libtool \ cscopelist-am ctags-am distclean distclean-generic \ distclean-libtool distdir dvi dvi-am html html-am info info-am \ install install-am install-data install-data-am install-dvi \ install-dvi-am install-exec install-exec-am install-html \ install-html-am install-info install-info-am install-man \ install-pdf install-pdf-am install-ps install-ps-am \ install-strip install-toolsSCRIPTS installcheck \ installcheck-am installdirs maintainer-clean \ maintainer-clean-generic mostlyclean mostlyclean-generic \ mostlyclean-libtool pdf pdf-am ps ps-am tags-am uninstall \ uninstall-am uninstall-toolsSCRIPTS telepathy-glib-env: telepathy-glib-env.in Makefile sed -e 's![@]abs_top_builddir[@]!$(abs_top_builddir)!' $< > $@ chmod +x $@ all: $(EXTRA_DIST) libglibcodegen.py: libtpcodegen.py $(AM_V_GEN)test -e ${srcdir}/$@ && touch ${srcdir}/$@ c-constants-gen.py: libglibcodegen.py $(AM_V_GEN)test -e ${srcdir}/$@ && touch ${srcdir}/$@ glib-client-marshaller-gen.py: libglibcodegen.py $(AM_V_GEN)test -e ${srcdir}/$@ && touch ${srcdir}/$@ glib-errors-enum-body-gen.py: libglibcodegen.py $(AM_V_GEN)test -e ${srcdir}/$@ && touch ${srcdir}/$@ glib-errors-enum-header-gen.py: libglibcodegen.py $(AM_V_GEN)test -e ${srcdir}/$@ && touch ${srcdir}/$@ glib-ginterface-gen.py: libglibcodegen.py $(AM_V_GEN)test -e ${srcdir}/$@ && touch ${srcdir}/$@ glib-gtypes-generator.py: libglibcodegen.py $(AM_V_GEN)test -e ${srcdir}/$@ && touch ${srcdir}/$@ glib-interfaces-gen.py: libglibcodegen.py $(AM_V_GEN)test -e ${srcdir}/$@ && touch ${srcdir}/$@ maintainer-update-from-telepathy-glib: set -e && cd $(srcdir) && \ for x in $(EXTRA_DIST); do \ if test -f $(TELEPATHY_GLIB_SRCDIR)/tools/$$x; then \ cp $(TELEPATHY_GLIB_SRCDIR)/tools/$$x $$x; \ fi; \ done # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: telepathy-gabble-0.18.2/tools/telepathy.am0000644000175000017500000000472212200204333020465 0ustar00smcvsmcv00000000000000## Useful top-level Makefile.am snippets for Telepathy projects. dist-hook: chmod u+w ${distdir}/ChangeLog if test -d ${top_srcdir}/.git; then \ ( cd ${top_srcdir} && git log --date=iso $(CHANGELOG_RANGE) ) > ${distdir}/ChangeLog; \ fi distcheck-hook: @test "z$(CHECK_FOR_UNRELEASED)" = z || \ case @VERSION@ in \ *.*.*.*|*+) ;; \ *) \ if grep -r UNRELEASED $(CHECK_FOR_UNRELEASED); \ then \ echo "^^^ This is meant to be a release, but some files say UNRELEASED" >&2; \ exit 2; \ fi \ ;; \ esac _is-release-check: @case @VERSION@ in \ (*.*.*.*|*+) \ echo "Hey! @VERSION@ is not a release!" >&2; \ exit 2; \ ;; \ esac @cd ${top_srcdir} && \ if ! git diff --no-ext-diff --quiet --exit-code; then \ echo "Hey! Your tree is dirty! No release for you." >&2; \ exit 2; \ fi @cd ${top_srcdir} && \ if ! git diff --cached --no-ext-diff --quiet --exit-code; then \ echo "Hey! You have changes staged! No release for you." >&2; \ exit 2; \ fi if ENABLE_GTK_DOC else @echo "Hey! You need to pass --enable-gtk-doc to configure!" @exit 2; endif %.tar.gz.asc: %.tar.gz $(AM_V_GEN)gpg --detach-sign --armor $@ @PACKAGE@-@VERSION@.tar.gz: $(MAKE) _is-release-check $(MAKE) check $(MAKE) distcheck maintainer-prepare-release: $(MAKE) _is-release-check $(MAKE) all $(MAKE) distcheck $(MAKE) release-mail git tag -s @PACKAGE@-@VERSION@ -m @PACKAGE@' '@VERSION@ gpg --detach-sign --armor @PACKAGE@-@VERSION@.tar.gz release-mail: NEWS $(AM_V_GEN)(python $(top_srcdir)/tools/make-release-mail.py \ @PACKAGE@ @VERSION@ $(top_srcdir)/NEWS > $@.tmp && \ mv $@.tmp $@) maintainer-upload-release: _maintainer-upload-release _maintainer-upload-release-check: _is-release-check test -f @PACKAGE@-@VERSION@.tar.gz test -f @PACKAGE@-@VERSION@.tar.gz.asc gpg --verify @PACKAGE@-@VERSION@.tar.gz.asc _maintainer-upload-release: _maintainer-upload-release-check rsync -vzP @PACKAGE@-@VERSION@.tar.gz telepathy.freedesktop.org:/srv/telepathy.freedesktop.org/www/releases/@PACKAGE@/@PACKAGE@-@VERSION@.tar.gz rsync -vzP @PACKAGE@-@VERSION@.tar.gz.asc telepathy.freedesktop.org:/srv/telepathy.freedesktop.org/www/releases/@PACKAGE@/@PACKAGE@-@VERSION@.tar.gz.asc maintainer-make-release: $(MAKE) maintainer-prepare-release $(MAKE) maintainer-upload-release @echo "Now:" @echo " • bump the nano-version;" @echo " • push the branch and tags upstream; and" @echo " • send release-mail to ." ## vim:set ft=automake: telepathy-gabble-0.18.2/tools/lcov.am0000644000175000017500000000150312227000321017423 0ustar00smcvsmcv00000000000000lcov-reset: lcov --directory @top_srcdir@ --zerocounters lcov-report: lcov --directory @top_srcdir@ --capture \ --output-file @top_builddir@/lcov.info.tmp lcov --directory @top_srcdir@ --output-file @top_builddir@/lcov.info \ --remove @top_builddir@/lcov.info.tmp telepathy-glib-scan.c rm @top_builddir@/lcov.info.tmp $(mkdir_p) @top_builddir@/lcov.html echo "Coming soon!" > @top_builddir@/lcov.html/index.html git_commit=`GIT_DIR=@top_srcdir@/.git git log -1 --pretty=format:%h 2>/dev/null`;\ genhtml --title "@PACKAGE_STRING@ $$git_commit" \ --output-directory @top_builddir@/lcov.html lcov.info @echo @echo 'lcov report can be found in:' @echo 'file://@abs_top_builddir@/lcov.html/index.html' @echo lcov-check: $(MAKE) lcov-reset $(MAKE) check $(LCOV_CHECK_ARGS) $(MAKE) lcov-report ## vim:set ft=automake: telepathy-gabble-0.18.2/m4/0000755000175000017500000000000012312537051015334 5ustar00smcvsmcv00000000000000telepathy-gabble-0.18.2/m4/Makefile.am0000644000175000017500000000015612200204333017360 0ustar00smcvsmcv00000000000000EXTRA_DIST = \ tp-compiler-flag.m4 \ tp-compiler-warnings.m4 \ compiler.m4 \ ax_config_dir.m4 telepathy-gabble-0.18.2/m4/Makefile.in0000644000175000017500000003130612312536074017410 0ustar00smcvsmcv00000000000000# Makefile.in generated by automake 1.14.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2013 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ VPATH = @srcdir@ am__is_gnu_make = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)' am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ subdir = m4 DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/ax_config_dir.m4 \ $(top_srcdir)/m4/compiler.m4 $(top_srcdir)/m4/libtool.m4 \ $(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \ $(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \ $(top_srcdir)/m4/tp-compiler-flag.m4 \ $(top_srcdir)/m4/tp-compiler-warnings.m4 \ $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = SOURCES = DIST_SOURCES = am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ AR = @AR@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CFLAGS = @CFLAGS@ CLIENT_TYPE = @CLIENT_TYPE@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CYGPATH_W = @CYGPATH_W@ DBUS_CFLAGS = @DBUS_CFLAGS@ DBUS_LIBS = @DBUS_LIBS@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DLLTOOL = @DLLTOOL@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ ENABLE_CODING_STYLE_CHECKS = @ENABLE_CODING_STYLE_CHECKS@ ENABLE_PLUGINS = @ENABLE_PLUGINS@ ERROR_CFLAGS = @ERROR_CFLAGS@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ GLIB_CFLAGS = @GLIB_CFLAGS@ GLIB_GENMARSHAL = @GLIB_GENMARSHAL@ GLIB_LIBS = @GLIB_LIBS@ GMODULE_CFLAGS = @GMODULE_CFLAGS@ GMODULE_LIBS = @GMODULE_LIBS@ GREP = @GREP@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ LD = @LD@ LDFLAGS = @LDFLAGS@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBTOOL = @LIBTOOL@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBOBJS = @LTLIBOBJS@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ NICE_CFLAGS = @NICE_CFLAGS@ NICE_LIBS = @NICE_LIBS@ NM = @NM@ NMEDIT = @NMEDIT@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ PYTHON = @PYTHON@ PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@ PYTHON_PLATFORM = @PYTHON_PLATFORM@ PYTHON_PREFIX = @PYTHON_PREFIX@ PYTHON_VERSION = @PYTHON_VERSION@ RANLIB = @RANLIB@ SED = @SED@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ SOUP_CFLAGS = @SOUP_CFLAGS@ SOUP_LIBS = @SOUP_LIBS@ STRIP = @STRIP@ TEST_PYTHON = @TEST_PYTHON@ TP_GLIB_CFLAGS = @TP_GLIB_CFLAGS@ TP_GLIB_LIBS = @TP_GLIB_LIBS@ VERSION = @VERSION@ WOCKY_CFLAGS = @WOCKY_CFLAGS@ WOCKY_LIBS = @WOCKY_LIBS@ XSLTPROC = @XSLTPROC@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ gabbletestsdir = @gabbletestsdir@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ mandir = @mandir@ mkdir_p = @mkdir_p@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ pkgpyexecdir = @pkgpyexecdir@ pkgpythondir = @pkgpythondir@ pluginexecdir = @pluginexecdir@ pluginexeclibdir = @pluginexeclibdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ pyexecdir = @pyexecdir@ pythondir = @pythondir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ srcdir = @srcdir@ sysconfdir = @sysconfdir@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ EXTRA_DIST = \ tp-compiler-flag.m4 \ tp-compiler-warnings.m4 \ compiler.m4 \ ax_config_dir.m4 all: all-am .SUFFIXES: $(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu m4/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --gnu m4/Makefile .PRECIOUS: Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs tags TAGS: ctags CTAGS: cscope cscopelist: distdir: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done check-am: all-am check: check-am all-am: Makefile installdirs: install: install-am install-exec: install-exec-am install-data: install-data-am uninstall: uninstall-am install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-am install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." clean: clean-am clean-am: clean-generic clean-libtool mostlyclean-am distclean: distclean-am -rm -f Makefile distclean-am: clean-am distclean-generic dvi: dvi-am dvi-am: html: html-am html-am: info: info-am info-am: install-data-am: install-dvi: install-dvi-am install-dvi-am: install-exec-am: install-html: install-html-am install-html-am: install-info: install-info-am install-info-am: install-man: install-pdf: install-pdf-am install-pdf-am: install-ps: install-ps-am install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-am -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-am mostlyclean-am: mostlyclean-generic mostlyclean-libtool pdf: pdf-am pdf-am: ps: ps-am ps-am: uninstall-am: .MAKE: install-am install-strip .PHONY: all all-am check check-am clean clean-generic clean-libtool \ cscopelist-am ctags-am distclean distclean-generic \ distclean-libtool distdir dvi dvi-am html html-am info info-am \ install install-am install-data install-data-am install-dvi \ install-dvi-am install-exec install-exec-am install-html \ install-html-am install-info install-info-am install-man \ install-pdf install-pdf-am install-ps install-ps-am \ install-strip installcheck installcheck-am installdirs \ maintainer-clean maintainer-clean-generic mostlyclean \ mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ tags-am uninstall uninstall-am # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: telepathy-gabble-0.18.2/m4/tp-compiler-warnings.m40000644000175000017500000000240112200204333021642 0ustar00smcvsmcv00000000000000dnl TP_COMPILER_WARNINGS(VARIABLE, WERROR_BY_DEFAULT, DESIRABLE, UNDESIRABLE) dnl $1 (VARIABLE): the variable to put flags into dnl $2 (WERROR_BY_DEFAULT): a command returning true if -Werror should be the dnl default dnl $3 (DESIRABLE): warning flags we want (e.g. all extra shadow) dnl $4 (UNDESIRABLE): warning flags we don't want (e.g. dnl missing-field-initializers unused-parameter) AC_DEFUN([TP_COMPILER_WARNINGS], [ AC_REQUIRE([AC_ARG_ENABLE])dnl AC_REQUIRE([AC_HELP_STRING])dnl AC_REQUIRE([TP_COMPILER_FLAG])dnl tp_warnings="" for tp_flag in $3; do TP_COMPILER_FLAG([-W$tp_flag], [tp_warnings="$tp_warnings -W$tp_flag"]) done tp_error_flags="-Werror" TP_COMPILER_FLAG([-Werror], [tp_werror=yes], [tp_werror=no]) for tp_flag in $4; do TP_COMPILER_FLAG([-Wno-$tp_flag], [tp_warnings="$tp_warnings -Wno-$tp_flag"]) TP_COMPILER_FLAG([-Wno-error=$tp_flag], [tp_error_flags="$tp_error_flags -Wno-error=$tp_flag"], [tp_werror=no]) done AC_ARG_ENABLE([Werror], AC_HELP_STRING([--disable-Werror], [compile without -Werror (normally enabled in development builds)]), tp_werror=$enableval, :) if test "x$tp_werror" = xyes && $2; then $1="$tp_warnings $tp_error_flags" else $1="$tp_warnings" fi ]) telepathy-gabble-0.18.2/m4/tp-compiler-flag.m40000644000175000017500000000170412200204333020730 0ustar00smcvsmcv00000000000000dnl A version of AS_COMPILER_FLAG that supports both C and C++. dnl Based on: dnl as-compiler-flag.m4 0.1.0 dnl autostars m4 macro for detection of compiler flags dnl David Schleef dnl $Id: as-compiler-flag.m4,v 1.1 2005/06/18 18:02:46 burgerman Exp $ dnl TP_COMPILER_FLAG(CFLAGS, ACTION-IF-ACCEPTED, [ACTION-IF-NOT-ACCEPTED]) dnl Tries to compile with the given CFLAGS and CXXFLAGS. dnl dnl Runs ACTION-IF-ACCEPTED if the compiler for the currently selected dnl AC_LANG can compile with the flags, and ACTION-IF-NOT-ACCEPTED otherwise. AC_DEFUN([TP_COMPILER_FLAG], [ AC_MSG_CHECKING([to see if compiler understands $1]) save_CFLAGS="$CFLAGS" save_CXXFLAGS="$CXXFLAGS" CFLAGS="$CFLAGS $1" CXXFLAGS="$CXXFLAGS $1" AC_TRY_COMPILE([ ], [], [flag_ok=yes], [flag_ok=no]) CFLAGS="$save_CFLAGS" CXXFLAGS="$save_CXXFLAGS" if test "X$flag_ok" = Xyes ; then $2 true else $3 true fi AC_MSG_RESULT([$flag_ok]) ]) telepathy-gabble-0.18.2/m4/lt~obsolete.m40000644000175000017500000001375612312536063020166 0ustar00smcvsmcv00000000000000# lt~obsolete.m4 -- aclocal satisfying obsolete definitions. -*-Autoconf-*- # # Copyright (C) 2004, 2005, 2007, 2009 Free Software Foundation, Inc. # Written by Scott James Remnant, 2004. # # This file is free software; the Free Software Foundation gives # unlimited permission to copy and/or distribute it, with or without # modifications, as long as this notice is preserved. # serial 5 lt~obsolete.m4 # These exist entirely to fool aclocal when bootstrapping libtool. # # In the past libtool.m4 has provided macros via AC_DEFUN (or AU_DEFUN) # which have later been changed to m4_define as they aren't part of the # exported API, or moved to Autoconf or Automake where they belong. # # The trouble is, aclocal is a bit thick. It'll see the old AC_DEFUN # in /usr/share/aclocal/libtool.m4 and remember it, then when it sees us # using a macro with the same name in our local m4/libtool.m4 it'll # pull the old libtool.m4 in (it doesn't see our shiny new m4_define # and doesn't know about Autoconf macros at all.) # # So we provide this file, which has a silly filename so it's always # included after everything else. This provides aclocal with the # AC_DEFUNs it wants, but when m4 processes it, it doesn't do anything # because those macros already exist, or will be overwritten later. # We use AC_DEFUN over AU_DEFUN for compatibility with aclocal-1.6. # # Anytime we withdraw an AC_DEFUN or AU_DEFUN, remember to add it here. # Yes, that means every name once taken will need to remain here until # we give up compatibility with versions before 1.7, at which point # we need to keep only those names which we still refer to. # This is to help aclocal find these macros, as it can't see m4_define. AC_DEFUN([LTOBSOLETE_VERSION], [m4_if([1])]) m4_ifndef([AC_LIBTOOL_LINKER_OPTION], [AC_DEFUN([AC_LIBTOOL_LINKER_OPTION])]) m4_ifndef([AC_PROG_EGREP], [AC_DEFUN([AC_PROG_EGREP])]) m4_ifndef([_LT_AC_PROG_ECHO_BACKSLASH], [AC_DEFUN([_LT_AC_PROG_ECHO_BACKSLASH])]) m4_ifndef([_LT_AC_SHELL_INIT], [AC_DEFUN([_LT_AC_SHELL_INIT])]) m4_ifndef([_LT_AC_SYS_LIBPATH_AIX], [AC_DEFUN([_LT_AC_SYS_LIBPATH_AIX])]) m4_ifndef([_LT_PROG_LTMAIN], [AC_DEFUN([_LT_PROG_LTMAIN])]) m4_ifndef([_LT_AC_TAGVAR], [AC_DEFUN([_LT_AC_TAGVAR])]) m4_ifndef([AC_LTDL_ENABLE_INSTALL], [AC_DEFUN([AC_LTDL_ENABLE_INSTALL])]) m4_ifndef([AC_LTDL_PREOPEN], [AC_DEFUN([AC_LTDL_PREOPEN])]) m4_ifndef([_LT_AC_SYS_COMPILER], [AC_DEFUN([_LT_AC_SYS_COMPILER])]) m4_ifndef([_LT_AC_LOCK], [AC_DEFUN([_LT_AC_LOCK])]) m4_ifndef([AC_LIBTOOL_SYS_OLD_ARCHIVE], [AC_DEFUN([AC_LIBTOOL_SYS_OLD_ARCHIVE])]) m4_ifndef([_LT_AC_TRY_DLOPEN_SELF], [AC_DEFUN([_LT_AC_TRY_DLOPEN_SELF])]) m4_ifndef([AC_LIBTOOL_PROG_CC_C_O], [AC_DEFUN([AC_LIBTOOL_PROG_CC_C_O])]) m4_ifndef([AC_LIBTOOL_SYS_HARD_LINK_LOCKS], [AC_DEFUN([AC_LIBTOOL_SYS_HARD_LINK_LOCKS])]) m4_ifndef([AC_LIBTOOL_OBJDIR], [AC_DEFUN([AC_LIBTOOL_OBJDIR])]) m4_ifndef([AC_LTDL_OBJDIR], [AC_DEFUN([AC_LTDL_OBJDIR])]) m4_ifndef([AC_LIBTOOL_PROG_LD_HARDCODE_LIBPATH], [AC_DEFUN([AC_LIBTOOL_PROG_LD_HARDCODE_LIBPATH])]) m4_ifndef([AC_LIBTOOL_SYS_LIB_STRIP], [AC_DEFUN([AC_LIBTOOL_SYS_LIB_STRIP])]) m4_ifndef([AC_PATH_MAGIC], [AC_DEFUN([AC_PATH_MAGIC])]) m4_ifndef([AC_PROG_LD_GNU], [AC_DEFUN([AC_PROG_LD_GNU])]) m4_ifndef([AC_PROG_LD_RELOAD_FLAG], [AC_DEFUN([AC_PROG_LD_RELOAD_FLAG])]) m4_ifndef([AC_DEPLIBS_CHECK_METHOD], [AC_DEFUN([AC_DEPLIBS_CHECK_METHOD])]) m4_ifndef([AC_LIBTOOL_PROG_COMPILER_NO_RTTI], [AC_DEFUN([AC_LIBTOOL_PROG_COMPILER_NO_RTTI])]) m4_ifndef([AC_LIBTOOL_SYS_GLOBAL_SYMBOL_PIPE], [AC_DEFUN([AC_LIBTOOL_SYS_GLOBAL_SYMBOL_PIPE])]) m4_ifndef([AC_LIBTOOL_PROG_COMPILER_PIC], [AC_DEFUN([AC_LIBTOOL_PROG_COMPILER_PIC])]) m4_ifndef([AC_LIBTOOL_PROG_LD_SHLIBS], [AC_DEFUN([AC_LIBTOOL_PROG_LD_SHLIBS])]) m4_ifndef([AC_LIBTOOL_POSTDEP_PREDEP], [AC_DEFUN([AC_LIBTOOL_POSTDEP_PREDEP])]) m4_ifndef([LT_AC_PROG_EGREP], [AC_DEFUN([LT_AC_PROG_EGREP])]) m4_ifndef([LT_AC_PROG_SED], [AC_DEFUN([LT_AC_PROG_SED])]) m4_ifndef([_LT_CC_BASENAME], [AC_DEFUN([_LT_CC_BASENAME])]) m4_ifndef([_LT_COMPILER_BOILERPLATE], [AC_DEFUN([_LT_COMPILER_BOILERPLATE])]) m4_ifndef([_LT_LINKER_BOILERPLATE], [AC_DEFUN([_LT_LINKER_BOILERPLATE])]) m4_ifndef([_AC_PROG_LIBTOOL], [AC_DEFUN([_AC_PROG_LIBTOOL])]) m4_ifndef([AC_LIBTOOL_SETUP], [AC_DEFUN([AC_LIBTOOL_SETUP])]) m4_ifndef([_LT_AC_CHECK_DLFCN], [AC_DEFUN([_LT_AC_CHECK_DLFCN])]) m4_ifndef([AC_LIBTOOL_SYS_DYNAMIC_LINKER], [AC_DEFUN([AC_LIBTOOL_SYS_DYNAMIC_LINKER])]) m4_ifndef([_LT_AC_TAGCONFIG], [AC_DEFUN([_LT_AC_TAGCONFIG])]) m4_ifndef([AC_DISABLE_FAST_INSTALL], [AC_DEFUN([AC_DISABLE_FAST_INSTALL])]) m4_ifndef([_LT_AC_LANG_CXX], [AC_DEFUN([_LT_AC_LANG_CXX])]) m4_ifndef([_LT_AC_LANG_F77], [AC_DEFUN([_LT_AC_LANG_F77])]) m4_ifndef([_LT_AC_LANG_GCJ], [AC_DEFUN([_LT_AC_LANG_GCJ])]) m4_ifndef([AC_LIBTOOL_LANG_C_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_C_CONFIG])]) m4_ifndef([_LT_AC_LANG_C_CONFIG], [AC_DEFUN([_LT_AC_LANG_C_CONFIG])]) m4_ifndef([AC_LIBTOOL_LANG_CXX_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_CXX_CONFIG])]) m4_ifndef([_LT_AC_LANG_CXX_CONFIG], [AC_DEFUN([_LT_AC_LANG_CXX_CONFIG])]) m4_ifndef([AC_LIBTOOL_LANG_F77_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_F77_CONFIG])]) m4_ifndef([_LT_AC_LANG_F77_CONFIG], [AC_DEFUN([_LT_AC_LANG_F77_CONFIG])]) m4_ifndef([AC_LIBTOOL_LANG_GCJ_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_GCJ_CONFIG])]) m4_ifndef([_LT_AC_LANG_GCJ_CONFIG], [AC_DEFUN([_LT_AC_LANG_GCJ_CONFIG])]) m4_ifndef([AC_LIBTOOL_LANG_RC_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_RC_CONFIG])]) m4_ifndef([_LT_AC_LANG_RC_CONFIG], [AC_DEFUN([_LT_AC_LANG_RC_CONFIG])]) m4_ifndef([AC_LIBTOOL_CONFIG], [AC_DEFUN([AC_LIBTOOL_CONFIG])]) m4_ifndef([_LT_AC_FILE_LTDLL_C], [AC_DEFUN([_LT_AC_FILE_LTDLL_C])]) m4_ifndef([_LT_REQUIRED_DARWIN_CHECKS], [AC_DEFUN([_LT_REQUIRED_DARWIN_CHECKS])]) m4_ifndef([_LT_AC_PROG_CXXCPP], [AC_DEFUN([_LT_AC_PROG_CXXCPP])]) m4_ifndef([_LT_PREPARE_SED_QUOTE_VARS], [AC_DEFUN([_LT_PREPARE_SED_QUOTE_VARS])]) m4_ifndef([_LT_PROG_ECHO_BACKSLASH], [AC_DEFUN([_LT_PROG_ECHO_BACKSLASH])]) m4_ifndef([_LT_PROG_F77], [AC_DEFUN([_LT_PROG_F77])]) m4_ifndef([_LT_PROG_FC], [AC_DEFUN([_LT_PROG_FC])]) m4_ifndef([_LT_PROG_CXX], [AC_DEFUN([_LT_PROG_CXX])]) telepathy-gabble-0.18.2/m4/ltversion.m40000644000175000017500000000126212312536063017626 0ustar00smcvsmcv00000000000000# ltversion.m4 -- version numbers -*- Autoconf -*- # # Copyright (C) 2004 Free Software Foundation, Inc. # Written by Scott James Remnant, 2004 # # This file is free software; the Free Software Foundation gives # unlimited permission to copy and/or distribute it, with or without # modifications, as long as this notice is preserved. # @configure_input@ # serial 3337 ltversion.m4 # This file is part of GNU Libtool m4_define([LT_PACKAGE_VERSION], [2.4.2]) m4_define([LT_PACKAGE_REVISION], [1.3337]) AC_DEFUN([LTVERSION_VERSION], [macro_version='2.4.2' macro_revision='1.3337' _LT_DECL(, macro_version, 0, [Which release of libtool.m4 was used?]) _LT_DECL(, macro_revision, 0) ]) telepathy-gabble-0.18.2/m4/ltsugar.m40000644000175000017500000001042412312536063017262 0ustar00smcvsmcv00000000000000# ltsugar.m4 -- libtool m4 base layer. -*-Autoconf-*- # # Copyright (C) 2004, 2005, 2007, 2008 Free Software Foundation, Inc. # Written by Gary V. Vaughan, 2004 # # This file is free software; the Free Software Foundation gives # unlimited permission to copy and/or distribute it, with or without # modifications, as long as this notice is preserved. # serial 6 ltsugar.m4 # This is to help aclocal find these macros, as it can't see m4_define. AC_DEFUN([LTSUGAR_VERSION], [m4_if([0.1])]) # lt_join(SEP, ARG1, [ARG2...]) # ----------------------------- # Produce ARG1SEPARG2...SEPARGn, omitting [] arguments and their # associated separator. # Needed until we can rely on m4_join from Autoconf 2.62, since all earlier # versions in m4sugar had bugs. m4_define([lt_join], [m4_if([$#], [1], [], [$#], [2], [[$2]], [m4_if([$2], [], [], [[$2]_])$0([$1], m4_shift(m4_shift($@)))])]) m4_define([_lt_join], [m4_if([$#$2], [2], [], [m4_if([$2], [], [], [[$1$2]])$0([$1], m4_shift(m4_shift($@)))])]) # lt_car(LIST) # lt_cdr(LIST) # ------------ # Manipulate m4 lists. # These macros are necessary as long as will still need to support # Autoconf-2.59 which quotes differently. m4_define([lt_car], [[$1]]) m4_define([lt_cdr], [m4_if([$#], 0, [m4_fatal([$0: cannot be called without arguments])], [$#], 1, [], [m4_dquote(m4_shift($@))])]) m4_define([lt_unquote], $1) # lt_append(MACRO-NAME, STRING, [SEPARATOR]) # ------------------------------------------ # Redefine MACRO-NAME to hold its former content plus `SEPARATOR'`STRING'. # Note that neither SEPARATOR nor STRING are expanded; they are appended # to MACRO-NAME as is (leaving the expansion for when MACRO-NAME is invoked). # No SEPARATOR is output if MACRO-NAME was previously undefined (different # than defined and empty). # # This macro is needed until we can rely on Autoconf 2.62, since earlier # versions of m4sugar mistakenly expanded SEPARATOR but not STRING. m4_define([lt_append], [m4_define([$1], m4_ifdef([$1], [m4_defn([$1])[$3]])[$2])]) # lt_combine(SEP, PREFIX-LIST, INFIX, SUFFIX1, [SUFFIX2...]) # ---------------------------------------------------------- # Produce a SEP delimited list of all paired combinations of elements of # PREFIX-LIST with SUFFIX1 through SUFFIXn. Each element of the list # has the form PREFIXmINFIXSUFFIXn. # Needed until we can rely on m4_combine added in Autoconf 2.62. m4_define([lt_combine], [m4_if(m4_eval([$# > 3]), [1], [m4_pushdef([_Lt_sep], [m4_define([_Lt_sep], m4_defn([lt_car]))])]]dnl [[m4_foreach([_Lt_prefix], [$2], [m4_foreach([_Lt_suffix], ]m4_dquote(m4_dquote(m4_shift(m4_shift(m4_shift($@)))))[, [_Lt_sep([$1])[]m4_defn([_Lt_prefix])[$3]m4_defn([_Lt_suffix])])])])]) # lt_if_append_uniq(MACRO-NAME, VARNAME, [SEPARATOR], [UNIQ], [NOT-UNIQ]) # ----------------------------------------------------------------------- # Iff MACRO-NAME does not yet contain VARNAME, then append it (delimited # by SEPARATOR if supplied) and expand UNIQ, else NOT-UNIQ. m4_define([lt_if_append_uniq], [m4_ifdef([$1], [m4_if(m4_index([$3]m4_defn([$1])[$3], [$3$2$3]), [-1], [lt_append([$1], [$2], [$3])$4], [$5])], [lt_append([$1], [$2], [$3])$4])]) # lt_dict_add(DICT, KEY, VALUE) # ----------------------------- m4_define([lt_dict_add], [m4_define([$1($2)], [$3])]) # lt_dict_add_subkey(DICT, KEY, SUBKEY, VALUE) # -------------------------------------------- m4_define([lt_dict_add_subkey], [m4_define([$1($2:$3)], [$4])]) # lt_dict_fetch(DICT, KEY, [SUBKEY]) # ---------------------------------- m4_define([lt_dict_fetch], [m4_ifval([$3], m4_ifdef([$1($2:$3)], [m4_defn([$1($2:$3)])]), m4_ifdef([$1($2)], [m4_defn([$1($2)])]))]) # lt_if_dict_fetch(DICT, KEY, [SUBKEY], VALUE, IF-TRUE, [IF-FALSE]) # ----------------------------------------------------------------- m4_define([lt_if_dict_fetch], [m4_if(lt_dict_fetch([$1], [$2], [$3]), [$4], [$5], [$6])]) # lt_dict_filter(DICT, [SUBKEY], VALUE, [SEPARATOR], KEY, [...]) # -------------------------------------------------------------- m4_define([lt_dict_filter], [m4_if([$5], [], [], [lt_join(m4_quote(m4_default([$4], [[, ]])), lt_unquote(m4_split(m4_normalize(m4_foreach(_Lt_key, lt_car([m4_shiftn(4, $@)]), [lt_if_dict_fetch([$1], _Lt_key, [$2], [$3], [_Lt_key ])])))))])[]dnl ]) telepathy-gabble-0.18.2/m4/ltoptions.m40000644000175000017500000003007312312536063017636 0ustar00smcvsmcv00000000000000# Helper functions for option handling. -*- Autoconf -*- # # Copyright (C) 2004, 2005, 2007, 2008, 2009 Free Software Foundation, # Inc. # Written by Gary V. Vaughan, 2004 # # This file is free software; the Free Software Foundation gives # unlimited permission to copy and/or distribute it, with or without # modifications, as long as this notice is preserved. # serial 7 ltoptions.m4 # This is to help aclocal find these macros, as it can't see m4_define. AC_DEFUN([LTOPTIONS_VERSION], [m4_if([1])]) # _LT_MANGLE_OPTION(MACRO-NAME, OPTION-NAME) # ------------------------------------------ m4_define([_LT_MANGLE_OPTION], [[_LT_OPTION_]m4_bpatsubst($1__$2, [[^a-zA-Z0-9_]], [_])]) # _LT_SET_OPTION(MACRO-NAME, OPTION-NAME) # --------------------------------------- # Set option OPTION-NAME for macro MACRO-NAME, and if there is a # matching handler defined, dispatch to it. Other OPTION-NAMEs are # saved as a flag. m4_define([_LT_SET_OPTION], [m4_define(_LT_MANGLE_OPTION([$1], [$2]))dnl m4_ifdef(_LT_MANGLE_DEFUN([$1], [$2]), _LT_MANGLE_DEFUN([$1], [$2]), [m4_warning([Unknown $1 option `$2'])])[]dnl ]) # _LT_IF_OPTION(MACRO-NAME, OPTION-NAME, IF-SET, [IF-NOT-SET]) # ------------------------------------------------------------ # Execute IF-SET if OPTION is set, IF-NOT-SET otherwise. m4_define([_LT_IF_OPTION], [m4_ifdef(_LT_MANGLE_OPTION([$1], [$2]), [$3], [$4])]) # _LT_UNLESS_OPTIONS(MACRO-NAME, OPTION-LIST, IF-NOT-SET) # ------------------------------------------------------- # Execute IF-NOT-SET unless all options in OPTION-LIST for MACRO-NAME # are set. m4_define([_LT_UNLESS_OPTIONS], [m4_foreach([_LT_Option], m4_split(m4_normalize([$2])), [m4_ifdef(_LT_MANGLE_OPTION([$1], _LT_Option), [m4_define([$0_found])])])[]dnl m4_ifdef([$0_found], [m4_undefine([$0_found])], [$3 ])[]dnl ]) # _LT_SET_OPTIONS(MACRO-NAME, OPTION-LIST) # ---------------------------------------- # OPTION-LIST is a space-separated list of Libtool options associated # with MACRO-NAME. If any OPTION has a matching handler declared with # LT_OPTION_DEFINE, dispatch to that macro; otherwise complain about # the unknown option and exit. m4_defun([_LT_SET_OPTIONS], [# Set options m4_foreach([_LT_Option], m4_split(m4_normalize([$2])), [_LT_SET_OPTION([$1], _LT_Option)]) m4_if([$1],[LT_INIT],[ dnl dnl Simply set some default values (i.e off) if boolean options were not dnl specified: _LT_UNLESS_OPTIONS([LT_INIT], [dlopen], [enable_dlopen=no ]) _LT_UNLESS_OPTIONS([LT_INIT], [win32-dll], [enable_win32_dll=no ]) dnl dnl If no reference was made to various pairs of opposing options, then dnl we run the default mode handler for the pair. For example, if neither dnl `shared' nor `disable-shared' was passed, we enable building of shared dnl archives by default: _LT_UNLESS_OPTIONS([LT_INIT], [shared disable-shared], [_LT_ENABLE_SHARED]) _LT_UNLESS_OPTIONS([LT_INIT], [static disable-static], [_LT_ENABLE_STATIC]) _LT_UNLESS_OPTIONS([LT_INIT], [pic-only no-pic], [_LT_WITH_PIC]) _LT_UNLESS_OPTIONS([LT_INIT], [fast-install disable-fast-install], [_LT_ENABLE_FAST_INSTALL]) ]) ])# _LT_SET_OPTIONS ## --------------------------------- ## ## Macros to handle LT_INIT options. ## ## --------------------------------- ## # _LT_MANGLE_DEFUN(MACRO-NAME, OPTION-NAME) # ----------------------------------------- m4_define([_LT_MANGLE_DEFUN], [[_LT_OPTION_DEFUN_]m4_bpatsubst(m4_toupper([$1__$2]), [[^A-Z0-9_]], [_])]) # LT_OPTION_DEFINE(MACRO-NAME, OPTION-NAME, CODE) # ----------------------------------------------- m4_define([LT_OPTION_DEFINE], [m4_define(_LT_MANGLE_DEFUN([$1], [$2]), [$3])[]dnl ])# LT_OPTION_DEFINE # dlopen # ------ LT_OPTION_DEFINE([LT_INIT], [dlopen], [enable_dlopen=yes ]) AU_DEFUN([AC_LIBTOOL_DLOPEN], [_LT_SET_OPTION([LT_INIT], [dlopen]) AC_DIAGNOSE([obsolete], [$0: Remove this warning and the call to _LT_SET_OPTION when you put the `dlopen' option into LT_INIT's first parameter.]) ]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([AC_LIBTOOL_DLOPEN], []) # win32-dll # --------- # Declare package support for building win32 dll's. LT_OPTION_DEFINE([LT_INIT], [win32-dll], [enable_win32_dll=yes case $host in *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-cegcc*) AC_CHECK_TOOL(AS, as, false) AC_CHECK_TOOL(DLLTOOL, dlltool, false) AC_CHECK_TOOL(OBJDUMP, objdump, false) ;; esac test -z "$AS" && AS=as _LT_DECL([], [AS], [1], [Assembler program])dnl test -z "$DLLTOOL" && DLLTOOL=dlltool _LT_DECL([], [DLLTOOL], [1], [DLL creation program])dnl test -z "$OBJDUMP" && OBJDUMP=objdump _LT_DECL([], [OBJDUMP], [1], [Object dumper program])dnl ])# win32-dll AU_DEFUN([AC_LIBTOOL_WIN32_DLL], [AC_REQUIRE([AC_CANONICAL_HOST])dnl _LT_SET_OPTION([LT_INIT], [win32-dll]) AC_DIAGNOSE([obsolete], [$0: Remove this warning and the call to _LT_SET_OPTION when you put the `win32-dll' option into LT_INIT's first parameter.]) ]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([AC_LIBTOOL_WIN32_DLL], []) # _LT_ENABLE_SHARED([DEFAULT]) # ---------------------------- # implement the --enable-shared flag, and supports the `shared' and # `disable-shared' LT_INIT options. # DEFAULT is either `yes' or `no'. If omitted, it defaults to `yes'. m4_define([_LT_ENABLE_SHARED], [m4_define([_LT_ENABLE_SHARED_DEFAULT], [m4_if($1, no, no, yes)])dnl AC_ARG_ENABLE([shared], [AS_HELP_STRING([--enable-shared@<:@=PKGS@:>@], [build shared libraries @<:@default=]_LT_ENABLE_SHARED_DEFAULT[@:>@])], [p=${PACKAGE-default} case $enableval in yes) enable_shared=yes ;; no) enable_shared=no ;; *) enable_shared=no # Look at the argument we got. We use all the common list separators. lt_save_ifs="$IFS"; IFS="${IFS}$PATH_SEPARATOR," for pkg in $enableval; do IFS="$lt_save_ifs" if test "X$pkg" = "X$p"; then enable_shared=yes fi done IFS="$lt_save_ifs" ;; esac], [enable_shared=]_LT_ENABLE_SHARED_DEFAULT) _LT_DECL([build_libtool_libs], [enable_shared], [0], [Whether or not to build shared libraries]) ])# _LT_ENABLE_SHARED LT_OPTION_DEFINE([LT_INIT], [shared], [_LT_ENABLE_SHARED([yes])]) LT_OPTION_DEFINE([LT_INIT], [disable-shared], [_LT_ENABLE_SHARED([no])]) # Old names: AC_DEFUN([AC_ENABLE_SHARED], [_LT_SET_OPTION([LT_INIT], m4_if([$1], [no], [disable-])[shared]) ]) AC_DEFUN([AC_DISABLE_SHARED], [_LT_SET_OPTION([LT_INIT], [disable-shared]) ]) AU_DEFUN([AM_ENABLE_SHARED], [AC_ENABLE_SHARED($@)]) AU_DEFUN([AM_DISABLE_SHARED], [AC_DISABLE_SHARED($@)]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([AM_ENABLE_SHARED], []) dnl AC_DEFUN([AM_DISABLE_SHARED], []) # _LT_ENABLE_STATIC([DEFAULT]) # ---------------------------- # implement the --enable-static flag, and support the `static' and # `disable-static' LT_INIT options. # DEFAULT is either `yes' or `no'. If omitted, it defaults to `yes'. m4_define([_LT_ENABLE_STATIC], [m4_define([_LT_ENABLE_STATIC_DEFAULT], [m4_if($1, no, no, yes)])dnl AC_ARG_ENABLE([static], [AS_HELP_STRING([--enable-static@<:@=PKGS@:>@], [build static libraries @<:@default=]_LT_ENABLE_STATIC_DEFAULT[@:>@])], [p=${PACKAGE-default} case $enableval in yes) enable_static=yes ;; no) enable_static=no ;; *) enable_static=no # Look at the argument we got. We use all the common list separators. lt_save_ifs="$IFS"; IFS="${IFS}$PATH_SEPARATOR," for pkg in $enableval; do IFS="$lt_save_ifs" if test "X$pkg" = "X$p"; then enable_static=yes fi done IFS="$lt_save_ifs" ;; esac], [enable_static=]_LT_ENABLE_STATIC_DEFAULT) _LT_DECL([build_old_libs], [enable_static], [0], [Whether or not to build static libraries]) ])# _LT_ENABLE_STATIC LT_OPTION_DEFINE([LT_INIT], [static], [_LT_ENABLE_STATIC([yes])]) LT_OPTION_DEFINE([LT_INIT], [disable-static], [_LT_ENABLE_STATIC([no])]) # Old names: AC_DEFUN([AC_ENABLE_STATIC], [_LT_SET_OPTION([LT_INIT], m4_if([$1], [no], [disable-])[static]) ]) AC_DEFUN([AC_DISABLE_STATIC], [_LT_SET_OPTION([LT_INIT], [disable-static]) ]) AU_DEFUN([AM_ENABLE_STATIC], [AC_ENABLE_STATIC($@)]) AU_DEFUN([AM_DISABLE_STATIC], [AC_DISABLE_STATIC($@)]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([AM_ENABLE_STATIC], []) dnl AC_DEFUN([AM_DISABLE_STATIC], []) # _LT_ENABLE_FAST_INSTALL([DEFAULT]) # ---------------------------------- # implement the --enable-fast-install flag, and support the `fast-install' # and `disable-fast-install' LT_INIT options. # DEFAULT is either `yes' or `no'. If omitted, it defaults to `yes'. m4_define([_LT_ENABLE_FAST_INSTALL], [m4_define([_LT_ENABLE_FAST_INSTALL_DEFAULT], [m4_if($1, no, no, yes)])dnl AC_ARG_ENABLE([fast-install], [AS_HELP_STRING([--enable-fast-install@<:@=PKGS@:>@], [optimize for fast installation @<:@default=]_LT_ENABLE_FAST_INSTALL_DEFAULT[@:>@])], [p=${PACKAGE-default} case $enableval in yes) enable_fast_install=yes ;; no) enable_fast_install=no ;; *) enable_fast_install=no # Look at the argument we got. We use all the common list separators. lt_save_ifs="$IFS"; IFS="${IFS}$PATH_SEPARATOR," for pkg in $enableval; do IFS="$lt_save_ifs" if test "X$pkg" = "X$p"; then enable_fast_install=yes fi done IFS="$lt_save_ifs" ;; esac], [enable_fast_install=]_LT_ENABLE_FAST_INSTALL_DEFAULT) _LT_DECL([fast_install], [enable_fast_install], [0], [Whether or not to optimize for fast installation])dnl ])# _LT_ENABLE_FAST_INSTALL LT_OPTION_DEFINE([LT_INIT], [fast-install], [_LT_ENABLE_FAST_INSTALL([yes])]) LT_OPTION_DEFINE([LT_INIT], [disable-fast-install], [_LT_ENABLE_FAST_INSTALL([no])]) # Old names: AU_DEFUN([AC_ENABLE_FAST_INSTALL], [_LT_SET_OPTION([LT_INIT], m4_if([$1], [no], [disable-])[fast-install]) AC_DIAGNOSE([obsolete], [$0: Remove this warning and the call to _LT_SET_OPTION when you put the `fast-install' option into LT_INIT's first parameter.]) ]) AU_DEFUN([AC_DISABLE_FAST_INSTALL], [_LT_SET_OPTION([LT_INIT], [disable-fast-install]) AC_DIAGNOSE([obsolete], [$0: Remove this warning and the call to _LT_SET_OPTION when you put the `disable-fast-install' option into LT_INIT's first parameter.]) ]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([AC_ENABLE_FAST_INSTALL], []) dnl AC_DEFUN([AM_DISABLE_FAST_INSTALL], []) # _LT_WITH_PIC([MODE]) # -------------------- # implement the --with-pic flag, and support the `pic-only' and `no-pic' # LT_INIT options. # MODE is either `yes' or `no'. If omitted, it defaults to `both'. m4_define([_LT_WITH_PIC], [AC_ARG_WITH([pic], [AS_HELP_STRING([--with-pic@<:@=PKGS@:>@], [try to use only PIC/non-PIC objects @<:@default=use both@:>@])], [lt_p=${PACKAGE-default} case $withval in yes|no) pic_mode=$withval ;; *) pic_mode=default # Look at the argument we got. We use all the common list separators. lt_save_ifs="$IFS"; IFS="${IFS}$PATH_SEPARATOR," for lt_pkg in $withval; do IFS="$lt_save_ifs" if test "X$lt_pkg" = "X$lt_p"; then pic_mode=yes fi done IFS="$lt_save_ifs" ;; esac], [pic_mode=default]) test -z "$pic_mode" && pic_mode=m4_default([$1], [default]) _LT_DECL([], [pic_mode], [0], [What type of objects to build])dnl ])# _LT_WITH_PIC LT_OPTION_DEFINE([LT_INIT], [pic-only], [_LT_WITH_PIC([yes])]) LT_OPTION_DEFINE([LT_INIT], [no-pic], [_LT_WITH_PIC([no])]) # Old name: AU_DEFUN([AC_LIBTOOL_PICMODE], [_LT_SET_OPTION([LT_INIT], [pic-only]) AC_DIAGNOSE([obsolete], [$0: Remove this warning and the call to _LT_SET_OPTION when you put the `pic-only' option into LT_INIT's first parameter.]) ]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([AC_LIBTOOL_PICMODE], []) ## ----------------- ## ## LTDL_INIT Options ## ## ----------------- ## m4_define([_LTDL_MODE], []) LT_OPTION_DEFINE([LTDL_INIT], [nonrecursive], [m4_define([_LTDL_MODE], [nonrecursive])]) LT_OPTION_DEFINE([LTDL_INIT], [recursive], [m4_define([_LTDL_MODE], [recursive])]) LT_OPTION_DEFINE([LTDL_INIT], [subproject], [m4_define([_LTDL_MODE], [subproject])]) m4_define([_LTDL_TYPE], []) LT_OPTION_DEFINE([LTDL_INIT], [installable], [m4_define([_LTDL_TYPE], [installable])]) LT_OPTION_DEFINE([LTDL_INIT], [convenience], [m4_define([_LTDL_TYPE], [convenience])]) telepathy-gabble-0.18.2/m4/libtool.m40000644000175000017500000106011112312536063017244 0ustar00smcvsmcv00000000000000# libtool.m4 - Configure libtool for the host system. -*-Autoconf-*- # # Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2003, 2004, 2005, # 2006, 2007, 2008, 2009, 2010, 2011 Free Software # Foundation, Inc. # Written by Gordon Matzigkeit, 1996 # # This file is free software; the Free Software Foundation gives # unlimited permission to copy and/or distribute it, with or without # modifications, as long as this notice is preserved. m4_define([_LT_COPYING], [dnl # Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2003, 2004, 2005, # 2006, 2007, 2008, 2009, 2010, 2011 Free Software # Foundation, Inc. # Written by Gordon Matzigkeit, 1996 # # This file is part of GNU Libtool. # # GNU Libtool 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 2 of # the License, or (at your option) any later version. # # As a special exception to the GNU General Public License, # if you distribute this file as part of a program or library that # is built using GNU Libtool, you may include this file under the # same distribution terms that you use for the rest of that program. # # GNU Libtool 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 GNU Libtool; see the file COPYING. If not, a copy # can be downloaded from http://www.gnu.org/licenses/gpl.html, or # obtained by writing to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. ]) # serial 57 LT_INIT # LT_PREREQ(VERSION) # ------------------ # Complain and exit if this libtool version is less that VERSION. m4_defun([LT_PREREQ], [m4_if(m4_version_compare(m4_defn([LT_PACKAGE_VERSION]), [$1]), -1, [m4_default([$3], [m4_fatal([Libtool version $1 or higher is required], 63)])], [$2])]) # _LT_CHECK_BUILDDIR # ------------------ # Complain if the absolute build directory name contains unusual characters m4_defun([_LT_CHECK_BUILDDIR], [case `pwd` in *\ * | *\ *) AC_MSG_WARN([Libtool does not cope well with whitespace in `pwd`]) ;; esac ]) # LT_INIT([OPTIONS]) # ------------------ AC_DEFUN([LT_INIT], [AC_PREREQ([2.58])dnl We use AC_INCLUDES_DEFAULT AC_REQUIRE([AC_CONFIG_AUX_DIR_DEFAULT])dnl AC_BEFORE([$0], [LT_LANG])dnl AC_BEFORE([$0], [LT_OUTPUT])dnl AC_BEFORE([$0], [LTDL_INIT])dnl m4_require([_LT_CHECK_BUILDDIR])dnl dnl Autoconf doesn't catch unexpanded LT_ macros by default: m4_pattern_forbid([^_?LT_[A-Z_]+$])dnl m4_pattern_allow([^(_LT_EOF|LT_DLGLOBAL|LT_DLLAZY_OR_NOW|LT_MULTI_MODULE)$])dnl dnl aclocal doesn't pull ltoptions.m4, ltsugar.m4, or ltversion.m4 dnl unless we require an AC_DEFUNed macro: AC_REQUIRE([LTOPTIONS_VERSION])dnl AC_REQUIRE([LTSUGAR_VERSION])dnl AC_REQUIRE([LTVERSION_VERSION])dnl AC_REQUIRE([LTOBSOLETE_VERSION])dnl m4_require([_LT_PROG_LTMAIN])dnl _LT_SHELL_INIT([SHELL=${CONFIG_SHELL-/bin/sh}]) dnl Parse OPTIONS _LT_SET_OPTIONS([$0], [$1]) # This can be used to rebuild libtool when needed LIBTOOL_DEPS="$ltmain" # Always use our own libtool. LIBTOOL='$(SHELL) $(top_builddir)/libtool' AC_SUBST(LIBTOOL)dnl _LT_SETUP # Only expand once: m4_define([LT_INIT]) ])# LT_INIT # Old names: AU_ALIAS([AC_PROG_LIBTOOL], [LT_INIT]) AU_ALIAS([AM_PROG_LIBTOOL], [LT_INIT]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([AC_PROG_LIBTOOL], []) dnl AC_DEFUN([AM_PROG_LIBTOOL], []) # _LT_CC_BASENAME(CC) # ------------------- # Calculate cc_basename. Skip known compiler wrappers and cross-prefix. m4_defun([_LT_CC_BASENAME], [for cc_temp in $1""; do case $cc_temp in compile | *[[\\/]]compile | ccache | *[[\\/]]ccache ) ;; distcc | *[[\\/]]distcc | purify | *[[\\/]]purify ) ;; \-*) ;; *) break;; esac done cc_basename=`$ECHO "$cc_temp" | $SED "s%.*/%%; s%^$host_alias-%%"` ]) # _LT_FILEUTILS_DEFAULTS # ---------------------- # It is okay to use these file commands and assume they have been set # sensibly after `m4_require([_LT_FILEUTILS_DEFAULTS])'. m4_defun([_LT_FILEUTILS_DEFAULTS], [: ${CP="cp -f"} : ${MV="mv -f"} : ${RM="rm -f"} ])# _LT_FILEUTILS_DEFAULTS # _LT_SETUP # --------- m4_defun([_LT_SETUP], [AC_REQUIRE([AC_CANONICAL_HOST])dnl AC_REQUIRE([AC_CANONICAL_BUILD])dnl AC_REQUIRE([_LT_PREPARE_SED_QUOTE_VARS])dnl AC_REQUIRE([_LT_PROG_ECHO_BACKSLASH])dnl _LT_DECL([], [PATH_SEPARATOR], [1], [The PATH separator for the build system])dnl dnl _LT_DECL([], [host_alias], [0], [The host system])dnl _LT_DECL([], [host], [0])dnl _LT_DECL([], [host_os], [0])dnl dnl _LT_DECL([], [build_alias], [0], [The build system])dnl _LT_DECL([], [build], [0])dnl _LT_DECL([], [build_os], [0])dnl dnl AC_REQUIRE([AC_PROG_CC])dnl AC_REQUIRE([LT_PATH_LD])dnl AC_REQUIRE([LT_PATH_NM])dnl dnl AC_REQUIRE([AC_PROG_LN_S])dnl test -z "$LN_S" && LN_S="ln -s" _LT_DECL([], [LN_S], [1], [Whether we need soft or hard links])dnl dnl AC_REQUIRE([LT_CMD_MAX_LEN])dnl _LT_DECL([objext], [ac_objext], [0], [Object file suffix (normally "o")])dnl _LT_DECL([], [exeext], [0], [Executable file suffix (normally "")])dnl dnl m4_require([_LT_FILEUTILS_DEFAULTS])dnl m4_require([_LT_CHECK_SHELL_FEATURES])dnl m4_require([_LT_PATH_CONVERSION_FUNCTIONS])dnl m4_require([_LT_CMD_RELOAD])dnl m4_require([_LT_CHECK_MAGIC_METHOD])dnl m4_require([_LT_CHECK_SHAREDLIB_FROM_LINKLIB])dnl m4_require([_LT_CMD_OLD_ARCHIVE])dnl m4_require([_LT_CMD_GLOBAL_SYMBOLS])dnl m4_require([_LT_WITH_SYSROOT])dnl _LT_CONFIG_LIBTOOL_INIT([ # See if we are running on zsh, and set the options which allow our # commands through without removal of \ escapes INIT. if test -n "\${ZSH_VERSION+set}" ; then setopt NO_GLOB_SUBST fi ]) if test -n "${ZSH_VERSION+set}" ; then setopt NO_GLOB_SUBST fi _LT_CHECK_OBJDIR m4_require([_LT_TAG_COMPILER])dnl case $host_os in aix3*) # AIX sometimes has problems with the GCC collect2 program. For some # reason, if we set the COLLECT_NAMES environment variable, the problems # vanish in a puff of smoke. if test "X${COLLECT_NAMES+set}" != Xset; then COLLECT_NAMES= export COLLECT_NAMES fi ;; esac # Global variables: ofile=libtool can_build_shared=yes # All known linkers require a `.a' archive for static linking (except MSVC, # which needs '.lib'). libext=a with_gnu_ld="$lt_cv_prog_gnu_ld" old_CC="$CC" old_CFLAGS="$CFLAGS" # Set sane defaults for various variables test -z "$CC" && CC=cc test -z "$LTCC" && LTCC=$CC test -z "$LTCFLAGS" && LTCFLAGS=$CFLAGS test -z "$LD" && LD=ld test -z "$ac_objext" && ac_objext=o _LT_CC_BASENAME([$compiler]) # Only perform the check for file, if the check method requires it test -z "$MAGIC_CMD" && MAGIC_CMD=file case $deplibs_check_method in file_magic*) if test "$file_magic_cmd" = '$MAGIC_CMD'; then _LT_PATH_MAGIC fi ;; esac # Use C for the default configuration in the libtool script LT_SUPPORTED_TAG([CC]) _LT_LANG_C_CONFIG _LT_LANG_DEFAULT_CONFIG _LT_CONFIG_COMMANDS ])# _LT_SETUP # _LT_PREPARE_SED_QUOTE_VARS # -------------------------- # Define a few sed substitution that help us do robust quoting. m4_defun([_LT_PREPARE_SED_QUOTE_VARS], [# Backslashify metacharacters that are still active within # double-quoted strings. sed_quote_subst='s/\([["`$\\]]\)/\\\1/g' # Same as above, but do not quote variable references. double_quote_subst='s/\([["`\\]]\)/\\\1/g' # Sed substitution to delay expansion of an escaped shell variable in a # double_quote_subst'ed string. delay_variable_subst='s/\\\\\\\\\\\$/\\\\\\$/g' # Sed substitution to delay expansion of an escaped single quote. delay_single_quote_subst='s/'\''/'\'\\\\\\\'\''/g' # Sed substitution to avoid accidental globbing in evaled expressions no_glob_subst='s/\*/\\\*/g' ]) # _LT_PROG_LTMAIN # --------------- # Note that this code is called both from `configure', and `config.status' # now that we use AC_CONFIG_COMMANDS to generate libtool. Notably, # `config.status' has no value for ac_aux_dir unless we are using Automake, # so we pass a copy along to make sure it has a sensible value anyway. m4_defun([_LT_PROG_LTMAIN], [m4_ifdef([AC_REQUIRE_AUX_FILE], [AC_REQUIRE_AUX_FILE([ltmain.sh])])dnl _LT_CONFIG_LIBTOOL_INIT([ac_aux_dir='$ac_aux_dir']) ltmain="$ac_aux_dir/ltmain.sh" ])# _LT_PROG_LTMAIN ## ------------------------------------- ## ## Accumulate code for creating libtool. ## ## ------------------------------------- ## # So that we can recreate a full libtool script including additional # tags, we accumulate the chunks of code to send to AC_CONFIG_COMMANDS # in macros and then make a single call at the end using the `libtool' # label. # _LT_CONFIG_LIBTOOL_INIT([INIT-COMMANDS]) # ---------------------------------------- # Register INIT-COMMANDS to be passed to AC_CONFIG_COMMANDS later. m4_define([_LT_CONFIG_LIBTOOL_INIT], [m4_ifval([$1], [m4_append([_LT_OUTPUT_LIBTOOL_INIT], [$1 ])])]) # Initialize. m4_define([_LT_OUTPUT_LIBTOOL_INIT]) # _LT_CONFIG_LIBTOOL([COMMANDS]) # ------------------------------ # Register COMMANDS to be passed to AC_CONFIG_COMMANDS later. m4_define([_LT_CONFIG_LIBTOOL], [m4_ifval([$1], [m4_append([_LT_OUTPUT_LIBTOOL_COMMANDS], [$1 ])])]) # Initialize. m4_define([_LT_OUTPUT_LIBTOOL_COMMANDS]) # _LT_CONFIG_SAVE_COMMANDS([COMMANDS], [INIT_COMMANDS]) # ----------------------------------------------------- m4_defun([_LT_CONFIG_SAVE_COMMANDS], [_LT_CONFIG_LIBTOOL([$1]) _LT_CONFIG_LIBTOOL_INIT([$2]) ]) # _LT_FORMAT_COMMENT([COMMENT]) # ----------------------------- # Add leading comment marks to the start of each line, and a trailing # full-stop to the whole comment if one is not present already. m4_define([_LT_FORMAT_COMMENT], [m4_ifval([$1], [ m4_bpatsubst([m4_bpatsubst([$1], [^ *], [# ])], [['`$\]], [\\\&])]m4_bmatch([$1], [[!?.]$], [], [.]) )]) ## ------------------------ ## ## FIXME: Eliminate VARNAME ## ## ------------------------ ## # _LT_DECL([CONFIGNAME], VARNAME, VALUE, [DESCRIPTION], [IS-TAGGED?]) # ------------------------------------------------------------------- # CONFIGNAME is the name given to the value in the libtool script. # VARNAME is the (base) name used in the configure script. # VALUE may be 0, 1 or 2 for a computed quote escaped value based on # VARNAME. Any other value will be used directly. m4_define([_LT_DECL], [lt_if_append_uniq([lt_decl_varnames], [$2], [, ], [lt_dict_add_subkey([lt_decl_dict], [$2], [libtool_name], [m4_ifval([$1], [$1], [$2])]) lt_dict_add_subkey([lt_decl_dict], [$2], [value], [$3]) m4_ifval([$4], [lt_dict_add_subkey([lt_decl_dict], [$2], [description], [$4])]) lt_dict_add_subkey([lt_decl_dict], [$2], [tagged?], [m4_ifval([$5], [yes], [no])])]) ]) # _LT_TAGDECL([CONFIGNAME], VARNAME, VALUE, [DESCRIPTION]) # -------------------------------------------------------- m4_define([_LT_TAGDECL], [_LT_DECL([$1], [$2], [$3], [$4], [yes])]) # lt_decl_tag_varnames([SEPARATOR], [VARNAME1...]) # ------------------------------------------------ m4_define([lt_decl_tag_varnames], [_lt_decl_filter([tagged?], [yes], $@)]) # _lt_decl_filter(SUBKEY, VALUE, [SEPARATOR], [VARNAME1..]) # --------------------------------------------------------- m4_define([_lt_decl_filter], [m4_case([$#], [0], [m4_fatal([$0: too few arguments: $#])], [1], [m4_fatal([$0: too few arguments: $#: $1])], [2], [lt_dict_filter([lt_decl_dict], [$1], [$2], [], lt_decl_varnames)], [3], [lt_dict_filter([lt_decl_dict], [$1], [$2], [$3], lt_decl_varnames)], [lt_dict_filter([lt_decl_dict], $@)])[]dnl ]) # lt_decl_quote_varnames([SEPARATOR], [VARNAME1...]) # -------------------------------------------------- m4_define([lt_decl_quote_varnames], [_lt_decl_filter([value], [1], $@)]) # lt_decl_dquote_varnames([SEPARATOR], [VARNAME1...]) # --------------------------------------------------- m4_define([lt_decl_dquote_varnames], [_lt_decl_filter([value], [2], $@)]) # lt_decl_varnames_tagged([SEPARATOR], [VARNAME1...]) # --------------------------------------------------- m4_define([lt_decl_varnames_tagged], [m4_assert([$# <= 2])dnl _$0(m4_quote(m4_default([$1], [[, ]])), m4_ifval([$2], [[$2]], [m4_dquote(lt_decl_tag_varnames)]), m4_split(m4_normalize(m4_quote(_LT_TAGS)), [ ]))]) m4_define([_lt_decl_varnames_tagged], [m4_ifval([$3], [lt_combine([$1], [$2], [_], $3)])]) # lt_decl_all_varnames([SEPARATOR], [VARNAME1...]) # ------------------------------------------------ m4_define([lt_decl_all_varnames], [_$0(m4_quote(m4_default([$1], [[, ]])), m4_if([$2], [], m4_quote(lt_decl_varnames), m4_quote(m4_shift($@))))[]dnl ]) m4_define([_lt_decl_all_varnames], [lt_join($@, lt_decl_varnames_tagged([$1], lt_decl_tag_varnames([[, ]], m4_shift($@))))dnl ]) # _LT_CONFIG_STATUS_DECLARE([VARNAME]) # ------------------------------------ # Quote a variable value, and forward it to `config.status' so that its # declaration there will have the same value as in `configure'. VARNAME # must have a single quote delimited value for this to work. m4_define([_LT_CONFIG_STATUS_DECLARE], [$1='`$ECHO "$][$1" | $SED "$delay_single_quote_subst"`']) # _LT_CONFIG_STATUS_DECLARATIONS # ------------------------------ # We delimit libtool config variables with single quotes, so when # we write them to config.status, we have to be sure to quote all # embedded single quotes properly. In configure, this macro expands # each variable declared with _LT_DECL (and _LT_TAGDECL) into: # # ='`$ECHO "$" | $SED "$delay_single_quote_subst"`' m4_defun([_LT_CONFIG_STATUS_DECLARATIONS], [m4_foreach([_lt_var], m4_quote(lt_decl_all_varnames), [m4_n([_LT_CONFIG_STATUS_DECLARE(_lt_var)])])]) # _LT_LIBTOOL_TAGS # ---------------- # Output comment and list of tags supported by the script m4_defun([_LT_LIBTOOL_TAGS], [_LT_FORMAT_COMMENT([The names of the tagged configurations supported by this script])dnl available_tags="_LT_TAGS"dnl ]) # _LT_LIBTOOL_DECLARE(VARNAME, [TAG]) # ----------------------------------- # Extract the dictionary values for VARNAME (optionally with TAG) and # expand to a commented shell variable setting: # # # Some comment about what VAR is for. # visible_name=$lt_internal_name m4_define([_LT_LIBTOOL_DECLARE], [_LT_FORMAT_COMMENT(m4_quote(lt_dict_fetch([lt_decl_dict], [$1], [description])))[]dnl m4_pushdef([_libtool_name], m4_quote(lt_dict_fetch([lt_decl_dict], [$1], [libtool_name])))[]dnl m4_case(m4_quote(lt_dict_fetch([lt_decl_dict], [$1], [value])), [0], [_libtool_name=[$]$1], [1], [_libtool_name=$lt_[]$1], [2], [_libtool_name=$lt_[]$1], [_libtool_name=lt_dict_fetch([lt_decl_dict], [$1], [value])])[]dnl m4_ifval([$2], [_$2])[]m4_popdef([_libtool_name])[]dnl ]) # _LT_LIBTOOL_CONFIG_VARS # ----------------------- # Produce commented declarations of non-tagged libtool config variables # suitable for insertion in the LIBTOOL CONFIG section of the `libtool' # script. Tagged libtool config variables (even for the LIBTOOL CONFIG # section) are produced by _LT_LIBTOOL_TAG_VARS. m4_defun([_LT_LIBTOOL_CONFIG_VARS], [m4_foreach([_lt_var], m4_quote(_lt_decl_filter([tagged?], [no], [], lt_decl_varnames)), [m4_n([_LT_LIBTOOL_DECLARE(_lt_var)])])]) # _LT_LIBTOOL_TAG_VARS(TAG) # ------------------------- m4_define([_LT_LIBTOOL_TAG_VARS], [m4_foreach([_lt_var], m4_quote(lt_decl_tag_varnames), [m4_n([_LT_LIBTOOL_DECLARE(_lt_var, [$1])])])]) # _LT_TAGVAR(VARNAME, [TAGNAME]) # ------------------------------ m4_define([_LT_TAGVAR], [m4_ifval([$2], [$1_$2], [$1])]) # _LT_CONFIG_COMMANDS # ------------------- # Send accumulated output to $CONFIG_STATUS. Thanks to the lists of # variables for single and double quote escaping we saved from calls # to _LT_DECL, we can put quote escaped variables declarations # into `config.status', and then the shell code to quote escape them in # for loops in `config.status'. Finally, any additional code accumulated # from calls to _LT_CONFIG_LIBTOOL_INIT is expanded. m4_defun([_LT_CONFIG_COMMANDS], [AC_PROVIDE_IFELSE([LT_OUTPUT], dnl If the libtool generation code has been placed in $CONFIG_LT, dnl instead of duplicating it all over again into config.status, dnl then we will have config.status run $CONFIG_LT later, so it dnl needs to know what name is stored there: [AC_CONFIG_COMMANDS([libtool], [$SHELL $CONFIG_LT || AS_EXIT(1)], [CONFIG_LT='$CONFIG_LT'])], dnl If the libtool generation code is destined for config.status, dnl expand the accumulated commands and init code now: [AC_CONFIG_COMMANDS([libtool], [_LT_OUTPUT_LIBTOOL_COMMANDS], [_LT_OUTPUT_LIBTOOL_COMMANDS_INIT])]) ])#_LT_CONFIG_COMMANDS # Initialize. m4_define([_LT_OUTPUT_LIBTOOL_COMMANDS_INIT], [ # The HP-UX ksh and POSIX shell print the target directory to stdout # if CDPATH is set. (unset CDPATH) >/dev/null 2>&1 && unset CDPATH sed_quote_subst='$sed_quote_subst' double_quote_subst='$double_quote_subst' delay_variable_subst='$delay_variable_subst' _LT_CONFIG_STATUS_DECLARATIONS LTCC='$LTCC' LTCFLAGS='$LTCFLAGS' compiler='$compiler_DEFAULT' # A function that is used when there is no print builtin or printf. func_fallback_echo () { eval 'cat <<_LTECHO_EOF \$[]1 _LTECHO_EOF' } # Quote evaled strings. for var in lt_decl_all_varnames([[ \ ]], lt_decl_quote_varnames); do case \`eval \\\\\$ECHO \\\\""\\\\\$\$var"\\\\"\` in *[[\\\\\\\`\\"\\\$]]*) eval "lt_\$var=\\\\\\"\\\`\\\$ECHO \\"\\\$\$var\\" | \\\$SED \\"\\\$sed_quote_subst\\"\\\`\\\\\\"" ;; *) eval "lt_\$var=\\\\\\"\\\$\$var\\\\\\"" ;; esac done # Double-quote double-evaled strings. for var in lt_decl_all_varnames([[ \ ]], lt_decl_dquote_varnames); do case \`eval \\\\\$ECHO \\\\""\\\\\$\$var"\\\\"\` in *[[\\\\\\\`\\"\\\$]]*) eval "lt_\$var=\\\\\\"\\\`\\\$ECHO \\"\\\$\$var\\" | \\\$SED -e \\"\\\$double_quote_subst\\" -e \\"\\\$sed_quote_subst\\" -e \\"\\\$delay_variable_subst\\"\\\`\\\\\\"" ;; *) eval "lt_\$var=\\\\\\"\\\$\$var\\\\\\"" ;; esac done _LT_OUTPUT_LIBTOOL_INIT ]) # _LT_GENERATED_FILE_INIT(FILE, [COMMENT]) # ------------------------------------ # Generate a child script FILE with all initialization necessary to # reuse the environment learned by the parent script, and make the # file executable. If COMMENT is supplied, it is inserted after the # `#!' sequence but before initialization text begins. After this # macro, additional text can be appended to FILE to form the body of # the child script. The macro ends with non-zero status if the # file could not be fully written (such as if the disk is full). m4_ifdef([AS_INIT_GENERATED], [m4_defun([_LT_GENERATED_FILE_INIT],[AS_INIT_GENERATED($@)])], [m4_defun([_LT_GENERATED_FILE_INIT], [m4_require([AS_PREPARE])]dnl [m4_pushdef([AS_MESSAGE_LOG_FD])]dnl [lt_write_fail=0 cat >$1 <<_ASEOF || lt_write_fail=1 #! $SHELL # Generated by $as_me. $2 SHELL=\${CONFIG_SHELL-$SHELL} export SHELL _ASEOF cat >>$1 <<\_ASEOF || lt_write_fail=1 AS_SHELL_SANITIZE _AS_PREPARE exec AS_MESSAGE_FD>&1 _ASEOF test $lt_write_fail = 0 && chmod +x $1[]dnl m4_popdef([AS_MESSAGE_LOG_FD])])])# _LT_GENERATED_FILE_INIT # LT_OUTPUT # --------- # This macro allows early generation of the libtool script (before # AC_OUTPUT is called), incase it is used in configure for compilation # tests. AC_DEFUN([LT_OUTPUT], [: ${CONFIG_LT=./config.lt} AC_MSG_NOTICE([creating $CONFIG_LT]) _LT_GENERATED_FILE_INIT(["$CONFIG_LT"], [# Run this file to recreate a libtool stub with the current configuration.]) cat >>"$CONFIG_LT" <<\_LTEOF lt_cl_silent=false exec AS_MESSAGE_LOG_FD>>config.log { echo AS_BOX([Running $as_me.]) } >&AS_MESSAGE_LOG_FD lt_cl_help="\ \`$as_me' creates a local libtool stub from the current configuration, for use in further configure time tests before the real libtool is generated. Usage: $[0] [[OPTIONS]] -h, --help print this help, then exit -V, --version print version number, then exit -q, --quiet do not print progress messages -d, --debug don't remove temporary files Report bugs to ." lt_cl_version="\ m4_ifset([AC_PACKAGE_NAME], [AC_PACKAGE_NAME ])config.lt[]dnl m4_ifset([AC_PACKAGE_VERSION], [ AC_PACKAGE_VERSION]) configured by $[0], generated by m4_PACKAGE_STRING. Copyright (C) 2011 Free Software Foundation, Inc. This config.lt script is free software; the Free Software Foundation gives unlimited permision to copy, distribute and modify it." while test $[#] != 0 do case $[1] in --version | --v* | -V ) echo "$lt_cl_version"; exit 0 ;; --help | --h* | -h ) echo "$lt_cl_help"; exit 0 ;; --debug | --d* | -d ) debug=: ;; --quiet | --q* | --silent | --s* | -q ) lt_cl_silent=: ;; -*) AC_MSG_ERROR([unrecognized option: $[1] Try \`$[0] --help' for more information.]) ;; *) AC_MSG_ERROR([unrecognized argument: $[1] Try \`$[0] --help' for more information.]) ;; esac shift done if $lt_cl_silent; then exec AS_MESSAGE_FD>/dev/null fi _LTEOF cat >>"$CONFIG_LT" <<_LTEOF _LT_OUTPUT_LIBTOOL_COMMANDS_INIT _LTEOF cat >>"$CONFIG_LT" <<\_LTEOF AC_MSG_NOTICE([creating $ofile]) _LT_OUTPUT_LIBTOOL_COMMANDS AS_EXIT(0) _LTEOF chmod +x "$CONFIG_LT" # configure is writing to config.log, but config.lt does its own redirection, # appending to config.log, which fails on DOS, as config.log is still kept # open by configure. Here we exec the FD to /dev/null, effectively closing # config.log, so it can be properly (re)opened and appended to by config.lt. lt_cl_success=: test "$silent" = yes && lt_config_lt_args="$lt_config_lt_args --quiet" exec AS_MESSAGE_LOG_FD>/dev/null $SHELL "$CONFIG_LT" $lt_config_lt_args || lt_cl_success=false exec AS_MESSAGE_LOG_FD>>config.log $lt_cl_success || AS_EXIT(1) ])# LT_OUTPUT # _LT_CONFIG(TAG) # --------------- # If TAG is the built-in tag, create an initial libtool script with a # default configuration from the untagged config vars. Otherwise add code # to config.status for appending the configuration named by TAG from the # matching tagged config vars. m4_defun([_LT_CONFIG], [m4_require([_LT_FILEUTILS_DEFAULTS])dnl _LT_CONFIG_SAVE_COMMANDS([ m4_define([_LT_TAG], m4_if([$1], [], [C], [$1]))dnl m4_if(_LT_TAG, [C], [ # See if we are running on zsh, and set the options which allow our # commands through without removal of \ escapes. if test -n "${ZSH_VERSION+set}" ; then setopt NO_GLOB_SUBST fi cfgfile="${ofile}T" trap "$RM \"$cfgfile\"; exit 1" 1 2 15 $RM "$cfgfile" cat <<_LT_EOF >> "$cfgfile" #! $SHELL # `$ECHO "$ofile" | sed 's%^.*/%%'` - Provide generalized library-building support services. # Generated automatically by $as_me ($PACKAGE$TIMESTAMP) $VERSION # Libtool was configured on host `(hostname || uname -n) 2>/dev/null | sed 1q`: # NOTE: Changes made to this file will be lost: look at ltmain.sh. # _LT_COPYING _LT_LIBTOOL_TAGS # ### BEGIN LIBTOOL CONFIG _LT_LIBTOOL_CONFIG_VARS _LT_LIBTOOL_TAG_VARS # ### END LIBTOOL CONFIG _LT_EOF case $host_os in aix3*) cat <<\_LT_EOF >> "$cfgfile" # AIX sometimes has problems with the GCC collect2 program. For some # reason, if we set the COLLECT_NAMES environment variable, the problems # vanish in a puff of smoke. if test "X${COLLECT_NAMES+set}" != Xset; then COLLECT_NAMES= export COLLECT_NAMES fi _LT_EOF ;; esac _LT_PROG_LTMAIN # We use sed instead of cat because bash on DJGPP gets confused if # if finds mixed CR/LF and LF-only lines. Since sed operates in # text mode, it properly converts lines to CR/LF. This bash problem # is reportedly fixed, but why not run on old versions too? sed '$q' "$ltmain" >> "$cfgfile" \ || (rm -f "$cfgfile"; exit 1) _LT_PROG_REPLACE_SHELLFNS mv -f "$cfgfile" "$ofile" || (rm -f "$ofile" && cp "$cfgfile" "$ofile" && rm -f "$cfgfile") chmod +x "$ofile" ], [cat <<_LT_EOF >> "$ofile" dnl Unfortunately we have to use $1 here, since _LT_TAG is not expanded dnl in a comment (ie after a #). # ### BEGIN LIBTOOL TAG CONFIG: $1 _LT_LIBTOOL_TAG_VARS(_LT_TAG) # ### END LIBTOOL TAG CONFIG: $1 _LT_EOF ])dnl /m4_if ], [m4_if([$1], [], [ PACKAGE='$PACKAGE' VERSION='$VERSION' TIMESTAMP='$TIMESTAMP' RM='$RM' ofile='$ofile'], []) ])dnl /_LT_CONFIG_SAVE_COMMANDS ])# _LT_CONFIG # LT_SUPPORTED_TAG(TAG) # --------------------- # Trace this macro to discover what tags are supported by the libtool # --tag option, using: # autoconf --trace 'LT_SUPPORTED_TAG:$1' AC_DEFUN([LT_SUPPORTED_TAG], []) # C support is built-in for now m4_define([_LT_LANG_C_enabled], []) m4_define([_LT_TAGS], []) # LT_LANG(LANG) # ------------- # Enable libtool support for the given language if not already enabled. AC_DEFUN([LT_LANG], [AC_BEFORE([$0], [LT_OUTPUT])dnl m4_case([$1], [C], [_LT_LANG(C)], [C++], [_LT_LANG(CXX)], [Go], [_LT_LANG(GO)], [Java], [_LT_LANG(GCJ)], [Fortran 77], [_LT_LANG(F77)], [Fortran], [_LT_LANG(FC)], [Windows Resource], [_LT_LANG(RC)], [m4_ifdef([_LT_LANG_]$1[_CONFIG], [_LT_LANG($1)], [m4_fatal([$0: unsupported language: "$1"])])])dnl ])# LT_LANG # _LT_LANG(LANGNAME) # ------------------ m4_defun([_LT_LANG], [m4_ifdef([_LT_LANG_]$1[_enabled], [], [LT_SUPPORTED_TAG([$1])dnl m4_append([_LT_TAGS], [$1 ])dnl m4_define([_LT_LANG_]$1[_enabled], [])dnl _LT_LANG_$1_CONFIG($1)])dnl ])# _LT_LANG m4_ifndef([AC_PROG_GO], [ ############################################################ # NOTE: This macro has been submitted for inclusion into # # GNU Autoconf as AC_PROG_GO. When it is available in # # a released version of Autoconf we should remove this # # macro and use it instead. # ############################################################ m4_defun([AC_PROG_GO], [AC_LANG_PUSH(Go)dnl AC_ARG_VAR([GOC], [Go compiler command])dnl AC_ARG_VAR([GOFLAGS], [Go compiler flags])dnl _AC_ARG_VAR_LDFLAGS()dnl AC_CHECK_TOOL(GOC, gccgo) if test -z "$GOC"; then if test -n "$ac_tool_prefix"; then AC_CHECK_PROG(GOC, [${ac_tool_prefix}gccgo], [${ac_tool_prefix}gccgo]) fi fi if test -z "$GOC"; then AC_CHECK_PROG(GOC, gccgo, gccgo, false) fi ])#m4_defun ])#m4_ifndef # _LT_LANG_DEFAULT_CONFIG # ----------------------- m4_defun([_LT_LANG_DEFAULT_CONFIG], [AC_PROVIDE_IFELSE([AC_PROG_CXX], [LT_LANG(CXX)], [m4_define([AC_PROG_CXX], defn([AC_PROG_CXX])[LT_LANG(CXX)])]) AC_PROVIDE_IFELSE([AC_PROG_F77], [LT_LANG(F77)], [m4_define([AC_PROG_F77], defn([AC_PROG_F77])[LT_LANG(F77)])]) AC_PROVIDE_IFELSE([AC_PROG_FC], [LT_LANG(FC)], [m4_define([AC_PROG_FC], defn([AC_PROG_FC])[LT_LANG(FC)])]) dnl The call to [A][M_PROG_GCJ] is quoted like that to stop aclocal dnl pulling things in needlessly. AC_PROVIDE_IFELSE([AC_PROG_GCJ], [LT_LANG(GCJ)], [AC_PROVIDE_IFELSE([A][M_PROG_GCJ], [LT_LANG(GCJ)], [AC_PROVIDE_IFELSE([LT_PROG_GCJ], [LT_LANG(GCJ)], [m4_ifdef([AC_PROG_GCJ], [m4_define([AC_PROG_GCJ], defn([AC_PROG_GCJ])[LT_LANG(GCJ)])]) m4_ifdef([A][M_PROG_GCJ], [m4_define([A][M_PROG_GCJ], defn([A][M_PROG_GCJ])[LT_LANG(GCJ)])]) m4_ifdef([LT_PROG_GCJ], [m4_define([LT_PROG_GCJ], defn([LT_PROG_GCJ])[LT_LANG(GCJ)])])])])]) AC_PROVIDE_IFELSE([AC_PROG_GO], [LT_LANG(GO)], [m4_define([AC_PROG_GO], defn([AC_PROG_GO])[LT_LANG(GO)])]) AC_PROVIDE_IFELSE([LT_PROG_RC], [LT_LANG(RC)], [m4_define([LT_PROG_RC], defn([LT_PROG_RC])[LT_LANG(RC)])]) ])# _LT_LANG_DEFAULT_CONFIG # Obsolete macros: AU_DEFUN([AC_LIBTOOL_CXX], [LT_LANG(C++)]) AU_DEFUN([AC_LIBTOOL_F77], [LT_LANG(Fortran 77)]) AU_DEFUN([AC_LIBTOOL_FC], [LT_LANG(Fortran)]) AU_DEFUN([AC_LIBTOOL_GCJ], [LT_LANG(Java)]) AU_DEFUN([AC_LIBTOOL_RC], [LT_LANG(Windows Resource)]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([AC_LIBTOOL_CXX], []) dnl AC_DEFUN([AC_LIBTOOL_F77], []) dnl AC_DEFUN([AC_LIBTOOL_FC], []) dnl AC_DEFUN([AC_LIBTOOL_GCJ], []) dnl AC_DEFUN([AC_LIBTOOL_RC], []) # _LT_TAG_COMPILER # ---------------- m4_defun([_LT_TAG_COMPILER], [AC_REQUIRE([AC_PROG_CC])dnl _LT_DECL([LTCC], [CC], [1], [A C compiler])dnl _LT_DECL([LTCFLAGS], [CFLAGS], [1], [LTCC compiler flags])dnl _LT_TAGDECL([CC], [compiler], [1], [A language specific compiler])dnl _LT_TAGDECL([with_gcc], [GCC], [0], [Is the compiler the GNU compiler?])dnl # If no C compiler was specified, use CC. LTCC=${LTCC-"$CC"} # If no C compiler flags were specified, use CFLAGS. LTCFLAGS=${LTCFLAGS-"$CFLAGS"} # Allow CC to be a program name with arguments. compiler=$CC ])# _LT_TAG_COMPILER # _LT_COMPILER_BOILERPLATE # ------------------------ # Check for compiler boilerplate output or warnings with # the simple compiler test code. m4_defun([_LT_COMPILER_BOILERPLATE], [m4_require([_LT_DECL_SED])dnl ac_outfile=conftest.$ac_objext echo "$lt_simple_compile_test_code" >conftest.$ac_ext eval "$ac_compile" 2>&1 >/dev/null | $SED '/^$/d; /^ *+/d' >conftest.err _lt_compiler_boilerplate=`cat conftest.err` $RM conftest* ])# _LT_COMPILER_BOILERPLATE # _LT_LINKER_BOILERPLATE # ---------------------- # Check for linker boilerplate output or warnings with # the simple link test code. m4_defun([_LT_LINKER_BOILERPLATE], [m4_require([_LT_DECL_SED])dnl ac_outfile=conftest.$ac_objext echo "$lt_simple_link_test_code" >conftest.$ac_ext eval "$ac_link" 2>&1 >/dev/null | $SED '/^$/d; /^ *+/d' >conftest.err _lt_linker_boilerplate=`cat conftest.err` $RM -r conftest* ])# _LT_LINKER_BOILERPLATE # _LT_REQUIRED_DARWIN_CHECKS # ------------------------- m4_defun_once([_LT_REQUIRED_DARWIN_CHECKS],[ case $host_os in rhapsody* | darwin*) AC_CHECK_TOOL([DSYMUTIL], [dsymutil], [:]) AC_CHECK_TOOL([NMEDIT], [nmedit], [:]) AC_CHECK_TOOL([LIPO], [lipo], [:]) AC_CHECK_TOOL([OTOOL], [otool], [:]) AC_CHECK_TOOL([OTOOL64], [otool64], [:]) _LT_DECL([], [DSYMUTIL], [1], [Tool to manipulate archived DWARF debug symbol files on Mac OS X]) _LT_DECL([], [NMEDIT], [1], [Tool to change global to local symbols on Mac OS X]) _LT_DECL([], [LIPO], [1], [Tool to manipulate fat objects and archives on Mac OS X]) _LT_DECL([], [OTOOL], [1], [ldd/readelf like tool for Mach-O binaries on Mac OS X]) _LT_DECL([], [OTOOL64], [1], [ldd/readelf like tool for 64 bit Mach-O binaries on Mac OS X 10.4]) AC_CACHE_CHECK([for -single_module linker flag],[lt_cv_apple_cc_single_mod], [lt_cv_apple_cc_single_mod=no if test -z "${LT_MULTI_MODULE}"; then # By default we will add the -single_module flag. You can override # by either setting the environment variable LT_MULTI_MODULE # non-empty at configure time, or by adding -multi_module to the # link flags. rm -rf libconftest.dylib* echo "int foo(void){return 1;}" > conftest.c echo "$LTCC $LTCFLAGS $LDFLAGS -o libconftest.dylib \ -dynamiclib -Wl,-single_module conftest.c" >&AS_MESSAGE_LOG_FD $LTCC $LTCFLAGS $LDFLAGS -o libconftest.dylib \ -dynamiclib -Wl,-single_module conftest.c 2>conftest.err _lt_result=$? # If there is a non-empty error log, and "single_module" # appears in it, assume the flag caused a linker warning if test -s conftest.err && $GREP single_module conftest.err; then cat conftest.err >&AS_MESSAGE_LOG_FD # Otherwise, if the output was created with a 0 exit code from # the compiler, it worked. elif test -f libconftest.dylib && test $_lt_result -eq 0; then lt_cv_apple_cc_single_mod=yes else cat conftest.err >&AS_MESSAGE_LOG_FD fi rm -rf libconftest.dylib* rm -f conftest.* fi]) AC_CACHE_CHECK([for -exported_symbols_list linker flag], [lt_cv_ld_exported_symbols_list], [lt_cv_ld_exported_symbols_list=no save_LDFLAGS=$LDFLAGS echo "_main" > conftest.sym LDFLAGS="$LDFLAGS -Wl,-exported_symbols_list,conftest.sym" AC_LINK_IFELSE([AC_LANG_PROGRAM([],[])], [lt_cv_ld_exported_symbols_list=yes], [lt_cv_ld_exported_symbols_list=no]) LDFLAGS="$save_LDFLAGS" ]) AC_CACHE_CHECK([for -force_load linker flag],[lt_cv_ld_force_load], [lt_cv_ld_force_load=no cat > conftest.c << _LT_EOF int forced_loaded() { return 2;} _LT_EOF echo "$LTCC $LTCFLAGS -c -o conftest.o conftest.c" >&AS_MESSAGE_LOG_FD $LTCC $LTCFLAGS -c -o conftest.o conftest.c 2>&AS_MESSAGE_LOG_FD echo "$AR cru libconftest.a conftest.o" >&AS_MESSAGE_LOG_FD $AR cru libconftest.a conftest.o 2>&AS_MESSAGE_LOG_FD echo "$RANLIB libconftest.a" >&AS_MESSAGE_LOG_FD $RANLIB libconftest.a 2>&AS_MESSAGE_LOG_FD cat > conftest.c << _LT_EOF int main() { return 0;} _LT_EOF echo "$LTCC $LTCFLAGS $LDFLAGS -o conftest conftest.c -Wl,-force_load,./libconftest.a" >&AS_MESSAGE_LOG_FD $LTCC $LTCFLAGS $LDFLAGS -o conftest conftest.c -Wl,-force_load,./libconftest.a 2>conftest.err _lt_result=$? if test -s conftest.err && $GREP force_load conftest.err; then cat conftest.err >&AS_MESSAGE_LOG_FD elif test -f conftest && test $_lt_result -eq 0 && $GREP forced_load conftest >/dev/null 2>&1 ; then lt_cv_ld_force_load=yes else cat conftest.err >&AS_MESSAGE_LOG_FD fi rm -f conftest.err libconftest.a conftest conftest.c rm -rf conftest.dSYM ]) case $host_os in rhapsody* | darwin1.[[012]]) _lt_dar_allow_undefined='${wl}-undefined ${wl}suppress' ;; darwin1.*) _lt_dar_allow_undefined='${wl}-flat_namespace ${wl}-undefined ${wl}suppress' ;; darwin*) # darwin 5.x on # if running on 10.5 or later, the deployment target defaults # to the OS version, if on x86, and 10.4, the deployment # target defaults to 10.4. Don't you love it? case ${MACOSX_DEPLOYMENT_TARGET-10.0},$host in 10.0,*86*-darwin8*|10.0,*-darwin[[91]]*) _lt_dar_allow_undefined='${wl}-undefined ${wl}dynamic_lookup' ;; 10.[[012]]*) _lt_dar_allow_undefined='${wl}-flat_namespace ${wl}-undefined ${wl}suppress' ;; 10.*) _lt_dar_allow_undefined='${wl}-undefined ${wl}dynamic_lookup' ;; esac ;; esac if test "$lt_cv_apple_cc_single_mod" = "yes"; then _lt_dar_single_mod='$single_module' fi if test "$lt_cv_ld_exported_symbols_list" = "yes"; then _lt_dar_export_syms=' ${wl}-exported_symbols_list,$output_objdir/${libname}-symbols.expsym' else _lt_dar_export_syms='~$NMEDIT -s $output_objdir/${libname}-symbols.expsym ${lib}' fi if test "$DSYMUTIL" != ":" && test "$lt_cv_ld_force_load" = "no"; then _lt_dsymutil='~$DSYMUTIL $lib || :' else _lt_dsymutil= fi ;; esac ]) # _LT_DARWIN_LINKER_FEATURES([TAG]) # --------------------------------- # Checks for linker and compiler features on darwin m4_defun([_LT_DARWIN_LINKER_FEATURES], [ m4_require([_LT_REQUIRED_DARWIN_CHECKS]) _LT_TAGVAR(archive_cmds_need_lc, $1)=no _LT_TAGVAR(hardcode_direct, $1)=no _LT_TAGVAR(hardcode_automatic, $1)=yes _LT_TAGVAR(hardcode_shlibpath_var, $1)=unsupported if test "$lt_cv_ld_force_load" = "yes"; then _LT_TAGVAR(whole_archive_flag_spec, $1)='`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience ${wl}-force_load,$conv\"; done; func_echo_all \"$new_convenience\"`' m4_case([$1], [F77], [_LT_TAGVAR(compiler_needs_object, $1)=yes], [FC], [_LT_TAGVAR(compiler_needs_object, $1)=yes]) else _LT_TAGVAR(whole_archive_flag_spec, $1)='' fi _LT_TAGVAR(link_all_deplibs, $1)=yes _LT_TAGVAR(allow_undefined_flag, $1)="$_lt_dar_allow_undefined" case $cc_basename in ifort*) _lt_dar_can_shared=yes ;; *) _lt_dar_can_shared=$GCC ;; esac if test "$_lt_dar_can_shared" = "yes"; then output_verbose_link_cmd=func_echo_all _LT_TAGVAR(archive_cmds, $1)="\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring $_lt_dar_single_mod${_lt_dsymutil}" _LT_TAGVAR(module_cmds, $1)="\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dsymutil}" _LT_TAGVAR(archive_expsym_cmds, $1)="sed 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring ${_lt_dar_single_mod}${_lt_dar_export_syms}${_lt_dsymutil}" _LT_TAGVAR(module_expsym_cmds, $1)="sed -e 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dar_export_syms}${_lt_dsymutil}" m4_if([$1], [CXX], [ if test "$lt_cv_apple_cc_single_mod" != "yes"; then _LT_TAGVAR(archive_cmds, $1)="\$CC -r -keep_private_externs -nostdlib -o \${lib}-master.o \$libobjs~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \${lib}-master.o \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring${_lt_dsymutil}" _LT_TAGVAR(archive_expsym_cmds, $1)="sed 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC -r -keep_private_externs -nostdlib -o \${lib}-master.o \$libobjs~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \${lib}-master.o \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring${_lt_dar_export_syms}${_lt_dsymutil}" fi ],[]) else _LT_TAGVAR(ld_shlibs, $1)=no fi ]) # _LT_SYS_MODULE_PATH_AIX([TAGNAME]) # ---------------------------------- # Links a minimal program and checks the executable # for the system default hardcoded library path. In most cases, # this is /usr/lib:/lib, but when the MPI compilers are used # the location of the communication and MPI libs are included too. # If we don't find anything, use the default library path according # to the aix ld manual. # Store the results from the different compilers for each TAGNAME. # Allow to override them for all tags through lt_cv_aix_libpath. m4_defun([_LT_SYS_MODULE_PATH_AIX], [m4_require([_LT_DECL_SED])dnl if test "${lt_cv_aix_libpath+set}" = set; then aix_libpath=$lt_cv_aix_libpath else AC_CACHE_VAL([_LT_TAGVAR([lt_cv_aix_libpath_], [$1])], [AC_LINK_IFELSE([AC_LANG_PROGRAM],[ lt_aix_libpath_sed='[ /Import File Strings/,/^$/ { /^0/ { s/^0 *\([^ ]*\) *$/\1/ p } }]' _LT_TAGVAR([lt_cv_aix_libpath_], [$1])=`dump -H conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"` # Check for a 64-bit object if we didn't find anything. if test -z "$_LT_TAGVAR([lt_cv_aix_libpath_], [$1])"; then _LT_TAGVAR([lt_cv_aix_libpath_], [$1])=`dump -HX64 conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"` fi],[]) if test -z "$_LT_TAGVAR([lt_cv_aix_libpath_], [$1])"; then _LT_TAGVAR([lt_cv_aix_libpath_], [$1])="/usr/lib:/lib" fi ]) aix_libpath=$_LT_TAGVAR([lt_cv_aix_libpath_], [$1]) fi ])# _LT_SYS_MODULE_PATH_AIX # _LT_SHELL_INIT(ARG) # ------------------- m4_define([_LT_SHELL_INIT], [m4_divert_text([M4SH-INIT], [$1 ])])# _LT_SHELL_INIT # _LT_PROG_ECHO_BACKSLASH # ----------------------- # Find how we can fake an echo command that does not interpret backslash. # In particular, with Autoconf 2.60 or later we add some code to the start # of the generated configure script which will find a shell with a builtin # printf (which we can use as an echo command). m4_defun([_LT_PROG_ECHO_BACKSLASH], [ECHO='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' ECHO=$ECHO$ECHO$ECHO$ECHO$ECHO ECHO=$ECHO$ECHO$ECHO$ECHO$ECHO$ECHO AC_MSG_CHECKING([how to print strings]) # Test print first, because it will be a builtin if present. if test "X`( print -r -- -n ) 2>/dev/null`" = X-n && \ test "X`print -r -- $ECHO 2>/dev/null`" = "X$ECHO"; then ECHO='print -r --' elif test "X`printf %s $ECHO 2>/dev/null`" = "X$ECHO"; then ECHO='printf %s\n' else # Use this function as a fallback that always works. func_fallback_echo () { eval 'cat <<_LTECHO_EOF $[]1 _LTECHO_EOF' } ECHO='func_fallback_echo' fi # func_echo_all arg... # Invoke $ECHO with all args, space-separated. func_echo_all () { $ECHO "$*" } case "$ECHO" in printf*) AC_MSG_RESULT([printf]) ;; print*) AC_MSG_RESULT([print -r]) ;; *) AC_MSG_RESULT([cat]) ;; esac m4_ifdef([_AS_DETECT_SUGGESTED], [_AS_DETECT_SUGGESTED([ test -n "${ZSH_VERSION+set}${BASH_VERSION+set}" || ( ECHO='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' ECHO=$ECHO$ECHO$ECHO$ECHO$ECHO ECHO=$ECHO$ECHO$ECHO$ECHO$ECHO$ECHO PATH=/empty FPATH=/empty; export PATH FPATH test "X`printf %s $ECHO`" = "X$ECHO" \ || test "X`print -r -- $ECHO`" = "X$ECHO" )])]) _LT_DECL([], [SHELL], [1], [Shell to use when invoking shell scripts]) _LT_DECL([], [ECHO], [1], [An echo program that protects backslashes]) ])# _LT_PROG_ECHO_BACKSLASH # _LT_WITH_SYSROOT # ---------------- AC_DEFUN([_LT_WITH_SYSROOT], [AC_MSG_CHECKING([for sysroot]) AC_ARG_WITH([sysroot], [ --with-sysroot[=DIR] Search for dependent libraries within DIR (or the compiler's sysroot if not specified).], [], [with_sysroot=no]) dnl lt_sysroot will always be passed unquoted. We quote it here dnl in case the user passed a directory name. lt_sysroot= case ${with_sysroot} in #( yes) if test "$GCC" = yes; then lt_sysroot=`$CC --print-sysroot 2>/dev/null` fi ;; #( /*) lt_sysroot=`echo "$with_sysroot" | sed -e "$sed_quote_subst"` ;; #( no|'') ;; #( *) AC_MSG_RESULT([${with_sysroot}]) AC_MSG_ERROR([The sysroot must be an absolute path.]) ;; esac AC_MSG_RESULT([${lt_sysroot:-no}]) _LT_DECL([], [lt_sysroot], [0], [The root where to search for ]dnl [dependent libraries, and in which our libraries should be installed.])]) # _LT_ENABLE_LOCK # --------------- m4_defun([_LT_ENABLE_LOCK], [AC_ARG_ENABLE([libtool-lock], [AS_HELP_STRING([--disable-libtool-lock], [avoid locking (might break parallel builds)])]) test "x$enable_libtool_lock" != xno && enable_libtool_lock=yes # Some flags need to be propagated to the compiler or linker for good # libtool support. case $host in ia64-*-hpux*) # Find out which ABI we are using. echo 'int i;' > conftest.$ac_ext if AC_TRY_EVAL(ac_compile); then case `/usr/bin/file conftest.$ac_objext` in *ELF-32*) HPUX_IA64_MODE="32" ;; *ELF-64*) HPUX_IA64_MODE="64" ;; esac fi rm -rf conftest* ;; *-*-irix6*) # Find out which ABI we are using. echo '[#]line '$LINENO' "configure"' > conftest.$ac_ext if AC_TRY_EVAL(ac_compile); then if test "$lt_cv_prog_gnu_ld" = yes; then case `/usr/bin/file conftest.$ac_objext` in *32-bit*) LD="${LD-ld} -melf32bsmip" ;; *N32*) LD="${LD-ld} -melf32bmipn32" ;; *64-bit*) LD="${LD-ld} -melf64bmip" ;; esac else case `/usr/bin/file conftest.$ac_objext` in *32-bit*) LD="${LD-ld} -32" ;; *N32*) LD="${LD-ld} -n32" ;; *64-bit*) LD="${LD-ld} -64" ;; esac fi fi rm -rf conftest* ;; x86_64-*kfreebsd*-gnu|x86_64-*linux*|powerpc*-*linux*| \ s390*-*linux*|s390*-*tpf*|sparc*-*linux*) # Find out which ABI we are using. echo 'int i;' > conftest.$ac_ext if AC_TRY_EVAL(ac_compile); then case `/usr/bin/file conftest.o` in *32-bit*) case $host in x86_64-*kfreebsd*-gnu) LD="${LD-ld} -m elf_i386_fbsd" ;; x86_64-*linux*) case `/usr/bin/file conftest.o` in *x86-64*) LD="${LD-ld} -m elf32_x86_64" ;; *) LD="${LD-ld} -m elf_i386" ;; esac ;; powerpc64le-*) LD="${LD-ld} -m elf32lppclinux" ;; powerpc64-*) LD="${LD-ld} -m elf32ppclinux" ;; s390x-*linux*) LD="${LD-ld} -m elf_s390" ;; sparc64-*linux*) LD="${LD-ld} -m elf32_sparc" ;; esac ;; *64-bit*) case $host in x86_64-*kfreebsd*-gnu) LD="${LD-ld} -m elf_x86_64_fbsd" ;; x86_64-*linux*) LD="${LD-ld} -m elf_x86_64" ;; powerpcle-*) LD="${LD-ld} -m elf64lppc" ;; powerpc-*) LD="${LD-ld} -m elf64ppc" ;; s390*-*linux*|s390*-*tpf*) LD="${LD-ld} -m elf64_s390" ;; sparc*-*linux*) LD="${LD-ld} -m elf64_sparc" ;; esac ;; esac fi rm -rf conftest* ;; *-*-sco3.2v5*) # On SCO OpenServer 5, we need -belf to get full-featured binaries. SAVE_CFLAGS="$CFLAGS" CFLAGS="$CFLAGS -belf" AC_CACHE_CHECK([whether the C compiler needs -belf], lt_cv_cc_needs_belf, [AC_LANG_PUSH(C) AC_LINK_IFELSE([AC_LANG_PROGRAM([[]],[[]])],[lt_cv_cc_needs_belf=yes],[lt_cv_cc_needs_belf=no]) AC_LANG_POP]) if test x"$lt_cv_cc_needs_belf" != x"yes"; then # this is probably gcc 2.8.0, egcs 1.0 or newer; no need for -belf CFLAGS="$SAVE_CFLAGS" fi ;; *-*solaris*) # Find out which ABI we are using. echo 'int i;' > conftest.$ac_ext if AC_TRY_EVAL(ac_compile); then case `/usr/bin/file conftest.o` in *64-bit*) case $lt_cv_prog_gnu_ld in yes*) case $host in i?86-*-solaris*) LD="${LD-ld} -m elf_x86_64" ;; sparc*-*-solaris*) LD="${LD-ld} -m elf64_sparc" ;; esac # GNU ld 2.21 introduced _sol2 emulations. Use them if available. if ${LD-ld} -V | grep _sol2 >/dev/null 2>&1; then LD="${LD-ld}_sol2" fi ;; *) if ${LD-ld} -64 -r -o conftest2.o conftest.o >/dev/null 2>&1; then LD="${LD-ld} -64" fi ;; esac ;; esac fi rm -rf conftest* ;; esac need_locks="$enable_libtool_lock" ])# _LT_ENABLE_LOCK # _LT_PROG_AR # ----------- m4_defun([_LT_PROG_AR], [AC_CHECK_TOOLS(AR, [ar], false) : ${AR=ar} : ${AR_FLAGS=cru} _LT_DECL([], [AR], [1], [The archiver]) _LT_DECL([], [AR_FLAGS], [1], [Flags to create an archive]) AC_CACHE_CHECK([for archiver @FILE support], [lt_cv_ar_at_file], [lt_cv_ar_at_file=no AC_COMPILE_IFELSE([AC_LANG_PROGRAM], [echo conftest.$ac_objext > conftest.lst lt_ar_try='$AR $AR_FLAGS libconftest.a @conftest.lst >&AS_MESSAGE_LOG_FD' AC_TRY_EVAL([lt_ar_try]) if test "$ac_status" -eq 0; then # Ensure the archiver fails upon bogus file names. rm -f conftest.$ac_objext libconftest.a AC_TRY_EVAL([lt_ar_try]) if test "$ac_status" -ne 0; then lt_cv_ar_at_file=@ fi fi rm -f conftest.* libconftest.a ]) ]) if test "x$lt_cv_ar_at_file" = xno; then archiver_list_spec= else archiver_list_spec=$lt_cv_ar_at_file fi _LT_DECL([], [archiver_list_spec], [1], [How to feed a file listing to the archiver]) ])# _LT_PROG_AR # _LT_CMD_OLD_ARCHIVE # ------------------- m4_defun([_LT_CMD_OLD_ARCHIVE], [_LT_PROG_AR AC_CHECK_TOOL(STRIP, strip, :) test -z "$STRIP" && STRIP=: _LT_DECL([], [STRIP], [1], [A symbol stripping program]) AC_CHECK_TOOL(RANLIB, ranlib, :) test -z "$RANLIB" && RANLIB=: _LT_DECL([], [RANLIB], [1], [Commands used to install an old-style archive]) # Determine commands to create old-style static archives. old_archive_cmds='$AR $AR_FLAGS $oldlib$oldobjs' old_postinstall_cmds='chmod 644 $oldlib' old_postuninstall_cmds= if test -n "$RANLIB"; then case $host_os in openbsd*) old_postinstall_cmds="$old_postinstall_cmds~\$RANLIB -t \$tool_oldlib" ;; *) old_postinstall_cmds="$old_postinstall_cmds~\$RANLIB \$tool_oldlib" ;; esac old_archive_cmds="$old_archive_cmds~\$RANLIB \$tool_oldlib" fi case $host_os in darwin*) lock_old_archive_extraction=yes ;; *) lock_old_archive_extraction=no ;; esac _LT_DECL([], [old_postinstall_cmds], [2]) _LT_DECL([], [old_postuninstall_cmds], [2]) _LT_TAGDECL([], [old_archive_cmds], [2], [Commands used to build an old-style archive]) _LT_DECL([], [lock_old_archive_extraction], [0], [Whether to use a lock for old archive extraction]) ])# _LT_CMD_OLD_ARCHIVE # _LT_COMPILER_OPTION(MESSAGE, VARIABLE-NAME, FLAGS, # [OUTPUT-FILE], [ACTION-SUCCESS], [ACTION-FAILURE]) # ---------------------------------------------------------------- # Check whether the given compiler option works AC_DEFUN([_LT_COMPILER_OPTION], [m4_require([_LT_FILEUTILS_DEFAULTS])dnl m4_require([_LT_DECL_SED])dnl AC_CACHE_CHECK([$1], [$2], [$2=no m4_if([$4], , [ac_outfile=conftest.$ac_objext], [ac_outfile=$4]) echo "$lt_simple_compile_test_code" > conftest.$ac_ext lt_compiler_flag="$3" # Insert the option either (1) after the last *FLAGS variable, or # (2) before a word containing "conftest.", or (3) at the end. # Note that $ac_compile itself does not contain backslashes and begins # with a dollar sign (not a hyphen), so the echo should work correctly. # The option is referenced via a variable to avoid confusing sed. lt_compile=`echo "$ac_compile" | $SED \ -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ -e 's: [[^ ]]*conftest\.: $lt_compiler_flag&:; t' \ -e 's:$: $lt_compiler_flag:'` (eval echo "\"\$as_me:$LINENO: $lt_compile\"" >&AS_MESSAGE_LOG_FD) (eval "$lt_compile" 2>conftest.err) ac_status=$? cat conftest.err >&AS_MESSAGE_LOG_FD echo "$as_me:$LINENO: \$? = $ac_status" >&AS_MESSAGE_LOG_FD if (exit $ac_status) && test -s "$ac_outfile"; then # The compiler can only warn and ignore the option if not recognized # So say no if there are warnings other than the usual output. $ECHO "$_lt_compiler_boilerplate" | $SED '/^$/d' >conftest.exp $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2 if test ! -s conftest.er2 || diff conftest.exp conftest.er2 >/dev/null; then $2=yes fi fi $RM conftest* ]) if test x"[$]$2" = xyes; then m4_if([$5], , :, [$5]) else m4_if([$6], , :, [$6]) fi ])# _LT_COMPILER_OPTION # Old name: AU_ALIAS([AC_LIBTOOL_COMPILER_OPTION], [_LT_COMPILER_OPTION]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([AC_LIBTOOL_COMPILER_OPTION], []) # _LT_LINKER_OPTION(MESSAGE, VARIABLE-NAME, FLAGS, # [ACTION-SUCCESS], [ACTION-FAILURE]) # ---------------------------------------------------- # Check whether the given linker option works AC_DEFUN([_LT_LINKER_OPTION], [m4_require([_LT_FILEUTILS_DEFAULTS])dnl m4_require([_LT_DECL_SED])dnl AC_CACHE_CHECK([$1], [$2], [$2=no save_LDFLAGS="$LDFLAGS" LDFLAGS="$LDFLAGS $3" echo "$lt_simple_link_test_code" > conftest.$ac_ext if (eval $ac_link 2>conftest.err) && test -s conftest$ac_exeext; then # The linker can only warn and ignore the option if not recognized # So say no if there are warnings if test -s conftest.err; then # Append any errors to the config.log. cat conftest.err 1>&AS_MESSAGE_LOG_FD $ECHO "$_lt_linker_boilerplate" | $SED '/^$/d' > conftest.exp $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2 if diff conftest.exp conftest.er2 >/dev/null; then $2=yes fi else $2=yes fi fi $RM -r conftest* LDFLAGS="$save_LDFLAGS" ]) if test x"[$]$2" = xyes; then m4_if([$4], , :, [$4]) else m4_if([$5], , :, [$5]) fi ])# _LT_LINKER_OPTION # Old name: AU_ALIAS([AC_LIBTOOL_LINKER_OPTION], [_LT_LINKER_OPTION]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([AC_LIBTOOL_LINKER_OPTION], []) # LT_CMD_MAX_LEN #--------------- AC_DEFUN([LT_CMD_MAX_LEN], [AC_REQUIRE([AC_CANONICAL_HOST])dnl # find the maximum length of command line arguments AC_MSG_CHECKING([the maximum length of command line arguments]) AC_CACHE_VAL([lt_cv_sys_max_cmd_len], [dnl i=0 teststring="ABCD" case $build_os in msdosdjgpp*) # On DJGPP, this test can blow up pretty badly due to problems in libc # (any single argument exceeding 2000 bytes causes a buffer overrun # during glob expansion). Even if it were fixed, the result of this # check would be larger than it should be. lt_cv_sys_max_cmd_len=12288; # 12K is about right ;; gnu*) # Under GNU Hurd, this test is not required because there is # no limit to the length of command line arguments. # Libtool will interpret -1 as no limit whatsoever lt_cv_sys_max_cmd_len=-1; ;; cygwin* | mingw* | cegcc*) # On Win9x/ME, this test blows up -- it succeeds, but takes # about 5 minutes as the teststring grows exponentially. # Worse, since 9x/ME are not pre-emptively multitasking, # you end up with a "frozen" computer, even though with patience # the test eventually succeeds (with a max line length of 256k). # Instead, let's just punt: use the minimum linelength reported by # all of the supported platforms: 8192 (on NT/2K/XP). lt_cv_sys_max_cmd_len=8192; ;; mint*) # On MiNT this can take a long time and run out of memory. lt_cv_sys_max_cmd_len=8192; ;; amigaos*) # On AmigaOS with pdksh, this test takes hours, literally. # So we just punt and use a minimum line length of 8192. lt_cv_sys_max_cmd_len=8192; ;; netbsd* | freebsd* | openbsd* | darwin* | dragonfly*) # This has been around since 386BSD, at least. Likely further. if test -x /sbin/sysctl; then lt_cv_sys_max_cmd_len=`/sbin/sysctl -n kern.argmax` elif test -x /usr/sbin/sysctl; then lt_cv_sys_max_cmd_len=`/usr/sbin/sysctl -n kern.argmax` else lt_cv_sys_max_cmd_len=65536 # usable default for all BSDs fi # And add a safety zone lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 4` lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \* 3` ;; interix*) # We know the value 262144 and hardcode it with a safety zone (like BSD) lt_cv_sys_max_cmd_len=196608 ;; os2*) # The test takes a long time on OS/2. lt_cv_sys_max_cmd_len=8192 ;; osf*) # Dr. Hans Ekkehard Plesser reports seeing a kernel panic running configure # due to this test when exec_disable_arg_limit is 1 on Tru64. It is not # nice to cause kernel panics so lets avoid the loop below. # First set a reasonable default. lt_cv_sys_max_cmd_len=16384 # if test -x /sbin/sysconfig; then case `/sbin/sysconfig -q proc exec_disable_arg_limit` in *1*) lt_cv_sys_max_cmd_len=-1 ;; esac fi ;; sco3.2v5*) lt_cv_sys_max_cmd_len=102400 ;; sysv5* | sco5v6* | sysv4.2uw2*) kargmax=`grep ARG_MAX /etc/conf/cf.d/stune 2>/dev/null` if test -n "$kargmax"; then lt_cv_sys_max_cmd_len=`echo $kargmax | sed 's/.*[[ ]]//'` else lt_cv_sys_max_cmd_len=32768 fi ;; *) lt_cv_sys_max_cmd_len=`(getconf ARG_MAX) 2> /dev/null` if test -n "$lt_cv_sys_max_cmd_len" && \ test undefined != "$lt_cv_sys_max_cmd_len"; then lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 4` lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \* 3` else # Make teststring a little bigger before we do anything with it. # a 1K string should be a reasonable start. for i in 1 2 3 4 5 6 7 8 ; do teststring=$teststring$teststring done SHELL=${SHELL-${CONFIG_SHELL-/bin/sh}} # If test is not a shell built-in, we'll probably end up computing a # maximum length that is only half of the actual maximum length, but # we can't tell. while { test "X"`env echo "$teststring$teststring" 2>/dev/null` \ = "X$teststring$teststring"; } >/dev/null 2>&1 && test $i != 17 # 1/2 MB should be enough do i=`expr $i + 1` teststring=$teststring$teststring done # Only check the string length outside the loop. lt_cv_sys_max_cmd_len=`expr "X$teststring" : ".*" 2>&1` teststring= # Add a significant safety factor because C++ compilers can tack on # massive amounts of additional arguments before passing them to the # linker. It appears as though 1/2 is a usable value. lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 2` fi ;; esac ]) if test -n $lt_cv_sys_max_cmd_len ; then AC_MSG_RESULT($lt_cv_sys_max_cmd_len) else AC_MSG_RESULT(none) fi max_cmd_len=$lt_cv_sys_max_cmd_len _LT_DECL([], [max_cmd_len], [0], [What is the maximum length of a command?]) ])# LT_CMD_MAX_LEN # Old name: AU_ALIAS([AC_LIBTOOL_SYS_MAX_CMD_LEN], [LT_CMD_MAX_LEN]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([AC_LIBTOOL_SYS_MAX_CMD_LEN], []) # _LT_HEADER_DLFCN # ---------------- m4_defun([_LT_HEADER_DLFCN], [AC_CHECK_HEADERS([dlfcn.h], [], [], [AC_INCLUDES_DEFAULT])dnl ])# _LT_HEADER_DLFCN # _LT_TRY_DLOPEN_SELF (ACTION-IF-TRUE, ACTION-IF-TRUE-W-USCORE, # ACTION-IF-FALSE, ACTION-IF-CROSS-COMPILING) # ---------------------------------------------------------------- m4_defun([_LT_TRY_DLOPEN_SELF], [m4_require([_LT_HEADER_DLFCN])dnl if test "$cross_compiling" = yes; then : [$4] else lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 lt_status=$lt_dlunknown cat > conftest.$ac_ext <<_LT_EOF [#line $LINENO "configure" #include "confdefs.h" #if HAVE_DLFCN_H #include #endif #include #ifdef RTLD_GLOBAL # define LT_DLGLOBAL RTLD_GLOBAL #else # ifdef DL_GLOBAL # define LT_DLGLOBAL DL_GLOBAL # else # define LT_DLGLOBAL 0 # endif #endif /* We may have to define LT_DLLAZY_OR_NOW in the command line if we find out it does not work in some platform. */ #ifndef LT_DLLAZY_OR_NOW # ifdef RTLD_LAZY # define LT_DLLAZY_OR_NOW RTLD_LAZY # else # ifdef DL_LAZY # define LT_DLLAZY_OR_NOW DL_LAZY # else # ifdef RTLD_NOW # define LT_DLLAZY_OR_NOW RTLD_NOW # else # ifdef DL_NOW # define LT_DLLAZY_OR_NOW DL_NOW # else # define LT_DLLAZY_OR_NOW 0 # endif # endif # endif # endif #endif /* When -fvisbility=hidden is used, assume the code has been annotated correspondingly for the symbols needed. */ #if defined(__GNUC__) && (((__GNUC__ == 3) && (__GNUC_MINOR__ >= 3)) || (__GNUC__ > 3)) int fnord () __attribute__((visibility("default"))); #endif int fnord () { return 42; } int main () { void *self = dlopen (0, LT_DLGLOBAL|LT_DLLAZY_OR_NOW); int status = $lt_dlunknown; if (self) { if (dlsym (self,"fnord")) status = $lt_dlno_uscore; else { if (dlsym( self,"_fnord")) status = $lt_dlneed_uscore; else puts (dlerror ()); } /* dlclose (self); */ } else puts (dlerror ()); return status; }] _LT_EOF if AC_TRY_EVAL(ac_link) && test -s conftest${ac_exeext} 2>/dev/null; then (./conftest; exit; ) >&AS_MESSAGE_LOG_FD 2>/dev/null lt_status=$? case x$lt_status in x$lt_dlno_uscore) $1 ;; x$lt_dlneed_uscore) $2 ;; x$lt_dlunknown|x*) $3 ;; esac else : # compilation failed $3 fi fi rm -fr conftest* ])# _LT_TRY_DLOPEN_SELF # LT_SYS_DLOPEN_SELF # ------------------ AC_DEFUN([LT_SYS_DLOPEN_SELF], [m4_require([_LT_HEADER_DLFCN])dnl if test "x$enable_dlopen" != xyes; then enable_dlopen=unknown enable_dlopen_self=unknown enable_dlopen_self_static=unknown else lt_cv_dlopen=no lt_cv_dlopen_libs= case $host_os in beos*) lt_cv_dlopen="load_add_on" lt_cv_dlopen_libs= lt_cv_dlopen_self=yes ;; mingw* | pw32* | cegcc*) lt_cv_dlopen="LoadLibrary" lt_cv_dlopen_libs= ;; cygwin*) lt_cv_dlopen="dlopen" lt_cv_dlopen_libs= ;; darwin*) # if libdl is installed we need to link against it AC_CHECK_LIB([dl], [dlopen], [lt_cv_dlopen="dlopen" lt_cv_dlopen_libs="-ldl"],[ lt_cv_dlopen="dyld" lt_cv_dlopen_libs= lt_cv_dlopen_self=yes ]) ;; *) AC_CHECK_FUNC([shl_load], [lt_cv_dlopen="shl_load"], [AC_CHECK_LIB([dld], [shl_load], [lt_cv_dlopen="shl_load" lt_cv_dlopen_libs="-ldld"], [AC_CHECK_FUNC([dlopen], [lt_cv_dlopen="dlopen"], [AC_CHECK_LIB([dl], [dlopen], [lt_cv_dlopen="dlopen" lt_cv_dlopen_libs="-ldl"], [AC_CHECK_LIB([svld], [dlopen], [lt_cv_dlopen="dlopen" lt_cv_dlopen_libs="-lsvld"], [AC_CHECK_LIB([dld], [dld_link], [lt_cv_dlopen="dld_link" lt_cv_dlopen_libs="-ldld"]) ]) ]) ]) ]) ]) ;; esac if test "x$lt_cv_dlopen" != xno; then enable_dlopen=yes else enable_dlopen=no fi case $lt_cv_dlopen in dlopen) save_CPPFLAGS="$CPPFLAGS" test "x$ac_cv_header_dlfcn_h" = xyes && CPPFLAGS="$CPPFLAGS -DHAVE_DLFCN_H" save_LDFLAGS="$LDFLAGS" wl=$lt_prog_compiler_wl eval LDFLAGS=\"\$LDFLAGS $export_dynamic_flag_spec\" save_LIBS="$LIBS" LIBS="$lt_cv_dlopen_libs $LIBS" AC_CACHE_CHECK([whether a program can dlopen itself], lt_cv_dlopen_self, [dnl _LT_TRY_DLOPEN_SELF( lt_cv_dlopen_self=yes, lt_cv_dlopen_self=yes, lt_cv_dlopen_self=no, lt_cv_dlopen_self=cross) ]) if test "x$lt_cv_dlopen_self" = xyes; then wl=$lt_prog_compiler_wl eval LDFLAGS=\"\$LDFLAGS $lt_prog_compiler_static\" AC_CACHE_CHECK([whether a statically linked program can dlopen itself], lt_cv_dlopen_self_static, [dnl _LT_TRY_DLOPEN_SELF( lt_cv_dlopen_self_static=yes, lt_cv_dlopen_self_static=yes, lt_cv_dlopen_self_static=no, lt_cv_dlopen_self_static=cross) ]) fi CPPFLAGS="$save_CPPFLAGS" LDFLAGS="$save_LDFLAGS" LIBS="$save_LIBS" ;; esac case $lt_cv_dlopen_self in yes|no) enable_dlopen_self=$lt_cv_dlopen_self ;; *) enable_dlopen_self=unknown ;; esac case $lt_cv_dlopen_self_static in yes|no) enable_dlopen_self_static=$lt_cv_dlopen_self_static ;; *) enable_dlopen_self_static=unknown ;; esac fi _LT_DECL([dlopen_support], [enable_dlopen], [0], [Whether dlopen is supported]) _LT_DECL([dlopen_self], [enable_dlopen_self], [0], [Whether dlopen of programs is supported]) _LT_DECL([dlopen_self_static], [enable_dlopen_self_static], [0], [Whether dlopen of statically linked programs is supported]) ])# LT_SYS_DLOPEN_SELF # Old name: AU_ALIAS([AC_LIBTOOL_DLOPEN_SELF], [LT_SYS_DLOPEN_SELF]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([AC_LIBTOOL_DLOPEN_SELF], []) # _LT_COMPILER_C_O([TAGNAME]) # --------------------------- # Check to see if options -c and -o are simultaneously supported by compiler. # This macro does not hard code the compiler like AC_PROG_CC_C_O. m4_defun([_LT_COMPILER_C_O], [m4_require([_LT_DECL_SED])dnl m4_require([_LT_FILEUTILS_DEFAULTS])dnl m4_require([_LT_TAG_COMPILER])dnl AC_CACHE_CHECK([if $compiler supports -c -o file.$ac_objext], [_LT_TAGVAR(lt_cv_prog_compiler_c_o, $1)], [_LT_TAGVAR(lt_cv_prog_compiler_c_o, $1)=no $RM -r conftest 2>/dev/null mkdir conftest cd conftest mkdir out echo "$lt_simple_compile_test_code" > conftest.$ac_ext lt_compiler_flag="-o out/conftest2.$ac_objext" # Insert the option either (1) after the last *FLAGS variable, or # (2) before a word containing "conftest.", or (3) at the end. # Note that $ac_compile itself does not contain backslashes and begins # with a dollar sign (not a hyphen), so the echo should work correctly. lt_compile=`echo "$ac_compile" | $SED \ -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ -e 's: [[^ ]]*conftest\.: $lt_compiler_flag&:; t' \ -e 's:$: $lt_compiler_flag:'` (eval echo "\"\$as_me:$LINENO: $lt_compile\"" >&AS_MESSAGE_LOG_FD) (eval "$lt_compile" 2>out/conftest.err) ac_status=$? cat out/conftest.err >&AS_MESSAGE_LOG_FD echo "$as_me:$LINENO: \$? = $ac_status" >&AS_MESSAGE_LOG_FD if (exit $ac_status) && test -s out/conftest2.$ac_objext then # The compiler can only warn and ignore the option if not recognized # So say no if there are warnings $ECHO "$_lt_compiler_boilerplate" | $SED '/^$/d' > out/conftest.exp $SED '/^$/d; /^ *+/d' out/conftest.err >out/conftest.er2 if test ! -s out/conftest.er2 || diff out/conftest.exp out/conftest.er2 >/dev/null; then _LT_TAGVAR(lt_cv_prog_compiler_c_o, $1)=yes fi fi chmod u+w . 2>&AS_MESSAGE_LOG_FD $RM conftest* # SGI C++ compiler will create directory out/ii_files/ for # template instantiation test -d out/ii_files && $RM out/ii_files/* && rmdir out/ii_files $RM out/* && rmdir out cd .. $RM -r conftest $RM conftest* ]) _LT_TAGDECL([compiler_c_o], [lt_cv_prog_compiler_c_o], [1], [Does compiler simultaneously support -c and -o options?]) ])# _LT_COMPILER_C_O # _LT_COMPILER_FILE_LOCKS([TAGNAME]) # ---------------------------------- # Check to see if we can do hard links to lock some files if needed m4_defun([_LT_COMPILER_FILE_LOCKS], [m4_require([_LT_ENABLE_LOCK])dnl m4_require([_LT_FILEUTILS_DEFAULTS])dnl _LT_COMPILER_C_O([$1]) hard_links="nottested" if test "$_LT_TAGVAR(lt_cv_prog_compiler_c_o, $1)" = no && test "$need_locks" != no; then # do not overwrite the value of need_locks provided by the user AC_MSG_CHECKING([if we can lock with hard links]) hard_links=yes $RM conftest* ln conftest.a conftest.b 2>/dev/null && hard_links=no touch conftest.a ln conftest.a conftest.b 2>&5 || hard_links=no ln conftest.a conftest.b 2>/dev/null && hard_links=no AC_MSG_RESULT([$hard_links]) if test "$hard_links" = no; then AC_MSG_WARN([`$CC' does not support `-c -o', so `make -j' may be unsafe]) need_locks=warn fi else need_locks=no fi _LT_DECL([], [need_locks], [1], [Must we lock files when doing compilation?]) ])# _LT_COMPILER_FILE_LOCKS # _LT_CHECK_OBJDIR # ---------------- m4_defun([_LT_CHECK_OBJDIR], [AC_CACHE_CHECK([for objdir], [lt_cv_objdir], [rm -f .libs 2>/dev/null mkdir .libs 2>/dev/null if test -d .libs; then lt_cv_objdir=.libs else # MS-DOS does not allow filenames that begin with a dot. lt_cv_objdir=_libs fi rmdir .libs 2>/dev/null]) objdir=$lt_cv_objdir _LT_DECL([], [objdir], [0], [The name of the directory that contains temporary libtool files])dnl m4_pattern_allow([LT_OBJDIR])dnl AC_DEFINE_UNQUOTED(LT_OBJDIR, "$lt_cv_objdir/", [Define to the sub-directory in which libtool stores uninstalled libraries.]) ])# _LT_CHECK_OBJDIR # _LT_LINKER_HARDCODE_LIBPATH([TAGNAME]) # -------------------------------------- # Check hardcoding attributes. m4_defun([_LT_LINKER_HARDCODE_LIBPATH], [AC_MSG_CHECKING([how to hardcode library paths into programs]) _LT_TAGVAR(hardcode_action, $1)= if test -n "$_LT_TAGVAR(hardcode_libdir_flag_spec, $1)" || test -n "$_LT_TAGVAR(runpath_var, $1)" || test "X$_LT_TAGVAR(hardcode_automatic, $1)" = "Xyes" ; then # We can hardcode non-existent directories. if test "$_LT_TAGVAR(hardcode_direct, $1)" != no && # If the only mechanism to avoid hardcoding is shlibpath_var, we # have to relink, otherwise we might link with an installed library # when we should be linking with a yet-to-be-installed one ## test "$_LT_TAGVAR(hardcode_shlibpath_var, $1)" != no && test "$_LT_TAGVAR(hardcode_minus_L, $1)" != no; then # Linking always hardcodes the temporary library directory. _LT_TAGVAR(hardcode_action, $1)=relink else # We can link without hardcoding, and we can hardcode nonexisting dirs. _LT_TAGVAR(hardcode_action, $1)=immediate fi else # We cannot hardcode anything, or else we can only hardcode existing # directories. _LT_TAGVAR(hardcode_action, $1)=unsupported fi AC_MSG_RESULT([$_LT_TAGVAR(hardcode_action, $1)]) if test "$_LT_TAGVAR(hardcode_action, $1)" = relink || test "$_LT_TAGVAR(inherit_rpath, $1)" = yes; then # Fast installation is not supported enable_fast_install=no elif test "$shlibpath_overrides_runpath" = yes || test "$enable_shared" = no; then # Fast installation is not necessary enable_fast_install=needless fi _LT_TAGDECL([], [hardcode_action], [0], [How to hardcode a shared library path into an executable]) ])# _LT_LINKER_HARDCODE_LIBPATH # _LT_CMD_STRIPLIB # ---------------- m4_defun([_LT_CMD_STRIPLIB], [m4_require([_LT_DECL_EGREP]) striplib= old_striplib= AC_MSG_CHECKING([whether stripping libraries is possible]) if test -n "$STRIP" && $STRIP -V 2>&1 | $GREP "GNU strip" >/dev/null; then test -z "$old_striplib" && old_striplib="$STRIP --strip-debug" test -z "$striplib" && striplib="$STRIP --strip-unneeded" AC_MSG_RESULT([yes]) else # FIXME - insert some real tests, host_os isn't really good enough case $host_os in darwin*) if test -n "$STRIP" ; then striplib="$STRIP -x" old_striplib="$STRIP -S" AC_MSG_RESULT([yes]) else AC_MSG_RESULT([no]) fi ;; *) AC_MSG_RESULT([no]) ;; esac fi _LT_DECL([], [old_striplib], [1], [Commands to strip libraries]) _LT_DECL([], [striplib], [1]) ])# _LT_CMD_STRIPLIB # _LT_SYS_DYNAMIC_LINKER([TAG]) # ----------------------------- # PORTME Fill in your ld.so characteristics m4_defun([_LT_SYS_DYNAMIC_LINKER], [AC_REQUIRE([AC_CANONICAL_HOST])dnl m4_require([_LT_DECL_EGREP])dnl m4_require([_LT_FILEUTILS_DEFAULTS])dnl m4_require([_LT_DECL_OBJDUMP])dnl m4_require([_LT_DECL_SED])dnl m4_require([_LT_CHECK_SHELL_FEATURES])dnl AC_MSG_CHECKING([dynamic linker characteristics]) m4_if([$1], [], [ if test "$GCC" = yes; then case $host_os in darwin*) lt_awk_arg="/^libraries:/,/LR/" ;; *) lt_awk_arg="/^libraries:/" ;; esac case $host_os in mingw* | cegcc*) lt_sed_strip_eq="s,=\([[A-Za-z]]:\),\1,g" ;; *) lt_sed_strip_eq="s,=/,/,g" ;; esac lt_search_path_spec=`$CC -print-search-dirs | awk $lt_awk_arg | $SED -e "s/^libraries://" -e $lt_sed_strip_eq` case $lt_search_path_spec in *\;*) # if the path contains ";" then we assume it to be the separator # otherwise default to the standard path separator (i.e. ":") - it is # assumed that no part of a normal pathname contains ";" but that should # okay in the real world where ";" in dirpaths is itself problematic. lt_search_path_spec=`$ECHO "$lt_search_path_spec" | $SED 's/;/ /g'` ;; *) lt_search_path_spec=`$ECHO "$lt_search_path_spec" | $SED "s/$PATH_SEPARATOR/ /g"` ;; esac # Ok, now we have the path, separated by spaces, we can step through it # and add multilib dir if necessary. lt_tmp_lt_search_path_spec= lt_multi_os_dir=`$CC $CPPFLAGS $CFLAGS $LDFLAGS -print-multi-os-directory 2>/dev/null` for lt_sys_path in $lt_search_path_spec; do if test -d "$lt_sys_path/$lt_multi_os_dir"; then lt_tmp_lt_search_path_spec="$lt_tmp_lt_search_path_spec $lt_sys_path/$lt_multi_os_dir" else test -d "$lt_sys_path" && \ lt_tmp_lt_search_path_spec="$lt_tmp_lt_search_path_spec $lt_sys_path" fi done lt_search_path_spec=`$ECHO "$lt_tmp_lt_search_path_spec" | awk ' BEGIN {RS=" "; FS="/|\n";} { lt_foo=""; lt_count=0; for (lt_i = NF; lt_i > 0; lt_i--) { if ($lt_i != "" && $lt_i != ".") { if ($lt_i == "..") { lt_count++; } else { if (lt_count == 0) { lt_foo="/" $lt_i lt_foo; } else { lt_count--; } } } } if (lt_foo != "") { lt_freq[[lt_foo]]++; } if (lt_freq[[lt_foo]] == 1) { print lt_foo; } }'` # AWK program above erroneously prepends '/' to C:/dos/paths # for these hosts. case $host_os in mingw* | cegcc*) lt_search_path_spec=`$ECHO "$lt_search_path_spec" |\ $SED 's,/\([[A-Za-z]]:\),\1,g'` ;; esac sys_lib_search_path_spec=`$ECHO "$lt_search_path_spec" | $lt_NL2SP` else sys_lib_search_path_spec="/lib /usr/lib /usr/local/lib" fi]) library_names_spec= libname_spec='lib$name' soname_spec= shrext_cmds=".so" postinstall_cmds= postuninstall_cmds= finish_cmds= finish_eval= shlibpath_var= shlibpath_overrides_runpath=unknown version_type=none dynamic_linker="$host_os ld.so" sys_lib_dlsearch_path_spec="/lib /usr/lib" need_lib_prefix=unknown hardcode_into_libs=no # when you set need_version to no, make sure it does not cause -set_version # flags to be left without arguments need_version=unknown case $host_os in aix3*) version_type=linux # correct to gnu/linux during the next big refactor library_names_spec='${libname}${release}${shared_ext}$versuffix $libname.a' shlibpath_var=LIBPATH # AIX 3 has no versioning support, so we append a major version to the name. soname_spec='${libname}${release}${shared_ext}$major' ;; aix[[4-9]]*) version_type=linux # correct to gnu/linux during the next big refactor need_lib_prefix=no need_version=no hardcode_into_libs=yes if test "$host_cpu" = ia64; then # AIX 5 supports IA64 library_names_spec='${libname}${release}${shared_ext}$major ${libname}${release}${shared_ext}$versuffix $libname${shared_ext}' shlibpath_var=LD_LIBRARY_PATH else # With GCC up to 2.95.x, collect2 would create an import file # for dependence libraries. The import file would start with # the line `#! .'. This would cause the generated library to # depend on `.', always an invalid library. This was fixed in # development snapshots of GCC prior to 3.0. case $host_os in aix4 | aix4.[[01]] | aix4.[[01]].*) if { echo '#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 97)' echo ' yes ' echo '#endif'; } | ${CC} -E - | $GREP yes > /dev/null; then : else can_build_shared=no fi ;; esac # AIX (on Power*) has no versioning support, so currently we can not hardcode correct # soname into executable. Probably we can add versioning support to # collect2, so additional links can be useful in future. if test "$aix_use_runtimelinking" = yes; then # If using run time linking (on AIX 4.2 or later) use lib.so # instead of lib.a to let people know that these are not # typical AIX shared libraries. library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' else # We preserve .a as extension for shared libraries through AIX4.2 # and later when we are not doing run time linking. library_names_spec='${libname}${release}.a $libname.a' soname_spec='${libname}${release}${shared_ext}$major' fi shlibpath_var=LIBPATH fi ;; amigaos*) case $host_cpu in powerpc) # Since July 2007 AmigaOS4 officially supports .so libraries. # When compiling the executable, add -use-dynld -Lsobjs: to the compileline. library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' ;; m68k) library_names_spec='$libname.ixlibrary $libname.a' # Create ${libname}_ixlibrary.a entries in /sys/libs. finish_eval='for lib in `ls $libdir/*.ixlibrary 2>/dev/null`; do libname=`func_echo_all "$lib" | $SED '\''s%^.*/\([[^/]]*\)\.ixlibrary$%\1%'\''`; test $RM /sys/libs/${libname}_ixlibrary.a; $show "cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a"; cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a || exit 1; done' ;; esac ;; beos*) library_names_spec='${libname}${shared_ext}' dynamic_linker="$host_os ld.so" shlibpath_var=LIBRARY_PATH ;; bsdi[[45]]*) version_type=linux # correct to gnu/linux during the next big refactor need_version=no library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' soname_spec='${libname}${release}${shared_ext}$major' finish_cmds='PATH="\$PATH:/sbin" ldconfig $libdir' shlibpath_var=LD_LIBRARY_PATH sys_lib_search_path_spec="/shlib /usr/lib /usr/X11/lib /usr/contrib/lib /lib /usr/local/lib" sys_lib_dlsearch_path_spec="/shlib /usr/lib /usr/local/lib" # the default ld.so.conf also contains /usr/contrib/lib and # /usr/X11R6/lib (/usr/X11 is a link to /usr/X11R6), but let us allow # libtool to hard-code these into programs ;; cygwin* | mingw* | pw32* | cegcc*) version_type=windows shrext_cmds=".dll" need_version=no need_lib_prefix=no case $GCC,$cc_basename in yes,*) # gcc library_names_spec='$libname.dll.a' # DLL is installed to $(libdir)/../bin by postinstall_cmds postinstall_cmds='base_file=`basename \${file}`~ dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\${base_file}'\''i; echo \$dlname'\''`~ dldir=$destdir/`dirname \$dlpath`~ test -d \$dldir || mkdir -p \$dldir~ $install_prog $dir/$dlname \$dldir/$dlname~ chmod a+x \$dldir/$dlname~ if test -n '\''$stripme'\'' && test -n '\''$striplib'\''; then eval '\''$striplib \$dldir/$dlname'\'' || exit \$?; fi' postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; echo \$dlname'\''`~ dlpath=$dir/\$dldll~ $RM \$dlpath' shlibpath_overrides_runpath=yes case $host_os in cygwin*) # Cygwin DLLs use 'cyg' prefix rather than 'lib' soname_spec='`echo ${libname} | sed -e 's/^lib/cyg/'``echo ${release} | $SED -e 's/[[.]]/-/g'`${versuffix}${shared_ext}' m4_if([$1], [],[ sys_lib_search_path_spec="$sys_lib_search_path_spec /usr/lib/w32api"]) ;; mingw* | cegcc*) # MinGW DLLs use traditional 'lib' prefix soname_spec='${libname}`echo ${release} | $SED -e 's/[[.]]/-/g'`${versuffix}${shared_ext}' ;; pw32*) # pw32 DLLs use 'pw' prefix rather than 'lib' library_names_spec='`echo ${libname} | sed -e 's/^lib/pw/'``echo ${release} | $SED -e 's/[[.]]/-/g'`${versuffix}${shared_ext}' ;; esac dynamic_linker='Win32 ld.exe' ;; *,cl*) # Native MSVC libname_spec='$name' soname_spec='${libname}`echo ${release} | $SED -e 's/[[.]]/-/g'`${versuffix}${shared_ext}' library_names_spec='${libname}.dll.lib' case $build_os in mingw*) sys_lib_search_path_spec= lt_save_ifs=$IFS IFS=';' for lt_path in $LIB do IFS=$lt_save_ifs # Let DOS variable expansion print the short 8.3 style file name. lt_path=`cd "$lt_path" 2>/dev/null && cmd //C "for %i in (".") do @echo %~si"` sys_lib_search_path_spec="$sys_lib_search_path_spec $lt_path" done IFS=$lt_save_ifs # Convert to MSYS style. sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | sed -e 's|\\\\|/|g' -e 's| \\([[a-zA-Z]]\\):| /\\1|g' -e 's|^ ||'` ;; cygwin*) # Convert to unix form, then to dos form, then back to unix form # but this time dos style (no spaces!) so that the unix form looks # like /cygdrive/c/PROGRA~1:/cygdr... sys_lib_search_path_spec=`cygpath --path --unix "$LIB"` sys_lib_search_path_spec=`cygpath --path --dos "$sys_lib_search_path_spec" 2>/dev/null` sys_lib_search_path_spec=`cygpath --path --unix "$sys_lib_search_path_spec" | $SED -e "s/$PATH_SEPARATOR/ /g"` ;; *) sys_lib_search_path_spec="$LIB" if $ECHO "$sys_lib_search_path_spec" | [$GREP ';[c-zC-Z]:/' >/dev/null]; then # It is most probably a Windows format PATH. sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | $SED -e 's/;/ /g'` else sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | $SED -e "s/$PATH_SEPARATOR/ /g"` fi # FIXME: find the short name or the path components, as spaces are # common. (e.g. "Program Files" -> "PROGRA~1") ;; esac # DLL is installed to $(libdir)/../bin by postinstall_cmds postinstall_cmds='base_file=`basename \${file}`~ dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\${base_file}'\''i; echo \$dlname'\''`~ dldir=$destdir/`dirname \$dlpath`~ test -d \$dldir || mkdir -p \$dldir~ $install_prog $dir/$dlname \$dldir/$dlname' postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; echo \$dlname'\''`~ dlpath=$dir/\$dldll~ $RM \$dlpath' shlibpath_overrides_runpath=yes dynamic_linker='Win32 link.exe' ;; *) # Assume MSVC wrapper library_names_spec='${libname}`echo ${release} | $SED -e 's/[[.]]/-/g'`${versuffix}${shared_ext} $libname.lib' dynamic_linker='Win32 ld.exe' ;; esac # FIXME: first we should search . and the directory the executable is in shlibpath_var=PATH ;; darwin* | rhapsody*) dynamic_linker="$host_os dyld" version_type=darwin need_lib_prefix=no need_version=no library_names_spec='${libname}${release}${major}$shared_ext ${libname}$shared_ext' soname_spec='${libname}${release}${major}$shared_ext' shlibpath_overrides_runpath=yes shlibpath_var=DYLD_LIBRARY_PATH shrext_cmds='`test .$module = .yes && echo .so || echo .dylib`' m4_if([$1], [],[ sys_lib_search_path_spec="$sys_lib_search_path_spec /usr/local/lib"]) sys_lib_dlsearch_path_spec='/usr/local/lib /lib /usr/lib' ;; dgux*) version_type=linux # correct to gnu/linux during the next big refactor need_lib_prefix=no need_version=no library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname$shared_ext' soname_spec='${libname}${release}${shared_ext}$major' shlibpath_var=LD_LIBRARY_PATH ;; freebsd* | dragonfly*) # DragonFly does not have aout. When/if they implement a new # versioning mechanism, adjust this. if test -x /usr/bin/objformat; then objformat=`/usr/bin/objformat` else case $host_os in freebsd[[23]].*) objformat=aout ;; *) objformat=elf ;; esac fi version_type=freebsd-$objformat case $version_type in freebsd-elf*) library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext} $libname${shared_ext}' need_version=no need_lib_prefix=no ;; freebsd-*) library_names_spec='${libname}${release}${shared_ext}$versuffix $libname${shared_ext}$versuffix' need_version=yes ;; esac shlibpath_var=LD_LIBRARY_PATH case $host_os in freebsd2.*) shlibpath_overrides_runpath=yes ;; freebsd3.[[01]]* | freebsdelf3.[[01]]*) shlibpath_overrides_runpath=yes hardcode_into_libs=yes ;; freebsd3.[[2-9]]* | freebsdelf3.[[2-9]]* | \ freebsd4.[[0-5]] | freebsdelf4.[[0-5]] | freebsd4.1.1 | freebsdelf4.1.1) shlibpath_overrides_runpath=no hardcode_into_libs=yes ;; *) # from 4.6 on, and DragonFly shlibpath_overrides_runpath=yes hardcode_into_libs=yes ;; esac ;; haiku*) version_type=linux # correct to gnu/linux during the next big refactor need_lib_prefix=no need_version=no dynamic_linker="$host_os runtime_loader" library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}${major} ${libname}${shared_ext}' soname_spec='${libname}${release}${shared_ext}$major' shlibpath_var=LIBRARY_PATH shlibpath_overrides_runpath=yes sys_lib_dlsearch_path_spec='/boot/home/config/lib /boot/common/lib /boot/system/lib' hardcode_into_libs=yes ;; hpux9* | hpux10* | hpux11*) # Give a soname corresponding to the major version so that dld.sl refuses to # link against other versions. version_type=sunos need_lib_prefix=no need_version=no case $host_cpu in ia64*) shrext_cmds='.so' hardcode_into_libs=yes dynamic_linker="$host_os dld.so" shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes # Unless +noenvvar is specified. library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' soname_spec='${libname}${release}${shared_ext}$major' if test "X$HPUX_IA64_MODE" = X32; then sys_lib_search_path_spec="/usr/lib/hpux32 /usr/local/lib/hpux32 /usr/local/lib" else sys_lib_search_path_spec="/usr/lib/hpux64 /usr/local/lib/hpux64" fi sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec ;; hppa*64*) shrext_cmds='.sl' hardcode_into_libs=yes dynamic_linker="$host_os dld.sl" shlibpath_var=LD_LIBRARY_PATH # How should we handle SHLIB_PATH shlibpath_overrides_runpath=yes # Unless +noenvvar is specified. library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' soname_spec='${libname}${release}${shared_ext}$major' sys_lib_search_path_spec="/usr/lib/pa20_64 /usr/ccs/lib/pa20_64" sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec ;; *) shrext_cmds='.sl' dynamic_linker="$host_os dld.sl" shlibpath_var=SHLIB_PATH shlibpath_overrides_runpath=no # +s is required to enable SHLIB_PATH library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' soname_spec='${libname}${release}${shared_ext}$major' ;; esac # HP-UX runs *really* slowly unless shared libraries are mode 555, ... postinstall_cmds='chmod 555 $lib' # or fails outright, so override atomically: install_override_mode=555 ;; interix[[3-9]]*) version_type=linux # correct to gnu/linux during the next big refactor need_lib_prefix=no need_version=no library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}' soname_spec='${libname}${release}${shared_ext}$major' dynamic_linker='Interix 3.x ld.so.1 (PE, like ELF)' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=no hardcode_into_libs=yes ;; irix5* | irix6* | nonstopux*) case $host_os in nonstopux*) version_type=nonstopux ;; *) if test "$lt_cv_prog_gnu_ld" = yes; then version_type=linux # correct to gnu/linux during the next big refactor else version_type=irix fi ;; esac need_lib_prefix=no need_version=no soname_spec='${libname}${release}${shared_ext}$major' library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${release}${shared_ext} $libname${shared_ext}' case $host_os in irix5* | nonstopux*) libsuff= shlibsuff= ;; *) case $LD in # libtool.m4 will add one of these switches to LD *-32|*"-32 "|*-melf32bsmip|*"-melf32bsmip ") libsuff= shlibsuff= libmagic=32-bit;; *-n32|*"-n32 "|*-melf32bmipn32|*"-melf32bmipn32 ") libsuff=32 shlibsuff=N32 libmagic=N32;; *-64|*"-64 "|*-melf64bmip|*"-melf64bmip ") libsuff=64 shlibsuff=64 libmagic=64-bit;; *) libsuff= shlibsuff= libmagic=never-match;; esac ;; esac shlibpath_var=LD_LIBRARY${shlibsuff}_PATH shlibpath_overrides_runpath=no sys_lib_search_path_spec="/usr/lib${libsuff} /lib${libsuff} /usr/local/lib${libsuff}" sys_lib_dlsearch_path_spec="/usr/lib${libsuff} /lib${libsuff}" hardcode_into_libs=yes ;; # No shared lib support for Linux oldld, aout, or coff. linux*oldld* | linux*aout* | linux*coff*) dynamic_linker=no ;; # This must be glibc/ELF. linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*) version_type=linux # correct to gnu/linux during the next big refactor need_lib_prefix=no need_version=no library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' soname_spec='${libname}${release}${shared_ext}$major' finish_cmds='PATH="\$PATH:/sbin" ldconfig -n $libdir' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=no # Some binutils ld are patched to set DT_RUNPATH AC_CACHE_VAL([lt_cv_shlibpath_overrides_runpath], [lt_cv_shlibpath_overrides_runpath=no save_LDFLAGS=$LDFLAGS save_libdir=$libdir eval "libdir=/foo; wl=\"$_LT_TAGVAR(lt_prog_compiler_wl, $1)\"; \ LDFLAGS=\"\$LDFLAGS $_LT_TAGVAR(hardcode_libdir_flag_spec, $1)\"" AC_LINK_IFELSE([AC_LANG_PROGRAM([],[])], [AS_IF([ ($OBJDUMP -p conftest$ac_exeext) 2>/dev/null | grep "RUNPATH.*$libdir" >/dev/null], [lt_cv_shlibpath_overrides_runpath=yes])]) LDFLAGS=$save_LDFLAGS libdir=$save_libdir ]) shlibpath_overrides_runpath=$lt_cv_shlibpath_overrides_runpath # This implies no fast_install, which is unacceptable. # Some rework will be needed to allow for fast_install # before this can be enabled. hardcode_into_libs=yes # Append ld.so.conf contents to the search path if test -f /etc/ld.so.conf; then lt_ld_extra=`awk '/^include / { system(sprintf("cd /etc; cat %s 2>/dev/null", \[$]2)); skip = 1; } { if (!skip) print \[$]0; skip = 0; }' < /etc/ld.so.conf | $SED -e 's/#.*//;/^[ ]*hwcap[ ]/d;s/[:, ]/ /g;s/=[^=]*$//;s/=[^= ]* / /g;s/"//g;/^$/d' | tr '\n' ' '` sys_lib_dlsearch_path_spec="/lib /usr/lib $lt_ld_extra" fi # We used to test for /lib/ld.so.1 and disable shared libraries on # powerpc, because MkLinux only supported shared libraries with the # GNU dynamic linker. Since this was broken with cross compilers, # most powerpc-linux boxes support dynamic linking these days and # people can always --disable-shared, the test was removed, and we # assume the GNU/Linux dynamic linker is in use. dynamic_linker='GNU/Linux ld.so' ;; netbsdelf*-gnu) version_type=linux need_lib_prefix=no need_version=no library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}' soname_spec='${libname}${release}${shared_ext}$major' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=no hardcode_into_libs=yes dynamic_linker='NetBSD ld.elf_so' ;; netbsd*) version_type=sunos need_lib_prefix=no need_version=no if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${shared_ext}$versuffix' finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir' dynamic_linker='NetBSD (a.out) ld.so' else library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}' soname_spec='${libname}${release}${shared_ext}$major' dynamic_linker='NetBSD ld.elf_so' fi shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes hardcode_into_libs=yes ;; newsos6) version_type=linux # correct to gnu/linux during the next big refactor library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes ;; *nto* | *qnx*) version_type=qnx need_lib_prefix=no need_version=no library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' soname_spec='${libname}${release}${shared_ext}$major' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=no hardcode_into_libs=yes dynamic_linker='ldqnx.so' ;; openbsd*) version_type=sunos sys_lib_dlsearch_path_spec="/usr/lib" need_lib_prefix=no # Some older versions of OpenBSD (3.3 at least) *do* need versioned libs. case $host_os in openbsd3.3 | openbsd3.3.*) need_version=yes ;; *) need_version=no ;; esac library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${shared_ext}$versuffix' finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir' shlibpath_var=LD_LIBRARY_PATH if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then case $host_os in openbsd2.[[89]] | openbsd2.[[89]].*) shlibpath_overrides_runpath=no ;; *) shlibpath_overrides_runpath=yes ;; esac else shlibpath_overrides_runpath=yes fi ;; os2*) libname_spec='$name' shrext_cmds=".dll" need_lib_prefix=no library_names_spec='$libname${shared_ext} $libname.a' dynamic_linker='OS/2 ld.exe' shlibpath_var=LIBPATH ;; osf3* | osf4* | osf5*) version_type=osf need_lib_prefix=no need_version=no soname_spec='${libname}${release}${shared_ext}$major' library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' shlibpath_var=LD_LIBRARY_PATH sys_lib_search_path_spec="/usr/shlib /usr/ccs/lib /usr/lib/cmplrs/cc /usr/lib /usr/local/lib /var/shlib" sys_lib_dlsearch_path_spec="$sys_lib_search_path_spec" ;; rdos*) dynamic_linker=no ;; solaris*) version_type=linux # correct to gnu/linux during the next big refactor need_lib_prefix=no need_version=no library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' soname_spec='${libname}${release}${shared_ext}$major' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes hardcode_into_libs=yes # ldd complains unless libraries are executable postinstall_cmds='chmod +x $lib' ;; sunos4*) version_type=sunos library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${shared_ext}$versuffix' finish_cmds='PATH="\$PATH:/usr/etc" ldconfig $libdir' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes if test "$with_gnu_ld" = yes; then need_lib_prefix=no fi need_version=yes ;; sysv4 | sysv4.3*) version_type=linux # correct to gnu/linux during the next big refactor library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' soname_spec='${libname}${release}${shared_ext}$major' shlibpath_var=LD_LIBRARY_PATH case $host_vendor in sni) shlibpath_overrides_runpath=no need_lib_prefix=no runpath_var=LD_RUN_PATH ;; siemens) need_lib_prefix=no ;; motorola) need_lib_prefix=no need_version=no shlibpath_overrides_runpath=no sys_lib_search_path_spec='/lib /usr/lib /usr/ccs/lib' ;; esac ;; sysv4*MP*) if test -d /usr/nec ;then version_type=linux # correct to gnu/linux during the next big refactor library_names_spec='$libname${shared_ext}.$versuffix $libname${shared_ext}.$major $libname${shared_ext}' soname_spec='$libname${shared_ext}.$major' shlibpath_var=LD_LIBRARY_PATH fi ;; sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX* | sysv4*uw2*) version_type=freebsd-elf need_lib_prefix=no need_version=no library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext} $libname${shared_ext}' soname_spec='${libname}${release}${shared_ext}$major' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes hardcode_into_libs=yes if test "$with_gnu_ld" = yes; then sys_lib_search_path_spec='/usr/local/lib /usr/gnu/lib /usr/ccs/lib /usr/lib /lib' else sys_lib_search_path_spec='/usr/ccs/lib /usr/lib' case $host_os in sco3.2v5*) sys_lib_search_path_spec="$sys_lib_search_path_spec /lib" ;; esac fi sys_lib_dlsearch_path_spec='/usr/lib' ;; tpf*) # TPF is a cross-target only. Preferred cross-host = GNU/Linux. version_type=linux # correct to gnu/linux during the next big refactor need_lib_prefix=no need_version=no library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=no hardcode_into_libs=yes ;; uts4*) version_type=linux # correct to gnu/linux during the next big refactor library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' soname_spec='${libname}${release}${shared_ext}$major' shlibpath_var=LD_LIBRARY_PATH ;; *) dynamic_linker=no ;; esac AC_MSG_RESULT([$dynamic_linker]) test "$dynamic_linker" = no && can_build_shared=no variables_saved_for_relink="PATH $shlibpath_var $runpath_var" if test "$GCC" = yes; then variables_saved_for_relink="$variables_saved_for_relink GCC_EXEC_PREFIX COMPILER_PATH LIBRARY_PATH" fi if test "${lt_cv_sys_lib_search_path_spec+set}" = set; then sys_lib_search_path_spec="$lt_cv_sys_lib_search_path_spec" fi if test "${lt_cv_sys_lib_dlsearch_path_spec+set}" = set; then sys_lib_dlsearch_path_spec="$lt_cv_sys_lib_dlsearch_path_spec" fi _LT_DECL([], [variables_saved_for_relink], [1], [Variables whose values should be saved in libtool wrapper scripts and restored at link time]) _LT_DECL([], [need_lib_prefix], [0], [Do we need the "lib" prefix for modules?]) _LT_DECL([], [need_version], [0], [Do we need a version for libraries?]) _LT_DECL([], [version_type], [0], [Library versioning type]) _LT_DECL([], [runpath_var], [0], [Shared library runtime path variable]) _LT_DECL([], [shlibpath_var], [0],[Shared library path variable]) _LT_DECL([], [shlibpath_overrides_runpath], [0], [Is shlibpath searched before the hard-coded library search path?]) _LT_DECL([], [libname_spec], [1], [Format of library name prefix]) _LT_DECL([], [library_names_spec], [1], [[List of archive names. First name is the real one, the rest are links. The last name is the one that the linker finds with -lNAME]]) _LT_DECL([], [soname_spec], [1], [[The coded name of the library, if different from the real name]]) _LT_DECL([], [install_override_mode], [1], [Permission mode override for installation of shared libraries]) _LT_DECL([], [postinstall_cmds], [2], [Command to use after installation of a shared archive]) _LT_DECL([], [postuninstall_cmds], [2], [Command to use after uninstallation of a shared archive]) _LT_DECL([], [finish_cmds], [2], [Commands used to finish a libtool library installation in a directory]) _LT_DECL([], [finish_eval], [1], [[As "finish_cmds", except a single script fragment to be evaled but not shown]]) _LT_DECL([], [hardcode_into_libs], [0], [Whether we should hardcode library paths into libraries]) _LT_DECL([], [sys_lib_search_path_spec], [2], [Compile-time system search path for libraries]) _LT_DECL([], [sys_lib_dlsearch_path_spec], [2], [Run-time system search path for libraries]) ])# _LT_SYS_DYNAMIC_LINKER # _LT_PATH_TOOL_PREFIX(TOOL) # -------------------------- # find a file program which can recognize shared library AC_DEFUN([_LT_PATH_TOOL_PREFIX], [m4_require([_LT_DECL_EGREP])dnl AC_MSG_CHECKING([for $1]) AC_CACHE_VAL(lt_cv_path_MAGIC_CMD, [case $MAGIC_CMD in [[\\/*] | ?:[\\/]*]) lt_cv_path_MAGIC_CMD="$MAGIC_CMD" # Let the user override the test with a path. ;; *) lt_save_MAGIC_CMD="$MAGIC_CMD" lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR dnl $ac_dummy forces splitting on constant user-supplied paths. dnl POSIX.2 word splitting is done only on the output of word expansions, dnl not every word. This closes a longstanding sh security hole. ac_dummy="m4_if([$2], , $PATH, [$2])" for ac_dir in $ac_dummy; do IFS="$lt_save_ifs" test -z "$ac_dir" && ac_dir=. if test -f $ac_dir/$1; then lt_cv_path_MAGIC_CMD="$ac_dir/$1" if test -n "$file_magic_test_file"; then case $deplibs_check_method in "file_magic "*) file_magic_regex=`expr "$deplibs_check_method" : "file_magic \(.*\)"` MAGIC_CMD="$lt_cv_path_MAGIC_CMD" if eval $file_magic_cmd \$file_magic_test_file 2> /dev/null | $EGREP "$file_magic_regex" > /dev/null; then : else cat <<_LT_EOF 1>&2 *** Warning: the command libtool uses to detect shared libraries, *** $file_magic_cmd, produces output that libtool cannot recognize. *** The result is that libtool may fail to recognize shared libraries *** as such. This will affect the creation of libtool libraries that *** depend on shared libraries, but programs linked with such libtool *** libraries will work regardless of this problem. Nevertheless, you *** may want to report the problem to your system manager and/or to *** bug-libtool@gnu.org _LT_EOF fi ;; esac fi break fi done IFS="$lt_save_ifs" MAGIC_CMD="$lt_save_MAGIC_CMD" ;; esac]) MAGIC_CMD="$lt_cv_path_MAGIC_CMD" if test -n "$MAGIC_CMD"; then AC_MSG_RESULT($MAGIC_CMD) else AC_MSG_RESULT(no) fi _LT_DECL([], [MAGIC_CMD], [0], [Used to examine libraries when file_magic_cmd begins with "file"])dnl ])# _LT_PATH_TOOL_PREFIX # Old name: AU_ALIAS([AC_PATH_TOOL_PREFIX], [_LT_PATH_TOOL_PREFIX]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([AC_PATH_TOOL_PREFIX], []) # _LT_PATH_MAGIC # -------------- # find a file program which can recognize a shared library m4_defun([_LT_PATH_MAGIC], [_LT_PATH_TOOL_PREFIX(${ac_tool_prefix}file, /usr/bin$PATH_SEPARATOR$PATH) if test -z "$lt_cv_path_MAGIC_CMD"; then if test -n "$ac_tool_prefix"; then _LT_PATH_TOOL_PREFIX(file, /usr/bin$PATH_SEPARATOR$PATH) else MAGIC_CMD=: fi fi ])# _LT_PATH_MAGIC # LT_PATH_LD # ---------- # find the pathname to the GNU or non-GNU linker AC_DEFUN([LT_PATH_LD], [AC_REQUIRE([AC_PROG_CC])dnl AC_REQUIRE([AC_CANONICAL_HOST])dnl AC_REQUIRE([AC_CANONICAL_BUILD])dnl m4_require([_LT_DECL_SED])dnl m4_require([_LT_DECL_EGREP])dnl m4_require([_LT_PROG_ECHO_BACKSLASH])dnl AC_ARG_WITH([gnu-ld], [AS_HELP_STRING([--with-gnu-ld], [assume the C compiler uses GNU ld @<:@default=no@:>@])], [test "$withval" = no || with_gnu_ld=yes], [with_gnu_ld=no])dnl ac_prog=ld if test "$GCC" = yes; then # Check if gcc -print-prog-name=ld gives a path. AC_MSG_CHECKING([for ld used by $CC]) case $host in *-*-mingw*) # gcc leaves a trailing carriage return which upsets mingw ac_prog=`($CC -print-prog-name=ld) 2>&5 | tr -d '\015'` ;; *) ac_prog=`($CC -print-prog-name=ld) 2>&5` ;; esac case $ac_prog in # Accept absolute paths. [[\\/]]* | ?:[[\\/]]*) re_direlt='/[[^/]][[^/]]*/\.\./' # Canonicalize the pathname of ld ac_prog=`$ECHO "$ac_prog"| $SED 's%\\\\%/%g'` while $ECHO "$ac_prog" | $GREP "$re_direlt" > /dev/null 2>&1; do ac_prog=`$ECHO $ac_prog| $SED "s%$re_direlt%/%"` done test -z "$LD" && LD="$ac_prog" ;; "") # If it fails, then pretend we aren't using GCC. ac_prog=ld ;; *) # If it is relative, then search for the first ld in PATH. with_gnu_ld=unknown ;; esac elif test "$with_gnu_ld" = yes; then AC_MSG_CHECKING([for GNU ld]) else AC_MSG_CHECKING([for non-GNU ld]) fi AC_CACHE_VAL(lt_cv_path_LD, [if test -z "$LD"; then lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR for ac_dir in $PATH; do IFS="$lt_save_ifs" test -z "$ac_dir" && ac_dir=. if test -f "$ac_dir/$ac_prog" || test -f "$ac_dir/$ac_prog$ac_exeext"; then lt_cv_path_LD="$ac_dir/$ac_prog" # Check to see if the program is GNU ld. I'd rather use --version, # but apparently some variants of GNU ld only accept -v. # Break only if it was the GNU/non-GNU ld that we prefer. case `"$lt_cv_path_LD" -v 2>&1 &1 /dev/null 2>&1; then lt_cv_deplibs_check_method='file_magic ^x86 archive import|^x86 DLL' lt_cv_file_magic_cmd='func_win32_libid' else # Keep this pattern in sync with the one in func_win32_libid. lt_cv_deplibs_check_method='file_magic file format (pei*-i386(.*architecture: i386)?|pe-arm-wince|pe-x86-64)' lt_cv_file_magic_cmd='$OBJDUMP -f' fi ;; cegcc*) # use the weaker test based on 'objdump'. See mingw*. lt_cv_deplibs_check_method='file_magic file format pe-arm-.*little(.*architecture: arm)?' lt_cv_file_magic_cmd='$OBJDUMP -f' ;; darwin* | rhapsody*) lt_cv_deplibs_check_method=pass_all ;; freebsd* | dragonfly*) if echo __ELF__ | $CC -E - | $GREP __ELF__ > /dev/null; then case $host_cpu in i*86 ) # Not sure whether the presence of OpenBSD here was a mistake. # Let's accept both of them until this is cleared up. lt_cv_deplibs_check_method='file_magic (FreeBSD|OpenBSD|DragonFly)/i[[3-9]]86 (compact )?demand paged shared library' lt_cv_file_magic_cmd=/usr/bin/file lt_cv_file_magic_test_file=`echo /usr/lib/libc.so.*` ;; esac else lt_cv_deplibs_check_method=pass_all fi ;; haiku*) lt_cv_deplibs_check_method=pass_all ;; hpux10.20* | hpux11*) lt_cv_file_magic_cmd=/usr/bin/file case $host_cpu in ia64*) lt_cv_deplibs_check_method='file_magic (s[[0-9]][[0-9]][[0-9]]|ELF-[[0-9]][[0-9]]) shared object file - IA64' lt_cv_file_magic_test_file=/usr/lib/hpux32/libc.so ;; hppa*64*) [lt_cv_deplibs_check_method='file_magic (s[0-9][0-9][0-9]|ELF[ -][0-9][0-9])(-bit)?( [LM]SB)? shared object( file)?[, -]* PA-RISC [0-9]\.[0-9]'] lt_cv_file_magic_test_file=/usr/lib/pa20_64/libc.sl ;; *) lt_cv_deplibs_check_method='file_magic (s[[0-9]][[0-9]][[0-9]]|PA-RISC[[0-9]]\.[[0-9]]) shared library' lt_cv_file_magic_test_file=/usr/lib/libc.sl ;; esac ;; interix[[3-9]]*) # PIC code is broken on Interix 3.x, that's why |\.a not |_pic\.a here lt_cv_deplibs_check_method='match_pattern /lib[[^/]]+(\.so|\.a)$' ;; irix5* | irix6* | nonstopux*) case $LD in *-32|*"-32 ") libmagic=32-bit;; *-n32|*"-n32 ") libmagic=N32;; *-64|*"-64 ") libmagic=64-bit;; *) libmagic=never-match;; esac lt_cv_deplibs_check_method=pass_all ;; # This must be glibc/ELF. linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*) lt_cv_deplibs_check_method=pass_all ;; netbsd* | netbsdelf*-gnu) if echo __ELF__ | $CC -E - | $GREP __ELF__ > /dev/null; then lt_cv_deplibs_check_method='match_pattern /lib[[^/]]+(\.so\.[[0-9]]+\.[[0-9]]+|_pic\.a)$' else lt_cv_deplibs_check_method='match_pattern /lib[[^/]]+(\.so|_pic\.a)$' fi ;; newos6*) lt_cv_deplibs_check_method='file_magic ELF [[0-9]][[0-9]]*-bit [[ML]]SB (executable|dynamic lib)' lt_cv_file_magic_cmd=/usr/bin/file lt_cv_file_magic_test_file=/usr/lib/libnls.so ;; *nto* | *qnx*) lt_cv_deplibs_check_method=pass_all ;; openbsd*) if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then lt_cv_deplibs_check_method='match_pattern /lib[[^/]]+(\.so\.[[0-9]]+\.[[0-9]]+|\.so|_pic\.a)$' else lt_cv_deplibs_check_method='match_pattern /lib[[^/]]+(\.so\.[[0-9]]+\.[[0-9]]+|_pic\.a)$' fi ;; osf3* | osf4* | osf5*) lt_cv_deplibs_check_method=pass_all ;; rdos*) lt_cv_deplibs_check_method=pass_all ;; solaris*) lt_cv_deplibs_check_method=pass_all ;; sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX* | sysv4*uw2*) lt_cv_deplibs_check_method=pass_all ;; sysv4 | sysv4.3*) case $host_vendor in motorola) lt_cv_deplibs_check_method='file_magic ELF [[0-9]][[0-9]]*-bit [[ML]]SB (shared object|dynamic lib) M[[0-9]][[0-9]]* Version [[0-9]]' lt_cv_file_magic_test_file=`echo /usr/lib/libc.so*` ;; ncr) lt_cv_deplibs_check_method=pass_all ;; sequent) lt_cv_file_magic_cmd='/bin/file' lt_cv_deplibs_check_method='file_magic ELF [[0-9]][[0-9]]*-bit [[LM]]SB (shared object|dynamic lib )' ;; sni) lt_cv_file_magic_cmd='/bin/file' lt_cv_deplibs_check_method="file_magic ELF [[0-9]][[0-9]]*-bit [[LM]]SB dynamic lib" lt_cv_file_magic_test_file=/lib/libc.so ;; siemens) lt_cv_deplibs_check_method=pass_all ;; pc) lt_cv_deplibs_check_method=pass_all ;; esac ;; tpf*) lt_cv_deplibs_check_method=pass_all ;; esac ]) file_magic_glob= want_nocaseglob=no if test "$build" = "$host"; then case $host_os in mingw* | pw32*) if ( shopt | grep nocaseglob ) >/dev/null 2>&1; then want_nocaseglob=yes else file_magic_glob=`echo aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvVwWxXyYzZ | $SED -e "s/\(..\)/s\/[[\1]]\/[[\1]]\/g;/g"` fi ;; esac fi file_magic_cmd=$lt_cv_file_magic_cmd deplibs_check_method=$lt_cv_deplibs_check_method test -z "$deplibs_check_method" && deplibs_check_method=unknown _LT_DECL([], [deplibs_check_method], [1], [Method to check whether dependent libraries are shared objects]) _LT_DECL([], [file_magic_cmd], [1], [Command to use when deplibs_check_method = "file_magic"]) _LT_DECL([], [file_magic_glob], [1], [How to find potential files when deplibs_check_method = "file_magic"]) _LT_DECL([], [want_nocaseglob], [1], [Find potential files using nocaseglob when deplibs_check_method = "file_magic"]) ])# _LT_CHECK_MAGIC_METHOD # LT_PATH_NM # ---------- # find the pathname to a BSD- or MS-compatible name lister AC_DEFUN([LT_PATH_NM], [AC_REQUIRE([AC_PROG_CC])dnl AC_CACHE_CHECK([for BSD- or MS-compatible name lister (nm)], lt_cv_path_NM, [if test -n "$NM"; then # Let the user override the test. lt_cv_path_NM="$NM" else lt_nm_to_check="${ac_tool_prefix}nm" if test -n "$ac_tool_prefix" && test "$build" = "$host"; then lt_nm_to_check="$lt_nm_to_check nm" fi for lt_tmp_nm in $lt_nm_to_check; do lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR for ac_dir in $PATH /usr/ccs/bin/elf /usr/ccs/bin /usr/ucb /bin; do IFS="$lt_save_ifs" test -z "$ac_dir" && ac_dir=. tmp_nm="$ac_dir/$lt_tmp_nm" if test -f "$tmp_nm" || test -f "$tmp_nm$ac_exeext" ; then # Check to see if the nm accepts a BSD-compat flag. # Adding the `sed 1q' prevents false positives on HP-UX, which says: # nm: unknown option "B" ignored # Tru64's nm complains that /dev/null is an invalid object file case `"$tmp_nm" -B /dev/null 2>&1 | sed '1q'` in */dev/null* | *'Invalid file or object type'*) lt_cv_path_NM="$tmp_nm -B" break ;; *) case `"$tmp_nm" -p /dev/null 2>&1 | sed '1q'` in */dev/null*) lt_cv_path_NM="$tmp_nm -p" break ;; *) lt_cv_path_NM=${lt_cv_path_NM="$tmp_nm"} # keep the first match, but continue # so that we can try to find one that supports BSD flags ;; esac ;; esac fi done IFS="$lt_save_ifs" done : ${lt_cv_path_NM=no} fi]) if test "$lt_cv_path_NM" != "no"; then NM="$lt_cv_path_NM" else # Didn't find any BSD compatible name lister, look for dumpbin. if test -n "$DUMPBIN"; then : # Let the user override the test. else AC_CHECK_TOOLS(DUMPBIN, [dumpbin "link -dump"], :) case `$DUMPBIN -symbols /dev/null 2>&1 | sed '1q'` in *COFF*) DUMPBIN="$DUMPBIN -symbols" ;; *) DUMPBIN=: ;; esac fi AC_SUBST([DUMPBIN]) if test "$DUMPBIN" != ":"; then NM="$DUMPBIN" fi fi test -z "$NM" && NM=nm AC_SUBST([NM]) _LT_DECL([], [NM], [1], [A BSD- or MS-compatible name lister])dnl AC_CACHE_CHECK([the name lister ($NM) interface], [lt_cv_nm_interface], [lt_cv_nm_interface="BSD nm" echo "int some_variable = 0;" > conftest.$ac_ext (eval echo "\"\$as_me:$LINENO: $ac_compile\"" >&AS_MESSAGE_LOG_FD) (eval "$ac_compile" 2>conftest.err) cat conftest.err >&AS_MESSAGE_LOG_FD (eval echo "\"\$as_me:$LINENO: $NM \\\"conftest.$ac_objext\\\"\"" >&AS_MESSAGE_LOG_FD) (eval "$NM \"conftest.$ac_objext\"" 2>conftest.err > conftest.out) cat conftest.err >&AS_MESSAGE_LOG_FD (eval echo "\"\$as_me:$LINENO: output\"" >&AS_MESSAGE_LOG_FD) cat conftest.out >&AS_MESSAGE_LOG_FD if $GREP 'External.*some_variable' conftest.out > /dev/null; then lt_cv_nm_interface="MS dumpbin" fi rm -f conftest*]) ])# LT_PATH_NM # Old names: AU_ALIAS([AM_PROG_NM], [LT_PATH_NM]) AU_ALIAS([AC_PROG_NM], [LT_PATH_NM]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([AM_PROG_NM], []) dnl AC_DEFUN([AC_PROG_NM], []) # _LT_CHECK_SHAREDLIB_FROM_LINKLIB # -------------------------------- # how to determine the name of the shared library # associated with a specific link library. # -- PORTME fill in with the dynamic library characteristics m4_defun([_LT_CHECK_SHAREDLIB_FROM_LINKLIB], [m4_require([_LT_DECL_EGREP]) m4_require([_LT_DECL_OBJDUMP]) m4_require([_LT_DECL_DLLTOOL]) AC_CACHE_CHECK([how to associate runtime and link libraries], lt_cv_sharedlib_from_linklib_cmd, [lt_cv_sharedlib_from_linklib_cmd='unknown' case $host_os in cygwin* | mingw* | pw32* | cegcc*) # two different shell functions defined in ltmain.sh # decide which to use based on capabilities of $DLLTOOL case `$DLLTOOL --help 2>&1` in *--identify-strict*) lt_cv_sharedlib_from_linklib_cmd=func_cygming_dll_for_implib ;; *) lt_cv_sharedlib_from_linklib_cmd=func_cygming_dll_for_implib_fallback ;; esac ;; *) # fallback: assume linklib IS sharedlib lt_cv_sharedlib_from_linklib_cmd="$ECHO" ;; esac ]) sharedlib_from_linklib_cmd=$lt_cv_sharedlib_from_linklib_cmd test -z "$sharedlib_from_linklib_cmd" && sharedlib_from_linklib_cmd=$ECHO _LT_DECL([], [sharedlib_from_linklib_cmd], [1], [Command to associate shared and link libraries]) ])# _LT_CHECK_SHAREDLIB_FROM_LINKLIB # _LT_PATH_MANIFEST_TOOL # ---------------------- # locate the manifest tool m4_defun([_LT_PATH_MANIFEST_TOOL], [AC_CHECK_TOOL(MANIFEST_TOOL, mt, :) test -z "$MANIFEST_TOOL" && MANIFEST_TOOL=mt AC_CACHE_CHECK([if $MANIFEST_TOOL is a manifest tool], [lt_cv_path_mainfest_tool], [lt_cv_path_mainfest_tool=no echo "$as_me:$LINENO: $MANIFEST_TOOL '-?'" >&AS_MESSAGE_LOG_FD $MANIFEST_TOOL '-?' 2>conftest.err > conftest.out cat conftest.err >&AS_MESSAGE_LOG_FD if $GREP 'Manifest Tool' conftest.out > /dev/null; then lt_cv_path_mainfest_tool=yes fi rm -f conftest*]) if test "x$lt_cv_path_mainfest_tool" != xyes; then MANIFEST_TOOL=: fi _LT_DECL([], [MANIFEST_TOOL], [1], [Manifest tool])dnl ])# _LT_PATH_MANIFEST_TOOL # LT_LIB_M # -------- # check for math library AC_DEFUN([LT_LIB_M], [AC_REQUIRE([AC_CANONICAL_HOST])dnl LIBM= case $host in *-*-beos* | *-*-cegcc* | *-*-cygwin* | *-*-haiku* | *-*-pw32* | *-*-darwin*) # These system don't have libm, or don't need it ;; *-ncr-sysv4.3*) AC_CHECK_LIB(mw, _mwvalidcheckl, LIBM="-lmw") AC_CHECK_LIB(m, cos, LIBM="$LIBM -lm") ;; *) AC_CHECK_LIB(m, cos, LIBM="-lm") ;; esac AC_SUBST([LIBM]) ])# LT_LIB_M # Old name: AU_ALIAS([AC_CHECK_LIBM], [LT_LIB_M]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([AC_CHECK_LIBM], []) # _LT_COMPILER_NO_RTTI([TAGNAME]) # ------------------------------- m4_defun([_LT_COMPILER_NO_RTTI], [m4_require([_LT_TAG_COMPILER])dnl _LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)= if test "$GCC" = yes; then case $cc_basename in nvcc*) _LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)=' -Xcompiler -fno-builtin' ;; *) _LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)=' -fno-builtin' ;; esac _LT_COMPILER_OPTION([if $compiler supports -fno-rtti -fno-exceptions], lt_cv_prog_compiler_rtti_exceptions, [-fno-rtti -fno-exceptions], [], [_LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)="$_LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1) -fno-rtti -fno-exceptions"]) fi _LT_TAGDECL([no_builtin_flag], [lt_prog_compiler_no_builtin_flag], [1], [Compiler flag to turn off builtin functions]) ])# _LT_COMPILER_NO_RTTI # _LT_CMD_GLOBAL_SYMBOLS # ---------------------- m4_defun([_LT_CMD_GLOBAL_SYMBOLS], [AC_REQUIRE([AC_CANONICAL_HOST])dnl AC_REQUIRE([AC_PROG_CC])dnl AC_REQUIRE([AC_PROG_AWK])dnl AC_REQUIRE([LT_PATH_NM])dnl AC_REQUIRE([LT_PATH_LD])dnl m4_require([_LT_DECL_SED])dnl m4_require([_LT_DECL_EGREP])dnl m4_require([_LT_TAG_COMPILER])dnl # Check for command to grab the raw symbol name followed by C symbol from nm. AC_MSG_CHECKING([command to parse $NM output from $compiler object]) AC_CACHE_VAL([lt_cv_sys_global_symbol_pipe], [ # These are sane defaults that work on at least a few old systems. # [They come from Ultrix. What could be older than Ultrix?!! ;)] # Character class describing NM global symbol codes. symcode='[[BCDEGRST]]' # Regexp to match symbols that can be accessed directly from C. sympat='\([[_A-Za-z]][[_A-Za-z0-9]]*\)' # Define system-specific variables. case $host_os in aix*) symcode='[[BCDT]]' ;; cygwin* | mingw* | pw32* | cegcc*) symcode='[[ABCDGISTW]]' ;; hpux*) if test "$host_cpu" = ia64; then symcode='[[ABCDEGRST]]' fi ;; irix* | nonstopux*) symcode='[[BCDEGRST]]' ;; osf*) symcode='[[BCDEGQRST]]' ;; solaris*) symcode='[[BDRT]]' ;; sco3.2v5*) symcode='[[DT]]' ;; sysv4.2uw2*) symcode='[[DT]]' ;; sysv5* | sco5v6* | unixware* | OpenUNIX*) symcode='[[ABDT]]' ;; sysv4) symcode='[[DFNSTU]]' ;; esac # If we're using GNU nm, then use its standard symbol codes. case `$NM -V 2>&1` in *GNU* | *'with BFD'*) symcode='[[ABCDGIRSTW]]' ;; esac # Transform an extracted symbol line into a proper C declaration. # Some systems (esp. on ia64) link data and code symbols differently, # so use this general approach. lt_cv_sys_global_symbol_to_cdecl="sed -n -e 's/^T .* \(.*\)$/extern int \1();/p' -e 's/^$symcode* .* \(.*\)$/extern char \1;/p'" # Transform an extracted symbol line into symbol name and symbol address lt_cv_sys_global_symbol_to_c_name_address="sed -n -e 's/^: \([[^ ]]*\)[[ ]]*$/ {\\\"\1\\\", (void *) 0},/p' -e 's/^$symcode* \([[^ ]]*\) \([[^ ]]*\)$/ {\"\2\", (void *) \&\2},/p'" lt_cv_sys_global_symbol_to_c_name_address_lib_prefix="sed -n -e 's/^: \([[^ ]]*\)[[ ]]*$/ {\\\"\1\\\", (void *) 0},/p' -e 's/^$symcode* \([[^ ]]*\) \(lib[[^ ]]*\)$/ {\"\2\", (void *) \&\2},/p' -e 's/^$symcode* \([[^ ]]*\) \([[^ ]]*\)$/ {\"lib\2\", (void *) \&\2},/p'" # Handle CRLF in mingw tool chain opt_cr= case $build_os in mingw*) opt_cr=`$ECHO 'x\{0,1\}' | tr x '\015'` # option cr in regexp ;; esac # Try without a prefix underscore, then with it. for ac_symprfx in "" "_"; do # Transform symcode, sympat, and symprfx into a raw symbol and a C symbol. symxfrm="\\1 $ac_symprfx\\2 \\2" # Write the raw and C identifiers. if test "$lt_cv_nm_interface" = "MS dumpbin"; then # Fake it for dumpbin and say T for any non-static function # and D for any global variable. # Also find C++ and __fastcall symbols from MSVC++, # which start with @ or ?. lt_cv_sys_global_symbol_pipe="$AWK ['"\ " {last_section=section; section=\$ 3};"\ " /^COFF SYMBOL TABLE/{for(i in hide) delete hide[i]};"\ " /Section length .*#relocs.*(pick any)/{hide[last_section]=1};"\ " \$ 0!~/External *\|/{next};"\ " / 0+ UNDEF /{next}; / UNDEF \([^|]\)*()/{next};"\ " {if(hide[section]) next};"\ " {f=0}; \$ 0~/\(\).*\|/{f=1}; {printf f ? \"T \" : \"D \"};"\ " {split(\$ 0, a, /\||\r/); split(a[2], s)};"\ " s[1]~/^[@?]/{print s[1], s[1]; next};"\ " s[1]~prfx {split(s[1],t,\"@\"); print t[1], substr(t[1],length(prfx))}"\ " ' prfx=^$ac_symprfx]" else lt_cv_sys_global_symbol_pipe="sed -n -e 's/^.*[[ ]]\($symcode$symcode*\)[[ ]][[ ]]*$ac_symprfx$sympat$opt_cr$/$symxfrm/p'" fi lt_cv_sys_global_symbol_pipe="$lt_cv_sys_global_symbol_pipe | sed '/ __gnu_lto/d'" # Check to see that the pipe works correctly. pipe_works=no rm -f conftest* cat > conftest.$ac_ext <<_LT_EOF #ifdef __cplusplus extern "C" { #endif char nm_test_var; void nm_test_func(void); void nm_test_func(void){} #ifdef __cplusplus } #endif int main(){nm_test_var='a';nm_test_func();return(0);} _LT_EOF if AC_TRY_EVAL(ac_compile); then # Now try to grab the symbols. nlist=conftest.nm if AC_TRY_EVAL(NM conftest.$ac_objext \| "$lt_cv_sys_global_symbol_pipe" \> $nlist) && test -s "$nlist"; then # Try sorting and uniquifying the output. if sort "$nlist" | uniq > "$nlist"T; then mv -f "$nlist"T "$nlist" else rm -f "$nlist"T fi # Make sure that we snagged all the symbols we need. if $GREP ' nm_test_var$' "$nlist" >/dev/null; then if $GREP ' nm_test_func$' "$nlist" >/dev/null; then cat <<_LT_EOF > conftest.$ac_ext /* Keep this code in sync between libtool.m4, ltmain, lt_system.h, and tests. */ #if defined(_WIN32) || defined(__CYGWIN__) || defined(_WIN32_WCE) /* DATA imports from DLLs on WIN32 con't be const, because runtime relocations are performed -- see ld's documentation on pseudo-relocs. */ # define LT@&t@_DLSYM_CONST #elif defined(__osf__) /* This system does not cope well with relocations in const data. */ # define LT@&t@_DLSYM_CONST #else # define LT@&t@_DLSYM_CONST const #endif #ifdef __cplusplus extern "C" { #endif _LT_EOF # Now generate the symbol file. eval "$lt_cv_sys_global_symbol_to_cdecl"' < "$nlist" | $GREP -v main >> conftest.$ac_ext' cat <<_LT_EOF >> conftest.$ac_ext /* The mapping between symbol names and symbols. */ LT@&t@_DLSYM_CONST struct { const char *name; void *address; } lt__PROGRAM__LTX_preloaded_symbols[[]] = { { "@PROGRAM@", (void *) 0 }, _LT_EOF $SED "s/^$symcode$symcode* \(.*\) \(.*\)$/ {\"\2\", (void *) \&\2},/" < "$nlist" | $GREP -v main >> conftest.$ac_ext cat <<\_LT_EOF >> conftest.$ac_ext {0, (void *) 0} }; /* This works around a problem in FreeBSD linker */ #ifdef FREEBSD_WORKAROUND static const void *lt_preloaded_setup() { return lt__PROGRAM__LTX_preloaded_symbols; } #endif #ifdef __cplusplus } #endif _LT_EOF # Now try linking the two files. mv conftest.$ac_objext conftstm.$ac_objext lt_globsym_save_LIBS=$LIBS lt_globsym_save_CFLAGS=$CFLAGS LIBS="conftstm.$ac_objext" CFLAGS="$CFLAGS$_LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)" if AC_TRY_EVAL(ac_link) && test -s conftest${ac_exeext}; then pipe_works=yes fi LIBS=$lt_globsym_save_LIBS CFLAGS=$lt_globsym_save_CFLAGS else echo "cannot find nm_test_func in $nlist" >&AS_MESSAGE_LOG_FD fi else echo "cannot find nm_test_var in $nlist" >&AS_MESSAGE_LOG_FD fi else echo "cannot run $lt_cv_sys_global_symbol_pipe" >&AS_MESSAGE_LOG_FD fi else echo "$progname: failed program was:" >&AS_MESSAGE_LOG_FD cat conftest.$ac_ext >&5 fi rm -rf conftest* conftst* # Do not use the global_symbol_pipe unless it works. if test "$pipe_works" = yes; then break else lt_cv_sys_global_symbol_pipe= fi done ]) if test -z "$lt_cv_sys_global_symbol_pipe"; then lt_cv_sys_global_symbol_to_cdecl= fi if test -z "$lt_cv_sys_global_symbol_pipe$lt_cv_sys_global_symbol_to_cdecl"; then AC_MSG_RESULT(failed) else AC_MSG_RESULT(ok) fi # Response file support. if test "$lt_cv_nm_interface" = "MS dumpbin"; then nm_file_list_spec='@' elif $NM --help 2>/dev/null | grep '[[@]]FILE' >/dev/null; then nm_file_list_spec='@' fi _LT_DECL([global_symbol_pipe], [lt_cv_sys_global_symbol_pipe], [1], [Take the output of nm and produce a listing of raw symbols and C names]) _LT_DECL([global_symbol_to_cdecl], [lt_cv_sys_global_symbol_to_cdecl], [1], [Transform the output of nm in a proper C declaration]) _LT_DECL([global_symbol_to_c_name_address], [lt_cv_sys_global_symbol_to_c_name_address], [1], [Transform the output of nm in a C name address pair]) _LT_DECL([global_symbol_to_c_name_address_lib_prefix], [lt_cv_sys_global_symbol_to_c_name_address_lib_prefix], [1], [Transform the output of nm in a C name address pair when lib prefix is needed]) _LT_DECL([], [nm_file_list_spec], [1], [Specify filename containing input files for $NM]) ]) # _LT_CMD_GLOBAL_SYMBOLS # _LT_COMPILER_PIC([TAGNAME]) # --------------------------- m4_defun([_LT_COMPILER_PIC], [m4_require([_LT_TAG_COMPILER])dnl _LT_TAGVAR(lt_prog_compiler_wl, $1)= _LT_TAGVAR(lt_prog_compiler_pic, $1)= _LT_TAGVAR(lt_prog_compiler_static, $1)= m4_if([$1], [CXX], [ # C++ specific cases for pic, static, wl, etc. if test "$GXX" = yes; then _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_static, $1)='-static' case $host_os in aix*) # All AIX code is PIC. if test "$host_cpu" = ia64; then # AIX 5 now supports IA64 processor _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' fi ;; amigaos*) case $host_cpu in powerpc) # see comment about AmigaOS4 .so support _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' ;; m68k) # FIXME: we need at least 68020 code to build shared libraries, but # adding the `-m68020' flag to GCC prevents building anything better, # like `-m68040'. _LT_TAGVAR(lt_prog_compiler_pic, $1)='-m68020 -resident32 -malways-restore-a4' ;; esac ;; beos* | irix5* | irix6* | nonstopux* | osf3* | osf4* | osf5*) # PIC is the default for these OSes. ;; mingw* | cygwin* | os2* | pw32* | cegcc*) # This hack is so that the source file can tell whether it is being # built for inclusion in a dll (and should export symbols for example). # Although the cygwin gcc ignores -fPIC, still need this for old-style # (--disable-auto-import) libraries m4_if([$1], [GCJ], [], [_LT_TAGVAR(lt_prog_compiler_pic, $1)='-DDLL_EXPORT']) ;; darwin* | rhapsody*) # PIC is the default on this platform # Common symbols not allowed in MH_DYLIB files _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fno-common' ;; *djgpp*) # DJGPP does not support shared libraries at all _LT_TAGVAR(lt_prog_compiler_pic, $1)= ;; haiku*) # PIC is the default for Haiku. # The "-static" flag exists, but is broken. _LT_TAGVAR(lt_prog_compiler_static, $1)= ;; interix[[3-9]]*) # Interix 3.x gcc -fpic/-fPIC options generate broken code. # Instead, we relocate shared libraries at runtime. ;; sysv4*MP*) if test -d /usr/nec; then _LT_TAGVAR(lt_prog_compiler_pic, $1)=-Kconform_pic fi ;; hpux*) # PIC is the default for 64-bit PA HP-UX, but not for 32-bit # PA HP-UX. On IA64 HP-UX, PIC is the default but the pic flag # sets the default TLS model and affects inlining. case $host_cpu in hppa*64*) ;; *) _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' ;; esac ;; *qnx* | *nto*) # QNX uses GNU C++, but need to define -shared option too, otherwise # it will coredump. _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC -shared' ;; *) _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' ;; esac else case $host_os in aix[[4-9]]*) # All AIX code is PIC. if test "$host_cpu" = ia64; then # AIX 5 now supports IA64 processor _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' else _LT_TAGVAR(lt_prog_compiler_static, $1)='-bnso -bI:/lib/syscalls.exp' fi ;; chorus*) case $cc_basename in cxch68*) # Green Hills C++ Compiler # _LT_TAGVAR(lt_prog_compiler_static, $1)="--no_auto_instantiation -u __main -u __premain -u _abort -r $COOL_DIR/lib/libOrb.a $MVME_DIR/lib/CC/libC.a $MVME_DIR/lib/classix/libcx.s.a" ;; esac ;; mingw* | cygwin* | os2* | pw32* | cegcc*) # This hack is so that the source file can tell whether it is being # built for inclusion in a dll (and should export symbols for example). m4_if([$1], [GCJ], [], [_LT_TAGVAR(lt_prog_compiler_pic, $1)='-DDLL_EXPORT']) ;; dgux*) case $cc_basename in ec++*) _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' ;; ghcx*) # Green Hills C++ Compiler _LT_TAGVAR(lt_prog_compiler_pic, $1)='-pic' ;; *) ;; esac ;; freebsd* | dragonfly*) # FreeBSD uses GNU C++ ;; hpux9* | hpux10* | hpux11*) case $cc_basename in CC*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_static, $1)='${wl}-a ${wl}archive' if test "$host_cpu" != ia64; then _LT_TAGVAR(lt_prog_compiler_pic, $1)='+Z' fi ;; aCC*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_static, $1)='${wl}-a ${wl}archive' case $host_cpu in hppa*64*|ia64*) # +Z the default ;; *) _LT_TAGVAR(lt_prog_compiler_pic, $1)='+Z' ;; esac ;; *) ;; esac ;; interix*) # This is c89, which is MS Visual C++ (no shared libs) # Anyone wants to do a port? ;; irix5* | irix6* | nonstopux*) case $cc_basename in CC*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared' # CC pic flag -KPIC is the default. ;; *) ;; esac ;; linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*) case $cc_basename in KCC*) # KAI C++ Compiler _LT_TAGVAR(lt_prog_compiler_wl, $1)='--backend -Wl,' _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' ;; ecpc* ) # old Intel C++ for x86_64 which still supported -KPIC. _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-static' ;; icpc* ) # Intel C++, used to be incompatible with GCC. # ICC 10 doesn't accept -KPIC any more. _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-static' ;; pgCC* | pgcpp*) # Portland Group C++ compiler _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fpic' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' ;; cxx*) # Compaq C++ # Make sure the PIC flag is empty. It appears that all Alpha # Linux and Compaq Tru64 Unix objects are PIC. _LT_TAGVAR(lt_prog_compiler_pic, $1)= _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared' ;; xlc* | xlC* | bgxl[[cC]]* | mpixl[[cC]]*) # IBM XL 8.0, 9.0 on PPC and BlueGene _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_pic, $1)='-qpic' _LT_TAGVAR(lt_prog_compiler_static, $1)='-qstaticlink' ;; *) case `$CC -V 2>&1 | sed 5q` in *Sun\ C*) # Sun C++ 5.9 _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Qoption ld ' ;; esac ;; esac ;; lynxos*) ;; m88k*) ;; mvs*) case $cc_basename in cxx*) _LT_TAGVAR(lt_prog_compiler_pic, $1)='-W c,exportall' ;; *) ;; esac ;; netbsd* | netbsdelf*-gnu) ;; *qnx* | *nto*) # QNX uses GNU C++, but need to define -shared option too, otherwise # it will coredump. _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC -shared' ;; osf3* | osf4* | osf5*) case $cc_basename in KCC*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='--backend -Wl,' ;; RCC*) # Rational C++ 2.4.1 _LT_TAGVAR(lt_prog_compiler_pic, $1)='-pic' ;; cxx*) # Digital/Compaq C++ _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' # Make sure the PIC flag is empty. It appears that all Alpha # Linux and Compaq Tru64 Unix objects are PIC. _LT_TAGVAR(lt_prog_compiler_pic, $1)= _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared' ;; *) ;; esac ;; psos*) ;; solaris*) case $cc_basename in CC* | sunCC*) # Sun C++ 4.2, 5.x and Centerline C++ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Qoption ld ' ;; gcx*) # Green Hills C++ Compiler _LT_TAGVAR(lt_prog_compiler_pic, $1)='-PIC' ;; *) ;; esac ;; sunos4*) case $cc_basename in CC*) # Sun C++ 4.x _LT_TAGVAR(lt_prog_compiler_pic, $1)='-pic' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' ;; lcc*) # Lucid _LT_TAGVAR(lt_prog_compiler_pic, $1)='-pic' ;; *) ;; esac ;; sysv5* | unixware* | sco3.2v5* | sco5v6* | OpenUNIX*) case $cc_basename in CC*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' ;; esac ;; tandem*) case $cc_basename in NCC*) # NonStop-UX NCC 3.20 _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' ;; *) ;; esac ;; vxworks*) ;; *) _LT_TAGVAR(lt_prog_compiler_can_build_shared, $1)=no ;; esac fi ], [ if test "$GCC" = yes; then _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_static, $1)='-static' case $host_os in aix*) # All AIX code is PIC. if test "$host_cpu" = ia64; then # AIX 5 now supports IA64 processor _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' fi ;; amigaos*) case $host_cpu in powerpc) # see comment about AmigaOS4 .so support _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' ;; m68k) # FIXME: we need at least 68020 code to build shared libraries, but # adding the `-m68020' flag to GCC prevents building anything better, # like `-m68040'. _LT_TAGVAR(lt_prog_compiler_pic, $1)='-m68020 -resident32 -malways-restore-a4' ;; esac ;; beos* | irix5* | irix6* | nonstopux* | osf3* | osf4* | osf5*) # PIC is the default for these OSes. ;; mingw* | cygwin* | pw32* | os2* | cegcc*) # This hack is so that the source file can tell whether it is being # built for inclusion in a dll (and should export symbols for example). # Although the cygwin gcc ignores -fPIC, still need this for old-style # (--disable-auto-import) libraries m4_if([$1], [GCJ], [], [_LT_TAGVAR(lt_prog_compiler_pic, $1)='-DDLL_EXPORT']) ;; darwin* | rhapsody*) # PIC is the default on this platform # Common symbols not allowed in MH_DYLIB files _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fno-common' ;; haiku*) # PIC is the default for Haiku. # The "-static" flag exists, but is broken. _LT_TAGVAR(lt_prog_compiler_static, $1)= ;; hpux*) # PIC is the default for 64-bit PA HP-UX, but not for 32-bit # PA HP-UX. On IA64 HP-UX, PIC is the default but the pic flag # sets the default TLS model and affects inlining. case $host_cpu in hppa*64*) # +Z the default ;; *) _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' ;; esac ;; interix[[3-9]]*) # Interix 3.x gcc -fpic/-fPIC options generate broken code. # Instead, we relocate shared libraries at runtime. ;; msdosdjgpp*) # Just because we use GCC doesn't mean we suddenly get shared libraries # on systems that don't support them. _LT_TAGVAR(lt_prog_compiler_can_build_shared, $1)=no enable_shared=no ;; *nto* | *qnx*) # QNX uses GNU C++, but need to define -shared option too, otherwise # it will coredump. _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC -shared' ;; sysv4*MP*) if test -d /usr/nec; then _LT_TAGVAR(lt_prog_compiler_pic, $1)=-Kconform_pic fi ;; *) _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' ;; esac case $cc_basename in nvcc*) # Cuda Compiler Driver 2.2 _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Xlinker ' if test -n "$_LT_TAGVAR(lt_prog_compiler_pic, $1)"; then _LT_TAGVAR(lt_prog_compiler_pic, $1)="-Xcompiler $_LT_TAGVAR(lt_prog_compiler_pic, $1)" fi ;; esac else # PORTME Check for flag to pass linker flags through the system compiler. case $host_os in aix*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' if test "$host_cpu" = ia64; then # AIX 5 now supports IA64 processor _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' else _LT_TAGVAR(lt_prog_compiler_static, $1)='-bnso -bI:/lib/syscalls.exp' fi ;; mingw* | cygwin* | pw32* | os2* | cegcc*) # This hack is so that the source file can tell whether it is being # built for inclusion in a dll (and should export symbols for example). m4_if([$1], [GCJ], [], [_LT_TAGVAR(lt_prog_compiler_pic, $1)='-DDLL_EXPORT']) ;; hpux9* | hpux10* | hpux11*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' # PIC is the default for IA64 HP-UX and 64-bit HP-UX, but # not for PA HP-UX. case $host_cpu in hppa*64*|ia64*) # +Z the default ;; *) _LT_TAGVAR(lt_prog_compiler_pic, $1)='+Z' ;; esac # Is there a better lt_prog_compiler_static that works with the bundled CC? _LT_TAGVAR(lt_prog_compiler_static, $1)='${wl}-a ${wl}archive' ;; irix5* | irix6* | nonstopux*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' # PIC (with -KPIC) is the default. _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared' ;; linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*) case $cc_basename in # old Intel for x86_64 which still supported -KPIC. ecc*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-static' ;; # icc used to be incompatible with GCC. # ICC 10 doesn't accept -KPIC any more. icc* | ifort*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-static' ;; # Lahey Fortran 8.1. lf95*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_pic, $1)='--shared' _LT_TAGVAR(lt_prog_compiler_static, $1)='--static' ;; nagfor*) # NAG Fortran compiler _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,-Wl,,' _LT_TAGVAR(lt_prog_compiler_pic, $1)='-PIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' ;; pgcc* | pgf77* | pgf90* | pgf95* | pgfortran*) # Portland Group compilers (*not* the Pentium gcc compiler, # which looks to be a dead project) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fpic' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' ;; ccc*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' # All Alpha code is PIC. _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared' ;; xl* | bgxl* | bgf* | mpixl*) # IBM XL C 8.0/Fortran 10.1, 11.1 on PPC and BlueGene _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_pic, $1)='-qpic' _LT_TAGVAR(lt_prog_compiler_static, $1)='-qstaticlink' ;; *) case `$CC -V 2>&1 | sed 5q` in *Sun\ Ceres\ Fortran* | *Sun*Fortran*\ [[1-7]].* | *Sun*Fortran*\ 8.[[0-3]]*) # Sun Fortran 8.3 passes all unrecognized flags to the linker _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' _LT_TAGVAR(lt_prog_compiler_wl, $1)='' ;; *Sun\ F* | *Sun*Fortran*) _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Qoption ld ' ;; *Sun\ C*) # Sun C 5.9 _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' ;; *Intel*\ [[CF]]*Compiler*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-static' ;; *Portland\ Group*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fpic' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' ;; esac ;; esac ;; newsos6) _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' ;; *nto* | *qnx*) # QNX uses GNU C++, but need to define -shared option too, otherwise # it will coredump. _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC -shared' ;; osf3* | osf4* | osf5*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' # All OSF/1 code is PIC. _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared' ;; rdos*) _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared' ;; solaris*) _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' case $cc_basename in f77* | f90* | f95* | sunf77* | sunf90* | sunf95*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Qoption ld ';; *) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,';; esac ;; sunos4*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Qoption ld ' _LT_TAGVAR(lt_prog_compiler_pic, $1)='-PIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' ;; sysv4 | sysv4.2uw2* | sysv4.3*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' ;; sysv4*MP*) if test -d /usr/nec ;then _LT_TAGVAR(lt_prog_compiler_pic, $1)='-Kconform_pic' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' fi ;; sysv5* | unixware* | sco3.2v5* | sco5v6* | OpenUNIX*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' ;; unicos*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_can_build_shared, $1)=no ;; uts4*) _LT_TAGVAR(lt_prog_compiler_pic, $1)='-pic' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' ;; *) _LT_TAGVAR(lt_prog_compiler_can_build_shared, $1)=no ;; esac fi ]) case $host_os in # For platforms which do not support PIC, -DPIC is meaningless: *djgpp*) _LT_TAGVAR(lt_prog_compiler_pic, $1)= ;; *) _LT_TAGVAR(lt_prog_compiler_pic, $1)="$_LT_TAGVAR(lt_prog_compiler_pic, $1)@&t@m4_if([$1],[],[ -DPIC],[m4_if([$1],[CXX],[ -DPIC],[])])" ;; esac AC_CACHE_CHECK([for $compiler option to produce PIC], [_LT_TAGVAR(lt_cv_prog_compiler_pic, $1)], [_LT_TAGVAR(lt_cv_prog_compiler_pic, $1)=$_LT_TAGVAR(lt_prog_compiler_pic, $1)]) _LT_TAGVAR(lt_prog_compiler_pic, $1)=$_LT_TAGVAR(lt_cv_prog_compiler_pic, $1) # # Check to make sure the PIC flag actually works. # if test -n "$_LT_TAGVAR(lt_prog_compiler_pic, $1)"; then _LT_COMPILER_OPTION([if $compiler PIC flag $_LT_TAGVAR(lt_prog_compiler_pic, $1) works], [_LT_TAGVAR(lt_cv_prog_compiler_pic_works, $1)], [$_LT_TAGVAR(lt_prog_compiler_pic, $1)@&t@m4_if([$1],[],[ -DPIC],[m4_if([$1],[CXX],[ -DPIC],[])])], [], [case $_LT_TAGVAR(lt_prog_compiler_pic, $1) in "" | " "*) ;; *) _LT_TAGVAR(lt_prog_compiler_pic, $1)=" $_LT_TAGVAR(lt_prog_compiler_pic, $1)" ;; esac], [_LT_TAGVAR(lt_prog_compiler_pic, $1)= _LT_TAGVAR(lt_prog_compiler_can_build_shared, $1)=no]) fi _LT_TAGDECL([pic_flag], [lt_prog_compiler_pic], [1], [Additional compiler flags for building library objects]) _LT_TAGDECL([wl], [lt_prog_compiler_wl], [1], [How to pass a linker flag through the compiler]) # # Check to make sure the static flag actually works. # wl=$_LT_TAGVAR(lt_prog_compiler_wl, $1) eval lt_tmp_static_flag=\"$_LT_TAGVAR(lt_prog_compiler_static, $1)\" _LT_LINKER_OPTION([if $compiler static flag $lt_tmp_static_flag works], _LT_TAGVAR(lt_cv_prog_compiler_static_works, $1), $lt_tmp_static_flag, [], [_LT_TAGVAR(lt_prog_compiler_static, $1)=]) _LT_TAGDECL([link_static_flag], [lt_prog_compiler_static], [1], [Compiler flag to prevent dynamic linking]) ])# _LT_COMPILER_PIC # _LT_LINKER_SHLIBS([TAGNAME]) # ---------------------------- # See if the linker supports building shared libraries. m4_defun([_LT_LINKER_SHLIBS], [AC_REQUIRE([LT_PATH_LD])dnl AC_REQUIRE([LT_PATH_NM])dnl m4_require([_LT_PATH_MANIFEST_TOOL])dnl m4_require([_LT_FILEUTILS_DEFAULTS])dnl m4_require([_LT_DECL_EGREP])dnl m4_require([_LT_DECL_SED])dnl m4_require([_LT_CMD_GLOBAL_SYMBOLS])dnl m4_require([_LT_TAG_COMPILER])dnl AC_MSG_CHECKING([whether the $compiler linker ($LD) supports shared libraries]) m4_if([$1], [CXX], [ _LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols' _LT_TAGVAR(exclude_expsyms, $1)=['_GLOBAL_OFFSET_TABLE_|_GLOBAL__F[ID]_.*'] case $host_os in aix[[4-9]]*) # If we're using GNU nm, then we don't want the "-C" option. # -C means demangle to AIX nm, but means don't demangle with GNU nm # Also, AIX nm treats weak defined symbols like other global defined # symbols, whereas GNU nm marks them as "W". if $NM -V 2>&1 | $GREP 'GNU' > /dev/null; then _LT_TAGVAR(export_symbols_cmds, $1)='$NM -Bpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B") || (\$ 2 == "W")) && ([substr](\$ 3,1,1) != ".")) { print \$ 3 } }'\'' | sort -u > $export_symbols' else _LT_TAGVAR(export_symbols_cmds, $1)='$NM -BCpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B")) && ([substr](\$ 3,1,1) != ".")) { print \$ 3 } }'\'' | sort -u > $export_symbols' fi ;; pw32*) _LT_TAGVAR(export_symbols_cmds, $1)="$ltdll_cmds" ;; cygwin* | mingw* | cegcc*) case $cc_basename in cl*) _LT_TAGVAR(exclude_expsyms, $1)='_NULL_IMPORT_DESCRIPTOR|_IMPORT_DESCRIPTOR_.*' ;; *) _LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[[BCDGRS]][[ ]]/s/.*[[ ]]\([[^ ]]*\)/\1 DATA/;s/^.*[[ ]]__nm__\([[^ ]]*\)[[ ]][[^ ]]*/\1 DATA/;/^I[[ ]]/d;/^[[AITW]][[ ]]/s/.* //'\'' | sort | uniq > $export_symbols' _LT_TAGVAR(exclude_expsyms, $1)=['[_]+GLOBAL_OFFSET_TABLE_|[_]+GLOBAL__[FID]_.*|[_]+head_[A-Za-z0-9_]+_dll|[A-Za-z0-9_]+_dll_iname'] ;; esac ;; linux* | k*bsd*-gnu | gnu*) _LT_TAGVAR(link_all_deplibs, $1)=no ;; *) _LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols' ;; esac ], [ runpath_var= _LT_TAGVAR(allow_undefined_flag, $1)= _LT_TAGVAR(always_export_symbols, $1)=no _LT_TAGVAR(archive_cmds, $1)= _LT_TAGVAR(archive_expsym_cmds, $1)= _LT_TAGVAR(compiler_needs_object, $1)=no _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=no _LT_TAGVAR(export_dynamic_flag_spec, $1)= _LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols' _LT_TAGVAR(hardcode_automatic, $1)=no _LT_TAGVAR(hardcode_direct, $1)=no _LT_TAGVAR(hardcode_direct_absolute, $1)=no _LT_TAGVAR(hardcode_libdir_flag_spec, $1)= _LT_TAGVAR(hardcode_libdir_separator, $1)= _LT_TAGVAR(hardcode_minus_L, $1)=no _LT_TAGVAR(hardcode_shlibpath_var, $1)=unsupported _LT_TAGVAR(inherit_rpath, $1)=no _LT_TAGVAR(link_all_deplibs, $1)=unknown _LT_TAGVAR(module_cmds, $1)= _LT_TAGVAR(module_expsym_cmds, $1)= _LT_TAGVAR(old_archive_from_new_cmds, $1)= _LT_TAGVAR(old_archive_from_expsyms_cmds, $1)= _LT_TAGVAR(thread_safe_flag_spec, $1)= _LT_TAGVAR(whole_archive_flag_spec, $1)= # include_expsyms should be a list of space-separated symbols to be *always* # included in the symbol list _LT_TAGVAR(include_expsyms, $1)= # exclude_expsyms can be an extended regexp of symbols to exclude # it will be wrapped by ` (' and `)$', so one must not match beginning or # end of line. Example: `a|bc|.*d.*' will exclude the symbols `a' and `bc', # as well as any symbol that contains `d'. _LT_TAGVAR(exclude_expsyms, $1)=['_GLOBAL_OFFSET_TABLE_|_GLOBAL__F[ID]_.*'] # Although _GLOBAL_OFFSET_TABLE_ is a valid symbol C name, most a.out # platforms (ab)use it in PIC code, but their linkers get confused if # the symbol is explicitly referenced. Since portable code cannot # rely on this symbol name, it's probably fine to never include it in # preloaded symbol tables. # Exclude shared library initialization/finalization symbols. dnl Note also adjust exclude_expsyms for C++ above. extract_expsyms_cmds= case $host_os in cygwin* | mingw* | pw32* | cegcc*) # FIXME: the MSVC++ port hasn't been tested in a loooong time # When not using gcc, we currently assume that we are using # Microsoft Visual C++. if test "$GCC" != yes; then with_gnu_ld=no fi ;; interix*) # we just hope/assume this is gcc and not c89 (= MSVC++) with_gnu_ld=yes ;; openbsd*) with_gnu_ld=no ;; linux* | k*bsd*-gnu | gnu*) _LT_TAGVAR(link_all_deplibs, $1)=no ;; esac _LT_TAGVAR(ld_shlibs, $1)=yes # On some targets, GNU ld is compatible enough with the native linker # that we're better off using the native interface for both. lt_use_gnu_ld_interface=no if test "$with_gnu_ld" = yes; then case $host_os in aix*) # The AIX port of GNU ld has always aspired to compatibility # with the native linker. However, as the warning in the GNU ld # block says, versions before 2.19.5* couldn't really create working # shared libraries, regardless of the interface used. case `$LD -v 2>&1` in *\ \(GNU\ Binutils\)\ 2.19.5*) ;; *\ \(GNU\ Binutils\)\ 2.[[2-9]]*) ;; *\ \(GNU\ Binutils\)\ [[3-9]]*) ;; *) lt_use_gnu_ld_interface=yes ;; esac ;; *) lt_use_gnu_ld_interface=yes ;; esac fi if test "$lt_use_gnu_ld_interface" = yes; then # If archive_cmds runs LD, not CC, wlarc should be empty wlarc='${wl}' # Set some defaults for GNU ld with shared library support. These # are reset later if shared libraries are not supported. Putting them # here allows them to be overridden if necessary. runpath_var=LD_RUN_PATH _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir' _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}--export-dynamic' # ancient GNU ld didn't support --whole-archive et. al. if $LD --help 2>&1 | $GREP 'no-whole-archive' > /dev/null; then _LT_TAGVAR(whole_archive_flag_spec, $1)="$wlarc"'--whole-archive$convenience '"$wlarc"'--no-whole-archive' else _LT_TAGVAR(whole_archive_flag_spec, $1)= fi supports_anon_versioning=no case `$LD -v 2>&1` in *GNU\ gold*) supports_anon_versioning=yes ;; *\ [[01]].* | *\ 2.[[0-9]].* | *\ 2.10.*) ;; # catch versions < 2.11 *\ 2.11.93.0.2\ *) supports_anon_versioning=yes ;; # RH7.3 ... *\ 2.11.92.0.12\ *) supports_anon_versioning=yes ;; # Mandrake 8.2 ... *\ 2.11.*) ;; # other 2.11 versions *) supports_anon_versioning=yes ;; esac # See if GNU ld supports shared libraries. case $host_os in aix[[3-9]]*) # On AIX/PPC, the GNU linker is very broken if test "$host_cpu" != ia64; then _LT_TAGVAR(ld_shlibs, $1)=no cat <<_LT_EOF 1>&2 *** Warning: the GNU linker, at least up to release 2.19, is reported *** to be unable to reliably create shared libraries on AIX. *** Therefore, libtool is disabling shared libraries support. If you *** really care for shared libraries, you may want to install binutils *** 2.20 or above, or modify your PATH so that a non-GNU linker is found. *** You will then need to restart the configuration process. _LT_EOF fi ;; amigaos*) case $host_cpu in powerpc) # see comment about AmigaOS4 .so support _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='' ;; m68k) _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/a2ixlibrary.data~$ECHO "#define NAME $libname" > $output_objdir/a2ixlibrary.data~$ECHO "#define LIBRARY_ID 1" >> $output_objdir/a2ixlibrary.data~$ECHO "#define VERSION $major" >> $output_objdir/a2ixlibrary.data~$ECHO "#define REVISION $revision" >> $output_objdir/a2ixlibrary.data~$AR $AR_FLAGS $lib $libobjs~$RANLIB $lib~(cd $output_objdir && a2ixlibrary -32)' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' _LT_TAGVAR(hardcode_minus_L, $1)=yes ;; esac ;; beos*) if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then _LT_TAGVAR(allow_undefined_flag, $1)=unsupported # Joseph Beckenbach says some releases of gcc # support --undefined. This deserves some investigation. FIXME _LT_TAGVAR(archive_cmds, $1)='$CC -nostart $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' else _LT_TAGVAR(ld_shlibs, $1)=no fi ;; cygwin* | mingw* | pw32* | cegcc*) # _LT_TAGVAR(hardcode_libdir_flag_spec, $1) is actually meaningless, # as there is no search path for DLLs. _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}--export-all-symbols' _LT_TAGVAR(allow_undefined_flag, $1)=unsupported _LT_TAGVAR(always_export_symbols, $1)=no _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes _LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[[BCDGRS]][[ ]]/s/.*[[ ]]\([[^ ]]*\)/\1 DATA/;s/^.*[[ ]]__nm__\([[^ ]]*\)[[ ]][[^ ]]*/\1 DATA/;/^I[[ ]]/d;/^[[AITW]][[ ]]/s/.* //'\'' | sort | uniq > $export_symbols' _LT_TAGVAR(exclude_expsyms, $1)=['[_]+GLOBAL_OFFSET_TABLE_|[_]+GLOBAL__[FID]_.*|[_]+head_[A-Za-z0-9_]+_dll|[A-Za-z0-9_]+_dll_iname'] if $LD --help 2>&1 | $GREP 'auto-import' > /dev/null; then _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags -o $output_objdir/$soname ${wl}--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib' # If the export-symbols file already is a .def file (1st line # is EXPORTS), use it as is; otherwise, prepend... _LT_TAGVAR(archive_expsym_cmds, $1)='if test "x`$SED 1q $export_symbols`" = xEXPORTS; then cp $export_symbols $output_objdir/$soname.def; else echo EXPORTS > $output_objdir/$soname.def; cat $export_symbols >> $output_objdir/$soname.def; fi~ $CC -shared $output_objdir/$soname.def $libobjs $deplibs $compiler_flags -o $output_objdir/$soname ${wl}--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib' else _LT_TAGVAR(ld_shlibs, $1)=no fi ;; haiku*) _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' _LT_TAGVAR(link_all_deplibs, $1)=yes ;; interix[[3-9]]*) _LT_TAGVAR(hardcode_direct, $1)=no _LT_TAGVAR(hardcode_shlibpath_var, $1)=no _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath,$libdir' _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E' # Hack: On Interix 3.x, we cannot compile PIC because of a broken gcc. # Instead, shared libraries are loaded at an image base (0x10000000 by # default) and relocated if they conflict, which is a slow very memory # consuming and fragmenting process. To avoid this, we pick a random, # 256 KiB-aligned image base between 0x50000000 and 0x6FFC0000 at link # time. Moving up from 0x10000000 also allows more sbrk(2) space. _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-h,$soname ${wl}--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='sed "s,^,_," $export_symbols >$output_objdir/$soname.expsym~$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-h,$soname ${wl}--retain-symbols-file,$output_objdir/$soname.expsym ${wl}--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' ;; gnu* | linux* | tpf* | k*bsd*-gnu | kopensolaris*-gnu) tmp_diet=no if test "$host_os" = linux-dietlibc; then case $cc_basename in diet\ *) tmp_diet=yes;; # linux-dietlibc with static linking (!diet-dyn) esac fi if $LD --help 2>&1 | $EGREP ': supported targets:.* elf' > /dev/null \ && test "$tmp_diet" = no then tmp_addflag=' $pic_flag' tmp_sharedflag='-shared' case $cc_basename,$host_cpu in pgcc*) # Portland Group C compiler _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` ${wl}--no-whole-archive' tmp_addflag=' $pic_flag' ;; pgf77* | pgf90* | pgf95* | pgfortran*) # Portland Group f77 and f90 compilers _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` ${wl}--no-whole-archive' tmp_addflag=' $pic_flag -Mnomain' ;; ecc*,ia64* | icc*,ia64*) # Intel C compiler on ia64 tmp_addflag=' -i_dynamic' ;; efc*,ia64* | ifort*,ia64*) # Intel Fortran compiler on ia64 tmp_addflag=' -i_dynamic -nofor_main' ;; ifc* | ifort*) # Intel Fortran compiler tmp_addflag=' -nofor_main' ;; lf95*) # Lahey Fortran 8.1 _LT_TAGVAR(whole_archive_flag_spec, $1)= tmp_sharedflag='--shared' ;; xl[[cC]]* | bgxl[[cC]]* | mpixl[[cC]]*) # IBM XL C 8.0 on PPC (deal with xlf below) tmp_sharedflag='-qmkshrobj' tmp_addflag= ;; nvcc*) # Cuda Compiler Driver 2.2 _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` ${wl}--no-whole-archive' _LT_TAGVAR(compiler_needs_object, $1)=yes ;; esac case `$CC -V 2>&1 | sed 5q` in *Sun\ C*) # Sun C 5.9 _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}--whole-archive`new_convenience=; for conv in $convenience\"\"; do test -z \"$conv\" || new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` ${wl}--no-whole-archive' _LT_TAGVAR(compiler_needs_object, $1)=yes tmp_sharedflag='-G' ;; *Sun\ F*) # Sun Fortran 8.3 tmp_sharedflag='-G' ;; esac _LT_TAGVAR(archive_cmds, $1)='$CC '"$tmp_sharedflag""$tmp_addflag"' $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' if test "x$supports_anon_versioning" = xyes; then _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $output_objdir/$libname.ver~ cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~ echo "local: *; };" >> $output_objdir/$libname.ver~ $CC '"$tmp_sharedflag""$tmp_addflag"' $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-version-script ${wl}$output_objdir/$libname.ver -o $lib' fi case $cc_basename in xlf* | bgf* | bgxlf* | mpixlf*) # IBM XL Fortran 10.1 on PPC cannot create shared libs itself _LT_TAGVAR(whole_archive_flag_spec, $1)='--whole-archive$convenience --no-whole-archive' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir' _LT_TAGVAR(archive_cmds, $1)='$LD -shared $libobjs $deplibs $linker_flags -soname $soname -o $lib' if test "x$supports_anon_versioning" = xyes; then _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $output_objdir/$libname.ver~ cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~ echo "local: *; };" >> $output_objdir/$libname.ver~ $LD -shared $libobjs $deplibs $linker_flags -soname $soname -version-script $output_objdir/$libname.ver -o $lib' fi ;; esac else _LT_TAGVAR(ld_shlibs, $1)=no fi ;; netbsd* | netbsdelf*-gnu) if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then _LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable $libobjs $deplibs $linker_flags -o $lib' wlarc= else _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' fi ;; solaris*) if $LD -v 2>&1 | $GREP 'BFD 2\.8' > /dev/null; then _LT_TAGVAR(ld_shlibs, $1)=no cat <<_LT_EOF 1>&2 *** Warning: The releases 2.8.* of the GNU linker cannot reliably *** create shared libraries on Solaris systems. Therefore, libtool *** is disabling shared libraries support. We urge you to upgrade GNU *** binutils to release 2.9.1 or newer. Another option is to modify *** your PATH or compiler configuration so that the native linker is *** used, and then restart. _LT_EOF elif $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' else _LT_TAGVAR(ld_shlibs, $1)=no fi ;; sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX*) case `$LD -v 2>&1` in *\ [[01]].* | *\ 2.[[0-9]].* | *\ 2.1[[0-5]].*) _LT_TAGVAR(ld_shlibs, $1)=no cat <<_LT_EOF 1>&2 *** Warning: Releases of the GNU linker prior to 2.16.91.0.3 can not *** reliably create shared libraries on SCO systems. Therefore, libtool *** is disabling shared libraries support. We urge you to upgrade GNU *** binutils to release 2.16.91.0.3 or newer. Another option is to modify *** your PATH or compiler configuration so that the native linker is *** used, and then restart. _LT_EOF ;; *) # For security reasons, it is highly recommended that you always # use absolute paths for naming shared libraries, and exclude the # DT_RUNPATH tag from executables and libraries. But doing so # requires that you compile everything twice, which is a pain. if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir' _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' else _LT_TAGVAR(ld_shlibs, $1)=no fi ;; esac ;; sunos4*) _LT_TAGVAR(archive_cmds, $1)='$LD -assert pure-text -Bshareable -o $lib $libobjs $deplibs $linker_flags' wlarc= _LT_TAGVAR(hardcode_direct, $1)=yes _LT_TAGVAR(hardcode_shlibpath_var, $1)=no ;; *) if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' else _LT_TAGVAR(ld_shlibs, $1)=no fi ;; esac if test "$_LT_TAGVAR(ld_shlibs, $1)" = no; then runpath_var= _LT_TAGVAR(hardcode_libdir_flag_spec, $1)= _LT_TAGVAR(export_dynamic_flag_spec, $1)= _LT_TAGVAR(whole_archive_flag_spec, $1)= fi else # PORTME fill in a description of your system's linker (not GNU ld) case $host_os in aix3*) _LT_TAGVAR(allow_undefined_flag, $1)=unsupported _LT_TAGVAR(always_export_symbols, $1)=yes _LT_TAGVAR(archive_expsym_cmds, $1)='$LD -o $output_objdir/$soname $libobjs $deplibs $linker_flags -bE:$export_symbols -T512 -H512 -bM:SRE~$AR $AR_FLAGS $lib $output_objdir/$soname' # Note: this linker hardcodes the directories in LIBPATH if there # are no directories specified by -L. _LT_TAGVAR(hardcode_minus_L, $1)=yes if test "$GCC" = yes && test -z "$lt_prog_compiler_static"; then # Neither direct hardcoding nor static linking is supported with a # broken collect2. _LT_TAGVAR(hardcode_direct, $1)=unsupported fi ;; aix[[4-9]]*) if test "$host_cpu" = ia64; then # On IA64, the linker does run time linking by default, so we don't # have to do anything special. aix_use_runtimelinking=no exp_sym_flag='-Bexport' no_entry_flag="" else # If we're using GNU nm, then we don't want the "-C" option. # -C means demangle to AIX nm, but means don't demangle with GNU nm # Also, AIX nm treats weak defined symbols like other global # defined symbols, whereas GNU nm marks them as "W". if $NM -V 2>&1 | $GREP 'GNU' > /dev/null; then _LT_TAGVAR(export_symbols_cmds, $1)='$NM -Bpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B") || (\$ 2 == "W")) && ([substr](\$ 3,1,1) != ".")) { print \$ 3 } }'\'' | sort -u > $export_symbols' else _LT_TAGVAR(export_symbols_cmds, $1)='$NM -BCpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B")) && ([substr](\$ 3,1,1) != ".")) { print \$ 3 } }'\'' | sort -u > $export_symbols' fi aix_use_runtimelinking=no # Test if we are trying to use run time linking or normal # AIX style linking. If -brtl is somewhere in LDFLAGS, we # need to do runtime linking. case $host_os in aix4.[[23]]|aix4.[[23]].*|aix[[5-9]]*) for ld_flag in $LDFLAGS; do if (test $ld_flag = "-brtl" || test $ld_flag = "-Wl,-brtl"); then aix_use_runtimelinking=yes break fi done ;; esac exp_sym_flag='-bexport' no_entry_flag='-bnoentry' fi # When large executables or shared objects are built, AIX ld can # have problems creating the table of contents. If linking a library # or program results in "error TOC overflow" add -mminimal-toc to # CXXFLAGS/CFLAGS for g++/gcc. In the cases where that is not # enough to fix the problem, add -Wl,-bbigtoc to LDFLAGS. _LT_TAGVAR(archive_cmds, $1)='' _LT_TAGVAR(hardcode_direct, $1)=yes _LT_TAGVAR(hardcode_direct_absolute, $1)=yes _LT_TAGVAR(hardcode_libdir_separator, $1)=':' _LT_TAGVAR(link_all_deplibs, $1)=yes _LT_TAGVAR(file_list_spec, $1)='${wl}-f,' if test "$GCC" = yes; then case $host_os in aix4.[[012]]|aix4.[[012]].*) # We only want to do this on AIX 4.2 and lower, the check # below for broken collect2 doesn't work under 4.3+ collect2name=`${CC} -print-prog-name=collect2` if test -f "$collect2name" && strings "$collect2name" | $GREP resolve_lib_name >/dev/null then # We have reworked collect2 : else # We have old collect2 _LT_TAGVAR(hardcode_direct, $1)=unsupported # It fails to find uninstalled libraries when the uninstalled # path is not listed in the libpath. Setting hardcode_minus_L # to unsupported forces relinking _LT_TAGVAR(hardcode_minus_L, $1)=yes _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' _LT_TAGVAR(hardcode_libdir_separator, $1)= fi ;; esac shared_flag='-shared' if test "$aix_use_runtimelinking" = yes; then shared_flag="$shared_flag "'${wl}-G' fi _LT_TAGVAR(link_all_deplibs, $1)=no else # not using gcc if test "$host_cpu" = ia64; then # VisualAge C++, Version 5.5 for AIX 5L for IA-64, Beta 3 Release # chokes on -Wl,-G. The following line is correct: shared_flag='-G' else if test "$aix_use_runtimelinking" = yes; then shared_flag='${wl}-G' else shared_flag='${wl}-bM:SRE' fi fi fi _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-bexpall' # It seems that -bexpall does not export symbols beginning with # underscore (_), so it is better to generate a list of symbols to export. _LT_TAGVAR(always_export_symbols, $1)=yes if test "$aix_use_runtimelinking" = yes; then # Warning - without using the other runtime loading flags (-brtl), # -berok will link without error, but may produce a broken library. _LT_TAGVAR(allow_undefined_flag, $1)='-berok' # Determine the default libpath from the value encoded in an # empty executable. _LT_SYS_MODULE_PATH_AIX([$1]) _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-blibpath:$libdir:'"$aix_libpath" _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -o $output_objdir/$soname $libobjs $deplibs '"\${wl}$no_entry_flag"' $compiler_flags `if test "x${allow_undefined_flag}" != "x"; then func_echo_all "${wl}${allow_undefined_flag}"; else :; fi` '"\${wl}$exp_sym_flag:\$export_symbols $shared_flag" else if test "$host_cpu" = ia64; then _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-R $libdir:/usr/lib:/lib' _LT_TAGVAR(allow_undefined_flag, $1)="-z nodefs" _LT_TAGVAR(archive_expsym_cmds, $1)="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs '"\${wl}$no_entry_flag"' $compiler_flags ${wl}${allow_undefined_flag} '"\${wl}$exp_sym_flag:\$export_symbols" else # Determine the default libpath from the value encoded in an # empty executable. _LT_SYS_MODULE_PATH_AIX([$1]) _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-blibpath:$libdir:'"$aix_libpath" # Warning - without using the other run time loading flags, # -berok will link without error, but may produce a broken library. _LT_TAGVAR(no_undefined_flag, $1)=' ${wl}-bernotok' _LT_TAGVAR(allow_undefined_flag, $1)=' ${wl}-berok' if test "$with_gnu_ld" = yes; then # We only use this code for GNU lds that support --whole-archive. _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}--whole-archive$convenience ${wl}--no-whole-archive' else # Exported symbols can be pulled into shared objects from archives _LT_TAGVAR(whole_archive_flag_spec, $1)='$convenience' fi _LT_TAGVAR(archive_cmds_need_lc, $1)=yes # This is similar to how AIX traditionally builds its shared libraries. _LT_TAGVAR(archive_expsym_cmds, $1)="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs ${wl}-bnoentry $compiler_flags ${wl}-bE:$export_symbols${allow_undefined_flag}~$AR $AR_FLAGS $output_objdir/$libname$release.a $output_objdir/$soname' fi fi ;; amigaos*) case $host_cpu in powerpc) # see comment about AmigaOS4 .so support _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='' ;; m68k) _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/a2ixlibrary.data~$ECHO "#define NAME $libname" > $output_objdir/a2ixlibrary.data~$ECHO "#define LIBRARY_ID 1" >> $output_objdir/a2ixlibrary.data~$ECHO "#define VERSION $major" >> $output_objdir/a2ixlibrary.data~$ECHO "#define REVISION $revision" >> $output_objdir/a2ixlibrary.data~$AR $AR_FLAGS $lib $libobjs~$RANLIB $lib~(cd $output_objdir && a2ixlibrary -32)' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' _LT_TAGVAR(hardcode_minus_L, $1)=yes ;; esac ;; bsdi[[45]]*) _LT_TAGVAR(export_dynamic_flag_spec, $1)=-rdynamic ;; cygwin* | mingw* | pw32* | cegcc*) # When not using gcc, we currently assume that we are using # Microsoft Visual C++. # hardcode_libdir_flag_spec is actually meaningless, as there is # no search path for DLLs. case $cc_basename in cl*) # Native MSVC _LT_TAGVAR(hardcode_libdir_flag_spec, $1)=' ' _LT_TAGVAR(allow_undefined_flag, $1)=unsupported _LT_TAGVAR(always_export_symbols, $1)=yes _LT_TAGVAR(file_list_spec, $1)='@' # Tell ltmain to make .lib files, not .a files. libext=lib # Tell ltmain to make .dll files, not .so files. shrext_cmds=".dll" # FIXME: Setting linknames here is a bad hack. _LT_TAGVAR(archive_cmds, $1)='$CC -o $output_objdir/$soname $libobjs $compiler_flags $deplibs -Wl,-dll~linknames=' _LT_TAGVAR(archive_expsym_cmds, $1)='if test "x`$SED 1q $export_symbols`" = xEXPORTS; then sed -n -e 's/\\\\\\\(.*\\\\\\\)/-link\\\ -EXPORT:\\\\\\\1/' -e '1\\\!p' < $export_symbols > $output_objdir/$soname.exp; else sed -e 's/\\\\\\\(.*\\\\\\\)/-link\\\ -EXPORT:\\\\\\\1/' < $export_symbols > $output_objdir/$soname.exp; fi~ $CC -o $tool_output_objdir$soname $libobjs $compiler_flags $deplibs "@$tool_output_objdir$soname.exp" -Wl,-DLL,-IMPLIB:"$tool_output_objdir$libname.dll.lib"~ linknames=' # The linker will not automatically build a static lib if we build a DLL. # _LT_TAGVAR(old_archive_from_new_cmds, $1)='true' _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes _LT_TAGVAR(exclude_expsyms, $1)='_NULL_IMPORT_DESCRIPTOR|_IMPORT_DESCRIPTOR_.*' _LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[[BCDGRS]][[ ]]/s/.*[[ ]]\([[^ ]]*\)/\1,DATA/'\'' | $SED -e '\''/^[[AITW]][[ ]]/s/.*[[ ]]//'\'' | sort | uniq > $export_symbols' # Don't use ranlib _LT_TAGVAR(old_postinstall_cmds, $1)='chmod 644 $oldlib' _LT_TAGVAR(postlink_cmds, $1)='lt_outputfile="@OUTPUT@"~ lt_tool_outputfile="@TOOL_OUTPUT@"~ case $lt_outputfile in *.exe|*.EXE) ;; *) lt_outputfile="$lt_outputfile.exe" lt_tool_outputfile="$lt_tool_outputfile.exe" ;; esac~ if test "$MANIFEST_TOOL" != ":" && test -f "$lt_outputfile.manifest"; then $MANIFEST_TOOL -manifest "$lt_tool_outputfile.manifest" -outputresource:"$lt_tool_outputfile" || exit 1; $RM "$lt_outputfile.manifest"; fi' ;; *) # Assume MSVC wrapper _LT_TAGVAR(hardcode_libdir_flag_spec, $1)=' ' _LT_TAGVAR(allow_undefined_flag, $1)=unsupported # Tell ltmain to make .lib files, not .a files. libext=lib # Tell ltmain to make .dll files, not .so files. shrext_cmds=".dll" # FIXME: Setting linknames here is a bad hack. _LT_TAGVAR(archive_cmds, $1)='$CC -o $lib $libobjs $compiler_flags `func_echo_all "$deplibs" | $SED '\''s/ -lc$//'\''` -link -dll~linknames=' # The linker will automatically build a .lib file if we build a DLL. _LT_TAGVAR(old_archive_from_new_cmds, $1)='true' # FIXME: Should let the user specify the lib program. _LT_TAGVAR(old_archive_cmds, $1)='lib -OUT:$oldlib$oldobjs$old_deplibs' _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes ;; esac ;; darwin* | rhapsody*) _LT_DARWIN_LINKER_FEATURES($1) ;; dgux*) _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' _LT_TAGVAR(hardcode_shlibpath_var, $1)=no ;; # FreeBSD 2.2.[012] allows us to include c++rt0.o to get C++ constructor # support. Future versions do this automatically, but an explicit c++rt0.o # does not break anything, and helps significantly (at the cost of a little # extra space). freebsd2.2*) _LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags /usr/lib/c++rt0.o' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' _LT_TAGVAR(hardcode_direct, $1)=yes _LT_TAGVAR(hardcode_shlibpath_var, $1)=no ;; # Unfortunately, older versions of FreeBSD 2 do not have this feature. freebsd2.*) _LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags' _LT_TAGVAR(hardcode_direct, $1)=yes _LT_TAGVAR(hardcode_minus_L, $1)=yes _LT_TAGVAR(hardcode_shlibpath_var, $1)=no ;; # FreeBSD 3 and greater uses gcc -shared to do shared libraries. freebsd* | dragonfly*) _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' _LT_TAGVAR(hardcode_direct, $1)=yes _LT_TAGVAR(hardcode_shlibpath_var, $1)=no ;; hpux9*) if test "$GCC" = yes; then _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/$soname~$CC -shared $pic_flag ${wl}+b ${wl}$install_libdir -o $output_objdir/$soname $libobjs $deplibs $compiler_flags~test $output_objdir/$soname = $lib || mv $output_objdir/$soname $lib' else _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/$soname~$LD -b +b $install_libdir -o $output_objdir/$soname $libobjs $deplibs $linker_flags~test $output_objdir/$soname = $lib || mv $output_objdir/$soname $lib' fi _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}+b ${wl}$libdir' _LT_TAGVAR(hardcode_libdir_separator, $1)=: _LT_TAGVAR(hardcode_direct, $1)=yes # hardcode_minus_L: Not really in the search PATH, # but as the default location of the library. _LT_TAGVAR(hardcode_minus_L, $1)=yes _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E' ;; hpux10*) if test "$GCC" = yes && test "$with_gnu_ld" = no; then _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $libobjs $deplibs $compiler_flags' else _LT_TAGVAR(archive_cmds, $1)='$LD -b +h $soname +b $install_libdir -o $lib $libobjs $deplibs $linker_flags' fi if test "$with_gnu_ld" = no; then _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}+b ${wl}$libdir' _LT_TAGVAR(hardcode_libdir_separator, $1)=: _LT_TAGVAR(hardcode_direct, $1)=yes _LT_TAGVAR(hardcode_direct_absolute, $1)=yes _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E' # hardcode_minus_L: Not really in the search PATH, # but as the default location of the library. _LT_TAGVAR(hardcode_minus_L, $1)=yes fi ;; hpux11*) if test "$GCC" = yes && test "$with_gnu_ld" = no; then case $host_cpu in hppa*64*) _LT_TAGVAR(archive_cmds, $1)='$CC -shared ${wl}+h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags' ;; ia64*) _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag ${wl}+h ${wl}$soname ${wl}+nodefaultrpath -o $lib $libobjs $deplibs $compiler_flags' ;; *) _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $libobjs $deplibs $compiler_flags' ;; esac else case $host_cpu in hppa*64*) _LT_TAGVAR(archive_cmds, $1)='$CC -b ${wl}+h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags' ;; ia64*) _LT_TAGVAR(archive_cmds, $1)='$CC -b ${wl}+h ${wl}$soname ${wl}+nodefaultrpath -o $lib $libobjs $deplibs $compiler_flags' ;; *) m4_if($1, [], [ # Older versions of the 11.00 compiler do not understand -b yet # (HP92453-01 A.11.01.20 doesn't, HP92453-01 B.11.X.35175-35176.GP does) _LT_LINKER_OPTION([if $CC understands -b], _LT_TAGVAR(lt_cv_prog_compiler__b, $1), [-b], [_LT_TAGVAR(archive_cmds, $1)='$CC -b ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $libobjs $deplibs $compiler_flags'], [_LT_TAGVAR(archive_cmds, $1)='$LD -b +h $soname +b $install_libdir -o $lib $libobjs $deplibs $linker_flags'])], [_LT_TAGVAR(archive_cmds, $1)='$CC -b ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $libobjs $deplibs $compiler_flags']) ;; esac fi if test "$with_gnu_ld" = no; then _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}+b ${wl}$libdir' _LT_TAGVAR(hardcode_libdir_separator, $1)=: case $host_cpu in hppa*64*|ia64*) _LT_TAGVAR(hardcode_direct, $1)=no _LT_TAGVAR(hardcode_shlibpath_var, $1)=no ;; *) _LT_TAGVAR(hardcode_direct, $1)=yes _LT_TAGVAR(hardcode_direct_absolute, $1)=yes _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E' # hardcode_minus_L: Not really in the search PATH, # but as the default location of the library. _LT_TAGVAR(hardcode_minus_L, $1)=yes ;; esac fi ;; irix5* | irix6* | nonstopux*) if test "$GCC" = yes; then _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && func_echo_all "${wl}-set_version ${wl}$verstring"` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib' # Try to use the -exported_symbol ld option, if it does not # work, assume that -exports_file does not work either and # implicitly export all symbols. # This should be the same for all languages, so no per-tag cache variable. AC_CACHE_CHECK([whether the $host_os linker accepts -exported_symbol], [lt_cv_irix_exported_symbol], [save_LDFLAGS="$LDFLAGS" LDFLAGS="$LDFLAGS -shared ${wl}-exported_symbol ${wl}foo ${wl}-update_registry ${wl}/dev/null" AC_LINK_IFELSE( [AC_LANG_SOURCE( [AC_LANG_CASE([C], [[int foo (void) { return 0; }]], [C++], [[int foo (void) { return 0; }]], [Fortran 77], [[ subroutine foo end]], [Fortran], [[ subroutine foo end]])])], [lt_cv_irix_exported_symbol=yes], [lt_cv_irix_exported_symbol=no]) LDFLAGS="$save_LDFLAGS"]) if test "$lt_cv_irix_exported_symbol" = yes; then _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && func_echo_all "${wl}-set_version ${wl}$verstring"` ${wl}-update_registry ${wl}${output_objdir}/so_locations ${wl}-exports_file ${wl}$export_symbols -o $lib' fi else _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry ${output_objdir}/so_locations -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry ${output_objdir}/so_locations -exports_file $export_symbols -o $lib' fi _LT_TAGVAR(archive_cmds_need_lc, $1)='no' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir' _LT_TAGVAR(hardcode_libdir_separator, $1)=: _LT_TAGVAR(inherit_rpath, $1)=yes _LT_TAGVAR(link_all_deplibs, $1)=yes ;; netbsd* | netbsdelf*-gnu) if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then _LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags' # a.out else _LT_TAGVAR(archive_cmds, $1)='$LD -shared -o $lib $libobjs $deplibs $linker_flags' # ELF fi _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' _LT_TAGVAR(hardcode_direct, $1)=yes _LT_TAGVAR(hardcode_shlibpath_var, $1)=no ;; newsos6) _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' _LT_TAGVAR(hardcode_direct, $1)=yes _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir' _LT_TAGVAR(hardcode_libdir_separator, $1)=: _LT_TAGVAR(hardcode_shlibpath_var, $1)=no ;; *nto* | *qnx*) ;; openbsd*) if test -f /usr/libexec/ld.so; then _LT_TAGVAR(hardcode_direct, $1)=yes _LT_TAGVAR(hardcode_shlibpath_var, $1)=no _LT_TAGVAR(hardcode_direct_absolute, $1)=yes if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags ${wl}-retain-symbols-file,$export_symbols' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath,$libdir' _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E' else case $host_os in openbsd[[01]].* | openbsd2.[[0-7]] | openbsd2.[[0-7]].*) _LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' ;; *) _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath,$libdir' ;; esac fi else _LT_TAGVAR(ld_shlibs, $1)=no fi ;; os2*) _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' _LT_TAGVAR(hardcode_minus_L, $1)=yes _LT_TAGVAR(allow_undefined_flag, $1)=unsupported _LT_TAGVAR(archive_cmds, $1)='$ECHO "LIBRARY $libname INITINSTANCE" > $output_objdir/$libname.def~$ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~echo DATA >> $output_objdir/$libname.def~echo " SINGLE NONSHARED" >> $output_objdir/$libname.def~echo EXPORTS >> $output_objdir/$libname.def~emxexp $libobjs >> $output_objdir/$libname.def~$CC -Zdll -Zcrtdll -o $lib $libobjs $deplibs $compiler_flags $output_objdir/$libname.def' _LT_TAGVAR(old_archive_from_new_cmds, $1)='emximp -o $output_objdir/$libname.a $output_objdir/$libname.def' ;; osf3*) if test "$GCC" = yes; then _LT_TAGVAR(allow_undefined_flag, $1)=' ${wl}-expect_unresolved ${wl}\*' _LT_TAGVAR(archive_cmds, $1)='$CC -shared${allow_undefined_flag} $libobjs $deplibs $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && func_echo_all "${wl}-set_version ${wl}$verstring"` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib' else _LT_TAGVAR(allow_undefined_flag, $1)=' -expect_unresolved \*' _LT_TAGVAR(archive_cmds, $1)='$CC -shared${allow_undefined_flag} $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry ${output_objdir}/so_locations -o $lib' fi _LT_TAGVAR(archive_cmds_need_lc, $1)='no' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir' _LT_TAGVAR(hardcode_libdir_separator, $1)=: ;; osf4* | osf5*) # as osf3* with the addition of -msym flag if test "$GCC" = yes; then _LT_TAGVAR(allow_undefined_flag, $1)=' ${wl}-expect_unresolved ${wl}\*' _LT_TAGVAR(archive_cmds, $1)='$CC -shared${allow_undefined_flag} $pic_flag $libobjs $deplibs $compiler_flags ${wl}-msym ${wl}-soname ${wl}$soname `test -n "$verstring" && func_echo_all "${wl}-set_version ${wl}$verstring"` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir' else _LT_TAGVAR(allow_undefined_flag, $1)=' -expect_unresolved \*' _LT_TAGVAR(archive_cmds, $1)='$CC -shared${allow_undefined_flag} $libobjs $deplibs $compiler_flags -msym -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry ${output_objdir}/so_locations -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='for i in `cat $export_symbols`; do printf "%s %s\\n" -exported_symbol "\$i" >> $lib.exp; done; printf "%s\\n" "-hidden">> $lib.exp~ $CC -shared${allow_undefined_flag} ${wl}-input ${wl}$lib.exp $compiler_flags $libobjs $deplibs -soname $soname `test -n "$verstring" && $ECHO "-set_version $verstring"` -update_registry ${output_objdir}/so_locations -o $lib~$RM $lib.exp' # Both c and cxx compiler support -rpath directly _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-rpath $libdir' fi _LT_TAGVAR(archive_cmds_need_lc, $1)='no' _LT_TAGVAR(hardcode_libdir_separator, $1)=: ;; solaris*) _LT_TAGVAR(no_undefined_flag, $1)=' -z defs' if test "$GCC" = yes; then wlarc='${wl}' _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag ${wl}-z ${wl}text ${wl}-h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags' _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ $CC -shared $pic_flag ${wl}-z ${wl}text ${wl}-M ${wl}$lib.exp ${wl}-h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags~$RM $lib.exp' else case `$CC -V 2>&1` in *"Compilers 5.0"*) wlarc='' _LT_TAGVAR(archive_cmds, $1)='$LD -G${allow_undefined_flag} -h $soname -o $lib $libobjs $deplibs $linker_flags' _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ $LD -G${allow_undefined_flag} -M $lib.exp -h $soname -o $lib $libobjs $deplibs $linker_flags~$RM $lib.exp' ;; *) wlarc='${wl}' _LT_TAGVAR(archive_cmds, $1)='$CC -G${allow_undefined_flag} -h $soname -o $lib $libobjs $deplibs $compiler_flags' _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ $CC -G${allow_undefined_flag} -M $lib.exp -h $soname -o $lib $libobjs $deplibs $compiler_flags~$RM $lib.exp' ;; esac fi _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' _LT_TAGVAR(hardcode_shlibpath_var, $1)=no case $host_os in solaris2.[[0-5]] | solaris2.[[0-5]].*) ;; *) # The compiler driver will combine and reorder linker options, # but understands `-z linker_flag'. GCC discards it without `$wl', # but is careful enough not to reorder. # Supported since Solaris 2.6 (maybe 2.5.1?) if test "$GCC" = yes; then _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}-z ${wl}allextract$convenience ${wl}-z ${wl}defaultextract' else _LT_TAGVAR(whole_archive_flag_spec, $1)='-z allextract$convenience -z defaultextract' fi ;; esac _LT_TAGVAR(link_all_deplibs, $1)=yes ;; sunos4*) if test "x$host_vendor" = xsequent; then # Use $CC to link under sequent, because it throws in some extra .o # files that make .init and .fini sections work. _LT_TAGVAR(archive_cmds, $1)='$CC -G ${wl}-h $soname -o $lib $libobjs $deplibs $compiler_flags' else _LT_TAGVAR(archive_cmds, $1)='$LD -assert pure-text -Bstatic -o $lib $libobjs $deplibs $linker_flags' fi _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' _LT_TAGVAR(hardcode_direct, $1)=yes _LT_TAGVAR(hardcode_minus_L, $1)=yes _LT_TAGVAR(hardcode_shlibpath_var, $1)=no ;; sysv4) case $host_vendor in sni) _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' _LT_TAGVAR(hardcode_direct, $1)=yes # is this really true??? ;; siemens) ## LD is ld it makes a PLAMLIB ## CC just makes a GrossModule. _LT_TAGVAR(archive_cmds, $1)='$LD -G -o $lib $libobjs $deplibs $linker_flags' _LT_TAGVAR(reload_cmds, $1)='$CC -r -o $output$reload_objs' _LT_TAGVAR(hardcode_direct, $1)=no ;; motorola) _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' _LT_TAGVAR(hardcode_direct, $1)=no #Motorola manual says yes, but my tests say they lie ;; esac runpath_var='LD_RUN_PATH' _LT_TAGVAR(hardcode_shlibpath_var, $1)=no ;; sysv4.3*) _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' _LT_TAGVAR(hardcode_shlibpath_var, $1)=no _LT_TAGVAR(export_dynamic_flag_spec, $1)='-Bexport' ;; sysv4*MP*) if test -d /usr/nec; then _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' _LT_TAGVAR(hardcode_shlibpath_var, $1)=no runpath_var=LD_RUN_PATH hardcode_runpath_var=yes _LT_TAGVAR(ld_shlibs, $1)=yes fi ;; sysv4*uw2* | sysv5OpenUNIX* | sysv5UnixWare7.[[01]].[[10]]* | unixware7* | sco3.2v5.0.[[024]]*) _LT_TAGVAR(no_undefined_flag, $1)='${wl}-z,text' _LT_TAGVAR(archive_cmds_need_lc, $1)=no _LT_TAGVAR(hardcode_shlibpath_var, $1)=no runpath_var='LD_RUN_PATH' if test "$GCC" = yes; then _LT_TAGVAR(archive_cmds, $1)='$CC -shared ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' else _LT_TAGVAR(archive_cmds, $1)='$CC -G ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -G ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' fi ;; sysv5* | sco3.2v5* | sco5v6*) # Note: We can NOT use -z defs as we might desire, because we do not # link with -lc, and that would cause any symbols used from libc to # always be unresolved, which means just about no library would # ever link correctly. If we're not using GNU ld we use -z text # though, which does catch some bad symbols but isn't as heavy-handed # as -z defs. _LT_TAGVAR(no_undefined_flag, $1)='${wl}-z,text' _LT_TAGVAR(allow_undefined_flag, $1)='${wl}-z,nodefs' _LT_TAGVAR(archive_cmds_need_lc, $1)=no _LT_TAGVAR(hardcode_shlibpath_var, $1)=no _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-R,$libdir' _LT_TAGVAR(hardcode_libdir_separator, $1)=':' _LT_TAGVAR(link_all_deplibs, $1)=yes _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-Bexport' runpath_var='LD_RUN_PATH' if test "$GCC" = yes; then _LT_TAGVAR(archive_cmds, $1)='$CC -shared ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' else _LT_TAGVAR(archive_cmds, $1)='$CC -G ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -G ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' fi ;; uts4*) _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' _LT_TAGVAR(hardcode_shlibpath_var, $1)=no ;; *) _LT_TAGVAR(ld_shlibs, $1)=no ;; esac if test x$host_vendor = xsni; then case $host in sysv4 | sysv4.2uw2* | sysv4.3* | sysv5*) _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-Blargedynsym' ;; esac fi fi ]) AC_MSG_RESULT([$_LT_TAGVAR(ld_shlibs, $1)]) test "$_LT_TAGVAR(ld_shlibs, $1)" = no && can_build_shared=no _LT_TAGVAR(with_gnu_ld, $1)=$with_gnu_ld _LT_DECL([], [libext], [0], [Old archive suffix (normally "a")])dnl _LT_DECL([], [shrext_cmds], [1], [Shared library suffix (normally ".so")])dnl _LT_DECL([], [extract_expsyms_cmds], [2], [The commands to extract the exported symbol list from a shared archive]) # # Do we need to explicitly link libc? # case "x$_LT_TAGVAR(archive_cmds_need_lc, $1)" in x|xyes) # Assume -lc should be added _LT_TAGVAR(archive_cmds_need_lc, $1)=yes if test "$enable_shared" = yes && test "$GCC" = yes; then case $_LT_TAGVAR(archive_cmds, $1) in *'~'*) # FIXME: we may have to deal with multi-command sequences. ;; '$CC '*) # Test whether the compiler implicitly links with -lc since on some # systems, -lgcc has to come before -lc. If gcc already passes -lc # to ld, don't add -lc before -lgcc. AC_CACHE_CHECK([whether -lc should be explicitly linked in], [lt_cv_]_LT_TAGVAR(archive_cmds_need_lc, $1), [$RM conftest* echo "$lt_simple_compile_test_code" > conftest.$ac_ext if AC_TRY_EVAL(ac_compile) 2>conftest.err; then soname=conftest lib=conftest libobjs=conftest.$ac_objext deplibs= wl=$_LT_TAGVAR(lt_prog_compiler_wl, $1) pic_flag=$_LT_TAGVAR(lt_prog_compiler_pic, $1) compiler_flags=-v linker_flags=-v verstring= output_objdir=. libname=conftest lt_save_allow_undefined_flag=$_LT_TAGVAR(allow_undefined_flag, $1) _LT_TAGVAR(allow_undefined_flag, $1)= if AC_TRY_EVAL(_LT_TAGVAR(archive_cmds, $1) 2\>\&1 \| $GREP \" -lc \" \>/dev/null 2\>\&1) then lt_cv_[]_LT_TAGVAR(archive_cmds_need_lc, $1)=no else lt_cv_[]_LT_TAGVAR(archive_cmds_need_lc, $1)=yes fi _LT_TAGVAR(allow_undefined_flag, $1)=$lt_save_allow_undefined_flag else cat conftest.err 1>&5 fi $RM conftest* ]) _LT_TAGVAR(archive_cmds_need_lc, $1)=$lt_cv_[]_LT_TAGVAR(archive_cmds_need_lc, $1) ;; esac fi ;; esac _LT_TAGDECL([build_libtool_need_lc], [archive_cmds_need_lc], [0], [Whether or not to add -lc for building shared libraries]) _LT_TAGDECL([allow_libtool_libs_with_static_runtimes], [enable_shared_with_static_runtimes], [0], [Whether or not to disallow shared libs when runtime libs are static]) _LT_TAGDECL([], [export_dynamic_flag_spec], [1], [Compiler flag to allow reflexive dlopens]) _LT_TAGDECL([], [whole_archive_flag_spec], [1], [Compiler flag to generate shared objects directly from archives]) _LT_TAGDECL([], [compiler_needs_object], [1], [Whether the compiler copes with passing no objects directly]) _LT_TAGDECL([], [old_archive_from_new_cmds], [2], [Create an old-style archive from a shared archive]) _LT_TAGDECL([], [old_archive_from_expsyms_cmds], [2], [Create a temporary old-style archive to link instead of a shared archive]) _LT_TAGDECL([], [archive_cmds], [2], [Commands used to build a shared archive]) _LT_TAGDECL([], [archive_expsym_cmds], [2]) _LT_TAGDECL([], [module_cmds], [2], [Commands used to build a loadable module if different from building a shared archive.]) _LT_TAGDECL([], [module_expsym_cmds], [2]) _LT_TAGDECL([], [with_gnu_ld], [1], [Whether we are building with GNU ld or not]) _LT_TAGDECL([], [allow_undefined_flag], [1], [Flag that allows shared libraries with undefined symbols to be built]) _LT_TAGDECL([], [no_undefined_flag], [1], [Flag that enforces no undefined symbols]) _LT_TAGDECL([], [hardcode_libdir_flag_spec], [1], [Flag to hardcode $libdir into a binary during linking. This must work even if $libdir does not exist]) _LT_TAGDECL([], [hardcode_libdir_separator], [1], [Whether we need a single "-rpath" flag with a separated argument]) _LT_TAGDECL([], [hardcode_direct], [0], [Set to "yes" if using DIR/libNAME${shared_ext} during linking hardcodes DIR into the resulting binary]) _LT_TAGDECL([], [hardcode_direct_absolute], [0], [Set to "yes" if using DIR/libNAME${shared_ext} during linking hardcodes DIR into the resulting binary and the resulting library dependency is "absolute", i.e impossible to change by setting ${shlibpath_var} if the library is relocated]) _LT_TAGDECL([], [hardcode_minus_L], [0], [Set to "yes" if using the -LDIR flag during linking hardcodes DIR into the resulting binary]) _LT_TAGDECL([], [hardcode_shlibpath_var], [0], [Set to "yes" if using SHLIBPATH_VAR=DIR during linking hardcodes DIR into the resulting binary]) _LT_TAGDECL([], [hardcode_automatic], [0], [Set to "yes" if building a shared library automatically hardcodes DIR into the library and all subsequent libraries and executables linked against it]) _LT_TAGDECL([], [inherit_rpath], [0], [Set to yes if linker adds runtime paths of dependent libraries to runtime path list]) _LT_TAGDECL([], [link_all_deplibs], [0], [Whether libtool must link a program against all its dependency libraries]) _LT_TAGDECL([], [always_export_symbols], [0], [Set to "yes" if exported symbols are required]) _LT_TAGDECL([], [export_symbols_cmds], [2], [The commands to list exported symbols]) _LT_TAGDECL([], [exclude_expsyms], [1], [Symbols that should not be listed in the preloaded symbols]) _LT_TAGDECL([], [include_expsyms], [1], [Symbols that must always be exported]) _LT_TAGDECL([], [prelink_cmds], [2], [Commands necessary for linking programs (against libraries) with templates]) _LT_TAGDECL([], [postlink_cmds], [2], [Commands necessary for finishing linking programs]) _LT_TAGDECL([], [file_list_spec], [1], [Specify filename containing input files]) dnl FIXME: Not yet implemented dnl _LT_TAGDECL([], [thread_safe_flag_spec], [1], dnl [Compiler flag to generate thread safe objects]) ])# _LT_LINKER_SHLIBS # _LT_LANG_C_CONFIG([TAG]) # ------------------------ # Ensure that the configuration variables for a C compiler are suitably # defined. These variables are subsequently used by _LT_CONFIG to write # the compiler configuration to `libtool'. m4_defun([_LT_LANG_C_CONFIG], [m4_require([_LT_DECL_EGREP])dnl lt_save_CC="$CC" AC_LANG_PUSH(C) # Source file extension for C test sources. ac_ext=c # Object file extension for compiled C test sources. objext=o _LT_TAGVAR(objext, $1)=$objext # Code to be used in simple compile tests lt_simple_compile_test_code="int some_variable = 0;" # Code to be used in simple link tests lt_simple_link_test_code='int main(){return(0);}' _LT_TAG_COMPILER # Save the default compiler, since it gets overwritten when the other # tags are being tested, and _LT_TAGVAR(compiler, []) is a NOP. compiler_DEFAULT=$CC # save warnings/boilerplate of simple test code _LT_COMPILER_BOILERPLATE _LT_LINKER_BOILERPLATE ## CAVEAT EMPTOR: ## There is no encapsulation within the following macros, do not change ## the running order or otherwise move them around unless you know exactly ## what you are doing... if test -n "$compiler"; then _LT_COMPILER_NO_RTTI($1) _LT_COMPILER_PIC($1) _LT_COMPILER_C_O($1) _LT_COMPILER_FILE_LOCKS($1) _LT_LINKER_SHLIBS($1) _LT_SYS_DYNAMIC_LINKER($1) _LT_LINKER_HARDCODE_LIBPATH($1) LT_SYS_DLOPEN_SELF _LT_CMD_STRIPLIB # Report which library types will actually be built AC_MSG_CHECKING([if libtool supports shared libraries]) AC_MSG_RESULT([$can_build_shared]) AC_MSG_CHECKING([whether to build shared libraries]) test "$can_build_shared" = "no" && enable_shared=no # On AIX, shared libraries and static libraries use the same namespace, and # are all built from PIC. case $host_os in aix3*) test "$enable_shared" = yes && enable_static=no if test -n "$RANLIB"; then archive_cmds="$archive_cmds~\$RANLIB \$lib" postinstall_cmds='$RANLIB $lib' fi ;; aix[[4-9]]*) if test "$host_cpu" != ia64 && test "$aix_use_runtimelinking" = no ; then test "$enable_shared" = yes && enable_static=no fi ;; esac AC_MSG_RESULT([$enable_shared]) AC_MSG_CHECKING([whether to build static libraries]) # Make sure either enable_shared or enable_static is yes. test "$enable_shared" = yes || enable_static=yes AC_MSG_RESULT([$enable_static]) _LT_CONFIG($1) fi AC_LANG_POP CC="$lt_save_CC" ])# _LT_LANG_C_CONFIG # _LT_LANG_CXX_CONFIG([TAG]) # -------------------------- # Ensure that the configuration variables for a C++ compiler are suitably # defined. These variables are subsequently used by _LT_CONFIG to write # the compiler configuration to `libtool'. m4_defun([_LT_LANG_CXX_CONFIG], [m4_require([_LT_FILEUTILS_DEFAULTS])dnl m4_require([_LT_DECL_EGREP])dnl m4_require([_LT_PATH_MANIFEST_TOOL])dnl if test -n "$CXX" && ( test "X$CXX" != "Xno" && ( (test "X$CXX" = "Xg++" && `g++ -v >/dev/null 2>&1` ) || (test "X$CXX" != "Xg++"))) ; then AC_PROG_CXXCPP else _lt_caught_CXX_error=yes fi AC_LANG_PUSH(C++) _LT_TAGVAR(archive_cmds_need_lc, $1)=no _LT_TAGVAR(allow_undefined_flag, $1)= _LT_TAGVAR(always_export_symbols, $1)=no _LT_TAGVAR(archive_expsym_cmds, $1)= _LT_TAGVAR(compiler_needs_object, $1)=no _LT_TAGVAR(export_dynamic_flag_spec, $1)= _LT_TAGVAR(hardcode_direct, $1)=no _LT_TAGVAR(hardcode_direct_absolute, $1)=no _LT_TAGVAR(hardcode_libdir_flag_spec, $1)= _LT_TAGVAR(hardcode_libdir_separator, $1)= _LT_TAGVAR(hardcode_minus_L, $1)=no _LT_TAGVAR(hardcode_shlibpath_var, $1)=unsupported _LT_TAGVAR(hardcode_automatic, $1)=no _LT_TAGVAR(inherit_rpath, $1)=no _LT_TAGVAR(module_cmds, $1)= _LT_TAGVAR(module_expsym_cmds, $1)= _LT_TAGVAR(link_all_deplibs, $1)=unknown _LT_TAGVAR(old_archive_cmds, $1)=$old_archive_cmds _LT_TAGVAR(reload_flag, $1)=$reload_flag _LT_TAGVAR(reload_cmds, $1)=$reload_cmds _LT_TAGVAR(no_undefined_flag, $1)= _LT_TAGVAR(whole_archive_flag_spec, $1)= _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=no # Source file extension for C++ test sources. ac_ext=cpp # Object file extension for compiled C++ test sources. objext=o _LT_TAGVAR(objext, $1)=$objext # No sense in running all these tests if we already determined that # the CXX compiler isn't working. Some variables (like enable_shared) # are currently assumed to apply to all compilers on this platform, # and will be corrupted by setting them based on a non-working compiler. if test "$_lt_caught_CXX_error" != yes; then # Code to be used in simple compile tests lt_simple_compile_test_code="int some_variable = 0;" # Code to be used in simple link tests lt_simple_link_test_code='int main(int, char *[[]]) { return(0); }' # ltmain only uses $CC for tagged configurations so make sure $CC is set. _LT_TAG_COMPILER # save warnings/boilerplate of simple test code _LT_COMPILER_BOILERPLATE _LT_LINKER_BOILERPLATE # Allow CC to be a program name with arguments. lt_save_CC=$CC lt_save_CFLAGS=$CFLAGS lt_save_LD=$LD lt_save_GCC=$GCC GCC=$GXX lt_save_with_gnu_ld=$with_gnu_ld lt_save_path_LD=$lt_cv_path_LD if test -n "${lt_cv_prog_gnu_ldcxx+set}"; then lt_cv_prog_gnu_ld=$lt_cv_prog_gnu_ldcxx else $as_unset lt_cv_prog_gnu_ld fi if test -n "${lt_cv_path_LDCXX+set}"; then lt_cv_path_LD=$lt_cv_path_LDCXX else $as_unset lt_cv_path_LD fi test -z "${LDCXX+set}" || LD=$LDCXX CC=${CXX-"c++"} CFLAGS=$CXXFLAGS compiler=$CC _LT_TAGVAR(compiler, $1)=$CC _LT_CC_BASENAME([$compiler]) if test -n "$compiler"; then # We don't want -fno-exception when compiling C++ code, so set the # no_builtin_flag separately if test "$GXX" = yes; then _LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)=' -fno-builtin' else _LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)= fi if test "$GXX" = yes; then # Set up default GNU C++ configuration LT_PATH_LD # Check if GNU C++ uses GNU ld as the underlying linker, since the # archiving commands below assume that GNU ld is being used. if test "$with_gnu_ld" = yes; then _LT_TAGVAR(archive_cmds, $1)='$CC $pic_flag -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC $pic_flag -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir' _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}--export-dynamic' # If archive_cmds runs LD, not CC, wlarc should be empty # XXX I think wlarc can be eliminated in ltcf-cxx, but I need to # investigate it a little bit more. (MM) wlarc='${wl}' # ancient GNU ld didn't support --whole-archive et. al. if eval "`$CC -print-prog-name=ld` --help 2>&1" | $GREP 'no-whole-archive' > /dev/null; then _LT_TAGVAR(whole_archive_flag_spec, $1)="$wlarc"'--whole-archive$convenience '"$wlarc"'--no-whole-archive' else _LT_TAGVAR(whole_archive_flag_spec, $1)= fi else with_gnu_ld=no wlarc= # A generic and very simple default shared library creation # command for GNU C++ for the case where it uses the native # linker, instead of GNU ld. If possible, this setting should # overridden to take advantage of the native linker features on # the platform it is being used on. _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $lib' fi # Commands to make compiler produce verbose output that lists # what "hidden" libraries, object files and flags are used when # linking a shared library. output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP -v "^Configured with:" | $GREP "\-L"' else GXX=no with_gnu_ld=no wlarc= fi # PORTME: fill in a description of your system's C++ link characteristics AC_MSG_CHECKING([whether the $compiler linker ($LD) supports shared libraries]) _LT_TAGVAR(ld_shlibs, $1)=yes case $host_os in aix3*) # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; aix[[4-9]]*) if test "$host_cpu" = ia64; then # On IA64, the linker does run time linking by default, so we don't # have to do anything special. aix_use_runtimelinking=no exp_sym_flag='-Bexport' no_entry_flag="" else aix_use_runtimelinking=no # Test if we are trying to use run time linking or normal # AIX style linking. If -brtl is somewhere in LDFLAGS, we # need to do runtime linking. case $host_os in aix4.[[23]]|aix4.[[23]].*|aix[[5-9]]*) for ld_flag in $LDFLAGS; do case $ld_flag in *-brtl*) aix_use_runtimelinking=yes break ;; esac done ;; esac exp_sym_flag='-bexport' no_entry_flag='-bnoentry' fi # When large executables or shared objects are built, AIX ld can # have problems creating the table of contents. If linking a library # or program results in "error TOC overflow" add -mminimal-toc to # CXXFLAGS/CFLAGS for g++/gcc. In the cases where that is not # enough to fix the problem, add -Wl,-bbigtoc to LDFLAGS. _LT_TAGVAR(archive_cmds, $1)='' _LT_TAGVAR(hardcode_direct, $1)=yes _LT_TAGVAR(hardcode_direct_absolute, $1)=yes _LT_TAGVAR(hardcode_libdir_separator, $1)=':' _LT_TAGVAR(link_all_deplibs, $1)=yes _LT_TAGVAR(file_list_spec, $1)='${wl}-f,' if test "$GXX" = yes; then case $host_os in aix4.[[012]]|aix4.[[012]].*) # We only want to do this on AIX 4.2 and lower, the check # below for broken collect2 doesn't work under 4.3+ collect2name=`${CC} -print-prog-name=collect2` if test -f "$collect2name" && strings "$collect2name" | $GREP resolve_lib_name >/dev/null then # We have reworked collect2 : else # We have old collect2 _LT_TAGVAR(hardcode_direct, $1)=unsupported # It fails to find uninstalled libraries when the uninstalled # path is not listed in the libpath. Setting hardcode_minus_L # to unsupported forces relinking _LT_TAGVAR(hardcode_minus_L, $1)=yes _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' _LT_TAGVAR(hardcode_libdir_separator, $1)= fi esac shared_flag='-shared' if test "$aix_use_runtimelinking" = yes; then shared_flag="$shared_flag "'${wl}-G' fi else # not using gcc if test "$host_cpu" = ia64; then # VisualAge C++, Version 5.5 for AIX 5L for IA-64, Beta 3 Release # chokes on -Wl,-G. The following line is correct: shared_flag='-G' else if test "$aix_use_runtimelinking" = yes; then shared_flag='${wl}-G' else shared_flag='${wl}-bM:SRE' fi fi fi _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-bexpall' # It seems that -bexpall does not export symbols beginning with # underscore (_), so it is better to generate a list of symbols to # export. _LT_TAGVAR(always_export_symbols, $1)=yes if test "$aix_use_runtimelinking" = yes; then # Warning - without using the other runtime loading flags (-brtl), # -berok will link without error, but may produce a broken library. _LT_TAGVAR(allow_undefined_flag, $1)='-berok' # Determine the default libpath from the value encoded in an empty # executable. _LT_SYS_MODULE_PATH_AIX([$1]) _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-blibpath:$libdir:'"$aix_libpath" _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -o $output_objdir/$soname $libobjs $deplibs '"\${wl}$no_entry_flag"' $compiler_flags `if test "x${allow_undefined_flag}" != "x"; then func_echo_all "${wl}${allow_undefined_flag}"; else :; fi` '"\${wl}$exp_sym_flag:\$export_symbols $shared_flag" else if test "$host_cpu" = ia64; then _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-R $libdir:/usr/lib:/lib' _LT_TAGVAR(allow_undefined_flag, $1)="-z nodefs" _LT_TAGVAR(archive_expsym_cmds, $1)="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs '"\${wl}$no_entry_flag"' $compiler_flags ${wl}${allow_undefined_flag} '"\${wl}$exp_sym_flag:\$export_symbols" else # Determine the default libpath from the value encoded in an # empty executable. _LT_SYS_MODULE_PATH_AIX([$1]) _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-blibpath:$libdir:'"$aix_libpath" # Warning - without using the other run time loading flags, # -berok will link without error, but may produce a broken library. _LT_TAGVAR(no_undefined_flag, $1)=' ${wl}-bernotok' _LT_TAGVAR(allow_undefined_flag, $1)=' ${wl}-berok' if test "$with_gnu_ld" = yes; then # We only use this code for GNU lds that support --whole-archive. _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}--whole-archive$convenience ${wl}--no-whole-archive' else # Exported symbols can be pulled into shared objects from archives _LT_TAGVAR(whole_archive_flag_spec, $1)='$convenience' fi _LT_TAGVAR(archive_cmds_need_lc, $1)=yes # This is similar to how AIX traditionally builds its shared # libraries. _LT_TAGVAR(archive_expsym_cmds, $1)="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs ${wl}-bnoentry $compiler_flags ${wl}-bE:$export_symbols${allow_undefined_flag}~$AR $AR_FLAGS $output_objdir/$libname$release.a $output_objdir/$soname' fi fi ;; beos*) if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then _LT_TAGVAR(allow_undefined_flag, $1)=unsupported # Joseph Beckenbach says some releases of gcc # support --undefined. This deserves some investigation. FIXME _LT_TAGVAR(archive_cmds, $1)='$CC -nostart $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' else _LT_TAGVAR(ld_shlibs, $1)=no fi ;; chorus*) case $cc_basename in *) # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; esac ;; cygwin* | mingw* | pw32* | cegcc*) case $GXX,$cc_basename in ,cl* | no,cl*) # Native MSVC # hardcode_libdir_flag_spec is actually meaningless, as there is # no search path for DLLs. _LT_TAGVAR(hardcode_libdir_flag_spec, $1)=' ' _LT_TAGVAR(allow_undefined_flag, $1)=unsupported _LT_TAGVAR(always_export_symbols, $1)=yes _LT_TAGVAR(file_list_spec, $1)='@' # Tell ltmain to make .lib files, not .a files. libext=lib # Tell ltmain to make .dll files, not .so files. shrext_cmds=".dll" # FIXME: Setting linknames here is a bad hack. _LT_TAGVAR(archive_cmds, $1)='$CC -o $output_objdir/$soname $libobjs $compiler_flags $deplibs -Wl,-dll~linknames=' _LT_TAGVAR(archive_expsym_cmds, $1)='if test "x`$SED 1q $export_symbols`" = xEXPORTS; then $SED -n -e 's/\\\\\\\(.*\\\\\\\)/-link\\\ -EXPORT:\\\\\\\1/' -e '1\\\!p' < $export_symbols > $output_objdir/$soname.exp; else $SED -e 's/\\\\\\\(.*\\\\\\\)/-link\\\ -EXPORT:\\\\\\\1/' < $export_symbols > $output_objdir/$soname.exp; fi~ $CC -o $tool_output_objdir$soname $libobjs $compiler_flags $deplibs "@$tool_output_objdir$soname.exp" -Wl,-DLL,-IMPLIB:"$tool_output_objdir$libname.dll.lib"~ linknames=' # The linker will not automatically build a static lib if we build a DLL. # _LT_TAGVAR(old_archive_from_new_cmds, $1)='true' _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes # Don't use ranlib _LT_TAGVAR(old_postinstall_cmds, $1)='chmod 644 $oldlib' _LT_TAGVAR(postlink_cmds, $1)='lt_outputfile="@OUTPUT@"~ lt_tool_outputfile="@TOOL_OUTPUT@"~ case $lt_outputfile in *.exe|*.EXE) ;; *) lt_outputfile="$lt_outputfile.exe" lt_tool_outputfile="$lt_tool_outputfile.exe" ;; esac~ func_to_tool_file "$lt_outputfile"~ if test "$MANIFEST_TOOL" != ":" && test -f "$lt_outputfile.manifest"; then $MANIFEST_TOOL -manifest "$lt_tool_outputfile.manifest" -outputresource:"$lt_tool_outputfile" || exit 1; $RM "$lt_outputfile.manifest"; fi' ;; *) # g++ # _LT_TAGVAR(hardcode_libdir_flag_spec, $1) is actually meaningless, # as there is no search path for DLLs. _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}--export-all-symbols' _LT_TAGVAR(allow_undefined_flag, $1)=unsupported _LT_TAGVAR(always_export_symbols, $1)=no _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes if $LD --help 2>&1 | $GREP 'auto-import' > /dev/null; then _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $output_objdir/$soname ${wl}--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib' # If the export-symbols file already is a .def file (1st line # is EXPORTS), use it as is; otherwise, prepend... _LT_TAGVAR(archive_expsym_cmds, $1)='if test "x`$SED 1q $export_symbols`" = xEXPORTS; then cp $export_symbols $output_objdir/$soname.def; else echo EXPORTS > $output_objdir/$soname.def; cat $export_symbols >> $output_objdir/$soname.def; fi~ $CC -shared -nostdlib $output_objdir/$soname.def $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $output_objdir/$soname ${wl}--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib' else _LT_TAGVAR(ld_shlibs, $1)=no fi ;; esac ;; darwin* | rhapsody*) _LT_DARWIN_LINKER_FEATURES($1) ;; dgux*) case $cc_basename in ec++*) # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; ghcx*) # Green Hills C++ Compiler # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; *) # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; esac ;; freebsd2.*) # C++ shared libraries reported to be fairly broken before # switch to ELF _LT_TAGVAR(ld_shlibs, $1)=no ;; freebsd-elf*) _LT_TAGVAR(archive_cmds_need_lc, $1)=no ;; freebsd* | dragonfly*) # FreeBSD 3 and later use GNU C++ and GNU ld with standard ELF # conventions _LT_TAGVAR(ld_shlibs, $1)=yes ;; haiku*) _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' _LT_TAGVAR(link_all_deplibs, $1)=yes ;; hpux9*) _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}+b ${wl}$libdir' _LT_TAGVAR(hardcode_libdir_separator, $1)=: _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E' _LT_TAGVAR(hardcode_direct, $1)=yes _LT_TAGVAR(hardcode_minus_L, $1)=yes # Not in the search PATH, # but as the default # location of the library. case $cc_basename in CC*) # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; aCC*) _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/$soname~$CC -b ${wl}+b ${wl}$install_libdir -o $output_objdir/$soname $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~test $output_objdir/$soname = $lib || mv $output_objdir/$soname $lib' # Commands to make compiler produce verbose output that lists # what "hidden" libraries, object files and flags are used when # linking a shared library. # # There doesn't appear to be a way to prevent this compiler from # explicitly linking system object files so we need to strip them # from the output so that they don't get included in the library # dependencies. output_verbose_link_cmd='templist=`($CC -b $CFLAGS -v conftest.$objext 2>&1) | $EGREP "\-L"`; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "$list"' ;; *) if test "$GXX" = yes; then _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/$soname~$CC -shared -nostdlib $pic_flag ${wl}+b ${wl}$install_libdir -o $output_objdir/$soname $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~test $output_objdir/$soname = $lib || mv $output_objdir/$soname $lib' else # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no fi ;; esac ;; hpux10*|hpux11*) if test $with_gnu_ld = no; then _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}+b ${wl}$libdir' _LT_TAGVAR(hardcode_libdir_separator, $1)=: case $host_cpu in hppa*64*|ia64*) ;; *) _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E' ;; esac fi case $host_cpu in hppa*64*|ia64*) _LT_TAGVAR(hardcode_direct, $1)=no _LT_TAGVAR(hardcode_shlibpath_var, $1)=no ;; *) _LT_TAGVAR(hardcode_direct, $1)=yes _LT_TAGVAR(hardcode_direct_absolute, $1)=yes _LT_TAGVAR(hardcode_minus_L, $1)=yes # Not in the search PATH, # but as the default # location of the library. ;; esac case $cc_basename in CC*) # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; aCC*) case $host_cpu in hppa*64*) _LT_TAGVAR(archive_cmds, $1)='$CC -b ${wl}+h ${wl}$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' ;; ia64*) _LT_TAGVAR(archive_cmds, $1)='$CC -b ${wl}+h ${wl}$soname ${wl}+nodefaultrpath -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' ;; *) _LT_TAGVAR(archive_cmds, $1)='$CC -b ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' ;; esac # Commands to make compiler produce verbose output that lists # what "hidden" libraries, object files and flags are used when # linking a shared library. # # There doesn't appear to be a way to prevent this compiler from # explicitly linking system object files so we need to strip them # from the output so that they don't get included in the library # dependencies. output_verbose_link_cmd='templist=`($CC -b $CFLAGS -v conftest.$objext 2>&1) | $GREP "\-L"`; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "$list"' ;; *) if test "$GXX" = yes; then if test $with_gnu_ld = no; then case $host_cpu in hppa*64*) _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib -fPIC ${wl}+h ${wl}$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' ;; ia64*) _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $pic_flag ${wl}+h ${wl}$soname ${wl}+nodefaultrpath -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' ;; *) _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $pic_flag ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' ;; esac fi else # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no fi ;; esac ;; interix[[3-9]]*) _LT_TAGVAR(hardcode_direct, $1)=no _LT_TAGVAR(hardcode_shlibpath_var, $1)=no _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath,$libdir' _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E' # Hack: On Interix 3.x, we cannot compile PIC because of a broken gcc. # Instead, shared libraries are loaded at an image base (0x10000000 by # default) and relocated if they conflict, which is a slow very memory # consuming and fragmenting process. To avoid this, we pick a random, # 256 KiB-aligned image base between 0x50000000 and 0x6FFC0000 at link # time. Moving up from 0x10000000 also allows more sbrk(2) space. _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-h,$soname ${wl}--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='sed "s,^,_," $export_symbols >$output_objdir/$soname.expsym~$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-h,$soname ${wl}--retain-symbols-file,$output_objdir/$soname.expsym ${wl}--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' ;; irix5* | irix6*) case $cc_basename in CC*) # SGI C++ _LT_TAGVAR(archive_cmds, $1)='$CC -shared -all -multigot $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry ${output_objdir}/so_locations -o $lib' # Archives containing C++ object files must be created using # "CC -ar", where "CC" is the IRIX C++ compiler. This is # necessary to make sure instantiated templates are included # in the archive. _LT_TAGVAR(old_archive_cmds, $1)='$CC -ar -WR,-u -o $oldlib $oldobjs' ;; *) if test "$GXX" = yes; then if test "$with_gnu_ld" = no; then _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && func_echo_all "${wl}-set_version ${wl}$verstring"` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib' else _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && func_echo_all "${wl}-set_version ${wl}$verstring"` -o $lib' fi fi _LT_TAGVAR(link_all_deplibs, $1)=yes ;; esac _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir' _LT_TAGVAR(hardcode_libdir_separator, $1)=: _LT_TAGVAR(inherit_rpath, $1)=yes ;; linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*) case $cc_basename in KCC*) # Kuck and Associates, Inc. (KAI) C++ Compiler # KCC will only create a shared library if the output file # ends with ".so" (or ".sl" for HP-UX), so rename the library # to its proper name (with version) after linking. _LT_TAGVAR(archive_cmds, $1)='tempext=`echo $shared_ext | $SED -e '\''s/\([[^()0-9A-Za-z{}]]\)/\\\\\1/g'\''`; templib=`echo $lib | $SED -e "s/\${tempext}\..*/.so/"`; $CC $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags --soname $soname -o \$templib; mv \$templib $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='tempext=`echo $shared_ext | $SED -e '\''s/\([[^()0-9A-Za-z{}]]\)/\\\\\1/g'\''`; templib=`echo $lib | $SED -e "s/\${tempext}\..*/.so/"`; $CC $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags --soname $soname -o \$templib ${wl}-retain-symbols-file,$export_symbols; mv \$templib $lib' # Commands to make compiler produce verbose output that lists # what "hidden" libraries, object files and flags are used when # linking a shared library. # # There doesn't appear to be a way to prevent this compiler from # explicitly linking system object files so we need to strip them # from the output so that they don't get included in the library # dependencies. output_verbose_link_cmd='templist=`$CC $CFLAGS -v conftest.$objext -o libconftest$shared_ext 2>&1 | $GREP "ld"`; rm -f libconftest$shared_ext; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "$list"' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath,$libdir' _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}--export-dynamic' # Archives containing C++ object files must be created using # "CC -Bstatic", where "CC" is the KAI C++ compiler. _LT_TAGVAR(old_archive_cmds, $1)='$CC -Bstatic -o $oldlib $oldobjs' ;; icpc* | ecpc* ) # Intel C++ with_gnu_ld=yes # version 8.0 and above of icpc choke on multiply defined symbols # if we add $predep_objects and $postdep_objects, however 7.1 and # earlier do not add the objects themselves. case `$CC -V 2>&1` in *"Version 7."*) _LT_TAGVAR(archive_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' ;; *) # Version 8.0 or newer tmp_idyn= case $host_cpu in ia64*) tmp_idyn=' -i_dynamic';; esac _LT_TAGVAR(archive_cmds, $1)='$CC -shared'"$tmp_idyn"' $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared'"$tmp_idyn"' $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' ;; esac _LT_TAGVAR(archive_cmds_need_lc, $1)=no _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath,$libdir' _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}--export-dynamic' _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}--whole-archive$convenience ${wl}--no-whole-archive' ;; pgCC* | pgcpp*) # Portland Group C++ compiler case `$CC -V` in *pgCC\ [[1-5]].* | *pgcpp\ [[1-5]].*) _LT_TAGVAR(prelink_cmds, $1)='tpldir=Template.dir~ rm -rf $tpldir~ $CC --prelink_objects --instantiation_dir $tpldir $objs $libobjs $compile_deplibs~ compile_command="$compile_command `find $tpldir -name \*.o | sort | $NL2SP`"' _LT_TAGVAR(old_archive_cmds, $1)='tpldir=Template.dir~ rm -rf $tpldir~ $CC --prelink_objects --instantiation_dir $tpldir $oldobjs$old_deplibs~ $AR $AR_FLAGS $oldlib$oldobjs$old_deplibs `find $tpldir -name \*.o | sort | $NL2SP`~ $RANLIB $oldlib' _LT_TAGVAR(archive_cmds, $1)='tpldir=Template.dir~ rm -rf $tpldir~ $CC --prelink_objects --instantiation_dir $tpldir $predep_objects $libobjs $deplibs $convenience $postdep_objects~ $CC -shared $pic_flag $predep_objects $libobjs $deplibs `find $tpldir -name \*.o | sort | $NL2SP` $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='tpldir=Template.dir~ rm -rf $tpldir~ $CC --prelink_objects --instantiation_dir $tpldir $predep_objects $libobjs $deplibs $convenience $postdep_objects~ $CC -shared $pic_flag $predep_objects $libobjs $deplibs `find $tpldir -name \*.o | sort | $NL2SP` $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname ${wl}-retain-symbols-file ${wl}$export_symbols -o $lib' ;; *) # Version 6 and above use weak symbols _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname ${wl}-retain-symbols-file ${wl}$export_symbols -o $lib' ;; esac _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}--rpath ${wl}$libdir' _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}--export-dynamic' _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` ${wl}--no-whole-archive' ;; cxx*) # Compaq C++ _LT_TAGVAR(archive_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname -o $lib ${wl}-retain-symbols-file $wl$export_symbols' runpath_var=LD_RUN_PATH _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-rpath $libdir' _LT_TAGVAR(hardcode_libdir_separator, $1)=: # Commands to make compiler produce verbose output that lists # what "hidden" libraries, object files and flags are used when # linking a shared library. # # There doesn't appear to be a way to prevent this compiler from # explicitly linking system object files so we need to strip them # from the output so that they don't get included in the library # dependencies. output_verbose_link_cmd='templist=`$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP "ld"`; templist=`func_echo_all "$templist" | $SED "s/\(^.*ld.*\)\( .*ld .*$\)/\1/"`; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "X$list" | $Xsed' ;; xl* | mpixl* | bgxl*) # IBM XL 8.0 on PPC, with GNU ld _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir' _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}--export-dynamic' _LT_TAGVAR(archive_cmds, $1)='$CC -qmkshrobj $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' if test "x$supports_anon_versioning" = xyes; then _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $output_objdir/$libname.ver~ cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~ echo "local: *; };" >> $output_objdir/$libname.ver~ $CC -qmkshrobj $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-version-script ${wl}$output_objdir/$libname.ver -o $lib' fi ;; *) case `$CC -V 2>&1 | sed 5q` in *Sun\ C*) # Sun C++ 5.9 _LT_TAGVAR(no_undefined_flag, $1)=' -zdefs' _LT_TAGVAR(archive_cmds, $1)='$CC -G${allow_undefined_flag} -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -G${allow_undefined_flag} -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-retain-symbols-file ${wl}$export_symbols' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}--whole-archive`new_convenience=; for conv in $convenience\"\"; do test -z \"$conv\" || new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` ${wl}--no-whole-archive' _LT_TAGVAR(compiler_needs_object, $1)=yes # Not sure whether something based on # $CC $CFLAGS -v conftest.$objext -o libconftest$shared_ext 2>&1 # would be better. output_verbose_link_cmd='func_echo_all' # Archives containing C++ object files must be created using # "CC -xar", where "CC" is the Sun C++ compiler. This is # necessary to make sure instantiated templates are included # in the archive. _LT_TAGVAR(old_archive_cmds, $1)='$CC -xar -o $oldlib $oldobjs' ;; esac ;; esac ;; lynxos*) # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; m88k*) # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; mvs*) case $cc_basename in cxx*) # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; *) # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; esac ;; netbsd*) if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then _LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable -o $lib $predep_objects $libobjs $deplibs $postdep_objects $linker_flags' wlarc= _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' _LT_TAGVAR(hardcode_direct, $1)=yes _LT_TAGVAR(hardcode_shlibpath_var, $1)=no fi # Workaround some broken pre-1.5 toolchains output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP conftest.$objext | $SED -e "s:-lgcc -lc -lgcc::"' ;; *nto* | *qnx*) _LT_TAGVAR(ld_shlibs, $1)=yes ;; openbsd2*) # C++ shared libraries are fairly broken _LT_TAGVAR(ld_shlibs, $1)=no ;; openbsd*) if test -f /usr/libexec/ld.so; then _LT_TAGVAR(hardcode_direct, $1)=yes _LT_TAGVAR(hardcode_shlibpath_var, $1)=no _LT_TAGVAR(hardcode_direct_absolute, $1)=yes _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $lib' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath,$libdir' if test -z "`echo __ELF__ | $CC -E - | grep __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-retain-symbols-file,$export_symbols -o $lib' _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E' _LT_TAGVAR(whole_archive_flag_spec, $1)="$wlarc"'--whole-archive$convenience '"$wlarc"'--no-whole-archive' fi output_verbose_link_cmd=func_echo_all else _LT_TAGVAR(ld_shlibs, $1)=no fi ;; osf3* | osf4* | osf5*) case $cc_basename in KCC*) # Kuck and Associates, Inc. (KAI) C++ Compiler # KCC will only create a shared library if the output file # ends with ".so" (or ".sl" for HP-UX), so rename the library # to its proper name (with version) after linking. _LT_TAGVAR(archive_cmds, $1)='tempext=`echo $shared_ext | $SED -e '\''s/\([[^()0-9A-Za-z{}]]\)/\\\\\1/g'\''`; templib=`echo "$lib" | $SED -e "s/\${tempext}\..*/.so/"`; $CC $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags --soname $soname -o \$templib; mv \$templib $lib' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath,$libdir' _LT_TAGVAR(hardcode_libdir_separator, $1)=: # Archives containing C++ object files must be created using # the KAI C++ compiler. case $host in osf3*) _LT_TAGVAR(old_archive_cmds, $1)='$CC -Bstatic -o $oldlib $oldobjs' ;; *) _LT_TAGVAR(old_archive_cmds, $1)='$CC -o $oldlib $oldobjs' ;; esac ;; RCC*) # Rational C++ 2.4.1 # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; cxx*) case $host in osf3*) _LT_TAGVAR(allow_undefined_flag, $1)=' ${wl}-expect_unresolved ${wl}\*' _LT_TAGVAR(archive_cmds, $1)='$CC -shared${allow_undefined_flag} $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $soname `test -n "$verstring" && func_echo_all "${wl}-set_version $verstring"` -update_registry ${output_objdir}/so_locations -o $lib' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir' ;; *) _LT_TAGVAR(allow_undefined_flag, $1)=' -expect_unresolved \*' _LT_TAGVAR(archive_cmds, $1)='$CC -shared${allow_undefined_flag} $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -msym -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry ${output_objdir}/so_locations -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='for i in `cat $export_symbols`; do printf "%s %s\\n" -exported_symbol "\$i" >> $lib.exp; done~ echo "-hidden">> $lib.exp~ $CC -shared$allow_undefined_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -msym -soname $soname ${wl}-input ${wl}$lib.exp `test -n "$verstring" && $ECHO "-set_version $verstring"` -update_registry ${output_objdir}/so_locations -o $lib~ $RM $lib.exp' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-rpath $libdir' ;; esac _LT_TAGVAR(hardcode_libdir_separator, $1)=: # Commands to make compiler produce verbose output that lists # what "hidden" libraries, object files and flags are used when # linking a shared library. # # There doesn't appear to be a way to prevent this compiler from # explicitly linking system object files so we need to strip them # from the output so that they don't get included in the library # dependencies. output_verbose_link_cmd='templist=`$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP "ld" | $GREP -v "ld:"`; templist=`func_echo_all "$templist" | $SED "s/\(^.*ld.*\)\( .*ld.*$\)/\1/"`; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "$list"' ;; *) if test "$GXX" = yes && test "$with_gnu_ld" = no; then _LT_TAGVAR(allow_undefined_flag, $1)=' ${wl}-expect_unresolved ${wl}\*' case $host in osf3*) _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib ${allow_undefined_flag} $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && func_echo_all "${wl}-set_version ${wl}$verstring"` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib' ;; *) _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -nostdlib ${allow_undefined_flag} $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-msym ${wl}-soname ${wl}$soname `test -n "$verstring" && func_echo_all "${wl}-set_version ${wl}$verstring"` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib' ;; esac _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir' _LT_TAGVAR(hardcode_libdir_separator, $1)=: # Commands to make compiler produce verbose output that lists # what "hidden" libraries, object files and flags are used when # linking a shared library. output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP -v "^Configured with:" | $GREP "\-L"' else # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no fi ;; esac ;; psos*) # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; sunos4*) case $cc_basename in CC*) # Sun C++ 4.x # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; lcc*) # Lucid # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; *) # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; esac ;; solaris*) case $cc_basename in CC* | sunCC*) # Sun C++ 4.2, 5.x and Centerline C++ _LT_TAGVAR(archive_cmds_need_lc,$1)=yes _LT_TAGVAR(no_undefined_flag, $1)=' -zdefs' _LT_TAGVAR(archive_cmds, $1)='$CC -G${allow_undefined_flag} -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ $CC -G${allow_undefined_flag} ${wl}-M ${wl}$lib.exp -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~$RM $lib.exp' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' _LT_TAGVAR(hardcode_shlibpath_var, $1)=no case $host_os in solaris2.[[0-5]] | solaris2.[[0-5]].*) ;; *) # The compiler driver will combine and reorder linker options, # but understands `-z linker_flag'. # Supported since Solaris 2.6 (maybe 2.5.1?) _LT_TAGVAR(whole_archive_flag_spec, $1)='-z allextract$convenience -z defaultextract' ;; esac _LT_TAGVAR(link_all_deplibs, $1)=yes output_verbose_link_cmd='func_echo_all' # Archives containing C++ object files must be created using # "CC -xar", where "CC" is the Sun C++ compiler. This is # necessary to make sure instantiated templates are included # in the archive. _LT_TAGVAR(old_archive_cmds, $1)='$CC -xar -o $oldlib $oldobjs' ;; gcx*) # Green Hills C++ Compiler _LT_TAGVAR(archive_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-h $wl$soname -o $lib' # The C++ compiler must be used to create the archive. _LT_TAGVAR(old_archive_cmds, $1)='$CC $LDFLAGS -archive -o $oldlib $oldobjs' ;; *) # GNU C++ compiler with Solaris linker if test "$GXX" = yes && test "$with_gnu_ld" = no; then _LT_TAGVAR(no_undefined_flag, $1)=' ${wl}-z ${wl}defs' if $CC --version | $GREP -v '^2\.7' > /dev/null; then _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -nostdlib $LDFLAGS $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-h $wl$soname -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ $CC -shared $pic_flag -nostdlib ${wl}-M $wl$lib.exp -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~$RM $lib.exp' # Commands to make compiler produce verbose output that lists # what "hidden" libraries, object files and flags are used when # linking a shared library. output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP -v "^Configured with:" | $GREP "\-L"' else # g++ 2.7 appears to require `-G' NOT `-shared' on this # platform. _LT_TAGVAR(archive_cmds, $1)='$CC -G -nostdlib $LDFLAGS $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-h $wl$soname -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ $CC -G -nostdlib ${wl}-M $wl$lib.exp -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~$RM $lib.exp' # Commands to make compiler produce verbose output that lists # what "hidden" libraries, object files and flags are used when # linking a shared library. output_verbose_link_cmd='$CC -G $CFLAGS -v conftest.$objext 2>&1 | $GREP -v "^Configured with:" | $GREP "\-L"' fi _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-R $wl$libdir' case $host_os in solaris2.[[0-5]] | solaris2.[[0-5]].*) ;; *) _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}-z ${wl}allextract$convenience ${wl}-z ${wl}defaultextract' ;; esac fi ;; esac ;; sysv4*uw2* | sysv5OpenUNIX* | sysv5UnixWare7.[[01]].[[10]]* | unixware7* | sco3.2v5.0.[[024]]*) _LT_TAGVAR(no_undefined_flag, $1)='${wl}-z,text' _LT_TAGVAR(archive_cmds_need_lc, $1)=no _LT_TAGVAR(hardcode_shlibpath_var, $1)=no runpath_var='LD_RUN_PATH' case $cc_basename in CC*) _LT_TAGVAR(archive_cmds, $1)='$CC -G ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -G ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' ;; *) _LT_TAGVAR(archive_cmds, $1)='$CC -shared ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' ;; esac ;; sysv5* | sco3.2v5* | sco5v6*) # Note: We can NOT use -z defs as we might desire, because we do not # link with -lc, and that would cause any symbols used from libc to # always be unresolved, which means just about no library would # ever link correctly. If we're not using GNU ld we use -z text # though, which does catch some bad symbols but isn't as heavy-handed # as -z defs. _LT_TAGVAR(no_undefined_flag, $1)='${wl}-z,text' _LT_TAGVAR(allow_undefined_flag, $1)='${wl}-z,nodefs' _LT_TAGVAR(archive_cmds_need_lc, $1)=no _LT_TAGVAR(hardcode_shlibpath_var, $1)=no _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-R,$libdir' _LT_TAGVAR(hardcode_libdir_separator, $1)=':' _LT_TAGVAR(link_all_deplibs, $1)=yes _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-Bexport' runpath_var='LD_RUN_PATH' case $cc_basename in CC*) _LT_TAGVAR(archive_cmds, $1)='$CC -G ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -G ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' _LT_TAGVAR(old_archive_cmds, $1)='$CC -Tprelink_objects $oldobjs~ '"$_LT_TAGVAR(old_archive_cmds, $1)" _LT_TAGVAR(reload_cmds, $1)='$CC -Tprelink_objects $reload_objs~ '"$_LT_TAGVAR(reload_cmds, $1)" ;; *) _LT_TAGVAR(archive_cmds, $1)='$CC -shared ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' ;; esac ;; tandem*) case $cc_basename in NCC*) # NonStop-UX NCC 3.20 # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; *) # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; esac ;; vxworks*) # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; *) # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; esac AC_MSG_RESULT([$_LT_TAGVAR(ld_shlibs, $1)]) test "$_LT_TAGVAR(ld_shlibs, $1)" = no && can_build_shared=no _LT_TAGVAR(GCC, $1)="$GXX" _LT_TAGVAR(LD, $1)="$LD" ## CAVEAT EMPTOR: ## There is no encapsulation within the following macros, do not change ## the running order or otherwise move them around unless you know exactly ## what you are doing... _LT_SYS_HIDDEN_LIBDEPS($1) _LT_COMPILER_PIC($1) _LT_COMPILER_C_O($1) _LT_COMPILER_FILE_LOCKS($1) _LT_LINKER_SHLIBS($1) _LT_SYS_DYNAMIC_LINKER($1) _LT_LINKER_HARDCODE_LIBPATH($1) _LT_CONFIG($1) fi # test -n "$compiler" CC=$lt_save_CC CFLAGS=$lt_save_CFLAGS LDCXX=$LD LD=$lt_save_LD GCC=$lt_save_GCC with_gnu_ld=$lt_save_with_gnu_ld lt_cv_path_LDCXX=$lt_cv_path_LD lt_cv_path_LD=$lt_save_path_LD lt_cv_prog_gnu_ldcxx=$lt_cv_prog_gnu_ld lt_cv_prog_gnu_ld=$lt_save_with_gnu_ld fi # test "$_lt_caught_CXX_error" != yes AC_LANG_POP ])# _LT_LANG_CXX_CONFIG # _LT_FUNC_STRIPNAME_CNF # ---------------------- # func_stripname_cnf prefix suffix name # strip PREFIX and SUFFIX off of NAME. # PREFIX and SUFFIX must not contain globbing or regex special # characters, hashes, percent signs, but SUFFIX may contain a leading # dot (in which case that matches only a dot). # # This function is identical to the (non-XSI) version of func_stripname, # except this one can be used by m4 code that may be executed by configure, # rather than the libtool script. m4_defun([_LT_FUNC_STRIPNAME_CNF],[dnl AC_REQUIRE([_LT_DECL_SED]) AC_REQUIRE([_LT_PROG_ECHO_BACKSLASH]) func_stripname_cnf () { case ${2} in .*) func_stripname_result=`$ECHO "${3}" | $SED "s%^${1}%%; s%\\\\${2}\$%%"`;; *) func_stripname_result=`$ECHO "${3}" | $SED "s%^${1}%%; s%${2}\$%%"`;; esac } # func_stripname_cnf ])# _LT_FUNC_STRIPNAME_CNF # _LT_SYS_HIDDEN_LIBDEPS([TAGNAME]) # --------------------------------- # Figure out "hidden" library dependencies from verbose # compiler output when linking a shared library. # Parse the compiler output and extract the necessary # objects, libraries and library flags. m4_defun([_LT_SYS_HIDDEN_LIBDEPS], [m4_require([_LT_FILEUTILS_DEFAULTS])dnl AC_REQUIRE([_LT_FUNC_STRIPNAME_CNF])dnl # Dependencies to place before and after the object being linked: _LT_TAGVAR(predep_objects, $1)= _LT_TAGVAR(postdep_objects, $1)= _LT_TAGVAR(predeps, $1)= _LT_TAGVAR(postdeps, $1)= _LT_TAGVAR(compiler_lib_search_path, $1)= dnl we can't use the lt_simple_compile_test_code here, dnl because it contains code intended for an executable, dnl not a library. It's possible we should let each dnl tag define a new lt_????_link_test_code variable, dnl but it's only used here... m4_if([$1], [], [cat > conftest.$ac_ext <<_LT_EOF int a; void foo (void) { a = 0; } _LT_EOF ], [$1], [CXX], [cat > conftest.$ac_ext <<_LT_EOF class Foo { public: Foo (void) { a = 0; } private: int a; }; _LT_EOF ], [$1], [F77], [cat > conftest.$ac_ext <<_LT_EOF subroutine foo implicit none integer*4 a a=0 return end _LT_EOF ], [$1], [FC], [cat > conftest.$ac_ext <<_LT_EOF subroutine foo implicit none integer a a=0 return end _LT_EOF ], [$1], [GCJ], [cat > conftest.$ac_ext <<_LT_EOF public class foo { private int a; public void bar (void) { a = 0; } }; _LT_EOF ], [$1], [GO], [cat > conftest.$ac_ext <<_LT_EOF package foo func foo() { } _LT_EOF ]) _lt_libdeps_save_CFLAGS=$CFLAGS case "$CC $CFLAGS " in #( *\ -flto*\ *) CFLAGS="$CFLAGS -fno-lto" ;; *\ -fwhopr*\ *) CFLAGS="$CFLAGS -fno-whopr" ;; *\ -fuse-linker-plugin*\ *) CFLAGS="$CFLAGS -fno-use-linker-plugin" ;; esac dnl Parse the compiler output and extract the necessary dnl objects, libraries and library flags. if AC_TRY_EVAL(ac_compile); then # Parse the compiler output and extract the necessary # objects, libraries and library flags. # Sentinel used to keep track of whether or not we are before # the conftest object file. pre_test_object_deps_done=no for p in `eval "$output_verbose_link_cmd"`; do case ${prev}${p} in -L* | -R* | -l*) # Some compilers place space between "-{L,R}" and the path. # Remove the space. if test $p = "-L" || test $p = "-R"; then prev=$p continue fi # Expand the sysroot to ease extracting the directories later. if test -z "$prev"; then case $p in -L*) func_stripname_cnf '-L' '' "$p"; prev=-L; p=$func_stripname_result ;; -R*) func_stripname_cnf '-R' '' "$p"; prev=-R; p=$func_stripname_result ;; -l*) func_stripname_cnf '-l' '' "$p"; prev=-l; p=$func_stripname_result ;; esac fi case $p in =*) func_stripname_cnf '=' '' "$p"; p=$lt_sysroot$func_stripname_result ;; esac if test "$pre_test_object_deps_done" = no; then case ${prev} in -L | -R) # Internal compiler library paths should come after those # provided the user. The postdeps already come after the # user supplied libs so there is no need to process them. if test -z "$_LT_TAGVAR(compiler_lib_search_path, $1)"; then _LT_TAGVAR(compiler_lib_search_path, $1)="${prev}${p}" else _LT_TAGVAR(compiler_lib_search_path, $1)="${_LT_TAGVAR(compiler_lib_search_path, $1)} ${prev}${p}" fi ;; # The "-l" case would never come before the object being # linked, so don't bother handling this case. esac else if test -z "$_LT_TAGVAR(postdeps, $1)"; then _LT_TAGVAR(postdeps, $1)="${prev}${p}" else _LT_TAGVAR(postdeps, $1)="${_LT_TAGVAR(postdeps, $1)} ${prev}${p}" fi fi prev= ;; *.lto.$objext) ;; # Ignore GCC LTO objects *.$objext) # This assumes that the test object file only shows up # once in the compiler output. if test "$p" = "conftest.$objext"; then pre_test_object_deps_done=yes continue fi if test "$pre_test_object_deps_done" = no; then if test -z "$_LT_TAGVAR(predep_objects, $1)"; then _LT_TAGVAR(predep_objects, $1)="$p" else _LT_TAGVAR(predep_objects, $1)="$_LT_TAGVAR(predep_objects, $1) $p" fi else if test -z "$_LT_TAGVAR(postdep_objects, $1)"; then _LT_TAGVAR(postdep_objects, $1)="$p" else _LT_TAGVAR(postdep_objects, $1)="$_LT_TAGVAR(postdep_objects, $1) $p" fi fi ;; *) ;; # Ignore the rest. esac done # Clean up. rm -f a.out a.exe else echo "libtool.m4: error: problem compiling $1 test program" fi $RM -f confest.$objext CFLAGS=$_lt_libdeps_save_CFLAGS # PORTME: override above test on systems where it is broken m4_if([$1], [CXX], [case $host_os in interix[[3-9]]*) # Interix 3.5 installs completely hosed .la files for C++, so rather than # hack all around it, let's just trust "g++" to DTRT. _LT_TAGVAR(predep_objects,$1)= _LT_TAGVAR(postdep_objects,$1)= _LT_TAGVAR(postdeps,$1)= ;; linux*) case `$CC -V 2>&1 | sed 5q` in *Sun\ C*) # Sun C++ 5.9 # The more standards-conforming stlport4 library is # incompatible with the Cstd library. Avoid specifying # it if it's in CXXFLAGS. Ignore libCrun as # -library=stlport4 depends on it. case " $CXX $CXXFLAGS " in *" -library=stlport4 "*) solaris_use_stlport4=yes ;; esac if test "$solaris_use_stlport4" != yes; then _LT_TAGVAR(postdeps,$1)='-library=Cstd -library=Crun' fi ;; esac ;; solaris*) case $cc_basename in CC* | sunCC*) # The more standards-conforming stlport4 library is # incompatible with the Cstd library. Avoid specifying # it if it's in CXXFLAGS. Ignore libCrun as # -library=stlport4 depends on it. case " $CXX $CXXFLAGS " in *" -library=stlport4 "*) solaris_use_stlport4=yes ;; esac # Adding this requires a known-good setup of shared libraries for # Sun compiler versions before 5.6, else PIC objects from an old # archive will be linked into the output, leading to subtle bugs. if test "$solaris_use_stlport4" != yes; then _LT_TAGVAR(postdeps,$1)='-library=Cstd -library=Crun' fi ;; esac ;; esac ]) case " $_LT_TAGVAR(postdeps, $1) " in *" -lc "*) _LT_TAGVAR(archive_cmds_need_lc, $1)=no ;; esac _LT_TAGVAR(compiler_lib_search_dirs, $1)= if test -n "${_LT_TAGVAR(compiler_lib_search_path, $1)}"; then _LT_TAGVAR(compiler_lib_search_dirs, $1)=`echo " ${_LT_TAGVAR(compiler_lib_search_path, $1)}" | ${SED} -e 's! -L! !g' -e 's!^ !!'` fi _LT_TAGDECL([], [compiler_lib_search_dirs], [1], [The directories searched by this compiler when creating a shared library]) _LT_TAGDECL([], [predep_objects], [1], [Dependencies to place before and after the objects being linked to create a shared library]) _LT_TAGDECL([], [postdep_objects], [1]) _LT_TAGDECL([], [predeps], [1]) _LT_TAGDECL([], [postdeps], [1]) _LT_TAGDECL([], [compiler_lib_search_path], [1], [The library search path used internally by the compiler when linking a shared library]) ])# _LT_SYS_HIDDEN_LIBDEPS # _LT_LANG_F77_CONFIG([TAG]) # -------------------------- # Ensure that the configuration variables for a Fortran 77 compiler are # suitably defined. These variables are subsequently used by _LT_CONFIG # to write the compiler configuration to `libtool'. m4_defun([_LT_LANG_F77_CONFIG], [AC_LANG_PUSH(Fortran 77) if test -z "$F77" || test "X$F77" = "Xno"; then _lt_disable_F77=yes fi _LT_TAGVAR(archive_cmds_need_lc, $1)=no _LT_TAGVAR(allow_undefined_flag, $1)= _LT_TAGVAR(always_export_symbols, $1)=no _LT_TAGVAR(archive_expsym_cmds, $1)= _LT_TAGVAR(export_dynamic_flag_spec, $1)= _LT_TAGVAR(hardcode_direct, $1)=no _LT_TAGVAR(hardcode_direct_absolute, $1)=no _LT_TAGVAR(hardcode_libdir_flag_spec, $1)= _LT_TAGVAR(hardcode_libdir_separator, $1)= _LT_TAGVAR(hardcode_minus_L, $1)=no _LT_TAGVAR(hardcode_automatic, $1)=no _LT_TAGVAR(inherit_rpath, $1)=no _LT_TAGVAR(module_cmds, $1)= _LT_TAGVAR(module_expsym_cmds, $1)= _LT_TAGVAR(link_all_deplibs, $1)=unknown _LT_TAGVAR(old_archive_cmds, $1)=$old_archive_cmds _LT_TAGVAR(reload_flag, $1)=$reload_flag _LT_TAGVAR(reload_cmds, $1)=$reload_cmds _LT_TAGVAR(no_undefined_flag, $1)= _LT_TAGVAR(whole_archive_flag_spec, $1)= _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=no # Source file extension for f77 test sources. ac_ext=f # Object file extension for compiled f77 test sources. objext=o _LT_TAGVAR(objext, $1)=$objext # No sense in running all these tests if we already determined that # the F77 compiler isn't working. Some variables (like enable_shared) # are currently assumed to apply to all compilers on this platform, # and will be corrupted by setting them based on a non-working compiler. if test "$_lt_disable_F77" != yes; then # Code to be used in simple compile tests lt_simple_compile_test_code="\ subroutine t return end " # Code to be used in simple link tests lt_simple_link_test_code="\ program t end " # ltmain only uses $CC for tagged configurations so make sure $CC is set. _LT_TAG_COMPILER # save warnings/boilerplate of simple test code _LT_COMPILER_BOILERPLATE _LT_LINKER_BOILERPLATE # Allow CC to be a program name with arguments. lt_save_CC="$CC" lt_save_GCC=$GCC lt_save_CFLAGS=$CFLAGS CC=${F77-"f77"} CFLAGS=$FFLAGS compiler=$CC _LT_TAGVAR(compiler, $1)=$CC _LT_CC_BASENAME([$compiler]) GCC=$G77 if test -n "$compiler"; then AC_MSG_CHECKING([if libtool supports shared libraries]) AC_MSG_RESULT([$can_build_shared]) AC_MSG_CHECKING([whether to build shared libraries]) test "$can_build_shared" = "no" && enable_shared=no # On AIX, shared libraries and static libraries use the same namespace, and # are all built from PIC. case $host_os in aix3*) test "$enable_shared" = yes && enable_static=no if test -n "$RANLIB"; then archive_cmds="$archive_cmds~\$RANLIB \$lib" postinstall_cmds='$RANLIB $lib' fi ;; aix[[4-9]]*) if test "$host_cpu" != ia64 && test "$aix_use_runtimelinking" = no ; then test "$enable_shared" = yes && enable_static=no fi ;; esac AC_MSG_RESULT([$enable_shared]) AC_MSG_CHECKING([whether to build static libraries]) # Make sure either enable_shared or enable_static is yes. test "$enable_shared" = yes || enable_static=yes AC_MSG_RESULT([$enable_static]) _LT_TAGVAR(GCC, $1)="$G77" _LT_TAGVAR(LD, $1)="$LD" ## CAVEAT EMPTOR: ## There is no encapsulation within the following macros, do not change ## the running order or otherwise move them around unless you know exactly ## what you are doing... _LT_COMPILER_PIC($1) _LT_COMPILER_C_O($1) _LT_COMPILER_FILE_LOCKS($1) _LT_LINKER_SHLIBS($1) _LT_SYS_DYNAMIC_LINKER($1) _LT_LINKER_HARDCODE_LIBPATH($1) _LT_CONFIG($1) fi # test -n "$compiler" GCC=$lt_save_GCC CC="$lt_save_CC" CFLAGS="$lt_save_CFLAGS" fi # test "$_lt_disable_F77" != yes AC_LANG_POP ])# _LT_LANG_F77_CONFIG # _LT_LANG_FC_CONFIG([TAG]) # ------------------------- # Ensure that the configuration variables for a Fortran compiler are # suitably defined. These variables are subsequently used by _LT_CONFIG # to write the compiler configuration to `libtool'. m4_defun([_LT_LANG_FC_CONFIG], [AC_LANG_PUSH(Fortran) if test -z "$FC" || test "X$FC" = "Xno"; then _lt_disable_FC=yes fi _LT_TAGVAR(archive_cmds_need_lc, $1)=no _LT_TAGVAR(allow_undefined_flag, $1)= _LT_TAGVAR(always_export_symbols, $1)=no _LT_TAGVAR(archive_expsym_cmds, $1)= _LT_TAGVAR(export_dynamic_flag_spec, $1)= _LT_TAGVAR(hardcode_direct, $1)=no _LT_TAGVAR(hardcode_direct_absolute, $1)=no _LT_TAGVAR(hardcode_libdir_flag_spec, $1)= _LT_TAGVAR(hardcode_libdir_separator, $1)= _LT_TAGVAR(hardcode_minus_L, $1)=no _LT_TAGVAR(hardcode_automatic, $1)=no _LT_TAGVAR(inherit_rpath, $1)=no _LT_TAGVAR(module_cmds, $1)= _LT_TAGVAR(module_expsym_cmds, $1)= _LT_TAGVAR(link_all_deplibs, $1)=unknown _LT_TAGVAR(old_archive_cmds, $1)=$old_archive_cmds _LT_TAGVAR(reload_flag, $1)=$reload_flag _LT_TAGVAR(reload_cmds, $1)=$reload_cmds _LT_TAGVAR(no_undefined_flag, $1)= _LT_TAGVAR(whole_archive_flag_spec, $1)= _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=no # Source file extension for fc test sources. ac_ext=${ac_fc_srcext-f} # Object file extension for compiled fc test sources. objext=o _LT_TAGVAR(objext, $1)=$objext # No sense in running all these tests if we already determined that # the FC compiler isn't working. Some variables (like enable_shared) # are currently assumed to apply to all compilers on this platform, # and will be corrupted by setting them based on a non-working compiler. if test "$_lt_disable_FC" != yes; then # Code to be used in simple compile tests lt_simple_compile_test_code="\ subroutine t return end " # Code to be used in simple link tests lt_simple_link_test_code="\ program t end " # ltmain only uses $CC for tagged configurations so make sure $CC is set. _LT_TAG_COMPILER # save warnings/boilerplate of simple test code _LT_COMPILER_BOILERPLATE _LT_LINKER_BOILERPLATE # Allow CC to be a program name with arguments. lt_save_CC="$CC" lt_save_GCC=$GCC lt_save_CFLAGS=$CFLAGS CC=${FC-"f95"} CFLAGS=$FCFLAGS compiler=$CC GCC=$ac_cv_fc_compiler_gnu _LT_TAGVAR(compiler, $1)=$CC _LT_CC_BASENAME([$compiler]) if test -n "$compiler"; then AC_MSG_CHECKING([if libtool supports shared libraries]) AC_MSG_RESULT([$can_build_shared]) AC_MSG_CHECKING([whether to build shared libraries]) test "$can_build_shared" = "no" && enable_shared=no # On AIX, shared libraries and static libraries use the same namespace, and # are all built from PIC. case $host_os in aix3*) test "$enable_shared" = yes && enable_static=no if test -n "$RANLIB"; then archive_cmds="$archive_cmds~\$RANLIB \$lib" postinstall_cmds='$RANLIB $lib' fi ;; aix[[4-9]]*) if test "$host_cpu" != ia64 && test "$aix_use_runtimelinking" = no ; then test "$enable_shared" = yes && enable_static=no fi ;; esac AC_MSG_RESULT([$enable_shared]) AC_MSG_CHECKING([whether to build static libraries]) # Make sure either enable_shared or enable_static is yes. test "$enable_shared" = yes || enable_static=yes AC_MSG_RESULT([$enable_static]) _LT_TAGVAR(GCC, $1)="$ac_cv_fc_compiler_gnu" _LT_TAGVAR(LD, $1)="$LD" ## CAVEAT EMPTOR: ## There is no encapsulation within the following macros, do not change ## the running order or otherwise move them around unless you know exactly ## what you are doing... _LT_SYS_HIDDEN_LIBDEPS($1) _LT_COMPILER_PIC($1) _LT_COMPILER_C_O($1) _LT_COMPILER_FILE_LOCKS($1) _LT_LINKER_SHLIBS($1) _LT_SYS_DYNAMIC_LINKER($1) _LT_LINKER_HARDCODE_LIBPATH($1) _LT_CONFIG($1) fi # test -n "$compiler" GCC=$lt_save_GCC CC=$lt_save_CC CFLAGS=$lt_save_CFLAGS fi # test "$_lt_disable_FC" != yes AC_LANG_POP ])# _LT_LANG_FC_CONFIG # _LT_LANG_GCJ_CONFIG([TAG]) # -------------------------- # Ensure that the configuration variables for the GNU Java Compiler compiler # are suitably defined. These variables are subsequently used by _LT_CONFIG # to write the compiler configuration to `libtool'. m4_defun([_LT_LANG_GCJ_CONFIG], [AC_REQUIRE([LT_PROG_GCJ])dnl AC_LANG_SAVE # Source file extension for Java test sources. ac_ext=java # Object file extension for compiled Java test sources. objext=o _LT_TAGVAR(objext, $1)=$objext # Code to be used in simple compile tests lt_simple_compile_test_code="class foo {}" # Code to be used in simple link tests lt_simple_link_test_code='public class conftest { public static void main(String[[]] argv) {}; }' # ltmain only uses $CC for tagged configurations so make sure $CC is set. _LT_TAG_COMPILER # save warnings/boilerplate of simple test code _LT_COMPILER_BOILERPLATE _LT_LINKER_BOILERPLATE # Allow CC to be a program name with arguments. lt_save_CC=$CC lt_save_CFLAGS=$CFLAGS lt_save_GCC=$GCC GCC=yes CC=${GCJ-"gcj"} CFLAGS=$GCJFLAGS compiler=$CC _LT_TAGVAR(compiler, $1)=$CC _LT_TAGVAR(LD, $1)="$LD" _LT_CC_BASENAME([$compiler]) # GCJ did not exist at the time GCC didn't implicitly link libc in. _LT_TAGVAR(archive_cmds_need_lc, $1)=no _LT_TAGVAR(old_archive_cmds, $1)=$old_archive_cmds _LT_TAGVAR(reload_flag, $1)=$reload_flag _LT_TAGVAR(reload_cmds, $1)=$reload_cmds ## CAVEAT EMPTOR: ## There is no encapsulation within the following macros, do not change ## the running order or otherwise move them around unless you know exactly ## what you are doing... if test -n "$compiler"; then _LT_COMPILER_NO_RTTI($1) _LT_COMPILER_PIC($1) _LT_COMPILER_C_O($1) _LT_COMPILER_FILE_LOCKS($1) _LT_LINKER_SHLIBS($1) _LT_LINKER_HARDCODE_LIBPATH($1) _LT_CONFIG($1) fi AC_LANG_RESTORE GCC=$lt_save_GCC CC=$lt_save_CC CFLAGS=$lt_save_CFLAGS ])# _LT_LANG_GCJ_CONFIG # _LT_LANG_GO_CONFIG([TAG]) # -------------------------- # Ensure that the configuration variables for the GNU Go compiler # are suitably defined. These variables are subsequently used by _LT_CONFIG # to write the compiler configuration to `libtool'. m4_defun([_LT_LANG_GO_CONFIG], [AC_REQUIRE([LT_PROG_GO])dnl AC_LANG_SAVE # Source file extension for Go test sources. ac_ext=go # Object file extension for compiled Go test sources. objext=o _LT_TAGVAR(objext, $1)=$objext # Code to be used in simple compile tests lt_simple_compile_test_code="package main; func main() { }" # Code to be used in simple link tests lt_simple_link_test_code='package main; func main() { }' # ltmain only uses $CC for tagged configurations so make sure $CC is set. _LT_TAG_COMPILER # save warnings/boilerplate of simple test code _LT_COMPILER_BOILERPLATE _LT_LINKER_BOILERPLATE # Allow CC to be a program name with arguments. lt_save_CC=$CC lt_save_CFLAGS=$CFLAGS lt_save_GCC=$GCC GCC=yes CC=${GOC-"gccgo"} CFLAGS=$GOFLAGS compiler=$CC _LT_TAGVAR(compiler, $1)=$CC _LT_TAGVAR(LD, $1)="$LD" _LT_CC_BASENAME([$compiler]) # Go did not exist at the time GCC didn't implicitly link libc in. _LT_TAGVAR(archive_cmds_need_lc, $1)=no _LT_TAGVAR(old_archive_cmds, $1)=$old_archive_cmds _LT_TAGVAR(reload_flag, $1)=$reload_flag _LT_TAGVAR(reload_cmds, $1)=$reload_cmds ## CAVEAT EMPTOR: ## There is no encapsulation within the following macros, do not change ## the running order or otherwise move them around unless you know exactly ## what you are doing... if test -n "$compiler"; then _LT_COMPILER_NO_RTTI($1) _LT_COMPILER_PIC($1) _LT_COMPILER_C_O($1) _LT_COMPILER_FILE_LOCKS($1) _LT_LINKER_SHLIBS($1) _LT_LINKER_HARDCODE_LIBPATH($1) _LT_CONFIG($1) fi AC_LANG_RESTORE GCC=$lt_save_GCC CC=$lt_save_CC CFLAGS=$lt_save_CFLAGS ])# _LT_LANG_GO_CONFIG # _LT_LANG_RC_CONFIG([TAG]) # ------------------------- # Ensure that the configuration variables for the Windows resource compiler # are suitably defined. These variables are subsequently used by _LT_CONFIG # to write the compiler configuration to `libtool'. m4_defun([_LT_LANG_RC_CONFIG], [AC_REQUIRE([LT_PROG_RC])dnl AC_LANG_SAVE # Source file extension for RC test sources. ac_ext=rc # Object file extension for compiled RC test sources. objext=o _LT_TAGVAR(objext, $1)=$objext # Code to be used in simple compile tests lt_simple_compile_test_code='sample MENU { MENUITEM "&Soup", 100, CHECKED }' # Code to be used in simple link tests lt_simple_link_test_code="$lt_simple_compile_test_code" # ltmain only uses $CC for tagged configurations so make sure $CC is set. _LT_TAG_COMPILER # save warnings/boilerplate of simple test code _LT_COMPILER_BOILERPLATE _LT_LINKER_BOILERPLATE # Allow CC to be a program name with arguments. lt_save_CC="$CC" lt_save_CFLAGS=$CFLAGS lt_save_GCC=$GCC GCC= CC=${RC-"windres"} CFLAGS= compiler=$CC _LT_TAGVAR(compiler, $1)=$CC _LT_CC_BASENAME([$compiler]) _LT_TAGVAR(lt_cv_prog_compiler_c_o, $1)=yes if test -n "$compiler"; then : _LT_CONFIG($1) fi GCC=$lt_save_GCC AC_LANG_RESTORE CC=$lt_save_CC CFLAGS=$lt_save_CFLAGS ])# _LT_LANG_RC_CONFIG # LT_PROG_GCJ # ----------- AC_DEFUN([LT_PROG_GCJ], [m4_ifdef([AC_PROG_GCJ], [AC_PROG_GCJ], [m4_ifdef([A][M_PROG_GCJ], [A][M_PROG_GCJ], [AC_CHECK_TOOL(GCJ, gcj,) test "x${GCJFLAGS+set}" = xset || GCJFLAGS="-g -O2" AC_SUBST(GCJFLAGS)])])[]dnl ]) # Old name: AU_ALIAS([LT_AC_PROG_GCJ], [LT_PROG_GCJ]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([LT_AC_PROG_GCJ], []) # LT_PROG_GO # ---------- AC_DEFUN([LT_PROG_GO], [AC_CHECK_TOOL(GOC, gccgo,) ]) # LT_PROG_RC # ---------- AC_DEFUN([LT_PROG_RC], [AC_CHECK_TOOL(RC, windres,) ]) # Old name: AU_ALIAS([LT_AC_PROG_RC], [LT_PROG_RC]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([LT_AC_PROG_RC], []) # _LT_DECL_EGREP # -------------- # If we don't have a new enough Autoconf to choose the best grep # available, choose the one first in the user's PATH. m4_defun([_LT_DECL_EGREP], [AC_REQUIRE([AC_PROG_EGREP])dnl AC_REQUIRE([AC_PROG_FGREP])dnl test -z "$GREP" && GREP=grep _LT_DECL([], [GREP], [1], [A grep program that handles long lines]) _LT_DECL([], [EGREP], [1], [An ERE matcher]) _LT_DECL([], [FGREP], [1], [A literal string matcher]) dnl Non-bleeding-edge autoconf doesn't subst GREP, so do it here too AC_SUBST([GREP]) ]) # _LT_DECL_OBJDUMP # -------------- # If we don't have a new enough Autoconf to choose the best objdump # available, choose the one first in the user's PATH. m4_defun([_LT_DECL_OBJDUMP], [AC_CHECK_TOOL(OBJDUMP, objdump, false) test -z "$OBJDUMP" && OBJDUMP=objdump _LT_DECL([], [OBJDUMP], [1], [An object symbol dumper]) AC_SUBST([OBJDUMP]) ]) # _LT_DECL_DLLTOOL # ---------------- # Ensure DLLTOOL variable is set. m4_defun([_LT_DECL_DLLTOOL], [AC_CHECK_TOOL(DLLTOOL, dlltool, false) test -z "$DLLTOOL" && DLLTOOL=dlltool _LT_DECL([], [DLLTOOL], [1], [DLL creation program]) AC_SUBST([DLLTOOL]) ]) # _LT_DECL_SED # ------------ # Check for a fully-functional sed program, that truncates # as few characters as possible. Prefer GNU sed if found. m4_defun([_LT_DECL_SED], [AC_PROG_SED test -z "$SED" && SED=sed Xsed="$SED -e 1s/^X//" _LT_DECL([], [SED], [1], [A sed program that does not truncate output]) _LT_DECL([], [Xsed], ["\$SED -e 1s/^X//"], [Sed that helps us avoid accidentally triggering echo(1) options like -n]) ])# _LT_DECL_SED m4_ifndef([AC_PROG_SED], [ ############################################################ # NOTE: This macro has been submitted for inclusion into # # GNU Autoconf as AC_PROG_SED. When it is available in # # a released version of Autoconf we should remove this # # macro and use it instead. # ############################################################ m4_defun([AC_PROG_SED], [AC_MSG_CHECKING([for a sed that does not truncate output]) AC_CACHE_VAL(lt_cv_path_SED, [# Loop through the user's path and test for sed and gsed. # Then use that list of sed's as ones to test for truncation. as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for lt_ac_prog in sed gsed; do for ac_exec_ext in '' $ac_executable_extensions; do if $as_executable_p "$as_dir/$lt_ac_prog$ac_exec_ext"; then lt_ac_sed_list="$lt_ac_sed_list $as_dir/$lt_ac_prog$ac_exec_ext" fi done done done IFS=$as_save_IFS lt_ac_max=0 lt_ac_count=0 # Add /usr/xpg4/bin/sed as it is typically found on Solaris # along with /bin/sed that truncates output. for lt_ac_sed in $lt_ac_sed_list /usr/xpg4/bin/sed; do test ! -f $lt_ac_sed && continue cat /dev/null > conftest.in lt_ac_count=0 echo $ECHO_N "0123456789$ECHO_C" >conftest.in # Check for GNU sed and select it if it is found. if "$lt_ac_sed" --version 2>&1 < /dev/null | grep 'GNU' > /dev/null; then lt_cv_path_SED=$lt_ac_sed break fi while true; do cat conftest.in conftest.in >conftest.tmp mv conftest.tmp conftest.in cp conftest.in conftest.nl echo >>conftest.nl $lt_ac_sed -e 's/a$//' < conftest.nl >conftest.out || break cmp -s conftest.out conftest.nl || break # 10000 chars as input seems more than enough test $lt_ac_count -gt 10 && break lt_ac_count=`expr $lt_ac_count + 1` if test $lt_ac_count -gt $lt_ac_max; then lt_ac_max=$lt_ac_count lt_cv_path_SED=$lt_ac_sed fi done done ]) SED=$lt_cv_path_SED AC_SUBST([SED]) AC_MSG_RESULT([$SED]) ])#AC_PROG_SED ])#m4_ifndef # Old name: AU_ALIAS([LT_AC_PROG_SED], [AC_PROG_SED]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([LT_AC_PROG_SED], []) # _LT_CHECK_SHELL_FEATURES # ------------------------ # Find out whether the shell is Bourne or XSI compatible, # or has some other useful features. m4_defun([_LT_CHECK_SHELL_FEATURES], [AC_MSG_CHECKING([whether the shell understands some XSI constructs]) # Try some XSI features xsi_shell=no ( _lt_dummy="a/b/c" test "${_lt_dummy##*/},${_lt_dummy%/*},${_lt_dummy#??}"${_lt_dummy%"$_lt_dummy"}, \ = c,a/b,b/c, \ && eval 'test $(( 1 + 1 )) -eq 2 \ && test "${#_lt_dummy}" -eq 5' ) >/dev/null 2>&1 \ && xsi_shell=yes AC_MSG_RESULT([$xsi_shell]) _LT_CONFIG_LIBTOOL_INIT([xsi_shell='$xsi_shell']) AC_MSG_CHECKING([whether the shell understands "+="]) lt_shell_append=no ( foo=bar; set foo baz; eval "$[1]+=\$[2]" && test "$foo" = barbaz ) \ >/dev/null 2>&1 \ && lt_shell_append=yes AC_MSG_RESULT([$lt_shell_append]) _LT_CONFIG_LIBTOOL_INIT([lt_shell_append='$lt_shell_append']) if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then lt_unset=unset else lt_unset=false fi _LT_DECL([], [lt_unset], [0], [whether the shell understands "unset"])dnl # test EBCDIC or ASCII case `echo X|tr X '\101'` in A) # ASCII based system # \n is not interpreted correctly by Solaris 8 /usr/ucb/tr lt_SP2NL='tr \040 \012' lt_NL2SP='tr \015\012 \040\040' ;; *) # EBCDIC based system lt_SP2NL='tr \100 \n' lt_NL2SP='tr \r\n \100\100' ;; esac _LT_DECL([SP2NL], [lt_SP2NL], [1], [turn spaces into newlines])dnl _LT_DECL([NL2SP], [lt_NL2SP], [1], [turn newlines into spaces])dnl ])# _LT_CHECK_SHELL_FEATURES # _LT_PROG_FUNCTION_REPLACE (FUNCNAME, REPLACEMENT-BODY) # ------------------------------------------------------ # In `$cfgfile', look for function FUNCNAME delimited by `^FUNCNAME ()$' and # '^} FUNCNAME ', and replace its body with REPLACEMENT-BODY. m4_defun([_LT_PROG_FUNCTION_REPLACE], [dnl { sed -e '/^$1 ()$/,/^} # $1 /c\ $1 ()\ {\ m4_bpatsubsts([$2], [$], [\\], [^\([ ]\)], [\\\1]) } # Extended-shell $1 implementation' "$cfgfile" > $cfgfile.tmp \ && mv -f "$cfgfile.tmp" "$cfgfile" \ || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp") test 0 -eq $? || _lt_function_replace_fail=: ]) # _LT_PROG_REPLACE_SHELLFNS # ------------------------- # Replace existing portable implementations of several shell functions with # equivalent extended shell implementations where those features are available.. m4_defun([_LT_PROG_REPLACE_SHELLFNS], [if test x"$xsi_shell" = xyes; then _LT_PROG_FUNCTION_REPLACE([func_dirname], [dnl case ${1} in */*) func_dirname_result="${1%/*}${2}" ;; * ) func_dirname_result="${3}" ;; esac]) _LT_PROG_FUNCTION_REPLACE([func_basename], [dnl func_basename_result="${1##*/}"]) _LT_PROG_FUNCTION_REPLACE([func_dirname_and_basename], [dnl case ${1} in */*) func_dirname_result="${1%/*}${2}" ;; * ) func_dirname_result="${3}" ;; esac func_basename_result="${1##*/}"]) _LT_PROG_FUNCTION_REPLACE([func_stripname], [dnl # pdksh 5.2.14 does not do ${X%$Y} correctly if both X and Y are # positional parameters, so assign one to ordinary parameter first. func_stripname_result=${3} func_stripname_result=${func_stripname_result#"${1}"} func_stripname_result=${func_stripname_result%"${2}"}]) _LT_PROG_FUNCTION_REPLACE([func_split_long_opt], [dnl func_split_long_opt_name=${1%%=*} func_split_long_opt_arg=${1#*=}]) _LT_PROG_FUNCTION_REPLACE([func_split_short_opt], [dnl func_split_short_opt_arg=${1#??} func_split_short_opt_name=${1%"$func_split_short_opt_arg"}]) _LT_PROG_FUNCTION_REPLACE([func_lo2o], [dnl case ${1} in *.lo) func_lo2o_result=${1%.lo}.${objext} ;; *) func_lo2o_result=${1} ;; esac]) _LT_PROG_FUNCTION_REPLACE([func_xform], [ func_xform_result=${1%.*}.lo]) _LT_PROG_FUNCTION_REPLACE([func_arith], [ func_arith_result=$(( $[*] ))]) _LT_PROG_FUNCTION_REPLACE([func_len], [ func_len_result=${#1}]) fi if test x"$lt_shell_append" = xyes; then _LT_PROG_FUNCTION_REPLACE([func_append], [ eval "${1}+=\\${2}"]) _LT_PROG_FUNCTION_REPLACE([func_append_quoted], [dnl func_quote_for_eval "${2}" dnl m4 expansion turns \\\\ into \\, and then the shell eval turns that into \ eval "${1}+=\\\\ \\$func_quote_for_eval_result"]) # Save a `func_append' function call where possible by direct use of '+=' sed -e 's%func_append \([[a-zA-Z_]]\{1,\}\) "%\1+="%g' $cfgfile > $cfgfile.tmp \ && mv -f "$cfgfile.tmp" "$cfgfile" \ || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp") test 0 -eq $? || _lt_function_replace_fail=: else # Save a `func_append' function call even when '+=' is not available sed -e 's%func_append \([[a-zA-Z_]]\{1,\}\) "%\1="$\1%g' $cfgfile > $cfgfile.tmp \ && mv -f "$cfgfile.tmp" "$cfgfile" \ || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp") test 0 -eq $? || _lt_function_replace_fail=: fi if test x"$_lt_function_replace_fail" = x":"; then AC_MSG_WARN([Unable to substitute extended shell functions in $ofile]) fi ]) # _LT_PATH_CONVERSION_FUNCTIONS # ----------------------------- # Determine which file name conversion functions should be used by # func_to_host_file (and, implicitly, by func_to_host_path). These are needed # for certain cross-compile configurations and native mingw. m4_defun([_LT_PATH_CONVERSION_FUNCTIONS], [AC_REQUIRE([AC_CANONICAL_HOST])dnl AC_REQUIRE([AC_CANONICAL_BUILD])dnl AC_MSG_CHECKING([how to convert $build file names to $host format]) AC_CACHE_VAL(lt_cv_to_host_file_cmd, [case $host in *-*-mingw* ) case $build in *-*-mingw* ) # actually msys lt_cv_to_host_file_cmd=func_convert_file_msys_to_w32 ;; *-*-cygwin* ) lt_cv_to_host_file_cmd=func_convert_file_cygwin_to_w32 ;; * ) # otherwise, assume *nix lt_cv_to_host_file_cmd=func_convert_file_nix_to_w32 ;; esac ;; *-*-cygwin* ) case $build in *-*-mingw* ) # actually msys lt_cv_to_host_file_cmd=func_convert_file_msys_to_cygwin ;; *-*-cygwin* ) lt_cv_to_host_file_cmd=func_convert_file_noop ;; * ) # otherwise, assume *nix lt_cv_to_host_file_cmd=func_convert_file_nix_to_cygwin ;; esac ;; * ) # unhandled hosts (and "normal" native builds) lt_cv_to_host_file_cmd=func_convert_file_noop ;; esac ]) to_host_file_cmd=$lt_cv_to_host_file_cmd AC_MSG_RESULT([$lt_cv_to_host_file_cmd]) _LT_DECL([to_host_file_cmd], [lt_cv_to_host_file_cmd], [0], [convert $build file names to $host format])dnl AC_MSG_CHECKING([how to convert $build file names to toolchain format]) AC_CACHE_VAL(lt_cv_to_tool_file_cmd, [#assume ordinary cross tools, or native build. lt_cv_to_tool_file_cmd=func_convert_file_noop case $host in *-*-mingw* ) case $build in *-*-mingw* ) # actually msys lt_cv_to_tool_file_cmd=func_convert_file_msys_to_w32 ;; esac ;; esac ]) to_tool_file_cmd=$lt_cv_to_tool_file_cmd AC_MSG_RESULT([$lt_cv_to_tool_file_cmd]) _LT_DECL([to_tool_file_cmd], [lt_cv_to_tool_file_cmd], [0], [convert $build files to toolchain format])dnl ])# _LT_PATH_CONVERSION_FUNCTIONS telepathy-gabble-0.18.2/m4/compiler.m40000644000175000017500000000501712200204333017401 0ustar00smcvsmcv00000000000000# compiler.m4 - autoconf macros for compiler settings # # Copyright © 2005 Scott James Remnant . # # Permission is hereby granted, free of charge, to any person obtaining # a copy of this software and associated documentation files (the # "Software"), to deal in the Software without restriction, including # without limitation the rights to use, copy, modify, merge, publish, # distribute, sublicense, and/or sell copies of the Software, and to # permit persons to whom the Software is furnished to do so, subject to # the following conditions: # # The above copyright notice and this permission notice shall be # included in all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. # IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR # ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF # CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # COMPILER_WARNINGS # ---------------------- # Add configure option to enable additional compiler warnings and treat # them as errors. AC_DEFUN([COMPILER_WARNINGS], [AC_ARG_ENABLE(compiler-warnings, AS_HELP_STRING([--enable-compiler-warnings], [Enable additional compiler warnings]), [if test "x$enable_compiler_warnings" = "xyes"; then if test "x$GCC" = "xyes"; then CFLAGS="-Wall -Werror $CFLAGS" fi if test "x$GXX" = "xyes"; then CXXFLAGS="-Wall -Werror $CXXFLAGS" fi fi])dnl ])# COMPILER_WARNINGS # COMPILER_OPTIMISATIONS # --------------------------- # Add configure option to disable optimisations. AC_DEFUN([COMPILER_OPTIMISATIONS], [AC_ARG_ENABLE(compiler-optimisations, AS_HELP_STRING([--disable-compiler-optimisations], [Disable compiler optimisations]), [if test "x$enable_compiler_optimisations" = "xno"; then [CFLAGS=`echo "$CFLAGS" | sed -e "s/ -O[1-9]*\b/ -O0/g"`] fi])dnl ])# COMPILER_OPTIMISATIONS # COMPILER_COVERAGE # ---------------------- # Add configure option to enable coverage data. AC_DEFUN([COMPILER_COVERAGE], [AC_ARG_ENABLE(compiler-coverage, AS_HELP_STRING([--enable-compiler-coverage], [Enable generation of coverage data]), [if test "x$enable_compiler_coverage" = "xyes"; then if test "x$GCC" = "xyes"; then CFLAGS="$CFLAGS -fprofile-arcs -ftest-coverage" fi fi])dnl ])# COMPILER_COVERAGE telepathy-gabble-0.18.2/m4/ax_config_dir.m40000644000175000017500000000725512200204333020370 0ustar00smcvsmcv00000000000000dnl Copied from Audacity 1.3.10 which itself is licensed under the GPL v2 or dnl any later version dnl Function to configure a sub-library now, because we need to know the result dnl of the configuration now in order to take decisions. dnl We don't worry about whether the configuration worked or not - it is dnl assumed that the next thing after this will be a package-specific check to dnl see if the package is actually available. (Hint: use pkg-config and dnl -uninstalled.pc files if available). dnl code based on a simplification of _AC_OUTPUT_SUBDIRS in dnl /usr/share/autoconf/autoconf/status.m4 which implements part of dnl AC_CONFIG_SUBDIRS AC_DEFUN([AX_CONFIG_DIR], [AC_REQUIRE([AC_DISABLE_OPTION_CHECKING])] [m4_append([_AC_LIST_SUBDIRS], [$1], [])] [ # Remove --cache-file and --srcdir arguments so they do not pile up. ax_sub_configure_args= ax_prev= eval "set x $ac_configure_args" shift for ax_arg do if test -n "$ax_prev"; then ax_prev= continue fi case $ax_arg in -cache-file | --cache-file | --cache-fil | --cache-fi \ | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c) ax_prev=cache_file ;; -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \ | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* \ | --c=*) ;; --config-cache | -C) ;; -srcdir | --srcdir | --srcdi | --srcd | --src | --sr) ax_prev=srcdir ;; -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*) ;; -prefix | --prefix | --prefi | --pref | --pre | --pr | --p) ax_prev=prefix ;; -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*) ;; *) case $ax_arg in *\'*) ax_arg=`echo "$ax_arg" | sed "s/'/'\\\\\\\\''/g"` ;; esac ax_sub_configure_args="$ax_sub_configure_args '$ax_arg'" ;; esac done # Always prepend --prefix to ensure using the same prefix # in subdir configurations. ax_arg="--prefix=$prefix" case $ax_arg in *\'*) ax_arg=`echo "$ax_arg" | sed "s/'/'\\\\\\\\''/g"` ;; esac ax_sub_configure_args="'$ax_arg' $ax_sub_configure_args" # Pass --silent if test "$silent" = yes; then ax_sub_configure_args="--silent $ax_sub_configure_args" fi ax_popdir=`pwd` AC_MSG_NOTICE([Configuring sources in $1]) dnl for out-of-place builds srcdir and builddir will be different, and dnl builddir may not exist, so we must create it AS_MKDIR_P(["$1"]) dnl and also set the variables. As this isn't autoconf, the following may be dnl risky: _AC_SRCDIRS(["$1"]) cd "$1" # Check for guested configure; otherwise get Cygnus style configure. if test -f "configure.gnu"; then ax_sub_configure=$ac_srcdir/configure.gnu elif test -f "$ac_srcdir/configure"; then ax_sub_configure=$ac_srcdir/configure elif test -f "$ac_srcdir/configure.in"; then # This should be Cygnus configure. ax_sub_configure=$ac_aux_dir/configure else AC_MSG_WARN([no configuration information is in $1]) ax_sub_configure= fi # The recursion is here. if test -n "$ax_sub_configure"; then # Make the cache file name correct relative to the subdirectory. case $cache_file in [[\\/]]* | ?:[[\\/]]* ) ax_sub_cache_file=$cache_file ;; *) # Relative name. ax_sub_cache_file=$ac_top_build_prefix$cache_file ;; esac AC_MSG_NOTICE([running $SHELL $ax_sub_configure $ax_sub_configure_args --cache-file=$ax_sub_cache_file --srcdir=$ac_srcdir]) # The eval makes quoting arguments work. eval "\$SHELL \"\$ax_sub_configure\" $ax_sub_configure_args \ --cache-file=\"\$ax_sub_cache_file\" --srcdir=\"\$ax_srcdir\"" fi cd "$ax_popdir" AC_MSG_NOTICE([Done configuring in $1]) ])